rework imgui, new auto layout system
This commit is contained in:
parent
3282b1e90f
commit
97d9996160
2
src/dlib
2
src/dlib
@ -1 +1 @@
|
|||||||
Subproject commit c7f77523c4b7c86b625149ef7fde76257f6fcf51
|
Subproject commit 421f903cffaa22c7d7f9115ae49daa45576adfa8
|
||||||
@ -48,8 +48,6 @@ struct Editor
|
|||||||
FontFace font;
|
FontFace font;
|
||||||
FontAtlasBuf atlas_buf;
|
FontAtlasBuf atlas_buf;
|
||||||
|
|
||||||
Stack!(UIPanel*) panel_stack;
|
|
||||||
|
|
||||||
UVec2 res;
|
UVec2 res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,8 +162,7 @@ CreateEditor(PlatformWindow* window, u8[] buffer_data, u8[] buffer_name)
|
|||||||
|
|
||||||
assert(CreateGraphicsPipeline(&editor.rd, &editor.pipeline, &ui_info), "Unable to build UI pipeline");
|
assert(CreateGraphicsPipeline(&editor.rd, &editor.pipeline, &ui_info), "Unable to build UI pipeline");
|
||||||
|
|
||||||
InitUIContext(&editor.rd);
|
InitUICtx(&editor.rd, editor.atlas_buf.atlas);
|
||||||
SetProp!(CtxP.FontAtlas)(editor.atlas_buf.atlas);
|
|
||||||
|
|
||||||
CreateImageView(&editor.rd, &editor.font_atlas, editor.atlas_buf.atlas.width, editor.atlas_buf.atlas.height, 4, editor.atlas_buf.data);
|
CreateImageView(&editor.rd, &editor.font_atlas, editor.atlas_buf.atlas.width, editor.atlas_buf.atlas.height, 4, editor.atlas_buf.data);
|
||||||
|
|
||||||
@ -182,66 +179,27 @@ void
|
|||||||
Cycle(Editor* ed, Inputs* inputs)
|
Cycle(Editor* ed, Inputs* inputs)
|
||||||
{
|
{
|
||||||
Reset(&ed.temp_arena);
|
Reset(&ed.temp_arena);
|
||||||
UIBeginBuild();
|
|
||||||
|
|
||||||
/*
|
BeginBuild();
|
||||||
f32 pos = 0.0;
|
|
||||||
f32 h = ed.atlas_buf.atlas.size;
|
|
||||||
DrawBuffer(ed, 0.0, 0.0, h, ed.active_buffer);
|
|
||||||
*/
|
|
||||||
|
|
||||||
u32 count = 0;
|
Panel("##panel_1", 1.0, 0.33, A2D.X, Vec4(0.2, 0.5, 0.8, 1.0));
|
||||||
static bool init = false;
|
|
||||||
static Timer timer;
|
|
||||||
static u32 box_count = 1;
|
|
||||||
|
|
||||||
if (!init)
|
Panel("##sub_panel_1", 0.125, 1.0, A2D.X, Vec4(0.2, 0.4, 0.5, 1.0));
|
||||||
{
|
EndPanel();
|
||||||
timer = CreateTimer();
|
|
||||||
init = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 prev_box_count = box_count;
|
Panel("##sub_panel_2", 0.675, 1.0, A2D.X, Vec4(0.9, 0.6, 0.5, 1.0));
|
||||||
g_delta = DeltaTime(&timer);
|
EndPanel();
|
||||||
for(auto ev = inputs.list.first; ev != null; ev = ev.next)
|
|
||||||
{
|
|
||||||
count += 1;
|
|
||||||
switch(ev.value.key)
|
|
||||||
{
|
|
||||||
case KBI.One: box_count = 1; break;
|
|
||||||
case KBI.Two: box_count = 2; break;
|
|
||||||
case KBI.Three: box_count = 3; break;
|
|
||||||
case KBI.Four: box_count = 4; break;
|
|
||||||
case KBI.Five: box_count = 5; break;
|
|
||||||
case KBI.Six: box_count = 6; break;
|
|
||||||
case KBI.Seven: box_count = 7; break;
|
|
||||||
case KBI.Eight: box_count = 8; break;
|
|
||||||
case KBI.Nine: box_count = 9; break;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PanelStart("##Base", Vec3(0.5));
|
Panel("##sub_panel_3", 0.2, 1.0, A2D.X, Vec4(1.0, 0.4, 0.5, 1.0));
|
||||||
|
EndPanel();
|
||||||
|
|
||||||
PanelStart("##horizontal1", Vec3(0.2, 0.3, 0.7));
|
EndPanel();
|
||||||
|
|
||||||
|
Panel("##panel_2", 1.0, 0.33, A2D.X, Vec4(0.5, 0.2, 0.45, 1.0));
|
||||||
|
EndPanel();
|
||||||
|
|
||||||
PanelStart("##horizontal2", Vec3(0.2, 0.3, 0.7));
|
Panel("##panel_3", 1.0, 0.33, A2D.X, Vec4(0.3, 0.7, 0.6, 1.0));
|
||||||
|
EndPanel();
|
||||||
char[128] buf;
|
|
||||||
f32 x_pct = 1.0/box_count;
|
|
||||||
foreach(i; 0 .. box_count)
|
|
||||||
{
|
|
||||||
buf.sformat("##%s", i);
|
|
||||||
PanelStart(cast(u8[])buf, Vec3(x_pct * i));
|
|
||||||
PanelEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
PanelEnd();
|
|
||||||
|
|
||||||
PanelStart("##horizontal3", Vec3(0.2, 0.5, 0.9));
|
|
||||||
PanelEnd();
|
|
||||||
|
|
||||||
PanelEnd();
|
|
||||||
|
|
||||||
BeginFrame(&ed.rd);
|
BeginFrame(&ed.rd);
|
||||||
|
|
||||||
@ -258,9 +216,7 @@ Cycle(Editor* ed, Inputs* inputs)
|
|||||||
|
|
||||||
Bind(&ed.rd, ed.pipeline, ed.desc_set);
|
Bind(&ed.rd, ed.pipeline, ed.desc_set);
|
||||||
|
|
||||||
UIFinishBuild();
|
EndBuild();
|
||||||
|
|
||||||
DrawUI();
|
|
||||||
|
|
||||||
FinishRendering(&ed.rd);
|
FinishRendering(&ed.rd);
|
||||||
|
|
||||||
|
|||||||
667
src/editor/ui.d
667
src/editor/ui.d
@ -10,9 +10,37 @@ import std.stdio;
|
|||||||
import editor;
|
import editor;
|
||||||
|
|
||||||
UIContext g_ui_ctx;
|
UIContext g_ui_ctx;
|
||||||
|
|
||||||
const UIItem g_ui_nil_item;
|
const UIItem g_ui_nil_item;
|
||||||
UIItem* g_UI_NIL;
|
UIItem* g_UI_NIL;
|
||||||
|
|
||||||
|
enum Axis2D
|
||||||
|
{
|
||||||
|
X,
|
||||||
|
Y,
|
||||||
|
Max
|
||||||
|
}
|
||||||
|
|
||||||
|
alias A2D = Axis2D;
|
||||||
|
|
||||||
|
enum UIFlags
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
DrawBackground,
|
||||||
|
DragX,
|
||||||
|
DragY,
|
||||||
|
}
|
||||||
|
|
||||||
|
alias UIF = UIFlags;
|
||||||
|
|
||||||
|
enum SizeType
|
||||||
|
{
|
||||||
|
Pixels,
|
||||||
|
Percentage,
|
||||||
|
}
|
||||||
|
|
||||||
|
alias ST = SizeType;
|
||||||
|
|
||||||
struct UIContext
|
struct UIContext
|
||||||
{
|
{
|
||||||
HashTable!(UIHash, UIItem*) items;
|
HashTable!(UIHash, UIItem*) items;
|
||||||
@ -22,18 +50,42 @@ struct UIContext
|
|||||||
UIBuffer buffer;
|
UIBuffer buffer;
|
||||||
|
|
||||||
UIItem* root;
|
UIItem* root;
|
||||||
|
UIItem* top_parent;
|
||||||
|
|
||||||
// UI builder state
|
|
||||||
FontAtlas atlas;
|
FontAtlas atlas;
|
||||||
Vec4[4] bg_cols;
|
Vec4[4] color;
|
||||||
Vec4[4] border_cols;
|
Vec4[4] border_color;
|
||||||
UISize[A2D.max] size_axes;
|
Vec4 text_color;
|
||||||
Rect padding;
|
|
||||||
u32 tab_width;
|
|
||||||
f32 text_scale;
|
|
||||||
UIItem* parent_stack;
|
|
||||||
Axis2D layout_axis;
|
Axis2D layout_axis;
|
||||||
Vec2 position;
|
}
|
||||||
|
|
||||||
|
struct UIItem
|
||||||
|
{
|
||||||
|
UIFlags flags;
|
||||||
|
UIKey key;
|
||||||
|
|
||||||
|
UIItem* next;
|
||||||
|
UIItem* prev;
|
||||||
|
UIItem* first; // first child
|
||||||
|
UIItem* last; // last child
|
||||||
|
UIItem* parent;
|
||||||
|
|
||||||
|
UIItem* stack_next;
|
||||||
|
|
||||||
|
// Build Parameters
|
||||||
|
Axis2D layout_axis;
|
||||||
|
UISize[2] size_info;
|
||||||
|
|
||||||
|
// Calculated Parameters
|
||||||
|
Vec2 size;
|
||||||
|
Rect rect;
|
||||||
|
Vec4[4] color;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct UISize
|
||||||
|
{
|
||||||
|
SizeType type;
|
||||||
|
f32 value;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct UIBuffer
|
struct UIBuffer
|
||||||
@ -45,67 +97,6 @@ struct UIBuffer
|
|||||||
u32 count;
|
u32 count;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum CtxProperty
|
|
||||||
{
|
|
||||||
None,
|
|
||||||
BGColor,
|
|
||||||
BorderColor,
|
|
||||||
SizeKind,
|
|
||||||
SizeValue,
|
|
||||||
SizeStrictness,
|
|
||||||
FontAtlas,
|
|
||||||
AxisX,
|
|
||||||
AxisY,
|
|
||||||
Padding,
|
|
||||||
TextScale,
|
|
||||||
TabWidth,
|
|
||||||
LayoutAxis,
|
|
||||||
Position,
|
|
||||||
}
|
|
||||||
|
|
||||||
alias CtxP = CtxProperty;
|
|
||||||
|
|
||||||
enum Axis2D
|
|
||||||
{
|
|
||||||
X,
|
|
||||||
Y,
|
|
||||||
Max,
|
|
||||||
}
|
|
||||||
|
|
||||||
alias A2D = Axis2D;
|
|
||||||
|
|
||||||
enum UIRecurse
|
|
||||||
{
|
|
||||||
PostOrder,
|
|
||||||
PreOrder,
|
|
||||||
}
|
|
||||||
|
|
||||||
alias UIR = UIRecurse;
|
|
||||||
|
|
||||||
enum UIProperties : u64
|
|
||||||
{
|
|
||||||
Clickable = (1<<0),
|
|
||||||
ViewScroll = (1<<1),
|
|
||||||
DrawText = (1<<2),
|
|
||||||
DrawBackground = (1<<3),
|
|
||||||
DrawBorder = (1<<4),
|
|
||||||
Resizeable = (1<<5),
|
|
||||||
Draggable = (1<<6),
|
|
||||||
}
|
|
||||||
|
|
||||||
alias UIP = UIProperties;
|
|
||||||
|
|
||||||
enum SizeKind : u64
|
|
||||||
{
|
|
||||||
None,
|
|
||||||
Pixels,
|
|
||||||
TextContent,
|
|
||||||
PercentOfParent,
|
|
||||||
ChildrenSum,
|
|
||||||
}
|
|
||||||
|
|
||||||
alias SK = SizeKind;
|
|
||||||
|
|
||||||
union Rect
|
union Rect
|
||||||
{
|
{
|
||||||
Vec2[2] v;
|
Vec2[2] v;
|
||||||
@ -127,55 +118,218 @@ alias UIHash = u64;
|
|||||||
|
|
||||||
alias UIPair = KVPair!(UIHash, UIItem*);
|
alias UIPair = KVPair!(UIHash, UIItem*);
|
||||||
|
|
||||||
struct UISize
|
|
||||||
{
|
|
||||||
SizeKind kind;
|
|
||||||
f32 value;
|
|
||||||
f32 strictness;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct UIItemRec
|
|
||||||
{
|
|
||||||
UIItem* next;
|
|
||||||
i32 push_count;
|
|
||||||
i32 pop_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct UIItem
|
|
||||||
{
|
|
||||||
UIProperties flags;
|
|
||||||
UIKey key;
|
|
||||||
|
|
||||||
UIItem* first;
|
|
||||||
UIItem* last;
|
|
||||||
UIItem* next;
|
|
||||||
UIItem* prev;
|
|
||||||
UIItem* parent;
|
|
||||||
|
|
||||||
UIItem* list_next;
|
|
||||||
UIItem* list_prev;
|
|
||||||
|
|
||||||
debug { u8[] debug_id; }
|
|
||||||
|
|
||||||
UISize[A2D.max] size;
|
|
||||||
Vec4[4] bg_color;
|
|
||||||
Vec4[4] border_color;
|
|
||||||
Rect padding;
|
|
||||||
f32[A2D.max] computed_size;
|
|
||||||
f32[A2D.max] fixed_position;
|
|
||||||
Rect rect;
|
|
||||||
f32 text_scale;
|
|
||||||
Axis2D layout_axis;
|
|
||||||
f32 hot_t;
|
|
||||||
f32 active_t;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct UIKey
|
struct UIKey
|
||||||
{
|
{
|
||||||
u8[] text;
|
u8[] text;
|
||||||
u64 hash;
|
u64 hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
InitUICtx(Renderer* rd, FontAtlas atlas)
|
||||||
|
{
|
||||||
|
g_UI_NIL = cast(UIItem*)&g_ui_nil_item;
|
||||||
|
|
||||||
|
Arena arena = CreateArena(MB(4));
|
||||||
|
|
||||||
|
MappedBuffer!(Vertex) m_vtx = CreateMappedBuffer!(Vertex)(rd, BT.Vertex, 5000);
|
||||||
|
MappedBuffer!(u32) m_idx = CreateMappedBuffer!(u32)(rd, BT.Index, 15000);
|
||||||
|
|
||||||
|
UIContext ctx = {
|
||||||
|
rd: rd,
|
||||||
|
items: CreateHashTable!(UIHash, UIItem*)(12),
|
||||||
|
arena: CreateArena(MB(4)),
|
||||||
|
atlas: atlas,
|
||||||
|
root: g_UI_NIL,
|
||||||
|
top_parent: g_UI_NIL,
|
||||||
|
buffer: {
|
||||||
|
mapped_vtx: m_vtx,
|
||||||
|
mapped_idx: m_idx,
|
||||||
|
vtx: m_vtx.data,
|
||||||
|
idx: m_idx.data,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
g_ui_ctx = ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
UIItem*
|
||||||
|
PopParent()
|
||||||
|
{
|
||||||
|
UIContext* ctx = GetCtx();
|
||||||
|
UIItem* parent = ctx.top_parent;
|
||||||
|
ctx.top_parent = ctx.top_parent.stack_next;
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PushParent(UIItem* parent)
|
||||||
|
{
|
||||||
|
UIContext* ctx = GetCtx();
|
||||||
|
parent.stack_next = ctx.top_parent;
|
||||||
|
ctx.top_parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SetColor(Vec4 col)
|
||||||
|
{
|
||||||
|
SetColor(col, col, col, col);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SetColor(Vec4 col0, Vec4 col1, Vec4 col2, Vec4 col3)
|
||||||
|
{
|
||||||
|
UIContext* ctx = GetCtx();
|
||||||
|
ctx.color[0] = col0;
|
||||||
|
ctx.color[1] = col1;
|
||||||
|
ctx.color[2] = col2;
|
||||||
|
ctx.color[3] = col3;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SetLayoutAxis(Axis2D axis)
|
||||||
|
{
|
||||||
|
UIContext* ctx = GetCtx();
|
||||||
|
ctx.layout_axis = axis;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
BeginBuild()
|
||||||
|
{
|
||||||
|
UIContext* ctx = GetCtx();
|
||||||
|
|
||||||
|
ctx.buffer.count = 0;
|
||||||
|
|
||||||
|
ctx.root = Root();
|
||||||
|
PushParent(ctx.root);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
EndBuild()
|
||||||
|
{
|
||||||
|
UIContext* ctx = GetCtx();
|
||||||
|
|
||||||
|
PopParent();
|
||||||
|
|
||||||
|
assert(Nil(ctx.top_parent));
|
||||||
|
|
||||||
|
CalcFixedSizes(ctx.root);
|
||||||
|
CalcPercentageSizes(ctx.root);
|
||||||
|
static foreach(axis; A2D.min .. A2D.max)
|
||||||
|
{
|
||||||
|
CalcPositions!(axis)(ctx.root);
|
||||||
|
}
|
||||||
|
DrawUI(ctx, ctx.root);
|
||||||
|
|
||||||
|
BindBuffers(g_ui_ctx.rd, &g_ui_ctx.buffer.mapped_idx, &g_ui_ctx.buffer.mapped_vtx);
|
||||||
|
DrawIndexed(g_ui_ctx.rd, 6, g_ui_ctx.buffer.count, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CalcFixedSizes(UIItem* item)
|
||||||
|
{
|
||||||
|
if (!Nil(item))
|
||||||
|
{
|
||||||
|
static foreach(axis; A2D.min .. A2D.max)
|
||||||
|
{
|
||||||
|
if (item.size_info[axis].type == ST.Pixels)
|
||||||
|
{
|
||||||
|
item.size.v[axis] = item.size_info[axis].value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CalcFixedSizes(item.first);
|
||||||
|
CalcFixedSizes(item.next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CalcPercentageSizes(UIItem* item)
|
||||||
|
{
|
||||||
|
if (!Nil(item))
|
||||||
|
{
|
||||||
|
CalcPercentageSizes(item.first);
|
||||||
|
CalcPercentageSizes(item.next);
|
||||||
|
|
||||||
|
static foreach(axis; A2D.min .. A2D.max)
|
||||||
|
{
|
||||||
|
if (item.size_info[axis].type == ST.Percentage)
|
||||||
|
{
|
||||||
|
UIItem* parent;
|
||||||
|
for(UIItem* p = item.parent; !Nil(p); p = p.parent)
|
||||||
|
{
|
||||||
|
if (p.size_info[axis].type == ST.Pixels)
|
||||||
|
{
|
||||||
|
parent = p;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
item.size.v[axis] = parent.size_info[axis].value * item.size_info[axis].value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CalcPositions(alias axis)(UIItem* item, f32 pos = 0.0)
|
||||||
|
{
|
||||||
|
if (!Nil(item))
|
||||||
|
{
|
||||||
|
f32 end_pos = pos + item.size.v[axis];
|
||||||
|
item.rect.vec0.v[axis] = pos;
|
||||||
|
item.rect.vec1.v[axis] = end_pos;
|
||||||
|
|
||||||
|
f32 next_pos = item.parent.layout_axis == axis ? end_pos : pos;
|
||||||
|
|
||||||
|
CalcPositions!(axis)(item.first, pos);
|
||||||
|
CalcPositions!(axis)(item.next, next_pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
DrawUI(UIContext* ctx, UIItem* item)
|
||||||
|
{
|
||||||
|
if (!Nil(item))
|
||||||
|
{
|
||||||
|
DrawRect(ctx, item);
|
||||||
|
|
||||||
|
DrawUI(ctx, item.first);
|
||||||
|
DrawUI(ctx, item.next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UIItem*
|
||||||
|
BuildItem(string id, UISize size_x, UISize size_y, UIFlags properties)
|
||||||
|
{
|
||||||
|
u8[] u8_id = CastStr!(u8)(id);
|
||||||
|
return BuildItem(u8_id, size_x, size_y, properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
UIItem*
|
||||||
|
BuildItem(u8[] id, UISize size_x, UISize size_y, UIFlags properties)
|
||||||
|
{
|
||||||
|
UIContext* ctx = GetCtx();
|
||||||
|
UIKey key = MakeKey(id);
|
||||||
|
|
||||||
|
UIItem* item = Get(key);
|
||||||
|
item.first = item.last = item.next = item.prev = g_UI_NIL;
|
||||||
|
|
||||||
|
item.key = key;
|
||||||
|
item.flags = properties;
|
||||||
|
item.size_info[A2D.X] = size_x;
|
||||||
|
item.size_info[A2D.Y] = size_y;
|
||||||
|
item.color = ctx.color;
|
||||||
|
item.layout_axis = ctx.layout_axis;
|
||||||
|
|
||||||
|
item.parent = ctx.top_parent;
|
||||||
|
if (!Nil(item.parent))
|
||||||
|
{
|
||||||
|
DLLPush(item.parent, item, g_UI_NIL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
UIContext*
|
UIContext*
|
||||||
GetCtx()
|
GetCtx()
|
||||||
{
|
{
|
||||||
@ -189,249 +343,6 @@ RootSize()
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
UIBeginBuild()
|
|
||||||
{
|
|
||||||
UIContext* ui = GetCtx();
|
|
||||||
|
|
||||||
ui.buffer.count = 0;
|
|
||||||
|
|
||||||
ClearParentStack();
|
|
||||||
|
|
||||||
ui.root = RootItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
UIFinishBuild()
|
|
||||||
{
|
|
||||||
UIContext* ui = GetCtx();
|
|
||||||
|
|
||||||
|
|
||||||
// 1. Pixel/Text sizes (any order)
|
|
||||||
for(UIItem* item = ui.root; !Nil(item); item = Recurse!(UIR.PreOrder)(item, ui.root).next)
|
|
||||||
{
|
|
||||||
foreach(i, ref s; item.size)
|
|
||||||
{
|
|
||||||
if (s.kind == SK.Pixels)
|
|
||||||
{
|
|
||||||
item.computed_size[i] = s.value;
|
|
||||||
}
|
|
||||||
else if (s.kind == SK.TextContent)
|
|
||||||
{
|
|
||||||
// Text size + padding
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. PercentOfParent (Upward dependant) size (pre-order) (Ignore downward dependant sizes)
|
|
||||||
foreach(axis; A2D.min .. A2D.max)
|
|
||||||
{
|
|
||||||
for(UIItem* item = ui.root; !Nil(item); item = Recurse!(UIR.PreOrder)(item, ui.root).next)
|
|
||||||
{
|
|
||||||
for(UIItem* child = item.first; !Nil(child); child = child.next)
|
|
||||||
{
|
|
||||||
if (child.size[axis].kind == SK.PercentOfParent)
|
|
||||||
{
|
|
||||||
UIItem* fixed_parent = g_UI_NIL;
|
|
||||||
for(UIItem* p = child.parent; !Nil(p); p = p.parent)
|
|
||||||
{
|
|
||||||
if (p.size[axis].kind == SK.Pixels || p.size[axis].kind == SK.TextContent)
|
|
||||||
{
|
|
||||||
fixed_parent = p;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
child.computed_size[axis] = fixed_parent.computed_size[axis] * child.size[axis].value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. ChildrenSum (Downward dependant) (post-order)
|
|
||||||
{
|
|
||||||
UIItemRec rec;
|
|
||||||
foreach(axis; A2D.min .. A2D.max)
|
|
||||||
{
|
|
||||||
for(UIItem* item = ui.root; !Nil(item); item = rec.next)
|
|
||||||
{
|
|
||||||
rec = Recurse!(UIR.PreOrder)(item, ui.root);
|
|
||||||
i32 pop_index = 0;
|
|
||||||
for(UIItem* i = item; !Nil(item) && pop_index <= rec.pop_count; i = i.parent, pop_index += 1)
|
|
||||||
{
|
|
||||||
if (i.size[axis].kind == SK.ChildrenSum)
|
|
||||||
{
|
|
||||||
f32 sum = 0.0;
|
|
||||||
for(UIItem* child = i.first; !Nil(child); child = child.next)
|
|
||||||
{
|
|
||||||
sum = axis == child.layout_axis ? sum + child.computed_size[axis] : Max(sum, child.computed_size[axis]);
|
|
||||||
}
|
|
||||||
i.computed_size[axis] = sum;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 4. Solve violations e.g. extending past boundaries of parent (pre-order)
|
|
||||||
foreach(axis; A2D.min .. A2D.max)
|
|
||||||
{
|
|
||||||
for(UIItem* item = ui.root; !Nil(item); item = Recurse!(UIR.PreOrder)(item, ui.root).next)
|
|
||||||
{
|
|
||||||
if (!Nil(item.parent))
|
|
||||||
{
|
|
||||||
if (item.parent.computed_size[axis] < item.computed_size[axis])
|
|
||||||
{
|
|
||||||
item.computed_size[axis] = item.parent.computed_size[axis];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Compute relative positions of each widgets (pre-order)
|
|
||||||
foreach(axis; A2D.min .. A2D.max)
|
|
||||||
{
|
|
||||||
for(UIItem* item = ui.root; !Nil(item); item = Recurse!(UIR.PreOrder)(item, ui.root).next)
|
|
||||||
{
|
|
||||||
f32 layout_position = 0.0;
|
|
||||||
for(UIItem* child = item.first; !Nil(child); child = child.next)
|
|
||||||
{
|
|
||||||
child.fixed_position[axis] = layout_position;
|
|
||||||
|
|
||||||
child.rect.vec0.v[axis] = item.rect.vec0.v[axis] + child.fixed_position[axis];
|
|
||||||
child.rect.vec1.v[axis] = child.rect.vec0.v[axis] + child.computed_size[axis];
|
|
||||||
|
|
||||||
if (axis == item.layout_axis)
|
|
||||||
{
|
|
||||||
layout_position += child.computed_size[axis];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 count = 0;
|
|
||||||
for(UIItem* item = ui.root; !Nil(item); item = Recurse!(UIR.PreOrder)(item, ui.root).next)
|
|
||||||
{
|
|
||||||
if (item.flags & UIP.DrawBackground)
|
|
||||||
{
|
|
||||||
DrawRect(ui, item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WidgetsComplete();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
PrintTree(UIItem* item, u32 count = 0, string prefix = "", bool is_left = false)
|
|
||||||
{
|
|
||||||
if (!Nil(item))
|
|
||||||
{
|
|
||||||
writeln(is_left ? "f" : "l", count, prefix, is_left ? "├──" : "└──", item.debug_id);
|
|
||||||
|
|
||||||
prefix ~= is_left ? "│ " : " ";
|
|
||||||
PrintTree(item.first, count + 1, prefix, true);
|
|
||||||
PrintTree(item.next, count + 1, prefix, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
PushParent(UIItem* parent)
|
|
||||||
{
|
|
||||||
auto ctx = GetCtx();
|
|
||||||
parent.list_next = ctx.parent_stack;
|
|
||||||
ctx.parent_stack = parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
UIItem*
|
|
||||||
PopParent()
|
|
||||||
{
|
|
||||||
auto ctx = GetCtx();
|
|
||||||
UIItem* parent = ctx.parent_stack;
|
|
||||||
ctx.parent_stack = parent.list_next;
|
|
||||||
return parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
UIItem*
|
|
||||||
BuildItem(UIKey key, UIProperties flags)
|
|
||||||
{
|
|
||||||
UIContext* ui = GetCtx();
|
|
||||||
|
|
||||||
UIItem* item = Get(key);
|
|
||||||
item.first = item.last = item.next = item.prev = item.parent = item.list_next = item.list_prev = g_UI_NIL;
|
|
||||||
item.computed_size = 0;
|
|
||||||
|
|
||||||
item.flags = flags;
|
|
||||||
item.size = ui.size_axes;
|
|
||||||
item.padding = ui.padding;
|
|
||||||
item.layout_axis = ui.layout_axis;
|
|
||||||
|
|
||||||
if (item.flags & UIP.DrawBackground)
|
|
||||||
{
|
|
||||||
item.bg_color = ui.bg_cols;
|
|
||||||
}
|
|
||||||
if (item.flags & UIP.DrawBorder)
|
|
||||||
{
|
|
||||||
item.border_color = ui.border_cols;
|
|
||||||
}
|
|
||||||
if (item.flags & UIP.DrawText)
|
|
||||||
{
|
|
||||||
item.text_scale = ui.text_scale;
|
|
||||||
}
|
|
||||||
|
|
||||||
item.parent = ui.parent_stack;
|
|
||||||
if (!Nil(item.parent))
|
|
||||||
{
|
|
||||||
DLLPush(item.parent, item, g_UI_NIL);
|
|
||||||
}
|
|
||||||
|
|
||||||
debug
|
|
||||||
{
|
|
||||||
item.debug_id = key.text;
|
|
||||||
}
|
|
||||||
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ClearParentStack()
|
|
||||||
{
|
|
||||||
auto ctx = GetCtx();
|
|
||||||
ctx.parent_stack = g_UI_NIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
DrawUI()
|
|
||||||
{
|
|
||||||
BindBuffers(g_ui_ctx.rd, &g_ui_ctx.buffer.mapped_idx, &g_ui_ctx.buffer.mapped_vtx);
|
|
||||||
DrawIndexed(g_ui_ctx.rd, 6, g_ui_ctx.buffer.count, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
InitUIContext(Renderer* rd)
|
|
||||||
{
|
|
||||||
g_UI_NIL = cast(UIItem*)&g_ui_nil_item;
|
|
||||||
|
|
||||||
auto vtx = CreateMappedBuffer!(Vertex)(rd, BT.Vertex, Vertex.sizeof * UI_COUNT);
|
|
||||||
auto idx = CreateMappedBuffer!(u32)(rd, BT.Index, u32.sizeof * UI_COUNT);
|
|
||||||
|
|
||||||
g_ui_ctx.root = g_UI_NIL;
|
|
||||||
g_ui_ctx.items = CreateHashTable!(UIHash, UIItem*)(16);
|
|
||||||
g_ui_ctx.arena = CreateArena(MB(8));
|
|
||||||
g_ui_ctx.rd = rd;
|
|
||||||
g_ui_ctx.buffer.mapped_vtx = vtx;
|
|
||||||
g_ui_ctx.buffer.mapped_idx = idx;
|
|
||||||
g_ui_ctx.buffer.vtx = vtx.data;
|
|
||||||
g_ui_ctx.buffer.idx = idx.data;
|
|
||||||
|
|
||||||
InitWidgetContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
UIKey
|
|
||||||
MakeKey(string str)
|
|
||||||
{
|
|
||||||
u8[] id = (cast(u8*)str)[0 .. str.length];
|
|
||||||
return MakeKey(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
UIKey
|
UIKey
|
||||||
MakeKey(u8[] id)
|
MakeKey(u8[] id)
|
||||||
{
|
{
|
||||||
@ -497,7 +408,7 @@ Get(UIKey key)
|
|||||||
f32
|
f32
|
||||||
CalcTextWidth(u8[] str)
|
CalcTextWidth(u8[] str)
|
||||||
{
|
{
|
||||||
u32 tab_width = g_ui_ctx.tab_width;
|
u32 tab_width = 2; //g_ui_ctx.tab_width;
|
||||||
Glyph* space = g_ui_ctx.atlas.glyphs.ptr + ' ';
|
Glyph* space = g_ui_ctx.atlas.glyphs.ptr + ' ';
|
||||||
|
|
||||||
f32 width;
|
f32 width;
|
||||||
@ -515,7 +426,7 @@ CalcTextWidth(u8[] str)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return width * g_ui_ctx.text_scale;
|
return width; // * g_ui_ctx.text_scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma(inline) void
|
pragma(inline) void
|
||||||
@ -561,7 +472,7 @@ DrawRect(UIContext* ctx, UIItem* item)
|
|||||||
Vertex* v = ctx.buffer.vtx.ptr + ctx.buffer.count;
|
Vertex* v = ctx.buffer.vtx.ptr + ctx.buffer.count;
|
||||||
v.dst_start = item.rect.vec0;
|
v.dst_start = item.rect.vec0;
|
||||||
v.dst_end = item.rect.vec1;
|
v.dst_end = item.rect.vec1;
|
||||||
v.cols = item.bg_color;
|
v.cols = item.color;
|
||||||
v.border_thickness = 0.0;
|
v.border_thickness = 0.0;
|
||||||
v.corner_radius = 0.0;
|
v.corner_radius = 0.0;
|
||||||
v.edge_softness = 0.0;
|
v.edge_softness = 0.0;
|
||||||
@ -589,42 +500,7 @@ Nil(UIItem* item)
|
|||||||
return item == null || item == g_UI_NIL;
|
return item == null || item == g_UI_NIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
UIItemRec
|
/*
|
||||||
Recurse(alias mode)(UIItem* item, UIItem* root)
|
|
||||||
{
|
|
||||||
UIItem* sibling, child;
|
|
||||||
UIItemRec result = UIItemRec(next: g_UI_NIL);
|
|
||||||
|
|
||||||
static if (mode == UIR.PostOrder) child = item.last; else child = item.first;
|
|
||||||
|
|
||||||
if (!Nil(child))
|
|
||||||
{
|
|
||||||
result.next = child;
|
|
||||||
result.push_count = 1;
|
|
||||||
}
|
|
||||||
else for(UIItem* i = item; !Nil(i) && i != root; i = i.parent)
|
|
||||||
{
|
|
||||||
static if (mode == UIR.PostOrder)
|
|
||||||
{
|
|
||||||
sibling = i.prev;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sibling = i.next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Nil(sibling))
|
|
||||||
{
|
|
||||||
result.next = sibling;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
result.pop_count += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setter function in case I need to change to using a mutex
|
// Setter function in case I need to change to using a mutex
|
||||||
pragma(inline) void
|
pragma(inline) void
|
||||||
SetProp(alias P, T)(T value)
|
SetProp(alias P, T)(T value)
|
||||||
@ -683,6 +559,7 @@ SetProp(alias P, T)(T value)
|
|||||||
}
|
}
|
||||||
else static assert(false, "Unknown Type");
|
else static assert(false, "Unknown Type");
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
unittest
|
unittest
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,219 +1,31 @@
|
|||||||
import dlib;
|
import dlib;
|
||||||
|
|
||||||
import editor;
|
|
||||||
import ui;
|
import ui;
|
||||||
|
|
||||||
struct WidgetAnimation
|
UIItem*
|
||||||
|
Root()
|
||||||
{
|
{
|
||||||
f32 start;
|
Vec2 d = RootSize();
|
||||||
f32 end;
|
SetLayoutAxis(A2D.Y);
|
||||||
f32 time_start;
|
UIItem* root = BuildItem("##root_item", UISize(ST.Pixels, d.x), UISize(ST.Pixels, d.y), UIF.DrawBackground);
|
||||||
f32 time_remaining;
|
|
||||||
}
|
|
||||||
|
|
||||||
WidgetContext g_widget_ctx;
|
return root;
|
||||||
const UIPanel g_ui_panel;
|
|
||||||
UIPanel* g_PANEL_NIL;
|
|
||||||
|
|
||||||
struct WidgetContext
|
|
||||||
{
|
|
||||||
u64 frame;
|
|
||||||
Arena arena;
|
|
||||||
HashTable!(UIHash, UIPanel*) panels;
|
|
||||||
UIPanel* parent_panel;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct UIPanel
|
|
||||||
{
|
|
||||||
Axis2D split_axis;
|
|
||||||
f32 percent;
|
|
||||||
|
|
||||||
UIPanel* first;
|
|
||||||
UIPanel* last;
|
|
||||||
UIPanel* prev;
|
|
||||||
UIPanel* next;
|
|
||||||
UIPanel* parent;
|
|
||||||
|
|
||||||
UIPanel* list_next;
|
|
||||||
|
|
||||||
u64 last_frame;
|
|
||||||
bool new_children;
|
|
||||||
u32 child_count;
|
|
||||||
|
|
||||||
WidgetAnimation anim;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
InitWidgetContext()
|
|
||||||
{
|
|
||||||
g_PANEL_NIL = cast(UIPanel*)&g_ui_panel;
|
|
||||||
|
|
||||||
g_widget_ctx.arena = CreateArena(MB(1));
|
|
||||||
g_widget_ctx.parent_panel = g_PANEL_NIL;
|
|
||||||
g_widget_ctx.panels = CreateHashTable!(UIHash, UIPanel*)(6);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
WidgetsComplete()
|
|
||||||
{
|
|
||||||
g_widget_ctx.frame += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
Nil(UIPanel* panel)
|
|
||||||
{
|
|
||||||
return panel == g_PANEL_NIL || panel == null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UIItem*
|
UIItem*
|
||||||
RootItem()
|
Panel(string id, f32 x_pct, f32 y_pct, Axis2D layout_axis, Vec4 color)
|
||||||
{
|
{
|
||||||
Vec2 size = RootSize();
|
SetColor(color);
|
||||||
|
SetLayoutAxis(layout_axis);
|
||||||
UIKey key = MakeKey("##root_key");
|
UIItem* item = BuildItem(id, UISize(ST.Percentage, x_pct), UISize(ST.Percentage, y_pct), UIF.DrawBackground);
|
||||||
|
|
||||||
SetProp!(CtxP.AxisX)(UISize(SK.Pixels, size.x, 1.0));
|
|
||||||
SetProp!(CtxP.AxisY)(UISize(SK.Pixels, size.y, 1.0));
|
|
||||||
SetProp!(CtxP.BGColor)(Vec4(0.2, 0.5, 0.85, 1.0));
|
|
||||||
SetProp!(CtxP.LayoutAxis)(A2D.X);
|
|
||||||
|
|
||||||
UIItem* item = BuildItem(key, UIP.DrawBackground);
|
|
||||||
|
|
||||||
UIContext* ui = GetCtx();
|
|
||||||
|
|
||||||
PushParent(ui.root);
|
|
||||||
|
|
||||||
assert(g_widget_ctx.parent_panel == g_PANEL_NIL);
|
|
||||||
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
|
|
||||||
UIItem*
|
|
||||||
PanelStart(string str, Vec3 col)
|
|
||||||
{
|
|
||||||
u8[] u8_str = (cast(u8*)str.ptr)[0 .. str.length];
|
|
||||||
return PanelStart(u8_str, col);
|
|
||||||
}
|
|
||||||
|
|
||||||
UIPanel*
|
|
||||||
GetPanel(UIKey key)
|
|
||||||
{
|
|
||||||
UIPanel* panel;
|
|
||||||
Result!(UIPanel*) result = g_widget_ctx.panels[key.hash];
|
|
||||||
if (result.ok)
|
|
||||||
{
|
|
||||||
panel = result.value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
panel = Alloc!(UIPanel)(&g_widget_ctx.arena);
|
|
||||||
panel.last_frame = u64.max;
|
|
||||||
g_widget_ctx.panels[key.hash] = panel;
|
|
||||||
}
|
|
||||||
|
|
||||||
return panel;
|
|
||||||
}
|
|
||||||
|
|
||||||
UIItem*
|
|
||||||
PanelStart(u8[] str, Vec3 col)
|
|
||||||
{
|
|
||||||
UIKey key = MakeKey(str);
|
|
||||||
UIPanel* panel = GetPanel(key);
|
|
||||||
|
|
||||||
panel.first = panel.last = panel.next = panel.list_next = g_PANEL_NIL;
|
|
||||||
panel.child_count = 0;
|
|
||||||
|
|
||||||
if (panel.anim.time_remaining > 0.0)
|
|
||||||
{
|
|
||||||
f32 r = panel.anim.time_remaining;
|
|
||||||
|
|
||||||
r -= g_delta;
|
|
||||||
if (r < 0.0)
|
|
||||||
{
|
|
||||||
r = 0.0;
|
|
||||||
}
|
|
||||||
Logf("%f", r);
|
|
||||||
|
|
||||||
f32 t = Remap(r, 0.0, panel.anim.time_start, 1.0, 0.0);
|
|
||||||
panel.percent = Lerp(panel.anim.start, panel.anim.end, t);
|
|
||||||
|
|
||||||
panel.anim.time_remaining = r;
|
|
||||||
if (r == 0.0)
|
|
||||||
{
|
|
||||||
panel.anim.start = panel.anim.end = panel.anim.time_start = 0.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
panel.parent = g_widget_ctx.parent_panel;
|
|
||||||
panel.list_next = g_widget_ctx.parent_panel;
|
|
||||||
g_widget_ctx.parent_panel = panel;
|
|
||||||
|
|
||||||
f32 x_pct = 1.0, y_pct = 1.0;
|
|
||||||
if (!Nil(panel.parent))
|
|
||||||
{
|
|
||||||
if (panel.parent.split_axis == A2D.X)
|
|
||||||
{
|
|
||||||
x_pct = panel.percent;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
y_pct = panel.percent;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (panel.last_frame != g_widget_ctx.frame)
|
|
||||||
{
|
|
||||||
Logf("new");
|
|
||||||
panel.parent.new_children = true;
|
|
||||||
panel.anim.start = panel.anim.end = panel.anim.time_start = panel.anim.time_remaining = 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
panel.child_count += 1;
|
|
||||||
DLLPush(panel.parent, panel, g_PANEL_NIL);
|
|
||||||
}
|
|
||||||
|
|
||||||
SetProp!(CtxP.LayoutAxis)(panel.split_axis);
|
|
||||||
SetProp!(CtxP.AxisX)(UISize(SK.PercentOfParent, x_pct, 1.0));
|
|
||||||
SetProp!(CtxP.AxisY)(UISize(SK.PercentOfParent, y_pct, 1.0));
|
|
||||||
SetProp!(CtxP.BGColor)(Vec4(col, 1.0));
|
|
||||||
|
|
||||||
UIItem* item = BuildItem(key, UIP.DrawBackground);
|
|
||||||
|
|
||||||
PushParent(item);
|
PushParent(item);
|
||||||
|
|
||||||
panel.last_frame = g_widget_ctx.frame + 1;
|
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
PanelEnd()
|
EndPanel()
|
||||||
{
|
{
|
||||||
UIPanel* parent = g_widget_ctx.parent_panel;
|
|
||||||
if (parent.new_children)
|
|
||||||
{
|
|
||||||
f32 new_percent = 1.0/cast(f32)(parent.child_count);
|
|
||||||
for(UIPanel* child = parent.first; !Nil(child); child = child.next)
|
|
||||||
{
|
|
||||||
f32 percent = child.percent;
|
|
||||||
if (percent > 0.0)
|
|
||||||
{
|
|
||||||
percent -= new_percent/cast(f32)(parent.child_count-1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
percent = new_percent;
|
|
||||||
}
|
|
||||||
|
|
||||||
child.anim.start = child.percent;
|
|
||||||
child.anim.end = new_percent;
|
|
||||||
child.anim.time_start = 0.2;
|
|
||||||
child.anim.time_remaining = 0.2;
|
|
||||||
child.percent = percent;
|
|
||||||
}
|
|
||||||
|
|
||||||
parent.new_children = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_widget_ctx.parent_panel = g_widget_ctx.parent_panel.list_next;
|
|
||||||
PopParent();
|
PopParent();
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user