diff options
author | ache <ache@ache.one> | 2017-03-13 23:17:19 +0100 |
---|---|---|
committer | ache <ache@ache.one> | 2017-03-13 23:17:19 +0100 |
commit | 22d656903563f75678f3634964731ccf93355dfd (patch) | |
tree | e3cb6279d95c9764093072d5e946566ea6533799 /lib/wibox |
Init commit
Diffstat (limited to 'lib/wibox')
35 files changed, 12966 insertions, 0 deletions
diff --git a/lib/wibox/container/arcchart.lua b/lib/wibox/container/arcchart.lua new file mode 100644 index 0000000..3794630 --- /dev/null +++ b/lib/wibox/container/arcchart.lua @@ -0,0 +1,566 @@ +--------------------------------------------------------------------------- +-- +-- A circular chart (arc chart). +-- +-- It can contain a central widget (or not) and display multiple values. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_container_defaults_arcchart.svg) +-- +-- @author Emmanuel Lepage Vallee <elv1313@gmail.com> +-- @copyright 2013 Emmanuel Lepage Vallee +-- @classmod wibox.container.arcchart +--------------------------------------------------------------------------- + +local setmetatable = setmetatable +local base = require("wibox.widget.base") +local shape = require("gears.shape" ) +local util = require( "awful.util" ) +local color = require( "gears.color" ) +local beautiful = require("beautiful" ) + + +local arcchart = { mt = {} } + +--- The progressbar border background color. +-- @beautiful beautiful.arcchart_border_color + +--- The progressbar foreground color. +-- @beautiful beautiful.arcchart_color + +--- The progressbar border width. +-- @beautiful beautiful.arcchart_border_width + +--- The padding between the outline and the progressbar. +-- @beautiful beautiful.arcchart_paddings +-- @tparam[opt=0] table|number paddings A number or a table +-- @tparam[opt=0] number paddings.top +-- @tparam[opt=0] number paddings.bottom +-- @tparam[opt=0] number paddings.left +-- @tparam[opt=0] number paddings.right + +--- The arc thickness. +-- @beautiful beautiful.thickness +-- @param number + +local function outline_workarea(width, height) + local x, y = 0, 0 + local size = math.min(width, height) + + return {x=x+(width-size)/2, y=y+(height-size)/2, width=size, height=size} +end + +-- The child widget area +local function content_workarea(self, width, height) + local padding = self._private.paddings or {} + local border_width = self:get_border_width() or 0 + local wa = outline_workarea(width, height) + local thickness = math.max(border_width, self:get_thickness() or 5) + + wa.x = wa.x + (padding.left or 0) + thickness + 2*border_width + wa.y = wa.y + (padding.top or 0) + thickness + 2*border_width + wa.width = wa.width - (padding.left or 0) - (padding.right or 0) + - 2*thickness - 4*border_width + wa.height = wa.height - (padding.top or 0) - (padding.bottom or 0) + - 2*thickness - 4*border_width + + return wa +end + +-- Draw the radial outline and progress +function arcchart:after_draw_children(_, cr, width, height) + cr:restore() + + local values = self:get_values() or {} + local border_width = self:get_border_width() or 0 + local thickness = math.max(border_width, self:get_thickness() or 5) + + local offset = thickness + 2*border_width + + -- Draw a circular background + local bg = self:get_bg() + if bg then + cr:save() + cr:translate(offset/2, offset/2) + shape.circle( + cr, + width-offset, + height-offset + ) + cr:set_line_width(thickness+2*border_width) + cr:set_source(color(bg)) + cr:stroke() + cr:restore() + end + + if #values == 0 then + return + end + + local wa = outline_workarea(width, height) + cr:translate(wa.x+border_width/2, wa.y+border_width/2) + + + -- Get the min and max value + --local min_val = self:get_min_value() or 0 --TODO support min_values + local max_val = self:get_max_value() + local sum = 0 + + if not max_val then + for _, v in ipairs(values) do + sum = sum + v + end + max_val = sum + end + + max_val = math.max(max_val, sum) + + local use_rounded_edges = sum ~= max_val and self:get_rounded_edge() + + -- Fallback to the current foreground color + local colors = self:get_colors() or {} + + -- Draw the outline + local offset_angle = self:get_start_angle() or math.pi + local start_angle, end_angle = offset_angle, offset_angle + + for k, v in ipairs(values) do + end_angle = start_angle + (v*2*math.pi) / max_val + + if colors[k] then + cr:set_source(color(colors[k])) + end + + shape.arc(cr, wa.width-border_width, wa.height-border_width, + thickness+border_width, math.pi-end_angle, math.pi-start_angle, + (use_rounded_edges and k == 1), (use_rounded_edges and k == #values) + ) + + cr:fill() + start_angle = end_angle + end + + if border_width > 0 then + local border_color = self:get_border_color() + + cr:set_source(color(border_color)) + cr:set_line_width(border_width) + + shape.arc(cr, wa.width-border_width, wa.height-border_width, + thickness+border_width, math.pi-end_angle, math.pi-offset_angle, + use_rounded_edges, use_rounded_edges + ) + cr:stroke() + end + +end + +-- Set the clip +function arcchart:before_draw_children(_, cr, width, height) + cr:save() + local wa = content_workarea(self, width, height) + cr:translate(wa.x, wa.y) + shape.circle( + cr, + wa.width, + wa.height + ) + cr:clip() + cr:translate(-wa.x, -wa.y) +end + +-- Layout this layout +function arcchart:layout(_, width, height) + if self._private.widget then + local wa = content_workarea(self, width, height) + + return { base.place_widget_at( + self._private.widget, wa.x, wa.y, wa.width, wa.height + ) } + end +end + +-- Fit this layout into the given area +function arcchart:fit(_, width, height) + local size = math.min(width, height) + return size, size +end + +--- The widget to wrap in a radial proggressbar. +-- @property widget +-- @tparam widget widget The widget + +function arcchart:set_widget(widget) + if widget then + base.check_widget(widget) + end + self._private.widget = widget + self:emit_signal("widget::layout_changed") +end + +--- Get the children elements. +-- @treturn table The children +function arcchart:get_children() + return {self._private.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 arcchart:set_children(children) + self._private.widget = children and children[1] + self:emit_signal("widget::layout_changed") +end + +--- Reset this layout. The widget will be removed and the rotation reset. +function arcchart:reset() + self:set_widget(nil) +end + +for _,v in ipairs {"left", "right", "top", "bottom"} do + arcchart["set_"..v.."_padding"] = function(self, val) + self._private.paddings = self._private.paddings or {} + self._private.paddings[v] = val + self:emit_signal("widget::redraw_needed") + self:emit_signal("widget::layout_changed") + end +end + +--- The padding between the outline and the progressbar. +-- +-- +--![Usage example](../images/AUTOGEN_wibox_container_arcchart_paddings.svg) +-- +-- @property paddings +-- @tparam[opt=0] table|number paddings A number or a table +-- @tparam[opt=0] number paddings.top +-- @tparam[opt=0] number paddings.bottom +-- @tparam[opt=0] number paddings.left +-- @tparam[opt=0] number paddings.right + +--- The border background color. +-- +-- @property border_color + +--- The border foreground color. +-- +-- @property color + +--- The border width. +-- +-- +--![Usage example](../images/AUTOGEN_wibox_container_arcchart_border_width.svg) +-- +-- @property border_width +-- @tparam[opt=3] number border_width + +--- The minimum value. +-- @property min_value + +--- The maximum value. +-- @property max_value + +--- The radial background. +-- +-- +--![Usage example](../images/AUTOGEN_wibox_container_arcchart_bg.svg) +-- +-- @property bg +-- @param color +-- @see gears.color + +--- The value. +-- +-- +--![Usage example](../images/AUTOGEN_wibox_container_arcchart_value.svg) +-- +-- @property value +-- @tparam number value Between min_value and max_value +-- @see values + +--- The values. +-- The arcchart is designed to display multiple values at once. Each will be +-- shown in table order. +-- +-- @property values +-- @tparam table values An ordered set if values. +-- @see value + +--- If the chart has rounded edges. +-- +-- +--![Usage example](../images/AUTOGEN_wibox_container_arcchart_rounded_edge.svg) +-- +-- @property rounded_edge +-- @param[opt=false] boolean + +--- The arc thickness. +-- +-- +--![Usage example](../images/AUTOGEN_wibox_container_arcchart_thickness.svg) +-- +-- @property thickness +-- @param number + +--- The (radiant) angle where the first value start. +-- +-- +--![Usage example](../images/AUTOGEN_wibox_container_arcchart_start_angle.svg) +-- +-- @property start_angle +-- @param[opt=math.pi] number A number between 0 and 2*math.pi + +for _, prop in ipairs {"border_width", "border_color", "paddings", "colors", + "rounded_edge", "bg", "thickness", "values", "min_value", "max_value", + "start_angle" } do + arcchart["set_"..prop] = function(self, value) + self._private[prop] = value + self:emit_signal("property::"..prop) + self:emit_signal("widget::redraw_needed") + end + arcchart["get_"..prop] = function(self) + return self._private[prop] or beautiful["arcchart_"..prop] + end +end + +function arcchart:set_paddings(val) + self._private.paddings = type(val) == "number" and { + left = val, + right = val, + top = val, + bottom = val, + } or val or {} + self:emit_signal("property::paddings") + self:emit_signal("widget::redraw_needed") + self:emit_signal("widget::layout_changed") +end + +function arcchart:set_value(value) + self:set_values {value} +end + +--- Returns a new arcchart layout. +-- @param[opt] widget The widget to display. +-- @function wibox.container.arcchart +local function new(widget) + local ret = base.make_widget(nil, nil, { + enable_properties = true, + }) + + util.table.crush(ret, arcchart) + + ret:set_widget(widget) + + return ret +end + +function arcchart.mt:__call(...) + return new(...) +end + +--Imported documentation + + +--- Get a widex index. +-- @param widget The widget to look for +-- @param[opt] recursive Also check sub-widgets +-- @param[opt] ... Aditional widgets to add at the end of the \"path\" +-- @return The index +-- @return The parent layout +-- @return The path between \"self\" and \"widget\" +-- @function index + +--- Get all direct and indirect children widgets. +-- This will scan all containers recursively to find widgets +-- Warning: This method it prone to stack overflow id the widget, or any of its +-- children, contain (directly or indirectly) itself. +-- @treturn table The children +-- @function get_all_children + +--- Set a declarative widget hierarchy description. +-- See [The declarative layout system](../documentation/03-declarative-layout.md.html) +-- @param args An array containing the widgets disposition +-- @function setup + +--- Force a widget height. +-- @property forced_height +-- @tparam number|nil height The height (`nil` for automatic) + +--- Force a widget width. +-- @property forced_width +-- @tparam number|nil width The width (`nil` for automatic) + +--- The widget opacity (transparency). +-- @property opacity +-- @tparam[opt=1] number opacity The opacity (between 0 and 1) + +--- The widget visibility. +-- @property visible +-- @param boolean + +--- Set/get a widget's buttons. +-- @param _buttons The table of buttons that should bind to the widget. +-- @function buttons + +--- Emit a signal and ensure all parent widgets in the hierarchies also +-- forward the signal. This is useful to track signals when there is a dynamic +-- set of containers and layouts wrapping the widget. +-- @tparam string signal_name +-- @param ... Other arguments +-- @function emit_signal_recursive + +--- When the layout (size) change. +-- This signal is emitted when the previous results of `:layout()` and `:fit()` +-- are no longer valid. Unless this signal is emitted, `:layout()` and `:fit()` +-- must return the same result when called with the same arguments. +-- @signal widget::layout_changed +-- @see widget::redraw_needed + +--- When the widget content changed. +-- This signal is emitted when the content of the widget changes. The widget will +-- be redrawn, it is not re-layouted. Put differently, it is assumed that +-- `:layout()` and `:fit()` would still return the same results as before. +-- @signal widget::redraw_needed +-- @see widget::layout_changed + +--- When a mouse button is pressed over the widget. +-- @signal button::press +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When a mouse button is released over the widget. +-- @signal button::release +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse enter a widget. +-- @signal mouse::enter +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse leave a widget. +-- @signal mouse::leave +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + + +--Imported documentation + + +--- Disconnect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback that should be disconnected +-- @function disconnect_signal + +--- Emit a signal. +-- +-- @tparam string name The name of the signal +-- @param ... Extra arguments for the callback functions. Each connected +-- function receives the object as first argument and then any extra arguments +-- that are given to emit_signal() +-- @function emit_signal + +--- Connect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function connect_signal + +--- Connect to a signal weakly. This allows the callback function to be garbage +-- collected and automatically disconnects the signal when that happens. +-- +-- **Warning:** +-- Only use this function if you really, really, really know what you +-- are doing. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function weak_connect_signal + + +return setmetatable(arcchart, arcchart.mt) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/wibox/container/background.lua b/lib/wibox/container/background.lua new file mode 100644 index 0000000..2fb88c3 --- /dev/null +++ b/lib/wibox/container/background.lua @@ -0,0 +1,626 @@ +--------------------------------------------------------------------------- +-- A container capable of changing the background color, foreground color +-- widget shape. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_container_defaults_background.svg) +-- +-- @author Uli Schlachter +-- @copyright 2010 Uli Schlachter +-- @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 util = require("awful.util") +local setmetatable = setmetatable +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._private.widget or not self._private.widget:get_visible() then + return + end + + -- Keep the shape path in case there is a border + self._private.path = nil + + if self._private.shape then + -- Only add the offset if there is something to draw + local offset = ((self._private.shape_border_width and self._private.shape_border_color) + and self._private.shape_border_width or 0) / 2 + + cr:translate(offset, offset) + self._private.shape(cr, width - 2*offset, height - 2*offset, unpack(self._private.shape_args or {})) + cr:translate(-offset, -offset) + self._private.path = cr:copy_path() + cr:clip() + end + + if self._private.background then + cr:set_source(self._private.background) + cr:paint() + end + if self._private.bgimage then + if type(self._private.bgimage) == "function" then + self._private.bgimage(context, cr, width, height,unpack(self._private.bgimage_args)) + else + local pattern = cairo.Pattern.create_for_surface(self._private.bgimage) + cr:set_source(pattern) + cr:paint() + end + end + +end + +-- Draw the border +function background:after_draw_children(_, cr) + -- Draw the border + if self._private.path and self._private.shape_border_width and self._private.shape_border_width > 0 then + cr:append_path(self._private.path) + cr:set_source(color(self._private.shape_border_color or self._private.foreground or beautiful.fg_normal)) + + cr:set_line_width(self._private.shape_border_width) + cr:stroke() + self._private.path = nil + end +end + +-- Prepare drawing the children of this widget +function background:before_draw_children(_, cr) + if self._private.foreground then + cr:set_source(self._private.foreground) + end + + -- Clip the shape + if self._private.path and self._private.shape_clip then + cr:append_path(self._private.path) + cr:clip() + end +end + +-- Layout this widget +function background:layout(_, width, height) + if self._private.widget then + return { base.place_widget_at(self._private.widget, 0, 0, width, height) } + end +end + +-- Fit this widget into the given area +function background:fit(context, width, height) + if not self._private.widget then + return 0, 0 + end + + return base.fit_widget(self, context, self._private.widget, width, height) +end + +--- The widget displayed in the background widget. +-- @property widget +-- @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._private.widget = widget + self:emit_signal("widget::layout_changed") +end + +function background:get_widget() + return self._private.widget +end + +-- Get children element +-- @treturn table The children +function background:get_children() + return {self._private.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 + +--- The background color/pattern/gradient to use. +-- +-- +--![Usage example](../images/AUTOGEN_wibox_container_background_bg.svg) +-- +-- @usage +--local text_widget = { +-- text = 'Hello world!', +-- widget = wibox.widget.textbox +--} +--parent : setup { +-- { +-- text_widget, +-- bg = '#ff0000', +-- widget = wibox.container.background +-- }, +-- { +-- text_widget, +-- bg = '#00ff00', +-- widget = wibox.container.background +-- }, +-- { +-- text_widget, +-- bg = '#0000ff', +-- widget = wibox.container.background +-- }, +-- spacing = 10, +-- layout = wibox.layout.fixed.vertical +--} +-- @property bg +-- @param bg A color string, pattern or gradient +-- @see gears.color + +function background:set_bg(bg) + if bg then + self._private.background = color(bg) + else + self._private.background = nil + end + self:emit_signal("widget::redraw_needed") +end + +function background:get_bg() + return self._private.background +end + +--- The foreground (text) color/pattern/gradient to use. +-- +-- +--![Usage example](../images/AUTOGEN_wibox_container_background_fg.svg) +-- +-- @usage +--local text_widget = { +-- text = 'Hello world!', +-- widget = wibox.widget.textbox +--} +--parent : setup { +-- { +-- text_widget, +-- fg = '#ff0000', +-- widget = wibox.container.background +-- }, +-- { +-- text_widget, +-- fg = '#00ff00', +-- widget = wibox.container.background +-- }, +-- { +-- text_widget, +-- fg = '#0000ff', +-- widget = wibox.container.background +-- }, +-- spacing = 10, +-- layout = wibox.layout.fixed.vertical +--} +-- @property fg +-- @param fg A color string, pattern or gradient +-- @see gears.color + +function background:set_fg(fg) + if fg then + self._private.foreground = color(fg) + else + self._private.foreground = nil + end + self:emit_signal("widget::redraw_needed") +end + +function background:get_fg() + return self._private.foreground +end + +--- The background shap e. +-- +-- Use `set_shape` to set additional shape paramaters. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_container_background_shape.svg) +-- +-- @usage +--parent : setup { +-- { +-- -- Adding a shape without margin may result in cropped output +-- { +-- text = 'Hello world!', +-- widget = wibox.widget.textbox +-- }, +-- shape = gears.shape.hexagon, +-- bg = beautiful.bg_normal, +-- shape_border_color = beautiful.border_color, +-- shape_border_width = beautiful.border_width, +-- widget = wibox.container.background +-- }, +-- { +-- -- To solve this, use a margin +-- { +-- { +-- text = 'Hello world!', +-- widget = wibox.widget.textbox +-- }, +-- left = 10, +-- right = 10, +-- top = 3, +-- bottom = 3, +-- 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.container.background +-- }, +-- spacing = 10, +-- layout = wibox.layout.fixed.vertical +--} +-- @property shape +-- @param shape A function taking a context, width and height as arguments +-- @see gears.shape +-- @see set_shape + +--- Set the background shape. +-- +-- Any other arguments will be passed to the shape function +-- @param shape A function taking a context, width and height as arguments +-- @see gears.shape +-- @see shape +function background:set_shape(shape, ...) + local args = {...} + + if shape == self._private.shape and #args == 0 then return end + + self._private.shape = shape + self._private.shape_args = {...} + self:emit_signal("widget::redraw_needed") +end + +function background:get_shape() + return self._private.shape +end + +--- When a `shape` is set, also draw a border. +-- +-- See `wibox.container.background.shape` for an usage example. +-- @property shape_border_width +-- @tparam number width The border width + +function background:set_shape_border_width(width) + if self._private.shape_border_width == width then return end + + self._private.shape_border_width = width + self:emit_signal("widget::redraw_needed") +end + +function background:get_shape_border_width() + return self._private.shape_border_width +end + +--- When a `shape` is set, also draw a border. +-- +-- See `wibox.container.background.shape` for an usage example. +-- @property shape_border_color +-- @param[opt=self._private.foreground] fg The border color, pattern or gradient +-- @see gears.color + +function background:set_shape_border_color(fg) + if self._private.shape_border_color == fg then return end + + self._private.shape_border_color = fg + self:emit_signal("widget::redraw_needed") +end + +function background:get_shape_border_color() + return self._private.shape_border_color +end + +--- When a `shape` is set, make sure nothing is drawn outside of it. +-- +-- +--![Usage example](../images/AUTOGEN_wibox_container_background_clip.svg) +-- +-- @usage +--parent : setup { +-- { +-- -- Some content may be outside of the shape +-- { +-- text = 'Hello\nworld!', +-- widget = wibox.widget.textbox +-- }, +-- shape = gears.shape.circle, +-- bg = beautiful.bg_normal, +-- shape_border_color = beautiful.border_color, +-- widget = wibox.container.background +-- }, +-- { +-- -- To solve this, clip the content +-- { +-- text = 'Hello\nworld!', +-- widget = wibox.widget.textbox +-- }, +-- shape_clip = true, +-- shape = gears.shape.circle, +-- bg = beautiful.bg_normal, +-- shape_border_color = beautiful.border_color, +-- widget = wibox.container.background +-- }, +-- spacing = 10, +-- layout = wibox.layout.fixed.vertical +--} +-- @property shape_clip +-- @tparam boolean value If the shape clip is enable + +function background:set_shape_clip(value) + if self._private.shape_clip == value then return end + + self._private.shape_clip = value + self:emit_signal("widget::redraw_needed") +end + +function background:get_shape_clip() + return self._private.shape_clip or false +end + +--- 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. +-- @property bgimage +-- @param image A background image or a function +-- @see gears.surface + +function background:set_bgimage(image, ...) + self._private.bgimage = type(image) == "function" and image or surface.load(image) + self._private.bgimage_args = {...} + self:emit_signal("widget::redraw_needed") +end + +function background:get_bgimage() + return self._private.bgimage +end + +--- Returns a new background container. +-- +-- A background container 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 +-- @function wibox.container.background +local function new(widget, bg, shape) + local ret = base.make_widget(nil, nil, { + enable_properties = true, + }) + + util.table.crush(ret, background, true) + + ret._private.shape = shape + + ret:set_widget(widget) + ret:set_bg(bg) + + return ret +end + +function background.mt:__call(...) + return new(...) +end + +--Imported documentation + + +--- Get a widex index. +-- @param widget The widget to look for +-- @param[opt] recursive Also check sub-widgets +-- @param[opt] ... Aditional widgets to add at the end of the \"path\" +-- @return The index +-- @return The parent layout +-- @return The path between \"self\" and \"widget\" +-- @function index + +--- Get all direct and indirect children widgets. +-- This will scan all containers recursively to find widgets +-- Warning: This method it prone to stack overflow id the widget, or any of its +-- children, contain (directly or indirectly) itself. +-- @treturn table The children +-- @function get_all_children + +--- Set a declarative widget hierarchy description. +-- See [The declarative layout system](../documentation/03-declarative-layout.md.html) +-- @param args An array containing the widgets disposition +-- @function setup + +--- Force a widget height. +-- @property forced_height +-- @tparam number|nil height The height (`nil` for automatic) + +--- Force a widget width. +-- @property forced_width +-- @tparam number|nil width The width (`nil` for automatic) + +--- The widget opacity (transparency). +-- @property opacity +-- @tparam[opt=1] number opacity The opacity (between 0 and 1) + +--- The widget visibility. +-- @property visible +-- @param boolean + +--- Set/get a widget's buttons. +-- @param _buttons The table of buttons that should bind to the widget. +-- @function buttons + +--- Emit a signal and ensure all parent widgets in the hierarchies also +-- forward the signal. This is useful to track signals when there is a dynamic +-- set of containers and layouts wrapping the widget. +-- @tparam string signal_name +-- @param ... Other arguments +-- @function emit_signal_recursive + +--- When the layout (size) change. +-- This signal is emitted when the previous results of `:layout()` and `:fit()` +-- are no longer valid. Unless this signal is emitted, `:layout()` and `:fit()` +-- must return the same result when called with the same arguments. +-- @signal widget::layout_changed +-- @see widget::redraw_needed + +--- When the widget content changed. +-- This signal is emitted when the content of the widget changes. The widget will +-- be redrawn, it is not re-layouted. Put differently, it is assumed that +-- `:layout()` and `:fit()` would still return the same results as before. +-- @signal widget::redraw_needed +-- @see widget::layout_changed + +--- When a mouse button is pressed over the widget. +-- @signal button::press +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When a mouse button is released over the widget. +-- @signal button::release +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse enter a widget. +-- @signal mouse::enter +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse leave a widget. +-- @signal mouse::leave +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + + +--Imported documentation + + +--- Disconnect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback that should be disconnected +-- @function disconnect_signal + +--- Emit a signal. +-- +-- @tparam string name The name of the signal +-- @param ... Extra arguments for the callback functions. Each connected +-- function receives the object as first argument and then any extra arguments +-- that are given to emit_signal() +-- @function emit_signal + +--- Connect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function connect_signal + +--- Connect to a signal weakly. This allows the callback function to be garbage +-- collected and automatically disconnects the signal when that happens. +-- +-- **Warning:** +-- Only use this function if you really, really, really know what you +-- are doing. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function weak_connect_signal + + +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 0000000..a210387 --- /dev/null +++ b/lib/wibox/container/constraint.lua @@ -0,0 +1,371 @@ +--------------------------------------------------------------------------- +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_container_defaults_constraint.svg) +-- +-- @author Lukáš Hrázký +-- @copyright 2012 Lukáš Hrázký +-- @classmod wibox.container.constraint +--------------------------------------------------------------------------- + +local setmetatable = setmetatable +local base = require("wibox.widget.base") +local util = require("awful.util") +local math = math + +local constraint = { mt = {} } + +-- Layout a constraint layout +function constraint:layout(_, width, height) + if self._private.widget then + return { base.place_widget_at(self._private.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._private.widget then + w = self._private.strategy(width, self._private.width) + h = self._private.strategy(height, self._private.height) + + w, h = base.fit_widget(self, context, self._private.widget, w, h) + else + w, h = 0, 0 + end + + w = self._private.strategy(w, self._private.width) + h = self._private.strategy(h, self._private.height) + + return w, h +end + +--- The widget to be constrained. +-- @property widget +-- @tparam widget widget The widget + +function constraint:set_widget(widget) + self._private.widget = widget + self:emit_signal("widget::layout_changed") +end + +function constraint:get_widget() + return self._private.widget +end + +--- Get the number of children element +-- @treturn table The children +function constraint:get_children() + return {self._private.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. +-- @property strategy + +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._private.strategy = func[val] + self:emit_signal("widget::layout_changed") +end + +function constraint:get_strategy() + return self._private.strategy +end + +--- Set the maximum width to val. nil for no width limit. +-- @property height +-- @param number + +function constraint:set_width(val) + self._private.width = val + self:emit_signal("widget::layout_changed") +end + +function constraint:get_width() + return self._private.width +end + +--- Set the maximum height to val. nil for no height limit. +-- @property width +-- @param number + +function constraint:set_height(val) + self._private.height = val + self:emit_signal("widget::layout_changed") +end + +function constraint:get_height() + return self._private.height +end + +--- Reset this layout. The widget will be unreferenced, strategy set to "max" +-- and the constraints set to nil. +function constraint:reset() + self._private.width = nil + self._private.height = nil + self:set_strategy("max") + self:set_widget(nil) +end + +--- Returns a new constraint container. +-- This container 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. +-- @treturn table A new constraint container +-- @function wibox.container.constraint +local function new(widget, strategy, width, height) + local ret = base.make_widget(nil, nil, {enable_properties = true}) + + util.table.crush(ret, constraint, true) + + 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 + +--Imported documentation + + +--- Get a widex index. +-- @param widget The widget to look for +-- @param[opt] recursive Also check sub-widgets +-- @param[opt] ... Aditional widgets to add at the end of the \"path\" +-- @return The index +-- @return The parent layout +-- @return The path between \"self\" and \"widget\" +-- @function index + +--- Get all direct and indirect children widgets. +-- This will scan all containers recursively to find widgets +-- Warning: This method it prone to stack overflow id the widget, or any of its +-- children, contain (directly or indirectly) itself. +-- @treturn table The children +-- @function get_all_children + +--- Set a declarative widget hierarchy description. +-- See [The declarative layout system](../documentation/03-declarative-layout.md.html) +-- @param args An array containing the widgets disposition +-- @function setup + +--- Force a widget height. +-- @property forced_height +-- @tparam number|nil height The height (`nil` for automatic) + +--- Force a widget width. +-- @property forced_width +-- @tparam number|nil width The width (`nil` for automatic) + +--- The widget opacity (transparency). +-- @property opacity +-- @tparam[opt=1] number opacity The opacity (between 0 and 1) + +--- The widget visibility. +-- @property visible +-- @param boolean + +--- Set/get a widget's buttons. +-- @param _buttons The table of buttons that should bind to the widget. +-- @function buttons + +--- Emit a signal and ensure all parent widgets in the hierarchies also +-- forward the signal. This is useful to track signals when there is a dynamic +-- set of containers and layouts wrapping the widget. +-- @tparam string signal_name +-- @param ... Other arguments +-- @function emit_signal_recursive + +--- When the layout (size) change. +-- This signal is emitted when the previous results of `:layout()` and `:fit()` +-- are no longer valid. Unless this signal is emitted, `:layout()` and `:fit()` +-- must return the same result when called with the same arguments. +-- @signal widget::layout_changed +-- @see widget::redraw_needed + +--- When the widget content changed. +-- This signal is emitted when the content of the widget changes. The widget will +-- be redrawn, it is not re-layouted. Put differently, it is assumed that +-- `:layout()` and `:fit()` would still return the same results as before. +-- @signal widget::redraw_needed +-- @see widget::layout_changed + +--- When a mouse button is pressed over the widget. +-- @signal button::press +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When a mouse button is released over the widget. +-- @signal button::release +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse enter a widget. +-- @signal mouse::enter +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse leave a widget. +-- @signal mouse::leave +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + + +--Imported documentation + + +--- Disconnect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback that should be disconnected +-- @function disconnect_signal + +--- Emit a signal. +-- +-- @tparam string name The name of the signal +-- @param ... Extra arguments for the callback functions. Each connected +-- function receives the object as first argument and then any extra arguments +-- that are given to emit_signal() +-- @function emit_signal + +--- Connect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function connect_signal + +--- Connect to a signal weakly. This allows the callback function to be garbage +-- collected and automatically disconnects the signal when that happens. +-- +-- **Warning:** +-- Only use this function if you really, really, really know what you +-- are doing. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function weak_connect_signal + + +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 0000000..93158ab --- /dev/null +++ b/lib/wibox/container/init.lua @@ -0,0 +1,21 @@ +--------------------------------------------------------------------------- +--- Collection of containers that can be used in widget boxes +-- +-- @author Uli Schlachter +-- @copyright 2010 Uli Schlachter +-- @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"); + radialprogressbar = require("wibox.container.radialprogressbar"); + arcchart = require("wibox.container.arcchart"); +}, {__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 0000000..edf9673 --- /dev/null +++ b/lib/wibox/container/margin.lua @@ -0,0 +1,419 @@ +--------------------------------------------------------------------------- +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_container_defaults_margin.svg) +-- +-- @author Uli Schlachter +-- @copyright 2010 Uli Schlachter +-- @classmod wibox.container.margin +--------------------------------------------------------------------------- + +local pairs = pairs +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._private.left + local y = self._private.top + local w = self._private.right + local h = self._private.bottom + local color = self._private.color + + if not self._private.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._private.widget then + local x = self._private.left + local y = self._private.top + local w = self._private.right + local h = self._private.bottom + + return { base.place_widget_at(self._private.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._private.left + self._private.right + local extra_h = self._private.top + self._private.bottom + local w, h = 0, 0 + if self._private.widget then + w, h = base.fit_widget(self, context, self._private.widget, width - extra_w, height - extra_h) + end + + if self._private.draw_empty == false and (w == 0 or h == 0) then + return 0, 0 + end + + return w + extra_w, h + extra_h +end + +--- The widget to be wrapped the the margins. +-- @property widget +-- @tparam widget widget The widget + +function margin:set_widget(widget) + if widget then + base.check_widget(widget) + end + self._private.widget = widget + self:emit_signal("widget::layout_changed") +end + +function margin:get_widget() + return self._private.widget +end + +-- Get the number of children element +-- @treturn table The children +function margin:get_children() + return {self._private.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. +-- @property margins +-- @tparam number val The margin value + +function margin:set_margins(val) + if self._private.left == val and + self._private.right == val and + self._private.top == val and + self._private.bottom == val then + return + end + + self._private.left = val + self._private.right = val + self._private.top = val + self._private.bottom = val + self:emit_signal("widget::layout_changed") +end + +--- Set the margins color to create a border. +-- @property color +-- @param color A color used to fill the margin. + +function margin:set_color(color) + self._private.color = color and gcolor(color) + self:emit_signal("widget::redraw_needed") +end + +function margin:get_color() + return self._private.color +end + +--- Draw the margin even if the content size is 0x0 (default: true) +-- @function draw_empty +-- @tparam boolean draw_empty Draw nothing is content is 0x0 or draw the margin anyway + +function margin:set_draw_empty(draw_empty) + self._private.draw_empty = draw_empty + self:emit_signal("widget::layout_changed") +end + +function margin:get_draw_empty() + return self._private.draw_empty +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 margin The new margin to use. +-- @property left + +--- Set the right margin that this layout adds to its widget. +-- @param margin The new margin to use. +-- @property right + +--- Set the top margin that this layout adds to its widget. +-- @param margin The new margin to use. +-- @property top + +--- Set the bottom margin that this layout adds to its widget. +-- @param margin The new margin to use. +-- @property bottom + +-- Create setters for each direction +for _, v in pairs({ "left", "right", "top", "bottom" }) do + margin["set_" .. v] = function(layout, val) + if layout._private[v] == val then return end + layout._private[v] = val + layout:emit_signal("widget::layout_changed") + end + + margin["get_" .. v] = function(layout) + return layout._private[v] + end +end + +--- Returns a new margin container. +-- @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 +-- @treturn table A new margin container +-- @function wibox.container.margin +local function new(widget, left, right, top, bottom, color, draw_empty) + local ret = base.make_widget(nil, nil, {enable_properties = true}) + + util.table.crush(ret, margin, true) + + 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 + +--Imported documentation + + +--- Get a widex index. +-- @param widget The widget to look for +-- @param[opt] recursive Also check sub-widgets +-- @param[opt] ... Aditional widgets to add at the end of the \"path\" +-- @return The index +-- @return The parent layout +-- @return The path between \"self\" and \"widget\" +-- @function index + +--- Get all direct and indirect children widgets. +-- This will scan all containers recursively to find widgets +-- Warning: This method it prone to stack overflow id the widget, or any of its +-- children, contain (directly or indirectly) itself. +-- @treturn table The children +-- @function get_all_children + +--- Set a declarative widget hierarchy description. +-- See [The declarative layout system](../documentation/03-declarative-layout.md.html) +-- @param args An array containing the widgets disposition +-- @function setup + +--- Force a widget height. +-- @property forced_height +-- @tparam number|nil height The height (`nil` for automatic) + +--- Force a widget width. +-- @property forced_width +-- @tparam number|nil width The width (`nil` for automatic) + +--- The widget opacity (transparency). +-- @property opacity +-- @tparam[opt=1] number opacity The opacity (between 0 and 1) + +--- The widget visibility. +-- @property visible +-- @param boolean + +--- Set/get a widget's buttons. +-- @param _buttons The table of buttons that should bind to the widget. +-- @function buttons + +--- Emit a signal and ensure all parent widgets in the hierarchies also +-- forward the signal. This is useful to track signals when there is a dynamic +-- set of containers and layouts wrapping the widget. +-- @tparam string signal_name +-- @param ... Other arguments +-- @function emit_signal_recursive + +--- When the layout (size) change. +-- This signal is emitted when the previous results of `:layout()` and `:fit()` +-- are no longer valid. Unless this signal is emitted, `:layout()` and `:fit()` +-- must return the same result when called with the same arguments. +-- @signal widget::layout_changed +-- @see widget::redraw_needed + +--- When the widget content changed. +-- This signal is emitted when the content of the widget changes. The widget will +-- be redrawn, it is not re-layouted. Put differently, it is assumed that +-- `:layout()` and `:fit()` would still return the same results as before. +-- @signal widget::redraw_needed +-- @see widget::layout_changed + +--- When a mouse button is pressed over the widget. +-- @signal button::press +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When a mouse button is released over the widget. +-- @signal button::release +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse enter a widget. +-- @signal mouse::enter +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse leave a widget. +-- @signal mouse::leave +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + + +--Imported documentation + + +--- Disconnect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback that should be disconnected +-- @function disconnect_signal + +--- Emit a signal. +-- +-- @tparam string name The name of the signal +-- @param ... Extra arguments for the callback functions. Each connected +-- function receives the object as first argument and then any extra arguments +-- that are given to emit_signal() +-- @function emit_signal + +--- Connect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function connect_signal + +--- Connect to a signal weakly. This allows the callback function to be garbage +-- collected and automatically disconnects the signal when that happens. +-- +-- **Warning:** +-- Only use this function if you really, really, really know what you +-- are doing. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function weak_connect_signal + + +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 0000000..fc275cf --- /dev/null +++ b/lib/wibox/container/mirror.lua @@ -0,0 +1,340 @@ +--------------------------------------------------------------------------- +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_container_defaults_mirror.svg) +-- +-- @author dodo +-- @copyright 2012 dodo +-- @classmod wibox.container.mirror +--------------------------------------------------------------------------- + +local type = type +local error = error +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._private.widget then return end + + local m = matrix.identity + local t = { x = 0, y = 0 } -- translation + local s = { x = 1, y = 1 } -- scale + if self._private.horizontal then + t.x = width + s.x = -1 + end + if self._private.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._private.widget, m, width, height) } +end + +-- Fit this layout into the given area +function mirror:fit(context, ...) + if not self._private.widget then + return 0, 0 + end + return base.fit_widget(self, context, self._private.widget, ...) +end + +--- The widget to be reflected. +-- @property widget +-- @tparam widget widget The widget + +function mirror:set_widget(widget) + if widget then + base.check_widget(widget) + end + self._private.widget = widget + self:emit_signal("widget::layout_changed") +end + +function mirror:get_widget() + return self._private.widget +end + +--- Get the number of children element +-- @treturn table The children +function mirror:get_children() + return {self._private.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._private.horizontal = false + self._private.vertical = false + self:set_widget(nil) +end + +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._private[ref] = reflection[ref] + end + end + self:emit_signal("widget::layout_changed") +end + +--- Get the reflection of this mirror layout. +-- @property reflection +-- @param table reflection A table of booleans with the keys "horizontal", "vertical". +-- @param boolean reflection.horizontal +-- @param boolean reflection.vertical + +function mirror:get_reflection() + return { horizontal = self._private.horizontal, vertical = self._private.vertical } +end + +--- Returns a new mirror container. +-- A mirror container 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. +-- @treturn table A new mirror container +-- @function wibox.container.mirror +local function new(widget, reflection) + local ret = base.make_widget(nil, nil, {enable_properties = true}) + ret._private.horizontal = false + ret._private.vertical = false + + util.table.crush(ret, mirror, true) + + ret:set_widget(widget) + ret:set_reflection(reflection or {}) + + return ret +end + +function mirror.mt:__call(...) + return new(...) +end + +--Imported documentation + + +--- Get a widex index. +-- @param widget The widget to look for +-- @param[opt] recursive Also check sub-widgets +-- @param[opt] ... Aditional widgets to add at the end of the \"path\" +-- @return The index +-- @return The parent layout +-- @return The path between \"self\" and \"widget\" +-- @function index + +--- Get all direct and indirect children widgets. +-- This will scan all containers recursively to find widgets +-- Warning: This method it prone to stack overflow id the widget, or any of its +-- children, contain (directly or indirectly) itself. +-- @treturn table The children +-- @function get_all_children + +--- Set a declarative widget hierarchy description. +-- See [The declarative layout system](../documentation/03-declarative-layout.md.html) +-- @param args An array containing the widgets disposition +-- @function setup + +--- Force a widget height. +-- @property forced_height +-- @tparam number|nil height The height (`nil` for automatic) + +--- Force a widget width. +-- @property forced_width +-- @tparam number|nil width The width (`nil` for automatic) + +--- The widget opacity (transparency). +-- @property opacity +-- @tparam[opt=1] number opacity The opacity (between 0 and 1) + +--- The widget visibility. +-- @property visible +-- @param boolean + +--- Set/get a widget's buttons. +-- @param _buttons The table of buttons that should bind to the widget. +-- @function buttons + +--- Emit a signal and ensure all parent widgets in the hierarchies also +-- forward the signal. This is useful to track signals when there is a dynamic +-- set of containers and layouts wrapping the widget. +-- @tparam string signal_name +-- @param ... Other arguments +-- @function emit_signal_recursive + +--- When the layout (size) change. +-- This signal is emitted when the previous results of `:layout()` and `:fit()` +-- are no longer valid. Unless this signal is emitted, `:layout()` and `:fit()` +-- must return the same result when called with the same arguments. +-- @signal widget::layout_changed +-- @see widget::redraw_needed + +--- When the widget content changed. +-- This signal is emitted when the content of the widget changes. The widget will +-- be redrawn, it is not re-layouted. Put differently, it is assumed that +-- `:layout()` and `:fit()` would still return the same results as before. +-- @signal widget::redraw_needed +-- @see widget::layout_changed + +--- When a mouse button is pressed over the widget. +-- @signal button::press +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When a mouse button is released over the widget. +-- @signal button::release +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse enter a widget. +-- @signal mouse::enter +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse leave a widget. +-- @signal mouse::leave +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + + +--Imported documentation + + +--- Disconnect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback that should be disconnected +-- @function disconnect_signal + +--- Emit a signal. +-- +-- @tparam string name The name of the signal +-- @param ... Extra arguments for the callback functions. Each connected +-- function receives the object as first argument and then any extra arguments +-- that are given to emit_signal() +-- @function emit_signal + +--- Connect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function connect_signal + +--- Connect to a signal weakly. This allows the callback function to be garbage +-- collected and automatically disconnects the signal when that happens. +-- +-- **Warning:** +-- Only use this function if you really, really, really know what you +-- are doing. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function weak_connect_signal + + +return setmetatable(mirror, mirror.mt) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/wibox/container/radialprogressbar.lua b/lib/wibox/container/radialprogressbar.lua new file mode 100644 index 0000000..7c67906 --- /dev/null +++ b/lib/wibox/container/radialprogressbar.lua @@ -0,0 +1,481 @@ +--------------------------------------------------------------------------- +-- +-- A circular progressbar wrapper. +-- +-- If no child `widget` is set, then the radialprogressbar will take all the +-- available size. Use a `wibox.container.constraint` to prevent this. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_container_defaults_radialprogressbar.svg) +-- +-- @author Emmanuel Lepage Vallee <elv1313@gmail.com> +-- @copyright 2013 Emmanuel Lepage Vallee +-- @classmod wibox.container.radialprogressbar +--------------------------------------------------------------------------- + +local setmetatable = setmetatable +local base = require("wibox.widget.base") +local shape = require("gears.shape" ) +local util = require( "awful.util" ) +local color = require( "gears.color" ) +local beautiful = require("beautiful" ) + +local default_outline_width = 2 + +local radialprogressbar = { mt = {} } + +--- The progressbar border background color. +-- @beautiful beautiful.radialprogressbar_border_color + +--- The progressbar foreground color. +-- @beautiful beautiful.radialprogressbar_color + +--- The progressbar border width. +-- @beautiful beautiful.radialprogressbar_border_width + +--- The padding between the outline and the progressbar. +-- @beautiful beautiful.radialprogressbar_paddings +-- @tparam[opt=0] table|number paddings A number or a table +-- @tparam[opt=0] number paddings.top +-- @tparam[opt=0] number paddings.bottom +-- @tparam[opt=0] number paddings.left +-- @tparam[opt=0] number paddings.right + +local function outline_workarea(self, width, height) + local border_width = self._private.border_width or + beautiful.radialprogressbar_border_width or default_outline_width + + local x, y = 0, 0 + + -- Make sure the border fit in the clip area + local offset = border_width/2 + x, y = x + offset, y+offset + width, height = width-2*offset, height-2*offset + + return {x=x, y=y, width=width, height=height}, offset +end + +-- The child widget area +local function content_workarea(self, width, height) + local padding = self._private.paddings or {} + local wa = outline_workarea(self, width, height) + + wa.x = wa.x + (padding.left or 0) + wa.y = wa.y + (padding.top or 0) + wa.width = wa.width - (padding.left or 0) - (padding.right or 0) + wa.height = wa.height - (padding.top or 0) - (padding.bottom or 0) + + return wa +end + +-- Draw the radial outline and progress +function radialprogressbar:after_draw_children(_, cr, width, height) + cr:restore() + + local border_width = self._private.border_width or + beautiful.radialprogressbar_border_width or default_outline_width + + local wa = outline_workarea(self, width, height) + cr:translate(wa.x, wa.y) + + -- Draw the outline + shape.rounded_bar(cr, wa.width, wa.height) + cr:set_source(color(self:get_border_color() or "#0000ff")) + cr:set_line_width(border_width) + cr:stroke() + + -- Draw the progress + cr:set_source(color(self:get_color() or "#ff00ff")) + shape.radial_progress(cr, wa.width, wa.height, self._percent or 0) + cr:set_line_width(border_width) + cr:stroke() + +end + +-- Set the clip +function radialprogressbar:before_draw_children(_, cr, width, height) + cr:save() + local wa = content_workarea(self, width, height) + cr:translate(wa.x, wa.y) + shape.rounded_bar(cr, wa.width, wa.height) + cr:clip() + cr:translate(-wa.x, -wa.y) +end + +-- Layout this layout +function radialprogressbar:layout(_, width, height) + if self._private.widget then + local wa = content_workarea(self, width, height) + + return { base.place_widget_at( + self._private.widget, wa.x, wa.y, wa.width, wa.height + ) } + end +end + +-- Fit this layout into the given area +function radialprogressbar:fit(context, width, height) + if self._private.widget then + local wa = content_workarea(self, width, height) + local w, h = base.fit_widget(self, context, self._private.widget, wa.width, wa.height) + return wa.x + w, wa.y + h + end + + return width, height +end + +--- The widget to wrap in a radial proggressbar. +-- @property widget +-- @tparam widget widget The widget + +function radialprogressbar:set_widget(widget) + if widget then + base.check_widget(widget) + end + self._private.widget = widget + self:emit_signal("widget::layout_changed") +end + +--- Get the children elements +-- @treturn table The children +function radialprogressbar:get_children() + return {self._private.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 radialprogressbar:set_children(children) + self._private.widget = children and children[1] + self:emit_signal("widget::layout_changed") +end + +--- Reset this container. +function radialprogressbar:reset() + self:set_widget(nil) +end + +for _,v in ipairs {"left", "right", "top", "bottom"} do + radialprogressbar["set_"..v.."_padding"] = function(self, val) + self._private.paddings = self._private.paddings or {} + self._private.paddings[v] = val + self:emit_signal("widget::redraw_needed") + self:emit_signal("widget::layout_changed") + end +end + +--- The padding between the outline and the progressbar. +-- +-- +--![Usage example](../images/AUTOGEN_wibox_container_radialprogressbar_padding.svg) +-- +-- @property paddings +-- @tparam[opt=0] table|number paddings A number or a table +-- @tparam[opt=0] number paddings.top +-- @tparam[opt=0] number paddings.bottom +-- @tparam[opt=0] number paddings.left +-- @tparam[opt=0] number paddings.right + +--- The progressbar value. +-- +-- +--![Usage example](../images/AUTOGEN_wibox_container_radialprogressbar_value.svg) +-- +-- @property value +-- @tparam number value Between min_value and max_value + +function radialprogressbar:set_value(val) + if not val then self._percent = 0; return end + + if val > self._private.max_value then + self:set_max_value(val) + elseif val < self._private.min_value then + self:set_min_value(val) + end + + local delta = self._private.max_value - self._private.min_value + + self._percent = val/delta + self:emit_signal("widget::redraw_needed") +end + +--- The border background color. +-- +-- +--![Usage example](../images/AUTOGEN_wibox_container_radialprogressbar_border_color.svg) +-- +-- @property border_color + +--- The border foreground color. +-- +-- +--![Usage example](../images/AUTOGEN_wibox_container_radialprogressbar_color.svg) +-- +-- @property color + +--- The border width. +-- +-- +--![Usage example](../images/AUTOGEN_wibox_container_radialprogressbar_border_width.svg) +-- +-- @property border_width +-- @tparam[opt=3] number border_width + +--- The minimum value. +-- @property min_value + +--- The maximum value. +-- @property max_value + +for _, prop in ipairs {"max_value", "min_value", "border_color", "color", + "border_width", "paddings"} do + radialprogressbar["set_"..prop] = function(self, value) + self._private[prop] = value + self:emit_signal("property::"..prop) + self:emit_signal("widget::redraw_needed") + end + radialprogressbar["get_"..prop] = function(self) + return self._private[prop] or beautiful["radialprogressbar_"..prop] + end +end + +function radialprogressbar:set_paddings(val) + self._private.paddings = type(val) == "number" and { + left = val, + right = val, + top = val, + bottom = val, + } or val or {} + self:emit_signal("property::paddings") + self:emit_signal("widget::redraw_needed") + self:emit_signal("widget::layout_changed") +end + +--- Returns a new radialprogressbar layout. A radialprogressbar layout +-- radialprogressbars a given widget. Use `.widget` to set the widget. +-- @param[opt] widget The widget to display. +-- @function wibox.container.radialprogressbar +local function new(widget) + local ret = base.make_widget(nil, nil, { + enable_properties = true, + }) + + util.table.crush(ret, radialprogressbar) + ret._private.max_value = 1 + ret._private.min_value = 0 + + ret:set_widget(widget) + + return ret +end + +function radialprogressbar.mt:__call(_, ...) + return new(...) +end + +--Imported documentation + + +--- Get a widex index. +-- @param widget The widget to look for +-- @param[opt] recursive Also check sub-widgets +-- @param[opt] ... Aditional widgets to add at the end of the \"path\" +-- @return The index +-- @return The parent layout +-- @return The path between \"self\" and \"widget\" +-- @function index + +--- Get all direct and indirect children widgets. +-- This will scan all containers recursively to find widgets +-- Warning: This method it prone to stack overflow id the widget, or any of its +-- children, contain (directly or indirectly) itself. +-- @treturn table The children +-- @function get_all_children + +--- Set a declarative widget hierarchy description. +-- See [The declarative layout system](../documentation/03-declarative-layout.md.html) +-- @param args An array containing the widgets disposition +-- @function setup + +--- Force a widget height. +-- @property forced_height +-- @tparam number|nil height The height (`nil` for automatic) + +--- Force a widget width. +-- @property forced_width +-- @tparam number|nil width The width (`nil` for automatic) + +--- The widget opacity (transparency). +-- @property opacity +-- @tparam[opt=1] number opacity The opacity (between 0 and 1) + +--- The widget visibility. +-- @property visible +-- @param boolean + +--- Set/get a widget's buttons. +-- @param _buttons The table of buttons that should bind to the widget. +-- @function buttons + +--- Emit a signal and ensure all parent widgets in the hierarchies also +-- forward the signal. This is useful to track signals when there is a dynamic +-- set of containers and layouts wrapping the widget. +-- @tparam string signal_name +-- @param ... Other arguments +-- @function emit_signal_recursive + +--- When the layout (size) change. +-- This signal is emitted when the previous results of `:layout()` and `:fit()` +-- are no longer valid. Unless this signal is emitted, `:layout()` and `:fit()` +-- must return the same result when called with the same arguments. +-- @signal widget::layout_changed +-- @see widget::redraw_needed + +--- When the widget content changed. +-- This signal is emitted when the content of the widget changes. The widget will +-- be redrawn, it is not re-layouted. Put differently, it is assumed that +-- `:layout()` and `:fit()` would still return the same results as before. +-- @signal widget::redraw_needed +-- @see widget::layout_changed + +--- When a mouse button is pressed over the widget. +-- @signal button::press +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When a mouse button is released over the widget. +-- @signal button::release +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse enter a widget. +-- @signal mouse::enter +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse leave a widget. +-- @signal mouse::leave +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + + +--Imported documentation + + +--- Disconnect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback that should be disconnected +-- @function disconnect_signal + +--- Emit a signal. +-- +-- @tparam string name The name of the signal +-- @param ... Extra arguments for the callback functions. Each connected +-- function receives the object as first argument and then any extra arguments +-- that are given to emit_signal() +-- @function emit_signal + +--- Connect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function connect_signal + +--- Connect to a signal weakly. This allows the callback function to be garbage +-- collected and automatically disconnects the signal when that happens. +-- +-- **Warning:** +-- Only use this function if you really, really, really know what you +-- are doing. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function weak_connect_signal + + +return setmetatable(radialprogressbar, radialprogressbar.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 0000000..5fe4a8e --- /dev/null +++ b/lib/wibox/container/rotate.lua @@ -0,0 +1,384 @@ +--------------------------------------------------------------------------- +-- A container rotating the conained widget by 90 degrees. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_container_defaults_rotate.svg) +-- +-- @author Uli Schlachter +-- @copyright 2010 Uli Schlachter +-- @classmod wibox.container.rotate +--------------------------------------------------------------------------- + +local error = error +local pi = math.pi +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._private.widget or not self._private.widget._private.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._private.widget, m, transform(self, width, height)) } +end + +-- Fit this layout into the given area +function rotate:fit(context, width, height) + if not self._private.widget then + return 0, 0 + end + return transform(self, base.fit_widget(self, context, self._private.widget, transform(self, width, height))) +end + +--- The widget to be rotated. +-- @property widget +-- @tparam widget widget The widget + +function rotate:set_widget(widget) + if widget then + base.check_widget(widget) + end + self._private.widget = widget + self:emit_signal("widget::layout_changed") +end + +function rotate:get_widget() + return self._private.widget +end + +--- Get the number of children element +-- @treturn table The children +function rotate:get_children() + return {self._private.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._private.direction = nil + self:set_widget(nil) +end + +--Imported documentation + + +--- Get a widex index. +-- @param widget The widget to look for +-- @param[opt] recursive Also check sub-widgets +-- @param[opt] ... Aditional widgets to add at the end of the \"path\" +-- @return The index +-- @return The parent layout +-- @return The path between \"self\" and \"widget\" +-- @function index + +--- Get all direct and indirect children widgets. +-- This will scan all containers recursively to find widgets +-- Warning: This method it prone to stack overflow id the widget, or any of its +-- children, contain (directly or indirectly) itself. +-- @treturn table The children +-- @function get_all_children + +--- Set a declarative widget hierarchy description. +-- See [The declarative layout system](../documentation/03-declarative-layout.md.html) +-- @param args An array containing the widgets disposition +-- @function setup + +--- Force a widget height. +-- @property forced_height +-- @tparam number|nil height The height (`nil` for automatic) + +--- Force a widget width. +-- @property forced_width +-- @tparam number|nil width The width (`nil` for automatic) + +--- The widget opacity (transparency). +-- @property opacity +-- @tparam[opt=1] number opacity The opacity (between 0 and 1) + +--- The widget visibility. +-- @property visible +-- @param boolean + +--- Set/get a widget's buttons. +-- @param _buttons The table of buttons that should bind to the widget. +-- @function buttons + +--- Emit a signal and ensure all parent widgets in the hierarchies also +-- forward the signal. This is useful to track signals when there is a dynamic +-- set of containers and layouts wrapping the widget. +-- @tparam string signal_name +-- @param ... Other arguments +-- @function emit_signal_recursive + +--- When the layout (size) change. +-- This signal is emitted when the previous results of `:layout()` and `:fit()` +-- are no longer valid. Unless this signal is emitted, `:layout()` and `:fit()` +-- must return the same result when called with the same arguments. +-- @signal widget::layout_changed +-- @see widget::redraw_needed + +--- When the widget content changed. +-- This signal is emitted when the content of the widget changes. The widget will +-- be redrawn, it is not re-layouted. Put differently, it is assumed that +-- `:layout()` and `:fit()` would still return the same results as before. +-- @signal widget::redraw_needed +-- @see widget::layout_changed + +--- When a mouse button is pressed over the widget. +-- @signal button::press +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When a mouse button is released over the widget. +-- @signal button::release +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse enter a widget. +-- @signal mouse::enter +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse leave a widget. +-- @signal mouse::leave +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + + +--Imported documentation + + +--- Disconnect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback that should be disconnected +-- @function disconnect_signal + +--- Emit a signal. +-- +-- @tparam string name The name of the signal +-- @param ... Extra arguments for the callback functions. Each connected +-- function receives the object as first argument and then any extra arguments +-- that are given to emit_signal() +-- @function emit_signal + +--- Connect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function connect_signal + +--- Connect to a signal weakly. This allows the callback function to be garbage +-- collected and automatically disconnects the signal when that happens. +-- +-- **Warning:** +-- Only use this function if you really, really, really know what you +-- are doing. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function weak_connect_signal + + +--- The direction of this rotating container. +-- Valid values are: +-- +-- * *north* +-- * *east* +-- * *south* +-- * *north* +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_container_rotate_angle.svg) +-- +-- @usage +--local normal = create_arrow('Normal') +--local north = wibox.container { +-- create_arrow('North'), +-- direction = 'north', +-- widget = wibox.container.rotate +--} +--local south = wibox.container { +-- create_arrow('South'), +-- direction = 'south', +-- widget = wibox.container.rotate +--} +--local east = wibox.container { +-- create_arrow('East'), +-- direction = 'east', +-- widget = wibox.container.rotate +--} +--local west = wibox.container { +-- create_arrow('West'), +-- direction = 'west', +-- widget = wibox.container.rotate +--} +-- @property direction +-- @tparam string dir The direction + +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._private.direction = dir + self:emit_signal("widget::layout_changed") +end + +--- Get the direction of this rotating layout +function rotate:get_direction() + return self._private.direction or "north" +end + +--- Returns a new rotate container. +-- A rotate container 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. +-- @treturn table A new rotate container. +-- @function wibox.container.rotate +local function new(widget, dir) + local ret = base.make_widget(nil, nil, {enable_properties = true}) + + util.table.crush(ret, rotate, true) + + 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 0000000..0fb2ad5 --- /dev/null +++ b/lib/wibox/container/scroll.lua @@ -0,0 +1,716 @@ +--------------------------------------------------------------------------- +-- @author Uli Schlachter (based on ideas from Saleur Geoffrey) +-- @copyright 2015 Uli Schlachter +-- @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._private.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._private.expand and self._private.extra_space or 0 + local w, h + if self._private.dir == "h" then + w, h = base.fit_widget(self, context, self._private.widget, self._private.space_for_scrolling, height) + surface_width = w + extra_width = extra + else + w, h = base.fit_widget(self, context, self._private.widget, width, self._private.space_for_scrolling) + surface_height = h + extra_height = extra + end + result.fit_width, result.fit_height = w, h + if self._private.dir == "h" then + if self._private.max_size then + result.fit_width = math.min(w, self._private.max_size) + end + else + if self._private.max_size then + result.fit_height = math.min(h, self._private.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._private.step_function(self._private.timer:elapsed(), size, visible_size, self._private.speed, self._private.extra_space) + end + if self._private.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._private.expand and 0 or self._private.extra_space + if self._private.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._private.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._private.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._private.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._private.paused and not self._private.scroll_timer then + self._private.scroll_timer = timer.start_new(1 / self._private.fps, function() + self._private.scroll_timer = nil + self:emit_signal("widget::redraw_needed") + end) + end +end + +--- Pause the scrolling animation. +-- @see continue +function scroll:pause() + if self._private.paused then + return + end + self._private.paused = true + self._private.timer:stop() +end + +--- Continue the scrolling animation. +-- @see pause +function scroll:continue() + if not self._private.paused then + return + end + self._private.paused = false + self._private.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._private.timer:start() + if self._private.paused then + self._private.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._private.dir then + return + end + if dir ~= "h" and dir ~= "v" then + error("Invalid direction, can only be 'h' or 'v'") + end + self._private.dir = dir + self:emit_signal("widget::layout_changed") + self:emit_signal("widget::redraw_needed") +end + +--- The widget to be scrolled. +-- @property widget +-- @tparam widget widget The widget + +function scroll:set_widget(widget) + if widget == self._private.widget then + return + end + if widget then + base.check_widget(widget) + end + self._private.widget = widget + self:emit_signal("widget::layout_changed") + self:emit_signal("widget::redraw_needed") +end + +function scroll:get_widget() + return self._private.widget +end + +--- Get the number of children element +-- @treturn table The children +function scroll:get_children() + return {self._private.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._private.expand then + return + end + self._private.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._private.fps then + return + end + self._private.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._private.extra_space then + return + end + self._private.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._private.speed then + return + end + self._private.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._private.max_size then + return + end + self._private.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._private.step_function then + return + end + self._private.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._private.space_for_scrolling then + return + end + self._private.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(nil, nil, {enable_properties = true}) + + ret._private.paused = false + ret._private.timer = GLib.Timer() + ret._private.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 container. +-- @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 container. +-- @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 + +--Imported documentation + + +--- Get a widex index. +-- @param widget The widget to look for +-- @param[opt] recursive Also check sub-widgets +-- @param[opt] ... Aditional widgets to add at the end of the \"path\" +-- @return The index +-- @return The parent layout +-- @return The path between \"self\" and \"widget\" +-- @function index + +--- Get all direct and indirect children widgets. +-- This will scan all containers recursively to find widgets +-- Warning: This method it prone to stack overflow id the widget, or any of its +-- children, contain (directly or indirectly) itself. +-- @treturn table The children +-- @function get_all_children + +--- Set a declarative widget hierarchy description. +-- See [The declarative layout system](../documentation/03-declarative-layout.md.html) +-- @param args An array containing the widgets disposition +-- @function setup + +--- Force a widget height. +-- @property forced_height +-- @tparam number|nil height The height (`nil` for automatic) + +--- Force a widget width. +-- @property forced_width +-- @tparam number|nil width The width (`nil` for automatic) + +--- The widget opacity (transparency). +-- @property opacity +-- @tparam[opt=1] number opacity The opacity (between 0 and 1) + +--- The widget visibility. +-- @property visible +-- @param boolean + +--- Set/get a widget's buttons. +-- @param _buttons The table of buttons that should bind to the widget. +-- @function buttons + +--- Emit a signal and ensure all parent widgets in the hierarchies also +-- forward the signal. This is useful to track signals when there is a dynamic +-- set of containers and layouts wrapping the widget. +-- @tparam string signal_name +-- @param ... Other arguments +-- @function emit_signal_recursive + +--- When the layout (size) change. +-- This signal is emitted when the previous results of `:layout()` and `:fit()` +-- are no longer valid. Unless this signal is emitted, `:layout()` and `:fit()` +-- must return the same result when called with the same arguments. +-- @signal widget::layout_changed +-- @see widget::redraw_needed + +--- When the widget content changed. +-- This signal is emitted when the content of the widget changes. The widget will +-- be redrawn, it is not re-layouted. Put differently, it is assumed that +-- `:layout()` and `:fit()` would still return the same results as before. +-- @signal widget::redraw_needed +-- @see widget::layout_changed + +--- When a mouse button is pressed over the widget. +-- @signal button::press +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When a mouse button is released over the widget. +-- @signal button::release +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse enter a widget. +-- @signal mouse::enter +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse leave a widget. +-- @signal mouse::leave +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + + +--Imported documentation + + +--- Disconnect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback that should be disconnected +-- @function disconnect_signal + +--- Emit a signal. +-- +-- @tparam string name The name of the signal +-- @param ... Extra arguments for the callback functions. Each connected +-- function receives the object as first argument and then any extra arguments +-- that are given to emit_signal() +-- @function emit_signal + +--- Connect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function connect_signal + +--- Connect to a signal weakly. This allows the callback function to be garbage +-- collected and automatically disconnects the signal when that happens. +-- +-- **Warning:** +-- Only use this function if you really, really, really know what you +-- are doing. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function weak_connect_signal + + +return scroll + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/wibox/drawable.lua b/lib/wibox/drawable.lua new file mode 100644 index 0000000..330edc3 --- /dev/null +++ b/lib/wibox/drawable.lua @@ -0,0 +1,489 @@ +--------------------------------------------------------------------------- +--- Handling of drawables. A drawable is something that can be drawn to. +-- +-- @author Uli Schlachter +-- @copyright 2012 Uli Schlachter +-- @classmod wibox.drawable +--------------------------------------------------------------------------- + +local drawable = {} +local capi = { + awesome = awesome, + root = root, + screen = screen +} +local beautiful = require("beautiful") +local cairo = require("lgi").cairo +local color = require("gears.color") +local object = require("gears.object") +local surface = require("gears.surface") +local timer = require("gears.timer") +local grect = require("gears.geometry").rectangle +local matrix = require("gears.matrix") +local hierarchy = require("wibox.hierarchy") +local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1) + +local visible_drawables = {} + +-- Get the widget context. This should always return the same table (if +-- possible), so that our draw and fit caches can work efficiently. +local function get_widget_context(self) + local geom = self.drawable:geometry() + + local s = self._forced_screen + if not s then + local sgeos = {} + + for scr in capi.screen do + sgeos[scr] = scr.geometry + end + + s = grect.get_by_coord(sgeos, geom.x, geom.y) or capi.screen.primary + end + + local context = self._widget_context + local dpi = beautiful.xresources.get_dpi(s) + if (not context) or context.screen ~= s or context.dpi ~= dpi then + context = { + screen = s, + dpi = dpi, + drawable = self, + } + for k, v in pairs(self._widget_context_skeleton) do + context[k] = v + end + self._widget_context = context + + -- Give widgets a chance to react to the new context + self._need_complete_repaint = true + end + return context +end + +local function do_redraw(self) + if not self.drawable.valid then return end + if self._forced_screen and not self._forced_screen.valid then return end + + local surf = surface.load_silently(self.drawable.surface, false) + -- The surface can be nil if the drawable's parent was already finalized + if not surf then return end + local cr = cairo.Context(surf) + local geom = self.drawable:geometry(); + local x, y, width, height = geom.x, geom.y, geom.width, geom.height + local context = get_widget_context(self) + + -- Relayout + if self._need_relayout or self._need_complete_repaint then + self._need_relayout = false + if self._widget_hierarchy and self.widget then + self._widget_hierarchy:update(context, + self.widget, width, height, self._dirty_area) + else + self._need_complete_repaint = true + if self.widget then + self._widget_hierarchy_callback_arg = {} + self._widget_hierarchy = hierarchy.new(context, self.widget, width, height, + self._redraw_callback, self._layout_callback, self._widget_hierarchy_callback_arg) + else + self._widget_hierarchy = nil + end + end + + if self._need_complete_repaint then + self._need_complete_repaint = false + self._dirty_area:union_rectangle(cairo.RectangleInt{ + x = 0, y = 0, width = width, height = height + }) + end + end + + -- Clip to the dirty area + if self._dirty_area:is_empty() then + return + end + for i = 0, self._dirty_area:num_rectangles() - 1 do + local rect = self._dirty_area:get_rectangle(i) + cr:rectangle(rect.x, rect.y, rect.width, rect.height) + end + self._dirty_area = cairo.Region.create() + cr:clip() + + -- Draw the background + cr:save() + + if not capi.awesome.composite_manager_running then + -- This is pseudo-transparency: We draw the wallpaper in the background + local wallpaper = surface.load_silently(capi.root.wallpaper(), false) + if wallpaper then + cr.operator = cairo.Operator.SOURCE + cr:set_source_surface(wallpaper, -x, -y) + cr:paint() + end + cr.operator = cairo.Operator.OVER + else + -- This is true transparency: We draw a translucent background + cr.operator = cairo.Operator.SOURCE + end + + cr:set_source(self.background_color) + cr:paint() + + cr:restore() + + -- Paint the background image + if self.background_image then + cr:save() + if type(self.background_image) == "function" then + self.background_image(context, cr, width, height, unpack(self.background_image_args)) + else + local pattern = cairo.Pattern.create_for_surface(self.background_image) + cr:set_source(pattern) + cr:paint() + end + cr:restore() + end + + -- Draw the widget + if self._widget_hierarchy then + cr:set_source(self.foreground_color) + self._widget_hierarchy:draw(context, cr) + end + + self.drawable:refresh() + + assert(cr.status == "SUCCESS", "Cairo context entered error state: " .. cr.status) +end + +local function find_widgets(_drawable, result, _hierarchy, x, y) + local m = _hierarchy:get_matrix_from_device() + + -- Is (x,y) inside of this hierarchy or any child (aka the draw extents) + local x1, y1 = m:transform_point(x, y) + local x2, y2, w2, h2 = _hierarchy:get_draw_extents() + if x1 < x2 or x1 >= x2 + w2 then + return + end + if y1 < y2 or y1 >= y2 + h2 then + return + end + + -- Is (x,y) inside of this widget? + local width, height = _hierarchy:get_size() + if x1 >= 0 and y1 >= 0 and x1 <= width and y1 <= height then + -- Get the extents of this widget in the device space + local x3, y3, w3, h3 = matrix.transform_rectangle(_hierarchy:get_matrix_to_device(), + 0, 0, width, height) + table.insert(result, { + x = x3, y = y3, width = w3, height = h3, + widget_width = width, + widget_height = height, + drawable = _drawable, + widget = _hierarchy:get_widget(), + hierarchy = _hierarchy + }) + end + for _, child in ipairs(_hierarchy:get_children()) do + find_widgets(_drawable, result, child, x, y) + end +end + +--- Find a widget by a point. +-- The drawable must have drawn itself at least once for this to work. +-- @param x X coordinate of the point +-- @param y Y coordinate of the point +-- @treturn table A table containing a description of all the widgets that +-- contain the given point. Each entry is a table containing this drawable as +-- its `.drawable` entry, the widget under `.widget` and the instance of +-- `wibox.hierarchy` describing the size and position of the widget under +-- `.hierarchy`. For convenience, `.x`, `.y`, `.width` and `.height` contain an +-- approximation of the widget's extents on the surface. `widget_width` and +-- `widget_height` contain the exact size of the widget in its own, local +-- coordinate system (which may e.g. be rotated and scaled). +function drawable:find_widgets(x, y) + local result = {} + if self._widget_hierarchy then + find_widgets(self, result, self._widget_hierarchy, x, y) + end + return result +end + + +--- Set the widget that the drawable displays +function drawable:set_widget(widget) + self.widget = widget + + -- Make sure the widget gets drawn + self._need_relayout = true + self.draw() +end + +--- Set the background of the drawable +-- @param c The background to use. This must either be a cairo pattern object, +-- nil or a string that gears.color() understands. +-- @see gears.color +function drawable:set_bg(c) + c = c or "#000000" + local t = type(c) + + if t == "string" or t == "table" then + c = color(c) + end + + -- If the background is completely opaque, we don't need to redraw when + -- the drawable is moved + -- XXX: This isn't needed when awesome.composite_manager_running is true, + -- but a compositing manager could stop/start and we'd have to properly + -- handle this. So for now we choose the lazy approach. + local redraw_on_move = not color.create_opaque_pattern(c) + if self._redraw_on_move ~= redraw_on_move then + self._redraw_on_move = redraw_on_move + if redraw_on_move then + self.drawable:connect_signal("property::x", self._do_complete_repaint) + self.drawable:connect_signal("property::y", self._do_complete_repaint) + else + self.drawable:disconnect_signal("property::x", self._do_complete_repaint) + self.drawable:disconnect_signal("property::y", self._do_complete_repaint) + end + end + + self.background_color = c + self._do_complete_repaint() +end + +--- Set the background image of the drawable +-- 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 drawable:set_bgimage(image, ...) + if type(image) ~= "function" then + image = surface(image) + end + + self.background_image = image + self.background_image_args = {...} + + self._do_complete_repaint() +end + +--- Set the foreground of the drawable +-- @param c The foreground to use. This must either be a cairo pattern object, +-- nil or a string that gears.color() understands. +-- @see gears.color +function drawable:set_fg(c) + c = c or "#FFFFFF" + if type(c) == "string" or type(c) == "table" then + c = color(c) + end + self.foreground_color = c + self._do_complete_repaint() +end + +function drawable:_force_screen(s) + self._forced_screen = s +end + +function drawable:_inform_visible(visible) + self._visible = visible + if visible then + visible_drawables[self] = true + -- The wallpaper or widgets might have changed + self:_do_complete_repaint() + else + visible_drawables[self] = nil + end +end + +local function emit_difference(name, list, skip) + local function in_table(table, val) + for _, v in pairs(table) do + if v.widget == val.widget then + return true + end + end + return false + end + + for _, v in pairs(list) do + if not in_table(skip, v) then + v.widget:emit_signal(name,v) + end + end +end + +local function handle_leave(_drawable) + emit_difference("mouse::leave", _drawable._widgets_under_mouse, {}) + _drawable._widgets_under_mouse = {} +end + +local function handle_motion(_drawable, x, y) + if x < 0 or y < 0 or x > _drawable.drawable:geometry().width or y > _drawable.drawable:geometry().height then + return handle_leave(_drawable) + end + + -- Build a plain list of all widgets on that point + local widgets_list = _drawable:find_widgets(x, y) + + -- First, "leave" all widgets that were left + emit_difference("mouse::leave", _drawable._widgets_under_mouse, widgets_list) + -- Then enter some widgets + emit_difference("mouse::enter", widgets_list, _drawable._widgets_under_mouse) + + _drawable._widgets_under_mouse = widgets_list +end + +local function setup_signals(_drawable) + local d = _drawable.drawable + + local function clone_signal(name) + -- When "name" is emitted on wibox.drawin, also emit it on wibox + d:connect_signal(name, function(_, ...) + _drawable:emit_signal(name, ...) + end) + end + clone_signal("button::press") + clone_signal("button::release") + clone_signal("mouse::enter") + clone_signal("mouse::leave") + clone_signal("mouse::move") + clone_signal("property::surface") + clone_signal("property::width") + clone_signal("property::height") + clone_signal("property::x") + clone_signal("property::y") +end + +function drawable.new(d, widget_context_skeleton, drawable_name) + local ret = object() + ret.drawable = d + ret._widget_context_skeleton = widget_context_skeleton + ret._need_complete_repaint = true + ret._need_relayout = true + ret._dirty_area = cairo.Region.create() + setup_signals(ret) + + for k, v in pairs(drawable) do + if type(v) == "function" then + ret[k] = v + end + end + + -- Only redraw a drawable once, even when we get told to do so multiple times. + ret._redraw_pending = false + ret._do_redraw = function() + ret._redraw_pending = false + do_redraw(ret) + end + + -- Connect our signal when we need a redraw + ret.draw = function() + if not ret._redraw_pending then + timer.delayed_call(ret._do_redraw) + ret._redraw_pending = true + end + end + ret._do_complete_repaint = function() + ret._need_complete_repaint = true + ret:draw() + end + + -- Do a full redraw if the surface changes (the new surface has no content yet) + d:connect_signal("property::surface", ret._do_complete_repaint) + + -- Do a normal redraw when the drawable moves. This will likely do nothing + -- in most cases, but it makes us do a complete repaint when we are moved to + -- a different screen. + d:connect_signal("property::x", ret.draw) + d:connect_signal("property::y", ret.draw) + + -- Currently we aren't redrawing on move (signals not connected). + -- :set_bg() will later recompute this. + ret._redraw_on_move = false + + -- Set the default background + ret:set_bg(beautiful.bg_normal) + ret:set_fg(beautiful.fg_normal) + + -- Initialize internals + ret._widgets_under_mouse = {} + + local function button_signal(name) + d:connect_signal(name, function(_, x, y, button, modifiers) + local widgets = ret:find_widgets(x, y) + for _, v in pairs(widgets) do + -- Calculate x/y inside of the widget + local lx, ly = v.hierarchy:get_matrix_from_device():transform_point(x, y) + v.widget:emit_signal(name, lx, ly, button, modifiers,v) + end + end) + end + button_signal("button::press") + button_signal("button::release") + + d:connect_signal("mouse::move", function(_, x, y) handle_motion(ret, x, y) end) + d:connect_signal("mouse::leave", function() handle_leave(ret) end) + + -- Set up our callbacks for repaints + ret._redraw_callback = function(hierar, arg) + -- Avoid crashes when a drawable was partly finalized and dirty_area is broken. + if not ret._visible then + return + end + if ret._widget_hierarchy_callback_arg ~= arg then + return + end + local m = hierar:get_matrix_to_device() + local x, y, width, height = matrix.transform_rectangle(m, hierar:get_draw_extents()) + local x1, y1 = math.floor(x), math.floor(y) + local x2, y2 = math.ceil(x + width), math.ceil(y + height) + ret._dirty_area:union_rectangle(cairo.RectangleInt{ + x = x1, y = y1, width = x2 - x1, height = y2 - y1 + }) + ret:draw() + end + ret._layout_callback = function(_, arg) + if ret._widget_hierarchy_callback_arg ~= arg then + return + end + ret._need_relayout = true + -- When not visible, we will be redrawn when we become visible. In the + -- mean-time, the layout does not matter much. + if ret._visible then + ret:draw() + end + end + + -- Add __tostring method to metatable. + ret.drawable_name = drawable_name or object.modulename(3) + local mt = {} + local orig_string = tostring(ret) + mt.__tostring = function() + return string.format("%s (%s)", ret.drawable_name, orig_string) + end + ret = setmetatable(ret, mt) + + -- Make sure the drawable is drawn at least once + ret._do_complete_repaint() + + return ret +end + +-- Redraw all drawables when the wallpaper changes +capi.awesome.connect_signal("wallpaper_changed", function() + for d in pairs(visible_drawables) do + d:_do_complete_repaint() + end +end) + +-- Give drawables a chance to react to screen changes +local function draw_all() + for d in pairs(visible_drawables) do + d:draw() + end +end +screen.connect_signal("property::geometry", draw_all) +screen.connect_signal("added", draw_all) +screen.connect_signal("removed", draw_all) + +return setmetatable(drawable, { __call = function(_, ...) return drawable.new(...) end }) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/wibox/hierarchy.lua b/lib/wibox/hierarchy.lua new file mode 100644 index 0000000..6bf2167 --- /dev/null +++ b/lib/wibox/hierarchy.lua @@ -0,0 +1,333 @@ +--------------------------------------------------------------------------- +-- Management of widget hierarchies. Each widget hierarchy object has a widget +-- for which it saves e.g. size and transformation in its parent. Also, each +-- widget has a number of children. +-- +-- @author Uli Schlachter +-- @copyright 2015 Uli Schlachter +-- @module wibox.hierarchy +--------------------------------------------------------------------------- + +local matrix = require("gears.matrix") +local protected_call = require("gears.protected_call") +local cairo = require("lgi").cairo +local base = require("wibox.widget.base") +local no_parent = base.no_parent_I_know_what_I_am_doing + +local hierarchy = {} + +local function hierarchy_new(redraw_callback, layout_callback, callback_arg) + local result = { + _matrix = matrix.identity, + _matrix_to_device = matrix.identity, + _need_update = true, + _widget = nil, + _context = nil, + _redraw_callback = redraw_callback, + _layout_callback = layout_callback, + _callback_arg = callback_arg, + _size = { + width = nil, + height = nil + }, + _draw_extents = { + x = 0, + y = 0, + width = 0, + height = 0 + }, + _parent = nil, + _children = {} + } + + function result._redraw() + redraw_callback(result, callback_arg) + end + function result._layout() + local h = result + while h do + h._need_update = true + h = h._parent + end + layout_callback(result, callback_arg) + end + function result._emit_recursive(widget, name, ...) + local cur = result + assert(widget == cur._widget) + while cur do + if cur._widget then + cur._widget:emit_signal(name, ...) + end + cur = cur._parent + end + end + + for k, f in pairs(hierarchy) do + if type(f) == "function" then + result[k] = f + end + end + return result +end + +local hierarchy_update +function hierarchy_update(self, context, widget, width, height, region, matrix_to_parent, matrix_to_device) + if (not self._need_update) and self._widget == widget and + self._context == context and + self._size.width == width and self._size.height == height and + matrix.equals(self._matrix, matrix_to_parent) and + matrix.equals(self._matrix_to_device, matrix_to_device) then + -- Nothing changed + return + end + + self._need_update = false + + local old_x, old_y, old_width, old_height + local old_widget = self._widget + if self._size.width and self._size.height then + local x, y, w, h = matrix.transform_rectangle(self._matrix_to_device, 0, 0, self._size.width, self._size.height) + old_x, old_y = math.floor(x), math.floor(y) + old_width, old_height = math.ceil(x + w) - old_x, math.ceil(y + h) - old_y + else + old_x, old_y, old_width, old_height = 0, 0, 0, 0 + end + + -- Disconnect old signals + if old_widget and old_widget ~= widget then + self._widget:disconnect_signal("widget::redraw_needed", self._redraw) + self._widget:disconnect_signal("widget::layout_changed", self._layout) + self._widget:disconnect_signal("widget::emit_recursive", self._emit_recursive) + end + + -- Save the arguments we need to save + self._widget = widget + self._context = context + self._size.width = width + self._size.height = height + self._matrix = matrix_to_parent + self._matrix_to_device = matrix_to_device + + -- Connect signals + if old_widget ~= widget then + widget:weak_connect_signal("widget::redraw_needed", self._redraw) + widget:weak_connect_signal("widget::layout_changed", self._layout) + widget:weak_connect_signal("widget::emit_recursive", self._emit_recursive) + end + + -- Update children + local old_children = self._children + local layout_result = base.layout_widget(no_parent, context, widget, width, height) + self._children = {} + for _, w in ipairs(layout_result or {}) do + local r = table.remove(old_children, 1) + if not r then + r = hierarchy_new(self._redraw_callback, self._layout_callback, self._callback_arg) + r._parent = self + end + hierarchy_update(r, context, w._widget, w._width, w._height, region, w._matrix, w._matrix * matrix_to_device) + table.insert(self._children, r) + end + + -- Calculate the draw extents + local x1, y1, x2, y2 = 0, 0, width, height + for _, h in ipairs(self._children) do + local px, py, pwidth, pheight = matrix.transform_rectangle(h._matrix, h:get_draw_extents()) + x1 = math.min(x1, px) + y1 = math.min(y1, py) + x2 = math.max(x2, px + pwidth) + y2 = math.max(y2, py + pheight) + end + self._draw_extents = { + x = x1, y = y1, + width = x2 - x1, + height = y2 - y1 + } + + -- Check which part needs to be redrawn + + -- Are there any children which were removed? Their area needs a redraw. + for _, child in ipairs(old_children) do + local x, y, w, h = matrix.transform_rectangle(child._matrix_to_device, child:get_draw_extents()) + region:union_rectangle(cairo.RectangleInt{ + x = x, y = y, width = w, height = h + }) + child._parent = nil + end + + -- Did we change and need to be redrawn? + local x, y, w, h = matrix.transform_rectangle(self._matrix_to_device, 0, 0, self._size.width, self._size.height) + local new_x, new_y = math.floor(x), math.floor(y) + local new_width, new_height = math.ceil(x + w) - new_x, math.ceil(y + h) - new_y + if new_x ~= old_x or new_y ~= old_y or new_width ~= old_width or new_height ~= old_height or + widget ~= old_widget then + region:union_rectangle(cairo.RectangleInt{ + x = old_x, y = old_y, width = old_width, height = old_height + }) + region:union_rectangle(cairo.RectangleInt{ + x = new_x, y = new_y, width = new_width, height = new_height + }) + end +end + +--- Create a new widget hierarchy that has no parent. +-- @param context The context in which we are laid out. +-- @param widget The widget that is at the base of the hierarchy. +-- @param width The available width for this hierarchy. +-- @param height The available height for this hierarchy. +-- @param redraw_callback Callback that is called with the corresponding widget +-- hierarchy on widget::redraw_needed on some widget. +-- @param layout_callback Callback that is called with the corresponding widget +-- hierarchy on widget::layout_changed on some widget. +-- @param callback_arg A second argument that is given to the above callbacks. +-- @return A new widget hierarchy +function hierarchy.new(context, widget, width, height, redraw_callback, layout_callback, callback_arg) + local result = hierarchy_new(redraw_callback, layout_callback, callback_arg) + result:update(context, widget, width, height) + return result +end + +--- Update a widget hierarchy with some new state. +-- @param context The context in which we are laid out. +-- @param widget The widget that is at the base of the hierarchy. +-- @param width The available width for this hierarchy. +-- @param height The available height for this hierarchy. +-- @param[opt] region A region to use for accumulating changed parts +-- @return A cairo region describing the changed parts (either the `region` +-- argument or a new, internally created region). +function hierarchy:update(context, widget, width, height, region) + region = region or cairo.Region.create() + hierarchy_update(self, context, widget, width, height, region, self._matrix, self._matrix_to_device) + return region +end + +--- Get the widget that this hierarchy manages. +function hierarchy:get_widget() + return self._widget +end + +--- Get a matrix that transforms to the parent's coordinate space from this +-- hierarchy's coordinate system. +-- @return A matrix describing the transformation. +function hierarchy:get_matrix_to_parent() + return self._matrix +end + +--- Get a matrix that transforms to the base of this hierarchy's coordinate +-- system (aka the coordinate system of the device that this +-- hierarchy is applied upon) from this hierarchy's coordinate system. +-- @return A matrix describing the transformation. +function hierarchy:get_matrix_to_device() + return self._matrix_to_device +end + +--- Get a matrix that transforms from the parent's coordinate space into this +-- hierarchy's coordinate system. +-- @return A matrix describing the transformation. +function hierarchy:get_matrix_from_parent() + local m = self:get_matrix_to_parent() + return m:invert() +end + +--- Get a matrix that transforms from the base of this hierarchy's coordinate +-- system (aka the coordinate system of the device that this +-- hierarchy is applied upon) into this hierarchy's coordinate system. +-- @return A matrix describing the transformation. +function hierarchy:get_matrix_from_device() + local m = self:get_matrix_to_device() + return m:invert() +end + +--- Get the extents that this hierarchy possibly draws to (in the current coordinate space). +-- This includes the size of this element plus the size of all children +-- (after applying the corresponding transformation). +-- @return x, y, width, height +function hierarchy:get_draw_extents() + local ext = self._draw_extents + return ext.x, ext.y, ext.width, ext.height +end + +--- Get the size that this hierarchy logically covers (in the current coordinate space). +-- @return width, height +function hierarchy:get_size() + local ext = self._size + return ext.width, ext.height +end + +--- Get a list of all children. +-- @return List of all children hierarchies. +function hierarchy:get_children() + return self._children +end + +--- Does the given cairo context have an empty clip (aka "no drawing possible")? +local function empty_clip(cr) + local _, _, width, height = cr:clip_extents() + return width == 0 or height == 0 +end + +--- Draw a hierarchy to some cairo context. +-- This function draws the widgets in this widget hierarchy to the given cairo +-- context. The context's clip is used to skip parts that aren't visible. +-- @param context The context in which widgets are drawn. +-- @param cr The cairo context that is used for drawing. +function hierarchy:draw(context, cr) + local widget = self:get_widget() + if not widget._private.visible then + return + end + + cr:save() + cr:transform(self:get_matrix_to_parent():to_cairo_matrix()) + + -- Clip to the draw extents + cr:rectangle(self:get_draw_extents()) + cr:clip() + + -- Draw if needed + if not empty_clip(cr) then + local opacity = widget:get_opacity() + local function call(func, extra_arg1, extra_arg2) + if not func then return end + if not extra_arg2 then + protected_call(func, widget, context, cr, self:get_size()) + else + protected_call(func, widget, context, extra_arg1, extra_arg2, cr, self:get_size()) + end + end + + -- Prepare opacity handling + if opacity ~= 1 then + cr:push_group() + end + + -- Draw the widget + cr:save() + cr:rectangle(0, 0, self:get_size()) + cr:clip() + call(widget.draw) + cr:restore() + + -- Draw its children (We already clipped to the draw extents above) + call(widget.before_draw_children) + for i, wi in ipairs(self:get_children()) do + call(widget.before_draw_child, i, wi:get_widget()) + wi:draw(context, cr) + call(widget.after_draw_child, i, wi:get_widget()) + end + call(widget.after_draw_children) + + -- Apply opacity + if opacity ~= 1 then + cr:pop_group_to_source() + cr.operator = cairo.Operator.OVER + cr:paint_with_alpha(opacity) + end + end + + cr:restore() +end + +return hierarchy + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/wibox/init.lua b/lib/wibox/init.lua new file mode 100644 index 0000000..3fc89ce --- /dev/null +++ b/lib/wibox/init.lua @@ -0,0 +1,479 @@ +--------------------------------------------------------------------------- +-- @author Uli Schlachter +-- @copyright 2010 Uli Schlachter +-- @classmod wibox +--------------------------------------------------------------------------- + +local capi = { + drawin = drawin, + root = root, + awesome = awesome, + screen = screen +} +local setmetatable = setmetatable +local pairs = pairs +local type = type +local object = require("gears.object") +local grect = require("gears.geometry").rectangle +local beautiful = require("beautiful") +local base = require("wibox.widget.base") + +--- This provides widget box windows. Every wibox can also be used as if it were +-- a drawin. All drawin functions and properties are also available on wiboxes! +-- 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") + +local force_forward = { + shape_bounding = true, + shape_clip = true, +} + +--Imported documentation + +--- Border width. +-- +-- **Signal:** +-- +-- * *property::border_width* +-- +-- @property border_width +-- @param integer + +--- Border color. +-- +-- Please note that this property only support string based 24 bit or 32 bit +-- colors: +-- +-- Red Blue +-- _| _| +-- #FF00FF +-- T‾ +-- Green +-- +-- +-- Red Blue +-- _| _| +-- #FF00FF00 +-- T‾ ‾T +-- Green Alpha +-- +-- **Signal:** +-- +-- * *property::border_color* +-- +-- @property border_color +-- @param string + +--- On top of other windows. +-- +-- **Signal:** +-- +-- * *property::ontop* +-- +-- @property ontop +-- @param boolean + +--- The mouse cursor. +-- +-- **Signal:** +-- +-- * *property::cursor* +-- +-- @property cursor +-- @param string +-- @see mouse + +--- Visibility. +-- +-- **Signal:** +-- +-- * *property::visible* +-- +-- @property visible +-- @param boolean + +--- The opacity of the wibox, between 0 and 1. +-- +-- **Signal:** +-- +-- * *property::opacity* +-- +-- @property opacity +-- @tparam number opacity (between 0 and 1) + +--- The window type (desktop, normal, dock, ...). +-- +-- **Signal:** +-- +-- * *property::type* +-- +-- @property type +-- @param string +-- @see client.type + +--- The x coordinates. +-- +-- **Signal:** +-- +-- * *property::x* +-- +-- @property x +-- @param integer + +--- The y coordinates. +-- +-- **Signal:** +-- +-- * *property::y* +-- +-- @property y +-- @param integer + +--- The width of the wibox. +-- +-- **Signal:** +-- +-- * *property::width* +-- +-- @property width +-- @param width + +--- The height of the wibox. +-- +-- **Signal:** +-- +-- * *property::height* +-- +-- @property height +-- @param height + +--- The wibox screen. +-- +-- @property screen +-- @param screen + +--- The wibox's `drawable`. +-- +-- **Signal:** +-- +-- * *property::drawable* +-- +-- @property drawable +-- @tparam drawable drawable + +--- The widget that the `wibox` displays. +-- @property widget +-- @param widget + +--- The X window id. +-- +-- **Signal:** +-- +-- * *property::window* +-- +-- @property window +-- @param string +-- @see client.window + +--- The wibox's bounding shape as a (native) cairo surface. +-- +-- **Signal:** +-- +-- * *property::shape_bounding* +-- +-- @property shape_bounding +-- @param surface._native + +--- The wibox's clip shape as a (native) cairo surface. +-- +-- **Signal:** +-- +-- * *property::shape_clip* +-- +-- @property shape_clip +-- @param surface._native + +--- Get or set mouse buttons bindings to a wibox. +-- +-- @param buttons_table A table of buttons objects, or nothing. +-- @function buttons + +--- Get or set wibox geometry. That's the same as accessing or setting the x, +-- y, width or height properties of a wibox. +-- +-- @param A table with coordinates to modify. +-- @return A table with wibox coordinates and geometry. +-- @function geometry + +--- Get or set wibox struts. +-- +-- @param strut A table with new strut, or nothing +-- @return The wibox strut in a table. +-- @function struts +-- @see client.struts + +--- The default background color. +-- @beautiful beautiful.bg_normal +-- @see bg + +--- The default foreground (text) color. +-- @beautiful beautiful.fg_normal +-- @see fg + +--- Set a declarative widget hierarchy description. +-- See [The declarative layout system](../documentation/03-declarative-layout.md.html) +-- @param args An array containing the widgets disposition +-- @name setup +-- @class function + +--- The background of the wibox. +-- @param c The background to use. This must either be a cairo pattern object, +-- nil or a string that gears.color() understands. +-- @property bg +-- @see gears.color + +--- The background image of the drawable. +-- 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 +-- @property bgimage +-- @see gears.surface + +--- The foreground (text) of the wibox. +-- @param c The foreground to use. This must either be a cairo pattern object, +-- nil or a string that gears.color() understands. +-- @property fg +-- @see gears.color + +--- Find a widget by a point. +-- The wibox must have drawn itself at least once for this to work. +-- @tparam number x X coordinate of the point +-- @tparam number y Y coordinate of the point +-- @treturn table A sorted table of widgets positions. The first element is the biggest +-- container while the last is the topmost widget. The table contains *x*, *y*, +-- *width*, *height* and *widget*. +-- @name find_widgets +-- @class function + + +function wibox:set_widget(widget) + self._drawable:set_widget(widget) +end + +function wibox:get_widget() + return self._drawable.widget +end + +wibox.setup = base.widget.setup + +function wibox:set_bg(c) + self._drawable:set_bg(c) +end + +function wibox:set_bgimage(image, ...) + self._drawable:set_bgimage(image, ...) +end + +function wibox:set_fg(c) + self._drawable:set_fg(c) +end + +function wibox:find_widgets(x, y) + return self._drawable:find_widgets(x, y) +end + +function wibox:get_screen() + if self.screen_assigned and self.screen_assigned.valid then + return self.screen_assigned + else + self.screen_assigned = nil + end + local sgeos = {} + + for s in capi.screen do + sgeos[s] = s.geometry + end + + return grect.get_closest_by_coord(sgeos, self.x, self.y) +end + +function wibox:set_screen(s) + s = capi.screen[s or 1] + if s ~= self:get_screen() then + self.x = s.geometry.x + self.y = s.geometry.y + end + + -- Remember this screen so things work correctly if screens overlap and + -- (x,y) is not enough to figure out the correct screen. + self.screen_assigned = s + self._drawable:_force_screen(s) +end + +for _, k in pairs{ "buttons", "struts", "geometry", "get_xproperty", "set_xproperty" } do + wibox[k] = function(self, ...) + return self.drawin[k](self.drawin, ...) + end +end + +local function setup_signals(_wibox) + local obj + local function clone_signal(name) + -- When "name" is emitted on wibox.drawin, also emit it on wibox + obj:connect_signal(name, function(_, ...) + _wibox:emit_signal(name, ...) + end) + end + + obj = _wibox.drawin + clone_signal("property::border_color") + clone_signal("property::border_width") + clone_signal("property::buttons") + clone_signal("property::cursor") + clone_signal("property::height") + clone_signal("property::ontop") + clone_signal("property::opacity") + clone_signal("property::struts") + clone_signal("property::visible") + clone_signal("property::width") + clone_signal("property::x") + clone_signal("property::y") + clone_signal("property::geometry") + clone_signal("property::shape_bounding") + clone_signal("property::shape_clip") + + obj = _wibox._drawable + clone_signal("button::press") + clone_signal("button::release") + clone_signal("mouse::enter") + clone_signal("mouse::leave") + clone_signal("mouse::move") + clone_signal("property::surface") +end + +--- Create a wibox. +-- @tparam[opt=nil] table args +-- @tparam integer args.border_width Border width. +-- @tparam string args.border_color Border color. +-- @tparam boolean args.ontop On top of other windows. +-- @tparam string args.cursor The mouse cursor. +-- @tparam boolean args.visible Visibility. +-- @tparam number args.opacity The opacity of the wibox, between 0 and 1. +-- @tparam string args.type The window type (desktop, normal, dock, …). +-- @tparam integer args.x The x coordinates. +-- @tparam integer args.y The y coordinates. +-- @tparam integer args.width The width of the wibox. +-- @tparam integer args.height The height of the wibox. +-- @tparam screen args.screen The wibox screen. +-- @tparam wibox.widget args.widget The widget that the wibox displays. +-- @param args.shape_bounding The wibox’s bounding shape as a (native) cairo surface. +-- @param args.shape_clip The wibox’s clip shape as a (native) cairo surface. +-- @tparam color args.bg The background of the wibox. +-- @tparam surface args.bgimage The background image of the drawable. +-- @tparam color args.fg The foreground (text) of the wibox. +-- @treturn wibox The new wibox +-- @function .wibox + +local function new(args) + args = args or {} + local ret = object() + local w = capi.drawin(args) + + function w.get_wibox() + return ret + end + + ret.drawin = w + ret._drawable = wibox.drawable(w.drawable, { wibox = ret }, + "wibox drawable (" .. object.modulename(3) .. ")") + + ret._drawable:_inform_visible(w.visible) + w:connect_signal("property::visible", function() + ret._drawable:_inform_visible(w.visible) + end) + + for k, v in pairs(wibox) do + if type(v) == "function" then + ret[k] = v + end + end + + setup_signals(ret) + ret.draw = ret._drawable.draw + + -- Set the default background + ret:set_bg(args.bg or beautiful.bg_normal) + ret:set_fg(args.fg or beautiful.fg_normal) + + -- Add __tostring method to metatable. + local mt = {} + local orig_string = tostring(ret) + mt.__tostring = function() + return string.format("wibox: %s (%s)", + tostring(ret._drawable), orig_string) + end + ret = setmetatable(ret, mt) + + -- Make sure the wibox is drawn at least once + ret.draw() + + -- If a value is not found, look in the drawin + setmetatable(ret, { + __index = function(self, k) + if rawget(self, "get_"..k) then + return self["get_"..k](self) + else + return w[k] + end + end, + __newindex = function(self, k,v) + if rawget(self, "set_"..k) then + self["set_"..k](self, v) + elseif w[k] ~= nil or force_forward[k] then + w[k] = v + else + rawset(self, k, v) + end + end + }) + + -- Set other wibox specific arguments + if args.bgimage then + ret:set_bgimage( args.bgimage ) + end + + if args.widget then + ret:set_widget ( args.widget ) + end + + if args.screen then + ret:set_screen ( args.screen ) + end + + return ret +end + +--- Redraw a wibox. You should never have to call this explicitely because it is +-- automatically called when needed. +-- @param wibox +-- @function draw + +function wibox.mt:__call(...) + return new(...) +end + +-- Extend the luaobject +object.properties(capi.drawin, { + getter_class = wibox.object, + setter_class = wibox.object, + auto_emit = true, +}) + +return setmetatable(wibox, wibox.mt) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/wibox/layout/align.lua b/lib/wibox/layout/align.lua new file mode 100644 index 0000000..92a5582 --- /dev/null +++ b/lib/wibox/layout/align.lua @@ -0,0 +1,526 @@ +--------------------------------------------------------------------------- +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_layout_defaults_align.svg) +-- +-- @usage +--wibox.widget { +-- generic_widget( 'first' ), +-- generic_widget( 'second' ), +-- generic_widget( 'third' ), +-- layout = wibox.layout.align.horizontal +--} +-- @author Uli Schlachter +-- @copyright 2010 Uli Schlachter +-- @classmod wibox.layout.align +--------------------------------------------------------------------------- + +local table = table +local pairs = pairs +local type = type +local floor = math.floor +local util = require("awful.util") +local base = require("wibox.widget.base") + +local align = {} + +-- Calculate the layout of an align layout. +-- @param context The context in which we are drawn. +-- @param width The available width. +-- @param height The available height. +function align:layout(context, width, height) + local result = {} + + -- Draw will have to deal with all three align modes and should work in a + -- way that makes sense if one or two of the widgets are missing (if they + -- are all missing, it won't draw anything.) It should also handle the case + -- where the fit something that isn't set to expand (for instance the + -- outside widgets when the expand mode is "inside" or any of the widgets + -- when the expand mode is "none" wants to take up more space than is + -- allowed. + local size_first = 0 + -- start with all the space given by the parent, subtract as we go along + local size_remains = self._private.dir == "y" and height or width + -- This is only set & used if expand ~= "inside" and we have second width. + -- It contains the size allocated to the second widget. + local size_second + + -- we will prioritize the middle widget unless the expand mode is "inside" + -- if it is, we prioritize the first widget by not doing this block also, + -- if the second widget doesn't exist, we will prioritise the first one + -- instead + if self._private.expand ~= "inside" and self._private.second then + local w, h = base.fit_widget(self, context, self._private.second, width, height) + size_second = self._private.dir == "y" and h or w + -- if all the space is taken, skip the rest, and draw just the middle + -- widget + if size_second >= size_remains then + return { base.place_widget_at(self._private.second, 0, 0, width, height) } + else + -- the middle widget is sized first, the outside widgets are given + -- the remaining space if available we will draw later + size_remains = floor((size_remains - size_second) / 2) + end + end + if self._private.first then + local w, h, _ = width, height, nil + -- we use the fit function for the "inside" and "none" modes, but + -- ignore it for the "outside" mode, which will force it to expand + -- into the remaining space + if self._private.expand ~= "outside" then + if self._private.dir == "y" then + _, h = base.fit_widget(self, context, self._private.first, width, size_remains) + size_first = h + -- for "inside", the third widget will get a chance to use the + -- remaining space, then the middle widget. For "none" we give + -- the third widget the remaining space if there was no second + -- widget to take up any space (as the first if block is skipped + -- if this is the case) + if self._private.expand == "inside" or not self._private.second then + size_remains = size_remains - h + end + else + w, _ = base.fit_widget(self, context, self._private.first, size_remains, height) + size_first = w + if self._private.expand == "inside" or not self._private.second then + size_remains = size_remains - w + end + end + else + if self._private.dir == "y" then + h = size_remains + else + w = size_remains + end + end + table.insert(result, base.place_widget_at(self._private.first, 0, 0, w, h)) + end + -- size_remains will be <= 0 if first used all the space + if self._private.third and size_remains > 0 then + local w, h, _ = width, height, nil + if self._private.expand ~= "outside" then + if self._private.dir == "y" then + _, h = base.fit_widget(self, context, self._private.third, width, size_remains) + -- give the middle widget the rest of the space for "inside" mode + if self._private.expand == "inside" then + size_remains = size_remains - h + end + else + w, _ = base.fit_widget(self, context, self._private.third, size_remains, height) + if self._private.expand == "inside" then + size_remains = size_remains - w + end + end + else + if self._private.dir == "y" then + h = size_remains + else + w = size_remains + end + end + local x, y = width - w, height - h + table.insert(result, base.place_widget_at(self._private.third, x, y, w, h)) + end + -- here we either draw the second widget in the space set aside for it + -- in the beginning, or in the remaining space, if it is "inside" + if self._private.second and size_remains > 0 then + local x, y, w, h = 0, 0, width, height + if self._private.expand == "inside" then + if self._private.dir == "y" then + h = size_remains + x, y = 0, size_first + else + w = size_remains + x, y = size_first, 0 + end + else + local _ + if self._private.dir == "y" then + _, h = base.fit_widget(self, context, self._private.second, width, size_second) + y = floor( (height - h)/2 ) + else + w, _ = base.fit_widget(self, context, self._private.second, size_second, height) + x = floor( (width -w)/2 ) + end + end + table.insert(result, base.place_widget_at(self._private.second, x, y, w, h)) + end + return result +end + +--- Set the layout's first widget. +-- This is the widget that is at the left/top +-- @property first + +function align:set_first(widget) + if self._private.first == widget then + return + end + self._private.first = widget + self:emit_signal("widget::layout_changed") +end + +--- Set the layout's second widget. This is the centered one. +-- @property second + +function align:set_second(widget) + if self._private.second == widget then + return + end + self._private.second = widget + self:emit_signal("widget::layout_changed") +end + +--- Set the layout's third widget. +-- This is the widget that is at the right/bottom +-- @property third + +function align:set_third(widget) + if self._private.third == widget then + return + end + self._private.third = widget + self:emit_signal("widget::layout_changed") +end + +for _, prop in ipairs {"first", "second", "third", "expand" } do + align["get_"..prop] = function(self) + return self._private[prop] + end +end + +--- All direct children of this layout. +-- This can be used to replace all 3 widgets at once. +-- @treturn table a list of all widgets +-- @property children + +function align:get_children() + return util.from_sparse {self._private.first, self._private.second, self._private.third} +end + +function align:set_children(children) + self:set_first(children[1]) + self:set_second(children[2]) + self:set_third(children[3]) +end + +-- Fit the align layout into the given space. The align layout will +-- ask for the sum of the sizes of its sub-widgets in its direction +-- and the largest sized sub widget in the other direction. +-- @param context The context in which we are fit. +-- @param orig_width The available width. +-- @param orig_height The available height. +function align:fit(context, orig_width, orig_height) + local used_in_dir = 0 + local used_in_other = 0 + + for _, v in pairs{self._private.first, self._private.second, self._private.third} do + local w, h = base.fit_widget(self, context, v, orig_width, orig_height) + + local max = self._private.dir == "y" and w or h + if max > used_in_other then + used_in_other = max + end + + used_in_dir = used_in_dir + (self._private.dir == "y" and h or w) + end + + if self._private.dir == "y" then + return used_in_other, used_in_dir + end + return used_in_dir, used_in_other +end + +--- Set the expand mode which determines how sub widgets expand to take up +-- unused space. +-- +-- @tparam[opt=inside] string mode How to use unused space. +-- +-- * "inside" - Default option. Size of outside widgets is determined using +-- their fit function. Second, middle, or center widget expands to fill +-- remaining space. +-- * "outside" - Center widget is sized using its fit function and placed in +-- the center of the allowed space. Outside widgets expand (or contract) to +-- fill remaining space on their side. +-- * "none" - All widgets are sized using their fit function, drawn to only the +-- returned space, or remaining space, whichever is smaller. Center widget +-- gets priority. +-- @property expand + +function align:set_expand(mode) + if mode == "none" or mode == "outside" then + self._private.expand = mode + else + self._private.expand = "inside" + end + self:emit_signal("widget::layout_changed") +end + +function align:reset() + for _, v in pairs({ "first", "second", "third" }) do + self[v] = nil + end + self:emit_signal("widget::layout_changed") +end + +local function get_layout(dir, first, second, third) + local ret = base.make_widget(nil, nil, {enable_properties = true}) + ret._private.dir = dir + + for k, v in pairs(align) do + if type(v) == "function" then + rawset(ret, k, v) + end + end + + ret:set_expand("inside") + ret:set_first(first) + ret:set_second(second) + ret:set_third(third) + + -- An align layout allow set_children to have empty entries + ret.allow_empty_widget = true + + return ret +end + +--- Returns a new horizontal align layout. An align layout can display up to +-- three widgets. The widget set via :set_left() is left-aligned. :set_right() +-- sets a widget which will be right-aligned. The remaining space between those +-- two will be given to the widget set via :set_middle(). +-- @tparam[opt] widget left Widget to be put to the left. +-- @tparam[opt] widget middle Widget to be put to the middle. +-- @tparam[opt] widget right Widget to be put to the right. +function align.horizontal(left, middle, right) + local ret = get_layout("x", left, middle, right) + + rawset(ret, "set_left" , ret.set_first ) + rawset(ret, "set_middle", ret.set_second ) + rawset(ret, "set_right" , ret.set_third ) + + return ret +end + +--- Returns a new vertical align layout. An align layout can display up to +-- three widgets. The widget set via :set_top() is top-aligned. :set_bottom() +-- sets a widget which will be bottom-aligned. The remaining space between those +-- two will be given to the widget set via :set_middle(). +-- @tparam[opt] widget top Widget to be put to the top. +-- @tparam[opt] widget middle Widget to be put to the middle. +-- @tparam[opt] widget bottom Widget to be put to the right. +function align.vertical(top, middle, bottom) + local ret = get_layout("y", top, middle, bottom) + + rawset(ret, "set_top" , ret.set_first ) + rawset(ret, "set_middle", ret.set_second ) + rawset(ret, "set_bottom", ret.set_third ) + + return ret +end + +--Imported documentation + + +--- Get a widex index. +-- @param widget The widget to look for +-- @param[opt] recursive Also check sub-widgets +-- @param[opt] ... Aditional widgets to add at the end of the \"path\" +-- @return The index +-- @return The parent layout +-- @return The path between \"self\" and \"widget\" +-- @function index + +--- Get all direct and indirect children widgets. +-- This will scan all containers recursively to find widgets +-- Warning: This method it prone to stack overflow id the widget, or any of its +-- children, contain (directly or indirectly) itself. +-- @treturn table The children +-- @function get_all_children + +--- Set a declarative widget hierarchy description. +-- See [The declarative layout system](../documentation/03-declarative-layout.md.html) +-- @param args An array containing the widgets disposition +-- @function setup + +--- Force a widget height. +-- @property forced_height +-- @tparam number|nil height The height (`nil` for automatic) + +--- Force a widget width. +-- @property forced_width +-- @tparam number|nil width The width (`nil` for automatic) + +--- The widget opacity (transparency). +-- @property opacity +-- @tparam[opt=1] number opacity The opacity (between 0 and 1) + +--- The widget visibility. +-- @property visible +-- @param boolean + +--- Set/get a widget's buttons. +-- @param _buttons The table of buttons that should bind to the widget. +-- @function buttons + +--- Emit a signal and ensure all parent widgets in the hierarchies also +-- forward the signal. This is useful to track signals when there is a dynamic +-- set of containers and layouts wrapping the widget. +-- @tparam string signal_name +-- @param ... Other arguments +-- @function emit_signal_recursive + +--- When the layout (size) change. +-- This signal is emitted when the previous results of `:layout()` and `:fit()` +-- are no longer valid. Unless this signal is emitted, `:layout()` and `:fit()` +-- must return the same result when called with the same arguments. +-- @signal widget::layout_changed +-- @see widget::redraw_needed + +--- When the widget content changed. +-- This signal is emitted when the content of the widget changes. The widget will +-- be redrawn, it is not re-layouted. Put differently, it is assumed that +-- `:layout()` and `:fit()` would still return the same results as before. +-- @signal widget::redraw_needed +-- @see widget::layout_changed + +--- When a mouse button is pressed over the widget. +-- @signal button::press +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When a mouse button is released over the widget. +-- @signal button::release +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse enter a widget. +-- @signal mouse::enter +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse leave a widget. +-- @signal mouse::leave +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + + +--Imported documentation + + +--- Disconnect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback that should be disconnected +-- @function disconnect_signal + +--- Emit a signal. +-- +-- @tparam string name The name of the signal +-- @param ... Extra arguments for the callback functions. Each connected +-- function receives the object as first argument and then any extra arguments +-- that are given to emit_signal() +-- @function emit_signal + +--- Connect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function connect_signal + +--- Connect to a signal weakly. This allows the callback function to be garbage +-- collected and automatically disconnects the signal when that happens. +-- +-- **Warning:** +-- Only use this function if you really, really, really know what you +-- are doing. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function weak_connect_signal + + +return align + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/wibox/layout/constraint.lua b/lib/wibox/layout/constraint.lua new file mode 100644 index 0000000..5333b38 --- /dev/null +++ b/lib/wibox/layout/constraint.lua @@ -0,0 +1,17 @@ +--------------------------------------------------------------------------- +-- This class has been moved to `wibox.container.` +-- +-- @author Lukáš Hrázký +-- @copyright 2012 Lukáš Hrázký +-- @classmod wibox.layout.constraint +--------------------------------------------------------------------------- + +local util = require("awful.util") + +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/fixed.lua b/lib/wibox/layout/fixed.lua new file mode 100644 index 0000000..7258438 --- /dev/null +++ b/lib/wibox/layout/fixed.lua @@ -0,0 +1,585 @@ +--------------------------------------------------------------------------- +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_layout_defaults_fixed.svg) +-- +-- @usage +--wibox.widget { +-- generic_widget( 'first' ), +-- generic_widget( 'second' ), +-- generic_widget( 'third' ), +-- layout = wibox.layout.fixed.horizontal +--} +-- @author Uli Schlachter +-- @copyright 2010 Uli Schlachter +-- @classmod wibox.layout.fixed +--------------------------------------------------------------------------- + +local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1) +local base = require("wibox.widget.base") +local table = table +local pairs = pairs +local util = require("awful.util") + +local fixed = {} + +--Imported documentation + +--- Set a widget at a specific index, replace the current one. +-- **Signal:** widget::replaced The argument is the new widget and the old one +-- and the index. +-- @tparam number index A widget or a widget index +-- @param widget2 The widget to take the place of the first one +-- @treturn boolean If the operation is successful +-- @name set +-- @class function + +--- Replace the first instance of `widget` in the layout with `widget2`. +-- **Signal:** widget::replaced The argument is the new widget and the old one +-- and the index. +-- @param widget The widget to replace +-- @param widget2 The widget to replace `widget` with +-- @tparam[opt=false] boolean recursive Digg in all compatible layouts to find the widget. +-- @treturn boolean If the operation is successful +-- @name replace_widget +-- @class function + +--- Swap 2 widgets in a layout. +-- **Signal:** widget::swapped The arguments are both widgets and both (new) indexes. +-- @tparam number index1 The first widget index +-- @tparam number index2 The second widget index +-- @treturn boolean If the operation is successful +-- @name swap +-- @class function + +--- Swap 2 widgets in a layout. +-- If widget1 is present multiple time, only the first instance is swapped +-- **Signal:** widget::swapped The arguments are both widgets and both (new) indexes. +-- if the layouts not the same, then only `widget::replaced` will be emitted. +-- @param widget1 The first widget +-- @param widget2 The second widget +-- @tparam[opt=false] boolean recursive Digg in all compatible layouts to find the widget. +-- @treturn boolean If the operation is successful +-- @name swap_widgets +-- @class function + +--- Get all direct children of this layout. +-- @param layout The layout you are modifying. +-- @property children + +--- Reset a ratio layout. This removes all widgets from the layout. +-- **Signal:** widget::reset +-- @param layout The layout you are modifying. +-- @name reset +-- @class function + + +-- Layout a fixed layout. Each widget gets just the space it asks for. +-- @param context The context in which we are drawn. +-- @param width The available width. +-- @param height The available height. +function fixed:layout(context, width, height) + local result = {} + local pos,spacing = 0, self._private.spacing + + for k, v in pairs(self._private.widgets) do + local x, y, w, h, _ + if self._private.dir == "y" then + x, y = 0, pos + w, h = width, height - pos + if k ~= #self._private.widgets or not self._private.fill_space then + _, h = base.fit_widget(self, context, v, w, h); + end + pos = pos + h + spacing + else + x, y = pos, 0 + w, h = width - pos, height + if k ~= #self._private.widgets or not self._private.fill_space then + w, _ = base.fit_widget(self, context, v, w, h); + end + pos = pos + w + spacing + end + + if (self._private.dir == "y" and pos-spacing > height) or + (self._private.dir ~= "y" and pos-spacing > width) then + break + end + table.insert(result, base.place_widget_at(v, x, y, w, h)) + end + return result +end + +--- Add some widgets to the given fixed layout +-- @param ... Widgets that should be added (must at least be one) +function fixed:add(...) + -- No table.pack in Lua 5.1 :-( + local args = { n=select('#', ...), ... } + assert(args.n > 0, "need at least one widget to add") + for i=1, args.n do + base.check_widget(args[i]) + table.insert(self._private.widgets, args[i]) + end + self:emit_signal("widget::layout_changed") +end + + +--- Remove a widget from the layout +-- @tparam number index The widget index to remove +-- @treturn boolean index If the operation is successful +function fixed:remove(index) + if not index or index < 1 or index > #self._private.widgets then return false end + + table.remove(self._private.widgets, index) + + self:emit_signal("widget::layout_changed") + + return true +end + +--- Remove one or more widgets from the layout +-- The last parameter can be a boolean, forcing a recursive seach of the +-- widget(s) to remove. +-- @param widget ... Widgets that should be removed (must at least be one) +-- @treturn boolean If the operation is successful +function fixed:remove_widgets(...) + local args = { ... } + + local recursive = type(args[#args]) == "boolean" and args[#args] + + local ret = true + for k, rem_widget in ipairs(args) do + if recursive and k == #args then break end + + local idx, l = self:index(rem_widget, recursive) + + if idx and l and l.remove then + l:remove(idx, false) + else + ret = false + end + + end + + return #args > (recursive and 1 or 0) and ret +end + +function fixed:get_children() + return self._private.widgets +end + +function fixed:set_children(children) + self:reset() + if #children > 0 then + self:add(unpack(children)) + end +end + +--- Replace the first instance of `widget` in the layout with `widget2` +-- @param widget The widget to replace +-- @param widget2 The widget to replace `widget` with +-- @tparam[opt=false] boolean recursive Digg in all compatible layouts to find the widget. +-- @treturn boolean If the operation is successful +function fixed:replace_widget(widget, widget2, recursive) + local idx, l = self:index(widget, recursive) + + if idx and l then + l:set(idx, widget2) + return true + end + + return false +end + +function fixed:swap(index1, index2) + if not index1 or not index2 or index1 > #self._private.widgets + or index2 > #self._private.widgets then + return false + end + + local widget1, widget2 = self._private.widgets[index1], self._private.widgets[index2] + + self:set(index1, widget2) + self:set(index2, widget1) + + self:emit_signal("widget::swapped", widget1, widget2, index2, index1) + + return true +end + +function fixed:swap_widgets(widget1, widget2, recursive) + base.check_widget(widget1) + base.check_widget(widget2) + + local idx1, l1 = self:index(widget1, recursive) + local idx2, l2 = self:index(widget2, recursive) + + if idx1 and l1 and idx2 and l2 and (l1.set or l1.set_widget) and (l2.set or l2.set_widget) then + if l1.set then + l1:set(idx1, widget2) + if l1 == self then + self:emit_signal("widget::swapped", widget1, widget2, idx2, idx1) + end + elseif l1.set_widget then + l1:set_widget(widget2) + end + if l2.set then + l2:set(idx2, widget1) + if l2 == self then + self:emit_signal("widget::swapped", widget1, widget2, idx2, idx1) + end + elseif l2.set_widget then + l2:set_widget(widget1) + end + + return true + end + + return false +end + +function fixed:set(index, widget2) + if (not widget2) or (not self._private.widgets[index]) then return false end + + base.check_widget(widget2) + + local w = self._private.widgets[index] + + self._private.widgets[index] = widget2 + + self:emit_signal("widget::layout_changed") + self:emit_signal("widget::replaced", widget2, w, index) + + return true +end + +--- Insert a new widget in the layout at position `index` +-- **Signal:** widget::inserted The arguments are the widget and the index +-- @tparam number index The position +-- @param widget The widget +-- @treturn boolean If the operation is successful +function fixed:insert(index, widget) + if not index or index < 1 or index > #self._private.widgets + 1 then return false end + + base.check_widget(widget) + table.insert(self._private.widgets, index, widget) + self:emit_signal("widget::layout_changed") + self:emit_signal("widget::inserted", widget, #self._private.widgets) + + return true +end + +-- Fit the fixed layout into the given space +-- @param context The context in which we are fit. +-- @param orig_width The available width. +-- @param orig_height The available height. +function fixed:fit(context, orig_width, orig_height) + local width, height = orig_width, orig_height + local used_in_dir, used_max = 0, 0 + + for _, v in pairs(self._private.widgets) do + local w, h = base.fit_widget(self, context, v, width, height) + local in_dir, max + if self._private.dir == "y" then + max, in_dir = w, h + height = height - in_dir + else + in_dir, max = w, h + width = width - in_dir + end + if max > used_max then + used_max = max + end + used_in_dir = used_in_dir + in_dir + + if width <= 0 or height <= 0 then + if self._private.dir == "y" then + used_in_dir = orig_height + else + used_in_dir = orig_width + end + break + end + end + + local spacing = self._private.spacing * (#self._private.widgets-1) + + if self._private.dir == "y" then + return used_max, used_in_dir + spacing + end + return used_in_dir + spacing, used_max +end + +function fixed:reset() + self._private.widgets = {} + self:emit_signal("widget::layout_changed") + self:emit_signal("widget::reseted") +end + +--- Set the layout's fill_space property. If this property is true, the last +-- widget will get all the space that is left. If this is false, the last widget +-- won't be handled specially and there can be space left unused. +-- @property fill_space + +function fixed:fill_space(val) + if self._private.fill_space ~= val then + self._private.fill_space = not not val + self:emit_signal("widget::layout_changed") + end +end + +local function get_layout(dir, widget1, ...) + local ret = base.make_widget(nil, nil, {enable_properties = true}) + + util.table.crush(ret, fixed, true) + + ret._private.dir = dir + ret._private.widgets = {} + ret:set_spacing(0) + ret:fill_space(false) + + if widget1 then + ret:add(widget1, ...) + end + + return ret +end + +--- Returns a new horizontal fixed layout. Each widget will get as much space as it +-- asks for and each widget will be drawn next to its neighboring widget. +-- Widgets can be added via :add() or as arguments to this function. +-- @tparam widget ... Widgets that should be added to the layout. +-- @function wibox.layout.fixed.horizontal +function fixed.horizontal(...) + return get_layout("x", ...) +end + +--- Returns a new vertical fixed layout. Each widget will get as much space as it +-- asks for and each widget will be drawn next to its neighboring widget. +-- Widgets can be added via :add() or as arguments to this function. +-- @tparam widget ... Widgets that should be added to the layout. +-- @function wibox.layout.fixed.vertical +function fixed.vertical(...) + return get_layout("y", ...) +end + +--- Add spacing between each layout widgets +-- @property spacing +-- @tparam number spacing Spacing between widgets. + +function fixed:set_spacing(spacing) + if self._private.spacing ~= spacing then + self._private.spacing = spacing + self:emit_signal("widget::layout_changed") + end +end + +function fixed:get_spacing() + return self._private.spacing or 0 +end + +--Imported documentation + + +--- Get a widex index. +-- @param widget The widget to look for +-- @param[opt] recursive Also check sub-widgets +-- @param[opt] ... Aditional widgets to add at the end of the \"path\" +-- @return The index +-- @return The parent layout +-- @return The path between \"self\" and \"widget\" +-- @function index + +--- Get all direct and indirect children widgets. +-- This will scan all containers recursively to find widgets +-- Warning: This method it prone to stack overflow id the widget, or any of its +-- children, contain (directly or indirectly) itself. +-- @treturn table The children +-- @function get_all_children + +--- Set a declarative widget hierarchy description. +-- See [The declarative layout system](../documentation/03-declarative-layout.md.html) +-- @param args An array containing the widgets disposition +-- @function setup + +--- Force a widget height. +-- @property forced_height +-- @tparam number|nil height The height (`nil` for automatic) + +--- Force a widget width. +-- @property forced_width +-- @tparam number|nil width The width (`nil` for automatic) + +--- The widget opacity (transparency). +-- @property opacity +-- @tparam[opt=1] number opacity The opacity (between 0 and 1) + +--- The widget visibility. +-- @property visible +-- @param boolean + +--- Set/get a widget's buttons. +-- @param _buttons The table of buttons that should bind to the widget. +-- @function buttons + +--- Emit a signal and ensure all parent widgets in the hierarchies also +-- forward the signal. This is useful to track signals when there is a dynamic +-- set of containers and layouts wrapping the widget. +-- @tparam string signal_name +-- @param ... Other arguments +-- @function emit_signal_recursive + +--- When the layout (size) change. +-- This signal is emitted when the previous results of `:layout()` and `:fit()` +-- are no longer valid. Unless this signal is emitted, `:layout()` and `:fit()` +-- must return the same result when called with the same arguments. +-- @signal widget::layout_changed +-- @see widget::redraw_needed + +--- When the widget content changed. +-- This signal is emitted when the content of the widget changes. The widget will +-- be redrawn, it is not re-layouted. Put differently, it is assumed that +-- `:layout()` and `:fit()` would still return the same results as before. +-- @signal widget::redraw_needed +-- @see widget::layout_changed + +--- When a mouse button is pressed over the widget. +-- @signal button::press +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When a mouse button is released over the widget. +-- @signal button::release +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse enter a widget. +-- @signal mouse::enter +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse leave a widget. +-- @signal mouse::leave +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + + +--Imported documentation + + +--- Disconnect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback that should be disconnected +-- @function disconnect_signal + +--- Emit a signal. +-- +-- @tparam string name The name of the signal +-- @param ... Extra arguments for the callback functions. Each connected +-- function receives the object as first argument and then any extra arguments +-- that are given to emit_signal() +-- @function emit_signal + +--- Connect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function connect_signal + +--- Connect to a signal weakly. This allows the callback function to be garbage +-- collected and automatically disconnects the signal when that happens. +-- +-- **Warning:** +-- Only use this function if you really, really, really know what you +-- are doing. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function weak_connect_signal + + +return fixed + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/wibox/layout/flex.lua b/lib/wibox/layout/flex.lua new file mode 100644 index 0000000..1326f31 --- /dev/null +++ b/lib/wibox/layout/flex.lua @@ -0,0 +1,429 @@ +--------------------------------------------------------------------------- +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_layout_defaults_flex.svg) +-- +-- @usage +--wibox.widget { +-- generic_widget( 'first' ), +-- generic_widget( 'second' ), +-- generic_widget( 'third' ), +-- layout = wibox.layout.flex.horizontal +--} +-- @author Uli Schlachter +-- @copyright 2010 Uli Schlachter +-- @classmod wibox.layout.flex +--------------------------------------------------------------------------- + +local base = require("wibox.widget.base") +local fixed = require("wibox.layout.fixed") +local table = table +local pairs = pairs +local floor = math.floor +local util = require("awful.util") + +local flex = {} + +--Imported documentation + +--- Set a widget at a specific index, replace the current one. +-- **Signal:** widget::replaced The argument is the new widget and the old one +-- and the index. +-- @tparam number index A widget or a widget index +-- @param widget2 The widget to take the place of the first one +-- @treturn boolean If the operation is successful +-- @name set +-- @class function + +--- Replace the first instance of `widget` in the layout with `widget2`. +-- **Signal:** widget::replaced The argument is the new widget and the old one +-- and the index. +-- @param widget The widget to replace +-- @param widget2 The widget to replace `widget` with +-- @tparam[opt=false] boolean recursive Digg in all compatible layouts to find the widget. +-- @treturn boolean If the operation is successful +-- @name replace_widget +-- @class function + +--- Swap 2 widgets in a layout. +-- **Signal:** widget::swapped The arguments are both widgets and both (new) indexes. +-- @tparam number index1 The first widget index +-- @tparam number index2 The second widget index +-- @treturn boolean If the operation is successful +-- @name swap +-- @class function + +--- Swap 2 widgets in a layout. +-- If widget1 is present multiple time, only the first instance is swapped +-- **Signal:** widget::swapped The arguments are both widgets and both (new) indexes. +-- if the layouts not the same, then only `widget::replaced` will be emitted. +-- @param widget1 The first widget +-- @param widget2 The second widget +-- @tparam[opt=false] boolean recursive Digg in all compatible layouts to find the widget. +-- @treturn boolean If the operation is successful +-- @name swap_widgets +-- @class function + +--- Get all direct children of this layout. +-- @param layout The layout you are modifying. +-- @property children + +--- Reset a ratio layout. This removes all widgets from the layout. +-- **Signal:** widget::reset +-- @param layout The layout you are modifying. +-- @name reset +-- @class function + + +--- Replace the layout children +-- @tparam table children A table composed of valid widgets +-- @name set_children +-- @class function + +--- Add some widgets to the given fixed layout +-- @param layout The layout you are modifying. +-- @tparam widget ... Widgets that should be added (must at least be one) +-- @name add +-- @class function + +--- Remove a widget from the layout +-- @tparam index The widget index to remove +-- @treturn boolean index If the operation is successful +-- @name remove +-- @class function + +--- Remove one or more widgets from the layout +-- The last parameter can be a boolean, forcing a recursive seach of the +-- widget(s) to remove. +-- @param widget ... Widgets that should be removed (must at least be one) +-- @treturn boolean If the operation is successful +-- @name remove_widgets +-- @class function + +--- Insert a new widget in the layout at position `index` +-- @tparam number index The position +-- @param widget The widget +-- @treturn boolean If the operation is successful +-- @name insert +-- @class function + +function flex:layout(_, width, height) + local result = {} + local pos,spacing = 0, self._private.spacing + local num = #self._private.widgets + local total_spacing = (spacing*(num-1)) + + local space_per_item + if self._private.dir == "y" then + space_per_item = height / num - total_spacing/num + else + space_per_item = width / num - total_spacing/num + end + + if self._private.max_widget_size then + space_per_item = math.min(space_per_item, self._private.max_widget_size) + end + + for _, v in pairs(self._private.widgets) do + local x, y, w, h + if self._private.dir == "y" then + x, y = 0, util.round(pos) + w, h = width, floor(space_per_item) + else + x, y = util.round(pos), 0 + w, h = floor(space_per_item), height + end + + table.insert(result, base.place_widget_at(v, x, y, w, h)) + + pos = pos + space_per_item + spacing + + if (self._private.dir == "y" and pos-spacing >= height) or + (self._private.dir ~= "y" and pos-spacing >= width) then + break + end + end + + return result +end + +-- Fit the flex layout into the given space. +-- @param context The context in which we are fit. +-- @param orig_width The available width. +-- @param orig_height The available height. +function flex:fit(context, orig_width, orig_height) + local used_in_dir = 0 + local used_in_other = 0 + + -- Figure out the maximum size we can give out to sub-widgets + local sub_height = self._private.dir == "x" and orig_height or orig_height / #self._private.widgets + local sub_width = self._private.dir == "y" and orig_width or orig_width / #self._private.widgets + + for _, v in pairs(self._private.widgets) do + local w, h = base.fit_widget(self, context, v, sub_width, sub_height) + + local max = self._private.dir == "y" and w or h + if max > used_in_other then + used_in_other = max + end + + used_in_dir = used_in_dir + (self._private.dir == "y" and h or w) + end + + if self._private.max_widget_size then + used_in_dir = math.min(used_in_dir, + #self._private.widgets * self._private.max_widget_size) + end + + local spacing = self._private.spacing * (#self._private.widgets-1) + + if self._private.dir == "y" then + return used_in_other, used_in_dir + spacing + end + return used_in_dir + spacing, used_in_other +end + +--- Set the maximum size the widgets in this layout will take. +--That is, maximum width for horizontal and maximum height for vertical. +-- @property max_widget_size +-- @param number + +function flex:set_max_widget_size(val) + if self._private.max_widget_size ~= val then + self._private.max_widget_size = val + self:emit_signal("widget::layout_changed") + end +end + +local function get_layout(dir, widget1, ...) + local ret = fixed[dir](widget1, ...) + + util.table.crush(ret, flex, true) + + ret._private.fill_space = nil + + return ret +end + +--- Returns a new horizontal flex layout. A flex layout shares the available space +-- equally among all widgets. Widgets can be added via :add(widget). +-- @tparam widget ... Widgets that should be added to the layout. +-- @function wibox.layout.flex.horizontal +function flex.horizontal(...) + return get_layout("horizontal", ...) +end + +--- Returns a new vertical flex layout. A flex layout shares the available space +-- equally among all widgets. Widgets can be added via :add(widget). +-- @tparam widget ... Widgets that should be added to the layout. +-- @function wibox.layout.flex.vertical +function flex.vertical(...) + return get_layout("vertical", ...) +end + +--Imported documentation + + +--- Get a widex index. +-- @param widget The widget to look for +-- @param[opt] recursive Also check sub-widgets +-- @param[opt] ... Aditional widgets to add at the end of the \"path\" +-- @return The index +-- @return The parent layout +-- @return The path between \"self\" and \"widget\" +-- @function index + +--- Get all direct and indirect children widgets. +-- This will scan all containers recursively to find widgets +-- Warning: This method it prone to stack overflow id the widget, or any of its +-- children, contain (directly or indirectly) itself. +-- @treturn table The children +-- @function get_all_children + +--- Set a declarative widget hierarchy description. +-- See [The declarative layout system](../documentation/03-declarative-layout.md.html) +-- @param args An array containing the widgets disposition +-- @function setup + +--- Force a widget height. +-- @property forced_height +-- @tparam number|nil height The height (`nil` for automatic) + +--- Force a widget width. +-- @property forced_width +-- @tparam number|nil width The width (`nil` for automatic) + +--- The widget opacity (transparency). +-- @property opacity +-- @tparam[opt=1] number opacity The opacity (between 0 and 1) + +--- The widget visibility. +-- @property visible +-- @param boolean + +--- Set/get a widget's buttons. +-- @param _buttons The table of buttons that should bind to the widget. +-- @function buttons + +--- Emit a signal and ensure all parent widgets in the hierarchies also +-- forward the signal. This is useful to track signals when there is a dynamic +-- set of containers and layouts wrapping the widget. +-- @tparam string signal_name +-- @param ... Other arguments +-- @function emit_signal_recursive + +--- When the layout (size) change. +-- This signal is emitted when the previous results of `:layout()` and `:fit()` +-- are no longer valid. Unless this signal is emitted, `:layout()` and `:fit()` +-- must return the same result when called with the same arguments. +-- @signal widget::layout_changed +-- @see widget::redraw_needed + +--- When the widget content changed. +-- This signal is emitted when the content of the widget changes. The widget will +-- be redrawn, it is not re-layouted. Put differently, it is assumed that +-- `:layout()` and `:fit()` would still return the same results as before. +-- @signal widget::redraw_needed +-- @see widget::layout_changed + +--- When a mouse button is pressed over the widget. +-- @signal button::press +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When a mouse button is released over the widget. +-- @signal button::release +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse enter a widget. +-- @signal mouse::enter +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse leave a widget. +-- @signal mouse::leave +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + + +--Imported documentation + + +--- Disconnect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback that should be disconnected +-- @function disconnect_signal + +--- Emit a signal. +-- +-- @tparam string name The name of the signal +-- @param ... Extra arguments for the callback functions. Each connected +-- function receives the object as first argument and then any extra arguments +-- that are given to emit_signal() +-- @function emit_signal + +--- Connect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function connect_signal + +--- Connect to a signal weakly. This allows the callback function to be garbage +-- collected and automatically disconnects the signal when that happens. +-- +-- **Warning:** +-- Only use this function if you really, really, really know what you +-- are doing. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function weak_connect_signal + + +return flex + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/wibox/layout/init.lua b/lib/wibox/layout/init.lua new file mode 100644 index 0000000..4a9b006 --- /dev/null +++ b/lib/wibox/layout/init.lua @@ -0,0 +1,23 @@ +--------------------------------------------------------------------------- +--- Collection of layouts that can be used in widget boxes +-- +-- @author Uli Schlachter +-- @copyright 2010 Uli Schlachter +-- @classmod wibox.layout +--------------------------------------------------------------------------- +local base = require("wibox.widget.base") + +return setmetatable({ + fixed = require("wibox.layout.fixed"); + align = require("wibox.layout.align"); + flex = require("wibox.layout.flex"); + rotate = require("wibox.layout.rotate"); + margin = require("wibox.layout.margin"); + mirror = require("wibox.layout.mirror"); + constraint = require("wibox.layout.constraint"); + scroll = require("wibox.layout.scroll"); + ratio = require("wibox.layout.ratio"); + stack = require("wibox.layout.stack"); +}, {__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/layout/margin.lua b/lib/wibox/layout/margin.lua new file mode 100644 index 0000000..8d2eba2 --- /dev/null +++ b/lib/wibox/layout/margin.lua @@ -0,0 +1,17 @@ +--------------------------------------------------------------------------- +-- This class has been moved to `wibox.container.margin` +-- +-- @author Uli Schlachter +-- @copyright 2010 Uli Schlachter +-- @classmod wibox.layout.margin +--------------------------------------------------------------------------- + +local util = require("awful.util") + +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 new file mode 100644 index 0000000..16be754 --- /dev/null +++ b/lib/wibox/layout/mirror.lua @@ -0,0 +1,17 @@ +--------------------------------------------------------------------------- +-- This class has been moved to `wibox.container.mirror` +-- +-- @author dodo +-- @copyright 2012 dodo +-- @classmod wibox.layout.mirror +--------------------------------------------------------------------------- + +local util = require("awful.util") + +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/ratio.lua b/lib/wibox/layout/ratio.lua new file mode 100644 index 0000000..bcae443 --- /dev/null +++ b/lib/wibox/layout/ratio.lua @@ -0,0 +1,583 @@ +--------------------------------------------------------------------------- +--- A layout filling all the available space. Each widget is assigned a +-- ratio (percentage) of the total space. Multiple methods are available to +-- ajust this ratio. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_layout_defaults_ratio.svg) +-- +-- @usage +--local w = wibox.widget { +-- generic_widget( 'first' ), +-- generic_widget( 'second' ), +-- generic_widget( 'third' ), +-- layout = wibox.layout.ratio.horizontal +--} +--w:ajust_ratio(2, 0.44, 0.33, 0.22) +-- @author Emmanuel Lepage Vallee +-- @copyright 2016 Emmanuel Lepage Vallee +-- @classmod wibox.layout.ratio +--------------------------------------------------------------------------- + +local base = require("wibox.widget.base" ) +local flex = require("wibox.layout.flex" ) +local table = table +local pairs = pairs +local floor = math.floor +local util = require("awful.util") + +local ratio = {} + +--Imported documentation + +--- Set a widget at a specific index, replace the current one. +-- **Signal:** widget::replaced The argument is the new widget and the old one +-- and the index. +-- @tparam number index A widget or a widget index +-- @param widget2 The widget to take the place of the first one +-- @treturn boolean If the operation is successful +-- @name set +-- @class function + +--- Replace the first instance of `widget` in the layout with `widget2`. +-- **Signal:** widget::replaced The argument is the new widget and the old one +-- and the index. +-- @param widget The widget to replace +-- @param widget2 The widget to replace `widget` with +-- @tparam[opt=false] boolean recursive Digg in all compatible layouts to find the widget. +-- @treturn boolean If the operation is successful +-- @name replace_widget +-- @class function + +--- Swap 2 widgets in a layout. +-- **Signal:** widget::swapped The arguments are both widgets and both (new) indexes. +-- @tparam number index1 The first widget index +-- @tparam number index2 The second widget index +-- @treturn boolean If the operation is successful +-- @name swap +-- @class function + +--- Swap 2 widgets in a layout. +-- If widget1 is present multiple time, only the first instance is swapped +-- **Signal:** widget::swapped The arguments are both widgets and both (new) indexes. +-- if the layouts not the same, then only `widget::replaced` will be emitted. +-- @param widget1 The first widget +-- @param widget2 The second widget +-- @tparam[opt=false] boolean recursive Digg in all compatible layouts to find the widget. +-- @treturn boolean If the operation is successful +-- @name swap_widgets +-- @class function + +--- Get all direct children of this layout. +-- @param layout The layout you are modifying. +-- @property children + +--- Reset a ratio layout. This removes all widgets from the layout. +-- **Signal:** widget::reset +-- @param layout The layout you are modifying. +-- @name reset +-- @class function + + +-- Compute the sum of all ratio (ideally, it should be 1) +local function gen_sum(self, i_s, i_e) + local sum, new_w = 0,0 + + for i = i_s or 1, i_e or #self._private.widgets do + if self._private.ratios[i] then + sum = sum + self._private.ratios[i] + else + new_w = new_w + 1 + end + end + + return sum, new_w +end + +-- The ratios are expressed as percentages. For this to work, the sum of all +-- ratio must be 1. This function attempt to ajust them. Space can be taken +-- from or added to a ratio when widgets are being added or removed. If a +-- specific ratio must be enforced for a widget, it has to be done with the +-- `ajust_ratio` method after each insertion or deletion +local function normalize(self) + local count = #self._private.widgets + if count == 0 then return end + + -- Instead of adding "if" everywhere, just handle this common case + if count == 1 then + self._private.ratios = { 1 } + return + end + + local sum, new_w = gen_sum(self) + local old_count = #self._private.widgets - new_w + + local to_add = (sum == 0) and 1 or (sum / old_count) + + -- Make sure all widgets have a ratio + for i=1, #self._private.widgets do + if not self._private.ratios[i] then + self._private.ratios[i] = to_add + end + end + + sum = sum + to_add*new_w + + local delta, new_sum = (1 - sum) / count,0 + + -- Increase or decrease each ratio so it the sum become 1 + for i=1, #self._private.widgets do + self._private.ratios[i] = self._private.ratios[i] + delta + new_sum = new_sum + self._private.ratios[i] + end + + -- Floating points is not an exact science, but it should still be close + -- to 1.00. + assert(new_sum > 0.99 and new_sum < 1.01) +end + +function ratio:layout(_, width, height) + local result = {} + local pos,spacing = 0, self._private.spacing + + for k, v in ipairs(self._private.widgets) do + local space + local x, y, w, h + + if self._private.dir == "y" then + space = height * self._private.ratios[k] + x, y = 0, util.round(pos) + w, h = width, floor(space) + else + space = width * self._private.ratios[k] + x, y = util.round(pos), 0 + w, h = floor(space), height + end + + table.insert(result, base.place_widget_at(v, x, y, w, h)) + + pos = pos + space + spacing + + -- Make sure all widgets fit in the layout, if they aren't, something + -- went wrong + if (self._private.dir == "y" and util.round(pos) >= height) or + (self._private.dir ~= "y" and util.round(pos) >= width) then + break + end + end + + return result +end + +--- Increase the ratio of "widget" +-- If the increment produce an invalid ratio (not between 0 and 1), the method +-- do nothing. +-- @tparam number index The widget index to change +-- @tparam number increment An floating point value between -1 and 1 where the +-- end result is within 0 and 1 +function ratio:inc_ratio(index, increment) + if #self._private.widgets == 1 or (not index) or (not self._private.ratios[index]) + or increment < -1 or increment > 1 then + return + end + + assert(self._private.ratios[index]) + + self:set_ratio(index, self._private.ratios[index] + increment) +end + +--- Increment the ratio of the first instance of `widget` +-- If the increment produce an invalid ratio (not between 0 and 1), the method +-- do nothing. +-- @param widget The widget to ajust +-- @tparam number increment An floating point value between -1 and 1 where the +-- end result is within 0 and 1 +function ratio:inc_widget_ratio(widget, increment) + if not widget or not increment then return end + + local index = self:index(widget) + + self:inc_ratio(index, increment) +end + +--- Set the ratio of the widget at position `index` +-- @tparam number index The index of the widget to change +-- @tparam number percent An floating point value between 0 and 1 +function ratio:set_ratio(index, percent) + if not percent or #self._private.widgets == 1 or not index or not self._private.widgets[index] + or percent < 0 or percent > 1 then + return + end + + local old = self._private.ratios[index] + + -- Remove what has to be cleared from all widget + local delta = ( (percent-old) / (#self._private.widgets-1) ) + + for k in pairs(self._private.widgets) do + self._private.ratios[k] = self._private.ratios[k] - delta + end + + -- Set the new ratio + self._private.ratios[index] = percent + + -- As some widgets may now have a slightly negative ratio, normalize again + normalize(self) + + self:emit_signal("widget::layout_changed") +end + +--- Get the ratio at `index`. +-- @tparam number index The widget index to query +-- @treturn number The index (between 0 and 1) +function ratio:get_ratio(index) + if not index then return end + return self._private.ratios[index] +end + +--- Set the ratio of `widget` to `percent`. +-- @tparam widget widget The widget to ajust. +-- @tparam number percent A floating point value between 0 and 1. +function ratio:set_widget_ratio(widget, percent) + local index = self:index(widget) + + self:set_ratio(index, percent) +end + +--- Update all widgets to match a set of a ratio. +-- The sum of before, itself and after must be 1 or nothing will be done +-- @tparam number index The index of the widget to change +-- @tparam number before The sum of the ratio before the widget +-- @tparam number itself The ratio for "widget" +-- @tparam number after The sum of the ratio after the widget +function ratio:ajust_ratio(index, before, itself, after) + if not self._private.widgets[index] or not before or not itself or not after then + return + end + + local sum = before + itself + after + + -- As documented, it is the caller job to come up with valid numbers + if math.min(before, itself, after) < 0 then return end + if sum > 1.01 or sum < -0.99 then return end + + -- Compute the before and after offset to be applied to each widgets + local before_count, after_count = index-1, #self._private.widgets - index + + local b, a = gen_sum(self, 1, index-1), gen_sum(self, index+1) + + local db, da = (before - b)/before_count, (after - a)/after_count + + -- Apply the new ratio + self._private.ratios[index] = itself + + -- Equality split the delta among widgets before and after + for i = 1, index -1 do + self._private.ratios[i] = self._private.ratios[i] + db + end + for i = index+1, #self._private.widgets do + self._private.ratios[i] = self._private.ratios[i] + da + end + + -- Remove potential negative ratio + normalize(self) + + self:emit_signal("widget::layout_changed") +end + +--- Update all widgets to match a set of a ratio +-- @param widget The widget to ajust +-- @tparam number before The sum of the ratio before the widget +-- @tparam number itself The ratio for "widget" +-- @tparam number after The sum of the ratio after the widget +function ratio:ajust_widget_ratio(widget, before, itself, after) + local index = self:index(widget) + self:ajust_ratio(index, before, itself, after) +end + +--- Add some widgets to the given fixed layout +-- **Signal:** widget::added The argument are the widgets +-- @tparam widget ... Widgets that should be added (must at least be one) +function ratio:add(...) + -- No table.pack in Lua 5.1 :-( + local args = { n=select('#', ...), ... } + assert(args.n > 0, "need at least one widget to add") + for i=1, args.n do + base.check_widget(args[i]) + table.insert(self._private.widgets, args[i]) + end + + normalize(self) + self:emit_signal("widget::layout_changed") + self:emit_signal("widget::added", ...) +end + +--- Remove a widget from the layout +-- **Signal:** widget::removed The arguments are the widget and the index +-- @tparam number index The widget index to remove +-- @treturn boolean index If the operation is successful +function ratio:remove(index) + if not index or not self._private.widgets[index] then return false end + + local w = self._private.widgets[index] + + table.remove(self._private.ratios, index) + table.remove(self._private.widgets, index) + + normalize(self) + + self:emit_signal("widget::layout_changed") + self:emit_signal("widget::removed", w, index) + + return true +end + +--- Insert a new widget in the layout at position `index` +-- **Signal:** widget::inserted The arguments are the widget and the index +-- @tparam number index The position +-- @param widget The widget +function ratio:insert(index, widget) + if not index or index < 1 or index > #self._private.widgets + 1 then return false end + + base.check_widget(widget) + + table.insert(self._private.widgets, index, widget) + + normalize(self) + + self:emit_signal("widget::layout_changed") + self:emit_signal("widget::inserted", widget, #self._private.widgets) +end + +local function get_layout(dir, widget1, ...) + local ret = flex[dir](widget1, ...) + + util.table.crush(ret, ratio, true) + + ret._private.fill_space = nil + + ret._private.ratios = {} + + return ret +end + +--- Returns a new horizontal ratio layout. A ratio layout shares the available space +-- equally among all widgets. Widgets can be added via :add(widget). +-- @tparam widget ... Widgets that should be added to the layout. +function ratio.horizontal(...) + return get_layout("horizontal", ...) +end + +--- Returns a new vertical ratio layout. A ratio layout shares the available space +-- equally among all widgets. Widgets can be added via :add(widget). +-- @tparam widget ... Widgets that should be added to the layout. +function ratio.vertical(...) + return get_layout("vertical", ...) +end + +--Imported documentation + + +--- Get a widex index. +-- @param widget The widget to look for +-- @param[opt] recursive Also check sub-widgets +-- @param[opt] ... Aditional widgets to add at the end of the \"path\" +-- @return The index +-- @return The parent layout +-- @return The path between \"self\" and \"widget\" +-- @function index + +--- Get all direct and indirect children widgets. +-- This will scan all containers recursively to find widgets +-- Warning: This method it prone to stack overflow id the widget, or any of its +-- children, contain (directly or indirectly) itself. +-- @treturn table The children +-- @function get_all_children + +--- Set a declarative widget hierarchy description. +-- See [The declarative layout system](../documentation/03-declarative-layout.md.html) +-- @param args An array containing the widgets disposition +-- @function setup + +--- Force a widget height. +-- @property forced_height +-- @tparam number|nil height The height (`nil` for automatic) + +--- Force a widget width. +-- @property forced_width +-- @tparam number|nil width The width (`nil` for automatic) + +--- The widget opacity (transparency). +-- @property opacity +-- @tparam[opt=1] number opacity The opacity (between 0 and 1) + +--- The widget visibility. +-- @property visible +-- @param boolean + +--- Set/get a widget's buttons. +-- @param _buttons The table of buttons that should bind to the widget. +-- @function buttons + +--- Emit a signal and ensure all parent widgets in the hierarchies also +-- forward the signal. This is useful to track signals when there is a dynamic +-- set of containers and layouts wrapping the widget. +-- @tparam string signal_name +-- @param ... Other arguments +-- @function emit_signal_recursive + +--- When the layout (size) change. +-- This signal is emitted when the previous results of `:layout()` and `:fit()` +-- are no longer valid. Unless this signal is emitted, `:layout()` and `:fit()` +-- must return the same result when called with the same arguments. +-- @signal widget::layout_changed +-- @see widget::redraw_needed + +--- When the widget content changed. +-- This signal is emitted when the content of the widget changes. The widget will +-- be redrawn, it is not re-layouted. Put differently, it is assumed that +-- `:layout()` and `:fit()` would still return the same results as before. +-- @signal widget::redraw_needed +-- @see widget::layout_changed + +--- When a mouse button is pressed over the widget. +-- @signal button::press +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When a mouse button is released over the widget. +-- @signal button::release +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse enter a widget. +-- @signal mouse::enter +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse leave a widget. +-- @signal mouse::leave +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + + +--Imported documentation + + +--- Disconnect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback that should be disconnected +-- @function disconnect_signal + +--- Emit a signal. +-- +-- @tparam string name The name of the signal +-- @param ... Extra arguments for the callback functions. Each connected +-- function receives the object as first argument and then any extra arguments +-- that are given to emit_signal() +-- @function emit_signal + +--- Connect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function connect_signal + +--- Connect to a signal weakly. This allows the callback function to be garbage +-- collected and automatically disconnects the signal when that happens. +-- +-- **Warning:** +-- Only use this function if you really, really, really know what you +-- are doing. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function weak_connect_signal + + +return ratio + +-- 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 new file mode 100644 index 0000000..a6b5d7a --- /dev/null +++ b/lib/wibox/layout/rotate.lua @@ -0,0 +1,17 @@ +--------------------------------------------------------------------------- +-- This class has been moved to `wibox.container.rotate` +-- +-- @author Uli Schlachter +-- @copyright 2010 Uli Schlachter +-- @classmod wibox.layout.rotate +--------------------------------------------------------------------------- + +local util = require("awful.util") + +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 new file mode 100644 index 0000000..e0be79f --- /dev/null +++ b/lib/wibox/layout/scroll.lua @@ -0,0 +1,16 @@ +--------------------------------------------------------------------------- +-- This class has been moved to `wibox.container.scroll` +-- +-- @author Uli Schlachter (based on ideas from Saleur Geoffrey) +-- @copyright 2015 Uli Schlachter +-- @classmod wibox.layout.scroll +--------------------------------------------------------------------------- +local util = require("awful.util") + +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/layout/stack.lua b/lib/wibox/layout/stack.lua new file mode 100644 index 0000000..bd28703 --- /dev/null +++ b/lib/wibox/layout/stack.lua @@ -0,0 +1,402 @@ +--------------------------------------------------------------------------- +-- A stacked layout. +-- +-- This layout display widgets on top of each other. It can be used to overlay +-- a `wibox.widget.textbox` on top of a `awful.widget.progressbar` or manage +-- "pages" where only one is visible at any given moment. +-- +-- The indices are going from 1 (the bottom of the stack) up to the top of +-- the stack. The order can be changed either using `:swap` or `:raise`. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_layout_defaults_stack.svg) +-- +-- @usage +--wibox.widget { +-- generic_widget( 'first' ), +-- generic_widget( 'second' ), +-- generic_widget( 'third' ), +-- layout = wibox.layout.stack +--} +-- @author Emmanuel Lepage Vallee +-- @copyright 2016 Emmanuel Lepage Vallee +-- @classmod wibox.layout.stack +--------------------------------------------------------------------------- + +local base = require("wibox.widget.base" ) +local fixed = require("wibox.layout.fixed") +local table = table +local pairs = pairs +local util = require("awful.util") + +local stack = {mt={}} + +--Imported documentation + +--- Set a widget at a specific index, replace the current one. +-- **Signal:** widget::replaced The argument is the new widget and the old one +-- and the index. +-- @tparam number index A widget or a widget index +-- @param widget2 The widget to take the place of the first one +-- @treturn boolean If the operation is successful +-- @name set +-- @class function + +--- Replace the first instance of `widget` in the layout with `widget2`. +-- **Signal:** widget::replaced The argument is the new widget and the old one +-- and the index. +-- @param widget The widget to replace +-- @param widget2 The widget to replace `widget` with +-- @tparam[opt=false] boolean recursive Digg in all compatible layouts to find the widget. +-- @treturn boolean If the operation is successful +-- @name replace_widget +-- @class function + +--- Swap 2 widgets in a layout. +-- **Signal:** widget::swapped The arguments are both widgets and both (new) indexes. +-- @tparam number index1 The first widget index +-- @tparam number index2 The second widget index +-- @treturn boolean If the operation is successful +-- @name swap +-- @class function + +--- Swap 2 widgets in a layout. +-- If widget1 is present multiple time, only the first instance is swapped +-- **Signal:** widget::swapped The arguments are both widgets and both (new) indexes. +-- if the layouts not the same, then only `widget::replaced` will be emitted. +-- @param widget1 The first widget +-- @param widget2 The second widget +-- @tparam[opt=false] boolean recursive Digg in all compatible layouts to find the widget. +-- @treturn boolean If the operation is successful +-- @name swap_widgets +-- @class function + +--- Get all direct children of this layout. +-- @param layout The layout you are modifying. +-- @property children + +--- Reset a ratio layout. This removes all widgets from the layout. +-- **Signal:** widget::reset +-- @param layout The layout you are modifying. +-- @name reset +-- @class function + + +--- Add some widgets to the given stack layout +-- @param layout The layout you are modifying. +-- @tparam widget ... Widgets that should be added (must at least be one) +-- @name add +-- @class function + +--- Remove a widget from the layout +-- @tparam index The widget index to remove +-- @treturn boolean index If the operation is successful +-- @name remove +-- @class function + +--- Insert a new widget in the layout at position `index` +-- @tparam number index The position +-- @param widget The widget +-- @treturn boolean If the operation is successful +-- @name insert +-- @class function + +--- Remove one or more widgets from the layout +-- The last parameter can be a boolean, forcing a recursive seach of the +-- widget(s) to remove. +-- @param widget ... Widgets that should be removed (must at least be one) +-- @treturn boolean If the operation is successful +-- @name remove_widgets +-- @class function + +--- Add spacing between each layout widgets +-- @property spacing +-- @tparam number spacing Spacing between widgets. + +function stack:layout(_, width, height) + local result = {} + local spacing = self._private.spacing + + for _, v in pairs(self._private.widgets) do + table.insert(result, base.place_widget_at(v, spacing, spacing, width - 2*spacing, height - 2*spacing)) + if self._private.top_only then break end + end + + return result +end + +function stack:fit(context, orig_width, orig_height) + local max_w, max_h = 0,0 + local spacing = self._private.spacing + + for _, v in pairs(self._private.widgets) do + local w, h = base.fit_widget(self, context, v, orig_width, orig_height) + max_w, max_h = math.max(max_w, w+2*spacing), math.max(max_h, h+2*spacing) + end + + return math.min(max_w, orig_width), math.min(max_h, orig_height) +end + +--- If only the first stack widget is drawn +-- @property top_only + +function stack:get_top_only() + return self._private.top_only +end + +function stack:set_top_only(top_only) + self._private.top_only = top_only +end + +--- Raise a widget at `index` to the top of the stack +-- @tparam number index the widget index to raise +function stack:raise(index) + if (not index) or self._private.widgets[index] then return end + + local w = self._private.widgets[index] + table.remove(self._private.widgets, index) + table.insert(self._private.widgets, w) + + self:emit_signal("widget::layout_changed") +end + +--- Raise the first instance of `widget` +-- @param widget The widget to raise +-- @tparam[opt=false] boolean recursive Also look deeper in the hierarchy to +-- find the widget +function stack:raise_widget(widget, recursive) + local idx, layout = self:index(widget, recursive) + + if not idx or not layout then return end + + -- Bubble up in the stack until the right index is found + while layout and layout ~= self do + idx, layout = self:index(layout, recursive) + end + + if layout == self and idx ~= 1 then + self:raise(idx) + end +end + +--- Create a new stack layout. +-- @function wibox.layout.stack +-- @treturn widget A new stack layout + +local function new(...) + local ret = fixed.horizontal(...) + + util.table.crush(ret, stack, true) + + return ret +end + +function stack.mt:__call(_, ...) + return new(...) +end + +--Imported documentation + + +--- Get a widex index. +-- @param widget The widget to look for +-- @param[opt] recursive Also check sub-widgets +-- @param[opt] ... Aditional widgets to add at the end of the \"path\" +-- @return The index +-- @return The parent layout +-- @return The path between \"self\" and \"widget\" +-- @function index + +--- Get all direct and indirect children widgets. +-- This will scan all containers recursively to find widgets +-- Warning: This method it prone to stack overflow id the widget, or any of its +-- children, contain (directly or indirectly) itself. +-- @treturn table The children +-- @function get_all_children + +--- Set a declarative widget hierarchy description. +-- See [The declarative layout system](../documentation/03-declarative-layout.md.html) +-- @param args An array containing the widgets disposition +-- @function setup + +--- Force a widget height. +-- @property forced_height +-- @tparam number|nil height The height (`nil` for automatic) + +--- Force a widget width. +-- @property forced_width +-- @tparam number|nil width The width (`nil` for automatic) + +--- The widget opacity (transparency). +-- @property opacity +-- @tparam[opt=1] number opacity The opacity (between 0 and 1) + +--- The widget visibility. +-- @property visible +-- @param boolean + +--- Set/get a widget's buttons. +-- @param _buttons The table of buttons that should bind to the widget. +-- @function buttons + +--- Emit a signal and ensure all parent widgets in the hierarchies also +-- forward the signal. This is useful to track signals when there is a dynamic +-- set of containers and layouts wrapping the widget. +-- @tparam string signal_name +-- @param ... Other arguments +-- @function emit_signal_recursive + +--- When the layout (size) change. +-- This signal is emitted when the previous results of `:layout()` and `:fit()` +-- are no longer valid. Unless this signal is emitted, `:layout()` and `:fit()` +-- must return the same result when called with the same arguments. +-- @signal widget::layout_changed +-- @see widget::redraw_needed + +--- When the widget content changed. +-- This signal is emitted when the content of the widget changes. The widget will +-- be redrawn, it is not re-layouted. Put differently, it is assumed that +-- `:layout()` and `:fit()` would still return the same results as before. +-- @signal widget::redraw_needed +-- @see widget::layout_changed + +--- When a mouse button is pressed over the widget. +-- @signal button::press +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When a mouse button is released over the widget. +-- @signal button::release +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse enter a widget. +-- @signal mouse::enter +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse leave a widget. +-- @signal mouse::leave +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + + +--Imported documentation + + +--- Disconnect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback that should be disconnected +-- @function disconnect_signal + +--- Emit a signal. +-- +-- @tparam string name The name of the signal +-- @param ... Extra arguments for the callback functions. Each connected +-- function receives the object as first argument and then any extra arguments +-- that are given to emit_signal() +-- @function emit_signal + +--- Connect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function connect_signal + +--- Connect to a signal weakly. This allows the callback function to be garbage +-- collected and automatically disconnects the signal when that happens. +-- +-- **Warning:** +-- Only use this function if you really, really, really know what you +-- are doing. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function weak_connect_signal + + +return setmetatable(stack, stack.mt) +-- 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 new file mode 100644 index 0000000..3c35a0b --- /dev/null +++ b/lib/wibox/widget/background.lua @@ -0,0 +1,16 @@ +--------------------------------------------------------------------------- +-- This class has been moved to `wibox.container.background` +-- +-- @author Uli Schlachter +-- @copyright 2010 Uli Schlachter +-- @classmod wibox.widget.background +--------------------------------------------------------------------------- +local util = require("awful.util") + +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/lib/wibox/widget/base.lua b/lib/wibox/widget/base.lua new file mode 100644 index 0000000..dd80ec7 --- /dev/null +++ b/lib/wibox/widget/base.lua @@ -0,0 +1,694 @@ +--------------------------------------------------------------------------- +-- @author Uli Schlachter +-- @copyright 2010 Uli Schlachter +-- @classmod wibox.widget.base +--------------------------------------------------------------------------- + +local object = require("gears.object") +local cache = require("gears.cache") +local matrix = require("gears.matrix") +local protected_call = require("gears.protected_call") +local util = require("awful.util") +local setmetatable = setmetatable +local pairs = pairs +local type = type +local table = table + +local base = {} + +-- {{{ Functions on widgets + +--- Functions available on all widgets. +base.widget = {} + +--- Set/get a widget's buttons. +-- @tab _buttons The table of buttons that is bound to the widget. +-- @function buttons +function base.widget:buttons(_buttons) + if _buttons then + self._private.widget_buttons = _buttons + end + return self._private.widget_buttons +end + +--- Set a widget's visibility. +-- @tparam boolean b Whether the widget is visible. +-- @function set_visible +function base.widget:set_visible(b) + if b ~= self._private.visible then + self._private.visible = b + self:emit_signal("widget::layout_changed") + -- In case something ignored fit and drew the widget anyway. + self:emit_signal("widget::redraw_needed") + end +end + +--- Is the widget visible? +-- @treturn boolean +-- @function get_visible +function base.widget:get_visible() + return self._private.visible or false +end + +--- Set a widget's opacity. +-- @tparam number o The opacity to use (a number from 0 (transparent) to 1 +-- (opaque)). +-- @function set_opacity +function base.widget:set_opacity(o) + if o ~= self._private.opacity then + self._private.opacity = o + self:emit_signal("widget::redraw") + end +end + +--- Get the widget's opacity. +-- @treturn number The opacity (between 0 (transparent) and 1 (opaque)). +-- @function get_opacity +function base.widget:get_opacity() + return self._private.opacity +end + +--- Set the widget's forced width. +-- @tparam[opt] number width With `nil` the default mechanism of calling the +-- `:fit` method is used. +-- @see fit_widget +-- @function set_forced_width +function base.widget:set_forced_width(width) + if width ~= self._private.forced_width then + self._private.forced_width = width + self:emit_signal("widget::layout_changed") + end +end + +--- Get the widget's forced width. +-- +-- Note that widget instances can be used in different places simultaneously, +-- and therefore can have multiple dimensions. +-- If there is no forced width/height, then the only way to get the widget's +-- actual size is during a `mouse::enter`, `mouse::leave` or button event. +-- @treturn[opt] number The forced width (nil if automatic). +-- @see fit_widget +-- @function get_forced_width +function base.widget:get_forced_width() + return self._private.forced_width +end + +--- Set the widget's forced height. +-- @tparam[opt] number height With `nil` the default mechanism of calling the +-- `:fit` method is used. +-- @see fit_widget +-- @function set_height +function base.widget:set_forced_height(height) + if height ~= self._private.forced_height then + self._private.forced_height = height + self:emit_signal("widget::layout_changed") + end +end + +--- Get the widget's forced height. +-- +-- Note that widget instances can be used in different places simultaneously, +-- and therefore can have multiple dimensions. +-- If there is no forced width/height, then the only way to get the widget's +-- actual size is during a `mouse::enter`, `mouse::leave` or button event. +-- @treturn[opt] number The forced height (nil if automatic). +-- @function get_forced_height +function base.widget:get_forced_height() + return self._private.forced_height +end + +--- Get the widget's direct children widgets. +-- +-- This method should be re-implemented by the relevant widgets. +-- @treturn table The children +-- @function get_children +function base.widget:get_children() + return {} +end + +--- Replace the layout children. +-- +-- The default implementation does nothing, this must be re-implemented by +-- all layout and container widgets. +-- @tab children A table composed of valid widgets. +-- @function set_children +function base.widget:set_children(children) -- luacheck: no unused + -- Nothing on purpose +end + +-- It could have been merged into `get_all_children`, but it's not necessary. +local function digg_children(ret, tlw) + for _, w in ipairs(tlw:get_children()) do + table.insert(ret, w) + digg_children(ret, w) + end +end + +--- Get all direct and indirect children widgets. +-- +-- This will scan all containers recursively to find widgets. +-- +-- *Warning*: This method it prone to stack overflow if the widget, or any of +-- its children, contains (directly or indirectly) itself. +-- @treturn table The children +-- @function get_all_children +function base.widget:get_all_children() + local ret = {} + digg_children(ret, self) + return ret +end + +--- Emit a signal and ensure all parent widgets in the hierarchies also +-- forward the signal. This is useful to track signals when there is a dynamic +-- set of containers and layouts wrapping the widget. +-- +-- Note that this function has two flaws: +-- +-- 1. The signal is only forwarded once the widget tree has been built. This +-- happens after all currently scheduled functions have been executed. +-- Therefore, it will not start to work right away. +-- 2. In case the widget is present multiple times in a single widget tree, +-- this function will also forward the signal multiple time (one per upward +-- tree path). +-- +-- @tparam string signal_name +-- @param ... Other arguments +-- @function emit_signal_recursive +function base.widget:emit_signal_recursive(signal_name, ...) + -- This is a convenience wrapper, the real implementation is in the + -- hierarchy. + + self:emit_signal("widget::emit_recursive", signal_name, ...) +end + +--- Get the index of a widget. +-- @tparam widget widget The widget to look for. +-- @tparam[opt] boolean recursive Also check sub-widgets? +-- @tparam[opt] widget ... Additional widgets to add at the end of the "path" +-- @treturn number The index. +-- @treturn widget The parent widget. +-- @treturn table The path between "self" and "widget". +-- @function index +function base.widget:index(widget, recursive, ...) + local widgets = self:get_children() + for idx, w in ipairs(widgets) do + if w == widget then + return idx, self, {...} + elseif recursive then + local child_idx, l, path = w:index(widget, true, self, ...) + if child_idx and l then + return child_idx, l, path + end + end + end + return nil, self, {} +end +-- }}} + +-- {{{ Caches + +-- Indexes are widgets, allow them to be garbage-collected. +local widget_dependencies = setmetatable({}, { __mode = "kv" }) + +-- Get the cache of the given kind for this widget. This returns a gears.cache +-- that calls the callback of kind `kind` on the widget. +local function get_cache(widget, kind) + if not widget._private.widget_caches[kind] then + widget._private.widget_caches[kind] = cache.new(function(...) + return protected_call(widget[kind], widget, ...) + end) + end + return widget._private.widget_caches[kind] +end + +-- Special value to skip the dependency recording that is normally done by +-- base.fit_widget() and base.layout_widget(). The caller must ensure that no +-- caches depend on the result of the call and/or must handle the children's +-- widget::layout_changed signal correctly when using this. +base.no_parent_I_know_what_I_am_doing = {} + +-- Record a dependency from parent to child: The layout of `parent` depends on +-- the layout of `child`. +local function record_dependency(parent, child) + if parent == base.no_parent_I_know_what_I_am_doing then + return + end + + base.check_widget(parent) + base.check_widget(child) + + local deps = widget_dependencies[child] or {} + deps[parent] = true + widget_dependencies[child] = deps +end + +-- Clear the caches for `widget` and all widgets that depend on it. +local clear_caches +function clear_caches(widget) + local deps = widget_dependencies[widget] or {} + widget_dependencies[widget] = {} + widget._private.widget_caches = {} + for w in pairs(deps) do + clear_caches(w) + end +end + +-- }}} + +--- Figure out the geometry in the device coordinate space. +-- +-- This gives only tight bounds if no rotations by non-multiples of 90° are +-- used. +-- @function wibox.widget.base.rect_to_device_geometry +function base.rect_to_device_geometry(cr, x, y, width, height) + return matrix.transform_rectangle(cr.matrix, x, y, width, height) +end + +--- Fit a widget for the given available width and height. +-- +-- This calls the widget's `:fit` callback and caches the result for later use. +-- Never call `:fit` directly, but always through this function! +-- @tparam widget parent The parent widget which requests this information. +-- @tab context The context in which we are fit. +-- @tparam widget widget The widget to fit (this uses +-- `widget:fit(context, width, height)`). +-- @tparam number width The available width for the widget. +-- @tparam number height The available height for the widget. +-- @treturn number The width that the widget wants to use. +-- @treturn number The height that the widget wants to use. +-- @function wibox.widget.base.fit_widget +function base.fit_widget(parent, context, widget, width, height) + record_dependency(parent, widget) + + if not widget._private.visible then + return 0, 0 + end + + -- Sanitize the input. This also filters out e.g. NaN. + width = math.max(0, width) + height = math.max(0, height) + + local w, h = 0, 0 + if widget.fit then + w, h = get_cache(widget, "fit"):get(context, width, height) + else + -- If it has no fit method, calculate based on the size of children + local children = base.layout_widget(parent, context, widget, width, height) + for _, info in ipairs(children or {}) do + local x, y, w2, h2 = matrix.transform_rectangle(info._matrix, + 0, 0, info._width, info._height) + w, h = math.max(w, x + w2), math.max(h, y + h2) + end + end + + -- Apply forced size and handle nil's + w = widget._private.forced_width or w or 0 + h = widget._private.forced_height or h or 0 + + -- Also sanitize the output. + w = math.max(0, math.min(w, width)) + h = math.max(0, math.min(h, height)) + return w, h +end + +--- Lay out a widget for the given available width and height. +-- +-- This calls the widget's `:layout` callback and caches the result for later +-- use. Never call `:layout` directly, but always through this function! +-- However, normally there shouldn't be any reason why you need to use this +-- function. +-- @tparam widget parent The parent widget which requests this information. +-- @tab context The context in which we are laid out. +-- @tparam widget widget The widget to layout (this uses +-- `widget:layout(context, width, height)`). +-- @tparam number width The available width for the widget. +-- @tparam number height The available height for the widget. +-- @treturn[opt] table The result from the widget's `:layout` callback. +-- @function wibox.widget.base.layout_widget +function base.layout_widget(parent, context, widget, width, height) + record_dependency(parent, widget) + + if not widget._private.visible then + return + end + + -- Sanitize the input. This also filters out e.g. NaN. + width = math.max(0, width) + height = math.max(0, height) + + if widget.layout then + return get_cache(widget, "layout"):get(context, width, height) + end +end + +--- Handle a button event on a widget. +-- +-- This is used internally and should not be called directly. +-- @function wibox.widget.base.handle_button +function base.handle_button(event, widget, x, y, button, modifiers, geometry) + x = x or y -- luacheck: no unused + local function is_any(mod) + return #mod == 1 and mod[1] == "Any" + end + + local function tables_equal(a, b) + if #a ~= #b then + return false + end + for k, v in pairs(b) do + if a[k] ~= v then + return false + end + end + return true + end + + -- Find all matching button objects. + local matches = {} + for _, v in pairs(widget._private.widget_buttons) do + local match = true + -- Is it the right button? + if v.button ~= 0 and v.button ~= button then match = false end + -- Are the correct modifiers pressed? + if (not is_any(v.modifiers)) and (not tables_equal(v.modifiers, modifiers)) then match = false end + if match then + table.insert(matches, v) + end + end + + -- Emit the signals. + for _, v in pairs(matches) do + v:emit_signal(event,geometry) + end +end + +--- Create widget placement information. This should be used in a widget's +-- `:layout()` callback. +-- @tparam widget widget The widget that should be placed. +-- @param mat A matrix transforming from the parent widget's coordinate +-- system. For example, use matrix.create_translate(1, 2) to draw a +-- widget at position (1, 2) relative to the parent widget. +-- @tparam number width The width of the widget in its own coordinate system. +-- That is, after applying the transformation matrix. +-- @tparam number height The height of the widget in its own coordinate system. +-- That is, after applying the transformation matrix. +-- @treturn table An opaque object that can be returned from `:layout()`. +-- @function wibox.widget.base.place_widget_via_matrix +function base.place_widget_via_matrix(widget, mat, width, height) + return { + _widget = widget, + _width = width, + _height = height, + _matrix = mat + } +end + +--- Create widget placement information. This should be used for a widget's +-- `:layout()` callback. +-- @tparam widget widget The widget that should be placed. +-- @tparam number x The x coordinate for the widget. +-- @tparam number y The y coordinate for the widget. +-- @tparam number width The width of the widget in its own coordinate system. +-- That is, after applying the transformation matrix. +-- @tparam number height The height of the widget in its own coordinate system. +-- That is, after applying the transformation matrix. +-- @treturn table An opaque object that can be returned from `:layout()`. +-- @function wibox.widget.base.place_widget_at +function base.place_widget_at(widget, x, y, width, height) + return base.place_widget_via_matrix(widget, matrix.create_translate(x, y), width, height) +end + +-- Read the table, separate attributes from widgets. +local function parse_table(t, leave_empty) + local max = 0 + local attributes, widgets = {}, {} + for k,v in pairs(t) do + if type(k) == "number" then + if v then + -- Since `ipairs` doesn't always work on sparse tables, update + -- the maximum. + if k > max then + max = k + end + + widgets[k] = v + end + else + attributes[k] = v + end + end + + -- Pack the sparse table, if the container doesn't support sparse tables. + if not leave_empty then + widgets = util.table.from_sparse(widgets) + max = #widgets + end + + return max, attributes, widgets +end + +-- Recursively build a container from a declarative table. +local function drill(ids, content) + if not content then return end + + -- Alias `widget` to `layout` as they are handled the same way. + content.layout = content.layout or content.widget + + -- Make sure the layout is not indexed on a function. + local layout = type(content.layout) == "function" and content.layout() or content.layout + + -- Create layouts based on metatable's __call. + local l = layout.is_widget and layout or layout() + + -- Get the number of children widgets (including nil widgets). + local max, attributes, widgets = parse_table(content, l.allow_empty_widget) + + -- Get the optional identifier to create a virtual widget tree to place + -- in an "access table" to be able to retrieve the widget. + local id = attributes.id + + -- Clear the internal attributes. + attributes.id, attributes.layout, attributes.widget = nil, nil, nil + + -- Set layout attributes. + -- This has to be done before the widgets are added because it might affect + -- the output. + for attr, val in pairs(attributes) do + if l["set_"..attr] then + l["set_"..attr](l, val) + elseif type(l[attr]) == "function" then + l[attr](l, val) + else + l[attr] = val + end + end + + -- Add all widgets. + for k = 1, max do + -- ipairs cannot be used on sparse tables. + local v, id2, e = widgets[k], id, nil + if v then + -- It is another declarative container, parse it. + if not v.is_widget then + e, id2 = drill(ids, v) + widgets[k] = e + end + base.check_widget(widgets[k]) + + -- Place the widget in the access table. + if id2 then + l [id2] = e + ids[id2] = ids[id2] or {} + table.insert(ids[id2], e) + end + end + end + -- Replace all children (if any) with the new ones. + l:set_children(widgets) + return l, id +end + +-- Only available when the declarative system is used. +local function get_children_by_id(self, name) + if rawget(self, "_private") then + return self._private.by_id[name] or {} + else + return rawget(self, "_by_id")[name] or {} + end +end + +--- Set a declarative widget hierarchy description. +-- +-- See [The declarative layout system](../documentation/03-declarative-layout.md.html). +-- @tab args A table containing the widget's disposition. +-- @function setup +function base.widget:setup(args) + local f,ids = self.set_widget or self.add or self.set_first,{} + local w, id = drill(ids, args) + f(self,w) + if id then + -- Avoid being dropped by wibox metatable -> drawin + rawset(self, id, w) + ids[id] = ids[id] or {} + table.insert(ids[id], 1, w) + end + + if rawget(self, "_private") then + self._private.by_id = ids + else + rawset(self, "_by_id", ids) + end + + rawset(self, "get_children_by_id", get_children_by_id) +end + +--- Create a widget from a declarative description. +-- +-- See [The declarative layout system](../documentation/03-declarative-layout.md.html). +-- @tab args A table containing the widgets disposition. +-- @function wibox.widget.base.make_widget_declarative +function base.make_widget_declarative(args) + local ids = {} + + if (not args.layout) and (not args.widget) then + args.widget = base.make_widget(nil, args.id) + end + + local w, id = drill(ids, args) + + local mt = getmetatable(w) or {} + local orig_string = tostring(w) + + -- Add the main id (if any) + if id then + ids[id] = ids[id] or {} + table.insert(ids[id], 1, w) + end + + if rawget(w, "_private") then + w._private.by_id = ids + else + rawset(w, "_by_id", ids) + end + + rawset(w, "get_children_by_id", get_children_by_id) + + mt.__tostring = function() + return string.format("%s (%s)", id or w.widget_name or "N/A", orig_string) + end + + return setmetatable(w, mt) +end + +--- Create an empty widget skeleton. +-- +-- See [Creating new widgets](../documentation/04-new-widget.md.html). +-- @tparam[opt] widget proxy If this is set, the returned widget will be a +-- proxy for this widget. It will be equivalent to this widget. +-- This means it looks the same on the screen. +-- @tparam[opt] string widget_name Name of the widget. If not set, it will be +-- set automatically via @{gears.object.modulename}. +-- @tparam[opt={}] table args Widget settings +-- @tparam[opt=false] boolean args.enable_properties Enable automatic getter +-- and setter methods. +-- @tparam[opt=nil] table args.class The widget class +-- @see fit_widget +-- @function wibox.widget.base.make_widget +function base.make_widget(proxy, widget_name, args) + args = args or {} + local ret = object { + enable_properties = args.enable_properties, + class = args.class, + } + + -- Backwards compatibility. + -- TODO: Remove this + ret:connect_signal("widget::updated", function() + ret:emit_signal("widget::layout_changed") + ret:emit_signal("widget::redraw_needed") + end) + + -- Create a table used to store the widgets internal data. + rawset(ret, "_private", {}) + + -- No buttons yet. + ret._private.widget_buttons = {} + + -- Widget is visible. + ret._private.visible = true + + -- Widget is fully opaque. + ret._private.opacity = 1 + + -- Differentiate tables from widgets. + rawset(ret, "is_widget", true) + + -- Size is not restricted/forced. + ret._private.forced_width = nil + ret._private.forced_height = nil + + -- Make buttons work. + ret:connect_signal("button::press", function(...) + return base.handle_button("press", ...) + end) + ret:connect_signal("button::release", function(...) + return base.handle_button("release", ...) + end) + + if proxy then + rawset(ret, "fit", function(_, context, width, height) + return base.fit_widget(ret, context, proxy, width, height) + end) + rawset(ret, "layout", function(_, _, width, height) + return { base.place_widget_at(proxy, 0, 0, width, height) } + end) + proxy:connect_signal("widget::layout_changed", function() + ret:emit_signal("widget::layout_changed") + end) + proxy:connect_signal("widget::redraw_needed", function() + ret:emit_signal("widget::redraw_needed") + end) + end + + -- Set up caches. + clear_caches(ret) + ret:connect_signal("widget::layout_changed", function() + clear_caches(ret) + end) + + -- Add functions. + for k, v in pairs(base.widget) do + rawset(ret, k, v) + end + + -- Add __tostring method to metatable. + rawset(ret, "widget_name", widget_name or object.modulename(3)) + local mt = getmetatable(ret) or {} + local orig_string = tostring(ret) + mt.__tostring = function() + return string.format("%s (%s)", ret.widget_name, orig_string) + end + return setmetatable(ret, mt) +end + +--- Generate an empty widget which takes no space and displays nothing. +-- @function wibox.widget.base.empty_widget +function base.empty_widget() + return base.make_widget() +end + +--- Do some sanity checking on a widget. +-- +-- This function raises an error if the widget is not valid. +-- @function wibox.widget.base.check_widget +function base.check_widget(widget) + assert(type(widget) == "table", "Type should be table, but is " .. tostring(type(widget))) + assert(widget.is_widget, "Argument is not a widget!") + for _, func in pairs({ "connect_signal", "disconnect_signal" }) do + assert(type(widget[func]) == "function", func .. " is not a function") + end +end + +return base + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/wibox/widget/checkbox.lua b/lib/wibox/widget/checkbox.lua new file mode 100644 index 0000000..57bdaa3 --- /dev/null +++ b/lib/wibox/widget/checkbox.lua @@ -0,0 +1,530 @@ +--------------------------------------------------------------------------- +-- A boolean display widget. +-- +-- If necessary, themes can implement custom shape: +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_checkbox_custom.svg) +-- +-- +-- wibox.widget { +-- checked = true, +-- color = beautiful.bg_normal, +-- paddings = 2, +-- check_shape = function(cr, width, height) +-- local rs = math.min(width, height) +-- cr:move_to( 0 , 0 ) +-- cr:line_to( rs , 0 ) +-- cr:move_to( 0 , 0 ) +-- cr:line_to( 0 , rs ) +-- cr:move_to( 0 , rs ) +-- cr:line_to( rs , rs ) +-- cr:move_to( rs , 0 ) +-- cr:line_to( rs , rs ) +-- cr:move_to( 0 , 0 ) +-- cr:line_to( rs , rs ) +-- cr:move_to( 0 , rs ) +-- cr:line_to( rs , 0 ) +-- end, +-- check_border_color = '#ff0000', +-- check_color = '#00000000', +-- check_border_width = 1, +-- widget = wibox.widget.checkbox +-- } +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_defaults_checkbox.svg) +-- +-- @usage +--wibox.widget { +-- checked = true, +-- color = beautiful.bg_normal, +-- paddings = 2, +-- shape = gears.shape.circle, +-- widget = wibox.widget.checkbox +--} +-- @author Emmanuel Lepage Valle +-- @copyright 2010 Emmanuel Lepage Vallee +-- @classmod wibox.widget.checkbox +--------------------------------------------------------------------------- + +local color = require( "gears.color" ) +local base = require( "wibox.widget.base" ) +local beautiful = require( "beautiful" ) +local shape = require( "gears.shape" ) +local util = require( "awful.util" ) + +local checkbox = {} + +--- The outer (unchecked area) border width. +-- @beautiful beautiful.checkbox_border_width + +--- The outer (unchecked area) background color, pattern or gradient. +-- @beautiful beautiful.checkbox_bg + +--- The outer (unchecked area) border color. +-- @beautiful beautiful.checkbox_border_color + +--- The checked part border color. +-- @beautiful beautiful.checkbox_check_border_color + +--- The checked part border width. +-- @beautiful beautiful.checkbox_check_border_width + +--- The checked part filling color. +-- @beautiful beautiful.checkbox_check_color + +--- The outer (unchecked area) shape. +-- @beautiful beautiful.checkbox_shape +-- @see gears.shape + +--- The checked part shape. +-- If none is set, then the `shape` property will be used. +-- @beautiful beautiful.checkbox_check_shape +-- @see gears.shape + +--- The padding between the outline and the progressbar. +-- @beautiful beautiful.checkbox_paddings +-- @tparam[opt=0] table|number paddings A number or a table +-- @tparam[opt=0] number paddings.top +-- @tparam[opt=0] number paddings.bottom +-- @tparam[opt=0] number paddings.left +-- @tparam[opt=0] number paddings.right + +--- The checkbox color. +-- This will be used for the unchecked part border color and the checked part +-- filling color. Note that `check_color` and `border_color` have priority +-- over this property. +-- @beautiful beautiful.checkbox_color + +--- The outer (unchecked area) border width. +-- @property border_width + +--- The outer (unchecked area) background color, pattern or gradient. +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_checkbox_bg.svg) +-- +-- @usage +--wibox.widget { +-- checked = true, +-- color = beautiful.bg_normal, +-- bg = '#ff00ff', +-- border_width = 3, +-- paddings = 4, +-- border_color = '#0000ff', +-- check_color = '#ff0000', +-- check_border_color = '#ffff00', +-- check_border_width = 1, +-- widget = wibox.widget.checkbox +--} +-- @property bg + +--- The outer (unchecked area) border color. +-- @property border_color + +--- The checked part border color. +-- @property check_border_color + +--- The checked part border width. +-- @property check_border_width + +--- The checked part filling color. +-- @property check_color + +--- The outer (unchecked area) shape. +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_checkbox_shape.svg) +-- +-- @usage +--for _, s in ipairs {'rectangle', 'circle', 'losange', 'octogon'} do +-- wibox.widget { +-- checked = true, +-- color = beautiful.bg_normal, +-- paddings = 2, +-- shape = gears.shape[s], +-- widget = wibox.widget.checkbox +-- } +--end +-- @property shape +-- @see gears.shape + +--- The checked part shape. +-- If none is set, then the `shape` property will be used. +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_checkbox_check_shape.svg) +-- +-- @usage +--for _, s in ipairs {'rectangle', 'circle', 'losange', 'octogon'} do +-- wibox.widget { +-- checked = true, +-- color = beautiful.bg_normal, +-- paddings = 2, +-- check_shape = gears.shape[s], +-- widget = wibox.widget.checkbox +-- } +--end +-- @property check_shape +-- @see gears.shape + +--- The padding between the outline and the progressbar. +-- @property paddings +-- @tparam[opt=0] table|number paddings A number or a table +-- @tparam[opt=0] number paddings.top +-- @tparam[opt=0] number paddings.bottom +-- @tparam[opt=0] number paddings.left +-- @tparam[opt=0] number paddings.right + +--- The checkbox color. +-- This will be used for the unchecked part border color and the checked part +-- filling color. Note that `check_color` and `border_color` have priority +-- over this property. +-- @property color + +local function outline_workarea(self, width, height) + local offset = (self._private.border_width or + beautiful.checkbox_border_width or 1)/2 + + return { + x = offset, + y = offset, + width = width-2*offset, + height = height-2*offset + } +end + +-- The child widget area +local function content_workarea(self, width, height) + local padding = self._private.paddings or {} + local offset = self:get_check_border_width() or 0 + local wa = outline_workarea(self, width, height) + + wa.x = offset + wa.x + (padding.left or 1) + wa.y = offset + wa.y + (padding.top or 1) + wa.width = wa.width - (padding.left or 1) - (padding.right or 1) - 2*offset + wa.height = wa.height - (padding.top or 1) - (padding.bottom or 1) - 2*offset + + return wa +end + +local function draw(self, _, cr, width, height) + local size = math.min(width, height) + + local background_shape = self:get_shape() or shape.rectangle + local border_width = self:get_border_width() or 1 + + local main_color = self:get_color() + local bg = self:get_bg() + local border_color = self:get_border_color() + + -- If no color is set, it will fallback to the default one + if border_color or main_color then + cr:set_source(color(border_color or main_color)) + end + + local wa = outline_workarea(self, size, size) + cr:translate(wa.x, wa.y) + background_shape(cr, wa.width, wa.height) + cr:set_line_width(border_width) + + if bg then + cr:save() + cr:set_source(color(bg)) + cr:fill_preserve() + cr:restore() + end + + cr:stroke() + + cr:translate(-wa.x, -wa.y) + + -- Draw the checked part + if self._private.checked then + local col = self:get_check_color() or main_color + border_color = self:get_check_border_color() + border_width = self:get_check_border_width() or 0 + local check_shape = self:get_check_shape() or background_shape + + wa = content_workarea(self, size, size) + cr:translate(wa.x, wa.y) + + check_shape(cr, wa.width, wa.height) + + if col then + cr:set_source(color(col)) + end + + if border_width > 0 then + cr:fill_preserve() + cr:set_line_width(border_width) + cr:set_source(color(border_color)) + cr:stroke() + else + cr:fill() + end + end +end + +local function fit(_, _, w, h) + local size = math.min(w, h) + return size, size +end + +--- If the checkbox is checked. +-- @property checked +-- @param boolean + +for _, prop in ipairs {"border_width", "bg", "border_color", "check_border_color", + "check_border_width", "check_color", "shape", "check_shape", "paddings", + "checked", "color" } do + checkbox["set_"..prop] = function(self, value) + self._private[prop] = value + self:emit_signal("property::"..prop) + self:emit_signal("widget::redraw_needed") + end + checkbox["get_"..prop] = function(self) + return self._private[prop] or beautiful["checkbox_"..prop] + end +end + +--- The checkbox color. +-- @property color + +function checkbox:set_paddings(val) + self._private.paddings = type(val) == "number" and { + left = val, + right = val, + top = val, + bottom = val, + } or val or {} + self:emit_signal("property::paddings") + self:emit_signal("widget::redraw_needed") +end + +local function new(checked, args) + checked, args = checked or false, args or {} + + local ret = base.make_widget(nil, nil, { + enable_properties = true, + }) + + util.table.crush(ret, checkbox) + + ret._private.checked = checked + ret._private.color = args.color and color(args.color) or nil + + rawset(ret, "fit" , fit ) + rawset(ret, "draw", draw) + + return ret +end + +--Imported documentation + + +--- Get a widex index. +-- @param widget The widget to look for +-- @param[opt] recursive Also check sub-widgets +-- @param[opt] ... Aditional widgets to add at the end of the \"path\" +-- @return The index +-- @return The parent layout +-- @return The path between \"self\" and \"widget\" +-- @function index + +--- Get all direct and indirect children widgets. +-- This will scan all containers recursively to find widgets +-- Warning: This method it prone to stack overflow id the widget, or any of its +-- children, contain (directly or indirectly) itself. +-- @treturn table The children +-- @function get_all_children + +--- Set a declarative widget hierarchy description. +-- See [The declarative layout system](../documentation/03-declarative-layout.md.html) +-- @param args An array containing the widgets disposition +-- @function setup + +--- Force a widget height. +-- @property forced_height +-- @tparam number|nil height The height (`nil` for automatic) + +--- Force a widget width. +-- @property forced_width +-- @tparam number|nil width The width (`nil` for automatic) + +--- The widget opacity (transparency). +-- @property opacity +-- @tparam[opt=1] number opacity The opacity (between 0 and 1) + +--- The widget visibility. +-- @property visible +-- @param boolean + +--- Set/get a widget's buttons. +-- @param _buttons The table of buttons that should bind to the widget. +-- @function buttons + +--- Emit a signal and ensure all parent widgets in the hierarchies also +-- forward the signal. This is useful to track signals when there is a dynamic +-- set of containers and layouts wrapping the widget. +-- @tparam string signal_name +-- @param ... Other arguments +-- @function emit_signal_recursive + +--- When the layout (size) change. +-- This signal is emitted when the previous results of `:layout()` and `:fit()` +-- are no longer valid. Unless this signal is emitted, `:layout()` and `:fit()` +-- must return the same result when called with the same arguments. +-- @signal widget::layout_changed +-- @see widget::redraw_needed + +--- When the widget content changed. +-- This signal is emitted when the content of the widget changes. The widget will +-- be redrawn, it is not re-layouted. Put differently, it is assumed that +-- `:layout()` and `:fit()` would still return the same results as before. +-- @signal widget::redraw_needed +-- @see widget::layout_changed + +--- When a mouse button is pressed over the widget. +-- @signal button::press +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When a mouse button is released over the widget. +-- @signal button::release +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse enter a widget. +-- @signal mouse::enter +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse leave a widget. +-- @signal mouse::leave +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + + +--Imported documentation + + +--- Disconnect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback that should be disconnected +-- @function disconnect_signal + +--- Emit a signal. +-- +-- @tparam string name The name of the signal +-- @param ... Extra arguments for the callback functions. Each connected +-- function receives the object as first argument and then any extra arguments +-- that are given to emit_signal() +-- @function emit_signal + +--- Connect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function connect_signal + +--- Connect to a signal weakly. This allows the callback function to be garbage +-- collected and automatically disconnects the signal when that happens. +-- +-- **Warning:** +-- Only use this function if you really, really, really know what you +-- are doing. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function weak_connect_signal + + +return setmetatable({}, { __call = function(_, ...) return new(...) end}) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/wibox/widget/graph.lua b/lib/wibox/widget/graph.lua new file mode 100644 index 0000000..c03c726 --- /dev/null +++ b/lib/wibox/widget/graph.lua @@ -0,0 +1,575 @@ +--------------------------------------------------------------------------- +--- A graph widget. +-- +-- The graph goes from left to right. To change this to right to left, use +-- a `wibox.container.mirror` widget. This can also be used to have data +-- shown from top to bottom. +-- +-- To add text on top of the graph, use a `wibox.layout.stack` and a +-- `wibox.container.align` widgets. +-- +-- To display the graph vertically, use a `wibox.container.rotate` widget. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_defaults_graph.svg) +-- +-- @usage +--wibox.widget { +-- max_value = 29, +-- widget = wibox.widget.graph +--} +-- @author Julien Danjou <julien@danjou.info> +-- @copyright 2009 Julien Danjou +-- @classmod wibox.widget.graph +--------------------------------------------------------------------------- + +local setmetatable = setmetatable +local ipairs = ipairs +local math = math +local table = table +local type = type +local color = require("gears.color") +local base = require("wibox.widget.base") +local beautiful = require("beautiful") + +local graph = { mt = {} } + +--- Set the graph border color. +-- If the value is nil, no border will be drawn. +-- +-- @property border_color +-- @tparam gears.color border_color The border color to set. +-- @see gears.color + +--- Set the graph foreground color. +-- +-- @property color +-- @tparam color color The graph color. +-- @see gears.color + +--- Set the graph background color. +-- +-- @property background_color +-- @tparam gears.color background_color The graph background color. +-- @see gears.color + +--- Set the maximum value the graph should handle. +-- If "scale" is also set, the graph never scales up below this value, but it +-- automatically scales down to make all data fit. +-- +-- @property max_value +-- @param number + +--- The minimum value. +-- Note that the min_value is not supported when used along with the stack +-- property. +-- @property min_value +-- @param number + +--- Set the graph to automatically scale its values. Default is false. +-- +-- @property scale +-- @param boolean + +--- Set the width or the individual steps. +-- +-- Note that it isn't supported when used along with stacked graphs. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_graph_step.svg) +-- +-- @usage +--wibox.widget { +-- max_value = 29, +-- step_width = 3, +-- step_spacing = 1, +-- step_shape = function(cr, width, height) +-- gears.shape.rounded_rect(cr, width, height, 2) +-- end, +-- widget = wibox.widget.graph +--} +-- +-- @property step_width +-- @param[opt=1] number + +--- Set the spacing between the steps. +-- +-- Note that it isn't supported when used along with stacked graphs. +-- +-- @property step_spacing +-- @param[opt=0] number + +--- The step shape. +-- @property step_shape +-- @param[opt=rectangle] shape +-- @see gears.shape + +--- Set the graph to draw stacks. Default is false. +-- +-- @property stack +-- @param boolean + +--- Set the graph stacking colors. Order matters. +-- +-- @property stack_colors +-- @param stack_colors A table with stacking colors. + +--- The graph background color. +-- @beautiful beautiful.graph_bg + +--- The graph foreground color. +-- @beautiful beautiful.graph_fg + +--- The graph border color. +-- @beautiful beautiful.graph_border_color + +local properties = { "width", "height", "border_color", "stack", + "stack_colors", "color", "background_color", + "max_value", "scale", "min_value", "step_shape", + "step_spacing", "step_width" } + +function graph.draw(_graph, _, cr, width, height) + local max_value = _graph._private.max_value + local min_value = _graph._private.min_value or ( + _graph._private.scale and math.huge or 0) + local values = _graph._private.values + + local step_shape = _graph._private.step_shape + local step_spacing = _graph._private.step_spacing or 0 + local step_width = _graph._private.step_width or 1 + + cr:set_line_width(1) + + -- Draw the background first + cr:set_source(color(_graph._private.background_color or beautiful.graph_bg or "#000000aa")) + cr:paint() + + -- Account for the border width + cr:save() + if _graph._private.border_color then + cr:translate(1, 1) + width, height = width - 2, height - 2 + end + + -- Draw a stacked graph + if _graph._private.stack then + + if _graph._private.scale then + for _, v in ipairs(values) do + for _, sv in ipairs(v) do + if sv > max_value then + max_value = sv + end + if min_value > sv then + min_value = sv + end + end + end + end + + for i = 0, width do + local rel_i = 0 + local rel_x = i + 0.5 + + if _graph._private.stack_colors then + for idx, col in ipairs(_graph._private.stack_colors) do + local stack_values = values[idx] + if stack_values and i < #stack_values then + local value = stack_values[#stack_values - i] + rel_i + cr:move_to(rel_x, height * (1 - (rel_i / max_value))) + cr:line_to(rel_x, height * (1 - (value / max_value))) + cr:set_source(color(col or beautiful.graph_fg or "#ff0000")) + cr:stroke() + rel_i = value + end + end + end + end + else + if _graph._private.scale then + for _, v in ipairs(values) do + if v > max_value then + max_value = v + end + if min_value > v then + min_value = v + end + end + end + + -- Draw the background on no value + if #values ~= 0 then + -- Draw reverse + for i = 0, #values - 1 do + local value = values[#values - i] + if value >= 0 then + local x = i*step_width + ((i-1)*step_spacing) + 0.5 + value = (value - min_value) / max_value + cr:move_to(x, height * (1 - value)) + + if step_shape then + cr:translate(step_width + (i>1 and step_spacing or 0), height * (1 - value)) + step_shape(cr, step_width, height) + cr:translate(0, -(height * (1 - value))) + elseif step_width > 1 then + cr:rectangle(x, height * (1 - value), step_width, height) + else + cr:line_to(x, height) + end + end + end + cr:set_source(color(_graph._private.color or beautiful.graph_fg or "#ff0000")) + + if step_shape or step_width > 1 then + cr:fill() + else + cr:stroke() + end + end + + end + + -- Undo the cr:translate() for the border and step shapes + cr:restore() + + -- Draw the border last so that it overlaps already drawn values + if _graph._private.border_color then + -- We decremented these by two above + width, height = width + 2, height + 2 + + -- Draw the border + cr:rectangle(0.5, 0.5, width - 1, height - 1) + cr:set_source(color(_graph._private.border_color or beautiful.graph_border_color or "#ffffff")) + cr:stroke() + end +end + +function graph.fit(_graph) + return _graph._private.width, _graph._private.height +end + +--- Add a value to the graph +-- +-- @param value The value to be added to the graph +-- @param group The stack color group index. +function graph:add_value(value, group) + value = value or 0 + local values = self._private.values + local max_value = self._private.max_value + value = math.max(0, value) + if not self._private.scale then + value = math.min(max_value, value) + end + + if self._private.stack and group then + if not self._private.values[group] + or type(self._private.values[group]) ~= "table" + then + self._private.values[group] = {} + end + values = self._private.values[group] + end + table.insert(values, value) + + local border_width = 0 + if self._private.border_color then border_width = 2 end + + -- Ensure we never have more data than we can draw + while #values > self._private.width - border_width do + table.remove(values, 1) + end + + self:emit_signal("widget::redraw_needed") + return self +end + +--- Clear the graph. +function graph:clear() + self._private.values = {} + self:emit_signal("widget::redraw_needed") + return self +end + +--- Set the graph height. +-- @param height The height to set. +function graph:set_height(height) + if height >= 5 then + self._private.height = height + self:emit_signal("widget::layout_changed") + end + return self +end + +--- Set the graph width. +-- @param width The width to set. +function graph:set_width(width) + if width >= 5 then + self._private.width = width + self:emit_signal("widget::layout_changed") + end + return self +end + +-- Build properties function +for _, prop in ipairs(properties) do + if not graph["set_" .. prop] then + graph["set_" .. prop] = function(_graph, value) + if _graph._private[prop] ~= value then + _graph._private[prop] = value + _graph:emit_signal("widget::redraw_needed") + end + return _graph + end + end + if not graph["get_" .. prop] then + graph["get_" .. prop] = function(_graph) + return _graph._private[prop] + end + end +end + +--- Create a graph widget. +-- @param args Standard widget() arguments. You should add width and height +-- key to set graph geometry. +-- @return A new graph widget. +-- @function wibox.widget.graph +function graph.new(args) + args = args or {} + + local width = args.width or 100 + local height = args.height or 20 + + if width < 5 or height < 5 then return end + + local _graph = base.make_widget(nil, nil, {enable_properties = true}) + + _graph._private.width = width + _graph._private.height = height + _graph._private.values = {} + _graph._private.max_value = 1 + + -- Set methods + _graph.add_value = graph["add_value"] + _graph.clear = graph["clear"] + _graph.draw = graph.draw + _graph.fit = graph.fit + + for _, prop in ipairs(properties) do + _graph["set_" .. prop] = graph["set_" .. prop] + _graph["get_" .. prop] = graph["get_" .. prop] + end + + return _graph +end + +function graph.mt:__call(...) + return graph.new(...) +end + +--Imported documentation + + +--- Get a widex index. +-- @param widget The widget to look for +-- @param[opt] recursive Also check sub-widgets +-- @param[opt] ... Aditional widgets to add at the end of the \"path\" +-- @return The index +-- @return The parent layout +-- @return The path between \"self\" and \"widget\" +-- @function index + +--- Get all direct and indirect children widgets. +-- This will scan all containers recursively to find widgets +-- Warning: This method it prone to stack overflow id the widget, or any of its +-- children, contain (directly or indirectly) itself. +-- @treturn table The children +-- @function get_all_children + +--- Set a declarative widget hierarchy description. +-- See [The declarative layout system](../documentation/03-declarative-layout.md.html) +-- @param args An array containing the widgets disposition +-- @function setup + +--- Force a widget height. +-- @property forced_height +-- @tparam number|nil height The height (`nil` for automatic) + +--- Force a widget width. +-- @property forced_width +-- @tparam number|nil width The width (`nil` for automatic) + +--- The widget opacity (transparency). +-- @property opacity +-- @tparam[opt=1] number opacity The opacity (between 0 and 1) + +--- The widget visibility. +-- @property visible +-- @param boolean + +--- Set/get a widget's buttons. +-- @param _buttons The table of buttons that should bind to the widget. +-- @function buttons + +--- Emit a signal and ensure all parent widgets in the hierarchies also +-- forward the signal. This is useful to track signals when there is a dynamic +-- set of containers and layouts wrapping the widget. +-- @tparam string signal_name +-- @param ... Other arguments +-- @function emit_signal_recursive + +--- When the layout (size) change. +-- This signal is emitted when the previous results of `:layout()` and `:fit()` +-- are no longer valid. Unless this signal is emitted, `:layout()` and `:fit()` +-- must return the same result when called with the same arguments. +-- @signal widget::layout_changed +-- @see widget::redraw_needed + +--- When the widget content changed. +-- This signal is emitted when the content of the widget changes. The widget will +-- be redrawn, it is not re-layouted. Put differently, it is assumed that +-- `:layout()` and `:fit()` would still return the same results as before. +-- @signal widget::redraw_needed +-- @see widget::layout_changed + +--- When a mouse button is pressed over the widget. +-- @signal button::press +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When a mouse button is released over the widget. +-- @signal button::release +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse enter a widget. +-- @signal mouse::enter +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse leave a widget. +-- @signal mouse::leave +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + + +--Imported documentation + + +--- Disconnect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback that should be disconnected +-- @function disconnect_signal + +--- Emit a signal. +-- +-- @tparam string name The name of the signal +-- @param ... Extra arguments for the callback functions. Each connected +-- function receives the object as first argument and then any extra arguments +-- that are given to emit_signal() +-- @function emit_signal + +--- Connect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function connect_signal + +--- Connect to a signal weakly. This allows the callback function to be garbage +-- collected and automatically disconnects the signal when that happens. +-- +-- **Warning:** +-- Only use this function if you really, really, really know what you +-- are doing. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function weak_connect_signal + + +return setmetatable(graph, graph.mt) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/wibox/widget/imagebox.lua b/lib/wibox/widget/imagebox.lua new file mode 100644 index 0000000..bdfa499 --- /dev/null +++ b/lib/wibox/widget/imagebox.lua @@ -0,0 +1,395 @@ +--------------------------------------------------------------------------- +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_defaults_imagebox.svg) +-- +-- @usage +--wibox.widget { +-- image = beautiful.awesome_icon, +-- resize = false, +-- widget = wibox.widget.imagebox +--} +-- @author Uli Schlachter +-- @copyright 2010 Uli Schlachter +-- @classmod wibox.widget.imagebox +--------------------------------------------------------------------------- + +local base = require("wibox.widget.base") +local surface = require("gears.surface") +local util = require("awful.util") +local setmetatable = setmetatable +local type = type +local print = print +local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1) + +local imagebox = { mt = {} } + +-- Draw an imagebox with the given cairo context in the given geometry. +function imagebox:draw(_, cr, width, height) + if not self._private.image then return end + if width == 0 or height == 0 then return end + + if not self._private.resize_forbidden then + -- Let's scale the image so that it fits into (width, height) + local w = self._private.image:get_width() + local h = self._private.image:get_height() + local aspect = width / w + local aspect_h = height / h + if aspect > aspect_h then aspect = aspect_h end + + cr:scale(aspect, aspect) + end + + -- Set the clip + if self._private.clip_shape then + cr:clip(self._private.clip_shape(cr, width, height, unpack(self._private.clip_args))) + end + + cr:set_source_surface(self._private.image, 0, 0) + cr:paint() +end + +-- Fit the imagebox into the given geometry +function imagebox:fit(_, width, height) + if not self._private.image then + return 0, 0 + end + + local w = self._private.image:get_width() + local h = self._private.image:get_height() + + if w > width then + h = h * width / w + w = width + end + if h > height then + w = w * height / h + h = height + end + + if h == 0 or w == 0 then + return 0, 0 + end + + if not self._private.resize_forbidden then + local aspect = width / w + local aspect_h = height / h + + -- Use the smaller one of the two aspect ratios. + if aspect > aspect_h then aspect = aspect_h end + + w, h = w * aspect, h * aspect + end + + return w, h +end + +--- Set an imagebox' image +-- @property image +-- @param image Either a string or a cairo image surface. A string is +-- interpreted as the path to a png image file. +-- @return true on success, false if the image cannot be used + +function imagebox:set_image(image) + if type(image) == "string" then + image = surface.load(image) + if not image then + print(debug.traceback()) + return false + end + end + + image = surface.load(image) + + if image then + local w = image.width + local h = image.height + if w <= 0 or h <= 0 then + return false + end + end + + if self._private.image == image then + -- The image could have been modified, so better redraw + self:emit_signal("widget::redraw_needed") + return + end + + self._private.image = image + + self:emit_signal("widget::redraw_needed") + self:emit_signal("widget::layout_changed") + return true +end + +--- Set a clip shape for this imagebox +-- A clip shape define an area where the content is displayed and one where it +-- is trimmed. +-- +-- @property clip_shape +-- @param clip_shape A `gears_shape` compatible shape function +-- @see gears.shape +-- @see set_clip_shape + +--- Set a clip shape for this imagebox +-- A clip shape define an area where the content is displayed and one where it +-- is trimmed. +-- +-- Any other parameters will be passed to the clip shape function +-- +-- @param clip_shape A `gears_shape` compatible shape function +-- @see gears.shape +-- @see clip_shape +function imagebox:set_clip_shape(clip_shape, ...) + self._private.clip_shape = clip_shape + self._private.clip_args = {...} + self:emit_signal("widget::redraw_needed") +end + +--- Should the image be resized to fit into the available space? +-- @property resize +-- @param allowed If false, the image will be clipped, else it will be resized +-- to fit into the available space. + +function imagebox:set_resize(allowed) + self._private.resize_forbidden = not allowed + self:emit_signal("widget::redraw_needed") + self:emit_signal("widget::layout_changed") +end + +--- Returns a new imagebox. +-- Any other arguments will be passed to the clip shape function +-- @param image the image to display, may be nil +-- @param resize_allowed If false, the image will be clipped, else it will be resized +-- to fit into the available space. +-- @param clip_shape A `gears.shape` compatible function +-- @treturn table A new `imagebox` +-- @function wibox.widget.imagebox +local function new(image, resize_allowed, clip_shape) + local ret = base.make_widget(nil, nil, {enable_properties = true}) + + util.table.crush(ret, imagebox, true) + + if image then + ret:set_image(image) + end + if resize_allowed ~= nil then + ret:set_resize(resize_allowed) + end + + ret._private.clip_shape = clip_shape + ret._private.clip_args = {} + + return ret +end + +function imagebox.mt:__call(...) + return new(...) +end + +--Imported documentation + + +--- Get a widex index. +-- @param widget The widget to look for +-- @param[opt] recursive Also check sub-widgets +-- @param[opt] ... Aditional widgets to add at the end of the \"path\" +-- @return The index +-- @return The parent layout +-- @return The path between \"self\" and \"widget\" +-- @function index + +--- Get all direct and indirect children widgets. +-- This will scan all containers recursively to find widgets +-- Warning: This method it prone to stack overflow id the widget, or any of its +-- children, contain (directly or indirectly) itself. +-- @treturn table The children +-- @function get_all_children + +--- Set a declarative widget hierarchy description. +-- See [The declarative layout system](../documentation/03-declarative-layout.md.html) +-- @param args An array containing the widgets disposition +-- @function setup + +--- Force a widget height. +-- @property forced_height +-- @tparam number|nil height The height (`nil` for automatic) + +--- Force a widget width. +-- @property forced_width +-- @tparam number|nil width The width (`nil` for automatic) + +--- The widget opacity (transparency). +-- @property opacity +-- @tparam[opt=1] number opacity The opacity (between 0 and 1) + +--- The widget visibility. +-- @property visible +-- @param boolean + +--- Set/get a widget's buttons. +-- @param _buttons The table of buttons that should bind to the widget. +-- @function buttons + +--- Emit a signal and ensure all parent widgets in the hierarchies also +-- forward the signal. This is useful to track signals when there is a dynamic +-- set of containers and layouts wrapping the widget. +-- @tparam string signal_name +-- @param ... Other arguments +-- @function emit_signal_recursive + +--- When the layout (size) change. +-- This signal is emitted when the previous results of `:layout()` and `:fit()` +-- are no longer valid. Unless this signal is emitted, `:layout()` and `:fit()` +-- must return the same result when called with the same arguments. +-- @signal widget::layout_changed +-- @see widget::redraw_needed + +--- When the widget content changed. +-- This signal is emitted when the content of the widget changes. The widget will +-- be redrawn, it is not re-layouted. Put differently, it is assumed that +-- `:layout()` and `:fit()` would still return the same results as before. +-- @signal widget::redraw_needed +-- @see widget::layout_changed + +--- When a mouse button is pressed over the widget. +-- @signal button::press +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When a mouse button is released over the widget. +-- @signal button::release +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse enter a widget. +-- @signal mouse::enter +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse leave a widget. +-- @signal mouse::leave +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + + +--Imported documentation + + +--- Disconnect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback that should be disconnected +-- @function disconnect_signal + +--- Emit a signal. +-- +-- @tparam string name The name of the signal +-- @param ... Extra arguments for the callback functions. Each connected +-- function receives the object as first argument and then any extra arguments +-- that are given to emit_signal() +-- @function emit_signal + +--- Connect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function connect_signal + +--- Connect to a signal weakly. This allows the callback function to be garbage +-- collected and automatically disconnects the signal when that happens. +-- +-- **Warning:** +-- Only use this function if you really, really, really know what you +-- are doing. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function weak_connect_signal + + +return setmetatable(imagebox, imagebox.mt) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/wibox/widget/init.lua b/lib/wibox/widget/init.lua new file mode 100644 index 0000000..43c6fc0 --- /dev/null +++ b/lib/wibox/widget/init.lua @@ -0,0 +1,22 @@ +--------------------------------------------------------------------------- +-- @author Uli Schlachter +-- @copyright 2010 Uli Schlachter +-- @classmod wibox.widget +--------------------------------------------------------------------------- +local base = require("wibox.widget.base") + +return setmetatable({ + base = base; + textbox = require("wibox.widget.textbox"); + imagebox = require("wibox.widget.imagebox"); + background = require("wibox.widget.background"); + systray = require("wibox.widget.systray"); + textclock = require("wibox.widget.textclock"); + progressbar = require("wibox.widget.progressbar"); + graph = require("wibox.widget.graph"); + checkbox = require("wibox.widget.checkbox"); + piechart = require("wibox.widget.piechart"); + slider = require("wibox.widget.slider"); +}, {__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/widget/piechart.lua b/lib/wibox/widget/piechart.lua new file mode 100644 index 0000000..5a1c185 --- /dev/null +++ b/lib/wibox/widget/piechart.lua @@ -0,0 +1,467 @@ +--------------------------------------------------------------------------- +-- Display percentage in a circle. +-- +-- Note that this widget makes no attempts to prevent overlapping labels or +-- labels drawn outside of the widget boundaries. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_defaults_piechart.svg) +-- +-- @usage +--wibox.widget { +-- data_list = { +-- { 'L1', 100 }, +-- { 'L2', 200 }, +-- { 'L3', 300 }, +-- }, +-- border_width = 1, +-- colors = { +-- beautiful.bg_normal, +-- beautiful.bg_highlight, +-- beautiful.border_color, +-- }, +-- widget = wibox.widget.piechart +--} +-- @author Emmanuel Lepage Valle +-- @copyright 2012 Emmanuel Lepage Vallee +-- @classmod wibox.widget.piechart +--------------------------------------------------------------------------- + +local color = require( "gears.color" ) +local base = require( "wibox.widget.base" ) +local beautiful = require( "beautiful" ) +local util = require( "awful.util" ) +local pie = require( "gears.shape" ).pie +local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1) + +local module = {} + +local piechart = {} + +local function draw_label(cr,angle,radius,center_x,center_y,text) + local edge_x = center_x+(radius/2)*math.cos(angle) + local edge_y = center_y+(radius/2)*math.sin(angle) + + cr:move_to(edge_x, edge_y) + + cr:rel_line_to(radius*math.cos(angle), radius*math.sin(angle)) + + local x,y = cr:get_current_point() + + cr:rel_line_to(x > center_x and radius/2 or -radius/2, 0) + + local ext = cr:text_extents(text) + + cr:rel_move_to( + (x>center_x and radius/2.5 or (-radius/2.5 - ext.width)), + ext.height/2 + ) + + cr:show_text(text) --TODO eventually port away from the toy API + cr:stroke() + + cr:arc(edge_x, edge_y,2,0,2*math.pi) + cr:arc(x+(x>center_x and radius/2 or -radius/2),y,2,0,2*math.pi) + + cr:fill() +end + +local function compute_sum(data) + local ret = 0 + for _, entry in ipairs(data) do + ret = ret + entry[2] + end + + return ret +end + +local function draw(self, _, cr, width, height) + if not self._private.data_list then return end + + local radius = (height > width and width or height) / 4 + local sum, start, count = compute_sum(self._private.data_list),0,0 + local has_label = self._private.display_labels ~= false + + -- Labels need to be drawn later so the original source is kept + -- use get_source() wont work are the reference cannot be set from Lua(?) + local labels = {} + + local border_width = self:get_border_width() or 1 + local border_color = self:get_border_color() + border_color = border_color and color(border_color) + + -- Draw the pies + cr:save() + cr:set_line_width(border_width) + + -- Alternate from a given sets or colors + local colors = self:get_colors() + local col_count = colors and #colors or 0 + + for _, entry in ipairs(self._private.data_list) do + local k, v = entry[1], entry[2] + local end_angle = start + 2*math.pi*(v/sum) + + local col = colors and color(colors[math.fmod(count,col_count)+1]) or nil + + pie(cr, width, height, start, end_angle, radius) + + if col then + cr:save() + cr:set_source(color(col)) + end + + if border_width > 0 then + if col then + cr:fill_preserve() + cr:restore() + end + + -- By default, it uses the fg color + if border_color then + cr:set_source(border_color) + end + cr:stroke() + elseif col then + cr:fill() + cr:restore() + end + + -- Store the label position for later + if has_label then + table.insert(labels, { + --[[angle ]] start+(end_angle-start)/2, + --[[radius ]] radius, + --[[center_x]] width/2, + --[[center_y]] height/2, + --[[text ]] k, + }) + end + start,count = end_angle,count+1 + end + cr:restore() + + -- Draw the labels + if has_label then + for _, v in ipairs(labels) do + draw_label(cr, unpack(v)) + end + end +end + +local function fit(_, _, width, height) + return width, height +end + +--- The pie chart data list. +-- @property data_list +-- @tparam table data_list Sorted table where each entry has a label as its +-- first value and a number as its second value. + +--- The pie chart data. +-- @property data +-- @tparam table data Labels as keys and number as value. + +--- The border color. +-- If none is set, it will use current foreground (text) color. +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_piechart_border_color.svg) +-- +-- @property border_color +-- @param color +-- @see gears.color + +--- The pie elements border width. +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_piechart_border_width.svg) +-- +-- @property border_width +-- @tparam[opt=1] number border_width + +--- The pie chart colors. +-- If no color is set, only the border will be drawn. If less colors than +-- required are set, colors will be re-used in order. +-- @property colors +-- @tparam table colors A table of colors, one for each elements +-- @see gears.color + +--- The border color. +-- If none is set, it will use current foreground (text) color. +-- @beautiful beautiful.piechart_border_color +-- @param color +-- @see gears.color + +--- If the pie chart has labels. +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_piechart_label.svg) +-- +-- @property display_labels +-- @param[opt=true] boolean + +--- The pie elements border width. +-- @beautiful beautiful.piechart_border_width +-- @tparam[opt=1] number border_width + +--- The pie chart colors. +-- If no color is set, only the border will be drawn. If less colors than +-- required are set, colors will be re-used in order. +-- @beautiful beautiful.piechart_colors +-- @tparam table colors A table of colors, one for each elements +-- @see gears.color + +for _, prop in ipairs {"data_list", "border_color", "border_width", "colors", + "display_labels" + } do + piechart["set_"..prop] = function(self, value) + self._private[prop] = value + self:emit_signal("property::"..prop) + if prop == "data_list" then + self:emit_signal("property::data") + end + self:emit_signal("widget::redraw_needed") + end + piechart["get_"..prop] = function(self) + return self._private[prop] or beautiful["piechart_"..prop] + end +end + +function piechart:set_data(value) + local list = {} + for k, v in pairs(value) do + table.insert(list, { k, v }) + end + self:set_data_list(list) +end + +function piechart:get_data() + local list = {} + for _, entry in ipairs(self:get_data_list()) do + list[entry[1]] = entry[2] + end + return list +end + +local function new(data_list) + + local ret = base.make_widget(nil, nil, { + enable_properties = true, + }) + + util.table.crush(ret, piechart) + + rawset(ret, "fit" , fit ) + rawset(ret, "draw", draw) + + ret:set_data_list(data_list) + + return ret +end + +--Imported documentation + + +--- Get a widex index. +-- @param widget The widget to look for +-- @param[opt] recursive Also check sub-widgets +-- @param[opt] ... Aditional widgets to add at the end of the \"path\" +-- @return The index +-- @return The parent layout +-- @return The path between \"self\" and \"widget\" +-- @function index + +--- Get all direct and indirect children widgets. +-- This will scan all containers recursively to find widgets +-- Warning: This method it prone to stack overflow id the widget, or any of its +-- children, contain (directly or indirectly) itself. +-- @treturn table The children +-- @function get_all_children + +--- Set a declarative widget hierarchy description. +-- See [The declarative layout system](../documentation/03-declarative-layout.md.html) +-- @param args An array containing the widgets disposition +-- @function setup + +--- Force a widget height. +-- @property forced_height +-- @tparam number|nil height The height (`nil` for automatic) + +--- Force a widget width. +-- @property forced_width +-- @tparam number|nil width The width (`nil` for automatic) + +--- The widget opacity (transparency). +-- @property opacity +-- @tparam[opt=1] number opacity The opacity (between 0 and 1) + +--- The widget visibility. +-- @property visible +-- @param boolean + +--- Set/get a widget's buttons. +-- @param _buttons The table of buttons that should bind to the widget. +-- @function buttons + +--- Emit a signal and ensure all parent widgets in the hierarchies also +-- forward the signal. This is useful to track signals when there is a dynamic +-- set of containers and layouts wrapping the widget. +-- @tparam string signal_name +-- @param ... Other arguments +-- @function emit_signal_recursive + +--- When the layout (size) change. +-- This signal is emitted when the previous results of `:layout()` and `:fit()` +-- are no longer valid. Unless this signal is emitted, `:layout()` and `:fit()` +-- must return the same result when called with the same arguments. +-- @signal widget::layout_changed +-- @see widget::redraw_needed + +--- When the widget content changed. +-- This signal is emitted when the content of the widget changes. The widget will +-- be redrawn, it is not re-layouted. Put differently, it is assumed that +-- `:layout()` and `:fit()` would still return the same results as before. +-- @signal widget::redraw_needed +-- @see widget::layout_changed + +--- When a mouse button is pressed over the widget. +-- @signal button::press +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When a mouse button is released over the widget. +-- @signal button::release +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse enter a widget. +-- @signal mouse::enter +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse leave a widget. +-- @signal mouse::leave +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + + +--Imported documentation + + +--- Disconnect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback that should be disconnected +-- @function disconnect_signal + +--- Emit a signal. +-- +-- @tparam string name The name of the signal +-- @param ... Extra arguments for the callback functions. Each connected +-- function receives the object as first argument and then any extra arguments +-- that are given to emit_signal() +-- @function emit_signal + +--- Connect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function connect_signal + +--- Connect to a signal weakly. This allows the callback function to be garbage +-- collected and automatically disconnects the signal when that happens. +-- +-- **Warning:** +-- Only use this function if you really, really, really know what you +-- are doing. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function weak_connect_signal + + +return setmetatable(module, { __call = function(_, ...) return new(...) end }) +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/wibox/widget/progressbar.lua b/lib/wibox/widget/progressbar.lua new file mode 100644 index 0000000..943c49f --- /dev/null +++ b/lib/wibox/widget/progressbar.lua @@ -0,0 +1,754 @@ +--------------------------------------------------------------------------- +--- A progressbar widget. +-- +-- To add text on top of the progressbar, a `wibox.layout.stack` can be used: +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_progressbar_text.svg) +-- +-- +-- wibox.widget { +-- { +-- max_value = 1, +-- value = 0.5, +-- forced_height = 20, +-- forced_width = 100, +-- paddings = 1, +-- border_width = 1, +-- border_color = beautiful.border_color, +-- widget = wibox.widget.progressbar, +-- }, +-- { +-- text = '50%', +-- widget = wibox.widget.textbox, +-- }, +-- layout = wibox.layout.stack +-- } +-- +-- To display the progressbar vertically, use a `wibox.container.rotate` widget: +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_progressbar_vertical.svg) +-- +-- +-- wibox.widget { +-- { +-- max_value = 1, +-- value = 0.33, +-- widget = wibox.widget.progressbar, +-- }, +-- forced_height = 100, +-- forced_width = 20, +-- direction = 'east', +-- layout = wibox.container.rotate, +-- } +-- +-- By default, this widget will take all the available size. To prevent this, +-- a `wibox.container.constraint` widget or the `forced_width`/`forced_height` +-- properties have to be used. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_defaults_progressbar.svg) +-- +-- @usage +--wibox.widget { +-- max_value = 1, +-- value = 0.33, +-- forced_height = 20, +-- forced_width = 100, +-- shape = gears.shape.rounded_bar, +-- border_width = 2, +-- border_color = beautiful.border_color, +-- widget = wibox.widget.progressbar, +--} +-- +-- @author Julien Danjou <julien@danjou.info> +-- @copyright 2009 Julien Danjou +-- @classmod wibox.widget.progressbar +--------------------------------------------------------------------------- + +local setmetatable = setmetatable +local ipairs = ipairs +local math = math +local util = require("awful.util") +local base = require("wibox.widget.base") +local color = require("gears.color") +local beautiful = require("beautiful") +local shape = require("gears.shape") + +local progressbar = { mt = {} } + +--- The progressbar border color. +-- If the value is nil, no border will be drawn. +-- +-- @property border_color +-- @tparam gears.color color The border color to set. +-- @see gears.color + +--- The progressbar border width. +-- @property border_width + +--- The progressbar inner border color. +-- If the value is nil, no border will be drawn. +-- +-- @property bar_border_color +-- @tparam gears.color color The border color to set. +-- @see gears.color + +--- The progressbar inner border width. +-- @property bar_border_width + +--- The progressbar foreground color. +-- +-- @property color +-- @tparam gears.color color The progressbar color. +-- @see gears.color + +--- The progressbar background color. +-- +-- @property background_color +-- @tparam gears.color color The progressbar background color. +-- @see gears.color + +--- The progressbar inner shape. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_progressbar_bar_shape.svg) +-- +-- @usage +--for _, shape in ipairs {'rounded_bar', 'octogon', 'hexagon', 'powerline' } do +-- l:add(wibox.widget { +-- value = 0.33, +-- bar_shape = gears.shape[shape], +-- bar_border_color = beautiful.border_color, +-- bar_border_width = 1, +-- border_width = 2, +-- border_color = beautiful.border_color, +-- paddings = 1, +-- widget = wibox.widget.progressbar, +-- }) +--end +-- +-- @property bar_shape +-- @tparam[opt=gears.shape.rectangle] gears.shape shape +-- @see gears.shape + +--- The progressbar shape. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_progressbar_shape.svg) +-- +-- @usage +--for _, shape in ipairs {'rounded_bar', 'octogon', 'hexagon', 'powerline' } do +-- l:add(wibox.widget { +-- value = 0.33, +-- shape = gears.shape[shape], +-- border_width = 2, +-- border_color = beautiful.border_color, +-- widget = wibox.widget.progressbar, +-- }) +--end +-- +-- @property shape +-- @tparam[opt=gears.shape.rectangle] gears.shape shape +-- @see gears.shape + +--- Set the progressbar to draw vertically. +-- This doesn't do anything anymore, use a `wibox.container.rotate` widget. +-- @deprecated set_vertical +-- @tparam boolean vertical + +--- Force the inner part (the bar) to fit in the background shape. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_progressbar_clip.svg) +-- +-- @usage +--wibox.widget { +-- value = 75, +-- max_value = 100, +-- border_width = 2, +-- border_color = beautiful.border_color, +-- color = beautiful.border_color, +-- shape = gears.shape.rounded_bar, +-- bar_shape = gears.shape.rounded_bar, +-- clip = false, +-- forced_height = 30, +-- forced_width = 100, +-- paddings = 5, +-- margins = { +-- top = 12, +-- bottom = 12, +-- }, +-- widget = wibox.widget.progressbar, +--} +-- +-- @property clip +-- @tparam[opt=true] boolean clip + +--- The progressbar to draw ticks. Default is false. +-- +-- @property ticks +-- @param boolean + +--- The progressbar ticks gap. +-- +-- @property ticks_gap +-- @param number + +--- The progressbar ticks size. +-- +-- @property ticks_size +-- @param number + +--- The maximum value the progressbar should handle. +-- +-- @property max_value +-- @param number + +--- The progressbar background color. +-- @beautiful beautiful.progressbar_bg + +--- The progressbar foreground color. +-- @beautiful beautiful.progressbar_fg + +--- The progressbar shape. +-- @beautiful beautiful.progressbar_shape +-- @see gears.shape + +--- The progressbar border color. +-- @beautiful beautiful.progressbar_border_color + +--- The progressbar outer border width. +-- @beautiful beautiful.progressbar_border_width + +--- The progressbar inner shape. +-- @beautiful beautiful.progressbar_bar_shape +-- @see gears.shape + +--- The progressbar bar border width. +-- @beautiful beautiful.progressbar_bar_border_width + +--- The progressbar bar border color. +-- @beautiful beautiful.progressbar_bar_border_color + +--- The progressbar margins. +-- Note that if the `clip` is disabled, this allows the background to be smaller +-- than the bar. +-- +-- See the `clip` example. +-- +-- @tparam[opt=0] (table|number|nil) margins A table for each side or a number +-- @tparam[opt=0] number margins.top +-- @tparam[opt=0] number margins.bottom +-- @tparam[opt=0] number margins.left +-- @tparam[opt=0] number margins.right +-- @property margins +-- @see clip + +--- The progressbar padding. +-- Note that if the `clip` is disabled, this allows the bar to be taller +-- than the background. +-- +-- See the `clip` example. +-- +-- @tparam[opt=0] (table|number|nil) padding A table for each side or a number +-- @tparam[opt=0] number padding.top +-- @tparam[opt=0] number padding.bottom +-- @tparam[opt=0] number padding.left +-- @tparam[opt=0] number padding.right +-- @property paddings +-- @see clip + +--- The progressbar margins. +-- Note that if the `clip` is disabled, this allows the background to be smaller +-- than the bar. +-- @tparam[opt=0] (table|number|nil) margins A table for each side or a number +-- @tparam[opt=0] number margins.top +-- @tparam[opt=0] number margins.bottom +-- @tparam[opt=0] number margins.left +-- @tparam[opt=0] number margins.right +-- @beautiful beautiful.progressbar_margins +-- @see clip + +--- The progressbar padding. +-- Note that if the `clip` is disabled, this allows the bar to be taller +-- than the background. +-- @tparam[opt=0] (table|number|nil) padding A table for each side or a number +-- @tparam[opt=0] number padding.top +-- @tparam[opt=0] number padding.bottom +-- @tparam[opt=0] number padding.left +-- @tparam[opt=0] number padding.right +-- @beautiful beautiful.progressbar_paddings +-- @see clip + +local properties = { "border_color", "color" , "background_color", + "value" , "max_value" , "ticks", + "ticks_gap" , "ticks_size", "border_width", + "shape" , "bar_shape" , "bar_border_width", + "clip" , "margins" , "bar_border_color", + "paddings", + } + +function progressbar.draw(pbar, _, cr, width, height) + local ticks_gap = pbar._private.ticks_gap or 1 + local ticks_size = pbar._private.ticks_size or 4 + + -- We want one pixel wide lines + cr:set_line_width(1) + + local max_value = pbar._private.max_value + + local value = math.min(max_value, math.max(0, pbar._private.value)) + + if value >= 0 then + value = value / max_value + end + local border_width = pbar._private.border_width + or beautiful.progressbar_border_width or 0 + + local bcol = pbar._private.border_color or beautiful.progressbar_border_color + + border_width = bcol and border_width or 0 + + local bg = pbar._private.background_color or + beautiful.progressbar_bg or "#ff0000aa" + + local bg_width, bg_height = width, height + + local clip = pbar._private.clip ~= false and beautiful.progressbar_clip ~= false + + -- Apply the margins + local margin = pbar._private.margins or beautiful.progressbar_margins + + if margin then + if type(margin) == "number" then + cr:translate(margin, margin) + bg_width, bg_height = bg_width - 2*margin, bg_height - 2*margin + else + cr:translate(margin.left or 0, margin.top or 0) + bg_height = bg_height - + (margin.top or 0) - (margin.bottom or 0) + bg_width = bg_width - + (margin.left or 0) - (margin.right or 0) + end + end + + -- Draw the background shape + if border_width > 0 then + -- Cairo draw half of the border outside of the path area + cr:translate(border_width/2, border_width/2) + bg_width, bg_height = bg_width - border_width, bg_height - border_width + cr:set_line_width(border_width) + end + + local background_shape = pbar._private.shape or + beautiful.progressbar_shape or shape.rectangle + + background_shape(cr, bg_width, bg_height) + + cr:set_source(color(bg)) + + local over_drawn_width = bg_width + border_width + local over_drawn_height = bg_height + border_width + + if border_width > 0 then + cr:fill_preserve() + + -- Draw the border + cr:set_source(color(bcol)) + + cr:stroke() + + over_drawn_width = over_drawn_width - 2*border_width + over_drawn_height = over_drawn_height - 2*border_width + else + cr:fill() + end + + -- Undo the translation + cr:translate(-border_width/2, -border_width/2) + + -- Make sure the bar stay in the shape + if clip then + background_shape(cr, bg_width, bg_height) + cr:clip() + cr:translate(border_width, border_width) + else + -- Assume the background size is irrelevant to the bar itself + if type(margin) == "number" then + cr:translate(-margin, -margin) + else + cr:translate(-(margin.left or 0), -(margin.top or 0)) + end + + over_drawn_height = height + over_drawn_width = width + end + + -- Apply the padding + local padding = pbar._private.paddings or beautiful.progressbar_paddings + + if padding then + if type(padding) == "number" then + cr:translate(padding, padding) + over_drawn_height = over_drawn_height - 2*padding + over_drawn_width = over_drawn_width - 2*padding + else + cr:translate(padding.left or 0, padding.top or 0) + + over_drawn_height = over_drawn_height - + (padding.top or 0) - (padding.bottom or 0) + over_drawn_width = over_drawn_width - + (padding.left or 0) - (padding.right or 0) + end + end + + over_drawn_width = math.max(over_drawn_width , 0) + over_drawn_height = math.max(over_drawn_height, 0) + + local rel_x = over_drawn_width * value + + + -- Draw the progressbar shape + + local bar_shape = pbar._private.bar_shape or + beautiful.progressbar_bar_shape or shape.rectangle + + local bar_border_width = pbar._private.bar_border_width or + beautiful.progressbar_bar_border_width or pbar._private.border_width or + beautiful.progressbar_border_width or 0 + + local bar_border_color = pbar._private.bar_border_color or + beautiful.progressbar_bar_border_color + + bar_border_width = bar_border_color and bar_border_width or 0 + + over_drawn_width = over_drawn_width - bar_border_width + over_drawn_height = over_drawn_height - bar_border_width + cr:translate(bar_border_width/2, bar_border_width/2) + + bar_shape(cr, rel_x, over_drawn_height) + + cr:set_source(color(pbar._private.color or beautiful.progressbar_fg or "#ff0000")) + + if bar_border_width > 0 then + cr:fill_preserve() + cr:set_source(color(bar_border_color)) + cr:set_line_width(bar_border_width) + cr:stroke() + else + cr:fill() + end + + if pbar._private.ticks then + for i=0, width / (ticks_size+ticks_gap)-border_width do + local rel_offset = over_drawn_width / 1 - (ticks_size+ticks_gap) * i + + if rel_offset <= rel_x then + cr:rectangle(rel_offset, + border_width, + ticks_gap, + over_drawn_height) + end + end + cr:set_source(color(pbar._private.background_color or "#000000aa")) + cr:fill() + end +end + +function progressbar:fit(_, width, height) + return width, height +end + +--- Set the progressbar value. +-- @param value The progress bar value between 0 and 1. +function progressbar:set_value(value) + value = value or 0 + + self._private.value = value + + self:emit_signal("widget::redraw_needed") + return self +end + +function progressbar:set_max_value(max_value) + + self._private.max_value = max_value + + self:emit_signal("widget::redraw_needed") +end + +--- Set the progressbar height. +-- This method is deprecated. Use a `wibox.container.constraint` widget or +-- `forced_height`. +-- @param height The height to set. +-- @deprecated set_height +function progressbar:set_height(height) + util.deprecate("Use a `wibox.container.constraint` widget or `forced_height`") + self:set_forced_height(height) +end + +--- Set the progressbar width. +-- This method is deprecated. Use a `wibox.container.constraint` widget or +-- `forced_width`. +-- @param width The width to set. +-- @deprecated set_width +function progressbar:set_width(width) + util.deprecate("Use a `wibox.container.constraint` widget or `forced_width`") + self:set_forced_width(width) +end + +-- Build properties function +for _, prop in ipairs(properties) do + if not progressbar["set_" .. prop] then + progressbar["set_" .. prop] = function(pbar, value) + pbar._private[prop] = value + pbar:emit_signal("widget::redraw_needed") + return pbar + end + end +end + +function progressbar:set_vertical(value) --luacheck: no unused_args + util.deprecate("Use a `wibox.container.rotate` widget") +end + + +--- Create a progressbar widget. +-- @param args Standard widget() arguments. You should add width and height +-- key to set progressbar geometry. +-- @return A progressbar widget. +-- @function wibox.widget.progressbar +function progressbar.new(args) + args = args or {} + + local pbar = base.make_widget(nil, nil, { + enable_properties = true, + }) + + pbar._private.width = args.width or 100 + pbar._private.height = args.height or 20 + pbar._private.value = 0 + pbar._private.max_value = 1 + + util.table.crush(pbar, progressbar, true) + + return pbar +end + +function progressbar.mt:__call(...) + return progressbar.new(...) +end + +--Imported documentation + + +--- Get a widex index. +-- @param widget The widget to look for +-- @param[opt] recursive Also check sub-widgets +-- @param[opt] ... Aditional widgets to add at the end of the \"path\" +-- @return The index +-- @return The parent layout +-- @return The path between \"self\" and \"widget\" +-- @function index + +--- Get all direct and indirect children widgets. +-- This will scan all containers recursively to find widgets +-- Warning: This method it prone to stack overflow id the widget, or any of its +-- children, contain (directly or indirectly) itself. +-- @treturn table The children +-- @function get_all_children + +--- Set a declarative widget hierarchy description. +-- See [The declarative layout system](../documentation/03-declarative-layout.md.html) +-- @param args An array containing the widgets disposition +-- @function setup + +--- Force a widget height. +-- @property forced_height +-- @tparam number|nil height The height (`nil` for automatic) + +--- Force a widget width. +-- @property forced_width +-- @tparam number|nil width The width (`nil` for automatic) + +--- The widget opacity (transparency). +-- @property opacity +-- @tparam[opt=1] number opacity The opacity (between 0 and 1) + +--- The widget visibility. +-- @property visible +-- @param boolean + +--- Set/get a widget's buttons. +-- @param _buttons The table of buttons that should bind to the widget. +-- @function buttons + +--- Emit a signal and ensure all parent widgets in the hierarchies also +-- forward the signal. This is useful to track signals when there is a dynamic +-- set of containers and layouts wrapping the widget. +-- @tparam string signal_name +-- @param ... Other arguments +-- @function emit_signal_recursive + +--- When the layout (size) change. +-- This signal is emitted when the previous results of `:layout()` and `:fit()` +-- are no longer valid. Unless this signal is emitted, `:layout()` and `:fit()` +-- must return the same result when called with the same arguments. +-- @signal widget::layout_changed +-- @see widget::redraw_needed + +--- When the widget content changed. +-- This signal is emitted when the content of the widget changes. The widget will +-- be redrawn, it is not re-layouted. Put differently, it is assumed that +-- `:layout()` and `:fit()` would still return the same results as before. +-- @signal widget::redraw_needed +-- @see widget::layout_changed + +--- When a mouse button is pressed over the widget. +-- @signal button::press +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When a mouse button is released over the widget. +-- @signal button::release +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse enter a widget. +-- @signal mouse::enter +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse leave a widget. +-- @signal mouse::leave +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + + +--Imported documentation + + +--- Disconnect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback that should be disconnected +-- @function disconnect_signal + +--- Emit a signal. +-- +-- @tparam string name The name of the signal +-- @param ... Extra arguments for the callback functions. Each connected +-- function receives the object as first argument and then any extra arguments +-- that are given to emit_signal() +-- @function emit_signal + +--- Connect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function connect_signal + +--- Connect to a signal weakly. This allows the callback function to be garbage +-- collected and automatically disconnects the signal when that happens. +-- +-- **Warning:** +-- Only use this function if you really, really, really know what you +-- are doing. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function weak_connect_signal + + +return setmetatable(progressbar, progressbar.mt) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/wibox/widget/slider.lua b/lib/wibox/widget/slider.lua new file mode 100644 index 0000000..fd590c6 --- /dev/null +++ b/lib/wibox/widget/slider.lua @@ -0,0 +1,709 @@ +--------------------------------------------------------------------------- +-- An interactive mouse based slider widget. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_defaults_slider.svg) +-- +-- @usage +--wibox.widget { +-- bar_shape = gears.shape.rounded_rect, +-- bar_height = 3, +-- bar_color = beautiful.border_color, +-- handle_color = beautiful.bg_normal, +-- handle_shape = gears.shape.circle, +-- handle_border_color = beautiful.border_color, +-- handle_border_width = 1, +-- value = 25, +-- widget = wibox.widget.slider, +--} +-- +-- @author Grigory Mishchenko <grishkokot@gmail.com> +-- @author Emmanuel Lepage Vallee <elv1313@gmail.com> +-- @copyright 2015 Grigory Mishchenko, 2016 Emmanuel Lepage Vallee +-- @classmod wibox.widget.slider +--------------------------------------------------------------------------- + +local setmetatable = setmetatable +local type = type +local color = require("gears.color") +local util = require("awful.util") +local beautiful = require("beautiful") +local base = require("wibox.widget.base") +local shape = require("gears.shape") +local capi = { + mouse = mouse, + mousegrabber = mousegrabber, + root = root, +} + +local slider = {mt={}} + +--- The slider handle shape. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_slider_handle_shape.svg) +-- +-- +-- @property handle_shape +-- @tparam[opt=gears shape rectangle] gears.shape shape +-- @see gears.shape + +--- The slider handle color. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_slider_handle_color.svg) +-- +-- +-- @property handle_color +-- @param color + +--- The slider handle margins. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_slider_handle_margins.svg) +-- +-- +-- @property handle_margins +-- @tparam[opt={}] table margins +-- @tparam[opt=0] number margins.left +-- @tparam[opt=0] number margins.right +-- @tparam[opt=0] number margins.top +-- @tparam[opt=0] number margins.bottom + +--- The slider handle width. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_slider_handle_width.svg) +-- +-- +-- @property handle_width +-- @param number + +--- The handle border_color. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_slider_handle_border.svg) +-- +-- +-- @property handle_border_color +-- @param color + +--- The handle border width. +-- @property handle_border_width +-- @param[opt=0] number + +--- The bar (background) shape. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_slider_bar_shape.svg) +-- +-- +-- @property bar_shape +-- @tparam[opt=gears shape rectangle] gears.shape shape +-- @see gears.shape + +--- The bar (background) height. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_slider_bar_height.svg) +-- +-- +-- @property bar_height +-- @param number + +--- The bar (background) color. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_slider_bar_color.svg) +-- +-- +-- @property bar_color +-- @param color + +--- The bar (background) margins. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_slider_bar_margins.svg) +-- +-- +-- @property bar_margins +-- @tparam[opt={}] table margins +-- @tparam[opt=0] number margins.left +-- @tparam[opt=0] number margins.right +-- @tparam[opt=0] number margins.top +-- @tparam[opt=0] number margins.bottom + +--- The bar (background) border width. +-- @property bar_border_width +-- @param[opt=0] numbergb + +--- The bar (background) border_color. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_slider_bar_border.svg) +-- +-- +-- @property bar_border_color +-- @param color + +--- The slider value. +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_slider_value.svg) +-- +-- +-- @property value +-- @param[opt=0] number + +--- The slider minimum value. +-- @property minimum +-- @param[opt=0] number + +--- The slider maximum value. +-- @property maximum +-- @param[opt=100] number + +--- The bar (background) border width. +-- @beautiful beautiful.slider_bar_border_width +-- @param number + +--- The bar (background) border color. +-- @beautiful beautiful.slider_bar_border_color +-- @param color + +--- The handle border_color. +-- @beautiful beautiful.slider_handle_border_color +-- @param color + +--- The handle border width. +-- @beautiful beautiful.slider_handle_border_width +-- @param number + +--- The handle . +-- @beautiful beautiful.slider_handle_width +-- @param number + +-- @beautiful beautiful.slider_handle_color +-- @param color + +--- The handle shape. +-- @beautiful beautiful.slider_handle_shape +-- @tparam[opt=gears shape rectangle] gears.shape shape +-- @see gears.shape + +--- The bar (background) shape. +-- @beautiful beautiful.slider_bar_shape +-- @tparam[opt=gears shape rectangle] gears.shape shape +-- @see gears.shape + +--- The bar (background) height. +-- @beautiful beautiful.slider_bar_height +-- @param number + +--- The bar (background) margins. +-- @beautiful beautiful.slider_bar_margins +-- @tparam[opt={}] table margins +-- @tparam[opt=0] number margins.left +-- @tparam[opt=0] number margins.right +-- @tparam[opt=0] number margins.top +-- @tparam[opt=0] number margins.bottom + +--- The slider handle margins. +-- @beautiful beautiful.slider_handle_margins +-- @tparam[opt={}] table margins +-- @tparam[opt=0] number margins.left +-- @tparam[opt=0] number margins.right +-- @tparam[opt=0] number margins.top +-- @tparam[opt=0] number margins.bottom + +--- The bar (background) color. +-- @beautiful beautiful.slider_bar_color +-- @param color + +local properties = { + -- Handle + handle_shape = shape.rectangle, + handle_color = false, + handle_margins = {}, + handle_width = false, + handle_border_width = 0, + handle_border_color = false, + + -- Bar + bar_shape = shape.rectangle, + bar_height = false, + bar_color = false, + bar_margins = {}, + bar_border_width = 0, + bar_border_color = false, + + -- Content + value = 0, + minimum = 0, + maximum = 100, +} + +-- Create the accessors +for prop in pairs(properties) do + slider["set_"..prop] = function(self, value) + local changed = self._private[prop] ~= value + self._private[prop] = value + + if changed then + self:emit_signal("property::"..prop) + self:emit_signal("widget::redraw_needed") + end + end + + slider["get_"..prop] = function(self) + -- Ignoring the false's is on purpose + return self._private[prop] == nil + and properties[prop] + or self._private[prop] + end +end + +-- Add some validation to set_value +function slider:set_value(value) + value = math.min(value, self:get_maximum()) + value = math.max(value, self:get_minimum()) + local changed = self._private.value ~= value + + self._private.value = value + + if changed then + self:emit_signal( "property::value" ) + self:emit_signal( "widget::redraw_needed" ) + end +end + +local function get_extremums(self) + local min = self._private.minimum or properties.minimum + local max = self._private.maximum or properties.maximum + local interval = max - min + + return min, max, interval +end + +function slider:draw(_, cr, width, height) + local bar_height = self._private.bar_height + + -- If there is no background, then skip this + local bar_color = self._private.bar_color + or beautiful.slider_bar_color + + if bar_color then + cr:set_source(color(bar_color)) + end + + local margins = self._private.bar_margins + or beautiful.slider_bar_margins + + local x_offset, right_margin, y_offset = 0, 0 + + if margins then + if type(margins) == "number" then + bar_height = bar_height or (height - 2*margins) + x_offset, y_offset = margins, margins + right_margin = margins + else + bar_height = bar_height or ( + height - (margins.top or 0) - (margins.bottom or 0) + ) + x_offset, y_offset = margins.left or 0, margins.top or 0 + right_margin = margins.right or 0 + end + else + bar_height = bar_height or beautiful.slider_bar_height or height + y_offset = (height - bar_height)/2 + end + + + cr:translate(x_offset, y_offset) + + local bar_shape = self._private.bar_shape + or beautiful.slider_bar_shape + or properties.bar_shape + + local bar_border_width = self._private.bar_border_width + or beautiful.slider_bar_border_width + or properties.bar_border_width + + bar_shape(cr, width - x_offset - right_margin, bar_height or height) + + if bar_color then + if bar_border_width == 0 then + cr:fill() + else + cr:fill_preserve() + end + end + + -- Draw the bar border + if bar_border_width > 0 then + local bar_border_color = self._private.bar_border_color + or beautiful.slider_bar_border_color + or properties.bar_border_color + + cr:set_line_width(bar_border_width) + + if bar_border_color then + cr:save() + cr:set_source(color(bar_border_color)) + cr:stroke() + cr:restore() + else + cr:stroke() + end + end + + cr:translate(-x_offset, -y_offset) + + -- Paint the handle + local handle_color = self._private.handle_color + or beautiful.slider_handle_color + + -- It is ok if there is no color, it will be inherited + if handle_color then + cr:set_source(color(handle_color)) + end + + local handle_height, handle_width = height, self._private.handle_width + or beautiful.slider_handle_width + or height/2 + + local handle_shape = self._private.handle_shape + or beautiful.slider_handle_shape + or properties.handle_shape + + -- Lets get the margins for the handle + margins = self._private.handle_margins + or beautiful.slider_handle_margins + + x_offset, y_offset = 0, 0 + + if margins then + if type(margins) == "number" then + x_offset, y_offset = margins, margins + handle_width = handle_width - 2*margins + handle_height = handle_height - 2*margins + else + x_offset, y_offset = margins.left or 0, margins.top or 0 + handle_width = handle_width - + (margins.left or 0) - (margins.right or 0) + handle_height = handle_height - + (margins.top or 0) - (margins.bottom or 0) + end + end + + local value = self._private.value or self._private.min or 0 + + -- Get the widget size back to it's non-transfored value + local min, _, interval = get_extremums(self) + local rel_value = ((value-min)/interval) * (width-handle_width) + + cr:translate(x_offset + rel_value, y_offset) + + local handle_border_width = self._private.handle_border_width + or beautiful.slider_handle_border_width + or properties.handle_border_width or 0 + + handle_shape(cr, handle_width, handle_height) + + if handle_border_width > 0 then + cr:fill_preserve() + else + cr:fill() + end + + -- Draw the handle border + if handle_border_width > 0 then + local handle_border_color = self._private.handle_border_color + or beautiful.slider_handle_border_color + or properties.handle_border_color + + if handle_border_color then + cr:set_source(color(handle_border_color)) + end + + cr:set_line_width(handle_border_width) + cr:stroke() + end +end + +function slider:fit(_, width, height) + -- Use all the space, this should be used with a constraint widget + return width, height +end + +-- Move the handle to the correct location +local function move_handle(self, width, x, _) + local _, _, interval = get_extremums(self) + self:set_value(math.floor((x*interval)/width)) +end + +local function mouse_press(self, x, y, button_id, _, geo) + if button_id ~= 1 then return end + + local matrix_from_device = geo.hierarchy:get_matrix_from_device() + + -- Sigh. geo.width/geo.height is in device space. We need it in our own + -- coordinate system + local width = geo.widget_width + + move_handle(self, width, x, y) + + -- Calculate a matrix transforming from screen coordinates into widget coordinates + local wgeo = geo.drawable.drawable:geometry() + local matrix = matrix_from_device:translate(-wgeo.x, -wgeo.y) + + capi.mousegrabber.run(function(mouse) + if not mouse.buttons[1] then + return false + end + + -- Calculate the point relative to the widget + move_handle(self, width, matrix:transform_point(mouse.x, mouse.y)) + + return true + end,"fleur") +end + +--- Create a slider widget. +-- @tparam[opt={}] table args +-- @function wibox.widget.slider +local function new(args) + local ret = base.make_widget(nil, nil, { + enable_properties = true, + }) + + util.table.crush(ret._private, args or {}) + + util.table.crush(ret, slider, true) + + ret:connect_signal("button::press", mouse_press) + + return ret +end + +function slider.mt:__call(_, ...) + return new(...) +end + +--Imported documentation + + +--- Get a widex index. +-- @param widget The widget to look for +-- @param[opt] recursive Also check sub-widgets +-- @param[opt] ... Aditional widgets to add at the end of the \"path\" +-- @return The index +-- @return The parent layout +-- @return The path between \"self\" and \"widget\" +-- @function index + +--- Get all direct and indirect children widgets. +-- This will scan all containers recursively to find widgets +-- Warning: This method it prone to stack overflow id the widget, or any of its +-- children, contain (directly or indirectly) itself. +-- @treturn table The children +-- @function get_all_children + +--- Set a declarative widget hierarchy description. +-- See [The declarative layout system](../documentation/03-declarative-layout.md.html) +-- @param args An array containing the widgets disposition +-- @function setup + +--- Force a widget height. +-- @property forced_height +-- @tparam number|nil height The height (`nil` for automatic) + +--- Force a widget width. +-- @property forced_width +-- @tparam number|nil width The width (`nil` for automatic) + +--- The widget opacity (transparency). +-- @property opacity +-- @tparam[opt=1] number opacity The opacity (between 0 and 1) + +--- The widget visibility. +-- @property visible +-- @param boolean + +--- Set/get a widget's buttons. +-- @param _buttons The table of buttons that should bind to the widget. +-- @function buttons + +--- Emit a signal and ensure all parent widgets in the hierarchies also +-- forward the signal. This is useful to track signals when there is a dynamic +-- set of containers and layouts wrapping the widget. +-- @tparam string signal_name +-- @param ... Other arguments +-- @function emit_signal_recursive + +--- When the layout (size) change. +-- This signal is emitted when the previous results of `:layout()` and `:fit()` +-- are no longer valid. Unless this signal is emitted, `:layout()` and `:fit()` +-- must return the same result when called with the same arguments. +-- @signal widget::layout_changed +-- @see widget::redraw_needed + +--- When the widget content changed. +-- This signal is emitted when the content of the widget changes. The widget will +-- be redrawn, it is not re-layouted. Put differently, it is assumed that +-- `:layout()` and `:fit()` would still return the same results as before. +-- @signal widget::redraw_needed +-- @see widget::layout_changed + +--- When a mouse button is pressed over the widget. +-- @signal button::press +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When a mouse button is released over the widget. +-- @signal button::release +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse enter a widget. +-- @signal mouse::enter +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse leave a widget. +-- @signal mouse::leave +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + + +--Imported documentation + + +--- Disconnect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback that should be disconnected +-- @function disconnect_signal + +--- Emit a signal. +-- +-- @tparam string name The name of the signal +-- @param ... Extra arguments for the callback functions. Each connected +-- function receives the object as first argument and then any extra arguments +-- that are given to emit_signal() +-- @function emit_signal + +--- Connect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function connect_signal + +--- Connect to a signal weakly. This allows the callback function to be garbage +-- collected and automatically disconnects the signal when that happens. +-- +-- **Warning:** +-- Only use this function if you really, really, really know what you +-- are doing. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function weak_connect_signal + + +return setmetatable(slider, slider.mt) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/wibox/widget/systray.lua b/lib/wibox/widget/systray.lua new file mode 100644 index 0000000..e83347c --- /dev/null +++ b/lib/wibox/widget/systray.lua @@ -0,0 +1,186 @@ +--------------------------------------------------------------------------- +-- @author Uli Schlachter +-- @copyright 2010 Uli Schlachter +-- @classmod wibox.widget.systray +--------------------------------------------------------------------------- + +local wbase = require("wibox.widget.base") +local beautiful = require("beautiful") +local util = require("awful.util") +local capi = { + awesome = awesome, + screen = screen +} +local setmetatable = setmetatable +local error = error +local abs = math.abs + +local systray = { mt = {} } + +local instance = nil +local horizontal = true +local base_size = nil +local reverse = false +local display_on_screen = "primary" + +--- The systray background color. +-- @beautiful beautiful.bg_systray +-- @param string The color (string like "#ff0000" only) + +--- The systray icon spacing. +-- @beautiful beautiful.systray_icon_spacing +-- @tparam[opt=0] integer The icon spacing + +local function should_display_on(s) + if display_on_screen == "primary" then + return s == capi.screen.primary + end + return s == display_on_screen +end + +function systray:draw(context, cr, width, height) + if not should_display_on(context.screen) then + return + end + + local x, y, _, _ = wbase.rect_to_device_geometry(cr, 0, 0, width, height) + local num_entries = capi.awesome.systray() + local bg = beautiful.bg_systray or beautiful.bg_normal or "#000000" + local spacing = beautiful.systray_icon_spacing or 0 + + if context and not context.wibox then + error("The systray widget can only be placed inside a wibox.") + end + + -- Figure out if the cairo context is rotated + local dir_x, dir_y = cr:user_to_device_distance(1, 0) + local is_rotated = abs(dir_x) < abs(dir_y) + + local in_dir, ortho, base + if horizontal then + in_dir, ortho = width, height + is_rotated = not is_rotated + else + ortho, in_dir = width, height + end + if ortho * num_entries <= in_dir then + base = ortho + else + base = in_dir / num_entries + end + capi.awesome.systray(context.wibox.drawin, math.ceil(x), math.ceil(y), + base, is_rotated, bg, reverse, spacing) +end + +function systray:fit(context, width, height) + if not should_display_on(context.screen) then + return 0, 0 + end + + local num_entries = capi.awesome.systray() + local base = base_size + local spacing = beautiful.systray_icon_spacing or 0 + if num_entries == 0 then + return 0, 0 + end + if base == nil then + if width < height then + base = width + else + base = height + end + end + base = base + spacing + if horizontal then + return base * num_entries - spacing, base + end + return base, base * num_entries - spacing +end + +-- Check if the function was called like :foo() or .foo() and do the right thing +local function get_args(self, ...) + if self == instance then + return ... + end + return self, ... +end + +--- Set the size of a single icon. +-- If this is set to nil, then the size is picked dynamically based on the +-- available space. Otherwise, any single icon has a size of `size`x`size`. +-- @tparam integer|nil size The base size +function systray:set_base_size(size) + base_size = get_args(self, size) + if instance then + instance:emit_signal("widget::layout_changed") + end +end + +--- Decide between horizontal or vertical display. +-- @tparam boolean horiz Use horizontal mode? +function systray:set_horizontal(horiz) + horizontal = get_args(self, horiz) + if instance then + instance:emit_signal("widget::layout_changed") + end +end + +--- Should the systray icons be displayed in reverse order? +-- @tparam boolean rev Display in reverse order +function systray:set_reverse(rev) + reverse = get_args(self, rev) + if instance then + instance:emit_signal("widget::redraw_needed") + end +end + +--- Set the screen that the systray should be displayed on. +-- This can either be a screen, in which case the systray will be displayed on +-- exactly that screen, or the string `"primary"`, in which case it will be +-- visible on the primary screen. The default value is "primary". +-- @tparam screen|"primary" s The screen to display on. +function systray:set_screen(s) + display_on_screen = get_args(self, s) + if instance then + instance:emit_signal("widget::layout_changed") + end +end + +--- Create the systray widget. +-- Note that this widget can only exist once. +-- @tparam boolean revers Show in the opposite direction +-- @treturn table The new `systray` widget +-- @function wibox.widget.systray + +local function new(revers) + local ret = wbase.make_widget() + + util.table.crush(ret, systray, true) + + if revers then + ret:set_reverse(true) + end + + capi.awesome.connect_signal("systray::update", function() + ret:emit_signal("widget::layout_changed") + ret:emit_signal("widget::redraw_needed") + end) + capi.screen.connect_signal("primary_changed", function() + if display_on_screen == "primary" then + ret:emit_signal("widget::layout_changed") + end + end) + + return ret +end + +function systray.mt:__call(...) + if not instance then + instance = new(...) + end + return instance +end + +return setmetatable(systray, systray.mt) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/wibox/widget/textbox.lua b/lib/wibox/widget/textbox.lua new file mode 100644 index 0000000..add35e9 --- /dev/null +++ b/lib/wibox/widget/textbox.lua @@ -0,0 +1,507 @@ +--------------------------------------------------------------------------- +-- +-- +-- +--![Usage example](../images/AUTOGEN_wibox_widget_defaults_textbox.svg) +-- +-- @usage +--wibox.widget{ +-- markup = 'This <i>is</i> a <b>textbox</b>!!!', +-- align = 'center', +-- valign = 'center', +-- widget = wibox.widget.textbox +--} +-- @author Uli Schlachter +-- @author dodo +-- @copyright 2010, 2011 Uli Schlachter, dodo +-- @classmod wibox.widget.textbox +--------------------------------------------------------------------------- + +local base = require("wibox.widget.base") +local gdebug = require("gears.debug") +local beautiful = require("beautiful") +local lgi = require("lgi") +local util = require("awful.util") +local Pango = lgi.Pango +local PangoCairo = lgi.PangoCairo +local setmetatable = setmetatable + +local textbox = { mt = {} } + +--- The textbox font. +-- @beautiful beautiful.font + +--- Set the DPI of a Pango layout +local function setup_dpi(box, dpi) + if box._private.dpi ~= dpi then + box._private.dpi = dpi + box._private.ctx:set_resolution(dpi) + box._private.layout:context_changed() + end +end + +--- Setup a pango layout for the given textbox and dpi +local function setup_layout(box, width, height, dpi) + box._private.layout.width = Pango.units_from_double(width) + box._private.layout.height = Pango.units_from_double(height) + setup_dpi(box, dpi) +end + +-- Draw the given textbox on the given cairo context in the given geometry +function textbox:draw(context, cr, width, height) + setup_layout(self, width, height, context.dpi) + cr:update_layout(self._private.layout) + local _, logical = self._private.layout:get_pixel_extents() + local offset = 0 + if self._private.valign == "center" then + offset = (height - logical.height) / 2 + elseif self._private.valign == "bottom" then + offset = height - logical.height + end + cr:move_to(0, offset) + cr:show_layout(self._private.layout) +end + +local function do_fit_return(self) + local _, logical = self._private.layout:get_pixel_extents() + if logical.width == 0 or logical.height == 0 then + return 0, 0 + end + return logical.width, logical.height +end + +-- Fit the given textbox +function textbox:fit(context, width, height) + setup_layout(self, width, height, context.dpi) + return do_fit_return(self) +end + +--- Get the preferred size of a textbox. +-- This returns the size that the textbox would use if infinite space were +-- available. +-- @tparam integer|screen s The screen on which the textbox will be displayed. +-- @treturn number The preferred width. +-- @treturn number The preferred height. +function textbox:get_preferred_size(s) + return self:get_preferred_size_at_dpi(beautiful.xresources.get_dpi(s)) +end + +--- Get the preferred height of a textbox at a given width. +-- This returns the height that the textbox would use when it is limited to the +-- given width. +-- @tparam number width The available width. +-- @tparam integer|screen s The screen on which the textbox will be displayed. +-- @treturn number The needed height. +function textbox:get_height_for_width(width, s) + return self:get_height_for_width_at_dpi(width, beautiful.xresources.get_dpi(s)) +end + +--- Get the preferred size of a textbox. +-- This returns the size that the textbox would use if infinite space were +-- available. +-- @tparam number dpi The DPI value to render at. +-- @treturn number The preferred width. +-- @treturn number The preferred height. +function textbox:get_preferred_size_at_dpi(dpi) + local max_lines = 2^20 + setup_dpi(self, dpi) + self._private.layout.width = -1 -- no width set + self._private.layout.height = -max_lines -- show this many lines per paragraph + return do_fit_return(self) +end + +--- Get the preferred height of a textbox at a given width. +-- This returns the height that the textbox would use when it is limited to the +-- given width. +-- @tparam number width The available width. +-- @tparam number dpi The DPI value to render at. +-- @treturn number The needed height. +function textbox:get_height_for_width_at_dpi(width, dpi) + local max_lines = 2^20 + setup_dpi(self, dpi) + self._private.layout.width = Pango.units_from_double(width) + self._private.layout.height = -max_lines -- show this many lines per paragraph + local _, h = do_fit_return(self) + return h +end + +--- Set the text of the textbox (with +-- [Pango markup](https://developer.gnome.org/pango/stable/PangoMarkupFormat.html)). +-- @tparam string text The text to set. This can contain pango markup (e.g. +-- `<b>bold</b>`). You can use `awful.util.escape` to escape +-- parts of it. +-- @treturn[1] boolean true +-- @treturn[2] boolean false +-- @treturn[2] string Error message explaining why the markup was invalid. +function textbox:set_markup_silently(text) + if self._private.markup == text then + return true + end + + local attr, parsed = Pango.parse_markup(text, -1, 0) + -- In case of error, attr is false and parsed is a GLib.Error instance. + if not attr then + return false, parsed.message or tostring(parsed) + end + + self._private.markup = text + self._private.layout.text = parsed + self._private.layout.attributes = attr + self:emit_signal("widget::redraw_needed") + self:emit_signal("widget::layout_changed") + return true +end + +--- Set the text of the textbox (with +-- [Pango markup](https://developer.gnome.org/pango/stable/PangoMarkupFormat.html)). +-- @property markup +-- @tparam string text The text to set. This can contain pango markup (e.g. +-- `<b>bold</b>`). You can use `awful.util.escape` to escape +-- parts of it. +-- @see text + +function textbox:set_markup(text) + local success, message = self:set_markup_silently(text) + if not success then + gdebug.print_error(message) + end +end + +function textbox:get_markup() + return self._private.markup +end + +--- Set a textbox' text. +-- @property text +-- @param text The text to display. Pango markup is ignored and shown as-is. +-- @see markup + +function textbox:set_text(text) + if self._private.layout.text == text and self._private.layout.attributes == nil then + return + end + self._private.markup = nil + self._private.layout.text = text + self._private.layout.attributes = nil + self:emit_signal("widget::redraw_needed") + self:emit_signal("widget::layout_changed") +end + +function textbox:get_text() + return self._private.layout.text +end + +--- Set a textbox' ellipsize mode. +-- @property ellipsize +-- @param mode Where should long lines be shortened? "start", "middle" or "end" + +function textbox:set_ellipsize(mode) + local allowed = { none = "NONE", start = "START", middle = "MIDDLE", ["end"] = "END" } + if allowed[mode] then + if self._private.layout:get_ellipsize() == allowed[mode] then + return + end + self._private.layout:set_ellipsize(allowed[mode]) + self:emit_signal("widget::redraw_needed") + self:emit_signal("widget::layout_changed") + end +end + +--- Set a textbox' wrap mode. +-- @property wrap +-- @param mode Where to wrap? After "word", "char" or "word_char" + +function textbox:set_wrap(mode) + local allowed = { word = "WORD", char = "CHAR", word_char = "WORD_CHAR" } + if allowed[mode] then + if self._private.layout:get_wrap() == allowed[mode] then + return + end + self._private.layout:set_wrap(allowed[mode]) + self:emit_signal("widget::redraw_needed") + self:emit_signal("widget::layout_changed") + end +end + +--- The textbox' vertical alignment +-- @property valign +-- @param mode Where should the textbox be drawn? "top", "center" or "bottom" + +function textbox:set_valign(mode) + local allowed = { top = true, center = true, bottom = true } + if allowed[mode] then + if self._private.valign == mode then + return + end + self._private.valign = mode + self:emit_signal("widget::redraw_needed") + self:emit_signal("widget::layout_changed") + end +end + +--- Set a textbox' horizontal alignment. +-- @property align +-- @param mode Where should the textbox be drawn? "left", "center" or "right" + +function textbox:set_align(mode) + local allowed = { left = "LEFT", center = "CENTER", right = "RIGHT" } + if allowed[mode] then + if self._private.layout:get_alignment() == allowed[mode] then + return + end + self._private.layout:set_alignment(allowed[mode]) + self:emit_signal("widget::redraw_needed") + self:emit_signal("widget::layout_changed") + end +end + +--- Set a textbox' font +-- @property font +-- @param font The font description as string + +function textbox:set_font(font) + self._private.layout:set_font_description(beautiful.get_font(font)) + self:emit_signal("widget::redraw_needed") + self:emit_signal("widget::layout_changed") +end + +--- Create a new textbox. +-- @tparam[opt=""] string text The textbox content +-- @tparam[opt=false] boolean ignore_markup Ignore the pango/HTML markup +-- @treturn table A new textbox widget +-- @function wibox.widget.textbox +local function new(text, ignore_markup) + local ret = base.make_widget(nil, nil, {enable_properties = true}) + + util.table.crush(ret, textbox, true) + + ret._private.dpi = -1 + ret._private.ctx = PangoCairo.font_map_get_default():create_context() + ret._private.layout = Pango.Layout.new(ret._private.ctx) + + ret:set_ellipsize("end") + ret:set_wrap("word_char") + ret:set_valign("center") + ret:set_align("left") + ret:set_font(beautiful and beautiful.font) + + if text then + if ignore_markup then + ret:set_text(text) + else + ret:set_markup(text) + end + end + + return ret +end + +function textbox.mt.__call(_, ...) + return new(...) +end + +--Imported documentation + + +--- Get a widex index. +-- @param widget The widget to look for +-- @param[opt] recursive Also check sub-widgets +-- @param[opt] ... Aditional widgets to add at the end of the \"path\" +-- @return The index +-- @return The parent layout +-- @return The path between \"self\" and \"widget\" +-- @function index + +--- Get all direct and indirect children widgets. +-- This will scan all containers recursively to find widgets +-- Warning: This method it prone to stack overflow id the widget, or any of its +-- children, contain (directly or indirectly) itself. +-- @treturn table The children +-- @function get_all_children + +--- Set a declarative widget hierarchy description. +-- See [The declarative layout system](../documentation/03-declarative-layout.md.html) +-- @param args An array containing the widgets disposition +-- @function setup + +--- Force a widget height. +-- @property forced_height +-- @tparam number|nil height The height (`nil` for automatic) + +--- Force a widget width. +-- @property forced_width +-- @tparam number|nil width The width (`nil` for automatic) + +--- The widget opacity (transparency). +-- @property opacity +-- @tparam[opt=1] number opacity The opacity (between 0 and 1) + +--- The widget visibility. +-- @property visible +-- @param boolean + +--- Set/get a widget's buttons. +-- @param _buttons The table of buttons that should bind to the widget. +-- @function buttons + +--- Emit a signal and ensure all parent widgets in the hierarchies also +-- forward the signal. This is useful to track signals when there is a dynamic +-- set of containers and layouts wrapping the widget. +-- @tparam string signal_name +-- @param ... Other arguments +-- @function emit_signal_recursive + +--- When the layout (size) change. +-- This signal is emitted when the previous results of `:layout()` and `:fit()` +-- are no longer valid. Unless this signal is emitted, `:layout()` and `:fit()` +-- must return the same result when called with the same arguments. +-- @signal widget::layout_changed +-- @see widget::redraw_needed + +--- When the widget content changed. +-- This signal is emitted when the content of the widget changes. The widget will +-- be redrawn, it is not re-layouted. Put differently, it is assumed that +-- `:layout()` and `:fit()` would still return the same results as before. +-- @signal widget::redraw_needed +-- @see widget::layout_changed + +--- When a mouse button is pressed over the widget. +-- @signal button::press +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When a mouse button is released over the widget. +-- @signal button::release +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse enter a widget. +-- @signal mouse::enter +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse leave a widget. +-- @signal mouse::leave +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + + +--Imported documentation + + +--- Disconnect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback that should be disconnected +-- @function disconnect_signal + +--- Emit a signal. +-- +-- @tparam string name The name of the signal +-- @param ... Extra arguments for the callback functions. Each connected +-- function receives the object as first argument and then any extra arguments +-- that are given to emit_signal() +-- @function emit_signal + +--- Connect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function connect_signal + +--- Connect to a signal weakly. This allows the callback function to be garbage +-- collected and automatically disconnects the signal when that happens. +-- +-- **Warning:** +-- Only use this function if you really, really, really know what you +-- are doing. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function weak_connect_signal + + +return setmetatable(textbox, textbox.mt) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/wibox/widget/textclock.lua b/lib/wibox/widget/textclock.lua new file mode 100644 index 0000000..c688028 --- /dev/null +++ b/lib/wibox/widget/textclock.lua @@ -0,0 +1,254 @@ +--------------------------------------------------------------------------- +--- Text clock widget. +-- +-- @author Julien Danjou <julien@danjou.info> +-- @copyright 2009 Julien Danjou +-- @classmod wibox.widget.textclock +--------------------------------------------------------------------------- + +local setmetatable = setmetatable +local os = os +local textbox = require("wibox.widget.textbox") +local timer = require("gears.timer") +local DateTime = require("lgi").GLib.DateTime + +local textclock = { mt = {} } + +--- This lowers the timeout so that it occurs "correctly". For example, a timeout +-- of 60 is rounded so that it occurs the next time the clock reads ":00 seconds". +local function calc_timeout(real_timeout) + return real_timeout - os.time() % real_timeout +end + +--- Create a textclock widget. It draws the time it is in a textbox. +-- +-- @tparam[opt=" %a %b %d, %H:%M "] string format The time format. +-- @tparam[opt=60] number timeout How often update the time (in seconds). +-- @treturn table A textbox widget. +-- @function wibox.widget.textclock +function textclock.new(format, timeout) + format = format or " %a %b %d, %H:%M " + timeout = timeout or 60 + + local w = textbox() + local t + function w._private.textclock_update_cb() + w:set_markup(DateTime.new_now_local():format(format)) + t.timeout = calc_timeout(timeout) + t:again() + return true -- Continue the timer + end + t = timer.weak_start_new(timeout, w._private.textclock_update_cb) + t:emit_signal("timeout") + return w +end + +function textclock.mt:__call(...) + return textclock.new(...) +end + +--Imported documentation + + +--- Get a widex index. +-- @param widget The widget to look for +-- @param[opt] recursive Also check sub-widgets +-- @param[opt] ... Aditional widgets to add at the end of the \"path\" +-- @return The index +-- @return The parent layout +-- @return The path between \"self\" and \"widget\" +-- @function index + +--- Get all direct and indirect children widgets. +-- This will scan all containers recursively to find widgets +-- Warning: This method it prone to stack overflow id the widget, or any of its +-- children, contain (directly or indirectly) itself. +-- @treturn table The children +-- @function get_all_children + +--- Set a declarative widget hierarchy description. +-- See [The declarative layout system](../documentation/03-declarative-layout.md.html) +-- @param args An array containing the widgets disposition +-- @function setup + +--- Force a widget height. +-- @property forced_height +-- @tparam number|nil height The height (`nil` for automatic) + +--- Force a widget width. +-- @property forced_width +-- @tparam number|nil width The width (`nil` for automatic) + +--- The widget opacity (transparency). +-- @property opacity +-- @tparam[opt=1] number opacity The opacity (between 0 and 1) + +--- The widget visibility. +-- @property visible +-- @param boolean + +--- Set/get a widget's buttons. +-- @param _buttons The table of buttons that should bind to the widget. +-- @function buttons + +--- Emit a signal and ensure all parent widgets in the hierarchies also +-- forward the signal. This is useful to track signals when there is a dynamic +-- set of containers and layouts wrapping the widget. +-- @tparam string signal_name +-- @param ... Other arguments +-- @function emit_signal_recursive + +--- When the layout (size) change. +-- This signal is emitted when the previous results of `:layout()` and `:fit()` +-- are no longer valid. Unless this signal is emitted, `:layout()` and `:fit()` +-- must return the same result when called with the same arguments. +-- @signal widget::layout_changed +-- @see widget::redraw_needed + +--- When the widget content changed. +-- This signal is emitted when the content of the widget changes. The widget will +-- be redrawn, it is not re-layouted. Put differently, it is assumed that +-- `:layout()` and `:fit()` would still return the same results as before. +-- @signal widget::redraw_needed +-- @see widget::layout_changed + +--- When a mouse button is pressed over the widget. +-- @signal button::press +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When a mouse button is released over the widget. +-- @signal button::release +-- @tparam number lx The horizontal position relative to the (0,0) position in +-- the widget. +-- @tparam number ly The vertical position relative to the (0,0) position in the +-- widget. +-- @tparam number button The button number. +-- @tparam table mods The modifiers (mod4, mod1 (alt), Control, Shift) +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse enter a widget. +-- @signal mouse::enter +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + +--- When the mouse leave a widget. +-- @signal mouse::leave +-- @tparam table find_widgets_result The entry from the result of +-- @{wibox.drawable:find_widgets} for the position that the mouse hit. +-- @tparam wibox.drawable find_widgets_result.drawable The drawable containing +-- the widget. +-- @tparam widget find_widgets_result.widget The widget being displayed. +-- @tparam wibox.hierarchy find_widgets_result.hierarchy The hierarchy +-- managing the widget's geometry. +-- @tparam number find_widgets_result.x An approximation of the X position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.y An approximation of the Y position that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.width An approximation of the width that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.height An approximation of the height that +-- the widget is visible at on the surface. +-- @tparam number find_widgets_result.widget_width The exact width of the widget +-- in its local coordinate system. +-- @tparam number find_widgets_result.widget_height The exact height of the widget +-- in its local coordinate system. +-- @see mouse + + +--Imported documentation + + +--- Disconnect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback that should be disconnected +-- @function disconnect_signal + +--- Emit a signal. +-- +-- @tparam string name The name of the signal +-- @param ... Extra arguments for the callback functions. Each connected +-- function receives the object as first argument and then any extra arguments +-- that are given to emit_signal() +-- @function emit_signal + +--- Connect to a signal. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function connect_signal + +--- Connect to a signal weakly. This allows the callback function to be garbage +-- collected and automatically disconnects the signal when that happens. +-- +-- **Warning:** +-- Only use this function if you really, really, really know what you +-- are doing. +-- @tparam string name The name of the signal +-- @tparam function func The callback to call when the signal is emitted +-- @function weak_connect_signal + + +return setmetatable(textclock, textclock.mt) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 |