start work on some ui changes

This commit is contained in:
Matthew 2026-01-05 21:58:26 +11:00
parent dcb0d620c7
commit 57c56ce650
2 changed files with 182 additions and 82 deletions

View File

@ -104,48 +104,49 @@ alias A2D = Axis2D;
enum UIFlags : u64 enum UIFlags : u64
{ {
None = 0, None = 0,
DrawBackground = 1<<0, DrawBackground = 1LU<<0,
DrawText = 1<<1, DrawText = 1LU<<1,
DrawBorder = 1<<2, DrawBorder = 1LU<<2,
Clickable = 1<<3, Clickable = 1LU<<3,
Draggable = 1<<4, Draggable = 1LU<<4,
ScrollX = 1<<5, ScrollX = 1LU<<5,
ScrollY = 1<<6, ScrollY = 1LU<<6,
ClampX = 1<<7, ClampX = 1LU<<7,
ClampY = 1<<8, ClampY = 1LU<<8,
Resizeable = 1<<9, Resizeable = 1LU<<9,
ResizeAdjacent = 1<<10, ResizeAdjacent = 1LU<<10,
FloatingWindow = 1<<11, // todo FloatingWindow = 1LU<<11, // todo
Window = 1<<12, Window = 1LU<<12,
TextInput = 1<<13, // todo TextInput = 1LU<<13, // todo
RightAlignText = 1<<14, RightAlignText = 1LU<<14,
CenterAlignText = 1<<15, // todo CenterAlignText = 1LU<<15, // todo
TextWrap = 1<<16, TextWrap = 1LU<<16,
PortalViewX = 1<<17, ScissorX = 1LU<<17,
PortalViewY = 1<<18, ScissorY = 1LU<<18,
FixedPosition = 1<<19, FixedPosition = 1LU<<19,
FixedPosAnimated = 1<<20, // todo: add fixed_pos_target then interpolate position every frame FixedPosAnimated = 1LU<<20, // todo: add fixed_pos_target then interpolate position every frame
OverflowX = 1<<21, OverflowX = 1LU<<21,
OverflowY = 1<<22, OverflowY = 1LU<<22,
Gradient = 1<<23, Gradient = 1LU<<23,
VerticalAlignText = 1<<24, VerticalAlignText = 1LU<<24,
SetHot = 1<<25, SetHot = 1LU<<25,
SetReady = 1<<26, SetReady = 1LU<<26,
SetActive = 1<<27, SetActive = 1LU<<27,
AnimateReady = 1<<28, // Fade in/out AnimateReady = 1LU<<28, // Fade in/out
AnimateHot = 1<<29, // Hover highlight AnimateHot = 1LU<<29, // Hover highlight
AnimateActive = 1<<30, // Animate selected (probably do later) AnimateActive = 1LU<<30, // Animate selected (probably do later)
DrawDropShadow = 1<<31, DrawDropShadow = 1LU<<31,
ScrollFitChildren = 1LU<<32,
Clamp = UIFlags.ClampX | UIFlags.ClampY, Clamp = UIFlags.ClampX | UIFlags.ClampY,
PortalView = UIFlags.PortalViewX | UIFlags.PortalViewY, Scissor = UIFlags.ScissorX | UIFlags.ScissorY,
Scroll = UIFlags.ScrollX | UIFlags.ScrollY, Scroll = UIFlags.ScrollX | UIFlags.ScrollY,
Overflow = UIFlags.OverflowX | UIFlags.OverflowY, Overflow = UIFlags.OverflowX | UIFlags.OverflowY,
} }
alias UIF = UIFlags; alias UIF = UIFlags;
const UIFlags AUTO_FLAGS = UIF.ResizeAdjacent; 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 enum UISignal
{ {
@ -388,10 +389,11 @@ struct UIItem
string[] text_lines; string[] text_lines;
u8[][] token_lines; u8[][] token_lines;
Vec2 scroll_offset; Vec2 scroll_offset;
Vec2 scroll_size;
u8[] text_buffer; u8[] text_buffer;
f32 max_text_width; f32 max_text_width;
f32 resize_pct; f32 resize_pct;
bool rendered; bool processed;
f32 ready_t; // Item visible f32 ready_t; // Item visible
f32 hot_t; // Item to be interacted with (e.g. hover) 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) 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) 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.last_frame = ctx.frame;
item.rendered = false; item.processed = false;
return item; return item;
} }
@ -961,10 +945,9 @@ Signal(UIItem* item)
assert(!ZeroKey(item.key), "Zero key passed to Signal"); 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)) if(item.signal == UIS.None && !ZeroKey(item.key))
{ {
bool mouse_over = KeyEq(ctx.hover_key, item.key);
if(mouse_over) if(mouse_over)
{ {
item.signal |= UIS.Hovered; 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 void
@ -1268,11 +1266,6 @@ EndUI()
else if(item.size_info[axis].type == ST.TextSize) 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; 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); item.size.v[axis] = floor(size) + AxisPadding!(axis)(item);
} }
} }
@ -1325,7 +1318,7 @@ EndUI()
{ {
f32 size = InnerSize!(axis)(item); f32 size = InnerSize!(axis)(item);
if(item.flags & (UIF.PortalViewX << axis)) if(item.flags & ((UIF.ScissorX << axis) | (UIF.ScrollX << axis)))
{ {
continue; 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 // Calculate final sizes
{ {
f32 pos = 0.0; f32 pos = 0.0;
@ -1431,8 +1463,24 @@ EndUI()
assert(!isNaN(item.rect.p1.v[axis])); 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 // Render Items
for(UIItem* item = ctx.root_first; !Nil(item); item = Recurse!(true)(item, g_UI_NIL)) 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 void
FocusItem(Args...)(string str, Args args) FocusItem(Args...)(string str, Args args)
{ {
@ -1509,7 +1573,7 @@ FocusItem(T)(T focus) if(ItemAndKeyType!(T))
pragma(inline) void pragma(inline) void
RenderItem(UICtx* ctx, UIItem* item) RenderItem(UICtx* ctx, UIItem* item)
{ {
if(item.rendered) return; if(item.processed) return;
if(!(item.flags & DRAW_FLAGS)) return; if(!(item.flags & DRAW_FLAGS)) return;
Vec2 border_p0 = item.rect.p0 - Vec2(InnerOffset!(A2D.X, true )(item), InnerOffset!(A2D.Y, true )(item)); 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. // Doesn't really support nesting scissors, will maybe change this later.
bool scissor_x = cast(bool)(item.flags & UIF.PortalViewX); bool scissor_x = cast(bool)(item.flags & UIF.ScissorX);
bool scissor_y = cast(bool)(item.flags & UIF.PortalViewY); bool scissor_y = cast(bool)(item.flags & UIF.ScissorY);
if(scissor_x || scissor_y) scissor_x |= cast(bool)(item.flags & UIF.ScrollX);
scissor_y |= cast(bool)(item.flags & UIF.ScrollY);
if(scissor_x || scissor_y )
{ {
DrawUI(ctx); DrawUI(ctx);
@ -1641,20 +1707,25 @@ RenderItem(UICtx* ctx, UIItem* item)
SetScissor(&ctx.rd, x, y, w, h); SetScissor(&ctx.rd, x, y, w, h);
UIItem* first = item.first; UIItem* next = NextNonChild(item);
UIItem* last = item.last;
item.first = item.last = g_UI_NIL; 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); RenderItem(ctx, d);
d.processed = true;
item.first = first; }
item.last = last; }
else for(UIItem* d = item.first; !Nil(d) && d != next; d = Recurse!(true)(d, g_UI_NIL))
for(UIItem* d = item.first; !Nil(d) && d != next; d = Recurse!(true)(d, g_UI_NIL))
{ {
RenderItem(ctx, d); RenderItem(ctx, d);
d.rendered = true; d.processed = true;
} }
DrawUI(ctx); DrawUI(ctx);

View File

@ -70,7 +70,7 @@ LineCounterView(FontAtlasBuf* abuf, u64 max_line, u64 lines, i64 line_offset, f3
mixin(PushOnce!(lc_params)); 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!("text_col", q{ Vec4(1.0) } ));
mixin(PushScope!("size_info", q{ UISY(ST.Pixels, abuf.atlas.line_height) } )); 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 scroll_pos = cast(f32)(ed.line_offset)*text_size;
f32 padding = 8.0; 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 = [ enum UIPushInfo[] text_view_params = [
{ "layout_axis", q{ A2D.Y } }, { "layout_axis", q{ A2D.Y } },
{ "border_col", q{ Vec4(1.0) } }, { "border_col", q{ Vec4(1.0) } },
{ "size_info", q{ UIS2() } }, { "size_info", q{ UIS2() } },
{ "scroll_target", q{ Vec2(0.0, scroll_pos) } }, { "scroll_target", q{ Vec2(0.0, scroll_target) } },
{ "scroll_clamp", q{ Vec2(0.0, clamp_y) } }, //{ "scroll_clamp", q{ Vec2(0.0, clamp_y) } },
{ "view_offset", q{ Vec2(0.0, view_offset) } }, //{ "view_offset", q{ Vec2(0.0, view_offset) } },
{ "padding", q{ Vec2(4.0) } }, { "padding", q{ Vec2(4.0) } },
{ "edge_softness", q{ 0.0 } }, { "edge_softness", q{ 0.0 } },
{ "border_thickness", q{ 1.0 } }, { "border_thickness", q{ 1.0 } },
]; ];
mixin(PushOnce!(text_view_params)); 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 })); 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!("size_info", q{ UISY(ST.TextSize) } ));
mixin(PushScope!("syntax_highlight", q{ UISH.D } )); mixin(PushScope!("syntax_highlight", q{ UISH.D } ));
for(u64 i = 0; i < 200; i += 1)
{
MakeItem("%s", i, UIF.DrawText);
}
/*
u64 i = line_offset; 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)) 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); UIItem* line = MakeItem(zero, UIF.DrawText);
} }
*/
} }
void void