First prototype of coroutine based UI

This commit is contained in:
krzosa
2025-12-28 16:12:22 +01:00
parent 06cb073832
commit 2acf2c189c
14 changed files with 138 additions and 89 deletions

View File

@@ -5,13 +5,14 @@ Use session 1:
- OpenCommand in command window freezes the app - OpenCommand in command window freezes the app
- SDL popups are not working on linux ... - SDL popups are not working on linux ...
- CloseAll idea: should create a buffer interface with list of buffers, user would be able to select which buffers to save and which not, then button UICloseAll - CloseAll idea: should create a buffer interface with list of buffers, user would be able to select which buffers to save and which not, then button UICloseAll
- Creating files more efficiently - Creating files more efficiently, renaming
- Dialog popup on save? Or ctrl-shift-s? - Dialog popup on save? Or ctrl-shift-s?
- Maybe rename in bar and do :Rename - Maybe rename in bar and do :Rename
New UI Session New UI Session
- Cleanup String16/String with Open and EvalCommands after lua refactor - Cleanup String16/String with Open and EvalCommands after lua refactor
- Rename GotoBackward and others to Jump - Uneditable buffers ?
- Maybe marked allocations??? So that we can associate allocations with a buffer or view and then dealloc all at the same time
- Open with seek string (open at pattern) filename:32 filename:/^Window$/ - Open with seek string (open at pattern) filename:32 filename:/^Window$/
- build console window - build console window

View File

@@ -94,7 +94,7 @@ EM_JS(void, JS_Breakpoint, (), {
BREAK(); \ BREAK(); \
} }
#define InvalidCodepath() Assert(!"invalid codepath") #define InvalidCodepath() Assert(!"invalid codepath")
#define ElseInvalidCodepath() else {InvalidCodepath()} #define ElseInvalidCodepath() else {InvalidCodepath();}
#define KiB(x) ((x##ull) * 1024ull) #define KiB(x) ((x##ull) * 1024ull)
#define MiB(x) (KiB(x) * 1024ull) #define MiB(x) (KiB(x) * 1024ull)

View File

