1
0
mirror of https://github.com/kepler155c/opus synced 2025-07-06 12:02:50 +00:00
This commit is contained in:
kepler155c@gmail.com 2019-11-16 22:12:02 -07:00
parent efa1a5bbf5
commit a3a8c64be8
18 changed files with 123 additions and 46 deletions

View File

@ -6,8 +6,7 @@ local multishell = _ENV.multishell
local name = ({ ... })[1] or error('Syntax: inspect COMPONENT') local name = ({ ... })[1] or error('Syntax: inspect COMPONENT')
local events = { } local events = { }
local page local page, lastEvent, focused
local lastEvent
local function isRelevant(el) local function isRelevant(el)
return page.testContainer == el or el.parent and isRelevant(el.parent) return page.testContainer == el or el.parent and isRelevant(el.parent)
@ -43,11 +42,12 @@ end
page = UI.Page { page = UI.Page {
testContainer = UI.Window { testContainer = UI.Window {
ey = 10, ey = '50%',
testing = component.example(), testing = component.example(),
}, },
tabs = UI.Tabs { tabs = UI.Tabs {
y = 11, backgroundColor = colors.red,
y = '50%',
properties = UI.Tab { properties = UI.Tab {
tabTitle = 'Properties', tabTitle = 'Properties',
grid = UI.ScrollingGrid { grid = UI.ScrollingGrid {
@ -66,18 +66,29 @@ page = UI.Page {
index = 2, index = 2,
tabTitle = 'Methods', tabTitle = 'Methods',
grid = UI.ScrollingGrid { grid = UI.ScrollingGrid {
ex = '50%',
headerBackgroundColor = colors.red, headerBackgroundColor = colors.red,
sortColumn = 'key', sortColumn = 'key',
columns = { columns = {
{ heading = 'key', key = 'key' }, { heading = 'key', key = 'key' },
}, },
}, },
docs = UI.TextArea {
x = '50%',
backgroundColor = colors.black,
},
eventHandler = function (self, event)
if event.type == 'grid_focus_row' and focused then
self.docs:setText(focused:getDoc(event.selected.key) or '')
end
end,
}, },
events = UI.Tab { events = UI.Tab {
index = 1, index = 1,
tabTitle = 'Events', tabTitle = 'Events',
UI.MenuBar { UI.MenuBar {
y = -1, y = -1,
backgroundColor = colors.red,
buttons = { buttons = {
{ text = 'Clear' }, { text = 'Clear' },
} }
@ -124,6 +135,7 @@ page = UI.Page {
}, },
eventHandler = function (self, event) eventHandler = function (self, event)
if event.type == 'focus_change' and isRelevant(event.focused) then if event.type == 'focus_change' and isRelevant(event.focused) then
focused = event.focused
local t = { } local t = { }
for k,v in pairs(event.focused) do for k,v in pairs(event.focused) do
table.insert(t, { table.insert(t, {

View File

@ -398,6 +398,7 @@ local UI = Manager()
--[[-- Basic drawable area --]]-- --[[-- Basic drawable area --]]--
UI.Window = class() UI.Window = class()
UI.Window.uid = 1 UI.Window.uid = 1
UI.Window.docs = { }
UI.Window.defaults = { UI.Window.defaults = {
UIElement = 'Window', UIElement = 'Window',
x = 1, x = 1,
@ -482,13 +483,14 @@ function UI.Window:layout()
end end
if type(self.x) == 'string' then if type(self.x) == 'string' then
self.x = calc(self.x, self.parent.width) self.x = calc(self.x, self.parent.width) + 1
-- +1 in order to allow both x and ex to use the same %
end end
if type(self.ex) == 'string' then if type(self.ex) == 'string' then
self.ex = calc(self.ex, self.parent.width) self.ex = calc(self.ex, self.parent.width)
end end
if type(self.y) == 'string' then if type(self.y) == 'string' then
self.y = calc(self.y, self.parent.height) self.y = calc(self.y, self.parent.height) + 1
end end
if type(self.ey) == 'string' then if type(self.ey) == 'string' then
self.ey = calc(self.ey, self.parent.height) self.ey = calc(self.ey, self.parent.height)
@ -539,6 +541,22 @@ function UI.Window:setParent()
self:layout() self:layout()
-- Experimental
-- Inherit properties from the parent container
-- does this need to be in reverse order ?
local m = getmetatable(self) -- get the class for this instance
repeat
if m.inherits then
for k, v in pairs(m.inherits) do
local value = self.parent:getProperty(v)
if value then
self[k] = value
end
end
end
m = m._base
until not m
self:initChildren() self:initChildren()
end end
@ -555,6 +573,13 @@ function UI.Window:resize()
end end
end end
UI.Window.docs.add = [[add(TABLE)
Add element(s) to a window. Example:
page:add({
text = UI.Text {
x=5,value='help'
}
})]]
function UI.Window:add(children) function UI.Window:add(children)
UI:mergeProperties(self, children) UI:mergeProperties(self, children)
self:initChildren() self:initChildren()
@ -574,6 +599,8 @@ function UI.Window:setCursorBlink(blink)
self.parent:setCursorBlink(blink) self.parent:setCursorBlink(blink)
end end
UI.Window.docs.draw = [[draw(VOID)
Redraws the window in the internal buffer.]]
function UI.Window:draw() function UI.Window:draw()
self:clear(self.backgroundColor) self:clear(self.backgroundColor)
if self.children then if self.children then
@ -585,6 +612,21 @@ function UI.Window:draw()
end end
end end
UI.Window.docs.getDoc = [[getDoc(STRING method)
Gets the documentation for a method.]]
function UI.Window:getDoc(method)
local m = getmetatable(self) -- get the class for this instance
repeat
if m.docs and m.docs[method] then
return m.docs[method]
end
m = m._base
until not m
end
UI.Window.docs.sync = [[sync(VOID)
Invoke a screen update. Automatically called at top level after an input event.
Call to force a screen update.]]
function UI.Window:sync() function UI.Window:sync()
if self.parent then if self.parent then
self.parent:sync() self.parent:sync()
@ -614,9 +656,11 @@ function UI.Window:setTextScale(textScale)
self.parent:setTextScale(textScale) self.parent:setTextScale(textScale)
end end
UI.Window.docs.clear = [[clear(opt COLOR bg, opt COLOR fg)
Clears the window using the either the passed values or the defaults for that window.]]
function UI.Window:clear(bg, fg) function UI.Window:clear(bg, fg)
if self.canvas then if self.canvas then
self.canvas:clear(bg or self.backgroundColor, fg or self.textColor) self.canvas:clear(bg or self:getProperty('backgroundColor'), fg or self:getProperty('textColor'))
else else
self:clearArea(1 + self.offx, 1 + self.offy, self.width, self.height, bg) self:clearArea(1 + self.offx, 1 + self.offy, self.width, self.height, bg)
end end
@ -635,18 +679,18 @@ function UI.Window:clearArea(x, y, width, height, bg)
end end
end end
function UI.Window:write(x, y, text, bg, tc) function UI.Window:write(x, y, text, bg, fg)
bg = bg or self.backgroundColor bg = bg or self.backgroundColor
tc = tc or self.textColor fg = fg or self.textColor
if self.canvas then if self.canvas then
self.canvas:write(x, y, text, bg, tc) self.canvas:write(x, y, text, bg or self:getProperty('backgroundColor'), fg or self:getProperty('textColor'))
else else
x = x - self.offx x = x - self.offx
y = y - self.offy y = y - self.offy
if y <= self.height and y > 0 then if y <= self.height and y > 0 then
self.parent:write( self.parent:write(
self.x + x - 1, self.y + y - 1, tostring(text), bg, tc) self.x + x - 1, self.y + y - 1, tostring(text), bg, fg)
end end
end end
end end
@ -752,12 +796,20 @@ function UI.Window:print(text, bg, fg)
return self.cursorX, self.cursorY return self.cursorX, self.cursorY
end end
UI.Window.docs.focus = [[focus(VOID)
If the function is present on a class, it indicates
that this element can accept focus. Called when receiving focus.]]
UI.Window.docs.setFocus = [[setFocus(ELEMENT el)
Set the page's focus to the passed element.]]
function UI.Window:setFocus(focus) function UI.Window:setFocus(focus)
if self.parent then if self.parent then
self.parent:setFocus(focus) self.parent:setFocus(focus)
end end
end end
UI.Window.docs.capture = [[capture(ELEMENT el)
Restricts input to the passed element's tree.]]
function UI.Window:capture(child) function UI.Window:capture(child)
if self.parent then if self.parent then
self.parent:capture(child) self.parent:capture(child)
@ -793,6 +845,8 @@ function UI.Window:pointToChild(x, y)
} }
end end
UI.Window.docs.getFocusables = [[getFocusables(VOID)
Returns a list of children that can accept focus.]]
function UI.Window:getFocusables() function UI.Window:getFocusables()
local focusable = { } local focusable = { }

View File

@ -355,7 +355,9 @@ end
function Canvas:__blitClipped(device, offset) function Canvas:__blitClipped(device, offset)
if self.parent then if self.parent then
-- contain the rendered region in the parent's region -- contain the rendered region in the parent's region
local p = Region.new(1, 1, self.parent.width, self.parent.height) local p = Region.new(1, 1,
self.parent.width + offset.x - self.x + 1,
self.parent.height + offset.y - self.y + 1)
self.regions:andRegion(p) self.regions:andRegion(p)
end end

View File

@ -21,21 +21,20 @@ UI.Checkbox.defaults = {
mouse_click = 'checkbox_toggle', mouse_click = 'checkbox_toggle',
} }
} }
UI.Checkbox.inherits = {
labelBackgroundColor = 'backgroundColor',
}
function UI.Checkbox:postInit()
self.width = self.label and #self.label + 4 or 3
end
function UI.Checkbox:draw() function UI.Checkbox:draw()
local bg = self.backgroundColor local bg = self.focused and self.backgroundFocusColor or self.backgroundColor
if self.focused then
bg = self.backgroundFocusColor
end
if type(self.value) == 'string' then
self.value = nil -- TODO: fix form
end
local text = string.format('[%s]', not self.value and ' ' or self.checkedIndicator)
local x = 1 local x = 1
if self.label then if self.label then
self:write(1, 1, self.label) self:write(1, 1, self.label, self.labelBackgroundColor)
x = #self.label + 2 x = #self.label + 2
end end
self:write(x, 1, text, bg)
self:write(x, 1, self.leftMarker, self.backgroundColor, self.textColor) self:write(x, 1, self.leftMarker, self.backgroundColor, self.textColor)
self:write(x + 1, 1, not self.value and ' ' or self.checkedIndicator, bg) self:write(x + 1, 1, not self.value and ' ' or self.checkedIndicator, bg)
self:write(x + 2, 1, self.rightMarker, self.backgroundColor, self.textColor) self:write(x + 2, 1, self.rightMarker, self.backgroundColor, self.textColor)
@ -46,11 +45,12 @@ function UI.Checkbox:focus()
end end
function UI.Checkbox:setValue(v) function UI.Checkbox:setValue(v)
self.value = v self.value = not not v
end end
function UI.Checkbox:reset() function UI.Checkbox:reset()
self.value = false self.value = false
self:draw()
end end
function UI.Checkbox:eventHandler(event) function UI.Checkbox:eventHandler(event)
@ -63,7 +63,13 @@ function UI.Checkbox:eventHandler(event)
end end
function UI.Checkbox.example() function UI.Checkbox.example()
return UI.Checkbox { return UI.Window {
ex1 = UI.Checkbox {
label = 'test',
x = 2, y = 2, x = 2, y = 2,
},
ex2 = UI.Checkbox {
x = 2, y = 4,
},
} }
end end

View File

@ -3,7 +3,6 @@ local UI = require('opus.ui')
local colors = _G.colors local colors = _G.colors
--[[-- DropMenuItem --]]--
UI.DropMenuItem = class(UI.Button) UI.DropMenuItem = class(UI.Button)
UI.DropMenuItem.defaults = { UI.DropMenuItem.defaults = {
UIElement = 'DropMenuItem', UIElement = 'DropMenuItem',

View File

@ -1,7 +1,6 @@
local class = require('opus.class') local class = require('opus.class')
local UI = require('opus.ui') local UI = require('opus.ui')
--[[-- Menu --]]--
UI.Menu = class(UI.Grid) UI.Menu = class(UI.Grid)
UI.Menu.defaults = { UI.Menu.defaults = {
UIElement = 'Menu', UIElement = 'Menu',

View File

@ -51,6 +51,7 @@ function UI.MenuBar:addButtons(buttons)
x = self.lastx, x = self.lastx,
width = #(button.text or 'button') + self.spacing, width = #(button.text or 'button') + self.spacing,
centered = false, centered = false,
backgroundColor = self.backgroundColor,
} }
self.lastx = self.lastx + buttonProperties.width self.lastx = self.lastx + buttonProperties.width
UI:mergeProperties(buttonProperties, button) UI:mergeProperties(buttonProperties, button)

View File

@ -3,7 +3,6 @@ local UI = require('opus.ui')
local colors = _G.colors local colors = _G.colors
--[[-- MenuItem --]]--
UI.MenuItem = class(UI.Button) UI.MenuItem = class(UI.Button)
UI.MenuItem.defaults = { UI.MenuItem.defaults = {
UIElement = 'MenuItem', UIElement = 'MenuItem',

View File

@ -92,25 +92,26 @@ function UI.Notification:eventHandler(event)
end end
function UI.Notification.example() function UI.Notification.example()
return UI.Window { return UI.ActiveLayer {
notify = UI.Notification { notify1 = UI.Notification {
anchor = 'top', anchor = 'top',
}, },
notify2 = UI.Notification { },
button1 = UI.Button { button1 = UI.Button {
x = 2, y = 3, x = 2, y = 3,
text = 'success', text = 'example 1',
event = 'test_success', event = 'test_success',
}, },
button2 = UI.Button { button2 = UI.Button {
x = 2, y = 5, x = 2, y = 5,
text = 'error', text = 'example 2',
event = 'test_error', event = 'test_error',
}, },
eventHandler = function (self, event) eventHandler = function (self, event)
if event.type == 'test_success' then if event.type == 'test_success' then
self.notify:success('Example text') self.notify1:success('Example text')
elseif event.type == 'test_error' then elseif event.type == 'test_error' then
self.notify:error('Example text', 0) self.notify2:error('Example text', 0)
end end
end, end,
} }

View File

@ -2,7 +2,6 @@ local class = require('opus.class')
local UI = require('opus.ui') local UI = require('opus.ui')
local Util = require('opus.util') local Util = require('opus.util')
--[[-- ScrollingGrid --]]--
UI.ScrollingGrid = class(UI.Grid) UI.ScrollingGrid = class(UI.Grid)
UI.ScrollingGrid.defaults = { UI.ScrollingGrid.defaults = {
UIElement = 'ScrollingGrid', UIElement = 'ScrollingGrid',

View File

@ -1,7 +1,6 @@
local class = require('opus.class') local class = require('opus.class')
local UI = require('opus.ui') local UI = require('opus.ui')
--[[-- SlideOut --]]--
UI.SlideOut = class(UI.Window) UI.SlideOut = class(UI.Window)
UI.SlideOut.defaults = { UI.SlideOut.defaults = {
UIElement = 'SlideOut', UIElement = 'SlideOut',
@ -62,9 +61,12 @@ end
function UI.SlideOut.example() function UI.SlideOut.example()
-- for the transistion to work properly, the parent must have a canvas -- for the transistion to work properly, the parent must have a canvas
return UI.ActiveLayer { return UI.ActiveLayer {
y = 1, -- TODO: if this is set to anything greater than 1, then
-- the layer is not rendered in the correct location
-- a general issue in canvas layers
backgroundColor = colors.cyan, backgroundColor = colors.cyan,
button = UI.Button { button = UI.Button {
x = 2, y = 2, x = 2, y = 5,
text = 'show', text = 'show',
}, },
slideOut = UI.SlideOut { slideOut = UI.SlideOut {

View File

@ -1,12 +1,9 @@
local class = require('opus.class') local class = require('opus.class')
local UI = require('opus.ui') local UI = require('opus.ui')
local colors = _G.colors
UI.Tab = class(UI.ActiveLayer) UI.Tab = class(UI.ActiveLayer)
UI.Tab.defaults = { UI.Tab.defaults = {
UIElement = 'Tab', UIElement = 'Tab',
tabTitle = 'tab', tabTitle = 'tab',
backgroundColor = colors.cyan,
y = 2, y = 2,
} }

View File

@ -2,13 +2,13 @@ local class = require('opus.class')
local UI = require('opus.ui') local UI = require('opus.ui')
local Util = require('opus.util') local Util = require('opus.util')
local colors = _G.colors
UI.TabBar = class(UI.MenuBar) UI.TabBar = class(UI.MenuBar)
UI.TabBar.defaults = { UI.TabBar.defaults = {
UIElement = 'TabBar', UIElement = 'TabBar',
buttonClass = 'TabBarMenuItem', buttonClass = 'TabBarMenuItem',
selectedBackgroundColor = colors.cyan, }
UI.TabBar.inherits = {
selectedBackgroundColor = 'backgroundColor',
} }
function UI.TabBar:enable() function UI.TabBar:enable()
UI.MenuBar.enable(self) UI.MenuBar.enable(self)

View File

@ -3,7 +3,6 @@ local UI = require('opus.ui')
local colors = _G.colors local colors = _G.colors
--[[-- TabBarMenuItem --]]--
UI.TabBarMenuItem = class(UI.Button) UI.TabBarMenuItem = class(UI.Button)
UI.TabBarMenuItem.defaults = { UI.TabBarMenuItem.defaults = {
UIElement = 'TabBarMenuItem', UIElement = 'TabBarMenuItem',
@ -13,6 +12,9 @@ UI.TabBarMenuItem.defaults = {
unselectedBackgroundColor = colors.lightGray, unselectedBackgroundColor = colors.lightGray,
backgroundColor = colors.lightGray, backgroundColor = colors.lightGray,
} }
UI.TabBarMenuItem.inherits = {
selectedBackgroundColor = 'selectedBackgroundColor',
}
function UI.TabBarMenuItem:draw() function UI.TabBarMenuItem:draw()
if self.selected then if self.selected then
self.backgroundColor = self.selectedBackgroundColor self.backgroundColor = self.selectedBackgroundColor

View File

@ -3,6 +3,7 @@ local UI = require('opus.ui')
local Util = require('opus.util') local Util = require('opus.util')
UI.Tabs = class(UI.Window) UI.Tabs = class(UI.Window)
UI.Tabs.docs = { }
UI.Tabs.defaults = { UI.Tabs.defaults = {
UIElement = 'Tabs', UIElement = 'Tabs',
} }
@ -37,6 +38,8 @@ function UI.Tabs:add(children)
end end
end end
UI.Tabs.docs.selectTab = [[selectTab(TAB)
Make to the passed tab active.]]
function UI.Tabs:selectTab(tab) function UI.Tabs:selectTab(tab)
local menuItem = Util.find(self.tabBar.children, 'tabUid', tab.uid) local menuItem = Util.find(self.tabBar.children, 'tabUid', tab.uid)
if menuItem then if menuItem then

View File

@ -1,7 +1,6 @@
local class = require('opus.class') local class = require('opus.class')
local UI = require('opus.ui') local UI = require('opus.ui')
--[[-- TextArea --]]--
UI.TextArea = class(UI.Viewport) UI.TextArea = class(UI.Viewport)
UI.TextArea.defaults = { UI.TextArea.defaults = {
UIElement = 'TextArea', UIElement = 'TextArea',

View File

@ -16,6 +16,7 @@ local function transform(directive)
end end
UI.TextEntry = class(UI.Window) UI.TextEntry = class(UI.Window)
UI.TextEntry.docs = { }
UI.TextEntry.defaults = { UI.TextEntry.defaults = {
UIElement = 'TextEntry', UIElement = 'TextEntry',
--value = '', --value = '',
@ -92,6 +93,8 @@ function UI.TextEntry:draw()
end end
end end
UI.TextEntry.docs.reset = [[reset()
Clears the value and resets the cursor.]]
function UI.TextEntry:reset() function UI.TextEntry:reset()
self.entry:reset() self.entry:reset()
self.value = nil--'' self.value = nil--''

View File

@ -3,7 +3,6 @@ local UI = require('opus.ui')
local colors = _G.colors local colors = _G.colors
--[[-- Viewport --]]--
UI.Viewport = class(UI.Window) UI.Viewport = class(UI.Window)
UI.Viewport.defaults = { UI.Viewport.defaults = {
UIElement = 'Viewport', UIElement = 'Viewport',