From 13274d6212735ebd2aa776b1389ad680472957a8 Mon Sep 17 00:00:00 2001 From: zwim <36999612+zwim@users.noreply.github.com> Date: Mon, 23 May 2022 20:32:59 +0200 Subject: [PATCH] datetimewidget: simpler usage, allows 2 to 6 numberpickers for date and time (#9070) --- .../elements/common_settings_menu_table.lua | 3 +- frontend/ui/widget/datetimewidget.lua | 284 +++++++++++++----- plugins/autosuspend.koplugin/main.lua | 77 +++-- plugins/autowarmth.koplugin/main.lua | 1 - plugins/readtimer.koplugin/main.lua | 2 - 5 files changed, 256 insertions(+), 111 deletions(-) diff --git a/frontend/ui/elements/common_settings_menu_table.lua b/frontend/ui/elements/common_settings_menu_table.lua index ca9996806..0fca2468e 100644 --- a/frontend/ui/elements/common_settings_menu_table.lua +++ b/frontend/ui/elements/common_settings_menu_table.lua @@ -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 diff --git a/frontend/ui/widget/datetimewidget.lua b/frontend/ui/widget/datetimewidget.lua index 5c91af5a8..284328772 100644 --- a/frontend/ui/widget/datetimewidget.lua +++ b/frontend/ui/widget/datetimewidget.lua @@ -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 + -- 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 + 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 - 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, + 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, } - 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, + separator_time = TextWidget:new{ + text = _(":"), + face = self.title_face, + bold = true, } - self:mergeLayoutInHorizontal(day_min_widget) - local separator_space = TextBoxWidget:new{ - text = self.is_date and "–" or ":", - alignment = "center", + 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) diff --git a/plugins/autosuspend.koplugin/main.lua b/plugins/autosuspend.koplugin/main.lua index ab6713182..3fd551f4f 100644 --- a/plugins/autosuspend.koplugin/main.lua +++ b/plugins/autosuspend.koplugin/main.lua @@ -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, diff --git a/plugins/autowarmth.koplugin/main.lua b/plugins/autowarmth.koplugin/main.lua index 2eab82af1..fb2782a84 100644 --- a/plugins/autowarmth.koplugin/main.lua +++ b/plugins/autowarmth.koplugin/main.lua @@ -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"), diff --git a/plugins/readtimer.koplugin/main.lua b/plugins/readtimer.koplugin/main.lua index 3eb465fa4..f2971f68c 100644 --- a/plugins/readtimer.koplugin/main.lua +++ b/plugins/readtimer.koplugin/main.lua @@ -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,