#include "ui_defs.lua" Ui.ContentLayout = {} Ui.ContentLayout.Vertical = { Top = 123, Bottom = 1233, Middle = 123333 } Ui.ContentLayout.Horizontal = { Left = 1111, Right = 22222, Center = 33333 } Ui.ContentLayout.EndFrameScaled = function() local w, h = UiEndFrame() local sx, sy = UiGetScale() local sw = (sx ~= 0) and w / sx or 0 local sh = (sy ~= 0) and h / sy or 0 return sw, sh end function CreateUiWindow() local LayoutUiWindow = {} setmetatable(LayoutUiWindow, { __call = function(self, w, h) local cx, cy = UiGetCursorPos() local obj = { x = cx, y = cy, w = w, h = h } setmetatable(obj, {__index = LayoutUiWindow}) return obj end }) function LayoutUiWindow:TranslateToCenter(offsetX, offsetY) offsetX = offsetX or 0 offsetY = offsetY or 0 local cx = UiGetCursorPos() local sx = UiGetScale() local wc = self.x + self.w * sx / 2 local delta = (sx ~= 0) and ((wc - cx) / sx) or 0 UiTranslate(delta + offsetX, offsetY) end function LayoutUiWindow:TranslateToMiddle(offsetX, offsetY) offsetX = offsetX or 0 offsetY = offsetY or 0 local _, cy = UiGetCursorPos() local _, sy = UiGetScale() local hc = self.y + self.h * sy / 2 local delta = (sy ~= 0) and ((hc - cy) / sy) or 0 UiTranslate(offsetX, delta + offsetY) end function LayoutUiWindow:TranslateToRightEdge(offsetX, offsetY) offsetX = offsetX or 0 offsetY = offsetY or 0 local cx = UiGetCursorPos() local sx = UiGetScale() local wx = self.x + self.w * sx local delta = (sx ~= 0) and ((wx - cx) / sx) or 0 UiTranslate(delta + offsetX, offsetY) end function LayoutUiWindow:TranslateToLeftEdge(offsetX, offsetY) offsetX = offsetX or 0 offsetY = offsetY or 0 local cx = UiGetCursorPos() local wx = self.x UiTranslate((wx - cx) + offsetX, offsetY) end function LayoutUiWindow:TranslateToTopEdge(offsetX, offsetY) offsetX = offsetX or 0 offsetY = offsetY or 0 local _, cy = UiGetCursorPos() local wy = self.y UiTranslate(offsetX, (wy - cy) + offsetY) end function LayoutUiWindow:TranslateToBottomEdge(offsetX, offsetY) offsetX = offsetX or 0 offsetY = offsetY or 0 local _, cy = UiGetCursorPos() local _, sy = UiGetScale() local wy = self.y + self.h * sy local delta = (sy ~= 0) and ((wy - cy) / sy) or 0 UiTranslate(offsetX, delta + offsetY) end function LayoutUiWindow:IsInRect(x, y) return (x > self.x) and (x < (self.x + self.w)) and (y > self.y) and (y < (self.y + self.h)) end function LayoutUiWindow:FullyVisible(x, y, w, h) return (x > self.x) and (y > self.y) and ((self.x + self.w) > (x + w)) and ((self.y + self.h) > (y + h)) end return LayoutUiWindow end function Ui.Frame(obj) local FrameUiWindow = CreateUiWindow() local frame = Ui.Object:new { x = 0, y = 0, padding = { top = 0, left = 0, bottom = 0, right = 0 }, draw = function(self) UiPush() UiAlign("top left") self.window = FrameUiWindow (self.w, self.h) for child in filter(self.children, function(it) return it.visible end) do local cw, ch = child:getSize() child.actualSize = { w = cw, h = ch } UiPush() self:_translateAligned(child) UiPush() child:draw() UiPop() if self.debug then UiPush() UiFrameSkipItem(true) UiColor(1,0,0) UiRect(cw, 2) UiTranslate(cw - 2, 0) UiRect(2, ch) UiTranslate(-cw, ch - 2) UiRect(cw, 2) UiTranslate(0, -ch + 2) UiRect(2, ch) UiPop() end UiPop() end UiPop() end, getSize = function(self) UiPush() for child in filter(self.children, function(it) return it.visible end) do child:getSize() end UiPop() return self.w, self.h end, _translateAligned = function(self, obj) local hAlign = obj.hAlign or Ui.ContentLayout.Horizontal.Left local vAlign = obj.vAlign or Ui.ContentLayout.Vertical.Top if hAlign == Ui.ContentLayout.Horizontal.Left then self.window:TranslateToLeftEdge(self.padding.left + obj.margin.left, 0) elseif hAlign == Ui.ContentLayout.Horizontal.Right then self.window:TranslateToRightEdge(-(obj.actualSize.w + self.padding.right + obj.margin.right), 0) elseif hAlign == Ui.ContentLayout.Horizontal.Center then self.window:TranslateToCenter(-obj.actualSize.w / 2, 0) end if vAlign == Ui.ContentLayout.Vertical.Top then self.window:TranslateToTopEdge(0, self.padding.top + obj.margin.top) elseif vAlign == Ui.ContentLayout.Vertical.Bottom then self.window:TranslateToBottomEdge(0, -(obj.actualSize.h + self.padding.bottom + obj.margin.bottom)) elseif vAlign == Ui.ContentLayout.Vertical.Middle then self.window:TranslateToMiddle(0, -obj.actualSize.h / 2) end end } return frame:new(obj) end function Ui.VerticalLayout(obj) local VerticalLayoutUiWindow = CreateUiWindow() local vLayout = Ui.Object:new { padding = 0, alignment = Ui.ContentLayout.Horizontal.Left, draw = function(self) UiPush() UiAlign("top left") self:getSize() self.window = VerticalLayoutUiWindow(self.w, self.h) local nextOffset = 0 for child in filter(self.children, function(it) return it.visible end) do UiTranslate(0, nextOffset) self:_translateAligned(child) UiPush() child:draw() UiPop() if self.debug then local cw = child.actualSize.w local ch = child.actualSize.h UiPush() UiFrameSkipItem(true) UiColor(1,1,0) UiRect(cw, 2) UiTranslate(cw - 2, 0) UiRect(2, ch) UiTranslate(-cw, ch - 2) UiRect(cw, 2) UiTranslate(0, -ch + 2) UiRect(2, ch) UiPop() end UiTranslate(0, child.actualSize.h) nextOffset = child.margin.bottom + self.padding end UiPop() end, getSize = function(self) local maxW = 0 local totalH = 0 for child in filter(self.children, function(it) return it.visible end) do local cw, ch = child:getSize() child.actualSize = { w = cw, h = ch } maxW = math.max(maxW, cw + child.margin.left + child.margin.right) totalH = totalH + ch + child.margin.top + child.margin.bottom + self.padding end totalH = totalH - self.padding self.w, self.h = maxW, totalH return self.w, self.h end, _translateAligned = function(self, obj) --override hlayout alignment if a child has specified alignment explicitly local align = (self.alignment == obj.hAlign or (not obj.hAlign)) and self.alignment or obj.hAlign if align == Ui.ContentLayout.Horizontal.Left then self.window:TranslateToLeftEdge(obj.margin.left, obj.margin.top) elseif align == Ui.ContentLayout.Horizontal.Right then self.window:TranslateToRightEdge(-(obj.actualSize.w + obj.margin.right), obj.margin.top) elseif align == Ui.ContentLayout.Horizontal.Center then self.window:TranslateToCenter(-obj.actualSize.w / 2 + obj.margin.left - obj.margin.right, obj.margin.top) end end } return vLayout:new(obj) end function Ui.HorizontalLayout(obj) local HorizontalLayoutUiWindow = CreateUiWindow() local hLayout = Ui.Object:new { padding = 0, alignment = Ui.ContentLayout.Vertical.Top, draw = function(self) UiPush() UiAlign("top left") self:getSize() self.window = HorizontalLayoutUiWindow(self.w, self.h) local nextOffset = 0 for child in filter(self.children, function(it) return it.visible end) do UiTranslate(nextOffset, 0) self:_translateAligned(child) UiPush() child:draw() UiPop() if self.debug then local cw = child.actualSize.w local ch = child.actualSize.h UiPush() UiFrameSkipItem(true) UiColor(1,1,0) UiRect(cw, 2) UiTranslate(cw - 2, 0) UiRect(2, ch) UiTranslate(-cw, ch - 2) UiRect(cw, 2) UiTranslate(0, -ch + 2) UiRect(2, ch) UiPop() end UiTranslate(child.actualSize.w, 0) nextOffset = child.margin.right + self.padding end UiPop() end, getSize = function(self) local maxH = 0 local totalW = 0 for child in filter(self.children, function(it) return it.visible end) do local cw, ch = child:getSize() child.actualSize = { w = cw, h = ch } maxH = math.max(maxH, ch + child.margin.top + child.margin.bottom) totalW = totalW + cw + child.margin.left + child.margin.right + self.padding end totalW = totalW - self.padding self.w, self.h = totalW, maxH return self.w, self.h end, _translateAligned = function(self, obj) --override vlayout alignment if a child has specified alignment explicitly local align = (self.alignment == obj.vAlign or (not obj.vAlign)) and self.alignment or obj.vAlign if align == Ui.ContentLayout.Vertical.Top then self.window:TranslateToTopEdge(obj.margin.left, obj.margin.top) elseif align == Ui.ContentLayout.Vertical.Bottom then self.window:TranslateToBottomEdge(obj.margin.left, -(obj.actualSize.h + obj.margin.bottom)) elseif align == Ui.ContentLayout.Vertical.Middle then self.window:TranslateToMiddle(obj.margin.left, -obj.actualSize.h / 2 + obj.margin.top - obj.margin.bottom) end end } return hLayout:new(obj) end