MovableContainer: add support for anchoring initial position

When passing as 'anchor' a Geom object (ie. a widget dimen
or a ges.pos), or a function returning such an object,
a MovableContainer will be initially positionned near this
point/widget, instead of being centered on the screen.
Allow that for ButtonDialog and ButtonDialogTitle, so we
can make them behave as context menus (ie. for a titlebar
top left/right icons).
reviewable/pr10228/r4
poire-z 1 year ago
parent 25a7ca9a60
commit 8036ca0d56

@ -88,6 +88,7 @@ function ButtonDialog:init()
end
self.movable = MovableContainer:new{
alpha = self.alpha,
anchor = self.anchor,
FrameContainer:new{
ButtonTable:new{
buttons = self.buttons,

@ -88,6 +88,7 @@ function ButtonDialogTitle:init()
dimen = Screen:getSize(),
ignore_if_over = "height",
MovableContainer:new{
anchor = self.anchor,
FrameContainer:new{
VerticalGroup:new{
align = "center",

@ -13,6 +13,7 @@ position, Hold will toggle between full opacity and 0.7 transparency.
This container's content is expected to not change its width and height.
]]
local BD = require("ui/bidi")
local Blitbuffer = require("ffi/blitbuffer")
local Device = require("device")
local Geom = require("ui/geometry")
@ -35,6 +36,12 @@ local MovableContainer = InputContainer:extend{
-- Events to ignore (ie: ignore_events={"hold", "hold_release"})
ignore_events = nil,
-- Initial position can be set related to an existing widget
-- 'anchor' should be a Geom object (a widget's 'dimen', or a point), and
-- can be a function returning that object
anchor = nil,
_anchor_ensured = nil,
-- Current move offset (use getMovedOffset()/setMovedOffset() to access them)
_moved_offset_x = 0,
_moved_offset_y = 0,
@ -100,6 +107,59 @@ function MovableContainer:setMovedOffset(offset_point)
end
end
function MovableContainer:ensureAnchor(x, y)
local anchor_dimen = self.anchor
local prefers_pop_down
if type(self.anchor) == "function" then
anchor_dimen, prefers_pop_down = self.anchor()
end
if not anchor_dimen then
return
end
-- We try to find the best way to draw our content, depending on
-- the size of the content and the space available on the screen.
local content_w, content_h = self.dimen.w, self.dimen.h
local screen_w, screen_h = Screen:getWidth(), Screen:getHeight()
local left, top
if BD.mirroredUILayout() then
left = anchor_dimen.x + anchor_dimen.w - content_w
else
left = anchor_dimen.x
end
if left < 0 then
left = 0
elseif left + content_w > screen_w then
left = screen_w - content_w
end
-- We prefer displaying above the anchor if there is room (so it looks like popping up)
-- except if anchor() returned prefers_pop_down
local h_remaining_if_above = anchor_dimen.y - content_h
local h_remaining_if_below = screen_h - (anchor_dimen.y + anchor_dimen.h + content_h)
if h_remaining_if_above >= 0 and not prefers_pop_down then
-- Enough room above the anchor
top = anchor_dimen.y - content_h
elseif h_remaining_if_below >= 0 then
-- Enough room below the anchor
top = anchor_dimen.y + anchor_dimen.h
elseif h_remaining_if_above >= 0 then
-- Enough room above the anchor
top = anchor_dimen.y - content_h
else -- both negative
if h_remaining_if_above >= h_remaining_if_below then
top = 0
else
top = screen_h - content_h
end
end
-- Ensure we show the top if we would overflow
if top < 0 then
top = 0
end
-- Make the initial offsets so that we display at left/top
self._moved_offset_x = left - x
self._moved_offset_y = top - y
end
function MovableContainer:paintTo(bb, x, y)
if self[1] == nil then
return
@ -112,6 +172,12 @@ function MovableContainer:paintTo(bb, x, y)
self._orig_x = x
self._orig_y = y
-- If there is a widget passed as anchor, we need to set our initial position
-- related to it. After that, we allow it to be moved like any other movable.
if self.anchor and not self._anchor_ensured then
self:ensureAnchor(x, y)
self._anchor_ensured = true
end
-- We just need to shift painting by our _moved_offset_x/y
self.dimen.x = x + self._moved_offset_x
self.dimen.y = y + self._moved_offset_y

Loading…
Cancel
Save