From 63241a20ea8e29860581300377d2e1466670b66f Mon Sep 17 00:00:00 2001 From: Frans de Jonge Date: Mon, 14 Aug 2017 21:42:15 +0200 Subject: [PATCH] [UX] Add CheckButton and use it for show password Normal textinputs stay the same, password textinputs automatically gain a show password checkbutton underneath. Thanks to @robert00s for the idea. --- frontend/ui/widget/checkbutton.lua | 137 +++++++++++++++++++++++++++++ frontend/ui/widget/checkmark.lua | 4 +- frontend/ui/widget/inputdialog.lua | 22 ----- frontend/ui/widget/inputtext.lua | 77 +++++++++++++--- 4 files changed, 203 insertions(+), 37 deletions(-) create mode 100644 frontend/ui/widget/checkbutton.lua diff --git a/frontend/ui/widget/checkbutton.lua b/frontend/ui/widget/checkbutton.lua new file mode 100644 index 000000000..53e4e384f --- /dev/null +++ b/frontend/ui/widget/checkbutton.lua @@ -0,0 +1,137 @@ +--[[-- +Button widget that shows a checkmark (`✓`) when checked and an empty box (`□`) +when unchecked. + +Example: + + local CheckButton = require("ui/widget/CheckButton") + local parent_widget = OverlapGroup:new{} + table.insert(parent_widget, CheckButton:new{ + text = _("Show password"), + callback = function() end, + }) + UIManager:show(parent_widget) + +]] + +local CheckMark = require("ui/widget/checkmark") +local Device = require("device") +local Font = require("ui/font") +local FrameContainer = require("ui/widget/container/framecontainer") +local GestureRange = require("ui/gesturerange") +local HorizontalGroup = require("ui/widget/horizontalgroup") +local InputContainer = require("ui/widget/container/inputcontainer") +local TextWidget = require("ui/widget/textwidget") +local UIManager = require("ui/uimanager") +local Screen = Device.screen + +local CheckButton = InputContainer:new{ + callback = nil, + hold_callback = nil, + checked = false, + enabled = true, + face = Font:getFace("cfont"), + overlap_align = "right", + text = nil, + toggle_text = nil, + window = nil, + + padding = Screen:scaleBySize(5), + margin = Screen:scaleBySize(5), + bordersize = Screen:scaleBySize(3), +} + +function CheckButton:init() + self:initCheckButton(self.checked) +end + +function CheckButton:initCheckButton(checked) + self.checked = checked + self._checkmark = CheckMark:new{ + checked = self.checked, + } + self._textwidget = TextWidget:new{ + text = self.text, + face = self.face, + } + self._horizontalgroup = HorizontalGroup:new{ + self._checkmark, + self._textwidget, + } + self._frame = FrameContainer:new{ + bordersize = 0, + margin = 0, + padding = 0, + self._horizontalgroup, + } + self[1] = self._frame + self.dimen = self._frame:getSize() + if Device:isTouchDevice() then + self.ges_events = { + TapCheckButton = { + GestureRange:new{ + ges = "tap", + range = self.dimen, + }, + doc = "Tap Button", + }, + HoldCheckButton = { + GestureRange:new{ + ges = "hold", + range = self.dimen, + }, + doc = "Hold Button", + } + } + end +end + +function CheckButton:onTapCheckButton() + if self.enabled and self.callback then + UIManager:scheduleIn(0.0, function() + self.invert = true + UIManager:setDirty(self.show_parent, function() + return "ui", self.dimen + end) + end) + UIManager:scheduleIn(0.1, function() + self.callback() + self.invert = false + UIManager:setDirty(self.show_parent, function() + return "ui", self.dimen + end) + end) + elseif self.tap_input then + self:onInput(self.tap_input) + elseif type(self.tap_input_func) == "function" then + self:onInput(self.tap_input_func()) + end + return true +end + +function CheckButton:onHoldCheckButton() + if self.enabled and self.hold_callback then + self.hold_callback() + elseif self.hold_input then + self:onInput(self.hold_input) + elseif type(self.hold_input_func) == "function" then + self:onInput(self.hold_input_func()) + end + return true +end + +function CheckButton:check() + self:initCheckButton(true) + UIManager:setDirty(self.parent, function() + return "partial", self.dimen + end) +end + +function CheckButton:unCheck() + self:initCheckButton(false) + UIManager:setDirty(self.parent, function() + return "partial", self.dimen + end) +end + +return CheckButton diff --git a/frontend/ui/widget/checkmark.lua b/frontend/ui/widget/checkmark.lua index 5640da719..f0e46477b 100644 --- a/frontend/ui/widget/checkmark.lua +++ b/frontend/ui/widget/checkmark.lua @@ -29,7 +29,7 @@ local CheckMark = InputContainer:new{ function CheckMark:init() local checked_widget = TextWidget:new{ - text = " ✓", + text = " ✓", -- preceded by thin space for better alignment face = self.face, } local unchecked_widget = TextWidget:new{ @@ -45,6 +45,8 @@ function CheckMark:init() unchecked_widget } or empty_widget + + self.dimen = unchecked_widget:getSize() end return CheckMark diff --git a/frontend/ui/widget/inputdialog.lua b/frontend/ui/widget/inputdialog.lua index c270539cd..a0eafcac8 100644 --- a/frontend/ui/widget/inputdialog.lua +++ b/frontend/ui/widget/inputdialog.lua @@ -63,7 +63,6 @@ local VerticalGroup = require("ui/widget/verticalgroup") local WidgetContainer = require("ui/widget/container/widgetcontainer") local UIManager = require("ui/uimanager") local Screen = require("device").screen -local _ = require("gettext") local InputDialog = InputContainer:new{ title = "", @@ -73,7 +72,6 @@ local InputDialog = InputContainer:new{ buttons = nil, input_type = nil, enter_callback = nil, - show_password_toggle = true, width = nil, @@ -149,26 +147,6 @@ function InputDialog:init() scroll = false, parent = self, } - local is_password_type = false - if self._input_widget.text_type == "password" then - is_password_type = true - end - if self.show_password_toggle and is_password_type then - local button_switch = { - { - text = _("Show password"), - callback = function() - if self._input_widget.text_type == "text" then - self._input_widget.text_type = "password" - else - self._input_widget.text_type = "text" - end - self._input_widget:setText(self._input_widget:getText()) - end, - }, - } - table.insert(self.buttons[1], button_switch[1]) - end self.button_table = ButtonTable:new{ width = self.width, button_font_face = "cfont", diff --git a/frontend/ui/widget/inputtext.lua b/frontend/ui/widget/inputtext.lua index 96b45f5f6..790904afc 100644 --- a/frontend/ui/widget/inputtext.lua +++ b/frontend/ui/widget/inputtext.lua @@ -1,4 +1,5 @@ local Blitbuffer = require("ffi/blitbuffer") +local CheckButton = require("ui/widget/checkbutton") local Device = require("device") local FrameContainer = require("ui/widget/container/framecontainer") local Font = require("ui/font") @@ -7,8 +8,10 @@ local InputContainer = require("ui/widget/container/inputcontainer") local ScrollTextWidget = require("ui/widget/scrolltextwidget") local TextBoxWidget = require("ui/widget/textboxwidget") local UIManager = require("ui/uimanager") -local Screen = Device.screen +local VerticalGroup = require("ui/widget/verticalgroup") local util = require("util") +local _ = require("gettext") +local Screen = Device.screen local Keyboard @@ -20,14 +23,15 @@ local InputText = InputContainer:new{ input_type = nil, text_type = nil, text_widget = nil, -- Text Widget for cursor movement + show_password_toggle = true, width = nil, height = nil, face = Font:getFace("smallinfofont"), - padding = 5, - margin = 5, - bordersize = 2, + padding = Screen:scaleBySize(5), + margin = Screen:scaleBySize(5), + bordersize = Screen:scaleBySize(2), parent = nil, -- parent dialog that will be set dirty scroll = false, @@ -52,12 +56,12 @@ if Device.isTouchDevice() then if self.parent.onSwitchFocus then self.parent:onSwitchFocus(self) end - local x = ges.pos.x - self.dimen.x - self.bordersize - self.padding - local y = ges.pos.y - self.dimen.y - self.bordersize - self.padding + local x = ges.pos.x - self._frame_textwidget.dimen.x - self.bordersize - self.padding + local y = ges.pos.y - self._frame_textwidget.dimen.y - self.bordersize - self.padding if x > 0 and y > 0 then self.charpos = self.text_widget:moveCursor(x, y) UIManager:setDirty(self.parent, function() - return "ui", self[1].dimen + return "ui", self.text_widget.dimen end) end end @@ -72,8 +76,11 @@ function InputText:init() self:initEventListener() end -function InputText:initTextBox(text, char_added) +function InputText:initTextBox(text, char_added, is_password_type) self.text = text + if self.text_type == "password" then + is_password_type = true + end local fgcolor local show_charlist local show_text = text @@ -98,6 +105,36 @@ function InputText:initTextBox(text, char_added) self.charpos = #self.charlist + 1 end end + if is_password_type and self.show_password_toggle then + self._check_button = self._check_button or CheckButton:new{ + text = _("Show password"), + callback = function() + if self.text_type == "text" then + self.text_type = "password" + self._check_button:unCheck() + else + self.text_type = "text" + self._check_button:check() + end + self:setText(self:getText(), is_password_type) + end, + + width = self.width, + height = self.height, + + padding = self.padding, + margin = self.margin, + bordersize = self.bordersize, + } + self._password_toggle = FrameContainer:new{ + bordersize = 0, + padding = self.padding, + margin = self.margin, + self._check_button, + } + else + self._password_toggle = nil + end show_charlist = util.splitToChars(show_text) if self.scroll then self.text_widget = ScrollTextWidget:new{ @@ -123,17 +160,29 @@ function InputText:initTextBox(text, char_added) height = self.height, } end - self[1] = FrameContainer:new{ + self._frame_textwidget = FrameContainer:new{ bordersize = self.bordersize, padding = self.padding, margin = self.margin, color = self.focused and Blitbuffer.COLOR_BLACK or Blitbuffer.COLOR_GREY, self.text_widget, } - self.dimen = self[1]:getSize() - -- FIXME: self.parent is not always in the widget statck (BookStatusWidget) + self._verticalgroup = VerticalGroup:new{ + align = "left", + self._frame_textwidget, + self._password_toggle, + } + self._frame = FrameContainer:new{ + bordersize = 0, + margin = 0, + padding = 0, + self._verticalgroup, + } + self[1] = self._frame + self.dimen = self._frame:getSize() + -- FIXME: self.parent is not always in the widget stack (BookStatusWidget) UIManager:setDirty(self.parent, function() - return "ui", self[1].dimen + return "ui", self.dimen end) end @@ -202,9 +251,9 @@ function InputText:getText() return self.text end -function InputText:setText(text) +function InputText:setText(text, is_password_type) self.charpos = nil - self:initTextBox(text) + self:initTextBox(text, nil, is_password_type) UIManager:setDirty(self.parent, function() return "partial", self[1].dimen end)