From f6ffea1e02837be83175b645b080b59ff0ff52a7 Mon Sep 17 00:00:00 2001 From: Paul Liverman III Date: Wed, 8 Jun 2016 21:44:27 -0700 Subject: [PATCH] BUGGED AND BROKEN! :D --- demo/button.9.png | Bin 0 -> 1280 bytes demo/main.lua | 9 +- demo/pop/elements/box.lua | 32 +- demo/pop/elements/element.lua | 20 +- demo/pop/elements/text.lua | 20 +- demo/pop/third-party/patchy.lua | 530 ++++++++++++++++++++++++++++++++ demo/window.9.png | Bin 0 -> 4201 bytes lib/pop/elements/box.lua | 32 +- lib/pop/elements/element.lua | 20 +- lib/pop/elements/text.lua | 20 +- lib/pop/third-party/patchy.lua | 530 ++++++++++++++++++++++++++++++++ src/pop/elements/box.moon | 18 +- 12 files changed, 1194 insertions(+), 37 deletions(-) create mode 100644 demo/button.9.png create mode 100644 demo/pop/third-party/patchy.lua create mode 100644 demo/window.9.png create mode 100644 lib/pop/third-party/patchy.lua diff --git a/demo/button.9.png b/demo/button.9.png new file mode 100644 index 0000000000000000000000000000000000000000..dfb6c0aaa8d0fe307a622ae918232cfdb086dd4e GIT binary patch literal 1280 zcmah}TWHfz7>-kP4maHJMG*<{P1hzTy{(7UIh*RrOqpHLIuMp6r(M{R6O%KW4uv@p z6hRbx82F&*ix&_CK}6;rMZ6&B#0#R%r>Q7TAH)lK(sgyhih<<(=kR^s|DXSoqg|u6N7>21&c1UTuir!nhlz!K>9l1)E)g+N6-8e@IijA0d4fi27X()M=Mv7M4dk3{L z%;G6MlO2phh*WZ4=w-LMmV&qOh(pXgbi1aV1?yZSR=p*b;;c#JJg@vT_$2$2MX^ ze!-^-VSx<;KL`XPkw`Nu3P9imkq4p=L|{OKg20v^j(W4T9861F%f9F?#`P0oL7p!Z z3jRXCkL^4UqS2_QA&NeV@Hs`3D6Y?R*3T(O$Wd+GB04r%Pf_W^LnOx0NN2BLSQS~* zDW{1hjCU1_2Y$i3QVFQ36`VC>50;#3Dvt({flT61ET~{;2~S3Mb54F!m?o8Mhd}p2IIlGjCb{8f}MVBvGMK@yGu7UTyW;0k)#k5 z<7n-gDsT->eXWbRrShTt$HkCRW471;l0M3*Qj$xwEI1? zGIn%R8C+!2X|?HCE^j*9M@RZvQfkk*&p$i|4Tppkhu=J%su@o$Yj}0={ny$nqpME! zJa~99{QcF4)a#yU8f(ryId`Pv=j-1mFMPT2ZU6Y~15ai;GZSlnoS(jyt^Yh(JhpPr zSoG2IH7^stI-fOOKHgJ*_1&eTzf*0A7jn&#+Li3)5oS>pV;y(>MPp>g33%)B_vGoi zu?ug<64STuZuofbTktWz>h9$3orm(1GmX8>{UsYa4v2T2djF(kA|+kf(mVPGtM{Dz literal 0 HcmV?d00001 diff --git a/demo/main.lua b/demo/main.lua index a83f29f..eb9509e 100644 --- a/demo/main.lua +++ b/demo/main.lua @@ -1,5 +1,5 @@ local lg = love.graphics -local pop, inspect +local pop, inspect, patchy local debugDraw = false local videoFile = lg.newVideo("test.ogv") -- so we can loop playback @@ -9,6 +9,7 @@ function love.load() inspect = require "debug-lib/inspect" pop = require "pop" + patchy = require "pop.third-party.patchy" ---[[ local c = pop.box():align("center", "center"):setSize(300, 300) pop.box(c, {255, 0, 0, 255}):setSize(100, 50) @@ -58,7 +59,7 @@ function love.load() print(b.data.horizontal, b.data.vertical) print(c.data.horizontal, c.data.vertical) - local window = pop.window():align("center", "center"):setTitle("Welcome! This title is far too big!") + local window = pop.window():align("center", "center"):setTitle("Welcome! This title is far too big!"):resize(200) pop.window():setClose(false):setClose(true) @@ -74,6 +75,8 @@ function love.load() end print(all.__class.__name) --]] + + button = patchy.load("button.9.png") end function love.update(dt) @@ -88,6 +91,8 @@ end function love.draw() pop.draw() + button:draw(50, 50, 150, 50) + if debugDraw then pop.debugDraw() end diff --git a/demo/pop/elements/box.lua b/demo/pop/elements/box.lua index 91ec7c2..dadee09 100644 --- a/demo/pop/elements/box.lua +++ b/demo/pop/elements/box.lua @@ -75,10 +75,34 @@ do if background == nil then background = false end - _class_0.__parent.__init(self, parent) - self.data.w = 20 - self.data.h = 20 - self.data.background = background + if type(background) == "table" then + for k, v in pairs(background) do + if type(k) ~= "number" then + _class_0.__parent.__init(self, parent, background) + if not background.w then + self.data.w = 20 + end + if not background.h then + self.data.h = 20 + end + if self.data.background == nil then + self.data.background = false + end + return + end + end + else + _class_0.__parent.__init(self, parent) + if not self.data.w then + self.data.w = 20 + end + if not self.data.h then + self.data.h = 20 + end + if not self.data.background then + self.data.background = background + end + end end, __base = _base_0, __name = "box", diff --git a/demo/pop/elements/element.lua b/demo/pop/elements/element.lua index 5a679ca..3e81e14 100644 --- a/demo/pop/elements/element.lua +++ b/demo/pop/elements/element.lua @@ -261,8 +261,8 @@ do } _base_0.__index = _base_0 _class_0 = setmetatable({ - __init = function(self, parent) - self.data = { + __init = function(self, parent, data) + local default = { parent = parent, child = { }, w = 0, @@ -276,9 +276,19 @@ do update = true, move = true } - if parent then - self.data.x = parent.data.x - self.data.y = parent.data.y + if type(data) == "table" then + self.data = data + for k, v in pairs(default) do + if type(self.data[k]) == "nil" then + self.data[k] = v + end + end + else + self.data = default + end + if self.data.parent then + self.data.x = self.data.parent.data.x + self.data.y = self.data.parent.data.y end end, __base = _base_0, diff --git a/demo/pop/elements/text.lua b/demo/pop/elements/text.lua index 5645df9..55d8c99 100644 --- a/demo/pop/elements/text.lua +++ b/demo/pop/elements/text.lua @@ -109,10 +109,22 @@ do 255 } end - _class_0.__parent.__init(self, parent) - self.data.font = graphics.newFont(14) - self:setText(text) - self.data.color = color + if type(text) == "table" then + _class_0.__parent.__init(self, parent, text) + else + _class_0.__parent.__init(self, parent) + end + if not self.data.font then + self.data.font = graphics.newFont(14) + end + if not self.data.color then + self.data.color = color + end + if type(text) == "string" then + return self:setText(text) + else + return self:setSize() + end end, __base = _base_0, __name = "text", diff --git a/demo/pop/third-party/patchy.lua b/demo/pop/third-party/patchy.lua new file mode 100644 index 0000000..1496e78 --- /dev/null +++ b/demo/pop/third-party/patchy.lua @@ -0,0 +1,530 @@ +local nine = { + _VERSION = 'patchy.lua v2.0.0', + _DESCRIPTION = 'Simple 9patch library for LÖVE', + _URL = 'https://github.com/excessive/patchy/tree/master/patchy.lua', + _LICENSE = [[ + The MIT License (MIT) + + Copyright (c) 2015 Pablo Mayobre, Colby Klein, Landon Manning + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + ]] +} + +local function vertical(state, x, w, sx, row, first) + local function add(state, x, y, w, h, sx, sy, row, col) + state.areas[#state.areas + 1] = { + x = x, -- start x + y = y, -- start y + w = w, -- end x + h = h, -- end y + sx = sx, -- scale on x (bool) + sy = sy, -- scale on y (bool) + row = row, -- row number + col = col, -- col number + } + end + + local scale_y = state.scale_y + local current_pixel = 0 + local col = 0 + + for i=1, #scale_y do + if scale_y[i].y > current_pixel then + col = col + 1 + + add(state, x, current_pixel, w, scale_y[i].y - current_pixel, sx, false, row, col) + + if first then + state.static.y = state.static.y + scale_y[i].y - current_pixel + end + + current_pixel = scale_y[i].y + end + + if scale_y[i].y == current_pixel then + col = col + 1 + + add(state, x, scale_y[i].y, w, scale_y[i].h, sx, true, row, col) + + current_pixel = scale_y[i].y + scale_y[i].h + else + error("The start of the vertical line "..i.." comes before another line finishes", 2) + end + end + + if current_pixel < state.dimensions.h then + col = col + 1 + + add(state, x, current_pixel, w, state.dimensions.h - current_pixel, sx, false, row, col) + + if first then + state.static.y = state.static.y + state.dimensions.h - current_pixel + end + end +end + +local function horizontal(state) + local scale_x = state.scale_x + local current_pixel = 0 + local row = 0 + + for i=1, #scale_x do + -- If scale area is after current pixel + if scale_x[i].x > current_pixel then + row = row + 1 + + -- If first scale area + if i == 1 then + vertical(state, current_pixel, scale_x[i].x - current_pixel, false, row, true) + else + vertical(state, current_pixel, scale_x[i].x - current_pixel, false, row) + end + + -- calculate non-scaled area + state.static.x = state.static.x + scale_x[i].x - current_pixel + + -- set current pixel to start of scale area + current_pixel = scale_x[i].x + end + + -- If current pixel is the beginning of scale area + if scale_x[i].x == current_pixel then + row = row + 1 + + -- If first scale area and first scale area begins at 0 (no corner piece) + if i == 1 and row == 1 then + vertical(state, scale_x[i].x, scale_x[i].w, true, row, true) + else + vertical(state, scale_x[i].x, scale_x[i].w, true, row) + end + + -- set current pixel to end of scale area + current_pixel = scale_x[i].x + scale_x[i].w + else + error(string.format("The start of the horizontal line %d comes before another line finishes", i), 2) + end + end + + -- If current pixel is less than total size of image + if current_pixel < state.dimensions.w then + row = row + 1 + vertical(state, current_pixel, state.dimensions.w - current_pixel, false, row) + state.static.x = state.static.x + state.dimensions.w - current_pixel + end +end + +-- Content Box (border - padding) +local function get_content_box(p, x, y, w, h, preprocessed) + if not preprocessed then + x, y = (x or 0), (y or 0) + w = math.max((w or 0) - p.static.x, 0) + p.static.x + h = math.max((h or 0) - p.static.y, 0) + p.static.y + end + + return x + p.pad[4], y + p.pad[1], w - p.pad[2] - p.pad[4] + p.static.x, h - p.pad[1] - p.pad[3] + p.static.y +end +-- Border Box (content + padding) +local function get_border_box(p, x, y, w, h, preprocessed) + if not preprocessed then + x, y = (x or 0), (y or 0) + w, h = (w or 0), (h or 0) + end + + x, y, w, h = x - p.pad[4], y - p.pad[1], w + p.pad[2] + p.pad[4], h + p.pad[1] + p.pad[3] + + if not preprocessed then + w = math.max(w - p.static.x, 0) + p.static.x + h = math.max(h - p.static.y, 0) + p.static.x + end + return x, y, w, h +end + +local function draw(p, x, y, w, h, content_box) + local skip_update = false + + -- If all args match previous draw, no need to update the batch. + if p.last_args then + local ox, oy, ow, oh, ocontent_box = unpack(p.last_args) + if x == ox and y == oy and w == ow and h == oh and content_box == ocontent_box then + skip_update = true + else + p.last_args[1] = x + p.last_args[2] = y + p.last_args[3] = w + p.last_args[4] = h + p.last_args[5] = content_box + end + else + p.last_args = { x, y, w, h, content_box } + end + + -- Box Size + x, y = (x or 0), (y or 0) + w, h = (w or 0), (h or 0) + + if content_box then -- Content box model + x, y, w, h = get_border_box(p, x, y, w, h, true) + end + + w = math.max(w - p.static.x, 0) + h = math.max(h - p.static.y, 0) + + -- Content Box + local cx, cy, cw, ch = get_content_box(p, x, y, w, h, true) + + if not skip_update then + -- Divide size by scale area + local pax, pay = x, y + local sax, say = w / p.dynamic.x, h / p.dynamic.y + + local row, col = 1, 1 + for i=1, #p.areas do + if p.areas[i].row > row then + row = p.areas[i].row + pax = pax + p.areas[i - 1].w * (p.areas[i - 1].sx and sax or 1) + end + + if p.areas[i].col > col then + col = p.areas[i].col + pay = pay + p.areas[i - 1].h * (p.areas[i - 1].sy and say or 1) + elseif p.areas[i].col == 1 then + col = p.areas[i].col + pay = y + end + + if p.areas[i].id then + p.batch:set(p.areas[i].id, p.areas[i].quad, pax, pay, 0, p.areas[i].sx and sax or 1, p.areas[i].sy and say or 1) + end + end + end + + love.graphics.draw(p.batch) + + if debug_draw then + love.graphics.setColor(255, 0, 0, 255) + love.graphics.rectangle("line", get_border_box(p, cx, cy, cw, ch)) --Fixes debug_draw drawing the box littler than how it was + love.graphics.setColor(0, 255, 0, 255) + love.graphics.rectangle("line", cx, cy, cw, ch) + love.graphics.setColor(255, 255, 255, 255) + end + + -- return content box for lazy people who don't want to call get_content_box again + return cx, cy, cw, ch +end + +local function dump (state, filename, save_image) + --Dumb serialization + local str = [[local metadata = function () + return { + type = "9patch", + areas = {]] + + for _, area in ipairs(state.areas) do + str = str..[[ + + { + x = ]]..tostring(area.x)..[[, + y = ]]..tostring(area.y)..[[, + w = ]]..tostring(area.w)..[[, + h = ]]..tostring(area.h)..[[, + sx = ]]..tostring(area.sx)..[[, + sy = ]]..tostring(area.sy)..[[, + row = ]]..tostring(area.row)..[[, + col = ]]..tostring(area.col)..[[, + },]] + end + + str = str:sub(1, -2) .. [[ + + }, + dimensions = {w = ]] .. tostring(state.dimensions.w) .. ", h = ".. tostring(state.dimensions.h) .. [[}, + dynamic = {x = ]] .. tostring(state.dynamic.x) .. ", y = " .. tostring(state.dynamic.y) .. [[}, + static = {x = ]] .. tostring(state.static.x) .. ", y = ".. tostring(state.static.y) .. [[}, + pad = {]] + + for _, padding in ipairs(state.pad) do + str = str .. tostring(padding) .. "," + end + + str = str:sub(1, -2) .. [[} + } +end + +return metadata]] + + --Save the data in a file + love.filesystem.write(filename or "", str or "") + + --Also save the image if required + if save_image then + local filename = (filename:match("(.-)%..-$") or "image")..".png" --Maybe this should be ".9.png" ? + local data = state.image:getData() + data:encode(filename) + end + + return str +end + +local function postprocess(state) + if #state.areas < 1 then + error("There are no areas in this patch", 2) + else + for _, area in ipairs(state.areas) do + if area.w and area.w > 0 and area.h and area.h > 0 then + area.quad = love.graphics.newQuad(area.x, area.y, area.w, area.h, state.dimensions.w, state.dimensions.h) + area.id = state.batch:add(area.quad) + end + end + end + + state.draw = draw + state.get_border_box = get_border_box + state.get_content_box = get_content_box + state.dump = dump + + return state +end + +local function process(patch) + local image = patch.image + + if not image then + error("No images for this patch", 2) + end + + local _w, _h = image:getDimensions() + + local state = { + type = "9patch", + image = image, + batch = patch.batch, + pad = patch.pad, + scale_x = patch.scale_x, + scale_y = patch.scale_y, + areas = {}, + static = {x = 0, y = 0}, + dimensions = {w = _w, h = _h}, + } + + horizontal(state) + + state.dynamic = { + x = state.dimensions.w - state.static.x, + y = state.dimensions.h - state.static.y, + } + + state.scale_x, state.scale_y = nil, nil + + return postprocess(state) +end + +function nine.load(img, metadata) + -- *.9.png + if type(img) == "string" then + img = love.graphics.newImage(img) + end + + local data = img:getData() + local w, h = img:getDimensions() + + local aw, ah = img:getWidth()-2, img:getHeight()-2 + local asset = love.image.newImageData(aw, ah) + + asset:paste(data, 0, 0, 1, 1, aw, ah) + + local srgb = select(3, love.window.getMode()).srgb + local image = love.graphics.newImage(asset, srgb and "srgb" or nil) + + if type(metadata) == "string" then + metadata = love.filesystem.load(metadata) + if not metadata then error("Invalid metadata file passed to 'import'",2) end + metadata = metadata() + end + + if type(metadata) == "function" then + metadata = metadata() + end + + if metadata and metadata.type == "9patch" then + metadata.image = image + return nine.import(image, metadata) + end + + -- 9patch data + local scale_x, scale_y = {{}}, {{}} + local fill_x, fill_y = {}, {} + + -- Scan horizontal rows for 9patch data + for i=0, w - 1 do + -- Top row, scale + local r, g, b, a = data:getPixel(i, 0) + + -- If we are currently in a scale stream, check to see if we leave it (not black) + if scale_x[#scale_x].x then + if not scale_x[#scale_x].w and (r ~= 0 or g ~=0 or b ~= 0 or a ~= 255) then + scale_x[#scale_x].w = (i - 1) - scale_x[#scale_x].x + end + else + -- If we are not in a scale stream, check to see if we are starting one (black) + if r == 0 and g == 0 and b == 0 and a == 255 then + scale_x[#scale_x].x = i - 1 + end + end + + -- Bottom row, fill + local r, g, b, a = data:getPixel(i, h - 1) + + -- If we are in a fill stream, check to see if we leave it (not black) + if fill_x.x then + if not fill_x.w and (r ~= 0 or g ~= 0 or b ~= 0 or a ~= 255) then + fill_x.w = (i - 1) - fill_x.x + end + else + -- If we are not in a fill stream, check to see if we are starting one (black) + if r == 0 and g == 0 and b == 0 and a == 255 then + fill_x.x = i - 1 + end + end + + -- stahp + if scale_x[#scale_x].w then + scale_x[#scale_x + 1] = {} + end + end + + -- if the last table is empty (stahp) + if not scale_x[#scale_x].x then + -- if there is only one table, no 9patch data + if #scale_x == 1 then + error("Invalid 9-patch image, it doesnt contain an horizontal line", 2) + else + scale_x[#scale_x] = nil + end + end + + -- staaaaahp + if not scale_x[#scale_x].w then + scale_x[#scale_x].w = (w - 1) - scale_x[#scale_x].x + end + + --same as above, but for height! + for i=0, h - 1 do + local r, g, b, a = data:getPixel(0, i) + + if scale_y[#scale_y].y then + if not scale_y.h and (r ~= 0 or g ~=0 or b ~= 0 or a ~= 255) then + scale_y[#scale_y].h = (i - 1) - scale_y[#scale_y].y + end + else + if r == 0 and g == 0 and b == 0 and a == 255 then + scale_y[#scale_y].y = i - 1 + end + end + + local r, g, b, a = data:getPixel(w - 1, i) + + if fill_y.y then + if not fill_y.h and (r ~= 0 or g ~= 0 or b ~= 0 or a ~= 255) then + fill_y.h = (i - 1) - fill_y.y + end + else + if r == 0 and g == 0 and b == 0 and a == 255 then + fill_y.y = i - 1 + end + end + + if scale_y[#scale_y].h then + scale_y[#scale_y + 1] = {} + end + end + + if not scale_y[#scale_y].y then + if #scale_y == 1 then + error("Invalid 9-patch image, it doesnt contain a vertical line",2) + else + scale_y[#scale_y] = nil + end + end + + if not scale_y[#scale_y].h then + scale_y[#scale_y].h = (h - 1) - scale_y[#scale_y].y + end + + -- maaaaath + local scale_w = scale_x[#scale_x].x + scale_x[#scale_x].w - scale_x[1].x + local scale_h = scale_y[#scale_y].y + scale_y[#scale_y].h - scale_y[1].y + + fill_x.w = fill_x.x and (fill_x.w and fill_x.w or (w - 1 - fill_x.x)) or scale_w + fill_y.h = fill_y.y and (fill_y.h and fill_y.h or (h - 1 - fill_y.y)) or scale_h + + fill_x.x = fill_x.x and fill_x.x or scale_x[1].x + fill_y.y = fill_y.y and fill_y.y or scale_y[1].y + + local pad = { + - fill_y.y + scale_y[1].y, + - (scale_x[1].x + scale_w) + (fill_x.x + fill_x.w), + - (scale_y[1].y + scale_h) + (fill_y.y + fill_y.h), + - fill_x.x + scale_x[1].x, + } + + -- Dear Positive, + -- ?????????????????????????? + -- Sincerely, me. + local patch = { + image = image, + batch = love.graphics.newSpriteBatch(image, (#scale_x * 2 + 1) * (#scale_y * 2 + 1)), + pad = pad, + scale_x = scale_x, + scale_y = scale_y, + } + + return process(patch) +end + +function nine.import (image, metadata) + if type(metadata) == "string" then + metadata = love.filesystem.load(metadata) + if not metadata then error("Invalid metadata file passed to 'import'",2) end + metadata = metadata() + end + + if type(metadata) == "function" then + metadata = metadata() + end + + if type(metadata) ~= "table" then + error("bad argument #2 to 'import' (table expected, got "..type(metadata)..")", 2) + end + -- *.9.png + if type(image) == "string" then + image = love.graphics.newImage(image) + end + + if not image or not image.type or image:type() ~= "Image" then + error("bad argument #1 to 'import' (Image expected, got "..(image.type and image:type() or type(image))..")", 2) + end + + metadata.image = image + + metadata.batch = love.graphics.newSpriteBatch(image, #metadata.areas) + return postprocess(metadata) +end + +return nine diff --git a/demo/window.9.png b/demo/window.9.png new file mode 100644 index 0000000000000000000000000000000000000000..e2cc471a47afb48a3d286bf0a6ee21dc8559ec17 GIT binary patch literal 4201 zcmds3S5VW9v;9#F0YZt=5u`~I2u+X<3B8vfARwK9h!jy!6oUwaA}t~i2vU^Lr3NFt zBS>h{s}yM;pcFxAf4;dd_vOxgzIS$Z_RN{thdn!cW@F4v^%&^5=l}pjVU%o~9)H}~=j@_Xpz=g((=K=Ano`gwZa_W*#9DZCX5X|>L#b~v{W zH^QLb82MSUg83}racGugVR3$1WG&@A)k zB}C+7VtbFjhvxf?H5~rvKdT*AUoG9kR}av%fFW=78sJ_OdIeF2MY8nik6_{TSj0s z3!wQl0bT%T>QbQb>B3e(4mIH1=jOTum`eiAA}@Z70BHPCMkobf|AwEHA}1N(yW)Y- z2E0^&(t+EtI)JS-z~X7tp#)4w0@4OHp87yV9neB%rmq6P5J1`j6DbBzhXT$$f`TEy zs|ds?Djz z=JfubFx8X8gN3F=p>SEpa6Rgyg|@Hz#viT@q|m-{o%xZdcTz`xe!?>%&|%<8vK41p zvbY?OppA>)ll>qN15Xj_ygq#HzBn&&Or<`56R5bSP7#_cS?K6S_1j2<**2(U@elxZ z>-}24iGr!nUJ>61g3os}PW5uG187f!lmGy@t1Tpj?5)=51_OY0PPll57WaNDheQkL zYU`z$R{9fXg;#K)jy5ija~h4JzLwhc6H74nMFIT$d=yl-K7H#D1A__o<>$ zdLzMiqbG71zudkox6HlFziUs2chwAde`K{NYT0|wkaLB3MScb3kuIxcosYAeEzCti zZzkS>m*o;IKUzc=UXg!2gm=reRSmDe;#lY8k> z4O4|vxy50~qT+($xng?jJ?lPXV$ofz=jFQAa#l-4wBO#ASQq2XH?6p>P(><*(Z#4j zKcvR{FuT(jkX^fRyBXrWMNXyDQbQLXy&GqBhy=e6LrK)5@S7dNmHm^?9*ftfpYqhl zsTCLHDk0)YMj9T4>A`T*Ys6_H5{HoQY)hvOIz-FSRd{8b`?DK&; zw_ZEA`2k@&J-g{HI@{QfF!!W!_3zc^uv6rz?_a`!9>f+@4G{uOL&zMeoi_vgRC!?C z)4wbbj_sO=4=$-LY2RNZlZI6DM|EH8GR1p$IV=e+`F8nsuui731#yul;%bV;MIy)nvwHS2I^L$1~^aobQC(ksuZlKY18bE#E1y+a@+Vh`gg@ z_rccsj$^fM8NTfLSbbSN0ZJg5Vogi3$58DRzldLmMRlAND-)pUa-uKsxeLmb9aYy^ z-oA}(#6JK0{Pn3C4MX%KV+qe4p0I~Cfh@|mmFIBetZj|wo%?UsVJO=H=79;hCE}Gn zu?}y%Rpy8uiXBCI7_ol7%em~gFd zd&=XZr_cDC6Eu{kJ$_eBZfV8mkhEB}5?_|RTxl)i_v-m4sf_<{HJzMH_GXMU4_g}> zq;dJ-64xFE5s8eBw29J4@WXiE+u|j$!Qk2wS%wRm5shrJ*d(7u6tESv26Oodf6aX*gH^CB78>kzccvES<_*5sAUb2&Ow%o~Ub+!=8&s{dTY9s$mg3g<6DkqwlKT-T9?03cy4vpnAmcbY4$YM7Zaj0 zAuHU`e!a~ZGjD2&N;*jH1D?a4>p$isv+Wt*7N`A4V|>6cIaR|jf4gSu;J3~+EUjO3 zx;}!4Amul!eroU_d?EL%XdvGs_mO{*3fsf8wJzgF4~Dk6LmPg}|1&ym-*9-U{-tT! zn!?&-)N|D1*xU<@v9Dv9sc*nmH`OrJDUBc_ah&vRlxJ8CX_+{7V^fc@ z`&!c(ekWLxL4m>gbynA+puhH$<@H0_`9od?<*CQnt%s5Zy5kptq z$n*D@{B}Mw2YnybIgeXaX;2|M?0yQ`t(soBV)Mxc_fgr&cOz&+^$a^=)?J@fk1HoR z_>i`rHr9rE9cAWI|j@%t-8uVu;$zzNN0D zw5Snyx_&}H47d*nXS1K>o%NNov%$iSMmNXFL+L}gX}f9IX5VKYPB&UMIX_N!wRQ0} zeF>41xx0V+c&jydCYWa@f1b%~`5E5}(Q~gql?PUXgkvlx7JE)jmA-f4CMP+4@nM2o zjr6pE^M50!k$}BmX#Mr=9s&UUm4A~0z-4hmrFSI%lB^OSwnv?!hQXtR_*QzS04!<_A9M2Vmmg|KtEp? z-R!kpd{P;x^HY@)+gXs#p=elW zLlpt#!Dqa#X-l-R(e8q&#zNz#K!>liOHl$ z{S?fITCJNom-4klS=Xd_FY2b|6Sw&O`9rZ5?uFg0$dJE5V&v>w#9R&uCupgU3LoW9 zPtMHdYb1yA9}4^iDd`Su5ttB|>z}mCXZq(eXBMH)_Wn?yCp(-eOG&7J)-z)=$J5ygkH~^;L`~MCqIQfqn9b}*t z0>CSPnNkxB@c$1$@W1o6JTJGD9MO{3Uh@3$6{aMD{ujOe4+;7che+a}`mfa^hsNhT zWe3q!+gru_Ydc#B*wEk%F8QR3qVvM<@GlWWNVpa=H>+a8#HG2jjStv3N`8%pX05A; zg9ZWUt&!lMQxvr;U@t=Xk%dwQ-IdT%%zRS&MwjG3jt?i$nqRv(KUqQZE#4Pm)QRs@ zV-~*!c|!h%)^`FMe3W)(G`%ZAnjh?l+8K+V7-1uX zj(#+ldTIlsD4V#J@D-=zNX_bWbR!Ch^MY| zKXU_11m^b0tIw&N;W&FLXCJ#iavt{p`?a$dMBs?K7h3!RTvA_s;oN|Mj;VGz-1+5y E03?tpwEzGB literal 0 HcmV?d00001 diff --git a/lib/pop/elements/box.lua b/lib/pop/elements/box.lua index 91ec7c2..dadee09 100644 --- a/lib/pop/elements/box.lua +++ b/lib/pop/elements/box.lua @@ -75,10 +75,34 @@ do if background == nil then background = false end - _class_0.__parent.__init(self, parent) - self.data.w = 20 - self.data.h = 20 - self.data.background = background + if type(background) == "table" then + for k, v in pairs(background) do + if type(k) ~= "number" then + _class_0.__parent.__init(self, parent, background) + if not background.w then + self.data.w = 20 + end + if not background.h then + self.data.h = 20 + end + if self.data.background == nil then + self.data.background = false + end + return + end + end + else + _class_0.__parent.__init(self, parent) + if not self.data.w then + self.data.w = 20 + end + if not self.data.h then + self.data.h = 20 + end + if not self.data.background then + self.data.background = background + end + end end, __base = _base_0, __name = "box", diff --git a/lib/pop/elements/element.lua b/lib/pop/elements/element.lua index 5a679ca..3e81e14 100644 --- a/lib/pop/elements/element.lua +++ b/lib/pop/elements/element.lua @@ -261,8 +261,8 @@ do } _base_0.__index = _base_0 _class_0 = setmetatable({ - __init = function(self, parent) - self.data = { + __init = function(self, parent, data) + local default = { parent = parent, child = { }, w = 0, @@ -276,9 +276,19 @@ do update = true, move = true } - if parent then - self.data.x = parent.data.x - self.data.y = parent.data.y + if type(data) == "table" then + self.data = data + for k, v in pairs(default) do + if type(self.data[k]) == "nil" then + self.data[k] = v + end + end + else + self.data = default + end + if self.data.parent then + self.data.x = self.data.parent.data.x + self.data.y = self.data.parent.data.y end end, __base = _base_0, diff --git a/lib/pop/elements/text.lua b/lib/pop/elements/text.lua index 5645df9..55d8c99 100644 --- a/lib/pop/elements/text.lua +++ b/lib/pop/elements/text.lua @@ -109,10 +109,22 @@ do 255 } end - _class_0.__parent.__init(self, parent) - self.data.font = graphics.newFont(14) - self:setText(text) - self.data.color = color + if type(text) == "table" then + _class_0.__parent.__init(self, parent, text) + else + _class_0.__parent.__init(self, parent) + end + if not self.data.font then + self.data.font = graphics.newFont(14) + end + if not self.data.color then + self.data.color = color + end + if type(text) == "string" then + return self:setText(text) + else + return self:setSize() + end end, __base = _base_0, __name = "text", diff --git a/lib/pop/third-party/patchy.lua b/lib/pop/third-party/patchy.lua new file mode 100644 index 0000000..1496e78 --- /dev/null +++ b/lib/pop/third-party/patchy.lua @@ -0,0 +1,530 @@ +local nine = { + _VERSION = 'patchy.lua v2.0.0', + _DESCRIPTION = 'Simple 9patch library for LÖVE', + _URL = 'https://github.com/excessive/patchy/tree/master/patchy.lua', + _LICENSE = [[ + The MIT License (MIT) + + Copyright (c) 2015 Pablo Mayobre, Colby Klein, Landon Manning + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + ]] +} + +local function vertical(state, x, w, sx, row, first) + local function add(state, x, y, w, h, sx, sy, row, col) + state.areas[#state.areas + 1] = { + x = x, -- start x + y = y, -- start y + w = w, -- end x + h = h, -- end y + sx = sx, -- scale on x (bool) + sy = sy, -- scale on y (bool) + row = row, -- row number + col = col, -- col number + } + end + + local scale_y = state.scale_y + local current_pixel = 0 + local col = 0 + + for i=1, #scale_y do + if scale_y[i].y > current_pixel then + col = col + 1 + + add(state, x, current_pixel, w, scale_y[i].y - current_pixel, sx, false, row, col) + + if first then + state.static.y = state.static.y + scale_y[i].y - current_pixel + end + + current_pixel = scale_y[i].y + end + + if scale_y[i].y == current_pixel then + col = col + 1 + + add(state, x, scale_y[i].y, w, scale_y[i].h, sx, true, row, col) + + current_pixel = scale_y[i].y + scale_y[i].h + else + error("The start of the vertical line "..i.." comes before another line finishes", 2) + end + end + + if current_pixel < state.dimensions.h then + col = col + 1 + + add(state, x, current_pixel, w, state.dimensions.h - current_pixel, sx, false, row, col) + + if first then + state.static.y = state.static.y + state.dimensions.h - current_pixel + end + end +end + +local function horizontal(state) + local scale_x = state.scale_x + local current_pixel = 0 + local row = 0 + + for i=1, #scale_x do + -- If scale area is after current pixel + if scale_x[i].x > current_pixel then + row = row + 1 + + -- If first scale area + if i == 1 then + vertical(state, current_pixel, scale_x[i].x - current_pixel, false, row, true) + else + vertical(state, current_pixel, scale_x[i].x - current_pixel, false, row) + end + + -- calculate non-scaled area + state.static.x = state.static.x + scale_x[i].x - current_pixel + + -- set current pixel to start of scale area + current_pixel = scale_x[i].x + end + + -- If current pixel is the beginning of scale area + if scale_x[i].x == current_pixel then + row = row + 1 + + -- If first scale area and first scale area begins at 0 (no corner piece) + if i == 1 and row == 1 then + vertical(state, scale_x[i].x, scale_x[i].w, true, row, true) + else + vertical(state, scale_x[i].x, scale_x[i].w, true, row) + end + + -- set current pixel to end of scale area + current_pixel = scale_x[i].x + scale_x[i].w + else + error(string.format("The start of the horizontal line %d comes before another line finishes", i), 2) + end + end + + -- If current pixel is less than total size of image + if current_pixel < state.dimensions.w then + row = row + 1 + vertical(state, current_pixel, state.dimensions.w - current_pixel, false, row) + state.static.x = state.static.x + state.dimensions.w - current_pixel + end +end + +-- Content Box (border - padding) +local function get_content_box(p, x, y, w, h, preprocessed) + if not preprocessed then + x, y = (x or 0), (y or 0) + w = math.max((w or 0) - p.static.x, 0) + p.static.x + h = math.max((h or 0) - p.static.y, 0) + p.static.y + end + + return x + p.pad[4], y + p.pad[1], w - p.pad[2] - p.pad[4] + p.static.x, h - p.pad[1] - p.pad[3] + p.static.y +end +-- Border Box (content + padding) +local function get_border_box(p, x, y, w, h, preprocessed) + if not preprocessed then + x, y = (x or 0), (y or 0) + w, h = (w or 0), (h or 0) + end + + x, y, w, h = x - p.pad[4], y - p.pad[1], w + p.pad[2] + p.pad[4], h + p.pad[1] + p.pad[3] + + if not preprocessed then + w = math.max(w - p.static.x, 0) + p.static.x + h = math.max(h - p.static.y, 0) + p.static.x + end + return x, y, w, h +end + +local function draw(p, x, y, w, h, content_box) + local skip_update = false + + -- If all args match previous draw, no need to update the batch. + if p.last_args then + local ox, oy, ow, oh, ocontent_box = unpack(p.last_args) + if x == ox and y == oy and w == ow and h == oh and content_box == ocontent_box then + skip_update = true + else + p.last_args[1] = x + p.last_args[2] = y + p.last_args[3] = w + p.last_args[4] = h + p.last_args[5] = content_box + end + else + p.last_args = { x, y, w, h, content_box } + end + + -- Box Size + x, y = (x or 0), (y or 0) + w, h = (w or 0), (h or 0) + + if content_box then -- Content box model + x, y, w, h = get_border_box(p, x, y, w, h, true) + end + + w = math.max(w - p.static.x, 0) + h = math.max(h - p.static.y, 0) + + -- Content Box + local cx, cy, cw, ch = get_content_box(p, x, y, w, h, true) + + if not skip_update then + -- Divide size by scale area + local pax, pay = x, y + local sax, say = w / p.dynamic.x, h / p.dynamic.y + + local row, col = 1, 1 + for i=1, #p.areas do + if p.areas[i].row > row then + row = p.areas[i].row + pax = pax + p.areas[i - 1].w * (p.areas[i - 1].sx and sax or 1) + end + + if p.areas[i].col > col then + col = p.areas[i].col + pay = pay + p.areas[i - 1].h * (p.areas[i - 1].sy and say or 1) + elseif p.areas[i].col == 1 then + col = p.areas[i].col + pay = y + end + + if p.areas[i].id then + p.batch:set(p.areas[i].id, p.areas[i].quad, pax, pay, 0, p.areas[i].sx and sax or 1, p.areas[i].sy and say or 1) + end + end + end + + love.graphics.draw(p.batch) + + if debug_draw then + love.graphics.setColor(255, 0, 0, 255) + love.graphics.rectangle("line", get_border_box(p, cx, cy, cw, ch)) --Fixes debug_draw drawing the box littler than how it was + love.graphics.setColor(0, 255, 0, 255) + love.graphics.rectangle("line", cx, cy, cw, ch) + love.graphics.setColor(255, 255, 255, 255) + end + + -- return content box for lazy people who don't want to call get_content_box again + return cx, cy, cw, ch +end + +local function dump (state, filename, save_image) + --Dumb serialization + local str = [[local metadata = function () + return { + type = "9patch", + areas = {]] + + for _, area in ipairs(state.areas) do + str = str..[[ + + { + x = ]]..tostring(area.x)..[[, + y = ]]..tostring(area.y)..[[, + w = ]]..tostring(area.w)..[[, + h = ]]..tostring(area.h)..[[, + sx = ]]..tostring(area.sx)..[[, + sy = ]]..tostring(area.sy)..[[, + row = ]]..tostring(area.row)..[[, + col = ]]..tostring(area.col)..[[, + },]] + end + + str = str:sub(1, -2) .. [[ + + }, + dimensions = {w = ]] .. tostring(state.dimensions.w) .. ", h = ".. tostring(state.dimensions.h) .. [[}, + dynamic = {x = ]] .. tostring(state.dynamic.x) .. ", y = " .. tostring(state.dynamic.y) .. [[}, + static = {x = ]] .. tostring(state.static.x) .. ", y = ".. tostring(state.static.y) .. [[}, + pad = {]] + + for _, padding in ipairs(state.pad) do + str = str .. tostring(padding) .. "," + end + + str = str:sub(1, -2) .. [[} + } +end + +return metadata]] + + --Save the data in a file + love.filesystem.write(filename or "", str or "") + + --Also save the image if required + if save_image then + local filename = (filename:match("(.-)%..-$") or "image")..".png" --Maybe this should be ".9.png" ? + local data = state.image:getData() + data:encode(filename) + end + + return str +end + +local function postprocess(state) + if #state.areas < 1 then + error("There are no areas in this patch", 2) + else + for _, area in ipairs(state.areas) do + if area.w and area.w > 0 and area.h and area.h > 0 then + area.quad = love.graphics.newQuad(area.x, area.y, area.w, area.h, state.dimensions.w, state.dimensions.h) + area.id = state.batch:add(area.quad) + end + end + end + + state.draw = draw + state.get_border_box = get_border_box + state.get_content_box = get_content_box + state.dump = dump + + return state +end + +local function process(patch) + local image = patch.image + + if not image then + error("No images for this patch", 2) + end + + local _w, _h = image:getDimensions() + + local state = { + type = "9patch", + image = image, + batch = patch.batch, + pad = patch.pad, + scale_x = patch.scale_x, + scale_y = patch.scale_y, + areas = {}, + static = {x = 0, y = 0}, + dimensions = {w = _w, h = _h}, + } + + horizontal(state) + + state.dynamic = { + x = state.dimensions.w - state.static.x, + y = state.dimensions.h - state.static.y, + } + + state.scale_x, state.scale_y = nil, nil + + return postprocess(state) +end + +function nine.load(img, metadata) + -- *.9.png + if type(img) == "string" then + img = love.graphics.newImage(img) + end + + local data = img:getData() + local w, h = img:getDimensions() + + local aw, ah = img:getWidth()-2, img:getHeight()-2 + local asset = love.image.newImageData(aw, ah) + + asset:paste(data, 0, 0, 1, 1, aw, ah) + + local srgb = select(3, love.window.getMode()).srgb + local image = love.graphics.newImage(asset, srgb and "srgb" or nil) + + if type(metadata) == "string" then + metadata = love.filesystem.load(metadata) + if not metadata then error("Invalid metadata file passed to 'import'",2) end + metadata = metadata() + end + + if type(metadata) == "function" then + metadata = metadata() + end + + if metadata and metadata.type == "9patch" then + metadata.image = image + return nine.import(image, metadata) + end + + -- 9patch data + local scale_x, scale_y = {{}}, {{}} + local fill_x, fill_y = {}, {} + + -- Scan horizontal rows for 9patch data + for i=0, w - 1 do + -- Top row, scale + local r, g, b, a = data:getPixel(i, 0) + + -- If we are currently in a scale stream, check to see if we leave it (not black) + if scale_x[#scale_x].x then + if not scale_x[#scale_x].w and (r ~= 0 or g ~=0 or b ~= 0 or a ~= 255) then + scale_x[#scale_x].w = (i - 1) - scale_x[#scale_x].x + end + else + -- If we are not in a scale stream, check to see if we are starting one (black) + if r == 0 and g == 0 and b == 0 and a == 255 then + scale_x[#scale_x].x = i - 1 + end + end + + -- Bottom row, fill + local r, g, b, a = data:getPixel(i, h - 1) + + -- If we are in a fill stream, check to see if we leave it (not black) + if fill_x.x then + if not fill_x.w and (r ~= 0 or g ~= 0 or b ~= 0 or a ~= 255) then + fill_x.w = (i - 1) - fill_x.x + end + else + -- If we are not in a fill stream, check to see if we are starting one (black) + if r == 0 and g == 0 and b == 0 and a == 255 then + fill_x.x = i - 1 + end + end + + -- stahp + if scale_x[#scale_x].w then + scale_x[#scale_x + 1] = {} + end + end + + -- if the last table is empty (stahp) + if not scale_x[#scale_x].x then + -- if there is only one table, no 9patch data + if #scale_x == 1 then + error("Invalid 9-patch image, it doesnt contain an horizontal line", 2) + else + scale_x[#scale_x] = nil + end + end + + -- staaaaahp + if not scale_x[#scale_x].w then + scale_x[#scale_x].w = (w - 1) - scale_x[#scale_x].x + end + + --same as above, but for height! + for i=0, h - 1 do + local r, g, b, a = data:getPixel(0, i) + + if scale_y[#scale_y].y then + if not scale_y.h and (r ~= 0 or g ~=0 or b ~= 0 or a ~= 255) then + scale_y[#scale_y].h = (i - 1) - scale_y[#scale_y].y + end + else + if r == 0 and g == 0 and b == 0 and a == 255 then + scale_y[#scale_y].y = i - 1 + end + end + + local r, g, b, a = data:getPixel(w - 1, i) + + if fill_y.y then + if not fill_y.h and (r ~= 0 or g ~= 0 or b ~= 0 or a ~= 255) then + fill_y.h = (i - 1) - fill_y.y + end + else + if r == 0 and g == 0 and b == 0 and a == 255 then + fill_y.y = i - 1 + end + end + + if scale_y[#scale_y].h then + scale_y[#scale_y + 1] = {} + end + end + + if not scale_y[#scale_y].y then + if #scale_y == 1 then + error("Invalid 9-patch image, it doesnt contain a vertical line",2) + else + scale_y[#scale_y] = nil + end + end + + if not scale_y[#scale_y].h then + scale_y[#scale_y].h = (h - 1) - scale_y[#scale_y].y + end + + -- maaaaath + local scale_w = scale_x[#scale_x].x + scale_x[#scale_x].w - scale_x[1].x + local scale_h = scale_y[#scale_y].y + scale_y[#scale_y].h - scale_y[1].y + + fill_x.w = fill_x.x and (fill_x.w and fill_x.w or (w - 1 - fill_x.x)) or scale_w + fill_y.h = fill_y.y and (fill_y.h and fill_y.h or (h - 1 - fill_y.y)) or scale_h + + fill_x.x = fill_x.x and fill_x.x or scale_x[1].x + fill_y.y = fill_y.y and fill_y.y or scale_y[1].y + + local pad = { + - fill_y.y + scale_y[1].y, + - (scale_x[1].x + scale_w) + (fill_x.x + fill_x.w), + - (scale_y[1].y + scale_h) + (fill_y.y + fill_y.h), + - fill_x.x + scale_x[1].x, + } + + -- Dear Positive, + -- ?????????????????????????? + -- Sincerely, me. + local patch = { + image = image, + batch = love.graphics.newSpriteBatch(image, (#scale_x * 2 + 1) * (#scale_y * 2 + 1)), + pad = pad, + scale_x = scale_x, + scale_y = scale_y, + } + + return process(patch) +end + +function nine.import (image, metadata) + if type(metadata) == "string" then + metadata = love.filesystem.load(metadata) + if not metadata then error("Invalid metadata file passed to 'import'",2) end + metadata = metadata() + end + + if type(metadata) == "function" then + metadata = metadata() + end + + if type(metadata) ~= "table" then + error("bad argument #2 to 'import' (table expected, got "..type(metadata)..")", 2) + end + -- *.9.png + if type(image) == "string" then + image = love.graphics.newImage(image) + end + + if not image or not image.type or image:type() ~= "Image" then + error("bad argument #1 to 'import' (Image expected, got "..(image.type and image:type() or type(image))..")", 2) + end + + metadata.image = image + + metadata.batch = love.graphics.newSpriteBatch(image, #metadata.areas) + return postprocess(metadata) +end + +return nine diff --git a/src/pop/elements/box.moon b/src/pop/elements/box.moon index cdb592f..c93bc11 100644 --- a/src/pop/elements/box.moon +++ b/src/pop/elements/box.moon @@ -17,20 +17,20 @@ class box extends element @data.w = 20 if not background.h @data.h = 20 - if not @data.background + if @data.background == nil --is this correct? @data.background = false return --if #background < 3 or #background > 4 -- super parent, background -- background has too many or too few values to be a color...though this makes NO SENSE -- -- would need to do the same things as above here, but it makes no sense to have this, even though it is possible, it MUST be user error - - super parent - if not background.w - @data.w = 20 - if not background.h - @data.h = 20 - if not @data.background - @data.background = background -- we can only assume it is userdata (some LOVE object) or a color table (or the false default) + else + super parent + if not @data.w + @data.w = 20 + if not @data.h + @data.h = 20 + if not @data.background + @data.background = background -- we can only assume it is userdata (some LOVE object) or a color table (or the false default) draw: => if @data.background