fix(libtui): improve flex layout handling
This commit is contained in:
parent
e7666715b0
commit
c99426a3a9
@ -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
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user