initial commit

This commit is contained in:
airstruck
2015-10-21 18:35:14 -04:00
commit e490e2899f
52 changed files with 1506 additions and 0 deletions

12
luigi/base.lua Normal file
View File

@@ -0,0 +1,12 @@
return {
extend = function (self, subtype)
return setmetatable(subtype or {}, {
__index = self,
__call = function (self, ...)
local instance = setmetatable({}, { __index = self })
return instance, instance:constructor(...)
end
})
end,
constructor = function () end,
}

49
luigi/event.lua Normal file
View File

@@ -0,0 +1,49 @@
local ROOT = (...):gsub('[^.]*$', '')
local Base = require(ROOT .. 'base')
local Event = Base:extend({ name = 'Event' })
function Event:emit (observer, data, defaultAction)
local callbacks = self.registry[observer]
if not callbacks then
if defaultAction then defaultAction() end
return
end
for i, callback in ipairs(callbacks) do
local result = callback(data or {})
if result ~= nil then return result end
end
if defaultAction then defaultAction() end
end
function Event:bind (observer, callback)
local registry = self.registry
if not registry[observer] then
registry[observer] = {}
end
table.insert(registry[observer], callback)
end
local eventNames = {
'Display', 'Keyboard', 'Motion', 'Mouse', 'Reshape', 'Enter', 'Leave',
'Press', 'PressStart', 'PressDrag', 'PressMove', 'PressLeave', 'PressEnter',
'PressEnd'
}
local weakKeyMeta = { __mode = 'k' }
for i, name in ipairs(eventNames) do
Event[name] = Event:extend({
name = name,
registry = setmetatable({}, weakKeyMeta),
})
end
function Event.injectBinders (t)
for i, name in ipairs(eventNames) do
t['on' .. name] = function (...) return Event[name]:bind(...) end
end
end
return Event

60
luigi/font.lua Normal file
View File

@@ -0,0 +1,60 @@
local ROOT = (...):gsub('[^.]*$', '')
local Base = require(ROOT .. 'base')
local Font = Base:extend()
local cache = {}
function Font:constructor (path, size, color)
if not size then
size = 12
end
if not color then
color = { 0, 0, 0 }
end
local key = (path or '') .. '_' .. size
if not cache[key] then
if path then
cache[key] = love.graphics.newFont(path, size)
else
cache[key] = love.graphics.newFont(size)
end
end
self.layout = {}
self.font = cache[key]
self.color = color
end
function Font:setAlignment (align)
self.layout.align = align
end
function Font:setWidth (width)
self.layout.width = width
end
function Font:getLineHeight ()
return self.font:getLineHeight()
end
function Font:getAscender ()
return self.font:getAscent()
end
function Font:getDescender ()
return self.font:getDescent()
end
function Font:getAdvance (text)
return (self.font:getWidth(text))
end
function Font:getWrappedHeight (text)
local _, lines = self.font:getWrap(text, self.layout.width)
return #lines * self.font:getHeight()
end
return Font

65
luigi/hooker.lua Normal file
View File

@@ -0,0 +1,65 @@
local Hooker = {}
local wrapped = {}
local hooks = {}
local function hook (key, func)
if not func then
return
end
local next = hooks[key]
local item = { next = next, unhook = unhook, key = key, func = func }
if next then
next.prev = item
end
hooks[key] = item
return item
end
local function unhook (item)
if item.prev then
item.prev.next = item.next
end
if item.next then
item.next.prev = item.prev
end
if hooks[item.key] == item then
hooks[item.key] = item.next
end
item.prev = nil
item.next = nil
item.func = nil
end
function Hooker.hook (key, func)
if not wrapped[key] then
wrapped[key] = true
hook(key, love[key])
love[key] = function (...)
local item = hooks[key]
while item do
item.func(...)
item = item.next
end
end
end
return hook(key, func)
end
function Hooker.unhook (item)
return unhook(item)
end
return Hooker

