things coming together

This commit is contained in:
Matthew 2026-01-31 18:40:31 +11:00
parent 08fe3c4b81
commit 8e74e90109
3 changed files with 511 additions and 202 deletions

View File

@ -777,16 +777,19 @@ Move(FlatBuffer* fb, Input key, Modifier md)
case Up:
{
MoveToEmptyLine!(true)(fb);
taken = true;
} break;
case Down:
{
MoveToEmptyLine!(false)(fb);
taken = true;
} break;
case Left:
{
if(fb.pos > 0)
{
MoveToWordEdge!(false)(fb);
taken = true;
}
} break;
case Right:
@ -794,6 +797,7 @@ Move(FlatBuffer* fb, Input key, Modifier md)
if(fb.pos < fb.length)
{
MoveToWordEdge!(true)(fb);
taken = true;
}
} break;
case Home: fb.pos = 0; taken = true; break;
@ -816,6 +820,7 @@ Move(FlatBuffer* fb, Input key, Modifier md)
if(fb.pos > 0)
{
MoveToNextWord!(false)(fb);
taken = true;
}
} break;
case Right:
@ -823,6 +828,7 @@ Move(FlatBuffer* fb, Input key, Modifier md)
if(fb.pos < fb.length)
{
MoveToNextWord!(true)(fb);
taken = true;
}
} break;
default: break;
@ -871,8 +877,6 @@ Move(FlatBuffer* fb, Input key, Modifier md)
UpdateSelection(fb);
Logf("selection %s", fb.selection.v);
return taken;
}

View File

