awesome/docs/03-declarative-layout.md

300 lines
8.4 KiB
Markdown
Raw Normal View History

2016-02-10 07:07:47 +01:00
# The declarative layout system
2016-12-13 23:31:25 +01:00
The declarative layout system provides an alternative to the imperative system.
It is inspired by the one used by Awesome 3.2-3.4 and the Qt QML style.
2016-02-10 07:07:47 +01:00
## The default widgets
### Widgets
2016-12-13 23:31:25 +01:00
Awesome provides 2 collections of widgets:
* `wibox.widget`: Generic widgets, containers and layouts
* `awful.widget`: The Awesome specific widgets
@DOC_widget_WIDGET_LIST@
### Containers
2016-12-16 22:02:35 +01:00
A container is a widget that wraps another widget. It can be used to add
2016-12-13 23:31:25 +01:00
decorations or to modify the content of the child widget.
@DOC_container_WIDGET_LIST@
### Layouts
2016-12-13 23:31:25 +01:00
Layouts are collections of children widgets. They are placed according to
configurable rules.
@DOC_layout_WIDGET_LIST@
## Placing widgets
### A simple layout
2016-02-10 07:07:47 +01:00
* 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:
s.mywibox : setup {
2016-02-10 07:07:47 +01:00
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.container.background,
2016-02-10 07:07:47 +01:00
},
layout = wibox.layout.fixed.horizontal,
}
In this example `s == 1` is an inline expression. In the default `rc.lua`,
2016-12-13 23:31:25 +01:00
there is an `s` variable represent to define the current screen. Any Lua
logic expression can be used as long as it returns a valid widget or a
2016-02-10 07:07:47 +01:00
declarative layout, or `nil`.
### Composite widgets
@DOC_wibox_widget_progressbar_encapsulation_EXAMPLE@
2016-02-10 07:07:47 +01:00
### Define widgets inline and place them
2016-02-10 07:07:47 +01:00
* 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.container.background` (for visualization)
2016-02-10 07:07:47 +01:00
Code:
s.mywibox : setup {
2016-02-10 07:07:47 +01:00
{
-- Force the textbox to always be 300 pixel long
{
{
markup = "<b>Hello World!</b>",
align = "center",
widget = wibox.widget.textbox
},
bg = "#ff0000",
widget = wibox.container.background,
2016-02-10 07:07:47 +01:00
},
width = 300,
strategy = "min",
layout = wibox.layout.constraint
},
{
-- Add a border around the background
{
{
markup = "Foobar",
widget = wibox.widget.textbox
},
bg = "#0000ff",
widget = wibox.container.background
2016-02-10 07:07:47 +01:00
},
left = 10,
right = 10,
top = 1,
bottom = 2,
layout = wibox.container.margin
2016-02-10 07:07:47 +01:00
},
layout = wibox.layout.fixed.horizontal,
}
Result:
![Example2 screenshot](../images/widgetlayout1.png)
2016-02-10 07:07:47 +01:00
2016-12-13 23:31:25 +01:00
### Use a `wibox.layout.align` layout
2016-02-10 07:07:47 +01:00
The `wibox.layout.align` is a little different. While most layouts will
2016-12-13 23:31:25 +01:00
ignore any `nil` lines, the `align` layout relies on them so `left`, `middle`
2016-12-16 22:02:35 +01:00
and `right` can be defined.
2016-02-10 07:07:47 +01:00
Code:
s.mywibox : setup {
2016-02-10 07:07:47 +01:00
my_textbox1, -- Left
nil, -- Nothing in the middle
my_textbox2, -- Right
layout = wibox.layout.fixed.horizontal,
}
### Define new widgets
2016-02-10 07:07:47 +01:00
New trivial widgets can be created directly in the layout declaration. Here
is a simple circle widget:
Code:
s.mywibox : setup {
2016-02-10 07:07:47 +01:00
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](../images/widgetlayout2.png)
2016-02-10 07:07:47 +01:00
2016-12-13 23:31:25 +01:00
For more information about how to draw widgets, refer to the `Cairo` API:
2016-02-10 07:07:47 +01:00
* [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
2016-02-10 07:07:47 +01:00
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
s.mywibox : setup {
2016-02-10 07:07:47 +01:00
tb,
tb,
tb,
layout = wibox.layout.fixed.horizontal,
}
### Accessing widgets
2016-02-10 07:07:47 +01:00
For each widget or container, it is possible to add an `identifier` attribute
2016-12-16 22:02:35 +01:00
so that it can be accessed later.
2016-02-10 07:07:47 +01:00
2016-12-13 23:31:25 +01:00
Widgets defined using `setup` can be accessed using these methods:
2016-02-10 07:07:47 +01:00
2016-12-13 23:31:25 +01:00
* Avoiding the issue by using externally created widgets
* Using `my_wibox.my_first_widget.my_second_widget` style access
* Using JavaScript like `my_wibox:get_children_by_id("my_second_widget")[1]`
2016-02-10 07:07:47 +01:00
2016-12-13 23:31:25 +01:00
The first method mixes the imperative and declarative syntax, and makes the code
2016-02-10 07:07:47 +01:00
less readable. The second is a little verbose and only works if every node in
2016-12-13 23:31:25 +01:00
the chain has a valid identifier. The last one doesn't require long paths,
2016-02-10 07:07:47 +01:00
but it is not easy to get a specific instance if multiple widgets have the
same identifier.
2016-12-16 22:02:35 +01:00
WARNING: The widget identifier must not use a reserved name. This includes all
2016-02-10 07:07:47 +01:00
method names, existing widget attributes, `layout` and `widget`. Names should
2016-12-13 23:31:25 +01:00
also respect the Lua variable conventions (case-sensitive, alphanumeric,
2016-12-16 22:02:35 +01:00
underscore characters and non-numeric first character).
2016-02-10 07:07:47 +01:00
Code:
s.mywibox : setup {
2016-02-10 07:07:47 +01:00
{
id = "second",
widget = wibox.widget.textbox
},
{
id = "third",
widget = wibox.widget.textbox
},
id = "first",
layout = wibox.layout.fixed.horizontal,
}
s.mywibox.first.second:set_markup("changed!")
s.mywibox:get_children_by_id("third")[1]:set_markup("Also changed!")
2016-02-10 07:07:47 +01:00
### Extending the system
2016-02-10 07:07:47 +01:00
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
2016-12-13 23:31:25 +01:00
imaginary `myproperty`, it will first check if `set_myproperty` exists. If it
2016-02-10 07:07:47 +01:00
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
2016-12-13 23:31:25 +01:00
caught by a Lua `metatable` (operator overload).
2016-02-10 07:07:47 +01:00
Code:
-- "Monkeypatch" a new function to 3 widget classes to add vicious
2016-02-10 07:07:47 +01:00
-- extension support
for _, wdg in ipairs {
wibox.widget.textbox , wibox.widget.progressbar, wibox.widget.graph
} do
function wdg:vicious(args)
local f = unpack or table.unpack -- Lua 5.1 compat
vicious.register(self, f(args))
end
2016-02-10 07:07:47 +01:00
end
s.mywibox : setup {
2016-02-10 07:07:47 +01:00
{
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](https://github.com/Mic92/vicious) extension module can be
2016-02-10 07:07:47 +01:00
used directly in the layout declaration. This example will update the textbox
every 3 seconds to show the CPU usage.
### Handling sections
2016-02-10 07:07:47 +01:00
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
s.mywibox : setup {
2016-02-10 07:07:47 +01:00
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
s.mywibox : setup (three_circle)
2016-02-10 07:07:47 +01:00