From 954ba9608ee37994c0df968c6569268d33a2f3f8 Mon Sep 17 00:00:00 2001 From: poire-z Date: Sat, 14 Aug 2021 22:15:01 +0200 Subject: [PATCH] Button: reduce font size to avoid truncation (#8078) If the button text would be truncated, try to avoid it by reducing the font size, and even switching to a 2-lines TextBoxWidget. TextBoxWidget: fix possible glyphs truncations when a small line_height is used. Also avoid some bad result from getFontSizeToFitHeight(), possible due to some rounding errors. --- frontend/ui/widget/button.lua | 44 ++++++++++++++++++++++++++-- frontend/ui/widget/textboxwidget.lua | 33 ++++++++++++++------- 2 files changed, 65 insertions(+), 12 deletions(-) diff --git a/frontend/ui/widget/button.lua b/frontend/ui/widget/button.lua index ad29168bb..76f732a7f 100644 --- a/frontend/ui/widget/button.lua +++ b/frontend/ui/widget/button.lua @@ -25,6 +25,7 @@ local GestureRange = require("ui/gesturerange") local IconWidget = require("ui/widget/iconwidget") local InputContainer = require("ui/widget/container/inputcontainer") local Size = require("ui/size") +local TextBoxWidget = require("ui/widget/textboxwidget") local TextWidget = require("ui/widget/textwidget") local UIManager = require("ui/uimanager") local _ = require("gettext") @@ -52,6 +53,7 @@ local Button = InputContainer:new{ padding_v = nil, width = nil, max_width = nil, + avoid_text_truncation = true, text_font_face = "cfont", text_font_size = 20, text_font_bold = true, @@ -77,13 +79,51 @@ function Button:init() end if self.text then + local max_width = self.max_width and self.max_width - 2*self.padding_h - 2*self.margin - 2*self.bordersize or nil self.label_widget = TextWidget:new{ text = self.text, - max_width = self.max_width and self.max_width - 2*self.padding_h - 2*self.margin - 2*self.bordersize or nil, + max_width = max_width, fgcolor = self.enabled and Blitbuffer.COLOR_BLACK or Blitbuffer.COLOR_DARK_GRAY, bold = self.text_font_bold, face = Font:getFace(self.text_font_face, self.text_font_size) } + self.did_truncation_tweaks = false + if self.avoid_text_truncation and self.label_widget:isTruncated() then + self.did_truncation_tweaks = true + local max_height = self.label_widget:getSize().h + local font_size_2_lines = TextBoxWidget:getFontSizeToFitHeight(max_height, 2, 0) + while self.label_widget:isTruncated() do + local new_size = self.label_widget.face.orig_size - 1 + if new_size < font_size_2_lines then + -- Switch to a 2-lines TextBoxWidget + self.label_widget:free() + self.label_widget = TextBoxWidget:new{ + text = self.text, + line_height = 0, + alignment = "center", + width = max_width, + height = max_height, + height_adjust = true, + height_overflow_show_ellipsis = true, + fgcolor = self.enabled and Blitbuffer.COLOR_BLACK or Blitbuffer.COLOR_DARK_GRAY, + bold = self.text_font_bold, + face = Font:getFace(self.text_font_face, font_size_2_lines) + } + break + end + if new_size < 8 then -- don't go too small + break + end + self.label_widget:free() + self.label_widget = TextWidget:new{ + text = self.text, + max_width = max_width, + fgcolor = self.enabled and Blitbuffer.COLOR_BLACK or Blitbuffer.COLOR_DARK_GRAY, + bold = self.text_font_bold, + face = Font:getFace(self.text_font_face, new_size) + } + end + end else self.label_widget = IconWidget:new{ icon = self.icon, @@ -150,7 +190,7 @@ end function Button:setText(text, width) if text ~= self.text then -- Don't trash the frame if we're already a text button, and we're keeping the geometry intact - if self.text and width and width == self.width then + if self.text and width and width == self.width and not self.did_truncation_tweaks then self.text = text self.label_widget:setText(text) else diff --git a/frontend/ui/widget/textboxwidget.lua b/frontend/ui/widget/textboxwidget.lua index 1240f75f1..48e63b598 100644 --- a/frontend/ui/widget/textboxwidget.lua +++ b/frontend/ui/widget/textboxwidget.lua @@ -118,6 +118,20 @@ function TextBoxWidget:init() end self.line_height_px = Math.round( (1 + self.line_height) * self.face.size ) + -- Get accurate initial baseline and possible height overflow (so our bb + -- is tall enough to draw glyphs with descender larger than line height) + local face_height, face_ascender = self.face.ftface:getHeightAndAscender() + local line_heights_diff = math.floor(self.line_height_px - face_height) + if line_heights_diff >= 0 then + -- Glyphs will fit in our line_height_px: adjust baseline. + self.line_glyph_baseline = math.floor(face_ascender + line_heights_diff/2) + self.line_glyph_extra_height = 0 + else + -- Glyphs may be taller than our line_height_px + self.line_glyph_baseline = math.floor(face_ascender) + self.line_glyph_extra_height = -line_heights_diff + end + self.cursor_line = LineWidget:new{ dimen = Geom:new{ w = Size.line.medium, @@ -753,13 +767,13 @@ end ---- Lays out text. function TextBoxWidget:_renderText(start_row_idx, end_row_idx) - local font_height = self.face.size if start_row_idx < 1 then start_row_idx = 1 end if end_row_idx > #self.vertical_string_list then end_row_idx = #self.vertical_string_list end local row_count = end_row_idx == 0 and 1 or end_row_idx - start_row_idx + 1 -- We need a bb with the full height (even if we display only a few lines, we -- may have to draw an image bigger than these lines) local h = self.height or self.line_height_px * row_count + h = h + self.line_glyph_extra_height if self._bb then self._bb:free() end local bbtype = nil if self.line_num_to_image and self.line_num_to_image[start_row_idx] then @@ -767,8 +781,8 @@ function TextBoxWidget:_renderText(start_row_idx, end_row_idx) end self._bb = Blitbuffer.new(self.width, h, bbtype) self._bb:fill(Blitbuffer.COLOR_WHITE) - local y = font_height + local y = self.line_glyph_baseline if self.use_xtext then for i = start_row_idx, end_row_idx do local line = self.vertical_string_list[i] @@ -1056,9 +1070,12 @@ function TextBoxWidget:getFontSizeToFitHeight(height_px, nb_lines, line_height_e end -- We do the revert of what's done in :init(): -- self.line_height_px = Math.round( (1 + self.line_height) * self.face.size ) - local font_size = height_px / nb_lines / (1 + line_height_em) - font_size = font_size * 1000000 / Screen:scaleBySize(1000000) -- invert scaleBySize - return math.floor(font_size) + local face_size = height_px / nb_lines / (1 + line_height_em) + local font_size = math.floor(face_size * 1000000 / Screen:scaleBySize(1000000)) -- invert scaleBySize + if Screen:scaleBySize(font_size) > face_size then -- be really sure we won't get it larger + font_size = font_size - 1 + end + return font_size end function TextBoxWidget:getCharPos() @@ -1067,11 +1084,7 @@ function TextBoxWidget:getCharPos() end function TextBoxWidget:getSize() - if self.width and self.height then - return Geom:new{ w = self.width, h = self.height} - else - return Geom:new{ w = self.width, h = self._bb:getHeight()} - end + return Geom:new{ w = self.width, h = self._bb:getHeight()} end function TextBoxWidget:paintTo(bb, x, y)