@@ -186,6 +186,7 @@ void BeginFrameRender(float wx, float wy) {
// ---------- EndFrameRender for ES3 ---------- // ---------- EndFrameRender for ES3 ----------
void EndFrameRender(float wx, float wy, Color color) { void EndFrameRender(float wx, float wy, Color color) {
ProfileFunction();
glEnable(GL_BLEND); glEnable(GL_BLEND);
glEnable(GL_SCISSOR_TEST); glEnable(GL_SCISSOR_TEST);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

View File

@@ -152,5 +152,6 @@ void Test(mco_coro *co) {
void InitTests() { void InitTests() {
ConfigWaitForEvents = false; ConfigWaitForEvents = false;
TestDir = Format(TestArena, "%S/test_env", GetExeDir(TestArena)); TestDir = Format(TestArena, "%S/test_env", GetExeDir(TestArena));
CoAdd(Test); CoData *data = CoAdd(Test);
data->dont_wait_until_resolved = true;
} }

View File

@@ -1506,9 +1506,9 @@ String16 ToUnixString16(Allocator allocator, String string_) {
Buffer *BufferOpenFile(String path) { Buffer *BufferOpenFile(String path) {
ProfileFunction(); ProfileFunction();
Allocator sys_allocator = GetSystemAllocator(); Allocator sys_allocator = GetSystemAllocator();
Scratch scratch; Scratch scratch;
path = GetAbsolutePath(scratch, path); path = GetAbsolutePath(scratch, path);
Buffer *buffer = GetBuffer(path); Buffer *buffer = GetBuffer(path);
if (!IsNull(buffer) || (IsNull(buffer) && buffer->name == path)) { if (!IsNull(buffer) || (IsNull(buffer) && buffer->name == path)) {
return buffer; return buffer;
@@ -1519,10 +1519,11 @@ Buffer *BufferOpenFile(String path) {
} else if (IsDir(path)) { } else if (IsDir(path)) {
buffer = CreateBuffer(sys_allocator, path); buffer = CreateBuffer(sys_allocator, path);
buffer->is_dir = true; buffer->is_dir = true;
buffer->garbage = true;
} else { } else {
String string = ReadFile(scratch, path); String string = ReadFile(scratch, path);
buffer = CreateBuffer(sys_allocator, path, string.len * 4 + 4096); buffer = CreateBuffer(sys_allocator, path, string.len * 4 + 4096);
buffer->len = ConvertUTF8ToUTF16UnixLine(string, buffer->str, buffer->cap); buffer->len = ConvertUTF8ToUTF16UnixLine(string, buffer->str, buffer->cap);
buffer->file_mod_time = GetFileModTime(path); buffer->file_mod_time = GetFileModTime(path);
UpdateLines(buffer, {}, String16{(char16_t *)buffer->data, buffer->len}); UpdateLines(buffer, {}, String16{(char16_t *)buffer->data, buffer->len});
} }

View File

@@ -730,7 +730,8 @@ void Coro_OpenCode(mco_coro *co) {
void OpenCode(String dir) { void OpenCode(String dir) {
Coro_OpenCodeDir = dir; Coro_OpenCodeDir = dir;
CoAdd(Coro_OpenCode); CoData *data = CoAdd(Coro_OpenCode);
data->dont_wait_until_resolved = true;
} }
void Command_OpenCode() { void Command_OpenCode() {
@@ -747,7 +748,7 @@ void Command_CloseWindow() {
} RegisterCommand(Command_CloseWindow, ""); } RegisterCommand(Command_CloseWindow, "");
SaveResult TrySavingBuffer(Buffer *buffer) { SaveResult TrySavingBuffer(Buffer *buffer) {
if (buffer->special || buffer->is_dir || buffer->garbage) { if (buffer->special || buffer->garbage) {
return SAVE_NO; return SAVE_NO;
} }
if (buffer->dirty) { if (buffer->dirty) {
@@ -781,15 +782,13 @@ SaveResult TrySavingAllBuffers() {
return SAVE_YES; return SAVE_YES;
} }
void Command_Close() { void Coro_Close(mco_coro *co) {
BSet main = GetBSet(LastActiveLayoutWindowID); BSet main = GetBSet(LastActiveLayoutWindowID);
if (TrySavingBuffer(main.buffer) == SAVE_CANCEL) { if (main.buffer->special || main.buffer->garbage) {
Close(main.view->id);
return; return;
} }
main.window->active_view = FindInactiveView();
Close(main.view->id);
bool ref = false; bool ref = false;
For (Views) { For (Views) {
if (it->id == main.view->id) { if (it->id == main.view->id) {
@@ -801,9 +800,56 @@ void Command_Close() {
} }
} }
if (!ref) { if (ref) {
Close(main.buffer->id); Close(main.view->id);
return;
} }
if (!main.buffer->dirty) {
Close(main.buffer->id);
return;
}
Buffer *buffer = main.buffer;
JumpGarbageBuffer(&main);
NextActiveWindowID = main.window->id;
RawAppendf(main.buffer, R"==(
Do you want to save [%S] before closing?
:Yes :No :Cancel
)==", buffer->name);
main.view->carets[0] = FindNext(main.buffer, u":Yes", MakeCaret(0));
main.view->carets[0].range.min = main.view->carets[0].range.max;
Add(&main.view->hooks, {"Yes", "enter", [](){BSet active = GetBSet(ActiveWindowID); active.view->hook_cmd = "Yes";}, ParseKeyCached("enter")});
Add(&main.view->hooks, {"No", "", [](){BSet active = GetBSet(ActiveWindowID); active.view->hook_cmd = "No";}, ParseKeyCached("")});
Add(&main.view->hooks, {"Cancel", "escape", [](){BSet active = GetBSet(ActiveWindowID); active.view->hook_cmd = "Cancel";}, ParseKeyCached("escape")});
for (;;) {
if (main.window->active_view != main.view->id || main.window->close) {
Close(main.buffer->id);
return;
}
if (main.view->hook_cmd != "") {
break;
}
CoYield(co);
}
Close(main.buffer->id);
if (main.view->hook_cmd == "Yes") {
SaveBuffer(buffer);
Close(buffer->id);
} else if (main.view->hook_cmd == "No") {
Close(buffer->id);
} else if (main.view->hook_cmd == "Cancel") {
return;
} ElseInvalidCodepath();
}
void Command_Close() {
CoAdd(Coro_Close);
} RegisterCommand(Command_Close, "ctrl-w"); } RegisterCommand(Command_Close, "ctrl-w");
void Command_Quit() { void Command_Quit() {
@@ -829,63 +875,17 @@ void Command_CloseAll() {
} }
} RegisterCommand(Command_CloseAll, ""); } RegisterCommand(Command_CloseAll, "");
void Command_ForceClose() { void Command_JumpBack() {
BSet active = GetBSet(ActiveWindowID);
Close(active.buffer->id);
Close(active.view->id);
}
// @todo: make these uneditable?
// @todo: maybe turn this into a coroutine???
String BufferIDea = R"==(
Do you want to save buffer? [C:/Programming/text_editor/build.sh]
:Yes :No
)==";
void Command_TestUIIdea() {
BSet main = GetBSet(LastActiveLayoutWindowID);
JumpGarbageBuffer(&main, "ui");
NextActiveWindowID = main.window->id;
Add(&main.view->hooks, {"Yes", "enter", [](){
BSet active = GetBSet(ActiveWindowID);
Caret a = FindNext(active.buffer, u"[", MakeCaret(0));
Caret b = FindNext(active.buffer, u"]", MakeCaret(0));
if (GetSize(a) == 0 || GetSize(b) == 0) {
ReportWarningf("Failed to save, '[' or ']' not found");
return;
}
Scratch scratch;
String16 string16 = GetString(active.buffer, {a.range.max, b.range.min});
String string = ToString(scratch, string16);
Buffer *buffer = GetBuffer(string, NULL);
if (buffer) {
SaveBuffer(buffer);
} else {
ReportWarningf("Failed to save, buffer not found: %S", string);
}
Close(active.buffer->id);
Close(active.view->id);
}, ParseKey(Perm, "enter", "Command_TestUIIdea_Yes")}); // @todo: fix memory leak
Add(&main.view->hooks, {"No", "escape", Command_ForceClose, ParseKeyCached("escape")});
RawAppend(main.buffer, BufferIDea);
main.view->carets[0] = FindNext(main.buffer, u":Yes", MakeCaret(0));
main.view->carets[0].range.min = main.view->carets[0].range.max;
} RegisterCommand(Command_TestUIIdea, "");
void Command_GotoBackward() {
BSet main = GetBSet(LastActiveLayoutWindowID); BSet main = GetBSet(LastActiveLayoutWindowID);
main.window->skip_checkpoint = true; main.window->skip_checkpoint = true;
GotoBackward(main.window); JumpBack(main.window);
} RegisterCommand(Command_GotoBackward, "alt-q | mousex1"); } RegisterCommand(Command_JumpBack, "alt-q | mousex1");
void Command_GotoForward() { void Command_JumpForward() {
BSet main = GetBSet(LastActiveLayoutWindowID); BSet main = GetBSet(LastActiveLayoutWindowID);
main.window->skip_checkpoint = true; main.window->skip_checkpoint = true;
GotoForward(main.window); JumpForward(main.window);
} RegisterCommand(Command_GotoForward, "alt-shift-q | mousex2"); } RegisterCommand(Command_JumpForward, "alt-shift-q | mousex2");
void Command_OpenUpFolder() { void Command_OpenUpFolder() {
BSet main = GetBSet(LastActiveLayoutWindowID); BSet main = GetBSet(LastActiveLayoutWindowID);

View File

@@ -102,4 +102,4 @@ void Command_Cut() {
SaveCaretHistoryBeforeBeginEdit(active.buffer, active.view->carets); SaveCaretHistoryBeforeBeginEdit(active.buffer, active.view->carets);
ClipboardCopy(active.view); ClipboardCopy(active.view);
Replace(active.view, u""); Replace(active.view, u"");
} RegisterCommand(Command_Cut, "ctrl-x"); } RegisterCommand(Command_Cut, "ctrl-x");

View File

@@ -164,6 +164,9 @@ struct CachedTrigger {
Array<CachedTrigger> CachedTriggers; Array<CachedTrigger> CachedTriggers;
Trigger *ParseKeyCached(String key) { Trigger *ParseKeyCached(String key) {
if (key.len == 0) {
return NULL;
}
For (CachedTriggers) { For (CachedTriggers) {
if (it.key == key) { if (it.key == key) {
return it.trigger; return it.trigger;

View File

@@ -11,7 +11,7 @@ void CoRemove(String name) {
} }
#define CoAdd(x) CoAddEx(x, #x) #define CoAdd(x) CoAddEx(x, #x)
mco_coro *CoAddEx(CoroutineProc *proc, String name) { CoData *CoAddEx(CoroutineProc *proc, String name) {
CoRemove(name); CoRemove(name);
mco_desc desc = mco_desc_init(proc, 0); mco_desc desc = mco_desc_init(proc, 0);
@@ -24,7 +24,7 @@ mco_coro *CoAddEx(CoroutineProc *proc, String name) {
mco_resume(coro); mco_resume(coro);
Add(&ActiveCoroutines, {coro, name}); Add(&ActiveCoroutines, {coro, name});
return coro; return GetLast(ActiveCoroutines);
} }
void CoUpdate(Event *event) { void CoUpdate(Event *event) {
@@ -46,6 +46,8 @@ void CoUpdate(Event *event) {
ReportWarningf("failed to resume coroutine %d", ok); ReportWarningf("failed to resume coroutine %d", ok);
mco_destroy(it.co); mco_destroy(it.co);
remove_item = true; remove_item = true;
} else {
it.dont_wait_until_resolved = true;
} }
} }
} }
@@ -62,7 +64,7 @@ Event *CoYield(mco_coro *co) {
Assert(ok == MCO_SUCCESS); Assert(ok == MCO_SUCCESS);
Event *event = NULL; Event *event = NULL;
ok = mco_pop(co, &event, sizeof(Event *)); ok = mco_pop(co, &event, sizeof(Event *));
Assert(ok == MCO_SUCCESS); Assert(ok == MCO_SUCCESS);
return event; return event;

View File

@@ -87,7 +87,7 @@ Array<Variable> Variables;
Array<Process> ActiveProcesses = {}; Array<Process> ActiveProcesses = {};
Array<String> ProcessEnviroment = {}; Array<String> ProcessEnviroment = {};
struct CoData { mco_coro *co; String name; }; struct CoData { mco_coro *co; String name; bool dont_wait_until_resolved; };
Array<CoData> ActiveCoroutines; Array<CoData> ActiveCoroutines;
@@ -162,5 +162,5 @@ RegisterVariable(String, ConfigFont, "/home/krz/text_editor/package/CascadiaMono
RegisterVariable(String, ConfigVCVarsall, "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build/vcvars64.bat"); RegisterVariable(String, ConfigVCVarsall, "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build/vcvars64.bat");
RegisterVariable(Float, ConfigUndoMergeTimeWindow, 0.3); RegisterVariable(Float, ConfigUndoMergeTimeWindow, 0.3);
RegisterVariable(Float, ConfigJumpHistoryMergeTimeWindow, 0.3); RegisterVariable(Float, ConfigJumpHistoryMergeTimeWindow, 0.3);
RegisterVariable(Int, ConfigJumpHistorySize, 512); RegisterVariable(Int, ConfigJumpHistorySize, 4096);
RegisterVariable(String, ConfigInternetBrowser, "firefox"); RegisterVariable(String, ConfigInternetBrowser, "firefox");

View File

@@ -78,6 +78,7 @@ void SetMouseCursor(SDL_SystemCursor id) {
#endif #endif
void SetMouseCursor(Event event) { void SetMouseCursor(Event event) {
ProfileFunction();
Scratch scratch; Scratch scratch;
Array<Window *> order = GetWindowZOrder(scratch); Array<Window *> order = GetWindowZOrder(scratch);
Vec2I mouse = MouseVec2I(); Vec2I mouse = MouseVec2I();
@@ -448,7 +449,6 @@ void EvalCommand(String16 command) {
EvalCommand(ToString(scratch, command)); EvalCommand(ToString(scratch, command));
} }
void GarbageCollect() { void GarbageCollect() {
if (RunGCThisFrame == false) { if (RunGCThisFrame == false) {
return; return;
@@ -475,7 +475,11 @@ void GarbageCollect() {
InvalidCodepath(); InvalidCodepath();
} }
Buffer *buffer = GetBuffer(it->active_buffer); Buffer *buffer = GetBuffer(it->active_buffer, NULL);
if (buffer == NULL || buffer->close) {
it->close = true;
}
if (!it->close) { if (!it->close) {
if (!buffer->garbage) { if (!buffer->garbage) {
continue; continue;
@@ -487,7 +491,7 @@ void GarbageCollect() {
} }
} }
RawAppendf(GCInfoBuffer, "View %d %S\n", (int)it->id.id, buffer->name); RawAppendf(GCInfoBuffer, "View %d %S\n", (int)it->id.id, buffer ? buffer->name : String{"NULL"});
remove_item = true; remove_item = true;
Dealloc(it); Dealloc(it);
} }
@@ -527,6 +531,11 @@ void GarbageCollect() {
Dealloc(&it->goto_redo); Dealloc(&it->goto_redo);
Dealloc(sys_allocator, it); Dealloc(sys_allocator, it);
remove_item = true; remove_item = true;
} else {
View *view = FindView(it->active_view, NULL);
if (!view) {
JumpToLastValidView(it);
}
} }
} }
} }
@@ -673,8 +682,17 @@ void MainLoop() {
Update(it); Update(it);
} }
bool dont_wait_until_resolved = false;
For (ActiveCoroutines) {
if (it.dont_wait_until_resolved) {
dont_wait_until_resolved = true;
break;
}
}
WaitForEvents = ConfigWaitForEvents; WaitForEvents = ConfigWaitForEvents;
if (IsDocumentSelectionValid() || IsScrollbarSelectionValid() || ActiveProcesses.len || ActiveCoroutines.len) { if (IsDocumentSelectionValid() || IsScrollbarSelectionValid() || ActiveProcesses.len || dont_wait_until_resolved) {
WaitForEvents = false; WaitForEvents = false;
} }
@@ -847,7 +865,8 @@ int main(int argc, char **argv)
ReportConsolef("WorkDir = %S", WorkDir); ReportConsolef("WorkDir = %S", WorkDir);
if (Testing) InitTests(); if (Testing) InitTests();
#if OS_WINDOWS #if OS_WINDOWS
CoAdd(Windows_SetupVCVarsall); CoData *co_data = CoAdd(Windows_SetupVCVarsall);
co_data->dont_wait_until_resolved = true;
#endif #endif
#if OS_WASM #if OS_WASM
emscripten_set_main_loop(MainLoop, 0, 1); emscripten_set_main_loop(MainLoop, 0, 1);

View File

@@ -15,6 +15,7 @@ struct View {
Caret main_caret_on_begin_frame; Caret main_caret_on_begin_frame;
bool update_scroll; bool update_scroll;
String hook_cmd;
Array<CommandData> hooks; Array<CommandData> hooks;
String16 prev_search_line; String16 prev_search_line;
struct { struct {

View File

@@ -235,7 +235,7 @@ Int ScreenSpaceToBufferPosErrorOutOfBounds(Window *window, View *view, Buffer *b
return result; return result;
} }
GotoCrumb GetCrumb(Array<GotoCrumb> *cr) { GotoCrumb PopCrumb(Array<GotoCrumb> *cr) {
for (; cr->len;) { for (; cr->len;) {
GotoCrumb c = Pop(cr); GotoCrumb c = Pop(cr);
View *view = FindView(c.view_id, NULL); View *view = FindView(c.view_id, NULL);
@@ -246,13 +246,33 @@ GotoCrumb GetCrumb(Array<GotoCrumb> *cr) {
return {}; return {};
} }
void GotoBackward(Window *window) { View *GetLastValidView(Window *window) {
For (IterateInReverse(&window->goto_redo)) {
if (it.view_id == window->active_view) continue;
View *view = FindView(it.view_id, NULL);
if (view) return view;
}
For (IterateInReverse(&window->goto_history)) {
if (it.view_id == window->active_view) continue;
View *view = FindView(it.view_id, NULL);
if (view) return view;
}
return Views[0];
}
View *JumpToLastValidView(Window *window) {
View *view = GetLastValidView(window);
window->active_view = view->id;
return view;
}
void JumpBack(Window *window) {
if (window->jump_history == false) return; if (window->jump_history == false) return;
if (window->goto_history.len <= 0) return; if (window->goto_history.len <= 0) return;
BSet set = GetBSet(window); BSet set = GetBSet(window);
Add(&window->goto_redo, {set.view->id, set.view->carets[0], GetTimeSeconds()}); Add(&window->goto_redo, {set.view->id, set.view->carets[0], GetTimeSeconds()});
GotoCrumb c = GetCrumb(&window->goto_history); GotoCrumb c = PopCrumb(&window->goto_history);
window->active_view = c.view_id; window->active_view = c.view_id;
View *view = GetView(c.view_id); View *view = GetView(c.view_id);
view->carets[0] = c.caret; view->carets[0] = c.caret;
@@ -261,18 +281,18 @@ void GotoBackward(Window *window) {
if (window->goto_history.len) { if (window->goto_history.len) {
GotoCrumb *next = GetLast(window->goto_history); GotoCrumb *next = GetLast(window->goto_history);
if (c.view_id == next->view_id && c.time - next->time <= ConfigJumpHistoryMergeTimeWindow) { if (c.view_id == next->view_id && c.time - next->time <= ConfigJumpHistoryMergeTimeWindow) {
GotoBackward(window); JumpBack(window);
} }
} }
} }
void GotoForward(Window *window) { void JumpForward(Window *window) {
if (window->jump_history == false) return; if (window->jump_history == false) return;
if (window->goto_redo.len <= 0) return; if (window->goto_redo.len <= 0) return;
BSet set = GetBSet(window); BSet set = GetBSet(window);
Add(&window->goto_history, {set.view->id, set.view->carets[0], GetTimeSeconds()}); Add(&window->goto_history, {set.view->id, set.view->carets[0], GetTimeSeconds()});
GotoCrumb c = GetCrumb(&window->goto_redo); GotoCrumb c = PopCrumb(&window->goto_redo);
window->active_view = c.view_id; window->active_view = c.view_id;
View *view = GetView(c.view_id); View *view = GetView(c.view_id);
view->carets[0] = c.caret; view->carets[0] = c.caret;
@@ -281,7 +301,7 @@ void GotoForward(Window *window) {
if (window->goto_redo.len) { if (window->goto_redo.len) {
GotoCrumb *next = GetLast(window->goto_redo); GotoCrumb *next = GetLast(window->goto_redo);
if (c.view_id == next->view_id && next->time - c.time <= ConfigJumpHistoryMergeTimeWindow) { if (c.view_id == next->view_id && next->time - c.time <= ConfigJumpHistoryMergeTimeWindow) {
GotoForward(window); JumpForward(window);
} }
} }
} }

View File

@@ -160,7 +160,7 @@ void Command_ShowBufferList() {
NextActiveWindowID = command_bar.window->id; NextActiveWindowID = command_bar.window->id;
ResetBuffer(command_bar.buffer); ResetBuffer(command_bar.buffer);
For (Buffers) { For (Buffers) {
if (it->special || it->garbage || it->is_dir) { if (it->special || it->garbage) {
continue; continue;
} }
RawAppendf(command_bar.buffer, "\n%S", it->name); RawAppendf(command_bar.buffer, "\n%S", it->name);