First prototype of coroutine based UI
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1506,9 +1506,9 @@ String16 ToUnixString16(Allocator allocator, String string_) {
|
||||
Buffer *BufferOpenFile(String path) {
|
||||
ProfileFunction();
|
||||
Allocator sys_allocator = GetSystemAllocator();
|
||||
Scratch scratch;
|
||||
Scratch scratch;
|
||||
|
||||
path = GetAbsolutePath(scratch, path);
|
||||
path = GetAbsolutePath(scratch, path);
|
||||
Buffer *buffer = GetBuffer(path);
|
||||
if (!IsNull(buffer) || (IsNull(buffer) && buffer->name == path)) {
|
||||
return buffer;
|
||||
@@ -1519,10 +1519,11 @@ 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);
|
||||
buffer->len = ConvertUTF8ToUTF16UnixLine(string, buffer->str, buffer->cap);
|
||||
String string = ReadFile(scratch, path);
|
||||
buffer = CreateBuffer(sys_allocator, path, string.len * 4 + 4096);
|
||||
buffer->len = ConvertUTF8ToUTF16UnixLine(string, buffer->str, buffer->cap);
|
||||
buffer->file_mod_time = GetFileModTime(path);
|
||||
UpdateLines(buffer, {}, String16{(char16_t *)buffer->data, buffer->len});
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -102,4 +102,4 @@ void Command_Cut() {
|
||||
SaveCaretHistoryBeforeBeginEdit(active.buffer, active.view->carets);
|
||||
ClipboardCopy(active.view);
|
||||
Replace(active.view, u"");
|
||||
} RegisterCommand(Command_Cut, "ctrl-x");
|
||||
} RegisterCommand(Command_Cut, "ctrl-x");
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,7 +64,7 @@ Event *CoYield(mco_coro *co) {
|
||||
Assert(ok == MCO_SUCCESS);
|
||||
|
||||
Event *event = NULL;
|
||||
ok = mco_pop(co, &event, sizeof(Event *));
|
||||
ok = mco_pop(co, &event, sizeof(Event *));
|
||||
Assert(ok == MCO_SUCCESS);
|
||||
|
||||
return event;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user