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
- 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
- Creating files more efficiently
- Creating files more efficiently, renaming
- Dialog popup on save? Or ctrl-shift-s?
- Maybe rename in bar and do :Rename
New UI Session
- 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$/
- build console window

View File

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

View File

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

View File

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

View File

@@ -1519,6 +1519,7 @@ Buffer *BufferOpenFile(String path) {
} else if (IsDir(path)) {
buffer = CreateBuffer(sys_allocator, path);
buffer->is_dir = true;
buffer->garbage = true;
} else {
String string = ReadFile(scratch, path);
buffer = CreateBuffer(sys_allocator, path, string.len * 4 + 4096);

View File

@@ -730,7 +730,8 @@ void Coro_OpenCode(mco_coro *co) {
void OpenCode(String dir) {
Coro_OpenCodeDir = dir;
CoAdd(Coro_OpenCode);
CoData *data = CoAdd(Coro_OpenCode);
data->dont_wait_until_resolved = true;
}
void Command_OpenCode() {
@@ -747,7 +748,7 @@ void Command_CloseWindow() {
} RegisterCommand(Command_CloseWindow, "");
SaveResult TrySavingBuffer(Buffer *buffer) {
if (buffer->special || buffer->is_dir || buffer->garbage) {
if (buffer->special || buffer->garbage) {
return SAVE_NO;
}
if (buffer->dirty) {
@@ -781,15 +782,13 @@ SaveResult TrySavingAllBuffers() {
return SAVE_YES;
}
void Command_Close() {
void Coro_Close(mco_coro *co) {
BSet main = GetBSet(LastActiveLayoutWindowID);
if (TrySavingBuffer(main.buffer) == SAVE_CANCEL) {
if (main.buffer->special || main.buffer->garbage) {
Close(main.view->id);
return;
}
main.window->active_view = FindInactiveView();
Close(main.view->id);
bool ref = false;
For (Views) {
if (it->id == main.view->id) {
@@ -801,9 +800,56 @@ void Command_Close() {
}
}
if (!ref) {
Close(main.buffer->id);
if (ref) {
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");
void Command_Quit() {
@@ -829,63 +875,17 @@ void Command_CloseAll() {
}
} RegisterCommand(Command_CloseAll, "");
void Command_ForceClose() {
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() {
void Command_JumpBack() {
BSet main = GetBSet(LastActiveLayoutWindowID);
main.window->skip_checkpoint = true;
GotoBackward(main.window);
} RegisterCommand(Command_GotoBackward, "alt-q | mousex1");
JumpBack(main.window);
} RegisterCommand(Command_JumpBack, "alt-q | mousex1");
void Command_GotoForward() {
void Command_JumpForward() {
BSet main = GetBSet(LastActiveLayoutWindowID);
main.window->skip_checkpoint = true;
GotoForward(main.window);
} RegisterCommand(Command_GotoForward, "alt-shift-q | mousex2");
JumpForward(main.window);
} RegisterCommand(Command_JumpForward, "alt-shift-q | mousex2");
void Command_OpenUpFolder() {
BSet main = GetBSet(LastActiveLayoutWindowID);

View File

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

View File

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

View File

@@ -87,7 +87,7 @@ Array<Variable> Variables;
Array<Process> ActiveProcesses = {};
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;
@@ -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(Float, ConfigUndoMergeTimeWindow, 0.3);
RegisterVariable(Float, ConfigJumpHistoryMergeTimeWindow, 0.3);
RegisterVariable(Int, ConfigJumpHistorySize, 512);
RegisterVariable(Int, ConfigJumpHistorySize, 4096);
RegisterVariable(String, ConfigInternetBrowser, "firefox");

View File

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

View File

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

View File

@@ -235,7 +235,7 @@ Int ScreenSpaceToBufferPosErrorOutOfBounds(Window *window, View *view, Buffer *b
return result;
}
GotoCrumb GetCrumb(Array<GotoCrumb> *cr) {
GotoCrumb PopCrumb(Array<GotoCrumb> *cr) {
for (; cr->len;) {
GotoCrumb c = Pop(cr);
View *view = FindView(c.view_id, NULL);
@@ -246,13 +246,33 @@ GotoCrumb GetCrumb(Array<GotoCrumb> *cr) {
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->goto_history.len <= 0) return;
BSet set = GetBSet(window);
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;
View *view = GetView(c.view_id);
view->carets[0] = c.caret;
@@ -261,18 +281,18 @@ void GotoBackward(Window *window) {
if (window->goto_history.len) {
GotoCrumb *next = GetLast(window->goto_history);
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->goto_redo.len <= 0) return;
BSet set = GetBSet(window);
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;
View *view = GetView(c.view_id);
view->carets[0] = c.caret;
@@ -281,7 +301,7 @@ void GotoForward(Window *window) {
if (window->goto_redo.len) {
GotoCrumb *next = GetLast(window->goto_redo);
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;
ResetBuffer(command_bar.buffer);
For (Buffers) {
if (it->special || it->garbage || it->is_dir) {
if (it->special || it->garbage) {
continue;
}
RawAppendf(command_bar.buffer, "\n%S", it->name);