167
luigi/input.lua Normal file
View File

@@ -0,0 +1,167 @@
local ROOT = (...):gsub('[^.]*$', '')
local Base = require(ROOT .. 'base')
local Event = require(ROOT .. 'event')
local Renderer = require(ROOT .. 'renderer')
local Input = Base:extend()
local weakValueMeta = { __mode = 'v' }
function Input:constructor (layout)
self.layout = layout
self.pressedWidgets = setmetatable({}, weakValueMeta)
self.passedWidgets = setmetatable({}, weakValueMeta)
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 ()
local root = self.layout.root
if root then Renderer:render(root) end
Event.Display:emit(self.layout)
end
function Input:handleKeyboard (key, x, y)
local widget = self.layout.focusedWidget or self.layout:getWidgetAt(x, y)
self:bubbleEvent('Keyboard', widget, {
target = widget,
key = key, x = x, y = y
})
end
function Input:handleMotion (x, y)
local widget = self.layout:getWidgetAt(x, y)
local previousWidget = self.previousMotionWidget
if not widget.hovered then
if previousWidget then
previousWidget.hovered = nil
end
widget.hovered = true
-- self.layout:update()
widget:update()
end
self:bubbleEvent('Motion', widget, {
target = widget,
oldTarget = previousWidget,
x = x, y = y
})
if widget ~= previousWidget then
if previousWidget then
self:bubbleEvent('Leave', previousWidget, {
target = previousWidget,
newTarget = widget,
x = x, y = y
})
end
self:bubbleEvent('Enter', widget, {
target = widget,
oldTarget = previousWidget,
x = x, y = y
})
self.previousMotionWidget = widget
end
end
function Input:handlePressedMotion (x, y)
local widget = self.layout:getWidgetAt(x, y)
for button = 0, 2 do
local originWidget = self.pressedWidgets[button]
local passedWidget = self.passedWidgets[button]
if originWidget then
self:bubbleEvent('PressDrag', originWidget, {
target = originWidget,
newTarget = widget,
button = button,
x = x, y = y
})
if (widget == passedWidget) then
self:bubbleEvent('PressMove', widget, {
target = widget,
origin = originWidget,
button = button,
x = x, y = y
})
else
originWidget.pressed = (widget == originWidget) or nil
originWidget:update()
-- self.layout:update()
if passedWidget then
self:bubbleEvent('PressLeave', passedWidget, {
target = passedWidget,
newTarget = widget,
origin = originWidget,
button = button,
x = x, y = y
})
end
self:bubbleEvent('PressEnter', widget, {
target = widget,
oldTarget = passedWidget,
origin = originWidget,
button = button,
x = x, y = y
})
self.passedWidgets[button] = widget
end
end
end
end
function Input:handlePressStart (button, x, y)
local widget = self.layout:getWidgetAt(x, y)
widget.pressed = true
-- self.layout:update()
widget:update()
self.pressedWidgets[button] = widget
self.passedWidgets[button] = widget
self:bubbleEvent('PressStart', widget, {
target = widget,
buton = button, x = x, y = y
})
end
function Input:handlePressEnd (button, x, y)
local widget = self.layout:getWidgetAt(x, y)
local originWidget = self.pressedWidgets[button]
originWidget.pressed = nil
-- self.layout:update()
originWidget:update()
self:bubbleEvent('PressEnd', widget, {
target = widget,
origin = originWidget,
buton = button, x = x, y = y
})
if (widget == originWidget) then
self:bubbleEvent('Press', widget, {
target = widget,
buton = button, x = x, y = y
})
end
self.pressedWidgets[button] = nil
self.passedWidgets[button] = nil
end
function Input:handleReshape (width, height)
local layout = self.layout
local root = layout.root
for i, widget in ipairs(layout.widgets) do
widget.position = {}
widget.dimensions = {}
end
root.width = width
root.height = height
Event.Reshape:emit(root, {
target = root,
width = width, height = height
})
end
return Input

