Add a widget hierarchy implementation
A widget hierarchy describes the position of widgets. The hierarchy is a recursive tree of widget hierarchy instances. This functionality depends on a :layout function that is not yet implemented on widgets, but will be added later. Signed-off-by: Uli Schlachter <psychon@znc.in>
This commit is contained in:
parent
4785b63755
commit
02f67b61b4
|
@ -0,0 +1,285 @@
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
-- Management of widget hierarchies. Each widget hierarchy object has a widget
|
||||||
|
-- for which it saves e.g. size and transformation in its parent. Also, each
|
||||||
|
-- widget has a number of children.
|
||||||
|
--
|
||||||
|
-- @author Uli Schlachter
|
||||||
|
-- @copyright 2015 Uli Schlachter
|
||||||
|
-- @release @AWESOME_VERSION@
|
||||||
|
-- @module wibox.hierarchy
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local matrix = require("gears.matrix")
|
||||||
|
local cairo = require("lgi").cairo
|
||||||
|
local base = require("wibox.widget.base")
|
||||||
|
|
||||||
|
local hierarchy = {}
|
||||||
|
|
||||||
|
--- Create a new widget hierarchy that has no parent.
|
||||||
|
-- @param context The context in which we are laid out.
|
||||||
|
-- @param widget The widget that is at the base of the hierarchy.
|
||||||
|
-- @param width The available width for this hierarchy
|
||||||
|
-- @param height The available height for this hierarchy
|
||||||
|
-- @param redraw_callback Callback that is called with the corresponding widget
|
||||||
|
-- hierarchy on widget::redraw_needed on some widget.
|
||||||
|
-- @param layout_callback Callback that is called with the corresponding widget
|
||||||
|
-- hierarchy on widget::layout_changed on some widget.
|
||||||
|
-- @param callback_arg A second argument that is given to the above callbacks.
|
||||||
|
-- @param root The root of the widget hierarchy or nil if this creates the root.
|
||||||
|
-- @return A new widget hierarchy
|
||||||
|
local function hierarchy_new(context, widget, width, height, redraw_callback, layout_callback, callback_arg, root)
|
||||||
|
local children = base.layout_widget(context, widget, width, height)
|
||||||
|
local draws_x1, draws_y1, draws_x2, draws_y2 = 0, 0, width, height
|
||||||
|
local result = {
|
||||||
|
_parent = nil,
|
||||||
|
_root = nil,
|
||||||
|
_matrix = cairo.Matrix.create_identity(),
|
||||||
|
_widget = widget,
|
||||||
|
_size = {
|
||||||
|
width = width,
|
||||||
|
height = height
|
||||||
|
},
|
||||||
|
_draw_extents = nil,
|
||||||
|
_children = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
result._root = root or result
|
||||||
|
result._redraw = function() redraw_callback(result, callback_arg) end
|
||||||
|
result._layout = function() layout_callback(result, callback_arg) end
|
||||||
|
widget:weak_connect_signal("widget::redraw_needed", result._redraw)
|
||||||
|
widget:weak_connect_signal("widget::layout_changed", result._layout)
|
||||||
|
|
||||||
|
for _, w in ipairs(children or {}) do
|
||||||
|
local r = hierarchy_new(context, w._widget, w._width, w._height,
|
||||||
|
redraw_callback, layout_callback, callback_arg, result._root)
|
||||||
|
r._matrix = w._matrix
|
||||||
|
r._parent = result
|
||||||
|
table.insert(result._children, r)
|
||||||
|
|
||||||
|
-- Update our drawing extents
|
||||||
|
local s = r._draw_extents
|
||||||
|
local px, py, pwidth, pheight = matrix.transform_rectangle(r._matrix,
|
||||||
|
s.x, s.y, s.width, s.height)
|
||||||
|
local px2, py2 = px + pwidth, py + pheight
|
||||||
|
draws_x1 = math.min(draws_x1, px)
|
||||||
|
draws_y1 = math.min(draws_y1, py)
|
||||||
|
draws_x2 = math.max(draws_x2, px2)
|
||||||
|
draws_y2 = math.max(draws_y2, py2)
|
||||||
|
end
|
||||||
|
result._draw_extents = {
|
||||||
|
x = draws_x1,
|
||||||
|
y = draws_y1,
|
||||||
|
width = draws_x2 - draws_x1,
|
||||||
|
height = draws_y2 - draws_y1
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, f in pairs(hierarchy) do
|
||||||
|
if type(f) == "function" then
|
||||||
|
result[k] = f
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Create a new widget hierarchy that has no parent.
|
||||||
|
-- @param context The context in which we are laid out.
|
||||||
|
-- @param widget The widget that is at the base of the hierarchy.
|
||||||
|
-- @param width The available width for this hierarchy.
|
||||||
|
-- @param height The available height for this hierarchy.
|
||||||
|
-- @param redraw_callback Callback that is called with the corresponding widget
|
||||||
|
-- hierarchy on widget::redraw_needed on some widget.
|
||||||
|
-- @param layout_callback Callback that is called with the corresponding widget
|
||||||
|
-- hierarchy on widget::layout_changed on some widget.
|
||||||
|
-- @param callback_arg A second argument that is given to the above callbacks.
|
||||||
|
-- @return A new widget hierarchy
|
||||||
|
function hierarchy.new(context, widget, width, height, redraw_callback, layout_callback, callback_arg)
|
||||||
|
return hierarchy_new(context, widget, width, height, redraw_callback, layout_callback, callback_arg, nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the parent hierarchy of this widget hierarchy (or nil).
|
||||||
|
function hierarchy:get_parent()
|
||||||
|
return self._parent
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the widget that this hierarchy manages.
|
||||||
|
function hierarchy:get_widget()
|
||||||
|
return self._widget
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get a cairo matrix that transforms to the parent's coordinate space from
|
||||||
|
-- this hierarchy's coordinate system.
|
||||||
|
-- @return A cairo matrix describing the transformation.
|
||||||
|
function hierarchy:get_matrix_to_parent()
|
||||||
|
return matrix.copy(self._matrix)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get a cairo matrix that transforms to the base of this hierarchy's
|
||||||
|
-- coordinate system (aka the coordinate system of the device that this
|
||||||
|
-- hierarchy is applied upon) from this hierarchy's coordinate system.
|
||||||
|
-- @return A cairo matrix describing the transformation.
|
||||||
|
function hierarchy:get_matrix_to_device()
|
||||||
|
if not self._matrix_to_device then
|
||||||
|
local m = cairo.Matrix.create_identity()
|
||||||
|
local state = self
|
||||||
|
while state ~= nil do
|
||||||
|
m:multiply(m, state._matrix)
|
||||||
|
state = state._parent
|
||||||
|
end
|
||||||
|
self._matrix_to_device = m
|
||||||
|
end
|
||||||
|
return matrix.copy(self._matrix_to_device)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get a cairo matrix that transforms from the parent's coordinate space into
|
||||||
|
-- this hierarchy's coordinate system.
|
||||||
|
-- @return A cairo matrix describing the transformation.
|
||||||
|
function hierarchy:get_matrix_from_parent()
|
||||||
|
local m = self:get_matrix_to_parent()
|
||||||
|
m:invert()
|
||||||
|
return m
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get a cairo matrix that transforms from the base of this hierarchy's
|
||||||
|
-- coordinate system (aka the coordinate system of the device that this
|
||||||
|
-- hierarchy is applied upon) into this hierarchy's coordinate system.
|
||||||
|
-- @return A cairo matrix describing the transformation.
|
||||||
|
function hierarchy:get_matrix_from_device()
|
||||||
|
local m = self:get_matrix_to_device()
|
||||||
|
m:invert()
|
||||||
|
return m
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the extents that this hierarchy possibly draws to (in the current coordinate space).
|
||||||
|
-- This includes the size of this element plus the size of all children
|
||||||
|
-- (after applying the corresponding transformation).
|
||||||
|
-- @return x, y, width, height
|
||||||
|
function hierarchy:get_draw_extents()
|
||||||
|
local ext = self._draw_extents
|
||||||
|
return ext.x, ext.y, ext.width, ext.height
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the size that this hierarchy logically covers (in the current coordinate space).
|
||||||
|
-- @return width, height
|
||||||
|
function hierarchy:get_size()
|
||||||
|
local ext = self._size
|
||||||
|
return ext.width, ext.height
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get a list of all children.
|
||||||
|
-- @return List of all children hierarchies.
|
||||||
|
function hierarchy:get_children()
|
||||||
|
return self._children
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Compare two widget hierarchies and compute a cairo Region that contains all
|
||||||
|
-- rectangles that aren't the same between both hierarchies.
|
||||||
|
-- @param other The hierarchy to compare with
|
||||||
|
-- @return A cairo Region containing the differences.
|
||||||
|
function hierarchy:find_differences(other)
|
||||||
|
local region = cairo.Region.create()
|
||||||
|
local function needs_redraw(h)
|
||||||
|
local m = h:get_matrix_to_device()
|
||||||
|
local p = h._draw_extents
|
||||||
|
local x, y, width, height = matrix.transform_rectangle(m, p.x, p.y, p.width, p.height)
|
||||||
|
local x1, y1 = math.floor(x), math.floor(y)
|
||||||
|
local x2, y2 = math.ceil(x + width), math.ceil(y + height)
|
||||||
|
region:union_rectangle(cairo.RectangleInt({
|
||||||
|
x = x1, y = y1, width = x2 - x1, height = y2 - y1
|
||||||
|
}))
|
||||||
|
end
|
||||||
|
local compare
|
||||||
|
compare = function(self, other)
|
||||||
|
local s_size, o_size = self._size, other._size
|
||||||
|
if s_size.width ~= o_size.width or s_size.height ~= o_size.height or
|
||||||
|
#self._children ~= #other._children or self._widget ~= other._widget or
|
||||||
|
not matrix.equals(self._matrix, other._matrix) then
|
||||||
|
needs_redraw(self)
|
||||||
|
needs_redraw(other)
|
||||||
|
else
|
||||||
|
for i = 1, #self._children do
|
||||||
|
compare(self._children[i], other._children[i])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
compare(self, other)
|
||||||
|
return region
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Does the given cairo context have an empty clip (aka "no drawing possible")?
|
||||||
|
local function empty_clip(cr)
|
||||||
|
local x, y, width, height = cr:clip_extents()
|
||||||
|
return width == 0 or height == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Draw a hierarchy to some cairo context.
|
||||||
|
-- This function draws the widgets in this widget hierarchy to the given cairo
|
||||||
|
-- context. The context's clip is used to skip parts that aren't visible.
|
||||||
|
-- @param context The context in which widgets are drawn.
|
||||||
|
-- @param cr The cairo context that is used for drawing.
|
||||||
|
function hierarchy:draw(context, cr)
|
||||||
|
local widget = self:get_widget()
|
||||||
|
if not widget.visible then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
cr:save()
|
||||||
|
cr:transform(self:get_matrix_to_parent())
|
||||||
|
|
||||||
|
-- Clip to the draw extents
|
||||||
|
cr:rectangle(self:get_draw_extents())
|
||||||
|
cr:clip()
|
||||||
|
|
||||||
|
-- Draw if needed
|
||||||
|
if not empty_clip(cr) then
|
||||||
|
local opacity = widget.opacity
|
||||||
|
local function call(func, extra_arg1, extra_arg2)
|
||||||
|
if not func then return end
|
||||||
|
local function error_function(err)
|
||||||
|
print(debug.traceback("Error while drawing widget: " .. tostring(err), 2))
|
||||||
|
end
|
||||||
|
if not extra_arg2 then
|
||||||
|
xpcall(function()
|
||||||
|
func(widget, arg, cr, self:get_size())
|
||||||
|
end, error_function)
|
||||||
|
else
|
||||||
|
xpcall(function()
|
||||||
|
func(widget, arg, extra_arg1, extra_arg2, cr, self:get_size())
|
||||||
|
end, error_function)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Prepare opacity handling
|
||||||
|
if opacity ~= 1 then
|
||||||
|
cr:push_group()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Draw the widget
|
||||||
|
cr:save()
|
||||||
|
cr:rectangle(0, 0, self:get_size())
|
||||||
|
cr:clip()
|
||||||
|
call(widget.draw)
|
||||||
|
cr:restore()
|
||||||
|
|
||||||
|
-- Draw its children (We already clipped to the draw extents above)
|
||||||
|
call(widget.before_draw_children)
|
||||||
|
for i, wi in ipairs(self:get_children()) do
|
||||||
|
call(widget.before_draw_child, i, wi:get_widget())
|
||||||
|
wi:draw(context, cr)
|
||||||
|
call(widget.after_draw_child, i, wi:get_widget())
|
||||||
|
end
|
||||||
|
call(widget.after_draw_children)
|
||||||
|
|
||||||
|
-- Apply opacity
|
||||||
|
if opacity ~= 1 then
|
||||||
|
cr:pop_group_to_source()
|
||||||
|
cr.operator = cairo.Operator.OVER
|
||||||
|
cr:paint_with_alpha(opacity)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
cr:restore()
|
||||||
|
end
|
||||||
|
|
||||||
|
return hierarchy
|
||||||
|
|
||||||
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
|
@ -29,6 +29,7 @@ local wibox = { mt = {} }
|
||||||
wibox.layout = require("wibox.layout")
|
wibox.layout = require("wibox.layout")
|
||||||
wibox.widget = require("wibox.widget")
|
wibox.widget = require("wibox.widget")
|
||||||
wibox.drawable = require("wibox.drawable")
|
wibox.drawable = require("wibox.drawable")
|
||||||
|
wibox.hierarchy = require("wibox.hierarchy")
|
||||||
|
|
||||||
--- Set the widget that the wibox displays
|
--- Set the widget that the wibox displays
|
||||||
function wibox:set_widget(widget)
|
function wibox:set_widget(widget)
|
||||||
|
|
|
@ -0,0 +1,246 @@
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
-- @author Uli Schlachter
|
||||||
|
-- @copyright 2015 Uli Schlachter
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local hierarchy = require("wibox.hierarchy")
|
||||||
|
|
||||||
|
local cairo = require("lgi").cairo
|
||||||
|
local matrix = require("gears.matrix")
|
||||||
|
local object = require("gears.object")
|
||||||
|
|
||||||
|
local function make_widget(children)
|
||||||
|
local result = object()
|
||||||
|
result:add_signal("widget::redraw_needed")
|
||||||
|
result:add_signal("widget::layout_changed")
|
||||||
|
result.visible = true
|
||||||
|
result.layout = function()
|
||||||
|
return children
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
local function make_child(widget, width, height, matrix)
|
||||||
|
return { _widget = widget, _width = width, _height = height, _matrix = matrix }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe("wibox.hierarchy", function()
|
||||||
|
describe("Accessor functions", function()
|
||||||
|
local widget, instance
|
||||||
|
before_each(function()
|
||||||
|
local function nop() end
|
||||||
|
local context = {}
|
||||||
|
widget = make_widget(nil)
|
||||||
|
instance = hierarchy.new(context, widget, 10, 20, nop, nop)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("get_parent", function()
|
||||||
|
assert.is_nil(instance:get_parent())
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("get_widget", function()
|
||||||
|
assert.is.equal(instance:get_widget(), widget)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("get_matrix_to_parent", function()
|
||||||
|
assert.is_true(matrix.equals(cairo.Matrix.create_identity(),
|
||||||
|
instance:get_matrix_to_parent()))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("get_matrix_to_device", function()
|
||||||
|
assert.is_true(matrix.equals(cairo.Matrix.create_identity(),
|
||||||
|
instance:get_matrix_to_device()))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("get_matrix_from_parent", function()
|
||||||
|
assert.is_true(matrix.equals(cairo.Matrix.create_identity(),
|
||||||
|
instance:get_matrix_from_parent()))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("get_matrix_from_device", function()
|
||||||
|
assert.is_true(matrix.equals(cairo.Matrix.create_identity(),
|
||||||
|
instance:get_matrix_from_device()))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("get_draw_extents", function()
|
||||||
|
assert.is.same({ instance:get_draw_extents() }, { 0, 0, 10, 20 })
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("get_size", function()
|
||||||
|
assert.is.same({ instance:get_size() }, { 10, 20 })
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("get_children", function()
|
||||||
|
assert.is.same(instance:get_children(), {})
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("disconnect works", function()
|
||||||
|
local child = make_widget(nil)
|
||||||
|
local parent = make_widget({
|
||||||
|
make_child(child, 2, 5, cairo.Matrix.create_translate(10, 0))
|
||||||
|
})
|
||||||
|
|
||||||
|
local extra_arg = {}
|
||||||
|
local child_redraws, child_layouts = 0, 0
|
||||||
|
local parent_redraws, parent_layouts = 0, 0
|
||||||
|
local function redraw(arg, extra)
|
||||||
|
assert.is.equal(extra_arg, extra)
|
||||||
|
if arg:get_widget() == child then
|
||||||
|
child_redraws = child_redraws + 1
|
||||||
|
elseif arg:get_widget() == parent then
|
||||||
|
parent_redraws = parent_redraws + 1
|
||||||
|
else
|
||||||
|
error("Unknown widget")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local function layout(arg, extra)
|
||||||
|
assert.is.equal(extra_arg, extra)
|
||||||
|
if arg:get_widget() == child then
|
||||||
|
child_layouts = child_layouts + 1
|
||||||
|
elseif arg:get_widget() == parent then
|
||||||
|
parent_layouts = parent_layouts + 1
|
||||||
|
else
|
||||||
|
error("Unknown widget")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local context = {}
|
||||||
|
local instance = hierarchy.new(context, parent, 15, 20, redraw, layout, extra_arg)
|
||||||
|
|
||||||
|
-- There should be a connection
|
||||||
|
parent:emit_signal("widget::redraw_needed")
|
||||||
|
assert.is.same({ 0, 0, 1, 0 }, { child_redraws, child_layouts, parent_redraws, parent_layouts })
|
||||||
|
child:emit_signal("widget::redraw_needed")
|
||||||
|
assert.is.same({ 1, 0, 1, 0 }, { child_redraws, child_layouts, parent_redraws, parent_layouts })
|
||||||
|
child:emit_signal("widget::layout_changed")
|
||||||
|
assert.is.same({ 1, 1, 1, 0 }, { child_redraws, child_layouts, parent_redraws, parent_layouts })
|
||||||
|
parent:emit_signal("widget::layout_changed")
|
||||||
|
assert.is.same({ 1, 1, 1, 1 }, { child_redraws, child_layouts, parent_redraws, parent_layouts })
|
||||||
|
|
||||||
|
-- Garbage-collect the hierarchy
|
||||||
|
instance = nil
|
||||||
|
collectgarbage("collect")
|
||||||
|
|
||||||
|
-- No connections should be left
|
||||||
|
parent:emit_signal("widget::redraw_needed")
|
||||||
|
child:emit_signal("widget::redraw_needed")
|
||||||
|
child:emit_signal("widget::layout_changed")
|
||||||
|
parent:emit_signal("widget::layout_changed")
|
||||||
|
assert.is.same({ 1, 1, 1, 1 }, { child_redraws, child_layouts, parent_redraws, parent_layouts })
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("children", function()
|
||||||
|
local child, intermediate, parent
|
||||||
|
local hierarchy_child, hierarchy_intermediate, hierarchy_parent
|
||||||
|
before_each(function()
|
||||||
|
child = make_widget(nil)
|
||||||
|
intermediate = make_widget({
|
||||||
|
make_child(child, 10, 20, cairo.Matrix.create_translate(0, 5))
|
||||||
|
})
|
||||||
|
parent = make_widget({
|
||||||
|
make_child(intermediate, 5, 2, cairo.Matrix.create_translate(4, 0))
|
||||||
|
})
|
||||||
|
|
||||||
|
local function nop() end
|
||||||
|
local context = {}
|
||||||
|
hierarchy_parent = hierarchy.new(context, parent, 15, 16, nop, nop)
|
||||||
|
|
||||||
|
-- This also tests get_children
|
||||||
|
local children = hierarchy_parent:get_children()
|
||||||
|
assert.is.equal(#children, 1)
|
||||||
|
hierarchy_intermediate = children[1]
|
||||||
|
|
||||||
|
local children = hierarchy_intermediate:get_children()
|
||||||
|
assert.is.equal(#children, 1)
|
||||||
|
hierarchy_child = children[1]
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("get_parent", function()
|
||||||
|
assert.is.equal(hierarchy_child:get_parent(), hierarchy_intermediate)
|
||||||
|
assert.is.equal(hierarchy_intermediate:get_parent(), hierarchy_parent)
|
||||||
|
assert.is_nil(hierarchy_parent:get_parent())
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("get_widget", function()
|
||||||
|
assert.is.equal(hierarchy_child:get_widget(), child)
|
||||||
|
assert.is.equal(hierarchy_intermediate:get_widget(), intermediate)
|
||||||
|
assert.is.equal(hierarchy_parent:get_widget(), parent)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("get_matrix_to_parent", function()
|
||||||
|
assert.is_true(matrix.equals(hierarchy_child:get_matrix_to_parent(), cairo.Matrix.create_translate(0, 5)))
|
||||||
|
assert.is_true(matrix.equals(hierarchy_intermediate:get_matrix_to_parent(), cairo.Matrix.create_translate(4, 0)))
|
||||||
|
assert.is_true(matrix.equals(hierarchy_parent:get_matrix_to_parent(), cairo.Matrix.create_identity()))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("get_matrix_to_device", function()
|
||||||
|
assert.is_true(matrix.equals(hierarchy_child:get_matrix_to_device(), cairo.Matrix.create_translate(4, 5)))
|
||||||
|
assert.is_true(matrix.equals(hierarchy_intermediate:get_matrix_to_device(), cairo.Matrix.create_translate(4, 0)))
|
||||||
|
assert.is_true(matrix.equals(hierarchy_parent:get_matrix_to_device(), cairo.Matrix.create_identity()))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("get_matrix_from_parent", function()
|
||||||
|
assert.is_true(matrix.equals(hierarchy_child:get_matrix_from_parent(), cairo.Matrix.create_translate(0, -5)))
|
||||||
|
assert.is_true(matrix.equals(hierarchy_intermediate:get_matrix_from_parent(), cairo.Matrix.create_translate(-4, 0)))
|
||||||
|
assert.is_true(matrix.equals(hierarchy_parent:get_matrix_from_parent(), cairo.Matrix.create_identity()))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("get_matrix_from_device", function()
|
||||||
|
assert.is_true(matrix.equals(hierarchy_child:get_matrix_from_device(), cairo.Matrix.create_translate(-4, -5)))
|
||||||
|
assert.is_true(matrix.equals(hierarchy_intermediate:get_matrix_from_device(), cairo.Matrix.create_translate(-4, 0)))
|
||||||
|
assert.is_true(matrix.equals(hierarchy_parent:get_matrix_from_device(), cairo.Matrix.create_identity()))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("get_draw_extents", function()
|
||||||
|
assert.is.same({ hierarchy_child:get_draw_extents() }, { 0, 0, 10, 20 })
|
||||||
|
assert.is.same({ hierarchy_intermediate:get_draw_extents() }, { 0, 0, 10, 25 })
|
||||||
|
assert.is.same({ hierarchy_parent:get_draw_extents() }, { 0, 0, 15, 25 })
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("get_size", function()
|
||||||
|
assert.is.same({ hierarchy_child:get_size() }, { 10, 20 })
|
||||||
|
assert.is.same({ hierarchy_intermediate:get_size() }, { 5, 2 })
|
||||||
|
assert.is.same({ hierarchy_parent:get_size() }, { 15, 16 })
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("find_differences", function()
|
||||||
|
local child, intermediate, parent
|
||||||
|
local instance
|
||||||
|
local function nop() end
|
||||||
|
before_each(function()
|
||||||
|
child = make_widget(nil)
|
||||||
|
intermediate = make_widget({
|
||||||
|
make_child(child, 10, 20, cairo.Matrix.create_translate(0, 5))
|
||||||
|
})
|
||||||
|
parent = make_widget({
|
||||||
|
make_child(intermediate, 5, 2, cairo.Matrix.create_translate(4, 0))
|
||||||
|
})
|
||||||
|
|
||||||
|
local context = {}
|
||||||
|
instance = hierarchy.new(context, parent, 15, 16, nop, nop)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("No difference", function()
|
||||||
|
local context = {}
|
||||||
|
local instance2 = hierarchy.new(context, parent, 15, 16, nop, nop)
|
||||||
|
local region = instance:find_differences(instance2)
|
||||||
|
assert.is.equal(region:num_rectangles(), 0)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("child moved", function()
|
||||||
|
intermediate.layout = function()
|
||||||
|
return { make_child(child, 10, 20, cairo.Matrix.create_translate(0, 4)) }
|
||||||
|
end
|
||||||
|
local context = {}
|
||||||
|
local instance2 = hierarchy.new(context, parent, 15, 16, nop, nop)
|
||||||
|
local region = instance:find_differences(instance2)
|
||||||
|
assert.is.equal(region:num_rectangles(), 1)
|
||||||
|
local rect = region:get_rectangle(0)
|
||||||
|
-- The widget drew to 4, 5, 10, 20 before and 4, 4, 10, 20 after
|
||||||
|
assert.is.same({ rect.x, rect.y, rect.width, rect.height }, { 4, 4, 10, 21 })
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
Loading…
Reference in New Issue