From f08a268e8a8e4c7d6e3299be1cf70f2b8f951d86 Mon Sep 17 00:00:00 2001 From: Timur Kelman Date: Fri, 29 Apr 2022 15:55:08 +0200 Subject: [PATCH] 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` --- docs/Lua API.rst | 8 ++++++ library/lua/gui.lua | 2 +- library/lua/gui/widgets.lua | 53 +++++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) diff --git a/docs/Lua API.rst b/docs/Lua API.rst index acc9174b1..a61264c86 100644 --- a/docs/Lua API.rst +++ b/docs/Lua API.rst @@ -3782,6 +3782,14 @@ It has the following attributes: :auto_width: Sets self.frame.w from the text width. :on_click: A callback called when the label is 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 to the object via the ``text`` argument of the constructor, or via diff --git a/library/lua/gui.lua b/library/lua/gui.lua index a4541a6d8..8521e1dfe 100644 --- a/library/lua/gui.lua +++ b/library/lua/gui.lua @@ -96,7 +96,7 @@ function compute_frame_rect(wavail,havail,spec,xgap,ygap) return rect end -local function parse_inset(inset) +function parse_inset(inset) local l,r,t,b if type(inset) == 'table' then l,r = inset.l or inset.x or 0, inset.r or inset.x or 0 diff --git a/library/lua/gui/widgets.lua b/library/lua/gui/widgets.lua index 9306a75fa..8a091804c 100644 --- a/library/lua/gui/widgets.lua +++ b/library/lua/gui/widgets.lua @@ -412,7 +412,12 @@ Label.ATTRS{ auto_width = false, on_click = DEFAULT_NIL, on_rclick = DEFAULT_NIL, + -- 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) @@ -435,6 +440,39 @@ function Label:setText(text) 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() if self.auto_width then 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)) 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) local n = self.start_line_num + nlines n = math.min(n, self:getTextHeight() - self.frame_body.height + 1)