mirror of
https://github.com/koreader/koreader.git
synced 2025-12-18 12:02:09 +01:00
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.
This commit is contained in:
@@ -25,6 +25,7 @@ local GestureRange = require("ui/gesturerange")
|
|||||||
local IconWidget = require("ui/widget/iconwidget")
|
local IconWidget = require("ui/widget/iconwidget")
|
||||||
local InputContainer = require("ui/widget/container/inputcontainer")
|
local InputContainer = require("ui/widget/container/inputcontainer")
|
||||||
local Size = require("ui/size")
|
local Size = require("ui/size")
|
||||||
|
local TextBoxWidget = require("ui/widget/textboxwidget")
|
||||||
local TextWidget = require("ui/widget/textwidget")
|
local TextWidget = require("ui/widget/textwidget")
|
||||||
local UIManager = require("ui/uimanager")
|
local UIManager = require("ui/uimanager")
|
||||||
local _ = require("gettext")
|
local _ = require("gettext")
|
||||||
@@ -52,6 +53,7 @@ local Button = InputContainer:new{
|
|||||||
padding_v = nil,
|
padding_v = nil,
|
||||||
width = nil,
|
width = nil,
|
||||||
max_width = nil,
|
max_width = nil,
|
||||||
|
avoid_text_truncation = true,
|
||||||
text_font_face = "cfont",
|
text_font_face = "cfont",
|
||||||
text_font_size = 20,
|
text_font_size = 20,
|
||||||
text_font_bold = true,
|
text_font_bold = true,
|
||||||
@@ -77,13 +79,51 @@ function Button:init()
|
|||||||
end
|
end
|
||||||
|
|
||||||
if self.text then
|
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{
|
self.label_widget = TextWidget:new{
|
||||||
text = self.text,
|
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,
|
fgcolor = self.enabled and Blitbuffer.COLOR_BLACK or Blitbuffer.COLOR_DARK_GRAY,
|
||||||
bold = self.text_font_bold,
|
bold = self.text_font_bold,
|
||||||
face = Font:getFace(self.text_font_face, self.text_font_size)
|
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
|
else
|
||||||
self.label_widget = IconWidget:new{
|
self.label_widget = IconWidget:new{
|
||||||
icon = self.icon,
|
icon = self.icon,
|
||||||
@@ -150,7 +190,7 @@ end
|
|||||||
function Button:setText(text, width)
|
function Button:setText(text, width)
|
||||||
if text ~= self.text then
|
if text ~= self.text then
|
||||||
-- Don't trash the frame if we're already a text button, and we're keeping the geometry intact
|
-- 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.text = text
|
||||||
self.label_widget:setText(text)
|
self.label_widget:setText(text)
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -118,6 +118,20 @@ function TextBoxWidget:init()
|
|||||||
end
|
end
|
||||||
|
|
||||||
self.line_height_px = Math.round( (1 + self.line_height) * self.face.size )
|
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{
|
self.cursor_line = LineWidget:new{
|
||||||
dimen = Geom:new{
|
dimen = Geom:new{
|
||||||
w = Size.line.medium,
|
w = Size.line.medium,
|
||||||
@@ -753,13 +767,13 @@ end
|
|||||||
|
|
||||||
---- Lays out text.
|
---- Lays out text.
|
||||||
function TextBoxWidget:_renderText(start_row_idx, end_row_idx)
|
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 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
|
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
|
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
|
-- 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)
|
-- may have to draw an image bigger than these lines)
|
||||||
local h = self.height or self.line_height_px * row_count
|
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
|
if self._bb then self._bb:free() end
|
||||||
local bbtype = nil
|
local bbtype = nil
|
||||||
if self.line_num_to_image and self.line_num_to_image[start_row_idx] then
|
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
|
end
|
||||||
self._bb = Blitbuffer.new(self.width, h, bbtype)
|
self._bb = Blitbuffer.new(self.width, h, bbtype)
|
||||||
self._bb:fill(Blitbuffer.COLOR_WHITE)
|
self._bb:fill(Blitbuffer.COLOR_WHITE)
|
||||||
local y = font_height
|
|
||||||
|
|
||||||
|
local y = self.line_glyph_baseline
|
||||||
if self.use_xtext then
|
if self.use_xtext then
|
||||||
for i = start_row_idx, end_row_idx do
|
for i = start_row_idx, end_row_idx do
|
||||||
local line = self.vertical_string_list[i]
|
local line = self.vertical_string_list[i]
|
||||||
@@ -1056,9 +1070,12 @@ function TextBoxWidget:getFontSizeToFitHeight(height_px, nb_lines, line_height_e
|
|||||||
end
|
end
|
||||||
-- We do the revert of what's done in :init():
|
-- We do the revert of what's done in :init():
|
||||||
-- self.line_height_px = Math.round( (1 + self.line_height) * self.face.size )
|
-- self.line_height_px = Math.round( (1 + self.line_height) * self.face.size )
|
||||||
local font_size = height_px / nb_lines / (1 + line_height_em)
|
local face_size = height_px / nb_lines / (1 + line_height_em)
|
||||||
font_size = font_size * 1000000 / Screen:scaleBySize(1000000) -- invert scaleBySize
|
local font_size = math.floor(face_size * 1000000 / Screen:scaleBySize(1000000)) -- invert scaleBySize
|
||||||
return math.floor(font_size)
|
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
|
end
|
||||||
|
|
||||||
function TextBoxWidget:getCharPos()
|
function TextBoxWidget:getCharPos()
|
||||||
@@ -1067,11 +1084,7 @@ function TextBoxWidget:getCharPos()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function TextBoxWidget:getSize()
|
function TextBoxWidget:getSize()
|
||||||
if self.width and self.height then
|
return Geom:new{ w = self.width, h = self._bb:getHeight()}
|
||||||
return Geom:new{ w = self.width, h = self.height}
|
|
||||||
else
|
|
||||||
return Geom:new{ w = self.width, h = self._bb:getHeight()}
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function TextBoxWidget:paintTo(bb, x, y)
|
function TextBoxWidget:paintTo(bb, x, y)
|
||||||
|
|||||||
Reference in New Issue
Block a user