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"],
|
"sourcePaths": ["src/editor", "src/dlib", "src/dlib/external/xxhash", "src/VulkanRenderer"],
|
||||||
"libs-linux": ["X11", "vulkan", "stdc++", "xfixes", "freetype"],
|
"libs-linux": ["X11", "vulkan", "stdc++", "xfixes", "freetype"],
|
||||||
"libs-windows": [],
|
"libs-windows": [],
|
||||||
"versions": ["VULKAN_DEBUG"],
|
"versions": ["VULKAN_DEBUG", "ENABLE_RENDERER"],
|
||||||
"preGenerateCommands-linux": ["./build.sh"],
|
"preGenerateCommands-linux": ["./build.sh"],
|
||||||
"preGenerateCommands-windows": [],
|
"preGenerateCommands-windows": [],
|
||||||
"dflags": ["-Xcc=-mno-sse", "-P-I/usr/include/freetype2", "-Jbuild", "-Jassets", "-link-debuglib"],
|
"dflags": ["-Xcc=-mno-sse", "-P-I/usr/include/freetype2", "-Jbuild", "-Jassets", "-link-debuglib"],
|
||||||
"dflags-dmd": ["-P=-DSTBI_NO_SIMD"]
|
"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;
|
g_input_mode = ctx.state == ES.InputMode;
|
||||||
|
|
||||||
BeginUI(ctx, inputs);
|
BeginUI(inputs);
|
||||||
|
|
||||||
UIPanel* root = ctx.base_panel;
|
UIPanel* root = ctx.base_panel;
|
||||||
|
|
||||||
root.size.x = g_ui_ctx.res.x;
|
root.size.x = g_ui_ctx.res.x;
|
||||||
root.size.y = g_ui_ctx.res.y;
|
root.size.y = g_ui_ctx.res.y;
|
||||||
root.rect.vec0 = 0.0;
|
root.rect.p0 = 0.0;
|
||||||
root.rect.vec1 = 0.0;
|
root.rect.p1 = 0.0;
|
||||||
|
|
||||||
static foreach(axis; A2D.min .. A2D.max)
|
static foreach(axis; A2D.min .. A2D.max)
|
||||||
{
|
{
|
||||||
for(UIPanel* p = root; !Nil(p); p = Recurse(p, root, g_UI_NIL_PANEL))
|
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)
|
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.size.v[axis] = p.axis == axis ? c.pct * p.size.v[axis] : p.size.v[axis];
|
||||||
c.rect.vec0.v[axis] = pos;
|
c.rect.p0.v[axis] = pos;
|
||||||
c.rect.vec1.v[axis] = pos + c.size.v[axis];
|
c.rect.p1.v[axis] = pos + c.size.v[axis];
|
||||||
|
|
||||||
if(axis == p.axis)
|
if(axis == p.axis)
|
||||||
{
|
{
|
||||||
@ -571,13 +571,13 @@ HandleInputs(EditorCtx* ctx, Inputs* inputs)
|
|||||||
UIPanel* panel = GetFocusedPanel();
|
UIPanel* panel = GetFocusedPanel();
|
||||||
FlatBuffer* fb = !Nil(panel) ? &panel.ed.buf : null;
|
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;
|
bool taken = false;
|
||||||
|
|
||||||
Input key = node.value.key;
|
Input key = node.key;
|
||||||
Modifier md = node.value.md;
|
Modifier md = node.md;
|
||||||
bool pressed = node.value.pressed;
|
bool pressed = node.pressed;
|
||||||
|
|
||||||
if (pressed)
|
if (pressed)
|
||||||
{
|
{
|
||||||
@ -588,11 +588,11 @@ HandleInputs(EditorCtx* ctx, Inputs* inputs)
|
|||||||
}
|
}
|
||||||
else if(ctx.state == ES.InputMode)
|
else if(ctx.state == ES.InputMode)
|
||||||
{
|
{
|
||||||
taken = HandleInputMode(ctx, node.value);
|
taken = HandleInputMode(ctx, node);
|
||||||
}
|
}
|
||||||
else if(ctx.state == ES.CmdOpen)
|
else if(ctx.state == ES.CmdOpen)
|
||||||
{
|
{
|
||||||
taken = HandleCmdMode(ctx, node.value);
|
taken = HandleCmdMode(ctx, node);
|
||||||
}
|
}
|
||||||
else if(ctx.state == ES.SetPanelFocus)
|
else if(ctx.state == ES.SetPanelFocus)
|
||||||
{
|
{
|
||||||
@ -646,7 +646,7 @@ HandleInputs(EditorCtx* ctx, Inputs* inputs)
|
|||||||
} break;
|
} break;
|
||||||
case Semicolon:
|
case Semicolon:
|
||||||
{
|
{
|
||||||
if(Shift(node.value.md))
|
if(Shift(node.md))
|
||||||
{
|
{
|
||||||
ctx.state = ES.CmdOpen;
|
ctx.state = ES.CmdOpen;
|
||||||
taken = true;
|
taken = true;
|
||||||
@ -701,7 +701,7 @@ HandleInputs(EditorCtx* ctx, Inputs* inputs)
|
|||||||
|
|
||||||
if(taken)
|
if(taken)
|
||||||
{
|
{
|
||||||
DLLRemove(&inputs.list, node, null);
|
DLLRemove(inputs, node, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -714,7 +714,7 @@ HandleInputs(EditorCtx* ctx, Inputs* inputs)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MoveCursor(InputEvent ev)
|
MoveCursor(InputEvent* ev)
|
||||||
{
|
{
|
||||||
UIPanel* p = GetFocusedPanel();
|
UIPanel* p = GetFocusedPanel();
|
||||||
|
|
||||||
@ -754,7 +754,7 @@ CharCases()
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
HandleInputMode(EditorCtx* ctx, InputEvent ev)
|
HandleInputMode(EditorCtx* ctx, InputEvent* ev)
|
||||||
{
|
{
|
||||||
bool taken = false;
|
bool taken = false;
|
||||||
|
|
||||||
@ -912,7 +912,7 @@ GetCommands(CmdPalette* cmd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
HandleCmdMode(EditorCtx* ctx, InputEvent ev)
|
HandleCmdMode(EditorCtx* ctx, InputEvent* ev)
|
||||||
{
|
{
|
||||||
u8 result = 0;
|
u8 result = 0;
|
||||||
bool taken = false;
|
bool taken = false;
|
||||||
|
|||||||
@ -47,7 +47,7 @@ void main(string[] argv)
|
|||||||
SetExtent(&g_ui_ctx.rd, window.w, window.h);
|
SetExtent(&g_ui_ctx.rd, window.w, window.h);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(inputs.list.first == null)
|
if(inputs.first == null)
|
||||||
{
|
{
|
||||||
no_ev_count += 1;
|
no_ev_count += 1;
|
||||||
Pause();
|
Pause();
|
||||||
|
|||||||
689
src/editor/ui.d
689
src/editor/ui.d
@ -2,6 +2,7 @@ import dlib;
|
|||||||
import dlib.util : HTPush = Push;
|
import dlib.util : HTPush = Push;
|
||||||
|
|
||||||
import vulkan;
|
import vulkan;
|
||||||
|
import vulkan : RendererGetExtent = GetExtent;
|
||||||
import buffer;
|
import buffer;
|
||||||
import parsing;
|
import parsing;
|
||||||
import editor;
|
import editor;
|
||||||
@ -10,7 +11,7 @@ import views;
|
|||||||
|
|
||||||
import std.stdio;
|
import std.stdio;
|
||||||
import std.math.traits : isNaN;
|
import std.math.traits : isNaN;
|
||||||
import std.math.rounding : ceil;
|
import std.math.rounding : ceil, floor;
|
||||||
import std.math.exponential : pow;
|
import std.math.exponential : pow;
|
||||||
import std.math.remainder : fmod;
|
import std.math.remainder : fmod;
|
||||||
import std.format : sformat;
|
import std.format : sformat;
|
||||||
@ -48,39 +49,38 @@ const u8[] FONT_BYTES = import("pc-9800.ttf");
|
|||||||
const u8[] VERTEX_BYTES = import("gui.vert.spv");
|
const u8[] VERTEX_BYTES = import("gui.vert.spv");
|
||||||
const u8[] FRAGMENT_BYTES = import("gui.frag.spv");
|
const u8[] FRAGMENT_BYTES = import("gui.frag.spv");
|
||||||
|
|
||||||
f32 g_corner_radius_default = 2.0;
|
f32 g_corner_radius_default = 2.0;
|
||||||
f32 g_edge_softness_default = 0.1;
|
f32 g_edge_softness_default = 0.1;
|
||||||
f32 g_border_thickness_default = 2.0;
|
f32 g_border_thickness_default = 2.0;
|
||||||
Vec4[4] g_bg_col_default = BG_COL;
|
Vec4[4] g_bg_col_default = BG_COL;
|
||||||
Vec4[4] g_bg_hl_col_default = BG_HL_COL;
|
Vec4[4] g_bg_hl_col_default = BG_HL_COL;
|
||||||
Vec4[4] g_border_col_default = BORDER_COL;
|
Vec4[4] g_border_col_default = BORDER_COL;
|
||||||
Vec4[4] g_border_hl_col_default = BORDER_HL_COL;
|
Vec4[4] g_border_hl_col_default = BORDER_HL_COL;
|
||||||
Vec4 g_text_col_default = TEXT_COL;
|
Vec4 g_text_col_default = TEXT_COL;
|
||||||
Vec4 g_text_hl_col_default = TEXT_HL_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)];
|
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;
|
const UI_COUNT = 5000;
|
||||||
|
|
||||||
__gshared const UIPanel g_ui_nil_panel;
|
__gshared const UIPanel g_ui_nil_panel;
|
||||||
__gshared UIPanel* g_UI_NIL_PANEL;
|
__gshared UIPanel* g_UI_NIL_PANEL;
|
||||||
|
__gshared const UIItem g_ui_nil_item;
|
||||||
__gshared const UIItem g_ui_nil_item;
|
__gshared UIItem* g_UI_NIL;
|
||||||
__gshared UIItem* 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;
|
alias g_parent_default = g_UI_NIL;
|
||||||
|
|
||||||
__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;
|
|
||||||
|
|
||||||
enum Axis2D
|
enum Axis2D
|
||||||
{
|
{
|
||||||
X,
|
X,
|
||||||
@ -106,31 +106,47 @@ enum UIFlags
|
|||||||
|
|
||||||
Clamp = UIFlags.ClampX | UIFlags.ClampY,
|
Clamp = UIFlags.ClampX | UIFlags.ClampY,
|
||||||
}
|
}
|
||||||
|
|
||||||
alias UIF = UIFlags;
|
alias UIF = UIFlags;
|
||||||
|
|
||||||
enum UIEventFlag
|
|
||||||
{
|
|
||||||
None, // Events e.g. pressing/releasing/scrolling
|
|
||||||
}
|
|
||||||
|
|
||||||
enum UISignal
|
enum UISignal
|
||||||
{
|
{
|
||||||
None = 0x00,
|
None = 0,
|
||||||
Clicked = 0x01,
|
Clicked = 1<<0,
|
||||||
Dragged = 0x02, // Handle click/drag specifics in view?
|
Dragged = 1<<1,
|
||||||
|
Hovered = 1<<2,
|
||||||
}
|
}
|
||||||
|
|
||||||
alias UIS = UISignal;
|
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
|
mixin template
|
||||||
UICtxParameter(T, string name)
|
UICtxParameter(T, string name)
|
||||||
{
|
{
|
||||||
static string
|
static string
|
||||||
CtxParameterGen(T, string name)()
|
CtxParameterGen(T, string name)()
|
||||||
{
|
{
|
||||||
import std.traits;
|
import std.traits, std.array;
|
||||||
import std.array;
|
|
||||||
|
|
||||||
string stack_top = "\tStackTop!("~T.stringof~") "~name~";\n";
|
string stack_top = "\tStackTop!("~T.stringof~") "~name~";\n";
|
||||||
string stack = "\tStack!("~T.stringof~")* "~name~"_top;\n";
|
string stack = "\tStack!("~T.stringof~")* "~name~"_top;\n";
|
||||||
@ -178,7 +194,11 @@ struct UICtx
|
|||||||
u64 frame;
|
u64 frame;
|
||||||
u64 f_idx;
|
u64 f_idx;
|
||||||
|
|
||||||
PlatformWindow* window;
|
LinkedList!(UIInput) events;
|
||||||
|
IVec2 mouse_pos;
|
||||||
|
IVec2 drag_start;
|
||||||
|
|
||||||
|
PlatformWindow* window;
|
||||||
Renderer rd;
|
Renderer rd;
|
||||||
Descriptor font_atlas;
|
Descriptor font_atlas;
|
||||||
Descriptor sampler;
|
Descriptor sampler;
|
||||||
@ -186,7 +206,7 @@ PlatformWindow* window;
|
|||||||
DescSetLayout desc_set_layout;
|
DescSetLayout desc_set_layout;
|
||||||
DescSet desc_set;
|
DescSet desc_set;
|
||||||
PipelineLayout pipeline_layout;
|
PipelineLayout pipeline_layout;
|
||||||
PushConst pc;
|
Mat4 projection;
|
||||||
Vec2 res;
|
Vec2 res;
|
||||||
|
|
||||||
u8[] font_data;
|
u8[] font_data;
|
||||||
@ -215,6 +235,8 @@ PlatformWindow* window;
|
|||||||
mixin UICtxParameter!(Vec4, "text_col");
|
mixin UICtxParameter!(Vec4, "text_col");
|
||||||
mixin UICtxParameter!(Vec4, "text_hl_col");
|
mixin UICtxParameter!(Vec4, "text_hl_col");
|
||||||
mixin UICtxParameter!(UISize[2], "size_info");
|
mixin UICtxParameter!(UISize[2], "size_info");
|
||||||
|
mixin UICtxParameter!(Axis2D, "layout_axis");
|
||||||
|
mixin UICtxParameter!(Vec2, "padding");
|
||||||
|
|
||||||
debug bool dbg;
|
debug bool dbg;
|
||||||
}
|
}
|
||||||
@ -257,14 +279,17 @@ mixin template UIItemParameters()
|
|||||||
|
|
||||||
struct UIItem
|
struct UIItem
|
||||||
{
|
{
|
||||||
UIKey key;
|
UIKey key;
|
||||||
u64 last_frame;
|
UIFlags flags;
|
||||||
Vec2 dragged;
|
u64 last_frame;
|
||||||
|
IVec2 dragged;
|
||||||
|
|
||||||
UIItem* next, prev, first, last; // parent in mixin
|
UIItem* next, prev, first, last; // parent in mixin
|
||||||
|
|
||||||
|
Rect rect;
|
||||||
|
Vec2 size;
|
||||||
|
|
||||||
Rect pref_size;
|
u8[] display_string;
|
||||||
Rect size;
|
|
||||||
|
|
||||||
mixin UIItemParameters!();
|
mixin UIItemParameters!();
|
||||||
}
|
}
|
||||||
@ -273,7 +298,7 @@ enum SizeType
|
|||||||
{
|
{
|
||||||
Pixels,
|
Pixels,
|
||||||
Percentage,
|
Percentage,
|
||||||
FitChild,
|
ChildrenSum,
|
||||||
}
|
}
|
||||||
|
|
||||||
alias ST = SizeType;
|
alias ST = SizeType;
|
||||||
@ -294,23 +319,10 @@ struct UIBuffer
|
|||||||
u32 count;
|
u32 count;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PushConst
|
|
||||||
{
|
|
||||||
Mat4 projection;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct GlyphBounds
|
struct GlyphBounds
|
||||||
{
|
{
|
||||||
f32 r = 0.0;
|
f32 r = 0.0, l = 0.0, t = 0.0, b = 0.0, w = 0.0, h = 0.0;
|
||||||
f32 l = 0.0;
|
f32 atlas_r = 0.0, atlas_l = 0.0, atlas_t = 0.0, atlas_b = 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Vertex
|
struct Vertex
|
||||||
@ -331,18 +343,7 @@ struct Vertex
|
|||||||
union Rect
|
union Rect
|
||||||
{
|
{
|
||||||
Vec2[2] v;
|
Vec2[2] v;
|
||||||
struct
|
struct { Vec2 p0, p1; };
|
||||||
{
|
|
||||||
Vec2 vec0;
|
|
||||||
Vec2 vec1;
|
|
||||||
};
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
f32 x0;
|
|
||||||
f32 y0;
|
|
||||||
f32 x1;
|
|
||||||
f32 y1;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
alias UIHash = u64;
|
alias UIHash = u64;
|
||||||
@ -351,8 +352,7 @@ alias UIPair = KVPair!(UIHash, UIItem*);
|
|||||||
|
|
||||||
struct UIKey
|
struct UIKey
|
||||||
{
|
{
|
||||||
u8[] text;
|
u8[] text, hash_text;
|
||||||
u8[] hash_text;
|
|
||||||
u64 hash;
|
u64 hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -361,14 +361,7 @@ struct UIPanel
|
|||||||
u8[] id;
|
u8[] id;
|
||||||
Editor* ed;
|
Editor* ed;
|
||||||
|
|
||||||
UIPanel* parent;
|
UIPanel* parent, next, prev, first, last, list_next;
|
||||||
UIPanel* next;
|
|
||||||
UIPanel* prev;
|
|
||||||
UIPanel* first;
|
|
||||||
UIPanel* last;
|
|
||||||
|
|
||||||
UIPanel* list_next;
|
|
||||||
|
|
||||||
Axis2D axis;
|
Axis2D axis;
|
||||||
f32 pct;
|
f32 pct;
|
||||||
Vec4 color;
|
Vec4 color;
|
||||||
@ -376,12 +369,8 @@ struct UIPanel
|
|||||||
Rect rect;
|
Rect rect;
|
||||||
Vec2 size;
|
Vec2 size;
|
||||||
|
|
||||||
i64 start_ln;
|
i64 start_ln, end_ln, vis_lines, prev_start_ln;
|
||||||
i64 end_ln;
|
f32 scroll_offset, scroll_target;
|
||||||
i64 vis_lines;
|
|
||||||
i64 prev_start_ln;
|
|
||||||
f32 scroll_offset;
|
|
||||||
f32 scroll_target;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TextPart
|
struct TextPart
|
||||||
@ -391,28 +380,30 @@ struct TextPart
|
|||||||
u32 count;
|
u32 count;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep defaults, pop anything used once
|
|
||||||
|
|
||||||
enum bool StringType(T) = (is(T: string) || is(T: u8[]) || is(T: char[]));
|
enum bool StringType(T) = (is(T: string) || is(T: u8[]) || is(T: char[]));
|
||||||
|
|
||||||
void
|
void
|
||||||
InitUICtx(PlatformWindow* window)
|
InitUICtx(PlatformWindow* window)
|
||||||
{
|
{
|
||||||
version(linux)
|
version(ENABLE_RENDERER)
|
||||||
{
|
{
|
||||||
PlatformHandles handles = {
|
version(linux)
|
||||||
display: window.display,
|
{
|
||||||
window: window.window,
|
PlatformHandles handles = {
|
||||||
};
|
display: window.display,
|
||||||
}
|
window: window.window,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
version(Windows)
|
version(Windows)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
g_UI_NIL = cast(UIItem*)&g_ui_nil_item;
|
g_UI_NIL = cast(UIItem*)&g_ui_nil_item;
|
||||||
g_UI_NIL_PANEL = cast(UIPanel*)&g_ui_nil_panel;
|
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_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_GRAD_COL_STACK = cast(Stack!Vec4[4]*)&g_nil_gradient_col_stack;
|
||||||
g_NIL_VEC4_STACK = cast(Stack!Vec4*)&g_nil_vec4_stack;
|
g_NIL_VEC4_STACK = cast(Stack!Vec4*)&g_nil_vec4_stack;
|
||||||
@ -424,83 +415,79 @@ InitUICtx(PlatformWindow* window)
|
|||||||
|
|
||||||
UIBuffer[FRAME_OVERLAP] buffers;
|
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 = {
|
UICtx ctx = {
|
||||||
rd: InitRenderer(handles, MB(16), MB(8)),
|
|
||||||
items: CreateHashTable!(UIHash, UIItem*)(12),
|
items: CreateHashTable!(UIHash, UIItem*)(12),
|
||||||
arena: arena,
|
arena: arena,
|
||||||
temp_arena: CreateArena(MB(1)),
|
temp_arena: CreateArena(MB(1)),
|
||||||
drag_item: g_UI_NIL,
|
drag_item: g_UI_NIL,
|
||||||
atlas_buf: atlas_buf,
|
font: OpenFont(cast(u8[])FONT_BYTES),
|
||||||
font: font,
|
|
||||||
font_data: cast(u8[])FONT_BYTES,
|
font_data: cast(u8[])FONT_BYTES,
|
||||||
text_size: 16.0,
|
text_size: 16.0,
|
||||||
tab_width: 2,
|
tab_width: 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
for(u64 i = 0; i < FRAME_OVERLAP; i += 1)
|
ctx.atlas_buf = CreateAtlas(&arena, ctx.font, 16.0, 256);
|
||||||
|
|
||||||
|
version(ENABLE_RENDERER)
|
||||||
{
|
{
|
||||||
ctx.buffers[i].m_vtx = CreateMappedBuffer!(Vertex)(&ctx.rd, BT.Vertex, VERTEX_MAX_COUNT);
|
ctx.rd = InitRenderer(handles, MB(16), MB(8));
|
||||||
ctx.buffers[i].m_idx = CreateMappedBuffer!(u32)(&ctx.rd, BT.Index, cast(u64)(ceil(VERTEX_MAX_COUNT*1.5)));
|
|
||||||
ctx.buffers[i].vtx = ctx.buffers[i].m_vtx.data;
|
for(u64 i = 0; i < FRAME_OVERLAP; i += 1)
|
||||||
ctx.buffers[i].idx = ctx.buffers[i].m_idx.data;
|
{
|
||||||
|
ctx.buffers[i].m_vtx = CreateMappedBuffer!(Vertex)(&ctx.rd, BT.Vertex, VERTEX_MAX_COUNT);
|
||||||
|
ctx.buffers[i].m_idx = CreateMappedBuffer!(u32)(&ctx.rd, BT.Index, cast(u64)(ceil(VERTEX_MAX_COUNT*1.5)));
|
||||||
|
ctx.buffers[i].vtx = ctx.buffers[i].m_vtx.data;
|
||||||
|
ctx.buffers[i].idx = ctx.buffers[i].m_idx.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
DescLayoutBinding[2] layout_bindings = [
|
||||||
|
{ binding: 0, descriptorType: DT.Image, descriptorCount: 1, stageFlags: SS.All },
|
||||||
|
{ binding: 1, descriptorType: DT.Sampler, descriptorCount: 1, stageFlags: SS.All },
|
||||||
|
];
|
||||||
|
|
||||||
|
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, Mat4.sizeof);
|
||||||
|
|
||||||
|
Attribute[14] attributes = [
|
||||||
|
{ binding: 0, location: 0, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof + Vec4.sizeof * 0},
|
||||||
|
{ binding: 0, location: 1, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof + Vec4.sizeof * 1},
|
||||||
|
{ binding: 0, location: 2, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof + Vec4.sizeof * 2},
|
||||||
|
{ binding: 0, location: 3, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof + Vec4.sizeof * 3},
|
||||||
|
{ binding: 0, location: 4, format: FMT.RG_F32, offset: Vertex.dst_start.offsetof },
|
||||||
|
{ binding: 0, location: 5, format: FMT.RG_F32, offset: Vertex.dst_end.offsetof },
|
||||||
|
{ binding: 0, location: 6, format: FMT.RG_F32, offset: Vertex.src_start.offsetof },
|
||||||
|
{ binding: 0, location: 7, format: FMT.RG_F32, offset: Vertex.src_end.offsetof },
|
||||||
|
{ binding: 0, location: 8, format: FMT.R_F32, offset: Vertex.border_thickness.offsetof },
|
||||||
|
{ binding: 0, location: 9, format: FMT.R_F32, offset: Vertex.corner_radius.offsetof },
|
||||||
|
{ binding: 0, location: 10, format: FMT.R_F32, offset: Vertex.edge_softness.offsetof },
|
||||||
|
{ binding: 0, location: 11, format: FMT.R_F32, offset: Vertex.raised.offsetof },
|
||||||
|
{ binding: 0, location: 12, format: FMT.R_F32, offset: Vertex.z_index.offsetof },
|
||||||
|
{ binding: 0, location: 13, format: FMT.R_U32, offset: Vertex.texture.offsetof },
|
||||||
|
];
|
||||||
|
|
||||||
|
GfxPipelineInfo ui_info = {
|
||||||
|
vertex_shader: cast(u8[])VERTEX_BYTES,
|
||||||
|
frag_shader: cast(u8[])FRAGMENT_BYTES,
|
||||||
|
input_rate: IR.Instance,
|
||||||
|
input_rate_stride: Vertex.sizeof,
|
||||||
|
layout: ctx.pipeline_layout,
|
||||||
|
vertex_attributes: attributes,
|
||||||
|
src_color: BF.SrcAlpha,
|
||||||
|
dst_color: BF.OneMinusSrcAlpha,
|
||||||
|
color_op: BO.Add,
|
||||||
|
src_alpha: BF.One,
|
||||||
|
dst_alpha: BF.Zero,
|
||||||
|
alpha_op: BO.Add,
|
||||||
|
};
|
||||||
|
|
||||||
|
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]);
|
||||||
}
|
}
|
||||||
|
|
||||||
DescLayoutBinding[2] layout_bindings = [
|
|
||||||
{ binding: 0, descriptorType: DT.Image, descriptorCount: 1, stageFlags: SS.All },
|
|
||||||
{ binding: 1, descriptorType: DT.Sampler, descriptorCount: 1, stageFlags: SS.All },
|
|
||||||
];
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
Attribute[14] attributes = [
|
|
||||||
{ binding: 0, location: 0, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof + Vec4.sizeof * 0},
|
|
||||||
{ binding: 0, location: 1, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof + Vec4.sizeof * 1},
|
|
||||||
{ binding: 0, location: 2, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof + Vec4.sizeof * 2},
|
|
||||||
{ binding: 0, location: 3, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof + Vec4.sizeof * 3},
|
|
||||||
{ binding: 0, location: 4, format: FMT.RG_F32, offset: Vertex.dst_start.offsetof },
|
|
||||||
{ binding: 0, location: 5, format: FMT.RG_F32, offset: Vertex.dst_end.offsetof },
|
|
||||||
{ binding: 0, location: 6, format: FMT.RG_F32, offset: Vertex.src_start.offsetof },
|
|
||||||
{ binding: 0, location: 7, format: FMT.RG_F32, offset: Vertex.src_end.offsetof },
|
|
||||||
{ binding: 0, location: 8, format: FMT.R_F32, offset: Vertex.border_thickness.offsetof },
|
|
||||||
{ binding: 0, location: 9, format: FMT.R_F32, offset: Vertex.corner_radius.offsetof },
|
|
||||||
{ binding: 0, location: 10, format: FMT.R_F32, offset: Vertex.edge_softness.offsetof },
|
|
||||||
{ binding: 0, location: 11, format: FMT.R_F32, offset: Vertex.raised.offsetof },
|
|
||||||
{ binding: 0, location: 12, format: FMT.R_F32, offset: Vertex.z_index.offsetof },
|
|
||||||
{ binding: 0, location: 13, format: FMT.R_U32, offset: Vertex.texture.offsetof },
|
|
||||||
];
|
|
||||||
|
|
||||||
GfxPipelineInfo ui_info = {
|
|
||||||
vertex_shader: cast(u8[])VERTEX_BYTES,
|
|
||||||
frag_shader: cast(u8[])FRAGMENT_BYTES,
|
|
||||||
input_rate: IR.Instance,
|
|
||||||
input_rate_stride: Vertex.sizeof,
|
|
||||||
layout: ctx.pipeline_layout,
|
|
||||||
vertex_attributes: attributes,
|
|
||||||
src_color: BF.SrcAlpha,
|
|
||||||
dst_color: BF.OneMinusSrcAlpha,
|
|
||||||
color_op: BO.Add,
|
|
||||||
src_alpha: BF.One,
|
|
||||||
dst_alpha: BF.Zero,
|
|
||||||
alpha_op: BO.Add,
|
|
||||||
};
|
|
||||||
|
|
||||||
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);
|
InitStacks(&ctx);
|
||||||
|
|
||||||
g_ui_ctx = ctx;
|
g_ui_ctx = ctx;
|
||||||
@ -529,29 +516,123 @@ UIItem*
|
|||||||
MakeItem(T)(T k) if(is(T: UIKey) || StringType!T)
|
MakeItem(T)(T k) if(is(T: UIKey) || StringType!T)
|
||||||
{
|
{
|
||||||
UICtx* ctx = GetCtx();
|
UICtx* ctx = GetCtx();
|
||||||
|
|
||||||
UIItem* item = Get(k);
|
UIItem* item = Get(k);
|
||||||
|
|
||||||
Set(item, ctx);
|
Set(item, ctx);
|
||||||
|
|
||||||
if(!Nil(item.parent))
|
if(!Nil(item.parent))
|
||||||
{
|
{
|
||||||
DLLPush(item.parent, item, g_UI_NIL);
|
DLLPush(item.parent, item, g_UI_NIL);
|
||||||
}
|
}
|
||||||
|
|
||||||
AutoPopStacks(ctx);
|
AutoPopStacks(ctx);
|
||||||
|
|
||||||
return item;
|
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
|
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();
|
UICtx* ctx = GetCtx();
|
||||||
Arena* a = &ctx.temp_arena;
|
Arena* a = &ctx.temp_arena;
|
||||||
|
|
||||||
Reset(a);
|
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 state
|
||||||
ctx.f_idx = ctx.frame%FRAME_OVERLAP;
|
ctx.f_idx = ctx.frame%FRAME_OVERLAP;
|
||||||
ctx.inputs = inputs;
|
ctx.inputs = inputs;
|
||||||
@ -559,26 +640,34 @@ BeginUI(EditorCtx* edctx, Inputs* inputs)
|
|||||||
|
|
||||||
ResetStacks(ctx);
|
ResetStacks(ctx);
|
||||||
|
|
||||||
// Rendering
|
version(ENABLE_RENDERER)
|
||||||
BeginFrame(&ctx.rd);
|
{
|
||||||
BeginRendering(&ctx.rd);
|
BeginFrame(&ctx.rd);
|
||||||
|
BeginRendering(&ctx.rd);
|
||||||
|
}
|
||||||
|
|
||||||
Vec2 ext = GetExtent(&ctx.rd);
|
Vec2 ext = GetExtent(&ctx.rd);
|
||||||
if(ext != ctx.res)
|
if(ext != ctx.res)
|
||||||
{
|
{
|
||||||
ctx.res = ext;
|
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)
|
||||||
|
{
|
||||||
Bind(&ctx.rd, ctx.pipeline, ctx.desc_set);
|
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].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);
|
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.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
|
void
|
||||||
@ -592,32 +681,161 @@ EndUI()
|
|||||||
{
|
{
|
||||||
if(item.size_info[axis].type == ST.Pixels)
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
with(ctx)
|
version(ENABLE_RENDERER) with(ctx)
|
||||||
{
|
{
|
||||||
BindBuffers(&rd, &buffers[f_idx].m_idx, &buffers[f_idx].m_vtx);
|
BindBuffers(&rd, &buffers[f_idx].m_idx, &buffers[f_idx].m_vtx);
|
||||||
DrawIndexed(&rd, 6, buffers[f_idx].count, 0);
|
DrawIndexed(&rd, 6, buffers[f_idx].count, 0);
|
||||||
}
|
|
||||||
|
|
||||||
FinishRendering(&ctx.rd);
|
FinishRendering(&rd);
|
||||||
SubmitAndPresent(&ctx.rd);
|
SubmitAndPresent(&rd);
|
||||||
|
}
|
||||||
|
|
||||||
ctx.frame += 1;
|
ctx.frame += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
UIItem*
|
u32[2]
|
||||||
Root(UICtx* ctx)
|
GetExtent(Renderer* rd)
|
||||||
{
|
{
|
||||||
UISize[2] sizes = [UISize(ST.Pixels, ctx.res.x), UISize(ST.Pixels, ctx.res.y)];
|
version(ENABLE_RENDERER)
|
||||||
Push!("size_info")(ctx, sizes);
|
{
|
||||||
return MakeItem("###root");
|
return RendererGetExtent(rd);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return [1280, 720];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template StackIDs(string stack)
|
template
|
||||||
|
StackIDs(string stack)
|
||||||
{
|
{
|
||||||
import std.string : replace;
|
import std.string : replace;
|
||||||
struct Identifiers { string stack, stack_top_node; }
|
struct Identifiers { string stack, stack_top_node; }
|
||||||
@ -935,8 +1153,9 @@ GlyphWidth(Glyph* g)
|
|||||||
|
|
||||||
struct TextBuffer
|
struct TextBuffer
|
||||||
{
|
{
|
||||||
u8[] text;
|
u8[] text;
|
||||||
TS[] style;
|
TS[] style;
|
||||||
|
TextBuffer* next;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1094,7 +1313,7 @@ DrawPanel(UIPanel* panel, f32 lc_w, bool focus)
|
|||||||
{
|
{
|
||||||
UICtx* ctx = GetCtx();
|
UICtx* ctx = GetCtx();
|
||||||
|
|
||||||
Vec2 pos = panel.rect.vec0;
|
Vec2 pos = panel.rect.p0;
|
||||||
Vec2 size = panel.size;
|
Vec2 size = panel.size;
|
||||||
|
|
||||||
DrawRect(pos, size, ctx.corner_radius_top.value, ctx.border_thickness_top.value, ctx.bg_col_top.value);
|
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);
|
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*
|
pragma(inline) Glyph*
|
||||||
GetGlyph(u8 ch)
|
GetGlyph(u8 ch)
|
||||||
{
|
{
|
||||||
@ -1187,25 +1424,25 @@ Nil(UIItem* item)
|
|||||||
}
|
}
|
||||||
|
|
||||||
pragma(inline) bool
|
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
|
bool
|
||||||
Clicked(UIItem* item, Vec2 p0, Vec2 p1)
|
Clicked(UIItem* item, Rect* rect)
|
||||||
{
|
{
|
||||||
bool result;
|
bool result;
|
||||||
UICtx* ctx = GetCtx();
|
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;
|
result = true;
|
||||||
DLLRemove(&ctx.inputs.list, n, null);
|
DLLRemove(ctx.inputs, n, null);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1214,27 +1451,27 @@ Clicked(UIItem* item, Vec2 p0, Vec2 p1)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Dragged(UIItem* item, Vec2 p0, Vec2 p1)
|
Dragged(UIItem* item, Rect* rect)
|
||||||
{
|
{
|
||||||
bool result;
|
bool result;
|
||||||
UICtx* ctx = GetCtx();
|
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;
|
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;
|
bool taken;
|
||||||
|
|
||||||
if(ev.key == Input.MouseMotion)
|
if(ev.key == Input.MouseMotion)
|
||||||
{
|
{
|
||||||
item.dragged.x += cast(f32)ev.rel_x;
|
item.dragged.x += ev.rel_x;
|
||||||
item.dragged.y += cast(f32)ev.rel_y;
|
item.dragged.y += ev.rel_y;
|
||||||
result = true;
|
result = true;
|
||||||
taken = true;
|
taken = true;
|
||||||
}
|
}
|
||||||
@ -1246,7 +1483,7 @@ Dragged(UIItem* item, Vec2 p0, Vec2 p1)
|
|||||||
|
|
||||||
if(taken)
|
if(taken)
|
||||||
{
|
{
|
||||||
DLLRemove(&ctx.inputs.list, n, null);
|
DLLRemove(ctx.inputs, n, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1270,17 +1507,47 @@ unittest
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
UICtx ctx;
|
Inputs inputs;
|
||||||
InitStacks(&ctx);
|
InitUICtx(null);
|
||||||
ResetStacks(&ctx);
|
BeginUI(&inputs);
|
||||||
|
|
||||||
|
UICtx* ctx = GetCtx();
|
||||||
|
|
||||||
Vec4 w = Vec4(1.0);
|
Vec4 w = Vec4(1.0);
|
||||||
Vec4[4] col = w;
|
Vec4[4] col = w;
|
||||||
|
|
||||||
Push!("bg_col")(&ctx, col);
|
Push!("bg_col")(ctx, col);
|
||||||
assert(ctx.bg_col.top.value == 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);
|
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
|
parent.axis == A2D.Y ? 10 : 0
|
||||||
);
|
);
|
||||||
|
|
||||||
Vec2 p0 = panel.rect.vec0+adj;
|
Vec2 p0 = panel.rect.p0+adj;
|
||||||
Vec2 p1 = panel.rect.vec1-adj;
|
Vec2 p1 = panel.rect.p1-adj;
|
||||||
|
Rect r = Rect(p0: p0, p1: p1);
|
||||||
|
|
||||||
if(!Nil(prev)) with(panel)
|
if(!Nil(prev)) with(panel)
|
||||||
{
|
{
|
||||||
Vec2 d0 = rect.vec0-adj;
|
Vec2 d0 = rect.p0-adj;
|
||||||
Vec2 d1 = Vec2(
|
Vec2 d1 = Vec2(
|
||||||
pax == A2D.X ? rect.vec0.x+adj.x : rect.vec1.x,
|
pax == A2D.X ? rect.p0.x+adj.x : rect.p1.x,
|
||||||
pax == A2D.Y ? rect.vec0.y+adj.y : rect.vec1.y
|
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);
|
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))
|
if(CheckPanelBounds(pct + mov_pct) && CheckPanelBounds(panel.prev.pct - mov_pct))
|
||||||
@ -49,7 +51,7 @@ Panel(UIPanel* panel)
|
|||||||
|
|
||||||
if(panel.ed != null)
|
if(panel.ed != null)
|
||||||
{
|
{
|
||||||
if(Clicked(item, p0, p1))
|
if(Clicked(item, &r))
|
||||||
{
|
{
|
||||||
SetFocusedPanel(panel);
|
SetFocusedPanel(panel);
|
||||||
}
|
}
|
||||||
@ -76,8 +78,8 @@ Panel(UIPanel* panel)
|
|||||||
|
|
||||||
f32 y_rem = fmod(panel.scroll_offset, TEXT_SIZE);
|
f32 y_rem = fmod(panel.scroll_offset, TEXT_SIZE);
|
||||||
|
|
||||||
f32 x = panel.rect.x0;
|
f32 x = panel.rect.p0.x;
|
||||||
f32 y = panel.rect.y0 + TEXT_SIZE - fmod(panel.scroll_offset, TEXT_SIZE);
|
f32 y = panel.rect.p1.y + TEXT_SIZE - fmod(panel.scroll_offset, TEXT_SIZE);
|
||||||
|
|
||||||
u64 i = panel.start_ln;
|
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))
|
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)
|
else for(auto n = parts; n != null; n = n.next)
|
||||||
{
|
{
|
||||||
auto l = &n.value;
|
auto l = n;
|
||||||
|
|
||||||
if(pos.y == i)
|
if(pos.y == i)
|
||||||
{
|
{
|
||||||
@ -202,14 +204,14 @@ Nil(UIPanel* panel)
|
|||||||
return panel == null || panel == g_UI_NIL_PANEL;
|
return panel == null || panel == g_UI_NIL_PANEL;
|
||||||
}
|
}
|
||||||
|
|
||||||
Node!(TextBuffer)*
|
TextBuffer*
|
||||||
MakeMultiline(u8[] text, f32 width, TS[] style = [])
|
MakeMultiline(u8[] text, f32 width, TS[] style = [])
|
||||||
{
|
{
|
||||||
f32 scaled_width = width * (g_ui_ctx.atlas_buf.atlas.size/g_ui_ctx.text_size);
|
f32 scaled_width = width * (g_ui_ctx.atlas_buf.atlas.size/g_ui_ctx.text_size);
|
||||||
f32 text_width = CalcTextWidth(text);
|
f32 text_width = CalcTextWidth(text);
|
||||||
|
|
||||||
u64 line_count = cast(u64)(ceil(text_width/scaled_width));
|
u64 line_count = cast(u64)(ceil(text_width/scaled_width));
|
||||||
Node!(TextBuffer)* node = null;
|
TextBuffer* node = null;
|
||||||
if(line_count > 0)
|
if(line_count > 0)
|
||||||
{
|
{
|
||||||
f32 w = 0.0;
|
f32 w = 0.0;
|
||||||
@ -232,15 +234,15 @@ MakeMultiline(u8[] text, f32 width, TS[] style = [])
|
|||||||
stl = ScratchAlloc!(TS)(style, start, len);
|
stl = ScratchAlloc!(TS)(style, start, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
Node!(TextBuffer)* n = node;
|
TextBuffer* n = node;
|
||||||
for(;;)
|
for(;;)
|
||||||
{
|
{
|
||||||
if(node == null)
|
if(node == null)
|
||||||
{
|
{
|
||||||
node = ScratchAlloc!(Node!(TextBuffer))();
|
node = ScratchAlloc!(TextBuffer)();
|
||||||
|
|
||||||
node.value.text = str;
|
node.text = str;
|
||||||
node.value.style = stl;
|
node.style = stl;
|
||||||
node.next = null;
|
node.next = null;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -248,10 +250,10 @@ MakeMultiline(u8[] text, f32 width, TS[] style = [])
|
|||||||
|
|
||||||
if(n.next == null)
|
if(n.next == null)
|
||||||
{
|
{
|
||||||
n.next = ScratchAlloc!(Node!(TextBuffer))();
|
n.next = ScratchAlloc!(TextBuffer)();
|
||||||
|
|
||||||
n.next.value.text = str;
|
n.next.text = str;
|
||||||
n.next.value.style = stl;
|
n.next.style = stl;
|
||||||
n.next.next = null;
|
n.next.next = null;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user