2023-02-18 22:58:16 +00:00
|
|
|
-- Greenhouse is a simple text scrolling handler for terminal programs.
|
2023-02-18 20:57:27 +00:00
|
|
|
-- The idea is that it can be set a region to do its scrolling and paging
|
|
|
|
-- job and then the user can draw whatever outside it.
|
|
|
|
-- This reduces code duplication for the message viewer
|
|
|
|
-- and flowerbook.
|
|
|
|
|
|
|
|
local ansikit = require 'ansikit'
|
2023-07-10 02:38:34 +00:00
|
|
|
local lunacolors = require 'lunacolors'
|
2023-02-18 20:57:27 +00:00
|
|
|
local terminal = require 'terminal'
|
2023-07-10 02:38:34 +00:00
|
|
|
local Page = require 'nature.greenhouse.page'
|
2023-02-18 20:57:27 +00:00
|
|
|
local Object = require 'nature.object'
|
|
|
|
|
|
|
|
local Greenhouse = Object:extend()
|
|
|
|
|
|
|
|
function Greenhouse:new(sink)
|
|
|
|
local size = terminal.size()
|
|
|
|
self.region = size
|
|
|
|
self.start = 1
|
2023-07-10 02:38:34 +00:00
|
|
|
self.offset = 1 -- vertical text offset
|
2023-02-18 20:57:27 +00:00
|
|
|
self.sink = sink
|
2023-02-19 20:53:39 +00:00
|
|
|
self.pages = {}
|
|
|
|
self.curPage = 1
|
2023-07-10 00:22:59 +00:00
|
|
|
self.keybinds = {
|
|
|
|
['Up'] = function(self) self:scroll 'up' end,
|
|
|
|
['Down'] = function(self) self:scroll 'down' end,
|
|
|
|
['Ctrl-Left'] = self.previous,
|
2023-07-10 02:38:34 +00:00
|
|
|
['Ctrl-Right'] = self.next,
|
|
|
|
['Ctrl-N'] = function(self) self:toc(true) end,
|
2023-07-10 00:22:59 +00:00
|
|
|
}
|
2023-07-10 02:38:34 +00:00
|
|
|
self.isToc = false
|
|
|
|
self.tocPage = nil
|
|
|
|
self.tocPageIdx = 1
|
|
|
|
self.tocOffset = 1
|
2023-02-18 20:57:27 +00:00
|
|
|
|
|
|
|
return self
|
|
|
|
end
|
|
|
|
|
2023-02-19 20:53:39 +00:00
|
|
|
function Greenhouse:addPage(page)
|
|
|
|
table.insert(self.pages, page)
|
|
|
|
end
|
|
|
|
|
|
|
|
function Greenhouse:updateCurrentPage(text)
|
|
|
|
local page = self.pages[self.curPage]
|
|
|
|
page:setText(text)
|
2023-02-18 20:57:27 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function Greenhouse:draw()
|
2023-07-10 02:38:34 +00:00
|
|
|
local workingPage = self.pages[self.curPage]
|
|
|
|
local offset = self.offset
|
|
|
|
if self.isToc then
|
|
|
|
offset = self.tocOffset
|
|
|
|
workingPage = self.tocPage
|
|
|
|
end
|
|
|
|
|
|
|
|
local lines = workingPage.lines
|
2023-02-18 20:57:27 +00:00
|
|
|
self.sink:write(ansikit.getCSI(self.start .. ';1', 'H'))
|
2023-02-19 20:53:39 +00:00
|
|
|
self.sink:write(ansikit.getCSI(2, 'J'))
|
2023-02-18 20:57:27 +00:00
|
|
|
|
2023-02-19 20:53:39 +00:00
|
|
|
-- the -2 negate is for the command and status line
|
2023-07-10 02:38:34 +00:00
|
|
|
for i = offset, offset + (self.region.height - self.start) do
|
2023-02-19 20:53:39 +00:00
|
|
|
if i > #lines then break end
|
|
|
|
self.sink:writeln('\r' .. lines[i]:gsub('\t', ' '):sub(0, self.region.width - 2))
|
2023-02-18 20:57:27 +00:00
|
|
|
end
|
2023-02-18 22:58:16 +00:00
|
|
|
self.sink:write '\r'
|
2023-07-10 02:38:34 +00:00
|
|
|
self:render()
|
|
|
|
end
|
2023-02-19 20:53:39 +00:00
|
|
|
|
2023-07-10 02:38:34 +00:00
|
|
|
function Greenhouse:render()
|
2023-02-18 20:57:27 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function Greenhouse:scroll(direction)
|
2023-07-10 02:38:34 +00:00
|
|
|
if self.isToc then
|
|
|
|
if direction == 'down' then
|
|
|
|
self:next(true)
|
|
|
|
elseif direction == 'up' then
|
|
|
|
self:previous(true)
|
|
|
|
end
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2023-02-19 20:53:39 +00:00
|
|
|
local lines = self.pages[self.curPage].lines
|
|
|
|
|
2023-02-18 22:58:44 +00:00
|
|
|
local oldOffset = self.offset
|
2023-02-18 20:57:27 +00:00
|
|
|
if direction == 'down' then
|
2023-02-19 20:53:39 +00:00
|
|
|
self.offset = math.min(self.offset + 1, #lines)
|
2023-02-18 20:57:27 +00:00
|
|
|
elseif direction == 'up' then
|
2023-02-18 21:57:46 +00:00
|
|
|
self.offset = math.max(self.offset - 1, 1)
|
2023-02-18 20:57:27 +00:00
|
|
|
end
|
2023-02-18 22:58:44 +00:00
|
|
|
|
|
|
|
if self.offset ~= oldOffset then self:draw() end
|
2023-02-18 22:58:16 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function Greenhouse:update()
|
2023-07-10 02:38:34 +00:00
|
|
|
self:resize()
|
|
|
|
if self.isToc then
|
|
|
|
self:toc()
|
|
|
|
end
|
|
|
|
|
|
|
|
self:draw()
|
|
|
|
end
|
|
|
|
|
|
|
|
function Greenhouse:resize()
|
2023-02-18 22:58:16 +00:00
|
|
|
local size = terminal.size()
|
|
|
|
self.region = size
|
2023-07-10 02:38:34 +00:00
|
|
|
end
|
2023-02-18 22:58:16 +00:00
|
|
|
|
2023-07-10 02:38:34 +00:00
|
|
|
function Greenhouse:next(toc)
|
|
|
|
local oldCurrent = toc and self.tocPageIdx or self.curPage
|
|
|
|
local pageIdx = math.min(oldCurrent + 1, #self.pages)
|
|
|
|
|
|
|
|
if toc then
|
|
|
|
self.tocPageIdx = pageIdx
|
|
|
|
else
|
|
|
|
self.curPage = pageIdx
|
|
|
|
end
|
|
|
|
|
|
|
|
if pageIdx ~= oldCurrent then
|
|
|
|
self.offset = 1
|
|
|
|
self:update()
|
|
|
|
end
|
2023-02-18 20:57:27 +00:00
|
|
|
end
|
|
|
|
|
2023-07-10 02:38:34 +00:00
|
|
|
function Greenhouse:previous(toc)
|
|
|
|
local oldCurrent = toc and self.tocPageIdx or self.curPage
|
|
|
|
local pageIdx = math.max(self.curPage - 1, 1)
|
|
|
|
|
|
|
|
if toc then
|
|
|
|
self.tocPageIdx = pageIdx
|
|
|
|
else
|
|
|
|
self.curPage = pageIdx
|
|
|
|
end
|
|
|
|
|
|
|
|
if pageIdx ~= oldCurrent then
|
2023-02-19 21:05:25 +00:00
|
|
|
self.offset = 1
|
2023-07-10 02:38:34 +00:00
|
|
|
self:update()
|
2023-02-19 21:05:25 +00:00
|
|
|
end
|
2023-02-19 20:53:39 +00:00
|
|
|
end
|
|
|
|
|
2023-07-10 02:38:34 +00:00
|
|
|
function Greenhouse:jump(idx)
|
|
|
|
if idx ~= self.curPage then
|
2023-02-19 21:05:25 +00:00
|
|
|
self.offset = 1
|
|
|
|
end
|
2023-07-10 02:38:34 +00:00
|
|
|
self.curPage = idx
|
|
|
|
self:update()
|
2023-02-19 20:53:39 +00:00
|
|
|
end
|
|
|
|
|
2023-07-10 00:22:59 +00:00
|
|
|
function Greenhouse:keybind(key, callback)
|
|
|
|
self.keybinds[key] = callback
|
|
|
|
end
|
|
|
|
|
2023-07-10 02:38:34 +00:00
|
|
|
function Greenhouse:toc(toggle)
|
|
|
|
if not self.isToc then
|
|
|
|
self.tocPageIdx = self.curPage
|
|
|
|
end
|
|
|
|
if toggle then self.isToc = not self.isToc end
|
|
|
|
-- Generate a special page for our table of contents
|
|
|
|
local tocText = string.format([[
|
|
|
|
%s
|
|
|
|
|
|
|
|
]], lunacolors.cyan(lunacolors.bold '―― Table of Contents ――'))
|
|
|
|
|
|
|
|
local genericPageCount = 1
|
|
|
|
for i, page in ipairs(self.pages) do
|
|
|
|
local title = page.title
|
|
|
|
if title == 'Page' then
|
|
|
|
title = 'Page #' .. genericPageCount
|
|
|
|
genericPageCount = genericPageCount + 1
|
|
|
|
end
|
|
|
|
if i == self.tocPageIdx then
|
|
|
|
title = lunacolors.invert(title)
|
|
|
|
end
|
|
|
|
|
|
|
|
tocText = tocText .. title .. '\n'
|
|
|
|
end
|
|
|
|
self.tocPage = Page('TOC', tocText)
|
|
|
|
self:draw()
|
|
|
|
end
|
|
|
|
|
2023-07-10 03:07:32 +00:00
|
|
|
function Greenhouse:input(char)
|
|
|
|
end
|
|
|
|
|
2023-04-12 00:44:29 +00:00
|
|
|
function Greenhouse:initUi()
|
|
|
|
local ansikit = require 'ansikit'
|
|
|
|
local bait = require 'bait'
|
|
|
|
local commander = require 'commander'
|
|
|
|
local hilbish = require 'hilbish'
|
|
|
|
local terminal = require 'terminal'
|
|
|
|
local Page = require 'nature.greenhouse.page'
|
|
|
|
local done = false
|
|
|
|
|
|
|
|
bait.catch('signal.sigint', function()
|
|
|
|
ansikit.clear()
|
|
|
|
done = true
|
|
|
|
end)
|
|
|
|
|
|
|
|
bait.catch('signal.resize', function()
|
|
|
|
self:update()
|
|
|
|
end)
|
|
|
|
|
|
|
|
ansikit.screenAlt()
|
|
|
|
ansikit.clear(true)
|
|
|
|
self:draw()
|
|
|
|
|
|
|
|
hilbish.goro(function()
|
|
|
|
while not done do
|
|
|
|
local c = read()
|
2023-07-10 03:07:32 +00:00
|
|
|
self:keybind('Ctrl-D', function()
|
2023-04-12 00:44:29 +00:00
|
|
|
done = true
|
2023-07-10 03:07:32 +00:00
|
|
|
end)
|
2023-04-12 00:44:29 +00:00
|
|
|
|
2023-07-10 00:22:59 +00:00
|
|
|
if self.keybinds[c] then
|
|
|
|
self.keybinds[c](self)
|
2023-07-10 03:07:32 +00:00
|
|
|
else
|
|
|
|
self:input(c)
|
2023-07-09 20:27:11 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
--[[
|
2023-04-12 00:44:29 +00:00
|
|
|
if c == 27 then
|
|
|
|
local c1 = read()
|
|
|
|
if c1 == 91 then
|
|
|
|
local c2 = read()
|
|
|
|
if c2 == 66 then -- arrow down
|
|
|
|
self:scroll 'down'
|
|
|
|
elseif c2 == 65 then -- arrow up
|
|
|
|
self:scroll 'up'
|
|
|
|
end
|
|
|
|
|
|
|
|
if c2 == 49 then
|
|
|
|
local c3 = read()
|
|
|
|
if c3 == 59 then
|
|
|
|
local c4 = read()
|
|
|
|
if c4 == 53 then
|
|
|
|
local c5 = read()
|
|
|
|
if c5 == 67 then
|
|
|
|
self:next()
|
|
|
|
elseif c5 == 68 then
|
|
|
|
self:previous()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
goto continue
|
|
|
|
end
|
2023-07-09 20:27:11 +00:00
|
|
|
]]--
|
2023-04-12 00:44:29 +00:00
|
|
|
|
|
|
|
::continue::
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
|
|
|
while not done do
|
|
|
|
--
|
|
|
|
end
|
|
|
|
ansikit.screenMain()
|
|
|
|
end
|
|
|
|
|
|
|
|
function read()
|
|
|
|
terminal.saveState()
|
|
|
|
terminal.setRaw()
|
2023-07-09 20:27:11 +00:00
|
|
|
local c = hilbish.editor.readChar()
|
2023-04-12 00:44:29 +00:00
|
|
|
|
|
|
|
terminal.restoreState()
|
2023-07-09 20:27:11 +00:00
|
|
|
return c
|
2023-04-12 00:44:29 +00:00
|
|
|
end
|
|
|
|
|
2023-02-18 20:57:27 +00:00
|
|
|
return Greenhouse
|