From 1f9076fbd75b4886dd03183d0f1fa004937efd19 Mon Sep 17 00:00:00 2001 From: ruki Date: Sat, 28 Nov 2020 00:42:19 +0800 Subject: [PATCH] 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)