local BD = require ( " ui/bidi " )
local CenterContainer = require ( " ui/widget/container/centercontainer " )
local ConfirmBox = require ( " ui/widget/confirmbox " )
local Device = require ( " device " )
local Event = require ( " ui/event " )
local FFIUtil = require ( " ffi/util " )
local InputContainer = require ( " ui/widget/container/inputcontainer " )
local PluginLoader = require ( " pluginloader " )
local SetDefaults = require ( " apps/filemanager/filemanagersetdefaults " )
[RFC] Pagination UI shenanigans (#7335)
* Menu/KeyValuePage/ReaderGoTo: Unify the dialogs. (Generally, "Enter page number" as title, and "Go to page" as OK button).
* Allow *tapping* on pagination buttons, too. Added spacers around the text to accommodate for that.
* Disable input handlers when <= 1 pages, while still printing the label in black.
* Always display both the label and the chevrons, even on single page content. (Menu being an exception, because it can handle showing no content at all, in which case we hide the chevrons).
* KVP: Tweak the pagination buttons layout in order to have consistent centering, regardless of whether the return arrow is enabled or not. (Also, match Menu's layout, more or less).
* Menu: Minor layout tweaks to follow the KVP tweaks above. Fixes, among possibly other things, buttons in (non-FM) "List" menus overlapping the final entry (e.g., OPDS), and popout menus with a border being misaligned (e.g., Calibre, Find a file).
* CalendarView: Minor layout tweaks to follow the KVP tweaks. Ensures the pagination buttons are laid out in the same way as everywhere else (they used to be a wee bit higher).
4 years ago
local Size = require ( " ui/size " )
local UIManager = require ( " ui/uimanager " )
local Screen = Device.screen
local dbg = require ( " dbg " )
local lfs = require ( " libs/libkoreader-lfs " )
local logger = require ( " logger " )
local util = require ( " util " )
local _ = require ( " gettext " )
local T = FFIUtil.template
local FileManagerMenu = InputContainer : extend {
tab_item_table = nil ,
Clarify our OOP semantics across the codebase (#9586)
Basically:
* Use `extend` for class definitions
* Use `new` for object instantiations
That includes some minor code cleanups along the way:
* Updated `Widget`'s docs to make the semantics clearer.
* Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283)
* Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass).
* Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events.
* Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier.
* Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references.
* ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak).
* Terminal: Make sure the shell is killed on plugin teardown.
* InputText: Fix Home/End/Del physical keys to behave sensibly.
* InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...).
* OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of.
* ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed!
* Kobo: Minor code cleanups.
2 years ago
menu_items = nil , -- table, mandatory
registered_widgets = nil ,
}
function FileManagerMenu : init ( )
self.menu_items = {
[ " KOMenu:menu_buttons " ] = {
-- top menu
} ,
-- items in top menu
filemanager_settings = {
icon = " appbar.filebrowser " ,
} ,
setting = {
icon = " appbar.settings " ,
} ,
tools = {
icon = " appbar.tools " ,
} ,
search = {
icon = " appbar.search " ,
} ,
main = {
icon = " appbar.menu " ,
} ,
}
self.registered_widgets = { }
if Device : hasKeys ( ) then
self.key_events = {
ShowMenu = { { " Menu " } , doc = " show menu " } ,
}
end
self.activation_menu = G_reader_settings : readSetting ( " activate_menu " )
if self.activation_menu == nil then
self.activation_menu = " swipe_tap "
end
end
function FileManagerMenu : initGesListener ( )
if not Device : isTouchDevice ( ) then return end
local DTAP_ZONE_MENU = G_defaults : readSetting ( " DTAP_ZONE_MENU " )
local DTAP_ZONE_MENU_EXT = G_defaults : readSetting ( " DTAP_ZONE_MENU_EXT " )
self : registerTouchZones ( {
{
id = " filemanager_tap " ,
ges = " tap " ,
screen_zone = {
ratio_x = DTAP_ZONE_MENU.x , ratio_y = DTAP_ZONE_MENU.y ,
ratio_w = DTAP_ZONE_MENU.w , ratio_h = DTAP_ZONE_MENU.h ,
} ,
handler = function ( ges ) return self : onTapShowMenu ( ges ) end ,
} ,
{
id = " filemanager_ext_tap " ,
ges = " tap " ,
screen_zone = {
ratio_x = DTAP_ZONE_MENU_EXT.x , ratio_y = DTAP_ZONE_MENU_EXT.y ,
ratio_w = DTAP_ZONE_MENU_EXT.w , ratio_h = DTAP_ZONE_MENU_EXT.h ,
} ,
overrides = {
" filemanager_tap " ,
} ,
handler = function ( ges ) return self : onTapShowMenu ( ges ) end ,
} ,
{
id = " filemanager_swipe " ,
ges = " swipe " ,
screen_zone = {
ratio_x = DTAP_ZONE_MENU.x , ratio_y = DTAP_ZONE_MENU.y ,
ratio_w = DTAP_ZONE_MENU.w , ratio_h = DTAP_ZONE_MENU.h ,
} ,
overrides = {
" rolling_swipe " ,
" paging_swipe " ,
} ,
handler = function ( ges ) return self : onSwipeShowMenu ( ges ) end ,
} ,
{
id = " filemanager_ext_swipe " ,
ges = " swipe " ,
screen_zone = {
ratio_x = DTAP_ZONE_MENU_EXT.x , ratio_y = DTAP_ZONE_MENU_EXT.y ,
ratio_w = DTAP_ZONE_MENU_EXT.w , ratio_h = DTAP_ZONE_MENU_EXT.h ,
} ,
overrides = {
" filemanager_swipe " ,
} ,
handler = function ( ges ) return self : onSwipeShowMenu ( ges ) end ,
} ,
} )
end
function FileManagerMenu : onOpenLastDoc ( )
local last_file = G_reader_settings : readSetting ( " lastfile " )
if not last_file or lfs.attributes ( last_file , " mode " ) ~= " file " then
local InfoMessage = require ( " ui/widget/infomessage " )
UIManager : show ( InfoMessage : new {
text = _ ( " Cannot open last document " ) ,
} )
return
end
ReaderUI: Saner FM/RD lifecycle
* Ensure that going from one to the other tears down the former and
its plugins before instantiating the latter and its plugins.
UIManager: Unify Event sending & broadcasting
* Make the two behave the same way (walk the widget stack from top to
bottom), and properly handle the window stack shrinking shrinking
*and* growing.
Previously, broadcasting happened bottom-to-top and didn't really
handle the list shrinking/growing, while sending only handled the list
shrinking by a single element, and hopefully that element being the one
the event was just sent to.
These two items combined allowed us to optimize suboptimal
refresh behavior with Menu and other Menu classes when
opening/closing a document.
e.g., the "opening document" Notification is now properly regional,
and the "open last doc" option no longer flashes like a crazy person
anymore.
Plugins: Allow optimizing Menu refresh with custom menus, too.
Requires moving Menu's close_callback *after* onMenuSelect, which, eh,
probably makes sense, and is probably harmless in the grand scheme of
things.
3 years ago
-- Only close menu if we were called from the menu
if self.menu_container then
ReaderUI: Saner FM/RD lifecycle
* Ensure that going from one to the other tears down the former and
its plugins before instantiating the latter and its plugins.
UIManager: Unify Event sending & broadcasting
* Make the two behave the same way (walk the widget stack from top to
bottom), and properly handle the window stack shrinking shrinking
*and* growing.
Previously, broadcasting happened bottom-to-top and didn't really
handle the list shrinking/growing, while sending only handled the list
shrinking by a single element, and hopefully that element being the one
the event was just sent to.
These two items combined allowed us to optimize suboptimal
refresh behavior with Menu and other Menu classes when
opening/closing a document.
e.g., the "opening document" Notification is now properly regional,
and the "open last doc" option no longer flashes like a crazy person
anymore.
Plugins: Allow optimizing Menu refresh with custom menus, too.
Requires moving Menu's close_callback *after* onMenuSelect, which, eh,
probably makes sense, and is probably harmless in the grand scheme of
things.
3 years ago
-- Mimic's FileManager's onShowingReader refresh optimizations
self.ui . tearing_down = true
self.ui . dithered = nil
self : onCloseFileManagerMenu ( )
end
ReaderUI: Saner FM/RD lifecycle
* Ensure that going from one to the other tears down the former and
its plugins before instantiating the latter and its plugins.
UIManager: Unify Event sending & broadcasting
* Make the two behave the same way (walk the widget stack from top to
bottom), and properly handle the window stack shrinking shrinking
*and* growing.
Previously, broadcasting happened bottom-to-top and didn't really
handle the list shrinking/growing, while sending only handled the list
shrinking by a single element, and hopefully that element being the one
the event was just sent to.
These two items combined allowed us to optimize suboptimal
refresh behavior with Menu and other Menu classes when
opening/closing a document.
e.g., the "opening document" Notification is now properly regional,
and the "open last doc" option no longer flashes like a crazy person
anymore.
Plugins: Allow optimizing Menu refresh with custom menus, too.
Requires moving Menu's close_callback *after* onMenuSelect, which, eh,
probably makes sense, and is probably harmless in the grand scheme of
things.
3 years ago
local ReaderUI = require ( " apps/reader/readerui " )
ReaderUI : showReader ( last_file )
end
function FileManagerMenu : setUpdateItemTable ( )
-- setting tab
self.menu_items . filebrowser_settings = {
text = _ ( " Settings " ) ,
sub_item_table = {
{
text = _ ( " Show hidden files " ) ,
checked_func = function ( ) return self.ui . file_chooser.show_hidden end ,
callback = function ( ) self.ui : toggleHiddenFiles ( ) end ,
} ,
{
text = _ ( " Show unsupported files " ) ,
checked_func = function ( ) return self.ui . file_chooser.show_unsupported end ,
callback = function ( ) self.ui : toggleUnsupportedFiles ( ) end ,
separator = true ,
} ,
{
text = _ ( " Classic mode settings " ) ,
sub_item_table = {
{
text = _ ( " Items per page " ) ,
help_text = _ ( [ [ This sets the number of items per page in :
- File browser , history and favorites in ' classic ' display mode
- Search results and folder shortcuts
- File and folder selection
- Calibre and OPDS browsers / search results ] ] ) ,
callback = function ( )
local SpinWidget = require ( " ui/widget/spinwidget " )
local Menu = require ( " ui/widget/menu " )
local default_perpage = Menu.items_per_page_default
local curr_perpage = G_reader_settings : readSetting ( " items_per_page " ) or default_perpage
local items = SpinWidget : new {
value = curr_perpage ,
value_min = 6 ,
value_max = 24 ,
default_value = default_perpage ,
title_text = _ ( " Items per page " ) ,
keep_shown_on_apply = true ,
callback = function ( spin )
G_reader_settings : saveSetting ( " items_per_page " , spin.value )
self.ui : onRefresh ( )
end
}
UIManager : show ( items )
end ,
} ,
{
text = _ ( " Item font size " ) ,
callback = function ( )
local SpinWidget = require ( " ui/widget/spinwidget " )
local Menu = require ( " ui/widget/menu " )
local curr_perpage = G_reader_settings : readSetting ( " items_per_page " ) or Menu.items_per_page_default
local default_font_size = Menu.getItemFontSize ( curr_perpage )
local curr_font_size = G_reader_settings : readSetting ( " items_font_size " ) or default_font_size
local items_font = SpinWidget : new {
value = curr_font_size ,
value_min = 10 ,
value_max = 72 ,
default_value = default_font_size ,
keep_shown_on_apply = true ,
title_text = _ ( " Item font size " ) ,
callback = function ( spin )
if spin.value == default_font_size then
-- We can't know if the user has set a size or hit "Use default", but
-- assume that if it is the default font size, he will prefer to have
-- our default font size if he later updates per-page
G_reader_settings : delSetting ( " items_font_size " )
else
G_reader_settings : saveSetting ( " items_font_size " , spin.value )
end
self.ui : onRefresh ( )
end
}
UIManager : show ( items_font )
end ,
} ,
{
text = _ ( " Shrink item font size to fit more text " ) ,
checked_func = function ( )
return G_reader_settings : isTrue ( " items_multilines_show_more_text " )
end ,
callback = function ( )
G_reader_settings : flipNilOrFalse ( " items_multilines_show_more_text " )
self.ui : onRefresh ( )
end ,
separator = true ,
} ,
{
text = _ ( " Show opened files in bold " ) ,
checked_func = function ( )
return G_reader_settings : readSetting ( " show_file_in_bold " ) == " opened "
end ,
callback = function ( )
if G_reader_settings : readSetting ( " show_file_in_bold " ) == " opened " then
G_reader_settings : saveSetting ( " show_file_in_bold " , false )
else
G_reader_settings : saveSetting ( " show_file_in_bold " , " opened " )
end
self.ui : onRefresh ( )
end ,
} ,
{
text = _ ( " Show new (not yet opened) files in bold " ) ,
checked_func = function ( )
return G_reader_settings : hasNot ( " show_file_in_bold " )
end ,
callback = function ( )
if G_reader_settings : hasNot ( " show_file_in_bold " ) then
G_reader_settings : saveSetting ( " show_file_in_bold " , false )
else
G_reader_settings : delSetting ( " show_file_in_bold " )
end
self.ui : onRefresh ( )
end ,
} ,
} ,
} ,
{
text = _ ( " History settings " ) ,
sub_item_table = {
{
text = _ ( " Clear history of deleted files " ) ,
callback = function ( )
UIManager : show ( ConfirmBox : new {
text = _ ( " Clear history of deleted files? " ) ,
ok_text = _ ( " Clear " ) ,
ok_callback = function ( )
require ( " readhistory " ) : clearMissing ( )
end ,
} )
end ,
} ,
{
text = _ ( " Auto-remove deleted or purged items from history " ) ,
checked_func = function ( )
return G_reader_settings : isTrue ( " autoremove_deleted_items_from_history " )
end ,
callback = function ( )
G_reader_settings : flipNilOrFalse ( " autoremove_deleted_items_from_history " )
end ,
separator = true ,
} ,
{
text = _ ( " Show filename in Open last/previous menu items " ) ,
checked_func = function ( )
return G_reader_settings : isTrue ( " open_last_menu_show_filename " )
end ,
callback = function ( )
G_reader_settings : flipNilOrFalse ( " open_last_menu_show_filename " )
end ,
} ,
} ,
} ,
{
text = _ ( " Home folder settings " ) ,
sub_item_table = {
{
text = _ ( " Set home folder " ) ,
callback = function ( )
local text
local home_dir = G_reader_settings : readSetting ( " home_dir " )
if home_dir then
text = T ( _ ( " Home folder is set to: \n %1 " ) , home_dir )
else
text = _ ( " Home folder is not set. " )
home_dir = Device.home_dir
end
UIManager : show ( ConfirmBox : new {
text = text .. " \n Choose new folder to set as home? " ,
ok_text = _ ( " Choose folder " ) ,
ok_callback = function ( )
local path_chooser = require ( " ui/widget/pathchooser " ) : new {
select_file = false ,
show_files = false ,
path = home_dir ,
onConfirm = function ( new_path )
G_reader_settings : saveSetting ( " home_dir " , new_path )
end
}
UIManager : show ( path_chooser )
end ,
} )
end ,
} ,
{
text = _ ( " Shorten home folder " ) ,
checked_func = function ( )
return G_reader_settings : nilOrTrue ( " shorten_home_dir " )
end ,
callback = function ( )
G_reader_settings : flipNilOrTrue ( " shorten_home_dir " )
local FileManager = require ( " apps/filemanager/filemanager " )
if FileManager.instance then FileManager.instance : reinit ( ) end
end ,
help_text = _ ( [ [
" Shorten home folder " will display the home folder itself as " Home " instead of its full path .
Assuming the home folder is :
` / mnt / onboard / . books `
A subfolder will be shortened from :
` / mnt / onboard / . books / Manga / Cells at Work `
To :
` Manga / Cells at Work ` . ] ] ) ,
} ,
{
text = _ ( " Lock home folder " ) ,
enabled_func = function ( )
return G_reader_settings : has ( " home_dir " )
end ,
checked_func = function ( )
return G_reader_settings : isTrue ( " lock_home_folder " )
end ,
callback = function ( )
G_reader_settings : flipNilOrFalse ( " lock_home_folder " )
self.ui : onRefresh ( )
end ,
} ,
} ,
separator = true ,
} ,
{
text = _ ( " Info lists items per page " ) ,
help_text = _ ( [ [ This sets the number of items per page in :
- Book information
- Dictionary and Wikipedia lookup history
- Reading statistics details
- A few other plugins ] ] ) ,
keep_menu_open = true ,
callback = function ( )
local SpinWidget = require ( " ui/widget/spinwidget " )
local KeyValuePage = require ( " ui/widget/keyvaluepage " )
local default_perpage = KeyValuePage : getDefaultKeyValuesPerPage ( )
local curr_perpage = G_reader_settings : readSetting ( " keyvalues_per_page " ) or default_perpage
local items = SpinWidget : new {
value = curr_perpage ,
value_min = 10 ,
value_max = 24 ,
default_value = default_perpage ,
title_text = _ ( " Info lists items per page " ) ,
callback = function ( spin )
if spin.value == default_perpage then
-- We can't know if the user has set a value or hit "Use default", but
-- assume that if it is the default, he will prefer to stay with our
-- default if he later changes screen DPI
G_reader_settings : delSetting ( " keyvalues_per_page " )
else
G_reader_settings : saveSetting ( " keyvalues_per_page " , spin.value )
end
end
}
UIManager : show ( items )
end ,
} ,
}
}
for _ , widget in pairs ( self.registered_widgets ) do
local ok , err = pcall ( widget.addToMainMenu , widget , self.menu_items )
if not ok then
logger.err ( " failed to register widget " , widget.name , err )
end
end
self.menu_items . sort_by = self.ui : getSortingMenuTable ( )
self.menu_items . reverse_sorting = {
text = _ ( " Reverse sorting " ) ,
checked_func = function ( ) return self.ui . file_chooser.reverse_collate end ,
callback = function ( ) self.ui : toggleReverseCollate ( ) end
}
self.menu_items . start_with = self.ui : getStartWithMenuTable ( )
if Device : supportsScreensaver ( ) then
self.menu_items . screensaver = {
text = _ ( " Screensaver " ) ,
sub_item_table = require ( " ui/elements/screensaver_menu " ) ,
}
end
-- insert common settings
for id , common_setting in pairs ( dofile ( " frontend/ui/elements/common_settings_menu_table.lua " ) ) do
self.menu_items [ id ] = common_setting
end
-- tools tab
self.menu_items . advanced_settings = {
text = _ ( " Advanced settings " ) ,
callback = function ( )
SetDefaults : ConfirmEdit ( )
end ,
}
self.menu_items . plugin_management = {
text = _ ( " Plugin management " ) ,
sub_item_table = PluginLoader : genPluginManagerSubItem ( )
}
self.menu_items . developer_options = {
text = _ ( " Developer options " ) ,
sub_item_table = {
{
text = _ ( " Clear caches " ) ,
callback = function ( )
UIManager : show ( ConfirmBox : new {
text = _ ( " Clear the cache folder? " ) ,
ok_callback = function ( )
local DataStorage = require ( " datastorage " )
local cachedir = DataStorage : getDataDir ( ) .. " /cache "
if lfs.attributes ( cachedir , " mode " ) == " directory " then
FFIUtil.purgeDir ( cachedir )
end
lfs.mkdir ( cachedir )
-- Also remove from the Cache objet references to the cache files we've just deleted
local Cache = require ( " cache " )
Cache.cached = { }
local InfoMessage = require ( " ui/widget/infomessage " )
UIManager : show ( InfoMessage : new {
text = _ ( " Caches cleared. Please restart KOReader. " ) ,
} )
end ,
} )
end ,
} ,
[feat, Kobo] On Kobo, drop fb to 8bpp on startup (#4637)
* The Great 8bpp Experiment
Swap to 8bpp on Kobo, because we're 'effing grayscale, for pete's sake!
* Always swap to 8bpp, no matter the launch method.
Because it turned out that, even when restarting Nickel, we had to
restore the expected bitdepth ourselves, because pickel/Nickel didn't do
the job completely.
(I'm going to guess the grayscale flag wasn't getting flipped properly).
* Dither every non-transparent icon to the eInk palette
* Make sure hasBGRFrameBuffer is only enabled when the Kobo fb actually is
@ 32bpp...
* Re-process badly grayscaled icons
* And re-grayscale that one w/ gamma correction so the squares show up
better.
* Allow the fbdepth switch to be disabled (in Developer settings).
Also, allow setting debug mode that way.
Also, forcibly disable verbose logging when disabling debug.
* Update setting name to piggyback on the existing check in reader.lua
* Update icons postprocessing info
6 years ago
{
text = _ ( " Enable debug logging " ) ,
checked_func = function ( )
return G_reader_settings : isTrue ( " debug " )
end ,
callback = function ( )
G_reader_settings : flipNilOrFalse ( " debug " )
if G_reader_settings : isTrue ( " debug " ) then
dbg : turnOn ( )
else
dbg : setVerbose ( false )
dbg : turnOff ( )
G_reader_settings : makeFalse ( " debug_verbose " )
[feat, Kobo] On Kobo, drop fb to 8bpp on startup (#4637)
* The Great 8bpp Experiment
Swap to 8bpp on Kobo, because we're 'effing grayscale, for pete's sake!
* Always swap to 8bpp, no matter the launch method.
Because it turned out that, even when restarting Nickel, we had to
restore the expected bitdepth ourselves, because pickel/Nickel didn't do
the job completely.
(I'm going to guess the grayscale flag wasn't getting flipped properly).
* Dither every non-transparent icon to the eInk palette
* Make sure hasBGRFrameBuffer is only enabled when the Kobo fb actually is
@ 32bpp...
* Re-process badly grayscaled icons
* And re-grayscale that one w/ gamma correction so the squares show up
better.
* Allow the fbdepth switch to be disabled (in Developer settings).
Also, allow setting debug mode that way.
Also, forcibly disable verbose logging when disabling debug.
* Update setting name to piggyback on the existing check in reader.lua
* Update icons postprocessing info
6 years ago
end
end ,
} ,
{
text = _ ( " Enable verbose debug logging " ) ,
enabled_func = function ( )
return G_reader_settings : isTrue ( " debug " )
end ,
checked_func = function ( )
return G_reader_settings : isTrue ( " debug_verbose " )
end ,
callback = function ( )
G_reader_settings : flipNilOrFalse ( " debug_verbose " )
if G_reader_settings : isTrue ( " debug_verbose " ) then
dbg : setVerbose ( true )
else
dbg : setVerbose ( false )
end
end ,
} ,
}
}
if Device : isKobo ( ) and not Device : isSunxi ( ) then
[feat, Kobo] On Kobo, drop fb to 8bpp on startup (#4637)
* The Great 8bpp Experiment
Swap to 8bpp on Kobo, because we're 'effing grayscale, for pete's sake!
* Always swap to 8bpp, no matter the launch method.
Because it turned out that, even when restarting Nickel, we had to
restore the expected bitdepth ourselves, because pickel/Nickel didn't do
the job completely.
(I'm going to guess the grayscale flag wasn't getting flipped properly).
* Dither every non-transparent icon to the eInk palette
* Make sure hasBGRFrameBuffer is only enabled when the Kobo fb actually is
@ 32bpp...
* Re-process badly grayscaled icons
* And re-grayscale that one w/ gamma correction so the squares show up
better.
* Allow the fbdepth switch to be disabled (in Developer settings).
Also, allow setting debug mode that way.
Also, forcibly disable verbose logging when disabling debug.
* Update setting name to piggyback on the existing check in reader.lua
* Update icons postprocessing info
6 years ago
table.insert ( self.menu_items . developer_options.sub_item_table , {
text = _ ( " Disable forced 8-bit pixel depth " ) ,
[feat, Kobo] On Kobo, drop fb to 8bpp on startup (#4637)
* The Great 8bpp Experiment
Swap to 8bpp on Kobo, because we're 'effing grayscale, for pete's sake!
* Always swap to 8bpp, no matter the launch method.
Because it turned out that, even when restarting Nickel, we had to
restore the expected bitdepth ourselves, because pickel/Nickel didn't do
the job completely.
(I'm going to guess the grayscale flag wasn't getting flipped properly).
* Dither every non-transparent icon to the eInk palette
* Make sure hasBGRFrameBuffer is only enabled when the Kobo fb actually is
@ 32bpp...
* Re-process badly grayscaled icons
* And re-grayscale that one w/ gamma correction so the squares show up
better.
* Allow the fbdepth switch to be disabled (in Developer settings).
Also, allow setting debug mode that way.
Also, forcibly disable verbose logging when disabling debug.
* Update setting name to piggyback on the existing check in reader.lua
* Update icons postprocessing info
6 years ago
checked_func = function ( )
return G_reader_settings : isTrue ( " dev_startup_no_fbdepth " )
end ,
callback = function ( )
G_reader_settings : flipNilOrFalse ( " dev_startup_no_fbdepth " )
local InfoMessage = require ( " ui/widget/infomessage " )
UIManager : show ( InfoMessage : new {
text = _ ( " This will take effect on next restart. " ) ,
} )
end ,
} )
end
--- @note Currently, only Kobo, rM & PB have a fancy crash display (#5328)
if Device : isKobo ( ) or Device : isRemarkable ( ) or Device : isPocketBook ( ) then
table.insert ( self.menu_items . developer_options.sub_item_table , {
text = _ ( " Always abort on crash " ) ,
checked_func = function ( )
return G_reader_settings : isTrue ( " dev_abort_on_crash " )
end ,
callback = function ( )
G_reader_settings : flipNilOrFalse ( " dev_abort_on_crash " )
local InfoMessage = require ( " ui/widget/infomessage " )
UIManager : show ( InfoMessage : new {
text = _ ( " This will take effect on next restart. " ) ,
} )
end ,
} )
end
Clarify our OOP semantics across the codebase (#9586)
Basically:
* Use `extend` for class definitions
* Use `new` for object instantiations
That includes some minor code cleanups along the way:
* Updated `Widget`'s docs to make the semantics clearer.
* Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283)
* Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass).
* Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events.
* Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier.
* Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references.
* ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak).
* Terminal: Make sure the shell is killed on plugin teardown.
* InputText: Fix Home/End/Del physical keys to behave sensibly.
* InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...).
* OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of.
* ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed!
* Kobo: Minor code cleanups.
2 years ago
local Blitbuffer = require ( " ffi/blitbuffer " )
table.insert ( self.menu_items . developer_options.sub_item_table , {
text = _ ( " Disable C blitter " ) ,
enabled_func = function ( )
return Blitbuffer.has_cblitbuffer
end ,
checked_func = function ( )
return G_reader_settings : isTrue ( " dev_no_c_blitter " )
end ,
callback = function ( )
G_reader_settings : flipNilOrFalse ( " dev_no_c_blitter " )
Blitbuffer : enableCBB ( G_reader_settings : nilOrFalse ( " dev_no_c_blitter " ) )
end ,
} )
if Device : hasEinkScreen ( ) and Device : canHWDither ( ) then
table.insert ( self.menu_items . developer_options.sub_item_table , {
text = _ ( " Disable HW dithering " ) ,
checked_func = function ( )
return not Device.screen . hw_dithering
end ,
callback = function ( )
Device.screen : toggleHWDithering ( )
G_reader_settings : saveSetting ( " dev_no_hw_dither " , not Device.screen . hw_dithering )
-- Make sure SW dithering gets disabled when we enable HW dithering
if Device.screen . hw_dithering and Device.screen . sw_dithering then
G_reader_settings : makeTrue ( " dev_no_sw_dither " )
Device.screen : toggleSWDithering ( false )
end
UIManager : setDirty ( " all " , " full " )
end ,
} )
end
if Device : hasEinkScreen ( ) then
table.insert ( self.menu_items . developer_options.sub_item_table , {
text = _ ( " Disable SW dithering " ) ,
enabled_func = function ( )
return Device.screen . fb_bpp == 8
end ,
checked_func = function ( )
return not Device.screen . sw_dithering
end ,
callback = function ( )
Device.screen : toggleSWDithering ( )
G_reader_settings : saveSetting ( " dev_no_sw_dither " , not Device.screen . sw_dithering )
-- Make sure HW dithering gets disabled when we enable SW dithering
if Device.screen . hw_dithering and Device.screen . sw_dithering then
G_reader_settings : makeTrue ( " dev_no_hw_dither " )
Device.screen : toggleHWDithering ( false )
end
UIManager : setDirty ( " all " , " full " )
end ,
} )
end
--- @note: Currently, only Kobo implements this quirk
if Device : hasEinkScreen ( ) and Device : isKobo ( ) then
table.insert ( self.menu_items . developer_options.sub_item_table , {
-- @translators Highly technical (ioctl is a Linux API call, the uppercase stuff is a constant). What's translatable is essentially only the action ("bypass") and the article.
text = _ ( " Bypass the WAIT_FOR ioctls " ) ,
checked_func = function ( )
local mxcfb_bypass_wait_for
if G_reader_settings : has ( " mxcfb_bypass_wait_for " ) then
mxcfb_bypass_wait_for = G_reader_settings : isTrue ( " mxcfb_bypass_wait_for " )
else
mxcfb_bypass_wait_for = not Device : hasReliableMxcWaitFor ( )
end
return mxcfb_bypass_wait_for
end ,
callback = function ( )
local mxcfb_bypass_wait_for
if G_reader_settings : has ( " mxcfb_bypass_wait_for " ) then
mxcfb_bypass_wait_for = G_reader_settings : isTrue ( " mxcfb_bypass_wait_for " )
else
mxcfb_bypass_wait_for = not Device : hasReliableMxcWaitFor ( )
end
G_reader_settings : saveSetting ( " mxcfb_bypass_wait_for " , not mxcfb_bypass_wait_for )
local InfoMessage = require ( " ui/widget/infomessage " )
UIManager : show ( InfoMessage : new {
text = _ ( " This will take effect on next restart. " ) ,
} )
end ,
} )
end
--- @note: Intended to debug/investigate B288 quirks on PocketBook devices
if Device : hasEinkScreen ( ) and Device : isPocketBook ( ) then
table.insert ( self.menu_items . developer_options.sub_item_table , {
-- @translators B288 is the codename of the CPU/chipset (SoC stands for 'System on Chip').
text = _ ( " Ignore feature bans on B288 SoCs " ) ,
enabled_func = function ( )
return Device : isB288SoC ( )
end ,
checked_func = function ( )
return G_reader_settings : isTrue ( " pb_ignore_b288_quirks " )
end ,
callback = function ( )
G_reader_settings : flipNilOrFalse ( " pb_ignore_b288_quirks " )
local InfoMessage = require ( " ui/widget/infomessage " )
UIManager : show ( InfoMessage : new {
text = _ ( " This will take effect on next restart. " ) ,
} )
end ,
} )
end
if Device : isAndroid ( ) then
table.insert ( self.menu_items . developer_options.sub_item_table , {
text = _ ( " Start compatibility test " ) ,
callback = function ( )
Device : test ( )
end ,
} )
end
table.insert ( self.menu_items . developer_options.sub_item_table , {
text = _ ( " Disable enhanced UI text shaping (xtext) " ) ,
checked_func = function ( )
return G_reader_settings : isFalse ( " use_xtext " )
end ,
callback = function ( )
G_reader_settings : flipNilOrTrue ( " use_xtext " )
local InfoMessage = require ( " ui/widget/infomessage " )
UIManager : show ( InfoMessage : new {
text = _ ( " This will take effect on next restart. " ) ,
} )
end ,
} )
table.insert ( self.menu_items . developer_options.sub_item_table , {
text = _ ( " UI layout mirroring and text direction " ) ,
sub_item_table = {
{
text = _ ( " Reverse UI layout mirroring " ) ,
checked_func = function ( )
return G_reader_settings : isTrue ( " dev_reverse_ui_layout_mirroring " )
end ,
callback = function ( )
G_reader_settings : flipNilOrFalse ( " dev_reverse_ui_layout_mirroring " )
local InfoMessage = require ( " ui/widget/infomessage " )
UIManager : show ( InfoMessage : new {
text = _ ( " This will take effect on next restart. " ) ,
} )
end
} ,
{
text = _ ( " Reverse UI text direction " ) ,
checked_func = function ( )
return G_reader_settings : isTrue ( " dev_reverse_ui_text_direction " )
end ,
callback = function ( )
G_reader_settings : flipNilOrFalse ( " dev_reverse_ui_text_direction " )
local InfoMessage = require ( " ui/widget/infomessage " )
UIManager : show ( InfoMessage : new {
text = _ ( " This will take effect on next restart. " ) ,
} )
end
}
}
} )
table.insert ( self.menu_items . developer_options.sub_item_table , {
text_func = function ( )
if G_reader_settings : nilOrTrue ( " use_cre_call_cache " )
and G_reader_settings : isTrue ( " use_cre_call_cache_log_stats " ) then
return _ ( " Enable CRE call cache (with stats) " )
end
return _ ( " Enable CRE call cache " )
end ,
checked_func = function ( )
return G_reader_settings : nilOrTrue ( " use_cre_call_cache " )
end ,
callback = function ( )
G_reader_settings : flipNilOrTrue ( " use_cre_call_cache " )
-- No need to show "This will take effect on next CRE book opening."
-- as this menu is only accessible from file browser
end ,
hold_callback = function ( touchmenu_instance )
G_reader_settings : flipNilOrFalse ( " use_cre_call_cache_log_stats " )
touchmenu_instance : updateItems ( )
end ,
} )
table.insert ( self.menu_items . developer_options.sub_item_table , {
text = _ ( " Dump the fontlist cache " ) ,
callback = function ( )
local FontList = require ( " fontlist " )
FontList : dumpFontList ( )
end ,
} )
if Device : isKobo ( ) and Device : canToggleChargingLED ( ) then
table.insert ( self.menu_items . developer_options.sub_item_table , {
text = _ ( " Turn on the LED on PM entry failure " ) ,
checked_func = function ( )
return G_reader_settings : isTrue ( " pm_debug_entry_failure " )
end ,
callback = function ( )
G_reader_settings : toggle ( " pm_debug_entry_failure " )
end ,
} )
end
self.menu_items . cloud_storage = {
text = _ ( " Cloud storage " ) ,
callback = function ( )
local cloud_storage = require ( " apps/cloudstorage/cloudstorage " ) : new { }
UIManager : show ( cloud_storage )
local filemanagerRefresh = function ( ) self.ui : onRefresh ( ) end
function cloud_storage : onClose ( )
filemanagerRefresh ( )
UIManager : close ( cloud_storage )
end
end ,
}
self.menu_items . find_file = {
-- @translators Search for files by name.
text = _ ( " File search " ) ,
help_text = _ ( [ [ Search a book by filename in the current or home folder and its subfolders .
Wildcards for one ' ? ' or more ' * ' characters can be used .
A search for ' * ' will show all files .
The sorting order is the same as in filemanager .
Tap a book in the search results to open it . ] ] ) ,
callback = function ( )
self.ui : handleEvent ( Event : new ( " ShowFileSearch " ) )
end
}
-- main menu tab
self.menu_items . open_last_document = {
text_func = function ( )
if not G_reader_settings : isTrue ( " open_last_menu_show_filename " ) or G_reader_settings : hasNot ( " lastfile " ) then
return _ ( " Open last document " )
end
local last_file = G_reader_settings : readSetting ( " lastfile " )
local path , file_name = util.splitFilePathName ( last_file ) -- luacheck: no unused
return T ( _ ( " Last: %1 " ) , BD.filename ( file_name ) )
end ,
enabled_func = function ( )
return G_reader_settings : has ( " lastfile " )
end ,
callback = function ( )
self : onOpenLastDoc ( )
end ,
hold_callback = function ( )
local last_file = G_reader_settings : readSetting ( " lastfile " )
UIManager : show ( ConfirmBox : new {
text = T ( _ ( " Would you like to open the last document: %1? " ) , BD.filepath ( last_file ) ) ,
ok_text = _ ( " OK " ) ,
ok_callback = function ( )
self : onOpenLastDoc ( )
end ,
} )
end
}
-- insert common info
for id , common_setting in pairs ( dofile ( " frontend/ui/elements/common_info_menu_table.lua " ) ) do
self.menu_items [ id ] = common_setting
end
-- insert common exit for filemanager
for id , common_setting in pairs ( dofile ( " frontend/ui/elements/common_exit_menu_table.lua " ) ) do
self.menu_items [ id ] = common_setting
end
if not Device : isTouchDevice ( ) then
-- add a shortcut on non touch-device
-- because this menu is not accessible otherwise
self.menu_items . plus_menu = {
icon = " plus " ,
remember = false ,
callback = function ( )
self : onCloseFileManagerMenu ( )
self.ui : tapPlus ( )
end ,
}
end
local order = require ( " ui/elements/filemanager_menu_order " )
local MenuSorter = require ( " ui/menusorter " )
self.tab_item_table = MenuSorter : mergeAndSort ( " filemanager " , self.menu_items , order )
end
dbg : guard ( FileManagerMenu , ' setUpdateItemTable ' ,
function ( self )
local mock_menu_items = { }
for _ , widget in pairs ( self.registered_widgets ) do
-- make sure addToMainMenu works in debug mode
widget : addToMainMenu ( mock_menu_items )
end
end )
function FileManagerMenu : exitOrRestart ( callback , force )
UIManager : close ( self.menu_container )
-- Only restart sets a callback, which suits us just fine for this check ;)
if callback and not force and not Device : isStartupScriptUpToDate ( ) then
UIManager : show ( ConfirmBox : new {
text = _ ( " KOReader's startup script has been updated. You'll need to completely exit KOReader to finalize the update. " ) ,
ok_text = _ ( " Restart anyway " ) ,
ok_callback = function ( )
self : exitOrRestart ( callback , true )
end ,
} )
return
end
self.ui : onClose ( )
if callback then
callback ( )
end
end
function FileManagerMenu : onShowMenu ( tab_index )
if self.tab_item_table == nil then
self : setUpdateItemTable ( )
end
if not tab_index then
tab_index = G_reader_settings : readSetting ( " filemanagermenu_tab_index " ) or 1
end
local menu_container = CenterContainer : new {
ignore = " height " ,
dimen = Screen : getSize ( ) ,
}
local main_menu
if Device : isTouchDevice ( ) or Device : hasDPad ( ) then
local TouchMenu = require ( " ui/widget/touchmenu " )
main_menu = TouchMenu : new {
width = Screen : getWidth ( ) ,
last_index = tab_index ,
tab_item_table = self.tab_item_table ,
show_parent = menu_container ,
}
else
local Menu = require ( " ui/widget/menu " )
main_menu = Menu : new {
title = _ ( " File manager menu " ) ,
item_table = Menu.itemTableFromTouchMenu ( self.tab_item_table ) ,
[RFC] Pagination UI shenanigans (#7335)
* Menu/KeyValuePage/ReaderGoTo: Unify the dialogs. (Generally, "Enter page number" as title, and "Go to page" as OK button).
* Allow *tapping* on pagination buttons, too. Added spacers around the text to accommodate for that.
* Disable input handlers when <= 1 pages, while still printing the label in black.
* Always display both the label and the chevrons, even on single page content. (Menu being an exception, because it can handle showing no content at all, in which case we hide the chevrons).
* KVP: Tweak the pagination buttons layout in order to have consistent centering, regardless of whether the return arrow is enabled or not. (Also, match Menu's layout, more or less).
* Menu: Minor layout tweaks to follow the KVP tweaks above. Fixes, among possibly other things, buttons in (non-FM) "List" menus overlapping the final entry (e.g., OPDS), and popout menus with a border being misaligned (e.g., Calibre, Find a file).
* CalendarView: Minor layout tweaks to follow the KVP tweaks. Ensures the pagination buttons are laid out in the same way as everywhere else (they used to be a wee bit higher).
4 years ago
width = Screen : getWidth ( ) - ( Size.margin . fullscreen_popout * 2 ) ,
show_parent = menu_container ,
}
end
ReaderUI: Saner FM/RD lifecycle
* Ensure that going from one to the other tears down the former and
its plugins before instantiating the latter and its plugins.
UIManager: Unify Event sending & broadcasting
* Make the two behave the same way (walk the widget stack from top to
bottom), and properly handle the window stack shrinking shrinking
*and* growing.
Previously, broadcasting happened bottom-to-top and didn't really
handle the list shrinking/growing, while sending only handled the list
shrinking by a single element, and hopefully that element being the one
the event was just sent to.
These two items combined allowed us to optimize suboptimal
refresh behavior with Menu and other Menu classes when
opening/closing a document.
e.g., the "opening document" Notification is now properly regional,
and the "open last doc" option no longer flashes like a crazy person
anymore.
Plugins: Allow optimizing Menu refresh with custom menus, too.
Requires moving Menu's close_callback *after* onMenuSelect, which, eh,
probably makes sense, and is probably harmless in the grand scheme of
things.
3 years ago
main_menu.close_callback = function ( )
self : onCloseFileManagerMenu ( )
end
menu_container [ 1 ] = main_menu
-- maintain a reference to menu_container
self.menu_container = menu_container
UIManager : show ( menu_container )
return true
end
function FileManagerMenu : onCloseFileManagerMenu ( )
if not self.menu_container then return end
local last_tab_index = self.menu_container [ 1 ] . last_index
G_reader_settings : saveSetting ( " filemanagermenu_tab_index " , last_tab_index )
UIManager : close ( self.menu_container )
return true
end
function FileManagerMenu : _getTabIndexFromLocation ( ges )
if self.tab_item_table == nil then
self : setUpdateItemTable ( )
end
local last_tab_index = G_reader_settings : readSetting ( " filemanagermenu_tab_index " ) or 1
if not ges then
return last_tab_index
-- if the start position is far right
elseif ges.pos . x > Screen : getWidth ( ) * ( 2 / 3 ) then
return BD.mirroredUILayout ( ) and 1 or # self.tab_item_table
-- if the start position is far left
elseif ges.pos . x < Screen : getWidth ( ) * ( 1 / 3 ) then
return BD.mirroredUILayout ( ) and # self.tab_item_table or 1
-- if center return the last index
else
return last_tab_index
end
end
function FileManagerMenu : onTapShowMenu ( ges )
if self.activation_menu ~= " swipe " then
self : onShowMenu ( self : _getTabIndexFromLocation ( ges ) )
return true
end
end
function FileManagerMenu : onSwipeShowMenu ( ges )
if self.activation_menu ~= " tap " and ges.direction == " south " then
self : onShowMenu ( self : _getTabIndexFromLocation ( ges ) )
return true
end
end
function FileManagerMenu : onSetDimensions ( dimen )
self : onCloseFileManagerMenu ( )
-- update listening according to new screen dimen
if Device : isTouchDevice ( ) then
self : initGesListener ( )
end
end
function FileManagerMenu : registerToMainMenu ( widget )
table.insert ( self.registered_widgets , widget )
end
return FileManagerMenu