some code clean up, readme updated

This commit is contained in:
Matthew 2026-02-03 06:50:46 +11:00
parent 8e74e90109
commit d133fc90b6
18 changed files with 96426 additions and 344 deletions

BIN
media/animated-cursor.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 841 KiB

BIN
media/command-palette.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 MiB

BIN
media/editor-demo.mp4 Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

Binary file not shown.

BIN
media/panels.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 MiB

BIN
media/text-input.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 KiB

View File

@ -1,24 +1,30 @@
# ScratchPad # ScratchPad
A text editor I'm developing for personal use as well as getting used to 2D development with Vulkan. A text editor I'm developing for personal use as well as getting used to 2D and UI development with Vulkan.
It's currently in an early state, functionality isn't entirely implemented for what you would expect for a text editor and it currently only works on Linux and x86 due to the platform layer only supporting Linux/X11 and inline assembly being used respectively. Will eventually also implement the platform layer for Windows. It's currently in an early state but has core functionality built out and I'm currently in the process of building out functionality
## Details ## Details
- UI auto layout implementation that can handle resizable panels, text wrapping, windows and more - Editor that supports multiple text buffers through multiple panels that can be automatically resized and arbitrarily split
- Command palette for actions and eventually more such as config options - Command palette for actions such as opening/saving files, splitting text editor views and eventually will contain config options and key bindings.
- Implemented with minimal external dependencies, only using cglm/xxhash/Vulkan Memory Allocator as well as system libraries and Vulkan for the graphics API - Implemented with minimal external dependencies, only using cglm/xxhash/Vulkan Memory Allocator as well as system libraries and Vulkan for the graphics API
- Manual memory management primarily using arenas, no GC usage. - Manual memory management primarily using arenas, no GC usage.
- Syntax highlighting (for D and C-like languages) and auto indenting/auto closing pairs (strings, braces, etc) - Syntax highlighting (for D and C-like languages) and auto indenting/auto closing pairs (strings, braces, etc)
- Animations such as smooth scrolling, highlighting on focus and animated cursors with text after the cursor having its position animated
## Showcase ## Showcase
### Videos ### Gifs
https://git.sleepy.day/sleepy-day/editor/raw/branch/master/media/features.mp4 ![CommandPalette](https://git.sleepy.day/sleepy-day/editor/raw/branch/master/media/command-palette.gif)
https://git.sleepy.day/sleepy-day/editor/raw/branch/master/media/indents.mp4 ![Panels](https://git.sleepy.day/sleepy-day/editor/raw/branch/master/media/panels.gif)
### Screenshot ![AnimatedCursor](https://git.sleepy.day/sleepy-day/editor/raw/branch/master/media/animated-cursor.gif)
![TextInput](https://git.sleepy.day/sleepy-day/editor/raw/branch/master/media/text-input.gif)
### Demonstration Video
https://git.sleepy.day/sleepy-day/editor/raw/branch/master/media/editor-demo.mp4
![Screenshot](https://git.sleepy.day/sleepy-day/editor/raw/branch/master/media/editor.png)

@ -1 +1 @@
Subproject commit 246e7006f535f65e0d045c644bfa8d645c86f5f7 Subproject commit 53dbb8bf3492f99418f129d54184d8eddc895338

View File

@ -145,6 +145,7 @@ Init(FlatBuffer* fb, u8[] data, string file_name)
fb.selection = -1; fb.selection = -1;
fb.sel_mode = SM.None; fb.sel_mode = SM.None;
fb.linebufs = CreateLineBuffers(MB(1)); fb.linebufs = CreateLineBuffers(MB(1));
fb.pos = 0;
SetBuffers(fb, buf); SetBuffers(fb, buf);
@ -477,6 +478,13 @@ SliceLineBuffer(FlatBuffer* fb, Line* ls)
return lbuf; return lbuf;
} }
void
Clear(Editor* ed)
{
Change(&ed.buf, [], "");
ed.line_offset = 0;
}
pragma(inline) void pragma(inline) void
ResetBuffer(FlatBuffer* fb) ResetBuffer(FlatBuffer* fb)
{ {

View File

@ -7,7 +7,6 @@ import buffer;
import ui : Nil; import ui : Nil;
import ui; import ui;
import parsing; import parsing;
import views;
import std.algorithm.comparison : clamp; import std.algorithm.comparison : clamp;
import std.format; import std.format;
@ -50,13 +49,16 @@ struct Ctx
u64 frame; u64 frame;
u64 f_idx; u64 f_idx;
Notification* active_notifications;
Notification* free_notifications;
LinkedList!(UIInput) events; LinkedList!(UIInput) events;
IVec2 mouse_pos; IVec2 mouse_pos;
FontFace font; FontFace font;
FontGlyphs[FS] glyph_sets; FontSet[FS] font_sets;
u32 glyph_sets_used; u32 font_sets_used;
bool[FO] glyphs_to_load; bool[FO] fonts_to_load;
bool prev_has_tex; bool prev_has_tex;
u32 prev_atlas_index; u32 prev_atlas_index;
@ -71,6 +73,7 @@ struct Ctx
UIPanel* base_panel; UIPanel* base_panel;
UIPanel* focused_panel; UIPanel* focused_panel;
UIPanel* free_panels;
u64 last_hover_frame; u64 last_hover_frame;
@ -95,6 +98,14 @@ struct Ctx
alias rd_ctx this; alias rd_ctx this;
} }
struct Notification
{
Notification* next;
string message;
f32 anim_progress = 0.0;
f32 elapsed = 0.0;
}
struct TextInputBuffer struct TextInputBuffer
{ {
u8[] data; u8[] data;
@ -128,6 +139,8 @@ struct Editor
i64 line_offset; i64 line_offset;
i64 line_height; i64 line_height;
Editor* free_next;
alias buf this; alias buf this;
} }
@ -218,15 +231,24 @@ 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 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; } alias ES = EditState;
__gshared bool g_input_mode = false;
__gshared Ctx g_ctx; __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;
const Command NO_CMD = { const Command NO_CMD = {
name: [], name: [],
type: CT.None, type: CT.None,
}; };
Command[20] CMD_LIST = [ Command[21] CMD_LIST = [
{ {
name: "Open", name: "Open",
cmd: "open", cmd: "open",
@ -239,7 +261,7 @@ Command[20] CMD_LIST = [
cmd: "save", cmd: "save",
desc: "Save the current file in the focused editor view.", desc: "Save the current file in the focused editor view.",
type: CT.Callback, type: CT.Callback,
fn: &NotImplementedCallback, fn: &SaveFileWindow,
}, },
{ {
name: "New File", name: "New File",
@ -262,6 +284,13 @@ Command[20] CMD_LIST = [
type: CT.Callback, type: CT.Callback,
fn: &SplitHorizontally, fn: &SplitHorizontally,
}, },
{
name: "Close Panel",
cmd: "close",
desc: "Close the currently highlighted panel, if it's the last panel it will have the file closed instead.",
type: CT.Callback,
fn: &ClosePanelCmd,
},
{ {
name: "Test Option 1", name: "Test Option 1",
cmd: "testopt1", cmd: "testopt1",
@ -369,6 +398,59 @@ Command[20] CMD_LIST = [
}, },
]; ];
bool
ClosePanelCmd(Ctx* ctx)
{
Clear(ctx.focused_panel.ed);
if(ctx.focused_panel != ctx.base_panel)
{
DLLRemove(ctx.focused_panel.parent, ctx.focused_panel, g_NIL_PANEL);
u64 child_count;
UIPanel* removed = ctx.focused_panel;
UIPanel* parent = removed.parent;
if(!Nil(parent.first) && parent.first == parent.last)
{
UIPanel* only_child = parent.first;
parent.ed = only_child.ed;
only_child.ed = null;
DLLRemove(parent, only_child, g_NIL_PANEL);
PushFree(&ctx.free_panels, only_child);
Focus(parent);
}
else
{
for(UIPanel* child = parent.first; !Nil(child); child = child.next)
{
child_count += 1;
}
f32 add_pct = ctx.focused_panel.pct/child_count;
for(UIPanel* child = parent.first; !Nil(child); child = child.next)
{
child.pct += add_pct;
}
UIPanel* focus = ctx.base_panel;
for(UIPanel* panel = ctx.base_panel; true; panel = panel.first)
{
if(!CheckNil(g_NIL_ED, panel.ed))
{
Focus(panel);
break;
}
}
}
PushFree(&ctx.free_panels, removed);
}
return true;
}
bool bool
SplitVertically(Ctx* ctx) SplitVertically(Ctx* ctx)
{ {
@ -422,7 +504,6 @@ Cycle(Inputs* inputs)
ResetScratch(MB(4)); ResetScratch(MB(4));
g_delta = DeltaTime(&g_ctx.timer); g_delta = DeltaTime(&g_ctx.timer);
g_input_mode = Active(ES.InputMode);
BeginUI(inputs); BeginUI(inputs);
@ -455,16 +536,22 @@ InitCtx(PlatformWindow* window)
{ {
Ctx* ctx = &g_ctx; 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;
ctx.window = window; ctx.window = window;
ctx.arena = CreateArena(MB(2)); ctx.arena = CreateArena(MB(2));
ctx.cmd.text_input.data = Alloc!(u8)(1024); ctx.cmd.text_input.data = Alloc!(u8)(1024);
ctx.cmd.exec_cmd_input.data = Alloc!(u8)(1024); ctx.cmd.exec_cmd_input.data = Alloc!(u8)(1024);
ctx.cmd.exec_cmd_arena = CreateArena(MB(1)); ctx.cmd.exec_cmd_arena = CreateArena(MB(1));
ctx.timer = CreateTimer(); ctx.timer = CreateTimer();
ctx.free_panels = g_NIL_PANEL;
InitUI(ctx); InitUI(ctx);
ctx.base_panel = CreatePanel(CreateEditor()); ctx.base_panel = CreatePanel();
Focus(ctx.base_panel); Focus(ctx.base_panel);
if(getcwd() != "/") if(getcwd() != "/")
@ -505,12 +592,6 @@ GetCtx()
return &g_ctx; return &g_ctx;
} }
bool
EditModeActive()
{
return g_input_mode;
}
char[] char[]
ToAbsolutePath(string file_name) ToAbsolutePath(string file_name)
{ {
@ -648,7 +729,6 @@ Editor*
CreateEditor() CreateEditor()
{ {
Editor* ed = Alloc!(Editor)(&g_ctx.arena); Editor* ed = Alloc!(Editor)(&g_ctx.arena);
ed.arena = CreateArena(MB(4)); ed.arena = CreateArena(MB(4));
ed.buf = CreateFlatBuffer([], []); ed.buf = CreateFlatBuffer([], []);
ed.editor_id = g_ctx.editor_id_incr++; ed.editor_id = g_ctx.editor_id_incr++;
@ -656,55 +736,6 @@ CreateEditor()
return ed; return ed;
} }
/*
void
AddEditor(Panel* target, Axis2D axis)
{
if(CheckNil(g_NIL_PANEL, target.parent) || target.parent.layout_axis != axis)
{
Panel* first = CreatePanel(target.ed);
Panel* second = CreatePanel(CreateEditor());
target.layout_axis = axis;
target.ed = null;
DLLPush(target, first, g_NIL_PANEL);
DLLPush(target, second, g_NIL_PANEL);
first.parent = second.parent = target;
FocusEditor(second);
}
else if(target.parent.layout_axis == axis)
{
Panel* panel = CreatePanel(CreateEditor());
DLLPush(target.parent, target, panel);
panel.parent = target.parent;
UIItem* ed_item = GetEditorItem(panel.ed);
/*
if(!Nil(ed_item.parent))
{
u64 count;
for(UIItem* c = ed_item.parent.first; !Nil(c); c = c.next)
{
count += 1;
}
f32 pct = 1.0/cast(f32)count;
for(UIItem* c = ed_item.parent.first; !Nil(c); c = c.next)
{
c.resize_pct = pct;
}
}
* /
FocusEditor(panel);
}
}
*/
pragma(inline) void pragma(inline) void
InsertInputToBuf(Ctx* ctx, Editor* ed) InsertInputToBuf(Ctx* ctx, Editor* ed)
{ {
@ -932,7 +963,7 @@ HandleInputs(UIPanel* p, LinkedList!(UIInput)* inputs, bool hovered, bool focuse
{ {
if(hovered) if(hovered)
{ {
SetOffset(ed, node.scroll*30); SetOffset(ed, node.scroll*10);
taken = true; taken = true;
} }
} break; } break;
@ -1173,6 +1204,7 @@ HandleCmdMode(Ctx* ctx, bool* enter_hit, bool* buffer_changed, bool* selection_c
{ {
assert(ctx.cmd.cmds[ctx.cmd.selected].fn, "Callback type doesn't have a function pointer set"); assert(ctx.cmd.cmds[ctx.cmd.selected].fn, "Callback type doesn't have a function pointer set");
ctx.state = ES.RunCmd; ctx.state = ES.RunCmd;
taken = true;
} break; } break;
default: break; default: break;
} }
@ -1180,14 +1212,7 @@ HandleCmdMode(Ctx* ctx, bool* enter_hit, bool* buffer_changed, bool* selection_c
else else
{ {
*enter_hit = true; *enter_hit = true;
} taken = true;
} break;
case Backspace:
{
if(text_buffer.length)
{
text_buffer.length -= 1;
*buffer_changed = true;
} }
} break; } break;
case Up, Down: case Up, Down:
@ -1196,21 +1221,16 @@ HandleCmdMode(Ctx* ctx, bool* enter_hit, bool* buffer_changed, bool* selection_c
*selected = (*selected)+incr; *selected = (*selected)+incr;
*selection_changed = true; *selection_changed = true;
taken = true;
} break; } break;
case Escape: case Escape:
{ {
ResetCtx(); ResetCtx();
taken = true;
} break; } break;
default: default:
{ {
if(ev.text.length) *buffer_changed = taken = HandleTextInput(text_buffer, ev);
{
Check(text_buffer, ev.text.length);
text_buffer.data[text_buffer.length .. text_buffer.length+ev.text.length] = cast(u8[])ev.text[0 .. $];
text_buffer.length += ev.text.length;
*buffer_changed = true;
}
} break; } break;
} }
} break; } break;
@ -1224,6 +1244,37 @@ HandleCmdMode(Ctx* ctx, bool* enter_hit, bool* buffer_changed, bool* selection_c
} }
} }
bool
HandleTextInput(TextInputBuffer* text_input, UIInput* ev)
{
bool taken;
switch(ev.key) with(Input)
{
case Backspace:
{
if(text_input.length)
{
text_input.length -= 1;
taken = true;
}
} break;
default:
{
if(ev.text.length)
{
Check(text_input, ev.text.length);
text_input.data[text_input.length .. text_input.length+ev.text.length] = cast(u8[])ev.text[0 .. $];
text_input.length += ev.text.length;
taken = true;
}
} break;
}
return taken;
}
pragma(inline) void pragma(inline) void
Check(TextInputBuffer* text_buffer, u64 length) Check(TextInputBuffer* text_buffer, u64 length)
{ {

View File

@ -169,6 +169,8 @@ const TokenStyle[TT.max] TOKEN_STYLES = [
TT.Comment: TS.Comment, TT.Comment: TS.Comment,
]; ];
// Bad idea vvv
const TT[128] D_STD_TOKEN = [ const TT[128] D_STD_TOKEN = [
'$': TT.Dollar, '$': TT.Dollar,
'(': TT.LeftParen, '(': TT.LeftParen,
@ -190,7 +192,6 @@ const TT[128] D_OP_TOKEN = [
'^': TT.Caret, '^': TT.Caret,
'&': TT.Ampersand, '&': TT.Ampersand,
'*': TT.Asterisk, '*': TT.Asterisk,
'/': TT.Slash,
'-': TT.Minus, '-': TT.Minus,
'=': TT.Equals, '=': TT.Equals,
'+': TT.Plus, '+': TT.Plus,

File diff suppressed because it is too large Load Diff

View File

@ -1,29 +0,0 @@
import dlib;
import ui;
import ui : YELLOW, BLUE, RED;
import editor;
import parsing;
import buffer;
import std.algorithm.comparison : clamp;
import std.format;
import std.conv;
__gshared const Editor g_nil_ed;
__gshared Editor* g_NIL_ED;
/******
- Set up "themes" for different components (e.g. command palette window, editor view, status bar) then have the ability to create styles and apply them to different sections via a string lookup, e.g. start of function to draw cmd palette call ApplyTheme(selected_theme_name) then do code to draw things, get to options and call ApplyTheme(selected_theme_name_for_options), etc.
- Move scrolling behaviour into ui.d and externally be oblivious to it, e.g. just feed it all lines of text in a text buffer, will have to specifically handle extreme cases later but initially i think it should be fine
******/
const f32 CMD_X_PAD = 8.0;
const f32 CMD_Y_PAD = 4.0;
const u32 CMD_TITLE_PX = 14;
const u32 CMD_SUB_PX = 10;
shared static this()
{
g_NIL_ED = cast(Editor*)&g_nil_ed;
}

95844
test/files/miniaudio.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,30 @@
import std.stdio; import std.stdio;
struct TestStruct
{
int value;
float value2;
double value3;
}
struct TestStruct2
{
int value;
double value2;
}
struct TestStruct3
{
int value;
uint value2;
}
@nogc: bool @nogc: bool
Test(float flt, int i) Test(float flt, int i)
{ {
int x = 5; int x = 5;
int y = 10; int y = 10;
int z = 15;
x = x + y; x = x + y;
x = x - y; x = x - y;
@ -67,6 +87,12 @@ int WithParameters(float x, string y)
return cast(int)(x) - y[0]; return cast(int)(x) - y[0];
} }
struct TestStruct
{
int value;
float value2;
}
T MacroFunc(T)(T x) T MacroFunc(T)(T x)
{ {
return x; return x;