diff --git a/sys/apis/ui.lua b/sys/apis/ui.lua index 112bb2d..4f2541f 100644 --- a/sys/apis/ui.lua +++ b/sys/apis/ui.lua @@ -101,7 +101,7 @@ function Manager:init(args) elseif self.currentPage then if not self.currentPage.parent.device.side then local event = self:pointToChild(self.target, x, y) - if event.element.focus then + if event.element.focus and not event.element.inactive then self.currentPage:setFocus(event.element) self.currentPage:sync() end @@ -282,7 +282,7 @@ function Manager:pointToChild(parent, x, y) y = y + parent.offy - parent.y + 1 if parent.children then for _,child in pairs(parent.children) do - if child.enabled and + if child.enabled and not child.inactive and x >= child.x and x < child.x + child.width and y >= child.y and y < child.y + child.height then local c = self:pointToChild(child, x, y) @@ -653,8 +653,6 @@ function UI.Window:setTextScale(textScale) end function UI.Window:clear(bg, ...) - debug(bg) - debug({...}) if self.canvas then self.canvas:clear(bg or self.backgroundColor) else @@ -808,7 +806,7 @@ function UI.Window:getFocusables() local function getFocusable(parent, x, y) for _,child in Util.spairs(parent.children, focusSort) do - if child.enabled and child.focus then + if child.enabled and child.focus and not child.inactive then table.insert(focusable, child) end if child.children then @@ -853,6 +851,22 @@ function UI.Window:scrollIntoView() end end +function UI.Window:getCanvas() + local el = self + repeat + if el.canvas then + return el.canvas + end + el = el.parent + until not el +end + +function UI.Window:addLayer(bg, fg) + local canvas = self:getCanvas() + + return canvas:addLayer(self, bg, fg) +end + function UI.Window:addTransition(effect, args) if self.parent then args = args or { } @@ -2058,6 +2072,88 @@ function UI.TitleBar:eventHandler(event) end end +--[[-- Button --]]-- +UI.Button = class(UI.Window) +UI.Button.defaults = { + UIElement = 'Button', + text = 'button', + backgroundColor = colors.gray, + backgroundFocusColor = colors.lightGray, + textFocusColor = colors.white, + textInactiveColor = colors.gray, + textColor = colors.white, + centered = true, + height = 1, + focusIndicator = ' ', + event = 'button_press', + accelerators = { + space = 'button_activate', + enter = 'button_activate', + mouse_click = 'button_activate', + } +} +function UI.Button:init(args) + local defaults = UI:getDefaults(UI.Button, args) + UI.Window.init(self, defaults) +end + +function UI.Button:setParent() + if not self.width and not self.ex then + self.width = #self.text + 2 + end + UI.Window.setParent(self) +end + +function UI.Button:draw() + local fg = self.textColor + local bg = self.backgroundColor + local ind = ' ' + if self.focused then + bg = self.backgroundFocusColor + fg = self.textFocusColor + ind = self.focusIndicator + elseif self.inactive then + fg = self.textInactiveColor + end + local text = ind .. self.text .. ' ' + if self.centered then + self:clear(bg) + self:centeredWrite(1 + math.floor(self.height / 2), text, bg, fg) + else + self:write(1, 1, Util.widthify(text, self.width), bg, fg) + end +end + +function UI.Button:focus() + if self.focused then + self:scrollIntoView() + end + self:draw() +end + +function UI.Button:eventHandler(event) + if event.type == 'button_activate' then + self:emit({ type = self.event, button = self }) + return true + end + return false +end + +--[[-- MenuItem --]]-- +UI.MenuItem = class(UI.Button) +UI.MenuItem.defaults = { + UIElement = 'MenuItem', + textColor = colors.black, + backgroundColor = colors.lightGray, + textFocusColor = colors.white, + backgroundFocusColor = colors.lightGray, +} + +function UI.MenuItem:init(args) + local defaults = UI:getDefaults(UI.MenuItem, args) + UI.Button.init(self, defaults) +end + --[[-- MenuBar --]]-- UI.MenuBar = class(UI.Window) UI.MenuBar.defaults = { @@ -2068,7 +2164,9 @@ UI.MenuBar.defaults = { textColor = colors.black, spacing = 2, showBackButton = false, + buttonClass = 'MenuItem', } +UI.MenuBar.spacer = { spacer = true, text = 'spacer', inactive = true } function UI.MenuBar:init(args) local defaults = UI:getDefaults(UI.MenuBar, args) @@ -2090,10 +2188,17 @@ function UI.MenuBar:init(args) } x = x + buttonProperties.width UI:setProperties(buttonProperties, button) + + local parent = self + if button.dropdown then + buttonProperties.dropmenu = UI.DropMenu { buttons = button.dropdown } + table.insert(self.children, buttonProperties.dropmenu) + end + if button.name then - self[button.name] = UI.MenuItem(buttonProperties) + self[button.name] = UI[self.buttonClass](buttonProperties) else - table.insert(self.children, UI.MenuItem(buttonProperties)) + table.insert(self.children, UI[self.buttonClass](buttonProperties)) end end end @@ -2110,30 +2215,53 @@ function UI.MenuBar:init(args) UI.Window.init(self, defaults) end +function UI.MenuBar:getActive(menuItem) + return not menuItem.inactive +end + function UI.MenuBar:eventHandler(event) - if event.type == 'dropdown' then - -- better, but still a bad implementation - -- this at least will allow overrides - -- on the button and menubar - if event.button and event.button.dropdown then - local dropdown = self.parent[event.button.dropdown] - if dropdown then - if dropdown.enabled then - dropdown:hide(event.button) - else - dropdown:show(event.button) - end - return true + if event.type == 'button_press' and event.button.dropmenu then + if event.button.dropmenu.enabled then + event.button.dropmenu:hide() + return true + else + local x, y = getPosition(event.button) + if x + event.button.dropmenu.width > self.width then + x = self.width - event.button.dropmenu.width + 1 end + for _,c in pairs(event.button.dropmenu.children) do + if not c.spacer then + c.inactive = not self:getActive(c) + end + end + event.button.dropmenu:show(x, y + 1) end + return true end end +--[[-- DropMenuItem --]]-- +UI.DropMenuItem = class(UI.Button) +UI.DropMenuItem.defaults = { + UIElement = 'DropMenuItem', + textColor = colors.black, + backgroundColor = colors.white, + textFocusColor = colors.black, + textInactiveColor = colors.lightGray, + backgroundFocusColor = colors.lightGray, +} + +function UI.DropMenuItem:init(args) + local defaults = UI:getDefaults(UI.DropMenuItem, args) + UI.Button.init(self, defaults) +end + --[[-- DropMenu --]]-- UI.DropMenu = class(UI.MenuBar) UI.DropMenu.defaults = { UIElement = 'DropMenu', - backgroundColor = colors.lightGray, + backgroundColor = colors.white, + buttonClass = 'DropMenuItem', } function UI.DropMenu:init(args) local defaults = UI:getDefaults(UI.DropMenu, args) @@ -2153,36 +2281,41 @@ function UI.DropMenu:setParent() end for _,child in ipairs(self.children) do child.width = maxWidth + 2 + if child.spacer then + child.text = string.rep('-', child.width - 2) + end end - self.height = #self.children + self.height = #self.children + 1 self.width = maxWidth + 2 self.ow = self.width + + self.canvas = self:addLayer() end function UI.DropMenu:enable() self.enabled = false end -function UI.DropMenu:show(button) -- the x, y should be passed instead of button - self.button = button - self.x, self.y = getPosition(button) - self.y = self.y + 1 - if self.x + self.width > self.parent.width then - self.x = self.parent.width - self.width + 1 - end +function UI.DropMenu:show(x, y) + + self.x, self.y = x, y + self.canvas:move(x, y) + self.canvas:setVisible(true) + self.enabled = true for _,child in ipairs(self.children) do child:enable() end - self:setFocus(self.children[1]) + + self:focusFirst() self:draw() UI:capture(self) end function UI.DropMenu:hide() self:disable() - self.parent:draw() + self.canvas:setVisible(false) UI:release(self) end @@ -2641,85 +2774,6 @@ function UI.VerticalMeter:draw() self:clearArea(1, height + 1, self.width, self.height, self.meterColor) end ---[[-- Button --]]-- -UI.Button = class(UI.Window) -UI.Button.defaults = { - UIElement = 'Button', - text = 'button', - backgroundColor = colors.gray, - backgroundFocusColor = colors.lightGray, - textFocusColor = colors.white, - textColor = colors.white, - centered = true, - height = 1, - focusIndicator = ' ', - event = 'button_press', - accelerators = { - space = 'button_activate', - enter = 'button_activate', - mouse_click = 'button_activate', - } -} -function UI.Button:init(args) - local defaults = UI:getDefaults(UI.Button, args) - UI.Window.init(self, defaults) -end - -function UI.Button:setParent() - if not self.width and not self.ex then - self.width = #self.text + 2 - end - UI.Window.setParent(self) -end - -function UI.Button:draw() - local fg = self.textColor - local bg = self.backgroundColor - local ind = ' ' - if self.focused then - bg = self.backgroundFocusColor - fg = self.textFocusColor - ind = self.focusIndicator - end - local text = ind .. self.text .. ' ' - if self.centered then - self:clear(bg) - self:centeredWrite(1 + math.floor(self.height / 2), text, bg, fg) - else - self:write(1, 1, Util.widthify(text, self.width), bg, fg) - end -end - -function UI.Button:focus() - if self.focused then - self:scrollIntoView() - end - self:draw() -end - -function UI.Button:eventHandler(event) - if event.type == 'button_activate' then - self:emit({ type = self.event, button = self }) - return true - end - return false -end - ---[[-- MenuItem --]]-- -UI.MenuItem = class(UI.Button) -UI.MenuItem.defaults = { - UIElement = 'MenuItem', - textColor = colors.black, - backgroundColor = colors.lightGray, - textFocusColor = colors.white, - backgroundFocusColor = colors.lightGray, -} - -function UI.MenuItem:init(args) - local defaults = UI:getDefaults(UI.MenuItem, args) - UI.Button.init(self, defaults) -end - --[[-- TextEntry --]]-- UI.TextEntry = class(UI.Window) UI.TextEntry.defaults = { diff --git a/sys/apis/ui/canvas.lua b/sys/apis/ui/canvas.lua index c353955..fd69f74 100644 --- a/sys/apis/ui/canvas.lua +++ b/sys/apis/ui/canvas.lua @@ -44,6 +44,12 @@ function Canvas:init(args) end end +function Canvas:move(x, y) + self.x, self.y = x, y + self.ex = self.x + self.width - 1 + self.ey = self.y + self.height - 1 +end + function Canvas:resize(w, h) for i = self.height, h do self.lines[i] = { } diff --git a/sys/apps/Files.lua b/sys/apps/Files.lua index be79e88..05ede9b 100644 --- a/sys/apps/Files.lua +++ b/sys/apps/Files.lua @@ -32,43 +32,31 @@ end local Browser = UI.Page { menuBar = UI.MenuBar { buttons = { - { text = '^-', event = 'updir' }, - { text = 'File', event = 'dropdown', dropdown = 'fileMenu' }, - { text = 'Edit', event = 'dropdown', dropdown = 'editMenu' }, - { text = 'View', event = 'dropdown', dropdown = 'viewMenu' }, + { text = '^-', event = 'updir' }, + { text = 'File', dropdown = { + { text = 'Run', event = 'run' }, + { text = 'Edit e', event = 'edit' }, + { text = 'Shell s', event = 'shell' }, + UI.MenuBar.spacer, + { text = 'Quit q', event = 'quit' }, + } }, + { text = 'Edit', dropdown = { + { text = 'Cut ^x', event = 'cut' }, + { text = 'Copy ^c', event = 'copy' }, + { text = 'Paste ^v', event = 'paste' }, + UI.MenuBar.spacer, + { text = 'Mark m', event = 'mark' }, + { text = 'Unmark all u', event = 'unmark' }, + UI.MenuBar.spacer, + { text = 'Delete del', event = 'delete' }, + } }, + { text = 'View', dropdown = { + { text = 'Refresh r', event = 'refresh' }, + { text = 'Hidden ^h', event = 'toggle_hidden' }, + { text = 'Dir Size ^s', event = 'toggle_dirSize' }, + } }, }, }, - fileMenu = UI.DropMenu { - buttons = { - { text = 'Run', event = 'run' }, - { text = 'Edit e', event = 'edit' }, - { text = 'Shell s', event = 'shell' }, - UI.Text { value = ' ------------ ' }, - { text = 'Quit q', event = 'quit' }, - UI.Text { }, - } - }, - editMenu = UI.DropMenu { - buttons = { - { text = 'Cut ^x', event = 'cut' }, - { text = 'Copy ^c', event = 'copy' }, - { text = 'Paste ^v', event = 'paste' }, - UI.Text { value = ' --------------- ' }, - { text = 'Mark m', event = 'mark' }, - { text = 'Unmark all u', event = 'unmark' }, - UI.Text { value = ' --------------- ' }, - { text = 'Delete del', event = 'delete' }, - UI.Text { }, - } - }, - viewMenu = UI.DropMenu { - buttons = { - { text = 'Refresh r', event = 'refresh' }, - { text = 'Hidden ^h', event = 'toggle_hidden' }, - { text = 'Dir Size ^s', event = 'toggle_dirSize' }, - UI.Text { }, - } - }, grid = UI.ScrollingGrid { columns = { { heading = 'Name', key = 'name' }, @@ -107,6 +95,16 @@ function Browser:enable() self:setFocus(self.grid) end +function Browser.menuBar:getActive(menuItem) + local file = Browser.grid:getSelected() + if file then + if menuItem.event == 'edit' or menuItem.event == 'run' then + return not file.isDir + end + end + return true +end + function Browser.grid:sortCompare(a, b) if self.sortColumn == 'fsize' then return a.size < b.size