ui code almost ready to replace current code

This commit is contained in:
Matthew 2025-12-12 20:13:12 +11:00
parent 2703c95673
commit ad3ababe97
7 changed files with 537 additions and 251 deletions

View File

@ -13,11 +13,28 @@
"sourcePaths": ["src/editor", "src/dlib", "src/dlib/external/xxhash", "src/VulkanRenderer"], "sourcePaths": ["src/editor", "src/dlib", "src/dlib/external/xxhash", "src/VulkanRenderer"],
"libs-linux": ["X11", "vulkan", "stdc++", "xfixes", "freetype"], "libs-linux": ["X11", "vulkan", "stdc++", "xfixes", "freetype"],
"libs-windows": [], "libs-windows": [],
"versions": ["VULKAN_DEBUG"], "versions": ["VULKAN_DEBUG", "ENABLE_RENDERER"],
"preGenerateCommands-linux": ["./build.sh"], "preGenerateCommands-linux": ["./build.sh"],
"preGenerateCommands-windows": [], "preGenerateCommands-windows": [],
"dflags": ["-Xcc=-mno-sse", "-P-I/usr/include/freetype2", "-Jbuild", "-Jassets", "-link-debuglib"], "dflags": ["-Xcc=-mno-sse", "-P-I/usr/include/freetype2", "-Jbuild", "-Jassets", "-link-debuglib"],
"dflags-dmd": ["-P=-DSTBI_NO_SIMD"] "dflags-dmd": ["-P=-DSTBI_NO_SIMD"]
}, },
{
"name": "editor-test",
"targetType": "executable",
"targetName": "Editor",
"targetPath": "build",
"sourceFiles-linux": ["build/libvma.a", "build/libstb.a", "build/libm3d.a", "build/libcglm.a"],
"sourceFiles-windows": [],
"importPaths": ["src/editor", "src/dlib", "src/dlib/external/xxhash", "src/VulkanRenderer"],
"sourcePaths": ["src/editor", "src/dlib", "src/dlib/external/xxhash", "src/VulkanRenderer"],
"libs-linux": ["X11", "vulkan", "stdc++", "xfixes", "freetype"],
"libs-windows": [],
"versions": ["VULKAN_DEBUG"],
"preGenerateCommands-linux": ["./build.sh"],
"preGenerateCommands-windows": [],
"dflags": ["-Xcc=-mno-sse", "-P-I/usr/include/freetype2", "-Jbuild", "-Jassets", "-link-debuglib", "-unittest"],
"dflags-dmd": ["-P=-DSTBI_NO_SIMD"]
},
] ]
} }

@ -1 +1 @@
Subproject commit 589b2191babf009bef097d1272fac4a05a4c280d Subproject commit b3639d9c884c09fb98b750ba613118d633470312

@ -1 +1 @@
Subproject commit 60b4e0cb27ba304c7b9444a2da8a581211c21ca9 Subproject commit e5f91cae6d90c40e458e1208c2ab8f8eb917c99a

View File

