KeyValuePage: configurable items per page

Tweak building to start from items per page instead of
a fixed item height.
Guess the best font size that fit.
Update separator specification from using a "----" to
the now generic separator=true (this allows not wasting
a slot for each separator in the page and not have
only 12 items and 2 small lines in a 14 items page).
pull/7324/head
poire-z 3 years ago
parent 572900bfff
commit 8a0d798e9e

@ -64,8 +64,7 @@ function BookInfo:show(file, book_props)
table.insert(kv_pairs, { _("Format:"), filetype:upper() }) table.insert(kv_pairs, { _("Format:"), filetype:upper() })
table.insert(kv_pairs, { _("Size:"), size }) table.insert(kv_pairs, { _("Size:"), size })
table.insert(kv_pairs, { _("File date:"), os.date("%Y-%m-%d %H:%M:%S", file_modification) }) table.insert(kv_pairs, { _("File date:"), os.date("%Y-%m-%d %H:%M:%S", file_modification) })
table.insert(kv_pairs, { _("Directory:"), BD.dirpath(filemanagerutil.abbreviate(directory)) }) table.insert(kv_pairs, { _("Directory:"), BD.dirpath(filemanagerutil.abbreviate(directory)), separator = true })
table.insert(kv_pairs, "----")
-- book_props may be provided if caller already has them available -- book_props may be provided if caller already has them available
-- but it may lack 'pages', that we may get from sidecar file -- but it may lack 'pages', that we may get from sidecar file

