2
0
mirror of https://github.com/koreader/koreader synced 2024-11-16 06:12:56 +00:00

datetimewidget: simpler usage, allows 2 to 6 numberpickers for date and time (#9070)

This commit is contained in:
zwim 2022-05-23 20:32:59 +02:00 committed by GitHub
parent 8f316b1e8c
commit 13274d6212
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 259 additions and 114 deletions

View File

@ -133,11 +133,11 @@ if Device:setDateTime() then
local curr_hour = now_t.hour
local curr_min = now_t.min
local time_widget = DateTimeWidget:new{
is_date = false,
hour = curr_hour,
min = curr_min,
ok_text = _("Set time"),
title_text = _("Set time"),
info_text =_("Time is in hours and minutes."),
callback = function(time)
if Device:setDateTime(nil, nil, nil, time.hour, time.min) then
now_t = os.date("*t")
@ -169,6 +169,7 @@ if Device:setDateTime() then
day = curr_day,
ok_text = _("Set date"),
title_text = _("Set date"),
info_text = _("Date is in years, months and days."),
callback = function(time)
now_t = os.date("*t")
if Device:setDateTime(time.year, time.month, time.day, now_t.hour, now_t.min, now_t.sec) then

View File

@ -6,11 +6,11 @@ Example for input a time:
local @{gettext|_} = require("gettext")
local time_widget = DateTimeWidget:new{
is_date = false,
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
@ -33,6 +33,21 @@ Example for input a date:
}
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")
@ -47,7 +62,7 @@ local Font = require("ui/font")
local HorizontalGroup = require("ui/widget/horizontalgroup")
local NumberPickerWidget = require("ui/widget/numberpickerwidget")
local Size = require("ui/size")
local TextBoxWidget = require("ui/widget/textboxwidget")
local TextWidget = require("ui/widget/textwidget")
local TitleBar = require("ui/widget/titlebar")
local UIManager = require("ui/uimanager")
local VerticalGroup = require("ui/widget/verticalgroup")
@ -61,12 +76,6 @@ local DateTimeWidget = FocusManager:new{
info_text = nil,
width = nil,
height = nil,
is_date = true,
day = 1,
month = 1,
year = 2021,
hour = 12,
min = 0,
ok_text = _("Apply"),
cancel_text = _("Close"),
-- Optional extra button on bottom
@ -75,11 +84,38 @@ local DateTimeWidget = FocusManager:new{
}
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()
self.width = self.width or math.floor(math.min(self.screen_width, self.screen_height) *
(self.is_date and 0.8 or 0.6))
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
@ -101,57 +137,148 @@ function DateTimeWidget:init()
self:createLayout()
end
local year_widget, month_hour_widget, day_min_widget
-- Just a dummy with no operation
local dummy_widget = {}
function dummy_widget:free() end
function dummy_widget:getValue() end
function dummy_widget:update() end
local year_widget, month_widget, day_widget, hour_widget, min_widget, sec_widget
local separator_date, separator_date_time, separator_time
function DateTimeWidget:createLayout()
year_widget = NumberPickerWidget:new{
show_parent = self,
value = self.year,
value_min = 2021,
value_max = 2041,
value_step = 1,
value_hold_step = self.year_hold_step or 4,
}
if self.is_date then
self:mergeLayoutInHorizontal(year_widget)
-- 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
month_hour_widget = NumberPickerWidget:new{
show_parent = self,
value = self.is_date and self.month or self.hour,
value_min = self.hour_min or self.month_min or (self.is_date and 1 or 0),
value_max = self.hour_max or self.month_max or (self.is_date and 12 or 23),
value_step = 1,
value_hold_step = self.hour_hold_step or self.month_hold_step or 3,
}
self:mergeLayoutInHorizontal(month_hour_widget)
day_min_widget = NumberPickerWidget:new{
show_parent = self,
value = self.is_date and self.day or self.min,
value_min = self.min_min or self.day_min or (self.is_date and 1 or 0),
value_max = self.min_max or self.day_max or (self.is_date and 31 or 59),
value_step = 1,
value_hold_step = self.day_hold_step or self.min_hold_step or (self.is_date and 5 or 10),
date_month_hour = month_hour_widget,
date_year = year_widget,
}
self:mergeLayoutInHorizontal(day_min_widget)
local separator_space = TextBoxWidget:new{
text = self.is_date and "" or ":",
alignment = "center",
if self.year then
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(year_widget)
else
year_widget = dummy_widget
end
if self.month then
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(month_widget)
else
month_widget = dummy_widget
end
if self.day then
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(day_widget)
else
day_widget = dummy_widget
end
if self.hour then
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(hour_widget)
else
hour_widget = dummy_widget
end
if self.min then
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(min_widget)
else
min_widget = dummy_widget
end
if self.sec then
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(sec_widget)
else
sec_widget = dummy_widget
end
separator_date = TextWidget:new{
text = "",
face = self.title_face,
bold = true,
}
separator_time = TextWidget:new{
text = _(":"),
face = self.title_face,
bold = true,
}
separator_date_time = TextWidget:new{
text = _("/"),
face = self.title_face,
bold = true,
width = math.floor(math.min(self.screen_width, self.screen_height) *
(self.is_date and 0.02 or 0.05)),
}
local date_group = HorizontalGroup:new{
align = "center",
year_widget,
separator_space,
month_hour_widget,
separator_space,
day_min_widget,
}
if not self.is_date then
table.remove(date_group, 2)
align = "center",
year_widget, -- 1
separator_date, -- 2
month_widget, -- 3
separator_date, -- 4
day_widget, -- 5
separator_date_time, -- 6
hour_widget, -- 7
separator_time, -- 8
min_widget, -- 9
separator_time, -- 10
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
@ -172,8 +299,14 @@ function DateTimeWidget:createLayout()
text = self.default_text or T(_("Default value: %1"), self.default_value),
callback = function()
if self.default_callback then
self.default_callback(year_widget:getValue(), month_hour_widget:getValue(),
day_min_widget:getValue())
self.default_callback({
year = year_widget:getValue(),
month = month_widget:getValue(),
day = day_widget:getValue(),
hour = hour_widget:getValue(),
minute = min_widget:getValue(),
second = sec_widget:getValue(),
})
end
if not self.keep_shown_on_apply then -- assume extra wants it same as ok
self:onClose()
@ -196,6 +329,9 @@ function DateTimeWidget:createLayout()
{
text = self.cancel_text,
callback = function()
if self.cancel_callback then
self.cancel_callback(self)
end
self:onClose()
end,
},
@ -204,13 +340,11 @@ function DateTimeWidget:createLayout()
callback = function()
if self.callback then
self.year = year_widget:getValue()
if self.is_date then
self.month = month_hour_widget:getValue()
self.day = day_min_widget:getValue()
else
self.hour = month_hour_widget:getValue()
self.min = day_min_widget:getValue()
end
self.month = month_widget:getValue()
self.day = day_widget:getValue()
self.hour = hour_widget:getValue()
self.min = min_widget:getValue()
self.sec = sec_widget:getValue()
self:callback(self)
end
self:onClose()
@ -270,16 +404,32 @@ function DateTimeWidget:createLayout()
end)
end
function DateTimeWidget:update(left, mid, right)
year_widget.value = left
function DateTimeWidget:update(year, month, day, hour, min, sec)
year_widget.value = year
year_widget:update()
month_hour_widget.value = mid
month_hour_widget:update()
day_min_widget.value = right
day_min_widget:update()
month_widget.value = month
month_widget:update()
day_widget.value = day
day_widget:update()
hour_widget.value = hour
hour_widget:update()
min_widget.value = min
min_widget:update()
sec_widget.value = sec
sec_widget:update()
end
function DateTimeWidget:onCloseWidget()
year_widget:free()
month_widget:free()
day_widget:free()
hour_widget:free()
min_widget:free()
sec_widget:free()
separator_date:free()
separator_date_time:free()
separator_time:free()
UIManager:setDirty(nil, function()
return "ui", self.date_frame.dimen
end)

View File

@ -360,45 +360,47 @@ function AutoSuspend:pickTimeoutValue(touchmenu_instance, title, info, setting,
-- Standby uses a different scheduled task than suspend/shutdown
local is_standby = setting == "auto_standby_timeout_seconds"
local left_val
local day, hour, minute, second
local day_max, hour_max, min_max, sec_max
if time_scale == 2 then
left_val = math.floor(setting_val / (24*3600))
day = math.floor(setting_val / (24*3600))
hour = math.floor(setting_val / 3600) % 24
day_max = math.floor(range[2] / (24*3600)) - 1
hour_max = 23
elseif time_scale == 1 then
left_val = math.floor(setting_val / 3600)
hour = math.floor(setting_val / 3600)
minute = math.floor(setting_val / 60) % 60
hour_max = math.floor(range[2] / 3600) - 1
min_max = 59
else
left_val = math.floor(setting_val / 60)
minute = math.floor(setting_val / 60)
second = math.floor(setting_val) % 60
min_max = math.floor(range[2] / 60) - 1
sec_max = 59
end
local right_val
if time_scale == 2 then
right_val = math.floor(setting_val / 3600) % 24
elseif time_scale == 1 then
right_val = math.floor(setting_val / 60) % 60
else
right_val = math.floor(setting_val) % 60
end
local time_spinner
time_spinner = DateTimeWidget:new {
is_date = false,
hour = left_val,
min = right_val,
day = day,
hour = hour,
min = minute,
sec = second,
day_hold_step = 5,
hour_hold_step = 5,
min_hold_step = 10,
hour_max = (time_scale == 2 and math.floor(range[2] / (24*3600)))
or (time_scale == 1 and math.floor(range[2] / 3600))
or math.floor(range[2] / 60),
min_max = (time_scale == 2 and 23) or 59,
sec_hold_step = 10,
day_max = day_max,
hour_max = hour_max,
min_max = min_max,
sec_max = sec_max,
ok_text = _("Set timeout"),
title_text = title,
info_text = info,
callback = function(spinner)
if time_scale == 2 then
self[setting] = (spinner.hour * 24 + spinner.min) * 3600
elseif time_scale == 1 then
self[setting] = spinner.hour * 3600 + spinner.min * 60
else
self[setting] = spinner.hour * 60 + spinner.min
end
callback = function(t)
self[setting] = (((t.day or 0) * 24 +
(t.hour or 0)) * 60 +
(t.min or 0)) * 60 +
(t.sec or 0)
self[setting] = Math.clamp(self[setting], range[1], range[2])
G_reader_settings:saveSetting(setting, self[setting])
if is_standby then
@ -421,23 +423,18 @@ function AutoSuspend:pickTimeoutValue(touchmenu_instance, title, info, setting,
default_value = util.secondsToClockDuration("modern", default_value,
time_scale == 2 or time_scale == 1, true, true):gsub("00m$", ""):gsub("^00m:", ""),
default_callback = function()
local hour
local day, hour, min, sec -- luacheck: ignore 431
if time_scale == 2 then
hour = math.floor(default_value / (24*3600))
day = math.floor(default_value / (24*3600))
hour = math.floor(default_value / 3600) % 24
elseif time_scale == 1 then
hour = math.floor(default_value / 3600)
else
hour = math.floor(default_value / 60)
end
local min
if time_scale == 2 then
min = math.floor(default_value / 3600) % 24
elseif time_scale == 1 then
min = math.floor(default_value / 60) % 60
else
min = math.floor(default_value % 60)
min = math.floor(default_value / 60)
sec = math.floor(default_value % 60)
end
time_spinner:update(nil, hour, min)
time_spinner:update(nil, nil, day, hour, min, sec) -- It is ok to pass nils here.
end,
extra_text = _("Disable"),
extra_callback = function(this)
@ -480,7 +477,7 @@ function AutoSuspend:addToMainMenu(menu_items)
callback = function(touchmenu_instance)
-- 60 sec (1') is the minimum and 24*3600 sec (1day) is the maximum suspend time.
-- A suspend time of one day seems to be excessive.
-- But or battery testing it might give some sense.
-- But it might make sense for battery testing.
self:pickTimeoutValue(touchmenu_instance,
_("Timeout for autosuspend"), _("Enter time in hours and minutes."),
"auto_suspend_timeout_seconds", default_auto_suspend_timeout_seconds,
@ -509,7 +506,7 @@ function AutoSuspend:addToMainMenu(menu_items)
-- Maximum more than four weeks seems a bit excessive if you want to enable authoshutdown,
-- even if the battery can last up to three months.
self:pickTimeoutValue(touchmenu_instance,
_("Timeout for autoshutdown"), _("Enter time in days and hours."),
_("Timeout for autoshutdown"), _("Enter time in days and hours."),
"autoshutdown_timeout_seconds", default_autoshutdown_timeout_seconds,
{5*60, 28*24*3600}, 2)
end,

View File

@ -591,7 +591,6 @@ function AutoWarmth:getScheduleMenu()
UIManager:show(DateTimeWidget:new{
title_text = _("Set time"),
info_text = _("Enter time in hours and minutes."),
is_date = false,
hour = hh,
min = mm,
ok_text = _("Set time"),

View File

@ -87,7 +87,6 @@ function ReadTimer:addToMainMenu(menu_items)
local curr_hour = now_t.hour
local curr_min = now_t.min
local time_widget = DateTimeWidget:new{
is_date = false,
hour = curr_hour,
min = curr_min,
ok_text = _("Set alarm"),
@ -136,7 +135,6 @@ function ReadTimer:addToMainMenu(menu_items)
end
end
local time_widget = DateTimeWidget:new{
is_date = false,
hour = remain_hours or 0,
min = remain_minutes or 0,
hour_max = 17,