Compare commits

...

2 Commits

Author SHA1 Message Date
e6024b0ff4 fixes 2025-12-27 20:46:25 +11:00
2edc6777f9 many fices 2025-12-27 20:45:09 +11:00
43 changed files with 487 additions and 715 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -1 +1 @@
Subproject commit 11cf96b799c6ccee3c4f569e89c263d11c4d5ff8
Subproject commit c2b7544979b16e1f27f419475b866a2903bd9c0a

@ -1 +1 @@
Subproject commit bda9abf8b9c74844cb30edb32e81543cbd5ae89d
Subproject commit c00c404006147838fb8af3259cf214396da31c99

View File

@ -1,4 +1,5 @@
import dlib;
import dlib.alloc : Reset;
import core.stdc.stdio : EOF;
import parsing;
import parsing : SetBuffers;
@ -476,6 +477,12 @@ SliceLineBuffer(FlatBuffer* fb, Line* ls)
return lbuf;
}
pragma(inline) void
ResetBuffer(FlatBuffer* fb)
{
Reset(&fb.linebufs.arena);
}
void
MoveToEOL(FlatBuffer* fb)
{

View File

@ -61,10 +61,7 @@ struct Editor
I64Vec2 cursor_pos;
Vec2 select_start;
Vec2 select_end;
i64 line_offset;
f32 text_size;
}
struct ChangeStacks
@ -155,48 +152,6 @@ Cycle(Inputs* inputs)
{
CommandPalette(&g_ed_ctx.cmd);
}
/*
UIPanel* root = ctx.base_panel;
root.size.x = g_ui_ctx.res.x;
root.size.y = g_ui_ctx.res.y;
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, g_UI_NIL_PANEL))
{
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.p0.v[axis] = pos;
c.rect.p1.v[axis] = pos + c.size.v[axis];
if(axis == p.axis)
{
pos += c.size.v[axis];
}
}
}
}
for(auto p = ctx.base_panel; !Nil(p); p = Recurse(p, g_UI_NIL_PANEL))
{
Panel(p);
}
if(ctx.state == ES.CmdOpen)
{
if(ctx.cmd.commands.length == 0 && ctx.cmd.icount == 0)
{
GetCommands(&ctx.cmd);
}
CommandPalette(&ctx.cmd);
}
*/
EndUI();
}
@ -266,6 +221,7 @@ CreatePanel(EditorCtx* ctx, Editor* ed = null)
p.layout_axis = A2D.Y;
p.ed = ed;
p.id = ctx.panel_id++;
p.text_size = 18;
p.parent = p.first = p.last = p.next = p.prev = g_NIL_PANEL;
return p;

View File

