widget layouts: rewrite

Signed-off-by: Lukas Hrazky <lukkash@email.cz>
Signed-off-by: Julien Danjou <julien@danjou.info>
This commit is contained in:
Lukas Hrazky 2009-10-24 14:50:37 +02:00 committed by Julien Danjou
parent eb7ae0fb20
commit c09582dca3
6 changed files with 737 additions and 331 deletions

View File

@ -1,58 +0,0 @@
-------------------------------------------------
-- @author Gregor Best <farhaven@googlemail.com>
-- @copyright 2009 Gregor Best
-- @release @AWESOME_VERSION@
-------------------------------------------------
-- Grab environment
local ipairs = ipairs
local type = type
local table = table
local math = math
local setmetatable = setmetatable
local util = require("awful.util")
--- Simple default layout, emulating the fallback C layout
module("awful.widget.layout.default")
local function default(bounds, widgets, screen)
local geometries = {
free = { x = 0, y = 0, width = 0, height = bounds.height }
}
local width = 0
local keys = util.table.keys_filter(widgets, "table", "widget")
for _, k in ipairs(keys) do
local v = widgets[k]
if type(v) == "table" then
local layout = v.layout or default
local nbounds = util.table.clone(bounds)
local g = layout(nbounds, v, screen)
for _, w in ipairs(g) do
table.insert(geometries, w)
end
else
if v.visible then
local e = v:extents(screen)
e.x = 0
e.y = 0
e.width = math.min(e.width, bounds.width)
e.height = bounds.height
width = math.max(e.width, width)
table.insert(geometries, e)
else
table.insert(geometries, { x = 0, y = 0, width = 0, height = 0 })
end
end
end
geometries.free.width = bounds.width - width
geometries.free.x = width
return geometries
end
setmetatable(_M, { __call = function(_, ...) return default(...) end })

View File