83
luigi/layout.lua Normal file
View File

@@ -0,0 +1,83 @@
local ROOT = (...):gsub('[^.]*$', '')
local Base = require(ROOT .. 'base')
local Event = require(ROOT .. 'event')
local Window = require(ROOT .. 'window')
local Widget = require(ROOT .. 'widget')
local Input = require(ROOT .. 'input')
local Style = require(ROOT .. 'style')
local Layout = Base:extend()
local weakValueMeta = { __mode = 'v' }
function Layout:constructor (data)
self.widgets = setmetatable({}, weakValueMeta)
self.root = Widget.create(self, data or {})
self:setStyle()
self:setTheme()
end
function Layout:setStyle (rules)
self.style = Style(rules or {}, 'id', 'style')
end
function Layout:setTheme (rules)
self.theme = Style(rules or {}, 'type')
end
function Layout:show ()
local root = self.root
local width = root.width
local height = root.height
local title = root.title
if not self.input then
self.input = Input(self)
end
if not self.window then
self.window = Window(self.input)
end
self.window:show(width, height, title)
end
function Layout:hide ()
self.window:hide()
end
-- Update the display. Call this after you change widget properties
-- that affect display.
function Layout:update (reshape)
self.window:update(reshape)
end
-- Get the innermost widget at a position, within a root widget.
-- Should always return a widget since all positions are within
-- the layout's root widget.
function Layout:getWidgetAt (x, y, root)
local widget = root or self.root
local children = widget.children
local childCount = #children
-- Loop through in reverse, because siblings defined later in the tree
-- will overdraw earlier siblings.
for i = childCount, 1, -1 do
local child = children[i]
local inner = self:getWidgetAt(x, y, child)
if inner then return inner end
end
if widget:isAt(x, y) then return widget end
if widget == self.root then return widget end
end
-- Internal, called from Widget:new
function Layout:addWidget (widget)
if widget.id then
self[widget.id] = widget
end
table.insert(self.widgets, widget)
end
-- event binders
Event.injectBinders(Layout)
return Layout

144
luigi/renderer.lua Normal file
View File

@@ -0,0 +1,144 @@
local ROOT = (...):gsub('[^.]*$', '')
local Base = require(ROOT .. 'base')
local Event = require(ROOT .. 'event')
local Font = require(ROOT .. 'font')
local Renderer = Base:extend()
function Renderer:renderBackground (widget, window)
local bg = widget.background
if not bg then return end
local bend = widget.bend
local x1, y1, x2, y2 = widget:getRectangle(true)
window:fill(x1, y1, x2, y2, bg, bend)
end
function Renderer:renderOutline (widget, window)
if not widget.outline then return end
local x1, y1, x2, y2 = widget:getRectangle(true)
window:outline(x1, y1, x2, y2, widget.outline)
end
local imageCache = {}
local function loadImage (path)
if not imageCache[path] then
imageCache[path] = love.graphics.newImage(path)
end
return imageCache[path]
end
-- TODO: this function is a monster, fix it somehow
function Renderer:renderIconAndText (widget, window)
local x1, y1, x2, y2 = widget:getRectangle(true, true)
local icon = widget.icon and loadImage(widget.icon)
local align = widget.align or ''
local padding = widget.padding or 0
local text = widget.text
local x, y, iconWidth, iconHeight
if icon then
iconWidth, iconHeight = icon:getWidth(), icon:getHeight()
-- horizontal alignment
if align:find('right') then
x = x2 - iconWidth
elseif align:find('center') then
x = x1 + (x2 - x1) / 2 - iconWidth / 2
else -- if align:find('left') then
x = x1
end
-- vertical alignment
if align:find('bottom') then
y = y2 - iconHeight
elseif align:find('middle') then
y = y1 + (y2 - y1) / 2 - iconHeight / 2
else -- if align:find('top') then
y = y1
end
--[[
if text and align:find('center') then
if align:find('bottom') then
y = y - textHeight - padding
elseif align:find('middle') then
y = y - (textHeight + padding) / 2
end
end
--]]
love.graphics.draw(icon, x, y)
end
-- render text
if not text then return end
if not widget.fontData then
widget.fontData = Font(widget.font, widget.fontSize, widget.textColor)
end
local font = widget.fontData
if icon then
if align:find('center') then
-- y1 = y1 + iconHeight + padding
elseif align:find('right') then
x2 = x2 - iconWidth - padding
else
x1 = x1 + iconWidth + padding
end
end
font:setWidth(x2 - x1)
if align:find('right') then
font:setAlignment('right')
elseif align:find('center') then
font:setAlignment('center')
elseif align:find('justify') then
font:setAlignment('justify')
else -- if align:find('left') then
font:setAlignment('left')
end
local textHeight = font:getWrappedHeight(text)
local x, y
-- vertical alignment
if align:find('bottom') then
y = y2 - textHeight
elseif align:find('middle') then
y = y2 - (y2 - y1) / 2 - textHeight / 2
if icon and align:find('center') then
y = y1 + (iconHeight + padding) / 2
end
else -- if align:find('top') then
y = y1
if icon and align:find('center') then
y = y1 + iconHeight + padding
end
end
x = math.floor(x1)
y = math.floor(y)
window:write(x, y, x1, y1, x2, y2, text, font)
end
function Renderer:renderChildren (widget)
for i, child in ipairs(widget.children) do self:render(child) end
end
function Renderer:render (widget)
Event.Display:emit(widget, {}, function()
local window = widget.layout.window
self:renderBackground(widget, window)
self:renderOutline(widget, window)
self:renderIconAndText(widget, window)
return self:renderChildren(widget)
end)
end
return Renderer

