From f0ecbeb1d5a9a3db4b09acbb5f271e83eb1e433c Mon Sep 17 00:00:00 2001 From: poire-z Date: Thu, 13 May 2021 01:55:35 +0200 Subject: [PATCH] ReaderToc: option to show a dotted line (#7669) A dotted line joining the ToC entry text to the page number may make it easier to use. (This replaces the menu item separator from d879062e.) Also fix baselines aligment, which could be a bit off. --- .../apps/reader/modules/readerpagemap.lua | 3 + frontend/apps/reader/modules/readertoc.lua | 13 +-- frontend/ui/elements/reader_menu_order.lua | 2 +- frontend/ui/widget/menu.lua | 96 ++++++++++++++++--- 4 files changed, 96 insertions(+), 18 deletions(-) diff --git a/frontend/apps/reader/modules/readerpagemap.lua b/frontend/apps/reader/modules/readerpagemap.lua index f2daba680..e2e834a18 100644 --- a/frontend/apps/reader/modules/readerpagemap.lua +++ b/frontend/apps/reader/modules/readerpagemap.lua @@ -212,6 +212,7 @@ function ReaderPageMap:onShowPageList() -- We use the per-page and font-size settings set for the ToC local items_per_page = G_reader_settings:readSetting("toc_items_per_page") or 14 local items_font_size = G_reader_settings:readSetting("toc_items_font_size") or Menu.getItemFontSize(items_per_page) + local items_with_dots = G_reader_settings:nilOrTrue("toc_items_with_dots") local pl_menu = Menu:new{ title = _("Reference page numbers list"), @@ -225,6 +226,8 @@ function ReaderPageMap:onShowPageList() items_font_size = items_font_size, line_color = require("ffi/blitbuffer").COLOR_WHITE, single_line = true, + align_baselines = true, + with_dots = items_with_dots, on_close_ges = { GestureRange:new{ ges = "two_finger_swipe", diff --git a/frontend/apps/reader/modules/readertoc.lua b/frontend/apps/reader/modules/readertoc.lua index dc571df47..0174bcd3a 100644 --- a/frontend/apps/reader/modules/readertoc.lua +++ b/frontend/apps/reader/modules/readertoc.lua @@ -643,7 +643,7 @@ function ReaderToc:onShowToc() local items_per_page = G_reader_settings:readSetting("toc_items_per_page") or self.toc_items_per_page_default local items_font_size = G_reader_settings:readSetting("toc_items_font_size") or Menu.getItemFontSize(items_per_page) - local items_show_separator = G_reader_settings:isTrue("toc_items_show_separator") + local items_with_dots = G_reader_settings:nilOrTrue("toc_items_with_dots") -- Estimate expand/collapse icon size -- *2/5 to acount for Menu top title and bottom icons, and add some space between consecutive icons local icon_size = math.floor(Screen:getHeight() / items_per_page * 2/5) @@ -703,10 +703,11 @@ function ReaderToc:onShowToc() cface = Font:getFace("x_smallinfofont"), single_line = true, align_baselines = true, + with_dots = items_with_dots, items_per_page = items_per_page, items_font_size = items_font_size, items_padding = can_collapse and math.floor(Size.padding.fullscreen / 2) or nil, -- c.f., note above. Menu's default is twice that. - line_color = items_show_separator and Blitbuffer.COLOR_DARK_GRAY or Blitbuffer.COLOR_WHITE, + line_color = Blitbuffer.COLOR_WHITE, on_close_ges = { GestureRange:new{ ges = "two_finger_swipe", @@ -1062,14 +1063,14 @@ Enabling this option will restrict display to the chapter titles of progress bar UIManager:show(items_font) end, } - menu_items.toc_items_show_separator = { - text = _("Add a separator between ToC entries"), + menu_items.toc_items_with_dots = { + text = _("With dots"), keep_menu_open = true, checked_func = function() - return G_reader_settings:isTrue("toc_items_show_separator") + return G_reader_settings:nilOrTrue("toc_items_with_dots") end, callback = function() - G_reader_settings:flipNilOrFalse("toc_items_show_separator") + G_reader_settings:flipNilOrTrue("toc_items_with_dots") end } end diff --git a/frontend/ui/elements/reader_menu_order.lua b/frontend/ui/elements/reader_menu_order.lua index 16366535c..bd69c36b2 100644 --- a/frontend/ui/elements/reader_menu_order.lua +++ b/frontend/ui/elements/reader_menu_order.lua @@ -33,7 +33,7 @@ local order = { "----------------------------", "toc_items_per_page", "toc_items_font_size", - "toc_items_show_separator", + "toc_items_with_dots", "----------------------------", "bookmarks_items_per_page", "bookmarks_items_font_size", diff --git a/frontend/ui/widget/menu.lua b/frontend/ui/widget/menu.lua index dc303c6d4..804f28687 100644 --- a/frontend/ui/widget/menu.lua +++ b/frontend/ui/widget/menu.lua @@ -151,6 +151,8 @@ local MenuItem = InputContainer:new{ multilines_show_more_text = false, -- Align text & mandatory baselines (only when single_line=true) align_baselines = false, + -- Show a line of dots (also called tab or dot leaders) between text and mandatory + with_dots = false, } function MenuItem:init() @@ -265,6 +267,9 @@ function MenuItem:init() text = self.bidi_wrap_func(text) end + local dots_widget + local dots_left_padding = Size.padding.small + local dots_right_padding = Size.padding.small if self.single_line then -- items only in single line -- No font size change: text will be truncated if it overflows item_name = TextWidget:new{ @@ -279,21 +284,54 @@ function MenuItem:init() -- feeling (which might make it no more truncated, but well...) local text_max_width_if_ellipsis = available_width + text_mandatory_padding - text_ellipsis_mandatory_padding item_name:setMaxWidth(text_max_width_if_ellipsis) + else + if self.with_dots then + local dots_width = available_width + text_mandatory_padding - w - dots_left_padding - dots_right_padding + if dots_width > 0 then + local dots_text, dots_min_width = self:getDotsText(self.info_face) + -- Don't show any dots if there would be less than 3 + if dots_width >= dots_min_width then + dots_widget = TextWidget:new{ + text = dots_text, + face = self.info_face, -- same as mandatory widget, to keep their baseline adjusted + max_width = dots_width, + truncate_with_ellipsis = false, + } + end + end + end end if self.align_baselines then -- Align baselines of text and mandatory + -- The container widgets would additionally center these widgets, + -- so make sure they all get a height=self.dimen.h so they don't + -- risk being shifted later and becoming misaligned local name_baseline = item_name:getBaseline() - local mandatory_baseline = mandatory_widget:getBaseline() - local baselines_diff = Math.round(name_baseline - mandatory_baseline) + local mdtr_baseline = mandatory_widget:getBaseline() + local name_height = item_name:getSize().h + local mdtr_height = mandatory_widget:getSize().h + -- Make all the TextWidgets be self.dimen.h + item_name.forced_height = self.dimen.h + mandatory_widget.forced_height = self.dimen.h + if dots_widget then + dots_widget.forced_height = self.dimen.h + end + -- And adjust their baselines for proper centering and alignment + -- (We made sure the font sizes wouldn't exceed self.dimen.h, so we + -- get only non-negative pad_top here, and we're moving them down.) + local name_missing_pad_top = math.floor( (self.dimen.h - name_height) / 2) + local mdtr_missing_pad_top = math.floor( (self.dimen.h - mdtr_height) / 2) + name_baseline = name_baseline + name_missing_pad_top + mdtr_baseline = mdtr_baseline + mdtr_missing_pad_top + local baselines_diff = Math.round(name_baseline - mdtr_baseline) if baselines_diff > 0 then - mandatory_widget = VerticalGroup:new{ - VerticalSpan:new{width = baselines_diff}, - mandatory_widget, - } - elseif baselines_diff < 0 then - item_name = VerticalGroup:new{ - VerticalSpan:new{width = -baselines_diff}, - item_name, - } + mdtr_baseline = mdtr_baseline + baselines_diff + else + name_baseline = name_baseline - baselines_diff + end + item_name.forced_baseline = name_baseline + mandatory_widget.forced_baseline = mdtr_baseline + if dots_widget then + dots_widget.forced_baseline = mdtr_baseline end end @@ -381,6 +419,13 @@ function MenuItem:init() } } + if dots_widget then + mandatory_widget = HorizontalGroup:new{ + dots_widget, + HorizontalSpan:new{ width = dots_right_padding }, + mandatory_widget, + } + end local mandatory_container = RightContainer:new{ dimen = Geom:new{w = self.content_width, h = self.dimen.h}, mandatory_widget, @@ -427,6 +472,34 @@ function MenuItem:init() } end +local _dots_cached_info + +function MenuItem:getDotsText(face) + local screen_w = Screen:getWidth() + if not _dots_cached_info or _dots_cached_info.screen_width ~= screen_w + or _dots_cached_info.face ~= face then + local unit = "." + local tmp = TextWidget:new{ + text = unit, + face = face, + } + local unit_w = tmp:getSize().w + tmp:free() + -- (We assume/expect no kerning will happen between consecutive units) + local nb_units = math.ceil(screen_w / unit_w) + local min_width = unit_w * 3 -- have it not shown if smaller than this + local text = unit:rep(nb_units) + _dots_cached_info = { + text = text, + min_width = min_width, + screen_width = screen_w, + face = face, + } + end + return _dots_cached_info.text, _dots_cached_info.min_width + +end + function MenuItem:onFocus(initial_focus) if Device:isTouchDevice() then -- Devices which are Keys capable will get this onFocus called by @@ -1079,6 +1152,7 @@ function Menu:updateItems(select_number) single_line = self.single_line, multilines_show_more_text = multilines_show_more_text, align_baselines = self.align_baselines, + with_dots = self.with_dots, line_color = self.line_color, items_padding = self.items_padding, }