Files
koreader-mirror/frontend/ui/widget/datetimewidget.lua
NiLuJe 62059f8d68 Misc: Get rid of the legacy defaults.lua globals (#9546)
* This removes support for the following deprecated constants: `DTAP_ZONE_FLIPPING`, `DTAP_ZONE_BOOKMARK`, `DCREREADER_CONFIG_DEFAULT_FONT_GAMMA`
* The "Advanced settings" panel now highlights modified values in bold (think about:config in Firefox ;)).
* LuaData: Isolate global table lookup shenanigans, and fix a few issues in unused-in-prod codepaths.
* CodeStyle: Require module locals for Lua/C modules, too.
* ScreenSaver: Actually garbage collect our widget on close (ScreenSaver itself is not an instantiated object).
* DateTimeWidget: Code cleanups to ensure child widgets can be GC'ed.
2022-09-28 01:10:50 +02:00

453 lines
14 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
--[[--
Widget for setting the date or time.
Example for input a time:
local DateTimeWidget = require("ui/widget/datetimewidget")
local @{gettext|_} = require("gettext")
local time_widget = DateTimeWidget:new{
hour = 10,
min = 30,
ok_text = _("Set time"),
title_text = _("Set time"),
info_text = _("Some information"),
callback = function(time)
-- use time.hour and time.min here
end
}
UIManager:show(time_widget)
Example for input a date:
local DateTimeWidget = require("ui/widget/datetimewidget")
local @{gettext|_} = require("gettext")
local date_widget = DateTimeWidget:new{
year = 2021,
month = 12,
day = 31,
ok_text = _("Set date"),
title_text = _("Set date"),
callback = function(time)
-- use time.year, time.month, time.day here
end
}
UIManager:show(date_widget)
Example to input a duration in days, hours and minutes:
local DateTimeWidget = require("ui/widget/datetimewidget")
local @{gettext|_} = require("gettext")
local date_widget = DateTimeWidget:new{
day = 5,
hour = 12,
min = 0,
ok_text = _("Set"),
title_text = _("Set duration"),
callback = function(time)
-- use time.day, time.hour, time.min here
end
}
UIManager:show(date_widget)
--]]--
local Blitbuffer = require("ffi/blitbuffer")
local ButtonTable = require("ui/widget/buttontable")
local CenterContainer = require("ui/widget/container/centercontainer")
local Device = require("device")
local FocusManager = require("ui/widget/focusmanager")
local FrameContainer = require("ui/widget/container/framecontainer")
local Geom = require("ui/geometry")
local GestureRange = require("ui/gesturerange")
local Font = require("ui/font")
local HorizontalGroup = require("ui/widget/horizontalgroup")
local NumberPickerWidget = require("ui/widget/numberpickerwidget")
local Size = require("ui/size")
local TextWidget = require("ui/widget/textwidget")
local TitleBar = require("ui/widget/titlebar")
local UIManager = require("ui/uimanager")
local VerticalGroup = require("ui/widget/verticalgroup")
local WidgetContainer = require("ui/widget/container/widgetcontainer")
local _ = require("gettext")
local Screen = Device.screen
local T = require("ffi/util").template
local DateTimeWidget = FocusManager:extend{
title_face = Font:getFace("x_smalltfont"),
info_text = nil,
width = nil,
height = nil,
ok_text = _("Apply"),
cancel_text = _("Close"),
-- Optional extra button on bottom
extra_text = nil,
extra_callback = nil,
}
function DateTimeWidget:init()
self.nb_pickers = 0
if self.year then
self.nb_pickers = self.nb_pickers + 1
end
if self.month then
self.nb_pickers = self.nb_pickers + 1
end
if self.day then
self.nb_pickers = self.nb_pickers + 1
end
if self.hour then
self.nb_pickers = self.nb_pickers + 1
end
if self.min then
self.nb_pickers = self.nb_pickers + 1
end
if self.sec then
self.nb_pickers = self.nb_pickers + 1
end
self.layout = {}
self.screen_width = Screen:getWidth()
self.screen_height = Screen:getHeight()
local width_scale_factor = 0.6
if self.nb_pickers == 3 then
width_scale_factor = 0.8
elseif self.nb_pickers == 4 then
width_scale_factor = 0.85
elseif self.nb_pickers >=5 then
width_scale_factor = 0.95
end
self.width = self.width or math.floor(math.min(self.screen_width, self.screen_height) * width_scale_factor)
if Device:hasKeys() then
self.key_events.Close = { {Device.input.group.Back}, doc = "close date widget" }
end
if Device:isTouchDevice() then
self.ges_events = {
TapClose = {
GestureRange:new{
ges = "tap",
range = Geom:new{
w = self.screen_width,
h = self.screen_height,
}
},
},
}
end
-- Actually the widget layout
self:createLayout()
end
function DateTimeWidget:createLayout()
-- Empty table w/ the methods we use NOP'ed
local dummy_widget = {}
function dummy_widget:free() end
function dummy_widget:getValue() end
function dummy_widget:update() end
-- The following calculation is stolen from NumberPickerWidget
local number_picker_widgets_width = math.floor(math.min(self.screen_width, self.screen_height) * 0.2)
if self.nb_pickers > 3 then
number_picker_widgets_width = math.floor(number_picker_widgets_width * 3 / self.nb_pickers)
end
if self.year then
self.year_widget = NumberPickerWidget:new{
show_parent = self,
value = self.year,
value_min = self.year_min or 2021,
value_max = self.year_max or 2525,
value_step = 1,
value_hold_step = self.year_hold_step or 4,
width = number_picker_widgets_width,
}
self:mergeLayoutInHorizontal(self.year_widget)
else
self.year_widget = dummy_widget
end
if self.month then
self.month_widget = NumberPickerWidget:new{
show_parent = self,
value = self.month,
value_min = self.month_min or 1,
value_max = self.month_max or 12,
value_step = 1,
value_hold_step = self.month_hold_step or 3,
width = number_picker_widgets_width,
}
self:mergeLayoutInHorizontal(self.month_widget)
else
self.month_widget = dummy_widget
end
if self.day then
self.day_widget = NumberPickerWidget:new{
show_parent = self,
value = self.day,
value_min = self.day_min or 1,
value_max = self.day_max or 31,
value_step = 1,
value_hold_step = self.day_hold_step or 3,
width = number_picker_widgets_width,
}
self:mergeLayoutInHorizontal(self.day_widget)
else
self.day_widget = dummy_widget
end
if self.hour then
self.hour_widget = NumberPickerWidget:new{
show_parent = self,
value = self.hour,
value_min = self.hour_min or 0,
value_max = self.hour_max or 23,
value_step = 1,
value_hold_step = self.hour_hold_step or 4,
width = number_picker_widgets_width,
}
self:mergeLayoutInHorizontal(self.hour_widget)
else
self.hour_widget = dummy_widget
end
if self.min then
self.min_widget = NumberPickerWidget:new{
show_parent = self,
value = self.min,
value_min = self.min_min or 0,
value_max = self.min_max or 59,
value_step = 1,
value_hold_step = self.min_hold_step or 10,
width = number_picker_widgets_width,
}
self:mergeLayoutInHorizontal(self.min_widget)
else
self.min_widget = dummy_widget
end
if self.sec then
self.sec_widget = NumberPickerWidget:new{
show_parent = self,
value = self.sec,
value_min = self.sec_min or 0,
value_max = self.sec_max or 59,
value_step = 1,
value_hold_step = self.sec_hold_step or 10,
width = number_picker_widgets_width,
}
self:mergeLayoutInHorizontal(self.sec_widget)
else
self.sec_widget = dummy_widget
end
local separator_date = TextWidget:new{
text = "",
face = self.title_face,
bold = true,
}
local separator_time = TextWidget:new{
text = ":",
face = self.title_face,
bold = true,
}
local separator_date_time = TextWidget:new{
text = "/",
face = self.title_face,
bold = true,
}
local date_group = HorizontalGroup:new{
align = "center",
self.year_widget, -- 1
separator_date, -- 2
self.month_widget, -- 3
separator_date, -- 4
self.day_widget, -- 5
separator_date_time, -- 6
self.hour_widget, -- 7
separator_time, -- 8
self.min_widget, -- 9
separator_time, -- 10
self.sec_widget, -- 11
}
-- remove empty widgets plus trailling placeholder
for i = #date_group, 1, -2 do
if date_group[i] == dummy_widget then
table.remove(date_group, i)
table.remove(date_group, i-1)
end
end
-- clean up leading separator
if date_group[1] == separator_date or date_group[1] == separator_date_time or date_group[1] == separator_time then
table.remove(date_group, 1)
end
local title_bar = TitleBar:new{
width = self.width,
align = "left",
with_bottom_line = true,
title = self.title_text,
title_shrink_font_to_fit = true,
info_text = self.info_text,
show_parent = self,
}
local buttons = {}
if self.default_value then
table.insert(buttons, {
{
text = self.default_text or T(_("Default value: %1"), self.default_value),
callback = function()
if self.default_callback then
self.default_callback({
year = self.year_widget:getValue(),
month = self.month_widget:getValue(),
day = self.day_widget:getValue(),
hour = self.hour_widget:getValue(),
minute = self.min_widget:getValue(),
second = self.sec_widget:getValue(),
})
end
if not self.keep_shown_on_apply then -- assume extra wants it same as ok
self:onClose()
end
end,
},
})
end
if self.extra_text then
table.insert(buttons, {
{
text = self.extra_text,
callback = function()
self.extra_callback(self)
end,
},
})
end
table.insert(buttons, {
{
text = self.cancel_text,
callback = function()
if self.cancel_callback then
self.cancel_callback(self)
end
self:onClose()
end,
},
{
text = self.ok_text,
callback = function()
if self.callback then
self.year = self.year_widget:getValue()
self.month = self.month_widget:getValue()
self.day = self.day_widget:getValue()
self.hour = self.hour_widget:getValue()
self.min = self.min_widget:getValue()
self.sec = self.sec_widget:getValue()
self:callback(self)
end
self:onClose()
end,
},
})
local ok_cancel_buttons = ButtonTable:new{
width = self.width - 2*Size.padding.default,
buttons = buttons,
zero_sep = true,
show_parent = self,
}
self:mergeLayoutInVertical(ok_cancel_buttons)
self.date_frame = FrameContainer:new{
radius = Size.radius.window,
bordersize = Size.border.window,
padding = 0,
margin = 0,
background = Blitbuffer.COLOR_WHITE,
VerticalGroup:new{
align = "left",
title_bar,
CenterContainer:new{
dimen = Geom:new{
w = self.width,
h = math.floor(date_group:getSize().h * 1.2),
},
date_group
},
CenterContainer:new{
dimen = Geom:new{
w = self.width,
h = ok_cancel_buttons:getSize().h,
},
ok_cancel_buttons
}
}
}
self[1] = WidgetContainer:new{
align = "center",
dimen = Geom:new{
x = 0, y = 0,
w = self.screen_width,
h = self.screen_height,
},
FrameContainer:new{
bordersize = 0,
padding = Size.padding.default,
self.date_frame,
}
}
self:refocusWidget()
UIManager:setDirty(self, function()
return "ui", self.date_frame.dimen
end)
end
function DateTimeWidget:update(year, month, day, hour, min, sec)
self.year_widget.value = year
self.year_widget:update()
self.month_widget.value = month
self.month_widget:update()
self.day_widget.value = day
self.day_widget:update()
self.hour_widget.value = hour
self.hour_widget:update()
self.min_widget.value = min
self.min_widget:update()
self.sec_widget.value = sec
self.sec_widget:update()
end
function DateTimeWidget:onCloseWidget()
-- Let our main WidgetContainer free its child widgets
self[1]:free()
UIManager:setDirty(nil, function()
return "ui", self.date_frame.dimen
end)
end
function DateTimeWidget:onShow()
UIManager:setDirty(self, function()
return "ui", self.date_frame.dimen
end)
return true
end
function DateTimeWidget:onAnyKeyPressed()
UIManager:close(self)
return true
end
function DateTimeWidget:onTapClose(arg, ges_ev)
if ges_ev.pos:notIntersectWith(self.date_frame.dimen) then
self:onClose()
end
return true
end
function DateTimeWidget:onClose()
UIManager:close(self)
return true
end
return DateTimeWidget