79
luigi/style.lua Normal file
View File

@@ -0,0 +1,79 @@
local ROOT = (...):gsub('[^.]*$', '')
local Base = require(ROOT .. 'base')
local Style = Base:extend()
function Style:constructor (rules, ...)
self.rules = rules
self.lookupNames = { ... }
end
function Style:getProperty (object, property)
local ownProperty = rawget(object, property)
if ownProperty ~= nil then return ownProperty end
for styleDef in self:each(object) do
local result = self:getProperty(styleDef, property)
if result ~= nil then return result end
end
end
function Style:each (object)
local rules = self.rules
local nextStyleName = self:eachName(object)
return function ()
local styleName = nextStyleName()
while styleName do
local styleDef = rules[styleName]
if styleDef then return styleDef end
styleName = nextStyleName()
end
end
end
function Style:eachName (object)
local lookupNames = self.lookupNames
local lookupNameIndex = 0
local lookupPropIndex = 0
local lookupProp
local returnedSpecialName = {}
local function checkLookupProp()
if type(lookupProp) == 'table' and lookupPropIndex >= #lookupProp then
lookupProp = nil
end
while not lookupProp do
returnedSpecialName = {}
lookupPropIndex = 0
lookupNameIndex = lookupNameIndex + 1
if lookupNameIndex > #lookupNames then return end
lookupProp = rawget(object, lookupNames[lookupNameIndex])
if type(lookupProp) == 'string' then
lookupProp = { lookupProp }
end
end
return true
end
local function getSpecialName (...)
for k, name in ipairs({ ... }) do
if not returnedSpecialName[name] then
returnedSpecialName[name] = true
if rawget(object, name) then
return lookupProp[lookupPropIndex + 1] .. '_' .. name
else
return lookupProp[lookupPropIndex + 1] .. '_not_' .. name
end
end
end
end
return function ()
if not checkLookupProp() then return end
local specialName = getSpecialName('pressed', 'hovered')
if specialName then return specialName end
lookupPropIndex = lookupPropIndex + 1
return lookupProp[lookupPropIndex]
end
end
return Style

39
luigi/theme/light.lua Normal file
View File

