local BD = require ( " ui/bidi " )
local CenterContainer = require ( " ui/widget/container/centercontainer " )
local ConfirmBox = require ( " ui/widget/confirmbox " )
local Device = require ( " device " )
local FFIUtil = require ( " ffi/util " )
local InputContainer = require ( " ui/widget/container/inputcontainer " )
local KeyValuePage = require ( " ui/widget/keyvaluepage " )
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 SpinWidget = require ( " ui/widget/spinwidget " )
local UIManager = require ( " ui/uimanager " )
local Screen = Device.screen
local filemanagerutil = require ( " apps/filemanager/filemanagerutil " )
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 = { }
self : registerKeyEvents ( )
self.activation_menu = G_reader_settings : readSetting ( " activate_menu " )
if self.activation_menu == nil then
self.activation_menu = " swipe_tap "
end
end
function FileManagerMenu : registerKeyEvents ( )
if Device : hasKeys ( ) then
self.key_events . ShowMenu = { { " Menu " } }
end
end
FileManagerMenu.onPhysicalKeyboardConnected = FileManagerMenu.registerKeyEvents
-- NOTE: FileManager emits a SetDimensions on init, it's our only caller
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 ( )
local FileChooser = self.ui . file_chooser
-- setting tab
self.menu_items . filebrowser_settings = {
text = _ ( " Settings " ) ,
sub_item_table = {
{
text = _ ( " Show finished books " ) ,
checked_func = function ( ) return FileChooser.show_finished end ,
callback = function ( ) FileChooser : toggleShowFilesMode ( " show_finished " ) end ,
} ,
{
text = _ ( " Show hidden files " ) ,
checked_func = function ( ) return FileChooser.show_hidden end ,
callback = function ( ) FileChooser : toggleShowFilesMode ( " show_hidden " ) end ,
} ,
{
text = _ ( " Show unsupported files " ) ,
checked_func = function ( ) return FileChooser.show_unsupported end ,
callback = function ( ) FileChooser : toggleShowFilesMode ( " show_unsupported " ) end ,
separator = true ,
} ,
{
text = _ ( " Classic mode settings " ) ,
sub_item_table = {
{
text_func = function ( )
return T ( _ ( " Items per page: %1 " ) , G_reader_settings : readSetting ( " items_per_page " ) )
end ,
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 ( touchmenu_instance )
local current_value = G_reader_settings : readSetting ( " items_per_page " )
local default_value = FileChooser.items_per_page_default
local widget = SpinWidget : new {
title_text = _ ( " Items per page " ) ,
value = current_value ,
value_min = 6 ,
value_max = 30 ,
default_value = default_value ,
keep_shown_on_apply = true ,
callback = function ( spin )
G_reader_settings : saveSetting ( " items_per_page " , spin.value )
FileChooser : refreshPath ( )
touchmenu_instance : updateItems ( )
end ,
}
UIManager : show ( widget )
end ,
} ,
{
text_func = function ( )
return T ( _ ( " Item font size: %1 " ) , FileChooser.font_size )
end ,
callback = function ( touchmenu_instance )
local current_value = FileChooser.font_size
local default_value = FileChooser.getItemFontSize ( G_reader_settings : readSetting ( " items_per_page " ) )
local widget = SpinWidget : new {
title_text = _ ( " Item font size " ) ,
value = current_value ,
value_min = 10 ,
value_max = 72 ,
default_value = default_value ,
keep_shown_on_apply = true ,
callback = function ( spin )
if spin.value == default_value 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
FileChooser : refreshPath ( )
touchmenu_instance : updateItems ( )
end ,
}
UIManager : show ( widget )
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 = _ ( " Shorten date/time " ) ,
checked_func = function ( )
return G_reader_settings : isTrue ( " history_datetime_short " )
end ,
callback = function ( )
G_reader_settings : flipNilOrFalse ( " history_datetime_short " )
require ( " readhistory " ) : updateDateTimeString ( )
end ,
} ,
{
text = _ ( " Freeze last read date of finished books " ) ,
checked_func = function ( )
return G_reader_settings : isTrue ( " history_freeze_finished_books " )
end ,
callback = function ( )
G_reader_settings : flipNilOrFalse ( " history_freeze_finished_books " )
end ,
separator = true ,
} ,
{
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 title_header = _ ( " Current home folder: " )
local current_path = G_reader_settings : readSetting ( " home_dir " )
local default_path = filemanagerutil.getDefaultDir ( )
local caller_callback = function ( path )
G_reader_settings : saveSetting ( " home_dir " , path )
self.ui : updateTitleBarPath ( )
end
filemanagerutil.showChooseDialog ( title_header , caller_callback , current_path , default_path )
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 " )
self.ui : updateTitleBarPath ( )
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_func = function ( )
local default_value = KeyValuePage.getDefaultItemsPerPage ( )
local current_value = G_reader_settings : readSetting ( " keyvalues_per_page " ) or default_value
return T ( _ ( " Info lists items per page: %1 " ) , current_value )
end ,
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 ( touchmenu_instance )
local default_value = KeyValuePage.getDefaultItemsPerPage ( )
local current_value = G_reader_settings : readSetting ( " keyvalues_per_page " ) or default_value
local widget = SpinWidget : new {
value = current_value ,
value_min = 10 ,
value_max = 30 ,
default_value = default_value ,
title_text = _ ( " Info lists items per page " ) ,
callback = function ( spin )
if spin.value == default_value 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
touchmenu_instance : updateItems ( )
end
}
UIManager : show ( widget )
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 : getSortingMenuTable ( )
self.menu_items . reverse_sorting = {
text = _ ( " Reverse sorting " ) ,
checked_func = function ( )
return G_reader_settings : isTrue ( " reverse_collate " )
end ,
callback = function ( )
G_reader_settings : flipNilOrFalse ( " reverse_collate " )
FileChooser : refreshPath ( )
end ,
}
self.menu_items . sort_mixed = {
text = _ ( " Folders and files mixed " ) ,
enabled_func = function ( )
local collate = FileChooser : getCollate ( )
return collate.can_collate_mixed
end ,
checked_func = function ( )
local collate = FileChooser : getCollate ( )
return collate.can_collate_mixed and G_reader_settings : isTrue ( " collate_mixed " )
end ,
callback = function ( )
G_reader_settings : flipNilOrFalse ( " collate_mixed " )
FileChooser : refreshPath ( )
end ,
}
self.menu_items . start_with = self : 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
-- Settings > Navigation; this mostly concerns physical keys, and applies *everywhere*
if Device : hasKeys ( ) then
self.menu_items . physical_buttons_setup = require ( " ui/elements/physical_buttons " )
end
-- settings tab - Document submenu
self.menu_items . document_metadata_location_move = {
text = _ ( " Move book metadata " ) ,
keep_menu_open = true ,
callback = function ( )
self.ui . bookinfo : moveBookMetadata ( )
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 = { }
UIManager : askForRestart ( _ ( " 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 " )
UIManager : askForRestart ( )
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 " )
UIManager : askForRestart ( )
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
table.insert ( self.menu_items . developer_options.sub_item_table , {
text = _ ( " Anti-alias rounded corners " ) ,
checked_func = function ( )
return G_reader_settings : nilOrTrue ( " anti_alias_ui " )
end ,
callback = function ( )
G_reader_settings : flipNilOrTrue ( " anti_alias_ui " )
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 )
UIManager : askForRestart ( )
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 " )
UIManager : askForRestart ( )
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 " )
UIManager : askForRestart ( )
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 " )
UIManager : askForRestart ( )
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 " )
UIManager : askForRestart ( )
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 , {
-- @translators This is a debug option to help determine cases when standby failed to initiate properly. PM = power management.
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 . filesearcher : onShowFileSearch ( )
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 : getSortingMenuTable ( )
local sub_item_table = { }
for k , v in pairs ( self.ui . file_chooser.collates ) do
table.insert ( sub_item_table , {
text = v.text ,
menu_order = v.menu_order ,
checked_func = function ( )
local _ , id = self.ui . file_chooser : getCollate ( )
return k == id
end ,
callback = function ( )
G_reader_settings : saveSetting ( " collate " , k )
self.ui . file_chooser : clearSortingCache ( )
self.ui . file_chooser : refreshPath ( )
end ,
} )
end
table.sort ( sub_item_table , function ( a , b ) return a.menu_order < b.menu_order end )
return {
text_func = function ( )
local collate = self.ui . file_chooser : getCollate ( )
return T ( _ ( " Sort by: %1 " ) , collate.text )
end ,
sub_item_table = sub_item_table ,
}
end
function FileManagerMenu : getStartWithMenuTable ( )
local start_withs = {
{ _ ( " file browser " ) , " filemanager " } ,
{ _ ( " history " ) , " history " } ,
{ _ ( " favorites " ) , " favorites " } ,
{ _ ( " folder shortcuts " ) , " folder_shortcuts " } ,
{ _ ( " last file " ) , " last " } ,
}
local sub_item_table = { }
for i , v in ipairs ( start_withs ) do
table.insert ( sub_item_table , {
text = v [ 1 ] ,
checked_func = function ( )
return v [ 2 ] == G_reader_settings : readSetting ( " start_with " , " filemanager " )
end ,
callback = function ( )
G_reader_settings : saveSetting ( " start_with " , v [ 2 ] )
end ,
} )
end
return {
text_func = function ( )
local start_with = G_reader_settings : readSetting ( " start_with " ) or " filemanager "
for i , v in ipairs ( start_withs ) do
if v [ 2 ] == start_with then
return T ( _ ( " Start with: %1 " ) , v [ 1 ] )
end
end
end ,
sub_item_table = sub_item_table ,
}
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 true 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 )
self.menu_container = nil
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 )
-- This widget doesn't support in-place layout updates, so, close & reopen
if self.menu_container then
self : onCloseFileManagerMenu ( )
self : onShowMenu ( )
end
-- update gesture zones according to new screen dimen
self : initGesListener ( )
end
function FileManagerMenu : onMenuSearch ( )
self : onShowMenu ( )
self.menu_container [ 1 ] : onShowMenuSearch ( )
end
function FileManagerMenu : registerToMainMenu ( widget )
table.insert ( self.registered_widgets , widget )
end
return FileManagerMenu