@ -165,8 +165,8 @@ function FileManagerMenu:setUpdateItemTable()
- Calibre and OPDS browsers/search results]]), - Calibre and OPDS browsers/search results]]),
keep_menu_open = true, keep_menu_open = true,
callback = function() callback = function()
local Menu = require("ui/widget/menu")
local SpinWidget = require("ui/widget/spinwidget") local SpinWidget = require("ui/widget/spinwidget")
local Menu = require("ui/widget/menu")
local default_perpage = Menu.items_per_page_default local default_perpage = Menu.items_per_page_default
local curr_perpage = G_reader_settings:readSetting("items_per_page") or default_perpage local curr_perpage = G_reader_settings:readSetting("items_per_page") or default_perpage
local items = SpinWidget:new{ local items = SpinWidget:new{
@ -189,8 +189,8 @@ function FileManagerMenu:setUpdateItemTable()
text = _("Item font size"), text = _("Item font size"),
keep_menu_open = true, keep_menu_open = true,
callback = function() callback = function()
local Menu = require("ui/widget/menu")
local SpinWidget = require("ui/widget/spinwidget") 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 curr_perpage = G_reader_settings:readSetting("items_per_page") or Menu.items_per_page_default
local default_font_size = Menu.getItemFontSize(curr_perpage) local default_font_size = Menu.getItemFontSize(curr_perpage)
local curr_font_size = G_reader_settings:readSetting("items_font_size") or default_font_size local curr_font_size = G_reader_settings:readSetting("items_font_size") or default_font_size
@ -206,7 +206,7 @@ function FileManagerMenu:setUpdateItemTable()
if spin.value == default_font_size then if spin.value == default_font_size then
-- We can't know if the user has set a size or hit "Use default", but -- 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 -- assume that if it is the default font size, he will prefer to have
-- our default font size if he later update per-page -- our default font size if he later updates per-page
G_reader_settings:delSetting("items_font_size") G_reader_settings:delSetting("items_font_size")
else else
G_reader_settings:saveSetting("items_font_size", spin.value) G_reader_settings:saveSetting("items_font_size", spin.value)
@ -291,6 +291,41 @@ function FileManagerMenu:setUpdateItemTable()
text = _("Auto-remove deleted or purged items from history"), text = _("Auto-remove deleted or purged items from history"),
checked_func = function() return G_reader_settings:readSetting("autoremove_deleted_items_from_history") end, checked_func = function() return G_reader_settings:readSetting("autoremove_deleted_items_from_history") end,
callback = function() G_reader_settings:flipNilOrFalse("autoremove_deleted_items_from_history") end, callback = function() G_reader_settings:flipNilOrFalse("autoremove_deleted_items_from_history") 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{
width = math.floor(Screen:getWidth() * 0.6),
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,
}, },
} }
} }
@ -498,7 +533,7 @@ function FileManagerMenu:setUpdateItemTable()
end, end,
}) })
table.insert(self.menu_items.developer_options.sub_item_table, { table.insert(self.menu_items.developer_options.sub_item_table, {
text = "UI layout mirroring and text direction", text = _("UI layout mirroring and text direction"),
sub_item_table = { sub_item_table = {
{ {
text = _("Reverse UI layout mirroring"), text = _("Reverse UI layout mirroring"),

@ -241,6 +241,7 @@ function ReaderDictionary:addToMainMenu(menu_items)
end end
UIManager:show(KeyValuePage:new{ UIManager:show(KeyValuePage:new{
title = _("Dictionary lookup history"), title = _("Dictionary lookup history"),
value_overflow_align = "right",
kv_pairs = kv_pairs, kv_pairs = kv_pairs,
}) })
end, end,
@ -944,8 +945,7 @@ function ReaderDictionary:showDownload(downloadable_dicts)
end end
table.insert(kv_pairs, {lang, ""}) table.insert(kv_pairs, {lang, ""})
table.insert(kv_pairs, {" ".._("License"), dict.license}) table.insert(kv_pairs, {" ".._("License"), dict.license})
table.insert(kv_pairs, {" ".._("Entries"), dict.entries}) table.insert(kv_pairs, {" ".._("Entries"), dict.entries, separator = true})
table.insert(kv_pairs, "----------------------------")
end end
self.download_window = KeyValuePage:new{ self.download_window = KeyValuePage:new{
title = _("Tap dictionary name to download"), title = _("Tap dictionary name to download"),

@ -807,7 +807,7 @@ function ReaderTypography:onPreRenderDocument(config)
-- Add a menu item to language sub-menu, whether the lang is known or not, so the -- Add a menu item to language sub-menu, whether the lang is known or not, so the
-- user can see it and switch from and back to it easily -- user can see it and switch from and back to it easily
table.insert(self.language_submenu, 1, { table.insert(self.language_submenu, 1, {
text = T(_("Book language: %1"), self.book_lang_tag or _("n/a")), text = T(_("Book language: %1"), self.book_lang_tag or _("N/A")),
callback = function() callback = function()
UIManager:show(InfoMessage:new{ UIManager:show(InfoMessage:new{
text = T(_("Changed language for typography rules to book language: %1."), BD.wrap(self.book_lang_tag)), text = T(_("Changed language for typography rules to book language: %1."), BD.wrap(self.book_lang_tag)),

@ -106,6 +106,7 @@ function ReaderWikipedia:addToMainMenu(menu_items)
end end
UIManager:show(KeyValuePage:new{ UIManager:show(KeyValuePage:new{
title = _("Wikipedia history"), title = _("Wikipedia history"),
value_overflow_align = "right",
kv_pairs = kv_pairs, kv_pairs = kv_pairs,
}) })
end, end,

@ -117,8 +117,9 @@ local KeyValueItem = InputContainer:new{
key = nil, key = nil,
value = nil, value = nil,
value_lang = nil, value_lang = nil,
cface = Font:getFace("smallinfofont"), font_size = 20, -- will be adjusted depending on keyvalues_per_page
tface = Font:getFace("smallinfofontbold"), key_font_name = "smallinfofontbold",
value_font_name = "smallinfofont",
width = nil, width = nil,
height = nil, height = nil,
textviewer_width = nil, textviewer_width = nil,
@ -151,12 +152,12 @@ function KeyValueItem:init()
local key_widget = TextWidget:new{ local key_widget = TextWidget:new{
text = self.key, text = self.key,
max_width = available_width, max_width = available_width,
face = self.tface, face = Font:getFace(self.key_font_name, self.font_size),
} }
local value_widget = TextWidget:new{ local value_widget = TextWidget:new{
text = tvalue, text = tvalue,
max_width = available_width, max_width = available_width,
face = self.cface, face = Font:getFace(self.value_font_name, self.font_size),
lang = self.value_lang, lang = self.value_lang,
} }
local key_w_rendered = key_widget:getWidth() local key_w_rendered = key_widget:getWidth()
@ -251,6 +252,8 @@ function KeyValueItem:init()
self[1] = FrameContainer:new{ self[1] = FrameContainer:new{
padding = frame_padding, padding = frame_padding,
padding_top = 0,
padding_bottom = 0,
bordersize = 0, bordersize = 0,
background = Blitbuffer.COLOR_WHITE, background = Blitbuffer.COLOR_WHITE,
HorizontalGroup:new{ HorizontalGroup:new{
@ -456,20 +459,32 @@ function KeyValuePage:init()
local padding = Size.padding.large local padding = Size.padding.large
self.item_width = self.dimen.w - 2 * padding self.item_width = self.dimen.w - 2 * padding
self.item_height = Size.item.height_default
-- setup title bar -- setup title bar
self.title_bar = KeyValueTitle:new{ self.title_bar = KeyValueTitle:new{
title = self.title, title = self.title,
width = self.item_width, width = self.item_width,
height = self.item_height, height = Size.item.height_default,
use_top_page_count = self.use_top_page_count, use_top_page_count = self.use_top_page_count,
kv_page = self, kv_page = self,
} }
-- setup main content -- setup main content
self.item_margin = math.floor(self.item_height / 4) local available_height = self.dimen.h
local line_height = self.item_height + 2 * self.item_margin - self.title_bar:getSize().h
local content_height = self.dimen.h - self.title_bar:getSize().h - self.page_info:getSize().h - self.page_info:getSize().h
self.items_per_page = math.floor(content_height / line_height) - 2*Size.line.thick
-- account for possibly 2 separator lines added
self.items_per_page = G_reader_settings:readSetting("keyvalues_per_page") or self:getDefaultKeyValuesPerPage()
self.item_height = math.floor(available_height / self.items_per_page)
-- Put half of the pixels lost by floor'ing between title and content
local span_height = math.floor((available_height - (self.items_per_page * (self.item_height ))) / 2)
-- Font size is not configurable: we can get a good one from the following
local TextBoxWidget = require("ui/widget/textboxwidget")
local line_extra_height = 1.0 -- ~ 2em -- unscaled_size_check: ignore
-- (gives a font size similar to the fixed one from former implementation at 14 items per page)
self.items_font_size = TextBoxWidget:getFontSizeToFitHeight(self.item_height, 1, line_extra_height)
self.pages = math.ceil(#self.kv_pairs / self.items_per_page) self.pages = math.ceil(#self.kv_pairs / self.items_per_page)
self.main_content = VerticalGroup:new{} self.main_content = VerticalGroup:new{}
@ -485,6 +500,7 @@ function KeyValuePage:init()
VerticalGroup:new{ VerticalGroup:new{
align = "left", align = "left",
self.title_bar, self.title_bar,
VerticalSpan:new{ width = span_height },
self.main_content, self.main_content,
}, },
footer, footer,
@ -499,6 +515,15 @@ function KeyValuePage:init()
} }
end end
function KeyValuePage:getDefaultKeyValuesPerPage()
-- Get a default according to Screen DPI (roughly following
-- the former implementation building logic)
local default_item_height = Size.item.height_default * 1.5 -- we were adding 1/2 as margin
local nb_items = math.floor(Screen:getHeight() / default_item_height)
nb_items = nb_items - 3 -- account for title and footer heights
return nb_items
end
function KeyValuePage:nextPage() function KeyValuePage:nextPage()
local new_page = math.min(self.show_page+1, self.pages) local new_page = math.min(self.show_page+1, self.pages)
if new_page > self.show_page then if new_page > self.show_page then
@ -529,31 +554,37 @@ function KeyValuePage:_populateItems()
local entry = self.kv_pairs[idx_offset + idx] local entry = self.kv_pairs[idx_offset + idx]
if entry == nil then break end if entry == nil then break end
table.insert(self.main_content,
VerticalSpan:new{ width = self.item_margin })
if type(entry) == "table" then if type(entry) == "table" then
table.insert( table.insert(self.main_content, KeyValueItem:new{
self.main_content, height = self.item_height,
KeyValueItem:new{ width = self.item_width,
height = self.item_height, font_size = self.items_font_size,
width = self.item_width, key = entry[1],
key = entry[1], value = entry[2],
value = entry[2], value_lang = self.values_lang,
value_lang = self.values_lang, callback = entry.callback,
callback = entry.callback, callback_back = entry.callback_back,
callback_back = entry.callback_back, textviewer_width = self.textviewer_width,
textviewer_width = self.textviewer_width, textviewer_height = self.textviewer_height,
textviewer_height = self.textviewer_height, value_overflow_align = self.value_overflow_align,
value_overflow_align = self.value_overflow_align, value_align = self.value_align,
value_align = self.value_align, show_parent = self,
show_parent = self, })
} if entry.separator then
) table.insert(self.main_content, LineWidget:new{
background = Blitbuffer.COLOR_LIGHT_GRAY,
dimen = Geom:new{
w = self.item_width,
h = Size.line.thick
},
style = "solid",
})
end
elseif type(entry) == "string" then elseif type(entry) == "string" then
-- deprecated, use separator=true on a regular k/v table
-- (kept in case some user plugins would use this)
local c = string.sub(entry, 1, 1) local c = string.sub(entry, 1, 1)
if c == "-" then if c == "-" then
table.insert(self.main_content,
VerticalSpan:new{ width = self.item_margin })
table.insert(self.main_content, LineWidget:new{ table.insert(self.main_content, LineWidget:new{
background = Blitbuffer.COLOR_LIGHT_GRAY, background = Blitbuffer.COLOR_LIGHT_GRAY,
dimen = Geom:new{ dimen = Geom:new{
@ -564,8 +595,6 @@ function KeyValuePage:_populateItems()
}) })
end end
end end
table.insert(self.main_content,
VerticalSpan:new{ width = self.item_margin })
end end
self.page_info_text:setText(T(_("Page %1 of %2"), self.show_page, self.pages)) self.page_info_text:setText(T(_("Page %1 of %2"), self.show_page, self.pages))
self.page_info_left_chev:showHide(self.pages > 1) self.page_info_left_chev:showHide(self.pages > 1)

@ -63,19 +63,19 @@ function Usage:percentagePerHour()
end end
function Usage:remainingHours() function Usage:remainingHours()
if self:percentagePerHour() == 0 then return "n/a" end if self:percentagePerHour() == 0 then return "N/A" end
local curr = State:new() local curr = State:new()
return curr.percentage / self:percentagePerHour() return curr.percentage / self:percentagePerHour()
end end
function Usage:chargingHours() function Usage:chargingHours()
if self:percentagePerHour() == 0 then return "n/a" end if self:percentagePerHour() == 0 then return "N/A" end
local curr = State:new() local curr = State:new()
return math.abs(curr.percentage - 100) / self:percentagePerHour() return math.abs(curr.percentage - 100) / self:percentagePerHour()
end end
local function shorten(number) local function shorten(number)
if number == "n/a" then return _("n/a") end if number == "N/A" then return _("N/A") end
return string.format("%.2f", number); return string.format("%.2f", number);
end end
@ -199,7 +199,7 @@ function BatteryStat:showStatistics()
self:accumulate() self:accumulate()
local kv_pairs = self:dump() local kv_pairs = self:dump()
table.insert(kv_pairs, "----------") kv_pairs[#kv_pairs].separator = true
table.insert(kv_pairs, {_("If you would like to reset the data,"), "", table.insert(kv_pairs, {_("If you would like to reset the data,"), "",
callback = function() callback = function()
UIManager:setDirty(self.kv_page, "fast") UIManager:setDirty(self.kv_page, "fast")

@ -1099,8 +1099,8 @@ function ReaderStatistics:statMenu()
} }
UIManager:show(self.kv) UIManager:show(self.kv)
end, end,
separator = true,
}, },
"----",
{ _("Last week"),"", { _("Last week"),"",
callback = function() callback = function()
local kv = self.kv local kv = self.kv
@ -1303,8 +1303,7 @@ function ReaderStatistics:getCurrentStat(id_book)
{ _("Pages read this session"), tonumber(current_pages) }, { _("Pages read this session"), tonumber(current_pages) },
-- today -- today
{ _("Time spent reading today"), util.secondsToClock(today_duration, false) }, { _("Time spent reading today"), util.secondsToClock(today_duration, false) },
{ _("Pages read today"), tonumber(today_pages) }, { _("Pages read today"), tonumber(today_pages), separator = true },
"----",
-- Current book statistics -- Current book statistics
-- Includes re-reads -- Includes re-reads
{ _("Total time spent on this book"), util.secondsToClock(total_time_book, false) }, { _("Total time spent on this book"), util.secondsToClock(total_time_book, false) },
@ -1324,7 +1323,7 @@ function ReaderStatistics:getCurrentStat(id_book)
{ _("Estimated reading finished"), { _("Estimated reading finished"),
T(N_("%1 (1 day)", "%1 (%2 days)", estimate_days_to_read), estimate_end_of_read_date, estimate_days_to_read) }, T(N_("%1 (1 day)", "%1 (%2 days)", estimate_days_to_read), estimate_end_of_read_date, estimate_days_to_read) },
{ _("Highlights"), tonumber(highlights) }, { _("Highlights"), tonumber(highlights), separator = true },
-- { _("Total notes"), tonumber(notes) }, -- not accurate, don't show it -- { _("Total notes"), tonumber(notes) }, -- not accurate, don't show it
} }
end end
@ -1404,9 +1403,8 @@ function ReaderStatistics:getBookStat(id_book)
-- These 2 ones are about page actually read (not the current page and % into book) -- These 2 ones are about page actually read (not the current page and % into book)
{ _("Read pages/Total pages"), total_read_pages .. "/" .. pages }, { _("Read pages/Total pages"), total_read_pages .. "/" .. pages },
{ _("Percentage read"), Math.round(total_read_pages / pages * 100) .. "%" }, { _("Percentage read"), Math.round(total_read_pages / pages * 100) .. "%" },
{ _("Highlights"), highlights }, { _("Highlights"), highlights, separator = true },
-- { _("Total notes"), notes }, -- not accurate, don't show it -- { _("Total notes"), notes }, -- not accurate, don't show it
"----",
{ _("Show days"), _("Tap to display"), { _("Show days"), _("Tap to display"),
callback = function() callback = function()
local kv = self.kv local kv = self.kv

@ -31,6 +31,10 @@ function SystemStat:put(p)
table.insert(self.kv_pairs, p) table.insert(self.kv_pairs, p)
end end
function SystemStat:putSeparator()
self.kv_pairs[#self.kv_pairs].separator = true
end
function SystemStat:appendCounters() function SystemStat:appendCounters()
self:put({_("KOReader started at"), os.date("%c", self.start_sec)}) self:put({_("KOReader started at"), os.date("%c", self.start_sec)})
if self.suspend_sec then if self.suspend_sec then
@ -232,8 +236,11 @@ end
function SystemStat:showStatistics() function SystemStat:showStatistics()
self.kv_pairs = {} self.kv_pairs = {}
self:appendCounters() self:appendCounters()
self:putSeparator()
self:appendProcessInfo() self:appendProcessInfo()
self:putSeparator()
self:appendStorageInfo() self:appendStorageInfo()
self:putSeparator()
self:appendSystemInfo() self:appendSystemInfo()
UIManager:show(KeyValuePage:new{ UIManager:show(KeyValuePage:new{
title = _("System statistics"), title = _("System statistics"),

Loading…
Cancel
Save