From 04fba2205d88e818b97d7635ff4e2291c5140b14 Mon Sep 17 00:00:00 2001 From: poire-z Date: Sat, 29 Apr 2023 13:53:12 +0200 Subject: [PATCH] PageBrowser: show page number alongside thumbnails Add a top left menu item -/+ to show none, only on the first thumbnail of a row, or on all thumbnails. Also make the page slot separator longer in the bottom ribbon before pages that start a thumbnails row. Also show a little spike in the bottom ribbon below page slots that get their page number displayed, to ease figuring out the connection. --- frontend/ui/widget/bookmapwidget.lua | 30 +++ frontend/ui/widget/pagebrowserwidget.lua | 240 ++++++++++++++++++----- 2 files changed, 226 insertions(+), 44 deletions(-) diff --git a/frontend/ui/widget/bookmapwidget.lua b/frontend/ui/widget/bookmapwidget.lua index 03e43b0fe..eef5d8467 100644 --- a/frontend/ui/widget/bookmapwidget.lua +++ b/frontend/ui/widget/bookmapwidget.lua @@ -304,6 +304,7 @@ function BookMapRow:init() self.indicators = {} self.bottom_texts = {} local prev_page_was_read = true -- avoid one at start of row + local extended_marker_h = math.ceil(self.span_height * 0.3) local unread_marker_h = math.ceil(self.span_height * 0.05) local read_min_h = math.max(math.ceil(self.span_height * 0.1), unread_marker_h+Size.line.thick) if self.page_slot_width >= 5 * unread_marker_h then @@ -359,6 +360,35 @@ function BookMapRow:init() end prev_page_was_read = false end + -- Extended separators below the baseline if requested (by PageBrowser + -- to show the start of thumbnail rows) + if self.extended_sep_pages and self.extended_sep_pages[page] then + local w = Size.line.thin + local x + if _mirroredUI then + x = self:getPageX(page, true) - w + else + x = self:getPageX(page) + end + local y = self.pages_frame_height - self.pages_frame_border + table.insert(self.pages_markers, { + x = x, y = y, + w = w, h = extended_marker_h, + color = Blitbuffer.COLOR_BLACK, + }) + end + -- Add a little spike below the baseline above each page number displayed, so we + -- can more easily associate the (possibly wider) page number to its page slot. + if self.page_texts and self.page_texts[page] then + local w = Screen:scaleBySize(2) + local x = math.floor((self:getPageX(page) + self:getPageX(page, true) + 0.5)/2 - w/2) + local y = self.pages_frame_height - self.pages_frame_border + 2 + table.insert(self.pages_markers, { + x = x, y = y, + w = w, h = math.ceil(w*1.5), + color = Blitbuffer.COLOR_BLACK, + }) + end -- Indicator for bookmark/highlight type, and current page if self.bookmarked_pages[page] then local page_bookmark_types = self.bookmarked_pages[page] diff --git a/frontend/ui/widget/pagebrowserwidget.lua b/frontend/ui/widget/pagebrowserwidget.lua index f4c06560c..6408a0460 100644 --- a/frontend/ui/widget/pagebrowserwidget.lua +++ b/frontend/ui/widget/pagebrowserwidget.lua @@ -13,6 +13,7 @@ local InfoMessage = require("ui/widget/infomessage") local InputContainer = require("ui/widget/container/inputcontainer") local OverlapGroup = require("ui/widget/overlapgroup") local Size = require("ui/size") +local TextBoxWidget = require("ui/widget/textboxwidget") local TextWidget = require("ui/widget/textwidget") local TitleBar = require("ui/widget/titlebar") local UIManager = require("ui/uimanager") @@ -21,6 +22,7 @@ local VerticalSpan = require("ui/widget/verticalspan") local Input = Device.input local Screen = Device.screen local logger = require("logger") +local util = require("util") local _ = require("gettext") -- We use the BookMapRow widget, a local widget defined in bookmapwidget.lua, @@ -134,6 +136,21 @@ function PageBrowserWidget:init() self.span_height = test_w:getSize().h + BookMapRow.toc_span_border test_w:free() + -- For page numbers alongside thumbnails, use the same font size + -- we use for them in the ribbon + self.page_num_font_face = Font:getFace("infofont", 10) + if not self.page_num_width then + -- We'll be displaying the number vertically, so get the width we'd need + -- to display some wide single char (this will influence side and inter + -- thumbnails margins). + test_w = TextWidget:new{ + text = "W", + face = self.page_num_font_face, + } + self.page_num_width = test_w:getWidth() + test_w:free() + end + self.min_nb_rows = 1 self.max_nb_rows = 6 self.min_nb_cols = 1 @@ -245,7 +262,9 @@ function PageBrowserWidget:updateLayout() end self.nb_grid_items = self.nb_rows * self.nb_cols -- Set our items target size - self.grid_item_margin = Screen:scaleBySize(10) -- borders will eat into this, it should be larger than borders thin+thick + -- Borders may eat into the margin, and the horizontal margin should be able to contain the page number + -- (let's use this computed margin also vertically) + self.grid_item_margin = self.page_num_width + Size.padding.small + Size.border.thick + Size.border.thin self.grid_item_height = math.floor((self.grid_height - (self.nb_rows)*self.grid_item_margin) / self.nb_rows) -- no need for top margin, title bottom padding is enough self.grid_item_width = math.floor((self.grid_width - (1+self.nb_cols)*self.grid_item_margin) / self.nb_cols) self.grid_item_dimen = Geom:new{ @@ -255,9 +274,17 @@ function PageBrowserWidget:updateLayout() self.grid:clear() + self.thumbnails_pagenums = self.ui.doc_settings:readSetting("page_browser_thumbnails_pagenums") + or G_reader_settings:readSetting("page_browser_thumbnails_pagenums") or 2 for idx = 1, self.nb_grid_items do local row = math.floor((idx-1)/self.nb_cols) -- start from 0 local col = (idx-1) % self.nb_cols + local show_pagenum -- no page number shown on the left side of a thumbnail, unless: + if self.thumbnails_pagenums == 1 then -- only for the first thumbnail of each row + show_pagenum = col == 0 + elseif self.thumbnails_pagenums == 2 then -- for all thumnbnails + show_pagenum = true + end if BD.mirroredUILayout() then col = self.nb_cols - col - 1 end @@ -267,6 +294,7 @@ function PageBrowserWidget:updateLayout() dimen = self.grid_item_dimen:copy(), } table.insert(self.grid, FrameContainer:new{ + show_pagenum = show_pagenum, overlap_offset = {offset_x, offset_y}, margin = 0, padding = 0, @@ -305,6 +333,14 @@ function PageBrowserWidget:update() end self.requests_batch_id = "PageBrowserWidget"..tostring(os.time()) + for i=#self.grid, 1, -1 do + if self.grid[i].is_page_num_widget then + -- Remove page_num_widgets, as we'll be recreating them + local widget = table.remove(self.grid, i) + widget:free() + end + end + if not self.focus_page then self.focus_page = self.cur_page or 1 end @@ -326,64 +362,96 @@ function PageBrowserWidget:update() p_start = 1 end + -- Extended separators below the baseline for pages starting thumbnail rows + local extended_sep_pages = {} + for p=grid_page_start+self.nb_cols, grid_page_end, self.nb_cols do + extended_sep_pages[p] = true + end + -- Show the page number or label at the bottom page slot every N slots, with N -- the nb of thumbnails so we get at least one page label in our viewport. local page_texts_cycle = math.min(self.nb_grid_items, 10) -- but max 10 local next_p = p_start local cur_page_label_idx = 1 - local page_texts = {} + local page_texts = {} -- to be provided to the bottom ribbon BookMapRow + self.pagenum_page_texts = {} -- to be displayed alongside thumbnails for p=p_start, p_end do + -- This may be expensive, so compute only the ones we need for display + local show_at_bottom if p >= next_p then -- Only show a page text if there is no indicator on that slot if p ~= self.cur_page and not self.bookmarked_pages[p] and not self.previous_locations[p] then - local page_text - if self.page_labels then - local page_label - for idx=cur_page_label_idx, #self.page_labels do - local item = self.page_labels[idx] - if item.page >= p then - if item.page == p then - page_label = item.label - end - break + show_at_bottom = true + end + end + local show_near_thumbnail + if p >= grid_page_start and p <= grid_page_end then + show_near_thumbnail = self.grid[p - grid_page_start + 1].show_pagenum + end + if show_at_bottom or show_near_thumbnail then + local page_text, thumbnail_page_text + if self.page_labels then + local page_label + for idx=cur_page_label_idx, #self.page_labels do + local item = self.page_labels[idx] + if item.page >= p then + if item.page == p then + page_label = item.label end - cur_page_label_idx = idx - end - if page_label then - page_text = self.ui.pagemap:cleanPageLabel(page_label) - end - elseif self.has_hidden_flows then - local flow = self.ui.document:getPageFlow(p) - if flow == 0 then - page_text = tostring(self.ui.document:getPageNumberInFlow(p)) - else - page_text = string.format("[%d]%d", self.ui.document:getPageNumberInFlow(p), self.ui.document:getPageFlow(p)) + break end + cur_page_label_idx = idx + end + if page_label then + page_text = self.ui.pagemap:cleanPageLabel(page_label) + elseif show_near_thumbnail then + -- When reference pages may span multiple screen pages, the above may not get + -- a page_text for some pages, which is fine for the bottom ribbon: it will + -- display it for the next slot where a new reference page starts. + -- But for thumbnails, we want to show some page number text, so fetch + -- the previous one (that started on a previous screen page). + thumbnail_page_text = self.ui.pagemap:cleanPageLabel(self.page_labels[cur_page_label_idx].label) + end + elseif self.has_hidden_flows then + local flow = self.ui.document:getPageFlow(p) + if flow == 0 then + page_text = tostring(self.ui.document:getPageNumberInFlow(p)) else - page_text = tostring(p) + local page_number_in_flow = self.ui.document:getPageNumberInFlow(p) + local page_flow = self.ui.document:getPageFlow(p) + page_text = string.format("[%d]%d", page_number_in_flow, page_flow) + -- Use something that will feel alike brackets when vertically + -- (Harfbuzz will properly mirror these if the UI is RTL) + thumbnail_page_text = string.format("\u{2E1D}%d\u{2E0C}%d", page_number_in_flow, page_flow) end - if page_text then - local page_block, page_block_dx -- centered by default - if p == p_start or p == grid_page_start or p == grid_page_end+1 then - page_block = "left" - page_block_dx = Size.padding.tiny - if p == grid_page_start then - page_block_dx = page_block_dx + self.view_finder_bw + 1 - end - elseif p == p_end or p == grid_page_end or p == grid_page_start-1 then - page_block = "right" - page_block_dx = Size.padding.tiny - if p == grid_page_end then - page_block_dx = page_block_dx + self.view_finder_bw + 1 - end + else + page_text = tostring(p) + end + if page_text and show_at_bottom then + local page_block, page_block_dx -- centered by default + if p == p_start or p == grid_page_start or p == grid_page_end+1 then + page_block = "left" + page_block_dx = Size.padding.tiny + if p == grid_page_start then + page_block_dx = page_block_dx + self.view_finder_bw + 1 + end + elseif p == p_end or p == grid_page_end or p == grid_page_start-1 then + page_block = "right" + page_block_dx = Size.padding.tiny + if p == grid_page_end then + page_block_dx = page_block_dx + self.view_finder_bw + 1 end - page_texts[p] = { - text = page_text, - block = page_block, - block_dx = page_block_dx, - } - next_p = p + page_texts_cycle end + page_texts[p] = { + text = page_text, + block = page_block, + block_dx = page_block_dx, + } + next_p = p + page_texts_cycle + end + if show_near_thumbnail then + -- Dedicated thumbnail_page_text, or the default one + self.pagenum_page_texts[p] = thumbnail_page_text or page_text end end end @@ -477,6 +545,7 @@ function PageBrowserWidget:update() read_pages = self.read_pages, current_session_duration = self.current_session_duration, page_texts = page_texts, + extended_sep_pages = extended_sep_pages, } self.row[1] = row @@ -601,6 +670,36 @@ function PageBrowserWidget:showTile(grid_idx, page, tile, do_refresh) -- thumb_frame will overflow its CenterContainer because of the added borders, -- but CenterContainer handles that well. We will refresh the outer dimensions. + local page_num_widget + if item_frame.show_pagenum and self.pagenum_page_texts[page] then + local page_text = table.concat(util.splitToChars(self.pagenum_page_texts[page]), "\n") + page_num_widget = TextBoxWidget:new{ + text = page_text, + width = self.page_num_width, + face = self.page_num_font_face, + line_height = 0, -- no additional line height + alignment = BD.mirroredUILayout() and "left" or "right", + alignment_strict = true, + is_page_num_widget = true, -- so we can clear them in :update() + } + -- Only now that we know the thumbnail size, we can position this vertical + -- page number widget alongside and at the top of the thumbnail left edge + local thumb_frame_dimen = thumb_frame:getSize() + local dw = self.grid_item_width - thumb_frame_dimen.w + local dh = self.grid_item_height - thumb_frame_dimen.h + local dx = math.floor(dw/2) + local dy = math.floor(dh/2) + local offset_y = item_frame.overlap_offset[2] + dy + local offset_x + if BD.mirroredUILayout() then + offset_x = item_frame.overlap_offset[1] + self.grid_item_width - dx + Size.padding.small + else + offset_x = item_frame.overlap_offset[1] + dx - page_num_widget:getSize().w - Size.padding.small + end + page_num_widget.overlap_offset = {offset_x, offset_y} + table.insert(self.grid, page_num_widget) + end + if do_refresh then if self.wait_for_refresh_on_show_tile then self.wait_for_refresh_on_show_tile = nil @@ -609,6 +708,9 @@ function PageBrowserWidget:showTile(grid_idx, page, tile, do_refresh) UIManager:waitForVSync() end UIManager:setDirty(self, function() + if page_num_widget then + return "ui", thumb_frame.dimen:combine(page_num_widget.dimen) + end return "ui", thumb_frame.dimen end) end @@ -687,6 +789,33 @@ function PageBrowserWidget:showMenu() width = plus_minus_width, } }, + { + { + text = _("Thumbnail page numbers"), + callback = function() end, + align = "left", + }, + { + text = "\u{2796}", -- Heavy minus sign + enabled_func = function() return self.thumbnails_pagenums > 0 end, + callback = function() + if self:updateThumbnailPageNumsDisplayType(-1, true) then + self:updateLayout() + end + end, + width = plus_minus_width, + }, + { + text = "\u{2795}", -- Heavy plus sign + enabled_func = function() return self.thumbnails_pagenums < 2 end, + callback = function() + if self:updateThumbnailPageNumsDisplayType(1, true) then + self:updateLayout() + end + end, + width = plus_minus_width, + } + }, { { text = _("Chapters in bottom ribbon"), @@ -809,10 +938,12 @@ function PageBrowserWidget:saveSettings(reset) self.ui.doc_settings:saveSetting("page_browser_toc_depth", self.nb_toc_spans) self.ui.doc_settings:saveSetting("page_browser_nb_rows", self.nb_rows) self.ui.doc_settings:saveSetting("page_browser_nb_cols", self.nb_cols) + self.ui.doc_settings:saveSetting("page_browser_thumbnails_pagenums", self.thumbnails_pagenums) -- We also save nb_rows/nb_cols as global settings, so they will apply on other books -- where they were not already set G_reader_settings:saveSetting("page_browser_nb_rows", self.nb_rows) G_reader_settings:saveSetting("page_browser_nb_cols", self.nb_cols) + G_reader_settings:saveSetting("page_browser_thumbnails_pagenums", self.thumbnails_pagenums) end function PageBrowserWidget:updateNbTocSpans(value, relative, rollover) @@ -886,6 +1017,27 @@ function PageBrowserWidget:updateNbRows(value, relative) return true end +function PageBrowserWidget:updateThumbnailPageNumsDisplayType(value, relative) + local new_thumbnails_pagenums + if relative then + new_thumbnails_pagenums = self.thumbnails_pagenums + value + else + new_thumbnails_pagenums = value + end + if new_thumbnails_pagenums < 0 then + new_thumbnails_pagenums = 0 + end + if new_thumbnails_pagenums > 2 then + new_thumbnails_pagenums = 2 + end + if new_thumbnails_pagenums == self.thumbnails_pagenums then + return false + end + self.thumbnails_pagenums = new_thumbnails_pagenums + self:saveSettings() + return true +end + function PageBrowserWidget:updateFocusPage(value, relative) local new_focus_page if relative then