diff --git a/src/editor/ui.d b/src/editor/ui.d index 82fa5be..0005417 100644 --- a/src/editor/ui.d +++ b/src/editor/ui.d @@ -104,48 +104,49 @@ alias A2D = Axis2D; enum UIFlags : u64 { None = 0, - DrawBackground = 1<<0, - DrawText = 1<<1, - DrawBorder = 1<<2, - Clickable = 1<<3, - Draggable = 1<<4, - ScrollX = 1<<5, - ScrollY = 1<<6, - ClampX = 1<<7, - ClampY = 1<<8, - Resizeable = 1<<9, - ResizeAdjacent = 1<<10, - FloatingWindow = 1<<11, // todo - Window = 1<<12, - TextInput = 1<<13, // todo - RightAlignText = 1<<14, - CenterAlignText = 1<<15, // todo - TextWrap = 1<<16, - PortalViewX = 1<<17, - PortalViewY = 1<<18, - FixedPosition = 1<<19, - FixedPosAnimated = 1<<20, // todo: add fixed_pos_target then interpolate position every frame - OverflowX = 1<<21, - OverflowY = 1<<22, - Gradient = 1<<23, - VerticalAlignText = 1<<24, - SetHot = 1<<25, - SetReady = 1<<26, - 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, + DrawBackground = 1LU<<0, + DrawText = 1LU<<1, + DrawBorder = 1LU<<2, + Clickable = 1LU<<3, + Draggable = 1LU<<4, + ScrollX = 1LU<<5, + ScrollY = 1LU<<6, + ClampX = 1LU<<7, + ClampY = 1LU<<8, + Resizeable = 1LU<<9, + ResizeAdjacent = 1LU<<10, + FloatingWindow = 1LU<<11, // todo + Window = 1LU<<12, + TextInput = 1LU<<13, // todo + RightAlignText = 1LU<<14, + CenterAlignText = 1LU<<15, // todo + TextWrap = 1LU<<16, + ScissorX = 1LU<<17, + ScissorY = 1LU<<18, + FixedPosition = 1LU<<19, + FixedPosAnimated = 1LU<<20, // todo: add fixed_pos_target then interpolate position every frame + OverflowX = 1LU<<21, + OverflowY = 1LU<<22, + Gradient = 1LU<<23, + VerticalAlignText = 1LU<<24, + SetHot = 1LU<<25, + SetReady = 1LU<<26, + SetActive = 1LU<<27, + AnimateReady = 1LU<<28, // Fade in/out + AnimateHot = 1LU<<29, // Hover highlight + AnimateActive = 1LU<<30, // Animate selected (probably do later) + DrawDropShadow = 1LU<<31, + ScrollFitChildren = 1LU<<32, Clamp = UIFlags.ClampX | UIFlags.ClampY, - PortalView = UIFlags.PortalViewX | UIFlags.PortalViewY, + Scissor = UIFlags.ScissorX | UIFlags.ScissorY, Scroll = UIFlags.ScrollX | UIFlags.ScrollY, Overflow = UIFlags.OverflowX | UIFlags.OverflowY, } alias UIF = UIFlags; const UIFlags AUTO_FLAGS = UIF.ResizeAdjacent; -const UIFlags DRAW_FLAGS = UIF.DrawBackground|UIF.DrawBorder|UIF.DrawText|UIF.PortalView|UIF.DrawDropShadow; +const UIFlags DRAW_FLAGS = UIF.DrawBackground|UIF.DrawBorder|UIF.DrawText|UIF.Scissor|UIF.DrawDropShadow; enum UISignal { @@ -388,10 +389,11 @@ struct UIItem string[] text_lines; u8[][] token_lines; Vec2 scroll_offset; + Vec2 scroll_size; u8[] text_buffer; f32 max_text_width; f32 resize_pct; - bool rendered; + bool processed; f32 ready_t; // Item visible f32 hot_t; // Item to be interacted with (e.g. hover) @@ -814,24 +816,6 @@ MakeItem(T)(T k, UIFlags flags = UIF.None) if(KeyType!(T)) static foreach(axis; A2D.min .. A2D.max) { - if(item.flags & (UIF.ScrollX << axis)) - { - if(fabsf(item.scroll_offset.v[axis] - item.scroll_target.v[axis]) > 0.0009) - { - f32 v = ctx.scroll_rate * (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.v[axis] = clamp(item.scroll_offset.v[axis], item.scroll_clamp[axis].x, item.scroll_clamp[axis].y); - } } if(item.flags & UIF.AnimateHot) @@ -854,7 +838,7 @@ MakeItem(T)(T k, UIFlags flags = UIF.None) if(KeyType!(T)) } item.last_frame = ctx.frame; - item.rendered = false; + item.processed = false; return item; } @@ -961,10 +945,9 @@ Signal(UIItem* item) assert(!ZeroKey(item.key), "Zero key passed to Signal"); + bool mouse_over = KeyEq(ctx.hover_key, item.key); if(item.signal == UIS.None && !ZeroKey(item.key)) { - bool mouse_over = KeyEq(ctx.hover_key, item.key); - if(mouse_over) { item.signal |= UIS.Hovered; @@ -1008,6 +991,21 @@ Signal(UIItem* item) } } } + else if(item.flags & UIF.ResizeAdjacent && ZeroKey(ctx.drag_key)) + { + for(UIInput* i = ctx.events.first; !CheckNil(g_UI_NIL_INPUT, i); i = i.next) + { + if(mouse_over && i.type == UIE.DragStart) + { + item.signal |= UIS.Dragged; + } + + if(item.signal & UIS.Dragged) + { + item.dragged += i.rel; + } + } + } } void @@ -1268,11 +1266,6 @@ EndUI() else if(item.size_info[axis].type == ST.TextSize) { f32 size = axis == A2D.X ? item.max_text_width : GetFontAtlas(item.text_size).atlas.line_height * item.text_lines.length; - if(axis == A2D.Y) - { - // Logf("line height %f %f %f", size, floor(size), AxisPadding!(axis)(item)); - - } item.size.v[axis] = floor(size) + AxisPadding!(axis)(item); } } @@ -1325,7 +1318,7 @@ EndUI() { f32 size = InnerSize!(axis)(item); - if(item.flags & (UIF.PortalViewX << axis)) + if(item.flags & ((UIF.ScissorX << axis) | (UIF.ScrollX << axis))) { continue; } @@ -1390,6 +1383,45 @@ EndUI() } } + // Scrollable Size Calculation & Offsets + for(UIItem* item = ctx.root_first; !Nil(item); item = Recurse!(true)(item, g_UI_NIL)) + { + if(item.flags & UIF.ScrollFitChildren) + { + f32 scroll_size = 0.0; + for(UIItem* c = item.first; !Nil(c); c = c.next) + { + scroll_size += c.size.v[axis]; + } + + item.scroll_size.v[axis] = scroll_size; + } + + if(item.flags & (UIF.ScrollX << axis)) + { + if(fabsf(item.scroll_offset.v[axis] - item.scroll_target.v[axis]) > 0.0009) + { + f32 v = ctx.scroll_rate * (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) | UIF.ScrollFitChildren)) + { + f32 upper = item.scroll_size.v[axis] >= item.size.v[axis] ? item.scroll_size.v[axis]-item.size.v[axis] : 0.0; + item.scroll_offset.v[axis] = clamp(item.scroll_offset.v[axis], 0.0, upper); + } + else if(item.flags & (UIF.ClampX << axis)) + { + item.scroll_offset.v[axis] = clamp(item.scroll_offset.v[axis], item.scroll_clamp[axis].x, item.scroll_clamp[axis].y); + } + } + // Calculate final sizes { f32 pos = 0.0; @@ -1431,8 +1463,24 @@ EndUI() assert(!isNaN(item.rect.p1.v[axis])); } } + + // Calculate offsets for scrollable windows + { + for(UIItem* item = ctx.root_first; !Nil(item); item = Recurse!(true)(item, g_UI_NIL)) + { + if(item.flags & (UIF.ScrollX << axis)) + { + for(UIItem* c = item.first; !Nil(c); c = c.next) + { + c.rect.p0.v[axis] -= item.scroll_offset.v[axis]; + c.rect.p1.v[axis] -= item.scroll_offset.v[axis]; + } + } + } + } } + // Render Items for(UIItem* item = ctx.root_first; !Nil(item); item = Recurse!(true)(item, g_UI_NIL)) { @@ -1487,6 +1535,22 @@ EndUI() } } +UIItem* +NextNonChild(UIItem* item) +{ + UIItem* first = item.first; + UIItem* last = item.last; + + item.first = item.last = g_UI_NIL; + + UIItem* next = Recurse!(true)(item, g_UI_NIL); + + item.first = first; + item.last = last; + + return next; +} + void FocusItem(Args...)(string str, Args args) { @@ -1509,7 +1573,7 @@ FocusItem(T)(T focus) if(ItemAndKeyType!(T)) pragma(inline) void RenderItem(UICtx* ctx, UIItem* item) { - if(item.rendered) return; + if(item.processed) return; if(!(item.flags & DRAW_FLAGS)) return; Vec2 border_p0 = item.rect.p0 - Vec2(InnerOffset!(A2D.X, true )(item), InnerOffset!(A2D.Y, true )(item)); @@ -1628,9 +1692,11 @@ RenderItem(UICtx* ctx, UIItem* item) } // Doesn't really support nesting scissors, will maybe change this later. - bool scissor_x = cast(bool)(item.flags & UIF.PortalViewX); - bool scissor_y = cast(bool)(item.flags & UIF.PortalViewY); - if(scissor_x || scissor_y) + bool scissor_x = cast(bool)(item.flags & UIF.ScissorX); + bool scissor_y = cast(bool)(item.flags & UIF.ScissorY); + scissor_x |= cast(bool)(item.flags & UIF.ScrollX); + scissor_y |= cast(bool)(item.flags & UIF.ScrollY); + if(scissor_x || scissor_y ) { DrawUI(ctx); @@ -1641,20 +1707,25 @@ RenderItem(UICtx* ctx, UIItem* item) SetScissor(&ctx.rd, x, y, w, h); - UIItem* first = item.first; - UIItem* last = item.last; - - item.first = item.last = g_UI_NIL; + UIItem* next = NextNonChild(item); + + bool scroll_x = cast(bool)(item.flags & UIF.ScrollX); + bool scroll_y = cast(bool)(item.flags & UIF.ScrollY); + if(scroll_x || scroll_y) + { + for(UIItem* d = item.first; !Nil(d) && d != next; d = Recurse!(true)(d, g_UI_NIL)) + { + if(scroll_x && (d.rect.p1.x < item.rect.p0.x || d.rect.p0.x > item.rect.p1.x)) { d.processed = true; continue; } + if(scroll_y && (d.rect.p1.y < item.rect.p0.y || d.rect.p0.y > item.rect.p1.y)) { d.processed = true; continue; } - UIItem* next = Recurse!(true)(item, g_UI_NIL); - - item.first = first; - item.last = last; - - for(UIItem* d = item.first; !Nil(d) && d != next; d = Recurse!(true)(d, g_UI_NIL)) + RenderItem(ctx, d); + d.processed = true; + } + } + else for(UIItem* d = item.first; !Nil(d) && d != next; d = Recurse!(true)(d, g_UI_NIL)) { RenderItem(ctx, d); - d.rendered = true; + d.processed = true; } DrawUI(ctx); diff --git a/src/editor/views.d b/src/editor/views.d index 10f7a81..803975e 100644 --- a/src/editor/views.d +++ b/src/editor/views.d @@ -70,7 +70,7 @@ LineCounterView(FontAtlasBuf* abuf, u64 max_line, u64 lines, i64 line_offset, f3 mixin(PushOnce!(lc_params)); - UIItem* line_count = MakeItem(ZERO, UIF.DrawBorder|UIF.PortalViewY); + UIItem* line_count = MakeItem(ZERO, UIF.DrawBorder|UIF.ScissorY); mixin(PushScope!("text_col", q{ Vec4(1.0) } )); mixin(PushScope!("size_info", q{ UISY(ST.Pixels, abuf.atlas.line_height) } )); @@ -97,20 +97,42 @@ EditorTextView(UIItem* editor, Panel* p, FontAtlasBuf* abuf, u64 lines, i64 line f32 scroll_pos = cast(f32)(ed.line_offset)*text_size; f32 padding = 8.0; + f32 scroll_target = 0.0; + static bool toggled = true; + static f32 t = 0.0; + if(toggled) + { + scroll_target = 0.0; + t += g_delta; + if(t > 0.5) + { + toggled = !toggled; + } + } + else + { + scroll_target = 1000.0; + t -= g_delta; + if(t < 0.0) + { + toggled = !toggled; + } + } + enum UIPushInfo[] text_view_params = [ { "layout_axis", q{ A2D.Y } }, { "border_col", q{ Vec4(1.0) } }, { "size_info", q{ UIS2() } }, - { "scroll_target", q{ Vec2(0.0, scroll_pos) } }, - { "scroll_clamp", q{ Vec2(0.0, clamp_y) } }, - { "view_offset", q{ Vec2(0.0, view_offset) } }, + { "scroll_target", q{ Vec2(0.0, scroll_target) } }, + //{ "scroll_clamp", q{ Vec2(0.0, clamp_y) } }, + //{ "view_offset", q{ Vec2(0.0, view_offset) } }, { "padding", q{ Vec2(4.0) } }, { "edge_softness", q{ 0.0 } }, { "border_thickness", q{ 1.0 } }, ]; mixin(PushOnce!(text_view_params)); - editor = MakeItem(editor.key, UIF.DrawBorder|UIF.ScrollY|UIF.ClampY|UIF.PortalViewY); + editor = MakeItem(editor.key, UIF.DrawBorder|UIF.ScrollY|UIF.ClampY|UIF.ScissorY|UIF.ScrollFitChildren); mixin(PushScope!("parent", q{ editor })); @@ -139,6 +161,12 @@ EditorTextView(UIItem* editor, Panel* p, FontAtlasBuf* abuf, u64 lines, i64 line mixin(PushScope!("size_info", q{ UISY(ST.TextSize) } )); mixin(PushScope!("syntax_highlight", q{ UISH.D } )); + for(u64 i = 0; i < 200; i += 1) + { + MakeItem("%s", i, UIF.DrawText); + } + + /* 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)) { @@ -148,6 +176,7 @@ EditorTextView(UIItem* editor, Panel* p, FontAtlasBuf* abuf, u64 lines, i64 line UIItem* line = MakeItem(zero, UIF.DrawText); } + */ } void