add scroll icons to Label widget (#2101)

* WIP: add scroll icons to Label widget

It's an opt-out. The icons are rendered in the right-most column of the 1st and last row. They are only rendered when text can actually be scrolled in the corresponding direction.

WIP: Currently, the icons might overlay text characters, there is no mechanism preventing it

* gui.lua: expose the `parse_inset()` function

* refactor Label's scroll icon code

* since `render_scroll_icons` only works with a label, it's now a class function
* `update_scroll_inset` ensures `frame_inset.r` or `.l` is at least 1, according to `show_scroll_icons`
* `show_scroll_icons` has 4 possible values: `false` for no icons, `left` for icons on the first column on the left (also ensuring `frame_inset.l >= 1`), `right` - last column on the right, `DEFAULT_NIL` - same as `right` if text height greater than `frame_body.height`, else same as `false`.

* make `render_scroll_icons` always draw icons

The check now happens in `onRenderFrame`

* draw frame's background

calling `Label.super.onRenderFrame(self, dc, rect)` makes frame's background invisible for some reason

* remove trailing spaces

* fix scroll icons placed far above/below text

 With `Label.frame_inset = 1` the text could be vertically centered with plenty of space below and above,
but not all rendered. Before this change, the scroll icons would be at the very top and bottom of the frame
instead of near the first and last rendered text line.

* always `update_scroll_inset` to react to resized window

* draw scroll icons next to text

* update `Lua API.rst` with new `Label` parameters

* move comment separator up

This way every scroll related parameter is in one group

* list default values for new parameters in docs

* add missing description of `Label.scroll_keys`
develop
Timur Kelman 2022-04-29 15:55:08 +02:00 committed by GitHub
parent 8eb2831b7e
commit f08a268e8a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 62 additions and 1 deletions

@ -3782,6 +3782,14 @@ It has the following attributes:
:auto_width: Sets self.frame.w from the text width. :auto_width: Sets self.frame.w from the text width.
:on_click: A callback called when the label is clicked (optional) :on_click: A callback called when the label is clicked (optional)
:on_rclick: A callback called when the label is right-clicked (optional) :on_rclick: A callback called when the label is right-clicked (optional)
:scroll_keys: Specifies which keys the label should react to as a table. Default is ``STANDARDSCROLL`` (up or down arrows, page up or down).
:show_scroll_icons: Controls scroll icons' behaviour: ``false`` for no icons, ``'right'`` or ``'left'`` for
icons next to the text in an additional column (``frame_inset`` is adjusted to have ``.r`` or ``.l`` greater than ``0``),
``nil`` same as ``'right'`` but changes ``frame_inset`` only if a scroll icon is actually necessary
(if ``getTextHeight()`` is greater than ``frame_body.height``). Default is ``nil``.
:up_arrow_icon: The symbol for scroll up arrow. Default is ``string.char(24)`` (``↑``).
:down_arrow_icon: The symbol for scroll down arrow. Default is ``string.char(25)`` (``↓``).
:scroll_icon_pen: Specifies the pen for scroll icons. Default is ``COLOR_LIGHTCYAN``.
The text itself is represented as a complex structure, and passed The text itself is represented as a complex structure, and passed
to the object via the ``text`` argument of the constructor, or via to the object via the ``text`` argument of the constructor, or via

@ -96,7 +96,7 @@ function compute_frame_rect(wavail,havail,spec,xgap,ygap)
return rect return rect
end end
local function parse_inset(inset) function parse_inset(inset)
local l,r,t,b local l,r,t,b
if type(inset) == 'table' then if type(inset) == 'table' then
l,r = inset.l or inset.x or 0, inset.r or inset.x or 0 l,r = inset.l or inset.x or 0, inset.r or inset.x or 0

@ -412,7 +412,12 @@ Label.ATTRS{
auto_width = false, auto_width = false,
on_click = DEFAULT_NIL, on_click = DEFAULT_NIL,
on_rclick = DEFAULT_NIL, on_rclick = DEFAULT_NIL,
--
scroll_keys = STANDARDSCROLL, scroll_keys = STANDARDSCROLL,
show_scroll_icons = DEFAULT_NIL, -- DEFAULT_NIL, 'right', 'left', false
up_arrow_icon = string.char(24),
down_arrow_icon = string.char(25),
scroll_icon_pen = COLOR_LIGHTCYAN,
} }
function Label:init(args) function Label:init(args)
@ -435,6 +440,39 @@ function Label:setText(text)
end end
end end
function Label:update_scroll_inset()
if self.show_scroll_icons == nil then
self._show_scroll_icons = self:getTextHeight() > self.frame_body.height and 'right' or false
else
self._show_scroll_icons = self.show_scroll_icons
end
if self._show_scroll_icons then
-- here self._show_scroll_icons can only be either
-- 'left' or any true value which we interpret as right
local l,t,r,b = gui.parse_inset(self.frame_inset)
if self._show_scroll_icons == 'left' and l <= 0 then
l = 1
elseif r <= 0 then
r = 1
end
self.frame_inset = {l=l,t=t,r=r,b=b}
end
end
function Label:render_scroll_icons(dc, x, y1, y2)
if self.start_line_num ~= 1 then
dc:seek(x, y1):char(self.up_arrow_icon, self.scroll_icon_pen)
end
local last_visible_line = self.start_line_num + self.frame_body.height - 1
if last_visible_line < self:getTextHeight() then
dc:seek(x, y2):char(self.down_arrow_icon, self.scroll_icon_pen)
end
end
function Label:postComputeFrame()
self:update_scroll_inset()
end
function Label:preUpdateLayout() function Label:preUpdateLayout()
if self.auto_width then if self.auto_width then
self.frame = self.frame or {} self.frame = self.frame or {}
@ -465,6 +503,21 @@ function Label:onRenderBody(dc)
render_text(self,dc,0,0,text_pen,self.text_dpen,is_disabled(self)) render_text(self,dc,0,0,text_pen,self.text_dpen,is_disabled(self))
end end
function Label:onRenderFrame(dc, rect)
if self._show_scroll_icons
and self:getTextHeight() > self.frame_body.height
then
local x = self._show_scroll_icons == 'left'
and self.frame_body.x1-dc.x1-1
or self.frame_body.x2-dc.x1+1
self:render_scroll_icons(dc,
x,
self.frame_body.y1-dc.y1,
self.frame_body.y2-dc.y1
)
end
end
function Label:scroll(nlines) function Label:scroll(nlines)
local n = self.start_line_num + nlines local n = self.start_line_num + nlines
n = math.min(n, self:getTextHeight() - self.frame_body.height + 1) n = math.min(n, self:getTextHeight() - self.frame_body.height + 1)