Merge pull request #486 from Elv13/new_widget_syntax
widget: Add a new container declaration syntax
This commit is contained in:
commit
cd584fda55
|
@ -34,6 +34,11 @@ set(AWE_DOC_FILES
|
|||
${AWE_DOC_DIR}/02-contributing.md
|
||||
${SOURCE_DIR}/LICENSE)
|
||||
|
||||
set(AWE_DOC_IMAGES
|
||||
${AWE_DOC_DIR}/images/widgetlayout1.png
|
||||
${AWE_DOC_DIR}/images/widgetlayout2.png
|
||||
)
|
||||
|
||||
set(AWE_SRCS
|
||||
${BUILD_DIR}/awesome.c
|
||||
${BUILD_DIR}/banning.c
|
||||
|
@ -259,6 +264,9 @@ if(GENERATE_DOC)
|
|||
file(GLOB_RECURSE AWE_LUA_FILES ${BUILD_DIR}/lib/*.lua)
|
||||
file(GLOB_RECURSE AWE_MD_FILES ${AWE_DOC_DIR}/*.md)
|
||||
|
||||
# Copy the images to the build directory
|
||||
file(COPY ${SOURCE_DIR}/docs/images DESTINATION ${AWE_DOC_DIR})
|
||||
|
||||
# Run ldoc and make it fail if any warnings are generated. The
|
||||
# redirection-magic swaps stdout and stderr and awk exits with a non-zero
|
||||
# status if it sees at least one line of input.
|
||||
|
@ -341,7 +349,8 @@ install(DIRECTORY ${SOURCE_DIR}/themes DESTINATION ${AWESOME_DATA_PATH}
|
|||
PATTERN "*.lua" EXCLUDE)
|
||||
install(DIRECTORY ${BUILD_DIR}/themes DESTINATION ${AWESOME_DATA_PATH}
|
||||
PATTERN "*.lua")
|
||||
install(FILES ${AWE_DOC_FILES} DESTINATION ${AWESOME_DOC_PATH})
|
||||
install(FILES ${AWE_DOC_FILES} DESTINATION ${AWESOME_DOC_PATH})
|
||||
install(FILES ${AWE_DOC_IMAGES} DESTINATION ${AWESOME_DOC_PATH}/images)
|
||||
install(FILES "awesome.desktop" DESTINATION ${AWESOME_XSESSION_PATH})
|
||||
if(GENERATE_DOC)
|
||||
install(DIRECTORY ${BUILD_DIR}/doc DESTINATION ${AWESOME_DOC_PATH})
|
||||
|
|
112
awesomerc.lua
112
awesomerc.lua
|
@ -195,27 +195,24 @@ for s = 1, screen.count() do
|
|||
-- Create the wibox
|
||||
mywibox[s] = awful.wibox({ position = "top", screen = s })
|
||||
|
||||
-- Widgets that are aligned to the left
|
||||
local left_layout = wibox.layout.fixed.horizontal()
|
||||
left_layout:add(mylauncher)
|
||||
left_layout:add(mytaglist[s])
|
||||
left_layout:add(mypromptbox[s])
|
||||
|
||||
-- Widgets that are aligned to the right
|
||||
local right_layout = wibox.layout.fixed.horizontal()
|
||||
right_layout:add(mykeyboardlayout)
|
||||
|
||||
if s == 1 then right_layout:add(wibox.widget.systray()) end
|
||||
right_layout:add(mytextclock)
|
||||
right_layout:add(mylayoutbox[s])
|
||||
|
||||
-- Now bring it all together (with the tasklist in the middle)
|
||||
local layout = wibox.layout.align.horizontal()
|
||||
layout:set_left(left_layout)
|
||||
layout:set_middle(mytasklist[s])
|
||||
layout:set_right(right_layout)
|
||||
|
||||
mywibox[s]:set_widget(layout)
|
||||
-- Add widgets to the wibox
|
||||
mywibox[s]:setup {
|
||||
layout = wibox.layout.align.horizontal,
|
||||
{ -- Left widgets
|
||||
layout = wibox.layout.fixed.horizontal,
|
||||
mylauncher,
|
||||
mytaglist[s],
|
||||
mypromptbox[s],
|
||||
},
|
||||
mytasklist[s], -- Middle widget
|
||||
{ -- Right widgets
|
||||
layout = wibox.layout.fixed.horizontal,
|
||||
mykeyboardlayout,
|
||||
s == 1 and wibox.widget.systray(),
|
||||
mytextclock,
|
||||
mylayoutbox[s],
|
||||
},
|
||||
}
|
||||
end
|
||||
-- }}}
|
||||
|
||||
|
@ -482,45 +479,42 @@ client.connect_signal("manage", function (c)
|
|||
if titlebars_enabled and (c.type == "normal" or c.type == "dialog") then
|
||||
-- buttons for the titlebar
|
||||
local buttons = awful.util.table.join(
|
||||
awful.button({ }, 1, function()
|
||||
client.focus = c
|
||||
c:raise()
|
||||
awful.mouse.client.move(c)
|
||||
end),
|
||||
awful.button({ }, 3, function()
|
||||
client.focus = c
|
||||
c:raise()
|
||||
awful.mouse.client.resize(c)
|
||||
end)
|
||||
)
|
||||
awful.button({ }, 1, function()
|
||||
client.focus = c
|
||||
c:raise()
|
||||
awful.mouse.client.move(c)
|
||||
end),
|
||||
awful.button({ }, 3, function()
|
||||
client.focus = c
|
||||
c:raise()
|
||||
awful.mouse.client.resize(c)
|
||||
end)
|
||||
)
|
||||
|
||||
-- Widgets that are aligned to the left
|
||||
local left_layout = wibox.layout.fixed.horizontal()
|
||||
left_layout:add(awful.titlebar.widget.iconwidget(c))
|
||||
left_layout:buttons(buttons)
|
||||
|
||||
-- Widgets that are aligned to the right
|
||||
local right_layout = wibox.layout.fixed.horizontal()
|
||||
right_layout:add(awful.titlebar.widget.floatingbutton(c))
|
||||
right_layout:add(awful.titlebar.widget.maximizedbutton(c))
|
||||
right_layout:add(awful.titlebar.widget.stickybutton(c))
|
||||
right_layout:add(awful.titlebar.widget.ontopbutton(c))
|
||||
right_layout:add(awful.titlebar.widget.closebutton(c))
|
||||
|
||||
-- The title goes in the middle
|
||||
local middle_layout = wibox.layout.flex.horizontal()
|
||||
local title = awful.titlebar.widget.titlewidget(c)
|
||||
title:set_align("center")
|
||||
middle_layout:add(title)
|
||||
middle_layout:buttons(buttons)
|
||||
|
||||
-- Now bring it all together
|
||||
local layout = wibox.layout.align.horizontal()
|
||||
layout:set_left(left_layout)
|
||||
layout:set_right(right_layout)
|
||||
layout:set_middle(middle_layout)
|
||||
|
||||
awful.titlebar(c):set_widget(layout)
|
||||
awful.titlebar(c) : setup {
|
||||
{ -- Left
|
||||
awful.titlebar.widget.iconwidget(c),
|
||||
buttons = buttons,
|
||||
layout = wibox.layout.fixed.horizontal
|
||||
},
|
||||
{ -- Middle
|
||||
{ -- Title
|
||||
align = "center",
|
||||
widget = awful.titlebar.widget.titlewidget(c)
|
||||
},
|
||||
buttons = buttons,
|
||||
layout = wibox.layout.flex.horizontal
|
||||
},
|
||||
{ -- Right
|
||||
awful.titlebar.widget.floatingbutton (c),
|
||||
awful.titlebar.widget.maximizedbutton(c),
|
||||
awful.titlebar.widget.stickybutton (c),
|
||||
awful.titlebar.widget.ontopbutton (c),
|
||||
awful.titlebar.widget.closebutton (c),
|
||||
layout = wibox.layout.fixed.horizontal()
|
||||
},
|
||||
layout = wibox.layout.align.horizontal
|
||||
}
|
||||
end
|
||||
end)
|
||||
|
||||
|
|
|
@ -0,0 +1,263 @@
|
|||
# The declarative layout system
|
||||
|
||||
This system provide an alternative to the system used in Awesome 3.5 and is
|
||||
inspired by the one once used by Awesome 3.2-3.4 and Qt QML system.
|
||||
|
||||
## A simple layout
|
||||
|
||||
* Display `my_first_widget` only on screen one
|
||||
* Add a background color to `my_third_widget`
|
||||
* Dispose in a `wibox.layout.fixed.horizontal` layout
|
||||
|
||||
Code:
|
||||
|
||||
mywibox[s] : setup {
|
||||
s == 1 and my_first_widget, -- Only display on screen 1
|
||||
my_second_widget,
|
||||
{ -- Add a background color/pattern for my_third_widget
|
||||
my_third_widget,
|
||||
bg = beautiful.bg_focus,
|
||||
widget = wibox.widget.background,
|
||||
},
|
||||
layout = wibox.layout.fixed.horizontal,
|
||||
}
|
||||
|
||||
|
||||
In this example `s == 1` is an inline expression. In the default `rc.lua`,
|
||||
there is an `s` variable represent to define the current screen. Any lua
|
||||
logic expression can be used as long as it return a valid widget, or a
|
||||
declarative layout, or `nil`.
|
||||
|
||||
|
||||
## Define widgets inline and place them
|
||||
|
||||
* Create a `wibox.widget.textbox` with various properties
|
||||
* Force the textbox size using `wibox.layout.constraint`
|
||||
* Add a margin around another textbox
|
||||
* Add a `wibox.widget.background` (for visualization)
|
||||
|
||||
Code:
|
||||
|
||||
mywibox[s] : setup {
|
||||
{
|
||||
-- Force the textbox to always be 300 pixel long
|
||||
{
|
||||
{
|
||||
markup = "<b>Hello World!</b>",
|
||||
align = "center",
|
||||
widget = wibox.widget.textbox
|
||||
},
|
||||
bg = "#ff0000",
|
||||
widget = wibox.widget.background,
|
||||
},
|
||||
width = 300,
|
||||
strategy = "min",
|
||||
layout = wibox.layout.constraint
|
||||
},
|
||||
{
|
||||
-- Add a border around the background
|
||||
{
|
||||
{
|
||||
markup = "Foobar",
|
||||
widget = wibox.widget.textbox
|
||||
},
|
||||
bg = "#0000ff",
|
||||
widget = wibox.widget.background
|
||||
},
|
||||
left = 10,
|
||||
right = 10,
|
||||
top = 1,
|
||||
bottom = 2,
|
||||
layout = wibox.layout.margin
|
||||
},
|
||||
layout = wibox.layout.fixed.horizontal,
|
||||
}
|
||||
|
||||
|
||||
Result:
|
||||
![Example2 screenshot](../../docs/images/widgetlayout1.png)
|
||||
|
||||
|
||||
## Use an `wibox.layout.align` layout
|
||||
The `wibox.layout.align` is a little different. While most layouts will
|
||||
ignore any `nil` lines, the `align` layout rely on them so `left`, `middle`
|
||||
and `right` can be defined
|
||||
|
||||
Code:
|
||||
|
||||
mywibox[s] : setup {
|
||||
my_textbox1, -- Left
|
||||
nil, -- Nothing in the middle
|
||||
my_textbox2, -- Right
|
||||
layout = wibox.layout.fixed.horizontal,
|
||||
}
|
||||
|
||||
|
||||
|
||||
## Define new widgets
|
||||
|
||||
New trivial widgets can be created directly in the layout declaration. Here
|
||||
is a simple circle widget:
|
||||
|
||||
Code:
|
||||
|
||||
mywibox[s] : setup {
|
||||
fit = function(self, context, width, height)
|
||||
return height, height -- A square taking the full height
|
||||
end,
|
||||
draw = function(self, context, cr, width, height)
|
||||
cr:set_source_rgb(1, 0, 0) -- Red
|
||||
cr:arc(height/2, height/2, height/2, 0, math.pi*2)
|
||||
cr:fill()
|
||||
end,
|
||||
layout = wibox.widget.base.make_widget,
|
||||
}
|
||||
|
||||
Result:
|
||||
![Example4 screenshot](../../docs/images/widgetlayout2.png)
|
||||
|
||||
For more information about how to draw widgets, refer to the `Cairo` api:
|
||||
|
||||
* [Path](http://cairographics.org/manual/cairo-Paths.html)
|
||||
* [Context](http://cairographics.org/manual/cairo-cairo-t.html)
|
||||
* [Pattern](http://cairographics.org/manual/cairo-cairo-pattern-t.html)
|
||||
* [transformation](http://cairographics.org/manual/cairo-Transformations.html)
|
||||
* [Operator](http://cairographics.org/operators/)
|
||||
* [Pango text](https://developer.gnome.org/pango/stable/)
|
||||
|
||||
|
||||
## Externally defined widgets and layouts
|
||||
|
||||
This is useful when the widget is provided by an external module or when it
|
||||
requires complex manipulations which would make the declaration unreadable.
|
||||
|
||||
Code:
|
||||
|
||||
local tb = wibox.widget.textbox()
|
||||
tb:set_markup("Hello world! ")
|
||||
|
||||
-- Repeat "tb" 3 times
|
||||
mywibox[s] : setup {
|
||||
tb,
|
||||
tb,
|
||||
tb,
|
||||
layout = wibox.layout.fixed.horizontal,
|
||||
}
|
||||
|
||||
|
||||
|
||||
## Accessing widgets
|
||||
|
||||
For each widget or container, it is possible to add an `identifier` attribute
|
||||
so the widget can be accessed later.
|
||||
|
||||
Widgets defined using `setup` can be access by 3 means:
|
||||
|
||||
* Avoid the issue by using externally created widgets
|
||||
* Use `my_wibox.my_first_widget.my_second_widget` style access
|
||||
* Use JavaScript like `my_wibox:get_children_by_id("my_second_widget")[1]`
|
||||
|
||||
The first method mixes the imperative and declarative syntax, but makes the code
|
||||
less readable. The second is a little verbose and only works if every node in
|
||||
the chain have a valid identifier. The last one doesn't require long paths,
|
||||
but it is not easy to get a specific instance if multiple widgets have the
|
||||
same identifier.
|
||||
|
||||
WARNING: The widget identifier must not use reseved name. This include all
|
||||
method names, existing widget attributes, `layout` and `widget`. Names should
|
||||
also respect the lua variable name policies (case sensitive, alphanumeric and
|
||||
underscore characters and non-numeric first character)
|
||||
|
||||
Code:
|
||||
|
||||
mywibox[s] : setup {
|
||||
{
|
||||
id = "second",
|
||||
widget = wibox.widget.textbox
|
||||
},
|
||||
{
|
||||
id = "third",
|
||||
widget = wibox.widget.textbox
|
||||
},
|
||||
id = "first",
|
||||
layout = wibox.layout.fixed.horizontal,
|
||||
}
|
||||
|
||||
mywibox[s].first.second:set_markup("changed!")
|
||||
mywibox[s]:get_children_by_id("third")[1]:set_markup("Also changed!")
|
||||
|
||||
|
||||
|
||||
## Extending the system
|
||||
|
||||
This system is very flexible. Each section attribute (the entries with string
|
||||
keys) is directly linked to the layout or widget API. When setting the
|
||||
imaginary `myproperty`, it will first check if `set_myproperty` exist. If it
|
||||
doesn't, it will check if there is a `myproperty` method. Finally, it will
|
||||
just set the `mywidget.myproperty` directly in case it is used later or
|
||||
catched by a lua `metatable` (operator overload).
|
||||
|
||||
Code:
|
||||
|
||||
-- "Monkeypatch" a new function to wibox.widget.textbox to add vicious
|
||||
-- extension support
|
||||
function wibox.widget.textbox:vicious(args)
|
||||
local f = unpack or table.unpack -- Lua 5.1 compat
|
||||
vicious.register(w, f(args))
|
||||
end
|
||||
|
||||
mywibox[s] : setup {
|
||||
{
|
||||
vicious = {vicious.widgets.cpu, "CPU: $1%", 3},
|
||||
widget = wibox.widget.textbox
|
||||
},
|
||||
layout = wibox.layout.fixed.horizontal,
|
||||
}
|
||||
|
||||
|
||||
In this example, the system is extended to that the popular
|
||||
[Vicious](http://awesome.naquadah.org/wiki/Vicious) extension module can be
|
||||
used directly in the layout declaration. This example will update the textbox
|
||||
every 3 seconds to show the CPU usage.
|
||||
|
||||
|
||||
## Handling sections
|
||||
|
||||
The system allows sections to be defined externally, then composed into
|
||||
the final layout declaration. Here is an example re-using one of the above
|
||||
example:
|
||||
|
||||
Code:
|
||||
|
||||
local circle = {
|
||||
fit = function(self, context, width, height)
|
||||
return height, height -- A square taking the full height
|
||||
end,
|
||||
draw = function(self, context, cr, width, height)
|
||||
cr:set_source_rgb(1, 0, 0) -- Red
|
||||
cr:arc(height/2, height/2, height/2, 0, math.pi*2)
|
||||
cr:fill()
|
||||
end,
|
||||
layout = wibox.widget.base.make_widget,
|
||||
}
|
||||
|
||||
-- Define a layout with the imperative syntax
|
||||
local l = wibox.widget.align()
|
||||
|
||||
-- 3 circle
|
||||
mywibox[s] : setup {
|
||||
circle,
|
||||
circle,
|
||||
circle,
|
||||
l,
|
||||
layout = wibox.layout.align.horizontal
|
||||
}
|
||||
|
||||
-- This can be done instead
|
||||
local three_circle = {layout = wibox.layout.align.horizontal}
|
||||
for i=1, 3 do
|
||||
table.insert(three_circle, circle)
|
||||
end
|
||||
|
||||
mywibox[s] : setup (three_circle)
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
# Creating new widget
|
||||
|
||||
All widgets have to be generated via this function so that
|
||||
the needed signals are added and mouse input handling is set up.
|
||||
|
||||
The returned widget will have a `:buttons` member function that can be used to
|
||||
register a set of mouse button events with the widget.
|
||||
|
||||
To implement your own widget, you can implement some member functions on a
|
||||
freshly-created widget. Note that all of these functions should be deterministic
|
||||
in the sense that they will show the same behavior if they are repeatedly called
|
||||
with the same arguments (same width and height). If your widget is updated and
|
||||
needs to change, suitable signals have to be emitted. This will be explained
|
||||
later.
|
||||
|
||||
The first callback is `:fit`. This function is called to select the size of your
|
||||
widget. The arguments to this function is the available space and it should
|
||||
return its desired size. Note that this function only provides a hint which is
|
||||
not necessarily followed. The widget must also be able to draw itself at
|
||||
different sizes than the one requested.
|
||||
|
||||
function widget:fit(context, width, height)
|
||||
-- Find the maximum square available
|
||||
local m = math.min(width, height)
|
||||
return m, m
|
||||
end
|
||||
|
||||
The next callback is `:draw`. As the name suggests, this function is called to
|
||||
draw the widget. The arguments to this widget are the context that the widget is
|
||||
drawn in, the cairo context on which it should be drawn and the widget's size.
|
||||
The cairo context is set up in such a way that the widget as its top-left corner
|
||||
at (0, 0) and its bottom-right corner at (width, height). In other words, no
|
||||
special transformation needs to be done. Note that during this callback a
|
||||
suitable clip will already be applied to the cairo context so that this callback
|
||||
will not be able to draw outside of the area that was registered for the widget
|
||||
by the layout that placed this widget. You should not call `cr:reset_clip()`, as
|
||||
redraws will not be handled correctly in this case.
|
||||
|
||||
function widget:draw(context, cr, width, height)
|
||||
cr:move_to(0, 0)
|
||||
cr:line_to(width, height)
|
||||
cr:move_to(0, height)
|
||||
cr:line_to(width, 0)
|
||||
cr:stroke()
|
||||
end
|
||||
|
||||
There are two signals configured for a widget. When the result that `:fit` would
|
||||
return changes, the `widget::layout_changed` signal has to be emitted. If this
|
||||
actually causes layout changes, the affected areas will be redrawn. The other
|
||||
signal is `widget::redraw_needed`. This signal signals that `:draw` has to be
|
||||
called to redraw the widget, but it is safe to assume that `:fit` does still
|
||||
return the same values as before. If in doubt, you can emit both signals to be
|
||||
safe.
|
||||
|
||||
If your widget only needs to draw something to the screen, the above is all that
|
||||
is needed. The following callbacks can be used when implementing layouts which
|
||||
place other widgets on the screen.
|
||||
|
||||
The `:layout` callback is used to figure out which other widgets should be drawn
|
||||
relative to this widget. Note that it is allowed to place widgets outside of the
|
||||
extents of your own widget, for example at a negative position or at twice the
|
||||
size of this widget. Use this mechanism if your widget needs to draw outside of
|
||||
its own extents. If the result of this callback changes,
|
||||
`widget::layout_changed` has to be emitted. You can use `:fit_widget` to call
|
||||
the `:fit` callback of other widgets. Never call `:fit` directly! For example,
|
||||
if you want to place another widget `child` inside of your widget, you can do it
|
||||
like this:
|
||||
|
||||
-- For readability
|
||||
local base = wibox.widget.base
|
||||
function widget:layout(width, height)
|
||||
local result = {}
|
||||
table.insert(result, base.place_widget_at(child, width/2, 0, width/2, height)
|
||||
return result
|
||||
end
|
||||
|
||||
Finally, if you want to influence how children are drawn, there are four
|
||||
callbacks available that all get similar arguments:
|
||||
|
||||
function widget:before_draw_children(context, cr, width, height)
|
||||
function widget:after_draw_children(context, cr, width, height)
|
||||
function widget:before_draw_child(context, index, child, cr, width, height)
|
||||
function widget:after_draw_child(context, index, child, cr, width, height)
|
||||
|
||||
All of these are called with the same arguments as the `:draw()` method. Please
|
||||
note that a larger clip will be active during these callbacks that also contains
|
||||
the area of all children. These callbacks can be used to influence the way in
|
||||
which children are drawn, but they should not cause the drawing to cover a
|
||||
different area. As an example, these functions can be used to draw children
|
||||
translucently:
|
||||
|
||||
function widget:before_draw_children(context, cr, width, height)
|
||||
cr:push_group()
|
||||
end
|
||||
function widget:after_draw_children(context, cr, width, height)
|
||||
cr:pop_group_to_source()
|
||||
cr:paint_with_alpha(0.5)
|
||||
end
|
||||
|
||||
In pseudo-code, the call sequence for the drawing callbacks during a redraw
|
||||
looks like this:
|
||||
|
||||
widget:draw(context, cr, width, height)
|
||||
widget:before_draw_children(context, cr, width, height)
|
||||
for child do
|
||||
widget:before_draw_child(context, cr, child_index, child, width, height)
|
||||
cr:save()
|
||||
-- Draw child and all of its children recursively, taking into account the
|
||||
-- position and size given to base.place_widget_at() in :layout().
|
||||
cr:restore()
|
||||
widget:after_draw_child(context, cr, child_index, child, width, height)
|
||||
end
|
||||
widget:after_draw_children(context, cr, width, height)
|
|
@ -19,6 +19,8 @@ topics={
|
|||
'00-authors.md',
|
||||
'01-readme.md',
|
||||
'02-contributing.md',
|
||||
'03-declarative-layout.md',
|
||||
'04-new-widgets.md',
|
||||
}
|
||||
|
||||
-- Setup @client to be an alias for "@tparam client.object"
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
Binary file not shown.
After Width: | Height: | Size: 587 B |
|
@ -26,6 +26,12 @@ local titlebar = {
|
|||
widget = {}
|
||||
}
|
||||
|
||||
--- Set a declarative widget hierarchy description.
|
||||
-- See [The declarative layout system](../documentation/03-declarative-layout.md.html)
|
||||
-- @param args An array containing the widgets disposition
|
||||
-- @name setup
|
||||
-- @class function
|
||||
|
||||
--- Show tooltips when hover on titlebar buttons (defaults to 'true')
|
||||
titlebar.enable_tooltip = true
|
||||
|
||||
|
@ -108,6 +114,9 @@ local function new(c, args)
|
|||
-- Make sure the titlebar has the right colors applied
|
||||
bars[position].update_colors()
|
||||
|
||||
-- Handle declarative/recursive widget container
|
||||
ret.setup = base.widget.setup
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ local sort = require("gears.sort")
|
|||
local beautiful = require("beautiful")
|
||||
local surface = require("gears.surface")
|
||||
local cairo = require("lgi").cairo
|
||||
local base = require("wibox.widget.base")
|
||||
|
||||
--- This provides widget box windows. Every wibox can also be used as if it were
|
||||
-- a drawin. All drawin functions and properties are also available on wiboxes!
|
||||
|
@ -36,6 +37,13 @@ function wibox:set_widget(widget)
|
|||
self._drawable:set_widget(widget)
|
||||
end
|
||||
|
||||
--- Set a declarative widget hierarchy description.
|
||||
-- See [The declarative layout system](../documentation/03-declarative-layout.md.html)
|
||||
-- @param args An array containing the widgets disposition
|
||||
-- @name setup
|
||||
-- @class function
|
||||
wibox.setup = base.widget.setup
|
||||
|
||||
--- Set the background of the wibox
|
||||
-- @param c The background to use. This must either be a cairo pattern object,
|
||||
-- nil or a string that gears.color() understands.
|
||||
|
|
|
@ -171,6 +171,17 @@ function align:get_children()
|
|||
return util.from_sparse {self.first, self.second, self.third}
|
||||
end
|
||||
|
||||
--- Replace the layout children
|
||||
-- This layout only accept three children, all others will be ignored
|
||||
-- @tparam table children A table composed of valid widgets
|
||||
function align:set_children(children)
|
||||
if not children then return self:reset() end
|
||||
self.first = children[1]
|
||||
self.second = children[2]
|
||||
self.third = children[3]
|
||||
self:emit_signal("widget::layout_changed")
|
||||
end
|
||||
|
||||
--- Fit the align layout into the given space. The align layout will
|
||||
-- ask for the sum of the sizes of its sub-widgets in its direction
|
||||
-- and the largest sized sub widget in the other direction.
|
||||
|
@ -243,6 +254,9 @@ local function get_layout(dir, first, second, third)
|
|||
ret:set_second(second)
|
||||
ret:set_third(third)
|
||||
|
||||
-- An align layout allow set_children to have empty entries
|
||||
ret.allow_empty_widget = true
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
|
|
|
@ -50,6 +50,14 @@ function constraint:get_children()
|
|||
return {self.widget}
|
||||
end
|
||||
|
||||
--- Replace the layout children
|
||||
-- This layout only accept one children, all others will be ignored
|
||||
-- @tparam table children A table composed of valid widgets
|
||||
function constraint:set_children(children)
|
||||
self.widget = children and children[1]
|
||||
self:emit_signal("widget::layout_changed")
|
||||
end
|
||||
|
||||
--- Set the strategy to use for the constraining. Valid values are 'max',
|
||||
-- 'min' or 'exact'. Throws an error on invalid values.
|
||||
function constraint:set_strategy(val)
|
||||
|
|
|
@ -110,6 +110,14 @@ function fixed:get_children()
|
|||
return self.widgets
|
||||
end
|
||||
|
||||
--- Replace the layout children
|
||||
-- @tparam table children A table composed of valid widgets
|
||||
function fixed:set_children(children)
|
||||
if not children then return self:reset() end
|
||||
self.widgets = children
|
||||
self:emit_signal("widget::layout_changed")
|
||||
end
|
||||
|
||||
--- Replace the first instance of `widget` in the layout with `widget2`
|
||||
-- @param widget The widget to replace
|
||||
-- @param widget2 The widget to replace `widget` with
|
||||
|
|
|
@ -31,6 +31,11 @@ local flex = {}
|
|||
-- @name get_children
|
||||
-- @class function
|
||||
|
||||
--- Replace the layout children
|
||||
-- @tparam table children A table composed of valid widgets
|
||||
-- @name set_children
|
||||
-- @class function
|
||||
|
||||
--- Add some widgets to the given fixed layout
|
||||
-- @param layout The layout you are modifying.
|
||||
-- @tparam widget ... Widgets that should be added (must at least be one)
|
||||
|
|
|
@ -78,6 +78,14 @@ function margin:get_children()
|
|||
return {self.widget}
|
||||
end
|
||||
|
||||
--- Replace the layout children
|
||||
-- This layout only accept one children, all others will be ignored
|
||||
-- @tparam table children A table composed of valid widgets
|
||||
function margin:set_children(children)
|
||||
self.widget = children and children[1]
|
||||
self:emit_signal("widget::layout_changed")
|
||||
end
|
||||
|
||||
--- Set all the margins to val.
|
||||
function margin:set_margins(val)
|
||||
self.left = val
|
||||
|
|
|
@ -64,6 +64,14 @@ function mirror:get_children()
|
|||
return {self.widget}
|
||||
end
|
||||
|
||||
--- Replace the layout children
|
||||
-- This layout only accept one children, all others will be ignored
|
||||
-- @tparam table children A table composed of valid widgets
|
||||
function mirror:set_children(children)
|
||||
self.widget = children and children[1]
|
||||
self:emit_signal("widget::layout_changed")
|
||||
end
|
||||
|
||||
--- Reset this layout. The widget will be removed and the axes reset.
|
||||
function mirror:reset()
|
||||
self.horizontal = false
|
||||
|
|
|
@ -72,6 +72,14 @@ function rotate:get_children()
|
|||
return {self.widget}
|
||||
end
|
||||
|
||||
--- Replace the layout children
|
||||
-- This layout only accept one children, all others will be ignored
|
||||
-- @tparam table children A table composed of valid widgets
|
||||
function rotate:set_children(children)
|
||||
self.widget = children and children[1]
|
||||
self:emit_signal("widget::layout_changed")
|
||||
end
|
||||
|
||||
--- Reset this layout. The widget will be removed and the rotation reset.
|
||||
function rotate:reset()
|
||||
self.direction = nil
|
||||
|
|
|
@ -267,6 +267,14 @@ function scroll:get_children()
|
|||
return {self.widget}
|
||||
end
|
||||
|
||||
--- Replace the layout children
|
||||
-- This layout only accept one children, all others will be ignored
|
||||
-- @tparam table children A table composed of valid widgets
|
||||
function scroll:set_children(children)
|
||||
self.widget = children and children[1]
|
||||
self:emit_signal("widget::layout_changed")
|
||||
end
|
||||
|
||||
--- Specify the expand mode that is used for extra space.
|
||||
-- @tparam boolean expand If true, the widget is expanded to include the extra
|
||||
-- space. If false, the extra space is simply left empty.
|
||||
|
|
|
@ -101,6 +101,14 @@ function background:get_children()
|
|||
return {self.widget}
|
||||
end
|
||||
|
||||
--- Replace the layout children
|
||||
-- This layout only accept one children, all others will be ignored
|
||||
-- @tparam table children A table composed of valid widgets
|
||||
function background:set_children(children)
|
||||
self.widget = children and children[1]
|
||||
self:emit_signal("widget::layout_changed")
|
||||
end
|
||||
|
||||
--- Set the background to use
|
||||
function background:set_bg(bg)
|
||||
if bg then
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
local object = require("gears.object")
|
||||
local cache = require("gears.cache")
|
||||
local matrix = require("gears.matrix")
|
||||
local util = require("awful.util")
|
||||
local setmetatable = setmetatable
|
||||
local pairs = pairs
|
||||
local type = type
|
||||
|
@ -80,6 +81,14 @@ function base.widget:get_children()
|
|||
return {}
|
||||
end
|
||||
|
||||
--- Replace the layout children
|
||||
-- The default implementation does nothing, this must be re-implemented by
|
||||
-- all layout and container widgets.
|
||||
-- @tparam table children A table composed of valid widgets
|
||||
function base.widget:set_children(children)
|
||||
-- Nothing on purpose
|
||||
end
|
||||
|
||||
-- It could have been merged into `get_all_children`, but it's not necessary
|
||||
local function digg_children(ret, tlw)
|
||||
for k, w in ipairs(tlw:get_children()) do
|
||||
|
@ -321,126 +330,121 @@ function base.place_widget_at(widget, x, y, width, height)
|
|||
return base.place_widget_via_matrix(widget, matrix.create_translate(x, y), width, height)
|
||||
end
|
||||
|
||||
--[[--
|
||||
Create a new widget. All widgets have to be generated via this function so that
|
||||
the needed signals are added and mouse input handling is set up.
|
||||
-- Read the table, separate attributes from widgets
|
||||
local function parse_table(t, leave_empty)
|
||||
local keys= {}
|
||||
local max = 0
|
||||
local attributes, widgets = {}, {}
|
||||
for k,v in pairs(t) do
|
||||
if type(k) == "number" then
|
||||
-- As `ipairs` doesn't always work on sparse tables, update the
|
||||
-- maximum
|
||||
if k > max then
|
||||
max = k
|
||||
end
|
||||
|
||||
The returned widget will have a :buttons member function that can be used to
|
||||
register a set of mouse button events with the widget.
|
||||
|
||||
To implement your own widget, you can implement some member functions on a
|
||||
freshly-created widget. Note that all of these functions should be deterministic
|
||||
in the sense that they will show the same behavior if they are repeatedly called
|
||||
with the same arguments (same width and height). If your widget is updated and
|
||||
needs to change, suitable signals have to be emitted. This will be explained
|
||||
later.
|
||||
|
||||
The first callback is :fit. This function is called to select the size of your
|
||||
widget. The arguments to this function is the available space and it should
|
||||
return its desired size. Note that this function only provides a hint which is
|
||||
not necessarily followed. The widget must also be able to draw itself at
|
||||
different sizes than the one requested.
|
||||
|
||||
function widget:fit(context, width, height)
|
||||
-- Find the maximum square available
|
||||
local m = math.min(width, height)
|
||||
return m, m
|
||||
widgets[k] = v
|
||||
else
|
||||
attributes[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
The next callback is :draw. As the name suggests, this function is called to
|
||||
draw the widget. The arguments to this widget are the context that the widget is
|
||||
drawn in, the cairo context on which it should be drawn and the widget's size.
|
||||
The cairo context is set up in such a way that the widget as its top-left corner
|
||||
at (0, 0) and its bottom-right corner at (width, height). In other words, no
|
||||
special transformation needs to be done. Note that during this callback a
|
||||
suitable clip will already be applied to the cairo context so that this callback
|
||||
will not be able to draw outside of the area that was registered for the widget
|
||||
by the layout that placed this widget. You should not call `cr:reset_clip()`, as
|
||||
redraws will not be handled correctly in this case.
|
||||
|
||||
function widget:draw(context, cr, width, height)
|
||||
cr:move_to(0, 0)
|
||||
cr:line_to(width, height)
|
||||
cr:move_to(0, height)
|
||||
cr:line_to(width, 0)
|
||||
cr:stroke()
|
||||
-- Pack the sparse table if the container doesn't support sparse tables
|
||||
if not leave_empty then
|
||||
widgets = util.table.from_sparse(widgets)
|
||||
max = #widgets
|
||||
end
|
||||
|
||||
There are two signals configured for a widget. When the result that :fit would
|
||||
return changes, the `widget::layout_changed` signal has to be emitted. If this
|
||||
actually causes layout changes, the affected areas will be redrawn. The other
|
||||
signal is `widget::redraw_needed`. This signal signals that :draw has to be
|
||||
called to redraw the widget, but it is safe to assume that :fit does still
|
||||
return the same values as before. If in doubt, you can emit both signals to be
|
||||
safe.
|
||||
return max, attributes, widgets
|
||||
end
|
||||
|
||||
If your widget only needs to draw something to the screen, the above is all that
|
||||
is needed. The following callbacks can be used when implementing layouts which
|
||||
place other widgets on the screen.
|
||||
-- Recursively build a container from a declarative table
|
||||
local function drill(ids, content)
|
||||
if not content then return end
|
||||
|
||||
The :layout callback is used to figure out which other widgets should be drawn
|
||||
relative to this widget. Note that it is allowed to place widgets outside of the
|
||||
extents of your own widget, for example at a negative position or at twice the
|
||||
size of this widget. Use this mechanism if your widget needs to draw outside of
|
||||
its own extents. If the result of this callback changes,
|
||||
`widget::layout_changed` has to be emitted. You can use @{fit_widget} to call
|
||||
the `:fit` callback of other widgets. Never call `:fit` directly! For example,
|
||||
if you want to place another widget `child` inside of your widget, you can do it
|
||||
like this:
|
||||
-- Alias `widget` to `layout` as they are handled the same way
|
||||
content.layout = content.layout or content.widget
|
||||
|
||||
-- For readability
|
||||
local base = wibox.widget.base
|
||||
function widget:layout(width, height)
|
||||
local result = {}
|
||||
table.insert(result, base.place_widget_at(child, width/2, 0, width/2, height)
|
||||
return result
|
||||
-- Make sure the layout is not indexed on a function
|
||||
local layout = type(content.layout) == "function" and content.layout() or content.layout
|
||||
|
||||
-- Create layouts based on metatable __call
|
||||
local l = layout.is_widget and layout or layout()
|
||||
|
||||
-- Get the number of children widgets (including nil widgets)
|
||||
local max, attributes, widgets = parse_table(content, l.allow_empty_widget)
|
||||
|
||||
-- Get the optional identifier to create a virtual widget tree to place
|
||||
-- in an "access table" to be able to retrieve the widget
|
||||
local id = attributes.id
|
||||
|
||||
-- Clear the internal attributes
|
||||
attributes.id, attributes.layout, attributes.widget = nil, nil, nil
|
||||
|
||||
for k = 1, max do
|
||||
-- ipairs cannot be used on sparse tables
|
||||
local v, id2, e = widgets[k], id, nil
|
||||
if v then
|
||||
-- It is another declarative container, parse it
|
||||
if not v.is_widget then
|
||||
e, id2 = drill(ids, v)
|
||||
widgets[k] = e
|
||||
end
|
||||
|
||||
-- Place the widget in the access table
|
||||
if id2 then
|
||||
l [id2] = e
|
||||
ids[id2] = ids[id2] or {}
|
||||
table.insert(ids[id2], e)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Finally, if you want to influence how children are drawn, there are four
|
||||
callbacks available that all get similar arguments:
|
||||
-- Replace all children (if any) with the new ones
|
||||
l:set_children(widgets)
|
||||
|
||||
function widget:before_draw_children(context, cr, width, height)
|
||||
function widget:after_draw_children(context, cr, width, height)
|
||||
function widget:before_draw_child(context, index, child, cr, width, height)
|
||||
function widget:after_draw_child(context, index, child, cr, width, height)
|
||||
|
||||
All of these are called with the same arguments as the :draw() method. Please
|
||||
note that a larger clip will be active during these callbacks that also contains
|
||||
the area of all children. These callbacks can be used to influence the way in
|
||||
which children are drawn, but they should not cause the drawing to cover a
|
||||
different area. As an example, these functions can be used to draw children
|
||||
translucently:
|
||||
|
||||
function widget:before_draw_children(context, cr, width, height)
|
||||
cr:push_group()
|
||||
end
|
||||
function widget:after_draw_children(context, cr, width, height)
|
||||
cr:pop_group_to_source()
|
||||
cr:paint_with_alpha(0.5)
|
||||
-- Set layouts attributes
|
||||
for attr, val in pairs(attributes) do
|
||||
if l["set_"..attr] then
|
||||
l["set_"..attr](l, val)
|
||||
elseif type(l[attr]) == "function" then
|
||||
l[attr](l, val)
|
||||
else
|
||||
l[attr] = val
|
||||
end
|
||||
end
|
||||
|
||||
In pseudo-code, the call sequence for the drawing callbacks during a redraw
|
||||
looks like this:
|
||||
return l, id
|
||||
end
|
||||
|
||||
widget:draw(context, cr, width, height)
|
||||
widget:before_draw_children(context, cr, width, height)
|
||||
for child do
|
||||
widget:before_draw_child(context, cr, child_index, child, width, height)
|
||||
cr:save()
|
||||
-- Draw child and all of its children recursively, taking into account the
|
||||
-- position and size given to base.place_widget_at() in :layout().
|
||||
cr:restore()
|
||||
widget:after_draw_child(context, cr, child_index, child, width, height)
|
||||
-- Only available when the declarative system is used
|
||||
local function get_children_by_id(self, name)
|
||||
return self._by_id[name] or {}
|
||||
end
|
||||
|
||||
--- Set a declarative widget hierarchy description.
|
||||
-- See [The declarative layout system](../documentation/03-declarative-layout.md.html)
|
||||
-- @param args An array containing the widgets disposition
|
||||
function base.widget:setup(args)
|
||||
local f,ids = self.set_widget or self.add or self.set_first,{}
|
||||
local w, id = drill(ids, args)
|
||||
f(self,w)
|
||||
if id then
|
||||
-- Avoid being dropped by wibox metatable -> drawin
|
||||
rawset(self, id, w)
|
||||
end
|
||||
widget:after_draw_children(context, cr, width, height)
|
||||
rawset(self, "_by_id", ids)
|
||||
rawset(self, "get_children_by_id", get_children_by_id)
|
||||
end
|
||||
|
||||
@param proxy If this is set, the returned widget will be a proxy for this
|
||||
widget. It will be equivalent to this widget. This means it
|
||||
looks the same on the screen.
|
||||
@tparam[opt] string widget_name Name of the widget. If not set, it will be
|
||||
set automatically via `gears.object.modulename`.
|
||||
@see fit_widget
|
||||
--]]--
|
||||
--- Create an empty widget skeleton
|
||||
-- See [Creating new widgets](../documentation/04-new-widget.md.html)
|
||||
-- @param proxy If this is set, the returned widget will be a proxy for this
|
||||
-- widget. It will be equivalent to this widget. This means it
|
||||
-- looks the same on the screen.
|
||||
-- @tparam[opt] string widget_name Name of the widget. If not set, it will be
|
||||
-- set automatically via `gears.object.modulename`.
|
||||
-- @see fit_widget
|
||||
function base.make_widget(proxy, widget_name)
|
||||
local ret = object()
|
||||
|
||||
|
@ -470,6 +474,9 @@ function base.make_widget(proxy, widget_name)
|
|||
-- Widget is fully opaque
|
||||
ret.opacity = 1
|
||||
|
||||
-- Differentiate tables from widgets
|
||||
ret.is_widget = true
|
||||
|
||||
-- Size is not restricted/forced
|
||||
ret._forced_width = nil
|
||||
ret._forced_height = nil
|
||||
|
|
Loading…
Reference in New Issue