diff options
Diffstat (limited to 'awesome/lib/wibox/widget/base.lua')
-rw-r--r-- | awesome/lib/wibox/widget/base.lua | 694 |
1 files changed, 0 insertions, 694 deletions
diff --git a/awesome/lib/wibox/widget/base.lua b/awesome/lib/wibox/widget/base.lua deleted file mode 100644 index dd80ec7..0000000 --- a/awesome/lib/wibox/widget/base.lua +++ /dev/null @@ -1,694 +0,0 @@ ---------------------------------------------------------------------------- --- @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 |