Button: Better handling of translucent MovableContainer (#7223)

* DictQuickLookup: Preserve alpha when switching dict, and scrolling
inside a dict.

* Start moving the NumberPicker alpha hack to Button itself

This makes handling flash_ui easier and saner, avoiding flickering.

* Handle the transparency hack entirely from within Button

* Murder the now unnecessary NumberPicker update_callback hack

* Tweak comments

* And the Button handling made that redundant, too

* Squish debug print

* More comment tweaks

* Reset transparency on scrolling instead of rpeserving it

* Reset alpha when switching dictionaries

* Simplify the pre/post callbakc transparency state handling

And explain why we need to care.

* Give a named reference to ButtonDialog's MovableContainer, so the Button
alpha hack behaves with it

* Document the "self.movable" convention

* Amend that comment a bit

e.g., we don't care much about MultiConfirmBox'w MpvableContainer, as
any button action will close it.

* And make SkimTo's MovableContainer accessible so that Button can grok
that it's translucent
pull/7235/head
NiLuJe 3 years ago committed by GitHub
parent 4a89c93290
commit 285fc75aa7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -316,6 +316,10 @@ function SkimToWidget:init()
vertical_group_down, vertical_group_down,
} }
} }
self.movable = MovableContainer:new{
-- alpha = 0.8,
self.skimto_frame,
}
self[1] = WidgetContainer:new{ self[1] = WidgetContainer:new{
align = "center", align = "center",
dimen =Geom:new{ dimen =Geom:new{
@ -323,10 +327,7 @@ function SkimToWidget:init()
w = self.screen_width, w = self.screen_width,
h = self.screen_height, h = self.screen_height,
}, },
MovableContainer:new{ self.movable,
-- alpha = 0.8,
self.skimto_frame,
}
} }
if Device:hasDPad() then if Device:hasDPad() then

@ -674,6 +674,10 @@ to, among other things, flag the right widget as setDirty (c.f., those pesky deb
This is why you often see stuff doing, when instantiating a new widget, FancyWidget:new{ show_parent = self.show_parent or self }; This is why you often see stuff doing, when instantiating a new widget, FancyWidget:new{ show_parent = self.show_parent or self };
meaning, if I'm already a subwidget, cascade my parent, otherwise, it means I'm a window-level widget, so cascade myself as that widget's parent ;). meaning, if I'm already a subwidget, cascade my parent, otherwise, it means I'm a window-level widget, so cascade myself as that widget's parent ;).
Another convention (that a few things rely on) is naming a (persistent) MovableContainer wrapping a full widget `movable`, accessible as an instance field.
This is useful when it's used for transparency purposes, which, e.g., Button relies on to handle highlighting inside a transparent widget properly,
by checking if self.show_parent.movable exists and is currently translucent ;).
@usage @usage
UIManager:setDirty(self.widget, "partial") UIManager:setDirty(self.widget, "partial")