@ -37,18 +37,20 @@ enum Vec4 TEXT_HL_COL = Vec4(0.0, 0.0, 0.0, 1.0);
const u64 VERTEX_MAX_COUNT = 10000;
const Vec2 CLICK_BUFFER = Vec2(3.0, 3.0);
const u32 FONT_SIZES = 24;
const u32 ATLAS_DIMENSION = 512;
// TODO: add setting
const f32 TEXT_SIZE = 16.0;
const f32 SCROLL_SPEED = 48.0;
const f32 LINE_COUNT_PADDING = 4.0;
__gshared UICtx g_ui_ctx;
//const u8[] FONT_BYTES = import("pc-9800.ttf");
const u8[] FONT_BYTES = import("NuberNextCondensed-DemiBold.otf");
const u8[] VERTEX_BYTES = import("gui.vert.spv");
const u8[] FRAGMENT_BYTES = import("gui.frag.spv");
//const u8[] FONT_BYTES = import("NuberNextCondensed-DemiBold.otf");
u8[] FONT_BYTES = cast(u8[])import("jetbrains-mono/JetBrainsMono-Regular.ttf");
u8[] VERTEX_BYTES = cast(u8[])import("gui.vert.spv");
u8[] FRAGMENT_BYTES = cast(u8[])import("gui.frag.spv");
Vec4 g_corner_radius_default = 0.0;
f32 g_edge_softness_default = 0.8;
@ -62,7 +64,7 @@ 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);
f32 g_text_scale_default = 1.0;
u32 g_text_size_default = 18;
Vec2 g_scroll_target_default = Vec2(0.0);
Vec2 g_scroll_clamp_default = Vec2(0.0);
Vec2 g_view_offset_default = Vec2(0.0);
@ -131,6 +133,7 @@ enum UIFlags
alias UIF = UIFlags;
const UIFlags AUTO_FLAGS = UIF.ResizeAdjacent;
const UIFlags DRAW_FLAGS = UIF.DrawBackground|UIF.DrawBorder|UIF.DrawText|UIF.PortalView;
enum UISignal
{
@ -233,7 +236,8 @@ struct UICtx
PlatformWindow* window;
Renderer rd;
Descriptor font_atlas;
Descriptor[FONT_SIZES] font_descs;
Descriptor default_tex;
Descriptor sampler;
Pipeline pipeline;
DescSetLayout desc_set_layout;
@ -242,16 +246,13 @@ struct UICtx
Mat4 projection;
Vec2 res;
u8[] font_data;
FontFace font;
FontAtlasBuf atlas_buf;
f32 char_width;
f32 char_height;
FontGlyphs[FONT_SIZES] glyph_sets;
u32 glyph_sets_used;
UIBuffer[FRAME_OVERLAP] buffers;
u32 tab_width;
f32 text_size;
Vec4[TS.max][UISH.max] syntax_colors;
UIItem* window_root;
@ -278,10 +279,18 @@ struct UICtx
mixin UICtxParameter!(Vec4, "corner_radius");
mixin UICtxParameter!(f32, "border_thickness");
mixin UICtxParameter!(f32, "edge_softness");
mixin UICtxParameter!(f32, "text_scale");
mixin UICtxParameter!(u32, "text_size");
mixin UICtxParameter!(UISyntaxHighlight, "syntax_highlight");
debug bool dbg;
debug u64 item_count;
}
struct FontGlyphs
{
u32 size;
u32 index;
FontAtlasBuf abuf;
}
struct Stack(T)
@ -337,6 +346,7 @@ struct UIItem
UIItem* next, prev, first, last; // parent in mixin
UIItem* transient_next;
UIItem* last_relative_item;
Rect rect;
Vec2 size;
@ -412,6 +422,7 @@ struct Vertex
f32 edge_softness;
f32 raised;
u32 texture;
u32 atlas_index;
}
union Rect
@ -462,26 +473,23 @@ InitUICtx(PlatformWindow* window)
UIBuffer[FRAME_OVERLAP] buffers;
UICtx ctx = {
items: CreateHashTable!(UIHash, UIItem*)(12),
free_items: Alloc!(UIItem)(&arena),
arena: arena,
temp_arena: CreateArena(MB(1)),
drag_item: g_UI_NIL,
transient_items: g_UI_NIL,
font: OpenFont(cast(u8[])FONT_BYTES),
font_data: cast(u8[])FONT_BYTES,
text_size: 16.0,
tab_width: 2,
syntax_colors: [DEFAULT_COLORS, SYNTAX_COLORS],
};
UICtx* ctx = &g_ui_ctx;
ctx.items = CreateHashTable!(UIHash, UIItem*)(12);
ctx.free_items = Alloc!(UIItem)(&arena);
ctx.arena = arena;
ctx.temp_arena = CreateArena(MB(1));
ctx.drag_item = g_UI_NIL;
ctx.transient_items = g_UI_NIL;
ctx.font = OpenFont(cast(u8[])FONT_BYTES);
ctx.tab_width = 2;
ctx.syntax_colors = [DEFAULT_COLORS, SYNTAX_COLORS];
assert(ctx.font);
UIItem* fi = ctx.free_items;
fi.transient_next = fi.first = fi.last = fi.next = fi.prev = fi.parent = g_UI_NIL;
ctx.atlas_buf = CreateAtlas(&arena, ctx.font, 16.0, 256);
ctx.char_height = cast(f32)((ctx.font.size.metrics.ascender - ctx.font.size.metrics.descender) >> 6);
version(ENABLE_RENDERER)
{
ctx.rd = InitRenderer(handles, MB(16), MB(8));
@ -496,7 +504,7 @@ InitUICtx(PlatformWindow* window)
}
DescLayoutBinding[2] layout_bindings = [
{ binding: 0, descriptorType: DT.Image, descriptorCount: 1, stageFlags: SS.All },
{ binding: 0, descriptorType: DT.Image, descriptorCount: FONT_SIZES, stageFlags: SS.All },
{ binding: 1, descriptorType: DT.Sampler, descriptorCount: 1, stageFlags: SS.All },
];
@ -504,7 +512,7 @@ InitUICtx(PlatformWindow* window)
ctx.desc_set = AllocDescSet(&ctx.rd, ctx.desc_set_layout);
ctx.pipeline_layout = CreatePipelineLayout(&ctx.rd, ctx.desc_set_layout, Mat4.sizeof);
Attribute[13] attributes = [
Attribute[] attributes = [
{ binding: 0, location: 0, format: FMT.RGBA_F32, offset: Vertex.cols.offsetof },
{ binding: 0, location: 1, format: FMT.R_F32, offset: Vertex.corner_radius.offsetof + f32.sizeof*0 },
{ binding: 0, location: 2, format: FMT.R_F32, offset: Vertex.corner_radius.offsetof + f32.sizeof*1 },
@ -518,6 +526,7 @@ InitUICtx(PlatformWindow* window)
{ 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 },
{ binding: 0, location: 13, format: FMT.R_U32, offset: Vertex.atlas_index.offsetof },
];
GfxPipelineInfo ui_info = {
@ -535,16 +544,69 @@ InitUICtx(PlatformWindow* window)
alpha_op: BO.Add,
};
for(u32 i = 0; i < ctx.font_descs.length; i += 1)
{
CreateImageViewTex(&ctx.rd, &ctx.font_descs[i], ATLAS_DIMENSION, ATLAS_DIMENSION, DT.Image, 0, i);
ctx.glyph_sets[i].index = i;
}
FontAtlasBuf* abuf = GetFontAtlas(g_text_size_default);
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]);
u8[512*512*4] white_tex = 255;
for(u32 i = 1; i < ctx.font_descs.length; i += 1)
{
Transfer(&ctx.rd, &ctx.font_descs[i].view, white_tex, ATLAS_DIMENSION, ATLAS_DIMENSION);
}
Write(&ctx.rd, ctx.desc_set, ctx.font_descs);
Write(&ctx.rd, ctx.desc_set, &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;
FontAtlasBuf*
GetFontAtlas(u32 size)
{
return &GetFontGlyphs(size).abuf;
}
FontGlyphs*
GetFontGlyphs(u32 size)
{
UICtx* ctx = GetCtx();
FontGlyphs* fg = null;
for(u64 i = 0; i < ctx.glyph_sets_used; i += 1)
{
if(size == ctx.glyph_sets[i].size)
{
fg = &ctx.glyph_sets[i];
break;
}
}
if(!fg)
{
u32 i = ctx.glyph_sets_used;
fg = &ctx.glyph_sets[i];
assert(ctx.font);
fg.abuf = CreateAtlas(&ctx.arena, ctx.font, cast(f32)size, ATLAS_DIMENSION);
fg.size = size;
Transfer(&ctx.rd, &ctx.font_descs[i].view, fg.abuf.data, ATLAS_DIMENSION, ATLAS_DIMENSION);
Write(&ctx.rd, ctx.desc_set, &ctx.font_descs[i]);
ctx.glyph_sets_used += 1;
}
return fg;
}
void
@ -626,75 +688,41 @@ MakeItem(T)(T k, UIFlags flags = UIF.None) if(is(T: UIKey) || StringType!T)
}
}
item.max_text_width = 0.0;
FontAtlasBuf* abuf = GetFontAtlas(item.text_size);
string str = item.display_string.length ? item.display_string : item.key.text;
item.max_text_width = cast(f32)(str.length)*abuf.atlas.max_advance;
if(item.flags & UIF.TextWrap)
{
f32 width = item.size_info[A2D.X].type == ST.TextSize ? item.parent.size.x : item.size.x;
f32 text_width = CalcTextWidth(str);
u32 ch_per_line = cast(u32)floor(width/abuf.atlas.max_advance);
u64 lines = (str.length/ch_per_line) + (str.length%ch_per_line ? 1 : 0);
if(text_width < width || width == 0.0) goto InitSingleLine;
u64 lines = cast(u64)(ceil(text_width/width));
u32 max_chars = str.length > ch_per_line ? ch_per_line : cast(u32)str.length;
item.max_text_width = cast(f32)(max_chars)*abuf.atlas.max_advance;
item.text_lines = ScratchAlloc!(string)(lines);
assert(!item.syntax_tokens.length || item.syntax_tokens.length == str.length);
u64 start;
for(u64 i = 0; i < lines; i += 1)
{
u64 end = str.length-start <= ch_per_line ? str.length : start+ch_per_line;
item.text_lines[i] = str[start .. end];
}
if(item.syntax_tokens.length)
{
item.token_lines = ScratchAlloc!(u8[])(lines);
}
f32 w = 0.0;
u64 line = 0;
u64 ch_start = 0;
Glyph[] glyphs = ctx.atlas_buf.atlas.glyphs;
for(u64 i = 0; i < str.length; i += 1)
for(u64 i = 0; i < lines; i += 1)
{
Glyph* g = str[i] < glyphs.length ? glyphs.ptr+str[i] : glyphs.ptr+0;
f32 glyph_w = GlyphWidth(g);
if(glyph_w+w > width)
{
item.text_lines[line] = str[ch_start .. i];
if(item.syntax_tokens.length)
{
item.token_lines[line] = item.syntax_tokens[ch_start .. i];
u64 end = str.length-start <= ch_per_line ? str.length : start+ch_per_line;
item.token_lines[i] = item.syntax_tokens[start .. end];
}
line += 1;
ch_start = i;
item.max_text_width = item.max_text_width < w ? w : item.max_text_width;
if(line == lines-1)
{
item.text_lines[line] = str[ch_start .. $];
if(item.syntax_tokens.length)
{
item.token_lines[line] = item.syntax_tokens[ch_start .. $];
}
w = CalcTextWidth(item.text_lines[line]);
item.max_text_width = item.max_text_width < w ? w : item.max_text_width;
break;
}
w = 0.0;
}
w += glyph_w;
}
}
else
{
InitSingleLine:
item.text_lines = ScratchAlloc!(string)(1);
item.text_lines[0] = str;
item.max_text_width = CalcTextWidth(item.text_lines[0]);
if(item.syntax_tokens.length)
{
@ -816,6 +844,8 @@ BeginUI(Inputs* inputs)
UICtx* ctx = GetCtx();
Arena* a = &ctx.temp_arena;
debug ctx.item_count = 0;
Reset(a);
// Convert Inputs
@ -885,19 +915,20 @@ BeginUI(Inputs* inputs)
}
}
for(UIItem* item = ctx.transient_items; !Nil(item); item = item.transient_next)
for(UIItem* item = ctx.transient_items; !Nil(item);)
{
UIItem* next = item.transient_next;
memset(item, 0, UIItem.sizeof);
DLLPush(ctx.free_items, item, g_UI_NIL);
item = next;
}
ctx.frame += 1;
ctx.transient_items = g_UI_NIL;
// Ctx state
ctx.transient_items = g_UI_NIL;
ctx.frame += 1;
ctx.f_idx = ctx.frame%FRAME_OVERLAP;
ctx.inputs = inputs;
ctx.char_width = GlyphWidth(&ctx.atlas_buf.atlas.glyphs[' ']);
ResetStacks(ctx);
@ -992,7 +1023,7 @@ EndUI()
}
else
{
item.size.v[axis] = item.padding.y*2.0 + TEXT_SIZE*item.text_lines.length;
item.size.v[axis] = item.padding.y*2.0 + GetFontAtlas(item.text_size).atlas.line_height*item.text_lines.length;
}
}
}
@ -1074,10 +1105,7 @@ EndUI()
excess -= reduced;
c.size.v[axis] -= reduced;
if(excess <= 0.0009)
{
break;
}
if(excess <= 0.0009) break;
}
if(excess > 0.0009)
@ -1093,10 +1121,7 @@ EndUI()
excess -= reduced;
c.size.v[axis] -= reduced;
if(excess < 0.0009)
{
break;
}
if(excess < 0.0009) break;
}
}
}
@ -1118,72 +1143,42 @@ EndUI()
// Calculate final sizes
{
f32 pos = 0.0;
for(UIItem* item = ctx.root; !Nil(item);)
for(UIItem* item = ctx.root; !Nil(item); item = Recurse!(true)(item, g_UI_NIL))
{
item.last_relative_item = g_UI_NIL;
bool fixed = cast(bool)(item.flags & UIF.FixedPosition);
bool first_child_fixed = item.parent.flags & UIF.FixedPosition && item == item.parent.first;
bool on_layout_axis = item.parent.layout_axis == axis;
if(!on_layout_axis || first_child_fixed)
{
pos = item.parent.rect.p0.v[axis] + item.parent.padding.v[axis];
}
if(item.view_offset.x != 0.0 || item.view_offset.y != 0.0)
{
for(UIItem* c = item.first; !Nil(c); c = c.next)
{
if(!(c.flags & UIF.FixedPosition))
{
c.pos_offset = item.view_offset;
break;
}
}
}
f32 next_pos = 0.0;
UIItem* parent = item.parent;
if(fixed)
{
item.rect.p0.v[axis] = item.fixed_pos.v[axis] + item.parent.rect.p0.v[axis];
item.rect.p1.v[axis] = item.rect.p0.v[axis] + item.size.v[axis];
next_pos = pos;
item.rect.p0.v[axis] = parent.rect.p0.v[axis] + item.fixed_pos.v[axis] + item.padding.v[axis] + parent.view_offset.v[axis];
item.rect.p1.v[axis] = item.rect.p0.v[axis] + item.size.v[axis] - item.padding.v[axis];
}
else if(Nil(item.parent.last_relative_item))
{
item.rect.p0.v[axis] = parent.rect.p0.v[axis] + item.padding.v[axis] + parent.view_offset.v[axis];
item.rect.p1.v[axis] = item.rect.p0.v[axis] + item.size.v[axis] - item.padding.v[axis];
}
else if(axis != item.parent.layout_axis)
{
item.rect.p0.v[axis] = parent.rect.p0.v[axis] + parent.view_offset.v[axis] + item.padding.v[axis];
item.rect.p1.v[axis] = parent.rect.p0.v[axis] + item.size.v[axis] - item.padding.v[axis];
}
else
{
item.rect.p0.v[axis] = pos + item.pos_offset.v[axis];
item.rect.p1.v[axis] = item.rect.p0.v[axis] + item.size.v[axis];
UIItem* prev = parent.last_relative_item;
next_pos = item.parent.layout_axis == axis ? item.rect.p1.v[axis] : item.rect.p0.v[axis];
item.rect.p0.v[axis] = prev.rect.p1.v[axis] + prev.padding.v[axis] + item.padding.v[axis];
item.rect.p1.v[axis] = item.rect.p0.v[axis] + item.size.v[axis] - item.padding.v[axis];
}
if(!fixed && !Nil(parent))
{
parent.last_relative_item = item;
}
assert(!isNaN(item.rect.p0.v[axis]));
assert(!isNaN(item.rect.p1.v[axis]));
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.parent.rect.p0.v[axis];
break;
}
if(Nil(p))
{
item = g_UI_NIL;
break;
}
}
}
}
}
@ -1237,6 +1232,10 @@ void
RenderItem(UICtx* ctx, UIItem* item)
{
if(item.rendered) return;
if(!(item.flags & DRAW_FLAGS)) return;
Vec2 p0 = item.rect.p0 - item.padding;
Vec2 p1 = item.rect.p1 + item.padding;
// Doesn't really support nesting scissors, will maybe change this later.
bool scissor_x = cast(bool)(item.flags & UIF.PortalViewX);
@ -1245,18 +1244,18 @@ RenderItem(UICtx* ctx, UIItem* item)
{
DrawUI(ctx);
u32 x = cast(u32)(scissor_x ? floor(item.rect.p0.x) : 0);
u32 y = cast(u32)(scissor_y ? floor(item.rect.p0.y) : 0);
u32 w = cast(u32)(scissor_x ? floor(item.rect.p1.x) - x : ctx.res.x);
u32 h = cast(u32)(scissor_y ? floor(item.rect.p1.y) - y : ctx.res.y);
u32 x = cast(u32)(scissor_x ? floor(p0.x) : 0);
u32 y = cast(u32)(scissor_y ? floor(p0.y) : 0);
u32 w = cast(u32)(scissor_x ? floor(p1.x) - x : ctx.res.x);
u32 h = cast(u32)(scissor_y ? floor(p1.y) - y : ctx.res.y);
SetScissor(&ctx.rd, x, y, w, h);
}
if(item.flags & UIF.DrawBackground)
{
Vertex* v = GetVertex(ctx);
v.dst_start = item.rect.p0 + item.border_thickness;
v.dst_end = item.rect.p1 - item.border_thickness;
v.dst_start = p0 + item.border_thickness;
v.dst_end = p1 - item.border_thickness;
v.cols = item.bg_col;
v.corner_radius = item.flags & UIF.DrawBorder ? item.corner_radius*0.5 : item.corner_radius;
v.bounds = ItemBounds(item.parent);
@ -1267,8 +1266,8 @@ RenderItem(UICtx* ctx, UIItem* item)
if(item.flags & UIF.DrawBorder)
{
Vertex* v = GetVertex(ctx);
v.dst_start = item.rect.p0;
v.dst_end = item.rect.p1;
v.dst_start = p0;
v.dst_end = p1;
v.cols = item.border_col;
v.corner_radius = item.corner_radius;
v.border_thickness = clamp(item.border_thickness, 1.0, f32.max);
@ -1286,27 +1285,25 @@ RenderItem(UICtx* ctx, UIItem* item)
}
else
{
// The math around all this is fucked
FontAtlas* atl = &ctx.atlas_buf.atlas;
f32 y_pos = item.rect.p0.y + item.padding.y;
FontGlyphs* fg = GetFontGlyphs(item.text_size);
f32 y_pos = p0.y + fg.abuf.atlas.line_height;
Vec4[] syntax_cols = ctx.syntax_colors[item.syntax_highlight];
foreach(i; 0 .. item.text_lines.length)
{
string str = item.text_lines[i];
u8[] tks = item.token_lines.length ? item.token_lines[i] : [];
f32 x_pos = item.flags & UIF.RightAlignText ? item.rect.p1.x - item.padding.x - CalcTextWidth(str) :
item.rect.p0.x + item.padding.x;
f32 x_pos = item.flags & UIF.RightAlignText ? p1.x - item.padding.x - CalcTextWidth(str, &fg.abuf) : p0.x + item.padding.x;
x_pos = clamp(x_pos, item.rect.p0.x, item.rect.p1.x);
x_pos = clamp(x_pos, p0.x, p1.x);
if(tks.length)
{
foreach(j; 0 .. str.length)
{
u8 ch = str[j];
Glyph* g = ch < atl.glyphs.length ? atl.glyphs.ptr + ch : null;
DrawGlyph(item, g, &x_pos, y_pos, syntax_cols[tks[j]]);
Glyph* g = ch < fg.abuf.atlas.glyphs.length ? fg.abuf.atlas.glyphs.ptr + ch : null;
DrawGlyph(item, g, fg.index, &x_pos, y_pos, syntax_cols[tks[j]]);
}
}
else
@ -1314,12 +1311,12 @@ RenderItem(UICtx* ctx, UIItem* item)
foreach(j; 0 .. str.length)
{
u8 ch = str[j];
Glyph* g = ch < atl.glyphs.length ? atl.glyphs.ptr + ch : null;
DrawGlyph(item, g, &x_pos, y_pos, item.text_col);
Glyph* g = ch < fg.abuf.atlas.glyphs.length ? fg.abuf.atlas.glyphs.ptr + ch : null;
DrawGlyph(item, g, fg.index, &x_pos, y_pos, item.text_col);
}
}
y_pos += TEXT_SIZE;
y_pos += fg.abuf.atlas.line_height;
}
}
}
@ -1376,6 +1373,17 @@ StackIDs(string stack, string ctx = "ctx")
};
}
void
PushSizeInfo(SizeType type0, f32 value0, f32 strictness0, SizeType type1, f32 value1, f32 strictness1, bool auto_pop = false)
{
UISize[2] size_info = [
UISize(type0, value0, strictness0),
UISize(type1, value1, strictness1),
];
Push!("size_info")(size_info, auto_pop);
}
void
PushSizeInfoVec(int i)(SizeType type, f32 value, f32 strictness = 1.0, bool auto_pop = false)
{
@ -1463,24 +1471,52 @@ PushScrollTargetY(f32 target, bool auto_pop = false)
PushScrollTarget(Vec2(0.0, target), auto_pop);
}
static string
PushScope(string stack, string value)()
{
import std.conv;
return i"Push!(\"$(stack)\")($(value)); scope(exit) Pop!(\"$(stack)\")();".text;
}
static string
PushOnce(UIPushInfo[] info)()
{
import std.conv;
string result = "";
static foreach(i; info)
{
result ~= i"Push!(\"$(i.s)\")($(i.v), true); scope(exit) Pop!(\"$(i.s)\")();".text;
}
return result;
}
struct UIPushInfo
{
string s; // stack
string v; // value
}
void
Push(string stack_str, T)(T value, bool auto_pop = false)
{
import std.string : replace;
enum ids = StackIDs!(stack_str, "g_ui_ctx");
alias ST = typeof(mixin(ids.stack ~ ".top.value"));
auto stack = &mixin(ids.stack);
auto top = mixin(ids.stack_top_node);
Stack!(T)* node = stack.free;
Stack!(ST)* node = stack.free;
if(node)
{
stack.free = node.next;
}
else
{
node = Alloc!(Stack!(T))(&g_ui_ctx.temp_arena);
node = Alloc!(Stack!(ST))(&g_ui_ctx.temp_arena);
}
node.next = stack.top;
@ -1762,11 +1798,10 @@ NewItem(UICtx* ctx)
{
UIItem* item = g_UI_NIL;
if(!Nil(ctx.free_items.first))
{
debug ctx.item_count += 1;
item = DLLPop(ctx.free_items, g_UI_NIL);
}
else
if(Nil(item))
{
item = Alloc!(UIItem)(&ctx.arena);
}
@ -1807,6 +1842,7 @@ Get(T)(T k) if(is(T: UIKey) || StringType!T)
Result!(UIItem*) res = ctx.items[key.hash];
if(res.ok)
{
debug ctx.item_count += 1;
item = res.value;
}
else
@ -1823,7 +1859,7 @@ Get(T)(T k) if(is(T: UIKey) || StringType!T)
}
f32
CalcTextWidth(T)(T text) if(is(T == string) || StringType!T)
CalcTextWidth(bool fast = true, T, U)(T text, U param) if((is(T == string) || StringType!T) && (is(U: u32) || is(U == FontAtlasBuf*) ))
{
static if(is(T == string))
{
@ -1834,42 +1870,63 @@ CalcTextWidth(T)(T text) if(is(T == string) || StringType!T)
string str = ConvToStr(text);
}
u32 tab_width = g_ui_ctx.tab_width;
Glyph* space = g_ui_ctx.atlas_buf.atlas.glyphs.ptr + ' ';
static if(is(U: u32))
{
FontAtlasBuf* abuf = GetFontAtlas(param);
}
else
{
FontAtlasBuf* abuf = param;
}
u32 tab_width = g_ui_ctx.tab_width;
static if(fast)
{
f32 width = str.length * abuf.atlas.max_advance;
assert(abuf.atlas.max_advance > 0.0009);
}
else
{
f32 width = 0.0;
for(u64 i = 0; i < str.length; i += 1)
{
width += GlyphWidth(g_ui_ctx.atlas_buf.atlas.glyphs.ptr + str.ptr[i]);
width += GlyphWidth(abuf.atlas.glyphs.ptr + str.ptr[i], abuf);
}
}
return width;
}
pragma(inline) f32
GlyphWidth(Glyph* g)
GlyphWidth(Glyph* g, FontAtlasBuf* abuf)
{
return g.ch == '\t' ? (g_ui_ctx.atlas_buf.atlas.glyphs[' '].advance*cast(f32)(g_ui_ctx.tab_width)) : g.advance;
return g.ch == '\t' ? (abuf.atlas.glyphs[' '].advance*cast(f32)(g_ui_ctx.tab_width)) : g.advance;
}
void
DrawGlyph(UIItem* item, Glyph* glyph, f32* x_pos, f32 y, Vec4 col = Vec4(1.0))
DrawGlyph(UIItem* item, Glyph* glyph, u32 atlas_index, f32* x_pos, f32 y, Vec4 col = Vec4(1.0))
{
if(glyph)
{
UICtx* ctx = GetCtx();
Vertex* v = null;
f32 advance = glyph.advance * item.text_scale;
f32 advance = glyph.advance;
if(glyph.ch == '\t')
if(glyph.ch == ' ' || glyph.ch == '\n')
{
*x_pos += advance;
}
else if(glyph.ch == '\t')
{
*x_pos += advance * (GetCtx().tab_width - 1);
}
f32 r = glyph.plane_right * item.text_scale;
f32 l = glyph.plane_left * item.text_scale;
f32 t = glyph.plane_top * item.text_scale;
f32 b = glyph.plane_bottom * item.text_scale;
else
{
f32 r = glyph.plane_right;
f32 l = glyph.plane_left;
f32 t = glyph.plane_top;
f32 b = glyph.plane_bottom;
GlyphBounds gb = {
r: r,
@ -1886,13 +1943,14 @@ DrawGlyph(UIItem* item, Glyph* glyph, f32* x_pos, f32 y, Vec4 col = Vec4(1.0))
v = ctx.buffers[ctx.f_idx].vtx.ptr + ctx.buffers[ctx.f_idx].count;
f32 y_pos = (y - gb.t) + TEXT_SIZE;
f32 y_pos = y + gb.t;
v.dst_start = Vec2(*x_pos+gb.l, y_pos);
v.dst_end = Vec2(*x_pos+gb.w+gb.l, y_pos+gb.h);
v.cols = col;
v.texture = true;
v.bounds = ItemBounds(item);
v.atlas_index = atlas_index;
if(glyph.ch != '\t' && glyph.ch != '\n')
{
@ -1910,7 +1968,8 @@ DrawGlyph(UIItem* item, Glyph* glyph, f32* x_pos, f32 y, Vec4 col = Vec4(1.0))
v.src_end.x -= (v.src_end.x - v.src_start.x) * cull_pct;
}
f32 end_y = y + TEXT_SIZE;
/*
f32 end_y = y;
f32 height = end_y - y;
if(end_y > item.rect.p1.y)
{
@ -1918,6 +1977,7 @@ DrawGlyph(UIItem* item, Glyph* glyph, f32* x_pos, f32 y, Vec4 col = Vec4(1.0))
v.dst_end.y -= (v.dst_end.y - v.dst_start.y) * cull_pct;
v.src_end.y -= (v.src_end.y - v.src_start.y) * cull_pct;
}
*/
static foreach(axis; A2D.min .. A2D.max)
{
@ -1931,6 +1991,7 @@ DrawGlyph(UIItem* item, Glyph* glyph, f32* x_pos, f32 y, Vec4 col = Vec4(1.0))
*x_pos += advance;
}
}
}
pragma(inline) Vec2[2]
@ -1952,30 +2013,23 @@ GetVertex(UICtx* ctx)
}
static UISize[2]
MakeUISize(UISize x, UISize y)
UIS2(SizeType t0 = ST.Percentage, SizeType t1 = ST.Percentage, f32 v0 = 1.0, f32 v1 = 1.0, f32 s0 = 1.0, f32 s1 = 1.0)
{
return [x, y];
return [UISize(t0, v0, s0), UISize(t1, v1, s1)];
}
static UISize[2]
MakeUISizeX(SizeType type, f32 value, f32 strictness = 1.0)
UISX(SizeType type, f32 value = 1.0, 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)
UISY(SizeType type, f32 value = 1.0, f32 strictness = 1.0)
{
return [UISize(ST.Percentage, 1.0), UISize(type, value, strictness)];
}
pragma(inline) Glyph*
GetGlyph(u8 ch)
{
FontAtlas* a = &g_ui_ctx.atlas_buf.atlas;
return ch < a.glyphs.length ? a.glyphs.ptr + ch : a.glyphs.ptr;
}
pragma(inline) void
AddVertexCount(UICtx* ctx)
{

View File

@ -31,84 +31,105 @@ struct Panel
Axis2D layout_axis;
Editor* ed;
u64 id;
u32 text_size;
}
void
LineCounterView(u64 max_line, u64 lines, i64 line_offset, f32 view_offset)
LineCounterView(FontAtlasBuf* abuf, u64 max_line, u64 lines, i64 line_offset, f32 view_offset)
{
UICtx* ctx = GetCtx();
UIKey zero = ZeroKey();
u64 ch_width = max_line.toChars().length;
f32 lc_width = cast(f32)(ch_width+1)*ctx.char_width; // Should figure out how to accurately measure text width
f32 lc_width = cast(f32)(ch_width+1)*abuf.atlas.max_advance;
PushLayoutAxis(A2D.Y, true);
PushViewOffsetY(view_offset, true);
PushPaddingX(4.0, true);
PushSizeInfoX(ST.Pixels, lc_width, 1.0, true);
enum UIPushInfo[] lc_params = [
{ "layout_axis", q{ A2D.Y } },
{ "view_offset", q{ Vec2(0.0, view_offset) } },
{ "padding", q{ Vec2(8.0) } },
{ "size_info", q{ UISX(ST.Pixels, lc_width) }},
];
mixin(PushOnce!(lc_params));
UIItem* line_count = MakeItem(zero, UIF.DrawBorder|UIF.PortalViewY);
PushTextCol(Vec4(1.0));
PushSizeInfoY(ST.Pixels, TEXT_SIZE);
PushParent(line_count);
mixin(PushScope!("text_col", q{ Vec4(1.0) } ));
mixin(PushScope!("size_info", q{ UISY(ST.Pixels, abuf.atlas.line_height) } ));
mixin(PushScope!("parent", q{ line_count } ));
u64 end_line = lines+line_offset;
for(u64 i = line_offset; i < end_line && i < max_line; i += 1)
{
char[] buf = ScratchAlloc!(char)(ch_width);
Push!("display_string")(ConvToStr(sformat(buf, "%s", i)), true);
Push!("display_string")(ConvToStr(sformat(ScratchAlloc!(char)(ch_width), "%s", i)), true);
MakeItem(zero, UIF.DrawText);
}
Pop!("text_col", "size_info", "parent");
}
void
EditorTextView(UIItem* editor, Panel* p, u64 lines, i64 line_offset, f32 view_offset)
EditorTextView(UIItem* editor, Panel* p, FontAtlasBuf* abuf, u64 lines, i64 line_offset, f32 view_offset)
{
Editor* ed = p.ed;
PushLayoutAxis(A2D.Y, true);
f32 clamp_y = cast(f32)(ed.buf.line_count-lines)*TEXT_SIZE;
f32 scroll_pos = cast(f32)(ed.line_offset)*TEXT_SIZE;
f32 text_size = cast(f32)p.text_size;
f32 clamp_y = cast(f32)(ed.buf.line_count-lines)*text_size;
f32 scroll_pos = cast(f32)(ed.line_offset)*text_size;
f32 padding = 8.0;
PushLayoutAxis(A2D.Y, true);
PushScrollClampY(0.0, clamp_y, true);
PushSizeInfo(g_size_info_default, true);
PushBorderCol(Vec4(1.0), true);
PushScrollTargetY(scroll_pos, true);
PushViewOffsetY(view_offset, true);
enum UIPushInfo[] text_view_params = [
{ "layout_axis", q{ A2D.Y } },
{ "border_col", q{ Vec4(1.0) } },
{ "size_info", q{ UIS2() } },
{ "scroll_target", q{ Vec2(0.0, scroll_pos) } },
{ "scroll_clamp", q{ Vec2(0.0, clamp_y) } },
{ "view_offset", q{ Vec2(0.0, view_offset) } },
{ "padding", q{ Vec2(padding) } },
];
mixin(PushOnce!(text_view_params));
editor = MakeItem(editor.key, UIF.DrawBorder|UIF.ScrollY|UIF.ClampY);
PushParent(editor);
mixin(PushScope!("parent", q{ editor }));
UIKey zero = ZeroKey();
// Cursor
{
LineBuffer* lb = GetLine(&ed.buf, ed.cursor_pos.y);
f32 cursor_x = CalcTextWidth(lb.text[0 .. ed.cursor_pos.x]);
f32 cursor_y = cast(f32)(ed.cursor_pos.y - line_offset)*TEXT_SIZE + view_offset;
PushSizeInfo(MakeUISize(UISize(ST.Pixels, GetCtx().char_width), UISize(ST.Pixels, TEXT_SIZE)), true);
PushFixedPos(Vec2(cursor_x, cursor_y), true);
PushBgCol(Vec4(1.0), true);
u8 ch = lb.text.length > ed.cursor_pos.y ? lb.text[ed.cursor_pos.y] : 0;
f32 line_h = abuf.atlas.line_height;
f32 cursor_x = CalcTextWidth!(false)(lb.text[0 .. ed.cursor_pos.x], abuf);
f32 cursor_y = cast(f32)(ed.cursor_pos.y - line_offset)*line_h;
Glyph* g = abuf.atlas.glyphs.length > ch ? abuf.atlas.glyphs.ptr + ch : abuf.atlas.glyphs.ptr + ' ';
enum UIPushInfo[] cursor_info = [
{ "size_info", q{ UIS2(ST.Pixels, ST.Pixels, g.advance, line_h) } },
{ "fixed_pos", q{ Vec2(cursor_x, cursor_y) } },
{ "bg_col", q{ Vec4(1.0) } },
];
mixin(PushOnce!(cursor_info));
MakeItem(zero, UIF.DrawBackground|UIF.FixedPosition);
}
u64 end_line = line_offset+lines;
PushSizeInfoY(ST.TextSize, 1.0);
PushSyntaxHighlight(UISH.D);
mixin(PushScope!("size_info", q{ UISY(ST.TextSize) } ));
mixin(PushScope!("syntax_highlight", q{ UISH.D } ));
scope(exit) Pop!("size_info", "parent", "syntax_highlight");
// NEED TO FIX LINES BEING OFFSET FOR WHATEVER REASON
u64 i = line_offset;
for(LineBuffer* lb = GetLine(&ed.buf, i); !CheckNil(g_NIL_LINE_BUF, lb) && i < line_offset+lines; i += 1, lb = GetLine(&ed.buf, i))
{
PushDisplayString(ConvToStr(lb.text), true);
PushSyntaxTokens(cast(u8[])lb.style, true);
enum UIPushInfo[] lc_info = [
{ "display_string", q{ ConvToStr(lb.text) } },
{ "syntax_tokens", q{ cast(u8[])(lb.style) } },
];
mixin(PushOnce!(lc_info));
UIItem* line = MakeItem(zero, UIF.DrawText);
}
}
@ -119,6 +140,7 @@ EditorView(Panel* p)
Editor* ed = p.ed;
UICtx* ctx = GetCtx();
UIKey zero = ZeroKey();
FontAtlasBuf* abuf = GetFontAtlas(p.text_size);
if(CheckNil(g_NIL_ED, ed))
{
@ -132,24 +154,26 @@ EditorView(Panel* p)
UIKey ed_key = MakeKey("###ed_%s", ed.editor_id);
UIItem* editor = Get(ed_key);
u64 frame_line_offset = ed.line_offset;
f32 frame_view_offset = -(editor.scroll_offset.y%TEXT_SIZE);
f32 text_size = abuf.atlas.line_height;
PushBgCol(BG_COL, true);
PushSizeInfoX(ST.Percentage, 1.0, 1.0, true);
u64 frame_line_offset = ed.line_offset;
f32 frame_view_offset = -(editor.scroll_offset.y%text_size);
enum UIPushInfo[] c_info = [
{ "bg_col", q{ BG_COL } },
{ "size_info", q{ UIS2() } },
];
mixin(PushOnce!(c_info));
UIItem* container = MakeItem(zero, UIF.DrawBackground);
PushParent(container);
PushBorderCol(Vec4(1.0));
scope(exit) Pop!("parent", "border_col");
mixin(PushScope!("parent", q{ container } ));
mixin(PushScope!("border_col", q{ Vec4(1.0) } ));
u64 view_lines;
if(editor.size.y > 0.0)
{
view_lines = cast(u64)ceil(editor.size.y/TEXT_SIZE)+1;
view_lines = cast(u64)floor(editor.size.y/text_size);
const u64 SCROLL_BUFFER = 4;
u64 start = ed.line_offset;
@ -166,9 +190,9 @@ EditorView(Panel* p)
}
}
u64 start = cast(u64)floor(editor.scroll_offset.y/TEXT_SIZE);
LineCounterView(ed.buf.line_count, view_lines, start, frame_view_offset);
EditorTextView(editor, p, view_lines, start, frame_view_offset);
u64 start = cast(u64)floor(editor.scroll_offset.y/text_size);
LineCounterView(abuf, ed.buf.line_count, view_lines, start, frame_view_offset);
EditorTextView(editor, p, abuf, view_lines, start, frame_view_offset);
for(UIInput* i = ctx.events.first; !CheckNil(g_UI_NIL_INPUT, i) && g_ed_ctx.focused_editor == p.ed; i = i.next)
{
@ -201,6 +225,8 @@ EditorView(Panel* p)
ed.cursor_pos = VecPos(&ed.buf);
}
ResetBuffer(&ed.buf);
}
void
@ -208,348 +234,64 @@ CommandPalette(CmdPalette* cmd)
{
UICtx* ctx = GetCtx();
Vec2 ext = GetExtent();
FontAtlasBuf* abuf = &g_ui_ctx.glyph_sets[0].abuf;
f32 w = ext.x*0.4;
f32 h = ext.y*0.7;
PushFixedPos(Vec2(ext.x*0.3, ext.y*0.1), true);
PushBgCol(BG_COL, true);
PushBorderCol(HL_BORDER_COL, true);
PushBorderThickness(2.0f, true);
PushCornerRadius(8.0f, true);
PushViewOffset(Vec2(-2.0f), true);
PushLayoutAxis(A2D.Y, true);
PushSizeInfo(MakeUISize(UISize(ST.Pixels, w), UISize(ST.Pixels, h)));
enum UIPushInfo[] cmd_params = [
{ "layout_axis", q{ A2D.Y } },
{ "fixed_pos", q{ Vec2(ext.x*0.3, ext.y*0.1) } },
{ "bg_col", q{ BG_COL } },
{ "border_col", q{ HL_BORDER_COL } },
{ "border_thickness", q{ 4.0 } },
{ "corner_radius", q{ Vec4(8.0) } },
{ "size_info", q{ UIS2(ST.Pixels, ST.Pixels, w, h) } },
];
mixin(PushOnce!(cmd_params));
UIItem* cmd_item = MakeItem("###cmd_palette", UIF.Window|UIF.FixedPosition|UIF.DrawBackground|UIF.DrawBorder);
f32 padding_y = 8.0;
PushParent(cmd_item);
PushPadding(Vec2(4.0, padding_y), true);
scope(exit) Pop!("parent", "padding")();
f32 padding_y = 4.0;
PushBgCol(HL_BG_COL, true);
PushBorderCol(HL_BORDER_COL);
PushBorderThickness(2.0f, true);
PushCornerRadius(Vec4(8.0, 8.0, 0.0, 0.0), true);
PushSizeInfo(MakeUISize(UISize(ST.Pixels, w-padding_y), UISize(ST.TextSize, 1.0)), true);
PushDisplayString(ConvToStr(cmd.buffer[0 .. cmd.icount]), true);
mixin(PushScope!("parent", q{ cmd_item } ));
mixin(PushScope!("padding", q{ Vec2(4.0, padding_y) }));
enum UIPushInfo[] cmd_input_params = [
{ "bg_col", q{ HL_BG_COL } },
{ "size_info", q{ UISY(ST.TextSize) } },
{ "display_string", q{ ConvToStr(cmd.buffer[0 .. cmd.icount]) } },
];
mixin(PushOnce!(cmd_input_params));
UIKey zero = ZeroKey();
MakeItem(zero, UIF.DrawBorder|UIF.DrawBackground|UIF.DrawText|UIF.Overflow);
PushPadding(Vec2(4.0));
PushSizeInfoY(ST.TextSize, 1.0);
scope(exit) Pop!("size_info", "padding")();
enum UIPushInfo[] sep_params = [
{ "padding", q{ Vec2(0.0) } },
{ "size_info", q{ UISY(ST.Pixels, 4.0) } },
{ "bg_col", q{ HL_BORDER_COL } },
];
mixin(PushOnce!(sep_params));
MakeItem(zero, UIF.DrawBackground);
mixin(PushScope!("padding", q{ Vec2(4.0) } ));
mixin(PushScope!("size_info", q{ UISY(ST.TextSize) } ));
u64 max_opts = cast(u64)ceil(h/abuf.atlas.line_height);
Vec4[2] opt_cols = [
Vec4(0.22, 0.22, 0.22, 1.0),
Vec4(0.35, 0.35, 0.35, 1.0),
];
foreach(i; 0 .. cmd.opt_strs.length)
for(u64 i = 0; i < cmd.opt_strs.length && i < max_opts; i += 1)
{
PushDisplayString(ConvToStr(cmd.opt_strs[i]), true);
PushBgCol(cmd.selected == i ? HL_BG_COL : opt_cols[i%2], true);
MakeItem(zero, UIF.DrawBackground|UIF.DrawText);
}
//PushDisplayString(ConvToStr(cmd.buffer));
}
/*
void
Panel(UIPanel* panel)
{
UICtx* ctx = GetCtx();
UIItem* item = Get(panel.id);
Editor* ed = panel.ed;
bool focused = GetFocusedPanel() == panel;
UIPanel* parent = panel.parent;
UIPanel* prev = panel.prev;
Axis2D pax = parent.axis;
Vec2 adj = Vec2(
parent.axis == A2D.X ? 10 : 0,
parent.axis == A2D.Y ? 10 : 0
);
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.p0-adj;
Vec2 d1 = Vec2(
pax == A2D.X ? rect.p0.x+adj.x : rect.p1.x,
pax == A2D.Y ? rect.p0.y+adj.y : rect.p1.y
);
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))
{
pct += mov_pct;
panel.prev.pct -= mov_pct;
}
}
}
if(panel.ed != null)
{
if(Clicked(item, &r))
{
SetFocusedPanel(panel);
}
Vec2 inner = InnerSize(panel);
SetScrollOffset(panel);
panel.start_ln = cast(u64)(floor(panel.scroll_offset/TEXT_SIZE));
panel.end_ln = panel.start_ln + cast(u64)(ceil(inner.y/TEXT_SIZE));
panel.vis_lines = panel.end_ln - panel.start_ln;
char[64] ch_buf = '\0';
char[] fmt = ch_buf.sformat("%%0%ss", u64(panel.end_ln.toChars().length));
f32 lc_w = panel.end_ln.toChars().length*ctx.char_width + LINE_COUNT_PADDING*2.0;
f32 code_view_width = inner.x-lc_w;
StartLineBuffer(&panel.ed.buf, code_view_width);
U64Vec2 pos = VecPos(&ed.buf);
DrawPanel(panel, lc_w, focused);
f32 y_rem = 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))
{
f32 x_pos = x + LINE_COUNT_PADDING;
DrawLineCount(fmt, &x_pos, y, i);
u64 ch_offset;
auto parts = MakeMultiline(buf.text, code_view_width, buf.style);
if(parts == null)
{
if(pos.y == i)
{
DrawCursor([], 0, x+lc_w, y, ch_offset);
}
}
else for(auto n = parts; n != null; n = n.next)
{
auto l = n;
if(pos.y == i)
{
DrawCursor(l.text, pos.x, x+lc_w, y, ch_offset);
}
foreach(j; 0 .. l.text.length)
{
bool hl = pos.y == i && !EditModeActive() && j == pos.x-ch_offset;
DrawChar(l.text[j], &x_pos, y, hl ? Vec4(Vec3(0.0), 1.0) : SYNTAX_COLORS[l.style[j]]);
}
y += TEXT_SIZE;
x_pos = x + lc_w + LINE_COUNT_PADDING*2.0;
ch_offset += l.text.length;
}
}
}
}
void
CommandPalette(CmdPalette* cmd)
{
UICtx* ctx = GetCtx();
u8[] text = cmd.buffer[0 .. cmd.icount];
u8[][] options = cmd.opt_strs;
Vec2 size = RootSize();
f32 x = size.x*0.15;
f32 y = size.y*0.1;
f32 w = size.x*0.7;
f32 h = 40.0;
DrawCmdRect(Vec2(x, y), Vec2(w, h), false);
f32 y_off = h*0.5 + 6;
f32 ch_x = x + 6.0;
f32 ch_y = y + y_off;
foreach(i; 0 .. text.length)
{
DrawChar(text[i], &ch_x, ch_y, Vec4(1.0));
}
for(u64 i = 0; i < options.length; i += 1)
{
y += h;
ch_x = x + 6.0;
ch_y = y + y_off;
DrawCmdRect(Vec2(x, y), Vec2(w, h), cmd.selected == i);
foreach(j; 0 .. options[i].length)
{
DrawChar(options[i][j], &ch_x, ch_y, Vec4(1.0));
}
if(y+h > size.y+h) break;
}
}
bool
CheckPanelBounds(f32 pct)
{
return pct >= 0.0 && pct <= 1.0;
}
void
PushPanel(UIPanel* parent, UIPanel* panel)
{
DLLPush(parent, panel, g_UI_NIL_PANEL);
panel.parent = parent;
}
void
InsertPanel(UIPanel* parent, UIPanel* prev, UIPanel* panel)
{
DLLInsert(parent, panel, prev, g_UI_NIL_PANEL);
panel.parent = prev.parent;
}
void
SetFocusedPanel(UIPanel* panel)
{
if(!CheckNil(g_UI_NIL_PANEL, panel))
{
g_ui_ctx.focused_panel = panel;
}
}
UIPanel*
GetFocusedPanel()
{
return Nil(g_ui_ctx.focused_panel) ? g_UI_NIL_PANEL : g_ui_ctx.focused_panel;
}
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));
TextBuffer* node = null;
if(line_count > 0)
{
f32 w = 0.0;
u64 line = 0;
u64 start = 0;
const u64 extra_buf = 20;
for(u64 i = 0; i < text.length; i += 1)
{
f32 ch_w = GlyphWidth(g_ui_ctx.atlas_buf.atlas.glyphs.ptr + text[i]);
if(ch_w + w > scaled_width || i == text.length-1)
{
u64 len = i-start+1;
u8[] str = ScratchAlloc!(u8)(text, start, len);
TS[] stl = [];
if(style.length > 0)
{
stl = ScratchAlloc!(TS)(style, start, len);
}
TextBuffer* n = node;
for(;;)
{
if(node == null)
{
node = ScratchAlloc!(TextBuffer)();
node.text = str;
node.style = stl;
node.next = null;
break;
}
if(n.next == null)
{
n.next = ScratchAlloc!(TextBuffer)();
n.next.text = str;
n.next.style = stl;
n.next.next = null;
break;
}
n = n.next;
}
line += 1;
start = i;
w = 0.0;
}
w += ch_w;
}
}
return node;
}
pragma(inline) void
DrawCursor(u8[] text, u64 ch_x, f32 x, f32 y, u64 offset)
{
Glyph* g = GetGlyph(' ');
foreach(j; 0 .. text.length)
{
bool hl = j == ch_x-offset;
if(hl)
{
break;
}
g = j == text.length-1 ? GetGlyph(' ') : GetGlyph(text[j]);
x += GlyphWidth(g);
}
DrawRect(x, y, cast(u8)g.ch, Vec4(1.0), EditModeActive());
}
pragma(inline) void
DrawLineCount(char[] fmt, f32* x_pos, f32 y, u64 line)
{
char[32] line_buf = '\0';
char[] line_str = sformat(line_buf, fmt, line+1);
foreach(j; 0 .. line_str.length)
{
DrawChar(line_str[j], x_pos, y, Vec4(1.0));
}
*x_pos += LINE_COUNT_PADDING;
}
*/

View File

@ -5,9 +5,14 @@
#include "gui.layout"
layout (location = 0) flat in uint in_has_texture;
layout (location = 0) flat in struct FragDataFlatIn
{
uint texture;
uint atlas_index;
} FDF;
layout (location = 1) in struct FragDataIn {
layout (location = 2) in struct FragDataIn
{
vec4 color;
vec2 uv;
vec2 dst_pos;
@ -20,7 +25,6 @@ layout (location = 1) in struct FragDataIn {
float border_thickness;
} FD;
layout (location = 0) out vec4 FragColor;
float RectSDF(vec2 pos, vec2 half_size, float radius)
@ -75,7 +79,7 @@ void main()
vec4 gamma = vec4(1.0/1.4);
vec4 tex_color = vec4(1.0);
if(in_has_texture != 0)
if(FDF.texture != 0)
{
tex_color = texture(sampler2D(SpriteAtlas, SamplerNearest), FD.uv);
}

View File

@ -1,9 +1,11 @@
layout (rgba16f, set = 0, binding = 0) uniform image2D DrawImage;
layout (set = 1, binding = 0) uniform texture2D SpriteAtlas;
layout (set = 1, binding = 0) uniform texture2D SpriteAtlasArray[24];
layout (set = 1, binding = 1) uniform sampler SamplerNearest;
layout (push_constant) uniform Constants {
mat4 projection;
} PC;
#define SpriteAtlas SpriteAtlasArray[FDF.atlas_index]

View File

@ -17,10 +17,16 @@ layout (location = 9) in float border_thickness;
layout (location = 10) in float edge_softness;
layout (location = 11) in float raised;
layout (location = 12) in uint in_has_texture;
layout (location = 13) in uint in_atlas_index;
layout (location = 0) flat out uint out_has_texture;
layout (location = 0) flat out struct FragDataFlatOut
{
uint texture;
uint atlas_index;
} FDF;
layout (location = 1) out struct FragDataOut {
layout (location = 2) out struct FragDataOut
{
vec4 color;
vec2 uv;
vec2 dst_pos;
@ -31,7 +37,7 @@ layout (location = 1) out struct FragDataOut {
float softness;
float raised;
float border_thickness;
} FragData;
} FD;
vec2 Vertices[4] = vec2[4](
vec2(-1.0, -1.0),
@ -77,17 +83,18 @@ void main()
vec2 dst_verts_pct = vec2(bool(gl_VertexIndex >> 1) ? 1.0f : 0.0f,
bool(gl_VertexIndex & 1) ? 0.0f : 1.0f);
FragData.color = in_col;
FragData.uv = uvs[gl_VertexIndex] / tex_size;
FragData.dst_pos = pos;
FragData.dst_center = center;
FragData.dst_half_size = half_size;
FragData.corner_radius = corner_radius[gl_VertexIndex];
FragData.softness = edge_softness;
FragData.raised = raised;
FragData.border_thickness = border_thickness;
FragData.sdf_sample_pos = (2.0f * dst_verts_pct - 1.0f) * half_size;
out_has_texture = in_has_texture;
FD.color = in_col;
FD.uv = uvs[gl_VertexIndex] / tex_size;
FD.dst_pos = pos;
FD.dst_center = center;
FD.dst_half_size = half_size;
FD.corner_radius = corner_radius[gl_VertexIndex];
FD.softness = edge_softness;
FD.raised = raised;
FD.border_thickness = border_thickness;
FD.sdf_sample_pos = (2.0f * dst_verts_pct - 1.0f) * half_size;
FDF.texture = in_has_texture;
FDF.atlas_index = in_atlas_index;
vec4 v_pos = PC.projection * vec4(pos.x, pos.y, 0, 1);
gl_Position = vec4(v_pos.x, v_pos.y, v_pos.z, 1);