diff --git a/src/dlib b/src/dlib index 7f4c109..8098145 160000 --- a/src/dlib +++ b/src/dlib @@ -1 +1 @@ -Subproject commit 7f4c109106eaabda180158a8f7a92c9d2e60d3db +Subproject commit 809814577db807d5911e79216d6e114a2d5a2dfd diff --git a/src/editor/ui.d b/src/editor/ui.d index cce574f..b9fd2f9 100644 --- a/src/editor/ui.d +++ b/src/editor/ui.d @@ -154,7 +154,11 @@ struct UIInput UIInput* next, prev; union { - Input key; + struct + { + Input key; + string text; + }; IVec2 rel; IVec2 pos; } @@ -168,8 +172,8 @@ UICtxParameter(T, string name) { import std.traits, std.array; - string stack_top = "\tStackTop!("~T.stringof~") "~name~";\n"; - string stack = "\tStack!("~T.stringof~")* "~name~"_top;\n"; + string stack_top = "\tStackTop!("~T.stringof~") "~name~";\n"; + string stack = "\tStack!("~T.stringof~")* "~name~"_top;\n"; return stack_top~stack; } @@ -177,7 +181,8 @@ UICtxParameter(T, string name) mixin(CtxParameterGen!(T, name)()); } -template CtxMemberInfo(int i) +template +CtxMemberInfo(int i) { import std.string : endsWith, startsWith; import std.traits : isInstanceOf, isPointer; @@ -274,31 +279,32 @@ struct StackTop(T) Stack!(T)* free; } -mixin template UIItemParameters() +static string +UIItemParameterGen() { - static string - UIItemParameterGen() + string fields = ""; + static foreach(i, m; UICtx.tupleof) { - string fields = ""; - static foreach(i, m; UICtx.tupleof) { + enum info = CtxMemberInfo!(i); + static if(info.is_stack) { - enum info = CtxMemberInfo!(i); - static if(info.is_stack) + fields ~= typeof(UICtx.tupleof[i].top.value).stringof ~ " " ~ info.id; + static if(is(typeof(UICtx.tupleof[i]): f32)) { - fields ~= typeof(UICtx.tupleof[i].top.value).stringof ~ " " ~ info.id; - static if(is(typeof(UICtx.tupleof[i]): f32)) - { - fields ~= " = 0.0"; - } - fields ~= ";\n"; + fields ~= " = 0.0"; } + fields ~= ";\n"; } } - - return fields; } + return fields; +} + +mixin template +UIItemParameters() +{ mixin(UIItemParameterGen()); } @@ -677,17 +683,21 @@ InitSingleLine: { if(item.flags & (UIF.ScrollX << axis)) { - item.scroll_offset.v[axis] += scroll_speed * (item.scroll_target.v[axis] - item.scroll_offset.v[axis]); - - if(fabsf(item.scroll_offset.v[axis] - item.scroll_target.v[axis]) < 2.0) + if(fabsf(item.scroll_offset.v[axis] - item.scroll_target.v[axis]) > 0.0009) { - item.scroll_offset.v[axis] = item.scroll_target.v[axis]; + f32 v = scroll_speed * (item.scroll_target.v[axis] - item.scroll_offset.v[axis]); + item.scroll_offset.v[axis] += v; + + if(fabsf(item.scroll_offset.v[axis] - item.scroll_target.v[axis]) < 2.0) + { + item.scroll_offset.v[axis] = item.scroll_target.v[axis]; + } } } if(item.flags & (UIF.ClampX << axis)) { - item.scroll_offset = clamp(item.scroll_offset.v[axis], item.scroll_clamp[axis].x, item.scroll_clamp[axis].y); + item.scroll_offset.v[axis] = clamp(item.scroll_offset.v[axis], item.scroll_clamp[axis].x, item.scroll_clamp[axis].y); } } @@ -822,7 +832,7 @@ BeginUI(Inputs* inputs) } break; default: { - PushUIEvent(ctx, UIInput(type: UIE.Press, key: i.key)); + PushUIEvent(ctx, UIInput(type: UIE.Press, key: i.key, text: i.pressed ? i.text : [])); } break; } } @@ -851,7 +861,7 @@ BeginUI(Inputs* inputs) // Ctx state ctx.f_idx = ctx.frame%FRAME_OVERLAP; ctx.inputs = inputs; - ctx.char_width = GlyphWidth(&ctx.atlas_buf.atlas.glyphs['0']); + ctx.char_width = GlyphWidth(&ctx.atlas_buf.atlas.glyphs[' ']); ResetStacks(ctx); @@ -880,13 +890,13 @@ BeginUI(Inputs* inputs) // Root Item UISize[2] sizes = [UISize(ST.Pixels, ctx.res.x), UISize(ST.Pixels, ctx.res.y)]; - Push!("size_info")(ctx, sizes); + Push!("size_info")(sizes); ctx.root = MakeItem("###root"); ctx.window_root = MakeItem("###window_root"); - Pop!("size_info")(ctx); - Push!("parent")(ctx, ctx.root); + Pop!("size_info"); + Push!("parent")(ctx.root); } void @@ -999,7 +1009,7 @@ EndUI() { f32 size = InnerSize!(axis)(item); - if(item.flags & (UIF.PortalX << axis)) + if(item.flags & (UIF.PortalViewX << axis)) { continue; } @@ -1067,7 +1077,7 @@ EndUI() } else if(item == item.parent.first) { - pos += item.parent.padding.v[axis]; + pos += item.parent.padding.v[axis] + item.parent.view_offset.v[axis]; } f32 inner_pos = pos; @@ -1082,6 +1092,7 @@ EndUI() assert(!isNaN(item.rect.p0.v[axis])); assert(!isNaN(item.rect.p1.v[axis])); + /* debug { bool portal = cast(bool)(item.parent.flags & (UIF.PortalViewX << axis)); @@ -1103,6 +1114,7 @@ EndUI() assert(in_bounds_start && in_bounds_end); } } + */ if(!Nil(item.first)) { @@ -1169,8 +1181,8 @@ RenderItem(UICtx* ctx, UIItem* item) { DrawUI(ctx); - u32 x = cast(u32)(scissor_x ? floor(item.rect.p0.x) : ctx.res.x); - u32 y = cast(u32)(scissor_y ? floor(item.rect.p0.y) : ctx.res.y); + u32 x = cast(u32)(scissor_x ? floor(item.rect.p0.x) : 0); + u32 y = cast(u32)(scissor_y ? floor(item.rect.p0.y) : 0); u32 w = cast(u32)(scissor_x ? floor(item.rect.p1.x) - x : ctx.res.x); u32 h = cast(u32)(scissor_y ? floor(item.rect.p1.y) - y : ctx.res.y); SetScissor(&ctx.rd, x, y, w, h); @@ -1178,7 +1190,6 @@ RenderItem(UICtx* ctx, UIItem* item) if(item.flags & UIF.DrawBackground) { - // DrawRect Vertex* v = GetVertex(ctx); v.dst_start = item.rect.p0 + item.border_thickness; v.dst_end = item.rect.p1 - item.border_thickness; @@ -1284,23 +1295,117 @@ GetExtent(Renderer* rd) } template -StackIDs(string stack) +StackIDs(string stack, string ctx = "ctx") { import std.string : replace; struct Identifiers { string stack, stack_top_node; } enum Identifiers StackIDs = { - stack: replace("ctx.@", "@", stack), - stack_top_node: replace("ctx.@_top", "@", stack), + stack: replace(ctx~".@", "@", stack), + stack_top_node: replace(ctx~".@_top", "@", stack), }; } void -Push(string stack_str, bool auto_pop = false, T)(UICtx* ctx, T value) +PushSizeInfoVec(int i)(SizeType type, f32 value, f32 strictness = 1.0, bool auto_pop = false) +{ + UISize[2] size_info = g_size_info_default; + + size_info[i].type = type; + size_info[i].value = value; + size_info[i].strictness = strictness; + + Push!("size_info")(size_info, auto_pop); +} + +void +PushSizeInfoX(SizeType type, f32 value, f32 strictness = 1.0, bool auto_pop = false) +{ + PushSizeInfoVec!(0)(type, value, strictness, auto_pop); +} + +void +PushSizeInfoY(SizeType type, f32 value, f32 strictness = 1.0, bool auto_pop = false) +{ + PushSizeInfoVec!(1)(type, value, strictness, auto_pop); +} + +void +PushScrollClampVec(int i)(Vec2 clamp, bool auto_pop) +{ + Vec2[2] scroll_clamp = g_scroll_clamp_default; + scroll_clamp[i] = clamp; + Push!("scroll_clamp")(scroll_clamp, auto_pop); +} + +void +PushScrollClampX(f32 start, f32 end, bool auto_pop = false) +{ + PushScrollClampVec!(0)(Vec2(start, end), auto_pop); +} + +void +PushScrollClampY(f32 start, f32 end, bool auto_pop = false) +{ + PushScrollClampVec!(1)(Vec2(start, end), auto_pop); +} + +void +PushBorderCol(Vec4 col, bool auto_pop = false) +{ + Vec4[4] arr = col; + PushBorderCol(arr, auto_pop); +} + +void +PushBgCol(Vec4 col, bool auto_pop = false) +{ + Vec4[4] arr = col; + PushBgCol(arr, auto_pop); +} + +void +PushPaddingX(f32 padding, bool auto_pop = false) +{ + PushPadding(Vec2(padding, 0.0), auto_pop); +} + +void +PushPaddingY(f32 padding, bool auto_pop = false) +{ + PushPadding(Vec2(0.0, padding), auto_pop); +} + +void +PushViewOffsetX(f32 offset, bool auto_pop = false) +{ + PushViewOffset(Vec2(offset, 0.0), auto_pop); +} + +void +PushViewOffsetY(f32 offset, bool auto_pop = false) +{ + PushViewOffset(Vec2(0.0, offset), auto_pop); +} + +void +PushScrollTargetX(f32 target, bool auto_pop = false) +{ + PushScrollTarget(Vec2(target, 0.0), auto_pop); +} + +void +PushScrollTargetY(f32 target, bool auto_pop = false) +{ + PushScrollTarget(Vec2(0.0, target), auto_pop); +} + +void +Push(string stack_str, T)(T value, bool auto_pop = false) { import std.string : replace; - enum ids = StackIDs!(stack_str); + enum ids = StackIDs!(stack_str, "g_ui_ctx"); auto stack = &mixin(ids.stack); auto top = mixin(ids.stack_top_node); @@ -1312,7 +1417,7 @@ Push(string stack_str, bool auto_pop = false, T)(UICtx* ctx, T value) } else { - node = Alloc!(Stack!(T))(&ctx.temp_arena); + node = Alloc!(Stack!(T))(&g_ui_ctx.temp_arena); } node.next = stack.top; @@ -1322,12 +1427,29 @@ Push(string stack_str, bool auto_pop = false, T)(UICtx* ctx, T value) stack.top = node; } +static string +PushScope(string stack_str, alias __value)() +{ + enum string id = __traits(identifier, __value); + static if(id == "__value") + { + string value_str = "cast(typeof("~StackIDs!(stack_str, "g_ui_ctx").stack~".top.value))(" ~ __value.stringof ~ ")"; + } + else + { + string value_str = __traits(identifier, __value); + } + + + return "Push!(\"" ~ stack_str ~ "\")(" ~ value_str ~ ");\n scope(exit) Pop!(\"" ~ stack_str ~ "\");"; +} + auto -Pop(string stack_str)(UICtx* ctx) +Pop(string stack_str)() { import std.string : replace; - enum ids = StackIDs!(stack_str); + enum ids = StackIDs!(stack_str, "g_ui_ctx"); auto stack = &mixin(ids.stack); auto top = mixin(ids.stack_top_node); @@ -1345,14 +1467,52 @@ Pop(string stack_str)(UICtx* ctx) } void -Pop(stack_strs...)(UICtx* ctx) +Pop(stack_strs...)() { static foreach(stack; stack_strs) { - Pop!(stack)(ctx); + Pop!(stack)(); } } +static string +GenPushFuncs() +{ + import std.array : split; + import std.uni : toUpper; + + string funcs = ""; + static foreach(i, m; UICtx.tupleof) + { + { + enum info = CtxMemberInfo!(i); + static if(info.is_stack) + { + string[] parts = split(info.id, "_"); + + string fn_name = "Push"; + foreach(p; parts) + { + if(p.length == 1) + { + fn_name ~= toUpper(p[0 .. 1]); + } + else + { + fn_name ~= toUpper(p[0 .. 1]) ~ p[1 .. $]; + } + } + + funcs ~= "pragma(inline) void " ~ fn_name ~ "(T)(T value, bool auto_pop = false){ Push!(\"" ~ info.id ~ "\")(value, auto_pop); }\n"; + } + } + } + + return funcs; +} + +mixin(GenPushFuncs()); + void AutoPopStacks(UICtx* ctx) { @@ -1368,7 +1528,7 @@ AutoPopStacks(UICtx* ctx) if(ctx.tupleof[i].top.auto_pop) { ctx.tupleof[i].top.auto_pop = false; - Pop!(member_info.id)(ctx); + Pop!(member_info.id); } } } @@ -1595,8 +1755,6 @@ NewItem(UICtx* ctx) item = Alloc!(UIItem)(&ctx.arena); } - item.last_frame = ctx.frame-2; - return item; } @@ -1666,8 +1824,7 @@ CalcTextWidth(string str) pragma(inline) f32 GlyphWidth(Glyph* g) { - f32 width = 0.0; - width += g.ch == '\t' ? (g_ui_ctx.atlas_buf.atlas.glyphs[' '].advance*cast(f32)(g_ui_ctx.tab_width)) : g.advance; + f32 width = g.ch == '\t' ? (g_ui_ctx.atlas_buf.atlas.glyphs[' '].advance*cast(f32)(g_ui_ctx.tab_width)) : g.advance; return width; } @@ -1962,11 +2119,22 @@ Dragged(UIItem* item, Rect* rect) return result; } -pragma(inline) Vec4[4] -Vec4Arr(Vec4 col) +static Vec4[4] +Vec4Arr(Vec4 vec) { - Vec4[4] arr = col; - return arr; + return [vec, vec, vec, vec]; +} + +static Vec2[2] +Vec2ArrX(alias Vec2 vec)() +{ + return [vec, Vec2(0.0)]; +} + +static Vec2[2] +Vec2ArrY(alias Vec2 vec)() +{ + return [Vec2(0.0), vec]; } unittest @@ -1995,7 +2163,7 @@ unittest Vec4 w = Vec4(1.0); Vec4[4] col = w; - Push!("bg_col")(ctx, col); + Push!("bg_col")(col); assert(ctx.bg_col.top.value == col); EndUI(); @@ -2011,7 +2179,7 @@ unittest { BeginUI(&inputs); - Push!("size_info")(ctx, MakeUISizeX(ST.Percentage, 0.5)); + Push!("size_info")(MakeUISizeX(ST.Percentage, 0.5)); UIItem* root = ctx.root; UIItem* i0 = MakeItem("###i0"); diff --git a/src/editor/views.d b/src/editor/views.d index 4d8661a..075321b 100644 --- a/src/editor/views.d +++ b/src/editor/views.d @@ -14,6 +14,93 @@ Nil(UIPanel* panel) return panel == null || panel == g_UI_NIL_PANEL; } +void +LineCounterView(u64 max_line, u64 lines, u64 line_offset, f32 view_offset) +{ + UICtx* ctx = GetCtx(); + UIKey zero = ZeroKey(); + + u64 ch_width = max_line.toChars().length; + f32 lc_width = cast(f32)(ch_width+1)*ctx.char_width; // Should figure out how to accurately measure text width + + PushLayoutAxis(A2D.Y, true); + PushViewOffsetY(view_offset, true); + PushPaddingX(4.0, true); + PushSizeInfoX(ST.Pixels, lc_width, 1.0, true); + + UIItem* line_count = MakeItem(zero, UIF.DrawBorder|UIF.PortalViewY); + + PushTextCol(Vec4(1.0)); + PushSizeInfoY(ST.Pixels, TEXT_SIZE); + PushParent(line_count); + + u64 end_line = lines+line_offset; + for(u64 i = line_offset; i < end_line; i += 1) + { + char[] buf = ScratchAlloc!(char)(ch_width); + Push!("display_string")(ConvToStr(sformat(buf, "%s", i)), true); + MakeItem(zero, UIF.DrawText); + } + + Pop!("text_col", "size_info", "parent"); +} + +void +EditorTextView(UIItem* editor, Editor* ed, u64 lines, u64 line_offset, f32 view_offset) +{ + PushLayoutAxis(A2D.Y, true); + + f32 clamp_y = cast(f32)(ed.buf.line_count-lines)*TEXT_SIZE; + f32 scroll_pos = cast(f32)(ed.line_offset)*TEXT_SIZE; + + PushLayoutAxis(A2D.Y, true); + PushScrollClampY(0.0, clamp_y, true); + PushSizeInfo(g_size_info_default, true); + PushBorderCol(Vec4(1.0), true); + PushScrollTargetY(scroll_pos, true); + PushViewOffsetY(view_offset, true); + + static bool end; + static f32 count = 0.0; + + if(!end) + { + ed.cursor_pos.y = 500; + count += g_delta; + if(count > 0.5) + { + end = !end; + } + } + else + { + ed.cursor_pos.y = 0; + count -= g_delta; + if(count <= 0.0) + { + end = !end; + } + } + + editor = MakeItem(editor.key, UIF.DrawBorder|UIF.ScrollY|UIF.ClampY); + + u64 end_line = line_offset+lines; + + UIKey zero = ZeroKey(); + + PushSizeInfoY(ST.TextSize, 1.0); + PushParent(editor); + + scope(exit) Pop!("size_info", "parent"); + + u64 i = line_offset; + for(LineBuffer* lb = GetLine(&ed.buf, i); !CheckNil(g_NIL_LINE_BUF, lb) && i < line_offset+lines; i += 1, lb = GetLine(&ed.buf, i)) + { + PushDisplayString(ConvToStr(lb.text), true); + UIItem* line = MakeItem(zero, UIF.DrawText); + } +} + void EditorView(Editor* ed) { @@ -23,83 +110,44 @@ EditorView(Editor* ed) UIItem* editor = Get(ed_key); u64 frame_line_offset = ed.line_offset; - f32 frame_view_offset = editor.scroll_offset.y%TEXT_SIZE; + f32 frame_view_offset = -(editor.scroll_offset.y%TEXT_SIZE); + + PushBgCol(Vec4(0.2, 0.3, 0.8, 1.0), true); + PushSizeInfoX(ST.Percentage, 1.0, 1.0, true); - Push!("bg_col", true)(ctx, Vec4Arr(Vec4(0.2, 0.3, 0.8, 1.0))); - Push!("size_info", true)(ctx, MakeUISizeX(ST.Percentage, 1.0)); UIItem* container = MakeItem(zero, UIF.DrawBackground); - Push!("parent")(ctx, container); + PushParent(container); + PushBorderCol(Vec4(1.0)); - Push!("border_col")(ctx, Vec4Arr(Vec4(1.0))); - - // Line Counter - f32 lc_width = ed.buf.line_count.toChars().length*ctx.char_width; - - Push!("layout_axis", true)(ctx, A2D.Y); - Push!("view_offset" )(ctx, Vec2(0.0, frame_view_offset)); - Push!("padding", true)(ctx, Vec2(4.0, 0.0)); - Push!("size_info", true)(ctx, MakeUISizeX(ST.Pixels, lc_width)); - - UIItem* line_count = MakeItem(zero, UIF.DrawBorder); - - // Editor - f32 scroll_pos = cast(f32)(ed.line_offset)*TEXT_SIZE; - - Push!("layout_axis", true)(ctx, A2D.Y); - Push!("scroll_clamp", true)(ctx, cast(Vec2[2])[Vec2(0.0), Vec2(0.0, cast(f32)(ed.buf.line_count)*TEXT_SIZE)]); - Push!("size_info", true)(ctx, MakeUISize(UISize(ST.Percentage, 1.0), UISize(ST.Percentage, 1.0))); - Push!("border_col", true)(ctx, Vec4Arr(Vec4(1.0))); - Push!("scroll_target", true)(ctx, Vec2(0.0, scroll_pos)); - - editor = MakeItem(ed_key, UIF.DrawBorder|UIF.ScrollY|UIF.ClampY); - - Pop!("view_offset")(ctx); + scope(exit) Pop!("parent", "border_col"); u64 view_lines; if(editor.size.y > 0.0) { - view_lines = cast(u64)ceil(editor.size.y/TEXT_SIZE); + view_lines = cast(u64)ceil(editor.size.y/TEXT_SIZE)+1; - const u64 SCROLL_BUFFER = 2; + const u64 SCROLL_BUFFER = 4; u64 start = ed.line_offset; u64 end = start+view_lines; if(ed.cursor_pos.y < start) { - ed.line_offset = clamp(ed.cursor_pos.y - SCROLL_BUFFER, 0, ed.buf.line_count); + u64 pos = ed.cursor_pos.y > SCROLL_BUFFER ? ed.cursor_pos.y - SCROLL_BUFFER : ed.cursor_pos.y; + ed.line_offset = clamp(pos, 0, ed.buf.line_count); } else if(ed.cursor_pos.y > end) { ed.line_offset = clamp(ed.cursor_pos.y + SCROLL_BUFFER - view_lines, 0, ed.buf.line_count); - } + } } - Push!("size_info")(ctx, MakeUISizeY(ST.Pixels, TEXT_SIZE)); - Push!("parent" )(ctx, line_count); - Push!("text_col" )(ctx, Vec4(1.0)); + u64 start = cast(u64)floor(editor.scroll_offset.y/TEXT_SIZE); - u64 end_line = frame_line_offset+view_lines; + LineCounterView(ed.buf.line_count, view_lines, start, frame_view_offset); - for(u64 i = frame_line_offset; i < end_line; i += 1) - { - MakeItem("%s##ed_%s", i, ed.editor_id, UIF.DrawText); - } - - Pop!("parent", "size_info")(ctx); - - Push!("size_info")(ctx, MakeUISizeY(ST.TextSize, 1.0)); - Push!("parent")(ctx, editor); - - u64 i = frame_line_offset; - for(LineBuffer* lb = GetLine(&ed.buf, i); !CheckNil(g_NIL_LINE_BUF, lb) && i < end_line; i += 1, lb = GetLine(&ed.buf, i)) - { - Push!("display_string", true)(ctx, ConvToStr(lb.text)); - UIItem* line = MakeItem(zero, UIF.DrawText); - } - - Pop!("parent", "parent", "text_col")(ctx); + EditorTextView(editor, ed, view_lines, start, frame_view_offset); } /*