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
{
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;
UIItem* next = NextNonChild(item);
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);
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);

View File

@ -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