diff --git a/docs/03-declarative-layout.md b/docs/03-declarative-layout.md index be88a2153..e29308a2d 100644 --- a/docs/03-declarative-layout.md +++ b/docs/03-declarative-layout.md @@ -17,7 +17,7 @@ Code: { -- Add a background color/pattern for my_third_widget my_third_widget, bg = beautiful.bg_focus, - widget = wibox.widget.background, + widget = wibox.container.background, }, layout = wibox.layout.fixed.horizontal, } @@ -34,7 +34,7 @@ declarative layout, or `nil`. * 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) +* Add a `wibox.container.background` (for visualization) Code: @@ -48,7 +48,7 @@ Code: widget = wibox.widget.textbox }, bg = "#ff0000", - widget = wibox.widget.background, + widget = wibox.container.background, }, width = 300, strategy = "min", @@ -62,13 +62,13 @@ Code: widget = wibox.widget.textbox }, bg = "#0000ff", - widget = wibox.widget.background + widget = wibox.container.background }, left = 10, right = 10, top = 1, bottom = 2, - layout = wibox.layout.margin + layout = wibox.container.margin }, layout = wibox.layout.fixed.horizontal, } diff --git a/docs/config.ld b/docs/config.ld index 6d4f140d5..a6d1a629d 100644 --- a/docs/config.ld +++ b/docs/config.ld @@ -82,6 +82,7 @@ file = { '../lib/gears/init.lua', '../lib/wibox/layout/init.lua', '../lib/wibox/widget/init.lua', + '../lib/wibox/container/init.lua', -- Ignore some parts of the widget library '../lib/awful/widget/init.lua', diff --git a/lib/awful/hotkeys_popup/widget.lua b/lib/awful/hotkeys_popup/widget.lua index 85d61076c..3a9f0be4c 100644 --- a/lib/awful/hotkeys_popup/widget.lua +++ b/lib/awful/hotkeys_popup/widget.lua @@ -195,7 +195,7 @@ local function group_label(group, color) ) ) ) - local margin = wibox.layout.margin() + local margin = wibox.container.margin() margin:set_widget(textbox) margin:set_top(widget.group_margin) return margin @@ -315,7 +315,7 @@ local function create_wibox(s, available_groups) else available_width_px = available_width_px - item.max_width end - local column_margin = wibox.layout.margin() + local column_margin = wibox.container.margin() column_margin:set_widget(item.layout) column_margin:set_left(widget.group_margin) columns:add(column_margin) diff --git a/lib/awful/menu.lua b/lib/awful/menu.lua index 0dc622696..663b77042 100644 --- a/lib/awful/menu.lua +++ b/lib/awful/menu.lua @@ -384,7 +384,7 @@ function menu:add(args, index) item.width = item.width or theme.width item.height = item.height or theme.height wibox.widget.base.check_widget(item.widget) - item._background = wibox.widget.background() + item._background = wibox.container.background() item._background:set_widget(item.widget) item._background:set_fg(item.theme.fg_normal) item._background:set_bg(item.theme.bg_normal) @@ -516,7 +516,7 @@ function menu.entry(parent, args) -- luacheck: no unused args end, 1)) -- Set icon if needed local icon, iconbox - local margin = wibox.layout.margin() + local margin = wibox.container.margin() margin:set_widget(label) if args.icon then icon = surface.load(args.icon) diff --git a/lib/awful/tooltip.lua b/lib/awful/tooltip.lua index 6c37aeadb..57b376999 100644 --- a/lib/awful/tooltip.lua +++ b/lib/awful/tooltip.lua @@ -48,7 +48,7 @@ local a_placement = require("awful.placement") local abutton = require("awful.button") local beautiful = require("beautiful") local textbox = require("wibox.widget.textbox") -local background = require("wibox.widget.background") +local background = require("wibox.container.background") local dpi = require("beautiful").xresources.apply_dpi local setmetatable = setmetatable local ipairs = ipairs @@ -265,7 +265,7 @@ tooltip.new = function(args) -- Add margin. local m_lr = args.margin_leftright or dpi(5) local m_tb = args.margin_topbottom or dpi(3) - self.marginbox = wibox.layout.margin(self.background, m_lr, m_lr, m_tb, m_tb) + self.marginbox = wibox.container.margin(self.background, m_lr, m_lr, m_tb, m_tb) -- Add tooltip to objects if args.objects then diff --git a/lib/awful/widget/common.lua b/lib/awful/widget/common.lua index cfe18b7d2..78f571723 100644 --- a/lib/awful/widget/common.lua +++ b/lib/awful/widget/common.lua @@ -60,9 +60,9 @@ function common.list_update(w, buttons, label, data, objects) else ib = wibox.widget.imagebox() tb = wibox.widget.textbox() - bgb = wibox.widget.background() - tbm = wibox.layout.margin(tb, dpi(4), dpi(4)) - ibm = wibox.layout.margin(ib, dpi(4)) + bgb = wibox.container.background() + tbm = wibox.container.margin(tb, dpi(4), dpi(4)) + ibm = wibox.container.margin(ib, dpi(4)) l = wibox.layout.fixed.horizontal() -- All of this is added in a fixed widget diff --git a/lib/gears/object.lua b/lib/gears/object.lua index 85bfcc3c2..c65582ed8 100644 --- a/lib/gears/object.lua +++ b/lib/gears/object.lua @@ -230,7 +230,7 @@ end -- -- @tparam[opt=2] integer level Level for `debug.getinfo(level, "S")`. -- Typically 2 or 3. --- @treturn string The module name, e.g. "wibox.widget.background". +-- @treturn string The module name, e.g. "wibox.container.background". function object.modulename(level) return debug.getinfo(level, "S").source:gsub(".*/lib/", ""):gsub("/", "."):gsub("%.lua", "") end diff --git a/lib/naughty/core.lua b/lib/naughty/core.lua index 24f7c9606..3aa745589 100644 --- a/lib/naughty/core.lua +++ b/lib/naughty/core.lua @@ -522,7 +522,7 @@ function naughty.notify(args) -- create textbox local textbox = wibox.widget.textbox() - local marginbox = wibox.layout.margin() + local marginbox = wibox.container.margin() marginbox:set_margins(margin) marginbox:set_widget(textbox) textbox:set_valign("middle") @@ -538,7 +538,7 @@ function naughty.notify(args) if actions then for action, callback in pairs(actions) do local actiontextbox = wibox.widget.textbox() - local actionmarginbox = wibox.layout.margin() + local actionmarginbox = wibox.container.margin() actionmarginbox:set_margins(margin) actionmarginbox:set_widget(actiontextbox) actiontextbox:set_valign("middle") @@ -582,7 +582,7 @@ function naughty.notify(args) -- if we have an icon, use it if icon then iconbox = wibox.widget.imagebox() - iconmargin = wibox.layout.margin(iconbox, margin, margin, margin, margin) + iconmargin = wibox.container.margin(iconbox, margin, margin, margin, margin) if icon_size then local scaled = cairo.ImageSurface(cairo.Format.ARGB32, icon_size, icon_size) local cr = cairo.Context(scaled) diff --git a/lib/wibox/container/background.lua b/lib/wibox/container/background.lua new file mode 100644 index 000000000..7c0ac815a --- /dev/null +++ b/lib/wibox/container/background.lua @@ -0,0 +1,224 @@ +--------------------------------------------------------------------------- +-- A container capable of changing the background color, foreground color +-- widget shape. +-- @author Uli Schlachter +-- @copyright 2010 Uli Schlachter +-- @release @AWESOME_VERSION@ +-- @classmod wibox.container.background +--------------------------------------------------------------------------- + +local base = require("wibox.widget.base") +local color = require("gears.color") +local surface = require("gears.surface") +local beautiful = require("beautiful") +local cairo = require("lgi").cairo +local setmetatable = setmetatable +local pairs = pairs +local type = type +local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1) + +local background = { mt = {} } + +-- Draw this widget +function background:draw(context, cr, width, height) + if not self.widget or not self.widget.visible then + return + end + + -- Keep the shape path in case there is a border + self._path = nil + + if self._shape then + -- Only add the offset if there is something to draw + local offset = ((self._shape_border_width and self._shape_border_color) + and self._shape_border_width or 0) / 2 + + cr:translate(offset, offset) + self._shape(cr, width - 2*offset, height - 2*offset, unpack(self._shape_args or {})) + cr:translate(-offset, -offset) + self._path = cr:copy_path() + cr:clip() + end + + if self.background then + cr:set_source(self.background) + cr:paint() + end + if self.bgimage then + if type(self.bgimage) == "function" then + self.bgimage(context, cr, width, height,unpack(self.bgimage_args)) + else + local pattern = cairo.Pattern.create_for_surface(self.bgimage) + cr:set_source(pattern) + cr:paint() + end + end + +end + +-- Draw the border +function background:after_draw_children(_, cr) + -- Draw the border + if self._path and self._shape_border_width and self._shape_border_width > 0 then + cr:append_path(self._path) + cr:set_source(color(self._shape_border_color or self.foreground or beautiful.fg_normal)) + + cr:set_line_width(self._shape_border_width) + cr:stroke() + self._path = nil + end +end + +-- Prepare drawing the children of this widget +function background:before_draw_children(_, cr) + if self.foreground then + cr:set_source(self.foreground) + end + + -- Clip the shape + if self._path and self._shape_clip then + cr:append_path(self._path) + cr:clip() + end +end + +-- Layout this widget +function background:layout(_, width, height) + if self.widget then + return { base.place_widget_at(self.widget, 0, 0, width, height) } + end +end + +-- Fit this widget into the given area +function background:fit(context, width, height) + if not self.widget then + return 0, 0 + end + + return base.fit_widget(self, context, self.widget, width, height) +end + +--- Set the widget that is drawn on top of the background +-- @tparam widget widget The widget to be disaplayed inside of the background +-- area +function background:set_widget(widget) + if widget then + base.check_widget(widget) + end + self.widget = widget + self:emit_signal("widget::layout_changed") +end + +-- Get children element +-- @treturn table The children +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:set_widget(children[1]) +end + +--- Set the background to use. +--@DOC_wibox_widget_background_bg_EXAMPLE@ +-- @param bg A color string, pattern or gradient (see `gears.color`) +function background:set_bg(bg) + if bg then + self.background = color(bg) + else + self.background = nil + end + self:emit_signal("widget::redraw_needed") +end + +--- Set the foreground to use. +--@DOC_wibox_widget_background_fg_EXAMPLE@ +-- @param fg A color string, pattern or gradient (see `gears.color`) +function background:set_fg(fg) + if fg then + self.foreground = color(fg) + else + self.foreground = nil + end + self:emit_signal("widget::redraw_needed") +end + +--- Set the background shape. +-- +-- Any other arguments will be passed to the shape function +--@DOC_wibox_widget_background_shape_EXAMPLE@ +-- @param shape A function taking a context, width and height as arguments +function background:set_shape(shape, ...) + self._shape = shape + self._shape_args = {...} + self:emit_signal("widget::redraw_needed") +end + +--- When a `shape` is set, also draw a border. +-- +-- See `wibox.container.background.set_shape` for an usage example. +-- @tparam number width The border width +function background:set_shape_border_width(width) + self._shape_border_width = width + self:emit_signal("widget::redraw_needed") +end + +--- When a `shape` is set, also draw a border. +-- +-- See `wibox.container.background.set_shape` for an usage example. +-- @param[opt=self.foreground] fg The border color, pattern or gradient +function background:set_shape_border_color(fg) + self._shape_border_color = fg + self:emit_signal("widget::redraw_needed") +end + +--- When a `shape` is set, make sure nothing is drawn outside of it. +--@DOC_wibox_widget_background_clip_EXAMPLE@ +-- @tparam boolean value If the shape clip is enable +function background:set_shape_clip(value) + self._shape_clip = value + self:emit_signal("widget::redraw_needed") +end + +--- Set the background image to use +-- If `image` is a function, it will be called with `(context, cr, width, height)` +-- as arguments. Any other arguments passed to this method will be appended. +-- @param image A background image or a function +function background:set_bgimage(image, ...) + self.bgimage = type(image) == "function" and image or surface.load(image) + self.bgimage_args = {...} + self:emit_signal("widget::redraw_needed") +end + +--- Returns a new background layout. A background layout applies a background +-- and foreground color to another widget. +-- @param[opt] widget The widget to display. +-- @param[opt] bg The background to use for that widget. +-- @param[opt] shape A `gears.shape` compatible shape function +local function new(widget, bg, shape) + local ret = base.make_widget() + + for k, v in pairs(background) do + if type(v) == "function" then + ret[k] = v + end + end + + ret._shape = shape + + ret:set_widget(widget) + ret:set_bg(bg) + + return ret +end + +function background.mt:__call(...) + return new(...) +end + +return setmetatable(background, background.mt) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/wibox/container/constraint.lua b/lib/wibox/container/constraint.lua new file mode 100644 index 000000000..deeec7f6f --- /dev/null +++ b/lib/wibox/container/constraint.lua @@ -0,0 +1,140 @@ +--------------------------------------------------------------------------- +-- @author Lukáš Hrázký +-- @copyright 2012 Lukáš Hrázký +-- @release @AWESOME_VERSION@ +-- @classmod wibox.container.constraint +--------------------------------------------------------------------------- + +local pairs = pairs +local type = type +local setmetatable = setmetatable +local base = require("wibox.widget.base") +local math = math + +local constraint = { mt = {} } + +--- Layout a constraint layout +function constraint:layout(_, width, height) + if self.widget then + return { base.place_widget_at(self.widget, 0, 0, width, height) } + end +end + +--- Fit a constraint layout into the given space +function constraint:fit(context, width, height) + local w, h + if self.widget then + w = self._strategy(width, self._width) + h = self._strategy(height, self._height) + + w, h = base.fit_widget(self, context, self.widget, w, h) + else + w, h = 0, 0 + end + + w = self._strategy(w, self._width) + h = self._strategy(h, self._height) + + return w, h +end + +--- Set the widget that this layout adds a constraint on. +function constraint:set_widget(widget) + self.widget = widget + self:emit_signal("widget::layout_changed") +end + +--- Get the number of children element +-- @treturn table The children +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:set_widget(children[1]) +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) + local func = { + min = function(real_size, limit) + return limit and math.max(limit, real_size) or real_size + end, + max = function(real_size, limit) + return limit and math.min(limit, real_size) or real_size + end, + exact = function(real_size, limit) + return limit or real_size + end + } + + if not func[val] then + error("Invalid strategy for constraint layout: " .. tostring(val)) + end + + self._strategy = func[val] + self:emit_signal("widget::layout_changed") +end + +--- Set the maximum width to val. nil for no width limit. +function constraint:set_width(val) + self._width = val + self:emit_signal("widget::layout_changed") +end + +--- Set the maximum height to val. nil for no height limit. +function constraint:set_height(val) + self._height = val + self:emit_signal("widget::layout_changed") +end + +--- Reset this layout. The widget will be unreferenced, strategy set to "max" +-- and the constraints set to nil. +function constraint:reset() + self._width = nil + self._height = nil + self:set_strategy("max") + self:set_widget(nil) +end + +--- Returns a new constraint layout. This layout will constraint the size of a +-- widget according to the strategy. Note that this will only work for layouts +-- that respect the widget's size, eg. fixed layout. In layouts that don't +-- (fully) respect widget's requested size, the inner widget still might get +-- drawn with a size that does not fit the constraint, eg. in flex layout. +-- @param[opt] widget A widget to use. +-- @param[opt] strategy How to constraint the size. 'max' (default), 'min' or +-- 'exact'. +-- @param[opt] width The maximum width of the widget. nil for no limit. +-- @param[opt] height The maximum height of the widget. nil for no limit. +local function new(widget, strategy, width, height) + local ret = base.make_widget() + + for k, v in pairs(constraint) do + if type(v) == "function" then + ret[k] = v + end + end + + ret:set_strategy(strategy or "max") + ret:set_width(width) + ret:set_height(height) + + if widget then + ret:set_widget(widget) + end + + return ret +end + +function constraint.mt:__call(...) + return new(...) +end + +return setmetatable(constraint, constraint.mt) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/wibox/container/init.lua b/lib/wibox/container/init.lua new file mode 100644 index 000000000..b5cd7b378 --- /dev/null +++ b/lib/wibox/container/init.lua @@ -0,0 +1,20 @@ +--------------------------------------------------------------------------- +--- Collection of containers that can be used in widget boxes +-- +-- @author Uli Schlachter +-- @copyright 2010 Uli Schlachter +-- @release @AWESOME_VERSION@ +-- @classmod wibox.container +--------------------------------------------------------------------------- +local base = require("wibox.widget.base") + +return setmetatable({ + rotate = require("wibox.container.rotate"); + margin = require("wibox.container.margin"); + mirror = require("wibox.container.mirror"); + constraint = require("wibox.container.constraint"); + scroll = require("wibox.container.scroll"); + background = require("wibox.container.background"); +}, {__call = function(_, args) return base.make_widget_declarative(args) end}) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/wibox/container/margin.lua b/lib/wibox/container/margin.lua new file mode 100644 index 000000000..4f47c51da --- /dev/null +++ b/lib/wibox/container/margin.lua @@ -0,0 +1,198 @@ +--------------------------------------------------------------------------- +-- @author Uli Schlachter +-- @copyright 2010 Uli Schlachter +-- @release @AWESOME_VERSION@ +-- @classmod wibox.container.margin +--------------------------------------------------------------------------- + +local pairs = pairs +local type = type +local setmetatable = setmetatable +local base = require("wibox.widget.base") +local gcolor = require("gears.color") +local cairo = require("lgi").cairo + +local margin = { mt = {} } + +-- Draw a margin layout +function margin:draw(_, cr, width, height) + local x = self.left + local y = self.top + local w = self.right + local h = self.bottom + local color = self.color + + if not self.widget or width <= x + w or height <= y + h then + return + end + + if color then + cr:set_source(color) + cr:rectangle(0, 0, width, height) + cr:rectangle(x, y, width - x - w, height - y - h) + cr:set_fill_rule(cairo.FillRule.EVEN_ODD) + cr:fill() + end +end + +-- Layout a margin layout +function margin:layout(_, width, height) + if self.widget then + local x = self.left + local y = self.top + local w = self.right + local h = self.bottom + + return { base.place_widget_at(self.widget, x, y, width - x - w, height - y - h) } + end +end + +-- Fit a margin layout into the given space +function margin:fit(context, width, height) + local extra_w = self.left + self.right + local extra_h = self.top + self.bottom + local w, h = 0, 0 + if self.widget then + w, h = base.fit_widget(self, context, self.widget, width - extra_w, height - extra_h) + end + + if self._draw_empty == false and (w == 0 or h == 0) then + return 0, 0 + end + + return w + extra_w, h + extra_h +end + +--- Set the widget that this layout adds a margin on. +function margin:set_widget(widget) + if widget then + base.check_widget(widget) + end + self.widget = widget + self:emit_signal("widget::layout_changed") +end + +-- Get the number of children element +-- @treturn table The children +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:set_widget(children[1]) +end + +--- Set all the margins to val. +-- @tparam number val The margin value +function margin:set_margins(val) + if self.left == val and + self.right == val and + self.top == val and + self.bottom == val then + return + end + + self.left = val + self.right = val + self.top = val + self.bottom = val + self:emit_signal("widget::layout_changed") +end + +--- Set the margins color to create a border. +-- @param color A color used to fill the margin. +function margin:set_color(color) + self.color = color and gcolor(color) + self:emit_signal("widget::redraw_needed") +end + +--- Draw the margin even if the content size is 0x0 (default: true) +-- @tparam boolean draw_empty Draw nothing is content is 0x0 or draw the margin anyway +function margin:set_draw_empty(draw_empty) + self._draw_empty = draw_empty + self:emit_signal("widget::layout_changed") +end + +--- Reset this layout. The widget will be unreferenced, the margins set to 0 +-- and the color erased +function margin:reset() + self:set_widget(nil) + self:set_margins(0) + self:set_color(nil) +end + +--- Set the left margin that this layout adds to its widget. +-- @param layout The layout you are modifying. +-- @param margin The new margin to use. +-- @name set_left +-- @class function + +--- Set the right margin that this layout adds to its widget. +-- @param layout The layout you are modifying. +-- @param margin The new margin to use. +-- @name set_right +-- @class function + +--- Set the top margin that this layout adds to its widget. +-- @param layout The layout you are modifying. +-- @param margin The new margin to use. +-- @name set_top +-- @class function + +--- Set the bottom margin that this layout adds to its widget. +-- @param layout The layout you are modifying. +-- @param margin The new margin to use. +-- @name set_bottom +-- @class function + +-- Create setters for each direction +for _, v in pairs({ "left", "right", "top", "bottom" }) do + margin["set_" .. v] = function(layout, val) + if layout[v] == val then return end + layout[v] = val + layout:emit_signal("widget::layout_changed") + end +end + +--- Returns a new margin layout. +-- @param[opt] widget A widget to use. +-- @param[opt] left A margin to use on the left side of the widget. +-- @param[opt] right A margin to use on the right side of the widget. +-- @param[opt] top A margin to use on the top side of the widget. +-- @param[opt] bottom A margin to use on the bottom side of the widget. +-- @param[opt] color A color for the margins. +-- @param[opt] draw_empty whether or not to draw the margin when the content is empty +local function new(widget, left, right, top, bottom, color, draw_empty) + local ret = base.make_widget() + + for k, v in pairs(margin) do + if type(v) == "function" then + ret[k] = v + end + end + + ret:set_left(left or 0) + ret:set_right(right or 0) + ret:set_top(top or 0) + ret:set_bottom(bottom or 0) + ret:set_draw_empty(draw_empty) + + ret:set_color(color) + + if widget then + ret:set_widget(widget) + end + + return ret +end + +function margin.mt:__call(...) + return new(...) +end + +return setmetatable(margin, margin.mt) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/wibox/container/mirror.lua b/lib/wibox/container/mirror.lua new file mode 100644 index 000000000..7bacfaa24 --- /dev/null +++ b/lib/wibox/container/mirror.lua @@ -0,0 +1,127 @@ +--------------------------------------------------------------------------- +-- @author dodo +-- @copyright 2012 dodo +-- @release @AWESOME_VERSION@ +-- @classmod wibox.container.mirror +--------------------------------------------------------------------------- + +local type = type +local error = error +local pairs = pairs +local ipairs = ipairs +local setmetatable = setmetatable +local base = require("wibox.widget.base") +local matrix = require("gears.matrix") + +local mirror = { mt = {} } + +--- Layout this layout +function mirror:layout(_, width, height) + if not self.widget then return end + + local m = matrix.identity + local t = { x = 0, y = 0 } -- translation + local s = { x = 1, y = 1 } -- scale + if self.horizontal then + t.x = width + s.x = -1 + end + if self.vertical then + t.y = height + s.y = -1 + end + m = m:translate(t.x, t.y) + m = m:scale(s.x, s.y) + + return { base.place_widget_via_matrix(self.widget, m, width, height) } +end + +--- Fit this layout into the given area +function mirror:fit(context, ...) + if not self.widget then + return 0, 0 + end + return base.fit_widget(self, context, self.widget, ...) +end + +--- Set the widget that this layout mirrors. +-- @param widget The widget to mirror +function mirror:set_widget(widget) + if widget then + base.check_widget(widget) + end + self.widget = widget + self:emit_signal("widget::layout_changed") +end + +--- Get the number of children element +-- @treturn table The children +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:set_widget(children[1]) +end + +--- Reset this layout. The widget will be removed and the axes reset. +function mirror:reset() + self.horizontal = false + self.vertical = false + self:set_widget(nil) +end + +--- Set the reflection of this mirror layout. +-- @param reflection a table which contains new values for horizontal and/or vertical (booleans) +function mirror:set_reflection(reflection) + if type(reflection) ~= 'table' then + error("Invalid type of reflection for mirror layout: " .. + type(reflection) .. " (should be a table)") + end + for _, ref in ipairs({"horizontal", "vertical"}) do + if reflection[ref] ~= nil then + self[ref] = reflection[ref] + end + end + self:emit_signal("widget::layout_changed") +end + +--- Get the reflection of this mirror layout. +-- @return a table of booleans with the keys "horizontal", "vertical". +function mirror:get_reflection() + return { horizontal = self.horizontal, vertical = self.vertical } +end + +--- Returns a new mirror layout. A mirror layout mirrors a given widget. Use +-- :set_widget() to set the widget and +-- :set_horizontal() and :set_vertical() for the direction. +-- horizontal and vertical are by default false which doesn't change anything. +-- @param[opt] widget The widget to display. +-- @param[opt] reflection A table describing the reflection to apply. +local function new(widget, reflection) + local ret = base.make_widget() + ret.horizontal = false + ret.vertical = false + + for k, v in pairs(mirror) do + if type(v) == "function" then + ret[k] = v + end + end + + ret:set_widget(widget) + ret:set_reflection(reflection or {}) + + return ret +end + +function mirror.mt:__call(...) + return new(...) +end + +return setmetatable(mirror, mirror.mt) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/wibox/container/rotate.lua b/lib/wibox/container/rotate.lua new file mode 100644 index 000000000..fb3c434bf --- /dev/null +++ b/lib/wibox/container/rotate.lua @@ -0,0 +1,137 @@ +--------------------------------------------------------------------------- +-- @author Uli Schlachter +-- @copyright 2010 Uli Schlachter +-- @release @AWESOME_VERSION@ +-- @classmod wibox.container.rotate +--------------------------------------------------------------------------- + +local error = error +local pairs = pairs +local pi = math.pi +local type = type +local setmetatable = setmetatable +local tostring = tostring +local base = require("wibox.widget.base") +local matrix = require("gears.matrix") + +local rotate = { mt = {} } + +local function transform(layout, width, height) + local dir = layout:get_direction() + if dir == "east" or dir == "west" then + return height, width + end + return width, height +end + +--- Layout this layout +function rotate:layout(_, width, height) + if not self.widget or not self.widget.visible then + return + end + + local dir = self:get_direction() + + local m = matrix.identity + if dir == "west" then + m = m:rotate(pi / 2) + m = m:translate(0, -width) + elseif dir == "south" then + m = m:rotate(pi) + m = m:translate(-width, -height) + elseif dir == "east" then + m = m:rotate(3 * pi / 2) + m = m:translate(-height, 0) + end + + -- Since we rotated, we might have to swap width and height. + -- transform() does that for us. + return { base.place_widget_via_matrix(self.widget, m, transform(self, width, height)) } +end + +--- Fit this layout into the given area +function rotate:fit(context, width, height) + if not self.widget then + return 0, 0 + end + return transform(self, base.fit_widget(self, context, self.widget, transform(self, width, height))) +end + +--- Set the widget that this layout rotates. +function rotate:set_widget(widget) + if widget then + base.check_widget(widget) + end + self.widget = widget + self:emit_signal("widget::layout_changed") +end + +--- Get the number of children element +-- @treturn table The children +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:set_widget(children[1]) +end + +--- Reset this layout. The widget will be removed and the rotation reset. +function rotate:reset() + self.direction = nil + self:set_widget(nil) +end + +--- Set the direction of this rotating layout. Valid values are "north", "east", +-- "south" and "west". On an invalid value, this function will throw an error. +function rotate:set_direction(dir) + local allowed = { + north = true, + east = true, + south = true, + west = true + } + + if not allowed[dir] then + error("Invalid direction for rotate layout: " .. tostring(dir)) + end + + self.direction = dir + self:emit_signal("widget::layout_changed") +end + +--- Get the direction of this rotating layout +function rotate:get_direction() + return self.direction or "north" +end + +--- Returns a new rotate layout. A rotate layout rotates a given widget. Use +-- :set_widget() to set the widget and :set_direction() for the direction. +-- The default direction is "north" which doesn't change anything. +-- @param[opt] widget The widget to display. +-- @param[opt] dir The direction to rotate to. +local function new(widget, dir) + local ret = base.make_widget() + + for k, v in pairs(rotate) do + if type(v) == "function" then + ret[k] = v + end + end + + ret:set_widget(widget) + ret:set_direction(dir or "north") + + return ret +end + +function rotate.mt:__call(...) + return new(...) +end + +return setmetatable(rotate, rotate.mt) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/wibox/container/scroll.lua b/lib/wibox/container/scroll.lua new file mode 100644 index 000000000..b7433d0a5 --- /dev/null +++ b/lib/wibox/container/scroll.lua @@ -0,0 +1,509 @@ +--------------------------------------------------------------------------- +-- @author Uli Schlachter (based on ideas from Saleur Geoffrey) +-- @copyright 2015 Uli Schlachter +-- @release @AWESOME_VERSION@ +-- @classmod wibox.container.scroll +--------------------------------------------------------------------------- + +local cache = require("gears.cache") +local timer = require("gears.timer") +local hierarchy = require("wibox.hierarchy") +local base = require("wibox.widget.base") +local lgi = require("lgi") +local GLib = lgi.GLib + +local scroll = {} +local scroll_mt = { __index = scroll } +local _need_scroll_redraw + +-- "Strip" a context so that we can use it for our own drawing +local function cleanup_context(context) + local skip = { wibox = true, drawable = true, client = true, position = true } + local res = {} + for k, v in pairs(context) do + if not skip[k] then + res[k] = v + end + end + return res +end + +-- Create a hierarchy (and some more stuff) for drawing the given widget. This +-- allows "some stuff" to be re-used instead of re-created all the time. +local hierarchy_cache = cache.new(function(context, widget, width, height) + context = cleanup_context(context) + local layouts = setmetatable({}, { __mode = "k" }) + + -- Create a widget hierarchy and update when needed + local hier + local function do_pending_updates(layout) + layouts[layout] = true + hier:update(context, widget, width, height, nil) + end + local function emit(signal) + -- Make the scroll layouts redraw + for w in pairs(layouts) do + w:emit_signal(signal) + end + end + local function redraw_callback() + emit("widget::redraw_needed") + end + local function layout_callback() + emit("widget::redraw_needed") + emit("widget::layout_changed") + end + hier = hierarchy.new(context, widget, width, height, redraw_callback, layout_callback, nil) + + return hier, do_pending_updates, context +end) + +--- Calculate all the information needed for scrolling. +-- @param self The instance of the scrolling layout. +-- @param context A widget context under which we are fit/drawn. +-- @param width The available width +-- @param height The available height +-- @return A table with the following entries +-- @field fit_width The width that should be returned from :fit +-- @field fit_height The height that should be returned from :fit +-- @field surface_width The width for showing the child widget +-- @field surface_height The height for showing the child widget +-- @field first_x The x offset for drawing the child the first time +-- @field first_y The y offset for drawing the child the first time +-- @field[opt] second_x The x offset for drawing the child the second time +-- @field[opt] second_y The y offset for drawing the child the second time +-- @field hierarchy The wibox.hierarchy instance representing "everything" +-- @field context The widget context for drawing the hierarchy +local function calculate_info(self, context, width, height) + local result = {} + assert(self.widget) + + -- First, get the size of the widget (and the size of extra space) + local surface_width, surface_height = width, height + local extra_width, extra_height, extra = 0, 0, self.expand and self.extra_space or 0 + local w, h + if self.dir == "h" then + w, h = base.fit_widget(self, context, self.widget, self.space_for_scrolling, height) + surface_width = w + extra_width = extra + else + w, h = base.fit_widget(self, context, self.widget, width, self.space_for_scrolling) + surface_height = h + extra_height = extra + end + result.fit_width, result.fit_height = w, h + if self.dir == "h" then + if self.max_size then + result.fit_width = math.min(w, self.max_size) + end + else + if self.max_size then + result.fit_height = math.min(h, self.max_size) + end + end + if w > width or h > height then + -- There is less space available than we need, we have to scroll + _need_scroll_redraw(self) + + surface_width, surface_height = surface_width + extra_width, surface_height + extra_height + + local x, y = 0, 0 + local function get_scroll_offset(size, visible_size) + return self.step_function(self.timer:elapsed(), size, visible_size, self.speed, self.extra_space) + end + if self.dir == "h" then + x = -get_scroll_offset(surface_width - extra, width) + else + y = -get_scroll_offset(surface_height - extra, height) + end + result.first_x, result.first_y = x, y + -- Was the extra space already included elsewhere? + local extra_spacer = self.expand and 0 or self.extra_space + if self.dir == "h" then + x = x + surface_width + extra_spacer + else + y = y + surface_height + extra_spacer + end + result.second_x, result.second_y = x, y + else + result.first_x, result.first_y = 0, 0 + end + result.surface_width, result.surface_height = surface_width, surface_height + + -- Get the hierarchy and subscribe ourselves to updates + local hier, do_pending_updates, ctx = hierarchy_cache:get(context, + self.widget, surface_width, surface_height) + result.hierarchy = hier + result.context = ctx + do_pending_updates(self) + + return result +end + +--- Draw this scrolling layout. +-- @param context The context in which we are drawn. +-- @param cr The cairo context to draw to. +-- @param width The available width. +-- @param height The available height. +function scroll:draw(context, cr, width, height) + if not self.widget then + return + end + + local info = calculate_info(self, context, width, height) + + -- Draw the first instance of the child + cr:save() + cr:translate(info.first_x, info.first_y) + cr:rectangle(0, 0, info.surface_width, info.surface_height) + cr:clip() + info.hierarchy:draw(info.context, cr) + cr:restore() + + -- If there is one, draw the second instance (same code as above, minus the + -- clip) + if info.second_x and info.second_y then + cr:translate(info.second_x, info.second_y) + cr:rectangle(0, 0, info.surface_width, info.surface_height) + cr:clip() + info.hierarchy:draw(info.context, cr) + end +end + +--- Fit the scroll layout into the given space. +-- @param context The context in which we are fit. +-- @param width The available width. +-- @param height The available height. +function scroll:fit(context, width, height) + if not self.widget then + return 0, 0 + end + local info = calculate_info(self, context, width, height) + return info.fit_width, info.fit_height +end + +-- Internal function used for triggering redraws for scrolling. +-- The purpose is to start a timer for redrawing the widget for scrolling. +-- Redrawing works by simply emitting the `widget::redraw_needed` signal. +-- Pausing is implemented in this function: We just don't start a timer. +-- This function must be idempotent (calling it multiple times right after +-- another does not make a difference). +_need_scroll_redraw = function(self) + if not self.paused and not self.scroll_timer then + self.scroll_timer = timer.start_new(1 / self.fps, function() + self.scroll_timer = nil + self:emit_signal("widget::redraw_needed") + end) + end +end + +--- Pause the scrolling animation. +-- @see continue +function scroll:pause() + if self.paused then + return + end + self.paused = true + self.timer:stop() +end + +--- Continue the scrolling animation. +-- @see pause +function scroll:continue() + if not self.paused then + return + end + self.paused = false + self.timer:continue() + self:emit_signal("widget::redraw_needed") +end + +--- Reset the scrolling state to its initial condition. +-- For must scroll step functions, the effect of this function should be to +-- display the widget without any scrolling applied. +-- This function does not undo the effect of @{pause}. +function scroll:reset_scrolling() + self.timer:start() + if self.paused then + self.timer:stop() + end +end + +--- Set the direction in which this widget scroll. +-- @param dir Either "h" for horizontal scrolling or "v" for vertical scrolling +function scroll:set_direction(dir) + if dir == self.dir then + return + end + if dir ~= "h" and dir ~= "v" then + error("Invalid direction, can only be 'h' or 'v'") + end + self.dir = dir + self:emit_signal("widget::layout_changed") + self:emit_signal("widget::redraw_needed") +end + +--- Set the widget which we scroll. +-- @tparam widget widget The widget that we should display +function scroll:set_widget(widget) + if widget == self.widget then + return + end + if widget then + base.check_widget(widget) + end + self.widget = widget + self:emit_signal("widget::layout_changed") + self:emit_signal("widget::redraw_needed") +end + +--- Get the number of children element +-- @treturn table The children +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:set_widget(children[1]) +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. +-- @see set_extra_space +function scroll:set_expand(expand) + if expand == self.expand then + return + end + self.expand = expand + self:emit_signal("widget::redraw_needed") +end + +--- Set the number of frames per second that this widget should draw. +-- @tparam number fps The number of frames per second +function scroll:set_fps(fps) + if fps == self.fps then + return + end + self.fps = fps + -- No signal needed: If we are scrolling, the next redraw will apply the new + -- FPS, else it obviously doesn't make a difference. +end + +--- Set the amount of extra space that should be included in the scrolling. This +-- extra space will likely be left empty between repetitions of the widgets. +-- @tparam number extra_space The amount of extra space +-- @see set_expand +function scroll:set_extra_space(extra_space) + if extra_space == self.extra_space then + return + end + self.extra_space = extra_space + self:emit_signal("widget::redraw_needed") +end + +--- Set the speed of the scrolling animation. The exact meaning depends on the +-- step function that is used, but for the simplest step functions, this will be +-- in pixels per second. +-- @tparam number speed The speed for the animation +function scroll:set_speed(speed) + if speed == self.speed then + return + end + self.speed = speed + self:emit_signal("widget::redraw_needed") +end + +--- Set the maximum size of this widget in the direction set by +-- @{set_direction}. If the child widget is smaller than this size, no scrolling +-- is done. If the child widget is larger, then only this size will be visible +-- and the rest is made visible via scrolling. +-- @tparam number max_size The maximum size of this widget or nil for unlimited. +function scroll:set_max_size(max_size) + if max_size == self.max_size then + return + end + self.max_size = max_size + self:emit_signal("widget::layout_changed") +end + +--- Set the step function that determines the exact behaviour of the scrolling +-- animation. +-- The step function is called with five arguments: +-- +-- * The time in seconds since the state of the animation +-- * The size of the child widget +-- * The size of the visible part of the widget +-- * The speed of the animation. This should have a linear effect on this +-- function's behaviour. +-- * The extra space configured by @{set_extra_space}. This was not yet added to +-- the size of the child widget, but should likely be added to it in most +-- cases. +-- +-- The step function should return a single number. This number is the offset at +-- which the widget is drawn and should be between 0 and `size+extra_space`. +-- @tparam function step_function A step function. +-- @see step_functions +function scroll:set_step_function(step_function) + -- Call the step functions once to see if it works + step_function(0, 42, 10, 10, 5) + if step_function == self.step_function then + return + end + self.step_function = step_function + self:emit_signal("widget::redraw_needed") +end + +--- Set an upper limit for the space for scrolling. +-- This restricts the child widget's maximal size. +-- @tparam number space_for_scrolling The space for scrolling +function scroll:set_space_for_scrolling(space_for_scrolling) + if space_for_scrolling == self.space_for_scrolling then + return + end + self.space_for_scrolling = space_for_scrolling + self:emit_signal("widget::layout_changed") +end + +local function get_layout(dir, widget, fps, speed, extra_space, expand, max_size, step_function, space_for_scrolling) + local ret = base.make_widget() + + ret.paused = false + ret.timer = GLib.Timer() + ret.scroll_timer = nil + + setmetatable(ret, scroll_mt) + + ret:set_direction(dir) + ret:set_widget(widget) + ret:set_fps(fps or 20) + ret:set_speed(speed or 10) + ret:set_extra_space(extra_space or 0) + ret:set_expand(expand) + ret:set_max_size(max_size) + ret:set_step_function(step_function or scroll.step_functions.linear_increase) + ret:set_space_for_scrolling(space_for_scrolling or 2^1024) + + return ret +end + +--- Get a new horizontal scrolling layout. +-- @param[opt] widget The widget that should be scrolled +-- @param[opt=20] fps The number of frames per second +-- @param[opt=10] speed The speed of the animation +-- @param[opt=0] extra_space The amount of extra space to include +-- @tparam[opt=false] boolean expand Should the widget be expanded to include the +-- extra space? +-- @param[opt] max_size The maximum size of the child widget +-- @param[opt=step_functions.linear_increase] step_function The step function to be used +-- @param[opt=2^1024] space_for_scrolling The space for scrolling +function scroll.horizontal(widget, fps, speed, extra_space, expand, max_size, step_function, space_for_scrolling) + return get_layout("h", widget, fps, speed, extra_space, expand, max_size, step_function, space_for_scrolling) +end + +--- Get a new vertical scrolling layout. +-- @param[opt] widget The widget that should be scrolled +-- @param[opt=20] fps The number of frames per second +-- @param[opt=10] speed The speed of the animation +-- @param[opt=0] extra_space The amount of extra space to include +-- @tparam[opt=false] boolean expand Should the widget be expanded to include the +-- extra space? +-- @param[opt] max_size The maximum size of the child widget +-- @param[opt=step_functions.linear_increase] step_function The step function to be used +-- @param[opt=2^1024] space_for_scrolling The space for scrolling +function scroll.vertical(widget, fps, speed, extra_space, expand, max_size, step_function, space_for_scrolling) + return get_layout("v", widget, fps, speed, extra_space, expand, max_size, step_function, space_for_scrolling) +end + +--- A selection of step functions +-- @see set_step_function +scroll.step_functions = {} + +--- A step function that scrolls the widget in an increasing direction with +-- constant speed. +function scroll.step_functions.linear_increase(elapsed, size, _, speed, extra_space) + return (elapsed * speed) % (size + extra_space) +end + +--- A step function that scrolls the widget in an decreasing direction with +-- constant speed. +function scroll.step_functions.linear_decrease(elapsed, size, _, speed, extra_space) + return (-elapsed * speed) % (size + extra_space) +end + +--- A step function that scrolls the widget to its end and back to its +-- beginning, then back to its end, etc. The speed is constant. +function scroll.step_functions.linear_back_and_forth(elapsed, size, visible_size, speed) + local state = ((elapsed * speed) % (2 * size)) / size + state = state <= 1 and state or 2 - state + return (size - visible_size) * state +end + +--- A step function that scrolls the widget to its end and back to its +-- beginning, then back to its end, etc. The speed is null at the ends and +-- maximal in the middle. +function scroll.step_functions.nonlinear_back_and_forth(elapsed, size, visible_size, speed) + local state = ((elapsed * speed) % (2 * size)) / size + local negate = false + if state > 1 then + negate = true + state = state - 1 + end + if state < 1/3 then + -- In the first 1/3rd of time, do a quadratic increase in speed + state = 2 * state * state + elseif state < 2/3 then + -- In the center, do a linear increase. That means we need: + -- If state is 1/3, result is 2/9 = 2 * 1/3 * 1/3 + -- If state is 2/3, result is 7/9 = 1 - 2 * (1 - 2/3) * (1 - 2/3) + state = 5/3*state - 3/9 + else + -- In the last 1/3rd of time, do a quadratic decrease in speed + state = 1 - 2 * (1 - state) * (1 - state) + end + if negate then + state = 1 - state + end + return (size - visible_size) * state +end + +--- A step function that scrolls the widget to its end and back to its +-- beginning, then back to its end, etc. The speed is null at the ends and +-- maximal in the middle. At both ends the widget stands still for a moment. +function scroll.step_functions.waiting_nonlinear_back_and_forth(elapsed, size, visible_size, speed) + local state = ((elapsed * speed) % (2 * size)) / size + local negate = false + if state > 1 then + negate = true + state = state - 1 + end + if state < 1/5 or state > 4/5 then + -- One fifth of time, nothing moves + state = state < 1/5 and 0 or 1 + else + state = (state - 1/5) * 5/3 + if state < 1/3 then + -- In the first 1/3rd of time, do a quadratic increase in speed + state = 2 * state * state + elseif state < 2/3 then + -- In the center, do a linear increase. That means we need: + -- If state is 1/3, result is 2/9 = 2 * 1/3 * 1/3 + -- If state is 2/3, result is 7/9 = 1 - 2 * (1 - 2/3) * (1 - 2/3) + state = 5/3*state - 3/9 + else + -- In the last 1/3rd of time, do a quadratic decrease in speed + state = 1 - 2 * (1 - state) * (1 - state) + end + end + if negate then + state = 1 - state + end + return (size - visible_size) * state +end + +return scroll + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/wibox/init.lua b/lib/wibox/init.lua index a87d1a221..e4f4fdf1f 100644 --- a/lib/wibox/init.lua +++ b/lib/wibox/init.lua @@ -24,6 +24,7 @@ local base = require("wibox.widget.base") -- wibox local wibox = { mt = {}, object = {} } wibox.layout = require("wibox.layout") +wibox.container = require("wibox.container") wibox.widget = require("wibox.widget") wibox.drawable = require("wibox.drawable") wibox.hierarchy = require("wibox.hierarchy") diff --git a/lib/wibox/layout/constraint.lua b/lib/wibox/layout/constraint.lua index e355e5e4b..d6c07df3f 100644 --- a/lib/wibox/layout/constraint.lua +++ b/lib/wibox/layout/constraint.lua @@ -1,140 +1,18 @@ --------------------------------------------------------------------------- +-- This class has been moved to `wibox.container.` +-- -- @author Lukáš Hrázký -- @copyright 2012 Lukáš Hrázký -- @release @AWESOME_VERSION@ -- @classmod wibox.layout.constraint --------------------------------------------------------------------------- -local pairs = pairs -local type = type -local setmetatable = setmetatable -local base = require("wibox.widget.base") -local math = math +local util = require("awful.util") -local constraint = { mt = {} } - ---- Layout a constraint layout -function constraint:layout(_, width, height) - if self.widget then - return { base.place_widget_at(self.widget, 0, 0, width, height) } - end -end - ---- Fit a constraint layout into the given space -function constraint:fit(context, width, height) - local w, h - if self.widget then - w = self._strategy(width, self._width) - h = self._strategy(height, self._height) - - w, h = base.fit_widget(self, context, self.widget, w, h) - else - w, h = 0, 0 - end - - w = self._strategy(w, self._width) - h = self._strategy(h, self._height) - - return w, h -end - ---- Set the widget that this layout adds a constraint on. -function constraint:set_widget(widget) - self.widget = widget - self:emit_signal("widget::layout_changed") -end - ---- Get the number of children element --- @treturn table The children -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:set_widget(children[1]) -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) - local func = { - min = function(real_size, limit) - return limit and math.max(limit, real_size) or real_size - end, - max = function(real_size, limit) - return limit and math.min(limit, real_size) or real_size - end, - exact = function(real_size, limit) - return limit or real_size - end - } - - if not func[val] then - error("Invalid strategy for constraint layout: " .. tostring(val)) - end - - self._strategy = func[val] - self:emit_signal("widget::layout_changed") -end - ---- Set the maximum width to val. nil for no width limit. -function constraint:set_width(val) - self._width = val - self:emit_signal("widget::layout_changed") -end - ---- Set the maximum height to val. nil for no height limit. -function constraint:set_height(val) - self._height = val - self:emit_signal("widget::layout_changed") -end - ---- Reset this layout. The widget will be unreferenced, strategy set to "max" --- and the constraints set to nil. -function constraint:reset() - self._width = nil - self._height = nil - self:set_strategy("max") - self:set_widget(nil) -end - ---- Returns a new constraint layout. This layout will constraint the size of a --- widget according to the strategy. Note that this will only work for layouts --- that respect the widget's size, eg. fixed layout. In layouts that don't --- (fully) respect widget's requested size, the inner widget still might get --- drawn with a size that does not fit the constraint, eg. in flex layout. --- @param[opt] widget A widget to use. --- @param[opt] strategy How to constraint the size. 'max' (default), 'min' or --- 'exact'. --- @param[opt] width The maximum width of the widget. nil for no limit. --- @param[opt] height The maximum height of the widget. nil for no limit. -local function new(widget, strategy, width, height) - local ret = base.make_widget() - - for k, v in pairs(constraint) do - if type(v) == "function" then - ret[k] = v - end - end - - ret:set_strategy(strategy or "max") - ret:set_width(width) - ret:set_height(height) - - if widget then - ret:set_widget(widget) - end - - return ret -end - -function constraint.mt:__call(...) - return new(...) -end - -return setmetatable(constraint, constraint.mt) +return util.deprecate_class( + require("wibox.container.constraint"), + "wibox.layout.constraint", + "wibox.container.constraint" +) -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/wibox/layout/margin.lua b/lib/wibox/layout/margin.lua index 11448bc00..279d754f2 100644 --- a/lib/wibox/layout/margin.lua +++ b/lib/wibox/layout/margin.lua @@ -1,198 +1,18 @@ --------------------------------------------------------------------------- +-- This class has been moved to `wibox.container.margin` +-- -- @author Uli Schlachter -- @copyright 2010 Uli Schlachter -- @release @AWESOME_VERSION@ -- @classmod wibox.layout.margin --------------------------------------------------------------------------- -local pairs = pairs -local type = type -local setmetatable = setmetatable -local base = require("wibox.widget.base") -local gcolor = require("gears.color") -local cairo = require("lgi").cairo +local util = require("awful.util") -local margin = { mt = {} } - --- Draw a margin layout -function margin:draw(_, cr, width, height) - local x = self.left - local y = self.top - local w = self.right - local h = self.bottom - local color = self.color - - if not self.widget or width <= x + w or height <= y + h then - return - end - - if color then - cr:set_source(color) - cr:rectangle(0, 0, width, height) - cr:rectangle(x, y, width - x - w, height - y - h) - cr:set_fill_rule(cairo.FillRule.EVEN_ODD) - cr:fill() - end -end - --- Layout a margin layout -function margin:layout(_, width, height) - if self.widget then - local x = self.left - local y = self.top - local w = self.right - local h = self.bottom - - return { base.place_widget_at(self.widget, x, y, width - x - w, height - y - h) } - end -end - --- Fit a margin layout into the given space -function margin:fit(context, width, height) - local extra_w = self.left + self.right - local extra_h = self.top + self.bottom - local w, h = 0, 0 - if self.widget then - w, h = base.fit_widget(self, context, self.widget, width - extra_w, height - extra_h) - end - - if self._draw_empty == false and (w == 0 or h == 0) then - return 0, 0 - end - - return w + extra_w, h + extra_h -end - ---- Set the widget that this layout adds a margin on. -function margin:set_widget(widget) - if widget then - base.check_widget(widget) - end - self.widget = widget - self:emit_signal("widget::layout_changed") -end - --- Get the number of children element --- @treturn table The children -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:set_widget(children[1]) -end - ---- Set all the margins to val. --- @tparam number val The margin value -function margin:set_margins(val) - if self.left == val and - self.right == val and - self.top == val and - self.bottom == val then - return - end - - self.left = val - self.right = val - self.top = val - self.bottom = val - self:emit_signal("widget::layout_changed") -end - ---- Set the margins color to create a border. --- @param color A color used to fill the margin. -function margin:set_color(color) - self.color = color and gcolor(color) - self:emit_signal("widget::redraw_needed") -end - ---- Draw the margin even if the content size is 0x0 (default: true) --- @tparam boolean draw_empty Draw nothing is content is 0x0 or draw the margin anyway -function margin:set_draw_empty(draw_empty) - self._draw_empty = draw_empty - self:emit_signal("widget::layout_changed") -end - ---- Reset this layout. The widget will be unreferenced, the margins set to 0 --- and the color erased -function margin:reset() - self:set_widget(nil) - self:set_margins(0) - self:set_color(nil) -end - ---- Set the left margin that this layout adds to its widget. --- @param layout The layout you are modifying. --- @param margin The new margin to use. --- @name set_left --- @class function - ---- Set the right margin that this layout adds to its widget. --- @param layout The layout you are modifying. --- @param margin The new margin to use. --- @name set_right --- @class function - ---- Set the top margin that this layout adds to its widget. --- @param layout The layout you are modifying. --- @param margin The new margin to use. --- @name set_top --- @class function - ---- Set the bottom margin that this layout adds to its widget. --- @param layout The layout you are modifying. --- @param margin The new margin to use. --- @name set_bottom --- @class function - --- Create setters for each direction -for _, v in pairs({ "left", "right", "top", "bottom" }) do - margin["set_" .. v] = function(layout, val) - if layout[v] == val then return end - layout[v] = val - layout:emit_signal("widget::layout_changed") - end -end - ---- Returns a new margin layout. --- @param[opt] widget A widget to use. --- @param[opt] left A margin to use on the left side of the widget. --- @param[opt] right A margin to use on the right side of the widget. --- @param[opt] top A margin to use on the top side of the widget. --- @param[opt] bottom A margin to use on the bottom side of the widget. --- @param[opt] color A color for the margins. --- @param[opt] draw_empty whether or not to draw the margin when the content is empty -local function new(widget, left, right, top, bottom, color, draw_empty) - local ret = base.make_widget() - - for k, v in pairs(margin) do - if type(v) == "function" then - ret[k] = v - end - end - - ret:set_left(left or 0) - ret:set_right(right or 0) - ret:set_top(top or 0) - ret:set_bottom(bottom or 0) - ret:set_draw_empty(draw_empty) - - ret:set_color(color) - - if widget then - ret:set_widget(widget) - end - - return ret -end - -function margin.mt:__call(...) - return new(...) -end - -return setmetatable(margin, margin.mt) +return util.deprecate_class( + require("wibox.container.margin"), + "wibox.layout.margin", + "wibox.container.margin" +) -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/wibox/layout/mirror.lua b/lib/wibox/layout/mirror.lua index 2d19385f3..40283b67c 100644 --- a/lib/wibox/layout/mirror.lua +++ b/lib/wibox/layout/mirror.lua @@ -1,127 +1,18 @@ --------------------------------------------------------------------------- +-- This class has been moved to `wibox.container.mirror` +-- -- @author dodo -- @copyright 2012 dodo -- @release @AWESOME_VERSION@ -- @classmod wibox.layout.mirror --------------------------------------------------------------------------- -local type = type -local error = error -local pairs = pairs -local ipairs = ipairs -local setmetatable = setmetatable -local base = require("wibox.widget.base") -local matrix = require("gears.matrix") +local util = require("awful.util") -local mirror = { mt = {} } - ---- Layout this layout -function mirror:layout(_, width, height) - if not self.widget then return end - - local m = matrix.identity - local t = { x = 0, y = 0 } -- translation - local s = { x = 1, y = 1 } -- scale - if self.horizontal then - t.x = width - s.x = -1 - end - if self.vertical then - t.y = height - s.y = -1 - end - m = m:translate(t.x, t.y) - m = m:scale(s.x, s.y) - - return { base.place_widget_via_matrix(self.widget, m, width, height) } -end - ---- Fit this layout into the given area -function mirror:fit(context, ...) - if not self.widget then - return 0, 0 - end - return base.fit_widget(self, context, self.widget, ...) -end - ---- Set the widget that this layout mirrors. --- @param widget The widget to mirror -function mirror:set_widget(widget) - if widget then - base.check_widget(widget) - end - self.widget = widget - self:emit_signal("widget::layout_changed") -end - ---- Get the number of children element --- @treturn table The children -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:set_widget(children[1]) -end - ---- Reset this layout. The widget will be removed and the axes reset. -function mirror:reset() - self.horizontal = false - self.vertical = false - self:set_widget(nil) -end - ---- Set the reflection of this mirror layout. --- @param reflection a table which contains new values for horizontal and/or vertical (booleans) -function mirror:set_reflection(reflection) - if type(reflection) ~= 'table' then - error("Invalid type of reflection for mirror layout: " .. - type(reflection) .. " (should be a table)") - end - for _, ref in ipairs({"horizontal", "vertical"}) do - if reflection[ref] ~= nil then - self[ref] = reflection[ref] - end - end - self:emit_signal("widget::layout_changed") -end - ---- Get the reflection of this mirror layout. --- @return a table of booleans with the keys "horizontal", "vertical". -function mirror:get_reflection() - return { horizontal = self.horizontal, vertical = self.vertical } -end - ---- Returns a new mirror layout. A mirror layout mirrors a given widget. Use --- :set_widget() to set the widget and --- :set_horizontal() and :set_vertical() for the direction. --- horizontal and vertical are by default false which doesn't change anything. --- @param[opt] widget The widget to display. --- @param[opt] reflection A table describing the reflection to apply. -local function new(widget, reflection) - local ret = base.make_widget() - ret.horizontal = false - ret.vertical = false - - for k, v in pairs(mirror) do - if type(v) == "function" then - ret[k] = v - end - end - - ret:set_widget(widget) - ret:set_reflection(reflection or {}) - - return ret -end - -function mirror.mt:__call(...) - return new(...) -end - -return setmetatable(mirror, mirror.mt) +return util.deprecate_class( + require("wibox.container.mirror"), + "wibox.layout.mirror", + "wibox.container.mirror" +) -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/wibox/layout/rotate.lua b/lib/wibox/layout/rotate.lua index 46e60b8e0..56883d7da 100644 --- a/lib/wibox/layout/rotate.lua +++ b/lib/wibox/layout/rotate.lua @@ -1,137 +1,18 @@ --------------------------------------------------------------------------- +-- This class has been moved to `wibox.container.rotate` +-- -- @author Uli Schlachter -- @copyright 2010 Uli Schlachter -- @release @AWESOME_VERSION@ -- @classmod wibox.layout.rotate --------------------------------------------------------------------------- -local error = error -local pairs = pairs -local pi = math.pi -local type = type -local setmetatable = setmetatable -local tostring = tostring -local base = require("wibox.widget.base") -local matrix = require("gears.matrix") +local util = require("awful.util") -local rotate = { mt = {} } - -local function transform(layout, width, height) - local dir = layout:get_direction() - if dir == "east" or dir == "west" then - return height, width - end - return width, height -end - ---- Layout this layout -function rotate:layout(_, width, height) - if not self.widget or not self.widget.visible then - return - end - - local dir = self:get_direction() - - local m = matrix.identity - if dir == "west" then - m = m:rotate(pi / 2) - m = m:translate(0, -width) - elseif dir == "south" then - m = m:rotate(pi) - m = m:translate(-width, -height) - elseif dir == "east" then - m = m:rotate(3 * pi / 2) - m = m:translate(-height, 0) - end - - -- Since we rotated, we might have to swap width and height. - -- transform() does that for us. - return { base.place_widget_via_matrix(self.widget, m, transform(self, width, height)) } -end - ---- Fit this layout into the given area -function rotate:fit(context, width, height) - if not self.widget then - return 0, 0 - end - return transform(self, base.fit_widget(self, context, self.widget, transform(self, width, height))) -end - ---- Set the widget that this layout rotates. -function rotate:set_widget(widget) - if widget then - base.check_widget(widget) - end - self.widget = widget - self:emit_signal("widget::layout_changed") -end - ---- Get the number of children element --- @treturn table The children -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:set_widget(children[1]) -end - ---- Reset this layout. The widget will be removed and the rotation reset. -function rotate:reset() - self.direction = nil - self:set_widget(nil) -end - ---- Set the direction of this rotating layout. Valid values are "north", "east", --- "south" and "west". On an invalid value, this function will throw an error. -function rotate:set_direction(dir) - local allowed = { - north = true, - east = true, - south = true, - west = true - } - - if not allowed[dir] then - error("Invalid direction for rotate layout: " .. tostring(dir)) - end - - self.direction = dir - self:emit_signal("widget::layout_changed") -end - ---- Get the direction of this rotating layout -function rotate:get_direction() - return self.direction or "north" -end - ---- Returns a new rotate layout. A rotate layout rotates a given widget. Use --- :set_widget() to set the widget and :set_direction() for the direction. --- The default direction is "north" which doesn't change anything. --- @param[opt] widget The widget to display. --- @param[opt] dir The direction to rotate to. -local function new(widget, dir) - local ret = base.make_widget() - - for k, v in pairs(rotate) do - if type(v) == "function" then - ret[k] = v - end - end - - ret:set_widget(widget) - ret:set_direction(dir or "north") - - return ret -end - -function rotate.mt:__call(...) - return new(...) -end - -return setmetatable(rotate, rotate.mt) +return util.deprecate_class( + require("wibox.container.rotate"), + "wibox.layout.rotate", + "wibox.container.rotate" +) -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/wibox/layout/scroll.lua b/lib/wibox/layout/scroll.lua index 850fee4b0..a576a2920 100644 --- a/lib/wibox/layout/scroll.lua +++ b/lib/wibox/layout/scroll.lua @@ -1,509 +1,17 @@ --------------------------------------------------------------------------- +-- This class has been moved to `wibox.container.scroll` +-- -- @author Uli Schlachter (based on ideas from Saleur Geoffrey) -- @copyright 2015 Uli Schlachter -- @release @AWESOME_VERSION@ -- @classmod wibox.layout.scroll --------------------------------------------------------------------------- +local util = require("awful.util") -local cache = require("gears.cache") -local timer = require("gears.timer") -local hierarchy = require("wibox.hierarchy") -local base = require("wibox.widget.base") -local lgi = require("lgi") -local GLib = lgi.GLib - -local scroll = {} -local scroll_mt = { __index = scroll } -local _need_scroll_redraw - --- "Strip" a context so that we can use it for our own drawing -local function cleanup_context(context) - local skip = { wibox = true, drawable = true, client = true, position = true } - local res = {} - for k, v in pairs(context) do - if not skip[k] then - res[k] = v - end - end - return res -end - --- Create a hierarchy (and some more stuff) for drawing the given widget. This --- allows "some stuff" to be re-used instead of re-created all the time. -local hierarchy_cache = cache.new(function(context, widget, width, height) - context = cleanup_context(context) - local layouts = setmetatable({}, { __mode = "k" }) - - -- Create a widget hierarchy and update when needed - local hier - local function do_pending_updates(layout) - layouts[layout] = true - hier:update(context, widget, width, height, nil) - end - local function emit(signal) - -- Make the scroll layouts redraw - for w in pairs(layouts) do - w:emit_signal(signal) - end - end - local function redraw_callback() - emit("widget::redraw_needed") - end - local function layout_callback() - emit("widget::redraw_needed") - emit("widget::layout_changed") - end - hier = hierarchy.new(context, widget, width, height, redraw_callback, layout_callback, nil) - - return hier, do_pending_updates, context -end) - ---- Calculate all the information needed for scrolling. --- @param self The instance of the scrolling layout. --- @param context A widget context under which we are fit/drawn. --- @param width The available width --- @param height The available height --- @return A table with the following entries --- @field fit_width The width that should be returned from :fit --- @field fit_height The height that should be returned from :fit --- @field surface_width The width for showing the child widget --- @field surface_height The height for showing the child widget --- @field first_x The x offset for drawing the child the first time --- @field first_y The y offset for drawing the child the first time --- @field[opt] second_x The x offset for drawing the child the second time --- @field[opt] second_y The y offset for drawing the child the second time --- @field hierarchy The wibox.hierarchy instance representing "everything" --- @field context The widget context for drawing the hierarchy -local function calculate_info(self, context, width, height) - local result = {} - assert(self.widget) - - -- First, get the size of the widget (and the size of extra space) - local surface_width, surface_height = width, height - local extra_width, extra_height, extra = 0, 0, self.expand and self.extra_space or 0 - local w, h - if self.dir == "h" then - w, h = base.fit_widget(self, context, self.widget, self.space_for_scrolling, height) - surface_width = w - extra_width = extra - else - w, h = base.fit_widget(self, context, self.widget, width, self.space_for_scrolling) - surface_height = h - extra_height = extra - end - result.fit_width, result.fit_height = w, h - if self.dir == "h" then - if self.max_size then - result.fit_width = math.min(w, self.max_size) - end - else - if self.max_size then - result.fit_height = math.min(h, self.max_size) - end - end - if w > width or h > height then - -- There is less space available than we need, we have to scroll - _need_scroll_redraw(self) - - surface_width, surface_height = surface_width + extra_width, surface_height + extra_height - - local x, y = 0, 0 - local function get_scroll_offset(size, visible_size) - return self.step_function(self.timer:elapsed(), size, visible_size, self.speed, self.extra_space) - end - if self.dir == "h" then - x = -get_scroll_offset(surface_width - extra, width) - else - y = -get_scroll_offset(surface_height - extra, height) - end - result.first_x, result.first_y = x, y - -- Was the extra space already included elsewhere? - local extra_spacer = self.expand and 0 or self.extra_space - if self.dir == "h" then - x = x + surface_width + extra_spacer - else - y = y + surface_height + extra_spacer - end - result.second_x, result.second_y = x, y - else - result.first_x, result.first_y = 0, 0 - end - result.surface_width, result.surface_height = surface_width, surface_height - - -- Get the hierarchy and subscribe ourselves to updates - local hier, do_pending_updates, ctx = hierarchy_cache:get(context, - self.widget, surface_width, surface_height) - result.hierarchy = hier - result.context = ctx - do_pending_updates(self) - - return result -end - ---- Draw this scrolling layout. --- @param context The context in which we are drawn. --- @param cr The cairo context to draw to. --- @param width The available width. --- @param height The available height. -function scroll:draw(context, cr, width, height) - if not self.widget then - return - end - - local info = calculate_info(self, context, width, height) - - -- Draw the first instance of the child - cr:save() - cr:translate(info.first_x, info.first_y) - cr:rectangle(0, 0, info.surface_width, info.surface_height) - cr:clip() - info.hierarchy:draw(info.context, cr) - cr:restore() - - -- If there is one, draw the second instance (same code as above, minus the - -- clip) - if info.second_x and info.second_y then - cr:translate(info.second_x, info.second_y) - cr:rectangle(0, 0, info.surface_width, info.surface_height) - cr:clip() - info.hierarchy:draw(info.context, cr) - end -end - ---- Fit the scroll layout into the given space. --- @param context The context in which we are fit. --- @param width The available width. --- @param height The available height. -function scroll:fit(context, width, height) - if not self.widget then - return 0, 0 - end - local info = calculate_info(self, context, width, height) - return info.fit_width, info.fit_height -end - --- Internal function used for triggering redraws for scrolling. --- The purpose is to start a timer for redrawing the widget for scrolling. --- Redrawing works by simply emitting the `widget::redraw_needed` signal. --- Pausing is implemented in this function: We just don't start a timer. --- This function must be idempotent (calling it multiple times right after --- another does not make a difference). -_need_scroll_redraw = function(self) - if not self.paused and not self.scroll_timer then - self.scroll_timer = timer.start_new(1 / self.fps, function() - self.scroll_timer = nil - self:emit_signal("widget::redraw_needed") - end) - end -end - ---- Pause the scrolling animation. --- @see continue -function scroll:pause() - if self.paused then - return - end - self.paused = true - self.timer:stop() -end - ---- Continue the scrolling animation. --- @see pause -function scroll:continue() - if not self.paused then - return - end - self.paused = false - self.timer:continue() - self:emit_signal("widget::redraw_needed") -end - ---- Reset the scrolling state to its initial condition. --- For must scroll step functions, the effect of this function should be to --- display the widget without any scrolling applied. --- This function does not undo the effect of @{pause}. -function scroll:reset_scrolling() - self.timer:start() - if self.paused then - self.timer:stop() - end -end - ---- Set the direction in which this widget scroll. --- @param dir Either "h" for horizontal scrolling or "v" for vertical scrolling -function scroll:set_direction(dir) - if dir == self.dir then - return - end - if dir ~= "h" and dir ~= "v" then - error("Invalid direction, can only be 'h' or 'v'") - end - self.dir = dir - self:emit_signal("widget::layout_changed") - self:emit_signal("widget::redraw_needed") -end - ---- Set the widget which we scroll. --- @tparam widget widget The widget that we should display -function scroll:set_widget(widget) - if widget == self.widget then - return - end - if widget then - base.check_widget(widget) - end - self.widget = widget - self:emit_signal("widget::layout_changed") - self:emit_signal("widget::redraw_needed") -end - ---- Get the number of children element --- @treturn table The children -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:set_widget(children[1]) -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. --- @see set_extra_space -function scroll:set_expand(expand) - if expand == self.expand then - return - end - self.expand = expand - self:emit_signal("widget::redraw_needed") -end - ---- Set the number of frames per second that this widget should draw. --- @tparam number fps The number of frames per second -function scroll:set_fps(fps) - if fps == self.fps then - return - end - self.fps = fps - -- No signal needed: If we are scrolling, the next redraw will apply the new - -- FPS, else it obviously doesn't make a difference. -end - ---- Set the amount of extra space that should be included in the scrolling. This --- extra space will likely be left empty between repetitions of the widgets. --- @tparam number extra_space The amount of extra space --- @see set_expand -function scroll:set_extra_space(extra_space) - if extra_space == self.extra_space then - return - end - self.extra_space = extra_space - self:emit_signal("widget::redraw_needed") -end - ---- Set the speed of the scrolling animation. The exact meaning depends on the --- step function that is used, but for the simplest step functions, this will be --- in pixels per second. --- @tparam number speed The speed for the animation -function scroll:set_speed(speed) - if speed == self.speed then - return - end - self.speed = speed - self:emit_signal("widget::redraw_needed") -end - ---- Set the maximum size of this widget in the direction set by --- @{set_direction}. If the child widget is smaller than this size, no scrolling --- is done. If the child widget is larger, then only this size will be visible --- and the rest is made visible via scrolling. --- @tparam number max_size The maximum size of this widget or nil for unlimited. -function scroll:set_max_size(max_size) - if max_size == self.max_size then - return - end - self.max_size = max_size - self:emit_signal("widget::layout_changed") -end - ---- Set the step function that determines the exact behaviour of the scrolling --- animation. --- The step function is called with five arguments: --- --- * The time in seconds since the state of the animation --- * The size of the child widget --- * The size of the visible part of the widget --- * The speed of the animation. This should have a linear effect on this --- function's behaviour. --- * The extra space configured by @{set_extra_space}. This was not yet added to --- the size of the child widget, but should likely be added to it in most --- cases. --- --- The step function should return a single number. This number is the offset at --- which the widget is drawn and should be between 0 and `size+extra_space`. --- @tparam function step_function A step function. --- @see step_functions -function scroll:set_step_function(step_function) - -- Call the step functions once to see if it works - step_function(0, 42, 10, 10, 5) - if step_function == self.step_function then - return - end - self.step_function = step_function - self:emit_signal("widget::redraw_needed") -end - ---- Set an upper limit for the space for scrolling. --- This restricts the child widget's maximal size. --- @tparam number space_for_scrolling The space for scrolling -function scroll:set_space_for_scrolling(space_for_scrolling) - if space_for_scrolling == self.space_for_scrolling then - return - end - self.space_for_scrolling = space_for_scrolling - self:emit_signal("widget::layout_changed") -end - -local function get_layout(dir, widget, fps, speed, extra_space, expand, max_size, step_function, space_for_scrolling) - local ret = base.make_widget() - - ret.paused = false - ret.timer = GLib.Timer() - ret.scroll_timer = nil - - setmetatable(ret, scroll_mt) - - ret:set_direction(dir) - ret:set_widget(widget) - ret:set_fps(fps or 20) - ret:set_speed(speed or 10) - ret:set_extra_space(extra_space or 0) - ret:set_expand(expand) - ret:set_max_size(max_size) - ret:set_step_function(step_function or scroll.step_functions.linear_increase) - ret:set_space_for_scrolling(space_for_scrolling or 2^1024) - - return ret -end - ---- Get a new horizontal scrolling layout. --- @param[opt] widget The widget that should be scrolled --- @param[opt=20] fps The number of frames per second --- @param[opt=10] speed The speed of the animation --- @param[opt=0] extra_space The amount of extra space to include --- @tparam[opt=false] boolean expand Should the widget be expanded to include the --- extra space? --- @param[opt] max_size The maximum size of the child widget --- @param[opt=step_functions.linear_increase] step_function The step function to be used --- @param[opt=2^1024] space_for_scrolling The space for scrolling -function scroll.horizontal(widget, fps, speed, extra_space, expand, max_size, step_function, space_for_scrolling) - return get_layout("h", widget, fps, speed, extra_space, expand, max_size, step_function, space_for_scrolling) -end - ---- Get a new vertical scrolling layout. --- @param[opt] widget The widget that should be scrolled --- @param[opt=20] fps The number of frames per second --- @param[opt=10] speed The speed of the animation --- @param[opt=0] extra_space The amount of extra space to include --- @tparam[opt=false] boolean expand Should the widget be expanded to include the --- extra space? --- @param[opt] max_size The maximum size of the child widget --- @param[opt=step_functions.linear_increase] step_function The step function to be used --- @param[opt=2^1024] space_for_scrolling The space for scrolling -function scroll.vertical(widget, fps, speed, extra_space, expand, max_size, step_function, space_for_scrolling) - return get_layout("v", widget, fps, speed, extra_space, expand, max_size, step_function, space_for_scrolling) -end - ---- A selection of step functions --- @see set_step_function -scroll.step_functions = {} - ---- A step function that scrolls the widget in an increasing direction with --- constant speed. -function scroll.step_functions.linear_increase(elapsed, size, _, speed, extra_space) - return (elapsed * speed) % (size + extra_space) -end - ---- A step function that scrolls the widget in an decreasing direction with --- constant speed. -function scroll.step_functions.linear_decrease(elapsed, size, _, speed, extra_space) - return (-elapsed * speed) % (size + extra_space) -end - ---- A step function that scrolls the widget to its end and back to its --- beginning, then back to its end, etc. The speed is constant. -function scroll.step_functions.linear_back_and_forth(elapsed, size, visible_size, speed) - local state = ((elapsed * speed) % (2 * size)) / size - state = state <= 1 and state or 2 - state - return (size - visible_size) * state -end - ---- A step function that scrolls the widget to its end and back to its --- beginning, then back to its end, etc. The speed is null at the ends and --- maximal in the middle. -function scroll.step_functions.nonlinear_back_and_forth(elapsed, size, visible_size, speed) - local state = ((elapsed * speed) % (2 * size)) / size - local negate = false - if state > 1 then - negate = true - state = state - 1 - end - if state < 1/3 then - -- In the first 1/3rd of time, do a quadratic increase in speed - state = 2 * state * state - elseif state < 2/3 then - -- In the center, do a linear increase. That means we need: - -- If state is 1/3, result is 2/9 = 2 * 1/3 * 1/3 - -- If state is 2/3, result is 7/9 = 1 - 2 * (1 - 2/3) * (1 - 2/3) - state = 5/3*state - 3/9 - else - -- In the last 1/3rd of time, do a quadratic decrease in speed - state = 1 - 2 * (1 - state) * (1 - state) - end - if negate then - state = 1 - state - end - return (size - visible_size) * state -end - ---- A step function that scrolls the widget to its end and back to its --- beginning, then back to its end, etc. The speed is null at the ends and --- maximal in the middle. At both ends the widget stands still for a moment. -function scroll.step_functions.waiting_nonlinear_back_and_forth(elapsed, size, visible_size, speed) - local state = ((elapsed * speed) % (2 * size)) / size - local negate = false - if state > 1 then - negate = true - state = state - 1 - end - if state < 1/5 or state > 4/5 then - -- One fifth of time, nothing moves - state = state < 1/5 and 0 or 1 - else - state = (state - 1/5) * 5/3 - if state < 1/3 then - -- In the first 1/3rd of time, do a quadratic increase in speed - state = 2 * state * state - elseif state < 2/3 then - -- In the center, do a linear increase. That means we need: - -- If state is 1/3, result is 2/9 = 2 * 1/3 * 1/3 - -- If state is 2/3, result is 7/9 = 1 - 2 * (1 - 2/3) * (1 - 2/3) - state = 5/3*state - 3/9 - else - -- In the last 1/3rd of time, do a quadratic decrease in speed - state = 1 - 2 * (1 - state) * (1 - state) - end - end - if negate then - state = 1 - state - end - return (size - visible_size) * state -end - -return scroll +return util.deprecate_class( + require("wibox.container.scroll"), + "wibox.layout.scroll", + "wibox.container.scroll" +) -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/wibox/widget/background.lua b/lib/wibox/widget/background.lua index de8e88c5b..70a2af322 100644 --- a/lib/wibox/widget/background.lua +++ b/lib/wibox/widget/background.lua @@ -1,224 +1,17 @@ --------------------------------------------------------------------------- --- A container capable of changing the background color, foreground color --- widget shape. +-- This class has been moved to `wibox.container.background` +-- -- @author Uli Schlachter -- @copyright 2010 Uli Schlachter -- @release @AWESOME_VERSION@ -- @classmod wibox.widget.background --------------------------------------------------------------------------- +local util = require("awful.util") -local base = require("wibox.widget.base") -local color = require("gears.color") -local surface = require("gears.surface") -local beautiful = require("beautiful") -local cairo = require("lgi").cairo -local setmetatable = setmetatable -local pairs = pairs -local type = type -local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1) - -local background = { mt = {} } - --- Draw this widget -function background:draw(context, cr, width, height) - if not self.widget or not self.widget.visible then - return - end - - -- Keep the shape path in case there is a border - self._path = nil - - if self._shape then - -- Only add the offset if there is something to draw - local offset = ((self._shape_border_width and self._shape_border_color) - and self._shape_border_width or 0) / 2 - - cr:translate(offset, offset) - self._shape(cr, width - 2*offset, height - 2*offset, unpack(self._shape_args or {})) - cr:translate(-offset, -offset) - self._path = cr:copy_path() - cr:clip() - end - - if self.background then - cr:set_source(self.background) - cr:paint() - end - if self.bgimage then - if type(self.bgimage) == "function" then - self.bgimage(context, cr, width, height,unpack(self.bgimage_args)) - else - local pattern = cairo.Pattern.create_for_surface(self.bgimage) - cr:set_source(pattern) - cr:paint() - end - end - -end - --- Draw the border -function background:after_draw_children(_, cr) - -- Draw the border - if self._path and self._shape_border_width and self._shape_border_width > 0 then - cr:append_path(self._path) - cr:set_source(color(self._shape_border_color or self.foreground or beautiful.fg_normal)) - - cr:set_line_width(self._shape_border_width) - cr:stroke() - self._path = nil - end -end - --- Prepare drawing the children of this widget -function background:before_draw_children(_, cr) - if self.foreground then - cr:set_source(self.foreground) - end - - -- Clip the shape - if self._path and self._shape_clip then - cr:append_path(self._path) - cr:clip() - end -end - --- Layout this widget -function background:layout(_, width, height) - if self.widget then - return { base.place_widget_at(self.widget, 0, 0, width, height) } - end -end - --- Fit this widget into the given area -function background:fit(context, width, height) - if not self.widget then - return 0, 0 - end - - return base.fit_widget(self, context, self.widget, width, height) -end - ---- Set the widget that is drawn on top of the background --- @tparam widget widget The widget to be disaplayed inside of the background --- area -function background:set_widget(widget) - if widget then - base.check_widget(widget) - end - self.widget = widget - self:emit_signal("widget::layout_changed") -end - --- Get children element --- @treturn table The children -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:set_widget(children[1]) -end - ---- Set the background to use. ---@DOC_wibox_widget_background_bg_EXAMPLE@ --- @param bg A color string, pattern or gradient (see `gears.color`) -function background:set_bg(bg) - if bg then - self.background = color(bg) - else - self.background = nil - end - self:emit_signal("widget::redraw_needed") -end - ---- Set the foreground to use. ---@DOC_wibox_widget_background_fg_EXAMPLE@ --- @param fg A color string, pattern or gradient (see `gears.color`) -function background:set_fg(fg) - if fg then - self.foreground = color(fg) - else - self.foreground = nil - end - self:emit_signal("widget::redraw_needed") -end - ---- Set the background shape. --- --- Any other arguments will be passed to the shape function ---@DOC_wibox_widget_background_shape_EXAMPLE@ --- @param shape A function taking a context, width and height as arguments -function background:set_shape(shape, ...) - self._shape = shape - self._shape_args = {...} - self:emit_signal("widget::redraw_needed") -end - ---- When a `shape` is set, also draw a border. --- --- See `wibox.widget.background.set_shape` for an usage example. --- @tparam number width The border width -function background:set_shape_border_width(width) - self._shape_border_width = width - self:emit_signal("widget::redraw_needed") -end - ---- When a `shape` is set, also draw a border. --- --- See `wibox.widget.background.set_shape` for an usage example. --- @param[opt=self.foreground] fg The border color, pattern or gradient -function background:set_shape_border_color(fg) - self._shape_border_color = fg - self:emit_signal("widget::redraw_needed") -end - ---- When a `shape` is set, make sure nothing is drawn outside of it. ---@DOC_wibox_widget_background_clip_EXAMPLE@ --- @tparam boolean value If the shape clip is enable -function background:set_shape_clip(value) - self._shape_clip = value - self:emit_signal("widget::redraw_needed") -end - ---- Set the background image to use --- If `image` is a function, it will be called with `(context, cr, width, height)` --- as arguments. Any other arguments passed to this method will be appended. --- @param image A background image or a function -function background:set_bgimage(image, ...) - self.bgimage = type(image) == "function" and image or surface.load(image) - self.bgimage_args = {...} - self:emit_signal("widget::redraw_needed") -end - ---- Returns a new background layout. A background layout applies a background --- and foreground color to another widget. --- @param[opt] widget The widget to display. --- @param[opt] bg The background to use for that widget. --- @param[opt] shape A `gears.shape` compatible shape function -local function new(widget, bg, shape) - local ret = base.make_widget() - - for k, v in pairs(background) do - if type(v) == "function" then - ret[k] = v - end - end - - ret._shape = shape - - ret:set_widget(widget) - ret:set_bg(bg) - - return ret -end - -function background.mt:__call(...) - return new(...) -end - -return setmetatable(background, background.mt) +return util.deprecate_class( + require("wibox.container.background"), + "wibox.widget.background", + "wibox.container.background" +) -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/tests/examples/wibox/widget/background/bg.lua b/tests/examples/wibox/widget/background/bg.lua index f6562a6cf..9ef3e9595 100644 --- a/tests/examples/wibox/widget/background/bg.lua +++ b/tests/examples/wibox/widget/background/bg.lua @@ -10,17 +10,17 @@ parent : setup { { text_widget, bg = '#ff0000', - widget = wibox.widget.background + widget = wibox.container.background }, { text_widget, bg = '#00ff00', - widget = wibox.widget.background + widget = wibox.container.background }, { text_widget, bg = '#0000ff', - widget = wibox.widget.background + widget = wibox.container.background }, spacing = 10, layout = wibox.layout.fixed.vertical diff --git a/tests/examples/wibox/widget/background/clip.lua b/tests/examples/wibox/widget/background/clip.lua index 15bdb15e3..8184f8668 100644 --- a/tests/examples/wibox/widget/background/clip.lua +++ b/tests/examples/wibox/widget/background/clip.lua @@ -13,7 +13,7 @@ parent : setup { shape = gears.shape.circle, bg = beautiful.bg_normal, shape_border_color = beautiful.border_color, - widget = wibox.widget.background + widget = wibox.container.background }, { -- To solve this, clip the content @@ -25,7 +25,7 @@ parent : setup { shape = gears.shape.circle, bg = beautiful.bg_normal, shape_border_color = beautiful.border_color, - widget = wibox.widget.background + widget = wibox.container.background }, spacing = 10, layout = wibox.layout.fixed.vertical diff --git a/tests/examples/wibox/widget/background/fg.lua b/tests/examples/wibox/widget/background/fg.lua index 98a889c53..8e3c9450d 100644 --- a/tests/examples/wibox/widget/background/fg.lua +++ b/tests/examples/wibox/widget/background/fg.lua @@ -10,17 +10,17 @@ parent : setup { { text_widget, fg = '#ff0000', - widget = wibox.widget.background + widget = wibox.container.background }, { text_widget, fg = '#00ff00', - widget = wibox.widget.background + widget = wibox.container.background }, { text_widget, fg = '#0000ff', - widget = wibox.widget.background + widget = wibox.container.background }, spacing = 10, layout = wibox.layout.fixed.vertical diff --git a/tests/examples/wibox/widget/background/shape.lua b/tests/examples/wibox/widget/background/shape.lua index e8c16361b..139bb1ca1 100644 --- a/tests/examples/wibox/widget/background/shape.lua +++ b/tests/examples/wibox/widget/background/shape.lua @@ -14,7 +14,7 @@ parent : setup { bg = beautiful.bg_normal, shape_border_color = beautiful.border_color, shape_border_width = beautiful.border_width, - widget = wibox.widget.background + widget = wibox.container.background }, { -- To solve this, use a margin @@ -27,13 +27,13 @@ parent : setup { right = 10, top = 3, bottom = 3, - widget = wibox.layout.margin + widget = wibox.container.margin }, shape = gears.shape.hexagon, bg = beautiful.bg_normal, shape_border_color = beautiful.border_color, shape_border_width = beautiful.border_width, - widget = wibox.widget.background + widget = wibox.container.background }, spacing = 10, layout = wibox.layout.fixed.vertical diff --git a/tests/test-awful-widget-button.lua b/tests/test-awful-widget-button.lua index e35ac4516..f0cff928f 100644 --- a/tests/test-awful-widget-button.lua +++ b/tests/test-awful-widget-button.lua @@ -30,14 +30,14 @@ table.insert(steps, function() widget = wibox.widget.textbox, }, bg = "#ff0000", - widget = wibox.widget.background + widget = wibox.container.background }, { { widget = button, }, bg = "#ff00ff", - widget = wibox.widget.background + widget = wibox.container.background }, { { @@ -45,7 +45,7 @@ table.insert(steps, function() widget = wibox.widget.textbox, }, bg = "#0000ff", - widget = wibox.widget.background + widget = wibox.container.background }, layout = wibox.layout.flex.vertical }