diff --git a/src/editor/buffer.d b/src/editor/buffer.d index bc0e93d..634b288 100644 --- a/src/editor/buffer.d +++ b/src/editor/buffer.d @@ -15,7 +15,7 @@ struct FlatBuffer LineBuffers linebufs; BufferInfo info; - u8[] file_name; + string file_name; u8[] data; u64 length; @@ -116,7 +116,7 @@ const u64 SPACING = 2; const u64 BUF_START = 1; FlatBuffer -CreateFlatBuffer(u8[] data, u8[] file_name) +CreateFlatBuffer(u8[] data, string file_name) { FlatBuffer fb = { arena: CreateArena(MB(1)), @@ -133,7 +133,7 @@ CreateFlatBuffer(u8[] data, u8[] file_name) } 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); @@ -158,7 +158,7 @@ SetBuffers(FlatBuffer* fb, u8[] data) } void -Change(FlatBuffer* fb, u8[] data, u8[] file_name) +Change(FlatBuffer* fb, u8[] data, string file_name) { Free(fb.data); Reset(&fb.linebufs.arena); diff --git a/src/editor/editor.d b/src/editor/editor.d index bc04dd8..d263956 100644 --- a/src/editor/editor.d +++ b/src/editor/editor.d @@ -17,8 +17,6 @@ import std.string; import core.stdc.stdio; f32 g_delta = 0.0; -debug bool g_frame_step = false; -debug bool g_frame_continue = false; struct EditorCtx { @@ -31,7 +29,7 @@ struct EditorCtx u64 editor_id_incr; Timer timer; CmdPalette cmd; - u8[][] file_names; + string[] file_names; u64 panel_id; Editor* focused_editor; @@ -44,7 +42,7 @@ struct CmdPalette u8[] buffer; u32 icount; Command[] commands; - u8[][] opt_strs; + string[] opt_strs; i64 selected; Command current; Parameter[] params; @@ -83,14 +81,16 @@ struct EditorChange struct Command { - u8[] name; + string name; + string desc; + string cmd; CmdType type; } struct Parameter { - u8[] value; - bool visible; + string value; + bool visible; } enum CmdType @@ -123,6 +123,45 @@ const Command NO_CMD = { 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 Cycle(Inputs* inputs) { @@ -130,18 +169,7 @@ Cycle(Inputs* inputs) g_delta = DeltaTime(&g_ed_ctx.timer); - debug if(g_frame_step) - { - 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; + g_input_mode = Active(ES.InputMode); BeginUI(inputs); @@ -162,11 +190,7 @@ Cycle(Inputs* inputs) } UIItem* container = Container(A2D.Y, UIS2()); - Push!("parent")(container); - UIItem* ed_container = Container(A2D.X, UISY(ST.Percentage, 0.98)); - - Push!("parent")(ed_container); EditorView(g_ed_ctx.base_panel); Pop!("parent"); @@ -174,9 +198,12 @@ Cycle(Inputs* inputs) 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(); @@ -213,7 +240,7 @@ InitEditorCtx(PlatformWindow* window) count += 1; } - ctx.file_names = Alloc!(u8[])(&ctx.arena, count); + ctx.file_names = Alloc!(string)(&ctx.arena, count); count = 0; foreach(DirEntry e; dirEntries(".", SpanMode.breadth)) @@ -221,7 +248,7 @@ InitEditorCtx(PlatformWindow* window) if(indexOf(e.name, ".git") != -1 || e.isDir) continue; 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) @@ -260,7 +287,7 @@ EditModeActive() } char[] -ToAbsolutePath(u8[] file_name) +ToAbsolutePath(string file_name) { import core.stdc.string : strlen; @@ -294,7 +321,7 @@ ToAbsolutePath(u8[] file_name) 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); @@ -304,11 +331,11 @@ name_buf.sformat("%s/%s", wd, cast(char[])file_name); } void -SaveFile(Editor* ed, u8[] file_name) +SaveFile(Editor* ed, string file_name) { 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) { @@ -353,7 +380,7 @@ SaveFile(Editor* ed, u8[] file_name) } void -OpenFile(Editor* ed, u8[] file_name) +OpenFile(Editor* ed, string file_name) { import core.stdc.stdio; import std.file; @@ -590,8 +617,11 @@ HandleInputs(Panel* p, LinkedList!(UIInput)* inputs) { if(Shift(node.md)) { - ctx.state = ES.CmdOpen; - taken = true; + ctx.state = ES.CmdOpen; + ctx.cmd.commands = cast(Command[])CMD_LIST; + ctx.cmd.icount = 0; + ctx.cmd.params = []; + taken = true; } } break; case v: @@ -624,18 +654,6 @@ HandleInputs(Panel* p, LinkedList!(UIInput)* inputs) ctx.state = ES.SetPanelFocus; } } 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; } } @@ -746,14 +764,32 @@ Lower(u8 ch) } 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 -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; for(u64 i = 0; i < str.length; i += 1) { @@ -783,62 +819,26 @@ StrContains(bool begins_with)(u8[] str, u8[] match) void 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); - 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) { - 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; } } } - if(count == 0 && cmd.icount == 0) - { - 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; - } + cmd.commands = count ? cmd.commands[0 .. count] : cast(Command[])CMD_LIST; } bool @@ -904,7 +904,7 @@ HandleCmdMode(EditorCtx* ctx, Panel* panel, UIInput* ev) if(cmd.commands.length > 0) { 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.buffer[cmd.icount++] = ' '; } @@ -923,7 +923,7 @@ HandleCmdMode(EditorCtx* ctx, Panel* panel, UIInput* ev) } 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); } @@ -937,7 +937,7 @@ HandleCmdMode(EditorCtx* ctx, Panel* panel, UIInput* ev) } break; case SaveFile: { - u8[] param = GetParam(cmd); + string param = GetParam(cmd); } break; default: break; } @@ -957,15 +957,15 @@ Check(CmdPalette* cmd, u64 length) } } -u8[] +string GetParam(CmdPalette* cmd) { - u8[] param = []; + string param; for(u64 i = cmd.current.name.length; i < cmd.icount; i += 1) { if(cmd.buffer[i] != ' ') { - param = cmd.buffer[i .. cmd.icount]; + param = Str(cmd.buffer[i .. cmd.icount]); break; } } @@ -974,13 +974,13 @@ GetParam(CmdPalette* cmd) } 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) { 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) { @@ -992,7 +992,7 @@ PopulateParams(CmdPalette* cmd, u8[][] strs) 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; for(u64 i = 0; i < cmd.params.length; i += 1) diff --git a/src/editor/ui.d b/src/editor/ui.d index 032fafb..15335a3 100644 --- a/src/editor/ui.d +++ b/src/editor/ui.d @@ -1,5 +1,6 @@ import dlib; import dlib.util : HTPush = Push; +import dlib.math : Clamp; import vulkan; import vulkan : RendererGetExtent = GetExtent; @@ -130,9 +131,11 @@ enum UIFlags : u64 VerticalAlignText = 1<<24, SetHot = 1<<25, SetReady = 1<<26, - AnimateReady = 1<<27, // Fade in/out - AnimateHot = 1<<28, // Hover highlight - AnimateActive = 1<<29, // Animate selected (probably do later) + SetActive = 1<<27, + AnimateReady = 1<<28, // Fade in/out + AnimateHot = 1<<29, // Hover highlight + AnimateActive = 1<<30, // Animate selected (probably do later) + DrawDropShadow = 1<<31, Clamp = UIFlags.ClampX | UIFlags.ClampY, PortalView = UIFlags.PortalViewX | UIFlags.PortalViewY, @@ -142,7 +145,7 @@ enum UIFlags : u64 alias UIF = UIFlags; 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 { @@ -229,6 +232,9 @@ CtxMemberInfo(int i) struct UICtx { + enum FO = FRAME_OVERLAP; + enum FS = FONT_SIZES; + HashTable!(UIHash, UIItem*) items; UIItem* free_items; UIItem* transient_items; @@ -244,21 +250,22 @@ struct UICtx PlatformWindow* window; Renderer rd; - Descriptor[FONT_SIZES] font_descs; + Descriptor[FS][FO] font_descs; Descriptor default_tex; Descriptor sampler; Pipeline pipeline; DescSetLayout desc_set_layout; - DescSet desc_set; + DescSet[FO] desc_sets; PipelineLayout pipeline_layout; Mat4 projection; Vec2 res; FontFace font; - FontGlyphs[FONT_SIZES] glyph_sets; + FontGlyphs[FS] glyph_sets; u32 glyph_sets_used; + bool[FO] glyphs_to_load; - UIBuffer[FRAME_OVERLAP] buffers; + UIBuffer[FO] buffers; u32 tab_width; Vec4[TS.max][UISH.max] syntax_colors; @@ -271,6 +278,7 @@ struct UICtx f32 scroll_rate; f32 animation_rate; + f32 fade_rate; mixin UICtxParameter!(Vec4, "bg_col"); mixin UICtxParameter!(Vec4, "bg_col_end"); @@ -301,9 +309,10 @@ struct UICtx struct FontGlyphs { - u32 size; - u32 index; - FontAtlasBuf abuf; + FontAtlasBuf abuf; + u32 size; + u32 index; + bool[FRAME_OVERLAP] loaded; } struct Stack(T) @@ -417,7 +426,6 @@ struct Vertex Vec4 cols; Vec4 cols_end; Vec4 corner_radius; - Vec2[2] bounds; union { VPos[2] pos; @@ -530,6 +538,14 @@ InitUICtx(PlatformWindow* window) { 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) { 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].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); } - 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 = { vertex_shader: cast(u8[])VERTEX_BYTES, frag_shader: cast(u8[])FRAGMENT_BYTES, @@ -564,25 +571,37 @@ InitUICtx(PlatformWindow* window) 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); - ctx.glyph_sets[i].index = i; + for(u32 j = 0; j < ctx.font_descs[i].length; j += 1) + { + 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); + LoadFontGlyphs(); + CreateGraphicsPipeline(&ctx.rd, &ctx.pipeline, &ui_info); ctx.sampler = CreateSampler(&ctx.rd, MipmapMode.Nearest, 1); 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); - Write(&ctx.rd, ctx.desc_set, &ctx.sampler); + for(u64 i = 0; i < FRAME_OVERLAP; i += 1) + { + 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]); } @@ -617,18 +636,41 @@ GetFontGlyphs(u32 size) assert(ctx.font); - fg.abuf = CreateAtlas(&ctx.arena, ctx.font, cast(f32)size, ATLAS_DIMENSION); - fg.size = size; + fg.abuf = CreateAtlas(&ctx.arena, ctx.font, cast(f32)size, ATLAS_DIMENSION); + fg.size = size; + fg.loaded = false; - Transfer(&ctx.rd, &ctx.font_descs[i].view, fg.abuf.data, ATLAS_DIMENSION, ATLAS_DIMENSION); - Write(&ctx.rd, ctx.desc_set, &ctx.font_descs[i]); + Logf("setting size %s to %s", size, i); + ctx.glyphs_to_load = true; ctx.glyph_sets_used += 1; } 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 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) { - item.ready_t += ctx.animation_rate * (1.0 - item.ready_t); - item.bg_col.a *= item.ready_t; - item.bg_col_end.a *= item.ready_t; - item.text_col.a *= item.ready_t; - item.border_col.a *= item.ready_t; + bool is_ready = cast(bool)(item.flags & UIF.SetReady); + item.ready_t += ctx.fade_rate * (cast(f32)(is_ready) - item.ready_t); + SetColorTransparency(item, item.ready_t); } 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; } +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 Hovered(bool current_frame, T)(T param) { @@ -993,11 +1060,10 @@ BeginUI(Inputs* inputs) { last = last.last; } - + UIKey hovered = ZeroKey(); 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)) { hovered = item.key; @@ -1050,30 +1116,8 @@ BeginUI(Inputs* inputs) 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.fade_rate = 1.0 - pow(2.0, (-50.0f * g_delta)); ctx.scroll_rate = 1.0 - pow(2.0, (-60.0f * g_delta)); // Root Item @@ -1086,6 +1130,40 @@ EndUI() { 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 for(UIItem* item = ctx.root_first; !Nil(item); item = Recurse!(false)(item, g_UI_NIL)) { @@ -1296,6 +1374,28 @@ EndUI() // Render Items 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); } @@ -1326,6 +1426,13 @@ FocusItem(Args...)(string str, Args 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 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.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) { - v = GetVertex(ctx); + Vertex* v = GetVertex(ctx); v.dst_start = item.rect.p0; v.dst_end = item.rect.p1; v.cols = 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.edge_softness = item.edge_softness; - v.bounds = ItemBounds(item.parent); - AddVertexCount(ctx); + Clamp(ctx, v); } if(item.flags & UIF.DrawBorder) { - v = GetVertex(ctx); - v.dst_start = item.rect.p0 - Vec2(InnerOffset!(A2D.X, true )(item), InnerOffset!(A2D.Y, true )(item)); - v.dst_end = item.rect.p1 + Vec2(InnerOffset!(A2D.X, false)(item), InnerOffset!(A2D.Y, false)(item)); + Vertex* v = GetVertex(ctx); + v.dst_start = border_p0; + v.dst_end = border_p1; v.cols = item.border_col; v.cols_end = item.flags & UIF.Gradient ? item.bg_col_end : item.bg_col; v.corner_radius = item.corner_radius; v.border_thickness = item.border_thickness; v.edge_softness = item.edge_softness; - v.bounds = ItemBounds(item.parent); - AddVertexCount(ctx); - } - - 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)); + Clamp(ctx, v); } if(item.flags & UIF.DrawText || item.display_string) @@ -1706,7 +1829,7 @@ ResetStacks(UICtx* ctx) T* 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; 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 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; @@ -1969,7 +2092,6 @@ DrawGlyph(UIItem* item, Glyph* glyph, u32 atlas_index, f32* x_pos, f32 y, f32 li v.cols = col; v.cols_end = col; v.texture = true; - v.bounds = ItemBounds(item); v.atlas_index = index; v.src_start = Vec2(glyph.atlas_left, glyph.atlas_top); 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; } } } -pragma(inline) Vec2[2] -ItemBounds(UIItem* item) -{ - return [item.rect.p0, item.rect.p1]; -} - pragma(inline) f32 AxisPadding(Axis2D axis)(UIItem* item) { @@ -2041,7 +2155,8 @@ InnerOffset(Axis2D axis, bool start)(UIItem* item) pragma(inline) Vertex* 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] @@ -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)]; } -pragma(inline) void -AddVertexCount(UICtx* ctx) -{ - ctx.buffers[ctx.f_idx].count += 1; - assert(ctx.buffers[ctx.f_idx].count < VERTEX_MAX_COUNT); -} - bool Nil(UIItem* item) { diff --git a/src/editor/views.d b/src/editor/views.d index afac79f..4142afb 100644 --- a/src/editor/views.d +++ b/src/editor/views.d @@ -14,14 +14,17 @@ __gshared const Editor g_nil_ed; __gshared Editor* g_NIL_ED; __gshared const UIKey ZERO = ZeroKey(); -Vec4 BG_COL = Vec4(0.18, 0.18, 0.18, 1.0); - -Vec4 CMD_COL = Vec4(0.22, 0.22, 0.22, 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 BLUE = HexCol(0x4EA9FF); -Vec4 RED = HexCol(0xFF3268); -Vec4 YELLOW = HexCol(0xF7C443); +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_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_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 GREY = Vec4(0.45, 0.45, 0.45, 1.0); +Vec4 WHITE = HexCol(0xFFFFFF); +Vec4 BLUE = HexCol(0x4EA9FF); +Vec4 RED = HexCol(0xFF3268); +Vec4 YELLOW = HexCol(0xF7C443); shared static this() { @@ -239,27 +242,33 @@ EditorView(Panel* p) } void -CommandPalette(CmdPalette* cmd) +CommandPalette(CmdPalette* cmd, bool active) { UICtx* ctx = GetCtx(); Vec2 ext = GetExtent(); 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; enum UIPushInfo[] cmd_params = [ { "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 } }, { "border_col", q{ HL_BORDER_COL } }, { "border_thickness", q{ 4.0 } }, { "corner_radius", q{ Vec4(8.0) } }, { "size_info", q{ UIS2(ST.Pixels, ST.Pixels, w, h) } }, ]; - 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; @@ -267,54 +276,105 @@ CommandPalette(CmdPalette* cmd) mixin(PushScope!("padding", q{ Vec2(4.0, padding_y) })); enum UIPushInfo[] cmd_input_params = [ - { "bg_col", q{ HL_BG_COL } }, - { "size_info", q{ UISY(ST.TextSize) } }, - { "display_string", q{ Str(cmd.buffer[0 .. cmd.icount]) } }, + { "bg_col", q{ CMD_COL } }, + { "border_col", q{ CMD_BORDER_COL } }, + { "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)); - UIKey zero = ZeroKey(); - MakeItem(zero, UIF.DrawBorder|UIF.DrawBackground|UIF.DrawText|UIF.Overflow); + MakeItem(ZERO, UIF.DrawBorder|UIF.DrawBackground|UIF.DrawText|UIF.Overflow|UIF.VerticalAlignText); - enum UIPushInfo[] sep_params = [ - { "padding", q{ Vec2(0.0) } }, - { "size_info", q{ UISY(ST.Pixels, 4.0) } }, - { "bg_col", q{ HL_BORDER_COL } }, + // the two sizes seems to fuck this up somehow + u32 title_px = 16; + u32 sub_px = 16; + 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)); - MakeItem(zero, UIF.DrawBackground); + mixin(PushOnce!(cmd_opts_box_params)); - mixin(PushScope!("padding", q{ Vec2(4.0) } )); - mixin(PushScope!("size_info", q{ UISY(ST.TextSize) } )); + UIItem* opt_box = MakeItem(ZERO, UIF.DrawBorder); - 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(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 + // 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); - PushBgCol(opt_cols[i%2], true); - - UIKey k = MakeKey("###copt_%s", i); - if(Hovered!(true)(k)) + for(u64 i = 0; i < cmd.opt_strs.length && i < max_opts; i += 1) { - 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* -Container(Axis2D axis, UISize[2] size_info) +Container(bool push = true)(Axis2D axis, UISize[2] size_info) { enum UIPushInfo[] container_info = [ { "layout_axis", q{ axis } }, @@ -323,7 +383,14 @@ Container(Axis2D axis, UISize[2] size_info) mixin(PushOnce!(container_info)); - return MakeItem(ZeroKey()); + UIItem* item = MakeItem(ZERO); + + static if(push) + { + Push!("parent")(item); + } + + return item; } void