myspace / CommonLua /X /XLayouts.lua
sirnii's picture
Upload 1816 files
b6a38d7 verified
raw
history blame
22.5 kB
XWindowLayoutFuncs = {}
XWindowMeasureFuncs = {}
XWindowLayoutMethods = { "None", "Box", "HOverlappingList", "VOverlappingList", "HList", "VList", "HPanel", "VPanel", "Grid", "HWrap", "VWrap" }
----- None layout
function XWindowMeasureFuncs:None(max_width, max_height)
return max_width, max_height
end
function XWindowLayoutFuncs:None(x, y, width, height)
end
----- Box layout
function XWindowMeasureFuncs:Box(max_width, max_height)
local width = 0
local height = 0
for _, win in ipairs(self) do
if not win.Dock then
win:UpdateMeasure(max_width, max_height)
width = Max(width, win.measure_width)
height = Max(height, win.measure_height)
end
end
return width, height
end
function XWindowLayoutFuncs:Box(x, y, width, height)
for _, win in ipairs(self) do
if not win.Dock then
win:SetLayoutSpace(x, y, width, height)
end
end
end
----- HOverlappingList layout
function XWindowMeasureFuncs:HOverlappingList(max_width, max_height)
return XWindowMeasureFuncs.HList(self, max_width, max_height)
end
function XWindowLayoutFuncs:HOverlappingList(x, y, width, height)
local max_item_width, width_sum, items, last_item = 0, 0, 0
local spacing = ScaleXY(self.scale, self.LayoutHSpacing)
for _, win in ipairs(self) do
if not win.Dock then
max_item_width = Max(max_item_width, win.measure_width)
width_sum = width_sum + win.measure_width
last_item = win
items = items + 1
end
end
local overflow = 0
local fill = self.FillOverlappingSpace
if items > 1 then
local total_width = (self.UniformColumnWidth and items * max_item_width or width_sum) + (items - 1) * spacing
overflow = total_width - width
if not fill then
overflow = Max(overflow, 0)
end
end
local n = 1
for _, win in ipairs(self) do
if not win.Dock then
local item_width = Min(max_item_width, win.measure_width)
win:SetLayoutSpace(x, y, item_width, height)
local move = 0
if (fill or overflow > 0) and items > 1 then
move = -(overflow * n / (items - 1) - overflow * (n - 1) / (items - 1))
end
x = x + item_width + spacing + move
n = n + 1
end
end
end
----- VOverlappingList layout
function XWindowMeasureFuncs:VOverlappingList(max_width, max_height)
return XWindowMeasureFuncs.VList(self, max_width, max_height)
end
function XWindowLayoutFuncs:VOverlappingList(x, y, width, height)
local max_item_height, height_sum, items, last_item = 0, 0, 0
local _, spacing = ScaleXY(self.scale, 0, self.LayoutVSpacing)
for _, win in ipairs(self) do
if not win.Dock then
max_item_height = Max(max_item_height, win.measure_height)
height_sum = height_sum + win.measure_height
last_item = win
items = items + 1
end
end
local overflow = 0
local fill = self.FillOverlappingSpace
if items > 1 then
local total_height = (self.UniformRowHeight and items * max_item_height or height_sum) + (items - 1) * spacing
overflow = total_height - height
if not fill then
overflow = Max(total_height - height, 0)
end
end
local n = 1
for _, win in ipairs(self) do
if not win.Dock then
local item_height = Min(max_item_height, win.measure_height)
win:SetLayoutSpace(x, y, width, item_height)
local move = 0
if (fill or overflow > 0) and items > 1 then
move = -(overflow * n / (items - 1) - overflow * (n - 1) / (items - 1))
end
y = y + item_height + spacing + move
n = n + 1
end
end
end
----- HList layout
function XWindowMeasureFuncs:HList(max_width, max_height)
local item_width, item_height, width_sum, items = 0, 0, 0, 0
for _, win in ipairs(self) do
if not win.Dock then
win:UpdateMeasure(max_width, max_height)
item_width = Max(item_width, win.measure_width)
item_height = Max(item_height, win.measure_height)
width_sum = width_sum + win.measure_width
items = items + 1
end
end
local spacing = ScaleXY(self.scale, self.LayoutHSpacing)
return (self.UniformColumnWidth and items * item_width or width_sum) + Max(0, items - 1) * spacing, item_height
end
function XWindowLayoutFuncs:HList(x, y, width, height)
local max_item_width = 0
if self.UniformColumnWidth then
for _, win in ipairs(self) do
if not win.Dock then
max_item_width = Max(max_item_width, win.measure_width)
end
end
end
local spacing = ScaleXY(self.scale, self.LayoutHSpacing)
for _, win in ipairs(self) do
if not win.Dock then
local item_width = Max(max_item_width, win.measure_width)
win:SetLayoutSpace(x, y, item_width, height)
x = x + item_width + spacing
end
end
end
----- VList layout
function XWindowMeasureFuncs:VList(max_width, max_height)
local item_width, item_height, height_sum, items = 0, 0, 0, 0
for _, win in ipairs(self) do
if not win.Dock then
win:UpdateMeasure(max_width, max_height)
item_width = Max(item_width, win.measure_width)
item_height = Max(item_height, win.measure_height)
height_sum = height_sum + win.measure_height
items = items + 1
end
end
local _, spacing = ScaleXY(self.scale, 0, self.LayoutVSpacing)
return item_width, (self.UniformRowHeight and items * item_height or height_sum) + Max(0, items - 1) * spacing
end
function XWindowLayoutFuncs:VList(x, y, width, height)
local max_item_height = 0
if self.UniformRowHeight then
for _, win in ipairs(self) do
if not win.Dock then
max_item_height = Max(max_item_height, win.measure_height)
end
end
end
local _, spacing = ScaleXY(self.scale, 0, self.LayoutVSpacing)
for _, win in ipairs(self) do
if not win.Dock then
local item_height = Max(max_item_height, win.measure_height)
win:SetLayoutSpace(x, y, width, item_height)
y = y + item_height + spacing
end
end
end
----- HPanel layout
function XWindowMeasureFuncs:HPanel(max_width, max_height)
local min_width_total_size = 0
local max_width_total_size = 0
local total_items = 0
local sizers = 0
for _, win in ipairs(self) do
if not win.Dock and not IsKindOf(win, "XPanelSizer") then
if IsKindOf(win, "XPanelSizer") then
sizers = sizers + 1
else
local min_width, _, max_width = ScaleXY(win.scale, win.MinWidth, 0, win.MaxWidth)
min_width_total_size = min_width_total_size + min_width
max_width_total_size = max_width_total_size + max_width
total_items = total_items + 1
end
end
end
if sizers > 0 then
assert(max_width_total_size >= 100000, "MaxWidths of HPanel's children should be > 1000000")
if max_width_total_size < 100000 then
max_width_total_size = 0
for _, win in ipairs(self) do
if not win.Dock and not IsKindOf(win, "XPanelSizer") then
win.MaxWidth = 100000
local max_width = ScaleXY(win.scale, win.MaxWidth)
max_width_total_size = max_width_total_size + max_width
end
end
end
end
local width_difference_total = max_width_total_size - min_width_total_size
local spacing = ScaleXY(self.scale, self.LayoutHSpacing)
local to_distribute = max_width - min_width_total_size - Max(0, total_items - 1) * spacing
local used_width, height = 0, 0
for _, win in ipairs(self) do
if not win.Dock then
local win_min_width, _, win_max_width = ScaleXY(win.scale, win.MinWidth, 0, win.MaxWidth)
local new_width = win_min_width
if not IsKindOf(win, "XPanelSizer") then
local difference = win_max_width - win_min_width
new_width = win_min_width + Max(0, MulDivRound(to_distribute, difference, width_difference_total))
end
win:UpdateMeasure(new_width, max_height)
height = Max(height, win.measure_height)
used_width = used_width + win.measure_width
end
end
return used_width + Max(0, total_items - 1) * spacing, height
end
function XWindowLayoutFuncs:HPanel(x, y, width, height)
local min_width_total_size = 0
local max_width_total_size = 0
local total_items = 0
for _, win in ipairs(self) do
if not win.Dock then
local min_width, _, max_width = ScaleXY(win.scale, win.MinWidth, 0, win.MaxWidth)
min_width_total_size = min_width_total_size + min_width
max_width_total_size = max_width_total_size + max_width
total_items = total_items + 1
end
end
local width_difference_total = max_width_total_size - min_width_total_size
local spacing = ScaleXY(self.scale, self.LayoutHSpacing)
local to_distribute = width - min_width_total_size - Max(0, total_items - 1) * spacing
for _, win in ipairs(self) do
if not win.Dock then
local min_width, _, max_width = ScaleXY(win.scale, win.MinWidth, 0, win.MaxWidth)
local new_width = min_width
if not IsKindOf(win, "XPanelSizer") then
local difference = max_width - min_width
new_width = min_width + Max(0, MulDivRound(to_distribute, difference, width_difference_total))
end
win:SetLayoutSpace(x, y, new_width, height)
x = x + new_width + spacing
end
end
end
----- VPanel layout
function XWindowMeasureFuncs:VPanel(max_width, max_height)
local min_height_total_size = 0
local max_height_total_size = 0
local total_items = 0
local sizers = 0
for _, win in ipairs(self) do
if not win.Dock then
if IsKindOf(win, "XPanelSizer") then
sizers = sizers + 1
else
local _, min_height, _, max_height = ScaleXY(win.scale, 0, win.MinHeight, 0, win.MaxHeight)
min_height_total_size = min_height_total_size + min_height
max_height_total_size = max_height_total_size + max_height
total_items = total_items + 1
end
end
end
if sizers > 0 then
assert(max_height_total_size >= 100000, "MaxHeights of VPanel's children should be > 1000000")
if max_height_total_size < 100000 then
max_height_total_size = 0
for _, win in ipairs(self) do
if not win.Dock and not IsKindOf(win, "XPanelSizer") then
win.MaxHeight = 100000
local _, max_height = ScaleXY(win.scale, 0, win.MaxHeight)
max_height_total_size = max_height_total_size + max_height
end
end
end
end
local height_difference_total = max_height_total_size - min_height_total_size
local _, spacing = ScaleXY(self.scale, 0, self.LayoutVSpacing)
local to_distribute = max_height - min_height_total_size - Max(0, total_items - 1) * spacing
local used_height, width = 0, 0
for _, win in ipairs(self) do
if not win.Dock then
local _, win_min_height, _, win_max_height = ScaleXY(win.scale, 0, win.MinHeight, 0, win.MaxHeight)
local new_height = win_min_height
if not IsKindOf(win, "XPanelSizer") then
local difference = win_max_height - win_min_height
new_height = win_min_height + Max(0, MulDivRound(to_distribute, difference, height_difference_total))
end
win:UpdateMeasure(max_width, new_height)
width = Max(width, win.measure_width)
used_height = used_height + win.measure_height
end
end
return width, used_height + Max(0, total_items - 1) * spacing
end
function XWindowLayoutFuncs:VPanel(x, y, width, height)
local min_height_total_size = 0
local max_height_total_size = 0
local total_items = 0
for _, win in ipairs(self) do
if not win.Dock then
local _, min_height, _, max_height = ScaleXY(win.scale, 0, win.MinHeight, 0, win.MaxHeight)
min_height_total_size = min_height_total_size + min_height
max_height_total_size = max_height_total_size + max_height
total_items = total_items + 1
end
end
local height_difference_total = max_height_total_size - min_height_total_size
local _, spacing = ScaleXY(self.scale, 0, self.LayoutVSpacing)
local to_distribute = height - min_height_total_size - Max(0, total_items - 1) * spacing
for _, win in ipairs(self) do
if not win.Dock then
local _, min_height, _, max_height = ScaleXY(win.scale, 0, win.MinHeight, 0, win.MaxHeight)
local new_height = min_height
if not IsKindOf(win, "XPanelSizer") then
local difference = max_height - min_height
new_height = min_height + Max(0, MulDivRound(to_distribute, difference, height_difference_total))
end
win:SetLayoutSpace(x, y, width, new_height)
y = y + new_height + spacing
end
end
end
----- Grid layout
function XWindowMeasureFuncs:Grid(max_width, max_height)
local width, height = 0, 0
local max_col, max_row = 0, 0
local col_width, row_height
local col_widths, row_heights
for _, win in ipairs(self) do
if not win.Dock then
win:UpdateMeasure(max_width, max_height)
max_col = Max(max_col, win.GridX + win.GridWidth - 1)
max_row = Max(max_row, win.GridY + win.GridHeight - 1)
local width_per_col = win.measure_width / win.GridWidth
local height_per_row = win.measure_height / win.GridHeight
if self.UniformColumnWidth then
col_width = Max(col_width, width_per_col)
else
col_widths = col_widths or {}
for i = win.GridX, win.GridX + win.GridWidth - 1 do
col_widths[i] = Max(col_widths[i], width_per_col)
end
end
if self.UniformRowHeight then
row_height = Max(row_height, height_per_row)
else
row_heights = row_heights or {}
for i = win.GridY, win.GridY + win.GridHeight - 1 do
row_heights[i] = Max(row_heights[i], height_per_row)
end
end
end
end
local h_spacing, v_spacing = ScaleXY(self.scale, self.LayoutHSpacing, self.LayoutVSpacing)
for i = 1, max_col do
width = width + (col_width or col_widths[i] or 0)
end
width = width + Max(0, max_col - 1) * h_spacing
for i = 1, max_row do
height = height + (row_height or row_heights[i] or 0)
end
height = height + Max(0, max_row - 1) * v_spacing
return width, height
end
function XWindowLayoutFuncs:Grid(x, y, width, height)
local col_width, row_height
local col_widths, row_heights = {}, {}
local num_cols, num_rows
for _, win in ipairs(self) do
if not win.Dock then
local width_per_col = win.measure_width / win.GridWidth
local height_per_row = win.measure_height / win.GridHeight
if self.UniformColumnWidth then
col_width = Max(col_width, width_per_col)
else
for i = win.GridX, win.GridX + win.GridWidth - 1 do
col_widths[i] = Max(col_widths[i], width_per_col)
end
end
num_cols = Max(num_cols, win.GridX + win.GridWidth - 1)
if self.UniformRowHeight then
row_height = Max(row_height, height_per_row)
else
for i = win.GridY, win.GridY + win.GridHeight - 1 do
row_heights[i] = Max(row_heights[i], height_per_row)
end
end
num_rows = Max(num_rows, win.GridY + win.GridHeight - 1)
end
end
local total_width, total_height = 0, 0
local h_spacing, v_spacing = ScaleXY(self.scale, self.LayoutHSpacing, self.LayoutVSpacing)
if self.UniformColumnWidth then
total_width = col_width * num_cols
else
for i = 1, num_cols do
total_width = total_width + (col_widths[i] or 0)
end
end
local h_spacing_sum = Max(0, num_cols - 1) * h_spacing
local total_width_noSpacing = total_width
local width_noSpacing = width - h_spacing_sum
total_width = total_width + h_spacing_sum
if self.UniformRowHeight then
total_height = row_height * num_rows
else
for i = 1, num_rows do
total_height = total_height + (row_heights[i] or 0)
end
end
local v_spacing_sum = Max(0, num_rows - 1) * v_spacing
local total_height_noSpacing = total_height
local height_noSpacing = height - v_spacing_sum
total_height = total_height + v_spacing_sum
for _, win in ipairs(self) do
if not win.Dock then
local x_left = x
local space_width = 0
if total_width_noSpacing > 0 then
for i = 1, win.GridX - 1 do
-- GridStretch(X/Y) means we want to distribute the available space to the grid's children while
-- maintaining their ratio relative to each other (their space relative to the total measured space).
-- This can cause the items to either shrink or stretch.
-- If it is set to false then children will be given their measure size and could spill.
if self.GridStretchX then
x_left = x_left + (col_width or col_widths[i] or 0) * width_noSpacing / total_width_noSpacing
else
x_left = x_left + (col_width or col_widths[i] or 0)
end
end
x_left = x_left + Max(0, (win.GridX - 1)) * h_spacing
for i = win.GridX, win.GridX + win.GridWidth - 1 do
if self.GridStretchX then
space_width = space_width + (col_width or col_widths[i] or 0) * width_noSpacing / total_width_noSpacing
else
space_width = space_width + (col_width or col_widths[i] or 0)
end
end
space_width = space_width + Max(0, (win.GridWidth - 1)) * h_spacing
end
local y_top = y
local space_height = 0
if total_height_noSpacing > 0 then
for i = 1, win.GridY - 1 do
if self.GridStretchY then
y_top = y_top + (row_height or row_heights[i] or 0) * height_noSpacing / total_height_noSpacing
else
y_top = y_top + (row_height or row_heights[i] or 0)
end
end
y_top = y_top + Max(0, (win.GridY - 1)) * v_spacing
for i = win.GridY, win.GridY + win.GridHeight - 1 do
if self.GridStretchY then
space_height = space_height + (row_height or row_heights[i] or 0) * height_noSpacing / total_height_noSpacing
else
space_height = space_height + (row_height or row_heights[i] or 0)
end
end
space_height = space_height + Max(0, (win.GridHeight - 1)) * v_spacing
end
win:SetLayoutSpace(x_left, y_top, space_width, space_height)
end
end
end
----- HWrap layout
function XWindowMeasureFuncs:HWrap(max_width, max_height)
local line_width, line_height, total_width, total_height = 0, 0, 0, 0
local max_item_width, max_item_height, items = 0, 0, 0
local h_spacing, v_spacing = ScaleXY(self.scale, self.LayoutHSpacing, self.LayoutVSpacing)
for _, win in ipairs(self) do
if not win.Dock then
win:UpdateMeasure(max_width, max_height)
max_item_width = self.UniformColumnWidth and Max(max_item_width, win.measure_width) or max_item_width
max_item_height = self.UniformRowHeight and Max(max_item_height, win.measure_height) or max_item_height
end
end
for _, win in ipairs(self) do
if not win.Dock then
local item_width = Max(max_item_width, win.measure_width)
local item_height = Max(max_item_height, win.measure_height)
local new_width = line_width + (line_width > 0 and h_spacing or 0) + item_width
if new_width > max_width then
total_width = Max(total_width, line_width)
total_height = total_height + (total_height > 0 and v_spacing or 0) + line_height
line_width = item_width
line_height = item_height
else
line_width = new_width
line_height = Max(line_height, item_height)
end
items = items + 1
end
end
total_width = Max(total_width, line_width)
total_height = total_height + (total_height > 0 and v_spacing or 0) + line_height
return total_width, total_height
end
function XWindowLayoutFuncs:HWrap(x, y, width, height)
local x_left = x
local max_item_width, max_item_height = 0, 0
if self.UniformColumnWidth or self.UniformRowHeight then
for _, win in ipairs(self) do
if not win.Dock then
max_item_width = self.UniformColumnWidth and Max(max_item_width, win.measure_width) or max_item_width
max_item_height = self.UniformRowHeight and Max(max_item_height, win.measure_height) or max_item_height
end
end
end
local h_spacing, v_spacing = ScaleXY(self.scale, self.LayoutHSpacing, self.LayoutVSpacing)
local line_height = 0
for i = 1, #self do
local win = self[i]
if not win.Dock then
local item_width = Max(max_item_width, win.measure_width)
local item_height = Max(max_item_height, win.measure_height)
local x_right = x + item_width
if x_right > x_left + width then
local x_offset = x_left
if self.HAlign == "center" then
--center items on every line
local line_items_width = 0
for j = i, #self do
local line_win = self[j]
if line_items_width > width then
--can't fit more items on this row
break
end
line_items_width = line_items_width + Max(max_item_width, line_win.measure_width)
end
if line_items_width < width then
x_offset = x_left + (width - line_items_width) / 2
end
end
x = x_offset
y = y + line_height + (line_height > 0 and v_spacing or 0)
line_height = 0
end
win:SetLayoutSpace(x, y, item_width, item_height)
line_height = Max(line_height, item_height)
x = x + item_width + h_spacing
end
end
end
----- VWrap layout
function XWindowMeasureFuncs:VWrap(max_width, max_height)
local col_width, col_height, total_width, total_height = 0, 0, 0, 0
local max_item_width, max_item_height, items = 0, 0, 0
local h_spacing, v_spacing = ScaleXY(self.scale, self.LayoutHSpacing, self.LayoutVSpacing)
for _, win in ipairs(self) do
if not win.Dock then
win:UpdateMeasure(max_width, max_height)
max_item_width = self.UniformColumnWidth and Max(max_item_width, win.measure_width) or max_item_width
max_item_height = self.UniformRowHeight and Max(max_item_height, win.measure_height) or max_item_height
end
end
for _, win in ipairs(self) do
if not win.Dock then
local item_width = Max(max_item_width, win.measure_width)
local item_height = Max(max_item_height, win.measure_height)
local new_height = col_height + (col_height > 0 and v_spacing or 0) + item_height
if new_height > max_height then
total_height = Max(total_height, col_height)
total_width = total_width + (total_width > 0 and h_spacing or 0) + col_width
col_width = item_width
col_height = item_height
else
col_height = new_height
col_width = Max(col_width, item_width)
end
items = items + 1
end
end
total_height = Max(total_height, col_height)
total_width = total_width + (total_width > 0 and h_spacing or 0) + col_width
return total_width, total_height
end
function XWindowLayoutFuncs:VWrap(x, y, width, height)
local y_top = y
local max_item_width, max_item_height = 0, 0
if self.UniformColumnWidth or self.UniformRowHeight then
for _, win in ipairs(self) do
if not win.Dock then
max_item_width = self.UniformColumnWidth and Max(max_item_width, win.measure_width) or max_item_width
max_item_height = self.UniformRowHeight and Max(max_item_height, win.measure_height) or max_item_height
end
end
end
local h_spacing, v_spacing = ScaleXY(self.scale, self.LayoutHSpacing, self.LayoutVSpacing)
local line_width = 0
for _, win in ipairs(self) do
if not win.Dock then
local item_width = Max(max_item_width, win.measure_width)
local item_height = Max(max_item_height, win.measure_height)
local y_bottom = y + item_height
if y_bottom > y_top + height then
y = y_top
x = x + line_width + (line_width > 0 and h_spacing or 0)
line_width = 0
end
win:SetLayoutSpace(x, y, item_width, item_height)
line_width = Max(line_width, item_width)
y = y + item_height + v_spacing
end
end
end