mirror of
https://github.com/airstruck/luigi.git
synced 2026-01-09 15:58:22 +00:00
context menus working
This commit is contained in:
@@ -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' },
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user