2016-06-26 00:53:08 +00:00
|
|
|
--[[--
|
2021-12-01 11:37:18 +00:00
|
|
|
Widget that shows a confirmation alert with a message and Cancel/OK buttons.
|
2016-06-26 00:53:08 +00:00
|
|
|
|
|
|
|
Example:
|
|
|
|
|
|
|
|
UIManager:show(ConfirmBox:new{
|
|
|
|
text = _("Save the document?"),
|
|
|
|
ok_text = _("Save"), -- ok_text defaults to _("OK")
|
|
|
|
ok_callback = function()
|
|
|
|
-- save document
|
|
|
|
end,
|
|
|
|
})
|
|
|
|
|
2017-04-04 13:31:13 +00:00
|
|
|
It is strongly recommended to set a custom `ok_text` describing the action to be
|
|
|
|
confirmed, as demonstrated in the example above. No ok_text should be specified
|
|
|
|
if the resulting phrase would be longer than three words.
|
|
|
|
|
2016-06-26 00:53:08 +00:00
|
|
|
]]
|
|
|
|
|
2017-04-29 08:38:09 +00:00
|
|
|
local Blitbuffer = require("ffi/blitbuffer")
|
|
|
|
local ButtonTable = require("ui/widget/buttontable")
|
2013-10-18 20:38:07 +00:00
|
|
|
local CenterContainer = require("ui/widget/container/centercontainer")
|
2017-04-29 08:38:09 +00:00
|
|
|
local Device = require("device")
|
|
|
|
local Font = require("ui/font")
|
2014-07-02 14:52:17 +00:00
|
|
|
local FrameContainer = require("ui/widget/container/framecontainer")
|
2017-04-29 08:38:09 +00:00
|
|
|
local Geom = require("ui/geometry")
|
|
|
|
local GestureRange = require("ui/gesturerange")
|
2014-07-02 14:52:17 +00:00
|
|
|
local HorizontalGroup = require("ui/widget/horizontalgroup")
|
2017-04-29 08:38:09 +00:00
|
|
|
local HorizontalSpan = require("ui/widget/horizontalspan")
|
2020-12-19 11:18:30 +00:00
|
|
|
local IconWidget = require("ui/widget/iconwidget")
|
2017-04-29 08:38:09 +00:00
|
|
|
local InputContainer = require("ui/widget/container/inputcontainer")
|
2018-01-29 20:27:24 +00:00
|
|
|
local MovableContainer = require("ui/widget/container/movablecontainer")
|
2017-09-13 14:56:20 +00:00
|
|
|
local Size = require("ui/size")
|
2013-10-22 15:11:31 +00:00
|
|
|
local TextBoxWidget = require("ui/widget/textboxwidget")
|
|
|
|
local UIManager = require("ui/uimanager")
|
2017-04-29 08:38:09 +00:00
|
|
|
local VerticalGroup = require("ui/widget/verticalgroup")
|
2017-10-08 15:53:25 +00:00
|
|
|
local VerticalSpan = require("ui/widget/verticalspan")
|
2013-10-22 15:11:31 +00:00
|
|
|
local _ = require("gettext")
|
2022-05-23 11:52:52 +00:00
|
|
|
local Input = Device.input
|
2017-04-29 08:38:09 +00:00
|
|
|
local Screen = Device.screen
|
2012-06-10 15:52:09 +00:00
|
|
|
|
Clarify our OOP semantics across the codebase (#9586)
Basically:
* Use `extend` for class definitions
* Use `new` for object instantiations
That includes some minor code cleanups along the way:
* Updated `Widget`'s docs to make the semantics clearer.
* Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283)
* Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass).
* Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events.
* Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier.
* Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references.
* ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak).
* Terminal: Make sure the shell is killed on plugin teardown.
* InputText: Fix Home/End/Del physical keys to behave sensibly.
* InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...).
* OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of.
* ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed!
* Kobo: Minor code cleanups.
2022-10-06 00:14:48 +00:00
|
|
|
local ConfirmBox = InputContainer:extend{
|
2014-10-30 08:01:01 +00:00
|
|
|
modal = true,
|
2021-08-12 22:45:10 +00:00
|
|
|
keep_dialog_open = false,
|
2014-03-13 13:52:43 +00:00
|
|
|
text = _("no text"),
|
2017-04-29 08:38:09 +00:00
|
|
|
face = Font:getFace("infofont"),
|
2022-11-25 09:18:54 +00:00
|
|
|
icon = "notice-question",
|
2014-03-13 13:52:43 +00:00
|
|
|
ok_text = _("OK"),
|
|
|
|
cancel_text = _("Cancel"),
|
|
|
|
ok_callback = function() end,
|
|
|
|
cancel_callback = function() end,
|
2017-05-16 09:11:11 +00:00
|
|
|
other_buttons = nil,
|
2021-08-12 22:45:10 +00:00
|
|
|
other_buttons_first = false, -- set to true to place other buttons above Cancel-OK row
|
2017-09-13 14:56:20 +00:00
|
|
|
margin = Size.margin.default,
|
|
|
|
padding = Size.padding.default,
|
2017-10-08 15:53:25 +00:00
|
|
|
dismissable = true, -- set to false if any button callback is required
|
2021-01-01 13:34:55 +00:00
|
|
|
flush_events_on_show = false, -- set to true when it might be displayed after
|
|
|
|
-- some processing, to avoid accidental dismissal
|
2012-06-10 15:52:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function ConfirmBox:init()
|
2017-10-08 15:53:25 +00:00
|
|
|
if self.dismissable then
|
|
|
|
if Device:isTouchDevice() then
|
|
|
|
self.ges_events.TapClose = {
|
|
|
|
GestureRange:new{
|
|
|
|
ges = "tap",
|
|
|
|
range = Geom:new{
|
|
|
|
x = 0, y = 0,
|
|
|
|
w = Screen:getWidth(),
|
|
|
|
h = Screen:getHeight(),
|
|
|
|
}
|
2017-01-15 20:55:06 +00:00
|
|
|
}
|
|
|
|
}
|
2017-10-08 15:53:25 +00:00
|
|
|
end
|
|
|
|
if Device:hasKeys() then
|
2022-10-27 00:01:51 +00:00
|
|
|
self.key_events.Close = { { Device.input.group.Back } }
|
2017-10-08 15:53:25 +00:00
|
|
|
end
|
2017-01-15 20:55:06 +00:00
|
|
|
end
|
2020-02-24 14:25:06 +00:00
|
|
|
local text_widget = TextBoxWidget:new{
|
|
|
|
text = self.text,
|
|
|
|
face = self.face,
|
2021-09-25 08:51:58 +00:00
|
|
|
width = math.floor(math.min(Screen:getWidth(), Screen:getHeight()) * 2/3),
|
2020-02-24 14:25:06 +00:00
|
|
|
}
|
2014-07-02 14:52:17 +00:00
|
|
|
local content = HorizontalGroup:new{
|
|
|
|
align = "center",
|
2020-12-19 11:18:30 +00:00
|
|
|
IconWidget:new{
|
2022-11-25 09:18:54 +00:00
|
|
|
icon = self.icon,
|
|
|
|
alpha = true,
|
2014-07-02 14:52:17 +00:00
|
|
|
},
|
2017-09-13 14:56:20 +00:00
|
|
|
HorizontalSpan:new{ width = Size.span.horizontal_default },
|
2020-02-24 14:25:06 +00:00
|
|
|
text_widget,
|
2014-03-13 13:52:43 +00:00
|
|
|
}
|
2017-05-16 09:11:11 +00:00
|
|
|
|
|
|
|
local buttons = {{
|
|
|
|
text = self.cancel_text,
|
|
|
|
callback = function()
|
|
|
|
self.cancel_callback()
|
|
|
|
UIManager:close(self)
|
|
|
|
end,
|
|
|
|
}, {
|
|
|
|
text = self.ok_text,
|
|
|
|
callback = function()
|
|
|
|
self.ok_callback()
|
2021-08-12 22:45:10 +00:00
|
|
|
if self.keep_dialog_open then return end
|
2017-05-16 09:11:11 +00:00
|
|
|
UIManager:close(self)
|
|
|
|
end,
|
|
|
|
},}
|
2020-02-24 14:25:06 +00:00
|
|
|
buttons = { buttons } -- single row
|
2017-05-16 09:11:11 +00:00
|
|
|
|
|
|
|
if self.other_buttons ~= nil then
|
2020-02-24 14:25:06 +00:00
|
|
|
-- additional rows
|
2021-08-12 22:45:10 +00:00
|
|
|
local rownum = self.other_buttons_first and 0 or 1
|
|
|
|
for i, buttons_row in ipairs(self.other_buttons) do
|
2020-02-24 14:25:06 +00:00
|
|
|
local row = {}
|
2021-08-12 22:45:10 +00:00
|
|
|
table.insert(buttons, rownum + i, row)
|
2020-02-24 14:25:06 +00:00
|
|
|
for ___, button in ipairs(buttons_row) do
|
|
|
|
table.insert(row, {
|
|
|
|
text = button.text,
|
|
|
|
callback = function()
|
|
|
|
if button.callback ~= nil then
|
|
|
|
button.callback()
|
|
|
|
end
|
2021-08-12 22:45:10 +00:00
|
|
|
if self.keep_dialog_open then return end
|
2020-02-24 14:25:06 +00:00
|
|
|
UIManager:close(self)
|
|
|
|
end,
|
|
|
|
})
|
|
|
|
end
|
2017-05-16 09:11:11 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-07-02 14:52:17 +00:00
|
|
|
local button_table = ButtonTable:new{
|
|
|
|
width = content:getSize().w,
|
|
|
|
button_font_face = "cfont",
|
|
|
|
button_font_size = 20,
|
2020-02-24 14:25:06 +00:00
|
|
|
buttons = buttons,
|
2014-07-02 14:52:17 +00:00
|
|
|
zero_sep = true,
|
2014-05-01 10:37:12 +00:00
|
|
|
show_parent = self,
|
2014-03-13 13:52:43 +00:00
|
|
|
}
|
2012-06-10 15:52:09 +00:00
|
|
|
|
2020-02-24 14:25:06 +00:00
|
|
|
local frame = FrameContainer:new{
|
|
|
|
background = Blitbuffer.COLOR_WHITE,
|
|
|
|
margin = self.margin,
|
2020-12-19 07:25:00 +00:00
|
|
|
radius = Size.radius.window,
|
2020-02-24 14:25:06 +00:00
|
|
|
padding = self.padding,
|
|
|
|
padding_bottom = 0, -- no padding below buttontable
|
|
|
|
VerticalGroup:new{
|
|
|
|
align = "left",
|
|
|
|
content,
|
|
|
|
-- Add same vertical space after than before content
|
|
|
|
VerticalSpan:new{ width = self.margin + self.padding },
|
|
|
|
button_table,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self.movable = MovableContainer:new{
|
|
|
|
frame,
|
|
|
|
}
|
2014-03-13 13:52:43 +00:00
|
|
|
self[1] = CenterContainer:new{
|
|
|
|
dimen = Screen:getSize(),
|
2020-02-24 14:25:06 +00:00
|
|
|
self.movable,
|
2014-03-13 13:52:43 +00:00
|
|
|
}
|
2020-02-24 14:25:06 +00:00
|
|
|
|
|
|
|
-- Reduce font size until widget fit screen height if needed
|
|
|
|
local cur_size = frame:getSize()
|
|
|
|
if cur_size and cur_size.h > 0.95 * Screen:getHeight() then
|
|
|
|
local orig_font = text_widget.face.orig_font
|
|
|
|
local orig_size = text_widget.face.orig_size
|
|
|
|
local real_size = text_widget.face.size
|
|
|
|
if orig_size > 10 then -- don't go too small
|
|
|
|
while true do
|
|
|
|
orig_size = orig_size - 1
|
|
|
|
self.face = Font:getFace(orig_font, orig_size)
|
|
|
|
-- scaleBySize() in Font:getFace() may give the same
|
|
|
|
-- real font size even if we decreased orig_size,
|
|
|
|
-- so check we really got a smaller real font size
|
|
|
|
if self.face.size < real_size then
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
-- re-init this widget
|
|
|
|
self:free()
|
|
|
|
self:init()
|
|
|
|
end
|
|
|
|
end
|
2014-12-01 14:39:41 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function ConfirmBox:onShow()
|
|
|
|
UIManager:setDirty(self, function()
|
2015-04-26 18:07:17 +00:00
|
|
|
return "ui", self[1][1].dimen
|
2014-12-01 14:39:41 +00:00
|
|
|
end)
|
2021-01-01 13:34:55 +00:00
|
|
|
if self.flush_events_on_show then
|
AutoSuspend: Don't send LeaveStandby events from a zombie plugin instance (#9124)
Long story short: the LeaveStandby event is sent via `tickAfterNext`, so if we tear down the plugin right after calling it (in this case, that means that the very input event that wakes the device up from suspend is one that kills ReaderUI or FileManager), what's in UIManager's task queue isn't the actual function, but the anonymous nextTick wrapper constructed by `tickAfterNext` (c.f.,
https://github.com/koreader/koreader/issues/9112#issuecomment-1133999385).
Tweak `UIManager:tickAfterNext` to return a reference to said wrapper, so that we can store it and unschedule that one, too, in `AutoSuspend:onCloseWidget`.
Fix #9112 (many thanks to [@boredhominid](https://github.com/boredhominid) for his help in finding a repro for this ;)).
Re: #8638, as the extra debugging facilities (i.e., ebb81b98451e2a8f54c46f51e861c19fdfb40499) added during testing might help pinpoint the root issue for that one, too.
Also includes a minor simplification to `UIManager:_checkTasks`, and various other task queue related codepaths (e.g., `WakeupMgr`) ;).
2022-05-25 21:36:41 +00:00
|
|
|
-- Discard queued and upcoming input events to avoid accidental dismissal
|
2022-05-23 11:52:52 +00:00
|
|
|
Input:inhibitInputUntil(true)
|
2021-01-01 13:34:55 +00:00
|
|
|
end
|
2014-12-01 14:39:41 +00:00
|
|
|
end
|
2014-07-02 14:52:17 +00:00
|
|
|
|
2014-12-01 14:39:41 +00:00
|
|
|
function ConfirmBox:onCloseWidget()
|
|
|
|
UIManager:setDirty(nil, function()
|
Tame some ButtonTable users into re-using Buttontable instances if possible (#7166)
* QuickDictLookup, ImageViewer, NumberPicker: Smarter `update` that will re-use most of the widget's layout instead of re-instantiating all the things.
* SpinWidget/DoubleSpinWidget: The NumberPicker change above renders a hack to preserve alpha on these widgets almost unnecessary. Also fixed said hack to also apply to the center, value button.
* Button: Don't re-instantiate the frame in setText/setIcon when unnecessary (e.g., no change at all, or no layout change).
* Button: Add a refresh method that repaints and refreshes a *specific* Button (provided it's been painted once) all on its lonesome.
* ConfigDialog: Free everything that's going to be re-instatiated on update
* A few more post #7118 fixes:
* SkimTo: Always flag the chapter nav buttons as vsync
* Button: Fix the highlight on rounded buttons when vsync is enabled (e.g., it's now entirely visible, instead of showing a weird inverted corner glitch).
* Some more heuristic tweaks in Menu/TouchMenu/Button/IconButton
* ButtonTable: fix the annoying rounding issue I'd noticed in #7054 ;).
* Enable dithering in TextBoxWidget (e.g., in the Wikipedia full view). This involved moving the HW dithering align fixup to base, where it always ought to have been ;).
* Switch a few widgets that were using "partial" on close to "ui", or, more rarely, "flashui". The intent being to limit "partial" purely to the Reader, because it has a latency cost when mixed with other refreshes, which happens often enough in UI ;).
* Minor documentation tweaks around UIManager's `setDirty` to reflect that change.
* ReaderFooter: Force a footer repaint on resume if it is visible (otherwise, just update it).
* ReaderBookmark: In the same vein, don't repaint an invisible footer on bookmark count changes.
2021-01-28 23:20:15 +00:00
|
|
|
return "ui", self[1][1].dimen
|
2014-12-01 14:39:41 +00:00
|
|
|
end)
|
2012-06-10 15:52:09 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function ConfirmBox:onClose()
|
2017-01-15 20:55:06 +00:00
|
|
|
-- Call cancel_callback, parent may expect a choice
|
|
|
|
self.cancel_callback()
|
2014-03-13 13:52:43 +00:00
|
|
|
UIManager:close(self)
|
|
|
|
return true
|
2012-06-10 15:52:09 +00:00
|
|
|
end
|
|
|
|
|
2017-01-15 20:55:06 +00:00
|
|
|
function ConfirmBox:onTapClose(arg, ges)
|
|
|
|
if ges.pos:notIntersectWith(self[1][1].dimen) then
|
|
|
|
self:onClose()
|
|
|
|
end
|
2020-01-02 13:00:57 +00:00
|
|
|
-- Don't let it propagate to underlying widgets
|
|
|
|
return true
|
2017-01-15 20:55:06 +00:00
|
|
|
end
|
|
|
|
|
2013-10-18 20:38:07 +00:00
|
|
|
return ConfirmBox
|