From 04425b74fc6fe3768f89ed6127455a8084f8b20b Mon Sep 17 00:00:00 2001 From: poire-z Date: Fri, 2 Jun 2023 20:44:47 +0200 Subject: [PATCH] BookMap: add "Overview" mode Add a restricted but convenient mode showing BookMap like in initial view, while still allowing chapter levels to be tweaked. This allows getting back to this view with another gesture to see the overall progress in the book, while still having the normal BookMap in flat mode acting as an alternative ToC. Available as an action to associate to a gesture, and with long-press on the "Book map" menu item. --- .../apps/reader/modules/readerthumbnail.lua | 9 +- frontend/dispatcher.lua | 2 + frontend/ui/widget/bookmapwidget.lua | 92 +++++++++++++++---- 3 files changed, 85 insertions(+), 18 deletions(-) diff --git a/frontend/apps/reader/modules/readerthumbnail.lua b/frontend/apps/reader/modules/readerthumbnail.lua index 333526ad6..6b2c76f68 100644 --- a/frontend/apps/reader/modules/readerthumbnail.lua +++ b/frontend/apps/reader/modules/readerthumbnail.lua @@ -68,6 +68,12 @@ function ReaderThumbnail:addToMainMenu(menu_items) callback = function() self:onShowBookMap() end, + -- Show the alternative overview mode (which is just a restricted + -- variation of the main book map) with long-press (let's avoid + -- adding another item in the crowded first menu). + hold_callback = function() + self:onShowBookMap(true) + end, } menu_items.page_browser = { text = _("Page browser"), @@ -77,10 +83,11 @@ function ReaderThumbnail:addToMainMenu(menu_items) } end -function ReaderThumbnail:onShowBookMap() +function ReaderThumbnail:onShowBookMap(overview_mode) local BookMapWidget = require("ui/widget/bookmapwidget") UIManager:show(BookMapWidget:new{ ui = self.ui, + overview_mode = overview_mode, }) return true end diff --git a/frontend/dispatcher.lua b/frontend/dispatcher.lua index 879b73df3..45523fb29 100644 --- a/frontend/dispatcher.lua +++ b/frontend/dispatcher.lua @@ -141,6 +141,7 @@ local settingsList = { toc = {category="none", event="ShowToc", title=_("Table of contents"), reader=true}, book_map = {category="none", event="ShowBookMap", title=_("Book map"), reader=true, condition=Device:isTouchDevice()}, + book_map_overview = {category="none", event="ShowBookMap", arg=true, title=_("Book map (overview)"), reader=true, condition=Device:isTouchDevice()}, page_browser = {category="none", event="ShowPageBrowser", title=_("Page browser"), reader=true, condition=Device:isTouchDevice()}, bookmarks = {category="none", event="ShowBookmark", title=_("Bookmarks"), reader=true}, bookmark_search = {category="none", event="SearchBookmark", title=_("Bookmark search"), reader=true}, @@ -333,6 +334,7 @@ local dispatcher_menu_order = { "toc", "book_map", + "book_map_overview", "page_browser", "bookmarks", "bookmark_search", diff --git a/frontend/ui/widget/bookmapwidget.lua b/frontend/ui/widget/bookmapwidget.lua index 1bce8f9be..3cd2cf3d8 100644 --- a/frontend/ui/widget/bookmapwidget.lua +++ b/frontend/ui/widget/bookmapwidget.lua @@ -588,7 +588,6 @@ end -- BookMapWidget: shows a map of content, including TOC, boomarks, read pages, non-linear flows... local BookMapWidget = InputContainer:extend{ - title = _("Book map"), -- Focus page: show the BookMapRow containing this page -- in the middle of screen focus_page = nil, @@ -596,6 +595,8 @@ local BookMapWidget = InputContainer:extend{ launcher = nil, -- Extra symbols to show below pages extra_symbols_pages = nil, + -- Restricted mode, as initial view (all on one screen), but allowing chapter levels changes + overview_mode = false, -- Make this local subwidget available for reuse by PageBrowser BookMapRow = BookMapRow, @@ -675,12 +676,13 @@ function BookMapWidget:init() self.row_left_spacing = self.scrollbar_width self.swipe_hint_bar_width = Screen:scaleBySize(6) + local title = self.overview_mode and _("Book map (overview)") or _("Book map") self.title_bar = TitleBar:new{ fullscreen = true, - title = self.title, + title = title, left_icon = "appbar.menu", left_icon_tap_callback = function() self:showMenu() end, - left_icon_hold_callback = function() + left_icon_hold_callback = not self.overview_mode and function() self:toggleDefaultSettings() -- toggle between user settings and default view end, close_callback = function() self:onClose() end, @@ -809,6 +811,11 @@ function BookMapWidget:update() -- Non-flat book map shows a grid with TOC items following each others. self.flat_map = self.ui.doc_settings:readSetting("book_map_flat", false) self.toc_depth = self.ui.doc_settings:readSetting("book_map_toc_depth", self.max_toc_depth) + if self.overview_mode then + -- Restricted to grid mode, fitting on the screen. Only toc depth can be adjusted. + self.flat_map = false + self.toc_depth = self.ui.doc_settings:readSetting("book_map_overview_toc_depth", self.max_toc_depth) + end if self.flat_map then self.nb_toc_spans = 0 -- no span shown in grid else @@ -882,6 +889,9 @@ function BookMapWidget:update() -- Show the whole book without scrollbar initially self.pages_per_row = self.ui.doc_settings:readSetting("book_map_pages_per_row", self.fit_pages_per_row) + if self.overview_mode then + self.pages_per_row = self.fit_pages_per_row + end self.page_slot_width = nil -- will be fetched from the first BookMapRow -- Build BookMapRows as we walk the ToC @@ -1126,7 +1136,7 @@ function BookMapWidget:showMenu() local plus_minus_width = Screen:scaleBySize(60) local buttons = { {{ - text = _("About book map"), + text = self.overview_mode and _("About book map (overview)") or _("About book map"), align = "left", callback = function() self:showAbout() @@ -1166,7 +1176,7 @@ function BookMapWidget:showMenu() b:refresh() end, }}, - {{ + not self.overview_mode and {{ text = _("Switch current/initial views"), align = "left", enabled_func = function() return self.toc_depth > 0 end, @@ -1174,7 +1184,7 @@ function BookMapWidget:showMenu() self:toggleDefaultSettings() end, }}, - {{ + not self.overview_mode and {{ text = _("Switch grid/flat views"), align = "left", enabled_func = function() return self.toc_depth > 0 end, @@ -1211,7 +1221,7 @@ function BookMapWidget:showMenu() width = plus_minus_width, } }, - { + not self.overview_mode and { { text = _("Page slot width"), callback = function() end, @@ -1268,6 +1278,12 @@ function BookMapWidget:showMenu() } }, } + -- Remove false buttons from the list if overview_mode + for i = #buttons, 1, -1 do + if not buttons[i] then + table.remove(buttons, i) + end + end button_dialog = ButtonDialog:new{ -- width = math.floor(Screen:getWidth() / 2), width = math.floor(Screen:getWidth() * 0.9), -- max width, will get smaller @@ -1280,8 +1296,7 @@ function BookMapWidget:showMenu() UIManager:show(button_dialog) end function BookMapWidget:showAbout() - UIManager:show(InfoMessage:new{ - text = _([[ + local text = _([[ Book map displays an overview of the book content. If statistics are enabled, black bars are shown for already read pages (gray for pages read in the current reading session). Their heights vary depending on the time spent reading the page. @@ -1292,14 +1307,28 @@ Under the pages, these indicators may be shown: ▒ highlighted text  highlighted text with notes  bookmarked page -▢ focused page when coming from Pages browser +▢ focused page when coming from Pages browser]]) -On a newly opened book, the book map will start in grid mode showing all chapter levels, fitting on a single screen, to give the best initial overview of the book's content.]]), - }) + if self.overview_mode then + text = text .. "\n\n" .. _([[ +In overview mode, the book map is always in grid mode and ensured to fit on a single screen. Chapter levels can be changed for the most confortable overview.]]) + else + text = text .. "\n\n" .. _([[ +On a newly opened book, the book map will start in grid mode showing all chapter levels, fitting on a single screen, to give the best initial overview of the book's content.]]) + end + UIManager:show(InfoMessage:new{ text = text }) end function BookMapWidget:showGestures() - UIManager:show(InfoMessage:new{ + local text + if self.overview_mode then + text = _([[ +Tap on a location in the book to browse thumbnails of the pages there. + +Swipe along the left screen edge to change the level of chapters to include in the book map. + +Any multiswipe will close the book map.]]) + else text = _([[ Tap on a location in the book to browse thumbnails of the pages there. @@ -1311,8 +1340,9 @@ Swipe or pan vertically on content to scroll. Long-press on ≡ to switch between current and initial views. -Any multiswipe will close the book map.]]), - }) +Any multiswipe will close the book map.]]) + end + UIManager:show(InfoMessage:new{ text = text }) end function BookMapWidget:onClose(close_all_parents) @@ -1430,6 +1460,10 @@ function BookMapWidget:saveSettings(reset) self.toc_depth = nil self.pages_per_row = nil end + if self.overview_mode then + self.ui.doc_settings:saveSetting("book_map_overview_toc_depth", self.toc_depth) + return + end self.ui.doc_settings:saveSetting("book_map_flat", self.flat_map) self.ui.doc_settings:saveSetting("book_map_toc_depth", self.toc_depth) self.ui.doc_settings:saveSetting("book_map_pages_per_row", self.pages_per_row) @@ -1465,7 +1499,7 @@ function BookMapWidget:updateTocDepth(depth, flat) else new_toc_depth = new_toc_depth + depth end - if new_toc_depth < 0 then + if new_toc_depth < 0 and not self.overview_mode then new_toc_depth = - new_toc_depth new_flat_map = not new_flat_map end @@ -1524,6 +1558,9 @@ function BookMapWidget:onSwipe(arg, ges) end end if ges.pos.y > Screen:getHeight() * 7/8 then + if self.overview_mode then + return true + end -- Swipe along the bottom screen edge: increase/decrease pages per row if direction == "west" or direction == "east" then -- Have a swipe distance 0.8 x screen width do *2 or *1/2 @@ -1545,6 +1582,13 @@ function BookMapWidget:onSwipe(arg, ges) return true end end + if self.overview_mode and not self.cropping_widget._is_scrollable and direction == "south" then + -- Swipe south won't have any effect in overview mode as we fit on the page (except on + -- really big books, where we can still be scrollable), so allow swipe south to close + -- as on some other fullscreen widgets. + self:onClose() + return true + end -- Let our MovableContainer handle other swipes: -- return self.cropping_widget:onScrollableSwipe(arg, ges) -- No, we prefer not to, and have swipe north/south do full prev/next page @@ -1565,6 +1609,9 @@ function BookMapWidget:onSwipe(arg, ges) end function BookMapWidget:onPinch(arg, ges) + if self.overview_mode then + return true + end local updated = false if ges.direction == "horizontal" or ges.direction == "diagonal" then local new_pages_per_row = math.ceil(self.pages_per_row * 1.5) @@ -1594,6 +1641,9 @@ function BookMapWidget:onPinch(arg, ges) end function BookMapWidget:onSpread(arg, ges) + if self.overview_mode then + return true + end local updated = false if ges.direction == "horizontal" or ges.direction == "diagonal" then local new_pages_per_row = math.floor(self.pages_per_row * (2/3)) @@ -1663,7 +1713,9 @@ function BookMapWidget:paintTo(bb, x, y) InputContainer.paintTo(self, bb, x, y) -- And explicitely paint "swipe" hints along the left and bottom borders self:paintLeftVerticalSwipeHint(bb, x, y) - self:paintBottomHorizontalSwipeHint(bb, x, y) + if not self.overview_mode then + self:paintBottomHorizontalSwipeHint(bb, x, y) + end end function BookMapWidget:paintLeftVerticalSwipeHint(bb, x, y) @@ -1683,6 +1735,9 @@ function BookMapWidget:paintLeftVerticalSwipeHint(bb, x, y) v.top = self.title_bar_h + math.floor(self.crop_height * 1/6) v.height = math.floor(self.crop_height * 4/6) v.nb_units = self.max_toc_depth * 2 + 1 + if self.overview_mode then + v.nb_units = self.max_toc_depth + 1 + end v.unit_h = math.floor(v.height / v.nb_units) self.vs_hint_info = v end @@ -1695,6 +1750,9 @@ function BookMapWidget:paintLeftVerticalSwipeHint(bb, x, y) else -- lower part of the vertical bar unit_idx = self.max_toc_depth + self.toc_depth end + if self.overview_mode then + unit_idx = self.toc_depth + end local dy = unit_idx * v.unit_h if unit_idx == v.nb_units - 1 then -- avoid possible rounding error for last unit