@ -139,25 +139,25 @@ Cycle(EditorCtx* ctx, Inputs* inputs)
g_input_mode = ctx.state == ES.InputMode; g_input_mode = ctx.state == ES.InputMode;
BeginUI(ctx, inputs); BeginUI(inputs);
UIPanel* root = ctx.base_panel; UIPanel* root = ctx.base_panel;
root.size.x = g_ui_ctx.res.x; root.size.x = g_ui_ctx.res.x;
root.size.y = g_ui_ctx.res.y; root.size.y = g_ui_ctx.res.y;
root.rect.vec0 = 0.0; root.rect.p0 = 0.0;
root.rect.vec1 = 0.0; root.rect.p1 = 0.0;
static foreach(axis; A2D.min .. A2D.max) static foreach(axis; A2D.min .. A2D.max)
{ {
for(UIPanel* p = root; !Nil(p); p = Recurse(p, root, g_UI_NIL_PANEL)) for(UIPanel* p = root; !Nil(p); p = Recurse(p, root, g_UI_NIL_PANEL))
{ {
f32 pos = p.rect.vec0.v[axis]; f32 pos = p.rect.p0.v[axis];
for(UIPanel* c = p.first; !Nil(c); c = c.next) for(UIPanel* c = p.first; !Nil(c); c = c.next)
{ {
c.size.v[axis] = p.axis == axis ? c.pct * p.size.v[axis] : p.size.v[axis]; c.size.v[axis] = p.axis == axis ? c.pct * p.size.v[axis] : p.size.v[axis];
c.rect.vec0.v[axis] = pos; c.rect.p0.v[axis] = pos;
c.rect.vec1.v[axis] = pos + c.size.v[axis]; c.rect.p1.v[axis] = pos + c.size.v[axis];
if(axis == p.axis) if(axis == p.axis)
{ {
@ -571,13 +571,13 @@ HandleInputs(EditorCtx* ctx, Inputs* inputs)
UIPanel* panel = GetFocusedPanel(); UIPanel* panel = GetFocusedPanel();
FlatBuffer* fb = !Nil(panel) ? &panel.ed.buf : null; FlatBuffer* fb = !Nil(panel) ? &panel.ed.buf : null;
for(auto node = inputs.list.first; node != null; node = node.next) for(auto node = inputs.first; node != null; node = node.next)
{ {
bool taken = false; bool taken = false;
Input key = node.value.key; Input key = node.key;
Modifier md = node.value.md; Modifier md = node.md;
bool pressed = node.value.pressed; bool pressed = node.pressed;
if (pressed) if (pressed)
{ {
@ -588,11 +588,11 @@ HandleInputs(EditorCtx* ctx, Inputs* inputs)
} }
else if(ctx.state == ES.InputMode) else if(ctx.state == ES.InputMode)
{ {
taken = HandleInputMode(ctx, node.value); taken = HandleInputMode(ctx, node);
} }
else if(ctx.state == ES.CmdOpen) else if(ctx.state == ES.CmdOpen)
{ {
taken = HandleCmdMode(ctx, node.value); taken = HandleCmdMode(ctx, node);
} }
else if(ctx.state == ES.SetPanelFocus) else if(ctx.state == ES.SetPanelFocus)
{ {
@ -646,7 +646,7 @@ HandleInputs(EditorCtx* ctx, Inputs* inputs)
} break; } break;
case Semicolon: case Semicolon:
{ {
if(Shift(node.value.md)) if(Shift(node.md))
{ {
ctx.state = ES.CmdOpen; ctx.state = ES.CmdOpen;
taken = true; taken = true;
@ -701,7 +701,7 @@ HandleInputs(EditorCtx* ctx, Inputs* inputs)
if(taken) if(taken)
{ {
DLLRemove(&inputs.list, node, null); DLLRemove(inputs, node, null);
} }
} }
@ -714,7 +714,7 @@ HandleInputs(EditorCtx* ctx, Inputs* inputs)
} }
void void
MoveCursor(InputEvent ev) MoveCursor(InputEvent* ev)
{ {
UIPanel* p = GetFocusedPanel(); UIPanel* p = GetFocusedPanel();
@ -754,7 +754,7 @@ CharCases()
} }
bool bool
HandleInputMode(EditorCtx* ctx, InputEvent ev) HandleInputMode(EditorCtx* ctx, InputEvent* ev)
{ {
bool taken = false; bool taken = false;
@ -912,7 +912,7 @@ GetCommands(CmdPalette* cmd)
} }
bool bool
HandleCmdMode(EditorCtx* ctx, InputEvent ev) HandleCmdMode(EditorCtx* ctx, InputEvent* ev)
{ {
u8 result = 0; u8 result = 0;
bool taken = false; bool taken = false;

View File

@ -47,7 +47,7 @@ void main(string[] argv)
SetExtent(&g_ui_ctx.rd, window.w, window.h); SetExtent(&g_ui_ctx.rd, window.w, window.h);
} }
if(inputs.list.first == null) if(inputs.first == null)
{ {
no_ev_count += 1; no_ev_count += 1;
Pause(); Pause();

View File

@ -2,6 +2,7 @@ import dlib;
import dlib.util : HTPush = Push; import dlib.util : HTPush = Push;
import vulkan; import vulkan;
import vulkan : RendererGetExtent = GetExtent;
import buffer; import buffer;
import parsing; import parsing;
import editor; import editor;
@ -10,7 +11,7 @@ import views;
import std.stdio; import std.stdio;
import std.math.traits : isNaN; import std.math.traits : isNaN;
import std.math.rounding : ceil; import std.math.rounding : ceil, floor;
import std.math.exponential : pow; import std.math.exponential : pow;
import std.math.remainder : fmod; import std.math.remainder : fmod;
import std.format : sformat; import std.format : sformat;
@ -48,39 +49,38 @@ const u8[] FONT_BYTES = import("pc-9800.ttf");
const u8[] VERTEX_BYTES = import("gui.vert.spv"); const u8[] VERTEX_BYTES = import("gui.vert.spv");
const u8[] FRAGMENT_BYTES = import("gui.frag.spv"); const u8[] FRAGMENT_BYTES = import("gui.frag.spv");
f32 g_corner_radius_default = 2.0; f32 g_corner_radius_default = 2.0;
f32 g_edge_softness_default = 0.1; f32 g_edge_softness_default = 0.1;
f32 g_border_thickness_default = 2.0; f32 g_border_thickness_default = 2.0;
Vec4[4] g_bg_col_default = BG_COL; Vec4[4] g_bg_col_default = BG_COL;
Vec4[4] g_bg_hl_col_default = BG_HL_COL; Vec4[4] g_bg_hl_col_default = BG_HL_COL;
Vec4[4] g_border_col_default = BORDER_COL; Vec4[4] g_border_col_default = BORDER_COL;
Vec4[4] g_border_hl_col_default = BORDER_HL_COL; Vec4[4] g_border_hl_col_default = BORDER_HL_COL;
Vec4 g_text_col_default = TEXT_COL; Vec4 g_text_col_default = TEXT_COL;
Vec4 g_text_hl_col_default = TEXT_HL_COL; Vec4 g_text_hl_col_default = TEXT_HL_COL;
UISize[2] g_size_info_default = [UISize(ST.Percentage, 1.0), UISize(ST.Percentage, 1.0)]; UISize[2] g_size_info_default = [UISize(ST.Percentage, 1.0), UISize(ST.Percentage, 1.0)];
Axis2D g_layout_axis_default = A2D.X;
Vec2 g_padding_default = Vec2(0.0);
const UI_COUNT = 5000; const UI_COUNT = 5000;
__gshared const UIPanel g_ui_nil_panel; __gshared const UIPanel g_ui_nil_panel;
__gshared UIPanel* g_UI_NIL_PANEL; __gshared UIPanel* g_UI_NIL_PANEL;
__gshared const UIItem g_ui_nil_item;
__gshared const UIItem g_ui_nil_item; __gshared UIItem* g_UI_NIL;
__gshared UIItem* g_UI_NIL; __gshared const UIInput g_ui_nil_input;
__gshared UIInput* g_UI_NIL_INPUT;
__gshared const Stack!f32 g_nil_f32_stack;
__gshared Stack!f32* g_NIL_F32_STACK;
__gshared const Stack!Vec4[4] g_nil_gradient_col_stack;
__gshared Stack!Vec4[4]* g_NIL_GRAD_COL_STACK;
__gshared const Stack!Vec4 g_nil_vec4_stack;
__gshared Stack!Vec4* g_NIL_VEC4_STACK;
__gshared const Stack!u32 g_nil_u32_stack;
__gshared Stack!u32* g_NIL_U32_STACK;
alias g_parent_default = g_UI_NIL; alias g_parent_default = g_UI_NIL;
__gshared const Stack!f32 g_nil_f32_stack;
__gshared Stack!f32* g_NIL_F32_STACK;
__gshared const Stack!Vec4[4] g_nil_gradient_col_stack;
__gshared Stack!Vec4[4]* g_NIL_GRAD_COL_STACK;
__gshared const Stack!Vec4 g_nil_vec4_stack;
__gshared Stack!Vec4* g_NIL_VEC4_STACK;
__gshared const Stack!u32 g_nil_u32_stack;
__gshared Stack!u32* g_NIL_U32_STACK;
enum Axis2D enum Axis2D
{ {
X, X,
@ -106,31 +106,47 @@ enum UIFlags
Clamp = UIFlags.ClampX | UIFlags.ClampY, Clamp = UIFlags.ClampX | UIFlags.ClampY,
} }
alias UIF = UIFlags; alias UIF = UIFlags;
enum UIEventFlag
{
None, // Events e.g. pressing/releasing/scrolling
}
enum UISignal enum UISignal
{ {
None = 0x00, None = 0,
Clicked = 0x01, Clicked = 1<<0,
Dragged = 0x02, // Handle click/drag specifics in view? Dragged = 1<<1,
Hovered = 1<<2,
} }
alias UIS = UISignal; alias UIS = UISignal;
enum UIEvent
{
None = 0,
Click = 1<<0,
Drag = 1<<1,
DragStart = 1<<2,
Press = 1<<3,
}
alias UIE = UIEvent;
struct UIInput
{
UIEvent type;
UIInput* next, prev;
union
{
Input key;
IVec2 rel;
IVec2 pos;
}
}
mixin template mixin template
UICtxParameter(T, string name) UICtxParameter(T, string name)
{ {
static string static string
CtxParameterGen(T, string name)() CtxParameterGen(T, string name)()
{ {
import std.traits; import std.traits, std.array;
import std.array;
string stack_top = "\tStackTop!("~T.stringof~") "~name~";\n"; string stack_top = "\tStackTop!("~T.stringof~") "~name~";\n";
string stack = "\tStack!("~T.stringof~")* "~name~"_top;\n"; string stack = "\tStack!("~T.stringof~")* "~name~"_top;\n";
@ -178,7 +194,11 @@ struct UICtx
u64 frame; u64 frame;
u64 f_idx; u64 f_idx;
PlatformWindow* window; LinkedList!(UIInput) events;
IVec2 mouse_pos;
IVec2 drag_start;
PlatformWindow* window;
Renderer rd; Renderer rd;
Descriptor font_atlas; Descriptor font_atlas;
Descriptor sampler; Descriptor sampler;
@ -186,7 +206,7 @@ PlatformWindow* window;
DescSetLayout desc_set_layout; DescSetLayout desc_set_layout;
DescSet desc_set; DescSet desc_set;
PipelineLayout pipeline_layout; PipelineLayout pipeline_layout;
PushConst pc; Mat4 projection;
Vec2 res; Vec2 res;
u8[] font_data; u8[] font_data;
@ -215,6 +235,8 @@ PlatformWindow* window;
mixin UICtxParameter!(Vec4, "text_col"); mixin UICtxParameter!(Vec4, "text_col");
mixin UICtxParameter!(Vec4, "text_hl_col"); mixin UICtxParameter!(Vec4, "text_hl_col");
mixin UICtxParameter!(UISize[2], "size_info"); mixin UICtxParameter!(UISize[2], "size_info");
mixin UICtxParameter!(Axis2D, "layout_axis");
mixin UICtxParameter!(Vec2, "padding");
debug bool dbg; debug bool dbg;
} }
@ -257,14 +279,17 @@ mixin template UIItemParameters()
struct UIItem struct UIItem
{ {
UIKey key; UIKey key;
u64 last_frame; UIFlags flags;
Vec2 dragged; u64 last_frame;
IVec2 dragged;
UIItem* next, prev, first, last; // parent in mixin UIItem* next, prev, first, last; // parent in mixin
Rect rect;
Vec2 size;
Rect pref_size; u8[] display_string;
Rect size;
mixin UIItemParameters!(); mixin UIItemParameters!();
} }
@ -273,7 +298,7 @@ enum SizeType
{ {
Pixels, Pixels,
Percentage, Percentage,
FitChild, ChildrenSum,
} }
alias ST = SizeType; alias ST = SizeType;
@ -294,23 +319,10 @@ struct UIBuffer
u32 count; u32 count;
} }
struct PushConst
{
Mat4 projection;
}
struct GlyphBounds struct GlyphBounds
{ {
f32 r = 0.0; f32 r = 0.0, l = 0.0, t = 0.0, b = 0.0, w = 0.0, h = 0.0;
f32 l = 0.0; f32 atlas_r = 0.0, atlas_l = 0.0, atlas_t = 0.0, atlas_b = 0.0;
f32 t = 0.0;
f32 b = 0.0;
f32 w = 0.0;
f32 h = 0.0;
f32 atlas_r = 0.0;
f32 atlas_l = 0.0;
f32 atlas_t = 0.0;
f32 atlas_b = 0.0;
} }
struct Vertex struct Vertex
@ -331,18 +343,7 @@ struct Vertex
union Rect union Rect
{ {
Vec2[2] v; Vec2[2] v;
struct struct { Vec2 p0, p1; };
{
Vec2 vec0;
Vec2 vec1;
};
struct
{
f32 x0;
f32 y0;
f32 x1;
f32 y1;
};
} }
alias UIHash = u64; alias UIHash = u64;
@ -351,8 +352,7 @@ alias UIPair = KVPair!(UIHash, UIItem*);
struct UIKey struct UIKey
{ {
u8[] text; u8[] text, hash_text;
u8[] hash_text;
u64 hash; u64 hash;
} }
@ -361,14 +361,7 @@ struct UIPanel
u8[] id; u8[] id;
Editor* ed; Editor* ed;
UIPanel* parent; UIPanel* parent, next, prev, first, last, list_next;
UIPanel* next;
UIPanel* prev;
UIPanel* first;
UIPanel* last;
UIPanel* list_next;
Axis2D axis; Axis2D axis;
f32 pct; f32 pct;
Vec4 color; Vec4 color;
@ -376,12 +369,8 @@ struct UIPanel
Rect rect; Rect rect;
Vec2 size; Vec2 size;
i64 start_ln; i64 start_ln, end_ln, vis_lines, prev_start_ln;
i64 end_ln; f32 scroll_offset, scroll_target;
i64 vis_lines;
i64 prev_start_ln;
f32 scroll_offset;
f32 scroll_target;
} }
struct TextPart struct TextPart
@ -391,28 +380,30 @@ struct TextPart
u32 count; u32 count;
} }
// Keep defaults, pop anything used once
enum bool StringType(T) = (is(T: string) || is(T: u8[]) || is(T: char[])); enum bool StringType(T) = (is(T: string) || is(T: u8[]) || is(T: char[]));
void void
InitUICtx(PlatformWindow* window) InitUICtx(PlatformWindow* window)
{ {
version(linux) version(ENABLE_RENDERER)
{ {
PlatformHandles handles = { version(linux)
display: window.display, {
window: window.window, PlatformHandles handles = {
}; display: window.display,
} window: window.window,
};
}
version(Windows) version(Windows)
{ {
}
} }
g_UI_NIL = cast(UIItem*)&g_ui_nil_item; g_UI_NIL = cast(UIItem*)&g_ui_nil_item;
g_UI_NIL_PANEL = cast(UIPanel*)&g_ui_nil_panel; g_UI_NIL_PANEL = cast(UIPanel*)&g_ui_nil_panel;
g_UI_NIL_INPUT = cast(UIInput*)&g_ui_nil_input;
g_NIL_F32_STACK = cast(Stack!f32*)&g_nil_f32_stack; g_NIL_F32_STACK = cast(Stack!f32*)&g_nil_f32_stack;
g_NIL_GRAD_COL_STACK = cast(Stack!Vec4[4]*)&g_nil_gradient_col_stack; g_NIL_GRAD_COL_STACK = cast(Stack!Vec4[4]*)&g_nil_gradient_col_stack;
g_NIL_VEC4_STACK = cast(Stack!Vec4*)&g_nil_vec4_stack; g_NIL_VEC4_STACK = cast(Stack!Vec4*)&g_nil_vec4_stack;
@ -424,83 +415,79 @@ InitUICtx(PlatformWindow* window)
UIBuffer[FRAME_OVERLAP] buffers; UIBuffer[FRAME_OVERLAP] buffers;
FontFace font = OpenFont(cast(u8[])FONT_BYTES);
FontAtlasBuf atlas_buf = CreateAtlas(&arena, font, 16.0, 256);
Vec4 b = Vec4(Vec3(0.0), 1.0);
UICtx ctx = { UICtx ctx = {
rd: InitRenderer(handles, MB(16), MB(8)),
items: CreateHashTable!(UIHash, UIItem*)(12), items: CreateHashTable!(UIHash, UIItem*)(12),
arena: arena, arena: arena,
temp_arena: CreateArena(MB(1)), temp_arena: CreateArena(MB(1)),
drag_item: g_UI_NIL, drag_item: g_UI_NIL,
atlas_buf: atlas_buf, font: OpenFont(cast(u8[])FONT_BYTES),
font: font,
font_data: cast(u8[])FONT_BYTES, font_data: cast(u8[])FONT_BYTES,
text_size: 16.0, text_size: 16.0,
tab_width: 2, tab_width: 2,
}; };
for(u64 i = 0; i < FRAME_OVERLAP; i += 1) ctx.atlas_buf = CreateAtlas(&arena, ctx.font, 16.0, 256);
version(ENABLE_RENDERER)
{ {
ctx.buffers[i].m_vtx = CreateMappedBuffer!(Vertex)(&ctx.rd, BT.Vertex, VERTEX_MAX_COUNT); ctx.rd = InitRenderer(handles, MB(16), MB(8));
ctx.buffers[i].m_idx = CreateMappedBuffer!(u32)(&ctx.rd, BT.Index, cast(u64)(ceil(VERTEX_MAX_COUNT*1.5)));
ctx.buffers[i].vtx = ctx.buffers[i].m_vtx.data; for(u64 i = 0; i < FRAME_OVERLAP; i += 1)
ctx.buffers[i].idx = ctx.buffers[i].m_idx.data; {
ctx.buffers[i].m_vtx = CreateMappedBuffer!(Vertex)(&ctx.rd, BT.Vertex, VERTEX_MAX_COUNT);
ctx.buffers[i].m_idx = CreateMappedBuffer!(u32)(&ctx.rd, BT.Index, cast(u64)(ceil(VERTEX_MAX_COUNT*1.5)));
ctx.buffers[i].vtx = ctx.buffers[i].m_vtx.data;
ctx.buffers[i].idx = ctx.buffers[i].m_idx.data;
}
DescLayoutBinding[2] layout_bindings = [
{ binding: 0, descriptorType: DT.Image, descriptorCount: 1, stageFlags: SS.All },
{ binding: 1, descriptorType: DT.Sampler, descriptorCount: 1, stageFlags: SS.All },
];
ctx.desc_set_layout = CreateDescSetLayout(&ctx.rd, layout_bindings);
ctx.desc_set = AllocDescSet(&ctx.rd, ctx.desc_set_layout);
ctx.pipeline_layout = CreatePipelineLayout(&ctx.rd, ctx.desc_set_layout, Mat4.sizeof);
Attribute[14] attributes = [
{ binding: 0, location: 0, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof + Vec4.sizeof * 0},
{ binding: 0, location: 1, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof + Vec4.sizeof * 1},
{ binding: 0, location: 2, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof + Vec4.sizeof * 2},
{ binding: 0, location: 3, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof + Vec4.sizeof * 3},
{ binding: 0, location: 4, format: FMT.RG_F32, offset: Vertex.dst_start.offsetof },
{ binding: 0, location: 5, format: FMT.RG_F32, offset: Vertex.dst_end.offsetof },
{ binding: 0, location: 6, format: FMT.RG_F32, offset: Vertex.src_start.offsetof },
{ binding: 0, location: 7, format: FMT.RG_F32, offset: Vertex.src_end.offsetof },
{ binding: 0, location: 8, format: FMT.R_F32, offset: Vertex.border_thickness.offsetof },
{ binding: 0, location: 9, format: FMT.R_F32, offset: Vertex.corner_radius.offsetof },
{ binding: 0, location: 10, format: FMT.R_F32, offset: Vertex.edge_softness.offsetof },
{ binding: 0, location: 11, format: FMT.R_F32, offset: Vertex.raised.offsetof },
{ binding: 0, location: 12, format: FMT.R_F32, offset: Vertex.z_index.offsetof },
{ binding: 0, location: 13, format: FMT.R_U32, offset: Vertex.texture.offsetof },
];
GfxPipelineInfo ui_info = {
vertex_shader: cast(u8[])VERTEX_BYTES,
frag_shader: cast(u8[])FRAGMENT_BYTES,
input_rate: IR.Instance,
input_rate_stride: Vertex.sizeof,
layout: ctx.pipeline_layout,
vertex_attributes: attributes,
src_color: BF.SrcAlpha,
dst_color: BF.OneMinusSrcAlpha,
color_op: BO.Add,
src_alpha: BF.One,
dst_alpha: BF.Zero,
alpha_op: BO.Add,
};
CreateGraphicsPipeline(&ctx.rd, &ctx.pipeline, &ui_info);
CreateImageView(&ctx.rd, &ctx.font_atlas, ctx.atlas_buf.atlas.width, ctx.atlas_buf.atlas.height, 4, ctx.atlas_buf.data, DT.Image, 0);
ctx.sampler = CreateSampler(&ctx.rd, MipmapMode.Nearest, 1);
Write(&ctx.rd, ctx.desc_set, [ctx.font_atlas, ctx.sampler]);
SetClearColors(&ctx.rd, [0.0, 0.0, 0.0, 1.0], [0.0, 0.0, 0.0, 0.0]);
} }
DescLayoutBinding[2] layout_bindings = [
{ binding: 0, descriptorType: DT.Image, descriptorCount: 1, stageFlags: SS.All },
{ binding: 1, descriptorType: DT.Sampler, descriptorCount: 1, stageFlags: SS.All },
];
ctx.desc_set_layout = CreateDescSetLayout(&ctx.rd, layout_bindings);
ctx.desc_set = AllocDescSet(&ctx.rd, ctx.desc_set_layout);
ctx.pipeline_layout = CreatePipelineLayout(&ctx.rd, ctx.desc_set_layout, PushConst.sizeof);
Attribute[14] attributes = [
{ binding: 0, location: 0, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof + Vec4.sizeof * 0},
{ binding: 0, location: 1, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof + Vec4.sizeof * 1},
{ binding: 0, location: 2, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof + Vec4.sizeof * 2},
{ binding: 0, location: 3, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof + Vec4.sizeof * 3},
{ binding: 0, location: 4, format: FMT.RG_F32, offset: Vertex.dst_start.offsetof },
{ binding: 0, location: 5, format: FMT.RG_F32, offset: Vertex.dst_end.offsetof },
{ binding: 0, location: 6, format: FMT.RG_F32, offset: Vertex.src_start.offsetof },
{ binding: 0, location: 7, format: FMT.RG_F32, offset: Vertex.src_end.offsetof },
{ binding: 0, location: 8, format: FMT.R_F32, offset: Vertex.border_thickness.offsetof },
{ binding: 0, location: 9, format: FMT.R_F32, offset: Vertex.corner_radius.offsetof },
{ binding: 0, location: 10, format: FMT.R_F32, offset: Vertex.edge_softness.offsetof },
{ binding: 0, location: 11, format: FMT.R_F32, offset: Vertex.raised.offsetof },
{ binding: 0, location: 12, format: FMT.R_F32, offset: Vertex.z_index.offsetof },
{ binding: 0, location: 13, format: FMT.R_U32, offset: Vertex.texture.offsetof },
];
GfxPipelineInfo ui_info = {
vertex_shader: cast(u8[])VERTEX_BYTES,
frag_shader: cast(u8[])FRAGMENT_BYTES,
input_rate: IR.Instance,
input_rate_stride: Vertex.sizeof,
layout: ctx.pipeline_layout,
vertex_attributes: attributes,
src_color: BF.SrcAlpha,
dst_color: BF.OneMinusSrcAlpha,
color_op: BO.Add,
src_alpha: BF.One,
dst_alpha: BF.Zero,
alpha_op: BO.Add,
};
CreateGraphicsPipeline(&ctx.rd, &ctx.pipeline, &ui_info);
CreateImageView(&ctx.rd, &ctx.font_atlas, ctx.atlas_buf.atlas.width, ctx.atlas_buf.atlas.height, 4, ctx.atlas_buf.data, DT.Image, 0);
ctx.sampler = CreateSampler(&ctx.rd, MipmapMode.Nearest, 1);
Write(&ctx.rd, ctx.desc_set, [ctx.font_atlas, ctx.sampler]);
SetClearColors(&ctx.rd, [0.0, 0.0, 0.0, 1.0], [0.0, 0.0, 0.0, 0.0]);
InitStacks(&ctx); InitStacks(&ctx);
g_ui_ctx = ctx; g_ui_ctx = ctx;
@ -529,29 +516,123 @@ UIItem*
MakeItem(T)(T k) if(is(T: UIKey) || StringType!T) MakeItem(T)(T k) if(is(T: UIKey) || StringType!T)
{ {
UICtx* ctx = GetCtx(); UICtx* ctx = GetCtx();
UIItem* item = Get(k); UIItem* item = Get(k);
Set(item, ctx); Set(item, ctx);
if(!Nil(item.parent)) if(!Nil(item.parent))
{ {
DLLPush(item.parent, item, g_UI_NIL); DLLPush(item.parent, item, g_UI_NIL);
} }
AutoPopStacks(ctx); AutoPopStacks(ctx);
return item; return item;
} }
UISignal
Signal(UIItem* item)
{
UISignal signal;
UICtx* ctx = GetCtx();
i32 x = ctx.mouse_pos.x;
i32 y = ctx.mouse_pos.y;
if(x >= item.rect.p0.x && x <= item.rect.p1.x && y >= item.rect.p0.y && y <= item.rect.p1.y)
{
signal |= UIS.Hovered;
}
item.dragged = 0;
for(UIInput* i = ctx.events.first; !CheckNil(g_UI_NIL_INPUT, i); i = i.next)
{
bool taken;
if(item.flags & UIF.Clickable && i.type == UIE.Click && InBounds(ctx.mouse_pos, &item.rect))
{
signal |= UIS.Clicked;
taken = true;
}
if(Nil(ctx.drag_item) && item.flags & UIF.Draggable && i.type == UIE.DragStart && InBounds(i.pos, &item.rect))
{
signal |= UIS.Dragged;
ctx.drag_item = item;
taken = true;
}
if(ctx.drag_item == item && i.type == UIE.Drag)
{
item.dragged += i.rel;
taken = true;
}
if(taken)
{
DLLRemove(&ctx.events, i, g_UI_NIL_INPUT);
}
}
return signal;
}
void void
BeginUI(EditorCtx* edctx, Inputs* inputs) PushUIEvent(UICtx* ctx, UIInput input)
{
UIInput* ev = Alloc!(UIInput)(&ctx.temp_arena);
*ev = input;
DLLPush(&ctx.events, ev, null);
}
void
BeginUI(Inputs* inputs)
{ {
UICtx* ctx = GetCtx(); UICtx* ctx = GetCtx();
Arena* a = &ctx.temp_arena; Arena* a = &ctx.temp_arena;
Reset(a); Reset(a);
ctx.events.first = ctx.events.last = null;
static bool dragging;
static bool mouse_down;
for(auto i = inputs.first; i; i = i.next)
{
switch(i.key)
{
case Input.LeftClick:
{
mouse_down = i.pressed;
if(!mouse_down && !dragging)
{
PushUIEvent(ctx, UIInput(UIE.Click));
}
dragging = false;
} break;
case Input.MouseMotion:
{
ctx.mouse_pos = IVec2(i.x, i.y);
if(!dragging && mouse_down)
{
PushUIEvent(ctx, UIInput(type: UIE.DragStart, pos: IVec2(i.x, i.y)));
}
dragging = mouse_down;
if(dragging)
{
PushUIEvent(ctx, UIInput(type: UIE.Drag, rel: IVec2(i.rel_x, i.rel_y)));
}
} break;
default:
{
PushUIEvent(ctx, UIInput(type: UIE.Press, key: i.key));
} break;
}
}
// Ctx state // Ctx state
ctx.f_idx = ctx.frame%FRAME_OVERLAP; ctx.f_idx = ctx.frame%FRAME_OVERLAP;
ctx.inputs = inputs; ctx.inputs = inputs;
@ -559,26 +640,34 @@ BeginUI(EditorCtx* edctx, Inputs* inputs)
ResetStacks(ctx); ResetStacks(ctx);
// Rendering version(ENABLE_RENDERER)
BeginFrame(&ctx.rd); {
BeginRendering(&ctx.rd); BeginFrame(&ctx.rd);
BeginRendering(&ctx.rd);
}
Vec2 ext = GetExtent(&ctx.rd); Vec2 ext = GetExtent(&ctx.rd);
if(ext != ctx.res) if(ext != ctx.res)
{ {
ctx.res = ext; ctx.res = ext;
Ortho(&ctx.pc.projection, 0.0, 0.0, ext.x, ext.y, -10.0, 10.0); Ortho(&ctx.projection, 0.0, 0.0, ext.x, ext.y, -10.0, 10.0);
} }
PushConstants(&ctx.rd, ctx.pipeline, &ctx.pc); version(ENABLE_RENDERER)
{
Bind(&ctx.rd, ctx.pipeline, ctx.desc_set); PushConstants(&ctx.rd, ctx.pipeline, &ctx.projection);
Bind(&ctx.rd, ctx.pipeline, ctx.desc_set);
}
memset(ctx.buffers[ctx.f_idx].vtx.ptr, 0, Vertex.sizeof * ctx.buffers[ctx.f_idx].count); memset(ctx.buffers[ctx.f_idx].vtx.ptr, 0, Vertex.sizeof * ctx.buffers[ctx.f_idx].count);
memset(ctx.buffers[ctx.f_idx].idx.ptr, 0, u32.sizeof * ctx.buffers[ctx.f_idx].count); memset(ctx.buffers[ctx.f_idx].idx.ptr, 0, u32.sizeof * ctx.buffers[ctx.f_idx].count);
ctx.buffers[ctx.f_idx].count = 0; ctx.buffers[ctx.f_idx].count = 0;
ctx.root = Root(ctx); // Root Item
UISize[2] sizes = [UISize(ST.Pixels, ctx.res.x), UISize(ST.Pixels, ctx.res.y)];
Push!("size_info", true)(ctx, sizes);
ctx.root = MakeItem("###root");
Push!("parent")(ctx, ctx.root);
} }
void void
@ -592,32 +681,161 @@ EndUI()
{ {
if(item.size_info[axis].type == ST.Pixels) if(item.size_info[axis].type == ST.Pixels)
{ {
item.size.v[axis] = item.size_info[axis].value; item.size.v[axis] = item.size_info[axis].value + item.padding.v[axis];
}
}
for(UIItem* item = ctx.root; !Nil(item); item = Recurse!(true)(item, ctx.root, g_UI_NIL))
{
if(item.size_info[axis].type == ST.Percentage)
{
for(UIItem* p = item.parent; !Nil(p); p = p.parent)
{
if(p.size_info[axis].type == ST.Pixels)
{
item.size.v[axis] = floor(item.size_info[axis].value * item.parent.size[axis]);
break;
}
}
}
}
for(UIItem* item = ctx.root; !Nil(item); item = Recurse!(false)(item, ctx.root, g_UI_NIL))
{
if(item.size_info[axis].type == ST.ChildrenSum)
{
f32 size = 0.0;
for(UIItem* c = item.first; !Nil(c); c = c.next)
{
size += c.size.v[axis];
}
}
}
for(UIItem* item = ctx.root; !Nil(item); item = Recurse!(true)(item, ctx.root, g_UI_NIL))
{
if(axis == item.layout_axis)
{
f32 children_size = 0.0;
for(UIItem* c = item.first; !Nil(c); c = c.next)
{
children_size += c.size.v[axis];
}
if(children_size > item.size[axis])
{
Logf("%s %s %s %s", axis, cast(char[])item.key.hash_text, children_size, item.size[axis]);
u64 child_count;
for(UIItem* c = item.first; !Nil(c); c = c.next)
{
f32 reduced = c.size.v[axis] - c.size.v[axis]*c.size_info[axis].strictness;
children_size -= reduced;
c.size.v[axis] -= reduced;
child_count += 1;
}
if(children_size > 0.0)
{
for(UIItem* c = item.last; !Nil(c); c = c.prev)
{
f32 reduced = Min(children_size, c.size[axis]);
children_size -= reduced;
c.size.v[axis] -= reduced;
if(children_size < 0.0009)
{
break;
}
}
assert(children_size < 0.0009);
}
}
}
else
{
for(UIItem* c = item.first; !Nil(c); c = c.next)
{
if(c.size.v[axis] > item.size.v[axis])
{
c.size.v[axis] = item.size.v[axis];
}
}
}
}
{
f32 pos = 0.0;
for(UIItem* item = ctx.root; !Nil(item);)
{
f32 next_pos = 0.0;
f32 end_pos = pos + item.size.v[axis];
item.rect.p0.v[axis] = pos;
item.rect.p1.v[axis] = end_pos;
assert(!isNaN(item.rect.p0.v[axis]));
assert(!isNaN(item.rect.p1.v[axis]));
next_pos = item.parent.layout_axis == axis ? end_pos : pos;
if(!Nil(item.first))
{
item = item.first;
}
else if(!Nil(item.next))
{
item = item.next;
pos = next_pos;
}
else for(UIItem* p = item.parent;; p = p.parent)
{
if(!Nil(p.next))
{
item = p.next;
pos = item.parent.layout_axis == axis ? item.prev.rect.p1.v[axis] : item.prev.rect.p0.v[axis];
break;
}
if(Nil(p))
{
item = g_UI_NIL;
break;
}
}
} }
} }
} }
with(ctx) version(ENABLE_RENDERER) with(ctx)
{ {
BindBuffers(&rd, &buffers[f_idx].m_idx, &buffers[f_idx].m_vtx); BindBuffers(&rd, &buffers[f_idx].m_idx, &buffers[f_idx].m_vtx);
DrawIndexed(&rd, 6, buffers[f_idx].count, 0); DrawIndexed(&rd, 6, buffers[f_idx].count, 0);
}
FinishRendering(&ctx.rd); FinishRendering(&rd);
SubmitAndPresent(&ctx.rd); SubmitAndPresent(&rd);
}
ctx.frame += 1; ctx.frame += 1;
} }
UIItem* u32[2]
Root(UICtx* ctx) GetExtent(Renderer* rd)
{ {
UISize[2] sizes = [UISize(ST.Pixels, ctx.res.x), UISize(ST.Pixels, ctx.res.y)]; version(ENABLE_RENDERER)
Push!("size_info")(ctx, sizes); {
return MakeItem("###root"); return RendererGetExtent(rd);
}
else
{
return [1280, 720];
}
} }
template StackIDs(string stack) template
StackIDs(string stack)
{ {
import std.string : replace; import std.string : replace;
struct Identifiers { string stack, stack_top_node; } struct Identifiers { string stack, stack_top_node; }
@ -935,8 +1153,9 @@ GlyphWidth(Glyph* g)
struct TextBuffer struct TextBuffer
{ {
u8[] text; u8[] text;
TS[] style; TS[] style;
TextBuffer* next;
} }
/* /*
@ -1094,7 +1313,7 @@ DrawPanel(UIPanel* panel, f32 lc_w, bool focus)
{ {
UICtx* ctx = GetCtx(); UICtx* ctx = GetCtx();
Vec2 pos = panel.rect.vec0; Vec2 pos = panel.rect.p0;
Vec2 size = panel.size; Vec2 size = panel.size;
DrawRect(pos, size, ctx.corner_radius_top.value, ctx.border_thickness_top.value, ctx.bg_col_top.value); DrawRect(pos, size, ctx.corner_radius_top.value, ctx.border_thickness_top.value, ctx.bg_col_top.value);
@ -1102,6 +1321,24 @@ DrawPanel(UIPanel* panel, f32 lc_w, bool focus)
DrawBorder(pos, size, ctx.border_thickness_top.value, ctx.corner_radius_top.value, ctx.edge_softness_top.value, focus ? ctx.border_hl_col_top.value : ctx.border_col_top.value); DrawBorder(pos, size, ctx.border_thickness_top.value, ctx.corner_radius_top.value, ctx.edge_softness_top.value, focus ? ctx.border_hl_col_top.value : ctx.border_col_top.value);
} }
static UISize[2]
MakeUISize(UISize x, UISize y)
{
return [x, y];
}
static UISize[2]
MakeUISizeX(SizeType type, f32 value, f32 strictness = 1.0)
{
return [UISize(type, value, strictness), UISize(ST.Percentage, 1.0)];
}
static UISize[2]
MakeUISizeY(SizeType type, f32 value, f32 strictness = 1.0)
{
return [UISize(ST.Percentage, 1.0), UISize(type, value, strictness)];
}
pragma(inline) Glyph* pragma(inline) Glyph*
GetGlyph(u8 ch) GetGlyph(u8 ch)
{ {
@ -1187,25 +1424,25 @@ Nil(UIItem* item)
} }
pragma(inline) bool pragma(inline) bool
InBounds(InputEvent* ev, Vec2 p0, Vec2 p1) InBounds(T)(T pos, Rect* rect)
{ {
return ev.x >= p0.x && ev.x <= p1.x && ev.y >= p0.y && ev.y <= p1.y; return pos.x >= rect.p0.x && pos.x <= rect.p1.x && pos.y >= rect.p0.y && pos.y <= rect.p1.y;
} }
bool bool
Clicked(UIItem* item, Vec2 p0, Vec2 p1) Clicked(UIItem* item, Rect* rect)
{ {
bool result; bool result;
UICtx* ctx = GetCtx(); UICtx* ctx = GetCtx();
for(auto n = ctx.inputs.list.first; !CheckNil(null, n) && Nil(ctx.drag_item); n = n.next) for(auto n = ctx.inputs.first; !CheckNil(null, n) && Nil(ctx.drag_item); n = n.next)
{ {
InputEvent* ev = &n.value; InputEvent* ev = n;
if(ev.key == Input.LeftClick && ev.pressed && InBounds(ev, p0, p1)) if(ev.key == Input.LeftClick && ev.pressed && InBounds(ev, rect))
{ {
result = true; result = true;
DLLRemove(&ctx.inputs.list, n, null); DLLRemove(ctx.inputs, n, null);
break; break;
} }
} }
@ -1214,27 +1451,27 @@ Clicked(UIItem* item, Vec2 p0, Vec2 p1)
} }
bool bool
Dragged(UIItem* item, Vec2 p0, Vec2 p1) Dragged(UIItem* item, Rect* rect)
{ {
bool result; bool result;
UICtx* ctx = GetCtx(); UICtx* ctx = GetCtx();
item.dragged = 0.0; item.dragged = 0;
if(Nil(ctx.drag_item) && Clicked(item, p0, p1)) if(Nil(ctx.drag_item) && Clicked(item, rect))
{ {
ctx.drag_item = item; ctx.drag_item = item;
} }
for(auto n = ctx.inputs.list.first; !CheckNil(null, n) && ctx.drag_item == item; n = n.next) for(auto n = ctx.inputs.first; !CheckNil(null, n) && ctx.drag_item == item; n = n.next)
{ {
InputEvent* ev = &n.value; InputEvent* ev = n;
bool taken; bool taken;
if(ev.key == Input.MouseMotion) if(ev.key == Input.MouseMotion)
{ {
item.dragged.x += cast(f32)ev.rel_x; item.dragged.x += ev.rel_x;
item.dragged.y += cast(f32)ev.rel_y; item.dragged.y += ev.rel_y;
result = true; result = true;
taken = true; taken = true;
} }
@ -1246,7 +1483,7 @@ Dragged(UIItem* item, Vec2 p0, Vec2 p1)
if(taken) if(taken)
{ {
DLLRemove(&ctx.inputs.list, n, null); DLLRemove(ctx.inputs, n, null);
} }
} }
@ -1270,17 +1507,47 @@ unittest
} }
{ {
UICtx ctx; Inputs inputs;
InitStacks(&ctx); InitUICtx(null);
ResetStacks(&ctx); BeginUI(&inputs);
UICtx* ctx = GetCtx();
Vec4 w = Vec4(1.0); Vec4 w = Vec4(1.0);
Vec4[4] col = w; Vec4[4] col = w;
Push!("bg_col")(&ctx, col); Push!("bg_col")(ctx, col);
assert(ctx.bg_col.top.value == col); assert(ctx.bg_col.top.value == col);
Pop!("bg_col")(&ctx);
EndUI();
Vec2 extent = GetExtent(null);
assert(extent == ctx.root.size);
BeginUI(&inputs);
assert(ctx.bg_col.top.value == BG_COL); assert(ctx.bg_col.top.value == BG_COL);
EndUI();
// Layouts
{
BeginUI(&inputs);
Push!("size_info")(ctx, MakeUISizeX(ST.Percentage, 0.5));
UIItem* root = ctx.root;
UIItem* i0 = MakeItem("###i0");
UIItem* i1 = MakeItem("###i1");
assert(i0.parent == root);
assert(i1.parent == root);
EndUI();
Vec2 expected = Vec2(floor(root.size.x * 0.5), root.size.y);
assert(i0.size == expected);
assert(i1.size == expected);
}
} }
} }

View File

@ -25,18 +25,20 @@ Panel(UIPanel* panel)
parent.axis == A2D.Y ? 10 : 0 parent.axis == A2D.Y ? 10 : 0
); );
Vec2 p0 = panel.rect.vec0+adj; Vec2 p0 = panel.rect.p0+adj;
Vec2 p1 = panel.rect.vec1-adj; Vec2 p1 = panel.rect.p1-adj;
Rect r = Rect(p0: p0, p1: p1);
if(!Nil(prev)) with(panel) if(!Nil(prev)) with(panel)
{ {
Vec2 d0 = rect.vec0-adj; Vec2 d0 = rect.p0-adj;
Vec2 d1 = Vec2( Vec2 d1 = Vec2(
pax == A2D.X ? rect.vec0.x+adj.x : rect.vec1.x, pax == A2D.X ? rect.p0.x+adj.x : rect.p1.x,
pax == A2D.Y ? rect.vec0.y+adj.y : rect.vec1.y pax == A2D.Y ? rect.p0.y+adj.y : rect.p1.y
); );
if(Dragged(item, d0, d1) && item.dragged.v[pax] != 0.0) Rect dr = Rect(p0: d0, p1: d1);
if(Dragged(item, &dr) && item.dragged.v[pax] != 0.0)
{ {
f32 mov_pct = Remap(item.dragged.v[pax], 0.0, panel.parent.size.v[pax], 0.0, 1.0); f32 mov_pct = Remap(item.dragged.v[pax], 0.0, panel.parent.size.v[pax], 0.0, 1.0);
if(CheckPanelBounds(pct + mov_pct) && CheckPanelBounds(panel.prev.pct - mov_pct)) if(CheckPanelBounds(pct + mov_pct) && CheckPanelBounds(panel.prev.pct - mov_pct))
@ -49,7 +51,7 @@ Panel(UIPanel* panel)
if(panel.ed != null) if(panel.ed != null)
{ {
if(Clicked(item, p0, p1)) if(Clicked(item, &r))
{ {
SetFocusedPanel(panel); SetFocusedPanel(panel);
} }
@ -76,8 +78,8 @@ Panel(UIPanel* panel)
f32 y_rem = fmod(panel.scroll_offset, TEXT_SIZE); f32 y_rem = fmod(panel.scroll_offset, TEXT_SIZE);
f32 x = panel.rect.x0; f32 x = panel.rect.p0.x;
f32 y = panel.rect.y0 + TEXT_SIZE - fmod(panel.scroll_offset, TEXT_SIZE); f32 y = panel.rect.p1.y + TEXT_SIZE - fmod(panel.scroll_offset, TEXT_SIZE);
u64 i = panel.start_ln; u64 i = panel.start_ln;
for(auto buf = GetLine(&ed.buf, i); !CheckNil(g_NIL_LINE_BUF, buf) && i < panel.end_ln; i += 1, buf = GetLine(&ed.buf, i)) for(auto buf = GetLine(&ed.buf, i); !CheckNil(g_NIL_LINE_BUF, buf) && i < panel.end_ln; i += 1, buf = GetLine(&ed.buf, i))
@ -97,7 +99,7 @@ Panel(UIPanel* panel)
} }
else for(auto n = parts; n != null; n = n.next) else for(auto n = parts; n != null; n = n.next)
{ {
auto l = &n.value; auto l = n;
if(pos.y == i) if(pos.y == i)
{ {
@ -202,14 +204,14 @@ Nil(UIPanel* panel)
return panel == null || panel == g_UI_NIL_PANEL; return panel == null || panel == g_UI_NIL_PANEL;
} }
Node!(TextBuffer)* TextBuffer*
MakeMultiline(u8[] text, f32 width, TS[] style = []) MakeMultiline(u8[] text, f32 width, TS[] style = [])
{ {
f32 scaled_width = width * (g_ui_ctx.atlas_buf.atlas.size/g_ui_ctx.text_size); f32 scaled_width = width * (g_ui_ctx.atlas_buf.atlas.size/g_ui_ctx.text_size);
f32 text_width = CalcTextWidth(text); f32 text_width = CalcTextWidth(text);
u64 line_count = cast(u64)(ceil(text_width/scaled_width)); u64 line_count = cast(u64)(ceil(text_width/scaled_width));
Node!(TextBuffer)* node = null; TextBuffer* node = null;
if(line_count > 0) if(line_count > 0)
{ {
f32 w = 0.0; f32 w = 0.0;
@ -232,15 +234,15 @@ MakeMultiline(u8[] text, f32 width, TS[] style = [])
stl = ScratchAlloc!(TS)(style, start, len); stl = ScratchAlloc!(TS)(style, start, len);
} }
Node!(TextBuffer)* n = node; TextBuffer* n = node;
for(;;) for(;;)
{ {
if(node == null) if(node == null)
{ {
node = ScratchAlloc!(Node!(TextBuffer))(); node = ScratchAlloc!(TextBuffer)();
node.value.text = str; node.text = str;
node.value.style = stl; node.style = stl;
node.next = null; node.next = null;
break; break;
@ -248,10 +250,10 @@ MakeMultiline(u8[] text, f32 width, TS[] style = [])
if(n.next == null) if(n.next == null)
{ {
n.next = ScratchAlloc!(Node!(TextBuffer))(); n.next = ScratchAlloc!(TextBuffer)();
n.next.value.text = str; n.next.text = str;
n.next.value.style = stl; n.next.style = stl;
n.next.next = null; n.next.next = null;
break; break;