scissor me baby

This commit is contained in:
airstruck
2015-12-08 17:14:49 -05:00
parent d11ea06503
commit 5e6cd8c29e
12 changed files with 138 additions and 71 deletions

View File

@@ -52,27 +52,27 @@ return { id = 'mainWindow', type = 'panel',
{ type = 'panel', id = 'mainCanvas' },
{ type = 'sash' },
{ type = 'panel', id = 'rightSideBox', width = 200, minwidth = 64, scroll = true,
{ id = 'flowTest',
{ minheight = 64,
{ id = 'flowTest', height = 'auto', minheight = 128,
{
{ type = 'label', text = 'Slider' },
{ type = 'slider', id = 'slidey', value = 0 },
{ type = 'slider', id = 'slidey', width = false },
},
{ minheight = 64,
{
{ type = 'label', text = 'Stepper' },
{ type = 'stepper',
{ type = 'stepper', id = 'stepper', width = false,
{ value = 1, text = 'Thing One' },
{ value = 2, text = 'Thing Two' },
{ value = 3, text = 'Thing Three' },
},
},
{ minheight = 64,
{
{ type = 'label', text = 'Progress' },
{ type = 'progress', id = 'progressBar', },
{ type = 'progress', id = 'progressBar', width = false },
},
},
{
{ type = 'label', text = 'Above layout' },
{ type = 'check', text = 'Flow horizontal', id = 'flowToggle', },
{ height = 'auto',
{ type = 'label', text = 'Flow test' },
{ type = 'check', text = 'Vertical controls', id = 'flowToggle', },
{ type = 'label', text = 'Some radio widgets' },
{ type = 'radio', text = 'One fish' },
{ type = 'radio', text = 'Two fish' },

View File

@@ -11,9 +11,10 @@ layout.slidey:onChange(function (event)
end)
layout.flowToggle:onChange(function (event)
layout.flowTest.flow = event.value and 'x' or 'y'
layout.slidey.flow = event.value and 'y' or 'x'
layout.progressBar.flow = event.value and 'y' or 'x'
layout.stepper.flow = event.value and 'y' or 'x'
layout.flowTest.flow = event.value and 'x' or 'y'
end)
layout.newButton:onPress(function (event)

View File

@@ -9,6 +9,7 @@ return {
type = 'button',
align = 'center middle',
width = 48,
height = 48,
slices = function (self)
if self.focused or self.hovered or self.pressed then
return nil -- fall back to theme default

View File

@@ -283,7 +283,9 @@ To get the calculated height, use `Widget:getHeight`.
Attribute.height = {}
function Attribute.height.set (widget, value)
value = value and math.max(value, widget.minheight or 0)
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
@@ -415,6 +417,8 @@ and horizontal alignment is defined by either 'left', 'center', or 'right'.
For example, `align = 'top left'`
- This attribute cascades.
@attrib align
--]]--
Attribute.align = {}
@@ -424,6 +428,10 @@ function Attribute.align.set (widget, value)
widget.textData = nil
end
function Attribute.align.get (widget)
return widget.attributes.align or widget.parent and widget.parent.align
end
--[[--
Wrap text onto multiple lines.

View File

@@ -12,6 +12,8 @@ local Text = require((...) .. '.text')
local IntOut = ffi.typeof 'int[1]'
local stack = {}
-- create window and renderer
sdl.setHint(sdl.HINT_VIDEO_ALLOW_SCREENSAVER, '1')
@@ -257,6 +259,12 @@ Backend.setScissor = function (x, y, w, h)
sdl.renderSetClipRect(renderer, lastScissor)
end
Backend.getScissor = function ()
if lastScissor ~= nil then
return lastScissor.x, lastScissor.y, lastScissor.w, lastScissor.h
end
end
function Backend.hide (layout)
for _, item in ipairs(layout.hooks) do
Hooker.unhook(item)
@@ -269,15 +277,14 @@ local function hook (layout, key, method, hookLast)
callback, key, method, hookLast)
end
local stack = {}
Backend.pop = function ()
local history = stack[#stack]
local color = history.color or { 0, 0, 0, 255 }
lastColor = history.color or { 0, 0, 0, 255 }
lastScissor = history.scissor
sdl.setRenderDrawColor(renderer,
color[1], color[2], color[3], color[4] or 255)
sdl.renderSetClipRect(renderer, history.scissor) -- Backend.setScissor(history.scissor)
lastColor[1], lastColor[2], lastColor[3], lastColor[4] or 255)
sdl.renderSetClipRect(renderer, lastScissor) -- Backend.setScissor(history.scissor)
stack[#stack] = nil
end

View File

@@ -73,6 +73,8 @@ end
Backend.setScissor = love.graphics.setScissor
Backend.getScissor = love.graphics.getScissor
function Backend.hide (layout)
for _, item in ipairs(layout.hooks) do
Hooker.unhook(item)

View File

@@ -11,6 +11,25 @@ local Renderer = Base:extend()
local imageCache = {}
local sliceCache = {}
local function intersectScissor (x, y, w, h)
local sx, sy, sw, sh = Backend.getScissor()
if not sx then
return Backend.setScissor(x, y, w, h)
end
local x1 = math.max(sx, x)
local y1 = math.max(sy, y)
local x2 = math.min(sx + sw, x + w)
local y2 = math.min(sy + sh, y + h)
if x2 > x1 and y2 > y1 then
Backend.setScissor(x1, y1, x2 - x1, y2 - y1)
else
-- HACK
Backend.setScissor(-100, -100, 1, 1)
end
end
function Renderer:loadImage (path)
if not imageCache[path] then
imageCache[path] = Backend.Image(path)
@@ -194,8 +213,6 @@ function Renderer:renderIconAndText (widget)
return
end
local parentY = widget.parent and widget.parent:getY() or 0
-- calculate position for icon and text based on alignment and padding
local iconX, iconY, x1, y1, x2, y2 = self:positionIcon(
widget, x, y, x + w, y + h)
@@ -239,14 +256,12 @@ function Renderer:renderIconAndText (widget)
end
Backend.push()
Backend.setScissor(x, math.max(y, parentY), w, h)
intersectScissor(x, y, w, h)
-- draw the icon
if icon then
iconX, iconY = math.floor(iconX), math.floor(iconY)
if widget.tint then
Backend.setColor(widget.tint)
end
Backend.draw(icon, iconX, iconY)
end
@@ -277,9 +292,9 @@ function Renderer:render (widget)
Backend.push()
if widget.parent then
local parentY = widget.parent:getY()
Backend.setScissor(x, math.max(y, parentY), w, h)
local parent = widget.parent
if parent then
intersectScissor(x, y, w, h)
else
Backend.setScissor()
end
@@ -288,11 +303,10 @@ function Renderer:render (widget)
self:renderOutline(widget)
self:renderSlices(widget)
self:renderIconAndText(widget)
self:renderChildren(widget)
Backend.pop()
return self:renderChildren(widget)
end)
Event.Display:emit(widget, { target = widget })
end

View File

@@ -55,11 +55,11 @@ return function (config)
end
local function getSashHeight (self)
return self.parent.flow ~= 'x' and 4
return self.parent and self.parent.flow ~= 'x' and 4
end
local function getSashWidth (self)
return self.parent.flow == 'x' and 4
return self.parent and self.parent.flow == 'x' and 4
end
local function getTextSlices (self)
@@ -75,10 +75,10 @@ return function (config)
color = textColor,
minheight = 28,
minwidth = 28,
align = 'center middle',
},
button = {
type = 'control',
align = 'center middle',
padding = 6,
slices = getButtonSlices,
minwidth = 24,
@@ -92,11 +92,13 @@ return function (config)
color = textColor,
icon = getCheckOrRadioIcon,
padding = 4,
align = 'left middle',
},
label = {
type = 'control',
background = backColor,
padding = 4,
align = 'left middle',
},
menu = {
height = 24,
@@ -131,6 +133,7 @@ return function (config)
color = textColor,
icon = getCheckOrRadioIcon,
padding = 4,
align = 'left middle',
},
sash = {
background = getSashBackground,
@@ -153,7 +156,6 @@ return function (config)
slices = resources .. 'button_pressed.png',
},
['stepper.item'] = {
type = 'control',
align = 'center middle',
color = textColor,
},

View File

@@ -291,15 +291,11 @@ function Widget:addChild (data)
return child
end
local function clamp (value, min, max)
return value < min and min or value > max and max or value
end
local function checkReshape (widget)
if widget.needsReshape then
widget.position = {}
widget.dimensions = {}
widget.needsReshape = false
widget.needsReshape = nil
end
end
@@ -313,19 +309,21 @@ function Widget:calculateDimension (name)
local min = (name == 'width') and (self.minwidth or 0)
or (self.minheight or 0)
local windowWidth, windowHeight = Backend.getWindowSize()
local max = name == 'width' and windowWidth or windowHeight
if self[name] then
self.dimensions[name] = clamp(self[name], min, max)
if self[name] == 'auto' then
self.dimensions[name] = self:calculateDimensionMinimum(name)
return self.dimensions[name]
end
self.dimensions[name] = math.max(self[name], min)
return self.dimensions[name]
end
local parent = self.parent
if not parent then
self.dimensions[name] = max
local windowWidth, windowHeight = Backend.getWindowSize()
local size = name == 'width' and windowWidth or windowHeight
self.dimensions[name] = size
return self.dimensions[name]
end
@@ -333,21 +331,23 @@ function Widget:calculateDimension (name)
parentDimension = parentDimension - (parent.margin or 0) * 2
parentDimension = parentDimension - (parent.padding or 0) * 2
local parentFlow = parent.flow or 'y'
if (parentFlow == 'y' and name == 'width') or
(parentFlow == 'x' and name == 'height')
then
self.dimensions[name] = clamp(parentDimension, min, max)
if (parentFlow ~= 'x' and name == 'width')
or (parentFlow == 'x' and name == 'height') then
self.dimensions[name] = math.max(parentDimension, min)
return self.dimensions[name]
end
local claimed = 0
local unsized = 1
for i, widget in ipairs(self.parent) do
if widget ~= self then
if widget[name] then
claimed = claimed + widget:calculateDimension(name)
if claimed > parentDimension then
claimed = parentDimension
local value = widget[name]
if value == 'auto' then
if not widget.dimensions[name] then
widget.dimensions[name] = widget:calculateDimensionMinimum(name)
end
claimed = claimed + widget.dimensions[name]
elseif value then
claimed = claimed + value
else
unsized = unsized + 1
end
@@ -355,13 +355,13 @@ function Widget:calculateDimension (name)
end
local size = (parentDimension - claimed) / unsized
size = clamp(size, min, max)
size = math.max(size, min)
self.dimensions[name] = size
return size
end
local function calculateRootPosition (self, axis)
local value = (axis == 'x' and self.left) or (axis == 'y' and self.top)
local value = (axis == 'x' and self.left) or (axis ~= 'x' and self.top)
if value then
self.position[axis] = value
@@ -370,9 +370,9 @@ local function calculateRootPosition (self, axis)
local ww, wh = Backend.getWindowSize()
if axis == 'x' and self.width then
if axis == 'x' and type(self.width) == 'number' then
value = (ww - self.width) / 2
elseif axis == 'y' and self.height then
elseif axis ~= 'x' and type(self.height) == 'number' then
value = (wh - self.height) / 2
else
value = 0
@@ -394,7 +394,7 @@ function Widget:calculatePosition (axis)
return calculateRootPosition(self, axis)
else
scroll = axis == 'x' and (parent.scrollX or 0)
or axis == 'y' and (parent.scrollY or 0)
or axis ~= 'x' and (parent.scrollY or 0)
end
local parentPos = parent:calculatePosition(axis)
local p = parentPos - scroll
@@ -415,6 +415,24 @@ function Widget:calculatePosition (axis)
return 0
end
function Widget:calculateDimensionMinimum (name)
local space = (self.margin or 0) * 2 + (self.padding or 0) * 2
local min = name == 'width' and 'minwidth' or 'minheight'
local value = space
for _, child in ipairs(self) do
if (name == 'width' and self.flow == 'x')
or (name == 'height' and self.flow ~= 'x') then
value = value + child:calculateDimensionMinimum(name)
else
value = math.max(value, child:calculateDimensionMinimum(name))
end
end
local dim = self[name]
dim = type(dim) == 'number' and dim
return math.max(value, dim or 0, self[min] or 0)
end
--[[--
Get the widget's X coordinate.

View File

@@ -8,7 +8,7 @@ between 0 and 1 (inclusive) to change the width of the bar.
--]]--
return function (self)
self.value = 0
self.value = self.value or 0
local pad = self:addChild {
width = 0,
@@ -35,7 +35,7 @@ return function (self)
y1 = y1 + min
bar.width = false
bar.height = false
pad.height = h - (self.value * (y2 - y1) + min)
pad.height = math.ceil(h - (self.value * (y2 - y1) + min))
end
end)
end

View File

@@ -44,8 +44,6 @@ local function setDimension (widget, name, size)
or (widget.minheight or 0)
widget[name] = math.max(size, min)
return widget[name]
end
@@ -79,10 +77,9 @@ return function (self)
end
if nextSize then
setDimension(nextSibling, dimension,
nextSibling:calculatePosition(axis) +
nextSibling:calculateDimension(dimension) - event[axis])
nextSibling:calculatePosition(axis) + nextSize - event[axis])
end
self.parent:reshape()
end)
end

View File

@@ -21,13 +21,22 @@ return function (self)
self[index] = nil
end
local decrement = self:addChild { type = 'stepper.left' }
local before = self:addChild { type = 'stepper.left' }
local view = self:addChild()
local increment = self:addChild { type = 'stepper.right' }
local after = self:addChild { type = 'stepper.right' }
self:onReshape(function (event)
decrement.width = decrement:getHeight()
increment.width = increment:getHeight()
if self.flow == 'x' then
before.height = false
after.height = false
before.width = 0
after.width = 0
else
before.width = false
after.width = false
before.height = 0
after.height = 0
end
end)
local function updateValue ()
@@ -35,25 +44,33 @@ return function (self)
self.value = item.value
view[1] = nil
view:addChild(item)
item:reshape()
view:reshape()
end
decrement:onPress(function (event)
local function decrement ()
if not self.items then return end
self.index = self.index - 1
if self.index < 1 then
self.index = #self.items
end
updateValue()
end)
end
increment:onPress(function (event)
local function increment ()
if not self.items then return end
self.index = self.index + 1
if self.index > #self.items then
self.index = 1
end
updateValue()
end
before:onPress(function (event)
if self.flow == 'x' then decrement() else increment() end
end)
after:onPress(function (event)
if self.flow == 'x' then increment() else decrement() end
end)
updateValue()