2012-10-14 17:20:24 +02:00
|
|
|
---------------------------------------------------------------------------
|
2014-05-20 13:02:13 +02:00
|
|
|
--- Handling of drawables. A drawable is something that can be drawn to.
|
|
|
|
--
|
2012-10-14 17:20:24 +02:00
|
|
|
-- @author Uli Schlachter
|
|
|
|
-- @copyright 2012 Uli Schlachter
|
2015-02-25 11:18:53 +01:00
|
|
|
-- @classmod wibox.drawable
|
2012-10-14 17:20:24 +02:00
|
|
|
---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
local drawable = {}
|
|
|
|
local capi = {
|
|
|
|
awesome = awesome,
|
2016-02-26 18:42:57 +01:00
|
|
|
root = root,
|
|
|
|
screen = screen
|
2012-10-14 17:20:24 +02:00
|
|
|
}
|
|
|
|
local beautiful = require("beautiful")
|
2018-12-27 05:34:17 +01:00
|
|
|
local base = require("wibox.widget.base")
|
2012-10-14 17:20:24 +02:00
|
|
|
local cairo = require("lgi").cairo
|
|
|
|
local color = require("gears.color")
|
|
|
|
local object = require("gears.object")
|
|
|
|
local surface = require("gears.surface")
|
2015-01-11 10:58:49 +01:00
|
|
|
local timer = require("gears.timer")
|
2016-05-16 05:59:59 +02:00
|
|
|
local grect = require("gears.geometry").rectangle
|
2015-08-12 13:12:45 +02:00
|
|
|
local matrix = require("gears.matrix")
|
2022-07-03 08:10:31 +02:00
|
|
|
local whierarchy = require("wibox.hierarchy")
|
2016-02-07 14:13:43 +01:00
|
|
|
local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)
|
2012-10-14 17:20:24 +02:00
|
|
|
|
2016-10-04 21:20:55 +02:00
|
|
|
local visible_drawables = {}
|
2012-11-03 19:43:31 +01:00
|
|
|
|
Partly fix removal of systray from a wibox
This commit changes the systray widget, wibox.drawable and the C code to
fix the following bug: When the systray widget is removed from a
drawable without being moved somewhere else, the systray stayed visible.
This was because the systray is not drawn by awesome, but only placed.
When the widget is no longer "drawn", it stays wherever it was placed
last.
This change works by detecting the situation when the systray is
removed. Then, the C code is specifically told to remove the systray
window from the drawable.
Note that this is only a partial fix. This change works correctly when
the widget is removed completely, because it is no longer placed by its
parent widget. However, for example, when you do
wibox.widget.systray().visible = false, the effect is just that the
systray widget gets size 0x0. This is not really visible, but as far as
this change is concerned, the widget is still part of the drawable.
Signed-off-by: Uli Schlachter <psychon@znc.in>
2017-03-11 18:14:38 +01:00
|
|
|
local systray_widget
|
|
|
|
|
2015-08-08 13:13:47 +02:00
|
|
|
-- Get the widget context. This should always return the same table (if
|
|
|
|
-- possible), so that our draw and fit caches can work efficiently.
|
|
|
|
local function get_widget_context(self)
|
|
|
|
local geom = self.drawable:geometry()
|
2016-05-16 05:59:59 +02:00
|
|
|
|
2016-11-27 18:09:10 +01:00
|
|
|
local s = self._forced_screen
|
|
|
|
if not s then
|
|
|
|
local sgeos = {}
|
2016-05-16 05:59:59 +02:00
|
|
|
|
2016-12-25 13:05:26 +01:00
|
|
|
for scr in capi.screen do
|
|
|
|
sgeos[scr] = scr.geometry
|
2016-11-27 18:09:10 +01:00
|
|
|
end
|
2016-05-16 05:59:59 +02:00
|
|
|
|
2016-11-27 18:09:10 +01:00
|
|
|
s = grect.get_by_coord(sgeos, geom.x, geom.y) or capi.screen.primary
|
|
|
|
end
|
2016-05-16 05:59:59 +02:00
|
|
|
|
2015-08-08 13:13:47 +02:00
|
|
|
local context = self._widget_context
|
2019-06-15 08:16:27 +02:00
|
|
|
local dpi = s and s.dpi or 96
|
2015-08-08 13:13:47 +02:00
|
|
|
if (not context) or context.screen ~= s or context.dpi ~= dpi then
|
|
|
|
context = {
|
|
|
|
screen = s,
|
|
|
|
dpi = dpi,
|
|
|
|
drawable = self,
|
|
|
|
}
|
|
|
|
for k, v in pairs(self._widget_context_skeleton) do
|
|
|
|
context[k] = v
|
|
|
|
end
|
|
|
|
self._widget_context = context
|
2016-09-23 10:03:12 +02:00
|
|
|
|
|
|
|
-- Give widgets a chance to react to the new context
|
|
|
|
self._need_complete_repaint = true
|
2015-08-08 13:13:47 +02:00
|
|
|
end
|
|
|
|
return context
|
|
|
|
end
|
|
|
|
|
2012-10-14 17:20:24 +02:00
|
|
|
local function do_redraw(self)
|
2016-09-23 10:18:49 +02:00
|
|
|
if not self.drawable.valid then return end
|
2016-11-27 18:09:10 +01:00
|
|
|
if self._forced_screen and not self._forced_screen.valid then return end
|
2016-09-07 06:41:10 +02:00
|
|
|
|
2016-01-17 17:38:52 +01:00
|
|
|
local surf = surface.load_silently(self.drawable.surface, false)
|
2013-02-20 14:19:31 +01:00
|
|
|
-- The surface can be nil if the drawable's parent was already finalized
|
|
|
|
if not surf then return end
|
|
|
|
local cr = cairo.Context(surf)
|
2012-10-14 17:20:24 +02:00
|
|
|
local geom = self.drawable:geometry();
|
|
|
|
local x, y, width, height = geom.x, geom.y, geom.width, geom.height
|
2016-02-08 09:37:09 +01:00
|
|
|
local context = get_widget_context(self)
|
2012-10-14 17:20:24 +02:00
|
|
|
|
2015-08-12 13:12:45 +02:00
|
|
|
-- Relayout
|
2015-09-15 12:49:20 +02:00
|
|
|
if self._need_relayout or self._need_complete_repaint then
|
2015-08-12 13:12:45 +02:00
|
|
|
self._need_relayout = false
|
2018-12-27 05:24:06 +01:00
|
|
|
if self._widget_hierarchy and self._widget then
|
Partly fix removal of systray from a wibox
This commit changes the systray widget, wibox.drawable and the C code to
fix the following bug: When the systray widget is removed from a
drawable without being moved somewhere else, the systray stayed visible.
This was because the systray is not drawn by awesome, but only placed.
When the widget is no longer "drawn", it stays wherever it was placed
last.
This change works by detecting the situation when the systray is
removed. Then, the C code is specifically told to remove the systray
window from the drawable.
Note that this is only a partial fix. This change works correctly when
the widget is removed completely, because it is no longer placed by its
parent widget. However, for example, when you do
wibox.widget.systray().visible = false, the effect is just that the
systray widget gets size 0x0. This is not really visible, but as far as
this change is concerned, the widget is still part of the drawable.
Signed-off-by: Uli Schlachter <psychon@znc.in>
2017-03-11 18:14:38 +01:00
|
|
|
local had_systray = systray_widget and self._widget_hierarchy:get_count(systray_widget) > 0
|
|
|
|
|
2016-02-08 09:37:09 +01:00
|
|
|
self._widget_hierarchy:update(context,
|
2018-12-27 05:24:06 +01:00
|
|
|
self._widget, width, height, self._dirty_area)
|
Partly fix removal of systray from a wibox
This commit changes the systray widget, wibox.drawable and the C code to
fix the following bug: When the systray widget is removed from a
drawable without being moved somewhere else, the systray stayed visible.
This was because the systray is not drawn by awesome, but only placed.
When the widget is no longer "drawn", it stays wherever it was placed
last.
This change works by detecting the situation when the systray is
removed. Then, the C code is specifically told to remove the systray
window from the drawable.
Note that this is only a partial fix. This change works correctly when
the widget is removed completely, because it is no longer placed by its
parent widget. However, for example, when you do
wibox.widget.systray().visible = false, the effect is just that the
systray widget gets size 0x0. This is not really visible, but as far as
this change is concerned, the widget is still part of the drawable.
Signed-off-by: Uli Schlachter <psychon@znc.in>
2017-03-11 18:14:38 +01:00
|
|
|
|
|
|
|
local has_systray = systray_widget and self._widget_hierarchy:get_count(systray_widget) > 0
|
|
|
|
if had_systray and not has_systray then
|
|
|
|
systray_widget:_kickout(context)
|
|
|
|
end
|
2015-09-19 13:57:18 +02:00
|
|
|
else
|
|
|
|
self._need_complete_repaint = true
|
2018-12-27 05:24:06 +01:00
|
|
|
if self._widget then
|
2015-09-19 13:57:18 +02:00
|
|
|
self._widget_hierarchy_callback_arg = {}
|
2022-07-03 08:10:31 +02:00
|
|
|
self._widget_hierarchy = whierarchy.new(context, self._widget, width, height,
|
2015-09-19 13:57:18 +02:00
|
|
|
self._redraw_callback, self._layout_callback, self._widget_hierarchy_callback_arg)
|
|
|
|
else
|
|
|
|
self._widget_hierarchy = nil
|
|
|
|
end
|
|
|
|
end
|
2015-08-12 13:12:45 +02:00
|
|
|
|
2015-09-19 13:57:18 +02:00
|
|
|
if self._need_complete_repaint then
|
2015-08-12 13:12:45 +02:00
|
|
|
self._need_complete_repaint = false
|
|
|
|
self._dirty_area:union_rectangle(cairo.RectangleInt{
|
|
|
|
x = 0, y = 0, width = width, height = height
|
|
|
|
})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Clip to the dirty area
|
|
|
|
if self._dirty_area:is_empty() then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
for i = 0, self._dirty_area:num_rectangles() - 1 do
|
|
|
|
local rect = self._dirty_area:get_rectangle(i)
|
|
|
|
cr:rectangle(rect.x, rect.y, rect.width, rect.height)
|
|
|
|
end
|
|
|
|
self._dirty_area = cairo.Region.create()
|
|
|
|
cr:clip()
|
|
|
|
|
2012-10-14 17:20:24 +02:00
|
|
|
-- Draw the background
|
|
|
|
cr:save()
|
2014-02-23 00:02:57 +01:00
|
|
|
|
|
|
|
if not capi.awesome.composite_manager_running then
|
|
|
|
-- This is pseudo-transparency: We draw the wallpaper in the background
|
2016-03-27 11:29:28 +02:00
|
|
|
local wallpaper = surface.load_silently(capi.root.wallpaper(), false)
|
2017-01-25 23:10:37 +01:00
|
|
|
cr.operator = cairo.Operator.SOURCE
|
2014-02-23 00:02:57 +01:00
|
|
|
if wallpaper then
|
|
|
|
cr:set_source_surface(wallpaper, -x, -y)
|
2017-01-25 23:10:37 +01:00
|
|
|
else
|
|
|
|
cr:set_source_rgb(0, 0, 0)
|
2014-02-23 00:02:57 +01:00
|
|
|
end
|
2017-01-25 23:10:37 +01:00
|
|
|
cr:paint()
|
2014-02-23 00:02:57 +01:00
|
|
|
cr.operator = cairo.Operator.OVER
|
|
|
|
else
|
2014-02-23 12:19:26 +01:00
|
|
|
-- This is true transparency: We draw a translucent background
|
2012-10-14 17:20:24 +02:00
|
|
|
cr.operator = cairo.Operator.SOURCE
|
|
|
|
end
|
|
|
|
|
|
|
|
cr:set_source(self.background_color)
|
|
|
|
cr:paint()
|
2016-02-08 09:37:09 +01:00
|
|
|
|
2016-07-09 10:30:36 +02:00
|
|
|
cr:restore()
|
|
|
|
|
2016-02-08 09:37:09 +01:00
|
|
|
-- Paint the background image
|
|
|
|
if self.background_image then
|
2016-07-09 10:30:36 +02:00
|
|
|
cr:save()
|
2016-02-08 09:37:09 +01:00
|
|
|
if type(self.background_image) == "function" then
|
|
|
|
self.background_image(context, cr, width, height, unpack(self.background_image_args))
|
|
|
|
else
|
|
|
|
local pattern = cairo.Pattern.create_for_surface(self.background_image)
|
|
|
|
cr:set_source(pattern)
|
|
|
|
cr:paint()
|
|
|
|
end
|
2016-07-09 10:30:36 +02:00
|
|
|
cr:restore()
|
2016-02-08 09:37:09 +01:00
|
|
|
end
|
|
|
|
|
2012-10-14 17:20:24 +02:00
|
|
|
-- Draw the widget
|
2015-08-12 13:12:45 +02:00
|
|
|
if self._widget_hierarchy then
|
2012-10-14 17:20:24 +02:00
|
|
|
cr:set_source(self.foreground_color)
|
2016-02-08 09:37:09 +01:00
|
|
|
self._widget_hierarchy:draw(context, cr)
|
2012-10-14 17:20:24 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
self.drawable:refresh()
|
2014-04-01 16:20:45 +02:00
|
|
|
|
2015-09-06 13:23:05 +02:00
|
|
|
assert(cr.status == "SUCCESS", "Cairo context entered error state: " .. cr.status)
|
2012-10-14 17:20:24 +02:00
|
|
|
end
|
|
|
|
|
2022-07-03 08:10:31 +02:00
|
|
|
local function find_widgets(self, result, hierarchy, x, y)
|
|
|
|
local m = hierarchy:get_matrix_from_device()
|
2015-08-12 13:12:45 +02:00
|
|
|
|
|
|
|
-- Is (x,y) inside of this hierarchy or any child (aka the draw extents)
|
|
|
|
local x1, y1 = m:transform_point(x, y)
|
2022-07-03 08:10:31 +02:00
|
|
|
local x2, y2, w2, h2 = hierarchy:get_draw_extents()
|
2016-02-07 14:13:43 +01:00
|
|
|
if x1 < x2 or x1 >= x2 + w2 then
|
2015-08-12 13:12:45 +02:00
|
|
|
return
|
|
|
|
end
|
2016-02-07 14:13:43 +01:00
|
|
|
if y1 < y2 or y1 >= y2 + h2 then
|
2015-08-12 13:12:45 +02:00
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Is (x,y) inside of this widget?
|
2022-07-03 08:10:31 +02:00
|
|
|
local width, height = hierarchy:get_size()
|
2015-08-12 13:12:45 +02:00
|
|
|
if x1 >= 0 and y1 >= 0 and x1 <= width and y1 <= height then
|
|
|
|
-- Get the extents of this widget in the device space
|
2022-07-03 08:10:31 +02:00
|
|
|
local x3, y3, w3, h3 = matrix.transform_rectangle(hierarchy:get_matrix_to_device(),
|
2015-08-12 13:12:45 +02:00
|
|
|
0, 0, width, height)
|
|
|
|
table.insert(result, {
|
2016-02-07 14:13:43 +01:00
|
|
|
x = x3, y = y3, width = w3, height = h3,
|
2016-10-01 16:16:11 +02:00
|
|
|
widget_width = width,
|
|
|
|
widget_height = height,
|
2022-07-03 08:10:31 +02:00
|
|
|
drawable = self,
|
|
|
|
widget = hierarchy:get_widget(),
|
|
|
|
hierarchy = hierarchy
|
2015-08-12 13:12:45 +02:00
|
|
|
})
|
|
|
|
end
|
2022-07-03 08:10:31 +02:00
|
|
|
for _, child in ipairs(hierarchy:get_children()) do
|
|
|
|
find_widgets(self, result, child, x, y)
|
2015-08-12 13:12:45 +02:00
|
|
|
end
|
2012-10-14 17:20:24 +02:00
|
|
|
end
|
|
|
|
|
2021-12-21 06:54:15 +01:00
|
|
|
-- Find a widget by a point.
|
2012-10-14 17:20:24 +02:00
|
|
|
-- The drawable must have drawn itself at least once for this to work.
|
|
|
|
-- @param x X coordinate of the point
|
|
|
|
-- @param y Y coordinate of the point
|
2016-10-01 16:16:11 +02:00
|
|
|
-- @treturn table A table containing a description of all the widgets that
|
|
|
|
-- contain the given point. Each entry is a table containing this drawable as
|
|
|
|
-- its `.drawable` entry, the widget under `.widget` and the instance of
|
|
|
|
-- `wibox.hierarchy` describing the size and position of the widget under
|
|
|
|
-- `.hierarchy`. For convenience, `.x`, `.y`, `.width` and `.height` contain an
|
|
|
|
-- approximation of the widget's extents on the surface. `widget_width` and
|
|
|
|
-- `widget_height` contain the exact size of the widget in its own, local
|
|
|
|
-- coordinate system (which may e.g. be rotated and scaled).
|
2012-10-14 17:20:24 +02:00
|
|
|
function drawable:find_widgets(x, y)
|
2015-08-12 13:12:45 +02:00
|
|
|
local result = {}
|
|
|
|
if self._widget_hierarchy then
|
|
|
|
find_widgets(self, result, self._widget_hierarchy, x, y)
|
2012-10-14 17:20:24 +02:00
|
|
|
end
|
2015-08-12 13:12:45 +02:00
|
|
|
return result
|
2012-10-14 17:20:24 +02:00
|
|
|
end
|
|
|
|
|
Partly fix removal of systray from a wibox
This commit changes the systray widget, wibox.drawable and the C code to
fix the following bug: When the systray widget is removed from a
drawable without being moved somewhere else, the systray stayed visible.
This was because the systray is not drawn by awesome, but only placed.
When the widget is no longer "drawn", it stays wherever it was placed
last.
This change works by detecting the situation when the systray is
removed. Then, the C code is specifically told to remove the systray
window from the drawable.
Note that this is only a partial fix. This change works correctly when
the widget is removed completely, because it is no longer placed by its
parent widget. However, for example, when you do
wibox.widget.systray().visible = false, the effect is just that the
systray widget gets size 0x0. This is not really visible, but as far as
this change is concerned, the widget is still part of the drawable.
Signed-off-by: Uli Schlachter <psychon@znc.in>
2017-03-11 18:14:38 +01:00
|
|
|
-- Private API. Not documented on purpose.
|
|
|
|
function drawable._set_systray_widget(widget)
|
2022-07-03 08:10:31 +02:00
|
|
|
whierarchy.count_widget(widget)
|
Partly fix removal of systray from a wibox
This commit changes the systray widget, wibox.drawable and the C code to
fix the following bug: When the systray widget is removed from a
drawable without being moved somewhere else, the systray stayed visible.
This was because the systray is not drawn by awesome, but only placed.
When the widget is no longer "drawn", it stays wherever it was placed
last.
This change works by detecting the situation when the systray is
removed. Then, the C code is specifically told to remove the systray
window from the drawable.
Note that this is only a partial fix. This change works correctly when
the widget is removed completely, because it is no longer placed by its
parent widget. However, for example, when you do
wibox.widget.systray().visible = false, the effect is just that the
systray widget gets size 0x0. This is not really visible, but as far as
this change is concerned, the widget is still part of the drawable.
Signed-off-by: Uli Schlachter <psychon@znc.in>
2017-03-11 18:14:38 +01:00
|
|
|
systray_widget = widget
|
|
|
|
end
|
2012-10-14 17:20:24 +02:00
|
|
|
|
|
|
|
--- Set the widget that the drawable displays
|
|
|
|
function drawable:set_widget(widget)
|
2018-12-27 05:34:17 +01:00
|
|
|
self._widget = base.make_widget_from_value(widget)
|
2012-10-14 17:20:24 +02:00
|
|
|
|
|
|
|
-- Make sure the widget gets drawn
|
2015-08-12 13:12:45 +02:00
|
|
|
self._need_relayout = true
|
2012-10-14 17:20:24 +02:00
|
|
|
self.draw()
|
|
|
|
end
|
|
|
|
|
2018-12-27 05:24:06 +01:00
|
|
|
function drawable:get_widget()
|
|
|
|
return rawget(self, "_widget")
|
|
|
|
end
|
|
|
|
|
2012-10-14 17:20:24 +02:00
|
|
|
--- Set the background of the drawable
|
|
|
|
-- @param c The background to use. This must either be a cairo pattern object,
|
2014-05-20 13:02:13 +02:00
|
|
|
-- nil or a string that gears.color() understands.
|
2016-11-20 10:01:39 +01:00
|
|
|
-- @see gears.color
|
2012-10-14 17:20:24 +02:00
|
|
|
function drawable:set_bg(c)
|
2016-02-07 14:13:43 +01:00
|
|
|
c = c or "#000000"
|
2016-02-08 09:37:09 +01:00
|
|
|
local t = type(c)
|
|
|
|
|
|
|
|
if t == "string" or t == "table" then
|
2012-10-14 17:20:24 +02:00
|
|
|
c = color(c)
|
|
|
|
end
|
2014-03-16 14:58:14 +01:00
|
|
|
|
|
|
|
-- If the background is completely opaque, we don't need to redraw when
|
|
|
|
-- the drawable is moved
|
|
|
|
-- XXX: This isn't needed when awesome.composite_manager_running is true,
|
|
|
|
-- but a compositing manager could stop/start and we'd have to properly
|
|
|
|
-- handle this. So for now we choose the lazy approach.
|
|
|
|
local redraw_on_move = not color.create_opaque_pattern(c)
|
|
|
|
if self._redraw_on_move ~= redraw_on_move then
|
|
|
|
self._redraw_on_move = redraw_on_move
|
|
|
|
if redraw_on_move then
|
2015-08-12 13:12:45 +02:00
|
|
|
self.drawable:connect_signal("property::x", self._do_complete_repaint)
|
|
|
|
self.drawable:connect_signal("property::y", self._do_complete_repaint)
|
2014-03-16 14:58:14 +01:00
|
|
|
else
|
2015-08-12 13:12:45 +02:00
|
|
|
self.drawable:disconnect_signal("property::x", self._do_complete_repaint)
|
|
|
|
self.drawable:disconnect_signal("property::y", self._do_complete_repaint)
|
2014-03-16 14:58:14 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-10-14 17:20:24 +02:00
|
|
|
self.background_color = c
|
2015-08-12 13:12:45 +02:00
|
|
|
self._do_complete_repaint()
|
2012-10-14 17:20:24 +02:00
|
|
|
end
|
|
|
|
|
2016-02-08 09:37:09 +01:00
|
|
|
--- Set the background image of the drawable
|
|
|
|
-- If `image` is a function, it will be called with `(context, cr, width, height)`
|
|
|
|
-- as arguments. Any other arguments passed to this method will be appended.
|
|
|
|
-- @param image A background image or a function
|
|
|
|
function drawable:set_bgimage(image, ...)
|
2016-07-09 10:29:05 +02:00
|
|
|
if type(image) ~= "function" then
|
|
|
|
image = surface(image)
|
|
|
|
end
|
2016-02-08 09:37:09 +01:00
|
|
|
|
|
|
|
self.background_image = image
|
|
|
|
self.background_image_args = {...}
|
|
|
|
|
|
|
|
self._do_complete_repaint()
|
|
|
|
end
|
|
|
|
|
2012-10-14 17:20:24 +02:00
|
|
|
--- Set the foreground of the drawable
|
|
|
|
-- @param c The foreground to use. This must either be a cairo pattern object,
|
2014-05-20 13:02:13 +02:00
|
|
|
-- nil or a string that gears.color() understands.
|
2016-11-20 10:01:39 +01:00
|
|
|
-- @see gears.color
|
2012-10-14 17:20:24 +02:00
|
|
|
function drawable:set_fg(c)
|
2016-02-07 14:13:43 +01:00
|
|
|
c = c or "#FFFFFF"
|
2012-10-14 17:20:24 +02:00
|
|
|
if type(c) == "string" or type(c) == "table" then
|
|
|
|
c = color(c)
|
|
|
|
end
|
|
|
|
self.foreground_color = c
|
2015-08-12 13:12:45 +02:00
|
|
|
self._do_complete_repaint()
|
2012-10-14 17:20:24 +02:00
|
|
|
end
|
|
|
|
|
2016-11-27 18:09:10 +01:00
|
|
|
function drawable:_force_screen(s)
|
|
|
|
self._forced_screen = s
|
|
|
|
end
|
|
|
|
|
2016-10-04 21:18:18 +02:00
|
|
|
function drawable:_inform_visible(visible)
|
2016-10-04 21:27:10 +02:00
|
|
|
self._visible = visible
|
2016-10-04 21:20:55 +02:00
|
|
|
if visible then
|
|
|
|
visible_drawables[self] = true
|
|
|
|
-- The wallpaper or widgets might have changed
|
|
|
|
self:_do_complete_repaint()
|
|
|
|
else
|
|
|
|
visible_drawables[self] = nil
|
|
|
|
end
|
2016-10-04 21:18:18 +02:00
|
|
|
end
|
|
|
|
|
2012-10-14 17:20:24 +02:00
|
|
|
local function emit_difference(name, list, skip)
|
|
|
|
local function in_table(table, val)
|
2016-02-07 14:13:43 +01:00
|
|
|
for _, v in pairs(table) do
|
2013-03-22 05:52:02 +01:00
|
|
|
if v.widget == val.widget then
|
2012-10-14 17:20:24 +02:00
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
2016-02-07 14:13:43 +01:00
|
|
|
for _, v in pairs(list) do
|
2012-10-14 17:20:24 +02:00
|
|
|
if not in_table(skip, v) then
|
2013-03-22 05:52:02 +01:00
|
|
|
v.widget:emit_signal(name,v)
|
2012-10-14 17:20:24 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-07-03 08:10:31 +02:00
|
|
|
local function handle_leave(self)
|
|
|
|
emit_difference("mouse::leave", self._widgets_under_mouse, {})
|
|
|
|
self._widgets_under_mouse = {}
|
2012-10-14 17:20:24 +02:00
|
|
|
end
|
|
|
|
|
2022-07-03 08:10:31 +02:00
|
|
|
local function handle_motion(self, x, y)
|
|
|
|
local dgeo = self.drawable:geometry()
|
|
|
|
|
|
|
|
if x < 0 or y < 0 or x > dgeo.width or y > dgeo.height then
|
|
|
|
return handle_leave(self)
|
2012-10-14 17:20:24 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
-- Build a plain list of all widgets on that point
|
2022-07-03 08:10:31 +02:00
|
|
|
local widgets_list = self:find_widgets(x, y)
|
2012-10-14 17:20:24 +02:00
|
|
|
|
|
|
|
-- First, "leave" all widgets that were left
|
2022-07-03 08:10:31 +02:00
|
|
|
emit_difference("mouse::leave", self._widgets_under_mouse, widgets_list)
|
2012-10-14 17:20:24 +02:00
|
|
|
-- Then enter some widgets
|
2022-07-03 08:10:31 +02:00
|
|
|
emit_difference("mouse::enter", widgets_list, self._widgets_under_mouse)
|
2012-10-14 17:20:24 +02:00
|
|
|
|
2022-07-03 08:10:31 +02:00
|
|
|
self._widgets_under_mouse = widgets_list
|
2012-10-14 17:20:24 +02:00
|
|
|
end
|
|
|
|
|
2022-07-03 08:10:31 +02:00
|
|
|
local function setup_signals(self)
|
|
|
|
local d = self.drawable
|
2012-10-31 22:12:30 +01:00
|
|
|
|
|
|
|
local function clone_signal(name)
|
|
|
|
-- When "name" is emitted on wibox.drawin, also emit it on wibox
|
|
|
|
d:connect_signal(name, function(_, ...)
|
2022-07-03 08:10:31 +02:00
|
|
|
self:emit_signal(name, ...)
|
2012-10-31 22:12:30 +01:00
|
|
|
end)
|
|
|
|
end
|
|
|
|
clone_signal("button::press")
|
|
|
|
clone_signal("button::release")
|
|
|
|
clone_signal("mouse::enter")
|
|
|
|
clone_signal("mouse::leave")
|
|
|
|
clone_signal("mouse::move")
|
|
|
|
clone_signal("property::surface")
|
|
|
|
clone_signal("property::width")
|
|
|
|
clone_signal("property::height")
|
|
|
|
clone_signal("property::x")
|
|
|
|
clone_signal("property::y")
|
|
|
|
end
|
|
|
|
|
2015-08-08 13:13:47 +02:00
|
|
|
function drawable.new(d, widget_context_skeleton, drawable_name)
|
2012-10-14 17:20:24 +02:00
|
|
|
local ret = object()
|
|
|
|
ret.drawable = d
|
2015-08-08 13:13:47 +02:00
|
|
|
ret._widget_context_skeleton = widget_context_skeleton
|
2015-08-12 13:12:45 +02:00
|
|
|
ret._need_complete_repaint = true
|
|
|
|
ret._need_relayout = true
|
|
|
|
ret._dirty_area = cairo.Region.create()
|
2012-10-31 22:12:30 +01:00
|
|
|
setup_signals(ret)
|
2012-10-14 17:20:24 +02:00
|
|
|
|
|
|
|
for k, v in pairs(drawable) do
|
|
|
|
if type(v) == "function" then
|
|
|
|
ret[k] = v
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Only redraw a drawable once, even when we get told to do so multiple times.
|
|
|
|
ret._redraw_pending = false
|
|
|
|
ret._do_redraw = function()
|
|
|
|
ret._redraw_pending = false
|
|
|
|
do_redraw(ret)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Connect our signal when we need a redraw
|
|
|
|
ret.draw = function()
|
|
|
|
if not ret._redraw_pending then
|
2015-01-11 10:58:49 +01:00
|
|
|
timer.delayed_call(ret._do_redraw)
|
2012-10-14 17:20:24 +02:00
|
|
|
ret._redraw_pending = true
|
|
|
|
end
|
|
|
|
end
|
2015-08-12 13:12:45 +02:00
|
|
|
ret._do_complete_repaint = function()
|
|
|
|
ret._need_complete_repaint = true
|
|
|
|
ret:draw()
|
|
|
|
end
|
wibox.drawable: React to screen changes
The previous commit made wibox.drawable turn a "normal redraw" into a complete
repaint when it was moved to another screen. However, nothing happened until
that normal redraw.
This commit triggers a normal redraw when we are (possibly) moved to another
screen. More precise, this means that whenever a screen appears, disappears or
changes its geometry and when the drawable is moved, we trigger a normal redraw.
This redraw will likely do nothing, because no relayout is pending and no part
of the surface needs a redraw, so it is cheap.
However, if the drawable really ends up on another screen, then the code from
the previous commits makes us do a full relayout and redraw.
This commit likely fixes the current instability of test-screen-changes.lua. See
https://github.com/awesomeWM/awesome/issues/982#issuecomment-231712056.
As explained there, the test fails because the fake screen that it created is
still referenced, so cannot be garbage collected, but the test doesn't succeed
unless the screen is garbage collected. So something is still referencing the
screen that was removed. This something can be a client's titlebar, because the
underlying drawable still has a context member referring to the old screen.
This commit should fix that problem, because we now trigger a redraw which will
compute a new context and thus the reference to the old screen is released.
Signed-off-by: Uli Schlachter <psychon@znc.in>
2016-09-23 10:07:29 +02:00
|
|
|
|
|
|
|
-- Do a full redraw if the surface changes (the new surface has no content yet)
|
2015-08-12 13:12:45 +02:00
|
|
|
d:connect_signal("property::surface", ret._do_complete_repaint)
|
2014-03-16 14:58:14 +01:00
|
|
|
|
wibox.drawable: React to screen changes
The previous commit made wibox.drawable turn a "normal redraw" into a complete
repaint when it was moved to another screen. However, nothing happened until
that normal redraw.
This commit triggers a normal redraw when we are (possibly) moved to another
screen. More precise, this means that whenever a screen appears, disappears or
changes its geometry and when the drawable is moved, we trigger a normal redraw.
This redraw will likely do nothing, because no relayout is pending and no part
of the surface needs a redraw, so it is cheap.
However, if the drawable really ends up on another screen, then the code from
the previous commits makes us do a full relayout and redraw.
This commit likely fixes the current instability of test-screen-changes.lua. See
https://github.com/awesomeWM/awesome/issues/982#issuecomment-231712056.
As explained there, the test fails because the fake screen that it created is
still referenced, so cannot be garbage collected, but the test doesn't succeed
unless the screen is garbage collected. So something is still referencing the
screen that was removed. This something can be a client's titlebar, because the
underlying drawable still has a context member referring to the old screen.
This commit should fix that problem, because we now trigger a redraw which will
compute a new context and thus the reference to the old screen is released.
Signed-off-by: Uli Schlachter <psychon@znc.in>
2016-09-23 10:07:29 +02:00
|
|
|
-- Do a normal redraw when the drawable moves. This will likely do nothing
|
|
|
|
-- in most cases, but it makes us do a complete repaint when we are moved to
|
|
|
|
-- a different screen.
|
|
|
|
d:connect_signal("property::x", ret.draw)
|
|
|
|
d:connect_signal("property::y", ret.draw)
|
|
|
|
|
2014-03-16 14:58:14 +01:00
|
|
|
-- Currently we aren't redrawing on move (signals not connected).
|
|
|
|
-- :set_bg() will later recompute this.
|
|
|
|
ret._redraw_on_move = false
|
2012-10-14 17:20:24 +02:00
|
|
|
|
|
|
|
-- Set the default background
|
|
|
|
ret:set_bg(beautiful.bg_normal)
|
|
|
|
ret:set_fg(beautiful.fg_normal)
|
|
|
|
|
|
|
|
-- Initialize internals
|
|
|
|
ret._widgets_under_mouse = {}
|
|
|
|
|
|
|
|
local function button_signal(name)
|
2016-02-07 14:13:43 +01:00
|
|
|
d:connect_signal(name, function(_, x, y, button, modifiers)
|
2012-10-14 17:20:24 +02:00
|
|
|
local widgets = ret:find_widgets(x, y)
|
2016-02-07 14:13:43 +01:00
|
|
|
for _, v in pairs(widgets) do
|
2012-10-14 17:20:24 +02:00
|
|
|
-- Calculate x/y inside of the widget
|
2016-10-01 16:18:40 +02:00
|
|
|
local lx, ly = v.hierarchy:get_matrix_from_device():transform_point(x, y)
|
2013-03-22 05:52:02 +01:00
|
|
|
v.widget:emit_signal(name, lx, ly, button, modifiers,v)
|
2012-10-14 17:20:24 +02:00
|
|
|
end
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
button_signal("button::press")
|
|
|
|
button_signal("button::release")
|
|
|
|
|
|
|
|
d:connect_signal("mouse::move", function(_, x, y) handle_motion(ret, x, y) end)
|
|
|
|
d:connect_signal("mouse::leave", function() handle_leave(ret) end)
|
|
|
|
|
2015-08-12 13:12:45 +02:00
|
|
|
-- Set up our callbacks for repaints
|
2016-02-07 14:13:43 +01:00
|
|
|
ret._redraw_callback = function(hierar, arg)
|
2016-10-04 21:27:10 +02:00
|
|
|
-- Avoid crashes when a drawable was partly finalized and dirty_area is broken.
|
|
|
|
if not ret._visible then
|
2016-09-23 10:18:49 +02:00
|
|
|
return
|
|
|
|
end
|
2015-08-12 13:12:45 +02:00
|
|
|
if ret._widget_hierarchy_callback_arg ~= arg then
|
|
|
|
return
|
|
|
|
end
|
2016-02-07 14:13:43 +01:00
|
|
|
local m = hierar:get_matrix_to_device()
|
|
|
|
local x, y, width, height = matrix.transform_rectangle(m, hierar:get_draw_extents())
|
2015-08-12 13:12:45 +02:00
|
|
|
local x1, y1 = math.floor(x), math.floor(y)
|
|
|
|
local x2, y2 = math.ceil(x + width), math.ceil(y + height)
|
|
|
|
ret._dirty_area:union_rectangle(cairo.RectangleInt{
|
|
|
|
x = x1, y = y1, width = x2 - x1, height = y2 - y1
|
|
|
|
})
|
|
|
|
ret:draw()
|
|
|
|
end
|
2016-02-07 14:13:43 +01:00
|
|
|
ret._layout_callback = function(_, arg)
|
2015-08-12 13:12:45 +02:00
|
|
|
if ret._widget_hierarchy_callback_arg ~= arg then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
ret._need_relayout = true
|
2016-10-05 19:56:56 +02:00
|
|
|
-- When not visible, we will be redrawn when we become visible. In the
|
|
|
|
-- mean-time, the layout does not matter much.
|
|
|
|
if ret._visible then
|
|
|
|
ret:draw()
|
|
|
|
end
|
2015-08-12 13:12:45 +02:00
|
|
|
end
|
|
|
|
|
2015-07-29 21:12:41 +02:00
|
|
|
-- Add __tostring method to metatable.
|
|
|
|
ret.drawable_name = drawable_name or object.modulename(3)
|
|
|
|
local mt = {}
|
|
|
|
local orig_string = tostring(ret)
|
2016-02-07 14:13:43 +01:00
|
|
|
mt.__tostring = function()
|
2015-07-29 21:12:41 +02:00
|
|
|
return string.format("%s (%s)", ret.drawable_name, orig_string)
|
|
|
|
end
|
|
|
|
ret = setmetatable(ret, mt)
|
|
|
|
|
2012-10-14 17:20:24 +02:00
|
|
|
-- Make sure the drawable is drawn at least once
|
2015-08-12 13:12:45 +02:00
|
|
|
ret._do_complete_repaint()
|
2012-10-14 17:20:24 +02:00
|
|
|
|
2018-12-27 05:24:06 +01:00
|
|
|
return setmetatable(ret, {
|
|
|
|
__index = function(self, k)
|
|
|
|
if rawget(self, "get_"..k) then
|
|
|
|
return rawget(self, "get_"..k)(self)
|
|
|
|
else
|
|
|
|
return rawget(ret, k)
|
|
|
|
end
|
|
|
|
end,
|
|
|
|
__newindex = function(self, k,v)
|
|
|
|
if rawget(self, "set_"..k) then
|
|
|
|
rawget(self, "set_"..k)(self, v)
|
|
|
|
else
|
|
|
|
rawset(self, k, v)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
})
|
2012-10-14 17:20:24 +02:00
|
|
|
end
|
|
|
|
|
2012-11-03 19:43:31 +01:00
|
|
|
-- Redraw all drawables when the wallpaper changes
|
|
|
|
capi.awesome.connect_signal("wallpaper_changed", function()
|
2016-10-04 21:20:55 +02:00
|
|
|
for d in pairs(visible_drawables) do
|
|
|
|
d:_do_complete_repaint()
|
2012-11-03 19:43:31 +01:00
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
wibox.drawable: React to screen changes
The previous commit made wibox.drawable turn a "normal redraw" into a complete
repaint when it was moved to another screen. However, nothing happened until
that normal redraw.
This commit triggers a normal redraw when we are (possibly) moved to another
screen. More precise, this means that whenever a screen appears, disappears or
changes its geometry and when the drawable is moved, we trigger a normal redraw.
This redraw will likely do nothing, because no relayout is pending and no part
of the surface needs a redraw, so it is cheap.
However, if the drawable really ends up on another screen, then the code from
the previous commits makes us do a full relayout and redraw.
This commit likely fixes the current instability of test-screen-changes.lua. See
https://github.com/awesomeWM/awesome/issues/982#issuecomment-231712056.
As explained there, the test fails because the fake screen that it created is
still referenced, so cannot be garbage collected, but the test doesn't succeed
unless the screen is garbage collected. So something is still referencing the
screen that was removed. This something can be a client's titlebar, because the
underlying drawable still has a context member referring to the old screen.
This commit should fix that problem, because we now trigger a redraw which will
compute a new context and thus the reference to the old screen is released.
Signed-off-by: Uli Schlachter <psychon@znc.in>
2016-09-23 10:07:29 +02:00
|
|
|
-- Give drawables a chance to react to screen changes
|
|
|
|
local function draw_all()
|
2016-10-04 21:20:55 +02:00
|
|
|
for d in pairs(visible_drawables) do
|
|
|
|
d:draw()
|
wibox.drawable: React to screen changes
The previous commit made wibox.drawable turn a "normal redraw" into a complete
repaint when it was moved to another screen. However, nothing happened until
that normal redraw.
This commit triggers a normal redraw when we are (possibly) moved to another
screen. More precise, this means that whenever a screen appears, disappears or
changes its geometry and when the drawable is moved, we trigger a normal redraw.
This redraw will likely do nothing, because no relayout is pending and no part
of the surface needs a redraw, so it is cheap.
However, if the drawable really ends up on another screen, then the code from
the previous commits makes us do a full relayout and redraw.
This commit likely fixes the current instability of test-screen-changes.lua. See
https://github.com/awesomeWM/awesome/issues/982#issuecomment-231712056.
As explained there, the test fails because the fake screen that it created is
still referenced, so cannot be garbage collected, but the test doesn't succeed
unless the screen is garbage collected. So something is still referencing the
screen that was removed. This something can be a client's titlebar, because the
underlying drawable still has a context member referring to the old screen.
This commit should fix that problem, because we now trigger a redraw which will
compute a new context and thus the reference to the old screen is released.
Signed-off-by: Uli Schlachter <psychon@znc.in>
2016-09-23 10:07:29 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
screen.connect_signal("property::geometry", draw_all)
|
|
|
|
screen.connect_signal("added", draw_all)
|
|
|
|
screen.connect_signal("removed", draw_all)
|
|
|
|
|
2012-10-14 17:20:24 +02:00
|
|
|
return setmetatable(drawable, { __call = function(_, ...) return drawable.new(...) end })
|
|
|
|
|
|
|
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|