Text editor: rotate (#12658)

This commit is contained in:
hius07
2024-10-29 20:49:43 +02:00
committed by GitHub
parent b42b9a8799
commit 97a7ebefb9
9 changed files with 127 additions and 153 deletions

View File

@@ -60,28 +60,21 @@ local function isFile(file)
return lfs.attributes(file, "mode") == "file" return lfs.attributes(file, "mode") == "file"
end end
function FileManager:onSetRotationMode(rotation)
if rotation ~= nil and rotation ~= Screen:getRotationMode() then
Screen:setRotationMode(rotation)
if FileManager.instance then
self:reinit(self.path, self.focused_file)
end
end
return true
end
function FileManager:onPhysicalKeyboardConnected()
-- So that the key navigation shortcuts apply right away.
-- This will also naturally call registerKeyEvents
self:reinit(self.path, self.focused_file)
end
FileManager.onPhysicalKeyboardDisconnected = FileManager.onPhysicalKeyboardConnected
function FileManager:setRotationMode() function FileManager:setRotationMode()
local locked = G_reader_settings:isTrue("lock_rotation") local locked = G_reader_settings:isTrue("lock_rotation")
if not locked then if not locked then
local rotation_mode = G_reader_settings:readSetting("fm_rotation_mode") or Screen.DEVICE_ROTATED_UPRIGHT local mode = G_reader_settings:readSetting("fm_rotation_mode") or Screen.DEVICE_ROTATED_UPRIGHT
self:onSetRotationMode(rotation_mode) self:onSetRotationMode(mode)
end
end
function FileManager:onSetRotationMode(mode)
local old_mode = Screen:getRotationMode()
if mode ~= nil and mode ~= old_mode then
Screen:setRotationMode(mode)
if FileManager.instance then
self:rotate()
end
end end
end end
@@ -728,6 +721,8 @@ function FileManager:tapPlus()
end end
function FileManager:reinit(path, focused_file) function FileManager:reinit(path, focused_file)
path = path or self.path
focused_file = focused_file or self.focused_file
UIManager:flushSettings() UIManager:flushSettings()
self.dimen = Screen:getSize() self.dimen = Screen:getSize()
-- backup the root path and path items -- backup the root path and path items
@@ -748,6 +743,13 @@ function FileManager:reinit(path, focused_file)
-- self:onRefresh() -- self:onRefresh()
end end
FileManager.rotate = FileManager.reinit
-- So that the key navigation shortcuts apply right away.
-- This will also naturally call registerKeyEvents
FileManager.onPhysicalKeyboardConnected = FileManager.reinit
FileManager.onPhysicalKeyboardDisconnected = FileManager.reinit
function FileManager:getCurrentDir() function FileManager:getCurrentDir()
return FileManager.instance and FileManager.instance.file_chooser.path return FileManager.instance and FileManager.instance.file_chooser.path
end end

View File

@@ -2454,11 +2454,11 @@ function ReaderFooter:onSwapPageTurnButtons()
end end
ReaderFooter.onToggleReadingOrder = ReaderFooter.onSwapPageTurnButtons ReaderFooter.onToggleReadingOrder = ReaderFooter.onSwapPageTurnButtons
function ReaderFooter:onSetRotationMode() function ReaderFooter:onSetDimensions()
self:updateFooterContainer() self:updateFooterContainer()
self:resetLayout(true) self:resetLayout(true)
end end
ReaderFooter.onScreenResize = ReaderFooter.onSetRotationMode ReaderFooter.onScreenResize = ReaderFooter.onSetDimensions
function ReaderFooter:onSetPageHorizMargins(h_margins) function ReaderFooter:onSetPageHorizMargins(h_margins)
if self.settings.progress_margin then if self.settings.progress_margin then

View File

@@ -1,47 +0,0 @@
local InputContainer = require("ui/widget/container/inputcontainer")
local Device = require("device")
local Event = require("ui/event")
local _ = require("gettext")
local ReaderRotation = InputContainer:extend{
current_rotation = 0,
}
function ReaderRotation:init()
self:registerKeyEvents()
-- NOP our own gesture handling
self.ges_events = nil
end
function ReaderRotation:onGesture() end
function ReaderRotation:registerKeyEvents()
if Device:hasKeyboard() then
self.key_events = {
-- these will all generate the same event, just with different arguments
RotateLeft = {
{ "J" },
event = "Rotate",
args = -90
},
RotateRight = {
{ "K" },
event = "Rotate",
args = 90
},
}
end
end
ReaderRotation.onPhysicalKeyboardConnected = ReaderRotation.registerKeyEvents
--- @todo Reset rotation on new document, maybe on new page?
--- @fixme: More importantly, this breaks rendering, c.f., `Document:renderPage`
-- A modern implementation of this feature is available in Dispatcher via the `IterateRotation` Event.
function ReaderRotation:onRotate(rotate_by)
self.current_rotation = (self.current_rotation + rotate_by) % 360
self.ui:handleEvent(Event:new("RotationUpdate", self.current_rotation))
return true
end
return ReaderRotation

View File

@@ -836,13 +836,15 @@ function ReaderView:restoreViewContext(ctx)
return false return false
end end
function ReaderView:onSetRotationMode(rotation) function ReaderView:onSetRotationMode(mode)
if rotation ~= nil then local old_mode = Screen:getRotationMode()
local old_rotation = Screen:getRotationMode() if mode ~= nil and mode ~= old_mode then
if rotation == old_rotation then Screen:setRotationMode(mode)
return self:rotate(mode, old_mode)
end end
end
function ReaderView:rotate(mode, old_mode)
-- NOTE: We cannot rely on getScreenMode, as it actually checks the screen dimensions, instead of the rotation mode. -- NOTE: We cannot rely on getScreenMode, as it actually checks the screen dimensions, instead of the rotation mode.
-- (i.e., it returns how the screen *looks* like, not how it's oriented relative to its native layout). -- (i.e., it returns how the screen *looks* like, not how it's oriented relative to its native layout).
-- This would horribly break if you started in Portrait (both rotation and visually), -- This would horribly break if you started in Portrait (both rotation and visually),
@@ -850,26 +852,18 @@ function ReaderView:onSetRotationMode(rotation)
-- If you then attempted to switch to a Landscape *rotation*, it would mistakenly think the layout hadn't changed! -- If you then attempted to switch to a Landscape *rotation*, it would mistakenly think the layout hadn't changed!
-- So, instead, as we're concerned with *rotation* layouts, just compare the two. -- So, instead, as we're concerned with *rotation* layouts, just compare the two.
-- We use LinuxFB-style constants, so, Portraits are even, Landscapes are odds, making this trivial. -- We use LinuxFB-style constants, so, Portraits are even, Landscapes are odds, making this trivial.
local matching_orientation = bit.band(rotation, 1) == bit.band(old_rotation, 1) local matching_orientation = bit.band(mode, 1) == bit.band(old_mode, 1)
if matching_orientation then
if rotation ~= old_rotation and matching_orientation then -- No layout change, just rotate & repaint with a flash
-- No layout change, just rotate & repaint with a flash UIManager:setDirty(self.dialog, "full")
Screen:setRotationMode(rotation) else
UIManager:setDirty(self.dialog, "full") UIManager:setDirty(nil, "full") -- SetDimensions will only request a partial, we want a flash
Notification:notify(T(_("Rotation mode set to: %1"), optionsutil:getOptionText("SetRotationMode", rotation))) local new_screen_size = Screen:getSize()
return self.ui:handleEvent(Event:new("SetDimensions", new_screen_size))
end self.ui:onScreenResize(new_screen_size)
self.ui:handleEvent(Event:new("InitScrollPageStates"))
Screen:setRotationMode(rotation)
end end
Notification:notify(T(_("Rotation mode set to: %1"), optionsutil:getOptionText("SetRotationMode", mode)))
UIManager:setDirty(nil, "full") -- SetDimensions will only request a partial, we want a flash
local new_screen_size = Screen:getSize()
self.ui:handleEvent(Event:new("SetDimensions", new_screen_size))
self.ui:onScreenResize(new_screen_size)
self.ui:handleEvent(Event:new("InitScrollPageStates"))
Notification:notify(T(_("Rotation mode set to: %1"), optionsutil:getOptionText("SetRotationMode", rotation)))
return
end end
function ReaderView:onSetDimensions(dimensions) function ReaderView:onSetDimensions(dimensions)
@@ -975,7 +969,7 @@ function ReaderView:onBBoxUpdate(bbox)
self.use_bbox = bbox and true or false self.use_bbox = bbox and true or false
end end
--- @note: From ReaderRotation, which is broken and disabled. --- @note: From ReaderRotation, which was broken, and has been removed in #12658
function ReaderView:onRotationUpdate(rotation) function ReaderView:onRotationUpdate(rotation)
self.state.rotation = rotation self.state.rotation = rotation
self:recalculate() self:recalculate()

View File

@@ -322,7 +322,7 @@ function ReaderZooming:onRestoreDimensions(dimensions)
self:setZoom() self:setZoom()
end end
--- @note: From ReaderRotation, which is broken and disabled. --- @note: From ReaderRotation, which was broken, and has been removed in #12658
function ReaderZooming:onRotationUpdate(rotation) function ReaderZooming:onRotationUpdate(rotation)
self.rotation = rotation self.rotation = rotation
self:setZoom() self:setZoom()

View File

@@ -43,7 +43,6 @@ local ReaderLink = require("apps/reader/modules/readerlink")
local ReaderMenu = require("apps/reader/modules/readermenu") local ReaderMenu = require("apps/reader/modules/readermenu")
local ReaderPageMap = require("apps/reader/modules/readerpagemap") local ReaderPageMap = require("apps/reader/modules/readerpagemap")
local ReaderPanning = require("apps/reader/modules/readerpanning") local ReaderPanning = require("apps/reader/modules/readerpanning")
--local ReaderRotation = require("apps/reader/modules/readerrotation")
local ReaderPaging = require("apps/reader/modules/readerpaging") local ReaderPaging = require("apps/reader/modules/readerpaging")
local ReaderRolling = require("apps/reader/modules/readerrolling") local ReaderRolling = require("apps/reader/modules/readerrolling")
local ReaderSearch = require("apps/reader/modules/readersearch") local ReaderSearch = require("apps/reader/modules/readersearch")
@@ -159,15 +158,6 @@ function ReaderUI:init()
view = self.view, view = self.view,
ui = self ui = self
}) })
-- (legacy, and defunct) rotation controller
--- @fixme: Tripping this would break rendering, c.f., `Document:renderPage`
--[[
self:registerModule("rotation", ReaderRotation:new{
dialog = self.dialog,
view = self.view,
ui = self
})
--]]
-- Handmade/custom ToC and hidden flows -- Handmade/custom ToC and hidden flows
self:registerModule("handmade", ReaderHandMade:new{ self:registerModule("handmade", ReaderHandMade:new{
dialog = self.dialog, dialog = self.dialog,

View File

@@ -501,9 +501,9 @@ function Document:renderPage(pageno, rect, zoom, rotation, gamma, hinting)
-- Make the context match the rotation, -- Make the context match the rotation,
-- by pointing at the rotated origin via coordinates offsets. -- by pointing at the rotated origin via coordinates offsets.
-- NOTE: We rotate our *Screen* bb on rotation (SetRotationMode), not the document, -- NOTE: We rotate our *Screen* bb on rotation (SetRotationMode), not the document,
-- so we hardly ever exercise this codepath... -- so we hardly ever exercize this codepath...
-- AFAICT, the only thing that will *ever* (attempt to) rotate the document is ReaderRotation's key bindings (RotationUpdate). -- AFAICT, the only thing that *ever* (attempted to) rotate the document was ReaderRotation's key bindings (RotationUpdate).
--- @fixme: And whaddayano, it's broken ;). The aptly named key binds in question are J/K, I shit you not. --- @note: It was broken as all hell (it had likely never worked outside of its original implementation in KPV), and has been removed in #12658
if rotation == 90 then if rotation == 90 then
dc:setOffset(page_size.w, 0) dc:setOffset(page_size.w, 0)
elseif rotation == 180 then elseif rotation == 180 then

View File

@@ -237,6 +237,8 @@ function InputDialog:init()
title_multilines = true, title_multilines = true,
bottom_v_padding = self.bottom_v_padding, bottom_v_padding = self.bottom_v_padding,
info_text = self.description, info_text = self.description,
left_icon = self.title_bar_left_icon,
left_icon_tap_callback = self.title_bar_left_icon_tap_callback,
show_parent = self, show_parent = self,
} }
@@ -475,6 +477,31 @@ function InputDialog:init()
end end
end end
function InputDialog:reinit()
local visible = self:isKeyboardVisible()
self.input = self:getInputText() -- re-init with up-to-date text
self:onClose() -- will close keyboard and save view position
self._input_widget:onCloseWidget() -- proper cleanup of InputText and its keyboard
if self._added_widgets then
-- prevent these externally added widgets from being freed as :init() will re-add them
for i = 1, #self._added_widgets do
table.remove(self.vgroup, #self.vgroup-2)
end
end
self:free()
-- Restore original text_height (or reset it if none to force recomputing it)
self.text_height = self.orig_text_height or nil
-- Same deal as in toggleKeyboard...
self.keyboard_visible = visible and true or false
self:init()
if self.keyboard_visible then
self:onShowKeyboard()
end
-- Our position on screen has probably changed, so have the full screen refreshed
UIManager:setDirty("all", "flashui")
end
function InputDialog:addWidget(widget, re_init) function InputDialog:addWidget(widget, re_init)
table.insert(self.layout, #self.layout, {widget}) table.insert(self.layout, #self.layout, {widget})
if not re_init then -- backup widget for re-init if not re_init then -- backup widget for re-init
@@ -677,30 +704,7 @@ function InputDialog:onKeyboardClosed()
end end
end end
function InputDialog:onKeyboardHeightChanged() InputDialog.onKeyboardHeightChanged = InputDialog.reinit
local visible = self:isKeyboardVisible()
self.input = self:getInputText() -- re-init with up-to-date text
self:onClose() -- will close keyboard and save view position
self._input_widget:onCloseWidget() -- proper cleanup of InputText and its keyboard
if self._added_widgets then
-- prevent these externally added widgets from being freed as :init() will re-add them
for i = 1, #self._added_widgets do
table.remove(self.vgroup, #self.vgroup-2)
end
end
self:free()
-- Restore original text_height (or reset it if none to force recomputing it)
self.text_height = self.orig_text_height or nil
-- Same deal as in toggleKeyboard...
self.keyboard_visible = visible
self:init()
if self.keyboard_visible then
self:onShowKeyboard()
end
-- Our position on screen has probably changed, so have the full screen refreshed
UIManager:setDirty("all", "flashui")
end
function InputDialog:onCloseDialog() function InputDialog:onCloseDialog()
local close_button = self.button_table:getButtonById("close") local close_button = self.button_table:getButtonById("close")
@@ -724,6 +728,15 @@ function InputDialog:onClose()
self:onCloseKeyboard() self:onCloseKeyboard()
end end
function InputDialog:onSetRotationMode(mode)
if self.rotation_enabled and mode ~= nil then -- Text editor only
self.rotation_mode_backup = self.rotation_mode_backup or Screen:getRotationMode() -- backup only initial mode
Screen:setRotationMode(mode)
self:reinit()
return true -- we are the upper widget, stop event propagation
end
end
function InputDialog:refreshButtons() function InputDialog:refreshButtons()
-- Using what ought to be enough: -- Using what ought to be enough:
-- return "ui", self.button_table.dimen -- return "ui", self.button_table.dimen

View File

@@ -1,4 +1,5 @@
local BD = require("ui/bidi") local BD = require("ui/bidi")
local ButtonDialog = require("ui/widget/buttondialog")
local ConfirmBox = require("ui/widget/confirmbox") local ConfirmBox = require("ui/widget/confirmbox")
local DataStorage = require("datastorage") local DataStorage = require("datastorage")
local Dispatcher = require("dispatcher") local Dispatcher = require("dispatcher")
@@ -438,7 +439,7 @@ function TextEditor:checkEditFile(file_path, from_history, possibly_new_file)
-- No need to warn if readonly, the user will know it when we open -- No need to warn if readonly, the user will know it when we open
-- without keyboard and the Save button says "Read only". -- without keyboard and the Save button says "Read only".
local readonly = true local readonly = true
local file = io.open(file_path, 'r+b') local file = io.open(file_path, "r+b")
if file then if file then
file:close() file:close()
readonly = false readonly = false
@@ -474,18 +475,6 @@ function TextEditor:checkEditFile(file_path, from_history, possibly_new_file)
end end
end end
function TextEditor:readFileContent(file_path)
local file = io.open(file_path, "rb")
if not file then
-- We checked file existence before, so assume it's
-- because it's a new file
return ""
end
local file_content = file:read("*all")
file:close()
return file_content
end
function TextEditor:saveFileContent(file_path, content) function TextEditor:saveFileContent(file_path, content)
local ok, err = util.writeToFile(content, file_path) local ok, err = util.writeToFile(content, file_path)
if ok then if ok then
@@ -514,7 +503,6 @@ function TextEditor:editFile(file_path, readonly)
local directory, filename = util.splitFilePathName(file_path) -- luacheck: no unused local directory, filename = util.splitFilePathName(file_path) -- luacheck: no unused
local filename_without_suffix, filetype = util.splitFileNameSuffix(filename) -- luacheck: no unused local filename_without_suffix, filetype = util.splitFileNameSuffix(filename) -- luacheck: no unused
local is_lua = filetype:lower() == "lua" local is_lua = filetype:lower() == "lua"
local input
local para_direction_rtl = nil -- use UI language direction local para_direction_rtl = nil -- use UI language direction
if self.force_ltr_para_direction then if self.force_ltr_para_direction then
para_direction_rtl = false -- force LTR para_direction_rtl = false -- force LTR
@@ -524,7 +512,7 @@ function TextEditor:editFile(file_path, readonly)
table.insert(buttons_first_row, { table.insert(buttons_first_row, {
text = _("Lua check"), text = _("Lua check"),
callback = function() callback = function()
local parse_error = util.checkLuaSyntax(input:getInputText()) local parse_error = util.checkLuaSyntax(self.input:getInputText())
if parse_error then if parse_error then
UIManager:show(InfoMessage:new{ UIManager:show(InfoMessage:new{
text = T(_("Lua syntax check failed:\n\n%1"), parse_error) text = T(_("Lua syntax check failed:\n\n%1"), parse_error)
@@ -542,16 +530,16 @@ function TextEditor:editFile(file_path, readonly)
text = _("QR"), text = _("QR"),
callback = function() callback = function()
UIManager:show(QRMessage:new{ UIManager:show(QRMessage:new{
text = input:getInputText(), text = self.input:getInputText(),
height = Screen:getHeight(), height = Screen:getHeight(),
width = Screen:getWidth() width = Screen:getWidth()
}) })
end, end,
}) })
end end
input = InputDialog:new{ self.input = InputDialog:new{
title = filename, title = filename,
input = self:readFileContent(file_path), input = util.readFromFile(file_path, "rb"),
input_face = Font:getFace(self.font_face, self.font_size), input_face = Font:getFace(self.font_face, self.font_size),
para_direction_rtl = para_direction_rtl, para_direction_rtl = para_direction_rtl,
auto_para_direction = self.auto_para_direction, auto_para_direction = self.auto_para_direction,
@@ -561,6 +549,9 @@ 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,
title_bar_left_icon = "appbar.menu",
title_bar_left_icon_tap_callback = function() self:showMenu() end,
rotation_enabled = true,
keyboard_visible = self.show_keyboard_on_start, -- InputDialog will enforce false if readonly 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},
@@ -580,10 +571,13 @@ function TextEditor:editFile(file_path, readonly)
end, end,
-- File restoring callback -- File restoring callback
reset_callback = function(content) -- Will add a Reset button reset_callback = function(content) -- Will add a Reset button
return self:readFileContent(file_path), _("Text reset to last saved content") return util.readFromFile(file_path, "rb") or "", _("Text reset to last saved content")
end, end,
-- Close callback -- Close callback
close_callback = function() close_callback = function()
if self.input.rotation_mode_backup and self.input.rotation_mode_backup ~= Screen:getRotationMode() then
Screen:setRotationMode(self.input.rotation_mode_backup)
end
self:execWhenDoneFunc() self:execWhenDoneFunc()
end, end,
-- File saving callback -- File saving callback
@@ -656,9 +650,9 @@ Do you want to keep this file as empty, or do you prefer to delete it?
end, end,
} }
UIManager:show(input) UIManager:show(self.input)
if self.show_keyboard_on_start and not readonly then if self.show_keyboard_on_start and not readonly then
input:onShowKeyboard() self.input:onShowKeyboard()
end end
-- Note about readonly: -- 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
@@ -688,4 +682,32 @@ function TextEditor:quickEditFile(file_path, done_callback, possible_new_file)
self:checkEditFile(file_path, possible_new_file or false) self:checkEditFile(file_path, possible_new_file or false)
end end
-- TitleBar left button tap
function TextEditor:showMenu()
local dialog
local buttons = {}
local optionsutil = require("ui/data/optionsutil")
for i, mode in ipairs(optionsutil.rotation_modes) do
buttons[i] = {{
text = optionsutil.rotation_labels[i],
enabled_func = function()
return optionsutil.rotation_modes[i] ~= Screen:getRotationMode()
end,
callback = function()
UIManager:close(dialog)
self.input:onSetRotationMode(optionsutil.rotation_modes[i])
end,
}}
end
dialog = ButtonDialog:new{
shrink_unneeded_width = true,
buttons = buttons,
anchor = function()
return self.input.title_bar.left_button.image.dimen
end,
modal = true,
}
UIManager:show(dialog)
end
return TextEditor return TextEditor