Show/Hide Virtual keyboard and more keyboard shortcuts (#12162)

This commit is contained in:
David
2024-08-07 17:09:40 +01:00
committed by GitHub
parent 975efae929
commit 2900eef276
12 changed files with 141 additions and 19 deletions

View File

@@ -377,6 +377,8 @@ function FileManager:registerKeyEvents()
self.file_chooser.key_events.Back = { { Device.input.group.Back } }
if Device:hasScreenKB() then
self.key_events.ToggleWifi = { { "ScreenKB", "Home" } }
elseif Device:hasKeyboard() then
self.key_events.ToggleWifi = { { "Shift", "Home" } }
end
if not Device:hasFewKeys() then
-- Also remove the handler assigned to the "Back" key by menu.lua

View File

@@ -1,14 +1,15 @@
local ButtonDialog = require("ui/widget/buttondialog")
local CheckButton = require("ui/widget/checkbutton")
local ConfirmBox = require("ui/widget/confirmbox")
local Device = require("device")
local DocSettings = require("docsettings")
local DocumentRegistry = require("document/documentregistry")
local FileChooser = require("ui/widget/filechooser")
local InfoMessage = require("ui/widget/infomessage")
local InputContainer = require("ui/widget/container/inputcontainer")
local InputDialog = require("ui/widget/inputdialog")
local Menu = require("ui/widget/menu")
local UIManager = require("ui/uimanager")
local WidgetContainer = require("ui/widget/container/widgetcontainer")
local Utf8Proc = require("ffi/utf8proc")
local filemanagerutil = require("apps/filemanager/filemanagerutil")
local lfs = require("libs/libkoreader-lfs")
@@ -17,12 +18,23 @@ local _ = require("gettext")
local N_ = _.ngettext
local T = require("ffi/util").template
local FileSearcher = WidgetContainer:extend{
local FileSearcher = InputContainer:extend{
case_sensitive = false,
include_subfolders = true,
include_metadata = false,
}
function FileSearcher:init()
self:registerKeyEvents()
end
function FileSearcher:registerKeyEvents()
if Device:hasKeyboard() then
self.key_events.ShowFileSearch = { { "Alt", "F" } }
self.key_events.ShowFileSearchBlank = { { "Alt", "Shift", "F" }, event = "ShowFileSearch", args = "" }
end
end
function FileSearcher:onShowFileSearch(search_string)
local search_dialog
local check_button_case, check_button_subfolders, check_button_metadata
@@ -94,6 +106,7 @@ function FileSearcher:onShowFileSearch(search_string)
end
UIManager:show(search_dialog)
search_dialog:onShowKeyboard()
return true
end
function FileSearcher:doSearch()

View File

@@ -6,6 +6,7 @@ local DictQuickLookup = require("ui/widget/dictquicklookup")
local Event = require("ui/event")
local Geom = require("ui/geometry")
local InfoMessage = require("ui/widget/infomessage")
local InputContainer = require("ui/widget/container/inputcontainer")
local InputDialog = require("ui/widget/inputdialog")
local JSON = require("json")
local KeyValuePage = require("ui/widget/keyvaluepage")
@@ -15,7 +16,6 @@ local NetworkMgr = require("ui/network/manager")
local SortWidget = require("ui/widget/sortwidget")
local Trapper = require("ui/trapper")
local UIManager = require("ui/uimanager")
local WidgetContainer = require("ui/widget/container/widgetcontainer")
local ffi = require("ffi")
local C = ffi.C
local ffiUtil = require("ffi/util")
@@ -60,7 +60,7 @@ local function getIfosInDir(path)
return ifos
end
local ReaderDictionary = WidgetContainer:extend{
local ReaderDictionary = InputContainer:extend{
data_dir = nil,
lookup_msg = _("Searching dictionary for:\n%1"),
}
@@ -99,6 +99,8 @@ local function getDictionaryFixHtmlFunc(path)
end
function ReaderDictionary:init()
self:registerKeyEvents()
self.disable_lookup_history = G_reader_settings:isTrue("disable_lookup_history")
self.dicts_order = G_reader_settings:readSetting("dicts_order", {})
self.dicts_disabled = G_reader_settings:readSetting("dicts_disabled", {})
@@ -162,6 +164,12 @@ function ReaderDictionary:init()
end
end
function ReaderDictionary:registerKeyEvents()
if Device:hasKeyboard() then
self.key_events.ShowDictionaryLookup = { { "Alt", "D" } }
end
end
function ReaderDictionary:sortAvailableIfos()
table.sort(available_ifos, function(lifo, rifo)
local lord = self.dicts_order[lifo.file]

View File

@@ -3,6 +3,7 @@ local ButtonDialog = require("ui/widget/buttondialog")
local CheckButton = require("ui/widget/checkbutton")
local Device = require("device")
local InfoMessage = require("ui/widget/infomessage")
local InputContainer = require("ui/widget/container/inputcontainer")
local InputDialog = require("ui/widget/inputdialog")
local Menu = require("ui/widget/menu")
local Notification = require("ui/widget/notification")
@@ -10,7 +11,6 @@ local SpinWidget = require("ui/widget/spinwidget")
local TextBoxWidget = require("ui/widget/textboxwidget")
local UIManager = require("ui/uimanager")
local Utf8Proc = require("ffi/utf8proc")
local WidgetContainer = require("ui/widget/container/widgetcontainer")
local logger = require("logger")
local _ = require("gettext")
local C_ = _.pgettext
@@ -19,7 +19,7 @@ local T = require("ffi/util").template
local DGENERIC_ICON_SIZE = G_defaults:readSetting("DGENERIC_ICON_SIZE")
local ReaderSearch = WidgetContainer:extend{
local ReaderSearch = InputContainer:extend{
direction = 0, -- 0 for search forward, 1 for search backward
case_insensitive = true, -- default to case insensitive
@@ -41,6 +41,8 @@ local ReaderSearch = WidgetContainer:extend{
}
function ReaderSearch:init()
self:registerKeyEvents()
-- number of words before and after the search string in All search results
self.findall_nb_context_words = G_reader_settings:readSetting("fulltext_search_nb_context_words") or 5
self.findall_results_per_page = G_reader_settings:readSetting("fulltext_search_results_per_page") or 10
@@ -81,6 +83,13 @@ SRELL_ERROR_CODES[110] = _("No preceding expression in repetition.")
SRELL_ERROR_CODES[111] = _("Expression too complex, some hits will not be shown.")
SRELL_ERROR_CODES[666] = _("Expression may lead to an extremely long search time.")
function ReaderSearch:registerKeyEvents()
if Device:hasKeyboard() then
self.key_events.ShowFulltextSearchInputBlank = { { "Alt", "Shift", "S" }, event = "ShowFulltextSearchInput", args = "" }
self.key_events.ShowFulltextSearchInputRecent = { { "Alt", "S" }, event = "ShowFulltextSearchInput" }
end
end
function ReaderSearch:addToMainMenu(menu_items)
menu_items.fulltext_search_settings = {
text = _("Fulltext search settings"),
@@ -244,7 +253,7 @@ function ReaderSearch:searchCallback(reverse, text)
end
end
function ReaderSearch:onShowFulltextSearchInput()
function ReaderSearch:onShowFulltextSearchInput(search_string)
local backward_text = ""
local forward_text = ""
if BD.mirroredUILayout() then
@@ -253,7 +262,7 @@ function ReaderSearch:onShowFulltextSearchInput()
self.input_dialog = InputDialog:new{
title = _("Enter text to search for"),
width = math.floor(math.min(Screen:getWidth(), Screen:getHeight()) * 0.9),
input = self.last_search_text or self.ui.doc_settings:readSetting("fulltext_search_last_search_text"),
input = search_string or self.last_search_text or self.ui.doc_settings:readSetting("fulltext_search_last_search_text"),
buttons = {
{
{
@@ -310,6 +319,7 @@ function ReaderSearch:onShowFulltextSearchInput()
UIManager:show(self.input_dialog)
self.input_dialog:onShowKeyboard()
return true
end
function ReaderSearch:onShowSearchDialog(text, direction, regex, case_insensitive)

View File

@@ -1,5 +1,6 @@
local ConfirmBox = require("ui/widget/confirmbox")
local DataStorage = require("datastorage")
local Device = require("device")
local DictQuickLookup = require("ui/widget/dictquicklookup")
local InfoMessage = require("ui/widget/infomessage")
local InputDialog = require("ui/widget/inputdialog")
@@ -28,6 +29,7 @@ local ReaderWikipedia = ReaderDictionary:extend{
}
function ReaderWikipedia:init()
self:registerKeyEvents()
self.wiki_languages = {}
self.ui.menu:registerToMainMenu(self)
if not wikipedia_history then
@@ -35,6 +37,12 @@ function ReaderWikipedia:init()
end
end
function ReaderWikipedia:registerKeyEvents()
if Device:hasKeyboard() then
self.key_events.ShowWikipediaLookup = { { "Alt", "W" } }
end
end
function ReaderWikipedia:lookupInput()
self.input_dialog = InputDialog:new{
title = _("Enter a word or phrase to look up"),

View File

@@ -463,7 +463,8 @@ common_settings.back_in_reader = {
genGenericMenuEntry(_("Go to previous read page"), "back_in_reader", "previous_read_page"),
},
}
if Device:hasKeyboard() then
-- Kindle keyboard does not have a 'Backspace' key
if Device:hasKeyboard() and not Device:hasSymKey() then
common_settings.backspace_as_back = {
text = _("Backspace works as back button"),
checked_func = function()

View File

@@ -3,6 +3,7 @@ local CheckMark = require("ui/widget/checkmark")
local Device = require("device")
local FFIUtil = require("ffi/util")
local Font = require("ui/font")
local InfoMessage = require("ui/widget/infomessage")
local Language = require("ui/language")
local Size = require("ui/size")
local TextWidget = require("ui/widget/textwidget")
@@ -68,7 +69,6 @@ local function genKeyboardLayoutsSubmenu()
if #keyboard_layouts < 4 then
table.insert(keyboard_layouts, lang)
else -- no more space in the 'globe' popup
local InfoMessage = require("ui/widget/infomessage")
UIManager:show(InfoMessage:new{
text = _("Up to four layouts can be enabled."),
timeout = 2,
@@ -163,6 +163,9 @@ local sub_item_table = {
{
text = _("Keyboard appearance settings"),
keep_menu_open = true,
enabled_func = function()
return G_reader_settings:nilOrTrue("virtual_keyboard_enabled")
end,
callback = function(touchmenu_instance)
local InputDialog = require("ui/widget/inputdialog")
input_dialog = InputDialog:new{
@@ -224,7 +227,26 @@ local sub_item_table = {
end,
},
}
if Device:hasKeyboard() or Device:hasScreenKB() then
-- we use same pos. 4 as below so we are always above "keyboard apperance settings"
table.insert(sub_item_table, 4, {
text = _("Show virtual keyboard"),
help_text = _("Enable this setting to always display the virtual keyboard within a text input field. When a field is selected (in focus), you can temporarily toggle the keyboard on/off by pressing 'Shift' + 'Home'."),
checked_func = function()
return G_reader_settings:nilOrTrue("virtual_keyboard_enabled")
end,
callback = function()
G_reader_settings:flipNilOrTrue("virtual_keyboard_enabled")
if G_reader_settings:isFalse("virtual_keyboard_enabled") then
UIManager:show(InfoMessage:new{
text = _("When a text field is selected (in focus), you can temporarily bring up the virtual keyboard by pressing 'Shift' + 'Home'.")
})
end
end,
})
end
if Device:isTouchDevice() then
-- same pos. 4 as above so if both conditions are met we are above "Show virtual keyboard"
table.insert(sub_item_table, 4, {
text = _("Swipe to input additional characters"),
checked_func = function()

View File

@@ -29,6 +29,7 @@ local VerticalGroup = require("ui/widget/verticalgroup")
local VerticalSpan = require("ui/widget/verticalspan")
local logger = require("logger")
local serpent = require("ffi/serpent")
local util = require("util")
local _ = require("gettext")
local Screen = Device.screen
local T = require("ffi/util").template
@@ -894,8 +895,15 @@ function ConfigDialog:init()
}
if Device:hasKeys() then
-- set up keyboard events
local close_keys = Device:hasFewKeys() and { "Back", "Left" } or Device.input.group.Back
self.key_events.Close = { { close_keys } }
local back_group = util.tableDeepCopy(Device.input.group.Back)
if Device:hasFewKeys() then
table.insert(back_group, "Left")
self.key_events.Close = { { back_group } }
else
table.insert(back_group, "Menu")
table.insert(back_group, "AA")
self.key_events.Close = { { back_group } }
end
end
end

View File

@@ -222,6 +222,10 @@ function InputDialog:init()
if self.fullscreen or self.add_nav_bar then
self.deny_keyboard_hiding = true
end
if (Device:hasKeyboard() or Device:hasScreenKB()) and G_reader_settings:isFalse("virtual_keyboard_enabled") then
self.keyboard_visible = false
self.skip_first_show_keyboard = true
end
-- Title & description
self.title_bar = TitleBar:new{
@@ -450,7 +454,7 @@ function InputDialog:init()
end
end
-- If we're fullscreen without a keyboard, make sure only the toggle button can show the keyboard...
-- If we're fullscreen without the virtual keyboard, make sure only the toggle button can bring back the keyboard...
if self.fullscreen and not self.keyboard_visible then
self:lockKeyboard(true)
end
@@ -561,6 +565,11 @@ function InputDialog:onCloseWidget()
end
function InputDialog:onShowKeyboard(ignore_first_hold_release)
-- Don't initiate virtual keyboard when user has a physical keyboard and G_setting(vk_enabled) unchecked.
if self.skip_first_show_keyboard then
self.skip_first_show_keyboard = nil
return
end
-- 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).
@@ -580,6 +589,10 @@ function InputDialog:isKeyboardVisible()
end
function InputDialog:lockKeyboard(toggle)
if (Device:hasKeyboard() or Device:hasScreenKB()) and G_reader_settings:isFalse("virtual_keyboard_enabled") then
-- do not lock the virtual keyboard when user is hiding it, we still *might* want to activate it via shortcuts ("Shift" + "Home") when in need of special characters or symbols
return
end
return self._input_widget:lockKeyboard(toggle)
end

View File

@@ -300,6 +300,8 @@ local function initDPadEvents()
-- Event called by the focusmanager
if self.parent.onSwitchFocus then
self.parent:onSwitchFocus(self)
elseif (Device:hasKeyboard() or Device:hasScreenKB()) and G_reader_settings:isFalse("virtual_keyboard_enabled") then
do end -- luacheck: ignore 541
else
self:onShowKeyboard()
end
@@ -584,6 +586,17 @@ function InputText:focus()
self._frame_textwidget.color = Blitbuffer.COLOR_BLACK
end
-- NOTE: This key_map can be used for keyboards without numeric keys, such as on Kindles with keyboards. It is loosely 'inspired' by the symbol layer on the virtual keyboard but,
-- we have taken the liberty of making some adjustments since:
-- * K3 does not have numeric keys (top row) and,
-- * we want to prioritise the most-likely-used characters for "style tweaks" and note taking
-- (in English, sorry everybody else, there are just not enough keys)
local sym_key_map = {
["Q"] = "!", ["W"] = "?", ["E"] = "-", ["R"] = "_", ["T"] = "%", ["Y"] = "=", ["U"] = "7", ["I"] = "8", ["O"] = "9", ["P"] = "0",
["A"] = "<", ["S"] = ">", ["D"] = "(", ["F"] = ")", ["G"] = "#", ["H"] = "'", ["J"] = "4", ["K"] = "5", ["L"] = "6",
["Z"] = "{", ["X"] = "}", ["C"] = "[", ["V"] = "]", ["B"] = "1", ["N"] = "2", ["M"] = "3", ["."] = ":", ["AA"] = ";",
}
-- Handle real keypresses from a physical keyboard, even if the virtual keyboard
-- is shown. Mostly likely to be in the emulator, but could be Android + BT
-- keyboard, or a "coder's keyboard" Android input method.
@@ -609,9 +622,11 @@ function InputText:onKeyPress(key)
self:leftChar()
elseif key["Right"] then
self:rightChar()
elseif key["Up"] then
-- NOTE: When we are not showing the virtual keyboard, let focusmanger handle up/down keys, as they are used to directly move around the widget
-- seemlessly in and out of text fields and onto virtual buttons like `[cancel] [search dict]`, no need to unfocus first.
elseif key["Up"] and G_reader_settings:nilOrTrue("virtual_keyboard_enabled") then
self:upLine()
elseif key["Down"] then
elseif key["Down"] and G_reader_settings:nilOrTrue("virtual_keyboard_enabled") then
self:downLine()
elseif key["End"] then
self:goToEnd()
@@ -621,7 +636,8 @@ function InputText:onKeyPress(key)
self:addChars("\n")
elseif key["Tab"] then
self:addChars(" ")
elseif key["Back"] then
-- as stated before, we also don't need to unfocus when there is no keyboard, one less key press to exit widgets, yay!
elseif key["Back"] and G_reader_settings:nilOrTrue("virtual_keyboard_enabled") then
if self.focused then
self:unfocus()
end
@@ -641,8 +657,12 @@ function InputText:onKeyPress(key)
end
if not handled and (key["ScreenKB"] or key["Shift"]) then
handled = true
if key["Back"] then
if key["Back"] and Device:hasScreenKB() then
self:delChar()
elseif key["Back"] and Device:hasSymKey() then
self:delToStartOfLine()
elseif key["Del"] and Device:hasSymKey() then
self:delWord()
elseif key["Left"] then
self:leftChar()
elseif key["Right"] then
@@ -664,6 +684,15 @@ function InputText:onKeyPress(key)
handled = false
end
end
if not handled and Device:hasSymKey() then
local symkey = sym_key_map[key.key]
-- Do not match Shift + Sym + 'Alphabet keys'
if symkey and key.modifiers["Sym"] and not key.modifiers["Shift"] then
self:addChars(symkey)
else
handled = false
end
end
if not handled and Device:hasDPad() then
-- FocusManager may turn on alternative key maps.
-- These key map maybe single text keys.
@@ -680,6 +709,10 @@ function InputText:onKeyPress(key)
-- if it is single text char, insert it
local key_code = key.key -- is in upper case
if not Device.isSDL() and #key_code == 1 then
if key["Shift"] and key["Alt"] and key["G"] then
-- Allow the screenshot keyboard-shortcut to work when focus is on InputText
return false
end
if not key["Shift"] then
key_code = string.lower(key_code)
end

View File

@@ -237,7 +237,11 @@ function MultiInputDialog:onSwitchFocus(inputbox)
self._input_widget:focus()
self.focused_field_idx = inputbox.idx
-- Make sure we have a (new) visible keyboard
if (Device:hasKeyboard() or Device:hasScreenKB()) and G_reader_settings:isFalse("virtual_keyboard_enabled") then
-- do not load virtual keyboard when user is hiding it.
return
end
-- Otherwise make sure we have a (new) visible keyboard
self:onShowKeyboard()
end

View File

@@ -1886,7 +1886,7 @@ function TextBoxWidget:moveCursorDown()
end
function TextBoxWidget:moveCursorHome()
self:moveCursorToCharPos(self.vertical_string_list[self.current_line_num].offset)
self:moveCursorToCharPos(self.vertical_string_list[self.current_line_num] and self.vertical_string_list[self.current_line_num].offset or #self.charlist)
end
function TextBoxWidget:moveCursorEnd()