From 988223b67578e3cb5470aa1b380599acffb99bad Mon Sep 17 00:00:00 2001 From: Matthew Date: Mon, 15 Sep 2025 17:44:02 +1000 Subject: [PATCH] ui panels mostly working --- src/VulkanRenderer | 2 +- src/dlib | 2 +- src/editor/buffer.d | 56 ++--- src/editor/editor.d | 236 +++++++++++++++++-- src/editor/main.d | 8 +- src/editor/parsing.d | 68 +++--- src/editor/ui.d | 170 +++++++++----- src/editor/widgets.d | 374 +++++++++++++++++++++---------- src/shaders/gui.frag.glsl | 4 +- test/files/syntax_highlighting.d | 2 +- 10 files changed, 656 insertions(+), 266 deletions(-) diff --git a/src/VulkanRenderer b/src/VulkanRenderer index 65ac576..7a77505 160000 --- a/src/VulkanRenderer +++ b/src/VulkanRenderer @@ -1 +1 @@ -Subproject commit 65ac576cb0f37e797030365ef25d11a771468654 +Subproject commit 7a77505fc100da21a5bf804e415be8eb1f36ba19 diff --git a/src/dlib b/src/dlib index 5ff3cd9..6757559 160000 --- a/src/dlib +++ b/src/dlib @@ -1 +1 @@ -Subproject commit 5ff3cd91d73464e88bff3daef94a0a3bea2c3cb7 +Subproject commit 6757559089314da06956460c32ff632db7d10f88 diff --git a/src/editor/buffer.d b/src/editor/buffer.d index f042955..de9df6a 100644 --- a/src/editor/buffer.d +++ b/src/editor/buffer.d @@ -39,7 +39,7 @@ CreateFlatBuffer(u8[] data) u64 line_count = 1; for(u64 i = 0; i < cast(u64)data.length; i += 1) { - if (data.ptr[i] == '\n') + if(data.ptr[i] == '\n') { line_count += 1; } @@ -70,7 +70,7 @@ CreateLineBuffers(u64 arena_size) void Insert(FlatBuffer* buffer, u8[] insert, u64 length, u64 pos) { - if (buffer.length + length > buffer.data.length) + if(buffer.length + length > buffer.data.length) { FlatBuffer new_buf = CreateFlatBuffer(buffer.data); MFreeArray(buffer.data); @@ -79,7 +79,7 @@ Insert(FlatBuffer* buffer, u8[] insert, u64 length, u64 pos) for(u64 i = 0; i < length; i += 1) { - if (insert.ptr[i] == '\n') + if(insert.ptr[i] == '\n') { buffer.line_count += 1; } @@ -121,19 +121,19 @@ GetLines(FlatBuffer* buffer, LineBuffers* linebufs, u64 start_line, u64 length) u64 current_line = 0; for(u64 i = 0; i < buffer.length; i += 1) { - if (current_line == length) + if(current_line == length) { break; } bool new_line = (buffer.data.ptr[i] == '\n'); - if (line < start_line && new_line) + if(line < start_line && new_line) { line += 1; continue; } - if (start < 0 && new_line) + if(start < 0 && new_line) { linebufs.lines[current_line] = AllocArray!(u8)(&linebufs.arena, 1); linebufs.lines[current_line][0] = '\n'; @@ -141,13 +141,13 @@ GetLines(FlatBuffer* buffer, LineBuffers* linebufs, u64 start_line, u64 length) continue; } - if (start < 0) + if(start < 0) { start = cast(i64)i; continue; } - if (new_line) + if(new_line) { linebufs.lines[current_line] = AllocArray!(u8)(&linebufs.arena, (i-start)); linebufs.lines[current_line][0 .. i-start] = buffer.data[start .. i]; @@ -156,7 +156,7 @@ GetLines(FlatBuffer* buffer, LineBuffers* linebufs, u64 start_line, u64 length) continue; } - if (i == buffer.length-1) + if(i == buffer.length-1) { linebufs.lines[current_line] = AllocArray!(u8)(&linebufs.arena, (buffer.length-start)); linebufs.lines[current_line][0 .. buffer.length-start] = buffer.data[start .. buffer.length]; @@ -171,13 +171,13 @@ RangeToPos(FlatBuffer* buffer, Range range) u32 line, col; for(u64 i = 0; i < buffer.length; i += 1) { - if (range.y == line) + if(range.y == line) { buffer_pos = i; break; } - if (buffer.data.ptr[i] == '\n') + if(buffer.data.ptr[i] == '\n') { line += 1; } @@ -185,7 +185,7 @@ RangeToPos(FlatBuffer* buffer, Range range) for(u64 i = buffer_pos; i < buffer.length; i += 1) { - if (col == range.x) + if(col == range.x) { buffer_pos = i; break; @@ -206,7 +206,7 @@ Delete(FlatBuffer* buffer, u64 length, u64 pos) assert(end <= buffer.length, "Delete failure: pos+length is not in range"); u8[] temp; - if (end != buffer.length) + if(end != buffer.length) { temp = AllocArray!(u8)(&buffer.arena, buffer.length-end); temp[0 .. temp.length] = buffer.data[end .. buffer.length]; @@ -351,7 +351,7 @@ Advance(FlatBuffer* fb) bool delim = CheckDelimiter(ch); bool str = CheckString(ch); - if (!started && delim) + if(!started && delim) { tk.pos = i; tk.pos_end = i+1; @@ -359,7 +359,7 @@ Advance(FlatBuffer* fb) break; } - if (!started && !space) + if(!started && !space) { tk.pos = i; started = true; @@ -367,12 +367,12 @@ Advance(FlatBuffer* fb) continue; } - if (started && (delim || space) && !str_started) + if(started && (delim || space) && !str_started) { result = true; tk.pos_end = i; - if (ch == EOF) + if(ch == EOF) { Logf("final token"); tk.final_token = true; @@ -388,7 +388,7 @@ Advance(FlatBuffer* fb) break; } - if (started && str_started && str) + if(started && str_started && str) { result = true; tk.pos_end = i+1; @@ -562,7 +562,7 @@ Highlight(FlatBuffer* fb) u8[] token; u8[] prev_token; - if (tk.final_token) + if(tk.final_token) { goto InnerTokenStream; } @@ -574,48 +574,48 @@ InnerTokenStream: token = fb.data[tk.pos .. tk.pos_end]; scope(exit) prev_token = token; - if (token.length == 0) break; + if(token.length == 0) break; - if (token.length == 1 && CheckDelimiter(token.ptr[0])) + if(token.length == 1 && CheckDelimiter(token.ptr[0])) { tk.buffer[tk.pos] = TS.Bracket; continue TokenStream; } - if (CheckString(token[0]) && CheckString(token[token.length-1]) && token[0] == token[token.length-1]) + if(CheckString(token[0]) && CheckString(token[token.length-1]) && token[0] == token[token.length-1]) { tk.buffer[tk.pos .. tk.pos_end] = TS.String; continue TokenStream; } - if (token[0] >= '0' && token[0] <= '9') + if(token[0] >= '0' && token[0] <= '9') { tk.buffer[tk.pos .. tk.pos_end] = TS.Number; continue TokenStream; } - if ((token[0] >= 'a' && token[0] <= 'z') || (token[0] >= 'A' && token[0] <= 'Z') || token[0] == '_') + if((token[0] >= 'a' && token[0] <= 'z') || (token[0] >= 'A' && token[0] <= 'Z') || token[0] == '_') { - if (prev_token == r"import") + if(prev_token == r"import") { tk.buffer[tk.pos .. tk.pos_end] = TS.ImportTarget; continue TokenStream; } - if (tk.next_type == TS.Exclamation) + if(tk.next_type == TS.Exclamation) { tk.buffer[tk.pos .. tk.pos_end] = TS.Macro; continue TokenStream; } u8 index = cast(u8)(token[0]-cast(u8)95); - if (index > 0 && index < keywords.length) + if(index > 0 && index < keywords.length) { // TODO: look at simd string comparison const(u8[])[] key_table = keywords[index]; foreach(key; key_table) { - if (token == key) + if(token == key) { tk.buffer[tk.pos .. tk.pos_end] = TS.Keyword; continue TokenStream; diff --git a/src/editor/editor.d b/src/editor/editor.d index be821db..9199e9d 100644 --- a/src/editor/editor.d +++ b/src/editor/editor.d @@ -8,6 +8,7 @@ import std.format : sformat; import dlib.alloc; import buffer; import ui; +import widgets : Nil; import widgets; import std.stdio; @@ -15,12 +16,12 @@ import std.exception; f32 g_delta = 0.0; -EditorCtx g_ed_ctx; - struct EditorCtx { - Arena arena; - UIPanel* base_panel; + Arena arena; + UIPanel* base_panel; + u64 panel_id; + EditState state; } struct Editor @@ -40,26 +41,33 @@ struct Editor u64 line_offset; } +enum EditState +{ + NormalMode, + InputMode, + CmdOpen, + SetPanelFocus, +} + +alias ES = EditState; + void -Cycle(Inputs* inputs) +Cycle(EditorCtx* ctx, Inputs* inputs) { ResetScratch(MB(4)); + assert(Nil(ctx.base_panel.next)); + + HandleInputs(ctx, inputs); + BeginBuild(inputs); - static UIPanel panel = { - id: CastStr!(u8)("##base"), - axis: A2D.Y, - pct: 1.0, - }; - - Panel(&panel); - EndPanel(); + DrawPanels(ctx.base_panel); EndBuild(); } -void +EditorCtx InitEditorCtx(PlatformWindow* window) { InitFreeType(); @@ -70,7 +78,197 @@ InitEditorCtx(PlatformWindow* window) arena: CreateArena(MB(2)), }; - g_ed_ctx = ctx; + ctx.base_panel = CreatePanel(&ctx); + ctx.base_panel.ed = CreateEditor(&ctx); + SetFocusedPanel(ctx.base_panel); + + return ctx; +} + +UIPanel* +CreatePanel(EditorCtx* ctx) +{ + UIPanel* p = Alloc!(UIPanel)(&ctx.arena); + p.axis = A2D.Y; + p.id = AllocArray!(u8)(&ctx.arena, 10); + p.pct = 1.0; + (cast(char[])p.id).sformat("##%08s", ctx.panel_id); + p.parent = p.first = p.last = p.next = p.prev = g_UI_NIL_PANEL; + + ctx.panel_id += 1; + + return p; +} + +// Load all files then move things into editor after being created when selected + +Editor* +CreateEditor(EditorCtx* ctx) +{ + Editor* ed = Alloc!(Editor)(&ctx.arena); + + ed.arena = CreateArena(MB(4)); + + return ed; +} + +void +AddEditor(EditorCtx* ctx, UIPanel* target, Axis2D axis) +{ + if(Nil(target.parent) || target.parent.axis != axis) + { + UIPanel* first = CreatePanel(ctx); + UIPanel* second = CreatePanel(ctx); + + first.ed = target.ed; + second.ed = CreateEditor(ctx); + + first.pct = second.pct = 0.5; + + target.axis = axis; + target.ed = null; + + PushPanel(target, first); + PushPanel(target, second); + + SetFocusedPanel(second); + } + else if(target.parent.axis == axis) + { + UIPanel* panel = CreatePanel(ctx); + panel.ed = CreateEditor(ctx); + + InsertPanel(target.parent, target, panel); + + u64 count = 0; + for(UIPanel* p = target.parent.first; !Nil(p); p = p.next, count += 1) {} + + f32 pct = 1.0/count; + for(UIPanel* p = target.parent.first; !Nil(p); p = p.next) + { + p.pct = pct; + } + + SetFocusedPanel(panel); + } +} + +void +HandleInputs(EditorCtx* ctx, Inputs* inputs) +{ + for(auto node = inputs.list.first; node != null; node = node.next) + { + bool taken = false; + + Input key = node.value.key; + bool pressed = node.value.pressed; + + if (pressed) + { + switch(key) + { + case Input.i: + { + if(ctx.state == ES.NormalMode) + { + ctx.state = ES.InputMode; + taken = true; + } + } break; + case Input.Esc: + { + if(ctx.state == ES.InputMode || ctx.state == ES.CmdOpen) + { + ctx.state = ES.NormalMode; + taken = true; + } + } break; + case Input.Semicolon: + { + if(node.value.md & (MD.LeftShift | MD.RightShift)) + { + if(ctx.state != ES.NormalMode) + { + ctx.state = ES.CmdOpen; + taken = true; + } + } + } break; + case Input.w: + { + if(ctx.state == ES.NormalMode && node.value.md & (MD.LeftCtrl | MD.RightCtrl)) + { + ctx.state = ES.SetPanelFocus; + taken = true; + } + } break; + case Input.v: + { + if(ctx.state == ES.NormalMode) + { + AddEditor(ctx, GetFocusedPanel(), A2D.X); + } + } break; + case Input.c: + { + if(ctx.state == ES.NormalMode) + { + AddEditor(ctx, GetFocusedPanel(), A2D.Y); + } + } break; + case Input.Up: + case Input.Down: + case Input.Left: + case Input.Right: + { + if(false) + { + UIPanel* panel = GetFocusedPanel(); + UIPanel* focused = g_UI_NIL_PANEL; + for(UIPanel* p = panel; !Nil(p); p = p.parent) + { + if(p.parent.axis == A2D.X) + { + if(key == Input.Left && !Nil(p.prev) && p.prev.ed != null) + { + focused = p.prev; + break; + } + + if(key == Input.Right && !Nil(p.next) && p.next.ed != null) + { + focused = p.next; + break; + } + } + else + { + if(key == Input.Up && !Nil(p.prev) && p.prev.ed != null) + { + focused = p.prev; + break; + } + + if(key == Input.Down && !Nil(p.next) && p.next.ed != null) + { + focused = p.next; + break; + } + } + } + + SetFocusedPanel(focused); + } + } break; + default: break; + } + } + + if(taken) + { + DLLRemove(&inputs.list, node, null); + } + } } /* @@ -102,13 +300,13 @@ DrawBuffer(Editor* ed, f32 x, f32 y, f32 px, FlatBuffer* fb) { u8 ch = fb.data[i]; - if (ch > 0 && ch < ed.atlas_buf.atlas.glyphs.length) + if(ch > 0 && ch < ed.atlas_buf.atlas.glyphs.length) { Glyph* g = ed.atlas_buf.atlas.glyphs.ptr + ch; - if (g.advance > 0.0) + if(g.advance > 0.0) { - if (ch == '\t') + if(ch == '\t') { g.atlas_left = g.atlas_right = 0.0; foreach(j; 0 .. tab_count) @@ -116,7 +314,7 @@ DrawBuffer(Editor* ed, f32 x, f32 y, f32 px, FlatBuffer* fb) DrawGlyph(g, scale, &x_pos, y_pos); } } - else if (ch == '\n') + else if(ch == '\n') { g.atlas_left = g.atlas_right = 0.0; DrawGlyph(g, scale, &x_pos, y_pos); diff --git a/src/editor/main.d b/src/editor/main.d index c8be89e..80e3c9d 100644 --- a/src/editor/main.d +++ b/src/editor/main.d @@ -9,7 +9,7 @@ void main(string[] argv) { u8[] buffer = null; u8[] buffer_name = null; - if (argv.length > 1) + if(argv.length > 1) { buffer_name = (cast(u8*)argv[1].ptr)[0 .. argv[1].length]; File f = File(argv[1], "rb"); @@ -21,16 +21,16 @@ void main(string[] argv) PlatformWindow window = CreateWindow("Editor", 1920, 1080); StartPlatformThread(&window); - InitEditorCtx(&window); + EditorCtx ctx = InitEditorCtx(&window); while (true) { Inputs* inputs = GetEvents(&window); - if (window.close) + if(window.close) { break; } - Cycle(inputs); + Cycle(&ctx, inputs); } } diff --git a/src/editor/parsing.d b/src/editor/parsing.d index 591b7e7..eb0cd8c 100644 --- a/src/editor/parsing.d +++ b/src/editor/parsing.d @@ -154,7 +154,7 @@ Tokenize(FlatBuffer* fb) u64 semi_count; foreach(i; 0 .. fb.length) { - if (fb.data.ptr[i] == ';') + if(fb.data.ptr[i] == ';') { semi_count += 1; } @@ -172,7 +172,7 @@ Tokenize(FlatBuffer* fb) { scope(exit) tk.tk_count += 1; Token* t = NextToken(fb); - if (t == null) break; + if(t == null) break; switch(t.type) { @@ -201,16 +201,16 @@ Tokenize(FlatBuffer* fb) } break; case TT.LeftBracket: { - if (prev != null && _macro && prev.type != TT.Exclamation) + if(prev != null && _macro && prev.type != TT.Exclamation) { _macro = false; } } goto case TT.Comma; case TT.LeftBrace: { - if (prev) + if(prev) { - if (prev.type == TT.RightBracket) + if(prev.type == TT.RightBracket) { i64 i = tk.tk_count - 2; Token* pt = null; @@ -221,24 +221,24 @@ Tokenize(FlatBuffer* fb) pt = tk.tokens.ptr + i; i -= 1; - if (pt.type == TT.Keyword && brackets == 0) break; + if(pt.type == TT.Keyword && brackets == 0) break; - if (pt.type == TT.LeftBracket) + if(pt.type == TT.LeftBracket) { brackets -= 1; continue; } - if (pt.type == TT.RightBracket) + if(pt.type == TT.RightBracket) { brackets += 1; is_macro = true; continue; } - if (pt.type == TT.Identifier && brackets == 0) + if(pt.type == TT.Identifier && brackets == 0) { - if (i >= 0) + if(i >= 0) { Token* tmp = tk.tokens.ptr + i; tmp.type = TT.Type; @@ -258,24 +258,24 @@ Tokenize(FlatBuffer* fb) bool id_keyword = pt.type == TT.Identifier || pt.type == TT.Keyword; - if ( is_macro && brackets == 4) break; - if (!is_macro && brackets == 2) break; + if( is_macro && brackets == 4) break; + if(!is_macro && brackets == 2) break; - if (is_macro && brackets < 2 && id_keyword) + if(is_macro && brackets < 2 && id_keyword) { pt.type = TT.Type; tk.buffer[pt.start .. pt.end] = TS.Type; continue; } - if (((!is_macro && brackets < 2) || (is_macro && brackets == 3)) && id_keyword && (ppt.type == TT.LeftBracket || ppt.type == TT.Comma)) + if(((!is_macro && brackets < 2) || (is_macro && brackets == 3)) && id_keyword && (ppt.type == TT.LeftBracket || ppt.type == TT.Comma)) { pt.type = TT.Type; tk.buffer[pt.start .. pt.end] = TS.Type; continue; } - if (pt.type == TT.LeftBracket || pt.type == TT.RightBracket) + if(pt.type == TT.LeftBracket || pt.type == TT.RightBracket) { brackets += 1; continue; @@ -347,17 +347,17 @@ NextToken(FlatBuffer* fb) u8 ch = fb.data[tk.pos]; Token* t = null; - if (tk.pos < fb.length) + if(tk.pos < fb.length) { t = tk.tokens.ptr + tk.tk_count; t.start = tk.pos; bool ascii = (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'); - if (ch == 'r') + if(ch == 'r') { u8 next = PeekNextChar(fb); - if (next == '"') + if(next == '"') { tk.pos += 1; ParseStr(fb); @@ -367,14 +367,14 @@ NextToken(FlatBuffer* fb) ParseId(fb); } } - else if (ch >= '0' && ch <= '9') + else if(ch >= '0' && ch <= '9') { ParseNum(fb); } - else if (ch == 'P' || ch == 'E' || ch == 'e' || ch == 'p') + else if(ch == 'P' || ch == 'E' || ch == 'e' || ch == 'p') { u8 next = PeekNextChar(fb); - if (next == '-' || next == '+' || (next >= '0' && next <= '9')) + if(next == '-' || next == '+' || (next >= '0' && next <= '9')) { ParseNum(fb); } @@ -383,7 +383,7 @@ NextToken(FlatBuffer* fb) ParseId(fb); } } - else if (ascii || ch == '_') + else if(ascii || ch == '_') { ParseId(fb); } @@ -453,8 +453,8 @@ NextToken(FlatBuffer* fb) tk.pos += 1; u8 c = fb.data[tk.pos]; - if (CheckWhiteSpace(c)) break; - if (!(c >= 'a' && c <= 'z') && !(c >= 'A' && c <= 'Z')) break; + if(CheckWhiteSpace(c)) break; + if(!(c >= 'a' && c <= 'z') && !(c >= 'A' && c <= 'Z')) break; } t.end = tk.pos; @@ -465,7 +465,7 @@ NextToken(FlatBuffer* fb) while (tk.pos < fb.length) { tk.pos += 1; - if (tk.pos != '.') break; + if(tk.pos != '.') break; } t.end = tk.pos; } break; @@ -473,7 +473,7 @@ NextToken(FlatBuffer* fb) { t.type = TT.Slash; u8 next = PeekNextChar(fb); - if (next == '/') + if(next == '/') { tk.pos += 1; t.type = TT.Comment; @@ -482,7 +482,7 @@ NextToken(FlatBuffer* fb) { tk.pos += 1; - if (CheckEOL(fb.data[tk.pos])) + if(CheckEOL(fb.data[tk.pos])) { break; } @@ -498,7 +498,7 @@ NextToken(FlatBuffer* fb) case 251: { u8 next = PeekNextChar(fb); - if (next == '=') + if(next == '=') { tk.pos += 1; } @@ -538,7 +538,7 @@ ParseNum(FlatBuffer* fb) u8 ch = fb.data[tk.pos]; - if (ch != '_' && ch != '.' && !(ch >= '0' && ch <= '9') && ch != 'x' && ch != 'X' && !((ch == '-' || ch == '+') && first)) + if(ch != '_' && ch != '.' && !(ch >= '0' && ch <= '9') && ch != 'x' && ch != 'X' && !((ch == '-' || ch == '+') && first)) { break; } @@ -549,7 +549,7 @@ ParseNum(FlatBuffer* fb) while (tk.pos < fb.length) { u8 ch = fb.data[tk.pos]; - if (ch != 'l' && ch != 'u' && ch != 'U' && ch != 'L' && ch != 'f' && ch != 'F') + if(ch != 'l' && ch != 'u' && ch != 'U' && ch != 'L' && ch != 'f' && ch != 'F') { break; } @@ -593,7 +593,7 @@ ParseId(FlatBuffer* fb) u8 ch = fb.data[tk.pos]; - if (!(ch >= 'a' && ch <= 'z') && !(ch >= 'A' && ch <= 'Z') && ch != '.' && ch != '_' && !(ch >= '0' && ch <= '9')) + if(!(ch >= 'a' && ch <= 'z') && !(ch >= 'A' && ch <= 'Z') && ch != '.' && ch != '_' && !(ch >= '0' && ch <= '9')) { break; } @@ -602,12 +602,12 @@ ParseId(FlatBuffer* fb) t.end = tk.pos; u8 ch = cast(u8)(fb.data[t.start] - cast(u8)(95)); // Index into keywords lookup - if (ch < D_KEYWORDS.length && D_KEYWORDS[ch].length > 0) + if(ch < D_KEYWORDS.length && D_KEYWORDS[ch].length > 0) { u8[] id = fb.data[t.start .. t.end]; foreach(k; D_KEYWORDS[ch]) { - if (id == k) + if(id == k) { t.type = TT.Keyword; break; @@ -648,7 +648,7 @@ SkipWhiteSpace(FlatBuffer* fb) bool white_space = CheckWhiteSpace(ch); - if (!white_space) + if(!white_space) { break; } diff --git a/src/editor/ui.d b/src/editor/ui.d index 7e9237a..78f39b7 100644 --- a/src/editor/ui.d +++ b/src/editor/ui.d @@ -7,6 +7,7 @@ import dlib.fonts; import vulkan; import widgets; import std.stdio; +import std.math.traits : isNaN; import std.math.rounding : ceil; import std.format : sformat; import core.stdc.string : memset; @@ -44,6 +45,7 @@ enum UIFlags DrawBorder = 0x04, Clickable = 0x08, Draggable = 0x10, + TextInput = 0x20, } alias UIF = UIFlags; @@ -69,6 +71,7 @@ struct UICtx { HashTable!(UIHash, UIItem*) items; Arena arena; + Arena temp_arena; Inputs* inputs; u64 frame; u64 f_idx; @@ -94,10 +97,10 @@ struct UICtx UIItemNode* prev_sibling; u32 panel_level; UIItem* drag_item; + UIItem* focused; UIItemStackList stack_free_list; - FontAtlas atlas; Vec4[4] color; Vec4[4] border_color; Vec4 text_color; @@ -108,6 +111,9 @@ struct UICtx f32 border_thickness; f32 corner_radius; f32 edge_softness; + + debug u32 item_count; + debug u32 final_count; } struct UIItemStackList @@ -134,6 +140,7 @@ struct UIItem UIItem* last; // last child UIItem* parent; + u64 last_frame; u32 level; Vec2 dragged; @@ -246,11 +253,13 @@ InitUICtx(PlatformWindow* window) rd: InitRenderer(handles, MB(16), MB(8)), items: CreateHashTable!(UIHash, UIItem*)(12), arena: arena, + temp_arena: CreateArena(MB(1)), root: g_UI_NIL, top_parent: g_UI_NIL_NODE, prev_sibling: g_UI_NIL_NODE, drag_item: g_UI_NIL, - text_size: 14.0, + focused: g_UI_NIL, + text_size: 16.0, tab_width: 2, border_thickness: 0.0, corner_radius: 0.0, @@ -260,6 +269,7 @@ InitUICtx(PlatformWindow* window) atlas_buf: atlas_buf, font: font, font_data: cast(u8[])FONT_BYTES, + adjustment: 0.0, }; u64 vertex_size = 10000; @@ -330,7 +340,7 @@ PopItemNode() UICtx* ctx = GetCtx(); UIItemNode* node; - if (!CheckNil(g_UI_NIL_NODE, ctx.stack_free_list.first)) + if(!CheckNil(g_UI_NIL_NODE, ctx.stack_free_list.first)) { node = SLLPop(&ctx.stack_free_list, g_UI_NIL_NODE); } @@ -370,7 +380,7 @@ PopSiblingPanel() UIItem* panel = g_UI_NIL; UIItemNode* node = ctx.prev_sibling; - if (node != g_UI_NIL_NODE) + if(node != g_UI_NIL_NODE) { panel = node.item; ctx.prev_sibling = node.next; @@ -404,22 +414,22 @@ PrevSiblingPanel(UIItem* panel) UIItemNode* prev = ctx.prev_sibling; for(;;) { - if (panel == prev.item) + if(panel == prev.item) { prev = prev.next; } - if (prev == g_UI_NIL_NODE) + if(prev == g_UI_NIL_NODE) { break; } - if (prev.item.level < panel.level) + if(prev.item.level < panel.level) { break; } - if (prev.item.level == panel.level) + if(prev.item.level == panel.level) { result = prev.item; break; @@ -538,6 +548,21 @@ BeginBuild(Inputs* inputs) { UICtx* ctx = GetCtx(); + debug ctx.item_count = 0; + debug ctx.final_count = 0; + + Reset(&ctx.temp_arena); + + KVPair!(u64, UIItem*)*[] items = GetAllNodes(&ctx.temp_arena, &ctx.items); + for(u64 i = 0; i < items.length; i += 1) + { + UIItem* item = items[i].value; + if(item.last_frame != ctx.frame-1) + { + Delete(&ctx.items, items[i].key); + } + } + ctx.f_idx = ctx.frame%FRAME_OVERLAP; ctx.inputs = inputs; @@ -573,6 +598,8 @@ EndBuild() DrawUI(ctx, ctx.root); + debug assert(ctx.item_count == ctx.final_count); + BindBuffers(&ctx.rd, &ctx.buffers[ctx.f_idx].m_idx, &ctx.buffers[ctx.f_idx].m_vtx); DrawIndexed(&ctx.rd, 6, ctx.buffers[ctx.f_idx].count, 0); @@ -582,11 +609,11 @@ EndBuild() debug { - static bool first = false; - if (!first) + static u32 prev_count = 0; + if(prev_count != ctx.item_count) { - PrintNodes(ctx.root); - first = true; + //PrintNodes(ctx.root); + prev_count = ctx.item_count; } } } @@ -599,7 +626,7 @@ PrepRendering(UICtx* ctx) BeginRendering(&ctx.rd); Vec2 ext = GetExtent(&ctx.rd); - if (ext != ctx.res) + if(ext != ctx.res) { ctx.res = ext; Ortho(&ctx.pc.projection, 0.0, 0.0, ext.x, ext.y, 1000.0, 0.1); @@ -620,7 +647,7 @@ CompleteRendering(UICtx* ctx) void PrintNodes(UIItem* item) { - if (!Nil(item)) + if(!Nil(item)) { Logf("x0 %s x1 %s y0 %s y1 %s", item.rect.x0, item.rect.x1, item.rect.y0, item.rect.y1); @@ -636,9 +663,10 @@ CalcFixedSizes(UIItem* item) { static foreach(axis; A2D.min .. A2D.max) { - if (i.size_info[axis].type == ST.Pixels) + if(i.size_info[axis].type == ST.Pixels) { i.size.v[axis] = i.size_info[axis].value + i.adjustment.v[axis]; + assert(!isNaN(i.size.v[axis])); } } } @@ -651,9 +679,10 @@ CalcPercentageSizes(UIItem* item) { static foreach(axis; A2D.min .. A2D.max) { - if (i.size_info[axis].type == ST.Percentage) + if(i.size_info[axis].type == ST.Percentage) { i.size.v[axis] = (i.parent.size.v[axis]*i.size_info[axis].value) + i.adjustment.v[axis]; + assert(!isNaN(i.size.v[axis])); } } } @@ -669,18 +698,21 @@ CalcPositions(alias axis)(UIItem* item) i.rect.vec0.v[axis] = pos; i.rect.vec1.v[axis] = end_pos; + assert(!isNaN(i.rect.vec0.v[axis])); + assert(!isNaN(i.rect.vec1.v[axis])); + f32 next_pos = i.parent.layout_axis == axis ? end_pos : pos; - if (!Nil(i.first)) + if(!Nil(i.first)) { i = i.first; } - else if (!Nil(i.next)) + else if(!Nil(i.next)) { i = i.next; pos = next_pos; } - else if (!Nil(i.parent.next)) + else if(!Nil(i.parent.next)) { i = i.parent.next; pos = i.parent.layout_axis == axis ? i.prev.rect.vec1.v[axis] : i.prev.rect.vec0.v[axis]; @@ -697,20 +729,22 @@ DrawUI(UICtx* ctx, UIItem* item) { for(UIItem* i = item; !Nil(i); i = Recurse(i)) { - if (i.flags & UIF.DrawBackground) + if(i.flags & UIF.DrawBackground) { DrawRect(ctx, i); } - if (i.flags & UIF.DrawBorder) + if(i.flags & UIF.DrawBorder) { DrawBorder(ctx, i); } - if (i.flags & UIF.DrawText) + if(i.flags & UIF.DrawText) { DrawLine(i); } + + debug ctx.final_count += 1; } } @@ -718,17 +752,21 @@ UIItem* Recurse(UIItem* item) { UIItem* result = g_UI_NIL; - if (!Nil(item.first)) + if(!Nil(item.first)) { result = item.first; } - else if (!Nil(item.next)) + else if(!Nil(item.next)) { result = item.next; } - else if (!Nil(item.parent.next)) + else for(UIItem* i = item.parent; !Nil(i); i = i.parent) { - result = item.parent.next; + if(!Nil(i.next)) + { + result = i.next; + break; + } } return result; @@ -752,15 +790,18 @@ BuildItem(UIItem* item, UISize size_x, UISize size_y, UIFlags properties) item.border_thickness = ctx.border_thickness; item.corner_radius = ctx.corner_radius; item.edge_softness = ctx.edge_softness; + item.last_frame = ctx.frame; item.parent = ctx.top_parent == g_UI_NIL_NODE ? g_UI_NIL : ctx.top_parent.item; - if (!Nil(item.parent)) + if(!Nil(item.parent)) { DLLPush(item.parent, item, g_UI_NIL); } // Reset any once off values here ctx.adjustment = Vec2(0.0); + + debug ctx.item_count += 1; } void @@ -775,7 +816,7 @@ Signal(UIItem* item) { bool taken = false; - if (item.flags & UIF.Clickable && Clicked(item, n)) + if(item.flags & UIF.Clickable && Clicked(item, n)) { Logf("Clicked"); item.last_click_pos = Vec2(n.value.x, n.value.y) - item.rect.vec0; @@ -783,7 +824,7 @@ Signal(UIItem* item) taken = true; } - if (item.flags & UIF.Draggable && Clicked(item, n) && Nil(ctx.drag_item)) + if(item.flags & UIF.Draggable && Clicked(item, n) && Nil(ctx.drag_item)) { Logf("Dragged"); item.signal |= UIS.Dragged; @@ -791,14 +832,14 @@ Signal(UIItem* item) taken = true; } - if (ctx.drag_item == item && n.value.key == Input.LeftClick && !n.value.pressed) + if(ctx.drag_item == item && n.value.key == Input.LeftClick && !n.value.pressed) { Logf("Released"); ctx.drag_item = g_UI_NIL; taken = true; } - if (ctx.drag_item == item && n.value.key == Input.MouseMotion) + if(ctx.drag_item == item && n.value.key == Input.MouseMotion) { item.signal |= UIS.Dragged; item.dragged.x += cast(f32)n.value.rel_x; @@ -806,7 +847,7 @@ Signal(UIItem* item) taken = true; } - if (taken) + if(taken) { DLLRemove(&ctx.inputs.list, n, null); } @@ -847,9 +888,9 @@ MakeKey(u8[] id) u32 hash_count = 0; for(i64 i = id.length-1; i >= 0; i -= 1) { - if (hash_count == 2) + if(hash_count == 2) { - if (id[i] == '#') + if(id[i] == '#') { hash_count += 1; pos = i; @@ -858,19 +899,19 @@ MakeKey(u8[] id) break; } - if (id[i] == '#') + if(id[i] == '#') { pos = i; hash_count += 1; } } - if (hash_count == 2) + if(hash_count == 2) { key.text = id[0 .. pos]; key.hash = Hash(id); } - else if (hash_count == 3) + else if(hash_count == 3) { key.text = id[0 .. pos]; key.hash = Hash(id[pos+hash_count .. $]); @@ -888,7 +929,7 @@ pragma(inline) UIItem* Get(UIKey key) { Result!(UIItem*) result = g_ui_ctx.items[key.hash]; - if (!result.ok) + if(!result.ok) { result.value = Alloc!(UIItem)(&g_ui_ctx.arena); Push(&g_ui_ctx.items, key.hash, result.value); @@ -917,12 +958,12 @@ f32 CalcTextWidth(u8[] str) { u32 tab_width = g_ui_ctx.tab_width; - Glyph* space = g_ui_ctx.atlas.glyphs.ptr + ' '; + Glyph* space = g_ui_ctx.atlas_buf.atlas.glyphs.ptr + ' '; f32 width = 0.0; for(u64 i = 0; i < str.length; i += 1) { - width += GlyphWidth(g_ui_ctx.atlas.glyphs.ptr + str.ptr[i]); + width += GlyphWidth(g_ui_ctx.atlas_buf.atlas.glyphs.ptr + str.ptr[i]); } return width; @@ -932,9 +973,9 @@ pragma(inline) f32 GlyphWidth(Glyph* g) { f32 width = 0.0; - if (g.ch == '\t') + if(g.ch == '\t') { - width += g_ui_ctx.atlas.glyphs[' '].advance * cast(f32)(g_ui_ctx.tab_width); + width += g_ui_ctx.atlas_buf.atlas.glyphs[' '].advance * cast(f32)(g_ui_ctx.tab_width); } else { @@ -945,28 +986,28 @@ GlyphWidth(Glyph* g) } Node!(u8[])* -MakeMultiline(u8[] text, f32 width, u64 line_no) +MakeMultiline(u8[] text, f32 width, u8[] parent_id, u64 line_no) { - f32 scaled_width = width * (g_ui_ctx.atlas.size/g_ui_ctx.text_size); + f32 scaled_width = width * (g_ui_ctx.atlas_buf.atlas.size/g_ui_ctx.text_size); f32 text_width = CalcTextWidth(text); u64 line_count = cast(u64)(ceil(text_width/scaled_width)); Node!(u8[])* node = null; - if (line_count > 0) + if(line_count > 0) { f32 w = 0.0; u64 line = 0; u64 start = 0; - const u64 extra_buf = 10; + const u64 extra_buf = 20; for(u64 i = 0; i < text.length; i += 1) { - f32 ch_w = GlyphWidth(g_ui_ctx.atlas.glyphs.ptr + text[i]); + f32 ch_w = GlyphWidth(g_ui_ctx.atlas_buf.atlas.glyphs.ptr + text[i]); - if (ch_w + w > scaled_width || i == text.length-1) + if(ch_w + w > scaled_width || i == text.length-1) { u64 len = i-start+1; - u8[10] buf = 0; - (cast(char[])buf).sformat("##%04s%04s", line_no, line); + u8[extra_buf] buf = 0; + (cast(char[])buf).sformat("%s%05s%05s", cast(char[])parent_id, line_no, line); u8[] str = ScratchAlloc!(u8)(len+extra_buf); str[0 .. len] = text[start .. start+len]; @@ -975,7 +1016,7 @@ MakeMultiline(u8[] text, f32 width, u64 line_no) Node!(u8[])* n = node; for(;;) { - if (node == null) + if(node == null) { node = ScratchAlloc!(Node!(u8[]))(); node.value = str; @@ -983,7 +1024,7 @@ MakeMultiline(u8[] text, f32 width, u64 line_no) break; } - if (n.next == null) + if(n.next == null) { n.next = ScratchAlloc!(Node!(u8[]))(); n.next.value = str; @@ -1013,12 +1054,12 @@ DrawLine(UIItem* item) UICtx* ctx = GetCtx(); f32 y = item.rect.y0 + ctx.text_size; f32 x = item.rect.x0; - FontAtlas* atlas = &ctx.atlas; + FontAtlas* atlas = &ctx.atlas_buf.atlas; for(u64 i = 0; i < item.key.text.length && item.key.text[i] != '\0'; i += 1) { u8 ch = item.key.text.ptr[i]; - if (ch < 128) + if(ch < 128) { DrawGlyph(&atlas.glyphs[ch], atlas.size/ctx.text_size, &x, y); } @@ -1031,13 +1072,13 @@ DrawGlyph(Glyph* glyph, f32 scale, f32* x_pos, f32 y, bool draw_bg = false, Vec4 UICtx* ctx = GetCtx(); Vertex* bg_v = null, v = null; f32 h; - if (draw_bg) + if(draw_bg) { bg_v = ctx.buffers[ctx.f_idx].vtx.ptr + ctx.buffers[ctx.f_idx].count; AddUIIndices(ctx); } - if (glyph.ch == '\t') + if(glyph.ch == '\t') { *x_pos += glyph.advance * (GetCtx().tab_width - 1); } @@ -1055,7 +1096,7 @@ DrawGlyph(Glyph* glyph, f32 scale, f32* x_pos, f32 y, bool draw_bg = false, Vec4 v.dst_end.x = *x_pos + w + l; v.dst_end.y = y + h - y_pos; - if (glyph.ch != '\t' && glyph.ch != '\n') + if(glyph.ch != '\t' && glyph.ch != '\n') { v.src_start.x = glyph.atlas_left; v.src_start.y = glyph.atlas_top; @@ -1071,7 +1112,7 @@ DrawGlyph(Glyph* glyph, f32 scale, f32* x_pos, f32 y, bool draw_bg = false, Vec4 *x_pos += glyph.advance * scale; - if (draw_bg) + if(draw_bg) { Vec4 white = Vec4(1.0); @@ -1139,10 +1180,10 @@ ClickedCharIndex(UIItem* item) for(u64 i = 0; i < item.key.text.length; i += 1) { u8 ch = item.key.text[i]; - if (ch < ctx.atlas.glyphs.length) + if(ch < ctx.atlas_buf.atlas.glyphs.length) { - f32 ch_w = GlyphWidth(ctx.atlas.glyphs.ptr + ch); - if (ch_w + w > item.last_click_pos.x) + f32 ch_w = GlyphWidth(ctx.atlas_buf.atlas.glyphs.ptr + ch); + if(ch_w + w > item.last_click_pos.x) { Logf("char clicked"); //item.clicked_char_idx = i; @@ -1159,7 +1200,7 @@ Clicked(UIItem* item, DNode!(InputEvent)* n) bool result = false; InputEvent* ev = &n.value; - if ( + if( ev.key == Input.LeftClick && ev.x >= item.rect.x0 && ev.x <= item.rect.x1 && @@ -1174,6 +1215,11 @@ Clicked(UIItem* item, DNode!(InputEvent)* n) return result; } +void +SetFocus(UIItem* item) +{ + g_ui_ctx.focused = item; +} unittest { diff --git a/src/editor/widgets.d b/src/editor/widgets.d index 30d233b..a7a0cd2 100644 --- a/src/editor/widgets.d +++ b/src/editor/widgets.d @@ -1,6 +1,7 @@ import dlib; import buffer; +import ui : Nil; import ui; import editor; import std.format : sformat; @@ -15,11 +16,13 @@ WidgetCtx g_widget_ctx; struct WidgetCtx { UIPanel* parent; + UIPanel* focused_panel; } struct UIPanel { u8[] id; + Editor* ed; UIPanel* parent; UIPanel* next; @@ -34,34 +37,25 @@ struct UIPanel Vec4 color; } -void -PushPanel(UIPanel* parent) +struct TextPart { - parent.list_next = g_widget_ctx.parent; - g_widget_ctx.parent = parent; -} - -UIPanel* -PopPanel() -{ - UIPanel* parent = g_widget_ctx.parent; - g_widget_ctx.parent = parent.list_next; - parent.list_next = g_UI_NIL_PANEL; - return parent; -} - -void -InitWidgets() -{ - g_UI_NIL_PANEL = cast(UIPanel*)&g_ui_nil_panel; - g_widget_ctx.parent = g_UI_NIL_PANEL; + UIItem* item; + TextPart* next; } UIItem* Root() { Vec2 d = RootSize(); + + SetColor( + Vec4(0.3, 0.65, 0.86, 1.0), + Vec4(0.25, 0.60, 0.81, 1.0), + Vec4(0.25, 0.60, 0.81, 1.0), + Vec4(0.3, 0.65, 0.86, 1.0), + ); SetLayoutAxis(A2D.Y); + UIItem* root = Get("##root_item"); BuildItem(root, UISize(ST.Pixels, d.x), UISize(ST.Pixels, d.y), UIF.DrawBackground); @@ -81,60 +75,28 @@ Panel(UIPanel* panel) UIItem* item = Get(panel.id); UIItem* separator = g_UI_NIL; - UIItem* prev_panel = PeekSiblingPanel(); - UIItem* parent = PeekParent(); + UIPanel* parent_pn = panel.parent; - UIPanel* parent_pn = g_widget_ctx.parent; - - DLLPush(parent_pn, panel, g_UI_NIL_PANEL); + UIItem* prev_panel = !Nil(panel.prev) ? Get(panel.prev.id) : g_UI_NIL; + UIItem* parent = !Nil(parent_pn) ? Get(parent_pn.id) : PeekParent(); f32 x_pct = 1.0, y_pct = 1.0; - if (parent_pn != g_UI_NIL_PANEL) + if(!Nil(parent_pn)) { x_pct = parent_pn.axis == A2D.X ? panel.pct : 1.0; y_pct = parent_pn.axis == A2D.Y ? panel.pct : 1.0; } - Signal(item); + if(item.signal & UIS.Clicked) + { + Logf("%s clicked", cast(char[])panel.id); + SetFocusedPanel(panel); + } f32 adj_x = 0.0, adj_y = 0.0; - if (!Nil(prev_panel) && parent != prev_panel) + if(!Nil(prev_panel)) { - f32 sep_y = 1.0, sep_x = 1.0, parent_start = 0.0, parent_end = 0.0; - if (parent.layout_axis == A2D.X) - { - sep_x = 5.0; - adj_x = -5.0; - parent_start = parent.rect.vec0.x; - parent_end = parent.rect.vec1.x; - } - else - { - sep_y = 5.0; - adj_y = -5.0; - parent_start = parent.rect.vec0.y; - parent_end = parent.rect.vec1.y; - } - - u8[] buf = ScratchAlloc!(u8)(panel.id.length + 5); - (cast(char[])buf).sformat("%s_sep", cast(char[])panel.id); - - separator = Get(buf); - - Separator(separator, sep_x, sep_y, parent.layout_axis); - - Signal(separator); - - f32 pos = separator.dragged.v[parent.layout_axis]; - if (separator.signal & UIS.Dragged && pos != 0.0) - { - f32 pct = Remap(pos, 0.0, parent_start-parent_end, 0.0, 1.0); - if (CheckPanelBounds(panel.pct - pct) && CheckPanelBounds(panel.prev.pct + pct)) - { - panel.pct -= pct; - panel.prev.pct += pct; - } - } + Separator(panel, parent, &adj_x, &adj_y); } SetColor( @@ -145,18 +107,216 @@ Panel(UIPanel* panel) ); SetLayoutAxis(panel.axis); - BuildItem(item, UISize(ST.Percentage, x_pct), UISize(ST.Percentage, y_pct), UIF.DrawBackground); + BuildItem(item, UISize(ST.Percentage, x_pct), UISize(ST.Percentage, y_pct), UIF.DrawBackground|UIF.Clickable); - UIItem* sibling = PrevSiblingPanel(item); - - PushSiblingPanel(item); PushParent(item); - PushPanel(panel); - return item; } +void +Separator(UIPanel* panel, UIItem* parent, f32* adj_x, f32* adj_y) +{ + Axis2D axis = parent.layout_axis; + + f32 sep_y = 1.0, sep_x = 1.0, parent_start = 0.0, parent_end = 0.0; + if(axis == A2D.X) + { + sep_x = 5.0; + *adj_x = -5.0; + parent_start = parent.rect.vec0.x; + parent_end = parent.rect.vec1.x; + } + else + { + sep_y = 5.0; + *adj_y = -5.0; + parent_start = parent.rect.vec0.y; + parent_end = parent.rect.vec1.y; + } + + u8[] buf = ScratchAlloc!(u8)(panel.id.length + 5); + (cast(char[])buf).sformat("%s_sep", cast(char[])panel.id); + + SetColor(Vec4(0.1, 0.2, 0.6, 1.0)); + + SizeType x_t = axis == A2D.X ? ST.Pixels : ST.Percentage; + SizeType y_t = axis == A2D.Y ? ST.Pixels : ST.Percentage; + + UIItem* item = Get(buf); + + BuildItem(item, UISize(x_t, sep_x), UISize(y_t, sep_y), UIF.DrawBackground|UIF.Draggable); + + Signal(item); + + f32 pos = item.dragged.v[parent.layout_axis]; + if(item.signal & UIS.Dragged && pos != 0.0) + { + f32 pct = Remap(pos, 0.0, parent_start-parent_end, 0.0, 1.0); + if(CheckPanelBounds(panel.pct - pct) && CheckPanelBounds(panel.prev.pct + pct)) + { + panel.pct -= pct; + panel.prev.pct += pct; + } + } +} + +void +TextLine(u8[] text) +{ + UIItem* parent = PeekParent(); + + UIItem* item = Get(text); +} + +void +EditorView(UIPanel* panel) +{ + UICtx* ctx = GetCtx(); + UIItem* item = Panel(panel); + + TextPart* tp = WrappedTextLine(CastStr!(u8)("Test 1234"), panel.id, 14.0, 0); + if(TextClicked(tp)) SetFocusedPanel(panel); + tp = WrappedTextLine(CastStr!(u8)("Test 1234"), panel.id, 14.0, 1); + if(TextClicked(tp)) SetFocusedPanel(panel); + tp = WrappedTextLine(CastStr!(u8)("Test 1234"), panel.id, 14.0, 2); + if(TextClicked(tp)) SetFocusedPanel(panel); + tp = WrappedTextLine(CastStr!(u8)("Test 1234"), panel.id, 14.0, 3); + if(TextClicked(tp)) SetFocusedPanel(panel); + + Signal(item); + + if(item.signal & UIS.Clicked) + { + SetFocusedPanel(panel); + } + + EndPanel(); +} + +bool +TextClicked(TextPart* text_part) +{ + bool result = false; + + for(TextPart* tp = text_part; !Nil(tp.item); tp = tp.next) + { + if(tp.item.signal & UIS.Clicked) + { + result = true; + break; + } + } + + return result; +} + +TextPart* +WrappedTextLine(u8[] text, u8[] parent_id, f32 text_size, u64 line_no) +{ + UIItem* parent = PeekParent(); + TextPart* part = ScratchAlloc!(TextPart)(); + part.item = g_UI_NIL; + + SetColor(Vec4(1.0)); + SetTextSize(text_size); + + TextPart* tp = part; + Node!(u8[])* lines = MakeMultiline(text, parent.size.x, parent_id, line_no); + for(Node!(u8[])* line = lines; line != null; line = line.next, tp = tp.next) + { + tp.item = Get(line.value); + tp.next = ScratchAlloc!(TextPart)(); + tp.next.item = g_UI_NIL; + + Signal(tp.item); + + if(tp.item.signal & UIS.Clicked) + { + ClickedCharIndex(tp.item); + } + + BuildItem(tp.item, UISize(ST.Percentage, 1.0), UISize(ST.Pixels, text_size), UIF.DrawText|UIF.Clickable|UIF.Draggable); + } + + return part; +} + +void +EndPanel() +{ + PopParent(); +} + +void +DrawPanels(UIPanel* panel) +{ + if(!Nil(panel)) + { + debug + { + import core.stdc.math : fabsf; + + if(!Nil(panel.first)) + { + f32 total = 1.0; + for(UIPanel* child = panel.first; !Nil(child); child = child.next) + { + total -= child.pct; + } + + if(total > 0.00009) + { + Logf("%s failed with %f", cast(char[])panel.id, total); + assert(false); + } + } + } + + if(panel.ed) + { + EditorView(panel); + } + else + { + Panel(panel); + } + + DrawPanels(panel.first); + + if(panel.ed == null) + { + EndPanel(); + } + + DrawPanels(panel.next); + } +} + +UIPanel* +Recurse(UIPanel* panel) +{ + UIPanel* result = g_UI_NIL_PANEL; + if(!Nil(panel.first)) + { + result = panel.first; + } + else if(!Nil(panel.next)) + { + result = panel.next; + } + else for(UIPanel* p = panel.parent; !Nil(p); p = p.parent) + { + if(!Nil(p.next)) + { + result = p.next; + break; + } + } + + return result; +} + bool CheckPanelBounds(f32 pct) { @@ -164,58 +324,44 @@ CheckPanelBounds(f32 pct) } void -Separator(UIItem* item, f32 x_size, f32 y_size, Axis2D axis) +PushPanel(UIPanel* parent, UIPanel* panel) { - SetColor(Vec4(0.1, 0.2, 0.6, 1.0)); - - SizeType x_t = axis == A2D.X ? ST.Pixels : ST.Percentage; - SizeType y_t = axis == A2D.Y ? ST.Pixels : ST.Percentage; - - BuildItem(item, UISize(x_t, x_size), UISize(y_t, y_size), UIF.DrawBackground|UIF.Draggable); + DLLPush(parent, panel, g_UI_NIL_PANEL); + panel.parent = parent; } void -EditorView(Editor* ed) +InsertPanel(UIPanel* parent, UIPanel* prev, UIPanel* panel) { - UICtx* ctx = GetCtx(); - UIItem* parent = PeekParent(); - - if (ed.buf.length > 0) - { - - } -} - -UIItem* -TextLine(u8[] text, f32 text_size, u64 line_no) -{ - UIItem* item = g_UI_NIL; - UIItem* parent = PeekParent(); - - SetColor(Vec4(1.0)); - SetTextSize(text_size); - - Node!(u8[])* lines = MakeMultiline(text, parent.size.x, line_no); - for(Node!(u8[])* line = lines; line != null; line = line.next) - { - item = Get(line.value); - - Signal(item); - - if (item.signal & UIS.Clicked) - { - ClickedCharIndex(item); - } - - BuildItem(item, UISize(ST.Percentage, 1.0), UISize(ST.Pixels, text_size), UIF.DrawText|UIF.Clickable|UIF.Draggable); - } - - return item; + DLLInsert(parent, panel, prev, g_UI_NIL_PANEL); + panel.parent = prev.parent; } void -EndPanel() +InitWidgets() { - PopParent(); - PopPanel(); + g_UI_NIL_PANEL = cast(UIPanel*)&g_ui_nil_panel; + g_widget_ctx.parent = g_UI_NIL_PANEL; +} + +void +SetFocusedPanel(UIPanel* panel) +{ + if(!CheckNil(g_UI_NIL_PANEL, panel)) + { + g_widget_ctx.focused_panel = panel; + SetFocus(panel.ed != null ? Get(panel.ed.filename) : Get(panel.id)); + } +} + +UIPanel* +GetFocusedPanel() +{ + return Nil(g_widget_ctx.focused_panel) ? g_UI_NIL_PANEL : g_widget_ctx.focused_panel; +} + +bool +Nil(UIPanel* panel) +{ + return panel == null || panel == g_UI_NIL_PANEL; } diff --git a/src/shaders/gui.frag.glsl b/src/shaders/gui.frag.glsl index 20399b1..665ae18 100644 --- a/src/shaders/gui.frag.glsl +++ b/src/shaders/gui.frag.glsl @@ -54,7 +54,7 @@ void main() float sdf_factor = 1.0 - smoothstep(0, 2*softness, dist); float border_factor = 1.0; - if (FD.border_thickness != 0.0) + if(FD.border_thickness != 0.0) { vec2 interior_half_size = FD.dst_half_size - vec2(FD.border_thickness); @@ -72,7 +72,7 @@ void main() vec4 gamma = vec4(1.0/1.4); vec4 tex_color = vec4(1.0); - if (in_has_texture != 0) + if(in_has_texture != 0) { tex_color = texture(sampler2D(SpriteAtlas, SamplerNearest), FD.uv); //tex_color = pow(tex_color, gamma); diff --git a/test/files/syntax_highlighting.d b/test/files/syntax_highlighting.d index 1c96f36..6326531 100644 --- a/test/files/syntax_highlighting.d +++ b/test/files/syntax_highlighting.d @@ -25,7 +25,7 @@ Test() x ~= x; // Commented here - if (x > 1L && y < 2u || x <= 3U && y >= 4Lu || x == 5LU && x != 6uL || x == 7UL) + if(x > 1L && y < 2u || x <= 3U && y >= 4Lu || x == 5LU && x != 6uL || x == 7UL) { return true; }