mirror of
https://github.com/airstruck/luigi.git
synced 2026-01-11 08:48:23 +00:00
add keyboard focus
This commit is contained in:
@@ -17,6 +17,9 @@ local style = {
|
|||||||
align = 'center middle',
|
align = 'center middle',
|
||||||
width = 48,
|
width = 48,
|
||||||
},
|
},
|
||||||
|
toolButton_focused = {
|
||||||
|
slices = 'defer',
|
||||||
|
},
|
||||||
toolButton_not_hovered = {
|
toolButton_not_hovered = {
|
||||||
slices = false,
|
slices = false,
|
||||||
},
|
},
|
||||||
@@ -62,7 +65,7 @@ local mainForm = { title = "Test window", id = 'mainWindow', type = 'panel',
|
|||||||
{ type = 'sash', width = 4, },
|
{ type = 'sash', width = 4, },
|
||||||
{ type = 'panel', id = 'rightSideBox', width = 200,
|
{ type = 'panel', id = 'rightSideBox', width = 200,
|
||||||
{ type = 'panel', text = 'A slider', align = 'bottom', height = 24, padding = 4 },
|
{ type = 'panel', text = 'A slider', align = 'bottom', height = 24, padding = 4 },
|
||||||
{ type = 'slider', height = 32, margin = 4, id = 'slidey', },
|
{ type = 'slider', height = 32, margin = 4, id = 'slidey', value = 0 },
|
||||||
{ type = 'panel', text = 'A stepper', align = 'bottom', height = 24, padding = 4 },
|
{ type = 'panel', text = 'A stepper', align = 'bottom', height = 24, padding = 4 },
|
||||||
{ type = 'stepper', height = 32, margin = 4, options = {
|
{ type = 'stepper', height = 32, margin = 4, options = {
|
||||||
{ value = 1, text = 'Thing One' },
|
{ value = 1, text = 'Thing One' },
|
||||||
@@ -93,8 +96,8 @@ layout.leftSideBox:addChild {
|
|||||||
align = 'middle right'
|
align = 'middle right'
|
||||||
}
|
}
|
||||||
|
|
||||||
layout.slidey:onPressDrag(function (event)
|
layout.slidey:onChange(function (event)
|
||||||
layout.progressBar.value = layout.slidey.value
|
layout.progressBar:setValue(event.value)
|
||||||
layout.progressBar:reshape()
|
layout.progressBar:reshape()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|||||||
@@ -18,9 +18,18 @@ function Event:bind (target, callback)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local eventNames = {
|
local eventNames = {
|
||||||
'Reshape', 'Display', 'KeyPress', 'KeyRelease', 'TextInput', 'Move',
|
'Reshape', -- widget's dimensions changed
|
||||||
'Enter', 'Leave', 'PressEnter', 'PressLeave',
|
'Display', -- widget is being drawn
|
||||||
'PressStart', 'PressEnd', 'PressDrag', 'PressMove', 'Press',
|
'KeyPress', 'KeyRelease', -- keyboard key pressed/released
|
||||||
|
'TextInput', -- text is entered
|
||||||
|
'Move', -- cursor moves, no button pressed
|
||||||
|
'Enter', 'Leave', -- cursor enters/leaves widget, no button pressed
|
||||||
|
'PressEnter', 'PressLeave', -- cursor enters/leaves widget, button pressed
|
||||||
|
'PressStart', 'PressEnd', -- cursor or accelerator key press starts/ends
|
||||||
|
'PressDrag', -- pressed cursor moves, targets originating widget
|
||||||
|
'PressMove', -- pressed cursor moves, targets widget at cursor position
|
||||||
|
'Press', -- cursor is pressed and released on same widget
|
||||||
|
'Change', -- widget's value changed via Widget:setValue
|
||||||
}
|
}
|
||||||
|
|
||||||
local weakKeyMeta = { __mode = 'k' }
|
local weakKeyMeta = { __mode = 'k' }
|
||||||
|
|||||||
@@ -14,15 +14,6 @@ function Input:constructor (layout)
|
|||||||
self.passedWidgets = setmetatable({}, weakValueMeta)
|
self.passedWidgets = setmetatable({}, weakValueMeta)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Input:bubbleEvent (eventName, widget, data)
|
|
||||||
local event = Event[eventName]
|
|
||||||
for ancestor in widget:getAncestors(true) do
|
|
||||||
local result = event:emit(ancestor, data)
|
|
||||||
if result ~= nil then return result end
|
|
||||||
end
|
|
||||||
return event:emit(self.layout, data)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Input:handleDisplay ()
|
function Input:handleDisplay ()
|
||||||
local root = self.layout.root
|
local root = self.layout.root
|
||||||
if root then Renderer:render(root) end
|
if root then Renderer:render(root) end
|
||||||
@@ -31,40 +22,23 @@ end
|
|||||||
|
|
||||||
function Input:handleKeyPress (key, x, y)
|
function Input:handleKeyPress (key, x, y)
|
||||||
local widget = self.layout.focusedWidget or self.layout:getWidgetAt(x, y)
|
local widget = self.layout.focusedWidget or self.layout:getWidgetAt(x, y)
|
||||||
local result = self:bubbleEvent('KeyPress', widget, {
|
local result = widget:bubbleEvent('KeyPress', {
|
||||||
target = widget,
|
|
||||||
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
|
||||||
|
|
||||||
local acceleratedWidget = self.layout.accelerators[key]
|
|
||||||
|
|
||||||
if acceleratedWidget then
|
|
||||||
acceleratedWidget.hovered = true
|
|
||||||
self:handlePressStart(key, x, y, acceleratedWidget, key)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function Input:handleKeyRelease (key, x, y)
|
function Input:handleKeyRelease (key, x, y)
|
||||||
local widget = self.layout.focusedWidget or self.layout:getWidgetAt(x, y)
|
local widget = self.layout.focusedWidget or self.layout:getWidgetAt(x, y)
|
||||||
local result = self:bubbleEvent('KeyRelease', widget, {
|
local result = widget:bubbleEvent('KeyRelease', {
|
||||||
target = widget,
|
|
||||||
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
|
||||||
|
|
||||||
local acceleratedWidget = self.layout.accelerators[key]
|
|
||||||
|
|
||||||
if acceleratedWidget then
|
|
||||||
acceleratedWidget.hovered = false
|
|
||||||
self:handlePressEnd(key, x, y, acceleratedWidget, key)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function Input:handleTextInput (text, x, y)
|
function Input:handleTextInput (text, x, y)
|
||||||
local widget = self.layout.focusedWidget or self.layout:getWidgetAt(x, y)
|
local widget = self.layout.focusedWidget or self.layout:getWidgetAt(x, y)
|
||||||
self:bubbleEvent('TextInput', widget, {
|
widget:bubbleEvent('TextInput', {
|
||||||
target = widget,
|
|
||||||
text = text, x = x, y = y
|
text = text, x = x, y = y
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
@@ -78,21 +52,18 @@ function Input:handleMove (x, y)
|
|||||||
end
|
end
|
||||||
widget.hovered = true
|
widget.hovered = true
|
||||||
end
|
end
|
||||||
self:bubbleEvent('Move', widget, {
|
widget:bubbleEvent('Move', {
|
||||||
target = widget,
|
|
||||||
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
|
||||||
self:bubbleEvent('Leave', previousWidget, {
|
previousWidget:bubbleEvent('Leave', {
|
||||||
target = previousWidget,
|
|
||||||
newTarget = widget,
|
newTarget = widget,
|
||||||
x = x, y = y
|
x = x, y = y
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
self:bubbleEvent('Enter', widget, {
|
widget:bubbleEvent('Enter', {
|
||||||
target = widget,
|
|
||||||
oldTarget = previousWidget,
|
oldTarget = previousWidget,
|
||||||
x = x, y = y
|
x = x, y = y
|
||||||
})
|
})
|
||||||
@@ -106,15 +77,13 @@ function Input:handlePressedMove (x, y)
|
|||||||
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
|
||||||
self:bubbleEvent('PressDrag', originWidget, {
|
originWidget:bubbleEvent('PressDrag', {
|
||||||
target = originWidget,
|
|
||||||
newTarget = widget,
|
newTarget = widget,
|
||||||
button = button,
|
button = button,
|
||||||
x = x, y = y
|
x = x, y = y
|
||||||
})
|
})
|
||||||
if (widget == passedWidget) then
|
if (widget == passedWidget) then
|
||||||
self:bubbleEvent('PressMove', widget, {
|
widget:bubbleEvent('PressMove', {
|
||||||
target = widget,
|
|
||||||
origin = originWidget,
|
origin = originWidget,
|
||||||
button = button,
|
button = button,
|
||||||
x = x, y = y
|
x = x, y = y
|
||||||
@@ -122,16 +91,14 @@ function Input:handlePressedMove (x, y)
|
|||||||
else
|
else
|
||||||
originWidget.pressed = (widget == originWidget) or nil
|
originWidget.pressed = (widget == originWidget) or nil
|
||||||
if passedWidget then
|
if passedWidget then
|
||||||
self:bubbleEvent('PressLeave', passedWidget, {
|
passedWidget:bubbleEvent('PressLeave', {
|
||||||
target = passedWidget,
|
|
||||||
newTarget = widget,
|
newTarget = widget,
|
||||||
origin = originWidget,
|
origin = originWidget,
|
||||||
button = button,
|
button = button,
|
||||||
x = x, y = y
|
x = x, y = y
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
self:bubbleEvent('PressEnter', widget, {
|
widget:bubbleEvent('PressEnter', {
|
||||||
target = widget,
|
|
||||||
oldTarget = passedWidget,
|
oldTarget = passedWidget,
|
||||||
origin = originWidget,
|
origin = originWidget,
|
||||||
button = button,
|
button = button,
|
||||||
@@ -148,8 +115,8 @@ function Input:handlePressStart (button, x, y, widget, accelerator)
|
|||||||
widget.pressed = true
|
widget.pressed = true
|
||||||
self.pressedWidgets[button] = widget
|
self.pressedWidgets[button] = widget
|
||||||
self.passedWidgets[button] = widget
|
self.passedWidgets[button] = widget
|
||||||
self:bubbleEvent('PressStart', widget, {
|
self.layout:tryFocus(widget)
|
||||||
target = widget,
|
widget:bubbleEvent('PressStart', {
|
||||||
button = button,
|
button = button,
|
||||||
accelerator = accelerator,
|
accelerator = accelerator,
|
||||||
x = x, y = y
|
x = x, y = y
|
||||||
@@ -160,15 +127,13 @@ function Input:handlePressEnd (button, x, y, widget, accelerator)
|
|||||||
local widget = widget or self.layout:getWidgetAt(x, y)
|
local widget = widget or self.layout:getWidgetAt(x, y)
|
||||||
local originWidget = self.pressedWidgets[button]
|
local originWidget = self.pressedWidgets[button]
|
||||||
originWidget.pressed = nil
|
originWidget.pressed = nil
|
||||||
self:bubbleEvent('PressEnd', widget, {
|
widget:bubbleEvent('PressEnd', {
|
||||||
target = widget,
|
|
||||||
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
|
||||||
self:bubbleEvent('Press', widget, {
|
widget:bubbleEvent('Press', {
|
||||||
target = widget,
|
|
||||||
button = button,
|
button = button,
|
||||||
accelerator = accelerator,
|
accelerator = accelerator,
|
||||||
x = x, y = y
|
x = x, y = y
|
||||||
|
|||||||
131
luigi/layout.lua
131
luigi/layout.lua
@@ -12,7 +12,6 @@ local Layout = Base:extend()
|
|||||||
local weakValueMeta = { __mode = 'v' }
|
local weakValueMeta = { __mode = 'v' }
|
||||||
|
|
||||||
function Layout:constructor (data)
|
function Layout:constructor (data)
|
||||||
self.widgets = setmetatable({}, weakValueMeta)
|
|
||||||
self.accelerators = {}
|
self.accelerators = {}
|
||||||
self:setStyle()
|
self:setStyle()
|
||||||
self:setTheme()
|
self:setTheme()
|
||||||
@@ -21,8 +20,134 @@ function Layout:constructor (data)
|
|||||||
self.isManagingInput = false
|
self.isManagingInput = false
|
||||||
self.hooks = {}
|
self.hooks = {}
|
||||||
self.root = Widget(self, data or {})
|
self.root = Widget(self, data or {})
|
||||||
|
|
||||||
|
self:addDefaultHandlers ()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- focus a widget if it's focusable, and return success
|
||||||
|
function Layout:tryFocus (widget)
|
||||||
|
if widget.canFocus then
|
||||||
|
if self.focusedWidget then
|
||||||
|
self.focusedWidget.focused = false
|
||||||
|
end
|
||||||
|
widget.focused = true
|
||||||
|
self.focusedWidget = widget
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get the next widget, cycling back around to root (depth first)
|
||||||
|
function Layout:getNextWidget (widget)
|
||||||
|
if #widget.children > 0 then
|
||||||
|
return widget.children[1]
|
||||||
|
end
|
||||||
|
for ancestor in widget:eachAncestor(true) do
|
||||||
|
local nextWidget = ancestor:getNext()
|
||||||
|
if nextWidget then return nextWidget end
|
||||||
|
end
|
||||||
|
return self.root
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get the last child of the last child of the last child of the...
|
||||||
|
local function getGreatestDescendant (widget)
|
||||||
|
while #widget.children > 0 do
|
||||||
|
local children = widget.children
|
||||||
|
widget = children[#children]
|
||||||
|
end
|
||||||
|
return widget
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get the previous widget, cycling back around to root (depth first)
|
||||||
|
function Layout:getPreviousWidget (widget)
|
||||||
|
if widget == self.root then
|
||||||
|
return getGreatestDescendant(widget)
|
||||||
|
end
|
||||||
|
for ancestor in widget:eachAncestor(true) do
|
||||||
|
local previousWidget = ancestor:getPrevious()
|
||||||
|
if previousWidget then
|
||||||
|
return getGreatestDescendant(previousWidget)
|
||||||
|
end
|
||||||
|
if ancestor ~= widget then return ancestor end
|
||||||
|
end
|
||||||
|
return self.root
|
||||||
|
end
|
||||||
|
|
||||||
|
-- focus next focusable widget (depth first)
|
||||||
|
function Layout:focusNextWidget ()
|
||||||
|
local widget = self.focusedWidget or self.root
|
||||||
|
local nextWidget = self:getNextWidget(widget)
|
||||||
|
|
||||||
|
while nextWidget ~= widget do
|
||||||
|
if self:tryFocus(nextWidget) then return end
|
||||||
|
nextWidget = self:getNextWidget(nextWidget)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- focus previous focusable widget (depth first)
|
||||||
|
function Layout:focusPreviousWidget ()
|
||||||
|
local widget = self.focusedWidget or self.root
|
||||||
|
local previousWidget = self:getPreviousWidget(widget)
|
||||||
|
|
||||||
|
while previousWidget ~= widget do
|
||||||
|
if self:tryFocus(previousWidget) then return end
|
||||||
|
previousWidget = self:getPreviousWidget(previousWidget)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- handlers for keyboard accelerators and tab focus
|
||||||
|
function Layout:addDefaultHandlers ()
|
||||||
|
self:onKeyPress(function (event)
|
||||||
|
|
||||||
|
-- tab / shift-tab cycles focused widget
|
||||||
|
if event.key == 'tab' then
|
||||||
|
if love.keyboard.isDown('lshift', 'rshift') then
|
||||||
|
self:focusPreviousWidget()
|
||||||
|
else
|
||||||
|
self:focusNextWidget()
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- space / enter presses focused widget
|
||||||
|
local widget = self.focusedWidget
|
||||||
|
if widget and (event.key == 'return' or event.key == 'space') then
|
||||||
|
self.input:handlePressStart(event.key, event.x, event.y,
|
||||||
|
widget, event.key)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- accelerators
|
||||||
|
local acceleratedWidget = self.accelerators[event.key]
|
||||||
|
|
||||||
|
if acceleratedWidget then
|
||||||
|
acceleratedWidget.hovered = true
|
||||||
|
self.input:handlePressStart(event.key, event.x, event.y,
|
||||||
|
acceleratedWidget, event.key)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
self:onKeyRelease(function (event)
|
||||||
|
|
||||||
|
-- space / enter presses focused widget
|
||||||
|
local widget = self.focusedWidget
|
||||||
|
if widget and (event.key == 'return' or event.key == 'space') then
|
||||||
|
self.input:handlePressEnd(event.key, event.x, event.y,
|
||||||
|
widget, event.key)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- accelerators
|
||||||
|
local acceleratedWidget = self.accelerators[event.key]
|
||||||
|
|
||||||
|
if acceleratedWidget then
|
||||||
|
acceleratedWidget.hovered = false
|
||||||
|
self.input:handlePressEnd(event.key, event.x, event.y,
|
||||||
|
acceleratedWidget, event.key)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set the style from a definition table or function
|
||||||
function Layout:setStyle (rules)
|
function Layout:setStyle (rules)
|
||||||
if type(rules) == 'function' then
|
if type(rules) == 'function' then
|
||||||
rules = rules()
|
rules = rules()
|
||||||
@@ -30,6 +155,7 @@ function Layout:setStyle (rules)
|
|||||||
self.style = Style(rules or {}, { 'id', 'style' })
|
self.style = Style(rules or {}, { 'id', 'style' })
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- set the theme from a definition table or function
|
||||||
function Layout:setTheme (rules)
|
function Layout:setTheme (rules)
|
||||||
if type(rules) == 'function' then
|
if type(rules) == 'function' then
|
||||||
rules = rules()
|
rules = rules()
|
||||||
@@ -37,6 +163,7 @@ function Layout:setTheme (rules)
|
|||||||
self.theme = Style(rules or {}, { 'type' })
|
self.theme = Style(rules or {}, { 'type' })
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- show the layout (hooks all appropriate love events and callbacks)
|
||||||
function Layout:show ()
|
function Layout:show ()
|
||||||
local root = self.root
|
local root = self.root
|
||||||
local width = root.width
|
local width = root.width
|
||||||
@@ -55,6 +182,7 @@ function Layout:show ()
|
|||||||
root:reshape()
|
root:reshape()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- hide the layout (unhooks love events and callbacks)
|
||||||
function Layout:hide ()
|
function Layout:hide ()
|
||||||
if not self.isManagingInput then
|
if not self.isManagingInput then
|
||||||
return
|
return
|
||||||
@@ -89,7 +217,6 @@ function Layout:addWidget (widget)
|
|||||||
if widget.key then
|
if widget.key then
|
||||||
self.accelerators[widget.key] = widget
|
self.accelerators[widget.key] = widget
|
||||||
end
|
end
|
||||||
table.insert(self.widgets, widget)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- event stuff
|
-- event stuff
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ 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', 'hovered' }
|
local specialName = getSpecialName { 'pressed', 'focused', 'hovered' }
|
||||||
if specialName then return specialName end
|
if specialName then return specialName end
|
||||||
lookupPropIndex = lookupPropIndex + 1
|
lookupPropIndex = lookupPropIndex + 1
|
||||||
return lookupProp[lookupPropIndex]
|
return lookupProp[lookupPropIndex]
|
||||||
|
|||||||
@@ -9,29 +9,23 @@ return function (config)
|
|||||||
local highlight = config.highlight or { 180, 180, 255 }
|
local highlight = config.highlight or { 180, 180, 255 }
|
||||||
|
|
||||||
return {
|
return {
|
||||||
panel = {
|
|
||||||
background = backColor,
|
|
||||||
},
|
|
||||||
button = {
|
button = {
|
||||||
align = 'center middle',
|
align = 'center middle',
|
||||||
padding = 6,
|
padding = 6,
|
||||||
slices = RESOURCE .. 'button.png',
|
slices = RESOURCE .. 'button.png',
|
||||||
minimumWidth = 24,
|
minimumWidth = 24,
|
||||||
minimumHeight = 24
|
minimumHeight = 24,
|
||||||
|
canFocus = true
|
||||||
},
|
},
|
||||||
button_hovered = {
|
button_hovered = {
|
||||||
slices = RESOURCE .. 'button_hovered.png'
|
slices = RESOURCE .. 'button_hovered.png'
|
||||||
},
|
},
|
||||||
|
button_focused = {
|
||||||
|
slices = RESOURCE .. 'button_focused.png',
|
||||||
|
},
|
||||||
button_pressed = {
|
button_pressed = {
|
||||||
slices = RESOURCE .. 'button_pressed.png',
|
slices = RESOURCE .. 'button_pressed.png',
|
||||||
},
|
},
|
||||||
text = {
|
|
||||||
align = 'left middle',
|
|
||||||
slices = RESOURCE .. 'text.png',
|
|
||||||
padding = 6,
|
|
||||||
minimumWidth = 24,
|
|
||||||
minimumHeight = 24
|
|
||||||
},
|
|
||||||
sash = {
|
sash = {
|
||||||
background = lineColor
|
background = lineColor
|
||||||
},
|
},
|
||||||
@@ -44,6 +38,9 @@ return function (config)
|
|||||||
minimumWidth = 24,
|
minimumWidth = 24,
|
||||||
minimumHeight = 24
|
minimumHeight = 24
|
||||||
},
|
},
|
||||||
|
panel = {
|
||||||
|
background = backColor,
|
||||||
|
},
|
||||||
progress = {
|
progress = {
|
||||||
slices = RESOURCE .. 'button_pressed.png',
|
slices = RESOURCE .. 'button_pressed.png',
|
||||||
padding = 0,
|
padding = 0,
|
||||||
@@ -59,6 +56,17 @@ return function (config)
|
|||||||
},
|
},
|
||||||
stepper = {
|
stepper = {
|
||||||
},
|
},
|
||||||
|
text = {
|
||||||
|
align = 'left middle',
|
||||||
|
slices = RESOURCE .. 'text.png',
|
||||||
|
padding = 6,
|
||||||
|
minimumWidth = 24,
|
||||||
|
minimumHeight = 24,
|
||||||
|
canFocus = true,
|
||||||
|
},
|
||||||
|
text_focused = {
|
||||||
|
slices = RESOURCE .. 'text_focused.png',
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
BIN
luigi/theme/light/button_focused.png
Normal file
BIN
luigi/theme/light/button_focused.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 597 B |
Binary file not shown.
|
Before Width: | Height: | Size: 388 B After Width: | Height: | Size: 375 B |
BIN
luigi/theme/light/text_focused.png
Normal file
BIN
luigi/theme/light/text_focused.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 551 B |
@@ -9,6 +9,7 @@ Event.injectBinders(Widget)
|
|||||||
Widget.isWidget = true
|
Widget.isWidget = true
|
||||||
|
|
||||||
Widget.typeDecorators = {
|
Widget.typeDecorators = {
|
||||||
|
button = require(ROOT .. 'widget.button'),
|
||||||
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'),
|
||||||
@@ -34,7 +35,7 @@ local function new (Widget, layout, self)
|
|||||||
if value ~= nil then return value end
|
if value ~= nil then return value end
|
||||||
local style = self.layout.style
|
local style = self.layout.style
|
||||||
value = style and style:getProperty(self, property)
|
value = style and style:getProperty(self, property)
|
||||||
if value ~= nil then return value end
|
if value ~= nil and value ~= 'defer' then return value end
|
||||||
local theme = self.layout.theme
|
local theme = self.layout.theme
|
||||||
return theme and theme:getProperty(self, property)
|
return theme and theme:getProperty(self, property)
|
||||||
end
|
end
|
||||||
@@ -56,7 +57,29 @@ local function new (Widget, layout, self)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Widget:bubbleEvent (eventName, data)
|
||||||
|
local event = Event[eventName]
|
||||||
|
data = data or {}
|
||||||
|
data.target = self
|
||||||
|
for ancestor in self:eachAncestor(true) do
|
||||||
|
local result = event:emit(ancestor, data)
|
||||||
|
if result ~= nil then return result end
|
||||||
|
end
|
||||||
|
return event:emit(self.layout, data)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Widget:setValue (value)
|
||||||
|
local oldValue = self.value
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
self:bubbleEvent('Change', {
|
||||||
|
value = value,
|
||||||
|
oldValue = oldValue,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
function Widget:getPrevious ()
|
function Widget:getPrevious ()
|
||||||
|
if not self.parent then return end
|
||||||
local siblings = self.parent.children
|
local siblings = self.parent.children
|
||||||
for i, widget in ipairs(siblings) do
|
for i, widget in ipairs(siblings) do
|
||||||
if widget == self then return siblings[i - 1] end
|
if widget == self then return siblings[i - 1] end
|
||||||
@@ -64,6 +87,7 @@ function Widget:getPrevious ()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Widget:getNext ()
|
function Widget:getNext ()
|
||||||
|
if not self.parent then return end
|
||||||
local siblings = self.parent.children
|
local siblings = self.parent.children
|
||||||
for i, widget in ipairs(siblings) do
|
for i, widget in ipairs(siblings) do
|
||||||
if widget == self then return siblings[i + 1] end
|
if widget == self then return siblings[i + 1] end
|
||||||
@@ -76,7 +100,6 @@ function Widget:addChild (data)
|
|||||||
|
|
||||||
table.insert(self.children, child)
|
table.insert(self.children, child)
|
||||||
child.parent = self
|
child.parent = self
|
||||||
layout:addWidget(child)
|
|
||||||
|
|
||||||
return child
|
return child
|
||||||
end
|
end
|
||||||
@@ -236,7 +259,7 @@ function Widget:isAt (x, y)
|
|||||||
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:getAncestors (includeSelf)
|
function Widget:eachAncestor (includeSelf)
|
||||||
local instance = includeSelf and self or self.parent
|
local instance = includeSelf and self or self.parent
|
||||||
return function()
|
return function()
|
||||||
local widget = instance
|
local widget = instance
|
||||||
|
|||||||
3
luigi/widget/button.lua
Normal file
3
luigi/widget/button.lua
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
return function (self)
|
||||||
|
|
||||||
|
end
|
||||||
@@ -1,6 +1,11 @@
|
|||||||
return function (self)
|
return function (self)
|
||||||
|
|
||||||
self.value = 0.5
|
local function clamp (value)
|
||||||
|
return value < 0 and 0 or value > 1 and 1 or value
|
||||||
|
end
|
||||||
|
|
||||||
|
self:setValue(clamp(self.value or 0.5))
|
||||||
|
self.step = self.step or 0.01
|
||||||
self.flow = 'x' -- TODO: support vertical slider
|
self.flow = 'x' -- TODO: support vertical slider
|
||||||
|
|
||||||
local spacer = self:addChild()
|
local spacer = self:addChild()
|
||||||
@@ -13,22 +18,32 @@ return function (self)
|
|||||||
}
|
}
|
||||||
|
|
||||||
local function unpress ()
|
local function unpress ()
|
||||||
thumb.pressed = false
|
thumb.pressed = false -- don't make the thumb appear pushed in
|
||||||
|
return false -- don't press thumb on focused keyboard activation
|
||||||
end
|
end
|
||||||
|
|
||||||
thumb:onPressStart(unpress)
|
thumb:onPressStart(unpress)
|
||||||
thumb:onPressEnter(unpress)
|
thumb:onPressEnter(unpress)
|
||||||
|
|
||||||
|
thumb:onKeyPress(function (event)
|
||||||
|
local key = event.key
|
||||||
|
if key == 'left' or key == 'down' then
|
||||||
|
self:setValue(clamp(self.value - self.step))
|
||||||
|
self:reshape()
|
||||||
|
elseif event.key == 'right' or key == 'up' then
|
||||||
|
self:setValue(clamp(self.value + self.step))
|
||||||
|
self:reshape()
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
local function press (event)
|
local function press (event)
|
||||||
local x1, y1, x2, y2 = self:getRectangle(true, true)
|
local x1, y1, x2, y2 = self:getRectangle(true, true)
|
||||||
self.value = (event.x - x1) / (x2 - x1)
|
self:setValue(clamp((event.x - x1) / (x2 - x1)))
|
||||||
if self.value < 0 then self.value = 0 end
|
|
||||||
if self.value > 1 then self.value = 1 end
|
|
||||||
self:reshape()
|
self:reshape()
|
||||||
|
self.layout:tryFocus(thumb)
|
||||||
end
|
end
|
||||||
|
|
||||||
self:onPressStart(press)
|
self:onPressStart(press)
|
||||||
|
|
||||||
self:onPressDrag(press)
|
self:onPressDrag(press)
|
||||||
|
|
||||||
self:onEnter(function (event)
|
self:onEnter(function (event)
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ return function (self)
|
|||||||
type = 'text',
|
type = 'text',
|
||||||
align = 'middle center',
|
align = 'middle center',
|
||||||
margin = 0,
|
margin = 0,
|
||||||
|
canFocus = false,
|
||||||
}
|
}
|
||||||
|
|
||||||
local increment = self:addChild {
|
local increment = self:addChild {
|
||||||
@@ -31,7 +32,7 @@ return function (self)
|
|||||||
local function updateValue ()
|
local function updateValue ()
|
||||||
if not self.options then return end
|
if not self.options then return end
|
||||||
local option = self.options[self.index]
|
local option = self.options[self.index]
|
||||||
self.value = option.value
|
self:setValue(option.value)
|
||||||
view.text = option.text
|
view.text = option.text
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user