@@ -0,0 +1,39 @@
local backColor = { 240, 240, 240 }
local lineColor = { 220, 220, 220 }
local highlightColor = { 220, 220, 240 }
return {
panel = {
background = backColor,
padding = 4,
},
button = {
type = 'panel',
align = 'center middle',
outline = lineColor,
bend = 0.1,
margin = 4,
},
button_hovered = {
bend = 0.2,
},
button_pressed = {
bend = -0.1,
},
text = {
align = 'left middle',
background = { 255, 255, 255 },
outline = lineColor,
bend = -0.1,
margin = 4,
padding = 4,
},
sash = {
background = highlightColor
},
slider = {
type = 'panel',
outline = lineColor,
bend = 0.1,
},
}

246
luigi/widget.lua Normal file
View File

@@ -0,0 +1,246 @@
local ROOT = (...):gsub('[^.]*$', '')
local Base = require(ROOT .. 'base')
local Event = require(ROOT .. 'event')
local Widget = Base:extend()
Widget.isWidget = true
Widget.registeredTypes = {
sash = ROOT .. 'widget.sash',
slider = ROOT .. 'widget.slider',
text = ROOT .. 'widget.text',
}
function Widget.create (layout, data)
local path = data.type and Widget.registeredTypes[data.type]
if path then
return require(path)(layout, data)
end
return Widget(layout, data)
end
function Widget:constructor (layout, data)
self.type = 'generic'
self.layout = layout
self.children = {}
self.position = { x = nil, y = nil }
self.dimensions = { width = nil, height = nil }
self:extract(data)
layout:addWidget(self)
local widget = self
local meta = getmetatable(self)
local metaIndex = meta.__index
function meta:__index(property)
local value = metaIndex[property]
local style = widget.layout.style
local theme = widget.layout.theme
if value ~= nil then return value end
value = style and style:getProperty(self, property)
if value ~= nil then return value end
return theme and theme:getProperty(self, property)
end
end
function Widget:extract (data)
function toWidget(t)
if t.isWidget then return t end
return Widget.create(self.layout, t)
end
for k, v in pairs(data) do
if type(k) == 'number' then
self.children[k] = toWidget(v)
self.children[k].parent = self
else
self[k] = v
end
end
end
function Widget:getPrevious ()
local siblings = self.parent.children
for i, widget in ipairs(siblings) do
if widget == self then return siblings[i - 1] end
end
end
function Widget:getNext ()
local siblings = self.parent.children
for i, widget in ipairs(siblings) do
if widget == self then return siblings[i + 1] end
end
end
function Widget:addChild (data)
local layout = self.layout
local child = Widget.create(layout, data)
table.insert(self.children, child)
child.parent = self
layout:addWidget(child)
end
function Widget:calculateDimension (name)
function clamp(value, min, max)
if value < min then
value = min
elseif value > max then
value = max
end
return value
end
if self[name] then
self.dimensions[name] = clamp(self[name], 0, self.layout.root[name])
end
if self.dimensions[name] then
return self.dimensions[name]
end
local parent = self.parent
if not parent then
return self.layout
end
local parentDimension = parent:calculateDimension(name)
local parentFlow = parent.flow or 'y'
if (parentFlow == 'y' and name == 'width') or
(parentFlow == 'x' and name == 'height')
then
return parentDimension
end
local claimed = 0
local unsized = 1
for i, widget in ipairs(self.parent.children) do
if widget ~= self then
if widget[name] then
claimed = claimed + widget:calculateDimension(name)
if claimed > parentDimension then
claimed = parentDimension
end
else
unsized = unsized + 1
end
end
end
local size = (self.parent:calculateDimension(name) - claimed) / unsized
self.dimensions[name] = clamp(size, 0, self.layout.root[name])
return size
end
function Widget:calculatePosition (axis)
if self.position[axis] then
return self.position[axis]
end
local parent = self.parent
if not parent then
self.position[axis] = 0
return 0
end
local parentPos = parent:calculatePosition(axis)
local p = parentPos
local parentFlow = parent.flow or 'y'
for i, widget in ipairs(parent.children) do
if widget == self then
self.position[axis] = p
return p
end
if parentFlow == axis then
local dimension = (axis == 'x') and 'width' or 'height'
p = p + widget:calculateDimension(dimension)
end
end
self.position[axis] = 0
return 0
end
function Widget:getX ()
return self:calculatePosition('x')
end
function Widget:getY ()
return self:calculatePosition('y')
end
function Widget:getWidth ()
return self:calculateDimension('width')
end
function Widget:getHeight ()
return self:calculateDimension('height')
end
function Widget:setDimension (name, size)
local parentDimension = self.parent:calculateDimension(name)
local claimed = 0
for i, widget in ipairs(self.parent.children) do
if widget ~= self and widget[name] then
claimed = claimed + widget[name]
end
end
if claimed + size > parentDimension then
size = parentDimension - claimed
end
self[name] = size
end
function Widget:setWidth (size)
return self:setDimension('width', size)
end
function Widget:setHeight (size)
return self:setDimension('height', size)
end
function Widget:getOrigin ()
return self:getX(), self:getY()
end
function Widget:getExtent ()
local x, y = self:getX(), self:getY()
return x + self:getWidth(), y + self:getHeight()
end
function Widget:getRectangle (useMargin, usePadding)
local x1, y1 = self:getOrigin()
local x2, y2 = self:getExtent()
local function shrink(amount)
x1 = x1 + amount
y1 = y1 + amount
x2 = x2 - amount
y2 = y2 - amount
end
if useMargin then
shrink(self.margin or 0)
end
if usePadding then
shrink(self.padding or 0)
end
return x1, y1, x2, y2
end
function Widget:isAt (x, y)
local x1, y1, x2, y2 = self:getRectangle()
return (x1 < x) and (x2 > x) and (y1 < y) and (y2 > y)
end
function Widget:getAncestors (includeSelf)
local instance = includeSelf and self or self.parent
return function()
local widget = instance
if not widget then return end
instance = widget.parent
return widget
end
end
function Widget:update ()
self.layout:update()
end
-- event binders
Event.injectBinders(Widget)
return Widget

