summaryrefslogtreecommitdiff
path: root/awesome/lib/wibox/widget/base.lua
diff options
context:
space:
mode:
Diffstat (limited to 'awesome/lib/wibox/widget/base.lua')
-rw-r--r--awesome/lib/wibox/widget/base.lua694
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