mirror of
https://github.com/airstruck/luigi.git
synced 2025-12-18 18:06:44 +00:00
The `solid` attribute determines whether to propagate events downwards. When the widget being interacted with is not solid, events propagate down to the next layout or to underlying handlers (for example love callbacks). This is set by the theme and works mostly behind the scenes. The `modal` attribute can be placed on a layout root to prevent input events from propagating downwards, even those that fall outside of the layout.
595 lines
12 KiB
Lua
595 lines
12 KiB
Lua
--[[--
|
|
Widget attributes.
|
|
|
|
This module defines "attributes" (special fields) that are
|
|
recognized by all widgets. Their interpretation may vary
|
|
depending on the `type` of widget. Some widget types may also
|
|
recognize additional attributes.
|
|
|
|
Setting attributes can have side effects. For example, setting
|
|
`height` or `width` causes the parent widget and its descendants
|
|
to recalculate their size and position.
|
|
--]]--
|
|
|
|
local ROOT = (...):gsub('[^.]*$', '')
|
|
|
|
local Attribute = {}
|
|
|
|
local function cascade (widget, attribute)
|
|
local value = rawget(widget, 'attributes')[attribute]
|
|
if value ~= nil then return value end
|
|
local parent = rawget(widget, 'parent')
|
|
return parent and parent[attribute]
|
|
end
|
|
|
|
--[[--
|
|
Type of widget.
|
|
|
|
Should contain a string identifying the widget's type.
|
|
After the layout is built, may be replaced by an array
|
|
of strings identifying multiple types. This is used
|
|
internally by some widgets to provide information about
|
|
the widget's state to the theme (themes describe the
|
|
appearance of widgets according to their type).
|
|
|
|
If a type is registered with the widget's layout, the registered
|
|
type initializer function will run once when the widget is constructed.
|
|
|
|
@see Widget.register
|
|
|
|
@attrib type
|
|
--]]--
|
|
Attribute.type = {}
|
|
|
|
function Attribute.type.set (widget, value)
|
|
local oldType = widget.attributes.type
|
|
|
|
widget.attributes.type = value
|
|
|
|
if value and not widget.hasType then
|
|
widget.hasType = true
|
|
local Widget = require(ROOT .. 'widget')
|
|
local decorate = Widget.typeDecorators[value]
|
|
|
|
if decorate then
|
|
decorate(widget)
|
|
end
|
|
end
|
|
end
|
|
|
|
--[[--
|
|
Widget identifier.
|
|
|
|
Should contain a unique string identifying the widget, if present.
|
|
|
|
A reference to the widget will be stored in the associated layout
|
|
in a property having the same name as the widget's id.
|
|
|
|
Setting this attribute re-registers the widget with its layout.
|
|
|
|
@attrib id
|
|
--]]--
|
|
Attribute.id = {}
|
|
|
|
function Attribute.id.set (widget, value)
|
|
local layout = widget.layout.master or widget.layout
|
|
local oldValue = widget.attributes.id
|
|
|
|
if oldValue then
|
|
layout[oldValue] = nil
|
|
end
|
|
|
|
if value then
|
|
layout[value] = widget
|
|
end
|
|
|
|
widget.attributes.id = value
|
|
end
|
|
|
|
-- TODO: formalize this bitfield somewhere
|
|
local function parseKeyCombo (value)
|
|
local mainKey = (value):match '[^%-]+$'
|
|
local alt = (value):match 'alt%-' and 1 or 0
|
|
local ctrl = (value):match 'ctrl%-' and 2 or 0
|
|
local shift = (value):match 'shift%-' and 4 or 0
|
|
local modifierFlags = alt + ctrl + shift
|
|
|
|
return mainKey, modifierFlags
|
|
end
|
|
|
|
--[[--
|
|
Widget value.
|
|
|
|
Some widget types expect the value to be of a specific type and
|
|
within a specific range. For example, `slider` and `progress`
|
|
widgets expect a normalized number, `text` widgets expect
|
|
a string, and `check` and `radio` widgets expect a boolean.
|
|
|
|
Setting this attribute bubbles the `Change` event.
|
|
|
|
@attrib value
|
|
--]]--
|
|
Attribute.value = {}
|
|
|
|
function Attribute.value.set (widget, value)
|
|
local oldValue = widget.value
|
|
widget.attributes.value = value
|
|
widget:bubbleEvent('Change', { value = value, oldValue = oldValue })
|
|
end
|
|
|
|
--[[--
|
|
Solidity.
|
|
|
|
Should true or false.
|
|
|
|
@attrib icon
|
|
--]]--
|
|
Attribute.solid = {}
|
|
|
|
function Attribute.solid.set (widget, value)
|
|
widget.attributes.solid = value
|
|
end
|
|
|
|
Attribute.solid.get = cascade
|
|
|
|
--[[--
|
|
Context menu.
|
|
|
|
- This attribute cascades.
|
|
|
|
@attrib context
|
|
--]]--
|
|
Attribute.context = {}
|
|
|
|
function Attribute.context.set (widget, value)
|
|
widget.attributes.context = value
|
|
if not value then return end
|
|
value.isContextMenu = true
|
|
widget.layout:createWidget { type = 'menu', value }
|
|
end
|
|
|
|
Attribute.context.get = cascade
|
|
|
|
--[[--
|
|
Widget style.
|
|
|
|
Should contain a string or array of strings identifying
|
|
style rules to be applied to the widget. When resolving
|
|
any attribute with a `nil` value, these style rules are
|
|
searched for a corresponding attribute.
|
|
|
|
Setting this attribute resets the `Font` and `Text` object
|
|
associated with this widget.
|
|
|
|
Setting this attribute recalculates the size and position
|
|
of the parent widget and its descendants.
|
|
|
|
@attrib style
|
|
--]]--
|
|
Attribute.style = {}
|
|
|
|
function Attribute.style.set (widget, value)
|
|
widget.attributes.style = value
|
|
widget.fontData = nil
|
|
widget.textData = nil
|
|
widget.reshape(widget.parent or widget)
|
|
end
|
|
|
|
--[[--
|
|
Status message.
|
|
|
|
Should contain a string with a short message describing the
|
|
purpose or state of the widget.
|
|
|
|
This message will appear in the last created `status` widget
|
|
in the same layout, or in the master layout if one exists.
|
|
|
|
- This attribute cascades.
|
|
|
|
@attrib status
|
|
--]]--
|
|
Attribute.status = {}
|
|
|
|
Attribute.status.get = cascade
|
|
|
|
--[[--
|
|
Scroll ability.
|
|
|
|
Should contain `true` or `false` (or `nil`).
|
|
|
|
If set to `true`, moving the scroll wheel over the widget will adjust
|
|
its scroll position when the widget's contents overflow its boundary.
|
|
|
|
@attrib scroll
|
|
--]]--
|
|
Attribute.scroll = {}
|
|
|
|
--[[--
|
|
Keyboard Attributes.
|
|
|
|
@section keyboard
|
|
--]]--
|
|
|
|
--[[--
|
|
Focusable.
|
|
|
|
Should contain `true` if the widget can be focused by pressing the tab key.
|
|
|
|
@attrib focusable
|
|
--]]--
|
|
Attribute.focusable = {}
|
|
|
|
--[[--
|
|
Keyboard shortcut.
|
|
|
|
Should contain a string representing a key and optional modifiers,
|
|
separated by dashes; for example `'ctrl-c'` or `'alt-shift-escape'`.
|
|
|
|
Pressing this key combination bubbles a `Press` event on the widget,
|
|
as if it had been pressed with a mouse or touch interface.
|
|
|
|
Setting this attribute re-registers the widget with its layout.
|
|
|
|
@attrib shortcut
|
|
--]]--
|
|
Attribute.shortcut = {}
|
|
|
|
function Attribute.shortcut.set (widget, value)
|
|
local layout = widget.layout.master or widget.layout
|
|
local oldValue = widget.attributes.shortcut
|
|
|
|
if oldValue then
|
|
local mainKey, modifierFlags = parseKeyCombo(oldValue)
|
|
layout.shortcuts[modifierFlags][mainKey] = nil
|
|
end
|
|
|
|
if value then
|
|
local mainKey, modifierFlags = parseKeyCombo(value)
|
|
layout.shortcuts[modifierFlags][mainKey] = widget
|
|
end
|
|
|
|
widget.attributes.shortcut = value
|
|
end
|
|
|
|
--[[--
|
|
Size Attributes.
|
|
|
|
Setting these attributes recalculates the size and position
|
|
of the parent widget and its descendants.
|
|
|
|
@section size
|
|
--]]--
|
|
|
|
--[[--
|
|
Flow axis.
|
|
|
|
Should equal either `'x'` or `'y'`. Defaults to `'y'`.
|
|
|
|
This attribute determines the placement and default dimensions
|
|
of any child widgets.
|
|
|
|
When flow is `'x'`, the `height` of child widgets defaults
|
|
to this widget's height, and each child is placed to the
|
|
right of the previous child. When flow is `'y'`, the `width`
|
|
of child widgets defaults to this widget's width, and each
|
|
child is placed below the previous child.
|
|
|
|
Setting this attribute resets the `Text` object associated
|
|
with this widget.
|
|
|
|
@attrib flow
|
|
--]]--
|
|
Attribute.flow = {}
|
|
|
|
function Attribute.flow.set (widget, value)
|
|
widget.attributes.flow = value
|
|
widget.textData = nil
|
|
widget.reshape(widget.parent or widget)
|
|
end
|
|
|
|
--[[--
|
|
Width.
|
|
|
|
This attribute may not always hold a numeric value.
|
|
To get the calculated width, use `Widget:getWidth`.
|
|
|
|
Setting this attribute when the `wrap` attribute is
|
|
also present resets the `Text` object associated
|
|
with this widget.
|
|
|
|
@attrib width
|
|
--]]--
|
|
Attribute.width = {}
|
|
|
|
function Attribute.width.set (widget, value)
|
|
if value ~= 'auto' then
|
|
value = value and math.max(value, widget.minwidth or 0)
|
|
end
|
|
widget.attributes.width = value
|
|
if widget.wrap then
|
|
widget.textData = nil
|
|
end
|
|
widget.reshape(widget.parent or widget)
|
|
end
|
|
|
|
--[[--
|
|
Height.
|
|
|
|
This attribute may not always hold a numeric value.
|
|
To get the calculated height, use `Widget:getHeight`.
|
|
|
|
@attrib height
|
|
--]]--
|
|
Attribute.height = {}
|
|
|
|
function Attribute.height.set (widget, value)
|
|
if value ~= 'auto' then
|
|
value = value and math.max(value, widget.minheight or 0)
|
|
end
|
|
widget.attributes.height = value
|
|
widget.reshape(widget.parent or widget)
|
|
end
|
|
|
|
--[[--
|
|
Minimum width.
|
|
|
|
@attrib minwidth
|
|
--]]--
|
|
Attribute.minwidth = {}
|
|
|
|
function Attribute.minwidth.set (widget, value)
|
|
local attributes = widget.attributes
|
|
attributes.minwidth = value
|
|
if type(value) == 'number' then
|
|
local current = attributes.width
|
|
if type(current) == 'number' then
|
|
attributes.width = math.max(current, value)
|
|
end
|
|
end
|
|
widget.reshape(widget.parent or widget)
|
|
end
|
|
|
|
--[[--
|
|
Minimum height.
|
|
|
|
@attrib minheight
|
|
--]]--
|
|
Attribute.minheight = {}
|
|
|
|
function Attribute.minheight.set (widget, value)
|
|
local attributes = widget.attributes
|
|
attributes.minheight = value
|
|
if type(value) == 'number' then
|
|
local current = attributes.height
|
|
if type(current) == 'number' then
|
|
attributes.height = math.max(current, value)
|
|
end
|
|
end
|
|
widget.reshape(widget.parent or widget)
|
|
end
|
|
|
|
--[[--
|
|
Font Attributes.
|
|
|
|
Setting these attributes resets the Font and Text
|
|
objects associated with the widget.
|
|
|
|
@section font
|
|
--]]--
|
|
|
|
--[[--
|
|
Font path.
|
|
|
|
Should contain a path to a TrueType font to use for displaying
|
|
this widget's `text`.
|
|
|
|
- This attribute cascades.
|
|
|
|
@attrib 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)
|
|
widget.attributes.font = value
|
|
resetFont(widget)
|
|
end
|
|
|
|
Attribute.font.get = cascade
|
|
|
|
--[[--
|
|
Font size.
|
|
|
|
Should contain a number representing the size of the font, in points.
|
|
Defaults to 12.
|
|
|
|
- This attribute cascades.
|
|
|
|
@attrib size
|
|
--]]--
|
|
Attribute.size = {}
|
|
|
|
function Attribute.size.set (widget, value)
|
|
widget.attributes.size = value
|
|
widget.fontData = nil
|
|
widget.textData = nil
|
|
end
|
|
|
|
Attribute.size.get = cascade
|
|
|
|
--[[--
|
|
Text Attributes.
|
|
|
|
Setting these attributes resets the Text object
|
|
associated with the widget.
|
|
|
|
@section text
|
|
--]]--
|
|
|
|
--[[--
|
|
Text to display.
|
|
|
|
@attrib text
|
|
--]]--
|
|
Attribute.text = {}
|
|
|
|
function Attribute.text.set (widget, value)
|
|
widget.attributes.text = value
|
|
widget.textData = nil
|
|
end
|
|
|
|
--[[--
|
|
Text color.
|
|
|
|
Should contain an array with 3 or 4 values (RGB or RGBA) from 0 to 255.
|
|
|
|
- This attribute cascades.
|
|
|
|
@attrib color
|
|
--]]--
|
|
Attribute.color = {}
|
|
|
|
function Attribute.color.set (widget, value)
|
|
widget.attributes.color = value
|
|
widget.textData = nil
|
|
end
|
|
|
|
Attribute.color.get = cascade
|
|
|
|
--[[--
|
|
Text and icon alignment.
|
|
|
|
Should contain a string defining vertical and horizontal alignment.
|
|
Vertical alignment is defined by either 'top', 'middle', or 'bottom',
|
|
and horizontal alignment is defined by either 'left', 'center', or 'right'.
|
|
|
|
For example, `align = 'top left'`
|
|
|
|
- This attribute cascades.
|
|
|
|
@attrib align
|
|
--]]--
|
|
Attribute.align = {}
|
|
|
|
function Attribute.align.set (widget, value)
|
|
widget.attributes.align = value
|
|
widget.textData = nil
|
|
end
|
|
|
|
Attribute.align.get = cascade
|
|
|
|
--[[--
|
|
Wrap text onto multiple lines.
|
|
|
|
Should contain `true` for multiline text, or `false` or `nil`
|
|
for a single line. Even text containing line breaks will display
|
|
as a single line when this attribute is not set to `true`.
|
|
|
|
- This attribute cascades.
|
|
|
|
@attrib wrap
|
|
--]]--
|
|
Attribute.wrap = {}
|
|
|
|
function Attribute.wrap.set (widget, value)
|
|
widget.attributes.wrap = value
|
|
widget.textData = nil
|
|
end
|
|
|
|
Attribute.wrap.get = cascade
|
|
|
|
--[[--
|
|
Visual Attributes.
|
|
|
|
@section visual
|
|
--]]--
|
|
|
|
--[[--
|
|
Background color.
|
|
|
|
Should contain an array with 3 or 4 values (RGB or RGBA) from 0 to 255.
|
|
|
|
@attrib background
|
|
--]]--
|
|
Attribute.background = {}
|
|
|
|
--[[--
|
|
Outline color.
|
|
|
|
Should contain an array with 3 or 4 values (RGB or RGBA) from 0 to 255.
|
|
|
|
@attrib outline
|
|
--]]--
|
|
Attribute.outline = {}
|
|
|
|
--[[--
|
|
Slice image.
|
|
|
|
Should contain a path to an image with "slices" to display for this widget.
|
|
|
|
@attrib slices
|
|
--]]--
|
|
Attribute.slices = {}
|
|
|
|
--[[--
|
|
Margin size.
|
|
|
|
The margin area occupies space outside of the `outline` and `slices`.
|
|
|
|
@attrib margin
|
|
--]]--
|
|
Attribute.margin = {}
|
|
|
|
function Attribute.margin.set (widget, value)
|
|
widget.attributes.margin = value
|
|
widget.textData = nil
|
|
widget:reshape()
|
|
end
|
|
|
|
--[[--
|
|
Padding size.
|
|
|
|
The padding area occupies space inside the `outline` and `slices`,
|
|
and outside the space where the `icon` and `text` and any
|
|
child widgets appear.
|
|
|
|
@attrib padding
|
|
--]]--
|
|
Attribute.padding = {}
|
|
|
|
function Attribute.padding.set (widget, value)
|
|
widget.attributes.padding = value
|
|
widget.textData = nil
|
|
widget:reshape()
|
|
end
|
|
|
|
--[[--
|
|
Icon path.
|
|
|
|
Should contain a path to an image file.
|
|
|
|
@attrib icon
|
|
--]]--
|
|
Attribute.icon = {}
|
|
|
|
function Attribute.icon.set (widget, value)
|
|
widget.attributes.icon = value
|
|
widget.textData = nil
|
|
end
|
|
|
|
|
|
return Attribute
|