add menus

This commit is contained in:
airstruck
2015-11-08 15:06:16 -05:00
parent 21ace2a243
commit 7bcf8ffbda
16 changed files with 442 additions and 99 deletions

View File

@@ -144,7 +144,7 @@
</div> <!-- id="main" --> </div> <!-- id="main" -->
<div id="about"> <div id="about">
<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.3</a></i> <i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.3</a></i>
<i style="float:right;">Last updated 2015-11-04 15:07:53 </i> <i style="float:right;">Last updated 2015-11-06 00:18:26 </i>
</div> <!-- id="about" --> </div> <!-- id="about" -->
</div> <!-- id="container" --> </div> <!-- id="container" -->
</body> </body>

View File

@@ -49,10 +49,16 @@
<div id="content"> <div id="content">
<h1>Class <code>Layout</code></h1> <h1>Class <code>Layout</code></h1>
<p>Layout class.</p> <p>A Layout contains a tree of widgets with a single <code>root</code> widget.</p>
<p> <p>Layouts will resize to fit the window unless a <code>top</code> or <code>left</code>
property is found in the root widget.</p>
</p> <p>Layouts are drawn in the order that they were shown, so the
most recently shown layout shown will always appear on top.</p>
<p>Other events are sent to layouts in the opposite direction,
and are trapped by the first layout that can handle the event
(for example, the topmost layer that is focused or hovered).</p>
<h2><a href="#Functions">Functions</a></h2> <h2><a href="#Functions">Functions</a></h2>
@@ -289,7 +295,7 @@ found, and focuses that widget.
</div> <!-- id="main" --> </div> <!-- id="main" -->
<div id="about"> <div id="about">
<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.3</a></i> <i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.3</a></i>
<i style="float:right;">Last updated 2015-11-04 15:07:53 </i> <i style="float:right;">Last updated 2015-11-06 00:18:26 </i>
</div> <!-- id="about" --> </div> <!-- id="about" -->
</div> <!-- id="container" --> </div> <!-- id="container" -->
</body> </body>

View File

@@ -611,7 +611,7 @@ on the parent widget.
</div> <!-- id="main" --> </div> <!-- id="main" -->
<div id="about"> <div id="about">
<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.3</a></i> <i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.3</a></i>
<i style="float:right;">Last updated 2015-11-04 15:07:53 </i> <i style="float:right;">Last updated 2015-11-06 00:18:26 </i>
</div> <!-- id="about" --> </div> <!-- id="about" -->
</div> <!-- id="container" --> </div> <!-- id="container" -->
</body> </body>

View File

@@ -52,7 +52,7 @@
</tr> </tr>
<tr> <tr>
<td class="name" nowrap><a href="classes/Layout.html">Layout</a></td> <td class="name" nowrap><a href="classes/Layout.html">Layout</a></td>
<td class="summary">Layout class.</td> <td class="summary">A Layout contains a tree of widgets with a single <code>root</code> widget.</td>
</tr> </tr>
<tr> <tr>
<td class="name" nowrap><a href="classes/Widget.html">Widget</a></td> <td class="name" nowrap><a href="classes/Widget.html">Widget</a></td>
@@ -64,7 +64,7 @@
</div> <!-- id="main" --> </div> <!-- id="main" -->
<div id="about"> <div id="about">
<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.3</a></i> <i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.3</a></i>
<i style="float:right;">Last updated 2015-11-04 15:07:53 </i> <i style="float:right;">Last updated 2015-11-06 00:18:26 </i>
</div> <!-- id="about" --> </div> <!-- id="about" -->
</div> <!-- id="container" --> </div> <!-- id="container" -->
</body> </body>

View File

