mirror of
https://github.com/koreader/koreader
synced 2024-11-11 19:11:14 +00:00
VirtualKeyboard: Revamp visibility handling (#10852)
Move as much of the state tracking as possible inside VirtualKeyboard itself. InputDialog unfortunately needs an internal tracking of this state because it needs to know about it *before* the VK is shown, so we have to keep a bit of duplication in there, although we do try much harder to keep everything in sync (at least at function call edges), and to keep the damage contained to, essentially, the toggle button's handler. (Followup to #10803 & #10850)
This commit is contained in:
parent
4d620d9fd2
commit
4cc620b702
@ -920,10 +920,10 @@ function ReaderStyleTweak:editBookTweak(touchmenu_instance)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
-- Set/save view and cursor position callback
|
-- Store/retrieve view and cursor position callback
|
||||||
view_pos_callback = function(top_line_num, charpos)
|
view_pos_callback = function(top_line_num, charpos)
|
||||||
-- This same callback is called with no argument to get initial position,
|
-- This same callback is called with no arguments on init to retrieve the stored initial position,
|
||||||
-- and with arguments to give back final position when closed.
|
-- and with arguments to store the final position on close.
|
||||||
if top_line_num and charpos then
|
if top_line_num and charpos then
|
||||||
self.book_style_tweak_last_edit_pos = {top_line_num, charpos}
|
self.book_style_tweak_last_edit_pos = {top_line_num, charpos}
|
||||||
else
|
else
|
||||||
|
@ -141,7 +141,7 @@ local InputDialog = FocusManager:extend{
|
|||||||
add_scroll_buttons = false, -- add scroll Up/Down buttons to first row of buttons
|
add_scroll_buttons = false, -- add scroll Up/Down buttons to first row of buttons
|
||||||
add_nav_bar = false, -- append a row of page navigation buttons
|
add_nav_bar = false, -- append a row of page navigation buttons
|
||||||
-- note that the text widget can be scrolled with Swipe North/South even when no button
|
-- note that the text widget can be scrolled with Swipe North/South even when no button
|
||||||
keyboard_hidden = false, -- start with keyboard hidden in full fullscreen mode
|
keyboard_visible = true, -- whether we start with the keyboard visible or not (i.e., our caller skipped onShowKeyboard)
|
||||||
-- needs add_nav_bar to have a Show keyboard button to get it back
|
-- needs add_nav_bar to have a Show keyboard button to get it back
|
||||||
scroll_by_pan = false, -- allow scrolling by lines with Pan (= Swipe, but wait a bit at end
|
scroll_by_pan = false, -- allow scrolling by lines with Pan (= Swipe, but wait a bit at end
|
||||||
-- of gesture before releasing) (may conflict with movable)
|
-- of gesture before releasing) (may conflict with movable)
|
||||||
@ -162,8 +162,8 @@ local InputDialog = FocusManager:extend{
|
|||||||
edited_callback = nil, -- Called on each text modification
|
edited_callback = nil, -- Called on each text modification
|
||||||
|
|
||||||
-- For use by TextEditor plugin:
|
-- For use by TextEditor plugin:
|
||||||
view_pos_callback = nil, -- Called with no arg to get initial top_line_num/charpos,
|
view_pos_callback = nil, -- Called with no args on init to retrieve top_line_num/charpos (however the caller chooses to do so, e.g., some will store it in a LuaSettings),
|
||||||
-- called with (top_line_num, charpos) to give back position on close.
|
-- called with (top_line_num, charpos) on close to let the callback do its thing so that the no args branch spits back useful data..
|
||||||
|
|
||||||
-- Set to false if movable gestures conflicts with subwidgets gestures
|
-- Set to false if movable gestures conflicts with subwidgets gestures
|
||||||
is_movable = true,
|
is_movable = true,
|
||||||
@ -189,6 +189,7 @@ local InputDialog = FocusManager:extend{
|
|||||||
alignment_strict = false,
|
alignment_strict = false,
|
||||||
|
|
||||||
-- for internal use
|
-- for internal use
|
||||||
|
_keyboard_was_visible = nil, -- previous kb visibility state
|
||||||
_text_modified = false, -- previous known modified status
|
_text_modified = false, -- previous known modified status
|
||||||
_top_line_num = nil,
|
_top_line_num = nil,
|
||||||
_charpos = nil,
|
_charpos = nil,
|
||||||
@ -216,7 +217,7 @@ function InputDialog:init()
|
|||||||
self.text_width = self.text_width or math.floor(self.width * 0.9)
|
self.text_width = self.text_width or math.floor(self.width * 0.9)
|
||||||
end
|
end
|
||||||
if self.readonly then -- hide keyboard if we can't edit
|
if self.readonly then -- hide keyboard if we can't edit
|
||||||
self.keyboard_hidden = true
|
self.keyboard_visible = false
|
||||||
end
|
end
|
||||||
if self.fullscreen or self.add_nav_bar then
|
if self.fullscreen or self.add_nav_bar then
|
||||||
self.deny_keyboard_hiding = true
|
self.deny_keyboard_hiding = true
|
||||||
@ -299,10 +300,7 @@ function InputDialog:init()
|
|||||||
local text_height = input_widget:getTextHeight()
|
local text_height = input_widget:getTextHeight()
|
||||||
local line_height = input_widget:getLineHeight()
|
local line_height = input_widget:getLineHeight()
|
||||||
local input_pad_height = input_widget:getSize().h - text_height
|
local input_pad_height = input_widget:getSize().h - text_height
|
||||||
local keyboard_height = 0
|
local keyboard_height = self.keyboard_visible and input_widget:getKeyboardDimen().h or 0
|
||||||
if not self.keyboard_hidden then
|
|
||||||
keyboard_height = input_widget:getKeyboardDimen().h
|
|
||||||
end
|
|
||||||
input_widget:onCloseWidget() -- free() textboxwidget and keyboard
|
input_widget:onCloseWidget() -- free() textboxwidget and keyboard
|
||||||
-- Find out available height
|
-- Find out available height
|
||||||
local available_height = self.screen_height
|
local available_height = self.screen_height
|
||||||
@ -331,9 +329,14 @@ function InputDialog:init()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
if self.view_pos_callback then
|
if self.view_pos_callback then
|
||||||
-- Get initial cursor and top line num from callback
|
-- Retrieve cursor position and top line num from our callback.
|
||||||
-- (will work in case of re-init as these are saved by onClose()
|
-- Mainly used for runtime re-inits.
|
||||||
self._top_line_num, self._charpos = self.view_pos_callback()
|
-- c.f., our onClose handler for the other end of this.
|
||||||
|
-- *May* return nils, in which case, we do *not* want to override our caller's values!
|
||||||
|
local top_line_num, charpos = self.view_pos_callback()
|
||||||
|
if top_line_num and charpos then
|
||||||
|
self._top_line_num, self._charpos = top_line_num, charpos
|
||||||
|
end
|
||||||
end
|
end
|
||||||
self._input_widget = self.inputtext_class:new{
|
self._input_widget = self.inputtext_class:new{
|
||||||
text = self.input,
|
text = self.input,
|
||||||
@ -368,7 +371,6 @@ function InputDialog:init()
|
|||||||
scroll_by_pan = self.scroll_by_pan,
|
scroll_by_pan = self.scroll_by_pan,
|
||||||
cursor_at_end = self.cursor_at_end,
|
cursor_at_end = self.cursor_at_end,
|
||||||
readonly = self.readonly,
|
readonly = self.readonly,
|
||||||
manage_keyboard_state = not self.add_nav_bar, -- we handle keyboard toggle ourselve if nav_bar
|
|
||||||
parent = self,
|
parent = self,
|
||||||
is_text_edited = self._text_modified,
|
is_text_edited = self._text_modified,
|
||||||
top_line_num = self._top_line_num,
|
top_line_num = self._top_line_num,
|
||||||
@ -423,8 +425,7 @@ function InputDialog:init()
|
|||||||
}
|
}
|
||||||
frame = self.movable
|
frame = self.movable
|
||||||
end
|
end
|
||||||
local keyboard_height = self.keyboard_hidden and 0
|
local keyboard_height = self.keyboard_visible and self._input_widget:getKeyboardDimen().h or 0
|
||||||
or self._input_widget:getKeyboardDimen().h
|
|
||||||
self[1] = CenterContainer:new{
|
self[1] = CenterContainer:new{
|
||||||
dimen = Geom:new{
|
dimen = Geom:new{
|
||||||
w = self.screen_width,
|
w = self.screen_width,
|
||||||
@ -449,6 +450,11 @@ function InputDialog:init()
|
|||||||
self:addWidget(widget, true)
|
self:addWidget(widget, true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- If we're fullscreen without a keyboard, make sure only the toggle button can show the keyboard...
|
||||||
|
if self.fullscreen and not self.keyboard_visible then
|
||||||
|
self:lockKeyboard(true)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function InputDialog:addWidget(widget, re_init)
|
function InputDialog:addWidget(widget, re_init)
|
||||||
@ -474,13 +480,13 @@ function InputDialog:getAddedWidgetAvailableWidth()
|
|||||||
return self._input_widget.width
|
return self._input_widget.width
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Close the keyboard if we tap anywhere outside of the keyboard (that isn't an input field, where it would be caught via InputText:onTapTextBox)
|
||||||
function InputDialog:onTap()
|
function InputDialog:onTap()
|
||||||
|
-- This is slightly more fine-grained than VK's own visibility lock, hence the duplication...
|
||||||
if self.deny_keyboard_hiding then
|
if self.deny_keyboard_hiding then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if self._input_widget.onCloseKeyboard then
|
self:onCloseKeyboard()
|
||||||
self._input_widget:onCloseKeyboard()
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function InputDialog:getInputText()
|
function InputDialog:getInputText()
|
||||||
@ -525,24 +531,79 @@ function InputDialog:onCloseWidget()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function InputDialog:onShowKeyboard(ignore_first_hold_release)
|
function InputDialog:onShowKeyboard(ignore_first_hold_release)
|
||||||
if not self.readonly and not self.keyboard_hidden then
|
-- NOTE: There's no VirtualKeyboard widget instantiated at all when readonly,
|
||||||
|
-- and our input widget handles that itself, so we don't need any guards here.
|
||||||
|
-- (In which case, isKeyboardVisible will return `nil`, same as if we had a VK instantiated but *never* shown).
|
||||||
self._input_widget:onShowKeyboard(ignore_first_hold_release)
|
self._input_widget:onShowKeyboard(ignore_first_hold_release)
|
||||||
end
|
-- There's a bit of a chicken or egg issue in init where we would like to check the actual keyboard's visibility state,
|
||||||
|
-- but the widget might not exist or be shown yet, so we'll just have to keep this in sync...
|
||||||
|
self.keyboard_visible = self._input_widget:isKeyboardVisible()
|
||||||
|
end
|
||||||
|
|
||||||
|
function InputDialog:onCloseKeyboard()
|
||||||
|
self._input_widget:onCloseKeyboard()
|
||||||
|
self.keyboard_visible = self._input_widget:isKeyboardVisible()
|
||||||
|
end
|
||||||
|
|
||||||
|
function InputDialog:isKeyboardVisible()
|
||||||
|
return self._input_widget:isKeyboardVisible()
|
||||||
|
end
|
||||||
|
|
||||||
|
function InputDialog:lockKeyboard(toggle)
|
||||||
|
return self._input_widget:lockKeyboard(toggle)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- NOTE: Only called by fullscreen and/or add_nav_bar codepaths
|
||||||
|
-- We do not currently have !fullscreen add_nav_bar callers...
|
||||||
|
function InputDialog:toggleKeyboard(force_toggle)
|
||||||
|
-- Remember the *current* visibility, as the following close will reset it
|
||||||
|
local visible = self:isKeyboardVisible()
|
||||||
|
|
||||||
|
-- When we forcibly close the keyboard, remember its current visiblity state, so that we can properly restore it later.
|
||||||
|
-- (This is used by some buttons in fullscreen mode, where we might want to keep the original keyboard hidden when popping up a new one for another InputDialog).
|
||||||
|
if force_toggle == false then
|
||||||
|
-- NOTE: visible will be nil between our own init and a show of the keyboard, which is precisely what happens when we *hide* the keyboard.
|
||||||
|
self._keyboard_was_visible = visible == true
|
||||||
end
|
end
|
||||||
|
|
||||||
function InputDialog:toggleKeyboard(force_hide)
|
|
||||||
if force_hide and self.keyboard_hidden then return end
|
|
||||||
self.keyboard_hidden = not self.keyboard_hidden
|
|
||||||
self.input = self:getInputText() -- re-init with up-to-date text
|
self.input = self:getInputText() -- re-init with up-to-date text
|
||||||
self:onClose() -- will close keyboard and save view position
|
self:onClose() -- will close keyboard and save view position
|
||||||
self:free()
|
self:free()
|
||||||
self:init()
|
|
||||||
if not self.keyboard_hidden then
|
if force_toggle == false and not visible then
|
||||||
self:onShowKeyboard()
|
-- Already hidden, bye!
|
||||||
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Init needs to know the keyboard's visibility state *before* the widget is actually shown...
|
||||||
|
if force_toggle == true then
|
||||||
|
self.keyboard_visible = true
|
||||||
|
elseif force_toggle == false then
|
||||||
|
self.keyboard_visible = false
|
||||||
|
elseif self._keyboard_was_visible ~= nil then
|
||||||
|
self.keyboard_visible = self._keyboard_was_visible
|
||||||
|
self._keyboard_was_visible = nil
|
||||||
|
else
|
||||||
|
self.keyboard_visible = not visible
|
||||||
|
end
|
||||||
|
self:init()
|
||||||
|
|
||||||
|
-- NOTE: If we ever have non-fullscreen add_nav_bar callers, it might make sense *not* to lock the keyboard there?
|
||||||
|
if self.keyboard_visible then
|
||||||
|
self:lockKeyboard(false)
|
||||||
|
self:onShowKeyboard()
|
||||||
|
else
|
||||||
|
self:onCloseKeyboard()
|
||||||
|
-- Prevent InputText:onTapTextBox from opening the keyboard back up on top of our buttons
|
||||||
|
self:lockKeyboard(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Make sure we refresh the nav bar, as it will have moved, and it belongs to us, not to VK or our input widget...
|
||||||
|
self:refreshButtons()
|
||||||
end
|
end
|
||||||
|
|
||||||
function InputDialog:onKeyboardHeightChanged()
|
function InputDialog:onKeyboardHeightChanged()
|
||||||
|
local visible = self:isKeyboardVisible()
|
||||||
self.input = self:getInputText() -- re-init with up-to-date text
|
self.input = self:getInputText() -- re-init with up-to-date text
|
||||||
self:onClose() -- will close keyboard and save view position
|
self:onClose() -- will close keyboard and save view position
|
||||||
self._input_widget:onCloseWidget() -- proper cleanup of InputText and its keyboard
|
self._input_widget:onCloseWidget() -- proper cleanup of InputText and its keyboard
|
||||||
@ -555,8 +616,11 @@ function InputDialog:onKeyboardHeightChanged()
|
|||||||
self:free()
|
self:free()
|
||||||
-- Restore original text_height (or reset it if none to force recomputing it)
|
-- Restore original text_height (or reset it if none to force recomputing it)
|
||||||
self.text_height = self.orig_text_height or nil
|
self.text_height = self.orig_text_height or nil
|
||||||
|
|
||||||
|
-- Same deal as in toggleKeyboard...
|
||||||
|
self.keyboard_visible = visible
|
||||||
self:init()
|
self:init()
|
||||||
if not self.keyboard_hidden then
|
if self.keyboard_visible then
|
||||||
self:onShowKeyboard()
|
self:onShowKeyboard()
|
||||||
end
|
end
|
||||||
-- Our position on screen has probably changed, so have the full screen refreshed
|
-- Our position on screen has probably changed, so have the full screen refreshed
|
||||||
@ -573,14 +637,16 @@ function InputDialog:onCloseDialog()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function InputDialog:onClose()
|
function InputDialog:onClose()
|
||||||
|
-- Tell our input widget to poke its text widget so that we'll pickup up to date values
|
||||||
|
self._input_widget:resyncPos()
|
||||||
-- Remember current view & position in case of re-init
|
-- Remember current view & position in case of re-init
|
||||||
self._top_line_num = self._input_widget.top_line_num
|
self._top_line_num = self._input_widget.top_line_num
|
||||||
self._charpos = self._input_widget.charpos
|
self._charpos = self._input_widget.charpos
|
||||||
if self.view_pos_callback then
|
if self.view_pos_callback then
|
||||||
-- Give back top line num and cursor position
|
-- This lets the caller store/process the current top line num and cursor position via this callback
|
||||||
self.view_pos_callback(self._top_line_num, self._charpos)
|
self.view_pos_callback(self._top_line_num, self._charpos)
|
||||||
end
|
end
|
||||||
self._input_widget:onCloseKeyboard()
|
self:onCloseKeyboard()
|
||||||
end
|
end
|
||||||
|
|
||||||
function InputDialog:refreshButtons()
|
function InputDialog:refreshButtons()
|
||||||
@ -764,7 +830,7 @@ function InputDialog:_addScrollButtons(nav_bar)
|
|||||||
-- Also add Keyboard hide/show button if we can
|
-- Also add Keyboard hide/show button if we can
|
||||||
if self.fullscreen and not self.readonly then
|
if self.fullscreen and not self.readonly then
|
||||||
table.insert(row, {
|
table.insert(row, {
|
||||||
text = self.keyboard_hidden and "↑⌨" or "↓⌨",
|
text = self.keyboard_visible and "↓⌨" or "↑⌨",
|
||||||
id = "keyboard",
|
id = "keyboard",
|
||||||
callback = function()
|
callback = function()
|
||||||
self:toggleKeyboard()
|
self:toggleKeyboard()
|
||||||
@ -776,8 +842,7 @@ function InputDialog:_addScrollButtons(nav_bar)
|
|||||||
table.insert(row, {
|
table.insert(row, {
|
||||||
text = _("Find"),
|
text = _("Find"),
|
||||||
callback = function()
|
callback = function()
|
||||||
local keyboard_hidden_state = not self.keyboard_hidden
|
self:toggleKeyboard(false) -- hide text editor keyboard
|
||||||
self:toggleKeyboard(true) -- hide text editor keyboard
|
|
||||||
local input_dialog
|
local input_dialog
|
||||||
input_dialog = InputDialog:new{
|
input_dialog = InputDialog:new{
|
||||||
title = _("Enter text to search for"),
|
title = _("Enter text to search for"),
|
||||||
@ -790,21 +855,20 @@ function InputDialog:_addScrollButtons(nav_bar)
|
|||||||
id = "close",
|
id = "close",
|
||||||
callback = function()
|
callback = function()
|
||||||
UIManager:close(input_dialog)
|
UIManager:close(input_dialog)
|
||||||
self.keyboard_hidden = keyboard_hidden_state
|
|
||||||
self:toggleKeyboard()
|
self:toggleKeyboard()
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text = _("Find first"),
|
text = _("Find first"),
|
||||||
callback = function()
|
callback = function()
|
||||||
self:findCallback(keyboard_hidden_state, input_dialog, true)
|
self:findCallback(input_dialog, true)
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text = _("Find next"),
|
text = _("Find next"),
|
||||||
is_enter_default = true,
|
is_enter_default = true,
|
||||||
callback = function()
|
callback = function()
|
||||||
self:findCallback(keyboard_hidden_state, input_dialog)
|
self:findCallback(input_dialog)
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -829,8 +893,7 @@ function InputDialog:_addScrollButtons(nav_bar)
|
|||||||
table.insert(row, {
|
table.insert(row, {
|
||||||
text = _("Go"),
|
text = _("Go"),
|
||||||
callback = function()
|
callback = function()
|
||||||
local keyboard_hidden_state = not self.keyboard_hidden
|
self:toggleKeyboard(false) -- hide text editor keyboard
|
||||||
self:toggleKeyboard(true) -- hide text editor keyboard
|
|
||||||
local cur_line_num, last_line_num = self._input_widget:getLineNums()
|
local cur_line_num, last_line_num = self._input_widget:getLineNums()
|
||||||
local input_dialog
|
local input_dialog
|
||||||
input_dialog = InputDialog:new{
|
input_dialog = InputDialog:new{
|
||||||
@ -846,7 +909,6 @@ function InputDialog:_addScrollButtons(nav_bar)
|
|||||||
id = "close",
|
id = "close",
|
||||||
callback = function()
|
callback = function()
|
||||||
UIManager:close(input_dialog)
|
UIManager:close(input_dialog)
|
||||||
self.keyboard_hidden = keyboard_hidden_state
|
|
||||||
self:toggleKeyboard()
|
self:toggleKeyboard()
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
@ -857,7 +919,6 @@ function InputDialog:_addScrollButtons(nav_bar)
|
|||||||
local new_line_num = tonumber(input_dialog:getInputText())
|
local new_line_num = tonumber(input_dialog:getInputText())
|
||||||
if new_line_num and new_line_num >= 1 and new_line_num <= last_line_num then
|
if new_line_num and new_line_num >= 1 and new_line_num <= last_line_num then
|
||||||
UIManager:close(input_dialog)
|
UIManager:close(input_dialog)
|
||||||
self.keyboard_hidden = keyboard_hidden_state
|
|
||||||
self:toggleKeyboard()
|
self:toggleKeyboard()
|
||||||
self._input_widget:moveCursorToCharPos(self._input_widget:getLineCharPos(new_line_num))
|
self._input_widget:moveCursorToCharPos(self._input_widget:getLineCharPos(new_line_num))
|
||||||
end
|
end
|
||||||
@ -939,11 +1000,10 @@ function InputDialog:_addScrollButtons(nav_bar)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function InputDialog:findCallback(keyboard_hidden_state, input_dialog, find_first)
|
function InputDialog:findCallback(input_dialog, find_first)
|
||||||
self.search_value = input_dialog:getInputText()
|
self.search_value = input_dialog:getInputText()
|
||||||
if self.search_value == "" then return end
|
if self.search_value == "" then return end
|
||||||
UIManager:close(input_dialog)
|
UIManager:close(input_dialog)
|
||||||
self.keyboard_hidden = keyboard_hidden_state
|
|
||||||
self:toggleKeyboard()
|
self:toggleKeyboard()
|
||||||
local start_pos = find_first and 1 or self._charpos + 1
|
local start_pos = find_first and 1 or self._charpos + 1
|
||||||
local char_pos = util.stringSearch(self.input, self.search_value, self.case_sensitive, start_pos)
|
local char_pos = util.stringSearch(self.input, self.search_value, self.case_sensitive, start_pos)
|
||||||
|
@ -32,9 +32,8 @@ local InputText = InputContainer:extend{
|
|||||||
focused = true,
|
focused = true,
|
||||||
parent = nil, -- parent dialog that will be set dirty
|
parent = nil, -- parent dialog that will be set dirty
|
||||||
edit_callback = nil, -- called with true when text modified, false on init or text re-set
|
edit_callback = nil, -- called with true when text modified, false on init or text re-set
|
||||||
scroll_callback = nil, -- called with (low, high) when view is scrolled (cf ScrollTextWidget)
|
scroll_callback = nil, -- called with (low, high) when view is scrolled (c.f., ScrollTextWidget)
|
||||||
scroll_by_pan = false, -- allow scrolling by lines with Pan (needs scroll=true)
|
scroll_by_pan = false, -- allow scrolling by lines with Pan (needs scroll=true)
|
||||||
manage_keyboard_state = true, -- manage keyboard hidden/shown state
|
|
||||||
|
|
||||||
width = nil,
|
width = nil,
|
||||||
height = nil, -- when nil, will be set to original text height (possibly
|
height = nil, -- when nil, will be set to original text height (possibly
|
||||||
@ -54,7 +53,10 @@ local InputText = InputContainer:extend{
|
|||||||
auto_para_direction = false,
|
auto_para_direction = false,
|
||||||
alignment_strict = false,
|
alignment_strict = false,
|
||||||
|
|
||||||
|
readonly = nil, -- will not support a Keyboard widget if true
|
||||||
|
|
||||||
-- for internal use
|
-- for internal use
|
||||||
|
keyboard = nil, -- Keyboard widget (either VirtualKeyboard or PhysicalKeyboard)
|
||||||
text_widget = nil, -- Text Widget for cursor movement, possibly a ScrollTextWidget
|
text_widget = nil, -- Text Widget for cursor movement, possibly a ScrollTextWidget
|
||||||
charlist = nil, -- table of individual chars from input string
|
charlist = nil, -- table of individual chars from input string
|
||||||
charpos = nil, -- position of the cursor, where a new char would be inserted
|
charpos = nil, -- position of the cursor, where a new char would be inserted
|
||||||
@ -65,7 +67,6 @@ local InputText = InputContainer:extend{
|
|||||||
for_measurement_only = nil, -- When the widget is a one-off used to compute text height
|
for_measurement_only = nil, -- When the widget is a one-off used to compute text height
|
||||||
do_select = false, -- to start text selection
|
do_select = false, -- to start text selection
|
||||||
selection_start_pos = nil, -- selection start position
|
selection_start_pos = nil, -- selection start position
|
||||||
is_keyboard_hidden = true, -- to be able to show the keyboard again when it was hidden. (On init, it's the caller's responsibility to call onShowKeyboard, as far as we're concerned, it's hidden)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
-- These may be (internally) overloaded as needed, depending on Device capabilities.
|
-- These may be (internally) overloaded as needed, depending on Device capabilities.
|
||||||
@ -73,6 +74,11 @@ function InputText:initEventListener() end
|
|||||||
function InputText:onFocus() end
|
function InputText:onFocus() end
|
||||||
function InputText:onUnfocus() end
|
function InputText:onUnfocus() end
|
||||||
|
|
||||||
|
-- Resync our position state with our text widget's actual state
|
||||||
|
function InputText:resyncPos()
|
||||||
|
self.charpos, self.top_line_num = self.text_widget:getCharPos()
|
||||||
|
end
|
||||||
|
|
||||||
local function initTouchEvents()
|
local function initTouchEvents()
|
||||||
if Device:isTouchDevice() then
|
if Device:isTouchDevice() then
|
||||||
function InputText:initEventListener()
|
function InputText:initEventListener()
|
||||||
@ -133,8 +139,8 @@ local function initTouchEvents()
|
|||||||
if self.parent.onSwitchFocus then
|
if self.parent.onSwitchFocus then
|
||||||
self.parent:onSwitchFocus(self)
|
self.parent:onSwitchFocus(self)
|
||||||
else
|
else
|
||||||
if self.is_keyboard_hidden and self.manage_keyboard_state then
|
if self.keyboard then
|
||||||
self:onShowKeyboard()
|
self.keyboard:showKeyboard()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- zh keyboard with candidates shown here has _frame_textwidget.dimen = nil.
|
-- zh keyboard with candidates shown here has _frame_textwidget.dimen = nil.
|
||||||
@ -144,7 +150,7 @@ local function initTouchEvents()
|
|||||||
local x = ges.pos.x - self._frame_textwidget.dimen.x - textwidget_offset
|
local x = ges.pos.x - self._frame_textwidget.dimen.x - textwidget_offset
|
||||||
local y = ges.pos.y - self._frame_textwidget.dimen.y - textwidget_offset
|
local y = ges.pos.y - self._frame_textwidget.dimen.y - textwidget_offset
|
||||||
self.text_widget:moveCursorToXY(x, y, true) -- restrict_to_view=true
|
self.text_widget:moveCursorToXY(x, y, true) -- restrict_to_view=true
|
||||||
self.charpos, self.top_line_num = self.text_widget:getCharPos()
|
self:resyncPos()
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@ -512,7 +518,7 @@ function InputText:initTextBox(text, char_added)
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
-- Get back possibly modified charpos and virtual_line_num
|
-- Get back possibly modified charpos and virtual_line_num
|
||||||
self.charpos, self.top_line_num = self.text_widget:getCharPos()
|
self:resyncPos()
|
||||||
|
|
||||||
self._frame_textwidget = FrameContainer:new{
|
self._frame_textwidget = FrameContainer:new{
|
||||||
bordersize = self.bordersize,
|
bordersize = self.bordersize,
|
||||||
@ -676,22 +682,27 @@ dbg:guard(InputText, "onTextInput",
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
function InputText:onShowKeyboard(ignore_first_hold_release)
|
function InputText:onShowKeyboard(ignore_first_hold_release)
|
||||||
Device:startTextInput()
|
if self.keyboard then
|
||||||
|
self.keyboard:showKeyboard(ignore_first_hold_release)
|
||||||
if self.is_keyboard_hidden or not self.manage_keyboard_state then
|
|
||||||
self.keyboard.ignore_first_hold_release = ignore_first_hold_release
|
|
||||||
UIManager:show(self.keyboard)
|
|
||||||
self.is_keyboard_hidden = false
|
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function InputText:onCloseKeyboard()
|
function InputText:onCloseKeyboard()
|
||||||
Device:stopTextInput()
|
if self.keyboard then
|
||||||
|
self.keyboard:hideKeyboard()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if not self.is_keyboard_hidden or not self.manage_keyboard_state then
|
function InputText:isKeyboardVisible()
|
||||||
UIManager:close(self.keyboard)
|
if self.keyboard then
|
||||||
self.is_keyboard_hidden = true
|
return self.keyboard:isVisible()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function InputText:lockKeyboard(toggle)
|
||||||
|
if self.keyboard then
|
||||||
|
return self.keyboard:lockVisibility(toggle)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -873,71 +884,71 @@ end
|
|||||||
function InputText:leftChar()
|
function InputText:leftChar()
|
||||||
if self.charpos == 1 then return end
|
if self.charpos == 1 then return end
|
||||||
self.text_widget:moveCursorLeft()
|
self.text_widget:moveCursorLeft()
|
||||||
self.charpos, self.top_line_num = self.text_widget:getCharPos()
|
self:resyncPos()
|
||||||
end
|
end
|
||||||
|
|
||||||
function InputText:rightChar()
|
function InputText:rightChar()
|
||||||
if self.charpos > #self.charlist then return end
|
if self.charpos > #self.charlist then return end
|
||||||
self.text_widget:moveCursorRight()
|
self.text_widget:moveCursorRight()
|
||||||
self.charpos, self.top_line_num = self.text_widget:getCharPos()
|
self:resyncPos()
|
||||||
end
|
end
|
||||||
|
|
||||||
function InputText:goToStartOfLine()
|
function InputText:goToStartOfLine()
|
||||||
local new_pos = select(1, self:getStringPos({"\n", "\r"}, {"\n", "\r"}))
|
local new_pos = select(1, self:getStringPos({"\n", "\r"}, {"\n", "\r"}))
|
||||||
self.text_widget:moveCursorToCharPos(new_pos)
|
self.text_widget:moveCursorToCharPos(new_pos)
|
||||||
self.charpos, self.top_line_num = self.text_widget:getCharPos()
|
self:resyncPos()
|
||||||
end
|
end
|
||||||
|
|
||||||
function InputText:goToEndOfLine()
|
function InputText:goToEndOfLine()
|
||||||
local new_pos = select(2, self:getStringPos({"\n", "\r"}, {"\n", "\r"})) + 1
|
local new_pos = select(2, self:getStringPos({"\n", "\r"}, {"\n", "\r"})) + 1
|
||||||
self.text_widget:moveCursorToCharPos(new_pos)
|
self.text_widget:moveCursorToCharPos(new_pos)
|
||||||
self.charpos, self.top_line_num = self.text_widget:getCharPos()
|
self:resyncPos()
|
||||||
end
|
end
|
||||||
|
|
||||||
function InputText:goToHome()
|
function InputText:goToHome()
|
||||||
self.text_widget:moveCursorHome()
|
self.text_widget:moveCursorHome()
|
||||||
self.charpos, self.top_line_num = self.text_widget:getCharPos()
|
self:resyncPos()
|
||||||
end
|
end
|
||||||
|
|
||||||
function InputText:goToEnd()
|
function InputText:goToEnd()
|
||||||
self.text_widget:moveCursorEnd()
|
self.text_widget:moveCursorEnd()
|
||||||
self.charpos, self.top_line_num = self.text_widget:getCharPos()
|
self:resyncPos()
|
||||||
end
|
end
|
||||||
|
|
||||||
function InputText:moveCursorToCharPos(char_pos)
|
function InputText:moveCursorToCharPos(char_pos)
|
||||||
self.text_widget:moveCursorToCharPos(char_pos)
|
self.text_widget:moveCursorToCharPos(char_pos)
|
||||||
self.charpos, self.top_line_num = self.text_widget:getCharPos()
|
self:resyncPos()
|
||||||
end
|
end
|
||||||
|
|
||||||
function InputText:upLine()
|
function InputText:upLine()
|
||||||
self.text_widget:moveCursorUp()
|
self.text_widget:moveCursorUp()
|
||||||
self.charpos, self.top_line_num = self.text_widget:getCharPos()
|
self:resyncPos()
|
||||||
end
|
end
|
||||||
|
|
||||||
function InputText:downLine()
|
function InputText:downLine()
|
||||||
if #self.charlist == 0 then return end -- Avoid cursor moving within a hint.
|
if #self.charlist == 0 then return end -- Avoid cursor moving within a hint.
|
||||||
self.text_widget:moveCursorDown()
|
self.text_widget:moveCursorDown()
|
||||||
self.charpos, self.top_line_num = self.text_widget:getCharPos()
|
self:resyncPos()
|
||||||
end
|
end
|
||||||
|
|
||||||
function InputText:scrollDown()
|
function InputText:scrollDown()
|
||||||
self.text_widget:scrollDown()
|
self.text_widget:scrollDown()
|
||||||
self.charpos, self.top_line_num = self.text_widget:getCharPos()
|
self:resyncPos()
|
||||||
end
|
end
|
||||||
|
|
||||||
function InputText:scrollUp()
|
function InputText:scrollUp()
|
||||||
self.text_widget:scrollUp()
|
self.text_widget:scrollUp()
|
||||||
self.charpos, self.top_line_num = self.text_widget:getCharPos()
|
self:resyncPos()
|
||||||
end
|
end
|
||||||
|
|
||||||
function InputText:scrollToTop()
|
function InputText:scrollToTop()
|
||||||
self.text_widget:scrollToTop()
|
self.text_widget:scrollToTop()
|
||||||
self.charpos, self.top_line_num = self.text_widget:getCharPos()
|
self:resyncPos()
|
||||||
end
|
end
|
||||||
|
|
||||||
function InputText:scrollToBottom()
|
function InputText:scrollToBottom()
|
||||||
self.text_widget:scrollToBottom()
|
self.text_widget:scrollToBottom()
|
||||||
self.charpos, self.top_line_num = self.text_widget:getCharPos()
|
self:resyncPos()
|
||||||
end
|
end
|
||||||
|
|
||||||
function InputText:clear()
|
function InputText:clear()
|
||||||
|
@ -218,7 +218,9 @@ end
|
|||||||
function MultiInputDialog:onSwitchFocus(inputbox)
|
function MultiInputDialog:onSwitchFocus(inputbox)
|
||||||
-- unfocus current inputbox
|
-- unfocus current inputbox
|
||||||
self._input_widget:unfocus()
|
self._input_widget:unfocus()
|
||||||
self._input_widget:onCloseKeyboard()
|
-- and close its existing keyboard (via InputDialog's thin wrapper around _input_widget's own method)
|
||||||
|
self:onCloseKeyboard()
|
||||||
|
|
||||||
UIManager:setDirty(nil, function()
|
UIManager:setDirty(nil, function()
|
||||||
return "ui", self.dialog_frame.dimen
|
return "ui", self.dialog_frame.dimen
|
||||||
end)
|
end)
|
||||||
@ -226,7 +228,9 @@ function MultiInputDialog:onSwitchFocus(inputbox)
|
|||||||
-- focus new inputbox
|
-- focus new inputbox
|
||||||
self._input_widget = inputbox
|
self._input_widget = inputbox
|
||||||
self._input_widget:focus()
|
self._input_widget:focus()
|
||||||
self._input_widget:onShowKeyboard()
|
|
||||||
|
-- Make sure we have a (new) visible keyboard
|
||||||
|
self:onShowKeyboard()
|
||||||
end
|
end
|
||||||
|
|
||||||
return MultiInputDialog
|
return MultiInputDialog
|
||||||
|
@ -169,4 +169,11 @@ function PhysicalKeyboard:setupNumericMappingUI()
|
|||||||
self.dimen = keyboard_frame:getSize()
|
self.dimen = keyboard_frame:getSize()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Match VirtualKeyboard's API to ease caller's life
|
||||||
|
function PhysicalKeyboard:lockVisibility() end
|
||||||
|
function PhysicalKeyboard:setVisibility() end
|
||||||
|
function PhysicalKeyboard:isVisible() return true end
|
||||||
|
function PhysicalKeyboard:showKeyboard() end
|
||||||
|
function PhysicalKeyboard:hideKeyboard() end
|
||||||
|
|
||||||
return PhysicalKeyboard
|
return PhysicalKeyboard
|
||||||
|
@ -755,6 +755,8 @@ end
|
|||||||
|
|
||||||
local VirtualKeyboard = FocusManager:extend{
|
local VirtualKeyboard = FocusManager:extend{
|
||||||
name = "VirtualKeyboard",
|
name = "VirtualKeyboard",
|
||||||
|
visible = nil,
|
||||||
|
lock_visibility = false,
|
||||||
covers_footer = true,
|
covers_footer = true,
|
||||||
modal = true,
|
modal = true,
|
||||||
disable_double_tap = true,
|
disable_double_tap = true,
|
||||||
@ -929,11 +931,53 @@ end
|
|||||||
|
|
||||||
function VirtualKeyboard:onShow()
|
function VirtualKeyboard:onShow()
|
||||||
self:_refresh(true)
|
self:_refresh(true)
|
||||||
|
self.visible = true
|
||||||
|
Device:startTextInput()
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function VirtualKeyboard:onCloseWidget()
|
function VirtualKeyboard:onCloseWidget()
|
||||||
self:_refresh(true)
|
self:_refresh(true)
|
||||||
|
self.visible = false
|
||||||
|
-- NOTE: This effectively stops SDL text input when a keyboard is hidden (... but navigational stuff still works).
|
||||||
|
-- If you instead wanted it to be enabled as long as an input dialog is displayed, regardless of VK's state,
|
||||||
|
-- this could be moved to InputDialog's onShow/onCloseWidget handlers (but, it would allow input on unfocused fields).
|
||||||
|
-- NOTE: But something more complex, possibly based on an in-class ref count would have to be implemented in order to be able to deal
|
||||||
|
-- with multiple InputDialogs being shown and closed in asymmetric fashion... Ugh.
|
||||||
|
Device:stopTextInput()
|
||||||
|
end
|
||||||
|
|
||||||
|
function VirtualKeyboard:lockVisibility(toggle)
|
||||||
|
self.lock_visibility = toggle
|
||||||
|
end
|
||||||
|
|
||||||
|
function VirtualKeyboard:setVisibility(toggle)
|
||||||
|
if self.lock_visibility then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if toggle then
|
||||||
|
UIManager:show(self)
|
||||||
|
else
|
||||||
|
self:onClose()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function VirtualKeyboard:isVisible()
|
||||||
|
return self.visible
|
||||||
|
end
|
||||||
|
|
||||||
|
function VirtualKeyboard:showKeyboard(ignore_first_hold_release)
|
||||||
|
if not self:isVisible() then
|
||||||
|
self.ignore_first_hold_release = ignore_first_hold_release
|
||||||
|
self:setVisibility(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function VirtualKeyboard:hideKeyboard()
|
||||||
|
if self:isVisible() then
|
||||||
|
self:setVisibility(false)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function VirtualKeyboard:initLayer(layer)
|
function VirtualKeyboard:initLayer(layer)
|
||||||
|
@ -545,13 +545,13 @@ function TextEditor:editFile(file_path, readonly)
|
|||||||
cursor_at_end = false,
|
cursor_at_end = false,
|
||||||
readonly = readonly,
|
readonly = readonly,
|
||||||
add_nav_bar = true,
|
add_nav_bar = true,
|
||||||
keyboard_hidden = not self.show_keyboard_on_start,
|
keyboard_visible = self.show_keyboard_on_start, -- InputDialog will enforce false if readonly
|
||||||
scroll_by_pan = true,
|
scroll_by_pan = true,
|
||||||
buttons = {buttons_first_row},
|
buttons = {buttons_first_row},
|
||||||
-- Set/save view and cursor position callback
|
-- Store/retrieve view and cursor position callback
|
||||||
view_pos_callback = function(top_line_num, charpos)
|
view_pos_callback = function(top_line_num, charpos)
|
||||||
-- This same callback is called with no argument to get initial position,
|
-- This same callback is called with no arguments on init to retrieve the stored initial position,
|
||||||
-- and with arguments to give back final position when closed.
|
-- and with arguments to store the final position on close.
|
||||||
if top_line_num and charpos then
|
if top_line_num and charpos then
|
||||||
self.last_view_pos[file_path] = {top_line_num, charpos}
|
self.last_view_pos[file_path] = {top_line_num, charpos}
|
||||||
else
|
else
|
||||||
@ -572,7 +572,7 @@ function TextEditor:editFile(file_path, readonly)
|
|||||||
end,
|
end,
|
||||||
-- File saving callback
|
-- File saving callback
|
||||||
save_callback = function(content, closing) -- Will add Save/Close buttons
|
save_callback = function(content, closing) -- Will add Save/Close buttons
|
||||||
if self.readonly then
|
if readonly then
|
||||||
-- We shouldn't be called if read-only, but just in case
|
-- We shouldn't be called if read-only, but just in case
|
||||||
return false, _("File is read only")
|
return false, _("File is read only")
|
||||||
end
|
end
|
||||||
@ -641,8 +641,10 @@ Do you want to keep this file as empty, or do you prefer to delete it?
|
|||||||
|
|
||||||
}
|
}
|
||||||
UIManager:show(input)
|
UIManager:show(input)
|
||||||
|
if self.show_keyboard_on_start and not readonly then
|
||||||
input:onShowKeyboard()
|
input:onShowKeyboard()
|
||||||
-- Note about self.readonly:
|
end
|
||||||
|
-- Note about readonly:
|
||||||
-- We might have liked to still show keyboard even if readonly, just
|
-- We might have liked to still show keyboard even if readonly, just
|
||||||
-- to use the arrow keys for line by line scrolling with cursor.
|
-- to use the arrow keys for line by line scrolling with cursor.
|
||||||
-- But it's easier to just let InputDialog and InputText do their
|
-- But it's easier to just let InputDialog and InputText do their
|
||||||
|
Loading…
Reference in New Issue
Block a user