clean up, editor position/offset/etc within a buffer now tracked per editor per buffer

This commit is contained in:
Matthew 2026-06-26 08:32:22 +10:00
parent 117b5eb51e
commit 5261118dd0
6 changed files with 856 additions and 931 deletions

View File

@ -16,7 +16,7 @@
"versions": ["VULKAN_DEBUG", "ENABLE_RENDERER", "DLIB"],
"preGenerateCommands-linux": ["./build.sh"],
"preGenerateCommands-windows": [],
"dflags": ["-P-I/usr/include/freetype2", "-Jbuild", "-Jassets", "-P-DBUILD_VULKAN"],
"dflags": ["-P-I/usr/include/freetype2", "-Jbuild", "-Jassets", "-P-DBUILD_VULKAN", "-verrors=100"],
"dflags-ldc2": ["-link-debuglib", "--Xcc=-DBUILD_VULKAN"],
"dflags-dmd": ["-Xcc=-DBUILD_VULKAN"]
},

@ -1 +1 @@
Subproject commit 53eb211a1e227d1cf3d8036a0e8ecfb79f514eed
Subproject commit f3d1f551ccfa37de73d626479252bada0fc17cb2

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,6 @@
import dlib;
import dlib.util : Str;
import dlib.util : Str;
import dlib.alloc : Reset;
import vulkan;
import std.format : sformat;
@ -16,8 +17,12 @@ import std.file;
import std.string;
import core.stdc.stdio;
const u64 ARRAY_COUNT_INCREMENT = 32;
f32 g_delta = 0.0;
alias UIHashTable = HashTable!(UIHash, UIItem*);
struct RenderCtx
{
PlatformWindow* window;
@ -25,7 +30,6 @@ struct RenderCtx
Descriptor default_tex;
Descriptor sampler;
Pipeline pipeline;
Pipeline slug_pipeline;
DescSetLayout desc_set_layout;
DescSet[FO] desc_sets;
PipelineLayout pipeline_layout;
@ -34,70 +38,73 @@ struct RenderCtx
struct Ctx
{
RenderCtx rd_ctx;
RenderCtx rd_ctx;
HashTable!(UIHash, UIItem*) items;
UIItem* free_items;
UIItem* last_item;
UIHashTable items;
UIItem* free_items;
UIItem* last_item;
Arena arena;
Arena temp_arena;
Arena[FO] str_arenas;
Arena arena;
Arena temp_arena;
Arena[FO] str_arenas;
Inputs* inputs;
u64 frame;
u64 f_idx;
Inputs* inputs;
u64 frame;
u64 frame_index;
Notification* active_notifications;
Notification* free_notifications;
Notification* active_notifications;
Notification* free_notifications;
LinkedList!(UIInput) events;
IVec2 mouse_pos;
LinkedList!(UIInput) events;
IVec2 mouse_pos;
SlugFont slug_font;
FontFace font;
FontSet[FS] font_sets;
u32 font_sets_used;
bool[FO] fonts_to_load;
bool prev_has_tex;
u32 prev_atlas_index;
FontFace font;
FontSet[FS] font_sets;
u32 font_sets_used;
bool[FO] fonts_to_load;
bool prev_has_tex;
u32 prev_atlas_index;
UIBuffer[FO] buffers;
SlugBuffer[FO] slug_buffers;
MappedBuffer!(SlugVertex)[FO] slug_vertex_buffers;
MappedBuffer!(u32)[FO] slug_index_buffers;
UIBuffer[FO] buffers;
u32 tab_width;
Vec4[TS.max][UISH.max] syntax_colors;
u32 tab_width;
Vec4[TS.max][UISH.max] syntax_colors;
UIKey drag_key;
UIKey hover_key;
UIKey focus_key;
UIKey drag_key;
UIKey hover_key;
UIKey focus_key;
UIPanel* base_panel;
UIPanel* focused_panel;
UIPanel* free_panels;
UIPanel* base_panel;
UIPanel* focused_panel;
UIPanel* free_panels;
u64 last_hover_frame;
u64 last_hover_frame;
f32 pos_rate;
f32 scroll_rate;
f32 animation_rate;
f32 fade_rate;
f32 pos_rate;
f32 scroll_rate;
f32 animation_rate;
f32 fade_rate;
ConfigValue[CO.max] config_values;
ConfigValue[CO.max] config_values;
EditState state;
u8[128] input_buf;
u32 icount;
u64 editor_id_incr;
Timer timer;
CmdPalette cmd;
string[] file_names;
u64 panel_id;
EditState state;
u8[128] input_buf;
u32 icount;
u64 editor_id_incr;
Timer timer;
CmdPalette cmd;
string[] file_names;
u64 panel_id;
Editor[] editors;
u64 editor_count;
Editor* free_editors;
FlatBuffer[] text_buffers;
u64 text_buffer_count;
FlatBuffer* free_text_buffers;
debug bool dbg;
@property ref DescSet desc_set() => this.rd_ctx.desc_sets[this.frame_index];
debug bool dbg;
alias rd_ctx this;
}
@ -131,21 +138,17 @@ struct CmdPalette
struct Editor
{
Arena arena;
u64 editor_id;
FlatBuffer buf;
Tokenizer tk;
alias BufferViewTable = HashTable!(string, BufferViewInfo);
I64Vec2 cursor_pos;
Vec2 select_start;
Vec2 select_end;
i64 line_offset;
i64 line_height;
BufferViewInfo view_info_default;
FlatBuffer* buffer;
Editor* list_next;
BufferViewInfo* view_info;
u64 editor_id;
i64 line_height;
BufferViewTable view_info_table;
Editor* free_next;
alias buf this;
alias buffer this;
}
struct ChangeStacks
@ -165,7 +168,7 @@ struct EditorChange
EditorChange* next;
}
alias CmdFn = bool function(Ctx* ctx); // return true when completed
alias CmdFn = bool function(ref Ctx ctx); // return true when completed
struct Command
{
@ -235,17 +238,19 @@ enum EditState
SetPanelFocus, // if moving left/right move up parent tree until one is found with a2d.x, same thing for up/down a2d.y
} alias ES = EditState;
__gshared Ctx g_ctx;
__gshared const Editor g_nil_ed;
__gshared Editor* g_NIL_ED;
__gshared const UIItem g_ui_nil_item;
__gshared UIItem* g_UI_NIL;
__gshared const UIInput g_ui_nil_input;
__gshared UIInput* g_UI_NIL_INPUT;
__gshared const UIPanel g_nil_panel;
__gshared UIPanel* g_NIL_PANEL;
__gshared const Notification g_nil_notif;
__gshared Notification* g_NIL_NOTIF;
__gshared Ctx g_ctx;
__gshared Editor g_nil_ed;
__gshared UIItem g_ui_nil_item;
__gshared UIInput g_ui_nil_input;
__gshared UIPanel g_nil_panel;
__gshared Notification g_nil_notif;
__gshared Editor* g_NIL_ED = &g_nil_ed;
__gshared UIItem* g_UI_NIL_ITEM = &g_ui_nil_item;
__gshared UIInput* g_UI_NIL_INPUT = &g_ui_nil_input;
__gshared UIPanel* g_NIL_PANEL = &g_nil_panel;
__gshared Notification* g_NIL_NOTIF = &g_nil_notif;
const Command NO_CMD = {
name: [],
@ -402,10 +407,24 @@ Command[21] CMD_LIST = [
},
];
ref Ctx GetCtx() => g_ctx;
bool
ClosePanelCmd(Ctx* ctx)
ClosePanelCmd(ref Ctx ctx)
{
Clear(ctx.focused_panel.ed);
Editor* editor = ctx.focused_panel.ed;
ctx.focused_panel.ed = null;
if(editor.file_name.length == 0 && editor.length == 0)
{
editor.buffer.list_next = ctx.free_text_buffers;
ctx.free_text_buffers = editor.buffer;
}
FreeHashTable(editor.view_info_table);
memset(editor, 0, Editor.sizeof);
editor.list_next = ctx.free_editors;
ctx.free_editors = editor;
if(ctx.focused_panel != ctx.base_panel)
{
DLLRemove(ctx.focused_panel.parent, ctx.focused_panel, g_NIL_PANEL);
@ -456,38 +475,27 @@ ClosePanelCmd(Ctx* ctx)
}
bool
SplitVertically(Ctx* ctx)
SplitVertically(ref Ctx ctx)
{
AddPanel(ctx.focused_panel, A2D.X);
return true;
}
bool
SplitHorizontally(Ctx* ctx)
SplitHorizontally(ref Ctx ctx)
{
AddPanel(ctx.focused_panel, A2D.Y);
return true;
}
bool
NotImplementedCallback(Ctx* ctx)
NotImplementedCallback(ref Ctx ctx)
{
Logf("Not yet implemented!");
return true;
}
bool
CmdModeActive()
{
return g_ctx.state == ES.CmdPalette;
}
bool
Active(EditState state)
{
return g_ctx.state == state;
}
bool Active(EditState state) => g_ctx.state == state;
UIPanel*
MakePanel(UIPanel* parent = g_NIL_PANEL)
@ -511,7 +519,7 @@ Cycle(Inputs* inputs)
BeginUI(inputs);
Ctx* ctx = GetCtx();
ref Ctx ctx = GetCtx();
Panel(ctx, ctx.base_panel);
@ -538,12 +546,7 @@ Focus(UIPanel* panel)
void
InitCtx(PlatformWindow* window)
{
Ctx* ctx = &g_ctx;
g_NIL_ED = cast(Editor*)&g_nil_ed;
g_UI_NIL = cast(UIItem*)&g_ui_nil_item;
g_UI_NIL_INPUT = cast(UIInput*)&g_ui_nil_input;
g_NIL_PANEL = cast(UIPanel*)&g_nil_panel;
ref Ctx ctx = GetCtx();
ctx.window = window;
ctx.arena = CreateArena(MB(2));
@ -552,6 +555,8 @@ InitCtx(PlatformWindow* window)
ctx.cmd.exec_cmd_arena = CreateArena(MB(1));
ctx.timer = CreateTimer();
ctx.free_panels = g_NIL_PANEL;
ctx.text_buffers = Alloc!(FlatBuffer)(ARRAY_COUNT_INCREMENT);
ctx.editors = Alloc!(Editor)(ARRAY_COUNT_INCREMENT);
InitUI(ctx);
@ -569,7 +574,7 @@ InitCtx(PlatformWindow* window)
if(indexOf(e.name, ".git") != -1 || e.isDir) continue;
u64 start = indexOf(e.name, "./") == 0 ? 2 : 0;
count += 1;
count += 1;
}
ctx.file_names = Alloc!(string)(&ctx.arena, count);
@ -579,7 +584,7 @@ InitCtx(PlatformWindow* window)
{
if(indexOf(e.name, ".git") != -1 || e.isDir) continue;
u64 start = indexOf(e.name, "./") == 0 ? 2 : 0;
u64 start = indexOf(e.name, "./") == 0 ? 2 : 0;
ctx.file_names[count++] = Alloc(&ctx.arena, e.name);
}
}
@ -590,86 +595,36 @@ InitCtx(PlatformWindow* window)
}
}
Ctx*
GetCtx()
{
return &g_ctx;
}
char[]
ToAbsolutePath(string file_name)
{
import core.stdc.string : strlen;
char[1024] name_buf = '\0';
char[1024] wd_buf = '\0';
version(linux)
{
import core.sys.posix.unistd;
getcwd(wd_buf.ptr, wd_buf.length);
}
version(Windows)
{
import core.sys.windows.direct;
_getcwd(wd_buf.ptr, wd_buf.length);
}
char[] wd = wd_buf[0 .. strlen(wd_buf.ptr)];
version(linux)
{
if(file_name[0] != '/')
{
name_buf.sformat("%s/%s", wd, cast(char[])file_name);
}
else
{
name_buf.sformat("%s", cast(char[])file_name);
}
}
version(Windows)
{
name_buf.sformat("%s/%s", wd, cast(char[])file_name);
}
char[] path_buf = ScratchAlloc!(char)(strlen(name_buf.ptr)+1);
path_buf[0 .. $] = name_buf[0 .. path_buf.length];
return path_buf;
}
void
SaveFile(Editor* ed, string file_name)
{
import core.stdc.stdio;
file_name = file_name.length == 0 ? Str(ed.buf.file_name) : file_name;
file_name = file_name.length == 0 ? Str(ed.file_name) : file_name;
if(file_name.length > 0)
{
char[] file_path = ToAbsolutePath(file_name);
string file_path = AbsolutePath(file_name);
auto f = fopen(cast(char*)file_path.ptr, "wb");
if(f != null)
{
u64 tab_count;
for(u64 i = 0; i < ed.buf.length; i += 1)
for(u64 i = 0; i < ed.length; i += 1)
{
if(ed.buf.data[i] == '\t')
if(ed.data[i] == '\t')
{
tab_count += 1;
}
}
u64 tab_width = GetCtx().tab_width;
u64 buf_size = ed.buf.length + ((tab_width-1) * tab_count);
u64 buf_size = ed.length + ((tab_width-1) * tab_count);
u8[] temp_buf = ScratchAlloc!(u8)(buf_size);
u64 buf_pos;
for(u64 i = 0; i < ed.buf.length; i += 1)
for(u64 i = 0; i < ed.length; i += 1)
{
if(ed.buf.data[i] == '\t')
if(ed.data[i] == '\t')
{
for(u64 j = 0; j < tab_width; j += 1)
{
@ -678,7 +633,7 @@ SaveFile(Editor* ed, string file_name)
}
else
{
temp_buf[buf_pos++] = ed.buf.data[i];
temp_buf[buf_pos++] = ed.data[i];
}
}
@ -689,40 +644,6 @@ SaveFile(Editor* ed, string file_name)
}
}
void
OpenFile(Editor* ed, string file_name)
{
import core.stdc.stdio;
import std.file;
import std.conv;
if(file_name.length > 0)
{
char[] file_path = ToAbsolutePath(file_name);
auto f = fopen(file_path.ptr, "rb");
if(f != null)
{
fseek(f, 0, SEEK_END);
i64 len = ftell(f);
fseek(f, 0, SEEK_SET);
if(len > 0)
{
u8[] buf = ScratchAlloc!(u8)(len);
fread(buf.ptr, u8.sizeof, len, f);
Change(&ed.buf, buf, file_name);
ed.buf.file_name = file_name;
}
fclose(f);
}
else
{
perror("[Error] Unable to open file");
}
}
}
string
Str(TextInputBuffer text_buffer)
{
@ -732,20 +653,48 @@ Str(TextInputBuffer text_buffer)
Editor*
CreateEditor()
{
Editor* ed = Alloc!(Editor)(&g_ctx.arena);
ed.arena = CreateArena(MB(4));
ed.buf = CreateFlatBuffer([], []);
ed.editor_id = g_ctx.editor_id_incr++;
ref Ctx ctx = GetCtx();
if(ctx.editor_count == ctx.editors.length)
{
ctx.editors = Realloc(ctx.editors, ctx.editors.length+ARRAY_COUNT_INCREMENT);
}
Editor* editor = ctx.free_editors;
if(editor)
{
ctx.free_editors = editor.list_next;
}
else
{
editor = &ctx.editors[ctx.editor_count++];
}
editor.view_info_table = CreateHashTable!(string, BufferViewInfo)(4);
ResetViewInfo(&editor.view_info_default);
editor.view_info = &editor.view_info_default;
editor.buffer = ctx.free_text_buffers;
if(editor.buffer)
{
ctx.free_text_buffers = editor.buffer.list_next;
}
else
{
editor.buffer = CreateFlatBuffer(ctx);
}
editor.editor_id = g_ctx.editor_id_incr++;
return ed;
return editor;
}
pragma(inline) void
InsertInputToBuf(Ctx* ctx, Editor* ed)
InsertInputToBuf(ref Ctx ctx, Editor* ed)
{
if(ctx.icount > 0)
{
Insert(&ed.buf, ctx.input_buf, ctx.icount);
Insert(ed, ctx.input_buf, ctx.icount);
ctx.icount = 0;
}
}
@ -753,12 +702,12 @@ InsertInputToBuf(Ctx* ctx, Editor* ed)
void
ResetCtx(Editor* ed)
{
InsertInputToBuf(&g_ctx, ed);
InsertInputToBuf(GetCtx(), ed);
ResetCtx();
}
void
ResetCmd(Ctx* ctx)
ResetCmd(ref Ctx ctx)
{
ctx.cmd.text_input.length = 0;
ctx.cmd.selected = 0;
@ -836,9 +785,8 @@ bool
HandleInputs(UIPanel* p, LinkedList!(UIInput)* inputs, bool hovered, bool focused)
{
Editor* ed = p.ed;
Ctx* ctx = &g_ctx;
FlatBuffer* fb = &ed.buf;
u64 initial_len = p.ed.buf.length;
ref Ctx ctx = GetCtx();
u64 initial_len = p.ed.length;
u8[] cb_text;
for(auto node = inputs.first; node != null; node = node.next)
@ -896,17 +844,17 @@ HandleInputs(UIPanel* p, LinkedList!(UIInput)* inputs, bool hovered, bool focuse
{
case a, i:
{
if(key == a && Shift(md) && fb)
if(key == a && Shift(md))
{
MoveToEOL(fb);
MoveToEOL(ed);
}
else if(key == a)
{
Move(fb, Right, MD.None);
Move(ed, Right, MD.None);
}
else if(key == i && Shift(md))
{
MoveToSOL(fb);
MoveToSOL(ed);
}
ctx.state = ES.InputMode;
@ -916,7 +864,7 @@ HandleInputs(UIPanel* p, LinkedList!(UIInput)* inputs, bool hovered, bool focuse
{
if(Shift(node.md))
{
ctx.state = ES.CmdPalette;
ctx.state = ES.CmdPalette;
ResetCmd(ctx);
taken = true;
@ -930,11 +878,11 @@ HandleInputs(UIPanel* p, LinkedList!(UIInput)* inputs, bool hovered, bool focuse
}
if(Shift(md))
{
ToggleSelection(fb, SM.Line);
ToggleSelection(ed, SM.Line);
}
else
{
ToggleSelection(fb, SM.Normal);
ToggleSelection(ed, SM.Normal);
}
} break;
case c:
@ -984,22 +932,22 @@ HandleInputs(UIPanel* p, LinkedList!(UIInput)* inputs, bool hovered, bool focuse
if(cb_text.length > 0)
{
Insert(fb, cb_text, cb_text.length);
Insert(ed, cb_text, cb_text.length);
}
return initial_len != p.ed.buf.length;
return initial_len != p.ed.length;
}
void
SetOffset(Editor* ed, i64 adjust)
{
i64 max_offset = ed.buf.line_count-ed.line_height;
i64 max_offset = ed.line_count-ed.line_height;
if(max_offset < 0)
{
max_offset = 0;
}
ed.line_offset = clamp(ed.line_offset+adjust, 0, max_offset);
ed.view_info.line_offset = clamp(ed.view_info.line_offset+adjust, 0, max_offset);
}
bool
@ -1009,15 +957,15 @@ MoveCursor(Editor* ed, UIInput* ev)
switch(ev.key) with(Input)
{
case Up, Down, Left, Right: taken = Move(&ed.buf, ev.key, ev.md); break;
case Up, Down, Left, Right: taken = Move(ed, ev.key, ev.md); break;
default: break;
}
if(taken)
{
I64Vec2 pos = VecPos(&ed.buf);
i64 min = ed.line_offset+2;
i64 max = clamp(ed.line_offset+ed.line_height-3, 0, ed.buf.line_count);
I64Vec2 pos = VecPos(ed);
i64 min = ed.view_info.line_offset+2;
i64 max = clamp(ed.view_info.line_offset+ed.line_height-3, 0, ed.line_count);
if(pos.y > max)
{
SetOffset(ed, pos.y-max);
@ -1032,7 +980,7 @@ MoveCursor(Editor* ed, UIInput* ev)
}
pragma(inline) void
InsertChar(Ctx* ctx, Input input, bool modified)
InsertChar(ref Ctx ctx, Input input, bool modified)
{
ctx.input_buf[ctx.icount++] = InputToChar(input, modified);
}
@ -1055,7 +1003,7 @@ CharCases()
}
bool
HandleInputMode(Ctx* ctx, UIPanel* p, UIInput* ev)
HandleInputMode(ref Ctx ctx, UIPanel* p, UIInput* ev)
{
bool taken;
@ -1064,7 +1012,7 @@ HandleInputMode(Ctx* ctx, UIPanel* p, UIInput* ev)
mixin(CharCases());
case Input.Backspace:
{
Backspace(&p.ed.buf);
Backspace(p.ed);
taken = true;
} break;
case Input.Escape:
@ -1168,7 +1116,7 @@ StrContains(bool begins_with)(string str, string match)
}
void
HandleCmdMode(Ctx* ctx, bool* enter_hit, bool* buffer_changed, bool* selection_changed)
HandleCmdMode(ref Ctx ctx, bool* enter_hit, bool* buffer_changed, bool* selection_changed)
{
UIPanel* panel = ctx.focused_panel;
Editor* ed = panel.ed;

View File

@ -1,5 +1,8 @@
import dlib;
import dlib.alloc : Reset;
import editor;
import buffer;
enum TokenStyle : u8
@ -399,28 +402,22 @@ struct Tokenizer
}
bool
Nil(Token* tk)
Nil(Token* token)
{
return tk == g_NIL_TOKEN || tk == null;
}
void
SetBuffers(Tokenizer* tk, TokenStyle[] tokens)
{
tk.tokens = tokens;
return token == g_NIL_TOKEN || token == null;
}
Tokenizer
CreateTokenizer(FlatBuffer* fb)
CreateTokenizer(FlatBuffer* buffer)
{
Tokenizer tk = {
Tokenizer tokenizer = {
arena: CreateArena(MB(4)),
};
SetBuffers(&tk, Alloc!(TS)(fb.data.length));
tokenizer.tokens = Alloc!(TS)(buffer.data.length);
g_NIL_TOKEN = cast(Token*)&g_nil_tk;
tk.first = tk.last = g_NIL_TOKEN;
tokenizer.first = tokenizer.last = g_NIL_TOKEN;
for(u64 i = 0; i < D_KEYWORDS.length; i += 1)
{
@ -438,132 +435,133 @@ CreateTokenizer(FlatBuffer* fb)
}
}
return tk;
return tokenizer;
}
void
ResetTokenizer(Tokenizer* tk, u64 len)
ResetTokenizer(Tokenizer* tokenizer, u64 length)
{
Reset(&tk.arena);
Free(tk.tokens);
Reset(&tokenizer.arena);
Free(tokenizer.tokens);
SetBuffers(tk, Alloc!(TS)(len));
tk.pos = 0;
tk.first = tk.last = g_NIL_TOKEN;
tokenizer.tokens = Alloc!(TS)(length);
tokenizer.pos = 0;
tokenizer.first = tokenizer.last = g_NIL_TOKEN;
}
Token*
MakeToken(Tokenizer* tk, TokenType type, u64 start, u64 end)
MakeToken(Tokenizer* tokenizer, TokenType type, u64 length)
{
Token* t = Alloc!(Token)(&tk.arena);
Token* token = Alloc!(Token)(&tokenizer.arena);
t.type = type;
t.start = start;
t.end = end;
token.type = type;
token.start = tokenizer.pos;
token.end = tokenizer.pos+length;
DLLPush(tk, t, g_NIL_TOKEN);
DLLPush(tokenizer, token, g_NIL_TOKEN);
return t;
return token;
}
u8
Peek(FlatBuffer* fb)
Peek(T)(T* buffer) if(is(T == Editor) || is(T == FlatBuffer))
{
return fb.tk.pos+1 < fb.length ? fb.data[fb.tk.pos+1] : 0;
return buffer.tokenizer.pos+1 < buffer.length ? buffer.data[buffer.tokenizer.pos+1] : 0;
}
void
TokenizeD(FlatBuffer* fb)
TokenizeD(T)(T* buffer)
if(is(T == Editor) || is(T == FlatBuffer))
{
Tokenizer* tk = &fb.tk;
Tokenizer* tokenizer = &buffer.tokenizer;
Reset(&tk.arena);
tk.pos = 0;
tk.first = tk.last = g_NIL_TOKEN;
Reset(&tokenizer.arena);
tokenizer.pos = 0;
tokenizer.first = tokenizer.last = g_NIL_TOKEN;
for(; tk.pos < fb.length;)
for(; tokenizer.pos < buffer.length;)
{
u8 ch = fb.data[tk.pos];
u8 ch = buffer.data[tokenizer.pos];
bool alpha = (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z');
if(ch == ' ' || ch == '\t')
{
Token* t = MakeToken(tk, TT.Whitespace, tk.pos, tk.pos+1);
Token* token = MakeToken(tokenizer, TT.Whitespace, 1);
for(tk.pos += 1; tk.pos < fb.length; tk.pos += 1)
for(tokenizer.pos += 1; tokenizer.pos < buffer.length; tokenizer.pos += 1)
{
if(fb.data[tk.pos] != ' ' && fb.data[tk.pos] != '\t')
if(buffer.data[tokenizer.pos] != ' ' && buffer.data[tokenizer.pos] != '\t')
{
break;
}
}
t.end = tk.pos;
token.end = tokenizer.pos;
}
else if(ch == '\n')
{
Token* t = MakeToken(tk, TT.NewLine, tk.pos, tk.pos+1);
tk.pos += 1;
Token* token = MakeToken(tokenizer, TT.NewLine, 1);
tokenizer.pos += 1;
}
else if(ch == 'r')
{
u8 next = Peek(fb);
u8 next = Peek(buffer);
if(next == '"')
{
tk.pos += 1;
ParseStr!('"')(fb);
tokenizer.pos += 1;
ParseStr!('"')(buffer);
}
else
{
ParseId(fb);
ParseId(buffer);
}
}
else if(ch >= '0' && ch <= '9')
{
ParseNum(fb);
ParseNum(buffer);
}
else if(ch == 'P' || ch == 'E' || ch == 'e' || ch == 'p')
{
u8 next = Peek(fb);
u8 next = Peek(buffer);
if(next == '-' || next == '+' || (next >= '0' && next <= '9'))
{
ParseNum(fb);
ParseNum(buffer);
}
else
{
ParseId(fb);
ParseId(buffer);
}
}
else if(alpha || ch == '_')
{
ParseId(fb);
ParseId(buffer);
}
else if(ch < 128 && D_STD_TOKEN[ch] != TT.None)
{
Token* t = MakeToken(tk, D_STD_TOKEN[ch], tk.pos, tk.pos+1);
tk.pos += 1;
Token* token = MakeToken(tokenizer, D_STD_TOKEN[ch], 1);
tokenizer.pos += 1;
}
else if(ch < 128 && D_OP_TOKEN[ch] != TT.None)
{
Token* t = MakeToken(tk, D_OP_TOKEN[ch], tk.pos, tk.pos+1);
tk.pos += 1;
FixOpAssign(fb, t);
Token* token = MakeToken(tokenizer, D_OP_TOKEN[ch], 1);
tokenizer.pos += 1;
FixOpAssign(buffer, token);
}
else if(ch < 128 && D_STR_TOKEN[ch] != TT.None)
{
if(ch == '"')
{
ParseStr!('"')(fb);
ParseStr!('"')(buffer);
}
else if(ch == '`')
{
ParseStr!('`')(fb);
ParseStr!('`')(buffer);
}
else
{
ParseStr!('\'')(fb);
ParseStr!('\'')(buffer);
}
}
else
@ -572,108 +570,108 @@ TokenizeD(FlatBuffer* fb)
{
case '@':
{
Token* t = MakeToken(tk, TT.At, tk.pos, tk.pos+1);
tk.pos += 1;
Token* token = MakeToken(tokenizer, TT.At, 1);
tokenizer.pos += 1;
for(; tk.pos < fb.length; tk.pos += 1)
for(; tokenizer.pos < buffer.length; tokenizer.pos += 1)
{
u8 c = fb.data[tk.pos];
u8 c = buffer.data[tokenizer.pos];
if(CheckWhiteSpace(c)) break;
if(!(c >= 'a' && c <= 'z') && !(c >= 'A' && c <= 'Z')) break;
}
t.end = tk.pos;
token.end = tokenizer.pos;
} break;
case '.':
{
Token* t = MakeToken(tk, TT.Dot, tk.pos, tk.pos+1);
Token* token = MakeToken(tokenizer, TT.Dot, 1);
for(; tk.pos < fb.length; tk.pos += 1)
for(; tokenizer.pos < buffer.length; tokenizer.pos += 1)
{
if(fb.data[tk.pos] != '.') break;
if(buffer.data[tokenizer.pos] != '.') break;
}
t.end = tk.pos;
token.end = tokenizer.pos;
} break;
case '#':
{
Token* t = MakeToken(tk, TT.Import, tk.pos, tk.pos+1);
Token* token = MakeToken(tokenizer, TT.Import, 1);
for(; tk.pos < fb.length; tk.pos += 1)
for(; tokenizer.pos < buffer.length; tokenizer.pos += 1)
{
if(CheckWhiteSpace(fb.data[tk.pos])) break;
if(CheckWhiteSpace(buffer.data[tokenizer.pos])) break;
}
t.end = tk.pos;
token.end = tokenizer.pos;
} break;
case '/':
{
Token* t = MakeToken(tk, TT.Slash, tk.pos, tk.pos+1);
Token* token = MakeToken(tokenizer, TT.Slash, 1);
u8 next = Peek(fb);
u8 next = Peek(buffer);
if(next == '/')
{
tk.pos += 1;
t.type = TT.Comment;
tokenizer.pos += 1;
token.type = TT.Comment;
for(; tk.pos < fb.length; tk.pos += 1)
for(; tokenizer.pos < buffer.length; tokenizer.pos += 1)
{
if(CheckEOL(fb.data[tk.pos]))
if(CheckEOL(buffer.data[tokenizer.pos]))
{
break;
}
}
t.end = tk.pos+1;
token.end = tokenizer.pos+1;
}
else if(next == '*')
{
tk.pos += 1;
t.type = TT.Comment;
tokenizer.pos += 1;
token.type = TT.Comment;
for(; tk.pos < fb.length; tk.pos += 1)
for(; tokenizer.pos < buffer.length; tokenizer.pos += 1)
{
if(fb.data[tk.pos] == '/' && fb.data[tk.pos-1] == '*')
if(buffer.data[tokenizer.pos] == '/' && buffer.data[tokenizer.pos-1] == '*')
{
break;
}
}
tk.pos += 1;
t.end = tk.pos;
tokenizer.pos += 1;
token.end = tokenizer.pos;
}
else
{
tk.pos += 1;
FixOpAssign(fb, t);
tokenizer.pos += 1;
FixOpAssign(buffer, token);
}
} break;
default:
{
Token* t = MakeToken(tk, TT.None, tk.pos, tk.pos+1);
Token* token = MakeToken(tokenizer, TT.None, 1);
for(; tk.pos < fb.length; tk.pos += 1)
for(; tokenizer.pos < buffer.length; tokenizer.pos += 1)
{
if(CheckWhiteSpace(fb.data[tk.pos]))
if(CheckWhiteSpace(buffer.data[tokenizer.pos]))
{
break;
}
}
t.end = tk.pos;
tk.pos += 1;
token.end = tokenizer.pos;
tokenizer.pos += 1;
} break;
}
}
}
for(auto n = tk.first; !Nil(n); n = Next(n))
for(auto n = tokenizer.first; !Nil(n); n = Next(n))
{
Token* prev = Prev(n);
Token* next = Next(n);
if(CheckFuncOrTemplateSig(fb, n))
if(CheckFuncOrTemplateSig(n))
{
continue;
}
@ -688,7 +686,7 @@ TokenizeD(FlatBuffer* fb)
else if(prev.type == TT.TypeKeyword && n.type == TT.Identifier)
{
n.type = TT.Type;
CheckTypeDeclaration(fb, n);
CheckTypeDeclaration(n);
continue;
}
else if(prev.type == TT.EnumKeyword && n.type == TT.Identifier)
@ -715,9 +713,9 @@ TokenizeD(FlatBuffer* fb)
CheckVarDeclaration(n);
}
for(auto token = tk.first; !Nil(token); token = token.next)
for(auto token = tokenizer.first; !Nil(token); token = token.next)
{
tk.tokens[token.start .. token.end] = TOKEN_STYLES[token.type];
tokenizer.tokens[token.start .. token.end] = TOKEN_STYLES[token.type];
}
}
@ -767,20 +765,20 @@ CheckVarDeclaration(Token* token)
}
void
CheckTypeDeclaration(FlatBuffer* fb, Token* token)
CheckTypeDeclaration(Token* token)
{
for(Token* tk = Next(token); !Nil(tk) && tk.type != TT.RightBrace; tk = Next(tk))
for(Token* next_token = Next(token); !Nil(next_token) && next_token.type != TT.RightBrace; next_token = Next(next_token))
{
Token* prev = Prev(tk);
Token* prev = Prev(next_token);
if(prev.type == TT.LeftBrace || prev.type == TT.Semicolon)
{
tk.type = TT.Type;
next_token.type = TT.Type;
}
}
}
bool
CheckFuncOrTemplateSig(FlatBuffer* fb, Token* token)
CheckFuncOrTemplateSig(Token* token)
{
bool found = false;
@ -825,18 +823,18 @@ CheckFuncOrTemplateSig(FlatBuffer* fb, Token* token)
}
u8[]
TokenStr(FlatBuffer* fb, Token* t)
TokenStr(Editor* editor, Token* token)
{
return fb.data[t.start .. t.end];
return editor.data[token.start .. token.end];
}
Token*
Prev(types...)(Token* t)
Prev(types...)(Token* token)
{
Token* res = g_NIL_TOKEN;
PrevMain:
for(auto p = t.prev; !Nil(p); p = p.prev)
for(auto p = token.prev; !Nil(p); p = p.prev)
{
if(p.type == TT.NewLine || p.type == TT.Whitespace || p.type == TT.Comment)
{
@ -865,12 +863,12 @@ PrevMain:
alias PrevFnToken = Prev!(TT.Identifier, TT.LeftParen, TT.RightParen, TT.Comma, TT.Keyword, TT.Dot, TT.Exclamation, TT.Type);
Token*
Next(types...)(Token* t)
Next(types...)(Token* token)
{
Token* res = g_NIL_TOKEN;
NextMain:
for(auto n = t.next; !Nil(n); n = n.next)
for(auto n = token.next; !Nil(n); n = n.next)
{
if(n.type == TT.NewLine || n.type == TT.Whitespace || n.type == TT.Comment)
{
@ -899,20 +897,20 @@ NextMain:
alias NextFnToken = Next!(TT.Identifier, TT.LeftParen, TT.RightParen, TT.Comma, TT.Keyword, TT.Dot, TT.Exclamation, TT.Type);
void
ParseNum(FlatBuffer* fb)
ParseNum(T)(T* buffer) if(is(T == Editor) || is(T == FlatBuffer))
{
Tokenizer* tk = &fb.tk;
Tokenizer* tokenizer = &buffer.tokenizer;
Token* t = Alloc!(Token)(&tk.arena);
t.type = TT.Number;
t.start = tk.pos;
Token* token = Alloc!(Token)(&tokenizer.arena);
token.type = TT.Number;
token.start = tokenizer.pos;
bool first = true;
while (tk.pos < fb.length)
while (tokenizer.pos < buffer.length)
{
tk.pos += 1;
tokenizer.pos += 1;
u8 ch = fb.data[tk.pos];
u8 ch = buffer.data[tokenizer.pos];
if(ch != '_' && ch != '.' && !(ch >= '0' && ch <= '9') && ch != 'x' && ch != 'X' && !((ch == '-' || ch == '+') && first))
{
@ -922,76 +920,76 @@ ParseNum(FlatBuffer* fb)
first = false;
}
while (tk.pos < fb.length)
while (tokenizer.pos < buffer.length)
{
u8 ch = fb.data[tk.pos];
u8 ch = buffer.data[tokenizer.pos];
if(ch != 'l' && ch != 'u' && ch != 'U' && ch != 'L' && ch != 'f' && ch != 'F')
{
break;
}
tk.pos += 1;
tokenizer.pos += 1;
}
t.end = tk.pos;
token.end = tokenizer.pos;
DLLPush(tk, t, g_NIL_TOKEN);
DLLPush(tokenizer, token, g_NIL_TOKEN);
}
void
ParseStr(u8 str_ch)(FlatBuffer* fb)
ParseStr(u8 str_ch, T)(T* buffer) if(is(T == Editor) || is(T == FlatBuffer))
{
static assert(str_ch == '`' || str_ch == '\'' || str_ch == '"');
Tokenizer* tk = &fb.tk;
Tokenizer* tokenizer = &buffer.tokenizer;
Token* t = Alloc!(Token)(&tk.arena);
Token* token = Alloc!(Token)(&tokenizer.arena);
t.type = D_STR_TOKEN[str_ch];
t.start = tk.pos;
token.type = D_STR_TOKEN[str_ch];
token.start = tokenizer.pos;
static if(str_ch == '`') tk.pos += 1;
static if(str_ch == '`') tokenizer.pos += 1;
bool ignore = str_ch != '`';
for(; (fb.data[tk.pos] != str_ch || ignore) && tk.pos < fb.length; tk.pos += 1)
for(; (buffer.data[tokenizer.pos] != str_ch || ignore) && tokenizer.pos < buffer.length; tokenizer.pos += 1)
{
static if(str_ch != '`') ignore = fb.data[tk.pos] == '\\';
static if(str_ch != '`') ignore = buffer.data[tokenizer.pos] == '\\';
}
tk.pos += 1;
t.end = tk.pos;
tokenizer.pos += 1;
token.end = tokenizer.pos;
DLLPush(tk, t, g_NIL_TOKEN);
DLLPush(tokenizer, token, g_NIL_TOKEN);
}
void
ParseId(FlatBuffer* fb)
ParseId(T)(T* buffer) if(is(T == Editor) || is(T == FlatBuffer))
{
Tokenizer* tk = &fb.tk;
Token* t = MakeToken(tk, TT.Identifier, tk.pos, tk.pos+1);
Tokenizer* tokenizer = &buffer.tokenizer;
Token* token = MakeToken(tokenizer, TT.Identifier, 1);
for(; tk.pos < fb.length; tk.pos += 1)
for(; tokenizer.pos < buffer.length; tokenizer.pos += 1)
{
u8 ch = fb.data[tk.pos];
u8 ch = buffer.data[tokenizer.pos];
if(!(ch >= 'a' && ch <= 'z') && !(ch >= 'A' && ch <= 'Z') && ch != '.' && ch != '_' && !(ch >= '0' && ch <= '9'))
{
t.end = tk.pos;
token.end = tokenizer.pos;
break;
}
}
u8 ch = fb.data[t.start];
u8 ch = buffer.data[token.start];
if(ch < D_KEYWORDS.length && D_KEYWORDS[ch] != null && D_KEYWORDS[ch].length > 0)
{
bool found = false;
u8[] id = fb.data[t.start .. t.end];
u8[] id = buffer.data[token.start .. token.end];
foreach(ref k; D_TYPES[ch])
{
if(id == k)
{
t.type = TT.Type;
token.type = TT.Type;
found = true;
break;
}
@ -1005,19 +1003,19 @@ ParseId(FlatBuffer* fb)
{
if(k == r"import")
{
t.type = TT.Import;
token.type = TT.Import;
}
else if(k == r"class" || k == r"struct")
{
t.type = TT.TypeKeyword;
token.type = TT.TypeKeyword;
}
else if(k == r"enum")
{
t.type = TT.EnumKeyword;
token.type = TT.EnumKeyword;
}
else
{
t.type = TT.Keyword;
token.type = TT.Keyword;
}
break;
@ -1025,15 +1023,15 @@ ParseId(FlatBuffer* fb)
}
}
assert(t.start < t.end, "ParseId failure: start is lower or equal to end");
assert(token.start < token.end, "ParseId failure: start is lower or equal to end");
}
void
FixOpAssign(FlatBuffer* fb, Token* t)
FixOpAssign(T)(T* buffer, Token* token) if(is(T == Editor) || is(T == FlatBuffer))
{
if(t.end+1 < fb.length)
if(token.end+1 < buffer.length)
{
u8[] str = fb.data[t.start .. t.end+1];
u8[] str = buffer.data[token.start .. token.end+1];
if(
str == "/=" ||
str == "+=" ||
@ -1044,8 +1042,8 @@ FixOpAssign(FlatBuffer* fb, Token* t)
str == "!="
)
{
fb.tk.pos += 1;
t.end += 1;
buffer.tokenizer.pos += 1;
token.end += 1;
}
}
}
@ -1072,11 +1070,11 @@ CheckWhiteSpace(u8 ch)
}
void
SkipWhiteSpace(FlatBuffer* fb)
SkipWhiteSpace(Editor* editor)
{
while (fb.tk.pos < fb.length)
while (editor.tokenizer.pos < editor.length)
{
u8 ch = fb.data[fb.tk.pos];
u8 ch = editor.data[editor.tokenizer.pos];
bool white_space = CheckWhiteSpace(ch);
@ -1085,7 +1083,7 @@ SkipWhiteSpace(FlatBuffer* fb)
break;
}
fb.tk.pos += 1;
editor.tokenizer.pos += 1;
}
}

View File

@ -1,6 +1,7 @@
import dlib;
import dlib.util : HTPush = Push;
import dlib.math : Clamp;
import dlib.alloc : Reset;
import vulkan;
import vulkan : RendererGetExtent = GetExtent;
@ -64,8 +65,8 @@ u8[] FONT_BYTES = cast(u8[])import("jetbrains-mono/JetBrainsMono-Regula
u8[] VERTEX_BYTES = cast(u8[])import("gui.vert.spv");
u8[] FRAGMENT_BYTES = cast(u8[])import("gui.frag.spv");
const SEP_FLAGS = UIF.Drag|UIF.Priority|UIF.TargetLeniency;
const PANEL_FLAGS = UIF.Click|UIF.Drag;
const SEP_FLAGS = UIF.Drag | UIF.Priority | UIF.TargetLeniency;
const PANEL_FLAGS = UIF.Click | UIF.Drag;
const UI_COUNT = 5000;
@ -322,10 +323,27 @@ Attribute[10] attributes = [
{ binding: 0, location: 9, format: FMT.R_U32, offset: Vertex.has_texture.offsetof },
];
union Rect
struct Rect
{
Vec2[2] v;
struct { Vec2 p0, p1; alias p0 pos; };
union
{
Vec2[2] v;
struct
{
Vec2 p0, p1;
alias p0 pos;
};
};
ref Vec2 opIndex(usize index)
{
return this.v[index];
}
ref Vec2 opIndexAssign(usize index, Vec2 value)
{
return this.v[index] = value;
}
}
alias UIHash = u64;
@ -341,53 +359,11 @@ struct UIKey
bool opCast(T)() if(is(T == bool)) => this.hash != 0;
}
/*
"atlas": {
"type": "mtsdf",
"distanceRange": 2,
"distanceRangeMiddle": 0,
"size": 32.5,
"width": 216,
"height": 216,
"yOrigin": "bottom"
},
"metrics": {
"emSize": 1,
"lineHeight": 1.3200000000000001,
"ascender": 1.02,
"descender": -0.29999999999999999,
"underlineY": -0.17999999999999999,
"underlineThickness": 0.050000000000000003
},
"glyphs": [
{
"unicode": 32,
"advance": 0.59999999999999998
},
{
"unicode": 33,
"advance": 0.59999999999999998,
"planeBounds": {
"left": 0.19230769230769232,
"bottom": -0.046153846153846149,
"right": 0.40769230769230769,
"top": 0.78461538461538449
},
"atlasBounds": {
"left": 19.5,
"bottom": 151.5,
"right": 26.5,
"top": 178.5
}
},
*/
enum bool KeyType(T) = (StringType!(T) || is(T == UIKey) || is(T == const(UIKey)));
enum bool ItemAndKeyType(T) = (KeyType!(T) || is(T == UIItem*));
void
InitUI(Ctx* ctx)
InitUI(ref Ctx ctx)
{
version(ENABLE_RENDERER)
{
@ -438,18 +414,13 @@ InitUI(Ctx* ctx)
for(u64 i = 0; i < FRAME_OVERLAP; i += 1)
{
ctx.buffers[i].m_vtx = CreateMappedBuffer!(Vertex)(BT.Vertex, VERTEX_MAX_COUNT);
ctx.buffers[i].m_idx = CreateMappedBuffer!(u32)(BT.Index, 6);
ctx.buffers[i].vtx = ctx.buffers[i].m_vtx.data;
ctx.buffers[i].idx = ctx.buffers[i].m_idx.data;
ctx.buffers[i].m_vtx = CreateMappedBuffer!(Vertex)(BT.Vertex, VERTEX_MAX_COUNT);
ctx.buffers[i].m_idx = CreateMappedBuffer!(u32)(BT.Index, 6);
ctx.buffers[i].vtx = ctx.buffers[i].m_vtx.data;
ctx.buffers[i].idx = ctx.buffers[i].m_idx.data;
ctx.buffers[i].idx[0 .. $] = [0, 1, 2, 2, 1, 3];
ctx.desc_sets[i] = AllocDescSet(ctx.desc_set_layout);
ctx.str_arenas[i] = CreateArena(MB(1));
ctx.slug_vertex_buffers[i] = CreateMappedBuffer!(SlugVertex)(BT.Vertex, 1000);
ctx.slug_index_buffers[i] = CreateMappedBuffer!(u32)(BT.Index, 1000);
ctx.slug_buffers[i].vertices = ctx.slug_vertex_buffers[i].data;
ctx.slug_buffers[i].indices = ctx.slug_index_buffers[i].data;
ctx.desc_sets[i] = AllocDescSet(ctx.desc_set_layout);
ctx.str_arenas[i] = CreateArena(MB(1));
}
GfxPipelineInfo ui_info = {
@ -550,16 +521,16 @@ PushFree(string next_member = "free_next", T)(T** top, T* node)
*top = node;
}
f32
GetLineHeight(u32 size)
{
return GetFontSet(size).line_height;
}
f32
GetLineHeight(u32 size) => GetFontSet(size).line_height;
FontSet*
GetFontSet(T)(T font_set) if(is(T == FontSet*)) => font_set;
FontSet*
GetFontSet(u32 size)
GetFontSet(T)(T size) if(is(T: u32))
{
Ctx* ctx = GetCtx();
ref Ctx ctx = GetCtx();
FontSet* fs = null;
for(u64 i = 0; i < ctx.font_sets_used; i += 1)
@ -570,7 +541,7 @@ GetFontSet(u32 size)
break;
}
}
if(!fs)
{
fs = &ctx.font_sets[ctx.font_sets_used++];
@ -590,30 +561,30 @@ GetFontSet(u32 size)
void
LoadFontSet()
{
Ctx* ctx = GetCtx();
u64 fi = ctx.f_idx;
ref Ctx ctx = GetCtx();
for(u64 i = 0; i < ctx.font_sets_used; i += 1)
{
FontSet* fs = ctx.font_sets.ptr + i;
FontSet* fs = ctx.font_sets.ptr + i;
ref bool descriptor_written = fs.descriptor_written[ctx.frame_index];
if(!fs.texture_loaded)
{
Transfer!(true)(&ctx.font_descs[i], fs.atlas.data);
fs.texture_loaded = true;
}
if(!fs.descriptor_written[fi])
if(!descriptor_written)
{
Write(ctx.desc_sets[fi], ctx.font_descs);
fs.descriptor_written[fi] = true;
Write(ctx.desc_set, ctx.font_descs);
descriptor_written = true;
}
}
ctx.fonts_to_load[fi] = false;
ctx.fonts_to_load[ctx.frame_index] = false;
}
void
Scissor(Ctx* ctx, Rect rect, Style* style)
Scissor(ref Ctx ctx, Rect rect, Style* style)
{
DrawUI(ctx);
@ -630,14 +601,14 @@ Scissor(Ctx* ctx, Rect rect, Style* style)
}
void
EndScissor(Ctx* ctx)
EndScissor(ref Ctx ctx)
{
DrawUI(ctx);
ResetScissor();
}
void
Panel(Ctx* ctx, UIPanel* panel)
Panel(ref Ctx ctx, UIPanel* panel)
{
panel.item = MakeItem!(PANEL_FLAGS)("###panel_%s", panel.id);
Axis2D p_axis = Nil(panel.parent) ? A2D.X : panel.parent.axis;
@ -682,16 +653,16 @@ Panel(Ctx* ctx, UIPanel* panel)
panel.move_text_with_cursor = false;
}
panel.scroll_target.y = cast(f32)(panel.ed.line_offset)*fs.line_height;
panel.scroll_target.y = cast(f32)(panel.ed.view_info.line_offset)*fs.line_height;
f32 max_px = panel.ed.buf.line_count*fs.line_height;
f32 max_px = panel.ed.line_count*fs.line_height;
f32 offset = -(panel.item.scroll_offset.y%fs.line_height);
u64 start_ln = cast(u64)(floor(panel.scroll_offset.y/fs.line_height));
const f32 PAD = 4.0f;
// Line counter
u64 lc_chars = panel.ed.buf.line_count.toChars().length;
u64 lc_chars = panel.ed.line_count.toChars().length;
f32 lc_width = PAD*2.0 + cast(f32)(lc_chars)*fs.max_advance;
Style style = PANEL_STYLE;
@ -709,7 +680,7 @@ Panel(Ctx* ctx, UIPanel* panel)
f32 panel_height = Size!(A2D.Y)(line_count_rect);
panel.ed.line_height = cast(i64)ceil(panel_height/fs.line_height);
u64 max_ln = Min(panel.ed.line_height + start_ln, panel.ed.buf.line_count);
u64 max_ln = Min(panel.ed.line_height + start_ln, panel.ed.line_count);
for(u64 i = start_ln; i < max_ln; i += 1)
{
@ -729,12 +700,12 @@ Panel(Ctx* ctx, UIPanel* panel)
text_rect = Pad(text_view_rect, PAD);
I64Vec2 cursor_coords = VecPos(&panel.ed.buf);
LineBuffer* current_lbuf = GetLine(&panel.ed.buf, cursor_coords.y);
I64Vec2 cursor_coords = VecPos(panel.ed);
LineBuffer* current_lbuf = GetLine(panel.ed, cursor_coords.y);
UIItem* cursor = MakeItem("###cursor_%s", panel.id);
if(ctx.focused_panel == panel)
{
i64 y_pos = cursor_coords.y - panel.ed.line_offset;
i64 y_pos = cursor_coords.y - panel.ed.view_info.line_offset;
cursor.size.x = Active(ES.InputMode) ? 2.0 : fs.max_advance;
cursor.size.y = fs.line_height;
@ -760,7 +731,7 @@ Panel(Ctx* ctx, UIPanel* panel)
{
if(Active(ES.InputMode) && panel.move_text_with_cursor)
{
for(LineBuffer* lbuf = GetLine(&panel.ed.buf, i); i < max_ln && !CheckNil(g_NIL_LINE_BUF, lbuf); lbuf = GetLine(&panel.ed.buf, ++i))
for(LineBuffer* lbuf = GetLine(panel.ed, i); i < max_ln && !CheckNil(g_NIL_LINE_BUF, lbuf); lbuf = GetLine(panel.ed, ++i))
{
if(i == cursor_coords.y)
{
@ -799,7 +770,7 @@ Panel(Ctx* ctx, UIPanel* panel)
DrawLines(ctx, panel, start_ln, max_ln, text_rect, fs);
}
ResetBuffer(&panel.ed.buf);
ResetBuffer(panel.ed);
EndScissor(ctx);
DrawBorder(ctx, text_view_rect, &style);
@ -844,10 +815,10 @@ Panel(Ctx* ctx, UIPanel* panel)
}
void
DrawLines(bool hl = false)(Ctx* ctx, UIPanel* panel, u64 start_line, u64 end_line, Rect text_rect, FontSet* fs, I64Vec2 cursor_coords = I64Vec2(-1))
DrawLines(bool hl = false)(ref Ctx ctx, UIPanel* panel, u64 start_line, u64 end_line, Rect text_rect, FontSet* fs, I64Vec2 cursor_coords = I64Vec2(-1))
{
u64 i = start_line;
for(LineBuffer* lbuf = GetLine(&panel.ed.buf, i); i < end_line && !CheckNil(g_NIL_LINE_BUF, lbuf); lbuf = GetLine(&panel.ed.buf, ++i))
for(LineBuffer* lbuf = GetLine(panel.ed, i); i < end_line && !CheckNil(g_NIL_LINE_BUF, lbuf); lbuf = GetLine(panel.ed, ++i))
{
static if(hl)
{
@ -873,7 +844,7 @@ struct SearchWindowCtx(T)
void
SearchInputWindow(T)(string hash_text, SearchWindowCtx!(T)* window_ctx, bool ready_cond) if(is(T == Command) || is(T == string))
{
Ctx* ctx = GetCtx();
ref Ctx ctx = GetCtx();
Vec2 extent = GetExtent();
f32 x = extent.x * 0.1;
f32 y = extent.y * 0.2;
@ -955,7 +926,7 @@ SearchInputWindow(T)(string hash_text, SearchWindowCtx!(T)* window_ctx, bool rea
Rect opt_rect = inner_rect;
opt_rect.p0.y -= opt_start_offset;
UIItem* selected_opt_item = g_UI_NIL;
UIItem* selected_opt_item = g_UI_NIL_ITEM;
if(start_opt_index < options.length) foreach(i; start_opt_index .. options.length)
{
Rect[2] opt_rects = Split(opt_rect, full_opt_height, A2D.Y);
@ -1058,9 +1029,9 @@ SearchInputWindow(T)(string hash_text, SearchWindowCtx!(T)* window_ctx, bool rea
}
bool
SaveFileWindow(Ctx* ctx)
SaveFileWindow(ref Ctx ctx)
{
bool exit;
bool exit;
Editor* ed = ctx.focused_panel.ed;
static bool init = false;
@ -1068,7 +1039,6 @@ SaveFileWindow(Ctx* ctx)
{
ctx.cmd.exec_cmd_input.data[0 .. ed.file_name.length] = Arr!(u8)(ed.file_name[0 .. $]);
ctx.cmd.exec_cmd_input.length = ed.file_name.length;
init = true;
}
const PAD = 4.0f;
@ -1133,19 +1103,16 @@ SaveFileWindow(Ctx* ctx)
}
}
if(exit)
{
init = false;
}
init = !exit;
return exit;
}
bool
OpenFileWindow(Ctx* ctx)
OpenFileWindow(ref Ctx ctx)
{
bool exit;
CmdPalette* cmd = &ctx.cmd;
bool exit;
ref CmdPalette cmd = ctx.cmd;
if(!cmd.exec_cmd_opts.length && !cmd.exec_cmd_input.length)
{
@ -1218,9 +1185,9 @@ OpenFileWindow(Ctx* ctx)
}
void
CommandPalette(Ctx* ctx)
CommandPalette(ref Ctx ctx)
{
CmdPalette* cmd = &ctx.cmd;
ref CmdPalette cmd = ctx.cmd;
bool enter_hit, buffer_changed, selection_changed;
if(ctx.state == ES.CmdPalette)
@ -1416,15 +1383,9 @@ CreatePanel(Editor* ed = null)
else
{
bool allocated;
p = GetOrAlloc(&g_ctx.arena, &g_ctx.free_panels, g_NIL_PANEL, &allocated);
if(allocated)
{
p.id = panel_id++;
}
if(!p.ed)
{
p.ed = CreateEditor();
}
p = GetOrAlloc(&g_ctx.arena, &g_ctx.free_panels, g_NIL_PANEL, &allocated);
p.id = panel_id++;
p.ed = CreateEditor();
}
p.item = MakeItem!(PANEL_FLAGS)("###panel_%s", p.id);
@ -1500,7 +1461,7 @@ Hovered(bool current_frame, T, U)(T param, U* ptr, U index)
bool
Hovered(bool current_frame, T)(T param)
{
Ctx* ctx = GetCtx();
ref Ctx ctx = GetCtx();
bool result;
UIKey key = mixin(AssignKey!(T, param));
@ -1521,7 +1482,7 @@ Hovered(bool current_frame, T)(T param)
}
void
PushUIEvent(Ctx* ctx, UIInput input)
PushUIEvent(ref Ctx ctx, UIInput input)
{
UIInput* ev = Alloc!(UIInput)(&ctx.temp_arena);
*ev = input;
@ -1530,7 +1491,7 @@ PushUIEvent(Ctx* ctx, UIInput input)
}
void
DrawUI(Ctx* ctx, u32 atlas_index)
DrawUI(ref Ctx ctx, u32 atlas_index)
{
if(ctx.pc.atlas_index != atlas_index)
{
@ -1541,33 +1502,17 @@ DrawUI(Ctx* ctx, u32 atlas_index)
}
void
DrawSlug(Ctx* ctx)
DrawUI(ref Ctx ctx)
{
version(ENABLE_RENDERER)
{
// TODO: fix this
SlugBuffer* buffer = ctx.slug_buffers.ptr + ctx.f_idx;
auto vertex_buffer = ctx.slug_vertex_buffers.ptr + ctx.f_idx;
auto index_buffer = ctx.slug_index_buffers.ptr + ctx.f_idx;
BindBuffers(index_buffer, vertex_buffer);
DrawIndexed(buffer.quad_offset*6, 1, 0, 0, 0);
}
}
void
DrawUI(Ctx* ctx)
{
version(ENABLE_RENDERER)
{
UIBuffer* b = ctx.buffers.ptr + ctx.f_idx;
if(b.vtx_offset != b.count)
ref UIBuffer buffer = ctx.buffers[ctx.frame_index];
if(buffer.vtx_offset != buffer.count)
{
u32 count = b.count - b.vtx_offset;
BindBuffers(&b.m_idx, &b.m_vtx);
DrawIndexed(6, count, 0, 0, b.vtx_offset);
b.vtx_offset += count;
u32 count = buffer.count - buffer.vtx_offset;
BindBuffers(&buffer.m_idx, &buffer.m_vtx);
DrawIndexed(6, count, 0, 0, buffer.vtx_offset);
buffer.vtx_offset += count;
}
}
}
@ -1575,10 +1520,10 @@ DrawUI(Ctx* ctx)
void
BeginUI(Inputs* inputs)
{
Ctx* ctx = GetCtx();
Arena* a = &ctx.temp_arena;
ref Ctx ctx = GetCtx();
ref Arena arena = ctx.temp_arena;
Reset(a);
Reset(&arena);
// Convert Inputs
ctx.events.first = ctx.events.last = null;
@ -1687,11 +1632,11 @@ BeginUI(Inputs* inputs)
// Ctx state
ctx.frame = next_frame;
ctx.f_idx = ctx.frame%FRAME_OVERLAP;
ctx.frame_index = ctx.frame%FRAME_OVERLAP;
ctx.inputs = inputs;
ctx.last_item = g_UI_NIL;
ctx.last_item = g_UI_NIL_ITEM;
Reset(&ctx.str_arenas[ctx.f_idx]);
Reset(&ctx.str_arenas[ctx.frame_index]);
assert(!mouse_moved || ZeroKey(ctx.hover_key) || (ctx.frame == ctx.last_hover_frame));
@ -1704,7 +1649,7 @@ BeginUI(Inputs* inputs)
{
BeginFrame();
if(ctx.fonts_to_load[ctx.f_idx])
if(ctx.fonts_to_load[ctx.frame_index])
{
LoadFontSet();
}
@ -1714,22 +1659,19 @@ BeginUI(Inputs* inputs)
SetStencilTest(false);
PushConstants(ctx.pipeline, &ctx.pc);
Bind(ctx.pipeline, ctx.desc_sets[ctx.f_idx]);
Bind(ctx.pipeline, ctx.desc_sets[ctx.frame_index]);
}
UIBuffer* buffer = ctx.buffers.ptr + ctx.f_idx;
SlugBuffer* slug_buffer = ctx.slug_buffers.ptr + ctx.f_idx;
UIBuffer* buffer = ctx.buffers.ptr + ctx.frame_index;
memset(buffer.vtx.ptr, 0, Vertex.sizeof * buffer.count);
memset(slug_buffer.vertices.ptr, 0, SlugVertex.sizeof * slug_buffer.quad_offset);
memset(buffer.vtx.ptr, 0, Vertex.sizeof * buffer.count);
buffer.count = 0;
buffer.vtx_offset = 0;
slug_buffer.quad_offset = 0;
}
UIKey
FindHoverKey(bool priority)(Ctx* ctx)
FindHoverKey(bool priority)(ref Ctx ctx)
{
UIKey hovered;
@ -1762,11 +1704,11 @@ FindHoverKey(bool priority)(Ctx* ctx)
void
EndUI()
{
Ctx* ctx = GetCtx();
ref Ctx ctx = GetCtx();
if(ctx.dbg)
debug if(ctx.dbg)
{
const PAD = 4.0f;
const f32 PAD = 4.0f;
static FontSet* fs;
if(!fs)
{
@ -1780,7 +1722,7 @@ EndUI()
Rect debug_rect;
debug_rect.p0.x = ext.x - text_width - PAD*2.0f;
debug_rect.p0.x = ext.x - text_width - PAD*2.0f;
debug_rect.p0.y = ext.y - fs.line_height - PAD*2.0f;
debug_rect.p1 = ext;
@ -1814,7 +1756,7 @@ SetNil(Args...)(Args args)
{
static if(is(typeof(Args[i]) == UIItem**))
{
*args[i] = g_UI_NIL;
*args[i] = g_UI_NIL_ITEM;
}
else
{
@ -1824,7 +1766,7 @@ SetNil(Args...)(Args args)
}
void
Clamp(Ctx* ctx, Vertex* v)
Clamp(ref Ctx ctx, Vertex* v)
{
v.dst_start = Clamp(v.dst_start, Vec2(0.0), cast(Vec2)ctx.pc.resolution);
v.dst_end = Clamp(v.dst_end, Vec2(0.0), cast(Vec2)ctx.pc.resolution);
@ -1853,7 +1795,7 @@ UpdateState(f32* v, bool cond)
}
void
UpdateState(ISF flags)(Ctx* ctx, UIItem* item)
UpdateState(ISF flags)(ref Ctx ctx, UIItem* item)
{
static if(flags & ISF.Hot) UpdateState(&item.hot_t, cast(bool)(flags & ISF.SetHot) || Hovered!(true)(item));
static if(flags & ISF.Ready) UpdateState(&item.ready_t, cast(bool)(flags & ISF.SetReady));
@ -1892,7 +1834,7 @@ AnimateReady(Args...)(f32 ready, Args cols)
}
void
DrawDropShadow(Ctx* ctx, Rect rect, f32 ready_t = 1.0)
DrawDropShadow(ref Ctx ctx, Rect rect, f32 ready_t = 1.0)
{
Vec2 size = Vec2(rect.p1.x-rect.p0.x, rect.p1.y-rect.p0.y);
@ -1911,7 +1853,7 @@ DrawDropShadow(Ctx* ctx, Rect rect, f32 ready_t = 1.0)
}
void
DrawBorder(Ctx* ctx, Rect rect, Style* style, f32 hot_t = 0.0, f32 ready_t = 1.0)
DrawBorder(ref Ctx ctx, Rect rect, Style* style, f32 hot_t = 0.0, f32 ready_t = 1.0)
{
Vec4 col = style.border_col;
AnimateHot(hot_t, style.border_hl_col, &col);
@ -1929,7 +1871,7 @@ DrawBorder(Ctx* ctx, Rect rect, Style* style, f32 hot_t = 0.0, f32 ready_t = 1.0
}
void
DrawRect(Ctx* ctx, Rect rect, Style* style, f32 hot_t = 0.0f, f32 ready_t = 1.0f)
DrawRect(ref Ctx ctx, Rect rect, Style* style, f32 hot_t = 0.0f, f32 ready_t = 1.0f)
{
Vec4 col = style.col;
AnimateHot(hot_t, style.hl_col, &col);
@ -1955,19 +1897,11 @@ enum TextAlign
} alias TA = TextAlign;
void
DrawText(T, U, V)(Ctx* ctx, T text_param, U col_param, V text_size_param, Rect rect, TextAlign text_align, i64 hl_char = -1, f32 ready = 1.0)
DrawText(T, U, V)(ref Ctx ctx, T text_param, U col_param, V text_size_param, Rect rect, TextAlign text_align, i64 hl_char = -1, f32 ready = 1.0)
if(is(V: u32) || is(V == FontSet*))
{
static if(is(V == FontSet*))
{
FontSet* fs = text_size_param;
}
else if(is(V == u32))
{
FontSet* fs = GetFontSet(text_size_param);
}
else static assert(false, "invalid input parameter, V must be FontSet* or u32");
string text = Str(text_param);
FontSet* fs = GetFontSet(text_size_param);
string text = Str(text_param);
DrawUI(ctx, fs.index);
@ -2031,7 +1965,7 @@ InvertCol(Vec4 col)
}
void
BeginScissor(Ctx* ctx, UIItem* item, bool scissor_x = true, bool scissor_y = true)
BeginScissor(ref Ctx ctx, UIItem* item, bool scissor_x = true, bool scissor_y = true)
{
DrawUI(ctx);
@ -2050,7 +1984,10 @@ BeginScissor(Ctx* ctx, UIItem* item, bool scissor_x = true, bool scissor_y = tru
Vec2
GetExtent()
{
version(ENABLE_RENDERER) return Vec2(RendererGetExtent()); else return Vec2(1280, 720);
version(ENABLE_RENDERER)
return Vec2(RendererGetExtent());
else
return Vec2(1280, 720);
}
template
@ -2187,10 +2124,10 @@ ZeroKey(T)(T param) if(is(T == UIItem*) || is(T == UIKey))
}
UIItem*
NewItem(Ctx* ctx)
NewItem(ref Ctx ctx)
{
bool allocated;
return GetOrAlloc(&ctx.arena, &ctx.free_items, g_UI_NIL, &allocated);
return GetOrAlloc(&ctx.arena, &ctx.free_items, g_UI_NIL_ITEM, &allocated);
}
UIItem*
@ -2205,7 +2142,7 @@ MakeItem(UIFlags flags = UIF.None, Args...)(string str, Args args)
}
}
char[] key = sformat(Alloc!(char)(&g_ctx.str_arenas[g_ctx.f_idx], len), str, args);
char[] key = sformat(Alloc!(char)(&g_ctx.str_arenas[g_ctx.frame_index], len), str, args);
return MakeItem(key, flags);
}
@ -2213,7 +2150,7 @@ pragma(inline) UIItem*
MakeItem(T)(T k, UIFlags flags = UIF.None) if(KeyType!(T))
{
UIKey key = mixin(AssignKey!(T, k));
Ctx* ctx = GetCtx();
ref Ctx ctx = GetCtx();
UIItem* item;
Result!(UIItem*) res = ctx.items[key.hash];
@ -2309,14 +2246,7 @@ SetHot(UIItem* item, bool hot)
f32
CalcTextWidth(T, U)(T text, U param) if((is(T == string) || StringType!T) && (is(U: u32) || is(U == FontSet*) ))
{
static if(is(T == string))
{
string str = text;
}
else
{
string str = Str(text);
}
string str = Str(text);
static if(is(U: u32))
{
@ -2349,7 +2279,7 @@ DrawGlyph(Rect rect, Glyph* glyph, f32* x_pos, f32 y, f32 line_height, u32 size,
{
if(glyph)
{
Ctx* ctx = GetCtx();
ref Ctx ctx = GetCtx();
Vertex* v = null;
f32 advance = glyph.advance;
@ -2412,24 +2342,24 @@ DrawGlyph(Rect rect, Glyph* glyph, f32* x_pos, f32 y, f32 line_height, u32 size,
}
pragma(inline) Vertex*
GetVertex(Ctx* ctx)
GetVertex(ref Ctx ctx)
{
assert(ctx.buffers[ctx.f_idx].count < VERTEX_MAX_COUNT);
return ctx.buffers[ctx.f_idx].vtx.ptr + ctx.buffers[ctx.f_idx].count++;
assert(ctx.buffers[ctx.frame_index].count < VERTEX_MAX_COUNT);
return ctx.buffers[ctx.frame_index].vtx.ptr + ctx.buffers[ctx.frame_index].count++;
}
bool
Nil(T)(T item)
{
static if(is(T == UIItem*)) return item == null || item == g_UI_NIL;
static if(is(T == UIItem*)) return item == null || item == g_UI_NIL_ITEM;
else static if(is(T == UIInput*)) return item == null || item == g_UI_NIL_INPUT;
else static if(is(T == UIPanel*)) return item == null || item == g_NIL_PANEL;
else static assert(false, "Invalid nil type checked");
}
bool
Dragged(Ctx* ctx, UIItem* item, IVec2* dragged)
Dragged(ref Ctx ctx, UIItem* item, IVec2* dragged)
{
if(item.key == ctx.drag_key || (!ctx.drag_key && item.key == ctx.hover_key))
{
@ -2459,7 +2389,7 @@ Dragged(Ctx* ctx, UIItem* item, IVec2* dragged)
}
bool
Clicked(Ctx* ctx, UIItem* item)
Clicked(ref Ctx ctx, UIItem* item)
{
bool result;
@ -2550,7 +2480,7 @@ OklabToSRGB(Vec4 v)
string
EdScratchf(Args...)(string fmt, Args args)
{
char[] buf = Alloc!(char)(&g_ctx.str_arenas[g_ctx.f_idx], fmt.length < 16 ? 32 : 128);
char[] buf = Alloc!(char)(&g_ctx.str_arenas[g_ctx.frame_index], fmt.length < 16 ? 32 : 128);
return Str(sformat(buf, fmt, args));
}