From 2f3bce00e6d39e36b01e0c38faaf87b4b0529475 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Tue, 2 Nov 2021 21:59:56 -0700 Subject: [PATCH] tasklist: Refactor to base a normal widget. It was previously monkey-patching the input layout object into the final tasklist. This is a breaking change, but affects undocumented behaviors. By doing this, it becomes possible to expose the properties in the public API. This, in turn, allows to document them. Right now, the documentation is very vague on some behaviors. --- docs/89-NEWS.md | 4 + lib/awful/widget/tasklist.lua | 183 +++++++++++++++++++++++++++++----- 2 files changed, 164 insertions(+), 23 deletions(-) diff --git a/docs/89-NEWS.md b/docs/89-NEWS.md index 18d315cb1..4e19739fe 100644 --- a/docs/89-NEWS.md +++ b/docs/89-NEWS.md @@ -76,6 +76,10 @@ This document was last updated at commit v4.3-197-g9085ed631. old behavior, use `awful.rules.rules = {}; awful.rules.rules = my_new_rules`. * `client:relative_move()` now default `nil` values to zero. The previous behavior made no sense. + * The tasklist and taglist widgets are no longer directly an instance of + it's main layout. Use the `base_layout` property to access the layout. + This allows to replace the layout at runtime. The previous behavior + was undocumented. # Awesome window manager framework version 4.3 changes diff --git a/lib/awful/widget/tasklist.lua b/lib/awful/widget/tasklist.lua index aea5c23fb..1a4a646d1 100644 --- a/lib/awful/widget/tasklist.lua +++ b/lib/awful/widget/tasklist.lua @@ -94,6 +94,7 @@ local wmargin = require("wibox.container.margin") local wtextbox = require("wibox.widget.textbox") local clienticon = require("awful.widget.clienticon") local wbackground = require("wibox.container.background") +local gtable = require("gears.table") local function get_screen(s) return s and screen[s] @@ -479,7 +480,7 @@ end local function tasklist_update(s, w, buttons, filter, data, style, update_function, args) local clients = {} - local source = args and args.source or tasklist.source.all_clients or nil + local source = self.source or tasklist.source.all_clients or nil local list = source and source(s, args) or capi.client.get() for _, c in ipairs(list) do @@ -498,6 +499,117 @@ local function tasklist_update(s, w, buttons, filter, data, style, update_functi }) end +--- Set the tasklist layout. +-- +-- @property base_layout +-- @tparam[opt=wibox.layout.flex.horizontal] wibox.layout base_layout +-- @see wibox.layout.flex.horizontal + +function tasklist:set_base_layout(layout) + self._private.base_layout = base.make_widget_from_value( + layout or flex.horizontal + ) + + local spacing = self._private.style.spacing or beautiful.tasklist_spacing + + if self._private.base_layout.set_spacing and spacing then + self._private.base_layout:set_spacing(spacing) + end + + assert(self._private.base_layout.is_widget) + + self._do_tasklist_update() + + self:emit_signal("widget::layout_changed") + self:emit_signal("widget::redraw_needed") + self:emit_signal("property::base_layout", layout) +end + +function tasklist:layout(_, width, height) + if self._private.base_layout then + return { base.place_widget_at(self._private.base_layout, 0, 0, width, height) } + end +end + +function tasklist:fit(context, width, height) + if not self._private.base_layout then + return 0, 0 + end + + return base.fit_widget(self, context, self._private.base_layout, width, height) +end + +for _, prop in ipairs { "screen", "filter", "update_function", "widget_template", "source"} do + tasklist["set_"..prop] = function(self, value) + if value == self._private[prop] then return end + + self._private[prop] = value + + self._do_tasklist_update() + + self:emit_signal("widget::layout_changed") + self:emit_signal("widget::redraw_needed") + self:emit_signal("property::"..prop, value) + end + + tasklist["get_"..prop] = function(self) + return self._private[prop] + end +end + +local function update_screen(self, screen, old) + if not instances then return end + + if old and instances[old] then + for k, w in ipairs(instances[old]) do + if w == self then + table.remove(instances[old], k) + break + end + end + end + + local list = instances[screen] + + if not list then + list = setmetatable({}, { __mode = "v" }) + instances[screen] = list + end + + table.insert(list, self) +end + +function tasklist:set_screen(value) + value = get_screen(value) + + if value == self._private.screen then return end + + local old = self._private.screen + + self._private.screen = value + + update_screen(self, screen, old) + + self._do_tasklist_update() + + self:emit_signal("widget::layout_changed") + self:emit_signal("widget::redraw_needed") + self:emit_signal("property::screen", value) +end + +function tasklist:set_widget_template(widget_template) + self._private.widget_template = widget_template + + -- Remove the existing instances + self._private.data = setmetatable({}, { __mode = 'k' }) + + self._do_tasklist_update() + + self:emit_signal("widget::layout_changed") + self:emit_signal("widget::redraw_needed") + self:emit_signal("property::widget_template", widget_template) +end + --- Create a new tasklist widget. -- The last two arguments (update_function -- and layout) serve to customize the layout of the tasklist (eg. to @@ -530,7 +642,7 @@ end -- @tparam[opt=nil] string args.style.bg_image_focus -- @tparam[opt=nil] string args.style.bg_image_urgent -- @tparam[opt=nil] string args.style.bg_image_minimize --- @tparam[opt=nil] boolean args.style.tasklist_disable_icon +-- @tparam[opt=nil] boolean args.style.disable_icon -- @tparam[opt=nil] number args.style.icon_size The size of the icon -- @tparam[opt='▪'] string args.style.sticky Extra icon when client is sticky -- @tparam[opt='⌃'] string args.style.ontop Extra icon when client is ontop @@ -563,7 +675,7 @@ end -- @param buttons **DEPRECATED** use args.buttons -- @param style **DEPRECATED** use args.style -- @param update_function **DEPRECATED** use args.update_function --- @param base_widget **DEPRECATED** use args.base_widget +-- @param base_widget **DEPRECATED** use args.base_layout -- @constructorfct awful.widget.tasklist function tasklist.new(args, filter, buttons, style, update_function, base_widget) local screen = nil @@ -596,23 +708,37 @@ function tasklist.new(args, filter, buttons, style, update_function, base_widget screen = screen or get_screen(args.screen) local uf = args.update_function or common.list_update - local w = base.make_widget_from_value(args.layout or flex.horizontal) - local data = setmetatable({}, { __mode = 'k' }) + local w = base.make_widget(nil, nil, { + enable_properties = true, + }) - local spacing = args.style and args.style.spacing or args.layout and args.layout.spacing - or beautiful.tasklist_spacing - if w.set_spacing and spacing then - w:set_spacing(spacing) - end + gtable.crush(w._private, { + disable_task_name = args.disable_task_name, + disable_icon = args.disable_icon, + update_function = args.update_function, + filter = args.filter, + buttons = args.buttons, + style = args.style or {}, + screen = screen, + widget_template = args.widget_template, + source = args.source, + data = setmetatable({}, { __mode = 'k' }) + }) + + gtable.crush(w, tasklist, true) + rawset(w, "filter", nil) + rawset(w, "source", nil) local queued_update = false -- For the tests function w._do_tasklist_update_now() queued_update = false - if screen.valid then - tasklist_update(screen, w, args.buttons, args.filter, data, args.style, uf, args) + if w._private.screen.valid then + tasklist_update( + w._private.screen, w, w._private.buttons, w._private.filter, w._private.data, args.style, uf, args + ) end end @@ -624,7 +750,7 @@ function tasklist.new(args, filter, buttons, style, update_function, base_widget end end function w._unmanage(c) - data[c] = nil + w._private.data[c] = nil end if instances == nil then instances = setmetatable({}, { __mode = "k" }) @@ -681,17 +807,20 @@ function tasklist.new(args, filter, buttons, style, update_function, base_widget instances[get_screen(s)] = nil end) end + + tasklist.set_base_layout(w, args.layout or args.base_layout) + w._do_tasklist_update() - local list = instances[screen] - if not list then - list = setmetatable({}, { __mode = "v" }) - instances[screen] = list - end - table.insert(list, w) + + update_screen(w, screen) + return w end --- Filtering function to include all clients. +-- +--@DOC_sequences_client_tasklist_filter_allscreen1_EXAMPLE@ +-- -- @return true -- @filterfunction awful.widget.tasklist.filter.allscreen function tasklist.filter.allscreen() @@ -699,8 +828,11 @@ function tasklist.filter.allscreen() end --- Filtering function to include the clients from all tags on the screen. --- @param c The client. --- @param screen The screen we are drawing on. +-- +--@DOC_sequences_client_tasklist_filter_alltags1_EXAMPLE@ +-- +-- @tparam client c The client. +-- @tparam screen screen The screen we are drawing on. -- @return true if c is on screen, false otherwise -- @filterfunction awful.widget.tasklist.filter.alltags function tasklist.filter.alltags(c, screen) @@ -709,8 +841,13 @@ function tasklist.filter.alltags(c, screen) end --- Filtering function to include only the clients from currently selected tags. --- @param c The client. --- @param screen The screen we are drawing on. +-- +-- This is the filter used in the default `rc.lua`. +-- +--@DOC_sequences_client_tasklist_filter_currenttags1_EXAMPLE@ +-- +-- @tparam client c The client. +-- @tparam screen screen The screen we are drawing on. -- @return true if c is in a selected tag on screen, false otherwise -- @filterfunction awful.widget.tasklist.filter.currenttags function tasklist.filter.currenttags(c, screen)