@ -1,4 +1,5 @@
import dlib;
import dlib.util : Str;
import vulkan;
import std.format : sformat;
@ -94,16 +95,23 @@ struct Ctx
alias rd_ctx this;
}
struct TextInputBuffer
{
u8[] data;
u64 length;
}
struct CmdPalette
{
Arena arena;
Arena cmd_arena;
u8[] buffer;
u32 icount;
TextInputBuffer text_input;
i64 selected;
u64 visible_opts;
Command current;
Parameter[] params;
Command[CMD_LIST.length] cmd_arr;
Command[] cmds;
Arena exec_cmd_arena;
TextInputBuffer exec_cmd_input;
i64 exec_cmd_selected;
string[] exec_cmd_opts;
}
struct Editor
@ -148,7 +156,6 @@ struct Command
string desc;
string cmd;
CmdType type;
bool hidden;
union
{
CmdFn fn;
@ -211,12 +218,6 @@ enum EditState
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;
struct Parameter
{
string value;
bool visible;
}
__gshared bool g_input_mode = false;
__gshared Ctx g_ctx;
@ -225,13 +226,13 @@ const Command NO_CMD = {
type: CT.None,
};
Command[5] CMD_LIST = [
Command[20] CMD_LIST = [
{
name: "Open",
cmd: "open",
desc: "Open a file in the focused editor view.",
type: CT.Callback,
fn: &NotImplementedCallback,
fn: &OpenFileWindow,
},
{
name: "Save",
@ -252,17 +253,137 @@ Command[5] CMD_LIST = [
cmd: "vsplit",
desc: "Split the current editor view vertically.",
type: CT.Callback,
fn: &NotImplementedCallback,
fn: &SplitVertically,
},
{
name: "Horizontal Split",
cmd: "hsplit",
desc: "Split the current editor view horizontally.",
type: CT.Callback,
fn: &SplitHorizontally,
},
{
name: "Test Option 1",
cmd: "testopt1",
desc: "This is a test option",
type: CT.Callback,
fn: &NotImplementedCallback,
},
{
name: "Test Option 2",
cmd: "testopt2",
desc: "This is a test option",
type: CT.Callback,
fn: &NotImplementedCallback,
},
{
name: "Test Option 3",
cmd: "testopt3",
desc: "This is a test option",
type: CT.Callback,
fn: &NotImplementedCallback,
},
{
name: "Test Option 4",
cmd: "testopt4",
desc: "This is a test option",
type: CT.Callback,
fn: &NotImplementedCallback,
},
{
name: "Test Option 5",
cmd: "testopt5",
desc: "This is a test option",
type: CT.Callback,
fn: &NotImplementedCallback,
},
{
name: "Test Option 6",
cmd: "testopt6",
desc: "This is a test option",
type: CT.Callback,
fn: &NotImplementedCallback,
},
{
name: "Test Option 7",
cmd: "testopt7",
desc: "This is a test option",
type: CT.Callback,
fn: &NotImplementedCallback,
},
{
name: "Test Option 8",
cmd: "testopt8",
desc: "This is a test option",
type: CT.Callback,
fn: &NotImplementedCallback,
},
{
name: "Test Option 9",
cmd: "testopt9",
desc: "This is a test option",
type: CT.Callback,
fn: &NotImplementedCallback,
},
{
name: "Test Option 10",
cmd: "testopt10",
desc: "This is a test option",
type: CT.Callback,
fn: &NotImplementedCallback,
},
{
name: "Test Option 11",
cmd: "testopt11",
desc: "This is a test option",
type: CT.Callback,
fn: &NotImplementedCallback,
},
{
name: "Test Option 12",
cmd: "testopt12",
desc: "This is a test option",
type: CT.Callback,
fn: &NotImplementedCallback,
},
{
name: "Test Option 13",
cmd: "testopt13",
desc: "This is a test option",
type: CT.Callback,
fn: &NotImplementedCallback,
},
{
name: "Test Option 14",
cmd: "testopt14",
desc: "This is a test option",
type: CT.Callback,
fn: &NotImplementedCallback,
},
{
name: "Test Option 15",
cmd: "testopt15",
desc: "This is a test option",
type: CT.Callback,
fn: &NotImplementedCallback,
},
];
bool
SplitVertically(Ctx* ctx)
{
AddPanel(ctx.focused_panel, A2D.X);
return true;
}
bool
SplitHorizontally(Ctx* ctx)
{
AddPanel(ctx.focused_panel, A2D.Y);
return true;
}
bool
NotImplementedCallback(Ctx* ctx)
{
@ -311,7 +432,7 @@ Cycle(Inputs* inputs)
if(ctx.state == ES.RunCmd)
{
if(ctx.cmd.current.fn(ctx))
if(ctx.cmd.cmds[ctx.cmd.selected].fn(ctx))
{
ctx.state = ES.NormalMode;
}
@ -336,9 +457,9 @@ InitCtx(PlatformWindow* window)
ctx.window = window;
ctx.arena = CreateArena(MB(2));
ctx.cmd.arena = CreateArena(MB(1));
ctx.cmd.cmd_arena = CreateArena(MB(1));
ctx.cmd.buffer = Alloc!(u8)(1024);
ctx.cmd.text_input.data = Alloc!(u8)(1024);
ctx.cmd.exec_cmd_input.data = Alloc!(u8)(1024);
ctx.cmd.exec_cmd_arena = CreateArena(MB(1));
ctx.timer = CreateTimer();
InitUI(ctx);
@ -517,6 +638,12 @@ OpenFile(Editor* ed, string file_name)
}
}
string
Str(TextInputBuffer text_buffer)
{
return Str(text_buffer.data[0 .. text_buffer.length]);
}
Editor*
CreateEditor()
{
@ -595,12 +722,25 @@ ResetCtx(Editor* ed)
ResetCtx();
}
void
ResetCmd(Ctx* ctx)
{
ctx.cmd.text_input.length = 0;
ctx.cmd.selected = 0;
ctx.cmd.cmds = ctx.cmd.cmd_arr[0 .. $];
ctx.cmd.exec_cmd_input.length = 0;
ctx.cmd.exec_cmd_selected = 0;
ctx.cmd.exec_cmd_opts = [];
ctx.cmd.cmds = CMD_LIST[0 .. $];
Reset(&ctx.cmd.exec_cmd_arena);
}
void
ResetCtx()
{
g_ctx.state = ES.NormalMode;
g_ctx.cmd.icount = 0;
g_ctx.cmd.current = cast(Command)NO_CMD;
g_ctx.cmd.text_input.length = 0;
g_ctx.cmd.selected = 0;
}
@ -742,15 +882,7 @@ HandleInputs(UIPanel* p, LinkedList!(UIInput)* inputs, bool hovered, bool focuse
if(Shift(node.md))
{
ctx.state = ES.CmdPalette;
ctx.cmd.icount = 0;
ctx.cmd.selected = 0;
ctx.cmd.params = [];
ctx.cmd.visible_opts = CMD_LIST.length;
static foreach(i; 0 .. CMD_LIST.length)
{
CMD_LIST[i].hidden = false;
}
ResetCmd(ctx);
taken = true;
}
@ -890,20 +1022,22 @@ CharCases()
bool
HandleInputMode(Ctx* ctx, UIPanel* p, UIInput* ev)
{
bool taken = false;
bool taken;
switch(ev.key)
if(!taken) switch(ev.key)
{
mixin(CharCases());
case Input.Backspace:
{
Backspace(&p.ed.buf);
taken = true;
} break;
case Input.Escape:
{
ctx.state = ES.NormalMode;
taken = true;
} break;
default: MoveCursor(p.ed, ev); break;
default: taken = MoveCursor(p.ed, ev); break;
}
return taken;
@ -999,17 +1133,22 @@ StrContains(bool begins_with)(string str, string match)
}
void
HandleCmdMode(Ctx* ctx)
HandleCmdMode(Ctx* ctx, bool* enter_hit, bool* buffer_changed, bool* selection_changed)
{
UIPanel* panel = ctx.focused_panel;
Editor* ed = panel.ed;
CmdPalette* cmd = &ctx.cmd;
TextInputBuffer* text_buffer = ctx.state == ES.CmdPalette ? &ctx.cmd.text_input : &ctx.cmd.exec_cmd_input;
i64* selected = ctx.state == ES.CmdPalette ? &ctx.cmd.selected : &ctx.cmd.exec_cmd_selected;
bool buffer_changed;
for(UIInput* ev = ctx.events.first; ev && ctx.state == ES.CmdPalette; ev = ev.next)
for(UIInput* ev = ctx.events.first; ev; ev = ev.next)
{
bool taken;
if(ctx.state != ES.CmdPalette && ctx.state != ES.RunCmd)
{
break;
}
switch(ev.type)
{
case UIE.Press:
@ -1018,11 +1157,9 @@ HandleCmdMode(Ctx* ctx)
{
case Enter:
{
if(cmd.current.type != CT.None)
if(ctx.state == ES.CmdPalette)
{
cmd.current = CMD_LIST[cmd.selected];
switch(cmd.current.type) with(CmdType)
switch(ctx.cmd.cmds[ctx.cmd.selected].type) with(CmdType)
{
case Config:
{
@ -1034,45 +1171,31 @@ HandleCmdMode(Ctx* ctx)
} break;
case Callback:
{
assert(cmd.current.fn, "Callback type doesn't have a function pointer set");
assert(ctx.cmd.cmds[ctx.cmd.selected].fn, "Callback type doesn't have a function pointer set");
ctx.state = ES.RunCmd;
} break;
default: break;
}
}
else
{
*enter_hit = true;
}
} break;
case Backspace:
{
if(cmd.icount > 0)
if(text_buffer.length)
{
cmd.icount -= 1;
if(cmd.icount == cmd.current.name.length)
{
cmd.current = cast(Command)NO_CMD;
}
buffer_changed = true;
text_buffer.length -= 1;
*buffer_changed = true;
}
} break;
case Up, Down:
{
i32 incr = ev.key == Up ? -1 : +1;
i64 selected = cmd.selected;
for(i64 i = selected+incr; true; i += incr)
{
if(i < 0 || i >= CMD_LIST.length)
{
break;
}
*selected = (*selected)+incr;
if(!CMD_LIST[i].hidden)
{
selected = i;
break;
}
}
cmd.selected = selected;
*selection_changed = true;
} break;
case Escape:
{
@ -1082,11 +1205,11 @@ HandleCmdMode(Ctx* ctx)
{
if(ev.text.length)
{
Check(cmd, ev.text.length);
cmd.buffer[cmd.icount .. cmd.icount+ev.text.length] = cast(u8[])ev.text[0 .. $];
cmd.icount += ev.text.length;
Check(text_buffer, ev.text.length);
text_buffer.data[text_buffer.length .. text_buffer.length+ev.text.length] = cast(u8[])ev.text[0 .. $];
text_buffer.length += ev.text.length;
buffer_changed = true;
*buffer_changed = true;
}
} break;
}
@ -1099,67 +1222,14 @@ HandleCmdMode(Ctx* ctx)
DLLRemove(&ctx.events, ev, null);
}
}
if(buffer_changed)
{
if(cmd.icount)
{
cmd.visible_opts = 0;
string buffer_text = Str(cmd.buffer[0 .. cmd.icount]);
static foreach(i; 0 .. CMD_LIST.length)
{
CMD_LIST[i].hidden = !(StrContains!(false)(CMD_LIST[i].cmd, buffer_text) || StrContains!(false)(CMD_LIST[i].name, buffer_text));
cmd.visible_opts += !CMD_LIST[i].hidden;
}
if(CMD_LIST[cmd.selected].hidden)
{
i64 selected = 0;
foreach(i; 0 .. CMD_LIST.length)
{
if(!CMD_LIST[i].hidden)
{
selected = i;
break;
}
}
cmd.selected = selected;
}
}
else
{
static foreach(i; 0 .. CMD_LIST.length)
{
CMD_LIST[i].hidden = false;
}
cmd.visible_opts = CMD_LIST.length;
}
}
}
pragma(inline) void
Check(CmdPalette* cmd, u64 length)
Check(TextInputBuffer* text_buffer, u64 length)
{
if(cmd.icount+length >= cmd.buffer.length)
if(text_buffer.length+length >= text_buffer.data.length)
{
cmd.buffer = Realloc!(u8)(cmd.buffer, cmd.buffer.length*2);
text_buffer.data = Realloc!(u8)(text_buffer.data, text_buffer.data.length*2);
}
}
string
GetParam(CmdPalette* cmd)
{
string param;
for(u64 i = cmd.current.name.length; i < cmd.icount; i += 1)
{
if(cmd.buffer[i] != ' ')
{
param = Str(cmd.buffer[i .. cmd.icount]);
break;
}
}
return param;
}

View File

@ -75,6 +75,13 @@ __gshared Style SEP_STYLE = {
hl_col: Vec4(0.2, 0.2, 0.2, 1.0),
};
__gshared Style SCROLLBAR_STYLE = {
col: GREY,
hl_col: WHITE,
corner_radius: Vec4(4.0f),
edge_softness: 1.0,
};
__gshared Style CURSOR_STYLE = {
col: Vec4(1.0),
hl_col: Vec4(1.0),
@ -639,19 +646,19 @@ Panel(Ctx* ctx, UIPanel* panel)
Scissor(ctx, rects[1], &style);
text_rect = Pad(rects[1], PAD);
text_rect.p0.y += offset;
I64Vec2 cursor_pos = VecPos(&panel.ed.buf);
I64Vec2 cursor_coords = VecPos(&panel.ed.buf);
LineBuffer* current_lbuf = GetLine(&panel.ed.buf, cursor_coords.y);
UIItem* cursor = MakeItem("###cursor_%s", panel.id);
if(ctx.focused_panel == panel)
{
i64 y_pos = cursor_pos.y - panel.ed.line_offset;
i64 y_pos = cursor_coords.y - panel.ed.line_offset;
cursor.size.x = Active(ES.InputMode) ? 2.0 : fg.abuf.atlas.max_advance;
cursor.size.y = lheight;
cursor.target_pos.y = text_rect.p0.y + (y_pos*lheight);
cursor.target_pos.x = text_rect.p0.x + (cursor_pos.x*fg.abuf.atlas.max_advance);
cursor.target_pos.x = text_rect.p0.x + CalcTextWidth(current_lbuf.text[0 .. cursor_coords.x], &fg.abuf);
AnimatePos(cursor);
@ -663,6 +670,8 @@ Panel(Ctx* ctx, UIPanel* panel)
DrawRect(ctx, cursor.rect, &CURSOR_STYLE);
}
text_rect.p0.y += offset;
u64 i = start_ln;
if(ctx.focused_panel == panel)
@ -671,18 +680,18 @@ Panel(Ctx* ctx, UIPanel* panel)
{
for(LineBuffer* lbuf = GetLine(&panel.ed.buf, i); i < max_ln && !CheckNil(g_NIL_LINE_BUF, lbuf); i += 1, lbuf = GetLine(&panel.ed.buf, i))
{
if(i == cursor_pos.y)
if(i == cursor_coords.y)
{
string str = Str(lbuf.text[0 .. cursor_pos.x]);
u8[] tks = cast(u8[])lbuf.style[0 .. cursor_pos.x];
string str = Str(lbuf.text[0 .. cursor_coords.x]);
u8[] tks = cast(u8[])lbuf.style[0 .. cursor_coords.x];
DrawText(ctx, str, tks, fg, text_rect, TA.Left);
Rect split_rect = text_rect;
split_rect.p0.x = cursor.p0.x;
str = Str(lbuf.text[cursor_pos.x .. $]);
tks = cast(u8[])lbuf.style[cursor_pos.x .. $];
str = Str(lbuf.text[cursor_coords.x .. $]);
tks = cast(u8[])lbuf.style[cursor_coords.x .. $];
DrawText(ctx, str, tks, fg, split_rect, TA.Left);
}
@ -698,7 +707,7 @@ Panel(Ctx* ctx, UIPanel* panel)
{
for(LineBuffer* lbuf = GetLine(&panel.ed.buf, i); i < max_ln && !CheckNil(g_NIL_LINE_BUF, lbuf); i += 1, lbuf = GetLine(&panel.ed.buf, i))
{
DrawText(ctx, Str(lbuf.text), cast(u8[])lbuf.style, fg, text_rect, TA.Left, cursor_pos.y == i ? cursor_pos.x : -1);
DrawText(ctx, Str(lbuf.text), cast(u8[])lbuf.style, fg, text_rect, TA.Left, cursor_coords.y == i ? cursor_coords.x : -1);
text_rect.p0.y += lheight;
}
}
@ -754,65 +763,94 @@ Panel(Ctx* ctx, UIPanel* panel)
}
void
CommandPalette(Ctx* ctx)
SearchInputWindow(T)(string hash_text, TextInputBuffer* text_input, T[] options, bool ready_cond, i64* selected, bool scroll_to_selected) if(is(T == Command) || is(T == string))
{
Ctx* ctx = GetCtx();
Vec2 extent = GetExtent();
CmdPalette* cmd = &ctx.cmd;
f32 x = extent.x * 0.1;
f32 y = extent.y * 0.2;
f32 w = extent.x * 0.8;
f32 h = extent.y * 0.6;
UIItem* cmd_item = MakeItem("###cmd_palette");
SetReady(cmd_item, ctx.state == ES.CmdPalette);
if(cmd_item.ready_t > 0.0)
UIItem* window_item = MakeItem(hash_text);
SetReady(window_item, ready_cond);
if(window_item.ready_t > 0.0)
{
const f32 PAD = 2.0f;
FontGlyphs* fg = GetFontGlyphs(14);
FontGlyphs* sub_fg = GetFontGlyphs(12);
f32 lheight = fg.abuf.atlas.line_height;
static if(is(T == Command))
{
FontGlyphs* sub_fg = GetFontGlyphs(12);
f32 sub_lheight = sub_fg.abuf.atlas.line_height;
}
cmd_item.p0 = Vec2(x, y);
cmd_item.p1 = Vec2(x+w, y+h);
cmd_item.size = Vec2(w, h);
window_item.p0 = Vec2(x, y);
window_item.p1 = Vec2(x+w, y+h);
window_item.size = Vec2(w, h);
Style style = CMD_STYLE;
Rect[2] rects = Split(cmd_item.rect, lheight+PAD*4.0, A2D.Y);
Rect[2] rects = Split(window_item.rect, lheight+PAD*4.0, A2D.Y);
style.corner_radius.zw = 0.0;
DrawRect(ctx, rects[0], &style, 0.0, cmd_item.ready_t);
DrawBorder(ctx, rects[0], &style, 0.0, cmd_item.ready_t);
DrawRect(ctx, rects[0], &style, 0.0, window_item.ready_t);
DrawBorder(ctx, rects[0], &style, 0.0, window_item.ready_t);
Rect text_input_rect = rects[0];
text_input_rect.p0.y += PAD*2.0f;
text_input_rect.p0.x += PAD*4.0f;
string input_str = cmd.icount ? Str(cmd.buffer[0 .. cmd.icount]) : "Type to search...";
Vec4 input_col = cmd.icount ? WHITE : GREY;
DrawText(ctx, input_str, input_col, fg, text_input_rect, TA.Left, -1, cmd_item.ready_t);
string input_str = text_input.length ? Str(text_input.data[0 .. text_input.length]) : "Type to search...";
Vec4 input_col = text_input.length ? WHITE : GREY;
DrawText(ctx, input_str, input_col, fg, text_input_rect, TA.Left, -1, window_item.ready_t);
style.corner_radius.zw = CORNER_RADIUS;
style.corner_radius.xy = 0.0;
DrawRect(ctx, rects[1], &style, 0.0, cmd_item.ready_t);
DrawRect(ctx, rects[1], &style, 0.0, window_item.ready_t);
Rect border_rect = rects[1];
Scissor(ctx, rects[1], &CMD_STYLE);
rects[1].p0 += style.border_thickness;
rects[1].p1 -= style.border_thickness;
Rect opt_rect = rects[1];
foreach(i; 0 .. CMD_LIST.length)
f32 full_opt_height = lheight+PAD*3.0f;
static if(is(T == Command))
{
if(CMD_LIST[i].hidden) continue;
full_opt_height += sub_lheight;
}
Rect[2] opt_rects = Split(opt_rect, lheight+sub_lheight+PAD*3.0f, A2D.Y);
f32 opt_section_height = rects[1].p1.y-rects[1].p0.y;
i64 start_opt_index = cast(i64)floor(window_item.scroll_offset.y/full_opt_height);
f32 opt_start_offset = window_item.scroll_offset.y%full_opt_height;
if(scroll_to_selected)
{
f32 opt_start_pos = (*selected) * full_opt_height;
f32 opt_end_pos = opt_start_pos + full_opt_height;
if(window_item.scroll_target.y > opt_start_pos)
{
window_item.scroll_target.y = opt_start_pos;
}
else if(window_item.scroll_target.y+opt_section_height < opt_end_pos)
{
window_item.scroll_target.y = opt_end_pos-opt_section_height;
}
}
Rect opt_rect = rects[1];
opt_rect.p0.y -= opt_start_offset;
UIItem* selected_opt_item = g_UI_NIL;
if(start_opt_index < options.length) foreach(i; start_opt_index .. options.length)
{
Rect[2] opt_rects = Split(opt_rect, full_opt_height, A2D.Y);
if(opt_rects[0].p0.y == opt_rects[0].p1.y)
{
break;
@ -821,32 +859,204 @@ CommandPalette(Ctx* ctx)
UIItem* opt_item = MakeItem!(UIF.Click|UIF.Priority)("###opt_%s", i);
f32 opt_height = opt_rects[0].p1.y-opt_rects[1].p0.y;
opt_item.size = Vec2(cmd_item.size.x, opt_height);
opt_item.size = Vec2(window_item.size.x, opt_height);
opt_item.rect = opt_rects[0];
bool hovered = Hovered!(true)(opt_item, &cmd.selected, i) || cmd.selected == i;
bool hovered = Hovered!(true)(opt_item, selected, i) || *selected == i;
SetHot(opt_item, hovered);
DrawRect(ctx, opt_rects[0], &CMD_OPT_STYLES[i%2], opt_item.hot_t, cmd_item.ready_t);
DrawRect(ctx, opt_rects[0], &CMD_OPT_STYLES[i%2], opt_item.hot_t, window_item.ready_t);
if(opt_rects[0].p1.y - opt_rects[0].p0.y > PAD)
{
opt_rects[0].p0.y += PAD;
opt_rects[0].p0.x += PAD*2.0f;
DrawText(ctx, CMD_LIST[i].name, WHITE, fg, opt_rects[0], TA.Left, -1, cmd_item.ready_t);
static if(is(T == Command))
{
DrawText(ctx, options[i].name, WHITE, fg, opt_rects[0], TA.Left, -1, window_item.ready_t);
opt_rects[0].p0.y += lheight+PAD;
DrawText(ctx, CMD_LIST[i].desc, GREY, sub_fg, opt_rects[0], TA.Left, -1, cmd_item.ready_t);
DrawText(ctx, options[i].desc, GREY, sub_fg, opt_rects[0], TA.Left, -1, window_item.ready_t);
}
static if(is(T == string))
{
DrawText(ctx, options[i], WHITE, fg, opt_rects[0], TA.Left, -1, window_item.ready_t);
}
}
opt_rect = opt_rects[1];
}
DrawBorder(ctx, border_rect, &style, 0.0, cmd_item.ready_t);
f32 total_cmd_height = options.length*full_opt_height;
f32 scrollable_height = clamp(total_cmd_height-opt_section_height, 0.0, f32.max);
HandleCmdMode(ctx);
window_item.scroll_target.y = clamp(window_item.scroll_target.y, 0.0, scrollable_height);
if(options.length && total_cmd_height > opt_section_height)
{
UIItem* scrollbar_item = MakeItem!(UIF.Drag|UIF.Priority|UIF.TargetLeniency)("%s_scroll", hash_text);
f32 scrollbar_pct = opt_section_height/total_cmd_height;
f32 scrollbar_height = opt_section_height*scrollbar_pct;
f32 offset_pct = Remap(window_item.scroll_target.y, 0.0, scrollable_height, 0.0, 1.0);
f32 scrollable_range = opt_section_height-scrollbar_height;
f32 scrollbar_offset = offset_pct*scrollable_range;
f32 inner_width = rects[1].p1.x-rects[1].p0.x;
Rect[2] scrollbar_rects = Split(rects[1], inner_width-10.0f, A2D.X);
scrollbar_item.rect = scrollbar_rects[1];
scrollbar_item.rect.p0.y += scrollbar_offset;
scrollbar_item.rect.p1.y = scrollbar_item.rect.p0.y+scrollbar_height;
scrollbar_item.rect.p1.x -= 2.0f;
i32 scrolled;
for(UIInput* i = ctx.events.first; !Nil(i) && ready_cond; i = i.next)
{
if(i.type == UIE.Scroll)
{
scrolled += i.scroll;
DLLRemove(&ctx.events, i, g_UI_NIL_INPUT);
}
}
IVec2 drag;
if(Dragged(ctx, scrollbar_item, &drag) || scrolled != 0)
{
drag.y -= scrolled*40;
f32 mov_pct = Remap(drag.y, 0.0, scrollable_range, 0.0, 1.0);
offset_pct -= mov_pct;
offset_pct = clamp(offset_pct, 0.0, 1.0);
window_item.scroll_target.y = scrollable_height * offset_pct;
}
DrawRect(ctx, scrollbar_item.rect, &SCROLLBAR_STYLE, 0.0f, window_item.ready_t);
}
EndScissor(ctx);
DrawBorder(ctx, border_rect, &style, 0.0, window_item.ready_t);
}
else
{
window_item.scroll_target.y = 0.0;
}
}
bool
OpenFileWindow(Ctx* ctx)
{
bool exit;
CmdPalette* cmd = &ctx.cmd;
if(!cmd.exec_cmd_opts.length && !cmd.exec_cmd_input.length)
{
cmd.exec_cmd_opts = ctx.file_names[0 .. $];
}
bool enter_hit, buffer_changed, selection_changed;
if(ctx.state == ES.RunCmd)
{
HandleCmdMode(ctx, &enter_hit, &buffer_changed, &selection_changed);
if(buffer_changed)
{
if(cmd.exec_cmd_input.length)
{
Reset(&cmd.exec_cmd_arena);
cmd.exec_cmd_opts = Alloc!(string)(ctx.file_names.length);
u64 option_count;
string buffer_text = Str(cmd.exec_cmd_input);
foreach(i; 0 .. ctx.file_names.length)
{
if(StrContains!(false)(ctx.file_names[i], buffer_text))
{
cmd.exec_cmd_opts[option_count++] = ctx.file_names[i];
}
}
cmd.exec_cmd_opts = cmd.exec_cmd_opts[0 .. option_count];
}
else
{
cmd.exec_cmd_opts = ctx.file_names[0 .. $];
}
}
if(buffer_changed || selection_changed)
{
cmd.exec_cmd_selected = clamp(cmd.exec_cmd_selected, 0, cmd.exec_cmd_opts.length ? cmd.exec_cmd_opts.length-1 : 0);
}
if(cmd.exec_cmd_opts.length)
{
string file_name = cmd.exec_cmd_opts[cmd.exec_cmd_selected];
if(enter_hit && file_name.length)
{
OpenFile(ctx.focused_panel.ed, file_name);
exit = true;
}
}
}
SearchInputWindow(
"###open_file",
&cmd.exec_cmd_input,
cmd.exec_cmd_opts,
ctx.state == ES.RunCmd,
&cmd.exec_cmd_selected,
selection_changed
);
return exit;
}
void
CommandPalette(Ctx* ctx)
{
CmdPalette* cmd = &ctx.cmd;
bool enter_hit, buffer_changed, selection_changed;
if(ctx.state == ES.CmdPalette)
{
HandleCmdMode(ctx, &enter_hit, &buffer_changed, &selection_changed);
if(buffer_changed)
{
if(cmd.text_input.length)
{
u64 list_count;
string buffer_text = Str(cmd.text_input);
static foreach(i; 0 .. CMD_LIST.length)
{
if(StrContains!(false)(CMD_LIST[i].cmd, buffer_text) || StrContains!(false)(CMD_LIST[i].name, buffer_text))
{
cmd.cmd_arr[list_count++] = CMD_LIST[i];
}
}
cmd.cmds = cmd.cmd_arr[0 .. list_count];
}
else
{
cmd.cmds = CMD_LIST[0 .. $];
}
}
if(buffer_changed || selection_changed)
{
cmd.selected = clamp(cmd.selected, 0, cmd.cmds.length ? cmd.cmds.length-1 : 0);
}
}
SearchInputWindow(
"###cmd_palette",
&cmd.text_input,
cmd.cmds,
ctx.state == ES.CmdPalette,
&cmd.selected,
selection_changed
);
}
void
@ -866,7 +1076,7 @@ AddPanel(UIPanel* target, Axis2D axis)
first.parent = second.parent = target;
first.pct = second.pct = 0.5;
g_ctx.focus_key = second.key;
Focus(second);
}
else
{
@ -892,7 +1102,7 @@ AddPanel(UIPanel* target, Axis2D axis)
panel.pct = new_pct;
g_ctx.focus_key = panel.key;
Focus(panel);
}
}
@ -1096,7 +1306,9 @@ BeginUI(Inputs* inputs)
ctx.events.first = ctx.events.last = null;
bool mouse_moved;
static f32 mouse_down_elapsed = 0.0;
static bool dragging;
static i32 final_y;
static bool mouse_down;
for(InputEvent* i = inputs.first; i; i = i.next)
{
@ -1105,26 +1317,35 @@ BeginUI(Inputs* inputs)
case Input.LeftClick:
{
mouse_down = i.pressed;
if(!mouse_down)
if(mouse_down)
{
PushUIEvent(ctx, UIInput(dragging ? UIE.DragRelease : UIE.Click));
PushUIEvent(ctx, UIInput(UIE.Click));
}
else
{
if(dragging)
{
PushUIEvent(ctx, UIInput(UIE.DragRelease));
final_y = 0;
}
mouse_down_elapsed = 0.0;
dragging = false;
}
} break;
case Input.MouseMotion:
{
mouse_moved = true;
ctx.mouse_pos = IVec2(i.x, i.y);
if(!dragging && mouse_down)
if(!dragging && mouse_down_elapsed > 0.06f)
{
PushUIEvent(ctx, UIInput(type: UIE.DragStart, pos: IVec2(i.x, i.y)));
dragging = true;
}
dragging = mouse_down;
if(dragging)
{
final_y += i.rel_y;
PushUIEvent(ctx, UIInput(type: UIE.Drag, rel: IVec2(i.rel_x, i.rel_y)));
}
} break;
@ -1145,6 +1366,11 @@ BeginUI(Inputs* inputs)
}
}
if(mouse_down)
{
mouse_down_elapsed += g_delta;
}
u64 next_frame = ctx.frame+1;
// Check current mouse target
@ -1435,19 +1661,19 @@ DrawBorder(Ctx* ctx, Rect rect, Style* style, f32 hot_t = 0.0, f32 ready_t = 1.0
}
void
DrawRect(Ctx* ctx, Rect rect, Style* style, f32 hot_t = 0.0, f32 ready_t = 1.0)
DrawRect(Ctx* ctx, Rect rect, Style* style, f32 hot_t = 0.0f, f32 ready_t = 1.0f)
{
Vec4 col = style.col;
AnimateHot(hot_t, style.hl_col, &col);
AnimateReady(ready_t, &col);
bool bordered = style.border_thickness > 0.0009;
bool bordered = style.border_thickness > 0.0009f;
Vertex* v = GetVertex(ctx);
v.dst_start = rect.p0 + style.border_thickness;
v.dst_end = rect.p1 - style.border_thickness;
v.cols = col;
v.corner_radius = style.corner_radius*(cast(f32)(bordered)*0.5);
v.corner_radius = style.corner_radius * (bordered ? 0.5f : 1.0f);
v.edge_softness = style.edge_softness;
Clamp(ctx, v);
@ -1707,7 +1933,16 @@ NewItem(Ctx* ctx)
UIItem*
MakeItem(UIFlags flags = UIF.None, Args...)(string str, Args args)
{
char[] key = sformat(Alloc!(char)(&g_ctx.str_arenas[g_ctx.f_idx], cast(u64)(str.length*1.5)), str, args);
u64 len = cast(u64)(str.length*1.5);
static foreach(i; 0 .. Args.length)
{
static if(StringType!(Args[i]))
{
len += args[i].length;
}
}
char[] key = sformat(Alloc!(char)(&g_ctx.str_arenas[g_ctx.f_idx], len), str, args);
return MakeItem(key, flags);
}
@ -1861,7 +2096,7 @@ DrawGlyph(Rect rect, Glyph* glyph, f32* x_pos, f32 y, f32 line_height, Vec4 col
}
else if(glyph.ch == '\t')
{
*x_pos += advance * (ctx.tab_width - 1);
*x_pos += advance*ctx.tab_width;
}
else
{