@@ -1,10 +1,6 @@
local Layout = require 'luigi.layout' local Layout = require 'luigi.layout'
local style = { local style = {
mainWindow = {
width = 600,
height = 400,
},
short = { short = {
height = 48, height = 48,
}, },
@@ -36,7 +32,33 @@ local style = {
}, },
} }
local mainForm = { title = "Test window", id = 'mainWindow', type = 'panel', local mainForm = { id = 'mainWindow', type = 'panel',
{ type = 'menu', id = 'menubar', flow = 'x',
{ text = 'File', id = 'menuFile',
{ text = 'Save', id = 'menuFileSave', },
{ text = 'Quit' },
},
{ text = 'Edit',
{ text = 'Cut' },
{ text = 'Copy' },
{ text = 'Paste' },
{ type = 'slider' },
},
{ text = 'View',
{ text = 'Theme',
{ text = 'Light' },
{ text = 'Dark' },
},
{ text = 'Style',
{ text = 'Default' },
},
},
{ text = 'Help',
{ text = 'About Luigi', icon = 'icon/16px/Book.png', key = 'backspace', },
{ text = 'About Luigi Demo', icon = 'icon/16px/Book Red.png' },
{ text = 'Licenses' },
},
},
{ type = 'panel', id = 'toolbar', flow = 'x', { type = 'panel', id = 'toolbar', flow = 'x',
{ type = 'button', id = 'newButton', style = 'toolButton', key = 'z', { type = 'button', id = 'newButton', style = 'toolButton', key = 'z',
icon = 'icon/32px/Blueprint.png' }, icon = 'icon/32px/Blueprint.png' },
@@ -97,7 +119,8 @@ end)
layout:onMove(function (event) layout:onMove(function (event)
local w = event.target local w = event.target
layout.statusbar.text = (w.id or '(unnamed)') .. ' ' .. layout.statusbar.text = (w.type or '(generic) ') ..
(w.id or '(unnamed)') .. ' ' ..
w:getX() .. ', ' .. w:getY() .. ' | ' .. w:getX() .. ', ' .. w:getY() .. ' | ' ..
w:getWidth() .. 'x' .. w:getHeight() w:getWidth() .. 'x' .. w:getHeight()
end) end)
@@ -107,6 +130,14 @@ layout.newButton:onMove(function (event)
return false return false
end) end)
local foo = Layout { float = true, height = 100,
text = 'hello', align = 'center middle', background = {255,0,0}
}
foo:onReshape(function (event)
foo:hide()
end)
layout.newButton:onPress(function (event) layout.newButton:onPress(function (event)
print('creating a new thing!') print('creating a new thing!')
end) end)
@@ -114,6 +145,11 @@ end)
layout.aButton:onPress(function (event) layout.aButton:onPress(function (event)
layout.aButton.font = nil layout.aButton.font = nil
layout.aButton.width = layout.aButton.width + 10 layout.aButton.width = layout.aButton.width + 10
local w = layout.aButton:getWidth()
foo.root.width = w * 2
foo.root.left = layout.aButton:getX() - w
foo.root.top = layout.aButton:getY() - foo.root.height
foo:show()
end) end)
layout.mainCanvas.font = 'font/liberation/LiberationMono-Regular.ttf' layout.mainCanvas.font = 'font/liberation/LiberationMono-Regular.ttf'

View File

@@ -37,7 +37,7 @@ function Font:setWidth (width)
end end
function Font:getLineHeight () function Font:getLineHeight ()
return self.font:getLineHeight() return self.font:getHeight()
end end
function Font:getAscender () function Font:getAscender ()

View File

@@ -23,7 +23,7 @@ local function unhook (item)
item.func = nil item.func = nil
end end
local function hook (host, key, func) local function hook (host, key, func, atEnd)
if not func then if not func then
return return
end end
@@ -32,17 +32,30 @@ local function hook (host, key, func)
hooks[host] = {} hooks[host] = {}
end end
local next = hooks[host][key] local current = hooks[host][key]
local item = { local item = {
next = next, next = not atEnd and current or nil,
unhook = unhook, unhook = unhook,
host = host, host = host,
key = key, key = key,
func = func, func = func,
} }
if next then if atEnd then
next.prev = item if current then
while current.next do
current = current.next
end
current.next = item
item.prev = current
else
hooks[host][key] = item
end
return item
end
if current then
current.prev = item
end end
hooks[host][key] = item hooks[host][key] = item
@@ -54,7 +67,7 @@ function Hooker.unhook (item)
return unhook(item) return unhook(item)
end end
function Hooker.hook (host, key, func) function Hooker.hook (host, key, func, atEnd)
if not wrapped[host] then if not wrapped[host] then
wrapped[host] = {} wrapped[host] = {}
end end
@@ -68,16 +81,19 @@ function Hooker.hook (host, key, func)
local item = hooks[host][key] local item = hooks[host][key]
while item do while item do
local result = item.func(...) local nextItem = item.next
if result ~= nil then if item.func then
return result local result = item.func(...)
if result ~= nil then
return result
end
end end
item = item.next item = nextItem
end end
end end
end end
return hook(host, key, func) return hook(host, key, func, atEnd)
end end
return Hooker return Hooker

View File

@@ -8,62 +8,95 @@ local Input = Base:extend()
local weakValueMeta = { __mode = 'v' } local weakValueMeta = { __mode = 'v' }
function Input:constructor (layout) function Input:constructor () --(layout)
self.layout = layout -- layout = layout
self.pressedWidgets = setmetatable({}, weakValueMeta) self.pressedWidgets = setmetatable({}, weakValueMeta)
self.passedWidgets = setmetatable({}, weakValueMeta) self.passedWidgets = setmetatable({}, weakValueMeta)
end end
function Input:handleDisplay () function Input:handleDisplay (layout)
local root = self.layout.root local root = layout.root
if root then Renderer:render(root) end if root then Renderer:render(root) end
Event.Display:emit(self.layout) Event.Display:emit(layout)
end end
function Input:handleKeyPress (key, x, y) function Input:handleKeyPress (layout, key, x, y)
local widget = self.layout.focusedWidget or self.layout:getWidgetAt(x, y) local widget = layout.focusedWidget or layout:getWidgetAt(x, y)
local hit = true
if not widget then
hit = nil
widget = layout.root
end
local result = widget:bubbleEvent('KeyPress', { local result = widget:bubbleEvent('KeyPress', {
hit = hit,
key = key, x = x, y = y key = key, x = x, y = y
}) })
if result ~= nil then return result end if result ~= nil then return result end
return hit
end end
function Input:handleKeyRelease (key, x, y) function Input:handleKeyRelease (layout, key, x, y)
local widget = self.layout.focusedWidget or self.layout:getWidgetAt(x, y) local widget = layout.focusedWidget or layout:getWidgetAt(x, y)
local hit = true
if not widget then
hit = nil
widget = layout.root
end
local result = widget:bubbleEvent('KeyRelease', { local result = widget:bubbleEvent('KeyRelease', {
hit = hit,
key = key, x = x, y = y key = key, x = x, y = y
}) })
if result ~= nil then return result end if result ~= nil then return result end
return hit
end end
function Input:handleTextInput (text, x, y) function Input:handleTextInput (layout, text, x, y)
local widget = self.layout.focusedWidget or self.layout:getWidgetAt(x, y) local widget = layout.focusedWidget or layout:getWidgetAt(x, y)
local hit = true
if not widget then
hit = nil
widget = layout.root
end
widget:bubbleEvent('TextInput', { widget:bubbleEvent('TextInput', {
hit = hit,
text = text, x = x, y = y text = text, x = x, y = y
}) })
return hit
end end
function Input:handleMove (x, y) function Input:handleMove (layout, x, y)
local widget = self.layout:getWidgetAt(x, y) local widget = layout:getWidgetAt(x, y)
local hit = true
if not widget then
hit = nil
widget = layout.root
end
local previousWidget = self.previousMoveWidget local previousWidget = self.previousMoveWidget
if not widget.hovered then if widget ~= previousWidget then
if previousWidget then if previousWidget then
previousWidget.hovered = nil for ancestor in previousWidget:eachAncestor(true) do
ancestor.hovered = nil
end
end
for ancestor in widget:eachAncestor(true) do
ancestor.hovered = true
end end
widget.hovered = true
end end
widget:bubbleEvent('Move', { widget:bubbleEvent('Move', {
hit = hit,
oldTarget = previousWidget, oldTarget = previousWidget,
x = x, y = y x = x, y = y
}) })
if widget ~= previousWidget then if widget ~= previousWidget then
if previousWidget then if previousWidget then
previousWidget:bubbleEvent('Leave', { previousWidget:bubbleEvent('Leave', {
hit = hit,
newTarget = widget, newTarget = widget,
x = x, y = y x = x, y = y
}) })
end end
widget:bubbleEvent('Enter', { widget:bubbleEvent('Enter', {
hit = hit,
oldTarget = previousWidget, oldTarget = previousWidget,
x = x, y = y x = x, y = y
}) })
@@ -74,21 +107,29 @@ function Input:handleMove (x, y)
end end
self.previousMoveWidget = widget self.previousMoveWidget = widget
end end
return hit
end end
function Input:handlePressedMove (x, y) function Input:handlePressedMove (layout, x, y)
local widget = self.layout:getWidgetAt(x, y) local widget = layout:getWidgetAt(x, y)
local hit = true
if not widget then
hit = nil
widget = layout.root
end
for button = 1, 3 do for button = 1, 3 do
local originWidget = self.pressedWidgets[button] local originWidget = self.pressedWidgets[button]
local passedWidget = self.passedWidgets[button] local passedWidget = self.passedWidgets[button]
if originWidget then if originWidget then
originWidget:bubbleEvent('PressDrag', { originWidget:bubbleEvent('PressDrag', {
hit = hit,
newTarget = widget, newTarget = widget,
button = button, button = button,
x = x, y = y x = x, y = y
}) })
if (widget == passedWidget) then if (widget == passedWidget) then
widget:bubbleEvent('PressMove', { widget:bubbleEvent('PressMove', {
hit = hit,
origin = originWidget, origin = originWidget,
button = button, button = button,
x = x, y = y x = x, y = y
@@ -97,6 +138,7 @@ function Input:handlePressedMove (x, y)
originWidget.pressed = (widget == originWidget) or nil originWidget.pressed = (widget == originWidget) or nil
if passedWidget then if passedWidget then
passedWidget:bubbleEvent('PressLeave', { passedWidget:bubbleEvent('PressLeave', {
hit = hit,
newTarget = widget, newTarget = widget,
origin = originWidget, origin = originWidget,
button = button, button = button,
@@ -104,42 +146,58 @@ function Input:handlePressedMove (x, y)
}) })
end end
widget:bubbleEvent('PressEnter', { widget:bubbleEvent('PressEnter', {
oldTarget = passedWidget, hit = hit,
origin = originWidget, oldTarget = passedWidget,
button = button, origin = originWidget,
x = x, y = y button = button,
}) x = x, y = y
})
self.passedWidgets[button] = widget self.passedWidgets[button] = widget
end end
end end
end end
return hit
end end
function Input:handlePressStart (button, x, y, widget, accelerator) function Input:handlePressStart (layout, button, x, y, widget, accelerator)
local widget = widget or self.layout:getWidgetAt(x, y) local widget = widget or layout:getWidgetAt(x, y)
local hit = true
if not widget then
hit = nil
widget = layout.root
end
widget.pressed = true widget.pressed = true
self.pressedWidgets[button] = widget self.pressedWidgets[button] = widget
self.passedWidgets[button] = widget self.passedWidgets[button] = widget
widget:focus() widget:focus()
widget:bubbleEvent('PressStart', { widget:bubbleEvent('PressStart', {
hit = hit,
button = button, button = button,
accelerator = accelerator, accelerator = accelerator,
x = x, y = y x = x, y = y
}) })
return hit
end end
function Input:handlePressEnd (button, x, y, widget, accelerator) function Input:handlePressEnd (layout, button, x, y, widget, accelerator)
local widget = widget or self.layout:getWidgetAt(x, y) local widget = widget or layout:getWidgetAt(x, y)
local hit = true
if not widget then
hit = nil
widget = layout.root
end
local originWidget = self.pressedWidgets[button] local originWidget = self.pressedWidgets[button]
if not originWidget then return end if not originWidget then return end
originWidget.pressed = nil originWidget.pressed = nil
widget:bubbleEvent('PressEnd', { widget:bubbleEvent('PressEnd', {
hit = hit,
origin = originWidget, origin = originWidget,
accelerator = accelerator, accelerator = accelerator,
button = button, x = x, y = y button = button, x = x, y = y
}) })
if (widget == originWidget) then if (widget == originWidget) then
widget:bubbleEvent('Press', { widget:bubbleEvent('Press', {
hit = hit,
button = button, button = button,
accelerator = accelerator, accelerator = accelerator,
x = x, y = y x = x, y = y
@@ -147,13 +205,24 @@ function Input:handlePressEnd (button, x, y, widget, accelerator)
end end
self.pressedWidgets[button] = nil self.pressedWidgets[button] = nil
self.passedWidgets[button] = nil self.passedWidgets[button] = nil
return hit
end end
function Input:handleReshape (width, height) function Input:handleReshape (layout, width, height)
local root = self.layout.root local root = layout.root
Event.Reshape:emit(layout, {
target = layout
})
if root.float then
return
end
root.width = width root.width = width
root.height = height root.height = height
end end
Input.default = Input()
return Input return Input

View File

@@ -1,5 +1,15 @@
--[[-- --[[--
Layout class. A Layout contains a tree of widgets with a single `root` widget.
Layouts will resize to fit the window unless a `top` or `left`
property is found in the root widget.
Layouts are drawn in the order that they were shown, so the
most recently shown layout shown will always appear on top.
Other events are sent to layouts in the opposite direction,
and are trapped by the first layout that can handle the event
(for example, the topmost layer that is focused or hovered).
@classmod Layout @classmod Layout
--]]-- --]]--
@@ -15,6 +25,8 @@ local Hooker = require(ROOT .. 'hooker')
local Layout = Base:extend() local Layout = Base:extend()
Layout.isLayout = true
--[[-- --[[--
Layout constructor. Layout constructor.
@@ -32,8 +44,7 @@ function Layout:constructor (data)
self:setStyle() self:setStyle()
self:setTheme(require(ROOT .. 'theme.light')) self:setTheme(require(ROOT .. 'theme.light'))
self.isMousePressed = false self.isShown = false
self.isManagingInput = false
self.hooks = {} self.hooks = {}
self.root = data or {} self.root = data or {}
Widget(self, self.root) Widget(self, self.root)
@@ -71,20 +82,17 @@ Show the layout.
Hooks all appropriate Love events and callbacks. Hooks all appropriate Love events and callbacks.
--]]-- --]]--
function Layout:show () function Layout:show ()
if self.isShown then
self:unhook() -- return
self.isShown = nil
end
local root = self.root local root = self.root
local width = root.width
local height = root.height
local title = root.title
if not self.input then if not self.input then
self.input = Input(self) self.input = Input.default -- Input(self)
end end
local currentWidth, currentHeight, flags = love.window.getMode() self:manageInput()
love.window.setMode(width or currentWidth, height or currentHeight, flags)
if title then
love.window.setTitle(title)
end
self:manageInput(self.input)
root:reshape() root:reshape()
end end
@@ -94,10 +102,10 @@ Hide the layout.
Unhooks Love events and callbacks. Unhooks Love events and callbacks.
--]]-- --]]--
function Layout:hide () function Layout:hide ()
if not self.isManagingInput then if not self.isShown then
return return
end end
self.isManagingInput = false self.isShown = nil
self:unhook() self:unhook()
end end
@@ -163,7 +171,7 @@ function Layout:getWidgetAt (x, y, root)
if inner then return inner end if inner then return inner end
end end
if widget:isAt(x, y) then return widget end if widget:isAt(x, y) then return widget end
if widget == self.root then return widget end -- if widget == self.root then return widget end
end end
-- Internal, called from Widget:new -- Internal, called from Widget:new
@@ -233,8 +241,8 @@ end
-- event stuff -- event stuff
function Layout:hook (key, method) function Layout:hook (key, method, hookLast)
self.hooks[#self.hooks + 1] = Hooker.hook(love, key, method) self.hooks[#self.hooks + 1] = Hooker.hook(love, key, method, hookLast)
end end
function Layout:unhook () function Layout:unhook ()
@@ -244,7 +252,7 @@ function Layout:unhook ()
self.hooks = {} self.hooks = {}
end end
local getMouseButtonId local getMouseButtonId, isMouseDown
if love._version_minor < 10 then if love._version_minor < 10 then
getMouseButtonId = function (value) getMouseButtonId = function (value)
@@ -252,47 +260,56 @@ if love._version_minor < 10 then
or value == 'r' and 2 or value == 'r' and 2
or value == 'm' and 3 or value == 'm' and 3
end end
isMouseDown = function ()
return love.mouse.isDown('l', 'r', 'm')
end
else else
getMouseButtonId = function (value) getMouseButtonId = function (value)
return value return value
end end
isMouseDown = function ()
return love.mouse.isDown(1, 2, 3)
end
end end
function Layout:manageInput (input) function Layout:manageInput ()
if self.isManagingInput then if self.isShown then
return return
end end
self.isManagingInput = true self.isShown = true
local input = self.input
self:hook('draw', function () self:hook('draw', function ()
input:handleDisplay() input:handleDisplay(self)
end) end, true)
self:hook('resize', function (width, height) self:hook('resize', function (width, height)
return input:handleReshape(width, height) return input:handleReshape(self, width, height)
end) end)
self:hook('mousepressed', function (x, y, button) self:hook('mousepressed', function (x, y, button)
self.isMousePressed = true return input:handlePressStart(self, getMouseButtonId(button), x, y)
return input:handlePressStart(getMouseButtonId(button), x, y)
end) end)
self:hook('mousereleased', function (x, y, button) self:hook('mousereleased', function (x, y, button)
self.isMousePressed = false return input:handlePressEnd(self, getMouseButtonId(button), x, y)
return input:handlePressEnd(getMouseButtonId(button), x, y)
end) end)
self:hook('mousemoved', function (x, y, dx, dy) self:hook('mousemoved', function (x, y, dx, dy)
if self.isMousePressed then if isMouseDown() then
return input:handlePressedMove(x, y) return input:handlePressedMove(self, x, y)
else else
return input:handleMove(x, y) return input:handleMove(self, x, y)
end end
end) end)
self:hook('keypressed', function (key, isRepeat) self:hook('keypressed', function (key, isRepeat)
return input:handleKeyPress(key, love.mouse.getX(), love.mouse.getY()) return input:handleKeyPress(
self, key, love.mouse.getX(), love.mouse.getY())
end) end)
self:hook('keyreleased', function (key) self:hook('keyreleased', function (key)
return input:handleKeyRelease(key, love.mouse.getX(), love.mouse.getY()) return input:handleKeyRelease(
self, key, love.mouse.getX(), love.mouse.getY())
end) end)
self:hook('textinput', function (text) self:hook('textinput', function (text)
return input:handleTextInput(text, love.mouse.getX(), love.mouse.getY()) return input:handleTextInput(
self, text, love.mouse.getX(), love.mouse.getY())
end) end)
end end

View File

@@ -69,7 +69,9 @@ function Style:eachName (object)
end end
return function () return function ()
if not checkLookupProp() then return end if not checkLookupProp() then return end
local specialName = getSpecialName { 'pressed', 'focused', 'hovered' } local specialName = getSpecialName {
'pressed', 'focused', 'hovered', 'active',
}
if specialName then return specialName end if specialName then return specialName end
lookupPropIndex = lookupPropIndex + 1 lookupPropIndex = lookupPropIndex + 1
return lookupProp[lookupPropIndex] return lookupProp[lookupPropIndex]

View File

@@ -25,6 +25,21 @@ return function (config)
button_pressed = { button_pressed = {
slices = RESOURCE .. 'button_pressed.png', slices = RESOURCE .. 'button_pressed.png',
}, },
menu = {
height = 24,
},
['menu.item'] = {
padding = 4,
align = 'left middle',
},
['menu.item_active'] = {
background = highlight,
},
submenu = {
padding = 10,
margin = -10,
slices = RESOURCE .. 'submenu.png',
},
sash = { sash = {
background = lineColor background = lineColor
}, },
@@ -46,7 +61,7 @@ return function (config)
minwidth = 24, minwidth = 24,
minheight = 24 minheight = 24
}, },
progressInner = { ['progress.bar'] = {
slices = RESOURCE .. 'progress.png', slices = RESOURCE .. 'progress.png',
padding = 0, padding = 0,
minwidth = 12, minwidth = 12,

Binary file not shown.

After

Width:  |  Height:  |  Size: 786 B

View File

@@ -17,6 +17,8 @@ Widget.isWidget = true
Widget.typeDecorators = { Widget.typeDecorators = {
button = require(ROOT .. 'widget.button'), button = require(ROOT .. 'widget.button'),
menu = require(ROOT .. 'widget.menu'),
['menu.item'] = require(ROOT .. 'widget.menu.item'),
progress = require(ROOT .. 'widget.progress'), progress = require(ROOT .. 'widget.progress'),
sash = require(ROOT .. 'widget.sash'), sash = require(ROOT .. 'widget.sash'),
slider = require(ROOT .. 'widget.slider'), slider = require(ROOT .. 'widget.slider'),
@@ -55,14 +57,14 @@ local function metaNewIndex (self, property, value)
end end
if property == 'width' then if property == 'width' then
value = math.max(value, self.minwidth or 0) value = value and math.max(value, self.minwidth or 0)
self.shadowProperties[property] = value self.shadowProperties[property] = value
Widget.reshape(self.parent or self) Widget.reshape(self.parent or self)
return return
end end
if property == 'height' then if property == 'height' then
value = math.max(value, self.minheight or 0) value = value and math.max(value, self.minheight or 0)
self.shadowProperties[property] = value self.shadowProperties[property] = value
Widget.reshape(self.parent or self) Widget.reshape(self.parent or self)
return return
@@ -89,7 +91,7 @@ A Widget instance.
local function metaCall (Widget, layout, self) local function metaCall (Widget, layout, self)
self = self or {} self = self or {}
self.layout = layout self.layout = layout
self.children = {} self.children = self.children or {}
self.position = { x = nil, y = nil } self.position = { x = nil, y = nil }
self.dimensions = { width = nil, height = nil } self.dimensions = { width = nil, height = nil }
self.shadowProperties = {} self.shadowProperties = {}
@@ -295,6 +297,7 @@ function Widget:addChild (data)
table.insert(self.children, child) table.insert(self.children, child)
child.parent = self child.parent = self
child.layout = self.layout
return child return child
end end
@@ -375,8 +378,9 @@ function Widget:calculatePosition (axis)
end end
local parent = self.parent local parent = self.parent
if not parent then if not parent then
self.position[axis] = 0 self.position[axis] = axis == 'x' and (self.left or 0)
return 0 or axis == 'y' and (self.top or 0)
return self.position[axis]
end end
local parentPos = parent:calculatePosition(axis) local parentPos = parent:calculatePosition(axis)
local p = parentPos local p = parentPos
@@ -555,7 +559,7 @@ function Widget:isAt (x, y)
checkReshape(self) checkReshape(self)
local x1, y1, x2, y2 = self:getRectangle() local x1, y1, x2, y2 = self:getRectangle()
return (x1 < x) and (x2 > x) and (y1 < y) and (y2 > y) return (x1 <= x) and (x2 >= x) and (y1 <= y) and (y2 >= y)
end end
function Widget:eachAncestor (includeSelf) function Widget:eachAncestor (includeSelf)

9
luigi/widget/menu.lua Normal file
View File

@@ -0,0 +1,9 @@
return function (self)
for index, child in ipairs(self) do
child.type = child.type or 'menu.item'
child.parentMenu = self
child.rootMenu = self
end
end

169
luigi/widget/menu/item.lua Normal file
View File

@@ -0,0 +1,169 @@
local ROOT = (...):gsub('[^.]*.[^.]*.[^.]*$', '')
local Layout
local show
local function deactivateSiblings (target)
local sibling = target.parent and target.parent.children[1]
local wasSiblingOpen
if not sibling then
return
end
while sibling do
local layout = sibling.menuLayout
sibling.active = nil
if layout and layout.isShown then
wasSiblingOpen = true
layout:hide()
end
if sibling.items and sibling.items[1] then
deactivateSiblings(sibling.items[1])
end
sibling = sibling:getNextSibling()
end
return wasSiblingOpen
end
local function activate (event, ignoreIfNoneOpen)
local target = event.target
while target.parent
and target.parent.type ~= 'menu' and target.parent.type ~= 'submenu' do
target = target.parent
if not target then
return
end
end
local wasSiblingOpen = deactivateSiblings(target)
local ignore = ignoreIfNoneOpen and not wasSiblingOpen
if not ignore then
show(target)
target.active = true
end
end
show = function (self)
if not self.items or #self.items < 1 then
return
end
if self.menuLayout then
self.menuLayout:show()
return
end
local Layout = Layout or require(ROOT .. 'layout')
local isSubmenu = self.parentMenu and self.parentMenu.parentMenu
local x = isSubmenu and self:getWidth() or 0
local y = isSubmenu and 0 or self:getHeight()
local menuLayout = Layout {
type = 'submenu',
left = self:getX() + x,
top = self:getY() + y,
width = 0,
height = 0,
}
local root = menuLayout.root
local rootPad = root.padding or 0
local textWidth = 0
local keyWidth = 0
for index, child in ipairs(self.items) do
child.type = child.type or 'menu.item'
root:addChild(child)
local h = child:getHeight()
root.height = root:getHeight() + h
if child.type == 'menu.item' then
local pad = child.padding or 0
local tw = child.fontData:getAdvance(child.children[2].text)
+ pad * 2 + h
local kw = child.fontData:getAdvance(child.children[3].text)
+ pad * 2
textWidth = math.max(textWidth, tw)
keyWidth = math.max(keyWidth, kw)
end
end
root.width = textWidth + keyWidth + rootPad
menuLayout:onReshape(function (event)
menuLayout:hide()
deactivateSiblings(self.rootMenu.children[1])
end)
menuLayout:onPressStart(function (event)
if not event.hit then
menuLayout:hide()
deactivateSiblings(self.rootMenu.children[1])
end
activate(event)
end)
menuLayout:onEnter(activate)
menuLayout:onPressEnter(activate)
menuLayout:show()
self.menuLayout = menuLayout
end
local function extractChild (self, index, child)
self[index] = nil
self.items[#self.items + 1] = child
child.parentMenu = self
child.rootMenu = self.rootMenu
child.type = child.type or 'menu.item'
end
return function (self)
local pad = self.padding or 0
local isSubmenu = self.parentMenu and self.parentMenu.parentMenu
local text, key, icon = self.text or '', self.key or '', self.icon
local textWidth = self.fontData:getAdvance(text) + pad * 2
self.items = self.items or {}
for index, child in ipairs(self) do
extractChild(self, index, child)
end
if isSubmenu then
self.height = self.fontData:getLineHeight() + pad * 2
self.flow = 'x'
self:addChild({ icon = icon, width = self.height })
self:addChild({ text = text, width = textWidth })
self:addChild({ text = key, align = 'right', minwidth = self.height })
self.icon = nil
self.text = nil
else
self.width = textWidth
end
self:onPressStart(activate)
self:onEnter(function (event)
activate(event, true)
end)
self:onPressEnter(function (event)
activate(event, true)
end)
end

View File

@@ -3,7 +3,7 @@ return function (self)
self.flow = 'x' -- TODO: support vertical progress? self.flow = 'x' -- TODO: support vertical progress?
local bar = self:addChild { local bar = self:addChild {
type = 'progressInner', type = 'progress.bar',
width = 0, width = 0,
} }