--[[-- 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 } } 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: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