many fices

This commit is contained in:
Matthew 2025-12-27 20:45:09 +11:00
parent 94e64fef9e
commit 2edc6777f9
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 c637de10ffd949a07e51d6a792a6c4f49ac33ac5

View File

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

View File

@ -61,10 +61,7 @@ struct Editor
I64Vec2 cursor_pos; I64Vec2 cursor_pos;
Vec2 select_start; Vec2 select_start;
Vec2 select_end; Vec2 select_end;
i64 line_offset; i64 line_offset;
f32 text_size;
} }
struct ChangeStacks struct ChangeStacks
@ -155,48 +152,6 @@ Cycle(Inputs* inputs)
{ {
CommandPalette(&g_ed_ctx.cmd); 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(); EndUI();
} }
@ -266,6 +221,7 @@ CreatePanel(EditorCtx* ctx, Editor* ed = null)
p.layout_axis = A2D.Y; p.layout_axis = A2D.Y;
p.ed = ed; p.ed = ed;
p.id = ctx.panel_id++; p.id = ctx.panel_id++;
p.text_size = 18;
p.parent = p.first = p.last = p.next = p.prev = g_NIL_PANEL; p.parent = p.first = p.last = p.next = p.prev = g_NIL_PANEL;
return p; 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 u64 VERTEX_MAX_COUNT = 10000;
const Vec2 CLICK_BUFFER = Vec2(3.0, 3.0); const Vec2 CLICK_BUFFER = Vec2(3.0, 3.0);
const u32 FONT_SIZES = 24;
const u32 ATLAS_DIMENSION = 512;
// TODO: add setting // TODO: add setting
const f32 TEXT_SIZE = 16.0;
const f32 SCROLL_SPEED = 48.0; const f32 SCROLL_SPEED = 48.0;
const f32 LINE_COUNT_PADDING = 4.0; const f32 LINE_COUNT_PADDING = 4.0;
__gshared UICtx g_ui_ctx; __gshared UICtx g_ui_ctx;
//const u8[] FONT_BYTES = import("pc-9800.ttf"); //const u8[] FONT_BYTES = import("pc-9800.ttf");
const u8[] FONT_BYTES = import("NuberNextCondensed-DemiBold.otf"); //const u8[] FONT_BYTES = import("NuberNextCondensed-DemiBold.otf");
const u8[] VERTEX_BYTES = import("gui.vert.spv"); u8[] FONT_BYTES = cast(u8[])import("jetbrains-mono/JetBrainsMono-Regular.ttf");
const u8[] FRAGMENT_BYTES = import("gui.frag.spv"); 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; Vec4 g_corner_radius_default = 0.0;
f32 g_edge_softness_default = 0.8; 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)]; UISize[2] g_size_info_default = [UISize(ST.Percentage, 1.0), UISize(ST.Percentage, 1.0)];
Axis2D g_layout_axis_default = A2D.X; Axis2D g_layout_axis_default = A2D.X;
Vec2 g_padding_default = Vec2(0.0); 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_target_default = Vec2(0.0);
Vec2 g_scroll_clamp_default = Vec2(0.0); Vec2 g_scroll_clamp_default = Vec2(0.0);
Vec2 g_view_offset_default = Vec2(0.0); Vec2 g_view_offset_default = Vec2(0.0);
@ -131,6 +133,7 @@ enum UIFlags
alias UIF = UIFlags; alias UIF = UIFlags;
const UIFlags AUTO_FLAGS = UIF.ResizeAdjacent; const UIFlags AUTO_FLAGS = UIF.ResizeAdjacent;
const UIFlags DRAW_FLAGS = UIF.DrawBackground|UIF.DrawBorder|UIF.DrawText|UIF.PortalView;
enum UISignal enum UISignal
{ {
@ -233,7 +236,8 @@ struct UICtx
PlatformWindow* window; PlatformWindow* window;
Renderer rd; Renderer rd;
Descriptor font_atlas; Descriptor[FONT_SIZES] font_descs;
Descriptor default_tex;
Descriptor sampler; Descriptor sampler;
Pipeline pipeline; Pipeline pipeline;
DescSetLayout desc_set_layout; DescSetLayout desc_set_layout;
@ -242,16 +246,13 @@ struct UICtx
Mat4 projection; Mat4 projection;
Vec2 res; Vec2 res;
u8[] font_data;
FontFace font; FontFace font;
FontAtlasBuf atlas_buf; FontGlyphs[FONT_SIZES] glyph_sets;
f32 char_width; u32 glyph_sets_used;
f32 char_height;
UIBuffer[FRAME_OVERLAP] buffers; UIBuffer[FRAME_OVERLAP] buffers;
u32 tab_width; u32 tab_width;
f32 text_size;
Vec4[TS.max][UISH.max] syntax_colors; Vec4[TS.max][UISH.max] syntax_colors;
UIItem* window_root; UIItem* window_root;
@ -278,10 +279,18 @@ struct UICtx
mixin UICtxParameter!(Vec4, "corner_radius"); mixin UICtxParameter!(Vec4, "corner_radius");
mixin UICtxParameter!(f32, "border_thickness"); mixin UICtxParameter!(f32, "border_thickness");
mixin UICtxParameter!(f32, "edge_softness"); mixin UICtxParameter!(f32, "edge_softness");
mixin UICtxParameter!(f32, "text_scale"); mixin UICtxParameter!(u32, "text_size");
mixin UICtxParameter!(UISyntaxHighlight, "syntax_highlight"); mixin UICtxParameter!(UISyntaxHighlight, "syntax_highlight");
debug bool dbg; debug bool dbg;
debug u64 item_count;
}
struct FontGlyphs
{
u32 size;
u32 index;
FontAtlasBuf abuf;
} }
struct Stack(T) struct Stack(T)
@ -337,6 +346,7 @@ struct UIItem
UIItem* next, prev, first, last; // parent in mixin UIItem* next, prev, first, last; // parent in mixin
UIItem* transient_next; UIItem* transient_next;
UIItem* last_relative_item;
Rect rect; Rect rect;
Vec2 size; Vec2 size;
@ -412,6 +422,7 @@ struct Vertex
f32 edge_softness; f32 edge_softness;
f32 raised; f32 raised;
u32 texture; u32 texture;
u32 atlas_index;
} }
union Rect union Rect
@ -462,26 +473,23 @@ InitUICtx(PlatformWindow* window)
UIBuffer[FRAME_OVERLAP] buffers; UIBuffer[FRAME_OVERLAP] buffers;
UICtx ctx = { UICtx* ctx = &g_ui_ctx;
items: CreateHashTable!(UIHash, UIItem*)(12),
free_items: Alloc!(UIItem)(&arena), ctx.items = CreateHashTable!(UIHash, UIItem*)(12);
arena: arena, ctx.free_items = Alloc!(UIItem)(&arena);
temp_arena: CreateArena(MB(1)), ctx.arena = arena;
drag_item: g_UI_NIL, ctx.temp_arena = CreateArena(MB(1));
transient_items: g_UI_NIL, ctx.drag_item = g_UI_NIL;
font: OpenFont(cast(u8[])FONT_BYTES), ctx.transient_items = g_UI_NIL;
font_data: cast(u8[])FONT_BYTES, ctx.font = OpenFont(cast(u8[])FONT_BYTES);
text_size: 16.0, ctx.tab_width = 2;
tab_width: 2, ctx.syntax_colors = [DEFAULT_COLORS, SYNTAX_COLORS];
syntax_colors: [DEFAULT_COLORS, SYNTAX_COLORS],
}; assert(ctx.font);
UIItem* fi = ctx.free_items; UIItem* fi = ctx.free_items;
fi.transient_next = fi.first = fi.last = fi.next = fi.prev = fi.parent = g_UI_NIL; 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) version(ENABLE_RENDERER)
{ {
ctx.rd = InitRenderer(handles, MB(16), MB(8)); ctx.rd = InitRenderer(handles, MB(16), MB(8));
@ -496,7 +504,7 @@ InitUICtx(PlatformWindow* window)
} }
DescLayoutBinding[2] layout_bindings = [ 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 }, { 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.desc_set = AllocDescSet(&ctx.rd, ctx.desc_set_layout);
ctx.pipeline_layout = CreatePipelineLayout(&ctx.rd, ctx.desc_set_layout, Mat4.sizeof); 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: 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: 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 }, { 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: 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: 11, format: FMT.R_F32, offset: Vertex.raised.offsetof },
{ binding: 0, location: 12, format: FMT.R_U32, offset: Vertex.texture.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 = { GfxPipelineInfo ui_info = {
@ -535,16 +544,69 @@ InitUICtx(PlatformWindow* window)
alpha_op: BO.Add, 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); 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); 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]); 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 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; 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) if(item.flags & UIF.TextWrap)
{ {
f32 width = item.size_info[A2D.X].type == ST.TextSize ? item.parent.size.x : item.size.x; 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; 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;
u64 lines = cast(u64)(ceil(text_width/width));
item.text_lines = ScratchAlloc!(string)(lines); 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) if(item.syntax_tokens.length)
{ {
item.token_lines = ScratchAlloc!(u8[])(lines); item.token_lines = ScratchAlloc!(u8[])(lines);
} for(u64 i = 0; i < lines; i += 1)
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)
{ {
Glyph* g = str[i] < glyphs.length ? glyphs.ptr+str[i] : glyphs.ptr+0; u64 end = str.length-start <= ch_per_line ? str.length : start+ch_per_line;
item.token_lines[i] = item.syntax_tokens[start .. end];
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];
} }
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 else
{ {
InitSingleLine:
item.text_lines = ScratchAlloc!(string)(1); item.text_lines = ScratchAlloc!(string)(1);
item.text_lines[0] = str; item.text_lines[0] = str;
item.max_text_width = CalcTextWidth(item.text_lines[0]);
if(item.syntax_tokens.length) if(item.syntax_tokens.length)
{ {
@ -816,6 +844,8 @@ BeginUI(Inputs* inputs)
UICtx* ctx = GetCtx(); UICtx* ctx = GetCtx();
Arena* a = &ctx.temp_arena; Arena* a = &ctx.temp_arena;
debug ctx.item_count = 0;
Reset(a); Reset(a);
// Convert Inputs // 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); memset(item, 0, UIItem.sizeof);
DLLPush(ctx.free_items, item, g_UI_NIL); DLLPush(ctx.free_items, item, g_UI_NIL);
item = next;
} }
ctx.frame += 1;
ctx.transient_items = g_UI_NIL;
// Ctx state // Ctx state
ctx.transient_items = g_UI_NIL;
ctx.frame += 1;
ctx.f_idx = ctx.frame%FRAME_OVERLAP; ctx.f_idx = ctx.frame%FRAME_OVERLAP;
ctx.inputs = inputs; ctx.inputs = inputs;
ctx.char_width = GlyphWidth(&ctx.atlas_buf.atlas.glyphs[' ']);
ResetStacks(ctx); ResetStacks(ctx);
@ -992,7 +1023,7 @@ EndUI()
} }
else 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; excess -= reduced;
c.size.v[axis] -= reduced; c.size.v[axis] -= reduced;
if(excess <= 0.0009) if(excess <= 0.0009) break;
{
break;
}
} }
if(excess > 0.0009) if(excess > 0.0009)
@ -1093,10 +1121,7 @@ EndUI()
excess -= reduced; excess -= reduced;
c.size.v[axis] -= reduced; c.size.v[axis] -= reduced;
if(excess < 0.0009) if(excess < 0.0009) break;
{
break;
}
} }
} }
} }
@ -1118,72 +1143,42 @@ EndUI()
// Calculate final sizes // Calculate final sizes
{ {
f32 pos = 0.0; 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 fixed = cast(bool)(item.flags & UIF.FixedPosition);
UIItem* parent = item.parent;
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;
if(fixed) if(fixed)
{ {
item.rect.p0.v[axis] = item.fixed_pos.v[axis] + item.parent.rect.p0.v[axis]; 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.rect.p1.v[axis] = item.rect.p0.v[axis] + item.size.v[axis] - item.padding.v[axis];
}
next_pos = pos; 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 else
{ {
item.rect.p0.v[axis] = pos + item.pos_offset.v[axis]; UIItem* prev = parent.last_relative_item;
item.rect.p1.v[axis] = item.rect.p0.v[axis] + item.size.v[axis];
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.p0.v[axis]));
assert(!isNaN(item.rect.p1.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) RenderItem(UICtx* ctx, UIItem* item)
{ {
if(item.rendered) return; 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. // Doesn't really support nesting scissors, will maybe change this later.
bool scissor_x = cast(bool)(item.flags & UIF.PortalViewX); bool scissor_x = cast(bool)(item.flags & UIF.PortalViewX);
@ -1245,18 +1244,18 @@ RenderItem(UICtx* ctx, UIItem* item)
{ {
DrawUI(ctx); DrawUI(ctx);
u32 x = cast(u32)(scissor_x ? floor(item.rect.p0.x) : 0); u32 x = cast(u32)(scissor_x ? floor(p0.x) : 0);
u32 y = cast(u32)(scissor_y ? floor(item.rect.p0.y) : 0); u32 y = cast(u32)(scissor_y ? floor(p0.y) : 0);
u32 w = cast(u32)(scissor_x ? floor(item.rect.p1.x) - x : ctx.res.x); u32 w = cast(u32)(scissor_x ? floor(p1.x) - x : ctx.res.x);
u32 h = cast(u32)(scissor_y ? floor(item.rect.p1.y) - y : ctx.res.y); u32 h = cast(u32)(scissor_y ? floor(p1.y) - y : ctx.res.y);
SetScissor(&ctx.rd, x, y, w, h); SetScissor(&ctx.rd, x, y, w, h);
} }
if(item.flags & UIF.DrawBackground) if(item.flags & UIF.DrawBackground)
{ {
Vertex* v = GetVertex(ctx); Vertex* v = GetVertex(ctx);
v.dst_start = item.rect.p0 + item.border_thickness; v.dst_start = p0 + item.border_thickness;
v.dst_end = item.rect.p1 - item.border_thickness; v.dst_end = p1 - item.border_thickness;
v.cols = item.bg_col; v.cols = item.bg_col;
v.corner_radius = item.flags & UIF.DrawBorder ? item.corner_radius*0.5 : item.corner_radius; v.corner_radius = item.flags & UIF.DrawBorder ? item.corner_radius*0.5 : item.corner_radius;
v.bounds = ItemBounds(item.parent); v.bounds = ItemBounds(item.parent);
@ -1267,8 +1266,8 @@ RenderItem(UICtx* ctx, UIItem* item)
if(item.flags & UIF.DrawBorder) if(item.flags & UIF.DrawBorder)
{ {
Vertex* v = GetVertex(ctx); Vertex* v = GetVertex(ctx);
v.dst_start = item.rect.p0; v.dst_start = p0;
v.dst_end = item.rect.p1; v.dst_end = p1;
v.cols = item.border_col; v.cols = item.border_col;
v.corner_radius = item.corner_radius; v.corner_radius = item.corner_radius;
v.border_thickness = clamp(item.border_thickness, 1.0, f32.max); v.border_thickness = clamp(item.border_thickness, 1.0, f32.max);
@ -1286,27 +1285,25 @@ RenderItem(UICtx* ctx, UIItem* item)
} }
else else
{ {
// The math around all this is fucked FontGlyphs* fg = GetFontGlyphs(item.text_size);
FontAtlas* atl = &ctx.atlas_buf.atlas; f32 y_pos = p0.y + fg.abuf.atlas.line_height;
f32 y_pos = item.rect.p0.y + item.padding.y;
Vec4[] syntax_cols = ctx.syntax_colors[item.syntax_highlight]; Vec4[] syntax_cols = ctx.syntax_colors[item.syntax_highlight];
foreach(i; 0 .. item.text_lines.length) foreach(i; 0 .. item.text_lines.length)
{ {
string str = item.text_lines[i]; string str = item.text_lines[i];
u8[] tks = item.token_lines.length ? item.token_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) : f32 x_pos = item.flags & UIF.RightAlignText ? p1.x - item.padding.x - CalcTextWidth(str, &fg.abuf) : p0.x + item.padding.x;
item.rect.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) if(tks.length)
{ {
foreach(j; 0 .. str.length) foreach(j; 0 .. str.length)
{ {
u8 ch = str[j]; u8 ch = str[j];
Glyph* g = ch < atl.glyphs.length ? atl.glyphs.ptr + ch : null; Glyph* g = ch < fg.abuf.atlas.glyphs.length ? fg.abuf.atlas.glyphs.ptr + ch : null;
DrawGlyph(item, g, &x_pos, y_pos, syntax_cols[tks[j]]); DrawGlyph(item, g, fg.index, &x_pos, y_pos, syntax_cols[tks[j]]);
} }
} }
else else
@ -1314,12 +1311,12 @@ RenderItem(UICtx* ctx, UIItem* item)
foreach(j; 0 .. str.length) foreach(j; 0 .. str.length)
{ {
u8 ch = str[j]; u8 ch = str[j];
Glyph* g = ch < atl.glyphs.length ? atl.glyphs.ptr + ch : null; Glyph* g = ch < fg.abuf.atlas.glyphs.length ? fg.abuf.atlas.glyphs.ptr + ch : null;
DrawGlyph(item, g, &x_pos, y_pos, item.text_col); 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 void
PushSizeInfoVec(int i)(SizeType type, f32 value, f32 strictness = 1.0, bool auto_pop = false) 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); 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 void
Push(string stack_str, T)(T value, bool auto_pop = false) Push(string stack_str, T)(T value, bool auto_pop = false)
{ {
import std.string : replace; import std.string : replace;
enum ids = StackIDs!(stack_str, "g_ui_ctx"); enum ids = StackIDs!(stack_str, "g_ui_ctx");
alias ST = typeof(mixin(ids.stack ~ ".top.value"));
auto stack = &mixin(ids.stack); auto stack = &mixin(ids.stack);
auto top = mixin(ids.stack_top_node); auto top = mixin(ids.stack_top_node);
Stack!(T)* node = stack.free; Stack!(ST)* node = stack.free;
if(node) if(node)
{ {
stack.free = node.next; stack.free = node.next;
} }
else else
{ {
node = Alloc!(Stack!(T))(&g_ui_ctx.temp_arena); node = Alloc!(Stack!(ST))(&g_ui_ctx.temp_arena);
} }
node.next = stack.top; node.next = stack.top;
@ -1762,11 +1798,10 @@ NewItem(UICtx* ctx)
{ {
UIItem* item = g_UI_NIL; UIItem* item = g_UI_NIL;
if(!Nil(ctx.free_items.first)) debug ctx.item_count += 1;
{
item = DLLPop(ctx.free_items, g_UI_NIL); item = DLLPop(ctx.free_items, g_UI_NIL);
} if(Nil(item))
else
{ {
item = Alloc!(UIItem)(&ctx.arena); 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]; Result!(UIItem*) res = ctx.items[key.hash];
if(res.ok) if(res.ok)
{ {
debug ctx.item_count += 1;
item = res.value; item = res.value;
} }
else else
@ -1823,7 +1859,7 @@ Get(T)(T k) if(is(T: UIKey) || StringType!T)
} }
f32 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)) static if(is(T == string))
{ {
@ -1834,42 +1870,63 @@ CalcTextWidth(T)(T text) if(is(T == string) || StringType!T)
string str = ConvToStr(text); string str = ConvToStr(text);
} }
u32 tab_width = g_ui_ctx.tab_width; static if(is(U: u32))
Glyph* space = g_ui_ctx.atlas_buf.atlas.glyphs.ptr + ' '; {
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; f32 width = 0.0;
for(u64 i = 0; i < str.length; i += 1) 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; return width;
} }
pragma(inline) f32 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 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) if(glyph)
{ {
UICtx* ctx = GetCtx(); UICtx* ctx = GetCtx();
Vertex* v = null; 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); *x_pos += advance * (GetCtx().tab_width - 1);
} }
else
f32 r = glyph.plane_right * item.text_scale; {
f32 l = glyph.plane_left * item.text_scale; f32 r = glyph.plane_right;
f32 t = glyph.plane_top * item.text_scale; f32 l = glyph.plane_left;
f32 b = glyph.plane_bottom * item.text_scale; f32 t = glyph.plane_top;
f32 b = glyph.plane_bottom;
GlyphBounds gb = { GlyphBounds gb = {
r: r, 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; 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_start = Vec2(*x_pos+gb.l, y_pos);
v.dst_end = Vec2(*x_pos+gb.w+gb.l, y_pos+gb.h); v.dst_end = Vec2(*x_pos+gb.w+gb.l, y_pos+gb.h);
v.cols = col; v.cols = col;
v.texture = true; v.texture = true;
v.bounds = ItemBounds(item); v.bounds = ItemBounds(item);
v.atlas_index = atlas_index;
if(glyph.ch != '\t' && glyph.ch != '\n') 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; 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; f32 height = end_y - y;
if(end_y > item.rect.p1.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.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; v.src_end.y -= (v.src_end.y - v.src_start.y) * cull_pct;
} }
*/
static foreach(axis; A2D.min .. A2D.max) static foreach(axis; A2D.min .. A2D.max)
{ {
@ -1932,6 +1992,7 @@ DrawGlyph(UIItem* item, Glyph* glyph, f32* x_pos, f32 y, Vec4 col = Vec4(1.0))
*x_pos += advance; *x_pos += advance;
} }
} }
}
pragma(inline) Vec2[2] pragma(inline) Vec2[2]
ItemBounds(UIItem* item) ItemBounds(UIItem* item)
@ -1952,30 +2013,23 @@ GetVertex(UICtx* ctx)
} }
static UISize[2] 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] 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)]; return [UISize(type, value, strictness), UISize(ST.Percentage, 1.0)];
} }
static UISize[2] 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)]; 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 pragma(inline) void
AddVertexCount(UICtx* ctx) AddVertexCount(UICtx* ctx)
{ {

View File

@ -31,84 +31,105 @@ struct Panel
Axis2D layout_axis; Axis2D layout_axis;
Editor* ed; Editor* ed;
u64 id; u64 id;
u32 text_size;
} }
void 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(); UICtx* ctx = GetCtx();
UIKey zero = ZeroKey(); UIKey zero = ZeroKey();
u64 ch_width = max_line.toChars().length; 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); enum UIPushInfo[] lc_params = [
PushViewOffsetY(view_offset, true); { "layout_axis", q{ A2D.Y } },
PushPaddingX(4.0, true); { "view_offset", q{ Vec2(0.0, view_offset) } },
PushSizeInfoX(ST.Pixels, lc_width, 1.0, true); { "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); UIItem* line_count = MakeItem(zero, UIF.DrawBorder|UIF.PortalViewY);
PushTextCol(Vec4(1.0)); mixin(PushScope!("text_col", q{ Vec4(1.0) } ));
PushSizeInfoY(ST.Pixels, TEXT_SIZE); mixin(PushScope!("size_info", q{ UISY(ST.Pixels, abuf.atlas.line_height) } ));
PushParent(line_count); mixin(PushScope!("parent", q{ line_count } ));
u64 end_line = lines+line_offset; u64 end_line = lines+line_offset;
for(u64 i = line_offset; i < end_line && i < max_line; i += 1) for(u64 i = line_offset; i < end_line && i < max_line; i += 1)
{ {
char[] buf = ScratchAlloc!(char)(ch_width); 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); MakeItem(zero, UIF.DrawText);
} }
Pop!("text_col", "size_info", "parent");
} }
void 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; Editor* ed = p.ed;
PushLayoutAxis(A2D.Y, true); PushLayoutAxis(A2D.Y, true);
f32 clamp_y = cast(f32)(ed.buf.line_count-lines)*TEXT_SIZE; f32 text_size = cast(f32)p.text_size;
f32 scroll_pos = cast(f32)(ed.line_offset)*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); enum UIPushInfo[] text_view_params = [
PushScrollClampY(0.0, clamp_y, true); { "layout_axis", q{ A2D.Y } },
PushSizeInfo(g_size_info_default, true); { "border_col", q{ Vec4(1.0) } },
PushBorderCol(Vec4(1.0), true); { "size_info", q{ UIS2() } },
PushScrollTargetY(scroll_pos, true); { "scroll_target", q{ Vec2(0.0, scroll_pos) } },
PushViewOffsetY(view_offset, true); { "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); editor = MakeItem(editor.key, UIF.DrawBorder|UIF.ScrollY|UIF.ClampY);
PushParent(editor); mixin(PushScope!("parent", q{ editor }));
UIKey zero = ZeroKey(); UIKey zero = ZeroKey();
// Cursor // Cursor
{ {
LineBuffer* lb = GetLine(&ed.buf, ed.cursor_pos.y); LineBuffer* lb = GetLine(&ed.buf, ed.cursor_pos.y);
f32 cursor_x = CalcTextWidth(lb.text[0 .. ed.cursor_pos.x]); u8 ch = lb.text.length > ed.cursor_pos.y ? lb.text[ed.cursor_pos.y] : 0;
f32 cursor_y = cast(f32)(ed.cursor_pos.y - line_offset)*TEXT_SIZE + view_offset; f32 line_h = abuf.atlas.line_height;
PushSizeInfo(MakeUISize(UISize(ST.Pixels, GetCtx().char_width), UISize(ST.Pixels, TEXT_SIZE)), true); f32 cursor_x = CalcTextWidth!(false)(lb.text[0 .. ed.cursor_pos.x], abuf);
PushFixedPos(Vec2(cursor_x, cursor_y), true); f32 cursor_y = cast(f32)(ed.cursor_pos.y - line_offset)*line_h;
PushBgCol(Vec4(1.0), true); 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); MakeItem(zero, UIF.DrawBackground|UIF.FixedPosition);
} }
u64 end_line = line_offset+lines; mixin(PushScope!("size_info", q{ UISY(ST.TextSize) } ));
PushSizeInfoY(ST.TextSize, 1.0); mixin(PushScope!("syntax_highlight", q{ UISH.D } ));
PushSyntaxHighlight(UISH.D);
scope(exit) Pop!("size_info", "parent", "syntax_highlight"); // NEED TO FIX LINES BEING OFFSET FOR WHATEVER REASON
u64 i = line_offset; 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)) 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); enum UIPushInfo[] lc_info = [
PushSyntaxTokens(cast(u8[])lb.style, true); { "display_string", q{ ConvToStr(lb.text) } },
{ "syntax_tokens", q{ cast(u8[])(lb.style) } },
];
mixin(PushOnce!(lc_info));
UIItem* line = MakeItem(zero, UIF.DrawText); UIItem* line = MakeItem(zero, UIF.DrawText);
} }
} }
@ -119,6 +140,7 @@ EditorView(Panel* p)
Editor* ed = p.ed; Editor* ed = p.ed;
UICtx* ctx = GetCtx(); UICtx* ctx = GetCtx();
UIKey zero = ZeroKey(); UIKey zero = ZeroKey();
FontAtlasBuf* abuf = GetFontAtlas(p.text_size);
if(CheckNil(g_NIL_ED, ed)) if(CheckNil(g_NIL_ED, ed))
{ {
@ -132,24 +154,26 @@ EditorView(Panel* p)
UIKey ed_key = MakeKey("###ed_%s", ed.editor_id); UIKey ed_key = MakeKey("###ed_%s", ed.editor_id);
UIItem* editor = Get(ed_key); UIItem* editor = Get(ed_key);
u64 frame_line_offset = ed.line_offset; f32 text_size = abuf.atlas.line_height;
f32 frame_view_offset = -(editor.scroll_offset.y%TEXT_SIZE);
PushBgCol(BG_COL, true); u64 frame_line_offset = ed.line_offset;
PushSizeInfoX(ST.Percentage, 1.0, 1.0, true); 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); UIItem* container = MakeItem(zero, UIF.DrawBackground);
PushParent(container); mixin(PushScope!("parent", q{ container } ));
PushBorderCol(Vec4(1.0)); mixin(PushScope!("border_col", q{ Vec4(1.0) } ));
scope(exit) Pop!("parent", "border_col");
u64 view_lines; u64 view_lines;
if(editor.size.y > 0.0) 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; const u64 SCROLL_BUFFER = 4;
u64 start = ed.line_offset; u64 start = ed.line_offset;
@ -166,9 +190,9 @@ EditorView(Panel* p)
} }
} }
u64 start = cast(u64)floor(editor.scroll_offset.y/TEXT_SIZE); u64 start = cast(u64)floor(editor.scroll_offset.y/text_size);
LineCounterView(ed.buf.line_count, view_lines, start, frame_view_offset); LineCounterView(abuf, ed.buf.line_count, view_lines, start, frame_view_offset);
EditorTextView(editor, p, 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) 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); ed.cursor_pos = VecPos(&ed.buf);
} }
ResetBuffer(&ed.buf);
} }
void void
@ -208,348 +234,64 @@ CommandPalette(CmdPalette* cmd)
{ {
UICtx* ctx = GetCtx(); UICtx* ctx = GetCtx();
Vec2 ext = GetExtent(); Vec2 ext = GetExtent();
FontAtlasBuf* abuf = &g_ui_ctx.glyph_sets[0].abuf;
f32 w = ext.x*0.4; f32 w = ext.x*0.4;
f32 h = ext.y*0.7; f32 h = ext.y*0.7;
PushFixedPos(Vec2(ext.x*0.3, ext.y*0.1), true); enum UIPushInfo[] cmd_params = [
PushBgCol(BG_COL, true); { "layout_axis", q{ A2D.Y } },
PushBorderCol(HL_BORDER_COL, true); { "fixed_pos", q{ Vec2(ext.x*0.3, ext.y*0.1) } },
PushBorderThickness(2.0f, true); { "bg_col", q{ BG_COL } },
PushCornerRadius(8.0f, true); { "border_col", q{ HL_BORDER_COL } },
PushViewOffset(Vec2(-2.0f), true); { "border_thickness", q{ 4.0 } },
PushLayoutAxis(A2D.Y, true); { "corner_radius", q{ Vec4(8.0) } },
PushSizeInfo(MakeUISize(UISize(ST.Pixels, w), UISize(ST.Pixels, h))); { "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); UIItem* cmd_item = MakeItem("###cmd_palette", UIF.Window|UIF.FixedPosition|UIF.DrawBackground|UIF.DrawBorder);
f32 padding_y = 8.0; f32 padding_y = 4.0;
PushParent(cmd_item);
PushPadding(Vec2(4.0, padding_y), true);
scope(exit) Pop!("parent", "padding")();
PushBgCol(HL_BG_COL, true); mixin(PushScope!("parent", q{ cmd_item } ));
PushBorderCol(HL_BORDER_COL); mixin(PushScope!("padding", q{ Vec2(4.0, padding_y) }));
PushBorderThickness(2.0f, true);
PushCornerRadius(Vec4(8.0, 8.0, 0.0, 0.0), true); enum UIPushInfo[] cmd_input_params = [
PushSizeInfo(MakeUISize(UISize(ST.Pixels, w-padding_y), UISize(ST.TextSize, 1.0)), true); { "bg_col", q{ HL_BG_COL } },
PushDisplayString(ConvToStr(cmd.buffer[0 .. cmd.icount]), true); { "size_info", q{ UISY(ST.TextSize) } },
{ "display_string", q{ ConvToStr(cmd.buffer[0 .. cmd.icount]) } },
];
mixin(PushOnce!(cmd_input_params));
UIKey zero = ZeroKey(); UIKey zero = ZeroKey();
MakeItem(zero, UIF.DrawBorder|UIF.DrawBackground|UIF.DrawText|UIF.Overflow); MakeItem(zero, UIF.DrawBorder|UIF.DrawBackground|UIF.DrawText|UIF.Overflow);
PushPadding(Vec2(4.0)); enum UIPushInfo[] sep_params = [
PushSizeInfoY(ST.TextSize, 1.0); { "padding", q{ Vec2(0.0) } },
scope(exit) Pop!("size_info", "padding")(); { "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[2] opt_cols = [
Vec4(0.22, 0.22, 0.22, 1.0), Vec4(0.22, 0.22, 0.22, 1.0),
Vec4(0.35, 0.35, 0.35, 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); PushDisplayString(ConvToStr(cmd.opt_strs[i]), true);
PushBgCol(cmd.selected == i ? HL_BG_COL : opt_cols[i%2], true); PushBgCol(cmd.selected == i ? HL_BG_COL : opt_cols[i%2], true);
MakeItem(zero, UIF.DrawBackground|UIF.DrawText); 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" #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; vec4 color;
vec2 uv; vec2 uv;
vec2 dst_pos; vec2 dst_pos;
@ -20,7 +25,6 @@ layout (location = 1) in struct FragDataIn {
float border_thickness; float border_thickness;
} FD; } FD;
layout (location = 0) out vec4 FragColor; layout (location = 0) out vec4 FragColor;
float RectSDF(vec2 pos, vec2 half_size, float radius) float RectSDF(vec2 pos, vec2 half_size, float radius)
@ -75,7 +79,7 @@ void main()
vec4 gamma = vec4(1.0/1.4); vec4 gamma = vec4(1.0/1.4);
vec4 tex_color = vec4(1.0); vec4 tex_color = vec4(1.0);
if(in_has_texture != 0) if(FDF.texture != 0)
{ {
tex_color = texture(sampler2D(SpriteAtlas, SamplerNearest), FD.uv); tex_color = texture(sampler2D(SpriteAtlas, SamplerNearest), FD.uv);
} }

View File

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