context menus working

This commit is contained in:
airstruck
2015-12-24 09:42:04 -05:00
parent 2d0dec2c70
commit 3abcce1615
9 changed files with 148 additions and 61 deletions

View File

@@ -40,13 +40,12 @@ return { id = 'mainWindow',
}, },
{ flow = 'x', { flow = 'x',
{ id = 'leftSideBox', minwidth = 200, width = 200, scroll = true, type = 'panel', { id = 'leftSideBox', minwidth = 200, width = 200, scroll = true, type = 'panel',
{ style = 'listThing', align = 'middle center', context = {
text = 'Try the scroll wheel on this area.', { text = 'Use sans-serif font', id = 'sans' },
context = { { text = 'Use monospace font', id = 'mono' }
{ text = 'Use sans-serif font', id = 'sans' },
{ text = 'Use monospace font' }
}
}, },
{ style = 'listThing', align = 'middle center',
text = 'Try the scroll wheel on this area.', },
{ style = 'listThing', align = 'middle center', { style = 'listThing', align = 'middle center',
text = 'This text is centered, and in the middle vertically.' }, text = 'This text is centered, and in the middle vertically.' },
{ style = 'listThing', align = 'middle left', { style = 'listThing', align = 'middle left',
@@ -65,11 +64,15 @@ return { id = 'mainWindow',
{ id = 'flowTest', height = 'auto', minheight = 128, { id = 'flowTest', height = 'auto', minheight = 128,
{ {
{ type = 'label', text = 'Slider' }, { type = 'label', text = 'Slider' },
{ type = 'slider', id = 'slidey', width = false }, { type = 'slider', id = 'slidey', width = false, },
}, },
{ {
{ type = 'label', text = 'Stepper' }, { type = 'label', text = 'Stepper' },
{ type = 'stepper', id = 'stepper', width = false, wrap = true, { type = 'stepper', id = 'stepper', width = false, wrap = true,
context = {
{ text = 'Use sans-serif font', id = 'sans2' },
{ text = 'Use monospace font', id = 'mono2' }
},
{ value = 1, text = 'Thing One' }, { value = 1, text = 'Thing One' },
{ value = 2, text = 'Thing Two' }, { value = 2, text = 'Thing Two' },
{ value = 3, text = 'Thing Three' }, { value = 3, text = 'Thing Three' },

View File

@@ -80,6 +80,22 @@ end)
local Backend = require 'luigi.backend' local Backend = require 'luigi.backend'
layout.menuQuit:onPress(Backend.quit) layout.menuQuit:onPress(Backend.quit)
layout.mono:onPress(function()
layout.leftSideBox.font = 'font/DejaVuSansMono.ttf'
end)
layout.sans:onPress(function()
layout.leftSideBox.font = false
end)
layout.mono2:onPress(function()
layout.stepper.font = 'font/DejaVuSansMono.ttf'
end)
layout.sans2:onPress(function()
layout.stepper.font = false
end)
-- show the main layout -- show the main layout
layout:show() layout:show()

View File

@@ -54,10 +54,10 @@ window.restore:onPress(function ()
window.root.maximized = false window.root.maximized = false
end) end)
window:onReshape(function (event) window:onReshape(function (event)
local w, h = Backend:getWindowSize() -- local w, h = Backend:getWindowSize()
-- use widget.attributes to do a raw update, avoid firing onChange -- use widget.attributes to do a raw update, avoid firing onChange
window.width.attributes.value = tostring(w) window.width.attributes.value = tostring(event.width)
window.height.attributes.value = tostring(h) window.height.attributes.value = tostring(event.height)
end) end)
window:onChange(function (event) window:onChange(function (event)
local target = event.target local target = event.target

View File

@@ -16,9 +16,9 @@ local ROOT = (...):gsub('[^.]*$', '')
local Attribute = {} local Attribute = {}
local function cascade (widget, attribute) local function cascade (widget, attribute)
local value = widget.attributes[attribute] local value = rawget(widget, 'attributes')[attribute]
if value ~= nil then return value end if value ~= nil then return value end
local parent = widget.parent local parent = rawget(widget, 'parent')
return parent and parent[attribute] return parent and parent[attribute]
end end
@@ -117,6 +117,37 @@ function Attribute.value.set (widget, value)
widget:bubbleEvent('Change', { value = value, oldValue = oldValue }) widget:bubbleEvent('Change', { value = value, oldValue = oldValue })
end end
--[[--
Context menu.
- This attribute cascades.
@attrib context
--]]--
Attribute.context = {}
function Attribute.context.set (widget, value)
widget.attributes.context = value
for i = #widget, 1, -1 do
local child = widget[i]
if child.isContextMenu then
table.remove(widget, i)
end
end
if not value then return end
widget:addChild {
type = 'menu',
isContextMenu = true,
width = 0,
height = 0,
value
}
value.menuLayout.isContextMenu = true
widget.contextMenu = value
end
Attribute.context.get = cascade
--[[-- --[[--
Widget style. Widget style.
@@ -356,10 +387,23 @@ this widget's `text`.
--]]-- --]]--
Attribute.font = {} Attribute.font = {}
local function resetFont (widget)
rawset(widget, 'fontData', nil)
rawset(widget, 'textData', nil)
for _, child in ipairs(widget) do
resetFont(child)
end
local items = widget.items
if items then
for _, child in ipairs(items) do
resetFont(child)
end
end
end
function Attribute.font.set (widget, value) function Attribute.font.set (widget, value)
widget.attributes.font = value widget.attributes.font = value
widget.fontData = nil resetFont(widget)
widget.textData = nil
end end
Attribute.font.get = cascade Attribute.font.get = cascade

View File

@@ -8,8 +8,7 @@ local Input = Base:extend()
local weakValueMeta = { __mode = 'v' } local weakValueMeta = { __mode = 'v' }
function Input:constructor () --(layout) function Input:constructor ()
-- layout = layout
self.pressedWidgets = setmetatable({}, weakValueMeta) self.pressedWidgets = setmetatable({}, weakValueMeta)
self.passedWidgets = setmetatable({}, weakValueMeta) self.passedWidgets = setmetatable({}, weakValueMeta)
end end
@@ -225,11 +224,14 @@ function Input:handleReshape (layout, width, height)
end end
end end
Event.Reshape:emit(layout, { target = layout }) Event.Reshape:emit(layout, {
target = layout,
width = width,
height = height
})
end end
function Input:handleWheelMove (layout, x, y) function Input:handleWheelMove (layout, x, y)
local root = layout.root
local mx, my = Backend.getMousePosition() local mx, my = Backend.getMousePosition()
local widget = layout:getWidgetAt(mx, my) local widget = layout:getWidgetAt(mx, my)
local hit = true local hit = true
@@ -239,10 +241,7 @@ function Input:handleWheelMove (layout, x, y)
widget = layout.root widget = layout.root
end end
widget:bubbleEvent('WheelMove', { widget:bubbleEvent('WheelMove', { hit = hit, x = x, y = y })
hit = hit,
x = x, y = y
})
return hit return hit
end end

View File

@@ -66,18 +66,20 @@ local function clearWidget (widget)
widget.position = {} widget.position = {}
widget.dimensions = {} widget.dimensions = {}
widget.type = widget.type widget.type = widget.type
for _, child in ipairs(widget) do
clearWidget(child)
end
local items = widget.items
if items then
for _, item in ipairs(items) do
clearWidget(item)
end
end
end end
local function reset (self) local function reset (self)
if not self.root then return end if not self.root then return end
local widget = self.root:getNextNeighbor()
clearWidget(self.root) clearWidget(self.root)
while widget ~= self.root do
clearWidget(widget)
widget = widget:getNextNeighbor()
end
end end
--[[-- --[[--
@@ -266,24 +268,32 @@ function Layout:addDefaultHandlers ()
end end
self:onPressStart(function (event) self:onPressStart(function (event)
-- show context menu on right click
if event.button ~= 'right' then return end if event.button ~= 'right' then return end
local menu = event.target.contextMenu local menu = event.target.context
if not menu then if not menu then return end
local context = event.target.context menu:bubbleEvent('PressStart', event)
if not context then return end -- make sure it fits in the window
context.text = 'foo' -- TODO: open in a new borderless window under SDL?
menu = event.target:addChild { local windowWidth, windowHeight = Backend.getWindowSize()
type = 'menu', local left, top = event.x, event.y
width = 0, local root = menu.menuLayout.root
height = 0, -- place context menu left of cursor if there's no room to the right
context local layoutWidth = root:getWidth()
} if left + layoutWidth > windowWidth then
menu[1].menuLayout.isContextMenu = true left = left - layoutWidth - 1
event.target.contextMenu = menu else
left = left + 1
end end
menu[1]:bubbleEvent('PressStart', event) -- place context menu above cursor if there's no room below
menu[1].menuLayout.root.left = event.x + 1 local layoutHeight = root:getHeight()
menu[1].menuLayout.root.top = event.y + 1 if top + layoutHeight > windowHeight then
top = top - layoutHeight - 1
else
top = top + 1
end
root.left = left
root.top = top
return false return false
end) end)

View File

@@ -753,7 +753,7 @@ fires a Reshape event (does not bubble). Called recursively for each child.
When setting a widget's width or height, this function is automatically called When setting a widget's width or height, this function is automatically called
on the parent widget. on the parent widget.
--]]-- --]]--
function Widget:reshape (keepText) function Widget:reshape ()
if self.isReshaping then return end if self.isReshaping then return end
self.isReshaping = true self.isReshaping = true
@@ -762,12 +762,20 @@ function Widget:reshape (keepText)
self.position = {} self.position = {}
self.dimensions = {} self.dimensions = {}
if not keepText then self.textData = nil end self.textData = nil
Event.Reshape:emit(self, { target = self }) Event.Reshape:emit(self, { target = self })
for i, widget in ipairs(self) do for _, child in ipairs(self) do
if widget.reshape then if child.reshape then
widget:reshape(keepText) child:reshape()
end
end
local items = self.items
if items then
for _, child in ipairs(items) do
if child.reshape then
child:reshape()
end
end end
end end
self.isReshaping = nil self.isReshaping = nil
@@ -783,7 +791,7 @@ function Widget:scrollBy (x, y)
scrollY = math.max(math.min(scrollY, maxY), 0) scrollY = math.max(math.min(scrollY, maxY), 0)
if scrollY ~= self.scrollY then if scrollY ~= self.scrollY then
self.scrollY = scrollY self.scrollY = scrollY
self:reshape(true) self:reshape()
return true return true
end end
end end

View File

@@ -39,10 +39,11 @@ local function addLayoutChildren (self)
local childHeight = child:getHeight() local childHeight = child:getHeight()
height = height + childHeight height = height + childHeight
if child.type == 'menu.item' then if child.type == 'menu.item' then
local font = child:getFont()
local pad = child.padding or 0 local pad = child.padding or 0
local tw = child.fontData:getAdvance(child[2].text) local tw = font:getAdvance(child[2].text)
+ pad * 2 + childHeight + pad * 2 + childHeight
local kw = child.fontData:getAdvance(child[3].text) local kw = font:getAdvance(child[3].text)
+ pad * 2 + childHeight + pad * 2 + childHeight
textWidth = math.max(textWidth, tw) textWidth = math.max(textWidth, tw)
keyWidth = math.max(keyWidth, kw) keyWidth = math.max(keyWidth, kw)
@@ -164,17 +165,13 @@ local function registerLayoutEvents (self)
end end
local function initialize (self) local function initialize (self)
if not self.fontData then local font = self:getFont()
self.fontData = Backend.Font(self.font, self.size)
end
local pad = self.padding or 0 local pad = self.padding or 0
local isSubmenu = self.parentMenu and self.parentMenu.parentMenu local isSubmenu = self.parentMenu and self.parentMenu.parentMenu
local text, key, icon = self.text or '', self.key or '', self.icon local text, key, icon = self.text or '', self.key or '', self.icon
local textWidth = self.fontData:getAdvance(text) + pad * 2 local textWidth = font:getAdvance(text) + pad * 2
if isSubmenu then if isSubmenu then
local tc = self.color or { 0, 0, 0, 255 }
local keyColor = { tc[1], tc[2], tc[3], 0x90 }
local edgeType local edgeType
if #self.items > 0 then if #self.items > 0 then
key = ' ' key = ' '
@@ -182,7 +179,7 @@ local function initialize (self)
else else
key = key:gsub('%f[%w].', string.upper) -- :gsub('-', '+') key = key:gsub('%f[%w].', string.upper) -- :gsub('-', '+')
end end
self.height = self.fontData:getLineHeight() + pad * 2 self.height = font:getLineHeight() + pad * 2
self.flow = 'x' self.flow = 'x'
self:addChild { icon = icon, width = self.height } self:addChild { icon = icon, width = self.height }
self:addChild { text = text, width = textWidth } self:addChild { text = text, width = textWidth }
@@ -234,6 +231,7 @@ local function createLayout (self)
end end
return function (self) return function (self)
self.context = false
extractChildren(self) extractChildren(self)
initialize(self) initialize(self)
registerEvents(self) registerEvents(self)

View File

@@ -43,15 +43,24 @@ Contains the index in `items` of the item being displayed.
self.index = 1 self.index = 1
self.flow = 'x' -- TODO: support vertical stepper self.flow = 'x' -- TODO: support vertical stepper
local contextMenu
for index, child in ipairs(self) do for index, child in ipairs(self) do
child.type = child.type or 'stepper.item' child.type = child.type or 'stepper.item'
self.items[index] = child if child.isContextMenu then
contextMenu = child
else
self.items[index] = child
end
self[index] = nil self[index] = nil
end end
local before = self:addChild { type = 'stepper.before' } local before = self:addChild { type = 'stepper.before' }
local view = self:addChild { type = 'stepper.view' } local view = self:addChild { type = 'stepper.view' }
local after = self:addChild { type = 'stepper.after' } local after = self:addChild { type = 'stepper.after' }
if contextMenu then
self:addChild(contextMenu)
end
self:onReshape(function (event) self:onReshape(function (event)
if self.flow == 'x' then if self.flow == 'x' then