wibox: Add widget geometry cache

This commit adds and uses wibox.layout.base.fit_widget(). This function is a
wrapper for widget:fit() that caches the result and thus speeds things up.

This is necessary because some layouts call :fit() from their :fit() and :draw()
functions. Nesting such layouts means that at the widget at the tail of the
stack gets its :fit() function called quite often. If this function is not
blazingly fast, this results in noticeable slowness.

Signed-off-by: Uli Schlachter <psychon@znc.in>
This commit is contained in:
Uli Schlachter 2013-08-21 12:26:47 +02:00
parent a0e45e878e
commit 3edd216560
9 changed files with 43 additions and 15 deletions

View File

@ -28,10 +28,10 @@ function align:draw(wibox, cr, width, height)
if self.first then
local w, h, _ = width, height, nil
if self.dir == "y" then
_, h = self.first:fit(w, h)
_, h = base.fit_widget(self.first, w, h)
size_first = h
else
w, _ = self.first:fit(w, h)
w, _ = base.fit_widget(self.first, w, h)
size_first = w
end
base.draw_widget(wibox, cr, self.first, 0, 0, w, h)
@ -41,12 +41,12 @@ function align:draw(wibox, cr, width, height)
local w, h, x, y, _
if self.dir == "y" then
w, h = width, height - size_first
_, h = self.third:fit(w, h)
_, h = base.fit_widget(self.third, w, h)
x, y = 0, height - h
size_third = h
else
w, h = width - size_first, height
w, _ = self.third:fit(w, h)
w, _ = base.fit_widget(self.third, w, h)
x, y = width - w, 0
size_third = w
end
@ -57,12 +57,12 @@ function align:draw(wibox, cr, width, height)
local x, y, w, h
if self.dir == "y" then
w, h = width, size_limit - size_first - size_third
local real_w, real_h = self.second:fit(w, h)
local real_w, real_h = base.fit_widget(self.second, w, h)
x, y = 0, size_first + h / 2 - real_h / 2
h = real_h
else
w, h = size_limit - size_first - size_third, height
local real_w, real_h = self.second:fit(w, h)
local real_w, real_h = base.fit_widget(self.second, w, h)
x, y = size_first + w / 2 - real_w / 2, 0
w = real_w
end
@ -108,7 +108,7 @@ function align:fit(orig_width, orig_height)
local used_max = 0
for k, v in pairs{self.first, self.second, self.third} do
local w, h = v:fit(orig_width, orig_height)
local w, h = base.fit_widget(v, orig_width, orig_height)
local max = self.dir == "y" and w or h

View File

@ -26,6 +26,27 @@ function base.rect_to_device_geometry(cr, x, y, width, height)
return x, y, width, height
end
--- Fit a widget for the given available width and height
-- @param widget The widget to fit (this uses widget:fit(width, height)).
-- @param width The available width for the widget
-- @param height The available height for the widget
-- @return The width and height that the widget wants to use
function base.fit_widget(widget, width, height)
local cache = widget._fit_geometry_cache
local result = cache[width]
if not result then
result = {}
cache[width] = result
end
cache, result = result, result[height]
if not result then
local w, h = widget:fit(width, height)
result = { width = w, height = h }
cache[height] = result
end
return result.width, result.height
end
--- Draw a widget via a cairo context
-- @param wibox The wibox on which we are drawing
-- @param cr The cairo context used

View File

@ -30,7 +30,7 @@ function constraint:fit(width, height)
w = self._strategy(width, self._width)
h = self._strategy(height, self._height)
w, h = self.widget:fit(w, h)
w, h = base.fit_widget(self.widget, w, h)
else
w, h = 0, 0
end

View File

@ -28,7 +28,7 @@ function fixed:draw(wibox, cr, width, height)
x, y = 0, pos
w, h = width, height - pos
if k ~= #self.widgets or not self.fill_space then
_, h = v:fit(w, h);
_, h = base.fit_widget(v, w, h);
end
pos = pos + h
in_dir = h
@ -36,7 +36,7 @@ function fixed:draw(wibox, cr, width, height)
x, y = pos, 0
w, h = width - pos, height
if k ~= #self.widgets or not self.fill_space then
w, _ = v:fit(w, h);
w, _ = base.fit_widget(v, w, h);
end
pos = pos + w
in_dir = w
@ -66,7 +66,7 @@ function fixed:fit(orig_width, orig_height)
local used_in_dir, used_max = 0, 0
for k, v in pairs(self.widgets) do
local w, h = v:fit(width, height)
local w, h = base.fit_widget(v, width, height)
local in_dir, max
if self.dir == "y" then
max, in_dir = w, h

View File

@ -90,7 +90,7 @@ function flex:fit(orig_width, orig_height)
local sub_width = self.dir == "y" and orig_width or floor(used_in_dir / #self.widgets)
for k, v in pairs(self.widgets) do
local w, h = v:fit(sub_width, sub_height)
local w, h = base.fit_widget(v, sub_width, sub_height)
local max
if self.dir == "y" then
max = w

View File

@ -33,7 +33,7 @@ function margin:fit(width, height)
local extra_h = self.top + self.bottom
local w, h = 0, 0
if self.widget then
w, h = self.widget:fit(width - extra_w, height - extra_h)
w, h = base.fit_widget(self.widget, width - extra_w, height - extra_h)
end
return w + extra_w, h + extra_h
end

View File

@ -49,7 +49,7 @@ function mirror:fit(...)
if not self.widget then
return 0, 0
end
return self.widget:fit(...)
return base.fit_widget(self.widget, ...)
end
--- Set the widget that this layout mirrors.

View File

@ -51,7 +51,7 @@ function rotate:fit(width, height)
if not self.widget then
return 0, 0
end
return transform(self, self.widget:fit(transform(self, width, height)))
return transform(self, base.fit_widget(self.widget, transform(self, width, height)))
end
--- Set the widget that this layout rotates.

View File

@ -6,6 +6,7 @@
local debug = require("gears.debug")
local object = require("gears.object")
local setmetatable = setmetatable
local pairs = pairs
local type = type
local table = table
@ -94,6 +95,12 @@ function base.make_widget(proxy)
end)
end
-- Add a geometry for base.fit_widget() that is cleared when necessary
ret._fit_geometry_cache = setmetatable({}, { __mode = 'v' })
ret:connect_signal("widget::updated", function()
ret._fit_geometry_cache = setmetatable({}, { __mode = 'v' })
end)
return ret
end