@ -234,6 +234,13 @@ function Button:showHide(show)
end end
function Button:onTapSelectButton() function Button:onTapSelectButton()
-- NOTE: We have a few tricks up our sleeve in case our parent is inside a translucent MovableContainer...
local was_translucent = self.show_parent and self.show_parent.movable and self.show_parent.movable.alpha
-- We make a distinction between transparency pre- and post- callback, because if a widget *was* transparent,
-- but no longer is post-callback, we want to ensure that we refresh the *full* container,
-- instead of just the button's frame, in order to avoid leaving bits of the widget transparent ;).
local is_translucent = was_translucent
if self.enabled and self.callback then if self.enabled and self.callback then
if G_reader_settings:isFalse("flash_ui") then if G_reader_settings:isFalse("flash_ui") then
self.callback() self.callback()
@ -276,7 +283,13 @@ function Button:onTapSelectButton()
UIManager:forceRePaint() -- Ensures we have a chance to see the highlight UIManager:forceRePaint() -- Ensures we have a chance to see the highlight
end end
self.callback() self.callback()
UIManager:forceRePaint() -- Ensures whatever the callback wanted to paint will be shown *now*... -- Check if the callback reset transparency...
is_translucent = was_translucent and self.show_parent.movable.alpha
-- We don't want to fence the callback when we're *still* translucent, because we want a *single* refresh post-callback *and* post-unhighlight,
-- in order to avoid flickering.
if not is_translucent then
UIManager:forceRePaint() -- Ensures whatever the callback wanted to paint will be shown *now*...
end
if self.vsync then if self.vsync then
-- NOTE: This is mainly useful when the callback caused a REAGL update that we do not explicitly fence already, -- NOTE: This is mainly useful when the callback caused a REAGL update that we do not explicitly fence already,
-- (i.e., Kobo Mk. 7). -- (i.e., Kobo Mk. 7).
@ -346,6 +359,16 @@ function Button:onTapSelectButton()
elseif type(self.tap_input_func) == "function" then elseif type(self.tap_input_func) == "function" then
self:onInput(self.tap_input_func()) self:onInput(self.tap_input_func())
end end
-- If our parent belongs to a translucent MovableContainer, repaint all the things to honor alpha without layering glitches,
-- and refresh the full container, because the widget might have inhibited its own setDirty call to avoid flickering (c.f., *SpinWidget).
if was_translucent then
-- If the callback reset the transparency, we only need to repaint our parent
UIManager:setDirty(is_translucent and "all" or self.show_parent, function()
return "ui", self.show_parent.movable.dimen
end)
end
if self.readonly ~= true then if self.readonly ~= true then
return true return true
end end