@ -0,0 +1,203 @@
-------------------------------------------------
-- @author Lukas Hrazky <lukkash@email.cz>
-- @copyright 2009 Gregor Best, Lukas Hrazky
-- @release @AWESOME_VERSION@
-------------------------------------------------
local setmetatable = setmetatable
local require = require
local ipairs = ipairs
local pairs = pairs
local type = type
local insert = table.insert
local min = math.min
local max = math.max
local util = require("awful.util")
local clone = util.table.clone
local margins = awful.widget.layout.margins
local layout = awful.widget.layout
--module("awful.widget.layout.grid")
local M = {}
-- Creates an iterator over the cell sizes from the direction string.
-- It takes the bounds for the grid, the row/column sizes and a direction and returns an iterator
-- over the slices of the bounds whose sizes are determined by 'widths' or 'heights'. The 'direction'
-- determines which of these will be used and whether it will be in reverse or not.
-- @param direction The direction in which to iterate: 'leftright', 'rightleft', 'topdown' or 'bottomup'
-- @param bounds The bounds for the iterator, will be sliced according to direction
-- @param widths The widths of the columns in the grid
-- @param heights The heights of the rows in the grid
-- @return An iterator over the slices of the bounds
local function direction_iter(direction, bounds, widths, heights)
local iter
local i = 0
local idx, sizes
-- preset the closure upvalues according to the direction
if direction == "leftright" or direction == "rightleft" then
idx = layout.regular_index
sizes = widths
elseif direction == "topdown" or direction == "bottomup" then
idx = layout.switched_index
sizes = heights
end
if direction == "leftright" or direction == "topdown" then
-- the regular direction iterator
iter = function(bounds)
i = i + 1
if not sizes[i] then return end
local b = clone(bounds)
local width = min(sizes[i], b[idx.width])
b[idx.width] = width
bounds[idx.x] = bounds[idx.x] + width + sizes.gap
bounds[idx.width] = bounds[idx.width] - width
return b
end
elseif direction == "rightleft" or direction == "bottomup" then
-- the reverse direction iterator
iter = function(bounds)
i = i + 1
if not sizes[i] then return end
local b = clone(bounds)
local width = min(sizes[i], b[idx.width])
b[idx.x] = b[idx.x] + b[idx.width] - width + sizes.gap
b[idx.width] = width
bounds[idx.width] = bounds[idx.width] - width
return b
end
end
return iter, bounds
end
-- Places the widgets in a grid.
-- @see awful.widget.layout for a luadoc
local function grid(bounds, widgets, screen)
local widths = {}
local heights = {}
-- we either have a table with the widths of the cells, or its a number and there is a col_count
if type(widgets.cell_width) == "table" then
widths = widgets.cell_width
else
for i = 1, widgets.col_count do widths[i] = widgets.cell_width end
end
-- we either have a table with the heights of the cells, or its a number and there is a row_count
if type(widgets.cell_height) == "table" then
heights = widgets.cell_height
else
for i = 1, widgets.row_count do heights[i] = widgets.cell_height end
end
widths.gap = widgets.col_gap or 0
heights.gap = widgets.row_gap or 0
-- calculate total space required for the grid
local w = - widths.gap
local h = - heights.gap
for _, v in ipairs(widths) do w = w + v + widths.gap end
for _, v in ipairs(heights) do h = h + v + heights.gap end
bounds.width = min(bounds.width, w)
bounds.height = min(bounds.height, h)
-- the table for the geometries which will be returned
-- we clone the bounds to the 'total' attribute. bounds are used to keep the free space
-- throughout this function. at the end, 'total' is modified to represent the space taken
-- by all widgets
local geometries = {total = clone(bounds)}
-- i counts the widgets placed in the grid
local i = 1
-- the iterators return the bounds for the cells of the table. first one in secondary direction
-- returns rows or columns, second one returns cell bounds in that row/column
for sb in direction_iter(widgets.secondary_dir or "topdown", bounds, widths, heights) do
for b in direction_iter(widgets.primary_dir or "leftright", sb, widths, heights) do
-- get the next widget, if there is none, we are done, some cells won't be filled
local w = widgets[i]
if not w then break end
if type(w) == "table" or type(w) == "widget" then
local m = margins[w]
-- shrink the cell bounds by the margins
b.width = b.width - m.left - m.right
b.height = b.height - m.top - m.bottom
b.x = b.x + m.left
b.y = b.y + m.top
if type(w) == "table" then
-- backup the width and height of the table so we can restore it
local t_width = w.width
local t_height = w.height
-- if the 'widgets' table has height set and the table itself doesn't, we set it
if widgets.resize then
w.width = b.width
w.height = b.height
end
-- call the layout function recursively on this table
local layout = w.layout or layout.default
local g = layout(b, w, screen)
-- restore the table's original width and height
w.width = t_width
w.height = t_height
-- insert all geometries from the table to our geometries
for _, w in ipairs(g) do
insert(geometries, w)
end
elseif type(widgets[i]) == "widget" then
local g = {x = 0, y = 0, width = 0, height = 0}
if widgets[i].visible then
-- get the geometry of the widget
g = widgets[i]:extents(screen)
-- set the coords
g.x = b.x
g.y = b.y
-- if we are resizing or it doesn't fit inside the bounds, set width/height
if widgets.resize or g.width > b.width then
g.width = b.width
end
if widgets.resize or g.height > b.height then
g.height = b.height
end
end
insert(geometries, g)
end
i = i + 1
end
end
end
-- set zero geometries for any leftover widgets
-- Hack: if it has been registered as a widget in a wibox,
-- it's w.len since __len meta does not work on table until Lua 5.2.
-- Otherwise it's standard #w.
local len = (widgets.len or #widgets)
for j = i, len do
insert(geometries, {x = 0, y = 0, width = 0, height = 0})
end
return geometries
end
--setmetatable(_M, { __call = function(_, ...) return grid(...) end })
return grid
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -1,188 +1,95 @@
-------------------------------------------------
-- @author Gregor Best <farhaven@googlemail.com>
-- @copyright 2009 Gregor Best
-- @author Gregor Best <farhaven@googlemail.com>, Lukas Hrazky <lukkash@email.cz>
-- @copyright 2009 Gregor Best, Lukas Hrazky
-- @release @AWESOME_VERSION@
-------------------------------------------------
-- Grab environment
local require = require
local ipairs = ipairs
local type = type
local table = table
local math = math
local pairs = pairs
local util = require("awful.util")
local default = require("awful.widget.layout.default")
local margins = awful.widget.layout.margins
local layout = require("awful.widget.layout")
local linear = require("awful.widget.layout.linear_common")
--- Horizontal widget layout
--- Horizontal widget layouts
module("awful.widget.layout.horizontal")
local function horizontal(direction, bounds, widgets, screen)
local geometries = { }
local x = 0
-- index used for horizontal layouts
local idx = layout.regular_index
-- we are only interested in tables and widgets
local keys = util.table.keys_filter(widgets, "table", "widget")
--- Places the widgets in a row, aligned to the left.
-- This is the default layout.
-- It recognizes the following attributes in the encompassing table:
-- <p><code>width</code>: Maximum width of the layout. (optional)<br/>
-- <code>height</code>: Height of the layout. If set, all widgets are resized to this height. (optional)</p>
-- @param bounds The geometry of the bounds for the layout.
-- @param widgets The table of the widgets to layout. When layout function is placed in
-- a table, it is the table that is passed as this argument.
-- @param screen The screen of the wibox.
-- @return A table of geometries of all the widgets.
function leftright(bounds, widgets, screen)
return linear.fixed(idx, bounds, widgets, screen)
end
for _, k in ipairs(keys) do
local v = widgets[k]
if type(v) == "table" then
local layout = v.layout or default
if margins[v] then
bounds.width = bounds.width - (margins[v].left or 0) - (margins[v].right or 0)
bounds.height = bounds.height - (margins[v].top or 0) - (margins[v].bottom or 0)
--- Places the widgets in a row, aligned to the right.
-- It recognizes the following attributes in the encompassing table:
-- <p><code>width</code>: Maximum width of the layout. (optional)<br/>
-- <code>height</code>: Height of the layout. If set, all widgets are resized to this height. (optional)</p>
-- @param bounds The geometry of the bounds for the layout.
-- @param widgets The table of the widgets to layout. When layout function is placed in
-- a table, it is the table that is passed as this argument.
-- @param screen The screen of the wibox.
-- @return A table of geometries of all the widgets.
function rightleft(bounds, widgets, screen)
local g = linear.fixed(idx, util.table.clone(bounds), widgets, screen)
for _, w in pairs(g) do
w.x = w.x + bounds.width - g.total.width
end
local g = layout(bounds, v, screen)
if margins[v] then
x = x + (margins[v].left or 0)
return g
end
for _, v in ipairs(g) do
v.x = v.x + x
v.y = v.y + (margins[v] and (margins[v].top and margins[v].top or 0) or 0)
table.insert(geometries, v)
--- Places the widgets in a row, aligned to the center.
-- It recognizes the following attributes in the encompassing table:
-- <p><code>width</code>: Maximum width of the layout. (optional)<br/>
-- <code>height</code>: Height of the layout. If set, all widgets are resized to this height. (optional)</p>
-- The widgets will be placed in the center of the free space that is left after all widgets
-- preceding the table with this layout were placed. The area covered by widgets with this layout
-- is not subtracted from the total free area (so the widgets "cover no space"). Therefore, if you
-- put anything after a table with center layout, the widgets may overlap.<br/>
-- @param bounds The geometry of the bounds for the layout.
-- @param widgets The table of the widgets to layout. When layout function is placed in
-- a table, it is the table that is passed as this argument.
-- @param screen The screen of the wibox.
-- @return A table of geometries of all the widgets.
function center(bounds, widgets, screen)
local g = linear.fixed(idx, util.table.clone(bounds), widgets, screen)
for _, w in ipairs(g) do
w.x = w.x + (bounds.width - g.total.width) / 2
end
bounds = g.free
if margins[v] then
x = x + g.free.x + (margins[v].right or 0)
bounds.width = bounds.width - (margins[v].right or 0) - (margins[v].left or 0)
-- if width is set for widgets table, make total this wide,
-- otherwise move total.x to reflect new position
if widgets.width then
g.total.width = widgets.width
else
x = x + g.free.x
g.total.x = g.total.x + (bounds.width - g.total.width) / 2
end
elseif type(v) == "widget" then
local g
if v.visible then
g = v:extents(screen)
if margins[v] then
g.width = g.width + (margins[v].left or 0) + (margins[v].right or 0)
g.height = g.height + (margins[v].top or 0) + (margins[v].bottom or 0)
end
else
g = {
width = 0,
height = 0,
}
end
if v.resize and g.width > 0 and g.height > 0 then
local ratio = g.width / g.height
g.width = math.floor(bounds.height * ratio)
g.height = bounds.height
end
if g.width > bounds.width then
g.width = bounds.width
end
g.height = bounds.height
if margins[v] then
g.y = (margins[v].top or 0)
else
g.y = 0
end
if direction == "leftright" then
if margins[v] then
g.x = x + (margins[v].left or 0)
else
g.x = x
end
x = x + g.width
else
if margins[v] then
g.x = x + bounds.width - g.width + (margins[v].left or 0)
else
g.x = x + bounds.width - g.width
end
end
bounds.width = bounds.width - g.width
table.insert(geometries, g)
end
end
geometries.free = util.table.clone(bounds)
geometries.free.x = x
geometries.free.y = 0
return geometries
return g
end
--- Places the widgets in a row, making them equally wide.
-- It recognizes the following attributes in the encompassing table:
-- <p><code>width</code>: Maximum width of the layout. (optional)<br/>
-- <code>height</code>: Height of the layout. If set, all widgets are resized to this height. (optional)<br/>
-- <code>max_size</code>: Maximum width of one item. If not set, widgets' widths are set so that they always
-- cover all available space. (optional)<br/>
-- <code>gap</code>: Gap between the widgets. (optional)</p>
-- @param bounds The geometry of the bounds for the layout.
-- @param widgets The table of the widgets to layout. When layout function is placed in
-- a table, it is the table that is passed as this argument.
-- @param screen The screen of the wibox.
-- @return A table of geometries of all the widgets.
function flex(bounds, widgets, screen)
local geometries = {
free = util.table.clone(bounds)
}
-- the flex layout always uses the complete available place, thus we return
-- no usable free area
geometries.free.width = 0
-- we are only interested in tables and widgets
local keys = util.table.keys_filter(widgets, "table", "widget")
local nelements = 0
for _, k in ipairs(keys) do
local v = widgets[k]
if type(v) == "table" then
nelements = nelements + 1
elseif type(v) == "widget" then
local g = v:extents()
if v.resize and g.width > 0 and g.height > 0 then
bounds.width = bounds.width - bounds.height
elseif g.width > 0 and g.height > 0 then
nelements = nelements + 1
end
end
end
nelements = (nelements == 0) and 1 or nelements
local x = 0
local width = bounds.width / nelements
for _, k in ipairs(util.table.keys(widgets)) do
local v = widgets[k]
if type(v) == "table" then
local layout = v.layout or default
local g = layout(bounds, v, screen)
for _, v in ipairs(g) do
v.x = v.x + x
table.insert(geometries, v)
end
bounds = g.free
elseif type(v) == "widget" then
local g = v:extents(screen)
g.resize = v.resize
if v.resize and g.width > 0 and g.height > 0 then
g.width = bounds.height
g.height = bounds.height
g.x = x
g.y = bounds.y
x = x + g.width
elseif g.width > 0 and g.height > 0 then
g.x = x
g.y = bounds.y
g.width = math.floor(width + 0.5)
g.height = bounds.height
x = x + width
else
g.x = 0
g.y = 0
g.width = 0
g.height = 0
end
table.insert(geometries, g)
end
end
return geometries
end
function leftright(...)
return horizontal("leftright", ...)
end
function rightleft(...)
return horizontal("rightleft", ...)
return linear.flex(idx, bounds, widgets, screen)
end
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -1,23 +1,90 @@
-------------------------------------------------
-- @author Gregor Best <farhaven@googlemail.com>, Lukas Hrazky <lukkash@email.cz>
-- @copyright 2009 Gregor Best, Lukas Hrazky
-- @release @AWESOME_VERSION@
-------------------------------------------------
local setmetatable = setmetatable
local require = require
local type = type
-- Widget layouts
--- Widget layouts
module("awful.widget.layout")
--- Widgets margins.
-- <p>In this table you can set the margin you want the layout to use when
-- positionning your widgets.
-- For example, if you want to put 10 pixel free on left on a widget, add this:
-- <code>
-- the table that really contains the margins.
local margin_table = setmetatable({}, {__mode = 'k'})
-- empty margin table - used every time there is no margin for something
local empty_margin = setmetatable({}, {__index = function() return 0 end})
--- Margins of widgets and layout tables.
-- In this table you can set the margin you want the layout to use when positionning
-- your widgets or tables with widgets. The key is the widget or the table to set
-- margin for, value is either a number (setting the same margin to all four sides) or
-- a table with one or more of the following keys:
-- <p><code>left, right, top, bottom</code></p>
-- For example, if you want to put a 10 pixel free space on the left of a widget, add this:
-- <p><code>
-- awful.widget.layout.margins[mywidget] = { left = 10 }
-- </code>
-- </p>
-- </code></p>
-- @name margins
-- @class table
margins = setmetatable({}, { __mode = 'k' })
margins = setmetatable({}, {
__index = function(t, i)
return margin_table[i] or empty_margin
end,
__newindex = function(t, i, v)
if type(v) == "number" then
margin_table[i] = setmetatable({}, {__index = function() return v end})
elseif type(v) == "table" then
margin_table[i] = setmetatable(v, {__index = function() return 0 end})
end
end
})
-- A table used to index geometry properties. Returns the name with which it is indexed.
-- It is used in horizontal layouts, where it returns "x" when indexed by "x", etc.
regular_index = setmetatable({}, {__index = function(t, i) return i end})
-- A table used to index geometry properties. Returns the name opposite to the one
-- with which it is indexed. It is used in vertical layouts, where it returns "y"
-- when indexed by "x", etc.
switched_index = {
x = "y",
y = "x",
width = "height",
height = "width",
top = "left",
bottom = "right",
left = "top",
right = "bottom"
}
-- set the default layout function
local horizontal = require("awful.widget.layout.horizontal")
default = horizontal.leftright
require("awful.widget.layout.horizontal")
require("awful.widget.layout.vertical")
require("awful.widget.layout.default")
--- Places the widgets in a grid.
-- It recognizes the following attributes in the encompassing table:
-- <p><code>cell_width</code>: Either a table containing column widths or
-- a number (width for all columns). In case its a number, <code>col_count</code> is mandatory.<br/>
-- <code>cell_height</code>: Either a table containing row heights or
-- a number (height for all rows). In case its a number, <code>row_count</code> is mandatory.<br/>
-- <code>col_count</code>: In case <code>cell_width</code> is a number, sets the number of columns.<br/>
-- <code>row_count</code>: In case <code>cell_height</code> is a number, sets the number of rows.<br/>
-- <code>col_gap</code>: Gap between columns in pixels. (optional)<br/>
-- <code>row_gap</code>: Gap between rows in pixels. (optional)<br/>
-- <code>resize</code>: Boolean indicating whether widgets should be resized to the size of the cell.
-- (optional)</p>
-- @param bounds The geometry of the bounds for the layout.
-- @param widgets The table of the widgets to layout. When layout function is placed in
-- a table, it is the table that is passed as this argument.
-- @param screen The screen of the wibox.
-- @return A table of geometries of all the widgets.
-- @name grid
-- @class function
grid = require("awful.widget.layout.grid")
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -0,0 +1,290 @@
-------------------------------------------------
-- @author Gregor Best <farhaven@googlemail.com>, Lukas Hrazky <lukkash@email.cz>
-- @copyright 2009 Gregor Best, Lukas Hrazky
-- @release @AWESOME_VERSION@
-------------------------------------------------
local setmetatable = setmetatable
local require = require
local ipairs = ipairs
local type = type
local insert = table.insert
local min = math.min
local max = math.max
local floor = math.floor
local util = require("awful.util")
local clone = util.table.clone
local margins = awful.widget.layout.margins
local layout = awful.widget.layout
local linear_common = {}
-- Calculates geometries for fixed layouts.
-- This generic function is used for all layouts exept flex.
-- It is written for horizontal layouts, but by using a special index 'idx' to access
-- all the geometry attributes, it is used for vertical layouts too. In that case, 'idx'
-- returns 'y' for 'x', 'width' for 'height', etc.
-- @param idx An index table that defines whether horzintal or vertical arrangement will be
-- calculated. See regular_index and switched_index tables.
-- @param bounds The geometry of the bounds for the layout.
-- @param widgets The table of the widgets to layout, can be nested.
-- @param screen The screen of the wibox.
-- @return A table of geometries of all the widgets.
function linear_common.fixed(idx, bounds, widgets, screen)
-- set bounds width and height to what is preset in the widgets table (if there is something)
-- we don't need to use idx here since we're treating both directions the same way
if widgets.width and widgets.width < bounds.width then
bounds.width = widgets.width
end
if widgets.height and widgets.height < bounds.height then
bounds.height = widgets.height
end
-- the table for the geometries which will be returned
-- we clone the bounds to the 'total' attribute. bounds are used to keep the free space
-- throughout this function. at the end, 'total' is modified to represent the space taken
-- by all widgets
local geometries = {total = clone(bounds)}
-- the height of the heighest widget, will be the height of the total space taken
local maxh = 0
for _, v in ipairs(widgets) do
if type(v) == "table" or type(v) == "widget" then
local m = margins[v]
-- we can shrink the bounds by the horizontal margins right now, they affect our free
-- space directly (vertical don't, they are different for every item in 'widgets')
bounds[idx.width] = bounds[idx.width] - m[idx.left] - m[idx.right]
bounds[idx.x] = bounds[idx.x] + m[idx.left]
if type(v) == "table" then
-- create new bounds for the table and shrink it by the vertical margins
local t_bounds = clone(bounds)
t_bounds[idx.height] = t_bounds[idx.height] - m[idx.top] - m[idx.bottom]
t_bounds[idx.y] = t_bounds[idx.y] + m[idx.top]
-- backup the width and height of the table so we can restore it
local t_width = v.width
local t_height = v.height
-- if the 'widgets' table has height set and the table itself doesn't, we set it
v[idx.height] = v[idx.height] or widgets[idx.height]
-- call the layout function recursively on this table
local layout = v.layout or layout.default
local g = layout(t_bounds, v, screen)
-- restore the table's original width and height
v.width = t_width
v.height = t_height
-- subtract the space taken by the table from our bounds - only if the taken space
-- is either on the left or on the right of our bounds (not in the middle)
if g.total[idx.x] == bounds[idx.x] or
g.total[idx.x] + g.total[idx.width] == bounds[idx.x] + bounds[idx.width] then
bounds[idx.width] = bounds[idx.width] - g.total[idx.width]
end
-- we only move the 'x' coord if the taken space is on the left side of our bounds
if g.total[idx.x] == bounds[idx.x] then
bounds[idx.x] = bounds[idx.x] + g.total[idx.width] + m[idx.right]
end
-- update the maximum height with this new table
maxh = max(maxh, g.total[idx.height] + m[idx.top] + m[idx.bottom])
-- insert all geometries from the table to our geometries
for _, w in ipairs(g) do
insert(geometries, w)
end
elseif type(v) == "widget" then
local g = {x = 0, y = 0, width = 0, height = 0}
if v.visible then
-- get the geometry of the widget
g = v:extents(screen)
-- resize to fit the height available if requested
if v.resize and g.width > 0 and g.height > 0 then
local ratio = g[idx.width] / g[idx.height]
g[idx.width] = floor(bounds[idx.height] * ratio)
g[idx.height] = bounds[idx.height]
end
-- set the coords, apply the top margin
g[idx.y] = bounds[idx.y] + m[idx.top]
g[idx.x] = bounds[idx.x]
-- limit the width of the widget to what's available
g[idx.width] = min(g[idx.width], bounds[idx.width])
-- if the 'widgets' table has height set, we set it to the widget
g[idx.height] = widgets[idx.height] and
(widgets[idx.height] - m[idx.top] - m[idx.bottom]) or g[idx.height]
-- limit the height of the widget to what's available
g[idx.height] = min(g[idx.height], bounds[idx.height] - m[idx.top] - m[idx.bottom])
-- subtract the space taken by the widget from our bounds
bounds[idx.width] = bounds[idx.width] - g[idx.width]
-- move bounds right by the widget's width
bounds[idx.x] = bounds[idx.x] + g[idx.width] + m[idx.right]
-- update the maximum height with height of this widget
maxh = max(maxh, g[idx.height] + m[idx.top] + m[idx.bottom])
end
insert(geometries, g)
end
end
end
-- calculate the total space taken by the widgets
geometries.total[idx.width] = geometries.total[idx.width] - bounds[idx.width]
geometries.total[idx.height] = maxh
-- if the bounds are on the left of what was empty in the beginning of this function,
-- we move the total to the right. this, however, most probably happened cos of rightleft layout
-- inside 'widgets', and only if there was nothing aligned to the left
if geometries.total[idx.x] == bounds[idx.x] then
geometries.total[idx.x] = geometries.total[idx.x] + bounds[idx.width]
end
return geometries
end
-- Calculates geometries for flex layouts.
-- This generic function is used for all flex layouts.
-- It is written for horizontal layouts, but by using special index 'idx' to access
-- all the geometry attributes, it is used for vertical layouts too. In that case, 'idx'
-- returns 'y' for 'x', 'width' for 'height', etc.
-- @param idx An index table that defines whether horzintal or vertical arrangement will be
-- calculated. See regular_index and switched_index tables.
-- @param bounds The geometry of the bounds for the layout.
-- @param widgets The table of the widgets to layout, can be nested.
-- @param screen The screen of the wibox.
-- @return A table of geometries of all the widgets.
function linear_common.flex(idx, bounds, widgets, screen)
-- set bounds width and height to what is preset in the widgets table (if there is something)
-- we don't need to use idx here since we're treating both directions the same way
if widgets.width and widgets.width < bounds.width then
bounds.width = widgets.width
end
if widgets.height and widgets.height < bounds.height then
bounds.height = widgets.height
end
-- the table for the geometries which will be returned
-- we clone the bounds to the 'total' attribute. bounds are used to keep the free space
-- throughout this function. at the end, 'total' is modified to represent the space taken
-- by all widgets
local geometries = {total = clone(bounds)}
-- the height of the heighest widget, will be the height of the total space taken
local maxh = 0
-- the number of widgets/tables in 'widgets' argument
local n = 0
-- get the gap or set to 0
local gap = widgets.gap or 0
-- count the widgets/tables
for _, v in ipairs(widgets) do
if type(v) == "table" or (type(v) == "widget" and v.visible) then
n = n + 1
end
end
-- calculate the width. these vars keep the floating numbers, while bounds keep the
-- rounded ones and are set from these on each iteration to ensure proper rounding
local width = (n > 0) and ((widgets[idx.width] or bounds[idx.width]) - gap * (n - 1)) / n or 0
local x = bounds[idx.x]
-- if max_size is set and it is lower than the calculated width, use it
width = (widgets.max_size and widgets.max_size < width) and widgets.max_size or width
for _, v in ipairs(widgets) do
-- someone give me freaking continue
if type(v) == "widget" and not v.visible then
insert(geometries, {x = 0, y = 0, width = 0, height = 0})
elseif type(v) == "widget" or type(v) == "table" then
-- do the floating magic, calculate real_width which will be set to the geometries
x = x + width
local real_width = floor(x - bounds[idx.x] + 0.5)
local m = margins[v]
if type(v) == "table" then
-- create new bounds for the table and shrink it by the margins
local t_bounds = {}
t_bounds[idx.x] = bounds[idx.x] + m[idx.left]
t_bounds[idx.y] = bounds[idx.y] + m[idx.top]
t_bounds[idx.width] = real_width - m[idx.left] - m[idx.right]
t_bounds[idx.height] = bounds[idx.height] - m[idx.top] - m[idx.bottom]
-- backup the width and height of the table so we can restore it
local t_width = v.width
local t_height = v.height
-- set the table's width so it can flex what's inside it
v[idx.width] = real_width
-- if the 'widgets' table has height set and the table itself doesn't, we set it
v[idx.height] = v[idx.height] or widgets[idx.height]
-- call the layout function recursively on this table
local layout = v.layout or layout.default
local g = layout(t_bounds, v, screen)
-- restore the table's original width and height
v.width = t_width
v.height = t_height
-- update the maximum height with this new table
maxh = max(maxh, g.total[idx.height] + m[idx.top] + m[idx.bottom])
for _, v in ipairs(g) do
insert(geometries, v)
end
elseif type(v) == "widget" then
g = v:extents(screen)
-- resize to fit the width available if requested
if v.resize and g.width > 0 and g.height > 0 then
local ratio = g[idx.height] / g[idx.width]
g[idx.height] = real_width * ratio
end
-- set the widget geometry
g[idx.x] = bounds[idx.x] + m[idx.left]
g[idx.width] = real_width - m[idx.left] - m[idx.right]
g[idx.y] = bounds[idx.y] + m[idx.top]
-- if the 'widgets' table has height set, we set it to the widget
g[idx.height] = widgets[idx.height] and
(widgets[idx.height] - m[idx.top] - m[idx.bottom]) or g[idx.height]
-- limit the height of the widget to what's available
g[idx.height] = min(g[idx.height], bounds[idx.height] - m[idx.top] - m[idx.bottom])
-- update the maximum height with height of this widget
maxh = max(maxh, g[idx.height] + m[idx.top] + m[idx.bottom])
insert(geometries, g)
end
-- update the bounds (move it to the right by the widget's width)
bounds[idx.width] = bounds[idx.width] - real_width - gap
x = x + gap
bounds[idx.x] = floor(x + 0.5)
end
end
-- we have total already from the cloned bounds, just set the height to what we got
geometries.total[idx.width] = geometries.total[idx.width] - bounds[idx.width]
geometries.total[idx.height] = maxh
return geometries
end
return linear_common
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80

View File

@ -1,101 +1,98 @@
-------------------------------------------------
-- @author Gregor Best <farhaven@googlemail.com>
-- @copyright 2009 Gregor Best
-- @author Gregor Best <farhaven@googlemail.com>, Lukas Hrazky <lukkash@email.cz>
-- @copyright 2009 Gregor Best, Lukas Hrazky
-- @release @AWESOME_VERSION@
-------------------------------------------------
-- Grab environment
local require = require
local ipairs = ipairs
local type = type
local table = table
local math = math
local pairs = pairs
local util = require("awful.util")
local default = require("awful.widget.layout.default")
local layout = require("awful.widget.layout")
local linear = require("awful.widget.layout.linear_common")
--- Vertical widget layout
--- Vertical widget layouts
module("awful.widget.layout.vertical")
function flex(bounds, widgets, screen)
local geometries = {
free = util.table.clone(bounds)
}
-- index used for vertical layouts
local idx = layout.switched_index
local y = 0
--- Places the widgets in a column, aligned to the top.
-- It recognizes the following attributes in the encompassing table:
-- <p><code>width</code>: Maximum width of the layout. If set, all widgets are resized to this width.
-- (optional)<br/>
-- <code>height</code>: Height of the layout. (optional)</p>
-- @param bounds The geometry of the bounds for the layout.
-- @param widgets The table of the widgets to layout. When layout function is placed in
-- a table, it is the table that is passed as this argument.
-- @param screen The screen of the wibox.
-- @return A table of geometries of all the widgets.
function topdown(bounds, widgets, screen)
return linear.fixed(idx, bounds, widgets, screen)
end
-- we are only interested in tables and widgets
local keys = util.table.keys_filter(widgets, "table", "widget")
local nelements = 0
for _, k in ipairs(keys) do
local v = widgets[k]
if type(v) == "table" then
nelements = nelements + 1
else
local e = v:extents()
if v.visible and e.width > 0 and e.height > 0 then
nelements = nelements + 1
--- Places the widgets in a column, aligned to the bottom.
-- It recognizes the following attributes in the encompassing table:
-- <p><code>width</code>: Maximum width of the layout. If set, all widgets are resized to this width.
-- (optional)<br/>
-- <code>height</code>: Height of the layout. (optional)</p>
-- @param bounds The geometry of the bounds for the layout.
-- @param widgets The table of the widgets to layout. When layout function is placed in
-- a table, it is the table that is passed as this argument.
-- @param screen The screen of the wibox.
-- @return A table of geometries of all the widgets.
function bottomup(bounds, widgets, screen)
local g = linear.fixed(idx, util.table.clone(bounds), widgets, screen)
for _, w in pairs(g) do
w.y = w.y + bounds.height - g.total.height
end
return g
end
end
if nelements == 0 then return geometries end
local height = math.floor(bounds.height / nelements)
for _, k in ipairs(keys) do
local v = widgets[k]
if type(v) == "table" then
local layout = v.layout or default
-- we need to modify the height a bit because vertical layouts always span the
-- whole height
nbounds = util.table.clone(bounds)
nbounds.height = height
local g = layout(nbounds, v, screen)
--- Places the widgets in a column, aligned to the center.
-- It recognizes the following attributes in the encompassing table:
-- <p><code>width</code>: Maximum width of the layout. If set, all widgets are resized to this width.
-- (optional)<br/>
-- <code>height</code>: Height of the layout. (optional)</p>
-- The widgets will be placed in the center of the free space that is left after all widgets
-- preceding the table with this layout were placed. The area covered by widgets with this layout
-- is not subtracted from the total free area (so the widgets "cover no space"). Therefore, if you
-- put anything after a table with center layout, the widgets may overlap.
-- @param bounds The geometry of the bounds for the layout.
-- @param widgets The table of the widgets to layout. When layout function is placed in
-- a table, it is the table that is passed as this argument.
-- @param screen The screen of the wibox.
-- @return A table of geometries of all the widgets.
function center(bounds, widgets, screen)
local g = linear.fixed(idx, util.table.clone(bounds), widgets, screen)
for _, w in ipairs(g) do
w.y = w.y + y
table.insert(geometries, w)
w.y = w.y + (bounds.height - g.total.height) / 2
end
y = y + height
elseif type(v) == "widget" then
local g
if v.visible then
g = v:extents(screen)
-- if height is set for widgets table, make total this wide,
-- otherwise move total.y to reflect new position
if widgets.height then
g.total.height = widgets.height
else
g = {
["width"] = 0,
["height"] = 0
}
g.total.y = g.total.y + (bounds.height - g.total.height) / 2
end
return g
end
g.ratio = 1
if g.height > 0 and g.width > 0 then
g.ratio = g.width / g.height
end
g.height = height
if v.resize then
g.width = g.height * g.ratio
end
g.width = math.min(g.width, bounds.width)
geometries.free.x = math.max(geometries.free.x, g.width)
g.x = 0
g.y = y
y = y + g.height
bounds.height = bounds.height - g.height
table.insert(geometries, g)
end
--- Places the widgets in a column, making them equally high.
-- It recognizes the following attributes in the encompassing table:
-- <p><code>width</code>: Maximum width of the layout. If set, all widgets are resized to this width.
-- (optional)<br/>
-- <code>height</code>: Height of the layout. (optional)<br/>
-- <code>max_size</code>: Maximum height of one item. If not set, widgets' heights are set so that they always
-- cover all available space. (optional)<br/>
-- <code>gap</code>: Gap between the widgets. (optional)</p>
-- @param bounds The geometry of the bounds for the layout.
-- @param widgets The table of the widgets to layout. When layout function is placed in
-- a table, it is the table that is passed as this argument.
-- @param screen The screen of the wibox.
-- @return A table of geometries of all the widgets.
function flex(bounds, widgets, screen)
return linear.flex(idx, bounds, widgets, screen)
end
local maxw = 0
local maxx = 0
for _, v in ipairs(geometries) do
if v.width > maxw then maxw = v.width end
if v.x > maxx then maxx = v.x end
end
geometries.free.width = geometries.free.width - maxw
geometries.free.x = geometries.free.x + maxw
geometries.free.height = nelements * height
geometries.free.y = 0
return geometries
end
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80