Merge pull request #1647 from Elv13/free_layout
Add a manual (free floating) layout
This commit is contained in:
commit
ef1d7d8396
|
@ -12,6 +12,7 @@ return setmetatable({
|
|||
align = require("wibox.layout.align");
|
||||
flex = require("wibox.layout.flex");
|
||||
rotate = require("wibox.layout.rotate");
|
||||
manual = require("wibox.layout.manual");
|
||||
margin = require("wibox.layout.margin");
|
||||
mirror = require("wibox.layout.mirror");
|
||||
constraint = require("wibox.layout.constraint");
|
||||
|
|
|
@ -0,0 +1,217 @@
|
|||
---------------------------------------------------------------------------
|
||||
-- A layout with widgets added at specific positions.
|
||||
--
|
||||
-- Use cases include desktop icons, complex custom composed widgets, a floating
|
||||
-- client layout and fine grained control over the output.
|
||||
--
|
||||
--@DOC_wibox_layout_defaults_manual_EXAMPLE@
|
||||
-- @author Emmanuel Lepage Vallee
|
||||
-- @copyright 2016 Emmanuel Lepage Vallee
|
||||
-- @classmod wibox.layout.manual
|
||||
---------------------------------------------------------------------------
|
||||
local gtable = require("gears.table")
|
||||
local base = require("wibox.widget.base")
|
||||
local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)
|
||||
|
||||
local manual_layout = {}
|
||||
|
||||
--- Add some widgets to the given stack layout
|
||||
-- @param layout The layout you are modifying.
|
||||
-- @tparam widget ... Widgets that should be added
|
||||
-- @name add
|
||||
-- @class function
|
||||
|
||||
--- Remove a widget from the layout
|
||||
-- @tparam index The widget index to remove
|
||||
-- @treturn boolean index If the operation is successful
|
||||
-- @name remove
|
||||
-- @class function
|
||||
|
||||
--- Insert a new widget in the layout at position `index`
|
||||
-- @tparam number index The position
|
||||
-- @param widget The widget
|
||||
-- @treturn boolean If the operation is successful
|
||||
-- @name insert
|
||||
-- @class function
|
||||
|
||||
--- Remove one or more widgets from the layout
|
||||
-- The last parameter can be a boolean, forcing a recursive seach of the
|
||||
-- widget(s) to remove.
|
||||
-- @param widget ... Widgets that should be removed (must at least be one)
|
||||
-- @treturn boolean If the operation is successful
|
||||
-- @name remove_widgets
|
||||
-- @class function
|
||||
|
||||
|
||||
function manual_layout:fit(_, width, height)
|
||||
return width, height
|
||||
end
|
||||
|
||||
local function geometry(self, new)
|
||||
self._new_geo = new
|
||||
return self._new_geo or self
|
||||
end
|
||||
|
||||
function manual_layout:layout(context, width, height)
|
||||
local res = {}
|
||||
|
||||
for k, v in ipairs(self._private.widgets) do
|
||||
local pt = self._private.pos[k] or {x=0,y=0}
|
||||
local w, h = base.fit_widget(self, context, v, width, height)
|
||||
|
||||
-- Make sure the signature is compatible with `awful.placement`. `Wibox`,
|
||||
-- doesn't depend on `awful`, but it is still nice not to have to code
|
||||
-- geometry functions again and again.
|
||||
if type(pt) == "function" or (getmetatable(pt) or {}).__call then
|
||||
local geo = {
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = w,
|
||||
height = h,
|
||||
geometry = geometry,
|
||||
}
|
||||
pt = pt(geo, {
|
||||
parent = {
|
||||
x=0, y=0, width = width, height = height, geometry = geometry
|
||||
}
|
||||
})
|
||||
-- Trick to ensure compatibility with `awful.placement`
|
||||
gtable.crush(pt, geo._new_geo or {})
|
||||
end
|
||||
|
||||
assert(pt.x)
|
||||
assert(pt.y)
|
||||
|
||||
table.insert(res, base.place_widget_at(
|
||||
v, pt.x, pt.y, pt.width or w, pt.height or h
|
||||
))
|
||||
end
|
||||
|
||||
return res
|
||||
end
|
||||
|
||||
function manual_layout:add(...)
|
||||
local wdgs = {...}
|
||||
local old_count = #self._private.widgets
|
||||
gtable.merge(self._private.widgets, {...})
|
||||
|
||||
-- Add the points
|
||||
for k, v in ipairs(wdgs) do
|
||||
if v.point then
|
||||
self._private.pos[old_count+k] = v.point
|
||||
end
|
||||
end
|
||||
|
||||
self:emit_signal("widget::layout_changed")
|
||||
end
|
||||
|
||||
--- Add a widget at a specific point.
|
||||
--
|
||||
-- The point can either be a function or a table. The table follow the generic
|
||||
-- geometry format used elsewhere in Awesome.
|
||||
--
|
||||
-- * *x*: The horizontal position.
|
||||
-- * *y*: The vertical position.
|
||||
-- * *width*: The width.
|
||||
-- * *height*: The height.
|
||||
--
|
||||
-- If a function is used, it follows the same prototype as `awful.placement`
|
||||
-- functions.
|
||||
--
|
||||
-- * *geo*:
|
||||
-- * *x*: The horizontal position (always 0).
|
||||
-- * *y*: The vertical position (always 0).
|
||||
-- * *width*: The width.
|
||||
-- * *height*: The height.
|
||||
-- * *geometry*: A function to get or set the geometry (for compatibility).
|
||||
-- The function is compatible with the `awful.placement` prototype.
|
||||
-- * *args*:
|
||||
-- * *parent* The layout own geometry
|
||||
-- * *x*: The horizontal position (always 0).
|
||||
-- * *y*: The vertical position (always 0).
|
||||
-- * *width*: The width.
|
||||
-- * *height*: The height.
|
||||
-- * *geometry*: A function to get or set the geometry (for compatibility)
|
||||
-- The function is compatible with the `awful.placement` prototype.
|
||||
--
|
||||
--@DOC_wibox_layout_manual_add_at_EXAMPLE@
|
||||
-- @tparam widget widget The widget.
|
||||
-- @tparam table|function point Either an `{x=x,y=y}` table or a function
|
||||
-- returning the new geometry.
|
||||
function manual_layout:add_at(widget, point)
|
||||
assert(not widget.point, "2 points are specified, only one is supported")
|
||||
|
||||
-- Check is the point function is valid
|
||||
if type(point) == "function" or (getmetatable(point) or {}).__call then
|
||||
local fake_geo = {x=0,y=0,width=1,height=1,geometry=geometry}
|
||||
local pt = point(fake_geo, {
|
||||
parent = {
|
||||
x=0, y=0, width = 10, height = 10, geometry = geometry
|
||||
}
|
||||
})
|
||||
assert(pt and pt.x and pt.y, "The point function doesn't seem to be valid")
|
||||
end
|
||||
|
||||
self._private.pos[#self._private.widgets+1] = point
|
||||
self:add(widget)
|
||||
end
|
||||
|
||||
--- Move a widget (by index).
|
||||
-- @tparam number index The widget index.
|
||||
-- @tparam table|function point A new point value.
|
||||
-- @see add_at
|
||||
function manual_layout:move(index, point)
|
||||
assert(self._private.pos[index])
|
||||
self._private.pos[index] = point
|
||||
self:emit_signal( "widget::layout_changed" )
|
||||
end
|
||||
|
||||
--- Move a widget.
|
||||
--
|
||||
--@DOC_wibox_layout_manual_move_widget_EXAMPLE@
|
||||
-- @tparam widget widget The widget.
|
||||
-- @tparam table|function point A new point value.
|
||||
-- @see add_at
|
||||
function manual_layout:move_widget(widget, point)
|
||||
local idx, l = self:index(widget, false)
|
||||
|
||||
if idx then
|
||||
l:move(idx, point)
|
||||
end
|
||||
end
|
||||
|
||||
function manual_layout:get_children()
|
||||
return self._private.widgets
|
||||
end
|
||||
|
||||
function manual_layout:set_children(children)
|
||||
self:reset()
|
||||
self:add(unpack(children))
|
||||
end
|
||||
|
||||
function manual_layout:reset()
|
||||
self._private.widgets = {}
|
||||
self._private.pos = {}
|
||||
self:emit_signal( "widget::layout_changed" )
|
||||
end
|
||||
|
||||
--- Create a manual layout.
|
||||
-- @tparam table ... Widgets to add to the layout.
|
||||
local function new_manual(...)
|
||||
local ret = base.make_widget(nil, nil, {enable_properties = true})
|
||||
|
||||
gtable.crush(ret, manual_layout, true)
|
||||
ret._private.widgets = {}
|
||||
ret._private.pos = {}
|
||||
|
||||
ret:add(...)
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
|
||||
--@DOC_widget_COMMON@
|
||||
|
||||
--@DOC_object_COMMON@
|
||||
|
||||
return setmetatable(manual_layout, {__call=function(_,...) return new_manual(...) end})
|
|
@ -0,0 +1,24 @@
|
|||
local generic_widget = ... --DOC_HIDE
|
||||
local wibox = require("wibox") --DOC_HIDE
|
||||
|
||||
local w1, w2 = generic_widget(), generic_widget()
|
||||
w1.point = {x=75,y=5}
|
||||
w1.text = "first"
|
||||
w1.forced_width = 50
|
||||
|
||||
w2.text = "second"
|
||||
w2.point = function(geo, args)
|
||||
-- Bottom right
|
||||
return {
|
||||
x = args.parent.width-geo.width,
|
||||
y = args.parent.height-geo.height
|
||||
}
|
||||
end
|
||||
|
||||
return --DOC_HIDE
|
||||
wibox.layout {
|
||||
w1,
|
||||
w2,
|
||||
generic_widget("third"),
|
||||
layout = wibox.layout.manual
|
||||
}
|
|
@ -8,9 +8,9 @@ local w = generic_before_after(wibox.layout.grid, {
|
|||
forced_num_cols = 3,
|
||||
forced_num_rows = 2,
|
||||
homogeneous = true,
|
||||
}, 6, "add_widget_at", {wibox.widget( --DOC_HIDE
|
||||
}, 6, "add_widget_at", {--DOC_HIDE
|
||||
generic_widget("__new__",beautiful.bg_highlight) --DOC_HIDE
|
||||
), 1, 4, 1, 1 --DOC_HIDE
|
||||
, 1, 4, 1, 1 --DOC_HIDE
|
||||
}) --DOC_HIDE
|
||||
|
||||
return w, w:fit({dpi=96}, 9999, 9999) --DOC_HIDE
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
local generic_widget = ... --DOC_NO_USAGE --DOC_HIDE
|
||||
local wibox = require("wibox") --DOC_HIDE
|
||||
|
||||
local first = wibox.widget(generic_widget( "first" )) --DOC_HIDE
|
||||
local second = wibox.widget(generic_widget( "second" )) --DOC_HIDE
|
||||
local third = wibox.widget(generic_widget( "t\nh\ni\nr\nd" )) --DOC_HIDE
|
||||
local fourth = wibox.widget(generic_widget( "fourth" )) --DOC_HIDE
|
||||
local fifth = wibox.widget(generic_widget( "fifth" )) --DOC_HIDE
|
||||
local lorem = wibox.widget(generic_widget("Lorem ipsum dolor sit amet, consectetur " .. --DOC_HIDE
|
||||
"adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.")) --DOC_HIDE
|
||||
local first = generic_widget( "first" ) --DOC_HIDE
|
||||
local second = generic_widget( "second" ) --DOC_HIDE
|
||||
local third = generic_widget( "t\nh\ni\nr\nd" ) --DOC_HIDE
|
||||
local fourth = generic_widget( "fourth" ) --DOC_HIDE
|
||||
local fifth = generic_widget( "fifth" ) --DOC_HIDE
|
||||
local lorem = generic_widget("Lorem ipsum dolor sit amet, consectetur " .. --DOC_HIDE
|
||||
"adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.") --DOC_HIDE
|
||||
|
||||
local l = wibox.widget {
|
||||
homogeneous = true,
|
||||
|
|
|
@ -13,8 +13,8 @@ local w = wibox.widget {
|
|||
homogeneous = true,
|
||||
layout = wibox.layout.grid,
|
||||
}
|
||||
w:add_widget_at(wibox.widget(
|
||||
w:add_widget_at(
|
||||
generic_widget("fifth",beautiful.bg_highlight)
|
||||
), 1, 1, 1, 2)
|
||||
, 1, 1, 1, 2)
|
||||
|
||||
return w, w:fit({dpi=96}, 9999, 9999)
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
local generic_widget = ... --DOC_HIDE
|
||||
local wibox = require("wibox") --DOC_HIDE
|
||||
local awful = {placement = require("awful.placement")} --DOC_HIDE
|
||||
|
||||
local l = wibox.layout {
|
||||
layout = wibox.layout.manual
|
||||
}
|
||||
--
|
||||
-- Option 1: Set the point directly in the widget
|
||||
local w1 = generic_widget()
|
||||
w1.point = {x=75, y=5}
|
||||
w1.text = "first"
|
||||
w1.forced_width = 50
|
||||
l:add(w1)
|
||||
|
||||
--
|
||||
-- Option 2: Set the point directly in the widget as a function
|
||||
|
||||
local w2 = generic_widget()
|
||||
w2.text = "second"
|
||||
w2.point = function(geo, args)
|
||||
return {
|
||||
x = args.parent.width - geo.width,
|
||||
y = 0
|
||||
}
|
||||
end
|
||||
l:add(w2)
|
||||
|
||||
--
|
||||
-- Option 3: Set the point directly in the widget as an `awful.placement`
|
||||
-- function.
|
||||
|
||||
local w3 = generic_widget()
|
||||
w3.text = "third"
|
||||
w3.point = awful.placement.bottom_right
|
||||
l:add(w3)
|
||||
|
||||
--
|
||||
-- Option 4: Use `:add_at` instead of using the `.point` property. This works
|
||||
-- with all 3 ways to define the point.
|
||||
-- function.
|
||||
|
||||
local w4 = generic_widget()
|
||||
w4.text = "fourth"
|
||||
l:add_at(w4, awful.placement.centered + awful.placement.maximize_horizontally)
|
||||
|
||||
return l, 200, 100 --DOC_HIDE
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
local generic_widget = ... --DOC_HIDE
|
||||
local wibox = require("wibox") --DOC_HIDE
|
||||
local awful = {placement = require("awful.placement")} --DOC_HIDE
|
||||
|
||||
local l = wibox.layout {
|
||||
layout = wibox.layout.manual
|
||||
}
|
||||
--
|
||||
local w1 = generic_widget()
|
||||
w1.point = {x=75, y=5}
|
||||
w1.text = "first"
|
||||
w1.forced_width = 50
|
||||
l:add(w1)
|
||||
|
||||
l:move_widget(w1, awful.placement.bottom_right)
|
||||
|
||||
return l, 100, 50 --DOC_HIDE
|
||||
|
|
@ -8,7 +8,7 @@ local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibil
|
|||
|
||||
-- Create a generic rectangle widget to show layout disposition
|
||||
local function generic_widget(text, col)
|
||||
return {
|
||||
return wibox.widget {
|
||||
{
|
||||
{
|
||||
draw = function(_, _, cr, width, height)
|
||||
|
@ -21,15 +21,20 @@ local function generic_widget(text, col)
|
|||
end,
|
||||
widget = wibox.widget.base.make_widget
|
||||
},
|
||||
text and {
|
||||
{
|
||||
id = "text",
|
||||
align = "center",
|
||||
valign = "center",
|
||||
text = text,
|
||||
text = text or "foobar",
|
||||
widget = wibox.widget.textbox
|
||||
} or nil,
|
||||
widget = wibox.layout.stack
|
||||
},
|
||||
margins = 5,
|
||||
set_text = function(self, text2)
|
||||
self:get_children_by_id("text")[1]:set_text(text2)
|
||||
end,
|
||||
is_widget = true,
|
||||
widget = wibox.container.margin,
|
||||
}
|
||||
end
|
||||
|
@ -61,7 +66,7 @@ local function generic_before_after(layout, layout_args, count, method, method_a
|
|||
|
||||
local l = wibox.layout(args)
|
||||
for j=1, count or 3 do
|
||||
l:add(wibox.widget(generic_widget(names[j] or "N/A")))
|
||||
l:add(generic_widget(names[j] or "N/A"))
|
||||
end
|
||||
|
||||
ls[i] = l
|
||||
|
|
Loading…
Reference in New Issue