wip (some things are badly broken)

This commit is contained in:
Matthew 2026-01-01 18:38:57 +11:00
parent f4f311a49c
commit 62cd8c471f
4 changed files with 418 additions and 243 deletions

View File

@ -15,7 +15,7 @@ struct FlatBuffer
LineBuffers linebufs; LineBuffers linebufs;
BufferInfo info; BufferInfo info;
u8[] file_name; string file_name;
u8[] data; u8[] data;
u64 length; u64 length;
@ -116,7 +116,7 @@ const u64 SPACING = 2;
const u64 BUF_START = 1; const u64 BUF_START = 1;
FlatBuffer FlatBuffer
CreateFlatBuffer(u8[] data, u8[] file_name) CreateFlatBuffer(u8[] data, string file_name)
{ {
FlatBuffer fb = { FlatBuffer fb = {
arena: CreateArena(MB(1)), arena: CreateArena(MB(1)),
@ -133,7 +133,7 @@ CreateFlatBuffer(u8[] data, u8[] file_name)
} }
void void
Init(FlatBuffer* fb, u8[] data, u8[] file_name) Init(FlatBuffer* fb, u8[] data, string file_name)
{ {
u64 capacity = data.length > 0 ? RoundUp(cast(u64)(data.length)*2, KB(4)) : KB(4); u64 capacity = data.length > 0 ? RoundUp(cast(u64)(data.length)*2, KB(4)) : KB(4);
@ -158,7 +158,7 @@ SetBuffers(FlatBuffer* fb, u8[] data)
} }
void void
Change(FlatBuffer* fb, u8[] data, u8[] file_name) Change(FlatBuffer* fb, u8[] data, string file_name)
{ {
Free(fb.data); Free(fb.data);
Reset(&fb.linebufs.arena); Reset(&fb.linebufs.arena);

View File

@ -17,8 +17,6 @@ import std.string;
import core.stdc.stdio; import core.stdc.stdio;
f32 g_delta = 0.0; f32 g_delta = 0.0;
debug bool g_frame_step = false;
debug bool g_frame_continue = false;
struct EditorCtx struct EditorCtx
{ {
@ -31,7 +29,7 @@ struct EditorCtx
u64 editor_id_incr; u64 editor_id_incr;
Timer timer; Timer timer;
CmdPalette cmd; CmdPalette cmd;
u8[][] file_names; string[] file_names;
u64 panel_id; u64 panel_id;
Editor* focused_editor; Editor* focused_editor;
@ -44,7 +42,7 @@ struct CmdPalette
u8[] buffer; u8[] buffer;
u32 icount; u32 icount;
Command[] commands; Command[] commands;
u8[][] opt_strs; string[] opt_strs;
i64 selected; i64 selected;
Command current; Command current;
Parameter[] params; Parameter[] params;
@ -83,14 +81,16 @@ struct EditorChange
struct Command struct Command
{ {
u8[] name; string name;
string desc;
string cmd;
CmdType type; CmdType type;
} }
struct Parameter struct Parameter
{ {
u8[] value; string value;
bool visible; bool visible;
} }
enum CmdType enum CmdType
@ -123,6 +123,45 @@ const Command NO_CMD = {
type: CT.None, type: CT.None,
}; };
const Command[] CMD_LIST = [
{
name: "Open",
cmd: "open",
desc: "Open a file in the focused editor view.",
type: CT.OpenFile,
},
{
name: "Save",
cmd: "save",
desc: "Save the current file in the focused editor view.",
type: CT.SaveFile,
},
{
name: "New File",
cmd: "new file",
desc: "Create a new file with a specified name and location.",
type: CT.CreateFile,
},
{
name: "Vertical Split",
cmd: "vsplit",
desc: "Split the current editor view vertically.",
type: CT.VSplit,
},
{
name: "Horizontal Split",
cmd: "hsplit",
desc: "Split the current editor view horizontally.",
type: CT.HSplit,
},
];
bool
Active(EditState state)
{
return g_ed_ctx.state == state;
}
void void
Cycle(Inputs* inputs) Cycle(Inputs* inputs)
{ {
@ -130,18 +169,7 @@ Cycle(Inputs* inputs)
g_delta = DeltaTime(&g_ed_ctx.timer); g_delta = DeltaTime(&g_ed_ctx.timer);
debug if(g_frame_step) g_input_mode = Active(ES.InputMode);
{
g_delta = 0.01;
}
//HandleInputs(ctx, inputs);
debug if(g_frame_step && !g_frame_continue) return;
debug g_frame_continue = false;
g_input_mode = g_ed_ctx.state == ES.InputMode;
BeginUI(inputs); BeginUI(inputs);
@ -162,11 +190,7 @@ Cycle(Inputs* inputs)
} }
UIItem* container = Container(A2D.Y, UIS2()); UIItem* container = Container(A2D.Y, UIS2());
Push!("parent")(container);
UIItem* ed_container = Container(A2D.X, UISY(ST.Percentage, 0.98)); UIItem* ed_container = Container(A2D.X, UISY(ST.Percentage, 0.98));
Push!("parent")(ed_container);
EditorView(g_ed_ctx.base_panel); EditorView(g_ed_ctx.base_panel);
Pop!("parent"); Pop!("parent");
@ -174,9 +198,12 @@ Cycle(Inputs* inputs)
StatusBar(&g_ed_ctx); StatusBar(&g_ed_ctx);
if(g_ed_ctx.state == ES.CmdOpen) UIItem* cmd_item = Get("###cmd_palette");
bool cmd_active = Active(ES.CmdOpen);
if(cmd_active || Ready(cmd_item))
{ {
CommandPalette(&g_ed_ctx.cmd); CommandPalette(&g_ed_ctx.cmd, cmd_active);
} }
EndUI(); EndUI();
@ -213,7 +240,7 @@ InitEditorCtx(PlatformWindow* window)
count += 1; count += 1;
} }
ctx.file_names = Alloc!(u8[])(&ctx.arena, count); ctx.file_names = Alloc!(string)(&ctx.arena, count);
count = 0; count = 0;
foreach(DirEntry e; dirEntries(".", SpanMode.breadth)) foreach(DirEntry e; dirEntries(".", SpanMode.breadth))
@ -221,7 +248,7 @@ InitEditorCtx(PlatformWindow* window)
if(indexOf(e.name, ".git") != -1 || e.isDir) continue; if(indexOf(e.name, ".git") != -1 || e.isDir) continue;
u64 start = indexOf(e.name, "./") == 0 ? 2 : 0; u64 start = indexOf(e.name, "./") == 0 ? 2 : 0;
ctx.file_names[count++] = Alloc!(u8)(&ctx.arena, CastStr!(u8)(e.name[start .. $])); ctx.file_names[count++] = Alloc(&ctx.arena, e.name);
} }
} }
catch(Exception e) catch(Exception e)
@ -260,7 +287,7 @@ EditModeActive()
} }
char[] char[]
ToAbsolutePath(u8[] file_name) ToAbsolutePath(string file_name)
{ {
import core.stdc.string : strlen; import core.stdc.string : strlen;
@ -294,7 +321,7 @@ ToAbsolutePath(u8[] file_name)
version(Windows) version(Windows)
{ {
name_buf.sformat("%s/%s", wd, cast(char[])file_name); name_buf.sformat("%s/%s", wd, cast(char[])file_name);
} }
char[] path_buf = ScratchAlloc!(char)(strlen(name_buf.ptr)+1); char[] path_buf = ScratchAlloc!(char)(strlen(name_buf.ptr)+1);
@ -304,11 +331,11 @@ name_buf.sformat("%s/%s", wd, cast(char[])file_name);
} }
void void
SaveFile(Editor* ed, u8[] file_name) SaveFile(Editor* ed, string file_name)
{ {
import core.stdc.stdio; import core.stdc.stdio;
file_name = file_name.length == 0 ? ed.buf.file_name : file_name; file_name = file_name.length == 0 ? Str(ed.buf.file_name) : file_name;
if(file_name.length > 0) if(file_name.length > 0)
{ {
@ -353,7 +380,7 @@ SaveFile(Editor* ed, u8[] file_name)
} }
void void
OpenFile(Editor* ed, u8[] file_name) OpenFile(Editor* ed, string file_name)
{ {
import core.stdc.stdio; import core.stdc.stdio;
import std.file; import std.file;
@ -590,8 +617,11 @@ HandleInputs(Panel* p, LinkedList!(UIInput)* inputs)
{ {
if(Shift(node.md)) if(Shift(node.md))
{ {
ctx.state = ES.CmdOpen; ctx.state = ES.CmdOpen;
taken = true; ctx.cmd.commands = cast(Command[])CMD_LIST;
ctx.cmd.icount = 0;
ctx.cmd.params = [];
taken = true;
} }
} break; } break;
case v: case v:
@ -624,18 +654,6 @@ HandleInputs(Panel* p, LinkedList!(UIInput)* inputs)
ctx.state = ES.SetPanelFocus; ctx.state = ES.SetPanelFocus;
} }
} break; } break;
debug case d:
{
g_ui_ctx.dbg = !g_ui_ctx.dbg;
} break;
debug case g:
{
g_frame_step = !g_frame_step;
} break;
debug case s:
{
g_frame_continue = true;
} break;
default: taken = Move(fb, key, md); break; default: taken = Move(fb, key, md); break;
} }
} }
@ -746,14 +764,32 @@ Lower(u8 ch)
} }
bool bool
StrContains(bool begins_with)(const u8[] str, u8[] match) StrContains(bool begins_with)(u8[] str, u8[] match)
{ {
return StrContains!(begins_with)(cast(u8[])str, match); return StrContains!(begins_with)(Str(str), match);
} }
bool bool
StrContains(bool begins_with)(u8[] str, u8[] match) StrContains(bool begins_with, T, U)(T str_param, U match_param) if(StringType!(T) && StringType!(U))
{ {
static if(!is(T == string))
{
string str = Str(str_param);
}
else
{
string str = str_param;
}
static if(!is(U == string))
{
string match = Str(match_param);
}
else
{
string match = match_param;
}
u64 count; u64 count;
for(u64 i = 0; i < str.length; i += 1) for(u64 i = 0; i < str.length; i += 1)
{ {
@ -783,62 +819,26 @@ StrContains(bool begins_with)(u8[] str, u8[] match)
void void
GetCommands(CmdPalette* cmd) GetCommands(CmdPalette* cmd)
{ {
const Command[] cmd_list = [
{
name: CastStr!(u8)("open"),
type: CT.OpenFile,
},
{
name: CastStr!(u8)("save"),
type: CT.SaveFile,
},
{
name: CastStr!(u8)("create"),
type: CT.CreateFile,
},
{
name: CastStr!(u8)("vsplit"),
type: CT.VSplit,
},
{
name: CastStr!(u8)("hsplit"),
type: CT.HSplit,
},
];
Reset(&cmd.arena); Reset(&cmd.arena);
cmd.commands = Alloc!(Command)(&cmd.arena, cmd_list.length);
cmd.params = [];
u8[] str = cmd.buffer[0 .. cmd.icount]; cmd.commands = Alloc!(Command)(&cmd.arena, CMD_LIST.length);
cmd.params = [];
u8[] str = cmd.buffer[0 .. cmd.icount];
u64 count = 0; u64 count;
if(str.length > 0) if(str.length > 0)
{ {
for(u64 i = 0; i < cmd_list.length; i += 1) for(u64 i = 0; i < CMD_LIST.length; i += 1)
{ {
if(StrContains!(true)(cmd_list[i].name, str)) if(StrContains!(true)(CMD_LIST[i].cmd, str) || StrContains!(true)(CMD_LIST[i].name, str))
{ {
cmd.commands[count] = cast(Command)cmd_list[i]; cmd.commands[count] = cast(Command)CMD_LIST[i];
count += 1; count += 1;
} }
} }
} }
if(count == 0 && cmd.icount == 0) cmd.commands = count ? cmd.commands[0 .. count] : cast(Command[])CMD_LIST;
{
cmd.commands[] = cast(Command[])cmd_list[];
}
else
{
cmd.commands = cmd.commands[0 .. count];
}
cmd.opt_strs = Alloc!(u8[])(&cmd.arena, cmd.commands.length);
for(u64 i = 0; i < cmd.commands.length; i += 1)
{
cmd.opt_strs[i] = cmd.commands[i].name;
}
} }
bool bool
@ -904,7 +904,7 @@ HandleCmdMode(EditorCtx* ctx, Panel* panel, UIInput* ev)
if(cmd.commands.length > 0) if(cmd.commands.length > 0)
{ {
cmd.current = cmd.commands[cmd.selected]; cmd.current = cmd.commands[cmd.selected];
cmd.buffer[0 .. cmd.current.name.length] = cmd.current.name[0 .. $]; cmd.buffer[0 .. cmd.current.name.length] = Arr!(u8)(cmd.current.name[0 .. $]);
cmd.icount = cast(u32)cmd.current.name.length; cmd.icount = cast(u32)cmd.current.name.length;
cmd.buffer[cmd.icount++] = ' '; cmd.buffer[cmd.icount++] = ' ';
} }
@ -923,7 +923,7 @@ HandleCmdMode(EditorCtx* ctx, Panel* panel, UIInput* ev)
} break; } break;
} }
if(cmd.current.type == CT.None && (cmd.commands.length == 0 || prev_count != cmd.icount)) if(cmd.current.type == CT.None && prev_count != cmd.icount)
{ {
GetCommands(cmd); GetCommands(cmd);
} }
@ -937,7 +937,7 @@ HandleCmdMode(EditorCtx* ctx, Panel* panel, UIInput* ev)
} break; } break;
case SaveFile: case SaveFile:
{ {
u8[] param = GetParam(cmd); string param = GetParam(cmd);
} break; } break;
default: break; default: break;
} }
@ -957,15 +957,15 @@ Check(CmdPalette* cmd, u64 length)
} }
} }
u8[] string
GetParam(CmdPalette* cmd) GetParam(CmdPalette* cmd)
{ {
u8[] param = []; string param;
for(u64 i = cmd.current.name.length; i < cmd.icount; i += 1) for(u64 i = cmd.current.name.length; i < cmd.icount; i += 1)
{ {
if(cmd.buffer[i] != ' ') if(cmd.buffer[i] != ' ')
{ {
param = cmd.buffer[i .. cmd.icount]; param = Str(cmd.buffer[i .. cmd.icount]);
break; break;
} }
} }
@ -974,13 +974,13 @@ GetParam(CmdPalette* cmd)
} }
void void
PopulateParams(CmdPalette* cmd, u8[][] strs) PopulateParams(CmdPalette* cmd, string[] strs)
{ {
u8[] param = GetParam(cmd); string param = GetParam(cmd);
if(cmd.params.length == 0 || !param.length) if(cmd.params.length == 0 || !param.length)
{ {
cmd.params = Alloc!(Parameter)(&cmd.cmd_arena, strs.length); cmd.params = Alloc!(Parameter)(&cmd.cmd_arena, strs.length);
cmd.opt_strs = Alloc!(u8[])(&cmd.cmd_arena, strs.length); cmd.opt_strs = Alloc!(string)(&cmd.cmd_arena, strs.length);
for(u64 i = 0; i < cmd.params.length; i += 1) for(u64 i = 0; i < cmd.params.length; i += 1)
{ {
@ -992,7 +992,7 @@ PopulateParams(CmdPalette* cmd, u8[][] strs)
if(param.length) if(param.length)
{ {
cmd.opt_strs = Alloc!(u8[])(&cmd.cmd_arena, strs.length); cmd.opt_strs = Alloc!(string)(&cmd.cmd_arena, strs.length);
u64 matches; u64 matches;
for(u64 i = 0; i < cmd.params.length; i += 1) for(u64 i = 0; i < cmd.params.length; i += 1)

View File

@ -1,5 +1,6 @@
import dlib; import dlib;
import dlib.util : HTPush = Push; import dlib.util : HTPush = Push;
import dlib.math : Clamp;
import vulkan; import vulkan;
import vulkan : RendererGetExtent = GetExtent; import vulkan : RendererGetExtent = GetExtent;
@ -130,9 +131,11 @@ enum UIFlags : u64
VerticalAlignText = 1<<24, VerticalAlignText = 1<<24,
SetHot = 1<<25, SetHot = 1<<25,
SetReady = 1<<26, SetReady = 1<<26,
AnimateReady = 1<<27, // Fade in/out SetActive = 1<<27,
AnimateHot = 1<<28, // Hover highlight AnimateReady = 1<<28, // Fade in/out
AnimateActive = 1<<29, // Animate selected (probably do later) AnimateHot = 1<<29, // Hover highlight
AnimateActive = 1<<30, // Animate selected (probably do later)
DrawDropShadow = 1<<31,
Clamp = UIFlags.ClampX | UIFlags.ClampY, Clamp = UIFlags.ClampX | UIFlags.ClampY,
PortalView = UIFlags.PortalViewX | UIFlags.PortalViewY, PortalView = UIFlags.PortalViewX | UIFlags.PortalViewY,
@ -142,7 +145,7 @@ enum UIFlags : u64
alias UIF = UIFlags; alias UIF = UIFlags;
const UIFlags AUTO_FLAGS = UIF.ResizeAdjacent; const UIFlags AUTO_FLAGS = UIF.ResizeAdjacent;
const UIFlags DRAW_FLAGS = UIF.DrawBackground|UIF.DrawBorder|UIF.DrawText|UIF.PortalView; const UIFlags DRAW_FLAGS = UIF.DrawBackground|UIF.DrawBorder|UIF.DrawText|UIF.PortalView|UIF.DrawDropShadow;
enum UISignal enum UISignal
{ {
@ -229,6 +232,9 @@ CtxMemberInfo(int i)
struct UICtx struct UICtx
{ {
enum FO = FRAME_OVERLAP;
enum FS = FONT_SIZES;
HashTable!(UIHash, UIItem*) items; HashTable!(UIHash, UIItem*) items;
UIItem* free_items; UIItem* free_items;
UIItem* transient_items; UIItem* transient_items;
@ -244,21 +250,22 @@ struct UICtx
PlatformWindow* window; PlatformWindow* window;
Renderer rd; Renderer rd;
Descriptor[FONT_SIZES] font_descs; Descriptor[FS][FO] font_descs;
Descriptor default_tex; Descriptor default_tex;
Descriptor sampler; Descriptor sampler;
Pipeline pipeline; Pipeline pipeline;
DescSetLayout desc_set_layout; DescSetLayout desc_set_layout;
DescSet desc_set; DescSet[FO] desc_sets;
PipelineLayout pipeline_layout; PipelineLayout pipeline_layout;
Mat4 projection; Mat4 projection;
Vec2 res; Vec2 res;
FontFace font; FontFace font;
FontGlyphs[FONT_SIZES] glyph_sets; FontGlyphs[FS] glyph_sets;
u32 glyph_sets_used; u32 glyph_sets_used;
bool[FO] glyphs_to_load;
UIBuffer[FRAME_OVERLAP] buffers; UIBuffer[FO] buffers;
u32 tab_width; u32 tab_width;
Vec4[TS.max][UISH.max] syntax_colors; Vec4[TS.max][UISH.max] syntax_colors;
@ -271,6 +278,7 @@ struct UICtx
f32 scroll_rate; f32 scroll_rate;
f32 animation_rate; f32 animation_rate;
f32 fade_rate;
mixin UICtxParameter!(Vec4, "bg_col"); mixin UICtxParameter!(Vec4, "bg_col");
mixin UICtxParameter!(Vec4, "bg_col_end"); mixin UICtxParameter!(Vec4, "bg_col_end");
@ -301,9 +309,10 @@ struct UICtx
struct FontGlyphs struct FontGlyphs
{ {
u32 size; FontAtlasBuf abuf;
u32 index; u32 size;
FontAtlasBuf abuf; u32 index;
bool[FRAME_OVERLAP] loaded;
} }
struct Stack(T) struct Stack(T)
@ -417,7 +426,6 @@ struct Vertex
Vec4 cols; Vec4 cols;
Vec4 cols_end; Vec4 cols_end;
Vec4 corner_radius; Vec4 corner_radius;
Vec2[2] bounds;
union union
{ {
VPos[2] pos; VPos[2] pos;
@ -530,6 +538,14 @@ InitUICtx(PlatformWindow* window)
{ {
ctx.rd = InitRenderer(handles, MB(16), MB(8)); ctx.rd = InitRenderer(handles, MB(16), MB(8));
DescLayoutBinding[2] layout_bindings = [
{ binding: 0, descriptorType: DT.Image, descriptorCount: FONT_SIZES, stageFlags: SS.All },
{ binding: 1, descriptorType: DT.Sampler, descriptorCount: 1, stageFlags: SS.All },
];
ctx.desc_set_layout = CreateDescSetLayout(&ctx.rd, layout_bindings);
ctx.pipeline_layout = CreatePipelineLayout(&ctx.rd, ctx.desc_set_layout, Mat4.sizeof);
for(u64 i = 0; i < FRAME_OVERLAP; i += 1) for(u64 i = 0; i < FRAME_OVERLAP; i += 1)
{ {
ctx.buffers[i].m_vtx = CreateMappedBuffer!(Vertex)(&ctx.rd, BT.Vertex, VERTEX_MAX_COUNT); ctx.buffers[i].m_vtx = CreateMappedBuffer!(Vertex)(&ctx.rd, BT.Vertex, VERTEX_MAX_COUNT);
@ -537,18 +553,9 @@ InitUICtx(PlatformWindow* window)
ctx.buffers[i].vtx = ctx.buffers[i].m_vtx.data; ctx.buffers[i].vtx = ctx.buffers[i].m_vtx.data;
ctx.buffers[i].idx = ctx.buffers[i].m_idx.data; ctx.buffers[i].idx = ctx.buffers[i].m_idx.data;
ctx.buffers[i].idx[0 .. $] = [0, 1, 2, 2, 1, 3]; ctx.buffers[i].idx[0 .. $] = [0, 1, 2, 2, 1, 3];
ctx.desc_sets[i] = AllocDescSet(&ctx.rd, ctx.desc_set_layout);
} }
DescLayoutBinding[2] layout_bindings = [
{ binding: 0, descriptorType: DT.Image, descriptorCount: FONT_SIZES, 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);
GfxPipelineInfo ui_info = { GfxPipelineInfo ui_info = {
vertex_shader: cast(u8[])VERTEX_BYTES, vertex_shader: cast(u8[])VERTEX_BYTES,
frag_shader: cast(u8[])FRAGMENT_BYTES, frag_shader: cast(u8[])FRAGMENT_BYTES,
@ -564,25 +571,37 @@ InitUICtx(PlatformWindow* window)
alpha_op: BO.Add, alpha_op: BO.Add,
}; };
for(u32 i = 0; i < ctx.font_descs.length; i += 1) for(u64 i = 0; i < ctx.font_descs.length; i += 1)
{ {
CreateImageViewTex(&ctx.rd, &ctx.font_descs[i], ATLAS_DIMENSION, ATLAS_DIMENSION, DT.Image, 0, i); for(u32 j = 0; j < ctx.font_descs[i].length; j += 1)
ctx.glyph_sets[i].index = i; {
CreateImageViewTex(&ctx.rd, &ctx.font_descs[i][j], ATLAS_DIMENSION, ATLAS_DIMENSION, DT.Image, 0, j);
ctx.glyph_sets[j].index = j;
}
} }
FontAtlasBuf* abuf = GetFontAtlas(g_text_size_default); FontAtlasBuf* abuf = GetFontAtlas(g_text_size_default);
LoadFontGlyphs();
CreateGraphicsPipeline(&ctx.rd, &ctx.pipeline, &ui_info); CreateGraphicsPipeline(&ctx.rd, &ctx.pipeline, &ui_info);
ctx.sampler = CreateSampler(&ctx.rd, MipmapMode.Nearest, 1); ctx.sampler = CreateSampler(&ctx.rd, MipmapMode.Nearest, 1);
u8[512*512*4] white_tex = 255; u8[512*512*4] white_tex = 255;
for(u32 i = 1; i < ctx.font_descs.length; i += 1) for(u64 i = 0; i < ctx.font_descs.length; i += 1)
{ {
Transfer(&ctx.rd, &ctx.font_descs[i].view, white_tex, ATLAS_DIMENSION, ATLAS_DIMENSION); for(u32 j = 1; j < ctx.font_descs[i].length; j += 1)
{
Transfer(&ctx.rd, &ctx.font_descs[i][j].view, white_tex, ATLAS_DIMENSION, ATLAS_DIMENSION);
}
} }
Write(&ctx.rd, ctx.desc_set, ctx.font_descs); for(u64 i = 0; i < FRAME_OVERLAP; i += 1)
Write(&ctx.rd, ctx.desc_set, &ctx.sampler); {
Write(&ctx.rd, ctx.desc_sets[i], ctx.font_descs[i]);
Write(&ctx.rd, ctx.desc_sets[i], &ctx.sampler);
}
SetClearColors(&ctx.rd, [0.0, 0.0, 0.0, 1.0], [0.0, 0.0, 0.0, 0.0]); SetClearColors(&ctx.rd, [0.0, 0.0, 0.0, 1.0], [0.0, 0.0, 0.0, 0.0]);
} }
@ -617,18 +636,41 @@ GetFontGlyphs(u32 size)
assert(ctx.font); assert(ctx.font);
fg.abuf = CreateAtlas(&ctx.arena, ctx.font, cast(f32)size, ATLAS_DIMENSION); fg.abuf = CreateAtlas(&ctx.arena, ctx.font, cast(f32)size, ATLAS_DIMENSION);
fg.size = size; fg.size = size;
fg.loaded = false;
Transfer(&ctx.rd, &ctx.font_descs[i].view, fg.abuf.data, ATLAS_DIMENSION, ATLAS_DIMENSION); Logf("setting size %s to %s", size, i);
Write(&ctx.rd, ctx.desc_set, &ctx.font_descs[i]);
ctx.glyphs_to_load = true;
ctx.glyph_sets_used += 1; ctx.glyph_sets_used += 1;
} }
return fg; return fg;
} }
void
LoadFontGlyphs()
{
UICtx* ctx = GetCtx();
u64 fi = ctx.f_idx;
for(u64 i = 0; i < ctx.glyph_sets_used; i += 1)
{
FontGlyphs* fg = ctx.glyph_sets.ptr + i;
if(!fg.loaded[fi])
{
Logf("loading %s frame index %s", i, fi);
Transfer(&ctx.rd, &ctx.font_descs[fi][i].view, fg.abuf.data, ATLAS_DIMENSION, ATLAS_DIMENSION);
fg.loaded[fi] = true;
}
}
Write(&ctx.rd, ctx.desc_sets[fi], ctx.font_descs[fi]);
ctx.glyphs_to_load[fi] = false;
}
void void
Set(UIItem* item, UICtx* ctx) Set(UIItem* item, UICtx* ctx)
{ {
@ -789,11 +831,9 @@ MakeItem(T)(T k, UIFlags flags = UIF.None) if(is(T: UIKey) || StringType!T)
if(item.flags & UIF.AnimateReady) if(item.flags & UIF.AnimateReady)
{ {
item.ready_t += ctx.animation_rate * (1.0 - item.ready_t); bool is_ready = cast(bool)(item.flags & UIF.SetReady);
item.bg_col.a *= item.ready_t; item.ready_t += ctx.fade_rate * (cast(f32)(is_ready) - item.ready_t);
item.bg_col_end.a *= item.ready_t; SetColorTransparency(item, item.ready_t);
item.text_col.a *= item.ready_t;
item.border_col.a *= item.ready_t;
} }
if(item.flags & UIF.DrawBorder && item.border_thickness < 0.0009) if(item.flags & UIF.DrawBorder && item.border_thickness < 0.0009)
@ -807,6 +847,33 @@ MakeItem(T)(T k, UIFlags flags = UIF.None) if(is(T: UIKey) || StringType!T)
return item; return item;
} }
void
SetColorTransparency(UIItem* item, f32 v)
{
item.bg_col.a *= v;
item.bg_col_end.a *= v;
item.text_col.a *= v;
item.border_col.a *= v;
}
bool
Ready(UIItem* item)
{
return item.ready_t > 0.0001;
}
bool
Hovered(bool current_frame, T, U)(T param, U* ptr, U index)
{
bool result = Hovered!(current_frame)(param);
if(result)
{
*ptr = index;
}
return result;
}
bool bool
Hovered(bool current_frame, T)(T param) Hovered(bool current_frame, T)(T param)
{ {
@ -993,11 +1060,10 @@ BeginUI(Inputs* inputs)
{ {
last = last.last; last = last.last;
} }
UIKey hovered = ZeroKey(); UIKey hovered = ZeroKey();
for(UIItem* item = last; !Nil(item); item = Recurse!(false)(item, g_UI_NIL)) for(UIItem* item = last; !Nil(item); item = Recurse!(false)(item, g_UI_NIL))
{ {
Logf("key %s pos %s rect %s %s", item.key.hash_text, ctx.mouse_pos.v, item.rect.p0.v, item.rect.p1.v);
if(InBounds(ctx.mouse_pos, &item.rect) && !ZeroKey(item.key)) if(InBounds(ctx.mouse_pos, &item.rect) && !ZeroKey(item.key))
{ {
hovered = item.key; hovered = item.key;
@ -1050,30 +1116,8 @@ BeginUI(Inputs* inputs)
ResetStacks(ctx); ResetStacks(ctx);
version(ENABLE_RENDERER)
{
BeginFrame(&ctx.rd);
BeginRendering(&ctx.rd);
}
Vec2 ext = GetExtent();
if(ext != ctx.res)
{
ctx.res = ext;
Ortho(&ctx.projection, 0.0, 0.0, ext.x, ext.y, -10.0, 10.0);
}
version(ENABLE_RENDERER)
{
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);
ctx.buffers[ctx.f_idx].count = 0;
ctx.buffers[ctx.f_idx].vtx_offset = 0;
ctx.animation_rate = 1.0 - pow(2.0, (-30.0f * g_delta)); ctx.animation_rate = 1.0 - pow(2.0, (-30.0f * g_delta));
ctx.fade_rate = 1.0 - pow(2.0, (-50.0f * g_delta));
ctx.scroll_rate = 1.0 - pow(2.0, (-60.0f * g_delta)); ctx.scroll_rate = 1.0 - pow(2.0, (-60.0f * g_delta));
// Root Item // Root Item
@ -1086,6 +1130,40 @@ EndUI()
{ {
UICtx* ctx = GetCtx(); UICtx* ctx = GetCtx();
version(ENABLE_RENDERER)
{
BeginFrame(&ctx.rd);
if(ctx.glyphs_to_load[ctx.f_idx])
{
LoadFontGlyphs();
}
BeginRendering(&ctx.rd);
Vec2 ext = GetExtent();
if(ext != ctx.res)
{
ctx.res = ext;
Ortho(&ctx.projection, 0.0, 0.0, ext.x, ext.y, -10.0, 10.0);
}
PushConstants(&ctx.rd, ctx.pipeline, &ctx.projection);
Bind(&ctx.rd, ctx.pipeline, ctx.desc_sets[ctx.f_idx]);
}
else
{
Vec2 ext = GetExtent();
if(ext != ctx.res)
{
ctx.res = ext;
}
}
memset(ctx.buffers[ctx.f_idx].vtx.ptr, 0, Vertex.sizeof * ctx.buffers[ctx.f_idx].count);
ctx.buffers[ctx.f_idx].count = 0;
ctx.buffers[ctx.f_idx].vtx_offset = 0;
// Automatic signals // Automatic signals
for(UIItem* item = ctx.root_first; !Nil(item); item = Recurse!(false)(item, g_UI_NIL)) for(UIItem* item = ctx.root_first; !Nil(item); item = Recurse!(false)(item, g_UI_NIL))
{ {
@ -1296,6 +1374,28 @@ EndUI()
// Render Items // Render Items
for(UIItem* item = ctx.root_first; !Nil(item); item = Recurse!(true)(item, g_UI_NIL)) for(UIItem* item = ctx.root_first; !Nil(item); item = Recurse!(true)(item, g_UI_NIL))
{ {
if(item.flags & UIF.AnimateReady && item.ready_t < 1.0)
{
UIItem* first = item.first, last = item.last;
item.first = item.last = g_UI_NIL;
UIItem* next = Recurse!(true)(item, g_UI_NIL);
item.first = first;
item.last = last;
for(UIItem* c = item.first; !Nil(c) && c != next; c = Recurse!(true)(c, g_UI_NIL))
{
if(!(c.flags & UIF.AnimateReady))
{
c.flags |= UIF.AnimateReady;
c.ready_t = item.ready_t;
SetColorTransparency(c, item.ready_t);
}
}
}
RenderItem(ctx, item); RenderItem(ctx, item);
} }
@ -1326,6 +1426,13 @@ FocusItem(Args...)(string str, Args args)
g_ui_ctx.focus_item = Get(str, args); g_ui_ctx.focus_item = Get(str, args);
} }
void
Clamp(UICtx* ctx, Vertex* v)
{
v.dst_start = Clamp(v.dst_start, Vec2(0.0), Vec2(ctx.res));
v.dst_end = Clamp(v.dst_end, Vec2(0.0), Vec2(ctx.res));
}
void void
FocusItem(T)(T focus) if(is(T == UIKey) || StringType!T || is(T == UIItem*)) FocusItem(T)(T focus) if(is(T == UIKey) || StringType!T || is(T == UIItem*))
{ {
@ -1346,41 +1453,57 @@ RenderItem(UICtx* ctx, UIItem* item)
if(item.rendered) return; if(item.rendered) return;
if(!(item.flags & DRAW_FLAGS)) return; if(!(item.flags & DRAW_FLAGS)) return;
Vertex* v = null; Vec2 border_p0 = item.rect.p0 - Vec2(InnerOffset!(A2D.X, true )(item), InnerOffset!(A2D.Y, true )(item));
Vec2 border_p1 = item.rect.p1 + Vec2(InnerOffset!(A2D.X, false)(item), InnerOffset!(A2D.Y, false)(item));
assert(border_p0.x <= item.rect.p0.x && border_p0.y <= item.rect.p0.y);
assert(border_p1.x >= item.rect.p1.x && border_p1.y >= item.rect.p1.y);
if(item.flags & UIF.DrawDropShadow)
{
f32 px = clamp(((item.size.x+item.size.y)/2.0)*0.004, 1.0, f32.max);
f32 alpha = 0.1;
if(item.flags & UIF.AnimateReady)
{
alpha *= item.ready_t;
}
Vertex* v = GetVertex(ctx);
v.dst_start = border_p0 - Vec2(px);
v.dst_end = border_p1 + Vec2(px);
v.cols = Vec4(0.0, 0.0, 0.0, alpha);
v.cols_end = Vec4(0.0, 0.0, 0.0, alpha);
v.corner_radius = 0.8f;
v.edge_softness = 8.0f;
Clamp(ctx, v);
}
if(item.flags & UIF.DrawBackground) if(item.flags & UIF.DrawBackground)
{ {
v = GetVertex(ctx); Vertex* v = GetVertex(ctx);
v.dst_start = item.rect.p0; v.dst_start = item.rect.p0;
v.dst_end = item.rect.p1; v.dst_end = item.rect.p1;
v.cols = item.bg_col; v.cols = item.bg_col;
v.cols_end = item.flags & UIF.Gradient ? item.bg_col_end : item.bg_col; v.cols_end = item.flags & UIF.Gradient ? item.bg_col_end : item.bg_col;
v.corner_radius = item.flags & UIF.DrawBorder ? item.corner_radius*0.5 : item.corner_radius; v.corner_radius = item.flags & UIF.DrawBorder ? item.corner_radius*0.5 : item.corner_radius;
v.edge_softness = item.edge_softness; v.edge_softness = item.edge_softness;
v.bounds = ItemBounds(item.parent);
AddVertexCount(ctx); Clamp(ctx, v);
} }
if(item.flags & UIF.DrawBorder) if(item.flags & UIF.DrawBorder)
{ {
v = GetVertex(ctx); Vertex* v = GetVertex(ctx);
v.dst_start = item.rect.p0 - Vec2(InnerOffset!(A2D.X, true )(item), InnerOffset!(A2D.Y, true )(item)); v.dst_start = border_p0;
v.dst_end = item.rect.p1 + Vec2(InnerOffset!(A2D.X, false)(item), InnerOffset!(A2D.Y, false)(item)); v.dst_end = border_p1;
v.cols = item.border_col; v.cols = item.border_col;
v.cols_end = item.flags & UIF.Gradient ? item.bg_col_end : item.bg_col; v.cols_end = item.flags & UIF.Gradient ? item.bg_col_end : item.bg_col;
v.corner_radius = item.corner_radius; v.corner_radius = item.corner_radius;
v.border_thickness = item.border_thickness; v.border_thickness = item.border_thickness;
v.edge_softness = item.edge_softness; v.edge_softness = item.edge_softness;
v.bounds = ItemBounds(item.parent);
AddVertexCount(ctx); Clamp(ctx, v);
}
if(v)
{
v.dst_start = Clamp(v.dst_start, Vec2(0.0), Vec2(ctx.res));
v.dst_end = Clamp(v.dst_end, Vec2(0.0), Vec2(ctx.res));
} }
if(item.flags & UIF.DrawText || item.display_string) if(item.flags & UIF.DrawText || item.display_string)
@ -1706,7 +1829,7 @@ ResetStacks(UICtx* ctx)
T* T*
Recurse(bool pre = true, T)(T* node, T* nil) Recurse(bool pre = true, T)(T* node, T* nil)
{ {
T* child = pre ? node.first : node.last; T* child = pre ? node.first : node.last;
T* result = nil; T* result = nil;
if(!Nil(child)) if(!Nil(child))
@ -1960,7 +2083,7 @@ DrawGlyph(UIItem* item, Glyph* glyph, u32 atlas_index, f32* x_pos, f32 y, f32 li
f32 w = glyph.plane_right-glyph.plane_left; f32 w = glyph.plane_right-glyph.plane_left;
f32 h = glyph.plane_bottom-glyph.plane_top; f32 h = glyph.plane_bottom-glyph.plane_top;
v = ctx.buffers[ctx.f_idx].vtx.ptr + ctx.buffers[ctx.f_idx].count; v = GetVertex(ctx);
f32 y_pos = y + glyph.plane_top; f32 y_pos = y + glyph.plane_top;
@ -1969,7 +2092,6 @@ DrawGlyph(UIItem* item, Glyph* glyph, u32 atlas_index, f32* x_pos, f32 y, f32 li
v.cols = col; v.cols = col;
v.cols_end = col; v.cols_end = col;
v.texture = true; v.texture = true;
v.bounds = ItemBounds(item);
v.atlas_index = index; v.atlas_index = index;
v.src_start = Vec2(glyph.atlas_left, glyph.atlas_top); v.src_start = Vec2(glyph.atlas_left, glyph.atlas_top);
v.src_end = Vec2(glyph.atlas_right, glyph.atlas_bottom); v.src_end = Vec2(glyph.atlas_right, glyph.atlas_bottom);
@ -2001,19 +2123,11 @@ DrawGlyph(UIItem* item, Glyph* glyph, u32 atlas_index, f32* x_pos, f32 y, f32 li
} }
} }
AddVertexCount(ctx);
*x_pos += advance; *x_pos += advance;
} }
} }
} }
pragma(inline) Vec2[2]
ItemBounds(UIItem* item)
{
return [item.rect.p0, item.rect.p1];
}
pragma(inline) f32 pragma(inline) f32
AxisPadding(Axis2D axis)(UIItem* item) AxisPadding(Axis2D axis)(UIItem* item)
{ {
@ -2041,7 +2155,8 @@ InnerOffset(Axis2D axis, bool start)(UIItem* item)
pragma(inline) Vertex* pragma(inline) Vertex*
GetVertex(UICtx* ctx) GetVertex(UICtx* ctx)
{ {
return ctx.buffers[ctx.f_idx].vtx.ptr + ctx.buffers[ctx.f_idx].count; assert(ctx.buffers[ctx.f_idx].count < VERTEX_MAX_COUNT);
return ctx.buffers[ctx.f_idx].vtx.ptr + ctx.buffers[ctx.f_idx].count++;
} }
static UISize[2] static UISize[2]
@ -2062,13 +2177,6 @@ UISY(SizeType type, f32 value = 1.0, f32 strictness = 1.0)
return [UISize(ST.Percentage, 1.0), UISize(type, value, strictness)]; return [UISize(ST.Percentage, 1.0), UISize(type, value, strictness)];
} }
pragma(inline) void
AddVertexCount(UICtx* ctx)
{
ctx.buffers[ctx.f_idx].count += 1;
assert(ctx.buffers[ctx.f_idx].count < VERTEX_MAX_COUNT);
}
bool bool
Nil(UIItem* item) Nil(UIItem* item)
{ {

View File

@ -14,14 +14,17 @@ __gshared const Editor g_nil_ed;
__gshared Editor* g_NIL_ED; __gshared Editor* g_NIL_ED;
__gshared const UIKey ZERO = ZeroKey(); __gshared const UIKey ZERO = ZeroKey();
Vec4 BG_COL = Vec4(0.18, 0.18, 0.18, 1.0); Vec4 BG_COL = Vec4(0.18, 0.18, 0.18, 1.0);
Vec4 HL_BG_COL = Vec4(0.160, 0.533, 0.803, 1.0);
Vec4 CMD_COL = Vec4(0.22, 0.22, 0.22, 1.0); Vec4 HL_BORDER_COL = Vec4(0.172, 0.643, 0.988, 1.0);
Vec4 HL_BG_COL = Vec4(0.160, 0.533, 0.803, 1.0); Vec4 CMD_COL = Vec4(0.22, 0.22, 0.22, 1.0);
Vec4 HL_BORDER_COL = Vec4(0.172, 0.643, 0.988, 1.0); Vec4 CMD_BORDER_COL = Vec4(0.48, 0.48, 0.48, 1.0);
Vec4 BLUE = HexCol(0x4EA9FF); Vec4 CMD_INPUT_COL = Vec4(0.130, 0.420, 0.640, 1.0);
Vec4 RED = HexCol(0xFF3268); Vec4 GREY = Vec4(0.45, 0.45, 0.45, 1.0);
Vec4 YELLOW = HexCol(0xF7C443); Vec4 WHITE = HexCol(0xFFFFFF);
Vec4 BLUE = HexCol(0x4EA9FF);
Vec4 RED = HexCol(0xFF3268);
Vec4 YELLOW = HexCol(0xF7C443);
shared static this() shared static this()
{ {
@ -239,27 +242,33 @@ EditorView(Panel* p)
} }
void void
CommandPalette(CmdPalette* cmd) CommandPalette(CmdPalette* cmd, bool active)
{ {
UICtx* ctx = GetCtx(); UICtx* ctx = GetCtx();
Vec2 ext = GetExtent(); Vec2 ext = GetExtent();
FontAtlasBuf* abuf = &g_ui_ctx.glyph_sets[0].abuf; FontAtlasBuf* abuf = &g_ui_ctx.glyph_sets[0].abuf;
f32 w = ext.x*0.4; f32 w = ext.x*0.6;
f32 h = ext.y*0.7; f32 h = ext.y*0.7;
enum UIPushInfo[] cmd_params = [ enum UIPushInfo[] cmd_params = [
{ "layout_axis", q{ A2D.Y } }, { "layout_axis", q{ A2D.Y } },
{ "fixed_pos", q{ Vec2(ext.x*0.3, ext.y*0.1) } }, { "fixed_pos", q{ Vec2(ext.x*0.2, ext.y*0.1) } },
{ "bg_col", q{ BG_COL } }, { "bg_col", q{ BG_COL } },
{ "border_col", q{ HL_BORDER_COL } }, { "border_col", q{ HL_BORDER_COL } },
{ "border_thickness", q{ 4.0 } }, { "border_thickness", q{ 4.0 } },
{ "corner_radius", q{ Vec4(8.0) } }, { "corner_radius", q{ Vec4(8.0) } },
{ "size_info", q{ UIS2(ST.Pixels, ST.Pixels, w, h) } }, { "size_info", q{ UIS2(ST.Pixels, ST.Pixels, w, h) } },
]; ];
mixin(PushOnce!(cmd_params)); mixin(PushOnce!(cmd_params));
UIItem* cmd_item = MakeItem("###cmd_palette", UIF.Window|UIF.FixedPosition|UIF.DrawBackground|UIF.DrawBorder|UIF.AnimateReady);
UIFlags cmd_flags = UIF.Window|UIF.FixedPosition|UIF.AnimateReady|UIF.DrawDropShadow|UIF.DrawBackground;
if(active)
{
cmd_flags |= UIF.SetReady;
}
UIItem* cmd_item = MakeItem("###cmd_palette", cmd_flags);
f32 padding_y = 4.0; f32 padding_y = 4.0;
@ -267,54 +276,105 @@ CommandPalette(CmdPalette* cmd)
mixin(PushScope!("padding", q{ Vec2(4.0, padding_y) })); mixin(PushScope!("padding", q{ Vec2(4.0, padding_y) }));
enum UIPushInfo[] cmd_input_params = [ enum UIPushInfo[] cmd_input_params = [
{ "bg_col", q{ HL_BG_COL } }, { "bg_col", q{ CMD_COL } },
{ "size_info", q{ UISY(ST.TextSize) } }, { "border_col", q{ CMD_BORDER_COL } },
{ "display_string", q{ Str(cmd.buffer[0 .. cmd.icount]) } }, { "border_thickness", q{ 1.0 }},
{ "text_size", q{ 18 }},
{ "corner_radius", q{ Vec4(12.0, 12.0, 0.0, 0.0)}},
{ "edge_softness", q{ 1.0 }},
{ "size_info", q{ UISY(ST.TextSize) } },
{ "padding", q{ Vec2A2(6.0, 6.0, 10.0, 10.0)} },
{ "display_string", q{ Str(cmd.buffer[0 .. cmd.icount]) } },
]; ];
mixin(PushOnce!(cmd_input_params)); mixin(PushOnce!(cmd_input_params));
UIKey zero = ZeroKey(); MakeItem(ZERO, UIF.DrawBorder|UIF.DrawBackground|UIF.DrawText|UIF.Overflow|UIF.VerticalAlignText);
MakeItem(zero, UIF.DrawBorder|UIF.DrawBackground|UIF.DrawText|UIF.Overflow);
enum UIPushInfo[] sep_params = [ // the two sizes seems to fuck this up somehow
{ "padding", q{ Vec2(0.0) } }, u32 title_px = 16;
{ "size_info", q{ UISY(ST.Pixels, 4.0) } }, u32 sub_px = 16;
{ "bg_col", q{ HL_BORDER_COL } }, f32 title_pad = 2.0f;
f32 opt_y_pad = 4.0;
f32 opt_x_pad = 8.0;
f32 opt_height = cast(f32)(title_px + sub_px) + opt_y_pad*2.0 + title_pad;
enum UIPushInfo[] cmd_opts_box_params = [
{ "layout_axis", q{ A2D.Y }},
{ "border_col", q{ CMD_BORDER_COL } },
{ "border_thickness", q{ 1.0 } },
{ "corner_radius", q{ Vec4(0.0, 0.0, 12.0, 12.0) } },
{ "edge_softness", q{ 1.0 }},
{ "size_info", q{ UISY(ST.Pixels, h-opt_height) } },
]; ];
mixin(PushOnce!(sep_params)); mixin(PushOnce!(cmd_opts_box_params));
MakeItem(zero, UIF.DrawBackground);
mixin(PushScope!("padding", q{ Vec2(4.0) } )); UIItem* opt_box = MakeItem(ZERO, UIF.DrawBorder);
mixin(PushScope!("size_info", q{ UISY(ST.TextSize) } ));
u64 max_opts = cast(u64)ceil(h/abuf.atlas.line_height); mixin(PushScope!("parent", q{ opt_box }));
mixin(PushScope!("padding", q{ Vec2A2(opt_x_pad, opt_x_pad, opt_y_pad, opt_y_pad) } ));
mixin(PushScope!("size_info", q{ UISY(ST.Pixels, opt_height) } ));
u64 max_opts = cast(u64)ceil(h/opt_height);
Vec4[2] opt_cols = [ Vec4[2] opt_cols = [
Vec4(0.22, 0.22, 0.22, 1.0), Vec4(0.22, 0.22, 0.22, 1.0),
Vec4(0.35, 0.35, 0.35, 1.0), Vec4(0.35, 0.35, 0.35, 1.0),
]; ];
// Active should be highlights around the item like File Pilot // Active should be highlights around the item like File Pilot (maybe not)
for(u64 i = 0; i < cmd.opt_strs.length && i < max_opts; i += 1) bool opts = cast(bool)cmd.opt_strs.length;
if(opts)
{ {
PushDisplayString(Str(cmd.opt_strs[i]), true); for(u64 i = 0; i < cmd.opt_strs.length && i < max_opts; i += 1)
PushBgCol(opt_cols[i%2], true);
UIKey k = MakeKey("###copt_%s", i);
if(Hovered!(true)(k))
{ {
cmd.selected = i; PushDisplayString(Str(cmd.opt_strs[i]), true);
} PushBgCol(opt_cols[i%2], true);
MakeItem(k, UIF.DrawBackground|UIF.DrawText|UIF.AnimateHot|(cmd.selected == i ? UIF.SetHot : UIF.None)); UIKey k = MakeKey("###opt_%s", i);
Hovered!(true)(k, &cmd.selected, i);
MakeItem(k, UIF.DrawBackground|UIF.DrawText|UIF.AnimateHot|(cmd.selected == i ? UIF.SetHot : UIF.None));
}
}
else
{
for(u64 i = 0; i < cmd.commands.length && i < max_opts; i += 1)
{
PushBgCol(opt_cols[i%2], true);
PushLayoutAxis(A2D.Y, true);
UIKey k = MakeKey("###optc_%s", i);
Hovered!(true)(k, &cmd.selected, i);
UIItem* optc = MakeItem(k, UIF.DrawBackground|UIF.AnimateHot|(cmd.selected == i ? UIF.SetHot : UIF.None));
PushParent(optc);
PushSizeInfo(UISY(ST.TextSize));
PushTextSize(title_px, true);
PushTextCol(WHITE, true);
PushPadding(Vec2A2(opt_x_pad, opt_x_pad, 0.0, title_pad), true);
PushDisplayString(ScratchAlloc(cmd.commands[i].name), true);
MakeItem(ZERO, UIF.DrawText);
PushTextSize(sub_px, true);
PushTextCol(GREY, true);
PushPadding(Vec2A2(opt_x_pad, opt_x_pad, 0.0, 0.0), true);
PushDisplayString(ScratchAlloc(cmd.commands[i].desc), true);
MakeItem(ZERO, UIF.DrawText);
Pop!("parent", "size_info");
}
} }
} }
UIItem* UIItem*
Container(Axis2D axis, UISize[2] size_info) Container(bool push = true)(Axis2D axis, UISize[2] size_info)
{ {
enum UIPushInfo[] container_info = [ enum UIPushInfo[] container_info = [
{ "layout_axis", q{ axis } }, { "layout_axis", q{ axis } },
@ -323,7 +383,14 @@ Container(Axis2D axis, UISize[2] size_info)
mixin(PushOnce!(container_info)); mixin(PushOnce!(container_info));
return MakeItem(ZeroKey()); UIItem* item = MakeItem(ZERO);
static if(push)
{
Push!("parent")(item);
}
return item;
} }
void void