diff --git a/src/editor/editor.d b/src/editor/editor.d index adcfe24..97854aa 100644 --- a/src/editor/editor.d +++ b/src/editor/editor.d @@ -43,6 +43,7 @@ struct Ctx Arena arena; Arena temp_arena; + Arena[FO] str_arenas; Inputs* inputs; u64 frame; @@ -88,6 +89,8 @@ struct Ctx string[] file_names; u64 panel_id; + debug bool dbg; + alias rd_ctx this; } @@ -98,6 +101,7 @@ struct CmdPalette u8[] buffer; u32 icount; i64 selected; + u64 visible_opts; Command current; Parameter[] params; } @@ -144,6 +148,7 @@ struct Command string desc; string cmd; CmdType type; + bool hidden; union { CmdFn fn; @@ -736,11 +741,18 @@ 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 = []; - taken = true; + 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; + } + + taken = true; } } break; case v: @@ -766,6 +778,16 @@ HandleInputs(UIPanel* p, LinkedList!(UIInput)* inputs, bool hovered, bool focuse taken = true; } } break; + case g: + { + debug + { + if(Shift(md)) + { + ctx.dbg = !ctx.dbg; + } + } + } break; default: { taken = MoveCursor(ed, node); @@ -923,12 +945,6 @@ Lower(u8 ch) return ch; } -bool -StrContains(bool begins_with)(u8[] str, u8[] match) -{ - return StrContains!(begins_with)(Str(str), match); -} - bool StrContains(bool begins_with, T, U)(T str_param, U match_param) if(StringType!(T) && StringType!(U)) { @@ -950,6 +966,12 @@ StrContains(bool begins_with, T, U)(T str_param, U match_param) if(StringType!(T string match = match_param; } + return StrContains!(begins_with)(str, match); +} + +bool +StrContains(bool begins_with)(string str, string match) +{ u64 count; for(u64 i = 0; i < str.length; i += 1) { @@ -983,86 +1005,135 @@ HandleCmdMode(Ctx* ctx) Editor* ed = panel.ed; CmdPalette* cmd = &ctx.cmd; - for(InputEvent* ev = ctx.inputs.first; ev && ctx.state == ES.CmdPalette; ev = ev.next) + bool buffer_changed; + for(UIInput* ev = ctx.events.first; ev && ctx.state == ES.CmdPalette; ev = ev.next) { bool taken; - if(!ev.pressed) continue; - switch(ev.key) with(Input) + switch(ev.type) { - case Enter: + case UIE.Press: { - if(cmd.current.type != CT.None) + switch(ev.key) with(Input) { - cmd.current = CMD_LIST[cmd.selected]; + case Enter: + { + if(cmd.current.type != CT.None) + { + cmd.current = CMD_LIST[cmd.selected]; - switch(cmd.current.type) with(CmdType) - { - case Config: + switch(cmd.current.type) with(CmdType) + { + case Config: + { + // TODO: implement + } break; + case Hotkey: + { + // TODO: implement + } break; + case Callback: + { + assert(cmd.current.fn, "Callback type doesn't have a function pointer set"); + ctx.state = ES.RunCmd; + } break; + default: break; + } + } + } break; + case Backspace: + { + if(cmd.icount > 0) { - // TODO: implement - } break; - case Hotkey: + cmd.icount -= 1; + if(cmd.icount == cmd.current.name.length) + { + cmd.current = cast(Command)NO_CMD; + } + + 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) { - // TODO: implement - } break; - case Callback: + if(i < 0 || i >= CMD_LIST.length) + { + break; + } + + if(!CMD_LIST[i].hidden) + { + selected = i; + break; + } + } + + cmd.selected = selected; + } break; + case Escape: + { + ResetCtx(); + } break; + default: + { + if(ev.text.length) { - assert(cmd.current.fn, "Callback type doesn't have a function pointer set"); - ctx.state = ES.RunCmd; - } break; - default: break; - } - } - } break; - 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.current.type == CT.None) - { - cmd.current = CMD_LIST[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; - cmd.selected = clamp(cmd.selected+incr, 0, CMD_LIST.length-1); - } break; - case Escape: - { - ResetCtx(); - } 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; + Check(cmd, ev.text.length); + cmd.buffer[cmd.icount .. cmd.icount+ev.text.length] = cast(u8[])ev.text[0 .. $]; + cmd.icount += ev.text.length; + + buffer_changed = true; + } + } break; } } break; + default: break; } if(taken) { - DLLRemove(ctx.inputs, ev, null); + 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; } } } diff --git a/src/editor/ui.d b/src/editor/ui.d index b4f1e07..a7ba0cf 100644 --- a/src/editor/ui.d +++ b/src/editor/ui.d @@ -96,7 +96,7 @@ __gshared Style CMD_STYLE = { border_col: CMD_BORDER_COL, border_hl_col: BORDER_HL_COL, corner_radius: Vec4(CORNER_RADIUS), - border_thickness: 1.0, + border_thickness: 1.5, edge_softness: 1.0, }; @@ -405,6 +405,7 @@ InitUI(Ctx* ctx) ctx.buffers[i].idx = ctx.buffers[i].m_idx.data; ctx.buffers[i].idx[0 .. $] = [0, 1, 2, 2, 1, 3]; ctx.desc_sets[i] = AllocDescSet(&ctx.rd, ctx.desc_set_layout); + ctx.str_arenas[i] = CreateArena(MB(1)); } GfxPipelineInfo ui_info = { @@ -581,9 +582,16 @@ Panel(Ctx* ctx, UIPanel* panel) FontAtlasBuf* abuf = &fg.abuf; // Inputs - bool hovered = ctx.hover_key == panel.key; - bool focused = ctx.focus_key == panel.key && ctx.focused_panel == panel; - panel.move_text_with_cursor |= HandleInputs(panel, &ctx.events, hovered, focused); + if(ctx.state == ES.NormalMode || ctx.state == ES.InputMode) + { + bool hovered = ctx.hover_key == panel.key; + bool focused = ctx.focus_key == panel.key && ctx.focused_panel == panel; + panel.move_text_with_cursor |= HandleInputs(panel, &ctx.events, hovered, focused); + } + else + { + panel.move_text_with_cursor = false; + } f32 lheight = abuf.atlas.line_height; panel.scroll_target.y = cast(f32)(panel.ed.line_offset)*lheight; @@ -615,7 +623,7 @@ Panel(Ctx* ctx, UIPanel* panel) for(u64 i = start_ln; i < max_ln; i += 1) { - string line_num = Scratchf("%s", i+1); + string line_num = EdScratchf("%s", i+1); DrawText(ctx, line_num, Vec4(1.0), fg, text_rect, TA.Right); text_rect.p0.y += lheight; } @@ -751,9 +759,9 @@ CommandPalette(Ctx* ctx) Vec2 extent = GetExtent(); CmdPalette* cmd = &ctx.cmd; - f32 x = extent.x * 0.3; + f32 x = extent.x * 0.1; f32 y = extent.y * 0.2; - f32 w = extent.x * 0.4; + f32 w = extent.x * 0.8; f32 h = extent.y * 0.6; UIItem* cmd_item = MakeItem("###cmd_palette"); @@ -774,31 +782,42 @@ CommandPalette(Ctx* ctx) Style style = CMD_STYLE; - Rect[2] rects = Split(cmd_item.rect, lheight+PAD*2.0, A2D.Y); + Rect[2] rects = Split(cmd_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); - style.corner_radius.zw = 1.0; + 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); + + style.corner_radius.zw = CORNER_RADIUS; style.corner_radius.xy = 0.0; DrawRect(ctx, rects[1], &style, 0.0, cmd_item.ready_t); + Rect border_rect = rects[1]; + rects[1].p0 += style.border_thickness; rects[1].p1 -= style.border_thickness; - Rect opt_rect = rects[1]; + Rect opt_rect = rects[1]; foreach(i; 0 .. CMD_LIST.length) { + if(CMD_LIST[i].hidden) continue; + Rect[2] opt_rects = Split(opt_rect, lheight+sub_lheight+PAD*3.0f, A2D.Y); if(opt_rects[0].p0.y == opt_rects[0].p1.y) { break; } - UIItem* opt_item = MakeItem!(UIF.Click|UIF.Priority)("###opt_%s", i); f32 opt_height = opt_rects[0].p1.y-opt_rects[1].p0.y; @@ -812,7 +831,8 @@ CommandPalette(Ctx* ctx) if(opt_rects[0].p1.y - opt_rects[0].p0.y > PAD) { - opt_rects[0].p0 += 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); opt_rects[0].p0.y += lheight+PAD; @@ -823,7 +843,7 @@ CommandPalette(Ctx* ctx) opt_rect = opt_rects[1]; } - DrawBorder(ctx, rects[1], &style, 0.0, cmd_item.ready_t); + DrawBorder(ctx, border_rect, &style, 0.0, cmd_item.ready_t); HandleCmdMode(ctx); } @@ -1068,7 +1088,7 @@ void BeginUI(Inputs* inputs) { Ctx* ctx = GetCtx(); - Arena* a = &ctx.temp_arena; + Arena* a = &ctx.temp_arena; Reset(a); @@ -1146,8 +1166,6 @@ BeginUI(Inputs* inputs) { ctx.hover_key = ZeroKey(); } - - Logf("hover key %s", ctx.hover_key.hash_text); } // Clean up items @@ -1170,6 +1188,8 @@ BeginUI(Inputs* inputs) ctx.inputs = inputs; ctx.last_item = g_UI_NIL; + Reset(&ctx.str_arenas[ctx.f_idx]); + assert(!mouse_moved || ZeroKey(ctx.hover_key) || (ctx.frame == ctx.last_hover_frame)); ctx.animation_rate = 1.0 - pow(2.0, (-30.0f * g_delta)); @@ -1250,6 +1270,29 @@ EndUI() { Ctx* ctx = GetCtx(); + if(ctx.dbg) + { + const PAD = 4.0f; + static FontGlyphs* fg; + if(!fg) + { + fg = GetFontGlyphs(12); + } + + string debug_hover_key = Scratchf("Hover Key: %s", ctx.hover_key.hash_text.length ? ctx.hover_key.hash_text : "None"); + + Vec2 ext = GetExtent(); + f32 text_width = CalcTextWidth(debug_hover_key, &fg.abuf); + + Rect debug_rect; + + debug_rect.p0.x = ext.x - text_width - PAD*2.0f; + debug_rect.p0.y = ext.y - fg.abuf.atlas.line_height - PAD*2.0f; + debug_rect.p1 = ext; + + DrawText(ctx, debug_hover_key, WHITE, fg, debug_rect, TA.Left); + } + version(ENABLE_RENDERER) { DrawUI(ctx); @@ -1664,7 +1707,7 @@ NewItem(Ctx* ctx) UIItem* MakeItem(UIFlags flags = UIF.None, Args...)(string str, Args args) { - char[] key = sformat(ScratchAlloc!(char)(cast(u64)(str.length*1.5)), str, args); + char[] key = sformat(Alloc!(char)(&g_ctx.str_arenas[g_ctx.f_idx], cast(u64)(str.length*1.5)), str, args); return MakeItem(key, flags); } @@ -2004,6 +2047,13 @@ OklabToSRGB(Vec4 v) ); } +string +EdScratchf(Args...)(string fmt, Args args) +{ + char[] buf = Alloc!(char)(&g_ctx.str_arenas[g_ctx.f_idx], fmt.length < 16 ? 32 : 128); + return Str(sformat(buf, fmt, args)); +} + unittest { { // UI Key