Files
love-luigi/luigi/input.lua
2017-08-10 16:10:00 -04:00

233 lines
6.7 KiB
Lua

local ROOT = (...):gsub('[^.]*$', '')
local Backend = require(ROOT .. 'backend')
local Base = require(ROOT .. 'base')
local Event = require(ROOT .. 'event')
local Shortcut = require(ROOT .. 'shortcut')
local Input = Base:extend()
local weakValueMeta = { __mode = 'v' }
function Input:constructor ()
self.pressedWidgets = setmetatable({}, weakValueMeta)
self.passedWidgets = setmetatable({}, weakValueMeta)
end
function Input:handleDisplay (layout)
local root = layout.root
if root then root:paint() end
Event.Display:emit(layout)
end
function Input:handleKeyPress (layout, key, sc, x, y)
local widget = layout.focusedWidget or layout.root
local result = widget:bubbleEvent('KeyPress', {
key = key,
scanCode = sc,
modifierFlags = Shortcut.getModifierFlags(),
x = x, y = y
})
if result ~= nil then return result end
if layout.root.modal then return false end
end
function Input:handleKeyRelease (layout, key, sc, x, y)
local widget = layout.focusedWidget or layout.root
local result = widget:bubbleEvent('KeyRelease', {
key = key,
scanCode = sc,
modifierFlags = Shortcut.getModifierFlags(),
x = x, y = y
})
if result ~= nil then return result end
if layout.root.modal then return false end
end
function Input:handleTextInput (layout, text, x, y)
local widget = layout.focusedWidget or layout.root
local result = widget:bubbleEvent('TextInput', {
text = text,
x = x, y = y
})
if result ~= nil then return result end
if layout.root.modal then return false end
end
local function checkHit (widget, layout)
local root = layout.root
return widget and widget.solid or root.modal, widget or root
end
function Input:handleMove (layout, x, y)
local hit, widget = checkHit(layout:getWidgetAt(x, y), layout)
local previousWidget = self.previousMoveWidget
if widget ~= previousWidget then
if previousWidget then
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:bubbleEvent('Move', {
hit = hit,
oldTarget = previousWidget,
x = x, y = y
})
if widget ~= previousWidget then
if previousWidget then
previousWidget:bubbleEvent('Leave', {
hit = hit,
newTarget = widget,
x = x, y = y
})
end
widget:bubbleEvent('Enter', {
hit = hit,
oldTarget = previousWidget,
x = x, y = y
})
if widget.cursor then
Backend.setCursor(Backend.getSystemCursor(widget.cursor))
else
Backend.setCursor()
end
self.previousMoveWidget = widget
end
return hit
end
function Input:handlePressedMove (layout, x, y)
local hit, widget = checkHit(layout:getWidgetAt(x, y), layout)
for _, button in ipairs { 'left', 'middle', 'right' } do
local originWidget = self.pressedWidgets[button]
if originWidget then
local passedWidget = self.passedWidgets[button]
originWidget:bubbleEvent('PressDrag', {
hit = hit,
newTarget = widget,
button = button,
x = x, y = y
})
if (widget == passedWidget) then
widget:bubbleEvent('PressMove', {
hit = hit,
origin = originWidget,
button = button,
x = x, y = y
})
else
originWidget.pressed[button] = (widget == originWidget) or nil
if passedWidget then
passedWidget:bubbleEvent('PressLeave', {
hit = hit,
newTarget = widget,
origin = originWidget,
button = button,
x = x, y = y
})
end
widget:bubbleEvent('PressEnter', {
hit = hit,
oldTarget = passedWidget,
origin = originWidget,
button = button,
x = x, y = y
})
self.passedWidgets[button] = widget
end
end -- if originWidget
end -- mouse buttons
return hit
end
function Input:handlePressStart (layout, button, x, y, widget, shortcut)
local hit, widget = checkHit(widget or layout:getWidgetAt(x, y), layout)
-- if hit then
self.pressedWidgets[button] = widget
self.passedWidgets[button] = widget
widget.pressed[button] = true
if button == 'left' then
widget:focus()
end
-- end
widget:bubbleEvent('PressStart', {
hit = hit,
button = button,
shortcut = shortcut,
x = x, y = y
})
return hit
end
function Input:handlePressEnd (layout, button, x, y, widget, shortcut)
local originWidget = widget or self.pressedWidgets[button]
if not originWidget then return end
local hit, widget = checkHit(widget or layout:getWidgetAt(x, y), layout)
local wasPressed = originWidget.pressed[button]
if hit then
originWidget.pressed[button] = nil
end
widget:bubbleEvent('PressEnd', {
hit = hit,
origin = originWidget,
shortcut = shortcut,
button = button,
x = x, y = y
})
if (widget == originWidget and wasPressed) then
widget:bubbleEvent('Press', {
hit = hit,
button = button,
shortcut = shortcut,
x = x, y = y
})
end
if hit then
self.pressedWidgets[button] = nil
self.passedWidgets[button] = nil
end
return hit
end
function Input:handleReshape (layout, width, height)
local root = layout.root
root:reshape()
if root.type ~= 'window' then -- FIXME: move stuff below to a Widget method
if not root.width then
root.dimensions.width = width
end
if not root.height then
root.dimensions.height = height
end
end
Event.Reshape:emit(layout, {
target = layout,
width = width,
height = height
})
end
function Input:handleWheelMove (layout, scrollX, scrollY)
local x, y = Backend.getMousePosition()
local hit, widget = checkHit(layout:getWidgetAt(x, y), layout)
widget:bubbleEvent('WheelMove', {
hit = hit,
x = x, y = y,
scrollX = scrollX, scrollY = scrollY
})
return hit
end
Input.default = Input()
return Input