diff --git a/sys/apps/Tasks.lua b/sys/apps/Tasks.lua index 3dbd395..cde082a 100644 --- a/sys/apps/Tasks.lua +++ b/sys/apps/Tasks.lua @@ -24,7 +24,7 @@ local page = UI.Page { values = kernel.routines, sortColumn = 'uid', autospace = true, - getDisplayValues = function(_, row) + getDisplayValues = function (_, row) local elapsed = os.clock()-row.timestamp return { uid = row.uid, diff --git a/sys/apps/Welcome.lua b/sys/apps/Welcome.lua index a25278f..980f27f 100644 --- a/sys/apps/Welcome.lua +++ b/sys/apps/Welcome.lua @@ -60,6 +60,12 @@ local page = UI.Page { x = 3, ex = -3, y = 4, ey = -3, value = string.format(labelIntro, Ansi.white), }, + validate = function (self) + if self.label.value then + os.setComputerLabel(self.label.value) + end + return true + end, }, password = UI.WizardPage { index = 3, @@ -73,23 +79,18 @@ local page = UI.Page { mask = true, shadowText = 'password', }, ---[[ - groupLabel = UI.Text { - x = 3, y = 3, - value = 'Group' - }, - group = UI.TextEntry { - x = 12, ex = -3, y = 3, - limit = 32, - shadowText = 'network group', - }, -]] intro = UI.TextArea { textColor = colors.yellow, inactive = true, x = 3, ex = -3, y = 5, ey = -3, value = string.format(passwordIntro, Ansi.white), }, + validate = function (self) + if type(self.newPass.value) == "string" and #self.newPass.value > 0 then + Security.updatePassword(SHA.compute(self.newPass.value)) + end + return true + end, }, packages = UI.WizardPage { index = 4, @@ -119,27 +120,6 @@ local page = UI.Page { notification = UI.Notification { }, } -function page.wizard.pages.label:validate() - if self.label.value then - os.setComputerLabel(self.label.value) - end - return true -end - -function page.wizard.pages.password:validate() - if type(self.newPass.value) == "string" and #self.newPass.value > 0 then - Security.updatePassword(SHA.compute(self.newPass.value)) - end - --[[ - if #self.group.value > 0 then - local config = Config.load('os') - config.group = self.group.value - Config.update('os', config) - end - ]] - return true -end - function page:eventHandler(event) if event.type == 'skip' then self.wizard:emit({ type = 'nextView' }) diff --git a/sys/modules/opus/ui.lua b/sys/modules/opus/ui.lua index 6e464ad..4ff0bea 100644 --- a/sys/modules/opus/ui.lua +++ b/sys/modules/opus/ui.lua @@ -1,4 +1,3 @@ -local Canvas = require('opus.ui.canvas') local class = require('opus.class') local Event = require('opus.event') local Input = require('opus.input') @@ -19,18 +18,18 @@ local textutils = _G.textutils the bottom up. Once reaching the top, setParent is called top down. On :init(), elements do not know the parent or can calculate sizing. -]] --- need to add offsets to this test -local function getPosition(element) - local x, y = 1, 1 - repeat - x = element.x + x - 1 - y = element.y + y - 1 - element = element.parent - until not element - return x, y -end + Calling order: + window:postInit() + at this point, the window has all default values set + window:setParent() + parent has been assigned + following are called: + window:layout() + sizing / positioning is performed + window:initChildren() + each child of window will get initialized +]] --[[-- Top Level Manager --]]-- local Manager = class() @@ -953,7 +952,7 @@ function UI.Window:addTransition(effect, args) if self.parent then args = args or { } if not args.x then -- not good - args.x, args.y = getPosition(self) + args.x, args.y = self.x, self.y -- getPosition(self) args.width = self.width args.height = self.height end @@ -1117,149 +1116,7 @@ function UI.Device:sync() end end ---[[-- Page (focus manager) --]]-- -UI.Page = class(UI.Window) -UI.Page.defaults = { - UIElement = 'Page', - accelerators = { - down = 'focus_next', - enter = 'focus_next', - tab = 'focus_next', - ['shift-tab' ] = 'focus_prev', - up = 'focus_prev', - }, - backgroundColor = colors.cyan, - textColor = colors.white, -} -function UI.Page:postInit() - self.parent = self.parent or UI.defaultDevice - self.__target = self - self.canvas = Canvas({ - x = 1, y = 1, width = self.parent.width, height = self.parent.height, - isColor = self.parent.isColor, - }) - self.canvas:clear(self.backgroundColor, self.textColor) -end - -function UI.Page:enable() - self.canvas.visible = true - UI.Window.enable(self) - - if not self.focused or not self.focused.enabled then - self:focusFirst() - end -end - -function UI.Page:disable() - self.canvas.visible = false - UI.Window.disable(self) -end - -function UI.Page:sync() - if self.enabled then - self.parent:sync() - end -end - -function UI.Page:capture(child) - self.__target = child -end - -function UI.Page:release(child) - if self.__target == child then - self.__target = self - end -end - -function UI.Page:pointToChild(x, y) - if self.__target == self then - return UI.Window.pointToChild(self, x, y) - end - x = x + self.offx - self.x + 1 - y = y + self.offy - self.y + 1 - return self.__target:pointToChild(x, y) -end - -function UI.Page:getFocusables() - if self.__target == self or self.__target.pageType ~= 'modal' then - return UI.Window.getFocusables(self) - end - return self.__target:getFocusables() -end - -function UI.Page:getFocused() - return self.focused -end - -function UI.Page:focusPrevious() - local function getPreviousFocus(focused) - local focusables = self:getFocusables() - local k = Util.contains(focusables, focused) - if k then - if k > 1 then - return focusables[k - 1] - end - return focusables[#focusables] - end - end - - local focused = getPreviousFocus(self.focused) - if focused then - self:setFocus(focused) - end -end - -function UI.Page:focusNext() - local function getNextFocus(focused) - local focusables = self:getFocusables() - local k = Util.contains(focusables, focused) - if k then - if k < #focusables then - return focusables[k + 1] - end - return focusables[1] - end - end - - local focused = getNextFocus(self.focused) - if focused then - self:setFocus(focused) - end -end - -function UI.Page:setFocus(child) - if not child or not child.focus then - return - end - - if self.focused and self.focused ~= child then - self.focused.focused = false - self.focused:focus() - self.focused:emit({ type = 'focus_lost', focused = child }) - end - - self.focused = child - if not child.focused then - child.focused = true - child:emit({ type = 'focus_change', focused = child }) - --self:emit({ type = 'focus_change', focused = child }) - end - - child:focus() -end - -function UI.Page:eventHandler(event) - if self.focused then - if event.type == 'focus_next' then - self:focusNext() - return true - elseif event.type == 'focus_prev' then - self:focusPrevious() - return true - end - end -end - +-- lazy load components local function loadComponents() local function load(name) local s, m = Util.run(_ENV, 'sys/modules/opus/ui/components/' .. name .. '.lua') @@ -1295,7 +1152,6 @@ end loadComponents() UI:loadTheme('usr/config/ui.theme') Util.merge(UI.Window.defaults, UI.theme.Window) -Util.merge(UI.Page.defaults, UI.theme.Page) UI:setDefaultDevice(UI.Device({ device = term.current() })) return UI diff --git a/sys/modules/opus/ui/canvas.lua b/sys/modules/opus/ui/canvas.lua index cb46ede..6fe12b1 100644 --- a/sys/modules/opus/ui/canvas.lua +++ b/sys/modules/opus/ui/canvas.lua @@ -9,6 +9,7 @@ local colors = _G.colors local Canvas = class() +Canvas.__visualize = false Canvas.colorPalette = { } Canvas.darkPalette = { } Canvas.grayscalePalette = { } @@ -303,7 +304,7 @@ end -- the array. function Canvas:__renderLayers(device, offset) if #self.layers > 0 then - self.regions = self.regions or Region.new(self.x, self.y, self.ex, self.ey) + self.regions = self.regions or Region.new(self.x + offset.x, self.y + offset.y, self.ex + offset.x, self.ey + offset.y) for i = 1, #self.layers do local canvas = self.layers[i] @@ -379,31 +380,29 @@ function Canvas:__punch(rect, offset) rect.ey + offset.y) end +-- performance can probably be improved by using one more buffer tied to the device function Canvas:__blitRect(device, src, tgt) src = src or { x = 1, y = 1, ex = self.ex - self.x + 1, ey = self.ey - self.y + 1 } tgt = tgt or self - --[[ - -- for visualizing updates on the screen - local drew - for i = 0, src.ey - src.y do - local line = self.lines[src.y + i + (self.offy or 0)] - if line and line.dirty then - drew = true - local t, fg, bg = line.text, line.fg, line.bg - if src.x > 1 or src.ex < self.ex then - t = _sub(t, src.x, src.ex) - fg = _rep(1, src.ex-src.x + 1) - bg = _rep(2, src.ex-src.x + 1) + -- for visualizing updates on the screen + if Canvas.__visualize then + local drew + local t = _rep(' ', src.ex-src.x + 1) + local bg = _rep(2, src.ex-src.x + 1) + for i = 0, src.ey - src.y do + local line = self.lines[src.y + i + (self.offy or 0)] + if line and line.dirty then + drew = true + device.setCursorPos(tgt.x, tgt.y + i) + device.blit(t, bg, bg) end - device.setCursorPos(tgt.x, tgt.y + i) - device.blit(t, fg, bg) + end + if drew then + local t = os.clock() + repeat until os.clock()-t > .2 end end - if drew then - os.sleep(.3) - end - ]] for i = 0, src.ey - src.y do local line = self.lines[src.y + i + (self.offy or 0)] if line and line.dirty then diff --git a/sys/modules/opus/ui/components/Chooser.lua b/sys/modules/opus/ui/components/Chooser.lua index 9698f6f..32fd3e2 100644 --- a/sys/modules/opus/ui/components/Chooser.lua +++ b/sys/modules/opus/ui/components/Chooser.lua @@ -14,6 +14,11 @@ UI.Chooser.defaults = { leftIndicator = UI.extChars and '\17' or '<', rightIndicator = UI.extChars and '\16' or '>', height = 1, + accelerators = { + space = 'choice_next', + right = 'choice_next', + left = 'choice_prev', + } } function UI.Chooser:setParent() if not self.width and not self.ex then @@ -29,16 +34,11 @@ function UI.Chooser:setParent() end function UI.Chooser:draw() - local bg = self.backgroundColor - if self.focused then - bg = self.backgroundFocusColor - end + local bg = self.focused and self.backgroundFocusColor or self.backgroundColor local fg = self.inactive and self.textInactiveColor or self.textColor local choice = Util.find(self.choices, 'value', self.value) - local value = self.nochoice - if choice then - value = choice.name - end + local value = choice and choice.name or self.nochoice + self:write(1, 1, self.leftIndicator, self.backgroundColor, colors.black) self:write(2, 1, ' ' .. Util.widthify(tostring(value), self.width-4) .. ' ', bg, fg) self:write(self.width, 1, self.rightIndicator, self.backgroundColor, colors.black) @@ -49,39 +49,37 @@ function UI.Chooser:focus() end function UI.Chooser:eventHandler(event) - if event.type == 'key' then - if event.key == 'right' or event.key == 'space' then - local _,k = Util.find(self.choices, 'value', self.value) - local choice - if not k then k = 0 end - if k and k < #self.choices then - choice = self.choices[k+1] - else - choice = self.choices[1] - end - self.value = choice.value - self:emit({ type = 'choice_change', value = self.value, element = self, choice = choice }) - self:draw() - return true - elseif event.key == 'left' then - local _,k = Util.find(self.choices, 'value', self.value) - local choice - if k and k > 1 then - choice = self.choices[k-1] - else - choice = self.choices[#self.choices] - end - self.value = choice.value - self:emit({ type = 'choice_change', value = self.value, element = self, choice = choice }) - self:draw() - return true + if event.type == 'choice_next' then + local _,k = Util.find(self.choices, 'value', self.value) + local choice + if not k then k = 0 end + if k and k < #self.choices then + choice = self.choices[k+1] + else + choice = self.choices[1] end + self.value = choice.value + self:emit({ type = 'choice_change', value = self.value, element = self, choice = choice }) + self:draw() + return true + elseif event.type == 'choice_prev' then + local _,k = Util.find(self.choices, 'value', self.value) + local choice + if k and k > 1 then + choice = self.choices[k-1] + else + choice = self.choices[#self.choices] + end + self.value = choice.value + self:emit({ type = 'choice_change', value = self.value, element = self, choice = choice }) + self:draw() + return true elseif event.type == 'mouse_click' or event.type == 'mouse_doubleclick' then if event.x == 1 then - self:emit({ type = 'key', key = 'left' }) + self:emit({ type = 'choice_prev' }) return true elseif event.x == self.width then - self:emit({ type = 'key', key = 'right' }) + self:emit({ type = 'choice_next' }) return true end end diff --git a/sys/modules/opus/ui/components/Page.lua b/sys/modules/opus/ui/components/Page.lua new file mode 100644 index 0000000..7fbaa0f --- /dev/null +++ b/sys/modules/opus/ui/components/Page.lua @@ -0,0 +1,163 @@ +local Canvas = require('opus.ui.canvas') +local class = require('opus.class') +local UI = require('opus.ui') +local Util = require('opus.util') + +local colors = _G.colors + +-- need to add offsets to this test +local function getPosition(element) + local x, y = 1, 1 + repeat + x = element.x + x - 1 + y = element.y + y - 1 + element = element.parent + until not element + return x, y +end + +UI.Page = class(UI.Window) +UI.Page.defaults = { + UIElement = 'Page', + accelerators = { + down = 'focus_next', + enter = 'focus_next', + tab = 'focus_next', + ['shift-tab' ] = 'focus_prev', + up = 'focus_prev', + }, + backgroundColor = colors.cyan, + textColor = colors.white, +} +function UI.Page:postInit() + self.parent = self.parent or UI.defaultDevice + self.__target = self + self.canvas = Canvas({ + x = 1, y = 1, width = self.parent.width, height = self.parent.height, + isColor = self.parent.isColor, + }) + self.canvas:clear(self.backgroundColor, self.textColor) +end + +function UI.Page:enable() + self.canvas.visible = true + UI.Window.enable(self) + + if not self.focused or not self.focused.enabled then + self:focusFirst() + end +end + +function UI.Page:disable() + self.canvas.visible = false + UI.Window.disable(self) +end + +function UI.Page:sync() + if self.enabled then + self.parent:sync() + end +end + +function UI.Page:capture(child) + self.__target = child +end + +function UI.Page:release(child) + if self.__target == child then + self.__target = self + end +end + +function UI.Page:pointToChild(x, y) + if self.__target == self then + return UI.Window.pointToChild(self, x, y) + end + local absX, absY = getPosition(self.__target) + + -- this is sketchy + x = x + self.offx - self.x - (absX - self.__target.x) + 1 + y = y + self.offy - self.y - (absY - self.__target.y) + 1 + + return self.__target:pointToChild(x, y) +end + +function UI.Page:getFocusables() + if self.__target == self or self.__target.pageType ~= 'modal' then + return UI.Window.getFocusables(self) + end + return self.__target:getFocusables() +end + +function UI.Page:getFocused() + return self.focused +end + +function UI.Page:focusPrevious() + local function getPreviousFocus(focused) + local focusables = self:getFocusables() + local k = Util.contains(focusables, focused) + if k then + if k > 1 then + return focusables[k - 1] + end + return focusables[#focusables] + end + end + + local focused = getPreviousFocus(self.focused) + if focused then + self:setFocus(focused) + end +end + +function UI.Page:focusNext() + local function getNextFocus(focused) + local focusables = self:getFocusables() + local k = Util.contains(focusables, focused) + if k then + if k < #focusables then + return focusables[k + 1] + end + return focusables[1] + end + end + + local focused = getNextFocus(self.focused) + if focused then + self:setFocus(focused) + end +end + +function UI.Page:setFocus(child) + if not child or not child.focus then + return + end + + if self.focused and self.focused ~= child then + self.focused.focused = false + self.focused:focus() + self.focused:emit({ type = 'focus_lost', focused = child, unfocused = self.focused }) + end + + self.focused = child + if not child.focused then + child.focused = true + child:emit({ type = 'focus_change', focused = child }) + --self:emit({ type = 'focus_change', focused = child }) + end + + child:focus() +end + +function UI.Page:eventHandler(event) + if self.focused then + if event.type == 'focus_next' then + self:focusNext() + return true + elseif event.type == 'focus_prev' then + self:focusPrevious() + return true + end + end +end diff --git a/sys/modules/opus/ui/components/SlideOut.lua b/sys/modules/opus/ui/components/SlideOut.lua index 789220f..479be4b 100644 --- a/sys/modules/opus/ui/components/SlideOut.lua +++ b/sys/modules/opus/ui/components/SlideOut.lua @@ -61,17 +61,14 @@ end function UI.SlideOut.example() -- for the transistion to work properly, the parent must have a canvas 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, + y = 2, button = UI.Button { x = 2, y = 5, text = 'show', }, slideOut = UI.SlideOut { - backgroundColor = colors.yellow, - y = -4, height = 4, + backgroundColor = _G.colors.yellow, + y = -4, height = 4, x = 3, ex = -3, button = UI.Button { x = 2, y = 2, text = 'hide', diff --git a/sys/modules/opus/ui/transition.lua b/sys/modules/opus/ui/transition.lua index 4448760..f8cee0b 100644 --- a/sys/modules/opus/ui/transition.lua +++ b/sys/modules/opus/ui/transition.lua @@ -39,7 +39,7 @@ function Transition.expandUp(args) local easing = args.easing or 'linear' local pos = { y = args.ey + 1 } local tween = Tween.new(ticks, pos, { y = args.y }, easing) - +_syslog(args) args.canvas:move(args.x, pos.y) return function()