From 1f9076fbd75b4886dd03183d0f1fa004937efd19 Mon Sep 17 00:00:00 2001 From: ruki Date: Sat, 28 Nov 2020 00:42:19 +0800 Subject: [PATCH 01/12] add scrollbar --- src/ltui/action.lua | 1 + src/ltui/boxdialog.lua | 2 +- src/ltui/inputdialog.lua | 2 +- src/ltui/scrollbar.lua | 211 +++++++++++++++++++++++++++++++++++++++ src/ltui/textarea.lua | 10 +- src/ltui/textedit.lua | 4 +- 6 files changed, 222 insertions(+), 8 deletions(-) create mode 100644 src/ltui/scrollbar.lua diff --git a/src/ltui/action.lua b/src/ltui/action.lua index 82a90b6..818974a 100644 --- a/src/ltui/action.lua +++ b/src/ltui/action.lua @@ -42,6 +42,7 @@ action:register("ac_max", "ac_on_selected", "ac_on_clicked", "ac_on_resized", + "ac_on_scrolled", "ac_on_enter", "ac_on_load", "ac_on_save", diff --git a/src/ltui/boxdialog.lua b/src/ltui/boxdialog.lua index f27f0ac..7d17d28 100644 --- a/src/ltui/boxdialog.lua +++ b/src/ltui/boxdialog.lua @@ -40,7 +40,7 @@ function boxdialog:init(name, bounds, title) self:text():bounds().ey = self._TEXT_EY self:text():invalidate(true) self:text():option_set("selectable", false) - self:text():option_set("progress", false) + self:text():option_set("scrollable", false) -- insert box self:panel():insert(self:box()) diff --git a/src/ltui/inputdialog.lua b/src/ltui/inputdialog.lua index d772c00..f05744f 100644 --- a/src/ltui/inputdialog.lua +++ b/src/ltui/inputdialog.lua @@ -45,7 +45,7 @@ function inputdialog:init(name, bounds, title) self:text():bounds().ey = 1 self:text():invalidate(true) self:text():option_set("selectable", false) - self:text():option_set("progress", false) + self:text():option_set("scrollable", false) -- text changed self:text():action_set(action.ac_on_text_changed, function (v) diff --git a/src/ltui/scrollbar.lua b/src/ltui/scrollbar.lua new file mode 100644 index 0000000..cc64115 --- /dev/null +++ b/src/ltui/scrollbar.lua @@ -0,0 +1,211 @@ +---A cross-platform terminal ui library based on Lua +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- Copyright (C) 2015-2020, TBOOX Open Source Group. +-- +-- @author ruki +-- @file scrollbar.lua +-- + +-- load modules +local log = require("ltui/base/log") +local view = require("ltui/view") +local event = require("ltui/event") +local curses = require("ltui/curses") +local action = require("ltui/action") + +-- define module +local scrollbar = scrollbar or view() + +-- init scrollbar +function scrollbar:init(name, bounds, vertical) + + -- init view + view.init(self, name, bounds) + + -- init bar attribute + self:charattr_set("black") + + -- init bar vertical + self:vertical_set(vertical) + + -- init progress + self:progress_set(0) + + -- init character + self:char_set(' ') +end + +-- get bar attribute +function scrollbar:charattr() + return self:attr("charattr") +end + +-- set bar attribute, .e.g charattr_set("yellow onblue bold") +function scrollbar:charattr_set(attr) + return self:attr_set("charattr", attr) +end + +-- get the current char attribute value +function scrollbar:charattr_val() + + -- get text attribute + local charattr = self:charattr() + if not charattr then + return + end + + -- no text background? use view's background + if self:background() and not charattr:find("on") then + charattr = charattr .. " on" .. self:background() + end + + -- attempt to get the attribute value from the cache first + self._charattr = self._charattr or {} + local value = self._charattr[charattr] + if value then + return value + end + + -- update the cache + value = curses.calc_attr(charattr:split("%s+")) + self._charattr[charattr] = value + return value +end + +-- get bar character +function scrollbar:char() + return self:attr("char") or ' ' +end + +-- set bar character +function scrollbar:char_set(char) + if char ~= self:char() then + self:invalidate() + end + return self:attr_set("char", char) +end + +-- is vertical bar? +function scrollbar:vertical() + return self:attr("vertical") or true +end + +-- set bar vertical +function scrollbar:vertical_set(vertical) + return self:attr_set("vertical", vertical) +end + +-- get bar progress +function scrollbar:progress() + return self:attr("progress") or 0 +end + +-- set bar progress, [0, 1] +function scrollbar:progress_set(progress) + if progress > 1 then + progress = 1 + elseif progress < 0 then + progress = 0 + end + if progress ~= self:progress() then + self:invalidate() + end + return self:attr_set("progress", progress) +end + +-- get bar step width +function scrollbar:stepwidth() + return self:attr("stepwidth") or 0.1 +end + +-- set bar step width, [0, 1] +function scrollbar:stepwidth_set(stepwidth) + if stepwidth > 1 then + stepwidth = 1 + elseif stepwidth < 0 then + stepwidth = 0 + end + if stepwidth ~= self:stepwidth() then + self:invalidate() + end + return self:attr_set("stepwidth", stepwidth) +end + +-- draw scrollbar +function scrollbar:on_draw(transparent) + + -- draw background + view.on_draw(self, transparent) + + -- compute bar range + local char = self:char() + local charattr = self:charattr_val() + local progress = self:progress() + local stepwidth = self:stepwidth() + local pb = progress + local pe = progress + stepwidth + if pe > 1 then + pb = 1 - stepwidth + pe = 1 + end + + -- draw bar + if self:vertical() then + local sb = math.floor(self:height() * pb) + local se = math.ceil(self:height() * pe) + if se > sb and se - sb < self:height() then + self:canvas():attr(charattr):move(0, sb):putchar(char, se - sb, true) + end + else + local sb = math.floor(self:width() * pb) + local se = math.ceil(self:width() * pe) + if se > sb and se - sb < self:width() then + self:canvas():attr(charattr):move(sb, 0):putchar(char, se - sb) + end + end +end + +-- scroll bar, e.g. -1 * 0.1, 1 * 0.1 +function scrollbar:scroll(steps) + steps = steps or 1 + self:progress_set(self:progress() + steps * self:stepwidth()) + self:action_on(action.ac_on_scrolled, self:progress()) +end + +-- on event +function scrollbar:on_event(e) + if e.type == event.ev_keyboard then + if self:vertical() then + if e.key_name == "Up" then + self:scroll(-1) + return true + elseif e.key_name == "Down" then + self:scroll(1) + return true + end + else + if e.key_name == "Left" then + self:scroll(-1) + return true + elseif e.key_name == "Right" then + self:scroll(1) + return true + end + end + end +end + +-- return module +return scrollbar diff --git a/src/ltui/textarea.lua b/src/ltui/textarea.lua index b87f415..bdd5d57 100644 --- a/src/ltui/textarea.lua +++ b/src/ltui/textarea.lua @@ -24,6 +24,8 @@ local view = require("ltui/view") local label = require("ltui/label") local event = require("ltui/event") local curses = require("ltui/curses") +local action = require("ltui/action") +local scrollbar = require("ltui/scrollbar") -- define module local textarea = textarea or label() @@ -37,8 +39,8 @@ function textarea:init(name, bounds, text) -- mark as selectable self:option_set("selectable", true) - -- enable progress - self:option_set("progress", true) + -- mark as scrollable + self:option_set("scrollable", true) -- init start line self._STARTLINE = 0 @@ -60,8 +62,8 @@ function textarea:on_draw(transparent) self:canvas():attr(textattr):move(0, 0):putstrs(strs, self._STARTLINE + 1) end - -- draw progress - if self:option("progress") then + -- draw scrollable + if self:option("scrollable") then local tb = self._STARTLINE local fator = self:height() / self._LINECOUNT local sb = math.min(math.floor(tb * fator), self:height() - 1) diff --git a/src/ltui/textedit.lua b/src/ltui/textedit.lua index 285d065..978eccf 100644 --- a/src/ltui/textedit.lua +++ b/src/ltui/textedit.lua @@ -47,8 +47,8 @@ function textedit:init(name, bounds, text) self:option_set("mouseable", true) self:action_set(action.ac_on_clicked, function () return true end) - -- disable progress - self:option_set("progress", false) + -- disable scrollbar + self:option_set("scrollable", false) -- enable multiple line self:option_set("multiline", true) From 0903f700ca60d5a28643984293673b4dc32bf108 Mon Sep 17 00:00:00 2001 From: ruki Date: Sat, 28 Nov 2020 20:41:03 +0800 Subject: [PATCH 02/12] improve textdialog --- src/ltui/boxdialog.lua | 1 - src/ltui/inputdialog.lua | 1 - src/ltui/mconfdialog.lua | 2 ++ src/ltui/scrollbar.lua | 14 +++++++----- src/ltui/textarea.lua | 15 ------------- src/ltui/textdialog.lua | 48 ++++++++++++++++++++++++++++++++++------ src/ltui/textedit.lua | 3 --- tests/dialog.lua | 1 + 8 files changed, 53 insertions(+), 32 deletions(-) diff --git a/src/ltui/boxdialog.lua b/src/ltui/boxdialog.lua index 7d17d28..125b65c 100644 --- a/src/ltui/boxdialog.lua +++ b/src/ltui/boxdialog.lua @@ -40,7 +40,6 @@ function boxdialog:init(name, bounds, title) self:text():bounds().ey = self._TEXT_EY self:text():invalidate(true) self:text():option_set("selectable", false) - self:text():option_set("scrollable", false) -- insert box self:panel():insert(self:box()) diff --git a/src/ltui/inputdialog.lua b/src/ltui/inputdialog.lua index f05744f..1571eca 100644 --- a/src/ltui/inputdialog.lua +++ b/src/ltui/inputdialog.lua @@ -45,7 +45,6 @@ function inputdialog:init(name, bounds, title) self:text():bounds().ey = 1 self:text():invalidate(true) self:text():option_set("selectable", false) - self:text():option_set("scrollable", false) -- text changed self:text():action_set(action.ac_on_text_changed, function (v) diff --git a/src/ltui/mconfdialog.lua b/src/ltui/mconfdialog.lua index 1857131..d4a2e08 100644 --- a/src/ltui/mconfdialog.lua +++ b/src/ltui/mconfdialog.lua @@ -122,6 +122,7 @@ function mconfdialog:helpdialog() if not self._HELPDIALOG then local helpdialog = textdialog:new("mconfdialog.help", self:bounds(), "help") helpdialog:button_add("exit", "< Exit >", function (v) helpdialog:quit() end) + helpdialog:option_set("scrollable", true) self._HELPDIALOG = helpdialog end return self._HELPDIALOG @@ -132,6 +133,7 @@ function mconfdialog:resultdialog() if not self._RESULTDIALOG then local resultdialog = textdialog:new("mconfdialog.result", self:bounds(), "result") resultdialog:button_add("exit", "< Exit >", function (v) resultdialog:quit() end) + resultdialog:option_set("scrollable", true) self._RESULTDIALOG = resultdialog end return self._RESULTDIALOG diff --git a/src/ltui/scrollbar.lua b/src/ltui/scrollbar.lua index cc64115..8f22290 100644 --- a/src/ltui/scrollbar.lua +++ b/src/ltui/scrollbar.lua @@ -35,7 +35,7 @@ function scrollbar:init(name, bounds, vertical) view.init(self, name, bounds) -- init bar attribute - self:charattr_set("black") + self:charattr_set("black on black") -- init bar vertical self:vertical_set(vertical) @@ -165,14 +165,18 @@ function scrollbar:on_draw(transparent) if self:vertical() then local sb = math.floor(self:height() * pb) local se = math.ceil(self:height() * pe) - if se > sb and se - sb < self:height() then - self:canvas():attr(charattr):move(0, sb):putchar(char, se - sb, true) + if se > sb and se - sb <= self:height() then + for x = 0, self:width() - 1 do + self:canvas():attr(charattr):move(x, sb):putchar(char, se - sb, true) + end end else local sb = math.floor(self:width() * pb) local se = math.ceil(self:width() * pe) - if se > sb and se - sb < self:width() then - self:canvas():attr(charattr):move(sb, 0):putchar(char, se - sb) + if se > sb and se - sb <= self:width() then + for y = 0, self:height() - 1 do + self:canvas():attr(charattr):move(sb, y):putchar(char, se - sb) + end end end end diff --git a/src/ltui/textarea.lua b/src/ltui/textarea.lua index bdd5d57..296e3e3 100644 --- a/src/ltui/textarea.lua +++ b/src/ltui/textarea.lua @@ -25,7 +25,6 @@ local label = require("ltui/label") local event = require("ltui/event") local curses = require("ltui/curses") local action = require("ltui/action") -local scrollbar = require("ltui/scrollbar") -- define module local textarea = textarea or label() @@ -39,9 +38,6 @@ function textarea:init(name, bounds, text) -- mark as selectable self:option_set("selectable", true) - -- mark as scrollable - self:option_set("scrollable", true) - -- init start line self._STARTLINE = 0 self._LINECOUNT = 0 @@ -61,17 +57,6 @@ function textarea:on_draw(transparent) if strs and #strs > 0 and textattr then self:canvas():attr(textattr):move(0, 0):putstrs(strs, self._STARTLINE + 1) end - - -- draw scrollable - if self:option("scrollable") then - local tb = self._STARTLINE - local fator = self:height() / self._LINECOUNT - local sb = math.min(math.floor(tb * fator), self:height() - 1) - local se = math.min(sb + math.ceil(self:height() * fator), self:height()) - if se > sb and se - sb < self:height() then - self:canvas():attr("black"):move(self:width() - 1, sb):putchar(' ', se - sb, true) - end - end end -- set text diff --git a/src/ltui/textdialog.lua b/src/ltui/textdialog.lua index d8fca4e..892c9ad 100644 --- a/src/ltui/textdialog.lua +++ b/src/ltui/textdialog.lua @@ -19,13 +19,14 @@ -- -- load modules -local log = require("ltui/base/log") -local rect = require("ltui/rect") -local event = require("ltui/event") -local dialog = require("ltui/dialog") -local curses = require("ltui/curses") -local textarea = require("ltui/textarea") -local action = require("ltui/action") +local log = require("ltui/base/log") +local rect = require("ltui/rect") +local event = require("ltui/event") +local dialog = require("ltui/dialog") +local curses = require("ltui/curses") +local textarea = require("ltui/textarea") +local scrollbar = require("ltui/scrollbar") +local action = require("ltui/action") -- define module local textdialog = textdialog or dialog() @@ -36,9 +37,15 @@ function textdialog:init(name, bounds, title) -- init window dialog.init(self, name, bounds, title) + -- mark as scrollable, disabled by default + self:option_set("scrollable", false) + -- insert text self:panel():insert(self:text()) + -- insert scrollbar + self:panel():insert(self:scrollbar()) + -- select buttons by default self:panel():select(self:buttons()) @@ -48,6 +55,24 @@ function textdialog:init(name, bounds, title) end) end +-- enable or disable scrollbar +function textdialog:option_set(name, value) + if name == "scrollable" then + local oldvalue = self:option(name) + if value ~= oldvalue then + if value then + self:text():bounds():resize(self:panel():width() - 1, self:panel():height() - 1) + self:scrollbar():show(true) + else + self:text():bounds():resize(self:panel():width(), self:panel():height() - 1) + self:scrollbar():show(false) + end + self:invalidate() + end + end + dialog.option_set(self, name, value) +end + -- get text function textdialog:text() if not self._TEXT then @@ -56,6 +81,15 @@ function textdialog:text() return self._TEXT end +-- get scrollbar +function textdialog:scrollbar() + if not self._SCROLLBAR then + self._SCROLLBAR = scrollbar:new("textdialog.scrollbar", rect:new(self:panel():width() - 1, 0, 1, self:panel():height() - 1)) + self._SCROLLBAR:show(false) + end + return self._SCROLLBAR +end + -- on event function textdialog:on_event(e) diff --git a/src/ltui/textedit.lua b/src/ltui/textedit.lua index 978eccf..0acef24 100644 --- a/src/ltui/textedit.lua +++ b/src/ltui/textedit.lua @@ -47,9 +47,6 @@ function textedit:init(name, bounds, text) self:option_set("mouseable", true) self:action_set(action.ac_on_clicked, function () return true end) - -- disable scrollbar - self:option_set("scrollable", false) - -- enable multiple line self:option_set("multiline", true) end diff --git a/tests/dialog.lua b/tests/dialog.lua index 823ca00..1e8371d 100644 --- a/tests/dialog.lua +++ b/tests/dialog.lua @@ -86,6 +86,7 @@ function demo:dialog_help() dialog_help:text():text_set(helptext) end dialog_help:button_add("exit", "< Exit >", function (v) self:remove(dialog_help) end) + dialog_help:option_set("scrollable", true) self._DIALOG_HELP = dialog_help end return dialog_help From 42b9a690af5aa938e6aed211e87a59174ae14086 Mon Sep 17 00:00:00 2001 From: ruki Date: Sat, 28 Nov 2020 20:52:40 +0800 Subject: [PATCH 03/12] resize textdialog for scrollbar --- src/ltui/textdialog.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/ltui/textdialog.lua b/src/ltui/textdialog.lua index 892c9ad..56c0920 100644 --- a/src/ltui/textdialog.lua +++ b/src/ltui/textdialog.lua @@ -51,7 +51,12 @@ function textdialog:init(name, bounds, title) -- on resize for panel self:panel():action_add(action.ac_on_resized, function (v) - self:text():bounds_set(rect:new(0, 0, v:width(), v:height() - 1)) + if self:option("scrollable") then + self:text():bounds_set(rect:new(0, 0, v:width() - 1, v:height() - 1)) + self:scrollbar():bounds_set(rect:new(v:width() - 1, 0, 1, v:height() - 1)) + else + self:text():bounds_set(rect:new(0, 0, v:width(), v:height() - 1)) + end end) end From 49526b95cbe1ff676fc17a70cf5c4d30d6280b33 Mon Sep 17 00:00:00 2001 From: ruki Date: Sat, 28 Nov 2020 21:32:23 +0800 Subject: [PATCH 04/12] improve scrollbar --- src/ltui/scrollbar.lua | 30 +++++++++++++++--------------- src/ltui/textarea.lua | 10 +++++++--- src/ltui/textdialog.lua | 5 +++++ 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/ltui/scrollbar.lua b/src/ltui/scrollbar.lua index 8f22290..94fd51e 100644 --- a/src/ltui/scrollbar.lua +++ b/src/ltui/scrollbar.lua @@ -149,30 +149,30 @@ function scrollbar:on_draw(transparent) -- draw background view.on_draw(self, transparent) - -- compute bar range + -- draw bar local char = self:char() local charattr = self:charattr_val() - local progress = self:progress() - local stepwidth = self:stepwidth() - local pb = progress - local pe = progress + stepwidth - if pe > 1 then - pb = 1 - stepwidth - pe = 1 - end - - -- draw bar if self:vertical() then - local sb = math.floor(self:height() * pb) - local se = math.ceil(self:height() * pe) + local sn = math.ceil(self:height() * self:stepwidth()) + local sb = math.floor(self:height() * self:progress()) + local se = sb + sn + if se > self:height() then + sb = self:height() - sn + se = self:height() + end if se > sb and se - sb <= self:height() then for x = 0, self:width() - 1 do self:canvas():attr(charattr):move(x, sb):putchar(char, se - sb, true) end end else - local sb = math.floor(self:width() * pb) - local se = math.ceil(self:width() * pe) + local sn = math.ceil(self:width() * self:stepwidth()) + local sb = math.floor(self:width() * self:progress()) + local se = sb + sn + if se > self:width() then + sb = self:width() - sn + se = self:width() + end if se > sb and se - sb <= self:width() then for y = 0, self:height() - 1 do self:canvas():attr(charattr):move(sb, y):putchar(char, se - sb) diff --git a/src/ltui/textarea.lua b/src/ltui/textarea.lua index 296e3e3..54de807 100644 --- a/src/ltui/textarea.lua +++ b/src/ltui/textarea.lua @@ -74,9 +74,11 @@ function textarea:scroll(lines) if self._STARTLINE < 0 then self._STARTLINE = 0 end - if self._STARTLINE > self._LINECOUNT - self:height() then - self._STARTLINE = self._LINECOUNT - self:height() + local startline_end = self._LINECOUNT - self:height() + if self._STARTLINE > startline_end then + self._STARTLINE = startline_end end + self:action_on(action.ac_on_scrolled, self._STARTLINE / startline_end) self:invalidate() end end @@ -84,7 +86,9 @@ end -- scroll to end function textarea:scroll_to_end() if self._LINECOUNT > self:height() then - self._STARTLINE = self._LINECOUNT - self:height() + local startline_end = self._LINECOUNT - self:height() + self._STARTLINE = startline_end + self:action_on(action.ac_on_scrolled, self._STARTLINE / startline_end) self:invalidate() end end diff --git a/src/ltui/textdialog.lua b/src/ltui/textdialog.lua index 56c0920..d88599f 100644 --- a/src/ltui/textdialog.lua +++ b/src/ltui/textdialog.lua @@ -58,6 +58,11 @@ function textdialog:init(name, bounds, title) self:text():bounds_set(rect:new(0, 0, v:width(), v:height() - 1)) end end) + + -- on scroll for text and scrollbar + self:text():action_set(action.ac_on_scrolled, function (v, progress) + self:scrollbar():progress_set(progress) + end) end -- enable or disable scrollbar From 50434c9365acf65487e1e80685f29b8800813277 Mon Sep 17 00:00:00 2001 From: ruki Date: Sat, 28 Nov 2020 21:40:41 +0800 Subject: [PATCH 05/12] fix codestyle --- src/ltui/panel.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ltui/panel.lua b/src/ltui/panel.lua index b8479df..87c22dd 100644 --- a/src/ltui/panel.lua +++ b/src/ltui/panel.lua @@ -64,7 +64,7 @@ function panel:init(name, bounds) local p = v:last() while p do - if p:option('selectable') and p:bounds():contains(x, y) then + if p:option("selectable") and p:bounds():contains(x, y) then if p:option("mouseable") then v:select(p) return p:action_on(action.ac_on_clicked, x, y) From cd2a4c4bd63cc67a41c9e62f789e2e0329b67203 Mon Sep 17 00:00:00 2001 From: ruki Date: Sat, 28 Nov 2020 22:02:56 +0800 Subject: [PATCH 06/12] improve textdialog --- src/ltui/textarea.lua | 9 +++++++-- src/ltui/textdialog.lua | 18 ++++++++++++++---- tests/dialog.lua | 2 +- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/ltui/textarea.lua b/src/ltui/textarea.lua index 54de807..98e1ddd 100644 --- a/src/ltui/textarea.lua +++ b/src/ltui/textarea.lua @@ -67,9 +67,14 @@ function textarea:text_set(text) return label.text_set(self, text) end +-- is scrollable? +function textarea:scrollable() + return self._LINECOUNT > self:height() +end + -- scroll function textarea:scroll(lines) - if self._LINECOUNT > self:height() then + if self:scrollable() then self._STARTLINE = self._STARTLINE + lines if self._STARTLINE < 0 then self._STARTLINE = 0 @@ -85,7 +90,7 @@ end -- scroll to end function textarea:scroll_to_end() - if self._LINECOUNT > self:height() then + if self:scrollable() then local startline_end = self._LINECOUNT - self:height() self._STARTLINE = startline_end self:action_on(action.ac_on_scrolled, self._STARTLINE / startline_end) diff --git a/src/ltui/textdialog.lua b/src/ltui/textdialog.lua index d88599f..cfec53c 100644 --- a/src/ltui/textdialog.lua +++ b/src/ltui/textdialog.lua @@ -59,9 +59,22 @@ function textdialog:init(name, bounds, title) end end) + -- show scrollbar? + self:text():action_set(action.ac_on_text_changed, function (v) + if self:option("scrollable") then + if v:scrollable() then + self:scrollbar():show(true) + else + self:scrollbar():show(false) + end + end + end) + -- on scroll for text and scrollbar self:text():action_set(action.ac_on_scrolled, function (v, progress) - self:scrollbar():progress_set(progress) + if self:option("scrollable") then + self:scrollbar():progress_set(progress) + end end) end @@ -72,12 +85,9 @@ function textdialog:option_set(name, value) if value ~= oldvalue then if value then self:text():bounds():resize(self:panel():width() - 1, self:panel():height() - 1) - self:scrollbar():show(true) else self:text():bounds():resize(self:panel():width(), self:panel():height() - 1) - self:scrollbar():show(false) end - self:invalidate() end end dialog.option_set(self, name, value) diff --git a/tests/dialog.lua b/tests/dialog.lua index 1e8371d..68dec0f 100644 --- a/tests/dialog.lua +++ b/tests/dialog.lua @@ -76,6 +76,7 @@ function demo:dialog_help() local dialog_help = self._DIALOG_HELP if not dialog_help then dialog_help = textdialog:new("dialog.help", rect {1, 1, self:width() - 1, self:height() - 1}, "README") + dialog_help:option_set("scrollable", true) local helptext = nil local file = io.open("./LICENSE.md", 'r') if file then @@ -86,7 +87,6 @@ function demo:dialog_help() dialog_help:text():text_set(helptext) end dialog_help:button_add("exit", "< Exit >", function (v) self:remove(dialog_help) end) - dialog_help:option_set("scrollable", true) self._DIALOG_HELP = dialog_help end return dialog_help From 3325a7394a8554ce969b5128f7f1e3ed36b633b2 Mon Sep 17 00:00:00 2001 From: ruki Date: Sat, 28 Nov 2020 22:35:39 +0800 Subject: [PATCH 07/12] improve mconfdialog tests --- tests/mconfdialog.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/mconfdialog.lua b/tests/mconfdialog.lua index 50290d0..a38379e 100644 --- a/tests/mconfdialog.lua +++ b/tests/mconfdialog.lua @@ -69,7 +69,7 @@ function demo:init() table.insert(configs, menuconf.number {value = 6, default = 10, description = "number config item"}) table.insert(configs, menuconf.string {value = "x86_64", description = "string config item"}) table.insert(configs, menuconf.menu {description = "menu config item", configs = configs_sub}) - table.insert(configs, menuconf.choice {value = 3, values = {1, 5, 6, 7}, default = 2, description = "choice config item"}) + table.insert(configs, menuconf.choice {value = 3, values = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, default = 2, description = "choice config item"}) -- init menu config dialog self:dialog_mconf():load(configs) From 66c7312db421646109118e8ca5d4746eaf5d3ca1 Mon Sep 17 00:00:00 2001 From: ruki Date: Sun, 29 Nov 2020 19:10:28 +0800 Subject: [PATCH 08/12] support scrollbar for choicedialog --- src/ltui/choicebox.lua | 150 +++++++++++++++++++++++++++----------- src/ltui/choicedialog.lua | 37 +++++++++- src/ltui/textarea.lua | 4 +- src/ltui/textdialog.lua | 2 +- 4 files changed, 148 insertions(+), 45 deletions(-) diff --git a/src/ltui/choicebox.lua b/src/ltui/choicebox.lua index d0598a6..aa385f4 100644 --- a/src/ltui/choicebox.lua +++ b/src/ltui/choicebox.lua @@ -38,8 +38,81 @@ function choicebox:init(name, bounds) -- init panel panel.init(self, name, bounds) - -- init values - self._VALUES = {} + -- init items + self._ITEMS = {} + + -- init start index + self._STARTINDEX = 1 +end + +-- load values +function choicebox:load(values, selected) + + -- clear the views first + self:clear() + + -- load items + local items = {} + for idx, value in ipairs(values) do + table.insert(items, self:_load_item(value, idx, idx == selected)) + end + self._ITEMS = items + + -- insert top-n items + local startindex = self._STARTINDEX + for idx = startindex, startindex + self:height() do + local item = items[idx] + if item then + self:insert(item) + else + break + end + end + + -- select the first item + self:select(self:first()) + + -- invalidate + self:invalidate() + + -- on loaded + self:action_on(action.ac_on_load) +end + +-- is scrollable? +function choicebox:scrollable() + return #self:_items() > self:height() +end + +-- scroll +function choicebox:scroll(count) + if self:scrollable() then + local items = self:_items() + local totalcount = #items + local startindex = self._STARTINDEX + count + if startindex > totalcount then + return + elseif startindex < 1 then + startindex = 1 + end + self._STARTINDEX = startindex + self:clear() + for idx = startindex, startindex + self:height() do + local item = items[idx] + if item then + item:bounds():move2(0, idx - startindex) + self:insert(item) + else + break + end + end + if count > 0 then + self:select(self:first()) + else + self:select(self:last()) + end + self:invalidate() + end end -- on event @@ -48,9 +121,19 @@ function choicebox:on_event(e) -- select config if e.type == event.ev_keyboard then if e.key_name == "Down" then - return self:select_next() + if self:current() == self:last() then + self:scroll(self:height()) + end + self:select_next() + self:_notify_scrolled() + return true elseif e.key_name == "Up" then - return self:select_prev() + if self:current() == self:first() then + self:scroll(-self:height()) + end + self:select_prev() + self:_notify_scrolled() + return true elseif e.key_name == "Enter" or e.key_name == " " then self:_do_select() return true @@ -61,44 +144,36 @@ function choicebox:on_event(e) end end --- load values -function choicebox:load(values, selected) - - -- clear the views first - self:clear() - - -- insert values - self._VALUES = values - for idx, value in ipairs(values) do - self:_do_insert(value, idx, idx == selected) - end - - -- select the first item - self:select(self:first()) - - -- invalidate - self:invalidate() -end - --- do insert a value item -function choicebox:_do_insert(value, index, selected) +-- load a item with value +function choicebox:_load_item(value, index, selected) -- init text local text = (selected and "(X) " or "( ) ") .. tostring(value) -- init a value item view - local item = button:new("choicebox.value." .. self:count(), - rect:new(0, self:count(), self:width(), 1), + local item = button:new("choicebox.value." .. index, + rect:new(0, index - 1, self:width(), 1), text, function (v, e) self:_do_select() end) - -- attach this index + -- attach index and value item:extra_set("index", index) + item:extra_set("value", value) + return item +end - -- insert this config item - self:insert(item) +-- notify scrolled +function choicebox:_notify_scrolled() + local totalcount = #self:_items() + local startindex = self:current():extra("index") + self:action_on(action.ac_on_scrolled, startindex / totalcount) +end + +-- get all items +function choicebox:_items() + return self._ITEMS end -- do select the current config @@ -108,24 +183,17 @@ function choicebox:_do_select() for v in self:views() do local text = v:text() if text and text:startswith("(X) ") then - local i = v:extra("index") - if i then - local t = self._VALUES[i] - v:text_set("( ) " .. tostring(t)) - end + local t = v:extra("value") + v:text_set("( ) " .. tostring(t)) end end -- get the current item local item = self:current() - -- get the current index - local index = item:extra("index") - - -- get the current value - local value = self._VALUES[index] - -- do action: on selected + local index = item:extra("index") + local value = item:extra("value") self:action_on(action.ac_on_selected, index, value) -- update text diff --git a/src/ltui/choicedialog.lua b/src/ltui/choicedialog.lua index 0061f74..7a16412 100644 --- a/src/ltui/choicedialog.lua +++ b/src/ltui/choicedialog.lua @@ -25,6 +25,7 @@ local event = require("ltui/event") local action = require("ltui/action") local curses = require("ltui/curses") local window = require("ltui/window") +local scrollbar = require("ltui/scrollbar") local choicebox = require("ltui/choicebox") local boxdialog = require("ltui/boxdialog") @@ -50,23 +51,57 @@ function choicedialog:init(name, bounds, title) end) self:buttons():select(self:button("select")) + -- insert scrollbar + self:box():panel():insert(self:scrollbar_box()) + -- insert choice box self:box():panel():insert(self:choicebox()) -- disable to select to box (disable Tab switch and only response to buttons) self:box():option_set("selectable", false) + + -- on resize for panel + self:box():panel():action_add(action.ac_on_resized, function (v) + self:choicebox():bounds_set(rect:new(0, 0, v:width() - 1, v:height())) + self:scrollbar_box():bounds_set(rect:new(v:width() - 1, 0, 1, v:height())) + end) + + -- show scrollbar? + self:choicebox():action_set(action.ac_on_load, function (v) + if v:scrollable() then + self:scrollbar_box():show(true) + else + self:scrollbar_box():show(false) + end + end) + + -- on scroll + self:choicebox():action_set(action.ac_on_scrolled, function (v, progress) + if self:scrollbar_box():state("visible") then + self:scrollbar_box():progress_set(progress) + end + end) end -- get choice box function choicedialog:choicebox() if not self._CHOICEBOX then local bounds = self:box():panel():bounds() - self._CHOICEBOX = choicebox:new("choicedialog.choicebox", rect:new(0, 0, bounds:width(), bounds:height())) + self._CHOICEBOX = choicebox:new("choicedialog.choicebox", rect:new(0, 0, bounds:width() - 1, bounds:height())) self._CHOICEBOX:state_set("focused", true) -- we can select and highlight selected item end return self._CHOICEBOX end +-- get box scrollbar +function choicedialog:scrollbar_box() + if not self._SCROLLBAR_BOX then + self._SCROLLBAR_BOX = scrollbar:new("choicedialog.scrollbar", rect:new(self:box():panel():width() - 1, 0, 1, self:box():panel():height())) + self._SCROLLBAR_BOX:show(false) + end + return self._SCROLLBAR_BOX +end + -- on event function choicedialog:on_event(e) diff --git a/src/ltui/textarea.lua b/src/ltui/textarea.lua index 98e1ddd..2cace15 100644 --- a/src/ltui/textarea.lua +++ b/src/ltui/textarea.lua @@ -79,7 +79,7 @@ function textarea:scroll(lines) if self._STARTLINE < 0 then self._STARTLINE = 0 end - local startline_end = self._LINECOUNT - self:height() + local startline_end = self._LINECOUNT > self:height() and self._LINECOUNT - self:height() or self._LINECOUNT if self._STARTLINE > startline_end then self._STARTLINE = startline_end end @@ -91,7 +91,7 @@ end -- scroll to end function textarea:scroll_to_end() if self:scrollable() then - local startline_end = self._LINECOUNT - self:height() + local startline_end = self._LINECOUNT > self:height() and self._LINECOUNT - self:height() or self._LINECOUNT self._STARTLINE = startline_end self:action_on(action.ac_on_scrolled, self._STARTLINE / startline_end) self:invalidate() diff --git a/src/ltui/textdialog.lua b/src/ltui/textdialog.lua index cfec53c..c375fc3 100644 --- a/src/ltui/textdialog.lua +++ b/src/ltui/textdialog.lua @@ -72,7 +72,7 @@ function textdialog:init(name, bounds, title) -- on scroll for text and scrollbar self:text():action_set(action.ac_on_scrolled, function (v, progress) - if self:option("scrollable") then + if self:scrollbar():state("visible") then self:scrollbar():progress_set(progress) end end) From f2ba2206eb35ef9e0ec4fb94c0b63bdfa2e9aae7 Mon Sep 17 00:00:00 2001 From: ruki Date: Sun, 29 Nov 2020 19:20:20 +0800 Subject: [PATCH 09/12] improve mconfigdialog test --- tests/mconfdialog.lua | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/mconfdialog.lua b/tests/mconfdialog.lua index a38379e..844aa14 100644 --- a/tests/mconfdialog.lua +++ b/tests/mconfdialog.lua @@ -60,6 +60,27 @@ function demo:init() table.insert(configs_sub, menuconf.menu {description = "menu config sub-item", configs = configs_sub2}) table.insert(configs_sub, menuconf.choice {value = 2, values = {2, 5, 16, 87}, description = "choice config sub-item"}) + table.insert(configs_sub, menuconf.number {value = 6, default = 10, description = "number config item1"}) + table.insert(configs_sub, menuconf.number {value = 6, default = 10, description = "number config item2"}) + table.insert(configs_sub, menuconf.number {value = 6, default = 10, description = "number config item3"}) + table.insert(configs_sub, menuconf.number {value = 6, default = 10, description = "number config item4"}) + table.insert(configs_sub, menuconf.number {value = 6, default = 10, description = "number config item5"}) + table.insert(configs_sub, menuconf.number {value = 6, default = 10, description = "number config item6"}) + table.insert(configs_sub, menuconf.number {value = 6, default = 10, description = "number config item7"}) + table.insert(configs_sub, menuconf.number {value = 6, default = 10, description = "number config item8"}) + table.insert(configs_sub, menuconf.number {value = 6, default = 10, description = "number config item9"}) + + table.insert(configs_sub, menuconf.number {value = 6, default = 10, description = "number config item10"}) + table.insert(configs_sub, menuconf.number {value = 6, default = 10, description = "number config item11"}) + table.insert(configs_sub, menuconf.number {value = 6, default = 10, description = "number config item12"}) + table.insert(configs_sub, menuconf.number {value = 6, default = 10, description = "number config item13"}) + table.insert(configs_sub, menuconf.number {value = 6, default = 10, description = "number config item14"}) + table.insert(configs_sub, menuconf.number {value = 6, default = 10, description = "number config item15"}) + table.insert(configs_sub, menuconf.number {value = 6, default = 10, description = "number config item16"}) + table.insert(configs_sub, menuconf.number {value = 6, default = 10, description = "number config item17"}) + table.insert(configs_sub, menuconf.number {value = 6, default = 10, description = "number config item18"}) + table.insert(configs_sub, menuconf.number {value = 6, default = 10, description = "number config item19"}) + local configs = {} table.insert(configs, menuconf.boolean {description = "boolean config item"}) table.insert(configs, menuconf.boolean {default = true, new = false, description = {"boolean config item2", From 41a054c1760376c5a7178ec7ac96996b6b37bb02 Mon Sep 17 00:00:00 2001 From: ruki Date: Sun, 29 Nov 2020 19:29:17 +0800 Subject: [PATCH 10/12] remove some comments --- src/ltui/choicebox.lua | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ltui/choicebox.lua b/src/ltui/choicebox.lua index aa385f4..4e16476 100644 --- a/src/ltui/choicebox.lua +++ b/src/ltui/choicebox.lua @@ -117,8 +117,6 @@ end -- on event function choicebox:on_event(e) - - -- select config if e.type == event.ev_keyboard then if e.key_name == "Down" then if self:current() == self:last() then From 75b1d2e4c7044cdbe38d1f0293811d2f3f63fe14 Mon Sep 17 00:00:00 2001 From: ruki Date: Sun, 29 Nov 2020 21:23:37 +0800 Subject: [PATCH 11/12] support scrollbar for menuconf --- src/ltui/choicebox.lua | 6 +- src/ltui/choicedialog.lua | 12 ++- src/ltui/mconfdialog.lua | 43 +++++++++- src/ltui/menuconf.lua | 176 +++++++++++++++++++++++++++++--------- src/ltui/textdialog.lua | 4 +- 5 files changed, 192 insertions(+), 49 deletions(-) diff --git a/src/ltui/choicebox.lua b/src/ltui/choicebox.lua index 4e16476..969cda6 100644 --- a/src/ltui/choicebox.lua +++ b/src/ltui/choicebox.lua @@ -72,11 +72,11 @@ function choicebox:load(values, selected) -- select the first item self:select(self:first()) - -- invalidate - self:invalidate() - -- on loaded self:action_on(action.ac_on_load) + + -- invalidate + self:invalidate() end -- is scrollable? diff --git a/src/ltui/choicedialog.lua b/src/ltui/choicedialog.lua index 7a16412..2820702 100644 --- a/src/ltui/choicedialog.lua +++ b/src/ltui/choicedialog.lua @@ -64,10 +64,15 @@ function choicedialog:init(name, bounds, title) self:box():panel():action_add(action.ac_on_resized, function (v) self:choicebox():bounds_set(rect:new(0, 0, v:width() - 1, v:height())) self:scrollbar_box():bounds_set(rect:new(v:width() - 1, 0, 1, v:height())) + if self:choicebox():scrollable() then + self:scrollbar_box():show(true) + else + self:scrollbar_box():show(false) + end end) -- show scrollbar? - self:choicebox():action_set(action.ac_on_load, function (v) + self:choicebox():action_add(action.ac_on_load, function (v) if v:scrollable() then self:scrollbar_box():show(true) else @@ -76,7 +81,7 @@ function choicedialog:init(name, bounds, title) end) -- on scroll - self:choicebox():action_set(action.ac_on_scrolled, function (v, progress) + self:choicebox():action_add(action.ac_on_scrolled, function (v, progress) if self:scrollbar_box():state("visible") then self:scrollbar_box():progress_set(progress) end @@ -96,7 +101,8 @@ end -- get box scrollbar function choicedialog:scrollbar_box() if not self._SCROLLBAR_BOX then - self._SCROLLBAR_BOX = scrollbar:new("choicedialog.scrollbar", rect:new(self:box():panel():width() - 1, 0, 1, self:box():panel():height())) + local bounds = self:box():panel():bounds() + self._SCROLLBAR_BOX = scrollbar:new("choicedialog.scrollbar", rect:new(bounds:width() - 1, 0, 1, bounds:height())) self._SCROLLBAR_BOX:show(false) end return self._SCROLLBAR_BOX diff --git a/src/ltui/mconfdialog.lua b/src/ltui/mconfdialog.lua index d4a2e08..454500e 100644 --- a/src/ltui/mconfdialog.lua +++ b/src/ltui/mconfdialog.lua @@ -26,6 +26,7 @@ local event = require("ltui/event") local action = require("ltui/action") local curses = require("ltui/curses") local window = require("ltui/window") +local scrollbar = require("ltui/scrollbar") local menuconf = require("ltui/menuconf") local boxdialog = require("ltui/boxdialog") local textdialog = require("ltui/textdialog") @@ -54,6 +55,9 @@ Pressing includes, excludes. Enter or to go back, for H self:button_add("save", "< Save >", function (v, e) self:action_on(action.ac_on_save) end) self:buttons():select(self:button("select")) + -- insert scrollbar + self:box():panel():insert(self:scrollbar_menuconf()) + -- insert menu config self:box():panel():insert(self:menuconf()) self:box():panel():action_add(action.ac_on_resized, function (v) @@ -64,6 +68,17 @@ Pressing includes, excludes. Enter or to go back, for H -- disable to select to box (disable Tab switch and only response to buttons) self:box():option_set("selectable", false) + -- on resize for panel + self:box():panel():action_add(action.ac_on_resized, function (v) + self:menuconf():bounds_set(rect:new(0, 0, v:width() - 1, v:height())) + self:scrollbar_menuconf():bounds_set(rect:new(v:width() - 1, 0, 1, v:height())) + if self:menuconf():scrollable() then + self:scrollbar_menuconf():show(true) + else + self:scrollbar_menuconf():show(false) + end + end) + -- on selected self:menuconf():action_set(action.ac_on_selected, function (v, config) @@ -94,6 +109,22 @@ Pressing includes, excludes. Enter or to go back, for H return true end end) + + -- show scrollbar? + self:menuconf():action_add(action.ac_on_load, function (v) + if v:scrollable() then + self:scrollbar_menuconf():show(true) + else + self:scrollbar_menuconf():show(false) + end + end) + + -- on scroll + self:menuconf():action_add(action.ac_on_scrolled, function (v, progress) + if self:scrollbar_menuconf():state("visible") then + self:scrollbar_menuconf():progress_set(progress) + end + end) end -- load configs @@ -111,12 +142,22 @@ end function mconfdialog:menuconf() if not self._MENUCONF then local bounds = self:box():panel():bounds() - self._MENUCONF = menuconf:new("mconfdialog.menuconf", rect:new(0, 0, bounds:width(), bounds:height())) + self._MENUCONF = menuconf:new("mconfdialog.menuconf", rect:new(0, 0, bounds:width() - 1, bounds:height())) self._MENUCONF:state_set("focused", true) -- we can select and highlight selected item end return self._MENUCONF end +-- get menu scrollbar +function mconfdialog:scrollbar_menuconf() + if not self._SCROLLBAR_MENUCONF then + local bounds = self:box():panel():bounds() + self._SCROLLBAR_MENUCONF = scrollbar:new("mconfdialog.scrollbar", rect:new(bounds:width() - 1, 0, 1, bounds:height())) + self._SCROLLBAR_MENUCONF:show(false) + end + return self._SCROLLBAR_MENUCONF +end + -- get help dialog function mconfdialog:helpdialog() if not self._HELPDIALOG then diff --git a/src/ltui/menuconf.lua b/src/ltui/menuconf.lua index 8709ae9..ac109c6 100644 --- a/src/ltui/menuconf.lua +++ b/src/ltui/menuconf.lua @@ -40,18 +40,132 @@ function menuconf:init(name, bounds) -- init configs self._CONFIGS = {} + + -- init items + self._ITEMS = {} + + -- init start index + self._STARTINDEX = 1 + + -- on resize for panel + self:action_add(action.ac_on_resized, function (v) + local items = self:_items() + local totalcount = #items + local startindex = self._STARTINDEX + self:clear() + for idx = startindex, startindex + self:height() do + local item = items[idx] + if item then + item:bounds():move2(0, idx - startindex) + self:insert(item) + else + break + end + end + self:select(self:first()) + self:invalidate() + end) +end + +-- load configs +function menuconf:load(configs) + + -- clear the views first + self:clear() + + -- detach the previous config and view + local configs_prev = self._CONFIGS._PREV + if configs_prev then + for _, config in ipairs(configs_prev) do + config._view = nil + end + end + + -- save configs + self._CONFIGS = configs + + -- load items + local items = {} + for idx, config in ipairs(configs) do + table.insert(items, self:_load_item(config, idx)) + end + self._ITEMS = items + + -- insert top-n items + local startindex = self._STARTINDEX + for idx = startindex, startindex + self:height() do + local item = items[idx] + if item then + self:insert(item) + else + break + end + end + + -- select the first item + self:select(self:first()) + + -- on loaded + self:action_on(action.ac_on_load) + + -- invalidate + self:invalidate() +end + +-- is scrollable? +function menuconf:scrollable() + return #self:_items() > self:height() +end + +-- scroll +function menuconf:scroll(count) + if self:scrollable() then + local items = self:_items() + local totalcount = #items + local startindex = self._STARTINDEX + count + if startindex > totalcount then + return + elseif startindex < 1 then + startindex = 1 + end + self._STARTINDEX = startindex + self:clear() + for idx = startindex, startindex + self:height() do + local item = items[idx] + if item then + item:bounds():move2(0, idx - startindex) + self:insert(item) + else + break + end + end + if count > 0 then + self:select(self:first()) + else + self:select(self:last()) + end + self:invalidate() + end end -- on event function menuconf:on_event(e) - - -- select config local back = false if e.type == event.ev_keyboard then if e.key_name == "Down" then - return self:select_next() + if self:current() == self:last() then + self:scroll(self:height()) + end + self:select_next() + self:_notify_scrolled() + return true elseif e.key_name == "Up" then - return self:select_prev() + if self:current() == self:first() then + self:scroll(-self:height()) + end + self:select_prev() + self:_notify_scrolled() + return true elseif e.key_name == "Enter" or e.key_name == " " then self:_do_select() return true @@ -85,54 +199,36 @@ function menuconf:on_event(e) end end --- load configs -function menuconf:load(configs) - - -- clear the views first - self:clear() - - -- detach the previous config and view - local configs_prev = self._CONFIGS._PREV - if configs_prev then - for _, config in ipairs(configs_prev) do - config._view = nil - end - end - - -- insert configs - self._CONFIGS = configs - for _, config in ipairs(configs) do - if self:count() < self:height() then - self:_do_insert(config) - end - end - - -- select the first item - self:select(self:first()) - - -- invalidate - self:invalidate() -end - --- do insert a config item -function menuconf:_do_insert(config) +-- load a config item +function menuconf:_load_item(config, index) -- init a config item view - local item = button:new("menuconf.config." .. self:count(), - rect:new(0, self:count(), self:width(), 1), + local item = button:new("menuconf.config." .. index, + rect:new(0, index - 1, self:width(), 1), tostring(config), function (v, e) self:_do_select() end) - -- attach this config + -- attach this index and config + item:extra_set("index", index) item:extra_set("config", config) -- attach this view config._view = item + return item +end - -- insert this config item - self:insert(item) +-- notify scrolled +function menuconf:_notify_scrolled() + local totalcount = #self:_items() + local startindex = self:current():extra("index") + self:action_on(action.ac_on_scrolled, startindex / totalcount) +end + +-- get all items +function menuconf:_items() + return self._ITEMS end -- do select the current config diff --git a/src/ltui/textdialog.lua b/src/ltui/textdialog.lua index c375fc3..a0f7d12 100644 --- a/src/ltui/textdialog.lua +++ b/src/ltui/textdialog.lua @@ -60,7 +60,7 @@ function textdialog:init(name, bounds, title) end) -- show scrollbar? - self:text():action_set(action.ac_on_text_changed, function (v) + self:text():action_add(action.ac_on_text_changed, function (v) if self:option("scrollable") then if v:scrollable() then self:scrollbar():show(true) @@ -71,7 +71,7 @@ function textdialog:init(name, bounds, title) end) -- on scroll for text and scrollbar - self:text():action_set(action.ac_on_scrolled, function (v, progress) + self:text():action_add(action.ac_on_scrolled, function (v, progress) if self:scrollbar():state("visible") then self:scrollbar():progress_set(progress) end From b014d789ffa84c0d8bc84a8e6bef3dba9e69a470 Mon Sep 17 00:00:00 2001 From: ruki Date: Sun, 29 Nov 2020 22:14:41 +0800 Subject: [PATCH 12/12] fix scroll bug --- src/ltui/choicebox.lua | 33 +++++++++++++++++++++++---- src/ltui/menuconf.lua | 52 +++++++++++++++++++++++------------------- 2 files changed, 58 insertions(+), 27 deletions(-) diff --git a/src/ltui/choicebox.lua b/src/ltui/choicebox.lua index 969cda6..bbb889f 100644 --- a/src/ltui/choicebox.lua +++ b/src/ltui/choicebox.lua @@ -60,7 +60,7 @@ function choicebox:load(values, selected) -- insert top-n items local startindex = self._STARTINDEX - for idx = startindex, startindex + self:height() do + for idx = startindex, startindex + self:height() - 1 do local item = items[idx] if item then self:insert(item) @@ -97,7 +97,7 @@ function choicebox:scroll(count) end self._STARTINDEX = startindex self:clear() - for idx = startindex, startindex + self:height() do + for idx = startindex, startindex + self:height() - 1 do local item = items[idx] if item then item:bounds():move2(0, idx - startindex) @@ -115,21 +115,46 @@ function choicebox:scroll(count) end end +-- on resize +function choicebox:on_resize() + local items = self:_items() + local totalcount = #items + local startindex = self._STARTINDEX + for idx = 1, totalcount do + local item = items[idx] + if item then + if idx >= startindex and idx < startindex + self:height() then + if not self:view(item:name()) then + item:bounds():move2(0, idx - startindex) + self:insert(item) + end + else + if self:view(item:name()) then + self:remove(item) + end + end + end + end + panel.on_resize(self) +end + -- on event function choicebox:on_event(e) if e.type == event.ev_keyboard then if e.key_name == "Down" then if self:current() == self:last() then self:scroll(self:height()) + else + self:select_next() end - self:select_next() self:_notify_scrolled() return true elseif e.key_name == "Up" then if self:current() == self:first() then self:scroll(-self:height()) + else + self:select_prev() end - self:select_prev() self:_notify_scrolled() return true elseif e.key_name == "Enter" or e.key_name == " " then diff --git a/src/ltui/menuconf.lua b/src/ltui/menuconf.lua index ac109c6..6955bbe 100644 --- a/src/ltui/menuconf.lua +++ b/src/ltui/menuconf.lua @@ -46,25 +46,6 @@ function menuconf:init(name, bounds) -- init start index self._STARTINDEX = 1 - - -- on resize for panel - self:action_add(action.ac_on_resized, function (v) - local items = self:_items() - local totalcount = #items - local startindex = self._STARTINDEX - self:clear() - for idx = startindex, startindex + self:height() do - local item = items[idx] - if item then - item:bounds():move2(0, idx - startindex) - self:insert(item) - else - break - end - end - self:select(self:first()) - self:invalidate() - end) end -- load configs @@ -93,7 +74,7 @@ function menuconf:load(configs) -- insert top-n items local startindex = self._STARTINDEX - for idx = startindex, startindex + self:height() do + for idx = startindex, startindex + self:height() - 1 do local item = items[idx] if item then self:insert(item) @@ -130,7 +111,7 @@ function menuconf:scroll(count) end self._STARTINDEX = startindex self:clear() - for idx = startindex, startindex + self:height() do + for idx = startindex, startindex + self:height() - 1 do local item = items[idx] if item then item:bounds():move2(0, idx - startindex) @@ -148,6 +129,29 @@ function menuconf:scroll(count) end end +-- on resize +function menuconf:on_resize() + local items = self:_items() + local totalcount = #items + local startindex = self._STARTINDEX + for idx = 1, totalcount do + local item = items[idx] + if item then + if idx >= startindex and idx < startindex + self:height() then + if not self:view(item:name()) then + item:bounds():move2(0, idx - startindex) + self:insert(item) + end + else + if self:view(item:name()) then + self:remove(item) + end + end + end + end + panel.on_resize(self) +end + -- on event function menuconf:on_event(e) local back = false @@ -155,15 +159,17 @@ function menuconf:on_event(e) if e.key_name == "Down" then if self:current() == self:last() then self:scroll(self:height()) + else + self:select_next() end - self:select_next() self:_notify_scrolled() return true elseif e.key_name == "Up" then if self:current() == self:first() then self:scroll(-self:height()) + else + self:select_prev() end - self:select_prev() self:_notify_scrolled() return true elseif e.key_name == "Enter" or e.key_name == " " then