restructure
This commit is contained in:
parent
d96273ab5b
commit
439aa2b526
@ -1 +1 @@
|
||||
Subproject commit 5c2a3ab5efa1aaa15ca6f3144655550dd024823a
|
||||
Subproject commit 65ac576cb0f37e797030365ef25d11a771468654
|
||||
@ -108,7 +108,7 @@ Insert(FlatBuffer* buffer, u8[] insert, u64 length, Range pos)
|
||||
|
||||
// TODO: handle case for when lines are longer than line buffer
|
||||
void
|
||||
GetLines(FlatBuffer* buffer, LineBuffers* linebufs, u64 start_line, u64 length, bool add_hash = false)
|
||||
GetLines(FlatBuffer* buffer, LineBuffers* linebufs, u64 start_line, u64 length)
|
||||
{
|
||||
assert(linebufs != null, "GetLines failure: linebufs is null");
|
||||
length = length > buffer.line_count ? buffer.line_count : length;
|
||||
@ -116,7 +116,6 @@ GetLines(FlatBuffer* buffer, LineBuffers* linebufs, u64 start_line, u64 length,
|
||||
Reset(&linebufs.arena);
|
||||
linebufs.lines = AllocArray!(u8[])(&linebufs.arena, length);
|
||||
|
||||
u64 extra_buffer = add_hash ? 10 : 0;
|
||||
i64 start = -1;
|
||||
u64 line = 0;
|
||||
u64 current_line = 0;
|
||||
@ -136,7 +135,7 @@ GetLines(FlatBuffer* buffer, LineBuffers* linebufs, u64 start_line, u64 length,
|
||||
|
||||
if (start < 0 && new_line)
|
||||
{
|
||||
linebufs.lines[current_line] = AllocArray!(u8)(&linebufs.arena, 1 + extra_buffer);
|
||||
linebufs.lines[current_line] = AllocArray!(u8)(&linebufs.arena, 1);
|
||||
linebufs.lines[current_line][0] = '\n';
|
||||
current_line += 1;
|
||||
continue;
|
||||
@ -150,7 +149,7 @@ GetLines(FlatBuffer* buffer, LineBuffers* linebufs, u64 start_line, u64 length,
|
||||
|
||||
if (new_line)
|
||||
{
|
||||
linebufs.lines[current_line] = AllocArray!(u8)(&linebufs.arena, (i-start) + extra_buffer);
|
||||
linebufs.lines[current_line] = AllocArray!(u8)(&linebufs.arena, (i-start));
|
||||
linebufs.lines[current_line][0 .. i-start] = buffer.data[start .. i];
|
||||
current_line += 1;
|
||||
start = -1;
|
||||
@ -159,23 +158,10 @@ GetLines(FlatBuffer* buffer, LineBuffers* linebufs, u64 start_line, u64 length,
|
||||
|
||||
if (i == buffer.length-1)
|
||||
{
|
||||
linebufs.lines[current_line] = AllocArray!(u8)(&linebufs.arena, (buffer.length-start) + extra_buffer);
|
||||
linebufs.lines[current_line] = AllocArray!(u8)(&linebufs.arena, (buffer.length-start));
|
||||
linebufs.lines[current_line][0 .. buffer.length-start] = buffer.data[start .. buffer.length];
|
||||
}
|
||||
}
|
||||
|
||||
if (add_hash)
|
||||
{
|
||||
for (u64 i = 0; i < linebufs.lines.length; i += 1)
|
||||
{
|
||||
if (linebufs.lines[i].length > 0)
|
||||
{
|
||||
u8[10] buf = 0;
|
||||
(cast(char[])buf).sformat("##%08s", i);
|
||||
linebufs.lines[i][linebufs.lines[i].length - extra_buffer .. $] = buf[0 .. $];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u64
|
||||
|
||||
@ -13,289 +13,58 @@ import widgets;
|
||||
import std.stdio;
|
||||
import std.exception;
|
||||
|
||||
//const u8[] FONT_BYTES = import("Roboto.ttf");
|
||||
const u8[] FONT_BYTES = import("pc-9800.ttf");
|
||||
const u8[] VERTEX_BYTES = import("gui.vert.spv");
|
||||
const u8[] FRAGMENT_BYTES = import("gui.frag.spv");
|
||||
|
||||
const UI_COUNT = 5000;
|
||||
|
||||
f32 g_delta = 0.0;
|
||||
Editor* g_editor;
|
||||
|
||||
EditorCtx g_ed_ctx;
|
||||
|
||||
struct EditorCtx
|
||||
{
|
||||
Arena arena;
|
||||
UIPanel* base_panel;
|
||||
}
|
||||
|
||||
struct Editor
|
||||
{
|
||||
Arena arena;
|
||||
Arena temp_arena;
|
||||
PlatformWindow* window;
|
||||
Arena arena;
|
||||
|
||||
Renderer rd;
|
||||
ImageView font_atlas;
|
||||
Pipeline pipeline;
|
||||
DescSetLayout desc_set_layout;
|
||||
DescSet desc_set;
|
||||
PipelineLayout pipeline_layout;
|
||||
PushConst pc;
|
||||
u8[] filename;
|
||||
FlatBuffer buf;
|
||||
Tokenizer tk;
|
||||
LineBuffers linebufs;
|
||||
|
||||
FlatBuffer[] buffers;
|
||||
Tokenizer[] tokenizers;
|
||||
u8[][] buffer_names;
|
||||
u32 buffer_count;
|
||||
FlatBuffer* active_buffer;
|
||||
LineBuffers linebufs;
|
||||
Vec2 cursor_pos;
|
||||
Vec2 select_start;
|
||||
Vec2 select_end;
|
||||
|
||||
u8[] font_data;
|
||||
FontFace font;
|
||||
FontAtlasBuf atlas_buf;
|
||||
|
||||
Vec2 res;
|
||||
}
|
||||
|
||||
struct PushConst
|
||||
{
|
||||
Mat4 projection;
|
||||
}
|
||||
|
||||
struct Vertex
|
||||
{
|
||||
Vec4[4] cols;
|
||||
Vec2 dst_start;
|
||||
Vec2 dst_end;
|
||||
Vec2 src_start;
|
||||
Vec2 src_end;
|
||||
f32 border_thickness;
|
||||
f32 corner_radius;
|
||||
f32 edge_softness;
|
||||
f32 raised;
|
||||
u32 texture;
|
||||
f32 text_size;
|
||||
u64 line_offset;
|
||||
}
|
||||
|
||||
void
|
||||
Cycle(Editor* ed, Inputs* inputs)
|
||||
Cycle(Inputs* inputs)
|
||||
{
|
||||
Reset(&ed.temp_arena);
|
||||
ResetScratch(MB(4));
|
||||
|
||||
BeginBuild(inputs);
|
||||
|
||||
static UIPanel panel = {
|
||||
id: CastStr!(u8)("##main_panel"),
|
||||
pct: 1.0,
|
||||
axis: A2D.X,
|
||||
color: Vec4(0.2, 0.4, 0.8, 1.0),
|
||||
};
|
||||
|
||||
static UIPanel panel_l = {
|
||||
id: CastStr!(u8)("##panel_l"),
|
||||
pct: 0.5,
|
||||
axis: A2D.Y,
|
||||
color: Vec4(0.2, 0.4, 0.8, 1.0),
|
||||
};
|
||||
|
||||
static UIPanel panel_r = {
|
||||
id: CastStr!(u8)("##panel_r"),
|
||||
pct: 0.5,
|
||||
axis: A2D.Y,
|
||||
color: Vec4(0.4, 0.3, 0.7, 1.0),
|
||||
};
|
||||
|
||||
BeginFrame(&ed.rd);
|
||||
|
||||
Vec2 ext = GetExtent(&ed.rd);
|
||||
if (ext != ed.res)
|
||||
{
|
||||
ed.res = ext;
|
||||
Ortho(&ed.pc.projection, 0.0, 0.0, ext.x, ext.y, 1000.0, 0.1);
|
||||
}
|
||||
|
||||
u32 rows = cast(u32)(ext.y / ed.atlas_buf.atlas.size) + 5;
|
||||
Panel(&panel);
|
||||
{
|
||||
Panel(&panel_l);
|
||||
{
|
||||
if (ed.active_buffer != null)
|
||||
{
|
||||
GetLines(&ed.buffers[0], &ed.linebufs, 0, rows);
|
||||
|
||||
for(u64 i = 0; i < ed.linebufs.lines.length; i += 1)
|
||||
{
|
||||
TextLine(ed.linebufs.lines[i], ed.atlas_buf.atlas.size, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
EndPanel();
|
||||
|
||||
Panel(&panel_r);
|
||||
{
|
||||
|
||||
}
|
||||
EndPanel();
|
||||
}
|
||||
EndPanel();
|
||||
|
||||
BeginRendering(&ed.rd);
|
||||
|
||||
PushConstants(&ed.rd, ed.pipeline, &ed.pc);
|
||||
|
||||
Bind(&ed.rd, ed.pipeline, ed.desc_set);
|
||||
|
||||
EndBuild();
|
||||
|
||||
FinishRendering(&ed.rd);
|
||||
|
||||
SubmitAndPresent(&ed.rd);
|
||||
}
|
||||
|
||||
Editor
|
||||
CreateEditor(PlatformWindow* window, u8[] buffer_data, u8[] buffer_name)
|
||||
void
|
||||
InitEditorCtx(PlatformWindow* window)
|
||||
{
|
||||
InitFreeType();
|
||||
|
||||
version(linux)
|
||||
{
|
||||
PlatformHandles handles = {
|
||||
window: window.window,
|
||||
conn: window.conn,
|
||||
};
|
||||
}
|
||||
InitUICtx(window);
|
||||
|
||||
version(Windows)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Arena arena = CreateArena(MB(32));
|
||||
|
||||
FontFace font = OpenFont(cast(u8[])FONT_BYTES);
|
||||
FontAtlasBuf atlas_buf = CreateAtlas(&arena, font, 16.0, 256);
|
||||
|
||||
Editor editor = {
|
||||
window: window,
|
||||
arena: arena,
|
||||
temp_arena: CreateArena(MB(8)),
|
||||
rd: InitRenderer(handles, MB(32), MB(16)),
|
||||
font_data: cast(u8[])FONT_BYTES,
|
||||
font: font,
|
||||
atlas_buf: atlas_buf,
|
||||
buffers: MAllocArray!(FlatBuffer)(32),
|
||||
buffer_names: MAllocArray!(u8[])(32),
|
||||
linebufs: CreateLineBuffers(MB(32)),
|
||||
EditorCtx ctx = {
|
||||
arena: CreateArena(MB(2)),
|
||||
};
|
||||
|
||||
if (buffer_data != null)
|
||||
{
|
||||
assert(buffer_name != null, "CreateEditor failure: buffer_name is null while buffer_data is not");
|
||||
|
||||
editor.buffers[0] = CreateFlatBuffer(buffer_data);
|
||||
editor.buffer_names[0] = AllocArray!(u8)(&editor.arena, buffer_name.length);
|
||||
editor.buffer_names[0][0 .. $] = buffer_name[0 .. $];
|
||||
|
||||
editor.buffer_count += 1;
|
||||
editor.active_buffer = &editor.buffers[0];
|
||||
}
|
||||
|
||||
editor.active_buffer = &editor.buffers[0];
|
||||
|
||||
DescLayoutBinding[2] layout_bindings = [
|
||||
{ binding: 0, descriptorType: DT.Image, descriptorCount: 1, stageFlags: SS.All },
|
||||
{ binding: 1, descriptorType: DT.Storage, descriptorCount: 1, stageFlags: SS.All },
|
||||
];
|
||||
|
||||
editor.desc_set_layout = CreateDescSetLayout(&editor.rd, layout_bindings);
|
||||
editor.desc_set = AllocDescSet(&editor.rd, editor.desc_set_layout);
|
||||
editor.pipeline_layout = CreatePipelineLayout(&editor.rd, editor.desc_set_layout, PushConst.sizeof);
|
||||
|
||||
Attribute[13] 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_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: editor.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,
|
||||
};
|
||||
|
||||
assert(CreateGraphicsPipeline(&editor.rd, &editor.pipeline, &ui_info), "Unable to build UI pipeline");
|
||||
|
||||
InitUICtx(&editor.rd, 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);
|
||||
|
||||
Write(&editor.rd, editor.desc_set, &editor.font_atlas, 0, DT.Image);
|
||||
|
||||
SetClearColors(&editor.rd, [0.0, 0.0, 0.0, 1.0], [0.0, 0.0, 0.0, 0.0]);
|
||||
|
||||
Reset(&editor.temp_arena);
|
||||
|
||||
return editor;
|
||||
}
|
||||
|
||||
void
|
||||
DrawText(Editor* ed, f32 x, f32 y, f32 px, string str)
|
||||
{
|
||||
u8[] str_buf = (cast(u8*)str.ptr)[0 .. str.length];
|
||||
DrawText(ed, x, y, px, str_buf);
|
||||
}
|
||||
|
||||
void
|
||||
DrawText(Editor* ed, f32 x, f32 y, f32 px, u8[] str)
|
||||
{
|
||||
u32 tab_count = 2;
|
||||
f32 x_pos = x;
|
||||
f32 scale = px / ed.atlas_buf.atlas.size;
|
||||
foreach(ch; str)
|
||||
{
|
||||
foreach(glyph; ed.atlas_buf.atlas.glyphs)
|
||||
{
|
||||
if (ch == glyph.ch)
|
||||
{
|
||||
if (ch == '\t')
|
||||
{
|
||||
Glyph g = glyph;
|
||||
g.atlas_left = g.atlas_right = 0.0;
|
||||
foreach(i; 0 .. tab_count)
|
||||
{
|
||||
DrawGlyph(&g, scale, &x_pos, y);
|
||||
}
|
||||
}
|
||||
else if (ch == '\n')
|
||||
{
|
||||
Glyph g = glyph;
|
||||
g.atlas_left = g.atlas_right = 0.0;
|
||||
DrawGlyph(&g, scale, &x_pos, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawGlyph(&glyph, scale, &x_pos, y);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
g_ed_ctx = ctx;
|
||||
}
|
||||
|
||||
/*
|
||||
void
|
||||
DrawBuffer(Editor* ed, f32 x, f32 y, f32 px, FlatBuffer* fb)
|
||||
{
|
||||
@ -347,9 +116,10 @@ DrawBuffer(Editor* ed, f32 x, f32 y, f32 px, FlatBuffer* fb)
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawGlyph(g, scale, &x_pos, y_pos, cols[fb.tk.buffer[i]]);
|
||||
DrawGlyph(g, scale, &x_pos, y_pos, false, cols[fb.tk.buffer[i]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@ -21,7 +21,7 @@ void main(string[] argv)
|
||||
PlatformWindow window = CreateWindow("Editor", 1920, 1080);
|
||||
StartPlatformThread(&window);
|
||||
|
||||
Editor editor = CreateEditor(&window, buffer, buffer_name);
|
||||
InitEditorCtx(&window);
|
||||
|
||||
while (true)
|
||||
{
|
||||
@ -31,6 +31,6 @@ void main(string[] argv)
|
||||
break;
|
||||
}
|
||||
|
||||
Cycle(&editor, inputs);
|
||||
Cycle(inputs);
|
||||
}
|
||||
}
|
||||
|
||||
354
src/editor/ui.d
354
src/editor/ui.d
@ -13,6 +13,12 @@ import core.stdc.string : memset;
|
||||
|
||||
import editor;
|
||||
|
||||
const u8[] FONT_BYTES = import("pc-9800.ttf");
|
||||
const u8[] VERTEX_BYTES = import("gui.vert.spv");
|
||||
const u8[] FRAGMENT_BYTES = import("gui.frag.spv");
|
||||
|
||||
const UI_COUNT = 5000;
|
||||
|
||||
UICtx g_ui_ctx;
|
||||
|
||||
const UIItem g_ui_nil_item;
|
||||
@ -35,8 +41,9 @@ enum UIFlags
|
||||
None = 0x00,
|
||||
DrawBackground = 0x01,
|
||||
DrawText = 0x02,
|
||||
Clickable = 0x04,
|
||||
Draggable = 0x08,
|
||||
DrawBorder = 0x04,
|
||||
Clickable = 0x08,
|
||||
Draggable = 0x10,
|
||||
}
|
||||
|
||||
alias UIF = UIFlags;
|
||||
@ -62,11 +69,24 @@ struct UICtx
|
||||
{
|
||||
HashTable!(UIHash, UIItem*) items;
|
||||
Arena arena;
|
||||
Renderer* rd;
|
||||
Inputs* inputs;
|
||||
u64 frame;
|
||||
u64 f_idx;
|
||||
|
||||
PlatformWindow* window;
|
||||
Renderer rd;
|
||||
ImageView font_atlas;
|
||||
Pipeline pipeline;
|
||||
DescSetLayout desc_set_layout;
|
||||
DescSet desc_set;
|
||||
PipelineLayout pipeline_layout;
|
||||
PushConst pc;
|
||||
Vec2 res;
|
||||
|
||||
u8[] font_data;
|
||||
FontFace font;
|
||||
FontAtlasBuf atlas_buf;
|
||||
|
||||
UIBuffer[FRAME_OVERLAP] buffers;
|
||||
|
||||
UIItem* root;
|
||||
@ -85,6 +105,9 @@ struct UICtx
|
||||
Vec2 adjustment;
|
||||
u32 tab_width;
|
||||
f32 text_size;
|
||||
f32 border_thickness;
|
||||
f32 corner_radius;
|
||||
f32 edge_softness;
|
||||
}
|
||||
|
||||
struct UIItemStackList
|
||||
@ -123,6 +146,11 @@ struct UIItem
|
||||
Vec2 size;
|
||||
Rect rect;
|
||||
Vec4[4] color;
|
||||
Vec4[4] border_color;
|
||||
f32 border_thickness;
|
||||
f32 corner_radius;
|
||||
f32 edge_softness;
|
||||
Vec2 last_click_pos;
|
||||
}
|
||||
|
||||
struct UISize
|
||||
@ -140,6 +168,25 @@ struct UIBuffer
|
||||
u32 count;
|
||||
}
|
||||
|
||||
struct PushConst
|
||||
{
|
||||
Mat4 projection;
|
||||
}
|
||||
|
||||
struct Vertex
|
||||
{
|
||||
Vec4[4] cols;
|
||||
Vec2 dst_start;
|
||||
Vec2 dst_end;
|
||||
Vec2 src_start;
|
||||
Vec2 src_end;
|
||||
f32 border_thickness;
|
||||
f32 corner_radius;
|
||||
f32 edge_softness;
|
||||
f32 raised;
|
||||
u32 texture;
|
||||
}
|
||||
|
||||
union Rect
|
||||
{
|
||||
Vec2[2] v;
|
||||
@ -168,8 +215,21 @@ struct UIKey
|
||||
}
|
||||
|
||||
void
|
||||
InitUICtx(Renderer* rd, FontAtlas atlas)
|
||||
InitUICtx(PlatformWindow* window)
|
||||
{
|
||||
version(linux)
|
||||
{
|
||||
PlatformHandles handles = {
|
||||
window: window.window,
|
||||
conn: window.conn,
|
||||
};
|
||||
}
|
||||
|
||||
version(Windows)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
g_UI_NIL = cast(UIItem*)&g_ui_nil_item;
|
||||
g_UI_NIL_NODE = cast(UIItemNode*)&g_ui_nil_item_node;
|
||||
|
||||
@ -177,29 +237,88 @@ InitUICtx(Renderer* rd, FontAtlas atlas)
|
||||
|
||||
UIBuffer[FRAME_OVERLAP] buffers;
|
||||
|
||||
u64 vertex_size = 10000;
|
||||
for(u64 i = 0; i < FRAME_OVERLAP; i += 1)
|
||||
{
|
||||
buffers[i].m_vtx = CreateMappedBuffer!(Vertex)(rd, BT.Vertex, vertex_size);
|
||||
buffers[i].m_idx = CreateMappedBuffer!(u32)(rd, BT.Index, cast(u64)(ceil(vertex_size * 1.5)));
|
||||
buffers[i].vtx = buffers[i].m_vtx.data;
|
||||
buffers[i].idx = buffers[i].m_idx.data;
|
||||
}
|
||||
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: rd,
|
||||
rd: InitRenderer(handles, MB(16), MB(8)),
|
||||
items: CreateHashTable!(UIHash, UIItem*)(12),
|
||||
arena: CreateArena(MB(4)),
|
||||
atlas: atlas,
|
||||
arena: arena,
|
||||
root: g_UI_NIL,
|
||||
top_parent: g_UI_NIL_NODE,
|
||||
prev_sibling: g_UI_NIL_NODE,
|
||||
drag_item: g_UI_NIL,
|
||||
text_size: 14.0,
|
||||
tab_width: 2,
|
||||
buffers: buffers,
|
||||
border_thickness: 0.0,
|
||||
corner_radius: 0.0,
|
||||
edge_softness: 0.0,
|
||||
color: [b, b, b, b],
|
||||
border_color: [b, b, b, b],
|
||||
atlas_buf: atlas_buf,
|
||||
font: font,
|
||||
font_data: cast(u8[])FONT_BYTES,
|
||||
};
|
||||
|
||||
u64 vertex_size = 10000;
|
||||
for(u64 i = 0; i < FRAME_OVERLAP; i += 1)
|
||||
{
|
||||
ctx.buffers[i].m_vtx = CreateMappedBuffer!(Vertex)(&ctx.rd, BT.Vertex, vertex_size);
|
||||
ctx.buffers[i].m_idx = CreateMappedBuffer!(u32)(&ctx.rd, BT.Index, cast(u64)(ceil(vertex_size * 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.Storage, 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[13] 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_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,
|
||||
};
|
||||
|
||||
assert(CreateGraphicsPipeline(&ctx.rd, &ctx.pipeline, &ui_info), "Unable to build UI pipeline");
|
||||
|
||||
CreateImageView(&ctx.rd, &ctx.font_atlas, ctx.atlas_buf.atlas.width, ctx.atlas_buf.atlas.height, 4, ctx.atlas_buf.data);
|
||||
|
||||
Write(&ctx.rd, ctx.desc_set, &ctx.font_atlas, 0, DT.Image);
|
||||
|
||||
SetClearColors(&ctx.rd, [0.0, 0.0, 0.0, 1.0], [0.0, 0.0, 0.0, 0.0]);
|
||||
|
||||
g_ui_ctx = ctx;
|
||||
|
||||
InitWidgets();
|
||||
@ -351,6 +470,24 @@ SetAdjustment(Vec2 adj)
|
||||
g_ui_ctx.adjustment = adj;
|
||||
}
|
||||
|
||||
void
|
||||
SetBorderThickness(f32 value)
|
||||
{
|
||||
g_ui_ctx.border_thickness = value;
|
||||
}
|
||||
|
||||
void
|
||||
SetCornerRadius(f32 value)
|
||||
{
|
||||
g_ui_ctx.corner_radius = value;
|
||||
}
|
||||
|
||||
void
|
||||
SetEdgeSoftness(f32 value)
|
||||
{
|
||||
g_ui_ctx.edge_softness = value;
|
||||
}
|
||||
|
||||
void
|
||||
SetColor(Vec4 col)
|
||||
{
|
||||
@ -367,6 +504,22 @@ SetColor(Vec4 col0, Vec4 col1, Vec4 col2, Vec4 col3)
|
||||
ctx.color[3] = col3;
|
||||
}
|
||||
|
||||
void
|
||||
SetBorderColor(Vec4 col)
|
||||
{
|
||||
SetBorderColor(col, col, col, col);
|
||||
}
|
||||
|
||||
void
|
||||
SetBorderColor(Vec4 col0, Vec4 col1, Vec4 col2, Vec4 col3)
|
||||
{
|
||||
UICtx* ctx = GetCtx();
|
||||
ctx.border_color[0] = col0;
|
||||
ctx.border_color[1] = col1;
|
||||
ctx.border_color[2] = col2;
|
||||
ctx.border_color[3] = col3;
|
||||
}
|
||||
|
||||
void
|
||||
SetLayoutAxis(Axis2D axis)
|
||||
{
|
||||
@ -418,28 +571,61 @@ EndBuild()
|
||||
}
|
||||
DrawUI(ctx, ctx.root);
|
||||
|
||||
static bool first = false;
|
||||
if (!first)
|
||||
{
|
||||
DrawNodes(ctx.root);
|
||||
first = true;
|
||||
}
|
||||
PrepRendering(ctx);
|
||||
|
||||
BindBuffers(ctx.rd, &ctx.buffers[ctx.f_idx].m_idx, &ctx.buffers[ctx.f_idx].m_vtx);
|
||||
DrawIndexed(ctx.rd, 6, ctx.buffers[ctx.f_idx].count, 0);
|
||||
BindBuffers(&ctx.rd, &ctx.buffers[ctx.f_idx].m_idx, &ctx.buffers[ctx.f_idx].m_vtx);
|
||||
DrawIndexed(&ctx.rd, 6, ctx.buffers[ctx.f_idx].count, 0);
|
||||
|
||||
CompleteRendering(ctx);
|
||||
|
||||
ctx.frame += 1;
|
||||
|
||||
debug
|
||||
{
|
||||
static bool first = false;
|
||||
if (!first)
|
||||
{
|
||||
PrintNodes(ctx.root);
|
||||
first = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DrawNodes(UIItem* item)
|
||||
PrepRendering(UICtx* ctx)
|
||||
{
|
||||
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, 1000.0, 0.1);
|
||||
}
|
||||
|
||||
PushConstants(&ctx.rd, ctx.pipeline, &ctx.pc);
|
||||
|
||||
Bind(&ctx.rd, ctx.pipeline, ctx.desc_set);
|
||||
}
|
||||
|
||||
void
|
||||
CompleteRendering(UICtx* ctx)
|
||||
{
|
||||
FinishRendering(&ctx.rd);
|
||||
SubmitAndPresent(&ctx.rd);
|
||||
}
|
||||
|
||||
void
|
||||
PrintNodes(UIItem* item)
|
||||
{
|
||||
if (!Nil(item))
|
||||
{
|
||||
Logf("x0 %s x1 %s y0 %s y1 %s", item.rect.x0, item.rect.x1, item.rect.y0, item.rect.y1);
|
||||
|
||||
DrawNodes(item.first);
|
||||
DrawNodes(item.next);
|
||||
PrintNodes(item.first);
|
||||
PrintNodes(item.next);
|
||||
}
|
||||
}
|
||||
|
||||
@ -516,6 +702,11 @@ DrawUI(UICtx* ctx, UIItem* item)
|
||||
DrawRect(ctx, i);
|
||||
}
|
||||
|
||||
if (i.flags & UIF.DrawBorder)
|
||||
{
|
||||
DrawBorder(ctx, i);
|
||||
}
|
||||
|
||||
if (i.flags & UIF.DrawText)
|
||||
{
|
||||
DrawLine(i);
|
||||
@ -557,6 +748,10 @@ BuildItem(UIItem* item, UISize size_x, UISize size_y, UIFlags properties)
|
||||
item.layout_axis = ctx.layout_axis;
|
||||
item.level = ctx.panel_level;
|
||||
item.adjustment = ctx.adjustment;
|
||||
item.border_color = ctx.border_color;
|
||||
item.border_thickness = ctx.border_thickness;
|
||||
item.corner_radius = ctx.corner_radius;
|
||||
item.edge_softness = ctx.edge_softness;
|
||||
|
||||
item.parent = ctx.top_parent == g_UI_NIL_NODE ? g_UI_NIL : ctx.top_parent.item;
|
||||
if (!Nil(item.parent))
|
||||
@ -583,6 +778,7 @@ Signal(UIItem* item)
|
||||
if (item.flags & UIF.Clickable && Clicked(item, n))
|
||||
{
|
||||
Logf("Clicked");
|
||||
item.last_click_pos = Vec2(n.value.x, n.value.y) - item.rect.vec0;
|
||||
item.signal |= UIS.Clicked;
|
||||
taken = true;
|
||||
}
|
||||
@ -626,7 +822,7 @@ GetCtx()
|
||||
Vec2
|
||||
RootSize()
|
||||
{
|
||||
Vec2 size = GetExtent(GetCtx().rd);
|
||||
Vec2 size = GetExtent(&g_ui_ctx.rd);
|
||||
return size;
|
||||
}
|
||||
|
||||
@ -830,44 +1026,59 @@ DrawLine(UIItem* item)
|
||||
}
|
||||
|
||||
pragma(inline) void
|
||||
DrawGlyph(Glyph* glyph, f32 scale, f32* x_pos, f32 y, Vec4 col = Vec4(1.0))
|
||||
DrawGlyph(Glyph* glyph, f32 scale, f32* x_pos, f32 y, bool draw_bg = false, Vec4 col = Vec4(1.0))
|
||||
{
|
||||
UICtx* ctx = GetCtx();
|
||||
Vertex* bg_v = null, v = null;
|
||||
f32 h;
|
||||
if (draw_bg)
|
||||
{
|
||||
bg_v = ctx.buffers[ctx.f_idx].vtx.ptr + ctx.buffers[ctx.f_idx].count;
|
||||
AddUIIndices(ctx);
|
||||
}
|
||||
|
||||
if (glyph.ch == '\t')
|
||||
{
|
||||
*x_pos += glyph.advance * (GetCtx().tab_width - 1);
|
||||
}
|
||||
else if (glyph.atlas_left != glyph.atlas_right && glyph.ch != '\n')
|
||||
|
||||
v = ctx.buffers[ctx.f_idx].vtx.ptr + ctx.buffers[ctx.f_idx].count;
|
||||
|
||||
f32 r = glyph.plane_right * scale;
|
||||
f32 l = glyph.plane_left * scale;
|
||||
f32 w = r - l;
|
||||
h = (glyph.plane_bottom - glyph.plane_top) * scale;
|
||||
f32 y_pos = glyph.plane_top * scale;
|
||||
|
||||
v.dst_start.x = *x_pos + l;
|
||||
v.dst_start.y = y - y_pos;
|
||||
v.dst_end.x = *x_pos + w + l;
|
||||
v.dst_end.y = y + h - y_pos;
|
||||
|
||||
if (glyph.ch != '\t' && glyph.ch != '\n')
|
||||
{
|
||||
UICtx* ctx = GetCtx();
|
||||
Vertex* v = ctx.buffers[ctx.f_idx].vtx.ptr + ctx.buffers[ctx.f_idx].count;
|
||||
|
||||
f32 r = glyph.plane_right * scale;
|
||||
f32 l = glyph.plane_left * scale;
|
||||
f32 w = r - l;
|
||||
f32 h = (glyph.plane_bottom - glyph.plane_top) * scale;
|
||||
f32 y_pos = glyph.plane_top * scale;
|
||||
|
||||
v.dst_start.x = *x_pos + l;
|
||||
v.dst_start.y = y - y_pos;
|
||||
v.dst_end.x = *x_pos + w + l;
|
||||
v.dst_end.y = y + h - y_pos;
|
||||
|
||||
v.src_start.x = glyph.atlas_left;
|
||||
v.src_start.y = glyph.atlas_top;
|
||||
v.src_end.x = glyph.atlas_right;
|
||||
v.src_end.y = glyph.atlas_bottom;
|
||||
|
||||
v.cols[0] = col;
|
||||
v.cols[1] = col;
|
||||
v.cols[2] = col;
|
||||
v.cols[3] = col;
|
||||
|
||||
v.texture = 1;
|
||||
|
||||
AddUIIndices(ctx);
|
||||
}
|
||||
|
||||
v.cols = col;
|
||||
|
||||
v.texture = 1;
|
||||
|
||||
AddUIIndices(ctx);
|
||||
|
||||
*x_pos += glyph.advance * scale;
|
||||
|
||||
if (draw_bg)
|
||||
{
|
||||
Vec4 white = Vec4(1.0);
|
||||
|
||||
bg_v.dst_start = v.dst_start;
|
||||
bg_v.dst_end = v.dst_end;
|
||||
bg_v.cols = white;
|
||||
}
|
||||
}
|
||||
|
||||
pragma(inline) void
|
||||
@ -886,6 +1097,21 @@ DrawRect(UICtx* ctx, UIItem* item)
|
||||
AddUIIndices(ctx);
|
||||
}
|
||||
|
||||
pragma(inline) void
|
||||
DrawBorder(UICtx* ctx, UIItem* item)
|
||||
{
|
||||
Vertex* v = ctx.buffers[ctx.f_idx].vtx.ptr + ctx.buffers[ctx.f_idx].count;
|
||||
v.dst_start = item.rect.vec0;
|
||||
v.dst_end = item.rect.vec1;
|
||||
v.cols = item.border_color;
|
||||
v.border_thickness = item.border_thickness;
|
||||
v.corner_radius = item.corner_radius;
|
||||
v.edge_softness = item.edge_softness;
|
||||
v.raised = 0.0;
|
||||
|
||||
AddUIIndices(ctx);
|
||||
}
|
||||
|
||||
pragma(inline) void
|
||||
AddUIIndices(UICtx* ctx)
|
||||
{
|
||||
@ -905,6 +1131,28 @@ Nil(UIItem* item)
|
||||
return item == null || item == g_UI_NIL;
|
||||
}
|
||||
|
||||
void
|
||||
ClickedCharIndex(UIItem* item)
|
||||
{
|
||||
UICtx* ctx = GetCtx();
|
||||
f32 w = 0.0;
|
||||
for(u64 i = 0; i < item.key.text.length; i += 1)
|
||||
{
|
||||
u8 ch = item.key.text[i];
|
||||
if (ch < ctx.atlas.glyphs.length)
|
||||
{
|
||||
f32 ch_w = GlyphWidth(ctx.atlas.glyphs.ptr + ch);
|
||||
if (ch_w + w > item.last_click_pos.x)
|
||||
{
|
||||
Logf("char clicked");
|
||||
//item.clicked_char_idx = i;
|
||||
break;
|
||||
}
|
||||
w += ch_w;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Clicked(UIItem* item, DNode!(InputEvent)* n)
|
||||
{
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import dlib;
|
||||
|
||||
import buffer;
|
||||
import ui;
|
||||
import editor;
|
||||
import std.format : sformat;
|
||||
|
||||
const UIPanel g_ui_nil_panel;
|
||||
@ -135,7 +137,12 @@ Panel(UIPanel* panel)
|
||||
}
|
||||
}
|
||||
|
||||
SetColor(panel.color);
|
||||
SetColor(
|
||||
Vec4(0.3, 0.65, 0.86, 1.0),
|
||||
Vec4(0.25, 0.60, 0.81, 1.0),
|
||||
Vec4(0.25, 0.60, 0.81, 1.0),
|
||||
Vec4(0.3, 0.65, 0.86, 1.0),
|
||||
);
|
||||
SetLayoutAxis(panel.axis);
|
||||
|
||||
BuildItem(item, UISize(ST.Percentage, x_pct), UISize(ST.Percentage, y_pct), UIF.DrawBackground);
|
||||
@ -159,7 +166,7 @@ CheckPanelBounds(f32 pct)
|
||||
void
|
||||
Separator(UIItem* item, f32 x_size, f32 y_size, Axis2D axis)
|
||||
{
|
||||
SetColor(Vec4(0.0, 0.0, 0.0, 1.0));
|
||||
SetColor(Vec4(0.1, 0.2, 0.6, 1.0));
|
||||
|
||||
SizeType x_t = axis == A2D.X ? ST.Pixels : ST.Percentage;
|
||||
SizeType y_t = axis == A2D.Y ? ST.Pixels : ST.Percentage;
|
||||
@ -168,24 +175,44 @@ Separator(UIItem* item, f32 x_size, f32 y_size, Axis2D axis)
|
||||
}
|
||||
|
||||
void
|
||||
TextLine(u8[] text, f32 text_size, u64 line_no)
|
||||
EditorView(Editor* ed)
|
||||
{
|
||||
UICtx* ctx = GetCtx();
|
||||
UIItem* parent = PeekParent();
|
||||
|
||||
if (!Nil(parent) && parent.size.x > 0.0)
|
||||
if (ed.buf.length > 0)
|
||||
{
|
||||
SetColor(Vec4(1.0));
|
||||
SetTextSize(text_size);
|
||||
|
||||
Node!(u8[])* lines = MakeMultiline(text, parent.size.x, line_no);
|
||||
for(Node!(u8[])* line = lines; line != null; line = line.next)
|
||||
{
|
||||
UIItem* item = Get(line.value);
|
||||
BuildItem(item, UISize(ST.Percentage, 1.0), UISize(ST.Pixels, text_size), UIF.DrawText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UIItem*
|
||||
TextLine(u8[] text, f32 text_size, u64 line_no)
|
||||
{
|
||||
UIItem* item = g_UI_NIL;
|
||||
UIItem* parent = PeekParent();
|
||||
|
||||
SetColor(Vec4(1.0));
|
||||
SetTextSize(text_size);
|
||||
|
||||
Node!(u8[])* lines = MakeMultiline(text, parent.size.x, line_no);
|
||||
for(Node!(u8[])* line = lines; line != null; line = line.next)
|
||||
{
|
||||
item = Get(line.value);
|
||||
|
||||
Signal(item);
|
||||
|
||||
if (item.signal & UIS.Clicked)
|
||||
{
|
||||
ClickedCharIndex(item);
|
||||
}
|
||||
|
||||
BuildItem(item, UISize(ST.Percentage, 1.0), UISize(ST.Pixels, text_size), UIF.DrawText|UIF.Clickable|UIF.Draggable);
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
void
|
||||
EndPanel()
|
||||
{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user