@ -37,9 +37,7 @@ function ButtonDialog:init()
} }
} }
end end
self[1] = CenterContainer:new{ self.movable = MovableContainer:new{
dimen = Screen:getSize(),
MovableContainer:new{
alpha = self.alpha, alpha = self.alpha,
FrameContainer:new{ FrameContainer:new{
ButtonTable:new{ ButtonTable:new{
@ -56,19 +54,23 @@ function ButtonDialog:init()
padding_top = 0, padding_top = 0,
padding_bottom = 0, padding_bottom = 0,
} }
} }
self[1] = CenterContainer:new{
dimen = Screen:getSize(),
self.movable,
} }
end end
function ButtonDialog:onShow() function ButtonDialog:onShow()
UIManager:setDirty(self, function() UIManager:setDirty(self, function()
return "ui", self[1][1].dimen return "ui", self.movable.dimen
end) end)
end end
function ButtonDialog:onCloseWidget() function ButtonDialog:onCloseWidget()
UIManager:setDirty(nil, function() UIManager:setDirty(nil, function()
return "flashui", self[1][1].dimen return "flashui", self.movable.dimen
end) end)
end end
@ -87,7 +89,7 @@ end
function ButtonDialog:paintTo(...) function ButtonDialog:paintTo(...)
InputContainer.paintTo(self, ...) InputContainer.paintTo(self, ...)
self.dimen = self[1][1].dimen -- FrameContainer self.dimen = self.movable.dimen
end end
return ButtonDialog return ButtonDialog

@ -828,14 +828,15 @@ function DictQuickLookup:update()
end end
end end
-- Reset alpha to avoid stacking transparency on top of the previous content. -- If we're translucent, reset alpha to make the new definition actually readable.
-- NOTE: This doesn't take care of the Scroll*Widget, which will preserve alpha on scroll, if self.movable.alpha then
-- leading to increasingly opaque and muddy text as half-transparent stuff gets stacked on top of each other... self.movable.alpha = nil
self.movable.alpha = nil -- And skip the setDirty, Button will handle it post-callback & post-unhighlight.
else
UIManager:setDirty(self, function() UIManager:setDirty(self, function()
return "partial", self.dict_frame.dimen return "partial", self.dict_frame.dimen
end) end)
end
end end
function DictQuickLookup:getInitialVisibleArea() function DictQuickLookup:getInitialVisibleArea()

@ -83,10 +83,6 @@ function DoubleSpinWidget:init()
end end
function DoubleSpinWidget:update() function DoubleSpinWidget:update()
-- This picker_update_callback will be redefined later.
-- It's a hack to restore transparency after a Button unhighlight in NumberPicker,
-- in case the MovableContainer was actually made transparent.
local picker_update_callback = function() end
local left_widget = NumberPickerWidget:new{ local left_widget = NumberPickerWidget:new{
show_parent = self, show_parent = self,
width = self.picker_width, width = self.picker_width,
@ -96,7 +92,6 @@ function DoubleSpinWidget:update()
value_step = self.left_step, value_step = self.left_step,
value_hold_step = self.left_hold_step, value_hold_step = self.left_hold_step,
wrap = false, wrap = false,
update_callback = function() picker_update_callback() end,
} }
local right_widget = NumberPickerWidget:new{ local right_widget = NumberPickerWidget:new{
show_parent = self, show_parent = self,
@ -107,7 +102,6 @@ function DoubleSpinWidget:update()
value_step = self.right_step, value_step = self.right_step,
value_hold_step = self.right_hold_step, value_hold_step = self.right_hold_step,
wrap = false, wrap = false,
update_callback = function() picker_update_callback() end,
} }
local left_vertical_group = VerticalGroup:new{ local left_vertical_group = VerticalGroup:new{
align = "center", align = "center",
@ -287,31 +281,11 @@ function DoubleSpinWidget:update()
}, },
self.movable, self.movable,
} }
UIManager:setDirty(self, function() -- If we're translucent, Button itself will handle that post-callback, in order to preserve alpha without flickering.
return "ui", self.widget_frame.dimen if not self.movable.alpha then
end) UIManager:setDirty(self, function()
picker_update_callback = function() return "ui", self.widget_frame.dimen
-- If we're actually transparent, force an alpha-aware repaint. end)
if self.movable.alpha then
if G_reader_settings:nilOrTrue("flash_ui") then
-- It's delayed to the next tick to actually catch a Button unhighlight.
UIManager:nextTick(function()
UIManager:setDirty("all", function()
return "ui", self.movable.dimen
end)
end)
else
-- This should only really be necessary for the up/down buttons here,
-- because they repaint the center value button & text, unlike said button,
-- which just pops up the VK.
-- On the upside, we shouldn't need to delay anything without flash_ui ;).
UIManager:setDirty("all", function()
return "ui", self.movable.dimen
end)
end
end
-- If we'd like to have the values auto-applied, uncomment this:
-- self.callback(left_widget:getValue(), right_widget:getValue())
end end
end end

@ -12,7 +12,6 @@ Example:
value_step = 1, value_step = 1,
value_hold_step = 4, value_hold_step = 4,
wrap = true, wrap = true,
update_callback = function() end,
} }
--]] --]]
@ -46,7 +45,6 @@ local NumberPickerWidget = InputContainer:new{
value_table = nil, value_table = nil,
value_index = nil, value_index = nil,
wrap = true, wrap = true,
update_callback = function() end,
-- in case we need calculate number of days in a given month and year -- in case we need calculate number of days in a given month and year
date_month = nil, date_month = nil,
date_year = nil, date_year = nil,
@ -169,7 +167,6 @@ function NumberPickerWidget:init()
}, },
}, },
} }
self.update_callback()
UIManager:show(input_dialog) UIManager:show(input_dialog)
input_dialog:onShowKeyboard() input_dialog:onShowKeyboard()
end end
@ -229,7 +226,6 @@ function NumberPickerWidget:update()
UIManager:setDirty(self.show_parent, function() UIManager:setDirty(self.show_parent, function()
return "ui", self.dimen return "ui", self.dimen
end) end)
self.update_callback()
end end
--[[-- --[[--

@ -119,9 +119,19 @@ function ScrollHtmlWidget:scrollToRatio(ratio)
self.htmlbox_widget:freeBb() self.htmlbox_widget:freeBb()
self.htmlbox_widget:_render() self.htmlbox_widget:_render()
UIManager:setDirty(self.dialog, function() -- If our dialog is currently wrapped in a MovableContainer and that container has been made translucent,
return "partial", self.dimen -- reset the alpha and refresh the whole thing, because we assume that a scroll means the user actually wants to
end) -- *read* the content, which is kinda hard on a nearly transparent widget ;).
if self.dialog.movable and self.dialog.movable.alpha then
self.dialog.movable.alpha = nil
UIManager:setDirty(self.dialog, function()
return "partial", self.dialog.movable.dimen
end)
else
UIManager:setDirty(self.dialog, function()
return "partial", self.dimen
end)
end
end end
function ScrollHtmlWidget:scrollText(direction) function ScrollHtmlWidget:scrollText(direction)
@ -147,9 +157,17 @@ function ScrollHtmlWidget:scrollText(direction)
self.htmlbox_widget:freeBb() self.htmlbox_widget:freeBb()
self.htmlbox_widget:_render() self.htmlbox_widget:_render()
UIManager:setDirty(self.dialog, function() -- Handle the container's alpha as above...
return "partial", self.dimen if self.dialog.movable and self.dialog.movable.alpha then
end) self.dialog.movable.alpha = nil
UIManager:setDirty(self.dialog, function()
return "partial", self.dialog.movable.dimen
end)
else
UIManager:setDirty(self.dialog, function()
return "partial", self.dimen
end)
end
end end
function ScrollHtmlWidget:onScrollText(arg, ges) function ScrollHtmlWidget:onScrollText(arg, ges)

@ -150,9 +150,17 @@ function ScrollTextWidget:updateScrollBar(is_partial)
if is_partial then if is_partial then
refreshfunc = "partial" refreshfunc = "partial"
end end
UIManager:setDirty(self.dialog, function() -- Reset transparency if the dialog's MovableContainer is currently translucent...
return refreshfunc, self.dimen if is_partial and self.dialog.movable and self.dialog.movable.alpha then
end) self.dialog.movable.alpha = nil
UIManager:setDirty(self.dialog, function()
return refreshfunc, self.dialog.movable.dimen
end)
else
UIManager:setDirty(self.dialog, function()
return refreshfunc, self.dimen
end)
end
if self.scroll_callback then if self.scroll_callback then
self.scroll_callback(low, high) self.scroll_callback(low, high)
end end

@ -78,10 +78,6 @@ function SpinWidget:init()
end end
function SpinWidget:update() function SpinWidget:update()
-- This picker_update_callback will be redefined later.
-- It's a hack to restore transparency after a Button unhighlight in NumberPicker,
-- in case the MovableContainer was actually made transparent.
local picker_update_callback = function() end
local value_widget = NumberPickerWidget:new{ local value_widget = NumberPickerWidget:new{
show_parent = self, show_parent = self,
width = math.floor(self.screen_width * 0.2), width = math.floor(self.screen_width * 0.2),
@ -93,7 +89,6 @@ function SpinWidget:update()
value_step = self.value_step, value_step = self.value_step,
value_hold_step = self.value_hold_step, value_hold_step = self.value_hold_step,
precision = self.precision, precision = self.precision,
update_callback = function() picker_update_callback() end,
} }
local value_group = HorizontalGroup:new{ local value_group = HorizontalGroup:new{
align = "center", align = "center",
@ -239,29 +234,11 @@ function SpinWidget:update()
}, },
self.movable, self.movable,
} }
UIManager:setDirty(self, function() -- If we're translucent, Button itself will handle that post-callback, in order to preserve alpha without flickering.
return "ui", self.spin_frame.dimen if not self.movable.alpha then
end) UIManager:setDirty(self, function()
picker_update_callback = function() return "ui", self.spin_frame.dimen
-- If we're actually transparent, force an alpha-aware repaint. end)
if self.movable.alpha then
if G_reader_settings:nilOrTrue("flash_ui") then
-- It's delayed to the next tick to actually catch a Button unhighlight.
UIManager:nextTick(function()
UIManager:setDirty("all", function()
return "ui", self.movable.dimen
end)
end)
else
-- This should only really be necessary for the up/down buttons here,
-- because they repaint the center value button & text, unlike said button,
-- which just pops up the VK.
-- On the upside, we shouldn't need to delay anything without flash_ui ;).
UIManager:setDirty("all", function()
return "ui", self.movable.dimen
end)
end
end
end end
end end

Loading…
Cancel
Save