more work

This commit is contained in:
Matthew 2026-01-04 20:46:26 +11:00
parent 62cd8c471f
commit dcb0d620c7
9 changed files with 508 additions and 330 deletions

Binary file not shown.

Binary file not shown.

@ -1 +1 @@
Subproject commit 10c7fdcaedfdd98af47c640e00f1fad8705f476d Subproject commit 9f70b8982c71d6d24031a08c6f2780be2dd402e0

View File

@ -32,7 +32,7 @@ struct EditorCtx
string[] file_names; string[] file_names;
u64 panel_id; u64 panel_id;
Editor* focused_editor; Panel* focused_panel;
} }
struct CmdPalette struct CmdPalette
@ -109,12 +109,19 @@ enum EditState
{ {
NormalMode, NormalMode,
InputMode, InputMode,
CmdOpen, CmdPalette,
RunCmd,
SetPanelFocus, // if moving left/right move up parent tree until one is found with a2d.x, same thing for up/down a2d.y SetPanelFocus, // if moving left/right move up parent tree until one is found with a2d.x, same thing for up/down a2d.y
} }
alias ES = EditState; alias ES = EditState;
bool
CmdModeActive()
{
return g_ed_ctx.state == ES.CmdPalette;
}
__gshared bool g_input_mode = false; __gshared bool g_input_mode = false;
__gshared EditorCtx g_ed_ctx; __gshared EditorCtx g_ed_ctx;
@ -200,12 +207,17 @@ Cycle(Inputs* inputs)
UIItem* cmd_item = Get("###cmd_palette"); UIItem* cmd_item = Get("###cmd_palette");
bool cmd_active = Active(ES.CmdOpen); bool cmd_active = Active(ES.CmdPalette);
if(cmd_active || Ready(cmd_item)) if(cmd_active || Ready(cmd_item))
{ {
CommandPalette(&g_ed_ctx.cmd, cmd_active); CommandPalette(&g_ed_ctx.cmd, cmd_active);
} }
if(Active(ES.RunCmd))
{
}
EndUI(); EndUI();
} }
@ -221,10 +233,10 @@ InitEditorCtx(PlatformWindow* window)
ctx.cmd.arena = CreateArena(MB(1)); ctx.cmd.arena = CreateArena(MB(1));
ctx.cmd.cmd_arena = CreateArena(MB(1)); ctx.cmd.cmd_arena = CreateArena(MB(1));
ctx.cmd.buffer = Alloc!(u8)(1024); ctx.cmd.buffer = Alloc!(u8)(1024);
ctx.base_panel = CreatePanel(ctx, CreateEditor(ctx)); ctx.base_panel = CreatePanel(CreateEditor());
ctx.timer = CreateTimer(); ctx.timer = CreateTimer();
FocusEditor(ctx.base_panel.ed); FocusEditor(ctx.base_panel);
if(getcwd() != "/") if(getcwd() != "/")
{ {
@ -259,21 +271,20 @@ InitEditorCtx(PlatformWindow* window)
} }
void void
FocusEditor(Editor* ed) FocusEditor(Panel* p)
{ {
assert(ed); assert(p.ed);
g_ed_ctx.focused_editor = ed; g_ed_ctx.focused_panel = p;
Logf("editor %s", ed.editor_id);
} }
Panel* Panel*
CreatePanel(EditorCtx* ctx, Editor* ed = null) CreatePanel(Editor* ed = null)
{ {
Panel* p = Alloc!(Panel)(&ctx.arena); Panel* p = Alloc!(Panel)(&g_ed_ctx.arena);
p.layout_axis = A2D.Y; p.layout_axis = A2D.Y;
p.ed = ed; p.ed = ed;
p.id = ctx.panel_id++; p.id = g_ed_ctx.panel_id++;
p.text_size = 14; p.text_size = 14;
p.parent = p.first = p.last = p.next = p.prev = g_NIL_PANEL; p.parent = p.first = p.last = p.next = p.prev = g_NIL_PANEL;
@ -414,24 +425,24 @@ OpenFile(Editor* ed, string file_name)
} }
Editor* Editor*
CreateEditor(EditorCtx* ctx) CreateEditor()
{ {
Editor* ed = Alloc!(Editor)(&ctx.arena); Editor* ed = Alloc!(Editor)(&g_ed_ctx.arena);
ed.arena = CreateArena(MB(4)); ed.arena = CreateArena(MB(4));
ed.buf = CreateFlatBuffer([], []); ed.buf = CreateFlatBuffer([], []);
ed.editor_id = ctx.editor_id_incr++; ed.editor_id = g_ed_ctx.editor_id_incr++;
return ed; return ed;
} }
void void
AddEditor(EditorCtx* ctx, Panel* target, Axis2D axis) AddEditor(Panel* target, Axis2D axis)
{ {
if(CheckNil(g_NIL_PANEL, target.parent) || target.parent.layout_axis != axis) if(CheckNil(g_NIL_PANEL, target.parent) || target.parent.layout_axis != axis)
{ {
Panel* first = CreatePanel(ctx, target.ed); Panel* first = CreatePanel(target.ed);
Panel* second = CreatePanel(ctx, CreateEditor(ctx)); Panel* second = CreatePanel(CreateEditor());
target.layout_axis = axis; target.layout_axis = axis;
target.ed = null; target.ed = null;
@ -441,11 +452,11 @@ AddEditor(EditorCtx* ctx, Panel* target, Axis2D axis)
first.parent = second.parent = target; first.parent = second.parent = target;
FocusEditor(second.ed); FocusEditor(second);
} }
else if(target.parent.layout_axis == axis) else if(target.parent.layout_axis == axis)
{ {
Panel* panel = CreatePanel(ctx, CreateEditor(ctx)); Panel* panel = CreatePanel(CreateEditor());
DLLPush(target.parent, target, panel); DLLPush(target.parent, target, panel);
panel.parent = target.parent; panel.parent = target.parent;
@ -466,7 +477,7 @@ AddEditor(EditorCtx* ctx, Panel* target, Axis2D axis)
} }
} }
FocusEditor(panel.ed); FocusEditor(panel);
} }
} }
@ -487,15 +498,20 @@ InsertInputToBuf(EditorCtx* ctx, Editor* ed)
} }
void void
ResetCtx(EditorCtx* ctx, Editor* ed) ResetCtx(Editor* ed)
{ {
InsertInputToBuf(ctx, ed); InsertInputToBuf(&g_ed_ctx, ed);
ResetCtx();
}
ctx.state = ES.NormalMode; void
ctx.cmd.icount = 0; ResetCtx()
ctx.cmd.commands = []; {
ctx.cmd.current = cast(Command)NO_CMD; g_ed_ctx.state = ES.NormalMode;
ctx.cmd.selected = 0; g_ed_ctx.cmd.icount = 0;
g_ed_ctx.cmd.commands = [];
g_ed_ctx.cmd.current = cast(Command)NO_CMD;
g_ed_ctx.cmd.selected = 0;
} }
bool bool
@ -568,24 +584,20 @@ HandleInputs(Panel* p, LinkedList!(UIInput)* inputs)
if(key == Input.Escape) if(key == Input.Escape)
{ {
ResetCtx(ctx, ed); ResetCtx(ed);
taken = true; taken = true;
} }
else if(ctx.state == ES.InputMode) else if(ctx.state == ES.InputMode)
{ {
taken = HandleInputMode(ctx, p, node); taken = HandleInputMode(ctx, p, node);
} }
else if(ctx.state == ES.CmdOpen)
{
taken = HandleCmdMode(ctx, p, node);
}
else if(ctx.state == ES.SetPanelFocus) else if(ctx.state == ES.SetPanelFocus)
{ {
switch(key) with(Input) switch(key) with(Input)
{ {
case Escape: case Escape:
{ {
ResetCtx(ctx, ed); ResetCtx(ed);
taken = true; taken = true;
} break; } break;
default: break; default: break;
@ -617,7 +629,7 @@ HandleInputs(Panel* p, LinkedList!(UIInput)* inputs)
{ {
if(Shift(node.md)) if(Shift(node.md))
{ {
ctx.state = ES.CmdOpen; ctx.state = ES.CmdPalette;
ctx.cmd.commands = cast(Command[])CMD_LIST; ctx.cmd.commands = cast(Command[])CMD_LIST;
ctx.cmd.icount = 0; ctx.cmd.icount = 0;
ctx.cmd.params = []; ctx.cmd.params = [];
@ -816,9 +828,82 @@ StrContains(bool begins_with, T, U)(T str_param, U match_param) if(StringType!(T
return count == match.length; return count == match.length;
} }
void bool
GetCommands(CmdPalette* cmd) HandleCmdMode(CmdPalette* cmd, UIInput* ev)
{ {
bool taken;
Panel* panel = g_ed_ctx.focused_panel;
Editor* ed = panel.ed;
u64 prev_count = cmd.icount;
switch(ev.key) with(Input)
{
case Enter:
{
if(cmd.current.type == CT.None && cmd.commands.length > 0)
{
cmd.current = cmd.commands[cmd.selected];
g_ed_ctx.state = ES.RunCmd;
}
} goto CmdInputEnd;
case Backspace:
{
if(cmd.icount > 0)
{
cmd.icount -= 1;
if(cmd.icount == cmd.current.name.length)
{
cmd.current = cast(Command)NO_CMD;
}
}
} break;
case Space:
{
Check(cmd, 1);
cmd.buffer[cmd.icount++] = ' ';
} goto case Tab;
case Tab:
{
if(cmd.commands.length > 0)
{
cmd.current = cmd.commands[cmd.selected];
cmd.buffer[0 .. cmd.current.name.length] = Arr!(u8)(cmd.current.name[0 .. $]);
cmd.icount = cast(u32)cmd.current.name.length;
cmd.buffer[cmd.icount++] = ' ';
}
} break;
case Up, Down:
{
i32 incr = ev.key == Up ? -1 : +1;
if(cmd.opt_strs.length)
{
cmd.selected = clamp(cmd.selected+incr, 0, cmd.opt_strs.length-1);
}
else
{
cmd.selected = clamp(cmd.selected+incr, 0, cmd.commands.length);
}
} break;
case Escape:
{
ResetCtx();
} goto CmdInputEnd;
default:
{
if(ev.text.length)
{
cmd.buffer[cmd.icount .. cmd.icount+ev.text.length] = cast(u8[])ev.text[0 .. $];
cmd.icount += ev.text.length;
}
} break;
}
if(cmd.current.type == CT.None)
{
cmd.opt_strs = [];
if(prev_count != cmd.icount)
{
Reset(&cmd.arena); Reset(&cmd.arena);
cmd.commands = Alloc!(Command)(&cmd.arena, CMD_LIST.length); cmd.commands = Alloc!(Command)(&cmd.arena, CMD_LIST.length);
@ -839,93 +924,7 @@ GetCommands(CmdPalette* cmd)
} }
cmd.commands = count ? cmd.commands[0 .. count] : cast(Command[])CMD_LIST; cmd.commands = count ? cmd.commands[0 .. count] : cast(Command[])CMD_LIST;
}
bool
HandleCmdMode(EditorCtx* ctx, Panel* panel, UIInput* ev)
{
bool taken;
CmdPalette* cmd = &ctx.cmd;
Editor* ed = panel.ed;
u64 prev_count = cmd.icount;
switch(ev.key) with(Input)
{
case Enter:
{
if(cmd.current.type == CT.None && cmd.commands.length > 0)
{
if(cmd.commands[cmd.selected].type == CT.OpenFile)
{
goto case Tab;
} }
else
{
cmd.current = cmd.commands[cmd.selected];
}
}
switch(cmd.current.type)
{
case CT.OpenFile:
{
if(cmd.selected >= 0 && cmd.selected < cmd.opt_strs.length)
{
OpenFile(ed, cmd.opt_strs[cmd.selected]);
}
} break;
case CT.SaveFile:
{
SaveFile(ed, GetParam(cmd));
} break;
case CT.VSplit, CT.HSplit:
{
AddEditor(ctx, panel, cmd.current.type == CT.VSplit ? A2D.X : A2D.Y);
} break;
default: break;
}
ResetCtx(ctx, ed);
} goto CmdInputEnd;
case Backspace:
{
if(cmd.icount > 0 && cmd.buffer[--cmd.icount] == ' ')
{
cmd.current = cast(Command)NO_CMD;
}
} break;
case Space:
{
Check(cmd, 1);
cmd.buffer[cmd.icount++] = ' ';
} goto case Tab;
case Tab:
{
if(cmd.commands.length > 0)
{
cmd.current = cmd.commands[cmd.selected];
cmd.buffer[0 .. cmd.current.name.length] = Arr!(u8)(cmd.current.name[0 .. $]);
cmd.icount = cast(u32)cmd.current.name.length;
cmd.buffer[cmd.icount++] = ' ';
}
} break;
case Up, Down:
{
cmd.selected = clamp(cmd.selected+(ev.key == Up ? -1 : +1), 0, cmd.opt_strs.length-1);
} break;
default:
{
if(ev.text.length)
{
cmd.buffer[cmd.icount .. cmd.icount+ev.text.length] = cast(u8[])ev.text[0 .. $];
cmd.icount += ev.text.length;
}
} break;
}
if(cmd.current.type == CT.None && prev_count != cmd.icount)
{
GetCommands(cmd);
} }
else if(prev_count != cmd.icount) else if(prev_count != cmd.icount)
{ {
@ -933,7 +932,7 @@ HandleCmdMode(EditorCtx* ctx, Panel* panel, UIInput* ev)
{ {
case OpenFile: case OpenFile:
{ {
PopulateParams(cmd, ctx.file_names); PopulateParams(cmd, g_ed_ctx.file_names);
} break; } break;
case SaveFile: case SaveFile:
{ {

View File

@ -31,7 +31,7 @@ import core.stdc.stdio : sprintf;
*********************************/ *********************************/
enum Vec4 BG_COL = SRGBVec4(0.13, 0.13, 0.13, 1.0); enum Vec4 BG_COL = SRGBVec4(0.13, 0.13, 0.13, 1.0);
enum Vec4 BG_HL_COL = SRGBVec4(0.24, 0.45, 0.81, 1.0); enum Vec4 BG_HL_COL = SRGBVec4(0.12, 0.30, 0.63, 1.0);
enum Vec4 BORDER_COL = SRGBVec4(0.254, 0.254, 0.266, 1.0); enum Vec4 BORDER_COL = SRGBVec4(0.254, 0.254, 0.266, 1.0);
enum Vec4 BORDER_HL_COL = SRGBVec4(0.035, 0.549, 0.824, 1.0); enum Vec4 BORDER_HL_COL = SRGBVec4(0.035, 0.549, 0.824, 1.0);
enum Vec4 TEXT_COL = SRGBVec4(1.0); enum Vec4 TEXT_COL = SRGBVec4(1.0);
@ -48,8 +48,8 @@ const f32 LINE_COUNT_PADDING = 4.0;
__gshared UICtx g_ui_ctx; __gshared UICtx g_ui_ctx;
//const u8[] FONT_BYTES = import("pc-9800.ttf"); //u8[] FONT_BYTES = Arr!(u8)(import("pc-9800.ttf"));
//const u8[] FONT_BYTES = import("NuberNextCondensed-DemiBold.otf"); //u8[] FONT_BYTES = Arr!(u8)(import("NuberNextCondensed-DemiBold.otf"));
u8[] FONT_BYTES = cast(u8[])import("jetbrains-mono/JetBrainsMono-Regular.ttf"); u8[] FONT_BYTES = cast(u8[])import("jetbrains-mono/JetBrainsMono-Regular.ttf");
u8[] VERTEX_BYTES = cast(u8[])import("gui.vert.spv"); u8[] VERTEX_BYTES = cast(u8[])import("gui.vert.spv");
u8[] FRAGMENT_BYTES = cast(u8[])import("gui.frag.spv"); u8[] FRAGMENT_BYTES = cast(u8[])import("gui.frag.spv");
@ -230,6 +230,12 @@ CtxMemberInfo(int i)
enum MemberInfo CtxMemberInfo = MemberInfo(id: id, is_stack: is_stack, is_top: is_top); enum MemberInfo CtxMemberInfo = MemberInfo(id: id, is_stack: is_stack, is_top: is_top);
} }
struct PushConst
{
Mat4 projection;
u32 atlas_index;
}
struct UICtx struct UICtx
{ {
enum FO = FRAME_OVERLAP; enum FO = FRAME_OVERLAP;
@ -241,6 +247,8 @@ struct UICtx
Arena arena; Arena arena;
Arena temp_arena; Arena temp_arena;
Arena key_stack_arena;
Inputs* inputs; Inputs* inputs;
u64 frame; u64 frame;
u64 f_idx; u64 f_idx;
@ -257,13 +265,15 @@ struct UICtx
DescSetLayout desc_set_layout; DescSetLayout desc_set_layout;
DescSet[FO] desc_sets; DescSet[FO] desc_sets;
PipelineLayout pipeline_layout; PipelineLayout pipeline_layout;
Mat4 projection; PushConst pc;
Vec2 res; Vec2 res;
FontFace font; FontFace font;
FontGlyphs[FS] glyph_sets; FontGlyphs[FS] glyph_sets;
u32 glyph_sets_used; u32 glyph_sets_used;
bool[FO] glyphs_to_load; bool[FO] glyphs_to_load;
bool prev_has_tex;
u32 prev_atlas_index;
UIBuffer[FO] buffers; UIBuffer[FO] buffers;
@ -280,6 +290,8 @@ struct UICtx
f32 animation_rate; f32 animation_rate;
f32 fade_rate; f32 fade_rate;
Stack!(UIItem*)* key_item_stack;
mixin UICtxParameter!(Vec4, "bg_col"); mixin UICtxParameter!(Vec4, "bg_col");
mixin UICtxParameter!(Vec4, "bg_col_end"); mixin UICtxParameter!(Vec4, "bg_col_end");
mixin UICtxParameter!(Vec4, "bg_hl_col"); mixin UICtxParameter!(Vec4, "bg_hl_col");
@ -440,8 +452,7 @@ struct Vertex
f32 border_thickness; f32 border_thickness;
f32 edge_softness; f32 edge_softness;
f32 raised; f32 raised;
u32 texture; u32 has_texture;
u32 atlas_index;
} }
enum SettingType enum SettingType
@ -452,22 +463,18 @@ enum SettingType
Toggle, Toggle,
} }
Attribute[15] attributes = [ Attribute[11] attributes = [
{ binding: 0, location: 0, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof }, { binding: 0, location: 0, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof },
{ binding: 0, location: 1, format: FMT.RGBA_F32, offset: Vertex.cols_end.offsetof }, { binding: 0, location: 1, format: FMT.RGBA_F32, offset: Vertex.cols_end.offsetof },
{ binding: 0, location: 2, format: FMT.R_F32, offset: Vertex.corner_radius.offsetof + f32.sizeof*0 }, { binding: 0, location: 2, format: FMT.RGBA_F32, offset: Vertex.corner_radius.offsetof },
{ binding: 0, location: 3, format: FMT.R_F32, offset: Vertex.corner_radius.offsetof + f32.sizeof*1 }, { binding: 0, location: 3, format: FMT.RG_F32, offset: Vertex.dst_start.offsetof },
{ binding: 0, location: 4, format: FMT.R_F32, offset: Vertex.corner_radius.offsetof + f32.sizeof*2 }, { binding: 0, location: 4, format: FMT.RG_F32, offset: Vertex.dst_end.offsetof },
{ binding: 0, location: 5, format: FMT.R_F32, offset: Vertex.corner_radius.offsetof + f32.sizeof*3 }, { binding: 0, location: 5, format: FMT.RG_F32, offset: Vertex.src_start.offsetof },
{ binding: 0, location: 6, format: FMT.RG_F32, offset: Vertex.dst_start.offsetof }, { binding: 0, location: 6, format: FMT.RG_F32, offset: Vertex.src_end.offsetof },
{ binding: 0, location: 7, format: FMT.RG_F32, offset: Vertex.dst_end.offsetof }, { binding: 0, location: 7, format: FMT.R_F32, offset: Vertex.border_thickness.offsetof },
{ binding: 0, location: 8, format: FMT.RG_F32, offset: Vertex.src_start.offsetof }, { binding: 0, location: 8, format: FMT.R_F32, offset: Vertex.edge_softness.offsetof },
{ binding: 0, location: 9, format: FMT.RG_F32, offset: Vertex.src_end.offsetof }, { binding: 0, location: 9, format: FMT.R_F32, offset: Vertex.raised.offsetof },
{ binding: 0, location: 10, format: FMT.R_F32, offset: Vertex.border_thickness.offsetof }, { binding: 0, location: 10, format: FMT.R_U32, offset: Vertex.has_texture.offsetof },
{ binding: 0, location: 11, format: FMT.R_F32, offset: Vertex.edge_softness.offsetof },
{ binding: 0, location: 12, format: FMT.R_F32, offset: Vertex.raised.offsetof },
{ binding: 0, location: 13, format: FMT.R_U32, offset: Vertex.texture.offsetof },
{ binding: 0, location: 14, format: FMT.R_U32, offset: Vertex.atlas_index.offsetof },
]; ];
union Rect union Rect
@ -486,7 +493,8 @@ struct UIKey
u64 hash; u64 hash;
} }
enum bool StringType(T) = (is(T: string) || is(T: u8[]) || is(T: char[])); enum bool KeyType(T) = (StringType!(T) || is(T == UIKey) || is(T == const(UIKey)));
enum bool ItemAndKeyType(T) = (KeyType!(T) || is(T == UIItem*));
void void
InitUICtx(PlatformWindow* window) InitUICtx(PlatformWindow* window)
@ -524,6 +532,7 @@ InitUICtx(PlatformWindow* window)
ctx.free_items = Alloc!(UIItem)(&arena); ctx.free_items = Alloc!(UIItem)(&arena);
ctx.arena = arena; ctx.arena = arena;
ctx.temp_arena = CreateArena(MB(1)); ctx.temp_arena = CreateArena(MB(1));
ctx.key_stack_arena = CreateArena(MB(1));
ctx.transient_items = g_UI_NIL; ctx.transient_items = g_UI_NIL;
ctx.font = OpenFont(cast(u8[])FONT_BYTES); ctx.font = OpenFont(cast(u8[])FONT_BYTES);
ctx.tab_width = 2; ctx.tab_width = 2;
@ -544,7 +553,7 @@ InitUICtx(PlatformWindow* window)
]; ];
ctx.desc_set_layout = CreateDescSetLayout(&ctx.rd, layout_bindings); ctx.desc_set_layout = CreateDescSetLayout(&ctx.rd, layout_bindings);
ctx.pipeline_layout = CreatePipelineLayout(&ctx.rd, ctx.desc_set_layout, Mat4.sizeof); ctx.pipeline_layout = CreatePipelineLayout(&ctx.rd, ctx.desc_set_layout, PushConst.sizeof);
for(u64 i = 0; i < FRAME_OVERLAP; i += 1) for(u64 i = 0; i < FRAME_OVERLAP; i += 1)
{ {
@ -614,6 +623,12 @@ GetFontAtlas(u32 size)
return &GetFontGlyphs(size).abuf; return &GetFontGlyphs(size).abuf;
} }
f32
GetLineHeight(u32 size)
{
return GetFontAtlas(size).atlas.line_height;
}
FontGlyphs* FontGlyphs*
GetFontGlyphs(u32 size) GetFontGlyphs(u32 size)
{ {
@ -636,12 +651,10 @@ 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, size, UVec2(ATLAS_DIMENSION));
fg.size = size; fg.size = size;
fg.loaded = false; fg.loaded = false;
Logf("setting size %s to %s", size, i);
ctx.glyphs_to_load = true; ctx.glyphs_to_load = true;
ctx.glyph_sets_used += 1; ctx.glyph_sets_used += 1;
} }
@ -660,7 +673,6 @@ LoadFontGlyphs()
FontGlyphs* fg = ctx.glyph_sets.ptr + i; FontGlyphs* fg = ctx.glyph_sets.ptr + i;
if(!fg.loaded[fi]) 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); Transfer(&ctx.rd, &ctx.font_descs[fi][i].view, fg.abuf.data, ATLAS_DIMENSION, ATLAS_DIMENSION);
fg.loaded[fi] = true; fg.loaded[fi] = true;
} }
@ -712,7 +724,7 @@ MakeItem(Args...)(string str, Args args)
} }
UIItem* UIItem*
MakeItem(T)(T k, UIFlags flags = UIF.None) if(is(T: UIKey) || StringType!T) MakeItem(T)(T k, UIFlags flags = UIF.None) if(KeyType!(T))
{ {
UICtx* ctx = GetCtx(); UICtx* ctx = GetCtx();
UIItem* item = Get(k); UIItem* item = Get(k);
@ -724,7 +736,7 @@ MakeItem(T)(T k, UIFlags flags = UIF.None) if(is(T: UIKey) || StringType!T)
if(Nil(ctx.root_first)) if(Nil(ctx.root_first))
{ {
ctx.root_first = ctx.root_last = item; ctx.root_first = ctx.root_last = item;
Push!("parent")(item); Push!("parent")(item, false);
} }
if(item.flags & (UIF.Window | UIF.FloatingWindow) || Nil(ctx.root_first)) if(item.flags & (UIF.Window | UIF.FloatingWindow) || Nil(ctx.root_first))
{ {
@ -856,9 +868,54 @@ SetColorTransparency(UIItem* item, f32 v)
item.border_col.a *= v; item.border_col.a *= v;
} }
bool static string
Ready(UIItem* item) AssignItem(T, alias p)()
{ {
import std.format : format;
enum string id = __traits(identifier, p);
string result = "";
static if(is(T == UIItem*))
{
result = id;
}
else static if(is(T == UIKey) || StringType!(T))
{
result = format("Get(%s)", id);
}
else static assert(false, "Unknown type for AssignItem");
return result;
}
static string
AssignKey(T, alias p)()
{
import std.format : format;
enum string id = __traits(identifier, p);
string result = "";
static if(is(T == UIItem*))
{
result = format("%s.key", id);
}
else static if(is(T == UIKey) || is(T == const(UIKey)))
{
result = id;
}
else static if(StringType!(T))
{
result = format("MakeKey(%s)", id);
}
else static assert(false, "Unknown type for AssignKey");
return result;
}
bool
Ready(T)(T param) if(ItemAndKeyType!(T))
{
UIItem* item = mixin(AssignItem!(T, param));
return item.ready_t > 0.0001; return item.ready_t > 0.0001;
} }
@ -880,14 +937,7 @@ Hovered(bool current_frame, T)(T param)
UICtx* ctx = GetCtx(); UICtx* ctx = GetCtx();
bool result; bool result;
static if(is(T == UIKey)) UIKey key = mixin(AssignKey!(T, param));
{
UIKey key = param;
}
else static if(is(T == UIItem*))
{
UIKey key = param.key;
}
if(!ZeroKey(ctx.hover_key)) if(!ZeroKey(ctx.hover_key))
{ {
@ -909,9 +959,11 @@ Signal(UIItem* item)
{ {
UICtx* ctx = GetCtx(); UICtx* ctx = GetCtx();
if(item.signal != UIS.None) assert(!ZeroKey(item.key), "Zero key passed to Signal");
if(item.signal == UIS.None && !ZeroKey(item.key))
{ {
bool mouse_over = InBounds(ctx.mouse_pos, &item.rect); bool mouse_over = KeyEq(ctx.hover_key, item.key);
if(mouse_over) if(mouse_over)
{ {
@ -924,13 +976,13 @@ Signal(UIItem* item)
{ {
bool taken; bool taken;
if(item.flags & UIF.Clickable && i.type == UIE.Click && InBounds(ctx.mouse_pos, &item.rect)) if(item.flags & UIF.Clickable && i.type == UIE.Click && mouse_over)
{ {
item.signal |= UIS.Clicked; item.signal |= UIS.Clicked;
taken = true; taken = true;
} }
if(ZeroKey(ctx.drag_key) && item.flags & UIF.Draggable && i.type == UIE.DragStart && InBounds(i.pos, &item.rect)) if(ZeroKey(ctx.drag_key) && item.flags & UIF.Draggable && i.type == UIE.DragStart && mouse_over)
{ {
item.signal |= UIS.Dragged; item.signal |= UIS.Dragged;
ctx.drag_key = item.key; ctx.drag_key = item.key;
@ -967,6 +1019,17 @@ PushUIEvent(UICtx* ctx, UIInput input)
DLLPush(&ctx.events, ev, null); DLLPush(&ctx.events, ev, null);
} }
void
DrawUI(UICtx* ctx, u32 atlas_index)
{
if(ctx.pc.atlas_index != atlas_index)
{
DrawUI(ctx);
ctx.pc.atlas_index = atlas_index;
PushConstants(&ctx.rd, ctx.pipeline, &ctx.pc);
}
}
void void
DrawUI(UICtx* ctx) DrawUI(UICtx* ctx)
{ {
@ -1055,18 +1118,12 @@ BeginUI(Inputs* inputs)
// Check current mouse target // Check current mouse target
if(mouse_moved && !Nil(ctx.root_last)) if(mouse_moved && !Nil(ctx.root_last))
{ {
UIItem* last = ctx.root_last;
while(!Nil(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(Stack!(UIItem*)* node = ctx.key_item_stack; node; node = node.next)
{ {
if(InBounds(ctx.mouse_pos, &item.rect) && !ZeroKey(item.key)) if(InBounds(ctx.mouse_pos, &node.value.rect))
{ {
hovered = item.key; hovered = node.value.key;
break; break;
} }
} }
@ -1082,6 +1139,7 @@ BeginUI(Inputs* inputs)
} }
} }
ctx.key_item_stack = null;
ctx.root_first = ctx.root_last = g_UI_NIL; ctx.root_first = ctx.root_last = g_UI_NIL;
// Clean up items // Clean up items
@ -1105,7 +1163,6 @@ BeginUI(Inputs* inputs)
item = next; item = next;
} }
// Ctx state // Ctx state
ctx.transient_items = g_UI_NIL; ctx.transient_items = g_UI_NIL;
ctx.frame = next_frame; ctx.frame = next_frame;
@ -1145,10 +1202,10 @@ EndUI()
if(ext != ctx.res) if(ext != ctx.res)
{ {
ctx.res = ext; ctx.res = ext;
Ortho(&ctx.projection, 0.0, 0.0, ext.x, ext.y, -10.0, 10.0); Ortho(&ctx.pc.projection, 0.0, 0.0, ext.x, ext.y, -10.0, 10.0);
} }
PushConstants(&ctx.rd, ctx.pipeline, &ctx.projection); PushConstants(&ctx.rd, ctx.pipeline, &ctx.pc);
Bind(&ctx.rd, ctx.pipeline, ctx.desc_sets[ctx.f_idx]); Bind(&ctx.rd, ctx.pipeline, ctx.desc_sets[ctx.f_idx]);
} }
else else
@ -1206,12 +1263,17 @@ EndUI()
{ {
if(item.size_info[axis].type == ST.Pixels) if(item.size_info[axis].type == ST.Pixels)
{ {
item.size.v[axis] = AxisPadding!(axis)(item) + item.size_info[axis].value; item.size.v[axis] = item.size_info[axis].value;
} }
else if(item.size_info[axis].type == ST.TextSize) else if(item.size_info[axis].type == ST.TextSize)
{ {
f32 size = axis == A2D.X ? item.max_text_width : GetFontAtlas(item.text_size).atlas.line_height*item.text_lines.length; f32 size = axis == A2D.X ? item.max_text_width : GetFontAtlas(item.text_size).atlas.line_height * item.text_lines.length;
item.size.v[axis] = size + AxisPadding!(axis)(item); if(axis == A2D.Y)
{
// Logf("line height %f %f %f", size, floor(size), AxisPadding!(axis)(item));
}
item.size.v[axis] = floor(size) + AxisPadding!(axis)(item);
} }
} }
@ -1396,6 +1458,11 @@ EndUI()
} }
} }
if(!ZeroKey(item.key))
{
PushKeyedItem(item);
}
RenderItem(ctx, item); RenderItem(ctx, item);
} }
@ -1434,17 +1501,9 @@ Clamp(UICtx* ctx, Vertex* v)
} }
void void
FocusItem(T)(T focus) if(is(T == UIKey) || StringType!T || is(T == UIItem*)) FocusItem(T)(T focus) if(ItemAndKeyType!(T))
{ {
static if(is(T == UIItem*)) g_ui_ctx.focus_item = mixin(AssignItem(T, focus));
{
assert(!Nil(focus));
g_ui_ctx.focus_item = focus;
}
else
{
g_ui_ctx.focus_item = Get(focus);
}
} }
pragma(inline) void pragma(inline) void
@ -1513,9 +1572,16 @@ RenderItem(UICtx* ctx, UIItem* item)
Vec4[] syntax_cols = ctx.syntax_colors[item.syntax_highlight]; Vec4[] syntax_cols = ctx.syntax_colors[item.syntax_highlight];
f32 line_height = fg.abuf.atlas.line_height; f32 line_height = fg.abuf.atlas.line_height;
DrawUI(ctx, fg.index);
if(item.flags & UIF.VerticalAlignText) if(item.flags & UIF.VerticalAlignText)
{ {
y_pos += (item.rect.p1.y - item.rect.p0.y - line_height) / 2.0; f32 adj = floor((item.rect.p1.y - item.rect.p0.y - line_height) / 2.0);
y_pos += adj;
}
else
{
y_pos += item.padding[A2D.Y].x;
} }
foreach(i; 0 .. item.text_lines.length) foreach(i; 0 .. item.text_lines.length)
@ -1530,7 +1596,6 @@ RenderItem(UICtx* ctx, UIItem* item)
else if(item.flags & UIF.CenterAlignText) else if(item.flags & UIF.CenterAlignText)
{ {
x_pos = ((item.rect.p1.x-item.rect.p0.x - CalcTextWidth(str, &fg.abuf)) / 2.0) + item.padding[A2D.X].x; x_pos = ((item.rect.p1.x-item.rect.p0.x - CalcTextWidth(str, &fg.abuf)) / 2.0) + item.padding[A2D.X].x;
Logf("%f", x_pos);
} }
else else
{ {
@ -1545,7 +1610,7 @@ RenderItem(UICtx* ctx, UIItem* item)
{ {
u8 ch = str[j]; u8 ch = str[j];
Glyph* g = ch < fg.abuf.atlas.glyphs.length ? fg.abuf.atlas.glyphs.ptr + ch : null; Glyph* g = ch < fg.abuf.atlas.glyphs.length ? fg.abuf.atlas.glyphs.ptr + ch : null;
DrawGlyph(item, g, fg.index, &x_pos, y_pos, line_height, fg.index, syntax_cols[tks[j]]); DrawGlyph(item, g, &x_pos, y_pos, line_height, syntax_cols[tks[j]]);
} }
} }
else else
@ -1554,7 +1619,7 @@ RenderItem(UICtx* ctx, UIItem* item)
{ {
u8 ch = str[j]; u8 ch = str[j];
Glyph* g = ch < fg.abuf.atlas.glyphs.length ? fg.abuf.atlas.glyphs.ptr + ch : null; Glyph* g = ch < fg.abuf.atlas.glyphs.length ? fg.abuf.atlas.glyphs.ptr + ch : null;
DrawGlyph(item, g, fg.index, &x_pos, y_pos, line_height, fg.index, item.text_col); DrawGlyph(item, g, &x_pos, y_pos, line_height, item.text_col);
} }
} }
@ -1569,13 +1634,10 @@ RenderItem(UICtx* ctx, UIItem* item)
{ {
DrawUI(ctx); DrawUI(ctx);
Vec2 p0 = Clamp(item.rect.p0 - item.padding[A2D.X], Vec2(0.0), Vec2(ctx.res.x, ctx.res.y)); i32 x = cast(i32)clamp(scissor_x ? floor(item.rect.p0.x) : 0, 0.0, ctx.res.x);
Vec2 p1 = Clamp(item.rect.p1 + item.padding[A2D.Y], Vec2(0.0), Vec2(ctx.res.x, ctx.res.y)); i32 y = cast(i32)clamp(scissor_y ? floor(item.rect.p0.y) : 0, 0.0, ctx.res.y);
i32 w = cast(i32)clamp(scissor_x ? floor(item.rect.p1.x) - x : ctx.res.x, 0.0, ctx.res.x);
u32 x = cast(u32)(scissor_x ? floor(p0.x) : 0); i32 h = cast(i32)clamp(scissor_y ? floor(item.rect.p1.y) - y : ctx.res.y, 0.0, ctx.res.y);
u32 y = cast(u32)(scissor_y ? floor(p0.y) : 0);
u32 w = cast(u32)(scissor_x ? floor(p1.x) - x : ctx.res.x);
u32 h = cast(u32)(scissor_y ? floor(p1.y) - y : ctx.res.y);
SetScissor(&ctx.rd, x, y, w, h); SetScissor(&ctx.rd, x, y, w, h);
@ -1607,6 +1669,41 @@ GetExtent()
version(ENABLE_RENDERER) return RendererGetExtent(&g_ui_ctx.rd); else return [1280, 720]; version(ENABLE_RENDERER) return RendererGetExtent(&g_ui_ctx.rd); else return [1280, 720];
} }
void
PushKeyedItem(UIItem* item)
{
UICtx* ctx = GetCtx();
assert(!Nil(item));
assert(!ZeroKey(item.key));
Stack!(UIItem*)* node = Alloc!(Stack!(UIItem*))(&ctx.key_stack_arena);
node.next = ctx.key_item_stack;
node.value = item;
ctx.key_item_stack = node;
}
void
PushDisplayStringCopy(T)(T param, bool auto_pop = true)
{
PushDisplayString(ScratchAlloc(mixin(AssignStr!(T, param))), auto_pop);
}
void
PushDisplayString(Args...)(string fmt, Args args)
{
static if(is(Args[Args.length-1] == bool))
{
PushDisplayString(Scratchf(fmt, args[0 .. Args.length-1]), args[Args.length-1]);
}
else
{
PushDisplayString(Scratchf(fmt, args));
}
}
template template
StackIDs(string stack, string ctx = "ctx") StackIDs(string stack, string ctx = "ctx")
{ {
@ -1623,7 +1720,13 @@ static string
PushScope(string stack, string value)() PushScope(string stack, string value)()
{ {
import std.conv; import std.conv;
return i"Push!(\"$(stack)\")($(value)); scope(exit) Pop!(\"$(stack)\")();".text; return i"Push!(\"$(stack)\")($(value), false); scope(exit) Pop!(\"$(stack)\")();".text;
}
struct UIPushInfo
{
string s; // stack
string v; // value
} }
static string static string
@ -1640,14 +1743,22 @@ PushOnce(UIPushInfo[] info)()
return result; return result;
} }
struct UIPushInfo void
PushOnce(Args...)()
{ {
string s; // stack static assert(Args.length%2 == 0, "Must provide key/value pairs in the form of string/value.");
string v; // value
static foreach(idx; 0 .. Args.length/2)
{
{
enum i = idx*2;
Push!(Args[i+0])(Args[i+1], true);
}
}
} }
void void
Push(string stack_str, T)(T value, bool auto_pop = false) Push(string stack_str, T)(T value, bool auto_pop = true)
{ {
import std.string : replace; import std.string : replace;
@ -1668,8 +1779,15 @@ Push(string stack_str, T)(T value, bool auto_pop = false)
} }
node.next = stack.top; node.next = stack.top;
node.value = value;
node.auto_pop = auto_pop; node.auto_pop = auto_pop;
static if(is(ST == string) && StringType!(T))
{
node.value = Str(value);
}
else
{
node.value = value;
}
stack.top = node; stack.top = node;
} }
@ -1750,7 +1868,7 @@ GenPushFuncs()
} }
} }
funcs ~= "pragma(inline) void " ~ fn_name ~ "(T)(T value, bool auto_pop = false){ Push!(\"" ~ info.id ~ "\")(value, auto_pop); }\n"; funcs ~= "pragma(inline) void " ~ fn_name ~ "(T)(T value, bool auto_pop = true){ Push!(\"" ~ info.id ~ "\")(value, auto_pop); }\n";
} }
} }
} }
@ -1953,8 +2071,9 @@ ZeroKey()
} }
bool bool
ZeroKey(UIKey key) ZeroKey(T)(T param) if(is(T == UIItem*) || is(T == UIKey))
{ {
UIKey key = mixin(AssignKey!(T, param));
return key.hash == 0 && key.hash_text.length == 0; return key.hash == 0 && key.hash_text.length == 0;
} }
@ -1982,19 +2101,11 @@ Get(Args...)(string str, Args args)
} }
pragma(inline) UIItem* pragma(inline) UIItem*
Get(T)(T k) if(is(T: UIKey) || StringType!T) Get(T)(T k) if(KeyType!(T))
{ {
static if(is(T: UIKey)) UIKey key = mixin(AssignKey!(T, k));
{
UIKey key = k;
}
else
{
UIKey key = MakeKey(k);
}
UIItem* item;
UICtx* ctx = GetCtx(); UICtx* ctx = GetCtx();
UIItem* item;
if(ZeroKey(key)) if(ZeroKey(key))
{ {
@ -2062,7 +2173,7 @@ GlyphWidth(Glyph* g, FontAtlasBuf* abuf)
} }
pragma(inline) void pragma(inline) void
DrawGlyph(UIItem* item, Glyph* glyph, u32 atlas_index, f32* x_pos, f32 y, f32 line_height, u32 index, Vec4 col = Vec4(1.0)) DrawGlyph(UIItem* item, Glyph* glyph, f32* x_pos, f32 y, f32 line_height, Vec4 col = Vec4(1.0))
{ {
if(glyph) if(glyph)
{ {
@ -2091,10 +2202,9 @@ DrawGlyph(UIItem* item, Glyph* glyph, u32 atlas_index, f32* x_pos, f32 y, f32 li
v.dst_end = Vec2(*x_pos+glyph.plane_left+w, y_pos+h); v.dst_end = Vec2(*x_pos+glyph.plane_left+w, y_pos+h);
v.cols = col; v.cols = col;
v.cols_end = col; v.cols_end = col;
v.texture = true;
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);
v.has_texture = true;
f32 end_x = *x_pos + advance; f32 end_x = *x_pos + advance;
f32 width = end_x - *x_pos; f32 width = end_x - *x_pos;

View File

@ -14,18 +14,28 @@ __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();
/******
- Set up "themes" for different components (e.g. command palette window, editor view, status bar) then have the ability to create styles and apply them to different sections via a string lookup, e.g. start of function to draw cmd palette call ApplyTheme(selected_theme_name) then do code to draw things, get to options and call ApplyTheme(selected_theme_name_for_options), etc.
- Move scrolling behaviour into ui.d and externally be oblivious to it, e.g. just feed it all lines of text in a text buffer, will have to specifically handle extreme cases later but initially i think it should be fine
******/
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 HL_BG_COL = Vec4(0.160, 0.533, 0.803, 1.0);
Vec4 HL_BORDER_COL = Vec4(0.172, 0.643, 0.988, 1.0); Vec4 HL_BORDER_COL = Vec4(0.172, 0.643, 0.988, 1.0);
Vec4 CMD_COL = Vec4(0.22, 0.22, 0.22, 1.0); Vec4 CMD_COL = Vec4(0.22, 0.22, 0.22, 1.0);
Vec4 CMD_BORDER_COL = Vec4(0.48, 0.48, 0.48, 1.0); Vec4 CMD_BORDER_COL = Vec4(0.48, 0.48, 0.48, 1.0);
Vec4 CMD_INPUT_COL = Vec4(0.130, 0.420, 0.640, 1.0); Vec4 CMD_INPUT_COL = Vec4(0.130, 0.420, 0.640, 1.0);
Vec4 GREY = Vec4(0.45, 0.45, 0.45, 1.0); Vec4 GREY = Vec4(0.80, 0.80, 0.80, 1.0);
Vec4 WHITE = HexCol(0xFFFFFF); Vec4 WHITE = HexCol(0xFFFFFF);
Vec4 BLUE = HexCol(0x4EA9FF); Vec4 BLUE = HexCol(0x4EA9FF);
Vec4 RED = HexCol(0xFF3268); Vec4 RED = HexCol(0xFF3268);
Vec4 YELLOW = HexCol(0xF7C443); Vec4 YELLOW = HexCol(0xF7C443);
const f32 CMD_X_PAD = 8.0;
const f32 CMD_Y_PAD = 4.0;
const u32 CMD_TITLE_PX = 14;
const u32 CMD_SUB_PX = 10;
shared static this() shared static this()
{ {
g_NIL_PANEL = cast(Panel* )&g_nil_panel; g_NIL_PANEL = cast(Panel* )&g_nil_panel;
@ -70,7 +80,7 @@ LineCounterView(FontAtlasBuf* abuf, u64 max_line, u64 lines, i64 line_offset, f3
for(u64 i = line_offset; i < end_line && i < max_line; i += 1) for(u64 i = line_offset; i < end_line && i < max_line; i += 1)
{ {
char[] buf = ScratchAlloc!(char)(ch_width); char[] buf = ScratchAlloc!(char)(ch_width);
Push!("display_string")(Scratchf("%s", i), true); Push!("display_string")(Scratchf("%s", i));
MakeItem(ZERO, UIF.DrawText); MakeItem(ZERO, UIF.DrawText);
} }
} }
@ -80,7 +90,7 @@ EditorTextView(UIItem* editor, Panel* p, FontAtlasBuf* abuf, u64 lines, i64 line
{ {
Editor* ed = p.ed; Editor* ed = p.ed;
PushLayoutAxis(A2D.Y, true); PushLayoutAxis(A2D.Y);
f32 text_size = cast(f32)p.text_size; f32 text_size = cast(f32)p.text_size;
f32 clamp_y = cast(f32)(ed.buf.line_count-lines)*text_size; f32 clamp_y = cast(f32)(ed.buf.line_count-lines)*text_size;
@ -129,16 +139,12 @@ EditorTextView(UIItem* editor, Panel* p, FontAtlasBuf* abuf, u64 lines, i64 line
mixin(PushScope!("size_info", q{ UISY(ST.TextSize) } )); mixin(PushScope!("size_info", q{ UISY(ST.TextSize) } ));
mixin(PushScope!("syntax_highlight", q{ UISH.D } )); mixin(PushScope!("syntax_highlight", q{ UISH.D } ));
// Do a thing like the bottom 3 colours on lost trail (arknights) on highlighting but make it slide to the right one colour every time you scroll down
u64 i = line_offset; u64 i = line_offset;
for(LineBuffer* lb = GetLine(&ed.buf, i); !CheckNil(g_NIL_LINE_BUF, lb) && i < line_offset+lines; i += 1, lb = GetLine(&ed.buf, i)) for(LineBuffer* lb = GetLine(&ed.buf, i); !CheckNil(g_NIL_LINE_BUF, lb) && i < line_offset+lines; i += 1, lb = GetLine(&ed.buf, i))
{ {
enum UIPushInfo[] lc_info = [ string txt = Str(lb.text);
{ "display_string", q{ Str(lb.text) } }, u8[] tokens = cast(u8[])(lb.style);
{ "syntax_tokens", q{ cast(u8[])(lb.style) } }, PushOnce!("display_string", txt, "syntax_tokens", tokens);
];
mixin(PushOnce!(lc_info));
UIItem* line = MakeItem(zero, UIF.DrawText); UIItem* line = MakeItem(zero, UIF.DrawText);
} }
@ -163,6 +169,8 @@ EditorView(Panel* p)
} }
else else
{ {
assert(ed);
UIKey ed_key = MakeKey("###ed_%s", ed.editor_id); UIKey ed_key = MakeKey("###ed_%s", ed.editor_id);
UIItem* editor = Get(ed_key); UIItem* editor = Get(ed_key);
@ -202,7 +210,7 @@ EditorView(Panel* p)
} }
} }
for(UIInput* i = ctx.events.first; !CheckNil(g_UI_NIL_INPUT, i) && g_ed_ctx.focused_editor == p.ed; i = i.next) for(UIInput* i = ctx.events.first; !CheckNil(g_UI_NIL_INPUT, i) && g_ed_ctx.focused_panel == p && !CmdModeActive(); i = i.next)
{ {
bool taken; bool taken;
@ -229,6 +237,9 @@ EditorView(Panel* p)
{ {
DLLRemove(&ctx.events, i, g_UI_NIL_INPUT); DLLRemove(&ctx.events, i, g_UI_NIL_INPUT);
} }
if(g_ed_ctx.focused_panel != p) break;
if(g_ed_ctx.state == ES.CmdPalette) break;
} }
ed.cursor_pos = VecPos(&ed.buf); ed.cursor_pos = VecPos(&ed.buf);
@ -241,19 +252,12 @@ EditorView(Panel* p)
ResetBuffer(&ed.buf); ResetBuffer(&ed.buf);
} }
void UIItem*
CommandPalette(CmdPalette* cmd, bool active) CommandWindow(f32 w, f32 h, f32 x, f32 y, bool active)
{ {
UICtx* ctx = GetCtx();
Vec2 ext = GetExtent();
FontAtlasBuf* abuf = &g_ui_ctx.glyph_sets[0].abuf;
f32 w = ext.x*0.6;
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.2, ext.y*0.1) } }, { "fixed_pos", q{ Vec2(x, y) } },
{ "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 } },
@ -268,36 +272,115 @@ CommandPalette(CmdPalette* cmd, bool active)
cmd_flags |= UIF.SetReady; cmd_flags |= UIF.SetReady;
} }
UIItem* cmd_item = MakeItem("###cmd_palette", cmd_flags); UIItem* window = MakeItem("###cmd_palette", cmd_flags);
Push!("parent")(window, false);
f32 padding_y = 4.0; return window;
}
mixin(PushScope!("parent", q{ cmd_item } )); UIItem*
mixin(PushScope!("padding", q{ Vec2(4.0, padding_y) })); CommandInput(CmdPalette* cmd, string placeholder = "")
{
string input_str = cmd.icount ? Str(cmd.buffer[0 .. cmd.icount]) : "Search...";
Vec4 input_text_col = cmd.icount ? WHITE : GREY;
enum UIPushInfo[] cmd_input_params = [ enum UIPushInfo[] cmd_input_params = [
{ "bg_col", q{ CMD_COL } }, { "bg_col", q{ CMD_COL } },
{ "border_col", q{ CMD_BORDER_COL } }, { "border_col", q{ CMD_BORDER_COL } },
{ "border_thickness", q{ 1.0 }}, { "border_thickness", q{ 1.0 }},
{ "text_size", q{ 18 }}, { "text_size", q{ 14 }},
{ "corner_radius", q{ Vec4(12.0, 12.0, 0.0, 0.0)}}, { "corner_radius", q{ Vec4(12.0, 12.0, 0.0, 0.0) } },
{ "edge_softness", q{ 1.0 }}, { "edge_softness", q{ 1.0 } },
{ "size_info", q{ UISY(ST.TextSize) } }, { "size_info", q{ UISY(ST.TextSize) } },
{ "padding", q{ Vec2A2(6.0, 6.0, 10.0, 10.0)} }, { "padding", q{ Vec2A2(6.0, 6.0, 6.0, 6.0)} },
{ "display_string", q{ Str(cmd.buffer[0 .. cmd.icount]) } }, { "display_string", q{ input_str } },
{ "text_col", q{ input_text_col } },
]; ];
mixin(PushOnce!(cmd_input_params)); mixin(PushOnce!(cmd_input_params));
return MakeItem("###cmd_input", UIF.DrawBorder|UIF.DrawBackground|UIF.DrawText|UIF.Overflow);
}
MakeItem(ZERO, UIF.DrawBorder|UIF.DrawBackground|UIF.DrawText|UIF.Overflow|UIF.VerticalAlignText); UIItem*
CommandOpt(T)(CmdPalette* cmd, T opt, u64 i)
{
Vec4[2] opt_cols = [
Vec4(0.22, 0.22, 0.22, 1.0),
Vec4(0.31, 0.31, 0.31, 1.0),
];
// the two sizes seems to fuck this up somehow UIFlags flags = UIF.DrawBackground|UIF.AnimateHot|UIF.Clickable;
u32 title_px = 16;
u32 sub_px = 16; PushBgCol(opt_cols[i%2]);
PushLayoutAxis(A2D.Y);
static if(is(T == string))
{
PushDisplayString(cmd.opt_strs[i]);
UIKey k = MakeKey("###opt_%s", i);
Hovered!(true)(k, &cmd.selected, i);
UIItem* item = MakeItem(k, flags|UIF.DrawText|SetHot(cmd.selected == i));
Signal(item);
return item;
}
else if(is(T == Command*))
{
UIKey k = MakeKey("###optc_%s", i);
Hovered!(true)(k, &cmd.selected, i);
UIItem* optc = MakeItem(k, flags|SetHot(cmd.selected == i));
Signal(optc);
PushParent(optc, false);
PushSizeInfo(UISY(ST.TextSize), false);
PushTextSize(CMD_TITLE_PX);
PushPadding(Vec2A2(CMD_X_PAD, CMD_X_PAD, CMD_Y_PAD, 2.0));
PushDisplayStringCopy(cmd.commands[i].name);
MakeItem(ZERO, UIF.DrawText);
PushTextSize(CMD_SUB_PX);
PushTextCol(GREY);
PushPadding(Vec2A2(CMD_X_PAD, CMD_X_PAD, 0.0, 0.0));
PushDisplayStringCopy(cmd.commands[i].desc);
MakeItem(ZERO, UIF.DrawText);
Pop!("parent", "size_info");
return optc;
}
}
void
CommandPalette(CmdPalette* cmd, bool active)
{
UICtx* ctx = GetCtx();
Vec2 ext = GetExtent();
f32 w = ext.x*0.6;
f32 h = ext.y*0.7;
UIItem* cmd_item = CommandWindow(w, h, ext.x*0.2, ext.y*0.1, active);
UIItem* input = CommandInput(cmd, "Search...");
// Command Palette Options
{
u32 title_px = 14;
u32 sub_px = 10;
f32 title_size = GetLineHeight(title_px);
f32 sub_size = GetLineHeight(sub_px);
f32 title_pad = 2.0f; f32 title_pad = 2.0f;
f32 opt_y_pad = 4.0; f32 y_pad = 4.0;
f32 opt_x_pad = 8.0; f32 x_pad = 8.0;
f32 opt_height = cast(f32)(title_px + sub_px) + opt_y_pad*2.0 + title_pad; f32 opt_height = cast(f32)(title_size + sub_size) + y_pad*2.0 + title_pad;
enum UIPushInfo[] cmd_opts_box_params = [ enum UIPushInfo[] cmd_opts_box_params = [
{ "layout_axis", q{ A2D.Y }}, { "layout_axis", q{ A2D.Y }},
@ -313,64 +396,55 @@ CommandPalette(CmdPalette* cmd, bool active)
UIItem* opt_box = MakeItem(ZERO, UIF.DrawBorder); UIItem* opt_box = MakeItem(ZERO, UIF.DrawBorder);
mixin(PushScope!("parent", q{ opt_box })); 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) } )); mixin(PushScope!("size_info", q{ UISY(ST.Pixels, opt_height) } ));
u64 max_opts = cast(u64)ceil(h/opt_height); u64 max_opts = cast(u64)ceil(h/opt_height);
Vec4[2] opt_cols = [
Vec4(0.22, 0.22, 0.22, 1.0),
Vec4(0.35, 0.35, 0.35, 1.0),
];
// Active should be highlights around the item like File Pilot (maybe not)
bool opts = cast(bool)cmd.opt_strs.length; bool opts = cast(bool)cmd.opt_strs.length;
if(opts) if(opts)
{ {
mixin(PushScope!("padding", q{ Vec2A2(CMD_X_PAD, CMD_X_PAD, CMD_Y_PAD, CMD_Y_PAD) } ));
for(u64 i = 0; i < cmd.opt_strs.length && i < max_opts; i += 1) for(u64 i = 0; i < cmd.opt_strs.length && i < max_opts; i += 1)
{ {
PushDisplayString(Str(cmd.opt_strs[i]), true); UIItem* opt = CommandOpt(cmd, cmd.opt_strs[i], i);
PushBgCol(opt_cols[i%2], true);
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 else
{ {
for(u64 i = 0; i < cmd.commands.length && i < max_opts; i += 1) for(u64 i = 0; i < cmd.commands.length && i < max_opts; i += 1)
{ {
PushBgCol(opt_cols[i%2], true); UIItem* opt = CommandOpt(cmd, &cmd.commands[i], i);
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");
} }
} }
}
for(UIInput* i = ctx.events.first; !CheckNil(g_UI_NIL_INPUT, i); i = i.next)
{
bool taken = HandleCmdMode(cmd, i);
if(taken)
{
DLLRemove(&ctx.events, i, g_UI_NIL_INPUT);
}
}
Pop!("parent");
}
void
CommandTextInput(CmdPalette* cmd, string[] opts, void function(string) callback)
{
UICtx* ctx = GetCtx();
Vec2 ext = GetExtent();
}
UIFlags
SetHot(bool cond)
{
return (cond ? UIF.SetHot : UIF.None);
} }
UIItem* UIItem*
@ -387,7 +461,7 @@ Container(bool push = true)(Axis2D axis, UISize[2] size_info)
static if(push) static if(push)
{ {
Push!("parent")(item); Push!("parent")(item, false);
} }
return item; return item;
@ -411,7 +485,7 @@ StatusBar(EditorCtx* ed_ctx)
status = "Input"; status = "Input";
status_col = YELLOW; status_col = YELLOW;
} }
else if(ed_ctx.state == ES.CmdOpen) else if(ed_ctx.state == ES.CmdPalette)
{ {
status = "Command"; status = "Command";
status_col = RED; status_col = RED;

View File

@ -66,7 +66,7 @@ void main()
vec4 gamma = vec4(1.0/1.4); vec4 gamma = vec4(1.0/1.4);
vec4 tex_color = vec4(1.0); vec4 tex_color = vec4(1.0);
if(FDF.texture != 0) if(FDF.has_texture != 0)
{ {
tex_color = texture(sampler2D(SpriteAtlas, SamplerNearest), FD.uv); tex_color = texture(sampler2D(SpriteAtlas, SamplerNearest), FD.uv);
} }

View File

@ -6,9 +6,10 @@ layout (set = 1, binding = 1) uniform sampler SamplerNearest;
layout (push_constant) uniform Constants { layout (push_constant) uniform Constants {
mat4 projection; mat4 projection;
uint atlas_index;
} PC; } PC;
#define SpriteAtlas SpriteAtlasArray[FDF.atlas_index] #define SpriteAtlas SpriteAtlasArray[PC.atlas_index]
#ifdef VERT_SHADER #ifdef VERT_SHADER
# define FragData out struct FragDataOut # define FragData out struct FragDataOut
@ -22,11 +23,10 @@ layout (push_constant) uniform Constants {
layout (location = 0) FragDataFlat layout (location = 0) FragDataFlat
{ {
uint texture; uint has_texture;
uint atlas_index;
} FDF; } FDF;
layout (location = 2) FragData layout (location = 1) FragData
{ {
vec4 color; vec4 color;
vec4 color_end; vec4 color_end;

View File

@ -8,19 +8,15 @@
layout (location = 0) in vec4 in_col_start; layout (location = 0) in vec4 in_col_start;
layout (location = 1) in vec4 in_col_end; layout (location = 1) in vec4 in_col_end;
layout (location = 2) in float in_corner_radius_x0y0; layout (location = 2) in vec4 in_corner_radius;
layout (location = 3) in float in_corner_radius_x1y0; layout (location = 3) in vec2 in_dst_start;
layout (location = 4) in float in_corner_radius_x0y1; layout (location = 4) in vec2 in_dst_end;
layout (location = 5) in float in_corner_radius_x1y1; layout (location = 5) in vec2 in_src_start;
layout (location = 6) in vec2 in_dst_start; layout (location = 6) in vec2 in_src_end;
layout (location = 7) in vec2 in_dst_end; layout (location = 7) in float border_thickness;
layout (location = 8) in vec2 in_src_start; layout (location = 8) in float edge_softness;
layout (location = 9) in vec2 in_src_end; layout (location = 9) in float raised;
layout (location = 10) in float border_thickness; layout (location = 10) in uint has_texture;
layout (location = 11) in float edge_softness;
layout (location = 12) in float raised;
layout (location = 13) in uint in_has_texture;
layout (location = 14) in uint in_atlas_index;
vec2 Vertices[4] = vec2[4]( vec2 Vertices[4] = vec2[4](
vec2(-1.0, -1.0), vec2(-1.0, -1.0),
@ -57,10 +53,10 @@ void main()
); );
float corner_radius[4] = float[4]( float corner_radius[4] = float[4](
in_corner_radius_x0y0, in_corner_radius.x,
in_corner_radius_x0y1, in_corner_radius.z,
in_corner_radius_x1y0, in_corner_radius.y,
in_corner_radius_x1y1 in_corner_radius.w
); );
vec2 dst_verts_pct = vec2(bool(gl_VertexIndex >> 1) ? 1.0f : 0.0f, vec2 dst_verts_pct = vec2(bool(gl_VertexIndex >> 1) ? 1.0f : 0.0f,
@ -77,8 +73,7 @@ void main()
FD.raised = raised; FD.raised = raised;
FD.border_thickness = border_thickness; FD.border_thickness = border_thickness;
FD.sdf_sample_pos = (2.0f * dst_verts_pct - 1.0f) * half_size; FD.sdf_sample_pos = (2.0f * dst_verts_pct - 1.0f) * half_size;
FDF.texture = in_has_texture; FDF.has_texture = has_texture;
FDF.atlas_index = in_atlas_index;
vec4 v_pos = PC.projection * vec4(pos.x, pos.y, 0, 1); vec4 v_pos = PC.projection * vec4(pos.x, pos.y, 0, 1);
gl_Position = vec4(v_pos.x, v_pos.y, v_pos.z, 1); gl_Position = vec4(v_pos.x, v_pos.y, v_pos.z, 1);