35
luigi/widget/sash.lua Normal file
View File

@@ -0,0 +1,35 @@
local Widget = require((...):gsub('%.[^.]*$', ''))
local Sash = Widget:extend()
function Sash:constructor(layout, data)
Widget.constructor(self, layout, data)
self:onPressDrag(function(event)
local axis = self.parent.flow
if axis == 'x' then
dimension = 'width'
else
axis = 'y'
dimension = 'height'
end
local prevSibling = self:getPrevious()
local nextSibling = self:getNext()
local prevSize = prevSibling and prevSibling[dimension]
local nextSize = nextSibling and nextSibling[dimension]
if prevSize then
prevSibling:setDimension(dimension,
event[axis] - prevSibling:calculatePosition(axis))
end
if nextSize then
nextSibling:setDimension(dimension,
nextSibling:calculatePosition(axis) +
nextSibling[dimension] - event[axis])
end
layout:update(true)
end)
end
return Sash

52
luigi/widget/slider.lua Normal file
View File

@@ -0,0 +1,52 @@
local Widget = require((...):gsub('%.[^.]*$', ''))
local Slider = Widget:extend()
function Slider:constructor(layout, data)
Widget.constructor(self, layout, data)
local function getCenter()
return self:getX() + self:getWidth() / 2
end
local position = 0.5
self:onPressDrag(function(event)
local x1, y1, x2, y2 = self:getRectangle(true, true)
position = (event.x - x1) / (x2 - x1)
if position < 0 then position = 0 end
if position > 1 then position = 1 end
self:update()
end)
self:onDisplay(function(event)
-- event:yield()
local x1, y1, x2, y2 = self:getRectangle(true, true)
local padding = self.padding or 0
self.layout.window:fill(
x1,
y1 + (y2 - y1) / 2 - padding / 2,
x2,
y1 + (y2 - y1) / 2 + padding / 2,
self.background, -(self.bend or 0)
)
self.layout.window:fill(
x1 + position * (x2 - x1) - padding,
y1 + padding,
x1 + position * (x2 - x1) + padding,
y2 - padding,
self.background, self.bend
)
self.layout.window:outline(
x1 + position * (x2 - x1) - padding,
y1 + padding,
x1 + position * (x2 - x1) + padding,
y2 - padding,
self.outline
)
return false
end)
end
return Slider

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

