mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
* [VirtualKeyboard] Add support for keynaviguation Also rename the variable "layout" to "keyboard_layout" because conflict with the layout from the focusmanager * Make the goto dialog compatible with key naviguation My solution is to change the order of the widget. The last one will the virtualkeybard so it catch all the keybinding, and below it, make the dialog "is_always_active = true" so it can receive touch event. * Correctly show the virtual keyboard on dpad devices * change the order to call the virtualKeyboard so it end up on top * Handle the multi input dialog * Support reopening the virtualKeyboard by the Press key * add check focusmanager * Fix https://github.com/koreader/koreader/issues/3797 * MultiInputDialog : Now work on non touch-device * Set the virtualkeyboard to be a modal widget * Fix the layout in multiinputwidget * Fix for the various combination of hasKeys,hasDpad,isTouchDevice * [Focusmanager] Better handling of malformed layout
255 lines
7.8 KiB
Lua
255 lines
7.8 KiB
Lua
--[[--
|
|
Widget for taking user input.
|
|
|
|
Example:
|
|
|
|
local UIManager = require("ui/uimanager")
|
|
local _ = require("gettext")
|
|
local sample_input
|
|
sample_input = InputDialog:new{
|
|
title = _("Dialog title"),
|
|
input = "default value",
|
|
input_hint = "hint text",
|
|
input_type = "string",
|
|
description = "Some more description",
|
|
-- text_type = "password",
|
|
buttons = {
|
|
{
|
|
{
|
|
text = _("Cancel"),
|
|
callback = function()
|
|
UIManager:close(sample_input)
|
|
end,
|
|
},
|
|
{
|
|
text = _("Save"),
|
|
-- button with is_enter_default set to true will be
|
|
-- triggered after user press the enter key from keyboard
|
|
is_enter_default = true,
|
|
callback = function()
|
|
print('Got user input as raw text:', sample_input:getInputText())
|
|
print('Got user input as value:', sample_input:getInputValue())
|
|
end,
|
|
},
|
|
}
|
|
},
|
|
}
|
|
UIManager:show(sample_input)
|
|
sample_input:onShowKeyboard()
|
|
|
|
If it would take the user more than half a minute to recover from a mistake,
|
|
a "Cancel" button <em>must</em> be added to the dialog. The cancellation button
|
|
should be kept on the left and the button executing the action on the right.
|
|
|
|
It is strongly recommended to use a text describing the action to be
|
|
executed, as demonstrated in the example above. If the resulting phrase would be
|
|
longer than three words it should just read "OK".
|
|
|
|
]]
|
|
|
|
local Blitbuffer = require("ffi/blitbuffer")
|
|
local ButtonTable = require("ui/widget/buttontable")
|
|
local CenterContainer = require("ui/widget/container/centercontainer")
|
|
local Device = require("device")
|
|
local Font = require("ui/font")
|
|
local FrameContainer = require("ui/widget/container/framecontainer")
|
|
local Geom = require("ui/geometry")
|
|
local InputContainer = require("ui/widget/container/inputcontainer")
|
|
local InputText = require("ui/widget/inputtext")
|
|
local LineWidget = require("ui/widget/linewidget")
|
|
local MovableContainer = require("ui/widget/container/movablecontainer")
|
|
local RenderText = require("ui/rendertext")
|
|
local Size = require("ui/size")
|
|
local TextBoxWidget = require("ui/widget/textboxwidget")
|
|
local TextWidget = require("ui/widget/textwidget")
|
|
local UIManager = require("ui/uimanager")
|
|
local VerticalGroup = require("ui/widget/verticalgroup")
|
|
local VerticalSpan = require("ui/widget/verticalspan")
|
|
local Screen = Device.screen
|
|
|
|
local InputDialog = InputContainer:new{
|
|
is_always_active = true,
|
|
title = "",
|
|
input = "",
|
|
input_hint = "",
|
|
description = nil,
|
|
buttons = nil,
|
|
input_type = nil,
|
|
enter_callback = nil,
|
|
|
|
width = nil,
|
|
|
|
text_width = nil,
|
|
text_height = nil,
|
|
|
|
title_face = Font:getFace("x_smalltfont"),
|
|
description_face = Font:getFace("x_smallinfofont"),
|
|
input_face = Font:getFace("x_smallinfofont"),
|
|
|
|
title_padding = Size.padding.default,
|
|
title_margin = Size.margin.title,
|
|
input_padding = Size.padding.large,
|
|
input_margin = Size.margin.default,
|
|
button_padding = Size.padding.default,
|
|
}
|
|
|
|
function InputDialog:init()
|
|
self.width = self.width or Screen:getWidth() * 0.8
|
|
local title_width = RenderText:sizeUtf8Text(0, self.width,
|
|
self.title_face, self.title, true).x
|
|
if title_width > self.width then
|
|
local indicator = " >> "
|
|
local indicator_w = RenderText:sizeUtf8Text(0, self.width,
|
|
self.title_face, indicator, true).x
|
|
self.title = RenderText:getSubTextByWidth(self.title, self.title_face,
|
|
self.width - indicator_w, true) .. indicator
|
|
end
|
|
self.title = FrameContainer:new{
|
|
padding = self.title_padding,
|
|
margin = self.title_margin,
|
|
bordersize = 0,
|
|
TextWidget:new{
|
|
text = self.title,
|
|
face = self.title_face,
|
|
width = self.width,
|
|
}
|
|
}
|
|
|
|
if self.description then
|
|
self.description = FrameContainer:new{
|
|
padding = self.title_padding,
|
|
margin = self.title_margin,
|
|
bordersize = 0,
|
|
TextBoxWidget:new{
|
|
text = self.description,
|
|
face = self.description_face,
|
|
width = self.width - 2*self.title_padding - 2*self.title_margin,
|
|
}
|
|
}
|
|
else
|
|
self.description = VerticalSpan:new{ width = self.title_margin + self.title_padding }
|
|
end
|
|
|
|
self._input_widget = InputText:new{
|
|
text = self.input,
|
|
hint = self.input_hint,
|
|
face = self.input_face,
|
|
width = self.text_width or self.width * 0.9,
|
|
height = self.text_height or nil,
|
|
input_type = self.input_type,
|
|
text_type = self.text_type,
|
|
enter_callback = self.enter_callback or function()
|
|
for _,btn_row in ipairs(self.buttons) do
|
|
for _,btn in ipairs(btn_row) do
|
|
if btn.is_enter_default then
|
|
btn.callback()
|
|
return
|
|
end
|
|
end
|
|
end
|
|
end,
|
|
scroll = false,
|
|
parent = self,
|
|
}
|
|
self.button_table = ButtonTable:new{
|
|
width = self.width - 2*self.button_padding,
|
|
button_font_face = "cfont",
|
|
button_font_size = 20,
|
|
buttons = self.buttons,
|
|
zero_sep = true,
|
|
show_parent = self,
|
|
}
|
|
|
|
self.title_bar = LineWidget:new{
|
|
dimen = Geom:new{
|
|
w = self.width,
|
|
h = Size.line.thick,
|
|
}
|
|
}
|
|
|
|
self.dialog_frame = FrameContainer:new{
|
|
radius = Size.radius.window,
|
|
padding = 0,
|
|
margin = 0,
|
|
background = Blitbuffer.COLOR_WHITE,
|
|
VerticalGroup:new{
|
|
align = "left",
|
|
self.title,
|
|
self.title_bar,
|
|
self.description,
|
|
-- input
|
|
CenterContainer:new{
|
|
dimen = Geom:new{
|
|
w = self.title_bar:getSize().w,
|
|
h = self._input_widget:getSize().h,
|
|
},
|
|
self._input_widget,
|
|
},
|
|
-- Add same vertical space after than before InputText
|
|
VerticalSpan:new{ width = self.title_margin + self.title_padding },
|
|
-- buttons
|
|
CenterContainer:new{
|
|
dimen = Geom:new{
|
|
w = self.title_bar:getSize().w,
|
|
h = self.button_table:getSize().h,
|
|
},
|
|
self.button_table,
|
|
}
|
|
}
|
|
}
|
|
if Device:hasKeys() then
|
|
--little hack to piggyback on the layout of the button_table to handle the new InputText
|
|
table.insert(self.button_table.layout, 1, {self._input_widget})
|
|
end
|
|
|
|
self[1] = CenterContainer:new{
|
|
dimen = Geom:new{
|
|
w = Screen:getWidth(),
|
|
h = Screen:getHeight() - self._input_widget:getKeyboardDimen().h,
|
|
},
|
|
MovableContainer:new{
|
|
self.dialog_frame,
|
|
},
|
|
}
|
|
end
|
|
|
|
function InputDialog:getInputText()
|
|
return self._input_widget:getText()
|
|
end
|
|
|
|
function InputDialog:getInputValue()
|
|
local text = self:getInputText()
|
|
if self.input_type == "number" then
|
|
return tonumber(text)
|
|
else
|
|
return text
|
|
end
|
|
end
|
|
|
|
function InputDialog:setInputText(text)
|
|
self._input_widget:setText(text)
|
|
end
|
|
|
|
function InputDialog:onShow()
|
|
UIManager:setDirty(self, function()
|
|
return "ui", self.dialog_frame.dimen
|
|
end)
|
|
end
|
|
|
|
function InputDialog:onCloseWidget()
|
|
self:onClose()
|
|
UIManager:setDirty(nil, function()
|
|
return "partial", self.dialog_frame.dimen
|
|
end)
|
|
end
|
|
|
|
function InputDialog:onShowKeyboard()
|
|
self._input_widget:onShowKeyboard()
|
|
end
|
|
|
|
function InputDialog:onClose()
|
|
self._input_widget:onCloseKeyboard()
|
|
end
|
|
|
|
return InputDialog
|