wibox.widget.base: Add caches for :layout and :fit
These caches, well, cache the result of the :layout and :fit callbacks on widgets. Clearing caches is done by recording dependencies between a widget. When a call to base.fit_widget() or base.layout_widget() recursively causes another call to such a function, this means that the earlier widget depends on the later widget. This dependency is recorded and when the later widget emits widget::layout_changed, the caches of all the widgets involved are cleared. Signed-off-by: Uli Schlachter <psychon@znc.in>
This commit is contained in:
parent
447492e986
commit
c62d323d09
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
local debug = require("gears.debug")
|
local debug = require("gears.debug")
|
||||||
local object = require("gears.object")
|
local object = require("gears.object")
|
||||||
|
local cache = require("gears.cache")
|
||||||
local matrix = require("gears.matrix")
|
local matrix = require("gears.matrix")
|
||||||
local Matrix = require("lgi").cairo.Matrix
|
local Matrix = require("lgi").cairo.Matrix
|
||||||
local setmetatable = setmetatable
|
local setmetatable = setmetatable
|
||||||
|
@ -16,6 +17,72 @@ local table = table
|
||||||
|
|
||||||
local base = {}
|
local base = {}
|
||||||
|
|
||||||
|
-- {{{ Caches
|
||||||
|
|
||||||
|
local call_stack = {}
|
||||||
|
-- Indexes are widgets, allow them to be garbage-collected
|
||||||
|
local widget_dependencies = setmetatable({}, { __mode = "k" })
|
||||||
|
|
||||||
|
-- Don't do this in unit tests
|
||||||
|
if awesome and awesome.connect_signal then
|
||||||
|
-- Reset the call stack at each refresh. This fixes things up in case there was
|
||||||
|
-- an error in some callback and thus put_cache() wasn't called (if this
|
||||||
|
-- happens, we possibly recorded too many deps, but so what?)
|
||||||
|
awesome.connect_signal("refresh", function()
|
||||||
|
call_stack = {}
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- When you call get_cache_and_record_deps(), the widget is recorded in a stack
|
||||||
|
-- until the following put_cache(). All other calls to
|
||||||
|
-- get_cache_and_record_deps() that happen during this will cause a dependency
|
||||||
|
-- between the widgets that are involved to be recorded. This information is
|
||||||
|
-- used by clear_caches() to also clear all caches of dependent widgets.
|
||||||
|
|
||||||
|
-- Get the caches for a widget and record its dependencies. All following
|
||||||
|
-- cache-uses will record this widgets as a dependency. This returns a function
|
||||||
|
-- that calls the callback of kind `kind` on the widget.
|
||||||
|
local function get_cache_and_record_deps(widget, kind)
|
||||||
|
-- Record dependencies (each entry in the call stack depends on `widget`)
|
||||||
|
local deps = widget_dependencies[widget] or {}
|
||||||
|
for _, w in pairs(call_stack) do
|
||||||
|
deps[w] = true
|
||||||
|
end
|
||||||
|
widget_dependencies[widget] = deps
|
||||||
|
|
||||||
|
-- Add widget to call stack
|
||||||
|
table.insert(call_stack, widget)
|
||||||
|
|
||||||
|
-- Create cache if needed
|
||||||
|
if not widget._widget_caches[kind] then
|
||||||
|
widget._widget_caches[kind] = cache.new(function(...)
|
||||||
|
return widget[kind](widget, ...)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
return widget._widget_caches[kind]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Each call to the above function should be followed by a call to this
|
||||||
|
-- function. Everything in-between is recorded as a dependency (it's
|
||||||
|
-- complicated...).
|
||||||
|
local function put_cache(widget)
|
||||||
|
assert(#call_stack ~= 0)
|
||||||
|
if table.remove(call_stack) ~= widget then
|
||||||
|
put_cache(widget)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Clear the caches for `widget` and all widgets that depend on it.
|
||||||
|
local function clear_caches(widget)
|
||||||
|
for w in pairs(widget_dependencies[widget] or {}) do
|
||||||
|
widget_dependencies[w] = {}
|
||||||
|
w._widget_caches = {}
|
||||||
|
end
|
||||||
|
widget_dependencies[widget] = {}
|
||||||
|
widget._widget_caches = {}
|
||||||
|
end
|
||||||
|
-- }}}
|
||||||
|
|
||||||
--- Figure out the geometry in device coordinate space. This gives only tight
|
--- Figure out the geometry in device coordinate space. This gives only tight
|
||||||
-- bounds if no rotations by non-multiples of 90° are used.
|
-- bounds if no rotations by non-multiples of 90° are used.
|
||||||
function base.rect_to_device_geometry(cr, x, y, width, height)
|
function base.rect_to_device_geometry(cr, x, y, width, height)
|
||||||
|
@ -41,7 +108,9 @@ function base.fit_widget(context, widget, width, height)
|
||||||
|
|
||||||
local w, h = 0, 0
|
local w, h = 0, 0
|
||||||
if widget.fit then
|
if widget.fit then
|
||||||
w, h = widget:fit(context, width, height)
|
local cache = get_cache_and_record_deps(widget, "fit")
|
||||||
|
w, h = cache:get(context, width, height)
|
||||||
|
put_cache(widget)
|
||||||
else
|
else
|
||||||
-- If it has no fit method, calculate based on the size of children
|
-- If it has no fit method, calculate based on the size of children
|
||||||
local children = base.layout_widget(context, widget, width, height)
|
local children = base.layout_widget(context, widget, width, height)
|
||||||
|
@ -77,7 +146,10 @@ function base.layout_widget(context, widget, width, height)
|
||||||
local height = math.max(0, height)
|
local height = math.max(0, height)
|
||||||
|
|
||||||
if widget.layout then
|
if widget.layout then
|
||||||
return widget:layout(context, width, height)
|
local cache = get_cache_and_record_deps(widget, "layout")
|
||||||
|
local result = cache:get(context, width, height)
|
||||||
|
put_cache(widget)
|
||||||
|
return result
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -324,6 +396,12 @@ function base.make_widget(proxy, widget_name)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Set up caches
|
||||||
|
clear_caches(ret)
|
||||||
|
ret:connect_signal("widget::layout_changed", function()
|
||||||
|
clear_caches(ret)
|
||||||
|
end)
|
||||||
|
|
||||||
-- Add visible property and setter.
|
-- Add visible property and setter.
|
||||||
ret.visible = true
|
ret.visible = true
|
||||||
function ret:set_visible(b)
|
function ret:set_visible(b)
|
||||||
|
|
|
@ -86,10 +86,12 @@ return {
|
||||||
w:add_signal("widget::layout_changed")
|
w:add_signal("widget::layout_changed")
|
||||||
w.visible = true
|
w.visible = true
|
||||||
w.opacity = 1
|
w.opacity = 1
|
||||||
|
if width or height then
|
||||||
w.fit = function()
|
w.fit = function()
|
||||||
return width or 10, height or 10
|
return width or 10, height or 10
|
||||||
end
|
end
|
||||||
w._fit_geometry_cache = cache.new(w.fit)
|
end
|
||||||
|
w._widget_caches = {}
|
||||||
|
|
||||||
return w
|
return w
|
||||||
end,
|
end,
|
||||||
|
|
Loading…
Reference in New Issue