@@ -0,0 +1,9 @@
local Widget = require((...):gsub('%.[^.]*$', ''))
local Text = Widget:extend()
function Text:constructor(layout, data)
Widget.constructor(self, layout, data)
end
return Text

125
luigi/window.lua Normal file
View File

@@ -0,0 +1,125 @@
local ROOT = (...):gsub('[^.]*$', '')
local Base = require(ROOT .. 'base')
local Hooker = require(ROOT .. 'hooker')
local Window = Base:extend()
local unpack = table.unpack or _G.unpack
function Window:constructor (input)
self.input = input
self.isMousePressed = false
self.isManagingInput = false
self.hooked = {}
self.hooks = {}
end
function Window:hook (key, method)
self.hooks[#self.hooks + 1] = Hooker.hook(key, method)
end
function Window:unhook ()
for _, item in ipairs(self.hooks) do
Hooker.unhook(item)
end
self.hooks = {}
end
function Window:manageInput (input)
if self.isManagingInput then
return
end
self.isManagingInput = true
self:hook('draw', function ()
input:handleDisplay()
end)
self:hook('resize', function (width, height)
return input:handleReshape(width, height)
end)
self:hook('mousepressed', function (x, y, button)
self.isMousePressed = true
return input:handlePressStart(button, x, y)
end)
self:hook('mousereleased', function (x, y, button)
self.isMousePressed = false
return input:handlePressEnd(button, x, y)
end)
self:hook('mousemoved', function (x, y, dx, dy)
if self.isMousePressed then
return input:handlePressedMotion(x, y)
else
return input:handleMotion(x, y)
end
end)
self:hook('keypressed', function (key, isRepeat)
return input:handleKeyboard(key, love.mouse.getX(), love.mouse.getY())
end)
end
function Window:show (width, height, title)
local currentWidth, currentHeight, flags = love.window.getMode()
love.window.setMode(width or currentWidth, height or currentHeight, flags)
if title then
love.window.setTitle(title)
end
self:manageInput(self.input)
end
function Window:hide ()
if not self.isManagingInput then
return
end
self.isManagingInput = false
self:unhook()
end
local function setColor (color)
love.graphics.setColor(color)
end
function Window:fill (x1, y1, x2, y2, color)
setColor(color)
love.graphics.rectangle('fill', x1, y1, x2 - x1, y2 - y1)
end
function Window:outline (x1, y1, x2, y2, color)
setColor(color)
love.graphics.rectangle('line', x1, y1, x2 - x1, y2 - y1)
end
function Window:write (x, y, x1, y1, x2, y2, text, font)
local width, height = x2 - x1, y2 - y1
if width < 1 or height < 1 then
return
end
local sx, sy, sw, sh = love.graphics.getScissor()
love.graphics.setScissor(x1, y1, width, height)
local oldFont = love.graphics.getFont()
love.graphics.setFont(font.font)
setColor(font.color)
local layout = font.layout
love.graphics.printf(text, x, y, layout.width or width, layout.align)
love.graphics.setScissor(sx, sy, sw, sh)
love.graphics.setFont(oldFont)
end
function Window:update (reshape)
if reshape then
for i, widget in ipairs(self.input.layout.widgets) do
widget.position = {}
widget.dimensions = {}
widget.fontData = nil
end
end
end
return Window