ui code almost ready to replace current code
This commit is contained in:
parent
2703c95673
commit
ad3ababe97
19
dub.json
19
dub.json
@ -13,11 +13,28 @@
|
||||
"sourcePaths": ["src/editor", "src/dlib", "src/dlib/external/xxhash", "src/VulkanRenderer"],
|
||||
"libs-linux": ["X11", "vulkan", "stdc++", "xfixes", "freetype"],
|
||||
"libs-windows": [],
|
||||
"versions": ["VULKAN_DEBUG"],
|
||||
"versions": ["VULKAN_DEBUG", "ENABLE_RENDERER"],
|
||||
"preGenerateCommands-linux": ["./build.sh"],
|
||||
"preGenerateCommands-windows": [],
|
||||
"dflags": ["-Xcc=-mno-sse", "-P-I/usr/include/freetype2", "-Jbuild", "-Jassets", "-link-debuglib"],
|
||||
"dflags-dmd": ["-P=-DSTBI_NO_SIMD"]
|
||||
},
|
||||
{
|
||||
"name": "editor-test",
|
||||
"targetType": "executable",
|
||||
"targetName": "Editor",
|
||||
"targetPath": "build",
|
||||
"sourceFiles-linux": ["build/libvma.a", "build/libstb.a", "build/libm3d.a", "build/libcglm.a"],
|
||||
"sourceFiles-windows": [],
|
||||
"importPaths": ["src/editor", "src/dlib", "src/dlib/external/xxhash", "src/VulkanRenderer"],
|
||||
"sourcePaths": ["src/editor", "src/dlib", "src/dlib/external/xxhash", "src/VulkanRenderer"],
|
||||
"libs-linux": ["X11", "vulkan", "stdc++", "xfixes", "freetype"],
|
||||
"libs-windows": [],
|
||||
"versions": ["VULKAN_DEBUG"],
|
||||
"preGenerateCommands-linux": ["./build.sh"],
|
||||
"preGenerateCommands-windows": [],
|
||||
"dflags": ["-Xcc=-mno-sse", "-P-I/usr/include/freetype2", "-Jbuild", "-Jassets", "-link-debuglib", "-unittest"],
|
||||
"dflags-dmd": ["-P=-DSTBI_NO_SIMD"]
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit 589b2191babf009bef097d1272fac4a05a4c280d
|
||||
Subproject commit b3639d9c884c09fb98b750ba613118d633470312
|
||||
2
src/dlib
2
src/dlib
@ -1 +1 @@
|
||||
Subproject commit 60b4e0cb27ba304c7b9444a2da8a581211c21ca9
|
||||
Subproject commit e5f91cae6d90c40e458e1208c2ab8f8eb917c99a
|
||||
@ -139,25 +139,25 @@ Cycle(EditorCtx* ctx, Inputs* inputs)
|
||||
|
||||
g_input_mode = ctx.state == ES.InputMode;
|
||||
|
||||
BeginUI(ctx, inputs);
|
||||
BeginUI(inputs);
|
||||
|
||||
UIPanel* root = ctx.base_panel;
|
||||
|
||||
root.size.x = g_ui_ctx.res.x;
|
||||
root.size.y = g_ui_ctx.res.y;
|
||||
root.rect.vec0 = 0.0;
|
||||
root.rect.vec1 = 0.0;
|
||||
root.rect.p0 = 0.0;
|
||||
root.rect.p1 = 0.0;
|
||||
|
||||
static foreach(axis; A2D.min .. A2D.max)
|
||||
{
|
||||
for(UIPanel* p = root; !Nil(p); p = Recurse(p, root, g_UI_NIL_PANEL))
|
||||
{
|
||||
f32 pos = p.rect.vec0.v[axis];
|
||||
f32 pos = p.rect.p0.v[axis];
|
||||
for(UIPanel* c = p.first; !Nil(c); c = c.next)
|
||||
{
|
||||
c.size.v[axis] = p.axis == axis ? c.pct * p.size.v[axis] : p.size.v[axis];
|
||||
c.rect.vec0.v[axis] = pos;
|
||||
c.rect.vec1.v[axis] = pos + c.size.v[axis];
|
||||
c.rect.p0.v[axis] = pos;
|
||||
c.rect.p1.v[axis] = pos + c.size.v[axis];
|
||||
|
||||
if(axis == p.axis)
|
||||
{
|
||||
@ -571,13 +571,13 @@ HandleInputs(EditorCtx* ctx, Inputs* inputs)
|
||||
UIPanel* panel = GetFocusedPanel();
|
||||
FlatBuffer* fb = !Nil(panel) ? &panel.ed.buf : null;
|
||||
|
||||
for(auto node = inputs.list.first; node != null; node = node.next)
|
||||
for(auto node = inputs.first; node != null; node = node.next)
|
||||
{
|
||||
bool taken = false;
|
||||
|
||||
Input key = node.value.key;
|
||||
Modifier md = node.value.md;
|
||||
bool pressed = node.value.pressed;
|
||||
Input key = node.key;
|
||||
Modifier md = node.md;
|
||||
bool pressed = node.pressed;
|
||||
|
||||
if (pressed)
|
||||
{
|
||||
@ -588,11 +588,11 @@ HandleInputs(EditorCtx* ctx, Inputs* inputs)
|
||||
}
|
||||
else if(ctx.state == ES.InputMode)
|
||||
{
|
||||
taken = HandleInputMode(ctx, node.value);
|
||||
taken = HandleInputMode(ctx, node);
|
||||
}
|
||||
else if(ctx.state == ES.CmdOpen)
|
||||
{
|
||||
taken = HandleCmdMode(ctx, node.value);
|
||||
taken = HandleCmdMode(ctx, node);
|
||||
}
|
||||
else if(ctx.state == ES.SetPanelFocus)
|
||||
{
|
||||
@ -646,7 +646,7 @@ HandleInputs(EditorCtx* ctx, Inputs* inputs)
|
||||
} break;
|
||||
case Semicolon:
|
||||
{
|
||||
if(Shift(node.value.md))
|
||||
if(Shift(node.md))
|
||||
{
|
||||
ctx.state = ES.CmdOpen;
|
||||
taken = true;
|
||||
@ -701,7 +701,7 @@ HandleInputs(EditorCtx* ctx, Inputs* inputs)
|
||||
|
||||
if(taken)
|
||||
{
|
||||
DLLRemove(&inputs.list, node, null);
|
||||
DLLRemove(inputs, node, null);
|
||||
}
|
||||
}
|
||||
|
||||
@ -714,7 +714,7 @@ HandleInputs(EditorCtx* ctx, Inputs* inputs)
|
||||
}
|
||||
|
||||
void
|
||||
MoveCursor(InputEvent ev)
|
||||
MoveCursor(InputEvent* ev)
|
||||
{
|
||||
UIPanel* p = GetFocusedPanel();
|
||||
|
||||
@ -754,7 +754,7 @@ CharCases()
|
||||
}
|
||||
|
||||
bool
|
||||
HandleInputMode(EditorCtx* ctx, InputEvent ev)
|
||||
HandleInputMode(EditorCtx* ctx, InputEvent* ev)
|
||||
{
|
||||
bool taken = false;
|
||||
|
||||
@ -912,7 +912,7 @@ GetCommands(CmdPalette* cmd)
|
||||
}
|
||||
|
||||
bool
|
||||
HandleCmdMode(EditorCtx* ctx, InputEvent ev)
|
||||
HandleCmdMode(EditorCtx* ctx, InputEvent* ev)
|
||||
{
|
||||
u8 result = 0;
|
||||
bool taken = false;
|
||||
|
||||
@ -47,7 +47,7 @@ void main(string[] argv)
|
||||
SetExtent(&g_ui_ctx.rd, window.w, window.h);
|
||||
}
|
||||
|
||||
if(inputs.list.first == null)
|
||||
if(inputs.first == null)
|
||||
{
|
||||
no_ev_count += 1;
|
||||
Pause();
|
||||
|
||||
515
src/editor/ui.d
515
src/editor/ui.d
@ -2,6 +2,7 @@ import dlib;
|
||||
import dlib.util : HTPush = Push;
|
||||
|
||||
import vulkan;
|
||||
import vulkan : RendererGetExtent = GetExtent;
|
||||
import buffer;
|
||||
import parsing;
|
||||
import editor;
|
||||
@ -10,7 +11,7 @@ import views;
|
||||
|
||||
import std.stdio;
|
||||
import std.math.traits : isNaN;
|
||||
import std.math.rounding : ceil;
|
||||
import std.math.rounding : ceil, floor;
|
||||
import std.math.exponential : pow;
|
||||
import std.math.remainder : fmod;
|
||||
import std.format : sformat;
|
||||
@ -58,29 +59,28 @@ Vec4[4] g_border_hl_col_default = BORDER_HL_COL;
|
||||
Vec4 g_text_col_default = TEXT_COL;
|
||||
Vec4 g_text_hl_col_default = TEXT_HL_COL;
|
||||
UISize[2] g_size_info_default = [UISize(ST.Percentage, 1.0), UISize(ST.Percentage, 1.0)];
|
||||
Axis2D g_layout_axis_default = A2D.X;
|
||||
Vec2 g_padding_default = Vec2(0.0);
|
||||
|
||||
const UI_COUNT = 5000;
|
||||
|
||||
__gshared const UIPanel g_ui_nil_panel;
|
||||
__gshared UIPanel* g_UI_NIL_PANEL;
|
||||
|
||||
__gshared const UIItem g_ui_nil_item;
|
||||
__gshared UIItem* g_UI_NIL;
|
||||
|
||||
alias g_parent_default = g_UI_NIL;
|
||||
|
||||
__gshared const UIInput g_ui_nil_input;
|
||||
__gshared UIInput* g_UI_NIL_INPUT;
|
||||
__gshared const Stack!f32 g_nil_f32_stack;
|
||||
__gshared Stack!f32* g_NIL_F32_STACK;
|
||||
|
||||
__gshared const Stack!Vec4[4] g_nil_gradient_col_stack;
|
||||
__gshared Stack!Vec4[4]* g_NIL_GRAD_COL_STACK;
|
||||
|
||||
__gshared const Stack!Vec4 g_nil_vec4_stack;
|
||||
__gshared Stack!Vec4* g_NIL_VEC4_STACK;
|
||||
|
||||
__gshared const Stack!u32 g_nil_u32_stack;
|
||||
__gshared Stack!u32* g_NIL_U32_STACK;
|
||||
|
||||
alias g_parent_default = g_UI_NIL;
|
||||
|
||||
enum Axis2D
|
||||
{
|
||||
X,
|
||||
@ -106,31 +106,47 @@ enum UIFlags
|
||||
|
||||
Clamp = UIFlags.ClampX | UIFlags.ClampY,
|
||||
}
|
||||
|
||||
alias UIF = UIFlags;
|
||||
|
||||
enum UIEventFlag
|
||||
{
|
||||
None, // Events e.g. pressing/releasing/scrolling
|
||||
}
|
||||
|
||||
enum UISignal
|
||||
{
|
||||
None = 0x00,
|
||||
Clicked = 0x01,
|
||||
Dragged = 0x02, // Handle click/drag specifics in view?
|
||||
None = 0,
|
||||
Clicked = 1<<0,
|
||||
Dragged = 1<<1,
|
||||
Hovered = 1<<2,
|
||||
}
|
||||
|
||||
alias UIS = UISignal;
|
||||
|
||||
enum UIEvent
|
||||
{
|
||||
None = 0,
|
||||
Click = 1<<0,
|
||||
Drag = 1<<1,
|
||||
DragStart = 1<<2,
|
||||
Press = 1<<3,
|
||||
}
|
||||
alias UIE = UIEvent;
|
||||
|
||||
struct UIInput
|
||||
{
|
||||
UIEvent type;
|
||||
UIInput* next, prev;
|
||||
union
|
||||
{
|
||||
Input key;
|
||||
IVec2 rel;
|
||||
IVec2 pos;
|
||||
}
|
||||
}
|
||||
|
||||
mixin template
|
||||
UICtxParameter(T, string name)
|
||||
{
|
||||
static string
|
||||
CtxParameterGen(T, string name)()
|
||||
{
|
||||
import std.traits;
|
||||
import std.array;
|
||||
import std.traits, std.array;
|
||||
|
||||
string stack_top = "\tStackTop!("~T.stringof~") "~name~";\n";
|
||||
string stack = "\tStack!("~T.stringof~")* "~name~"_top;\n";
|
||||
@ -178,7 +194,11 @@ struct UICtx
|
||||
u64 frame;
|
||||
u64 f_idx;
|
||||
|
||||
PlatformWindow* window;
|
||||
LinkedList!(UIInput) events;
|
||||
IVec2 mouse_pos;
|
||||
IVec2 drag_start;
|
||||
|
||||
PlatformWindow* window;
|
||||
Renderer rd;
|
||||
Descriptor font_atlas;
|
||||
Descriptor sampler;
|
||||
@ -186,7 +206,7 @@ PlatformWindow* window;
|
||||
DescSetLayout desc_set_layout;
|
||||
DescSet desc_set;
|
||||
PipelineLayout pipeline_layout;
|
||||
PushConst pc;
|
||||
Mat4 projection;
|
||||
Vec2 res;
|
||||
|
||||
u8[] font_data;
|
||||
@ -215,6 +235,8 @@ PlatformWindow* window;
|
||||
mixin UICtxParameter!(Vec4, "text_col");
|
||||
mixin UICtxParameter!(Vec4, "text_hl_col");
|
||||
mixin UICtxParameter!(UISize[2], "size_info");
|
||||
mixin UICtxParameter!(Axis2D, "layout_axis");
|
||||
mixin UICtxParameter!(Vec2, "padding");
|
||||
|
||||
debug bool dbg;
|
||||
}
|
||||
@ -258,13 +280,16 @@ mixin template UIItemParameters()
|
||||
struct UIItem
|
||||
{
|
||||
UIKey key;
|
||||
UIFlags flags;
|
||||
u64 last_frame;
|
||||
Vec2 dragged;
|
||||
IVec2 dragged;
|
||||
|
||||
UIItem* next, prev, first, last; // parent in mixin
|
||||
|
||||
Rect pref_size;
|
||||
Rect size;
|
||||
Rect rect;
|
||||
Vec2 size;
|
||||
|
||||
u8[] display_string;
|
||||
|
||||
mixin UIItemParameters!();
|
||||
}
|
||||
@ -273,7 +298,7 @@ enum SizeType
|
||||
{
|
||||
Pixels,
|
||||
Percentage,
|
||||
FitChild,
|
||||
ChildrenSum,
|
||||
}
|
||||
|
||||
alias ST = SizeType;
|
||||
@ -294,23 +319,10 @@ struct UIBuffer
|
||||
u32 count;
|
||||
}
|
||||
|
||||
struct PushConst
|
||||
{
|
||||
Mat4 projection;
|
||||
}
|
||||
|
||||
struct GlyphBounds
|
||||
{
|
||||
f32 r = 0.0;
|
||||
f32 l = 0.0;
|
||||
f32 t = 0.0;
|
||||
f32 b = 0.0;
|
||||
f32 w = 0.0;
|
||||
f32 h = 0.0;
|
||||
f32 atlas_r = 0.0;
|
||||
f32 atlas_l = 0.0;
|
||||
f32 atlas_t = 0.0;
|
||||
f32 atlas_b = 0.0;
|
||||
f32 r = 0.0, l = 0.0, t = 0.0, b = 0.0, w = 0.0, h = 0.0;
|
||||
f32 atlas_r = 0.0, atlas_l = 0.0, atlas_t = 0.0, atlas_b = 0.0;
|
||||
}
|
||||
|
||||
struct Vertex
|
||||
@ -331,18 +343,7 @@ struct Vertex
|
||||
union Rect
|
||||
{
|
||||
Vec2[2] v;
|
||||
struct
|
||||
{
|
||||
Vec2 vec0;
|
||||
Vec2 vec1;
|
||||
};
|
||||
struct
|
||||
{
|
||||
f32 x0;
|
||||
f32 y0;
|
||||
f32 x1;
|
||||
f32 y1;
|
||||
};
|
||||
struct { Vec2 p0, p1; };
|
||||
}
|
||||
|
||||
alias UIHash = u64;
|
||||
@ -351,8 +352,7 @@ alias UIPair = KVPair!(UIHash, UIItem*);
|
||||
|
||||
struct UIKey
|
||||
{
|
||||
u8[] text;
|
||||
u8[] hash_text;
|
||||
u8[] text, hash_text;
|
||||
u64 hash;
|
||||
}
|
||||
|
||||
@ -361,14 +361,7 @@ struct UIPanel
|
||||
u8[] id;
|
||||
Editor* ed;
|
||||
|
||||
UIPanel* parent;
|
||||
UIPanel* next;
|
||||
UIPanel* prev;
|
||||
UIPanel* first;
|
||||
UIPanel* last;
|
||||
|
||||
UIPanel* list_next;
|
||||
|
||||
UIPanel* parent, next, prev, first, last, list_next;
|
||||
Axis2D axis;
|
||||
f32 pct;
|
||||
Vec4 color;
|
||||
@ -376,12 +369,8 @@ struct UIPanel
|
||||
Rect rect;
|
||||
Vec2 size;
|
||||
|
||||
i64 start_ln;
|
||||
i64 end_ln;
|
||||
i64 vis_lines;
|
||||
i64 prev_start_ln;
|
||||
f32 scroll_offset;
|
||||
f32 scroll_target;
|
||||
i64 start_ln, end_ln, vis_lines, prev_start_ln;
|
||||
f32 scroll_offset, scroll_target;
|
||||
}
|
||||
|
||||
struct TextPart
|
||||
@ -391,13 +380,13 @@ struct TextPart
|
||||
u32 count;
|
||||
}
|
||||
|
||||
// Keep defaults, pop anything used once
|
||||
|
||||
enum bool StringType(T) = (is(T: string) || is(T: u8[]) || is(T: char[]));
|
||||
|
||||
void
|
||||
InitUICtx(PlatformWindow* window)
|
||||
{
|
||||
version(ENABLE_RENDERER)
|
||||
{
|
||||
version(linux)
|
||||
{
|
||||
PlatformHandles handles = {
|
||||
@ -410,9 +399,11 @@ InitUICtx(PlatformWindow* window)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
g_UI_NIL = cast(UIItem*)&g_ui_nil_item;
|
||||
g_UI_NIL_PANEL = cast(UIPanel*)&g_ui_nil_panel;
|
||||
g_UI_NIL_INPUT = cast(UIInput*)&g_ui_nil_input;
|
||||
g_NIL_F32_STACK = cast(Stack!f32*)&g_nil_f32_stack;
|
||||
g_NIL_GRAD_COL_STACK = cast(Stack!Vec4[4]*)&g_nil_gradient_col_stack;
|
||||
g_NIL_VEC4_STACK = cast(Stack!Vec4*)&g_nil_vec4_stack;
|
||||
@ -424,24 +415,23 @@ InitUICtx(PlatformWindow* window)
|
||||
|
||||
UIBuffer[FRAME_OVERLAP] buffers;
|
||||
|
||||
FontFace font = OpenFont(cast(u8[])FONT_BYTES);
|
||||
FontAtlasBuf atlas_buf = CreateAtlas(&arena, font, 16.0, 256);
|
||||
|
||||
Vec4 b = Vec4(Vec3(0.0), 1.0);
|
||||
|
||||
UICtx ctx = {
|
||||
rd: InitRenderer(handles, MB(16), MB(8)),
|
||||
items: CreateHashTable!(UIHash, UIItem*)(12),
|
||||
arena: arena,
|
||||
temp_arena: CreateArena(MB(1)),
|
||||
drag_item: g_UI_NIL,
|
||||
atlas_buf: atlas_buf,
|
||||
font: font,
|
||||
font: OpenFont(cast(u8[])FONT_BYTES),
|
||||
font_data: cast(u8[])FONT_BYTES,
|
||||
text_size: 16.0,
|
||||
tab_width: 2,
|
||||
};
|
||||
|
||||
ctx.atlas_buf = CreateAtlas(&arena, ctx.font, 16.0, 256);
|
||||
|
||||
version(ENABLE_RENDERER)
|
||||
{
|
||||
ctx.rd = InitRenderer(handles, MB(16), MB(8));
|
||||
|
||||
for(u64 i = 0; i < FRAME_OVERLAP; i += 1)
|
||||
{
|
||||
ctx.buffers[i].m_vtx = CreateMappedBuffer!(Vertex)(&ctx.rd, BT.Vertex, VERTEX_MAX_COUNT);
|
||||
@ -457,7 +447,7 @@ InitUICtx(PlatformWindow* window)
|
||||
|
||||
ctx.desc_set_layout = CreateDescSetLayout(&ctx.rd, layout_bindings);
|
||||
ctx.desc_set = AllocDescSet(&ctx.rd, ctx.desc_set_layout);
|
||||
ctx.pipeline_layout = CreatePipelineLayout(&ctx.rd, ctx.desc_set_layout, PushConst.sizeof);
|
||||
ctx.pipeline_layout = CreatePipelineLayout(&ctx.rd, ctx.desc_set_layout, Mat4.sizeof);
|
||||
|
||||
Attribute[14] attributes = [
|
||||
{ binding: 0, location: 0, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof + Vec4.sizeof * 0},
|
||||
@ -492,14 +482,11 @@ InitUICtx(PlatformWindow* window)
|
||||
};
|
||||
|
||||
CreateGraphicsPipeline(&ctx.rd, &ctx.pipeline, &ui_info);
|
||||
|
||||
CreateImageView(&ctx.rd, &ctx.font_atlas, ctx.atlas_buf.atlas.width, ctx.atlas_buf.atlas.height, 4, ctx.atlas_buf.data, DT.Image, 0);
|
||||
|
||||
ctx.sampler = CreateSampler(&ctx.rd, MipmapMode.Nearest, 1);
|
||||
|
||||
Write(&ctx.rd, ctx.desc_set, [ctx.font_atlas, ctx.sampler]);
|
||||
|
||||
SetClearColors(&ctx.rd, [0.0, 0.0, 0.0, 1.0], [0.0, 0.0, 0.0, 0.0]);
|
||||
}
|
||||
|
||||
InitStacks(&ctx);
|
||||
|
||||
@ -529,29 +516,123 @@ UIItem*
|
||||
MakeItem(T)(T k) if(is(T: UIKey) || StringType!T)
|
||||
{
|
||||
UICtx* ctx = GetCtx();
|
||||
|
||||
UIItem* item = Get(k);
|
||||
|
||||
Set(item, ctx);
|
||||
|
||||
if(!Nil(item.parent))
|
||||
{
|
||||
DLLPush(item.parent, item, g_UI_NIL);
|
||||
}
|
||||
|
||||
AutoPopStacks(ctx);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
UISignal
|
||||
Signal(UIItem* item)
|
||||
{
|
||||
UISignal signal;
|
||||
UICtx* ctx = GetCtx();
|
||||
|
||||
i32 x = ctx.mouse_pos.x;
|
||||
i32 y = ctx.mouse_pos.y;
|
||||
|
||||
if(x >= item.rect.p0.x && x <= item.rect.p1.x && y >= item.rect.p0.y && y <= item.rect.p1.y)
|
||||
{
|
||||
signal |= UIS.Hovered;
|
||||
}
|
||||
|
||||
item.dragged = 0;
|
||||
|
||||
for(UIInput* i = ctx.events.first; !CheckNil(g_UI_NIL_INPUT, i); i = i.next)
|
||||
{
|
||||
bool taken;
|
||||
|
||||
if(item.flags & UIF.Clickable && i.type == UIE.Click && InBounds(ctx.mouse_pos, &item.rect))
|
||||
{
|
||||
signal |= UIS.Clicked;
|
||||
taken = true;
|
||||
}
|
||||
|
||||
if(Nil(ctx.drag_item) && item.flags & UIF.Draggable && i.type == UIE.DragStart && InBounds(i.pos, &item.rect))
|
||||
{
|
||||
signal |= UIS.Dragged;
|
||||
ctx.drag_item = item;
|
||||
taken = true;
|
||||
}
|
||||
|
||||
if(ctx.drag_item == item && i.type == UIE.Drag)
|
||||
{
|
||||
item.dragged += i.rel;
|
||||
taken = true;
|
||||
}
|
||||
|
||||
if(taken)
|
||||
{
|
||||
DLLRemove(&ctx.events, i, g_UI_NIL_INPUT);
|
||||
}
|
||||
}
|
||||
|
||||
return signal;
|
||||
}
|
||||
|
||||
void
|
||||
BeginUI(EditorCtx* edctx, Inputs* inputs)
|
||||
PushUIEvent(UICtx* ctx, UIInput input)
|
||||
{
|
||||
UIInput* ev = Alloc!(UIInput)(&ctx.temp_arena);
|
||||
*ev = input;
|
||||
|
||||
DLLPush(&ctx.events, ev, null);
|
||||
}
|
||||
|
||||
void
|
||||
BeginUI(Inputs* inputs)
|
||||
{
|
||||
UICtx* ctx = GetCtx();
|
||||
Arena* a = &ctx.temp_arena;
|
||||
|
||||
Reset(a);
|
||||
|
||||
ctx.events.first = ctx.events.last = null;
|
||||
|
||||
static bool dragging;
|
||||
static bool mouse_down;
|
||||
for(auto i = inputs.first; i; i = i.next)
|
||||
{
|
||||
switch(i.key)
|
||||
{
|
||||
case Input.LeftClick:
|
||||
{
|
||||
mouse_down = i.pressed;
|
||||
if(!mouse_down && !dragging)
|
||||
{
|
||||
PushUIEvent(ctx, UIInput(UIE.Click));
|
||||
}
|
||||
dragging = false;
|
||||
} break;
|
||||
case Input.MouseMotion:
|
||||
{
|
||||
ctx.mouse_pos = IVec2(i.x, i.y);
|
||||
|
||||
if(!dragging && mouse_down)
|
||||
{
|
||||
PushUIEvent(ctx, UIInput(type: UIE.DragStart, pos: IVec2(i.x, i.y)));
|
||||
}
|
||||
|
||||
dragging = mouse_down;
|
||||
|
||||
if(dragging)
|
||||
{
|
||||
PushUIEvent(ctx, UIInput(type: UIE.Drag, rel: IVec2(i.rel_x, i.rel_y)));
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
{
|
||||
PushUIEvent(ctx, UIInput(type: UIE.Press, key: i.key));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
// Ctx state
|
||||
ctx.f_idx = ctx.frame%FRAME_OVERLAP;
|
||||
ctx.inputs = inputs;
|
||||
@ -559,26 +640,34 @@ BeginUI(EditorCtx* edctx, Inputs* inputs)
|
||||
|
||||
ResetStacks(ctx);
|
||||
|
||||
// Rendering
|
||||
version(ENABLE_RENDERER)
|
||||
{
|
||||
BeginFrame(&ctx.rd);
|
||||
BeginRendering(&ctx.rd);
|
||||
}
|
||||
|
||||
Vec2 ext = GetExtent(&ctx.rd);
|
||||
if(ext != ctx.res)
|
||||
{
|
||||
ctx.res = ext;
|
||||
Ortho(&ctx.pc.projection, 0.0, 0.0, ext.x, ext.y, -10.0, 10.0);
|
||||
Ortho(&ctx.projection, 0.0, 0.0, ext.x, ext.y, -10.0, 10.0);
|
||||
}
|
||||
|
||||
PushConstants(&ctx.rd, ctx.pipeline, &ctx.pc);
|
||||
|
||||
version(ENABLE_RENDERER)
|
||||
{
|
||||
PushConstants(&ctx.rd, ctx.pipeline, &ctx.projection);
|
||||
Bind(&ctx.rd, ctx.pipeline, ctx.desc_set);
|
||||
}
|
||||
|
||||
memset(ctx.buffers[ctx.f_idx].vtx.ptr, 0, Vertex.sizeof * ctx.buffers[ctx.f_idx].count);
|
||||
memset(ctx.buffers[ctx.f_idx].idx.ptr, 0, u32.sizeof * ctx.buffers[ctx.f_idx].count);
|
||||
ctx.buffers[ctx.f_idx].count = 0;
|
||||
|
||||
ctx.root = Root(ctx);
|
||||
// Root Item
|
||||
UISize[2] sizes = [UISize(ST.Pixels, ctx.res.x), UISize(ST.Pixels, ctx.res.y)];
|
||||
Push!("size_info", true)(ctx, sizes);
|
||||
ctx.root = MakeItem("###root");
|
||||
Push!("parent")(ctx, ctx.root);
|
||||
}
|
||||
|
||||
void
|
||||
@ -592,32 +681,161 @@ EndUI()
|
||||
{
|
||||
if(item.size_info[axis].type == ST.Pixels)
|
||||
{
|
||||
item.size.v[axis] = item.size_info[axis].value;
|
||||
item.size.v[axis] = item.size_info[axis].value + item.padding.v[axis];
|
||||
}
|
||||
}
|
||||
|
||||
for(UIItem* item = ctx.root; !Nil(item); item = Recurse!(true)(item, ctx.root, g_UI_NIL))
|
||||
{
|
||||
if(item.size_info[axis].type == ST.Percentage)
|
||||
{
|
||||
for(UIItem* p = item.parent; !Nil(p); p = p.parent)
|
||||
{
|
||||
if(p.size_info[axis].type == ST.Pixels)
|
||||
{
|
||||
item.size.v[axis] = floor(item.size_info[axis].value * item.parent.size[axis]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
with(ctx)
|
||||
for(UIItem* item = ctx.root; !Nil(item); item = Recurse!(false)(item, ctx.root, g_UI_NIL))
|
||||
{
|
||||
if(item.size_info[axis].type == ST.ChildrenSum)
|
||||
{
|
||||
f32 size = 0.0;
|
||||
for(UIItem* c = item.first; !Nil(c); c = c.next)
|
||||
{
|
||||
size += c.size.v[axis];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(UIItem* item = ctx.root; !Nil(item); item = Recurse!(true)(item, ctx.root, g_UI_NIL))
|
||||
{
|
||||
if(axis == item.layout_axis)
|
||||
{
|
||||
f32 children_size = 0.0;
|
||||
for(UIItem* c = item.first; !Nil(c); c = c.next)
|
||||
{
|
||||
children_size += c.size.v[axis];
|
||||
}
|
||||
|
||||
if(children_size > item.size[axis])
|
||||
{
|
||||
Logf("%s %s %s %s", axis, cast(char[])item.key.hash_text, children_size, item.size[axis]);
|
||||
u64 child_count;
|
||||
for(UIItem* c = item.first; !Nil(c); c = c.next)
|
||||
{
|
||||
f32 reduced = c.size.v[axis] - c.size.v[axis]*c.size_info[axis].strictness;
|
||||
|
||||
children_size -= reduced;
|
||||
c.size.v[axis] -= reduced;
|
||||
|
||||
child_count += 1;
|
||||
}
|
||||
|
||||
if(children_size > 0.0)
|
||||
{
|
||||
for(UIItem* c = item.last; !Nil(c); c = c.prev)
|
||||
{
|
||||
f32 reduced = Min(children_size, c.size[axis]);
|
||||
children_size -= reduced;
|
||||
c.size.v[axis] -= reduced;
|
||||
|
||||
if(children_size < 0.0009)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert(children_size < 0.0009);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(UIItem* c = item.first; !Nil(c); c = c.next)
|
||||
{
|
||||
if(c.size.v[axis] > item.size.v[axis])
|
||||
{
|
||||
c.size.v[axis] = item.size.v[axis];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
f32 pos = 0.0;
|
||||
for(UIItem* item = ctx.root; !Nil(item);)
|
||||
{
|
||||
f32 next_pos = 0.0;
|
||||
f32 end_pos = pos + item.size.v[axis];
|
||||
|
||||
item.rect.p0.v[axis] = pos;
|
||||
item.rect.p1.v[axis] = end_pos;
|
||||
|
||||
assert(!isNaN(item.rect.p0.v[axis]));
|
||||
assert(!isNaN(item.rect.p1.v[axis]));
|
||||
|
||||
next_pos = item.parent.layout_axis == axis ? end_pos : pos;
|
||||
|
||||
if(!Nil(item.first))
|
||||
{
|
||||
item = item.first;
|
||||
}
|
||||
else if(!Nil(item.next))
|
||||
{
|
||||
item = item.next;
|
||||
pos = next_pos;
|
||||
}
|
||||
else for(UIItem* p = item.parent;; p = p.parent)
|
||||
{
|
||||
if(!Nil(p.next))
|
||||
{
|
||||
item = p.next;
|
||||
pos = item.parent.layout_axis == axis ? item.prev.rect.p1.v[axis] : item.prev.rect.p0.v[axis];
|
||||
break;
|
||||
}
|
||||
|
||||
if(Nil(p))
|
||||
{
|
||||
item = g_UI_NIL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
version(ENABLE_RENDERER) with(ctx)
|
||||
{
|
||||
BindBuffers(&rd, &buffers[f_idx].m_idx, &buffers[f_idx].m_vtx);
|
||||
DrawIndexed(&rd, 6, buffers[f_idx].count, 0);
|
||||
}
|
||||
|
||||
FinishRendering(&ctx.rd);
|
||||
SubmitAndPresent(&ctx.rd);
|
||||
FinishRendering(&rd);
|
||||
SubmitAndPresent(&rd);
|
||||
}
|
||||
|
||||
ctx.frame += 1;
|
||||
}
|
||||
|
||||
UIItem*
|
||||
Root(UICtx* ctx)
|
||||
u32[2]
|
||||
GetExtent(Renderer* rd)
|
||||
{
|
||||
UISize[2] sizes = [UISize(ST.Pixels, ctx.res.x), UISize(ST.Pixels, ctx.res.y)];
|
||||
Push!("size_info")(ctx, sizes);
|
||||
return MakeItem("###root");
|
||||
version(ENABLE_RENDERER)
|
||||
{
|
||||
return RendererGetExtent(rd);
|
||||
}
|
||||
else
|
||||
{
|
||||
return [1280, 720];
|
||||
}
|
||||
}
|
||||
|
||||
template StackIDs(string stack)
|
||||
template
|
||||
StackIDs(string stack)
|
||||
{
|
||||
import std.string : replace;
|
||||
struct Identifiers { string stack, stack_top_node; }
|
||||
@ -937,6 +1155,7 @@ struct TextBuffer
|
||||
{
|
||||
u8[] text;
|
||||
TS[] style;
|
||||
TextBuffer* next;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1094,7 +1313,7 @@ DrawPanel(UIPanel* panel, f32 lc_w, bool focus)
|
||||
{
|
||||
UICtx* ctx = GetCtx();
|
||||
|
||||
Vec2 pos = panel.rect.vec0;
|
||||
Vec2 pos = panel.rect.p0;
|
||||
Vec2 size = panel.size;
|
||||
|
||||
DrawRect(pos, size, ctx.corner_radius_top.value, ctx.border_thickness_top.value, ctx.bg_col_top.value);
|
||||
@ -1102,6 +1321,24 @@ DrawPanel(UIPanel* panel, f32 lc_w, bool focus)
|
||||
DrawBorder(pos, size, ctx.border_thickness_top.value, ctx.corner_radius_top.value, ctx.edge_softness_top.value, focus ? ctx.border_hl_col_top.value : ctx.border_col_top.value);
|
||||
}
|
||||
|
||||
static UISize[2]
|
||||
MakeUISize(UISize x, UISize y)
|
||||
{
|
||||
return [x, y];
|
||||
}
|
||||
|
||||
static UISize[2]
|
||||
MakeUISizeX(SizeType type, f32 value, f32 strictness = 1.0)
|
||||
{
|
||||
return [UISize(type, value, strictness), UISize(ST.Percentage, 1.0)];
|
||||
}
|
||||
|
||||
static UISize[2]
|
||||
MakeUISizeY(SizeType type, f32 value, f32 strictness = 1.0)
|
||||
{
|
||||
return [UISize(ST.Percentage, 1.0), UISize(type, value, strictness)];
|
||||
}
|
||||
|
||||
pragma(inline) Glyph*
|
||||
GetGlyph(u8 ch)
|
||||
{
|
||||
@ -1187,25 +1424,25 @@ Nil(UIItem* item)
|
||||
}
|
||||
|
||||
pragma(inline) bool
|
||||
InBounds(InputEvent* ev, Vec2 p0, Vec2 p1)
|
||||
InBounds(T)(T pos, Rect* rect)
|
||||
{
|
||||
return ev.x >= p0.x && ev.x <= p1.x && ev.y >= p0.y && ev.y <= p1.y;
|
||||
return pos.x >= rect.p0.x && pos.x <= rect.p1.x && pos.y >= rect.p0.y && pos.y <= rect.p1.y;
|
||||
}
|
||||
|
||||
bool
|
||||
Clicked(UIItem* item, Vec2 p0, Vec2 p1)
|
||||
Clicked(UIItem* item, Rect* rect)
|
||||
{
|
||||
bool result;
|
||||
UICtx* ctx = GetCtx();
|
||||
|
||||
for(auto n = ctx.inputs.list.first; !CheckNil(null, n) && Nil(ctx.drag_item); n = n.next)
|
||||
for(auto n = ctx.inputs.first; !CheckNil(null, n) && Nil(ctx.drag_item); n = n.next)
|
||||
{
|
||||
InputEvent* ev = &n.value;
|
||||
InputEvent* ev = n;
|
||||
|
||||
if(ev.key == Input.LeftClick && ev.pressed && InBounds(ev, p0, p1))
|
||||
if(ev.key == Input.LeftClick && ev.pressed && InBounds(ev, rect))
|
||||
{
|
||||
result = true;
|
||||
DLLRemove(&ctx.inputs.list, n, null);
|
||||
DLLRemove(ctx.inputs, n, null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1214,27 +1451,27 @@ Clicked(UIItem* item, Vec2 p0, Vec2 p1)
|
||||
}
|
||||
|
||||
bool
|
||||
Dragged(UIItem* item, Vec2 p0, Vec2 p1)
|
||||
Dragged(UIItem* item, Rect* rect)
|
||||
{
|
||||
bool result;
|
||||
UICtx* ctx = GetCtx();
|
||||
|
||||
item.dragged = 0.0;
|
||||
item.dragged = 0;
|
||||
|
||||
if(Nil(ctx.drag_item) && Clicked(item, p0, p1))
|
||||
if(Nil(ctx.drag_item) && Clicked(item, rect))
|
||||
{
|
||||
ctx.drag_item = item;
|
||||
}
|
||||
|
||||
for(auto n = ctx.inputs.list.first; !CheckNil(null, n) && ctx.drag_item == item; n = n.next)
|
||||
for(auto n = ctx.inputs.first; !CheckNil(null, n) && ctx.drag_item == item; n = n.next)
|
||||
{
|
||||
InputEvent* ev = &n.value;
|
||||
InputEvent* ev = n;
|
||||
bool taken;
|
||||
|
||||
if(ev.key == Input.MouseMotion)
|
||||
{
|
||||
item.dragged.x += cast(f32)ev.rel_x;
|
||||
item.dragged.y += cast(f32)ev.rel_y;
|
||||
item.dragged.x += ev.rel_x;
|
||||
item.dragged.y += ev.rel_y;
|
||||
result = true;
|
||||
taken = true;
|
||||
}
|
||||
@ -1246,7 +1483,7 @@ Dragged(UIItem* item, Vec2 p0, Vec2 p1)
|
||||
|
||||
if(taken)
|
||||
{
|
||||
DLLRemove(&ctx.inputs.list, n, null);
|
||||
DLLRemove(ctx.inputs, n, null);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1270,17 +1507,47 @@ unittest
|
||||
}
|
||||
|
||||
{
|
||||
UICtx ctx;
|
||||
InitStacks(&ctx);
|
||||
ResetStacks(&ctx);
|
||||
Inputs inputs;
|
||||
InitUICtx(null);
|
||||
BeginUI(&inputs);
|
||||
|
||||
UICtx* ctx = GetCtx();
|
||||
|
||||
Vec4 w = Vec4(1.0);
|
||||
Vec4[4] col = w;
|
||||
|
||||
Push!("bg_col")(&ctx, col);
|
||||
Push!("bg_col")(ctx, col);
|
||||
assert(ctx.bg_col.top.value == col);
|
||||
Pop!("bg_col")(&ctx);
|
||||
|
||||
EndUI();
|
||||
|
||||
Vec2 extent = GetExtent(null);
|
||||
assert(extent == ctx.root.size);
|
||||
|
||||
BeginUI(&inputs);
|
||||
assert(ctx.bg_col.top.value == BG_COL);
|
||||
EndUI();
|
||||
|
||||
// Layouts
|
||||
{
|
||||
BeginUI(&inputs);
|
||||
|
||||
Push!("size_info")(ctx, MakeUISizeX(ST.Percentage, 0.5));
|
||||
UIItem* root = ctx.root;
|
||||
|
||||
UIItem* i0 = MakeItem("###i0");
|
||||
UIItem* i1 = MakeItem("###i1");
|
||||
|
||||
assert(i0.parent == root);
|
||||
assert(i1.parent == root);
|
||||
|
||||
EndUI();
|
||||
|
||||
Vec2 expected = Vec2(floor(root.size.x * 0.5), root.size.y);
|
||||
|
||||
assert(i0.size == expected);
|
||||
assert(i1.size == expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -25,18 +25,20 @@ Panel(UIPanel* panel)
|
||||
parent.axis == A2D.Y ? 10 : 0
|
||||
);
|
||||
|
||||
Vec2 p0 = panel.rect.vec0+adj;
|
||||
Vec2 p1 = panel.rect.vec1-adj;
|
||||
Vec2 p0 = panel.rect.p0+adj;
|
||||
Vec2 p1 = panel.rect.p1-adj;
|
||||
Rect r = Rect(p0: p0, p1: p1);
|
||||
|
||||
if(!Nil(prev)) with(panel)
|
||||
{
|
||||
Vec2 d0 = rect.vec0-adj;
|
||||
Vec2 d0 = rect.p0-adj;
|
||||
Vec2 d1 = Vec2(
|
||||
pax == A2D.X ? rect.vec0.x+adj.x : rect.vec1.x,
|
||||
pax == A2D.Y ? rect.vec0.y+adj.y : rect.vec1.y
|
||||
pax == A2D.X ? rect.p0.x+adj.x : rect.p1.x,
|
||||
pax == A2D.Y ? rect.p0.y+adj.y : rect.p1.y
|
||||
);
|
||||
|
||||
if(Dragged(item, d0, d1) && item.dragged.v[pax] != 0.0)
|
||||
Rect dr = Rect(p0: d0, p1: d1);
|
||||
if(Dragged(item, &dr) && item.dragged.v[pax] != 0.0)
|
||||
{
|
||||
f32 mov_pct = Remap(item.dragged.v[pax], 0.0, panel.parent.size.v[pax], 0.0, 1.0);
|
||||
if(CheckPanelBounds(pct + mov_pct) && CheckPanelBounds(panel.prev.pct - mov_pct))
|
||||
@ -49,7 +51,7 @@ Panel(UIPanel* panel)
|
||||
|
||||
if(panel.ed != null)
|
||||
{
|
||||
if(Clicked(item, p0, p1))
|
||||
if(Clicked(item, &r))
|
||||
{
|
||||
SetFocusedPanel(panel);
|
||||
}
|
||||
@ -76,8 +78,8 @@ Panel(UIPanel* panel)
|
||||
|
||||
f32 y_rem = fmod(panel.scroll_offset, TEXT_SIZE);
|
||||
|
||||
f32 x = panel.rect.x0;
|
||||
f32 y = panel.rect.y0 + TEXT_SIZE - fmod(panel.scroll_offset, TEXT_SIZE);
|
||||
f32 x = panel.rect.p0.x;
|
||||
f32 y = panel.rect.p1.y + TEXT_SIZE - fmod(panel.scroll_offset, TEXT_SIZE);
|
||||
|
||||
u64 i = panel.start_ln;
|
||||
for(auto buf = GetLine(&ed.buf, i); !CheckNil(g_NIL_LINE_BUF, buf) && i < panel.end_ln; i += 1, buf = GetLine(&ed.buf, i))
|
||||
@ -97,7 +99,7 @@ Panel(UIPanel* panel)
|
||||
}
|
||||
else for(auto n = parts; n != null; n = n.next)
|
||||
{
|
||||
auto l = &n.value;
|
||||
auto l = n;
|
||||
|
||||
if(pos.y == i)
|
||||
{
|
||||
@ -202,14 +204,14 @@ Nil(UIPanel* panel)
|
||||
return panel == null || panel == g_UI_NIL_PANEL;
|
||||
}
|
||||
|
||||
Node!(TextBuffer)*
|
||||
TextBuffer*
|
||||
MakeMultiline(u8[] text, f32 width, TS[] style = [])
|
||||
{
|
||||
f32 scaled_width = width * (g_ui_ctx.atlas_buf.atlas.size/g_ui_ctx.text_size);
|
||||
f32 text_width = CalcTextWidth(text);
|
||||
|
||||
u64 line_count = cast(u64)(ceil(text_width/scaled_width));
|
||||
Node!(TextBuffer)* node = null;
|
||||
TextBuffer* node = null;
|
||||
if(line_count > 0)
|
||||
{
|
||||
f32 w = 0.0;
|
||||
@ -232,15 +234,15 @@ MakeMultiline(u8[] text, f32 width, TS[] style = [])
|
||||
stl = ScratchAlloc!(TS)(style, start, len);
|
||||
}
|
||||
|
||||
Node!(TextBuffer)* n = node;
|
||||
TextBuffer* n = node;
|
||||
for(;;)
|
||||
{
|
||||
if(node == null)
|
||||
{
|
||||
node = ScratchAlloc!(Node!(TextBuffer))();
|
||||
node = ScratchAlloc!(TextBuffer)();
|
||||
|
||||
node.value.text = str;
|
||||
node.value.style = stl;
|
||||
node.text = str;
|
||||
node.style = stl;
|
||||
node.next = null;
|
||||
|
||||
break;
|
||||
@ -248,10 +250,10 @@ MakeMultiline(u8[] text, f32 width, TS[] style = [])
|
||||
|
||||
if(n.next == null)
|
||||
{
|
||||
n.next = ScratchAlloc!(Node!(TextBuffer))();
|
||||
n.next = ScratchAlloc!(TextBuffer)();
|
||||
|
||||
n.next.value.text = str;
|
||||
n.next.value.style = stl;
|
||||
n.next.text = str;
|
||||
n.next.style = stl;
|
||||
n.next.next = null;
|
||||
|
||||
break;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user