fix(libtui): improve flex layout handling

This commit is contained in:
Guillaume ARM 2026-06-08 00:53:52 +02:00
parent e7666715b0
commit c99426a3a9

View File

@ -1,4 +1,4 @@
local _VERSION = '0.1.0'; local _VERSION = '0.1.1';
local NODE_TEXT = 'text'; local NODE_TEXT = 'text';
local NODE_BUTTON = 'button'; local NODE_BUTTON = 'button';
@ -260,27 +260,46 @@ local function buttonLabel(props)
return '[ ' .. tostring(props.text or props.label or '') .. ' ]'; return '[ ' .. tostring(props.text or props.label or '') .. ' ]';
end end
local function flexOf(child)
local f = (child.props or {}).flex;
if f == true then
return 1;
end
if type(f) == 'number' and f > 0 then
return f;
end
return nil;
end
local naturalSize; local naturalSize;
local function childrenNaturalSize(children, direction, gap) local function childrenNaturalSize(children, direction, gap)
local width = 0; local width = 0;
local height = 0; local height = 0;
local mainCount = 0;
for index, child in ipairs(children) do for _, child in ipairs(children) do
local hasFlex = flexOf(child) ~= nil;
local childWidth, childHeight = naturalSize(child); local childWidth, childHeight = naturalSize(child);
if direction == 'row' then if direction == 'row' then
width = width + childWidth;
height = math.max(height, childHeight); height = math.max(height, childHeight);
if index > 1 then if not hasFlex then
if mainCount > 0 then
width = width + gap; width = width + gap;
end end
width = width + childWidth;
mainCount = mainCount + 1;
end
else else
width = math.max(width, childWidth); width = math.max(width, childWidth);
height = height + childHeight; if not hasFlex then
if index > 1 then if mainCount > 0 then
height = height + gap; height = height + gap;
end end
height = height + childHeight;
mainCount = mainCount + 1;
end
end end
end end
@ -336,41 +355,38 @@ local function layoutChildren(children, rect, direction, gap)
local flexSize = 0; local flexSize = 0;
local axisSize = direction == 'row' and rect.w or rect.h; local axisSize = direction == 'row' and rect.w or rect.h;
local layouts = {}; local layouts = {};
local remaining; local lastFlexIndex = nil;
local flexes = {};
for _, child in ipairs(children) do for index, child in ipairs(children) do
local props = child.props or {}; local flex = flexOf(child);
if props.flex then flexes[index] = flex;
flexSize = flexSize + props.flex; if flex then
flexSize = flexSize + flex;
lastFlexIndex = index;
else else
fixedSize = fixedSize + childAxisSize(child, direction); fixedSize = fixedSize + childAxisSize(child, direction);
end end
end end
remaining = axisSize - fixedSize - math.max(#children - 1, 0) * gap; local remaining = axisSize - fixedSize - math.max(#children - 1, 0) * gap;
if remaining < 0 then if remaining < 0 then
remaining = 0; remaining = 0;
end end
local cursor = direction == 'row' and rect.x or rect.y; local cursor = direction == 'row' and rect.x or rect.y;
local lastFlexIndex = nil;
local usedFlexSize = 0; local usedFlexSize = 0;
for index, child in ipairs(children) do
if (child.props or {}).flex then
lastFlexIndex = index;
end
end
for index, child in ipairs(children) do for index, child in ipairs(children) do
local props = child.props or {}; local props = child.props or {};
local flex = flexes[index];
local size; local size;
if props.flex then if flex then
if index == lastFlexIndex then if index == lastFlexIndex then
size = remaining - usedFlexSize; size = remaining - usedFlexSize;
else else
size = math.floor(remaining * props.flex / flexSize); size = math.floor(remaining * flex / flexSize);
usedFlexSize = usedFlexSize + size; usedFlexSize = usedFlexSize + size;
end end
else else
@ -548,12 +564,27 @@ local function createTui(eventloop)
end end
function api.rerender() function api.rerender()
if root == nil then
return;
end
safeRedraw(); safeRedraw();
end end
local function restoreTerminal()
if not previousState then
return;
end
term.setTextColor(previousState.color);
term.setBackgroundColor(previousState.bgColor);
term.clear();
term.setCursorPos(previousState.cursorX, previousState.cursorY);
term.setCursorBlink(previousState.cursorBlink);
previousState = nil;
end
function api.render(nextRoot) function api.render(nextRoot)
root = nextRoot;
finalEvent = nil; finalEvent = nil;
root = nextRoot;
previousState = { previousState = {
color = term.getTextColor(), color = term.getTextColor(),
@ -572,13 +603,7 @@ local function createTui(eventloop)
end end
eventloop.onStart(safeRedraw); eventloop.onStart(safeRedraw);
eventloop.onStop(function() eventloop.onStop(restoreTerminal);
term.setTextColor(previousState.color);
term.setBackgroundColor(previousState.bgColor);
term.clear();
term.setCursorPos(previousState.cursorX, previousState.cursorY);
term.setCursorBlink(previousState.cursorBlink);
end);
eventloop.register('mouse_click', function(button, x, y) eventloop.register('mouse_click', function(button, x, y)
for index = #clickables, 1, -1 do for index = #clickables, 1, -1 do
@ -606,6 +631,7 @@ local function createTui(eventloop)
local ok, reason = pcall(eventloop.startLoop); local ok, reason = pcall(eventloop.startLoop);
if not ok then if not ok then
pcall(restoreTerminal);
finalEvent = createErrorEvent(reason); finalEvent = createErrorEvent(reason);
end end
@ -616,18 +642,17 @@ local function createTui(eventloop)
end end
api.Text = makeText; api.Text = makeText;
api.text = makeText;
api.Button = makeButton; api.Button = makeButton;
api.button = makeButton;
api.Box = makeBox; api.Box = makeBox;
api.box = makeBox;
api.List = makeList; api.List = makeList;
api.list = makeList;
api.Fragment = makeFragment; api.Fragment = makeFragment;
api.version = _VERSION; api.version = _VERSION;
api.eventloop = eventloop; api.eventloop = eventloop;
api.text = makeText;
api.button = makeButton;
api.box = makeBox;
api.list = makeList;
return api; return api;
end end