1 ---------------------------------------------------------------------------
2 -- @author Uli Schlachter
3 -- @copyright 2012 Uli Schlachter
4 -- @release @AWESOME_VERSION@
5 ---------------------------------------------------------------------------
12 local beautiful
= require("beautiful")
13 local cairo
= require("lgi").cairo
14 local color
= require("gears.color")
15 local object
= require("gears.object")
16 local sort = require("gears.sort")
17 local surface
= require("gears.surface")
19 local function do_redraw(self
)
20 local cr
= cairo
.Context(surface(self
.drawable
.surface
))
21 local geom
= self
.drawable
:geometry();
22 local x
, y
, width
, height
= geom
.x
, geom
.y
, geom
.width
, geom
.height
24 -- Draw the background
26 -- This is pseudo-transparency: We draw the wallpaper in the background
27 local wallpaper
= surface(capi
.root
.wallpaper())
29 cr
.operator
= cairo
.Operator
.SOURCE
30 cr
:set_source_surface(wallpaper
, -x
, -y
)
34 cr
.operator
= cairo
.Operator
.OVER
35 cr
:set_source(self
.background_color
)
40 self
._widget_geometries
= {}
42 cr
:set_source(self
.foreground_color
)
43 self
.widget
:draw(self
.widget_arg
, cr
, width
, height
)
44 self
:widget_at(self
.widget
, 0, 0, width
, height
)
47 self
.drawable
:refresh()
50 --- Register a widget's position.
51 -- This is internal, don't call it yourself! Only wibox.layout.base.draw_widget
52 -- is allowed to call this.
53 function drawable
:widget_at(widget
, x
, y
, width
, height
)
57 width
= width
, height
= height
59 table.insert(self
._widget_geometries
, t
)
62 --- Find a widget by a point.
63 -- The drawable must have drawn itself at least once for this to work.
64 -- @param x X coordinate of the point
65 -- @param y Y coordinate of the point
66 -- @return A sorted table with all widgets that contain the given point. The
67 -- widgets are sorted by relevance.
68 function drawable
:find_widgets(x
, y
)
70 -- Find all widgets that contain the point
71 for k
, v
in pairs(self
._widget_geometries
) do
73 if v
.x
> x
or v
.x
+ v
.width
<= x
then match
= false end
74 if v
.y
> y
or v
.y
+ v
.height
<= y
then match
= false end
76 table.insert(matches
, v
)
80 -- Sort the matches by area, the assumption here is that widgets don't
81 -- overlap and so smaller widgets are "more specific".
82 local function cmp(a
, b
)
83 local area_a
= a
.width
* a
.height
84 local area_b
= b
.width
* b
.height
85 return area_a
< area_b
93 --- Set the widget that the drawable displays
94 function drawable
:set_widget(widget
)
96 -- Disconnect from the old widget so that we aren't updated due to it
97 self
.widget
:disconnect_signal("widget::updated", self
.draw
)
102 widget
:connect_signal("widget::updated", self
.draw
)
105 -- Make sure the widget gets drawn
109 --- Set the background of the drawable
110 -- @param drawable The drawable to use
111 -- @param c The background to use. This must either be a cairo pattern object,
112 -- nil or a string that gears.color() understands.
113 function drawable
:set_bg(c
)
114 local c
= c
or "#000000"
115 if type(c
) == "string" or type(c
) == "table" then
118 self
.background_color
= c
122 --- Set the foreground of the drawable
123 -- @param drawable The drawable to use
124 -- @param c The foreground to use. This must either be a cairo pattern object,
125 -- nil or a string that gears.color() understands.
126 function drawable
:set_fg(c
)
127 local c
= c
or "#FFFFFF"
128 if type(c
) == "string" or type(c
) == "table" then
131 self
.foreground_color
= c
135 local function emit_difference(name
, list
, skip
)
136 local function in_table(table, val
)
137 for k
, v
in pairs(table) do
145 for k
, v
in pairs(list
) do
146 if not in_table(skip
, v
) then
152 local function handle_leave(_drawable
)
153 emit_difference("mouse::leave", _drawable
._widgets_under_mouse
, {})
154 _drawable
._widgets_under_mouse
= {}
157 local function handle_motion(_drawable
, x
, y
)
158 if x
< 0 or y
< 0 or x
> _drawable
.drawable
:geometry().width
or y
> _drawable
.drawable
:geometry().height
then
159 return handle_leave(_drawable
)
162 -- Build a plain list of all widgets on that point
163 local widgets_list
= _drawable
:find_widgets(x
, y
)
165 for k
, v
in pairs(widgets_list
) do
166 widgets
[#widgets
+ 1] = v
.widget
169 -- First, "leave" all widgets that were left
170 emit_difference("mouse::leave", _drawable
._widgets_under_mouse
, widgets
)
171 -- Then enter some widgets
172 emit_difference("mouse::enter", widgets
, _drawable
._widgets_under_mouse
)
174 _drawable
._widgets_under_mouse
= widgets
177 function drawable
.new(d
, widget_arg
)
180 ret
.widget_arg
= widget_arg
or ret
182 for k
, v
in pairs(drawable
) do
183 if type(v
) == "function" then
188 -- Only redraw a drawable once, even when we get told to do so multiple times.
189 ret
._redraw_pending
= false
190 ret
._do_redraw
= function()
191 ret
._redraw_pending
= false
192 capi
.awesome
.disconnect_signal("refresh", ret
._do_redraw
)
196 -- Connect our signal when we need a redraw
197 ret
.draw
= function()
198 if not ret
._redraw_pending
then
199 capi
.awesome
.connect_signal("refresh", ret
._do_redraw
)
200 ret
._redraw_pending
= true
203 capi
.awesome
.connect_signal("wallpaper_changed", ret
.draw
)
204 d
:connect_signal("property::surface", ret
.draw
)
206 -- Set the default background
207 ret
:set_bg(beautiful
.bg_normal
)
208 ret
:set_fg(beautiful
.fg_normal
)
210 -- Initialize internals
211 ret
._widget_geometries
= {}
212 ret
._widgets_under_mouse
= {}
214 local function button_signal(name
)
215 d
:connect_signal(name
, function(d
, x
, y
, ...)
216 local widgets
= ret
:find_widgets(x
, y
)
217 for k
, v
in pairs(widgets
) do
218 -- Calculate x/y inside of the widget
221 v
.widget
:emit_signal(name
, lx
, ly
, ...)
225 button_signal("button::press")
226 button_signal("button::release")
228 d
:connect_signal("mouse::move", function(_
, x
, y
) handle_motion(ret
, x
, y
) end)
229 d
:connect_signal("mouse::leave", function() handle_leave(ret
) end)
231 -- Make sure the drawable is drawn at least once
237 --- Handling of drawables. A drawable is something that can be drawn to.
241 return setmetatable(drawable
, { __call
= function(_
, ...) return drawable
.new(...) end })
243 -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80