From 45c64951216dcc8d1771675154c464fb8179179e Mon Sep 17 00:00:00 2001 From: Krzosa Karol Date: Tue, 23 Jul 2024 14:13:03 +0200 Subject: [PATCH] Small refactor work on focus and windows --- src/text_editor/commands.cpp | 12 + ...s_clipboard.cpp => commands_clipboard.cpp} | 0 src/text_editor/commands_window.cpp | 432 ++++++++++++++++++ src/text_editor/text_editor.cpp | 64 +-- src/text_editor/text_editor.h | 13 +- src/text_editor/view_commands.cpp | 427 ----------------- src/text_editor/{windows.cpp => window.cpp} | 39 +- .../{view_draw.cpp => window_draw.cpp} | 0 8 files changed, 513 insertions(+), 474 deletions(-) rename src/text_editor/{view_commands_clipboard.cpp => commands_clipboard.cpp} (100%) create mode 100644 src/text_editor/commands_window.cpp delete mode 100644 src/text_editor/view_commands.cpp rename src/text_editor/{windows.cpp => window.cpp} (51%) rename src/text_editor/{view_draw.cpp => window_draw.cpp} (100%) diff --git a/src/text_editor/commands.cpp b/src/text_editor/commands.cpp index fefa062..d4caabe 100644 --- a/src/text_editor/commands.cpp +++ b/src/text_editor/commands.cpp @@ -184,4 +184,16 @@ Range EncloseWord(Buffer &buffer, Int pos) { result.min = MoveOnWhitespaceBoundaryBackward(buffer, pos); result.max = MoveOnWhitespaceBoundaryForward(buffer, pos); return result; +} + +void HandleGlobalCommands() { + if (CtrlPress(KEY_P)) { + Window *window = GetWindow(CommandWindowID); + if (window->visible) { + SetActiveWindow(LastActiveWindow); + } else { + SetActiveWindow(window->id); + } + window->visible = !window->visible; + } } \ No newline at end of file diff --git a/src/text_editor/view_commands_clipboard.cpp b/src/text_editor/commands_clipboard.cpp similarity index 100% rename from src/text_editor/view_commands_clipboard.cpp rename to src/text_editor/commands_clipboard.cpp diff --git a/src/text_editor/commands_window.cpp b/src/text_editor/commands_window.cpp new file mode 100644 index 0000000..56a185a --- /dev/null +++ b/src/text_editor/commands_window.cpp @@ -0,0 +1,432 @@ +void Command_Replace(View *view, String16 string) { + Buffer *buffer = GetBuffer(view->buffer_id); + Scratch scratch; + BeforeEdit(buffer, view->carets); + MergeCarets(&view->carets); + Array edits = {scratch}; + For(view->carets) AddEdit(&edits, it.range, string); + ApplyEdits(buffer, edits); + AfterEdit(buffer, &edits, &view->carets); +} + +void Command_DuplicateLine(View *view, int direction) { + Assert(direction == DIR_UP || direction == DIR_DOWN); + Buffer *buffer = GetBuffer(view->buffer_id); + BeforeEdit(buffer, view->carets); + For(view->carets) it = MakeCaret(GetFront(it)); + MergeCarets(&view->carets); + + Scratch scratch; + Array edits = {scratch}; + For(view->carets) { + Int line = PosToLine(*buffer, it.range.min); + Range range = GetLineRange(*buffer, line); + String16 string = Copy(scratch, GetString(*buffer, range)); + Int pos = direction == DIR_UP ? range.min : range.max; + AddEdit(&edits, Rng(pos), string); + } + ApplyEdits(buffer, edits); + AfterEdit(buffer, &edits, &view->carets); + + For(view->carets) it = MakeCaret(MovePos(*buffer, it.range.min, direction, false)); +} + +bool SHIFT_PRESSED = true; +void Command_MoveCursorsByPageSize(Window *window, int direction, bool shift = false) { + Assert(direction == DIR_UP || direction == DIR_DOWN); + View &view = *GetActiveView(window); + Buffer *buffer = GetBuffer(view.buffer_id); + + Rect2I visible_cells_rect = GetVisibleCells(*window); + Int y = GetSize(visible_cells_rect).y - 2; + if (direction == DIR_UP) y = -y; + + For(view.carets) { + XY xy = PosToXY(*buffer, GetFront(it)); + xy.line += y; + Int pos = XYToPos(*buffer, xy); + if (shift) { + it = ChangeFront(it, pos); + } else { + it = MakeCaret(pos); + } + } +} + +void Command_MoveCursorsToSide(Window *window, int direction, bool shift = false) { + Assert(direction == DIR_LEFT || direction == DIR_RIGHT); + View &view = *GetActiveView(window); + Buffer *buffer = GetBuffer(view.buffer_id); + + For(view.carets) { + Int end_of_buffer = 0; + Range line_range = GetLineRange(*buffer, PosToLine(*buffer, GetFront(it)), &end_of_buffer); + + Int pos = line_range.min; + if (direction == DIR_RIGHT) { + pos = line_range.max - (1 - end_of_buffer); + } + + if (shift) { + it = ChangeFront(it, pos); + } else { + it.range.max = it.range.min = pos; + } + } +} + +void Command_Delete(View *_view, int direction, bool ctrl = false) { + Assert(direction == DIR_LEFT || direction == DIR_RIGHT); + View &view = *_view; + Buffer *buffer = GetBuffer(view.buffer_id); + + // Select things to delete + For(view.carets) { + if (GetSize(it.range)) continue; + Int pos = MovePos(*buffer, it.range.min, direction, ctrl); + it = MakeCaret(pos, it.range.min); + } + + Command_Replace(&view, {}); +} + +void Command_CreateCursorVertical(View *_view, int direction) { + Assert(direction == DIR_UP || direction == DIR_DOWN); + View &view = *_view; + Buffer *buffer = GetBuffer(view.buffer_id); + + Scratch scratch; + Array arr = {scratch}; + For(view.carets) { + if (PosToLine(*buffer, it.range.min) == PosToLine(*buffer, it.range.max)) { + Int f = MovePos(*buffer, GetFront(it), direction); + Int b = MovePos(*buffer, GetBack(it), direction); + Add(&arr, MakeCaret(f, b)); + } else { + Int pos = direction == DIR_UP ? it.range.min : it.range.max; + Int min = MovePos(*buffer, pos, direction); + Add(&arr, MakeCaret(min)); + } + } + For(arr) Add(&view.carets, it); + MergeCarets(&view.carets); +} + +void HandleActiveWindowBindings(Window *window) { + View &view = *GetActiveView(window); + Buffer *buffer = GetBuffer(view.buffer_id); + Caret main_caret_on_begin_frame = view.carets[0]; + if (CtrlPress(KEY_F2)) { + LoadBigLine(buffer); + } else if (Press(KEY_F2)) { + LoadBigText(buffer); + } + + if (Press(KEY_ESCAPE)) { + view.carets.len = 1; + } + + if (CtrlAltPress(KEY_DOWN)) { + Command_DuplicateLine(&view, DIR_DOWN); + } else if (AltShiftPress(KEY_DOWN)) { + Command_CreateCursorVertical(&view, DIR_DOWN); + } else if (CtrlShiftPress(KEY_DOWN)) { + For(view.carets) it = ChangeFront(it, MovePos(*buffer, GetFront(it), DIR_DOWN, true)); + } else if (CtrlPress(KEY_DOWN)) { + For(view.carets) it = MakeCaret(MovePos(*buffer, it.range.max, DIR_DOWN, true)); + } else if (ShiftPress(KEY_DOWN)) { + For(view.carets) it = ChangeFront(it, MovePos(*buffer, GetFront(it), DIR_DOWN, false)); + } else if (Press(KEY_DOWN)) { + For(view.carets) { + if (GetSize(it.range) == 0) { + it = MakeCaret(MovePos(*buffer, it.range.max, DIR_DOWN, false)); + } else { + it = MakeCaret(it.range.max); + } + } + } + + if (CtrlAltPress(KEY_UP)) { + Command_DuplicateLine(&view, DIR_UP); + } else if (AltShiftPress(KEY_UP)) { + Command_CreateCursorVertical(&view, DIR_UP); + } else if (CtrlShiftPress(KEY_UP)) { + For(view.carets) it = ChangeFront(it, MovePos(*buffer, GetFront(it), DIR_UP, CTRL_PRESSED)); + } else if (CtrlPress(KEY_UP)) { + For(view.carets) it = MakeCaret(MovePos(*buffer, it.range.min, DIR_UP, CTRL_PRESSED)); + } else if (ShiftPress(KEY_UP)) { + For(view.carets) it = ChangeFront(it, MovePos(*buffer, GetFront(it), DIR_UP)); + } else if (Press(KEY_UP)) { + For(view.carets) { + if (GetSize(it.range) == 0) { + it = MakeCaret(MovePos(*buffer, it.range.min, DIR_UP)); + } else { + it = MakeCaret(it.range.min); + } + } + } + + if (CtrlShiftPress(KEY_LEFT)) { + For(view.carets) it = ChangeFront(it, MovePos(*buffer, GetFront(it), DIR_LEFT, true)); + } else if (CtrlPress(KEY_LEFT)) { + For(view.carets) { + if (GetSize(it.range) != 0 && GetFront(it) != it.range.min) { + it = MakeCaret(it.range.min); + } else { + it = MakeCaret(MovePos(*buffer, it.range.min, DIR_LEFT, true)); + } + } + } else if (ShiftPress(KEY_LEFT)) { + For(view.carets) it = ChangeFront(it, MovePos(*buffer, GetFront(it), DIR_LEFT, false)); + } else if (Press(KEY_LEFT)) { + For(view.carets) { + if (GetSize(it.range) == 0) { + it = MakeCaret(MovePos(*buffer, it.range.min, DIR_LEFT, false)); + } else { + it = MakeCaret(it.range.min); + } + } + } + + if (CtrlShiftPress(KEY_RIGHT)) { + For(view.carets) it = ChangeFront(it, MovePos(*buffer, GetFront(it), DIR_RIGHT, true)); + } else if (CtrlPress(KEY_RIGHT)) { + For(view.carets) { + if (GetSize(it.range) != 0 && GetFront(it) != it.range.max) { + it = MakeCaret(it.range.max); + } else { + it = MakeCaret(MovePos(*buffer, it.range.max, DIR_RIGHT, true)); + } + } + } else if (ShiftPress(KEY_RIGHT)) { + For(view.carets) it = ChangeFront(it, MovePos(*buffer, GetFront(it), DIR_RIGHT, false)); + } else if (Press(KEY_RIGHT)) { + For(view.carets) { + if (GetSize(it.range) == 0) { + it = MakeCaret(MovePos(*buffer, it.range.max, DIR_RIGHT, false)); + } else { + it = MakeCaret(it.range.max); + } + } + } + + if (CtrlPress(KEY_D)) { + Range range = view.carets[0].range; + String16 string = GetString(*buffer, range); + String16 string_buffer = GetString(*buffer, {range.max, INT64_MAX}); + Int index = 0; + if (Seek(string_buffer, string, &index)) { + Insert(&view.carets, MakeCaret(range.max + index, range.max + index + string.len), 0); + } else { + String16 string_buffer = GetString(*buffer); + if (Seek(string_buffer, string, &index)) { + Insert(&view.carets, MakeCaret(index, index + string.len), 0); + } + } + } + + if (CtrlShiftPress(KEY_Z)) { + RedoEdit(buffer, &view.carets); + } else if (CtrlPress(KEY_Z)) { + UndoEdit(buffer, &view.carets); + } + + if (CtrlPress(KEY_C)) { + Command_Copy(&view); + } else if (CtrlPress(KEY_V)) { + Command_Paste(&view); + } else if (CtrlPress(KEY_X)) { + Command_Copy(&view); + Command_Replace(&view, L""); + } + + bool dont_update_scroll = false; + if (CtrlPress(KEY_A)) { + view.carets.len = 1; + view.carets[0] = MakeCaret(0, buffer->len); + dont_update_scroll = true; + } + + if (ShiftPress(KEY_PAGE_UP)) { + Command_MoveCursorsByPageSize(window, DIR_UP, SHIFT_PRESSED); + } else if (Press(KEY_PAGE_UP)) { + Command_MoveCursorsByPageSize(window, DIR_UP); + } + + if (ShiftPress(KEY_PAGE_DOWN)) { + Command_MoveCursorsByPageSize(window, DIR_DOWN, SHIFT_PRESSED); + } else if (Press(KEY_PAGE_DOWN)) { + Command_MoveCursorsByPageSize(window, DIR_DOWN); + } + + if (ShiftPress(KEY_HOME)) { + Command_MoveCursorsToSide(window, DIR_LEFT, SHIFT_PRESSED); + } else if (Press(KEY_HOME)) { + Command_MoveCursorsToSide(window, DIR_LEFT); + } + + if (ShiftPress(KEY_END)) { + Command_MoveCursorsToSide(window, DIR_RIGHT, SHIFT_PRESSED); + } else if (Press(KEY_END)) { + Command_MoveCursorsToSide(window, DIR_RIGHT); + } + + if (window->banish_new_lines == false && Press(KEY_ENTER)) { + Command_Replace(&view, L"\n"); + } + + if (Press(KEY_TAB)) { + Command_Replace(&view, L" "); + } + + if (CtrlPress(KEY_BACKSPACE)) { + Command_Delete(&view, DIR_LEFT, CTRL_PRESSED); + } else if (Press(KEY_BACKSPACE)) { + Command_Delete(&view, DIR_LEFT); + } + + if (CtrlPress(KEY_DELETE)) { + Command_Delete(&view, DIR_RIGHT, CTRL_PRESSED); + } else if (Press(KEY_DELETE)) { + Command_Delete(&view, DIR_RIGHT); + } + + for (int c = GetCharPressed(); c; c = GetCharPressed()) { + // we interpret 2 byte sequences as 1 byte when rendering but we still + // want to read them properly. + String16 string = L"?"; + UTF16Result result = UTF32ToUTF16((uint32_t)c); + if (!result.error) string = {(wchar_t *)result.out_str, result.len}; + Command_Replace(&view, string); + } + + { + ProfileScope(mouse); + + Vec2 _mouse = GetMousePosition(); + bool mouse_in_view = CheckCollisionPointRec(_mouse, ToRectangle(window->document_rect)); + bool mouse_in_scrollbar = CheckCollisionPointRec(_mouse, ToRectangle(window->scrollbar_rect)); + Vec2I mouse = ToVec2I(_mouse); + + if (!(mouse_in_scrollbar || window->mouse_selecting_scrollbar) && (mouse_in_view || window->mouse_selecting)) { + Vec2I mworld = mouse - window->document_rect.min + view.scroll; + Vec2I pos = mworld / Vec2I{FontCharSpacing, FontLineSpacing}; + XY xy = {(Int)(pos.x), (Int)(pos.y)}; + Int p = XYToPosWithoutNL(*buffer, xy); + + if (mouse_in_view && IsMouseButtonDown(MOUSE_BUTTON_LEFT)) { + if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { + if (IsDoubleClick()) { + Caret *c = GetLast(view.carets); + if (InBounds({c->range.min - 1, c->range.max + 1}, p)) { + c->range = EncloseWord(*buffer, p); + view.selection_anchor = c->range; + } + } else { + if (!IsKeyDown(KEY_LEFT_CONTROL)) { + view.carets.len = 0; + } + Add(&view.carets, MakeCaret(p, p)); + view.selection_anchor = GetLast(view.carets)->range; + } + + MergeCarets(&view.carets); + } else if (IsMouseButtonDown(MOUSE_BUTTON_LEFT)) { + window->mouse_selecting = true; + } + } + + if (window->mouse_selecting) { + if (!IsMouseButtonDown(MOUSE_BUTTON_LEFT)) window->mouse_selecting = false; + Caret &caret = *GetLast(view.carets); + if (view.selection_anchor.min > p) { + caret = MakeCaret(p, view.selection_anchor.max); + } else if (view.selection_anchor.max < p) { + caret = MakeCaret(p, view.selection_anchor.min); + } else { + caret = MakeCaret(view.selection_anchor.max, view.selection_anchor.min); + } + MergeCarets(&view.carets, &view.selection_anchor); + } + } else if (!(mouse_in_view || window->mouse_selecting) && mouse_in_scrollbar || window->mouse_selecting_scrollbar) { + Scroller s = ComputeScrollerRect(*window); + double size_y = (double)GetSize(window->scrollbar_rect).y; + double p = _mouse.y - window->scrollbar_rect.min.y; + + if (IsMouseButtonDown(MOUSE_BUTTON_LEFT)) { + window->mouse_selecting_scrollbar = true; + } else if (!IsMouseButtonDown(MOUSE_BUTTON_LEFT)) { + window->mouse_selecting_scrollbar = false; + } + + if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { + if (_mouse.y < s.rect.min.y || _mouse.y > s.rect.max.y) { + view.scroll.y = (Int)(p / size_y * (double)s.line_count * (double)FontLineSpacing); + window->mouse_scroller_offset = -(double)GetSize(s.rect).y / 2.0 / size_y; + } else { + window->mouse_scroller_offset = (s.rect.min.y - p) / size_y; + } + } + + if (window->mouse_selecting_scrollbar) { + double v = p / size_y; + v = v + (window->mouse_scroller_offset); + view.scroll.y = (Int)(v * (double)s.line_count * (double)FontLineSpacing); + } + } + } + + // Scrolling with caret + if (!AreEqual(main_caret_on_begin_frame, view.carets[0]) && !dont_update_scroll) { + Caret c = view.carets[0]; + Int front = GetFront(c); + XY xy = PosToXY(*buffer, front); + + Rect2I visible = GetVisibleCells(*window); + Vec2I visible_cells = GetSize(visible); + Vec2I visible_size = visible_cells * Vec2I{FontCharSpacing, FontLineSpacing}; + Vec2I rect_size = GetSize(window->document_rect); + + if (xy.line > visible.max.y - 2) { + Int set_view_at_line = xy.line - (visible_cells.y - 1); + Int cut_off_y = Max((Int)0, visible_size.y - rect_size.y); + view.scroll.y = (set_view_at_line * FontLineSpacing) + cut_off_y; + } + + if (xy.line < visible.min.y + 1) { + view.scroll.y = xy.line * FontLineSpacing; + } + + if (xy.col >= visible.max.x - 1) { + Int set_view_at_line = xy.col - (visible_cells.x - 1); + Int cut_off_x = Max((Int)0, visible_size.x - rect_size.x); + view.scroll.x = (set_view_at_line * FontCharSpacing) + cut_off_x; + } + + if (xy.col <= visible.min.x) { + view.scroll.x = xy.col * FontCharSpacing; + } + } +} + +void HandleWindowBindings(Window *window) { + ProfileFunction(); + View &view = *GetActiveView(window); + Buffer *buffer = GetBuffer(view.buffer_id); + + if (IsActive(window)) { + HandleActiveWindowBindings(window); + } + + // Clip scroll + { + ProfileScope(clip_scroll); + Int last_line = LastLine(*buffer); + view.scroll.y = Clamp(view.scroll.y, (Int)0, Max((Int)0, (last_line - 1) * FontLineSpacing)); + + // @note: + // GetCharCountOfLongestLine is a bottleneck, there is probably an algorithm for + // calculating this value incrementally but do we even need X scrollbar or x clipping? + view.scroll.x = ClampBottom(view.scroll.x, (Int)0); + } +} \ No newline at end of file diff --git a/src/text_editor/text_editor.cpp b/src/text_editor/text_editor.cpp index 60dfcc2..cd84e85 100644 --- a/src/text_editor/text_editor.cpp +++ b/src/text_editor/text_editor.cpp @@ -7,30 +7,37 @@ #include "math_int.cpp" #include "math.cpp" #include "raylib.h" +#include "colors.cpp" +#include "raylib_utils.cpp" + #include "text_editor.h" + #include "buffer_helpers.cpp" #include "buffer.cpp" #include "buffer_multi_cursor.cpp" #include "buffer_history.cpp" #include "buffer_test_load.cpp" + #include "commands.cpp" -#include "colors.cpp" -#include "raylib_utils.cpp" -#include "windows.cpp" -#include "view_commands_clipboard.cpp" -#include "view_commands.cpp" -#include "view_draw.cpp" +#include "commands_clipboard.cpp" +#include "commands_window.cpp" +#include "window.cpp" +#include "window_draw.cpp" /* -j- Open file (utf8->utf16), process determine line endings, tabs to spaces?, Save file (utf16->utf8) +- Open file (utf8->utf16), process determine line endings, tabs to spaces?, Save file (utf16->utf8) +- resize windows +- command window + - maybe use lua and have there be lua commands that you choose with cursor + - open "asd/asd/asd/asd" - line endings -- command window - word completion - Colored strings - file dock on left side - move off raylib + - Adjust text position a little bit down? - proper double click that works on laptop - font cache and on demand unicode loads */ @@ -82,6 +89,8 @@ int main(void) { LoadTextA(b); AddView(w->id, v->id); } + ActiveWindow.id = 1; + { Window *w = CreateWindow(); Buffer *b = CreateBuffer(sys_allocator); @@ -101,14 +110,14 @@ int main(void) { w->draw_scrollbar = false; w->draw_line_numbers = false; w->draw_infobar = false; + w->visible = false; Buffer *b = CreateBuffer(sys_allocator); View *v = CreateView(b->id); LoadLine(b); AddView(w->id, v->id); + CommandWindowID = w->id; } - Int LastFrameIDWhenSwitchedActiveWindow = 0; - while (!WindowShouldClose()) { ProfileScope(game_loop); FrameID += 1; @@ -144,12 +153,12 @@ int main(void) { Rect2 screen_rect = GetScreenRectF(); Vec2 size = GetSize(screen_rect); CutTop(&screen_rect, size.y * 0.05f); - CutBottom(&screen_rect, size.y * 0.8f); CutLeft(&screen_rect, size.x * 0.2f); CutRight(&screen_rect, size.x * 0.2f); + Rect2 r = CutTop(&screen_rect, FontLineSpacing * 10.f); Windows[i].z = 1; - Windows[i].total_rect = ToRect2I(screen_rect); + Windows[i].total_rect = ToRect2I(r); Windows[i].document_rect = Windows[i].total_rect; } @@ -158,36 +167,11 @@ int main(void) { Scratch scratch; Array order = GetWindowZOrder(scratch); - - For(order) { - Window *window = &Windows[it]; - if (window->visible == false) continue; - View *view = GetActiveView(window); - - Vec2 _mouse = GetMousePosition(); - bool mouse_in_view = CheckCollisionPointRec(_mouse, ToRectangle(window->document_rect)); - bool mouse_in_scrollbar = CheckCollisionPointRec(_mouse, ToRectangle(window->scrollbar_rect)); - - if (mouse_in_view || mouse_in_scrollbar) { - if (IsKeyDown(KEY_F1)) { - view->scroll.x -= (Int)(GetMouseWheelMove() * 48); - } else { - view->scroll.y -= (Int)(GetMouseWheelMove() * 48); - } - - if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { - if (LastFrameIDWhenSwitchedActiveWindow != FrameID) { - ActiveWindow = window->id; - LastFrameIDWhenSwitchedActiveWindow = FrameID; - } - } - } - } - + HandleGlobalCommands(); + ChangeActiveWindowAndScroll(order); For(IterateInReverse(&order)) { Window &window = Windows[it]; - if (window.visible == false) continue; - HandleKeybindings(&window); + HandleWindowBindings(&window); DrawWindow(window); } SetMouseCursor(); diff --git a/src/text_editor/text_editor.h b/src/text_editor/text_editor.h index 0ea5daa..201a5a3 100644 --- a/src/text_editor/text_editor.h +++ b/src/text_editor/text_editor.h @@ -64,6 +64,7 @@ struct Window { bool draw_line_numbers : 1; bool draw_infobar : 1; bool visible : 1; + bool banish_new_lines : 1; // @todo: custom new line handler? custom vertical handler! (choosing options with cursor) }; }; @@ -82,9 +83,10 @@ WindowID WindowIDs = {0}; BufferID BufferIDs = {0}; ViewID ViewIDs = {0}; -WindowID NullWindowID = {0}; -BufferID NullBufferID = {0}; -ViewID NullViewID = {0}; +WindowID NullWindowID = {0}; +BufferID NullBufferID = {0}; +ViewID NullViewID = {0}; +WindowID CommandWindowID = {0}; Array Buffers = {}; Array Views = {}; @@ -101,6 +103,10 @@ Int FontLineSpacing; Int FontCharSpacing; Int FrameID; +Int LastFrameIDWhenScrolled; + +WindowID LastActiveWindow; +Int LastFrameIDWhenSwitchedActiveWindow; inline Window *GetWindow(WindowID id) { For(Windows) if (it.id.id == id.id) return ⁢ @@ -117,6 +123,7 @@ inline View *GetView(ViewID id) { return &Views[0]; } +void SetActiveWindow(WindowID window); inline ViewID AllocViewID() { return {ViewIDs.id++}; } inline WindowID AllocWindowID() { return {WindowIDs.id++}; } inline BufferID AllocBufferID() { return {BufferIDs.id++}; } diff --git a/src/text_editor/view_commands.cpp b/src/text_editor/view_commands.cpp deleted file mode 100644 index b951d9a..0000000 --- a/src/text_editor/view_commands.cpp +++ /dev/null @@ -1,427 +0,0 @@ -void Command_Replace(View *view, String16 string) { - Buffer *buffer = GetBuffer(view->buffer_id); - Scratch scratch; - BeforeEdit(buffer, view->carets); - MergeCarets(&view->carets); - Array edits = {scratch}; - For(view->carets) AddEdit(&edits, it.range, string); - ApplyEdits(buffer, edits); - AfterEdit(buffer, &edits, &view->carets); -} - -void Command_DuplicateLine(View *view, int direction) { - Assert(direction == DIR_UP || direction == DIR_DOWN); - Buffer *buffer = GetBuffer(view->buffer_id); - BeforeEdit(buffer, view->carets); - For(view->carets) it = MakeCaret(GetFront(it)); - MergeCarets(&view->carets); - - Scratch scratch; - Array edits = {scratch}; - For(view->carets) { - Int line = PosToLine(*buffer, it.range.min); - Range range = GetLineRange(*buffer, line); - String16 string = Copy(scratch, GetString(*buffer, range)); - Int pos = direction == DIR_UP ? range.min : range.max; - AddEdit(&edits, Rng(pos), string); - } - ApplyEdits(buffer, edits); - AfterEdit(buffer, &edits, &view->carets); - - For(view->carets) it = MakeCaret(MovePos(*buffer, it.range.min, direction, false)); -} - -bool SHIFT_PRESSED = true; -void Command_MoveCursorsByPageSize(Window *window, int direction, bool shift = false) { - Assert(direction == DIR_UP || direction == DIR_DOWN); - View &view = *GetActiveView(window); - Buffer *buffer = GetBuffer(view.buffer_id); - - Rect2I visible_cells_rect = GetVisibleCells(*window); - Int y = GetSize(visible_cells_rect).y - 2; - if (direction == DIR_UP) y = -y; - - For(view.carets) { - XY xy = PosToXY(*buffer, GetFront(it)); - xy.line += y; - Int pos = XYToPos(*buffer, xy); - if (shift) { - it = ChangeFront(it, pos); - } else { - it = MakeCaret(pos); - } - } -} - -void Command_MoveCursorsToSide(Window *window, int direction, bool shift = false) { - Assert(direction == DIR_LEFT || direction == DIR_RIGHT); - View &view = *GetActiveView(window); - Buffer *buffer = GetBuffer(view.buffer_id); - - For(view.carets) { - Int end_of_buffer = 0; - Range line_range = GetLineRange(*buffer, PosToLine(*buffer, GetFront(it)), &end_of_buffer); - - Int pos = line_range.min; - if (direction == DIR_RIGHT) { - pos = line_range.max - (1 - end_of_buffer); - } - - if (shift) { - it = ChangeFront(it, pos); - } else { - it.range.max = it.range.min = pos; - } - } -} - -void Command_Delete(View *_view, int direction, bool ctrl = false) { - Assert(direction == DIR_LEFT || direction == DIR_RIGHT); - View &view = *_view; - Buffer *buffer = GetBuffer(view.buffer_id); - - // Select things to delete - For(view.carets) { - if (GetSize(it.range)) continue; - Int pos = MovePos(*buffer, it.range.min, direction, ctrl); - it = MakeCaret(pos, it.range.min); - } - - Command_Replace(&view, {}); -} - -void Command_CreateCursorVertical(View *_view, int direction) { - Assert(direction == DIR_UP || direction == DIR_DOWN); - View &view = *_view; - Buffer *buffer = GetBuffer(view.buffer_id); - - Scratch scratch; - Array arr = {scratch}; - For(view.carets) { - if (PosToLine(*buffer, it.range.min) == PosToLine(*buffer, it.range.max)) { - Int f = MovePos(*buffer, GetFront(it), direction); - Int b = MovePos(*buffer, GetBack(it), direction); - Add(&arr, MakeCaret(f, b)); - } else { - Int pos = direction == DIR_UP ? it.range.min : it.range.max; - Int min = MovePos(*buffer, pos, direction); - Add(&arr, MakeCaret(min)); - } - } - For(arr) Add(&view.carets, it); - MergeCarets(&view.carets); -} - -void HandleKeybindings(Window *window) { - ProfileFunction(); - View &view = *GetActiveView(window); - Buffer *buffer = GetBuffer(view.buffer_id); - Caret main_caret_on_begin_frame = view.carets[0]; - - if (IsActive(window)) { - - if (CtrlPress(KEY_F2)) { - LoadBigLine(buffer); - } else if (Press(KEY_F2)) { - LoadBigText(buffer); - } - - if (Press(KEY_ESCAPE)) { - view.carets.len = 1; - } - - if (CtrlAltPress(KEY_DOWN)) { - Command_DuplicateLine(&view, DIR_DOWN); - } else if (AltShiftPress(KEY_DOWN)) { - Command_CreateCursorVertical(&view, DIR_DOWN); - } else if (CtrlShiftPress(KEY_DOWN)) { - For(view.carets) it = ChangeFront(it, MovePos(*buffer, GetFront(it), DIR_DOWN, true)); - } else if (CtrlPress(KEY_DOWN)) { - For(view.carets) it = MakeCaret(MovePos(*buffer, it.range.max, DIR_DOWN, true)); - } else if (ShiftPress(KEY_DOWN)) { - For(view.carets) it = ChangeFront(it, MovePos(*buffer, GetFront(it), DIR_DOWN, false)); - } else if (Press(KEY_DOWN)) { - For(view.carets) { - if (GetSize(it.range) == 0) { - it = MakeCaret(MovePos(*buffer, it.range.max, DIR_DOWN, false)); - } else { - it = MakeCaret(it.range.max); - } - } - } - - if (CtrlAltPress(KEY_UP)) { - Command_DuplicateLine(&view, DIR_UP); - } else if (AltShiftPress(KEY_UP)) { - Command_CreateCursorVertical(&view, DIR_UP); - } else if (CtrlShiftPress(KEY_UP)) { - For(view.carets) it = ChangeFront(it, MovePos(*buffer, GetFront(it), DIR_UP, CTRL_PRESSED)); - } else if (CtrlPress(KEY_UP)) { - For(view.carets) it = MakeCaret(MovePos(*buffer, it.range.min, DIR_UP, CTRL_PRESSED)); - } else if (ShiftPress(KEY_UP)) { - For(view.carets) it = ChangeFront(it, MovePos(*buffer, GetFront(it), DIR_UP)); - } else if (Press(KEY_UP)) { - For(view.carets) { - if (GetSize(it.range) == 0) { - it = MakeCaret(MovePos(*buffer, it.range.min, DIR_UP)); - } else { - it = MakeCaret(it.range.min); - } - } - } - - if (CtrlShiftPress(KEY_LEFT)) { - For(view.carets) it = ChangeFront(it, MovePos(*buffer, GetFront(it), DIR_LEFT, true)); - } else if (CtrlPress(KEY_LEFT)) { - For(view.carets) { - if (GetSize(it.range) != 0 && GetFront(it) != it.range.min) { - it = MakeCaret(it.range.min); - } else { - it = MakeCaret(MovePos(*buffer, it.range.min, DIR_LEFT, true)); - } - } - } else if (ShiftPress(KEY_LEFT)) { - For(view.carets) it = ChangeFront(it, MovePos(*buffer, GetFront(it), DIR_LEFT, false)); - } else if (Press(KEY_LEFT)) { - For(view.carets) { - if (GetSize(it.range) == 0) { - it = MakeCaret(MovePos(*buffer, it.range.min, DIR_LEFT, false)); - } else { - it = MakeCaret(it.range.min); - } - } - } - - if (CtrlShiftPress(KEY_RIGHT)) { - For(view.carets) it = ChangeFront(it, MovePos(*buffer, GetFront(it), DIR_RIGHT, true)); - } else if (CtrlPress(KEY_RIGHT)) { - For(view.carets) { - if (GetSize(it.range) != 0 && GetFront(it) != it.range.max) { - it = MakeCaret(it.range.max); - } else { - it = MakeCaret(MovePos(*buffer, it.range.max, DIR_RIGHT, true)); - } - } - } else if (ShiftPress(KEY_RIGHT)) { - For(view.carets) it = ChangeFront(it, MovePos(*buffer, GetFront(it), DIR_RIGHT, false)); - } else if (Press(KEY_RIGHT)) { - For(view.carets) { - if (GetSize(it.range) == 0) { - it = MakeCaret(MovePos(*buffer, it.range.max, DIR_RIGHT, false)); - } else { - it = MakeCaret(it.range.max); - } - } - } - - if (CtrlPress(KEY_D)) { - Range range = view.carets[0].range; - String16 string = GetString(*buffer, range); - String16 string_buffer = GetString(*buffer, {range.max, INT64_MAX}); - Int index = 0; - if (Seek(string_buffer, string, &index)) { - Insert(&view.carets, MakeCaret(range.max + index, range.max + index + string.len), 0); - } else { - String16 string_buffer = GetString(*buffer); - if (Seek(string_buffer, string, &index)) { - Insert(&view.carets, MakeCaret(index, index + string.len), 0); - } - } - } - - if (CtrlShiftPress(KEY_Z)) { - RedoEdit(buffer, &view.carets); - } else if (CtrlPress(KEY_Z)) { - UndoEdit(buffer, &view.carets); - } - - if (CtrlPress(KEY_C)) { - Command_Copy(&view); - } else if (CtrlPress(KEY_V)) { - Command_Paste(&view); - } else if (CtrlPress(KEY_X)) { - Command_Copy(&view); - Command_Replace(&view, L""); - } - - bool dont_update_scroll = false; - if (CtrlPress(KEY_A)) { - view.carets.len = 1; - view.carets[0] = MakeCaret(0, buffer->len); - dont_update_scroll = true; - } - - if (ShiftPress(KEY_PAGE_UP)) { - Command_MoveCursorsByPageSize(window, DIR_UP, SHIFT_PRESSED); - } else if (Press(KEY_PAGE_UP)) { - Command_MoveCursorsByPageSize(window, DIR_UP); - } - - if (ShiftPress(KEY_PAGE_DOWN)) { - Command_MoveCursorsByPageSize(window, DIR_DOWN, SHIFT_PRESSED); - } else if (Press(KEY_PAGE_DOWN)) { - Command_MoveCursorsByPageSize(window, DIR_DOWN); - } - - if (ShiftPress(KEY_HOME)) { - Command_MoveCursorsToSide(window, DIR_LEFT, SHIFT_PRESSED); - } else if (Press(KEY_HOME)) { - Command_MoveCursorsToSide(window, DIR_LEFT); - } - - if (ShiftPress(KEY_END)) { - Command_MoveCursorsToSide(window, DIR_RIGHT, SHIFT_PRESSED); - } else if (Press(KEY_END)) { - Command_MoveCursorsToSide(window, DIR_RIGHT); - } - - if (Press(KEY_ENTER)) { - Command_Replace(&view, L"\n"); - } - - if (Press(KEY_TAB)) { - Command_Replace(&view, L" "); - } - - if (CtrlPress(KEY_BACKSPACE)) { - Command_Delete(&view, DIR_LEFT, CTRL_PRESSED); - } else if (Press(KEY_BACKSPACE)) { - Command_Delete(&view, DIR_LEFT); - } - - if (CtrlPress(KEY_DELETE)) { - Command_Delete(&view, DIR_RIGHT, CTRL_PRESSED); - } else if (Press(KEY_DELETE)) { - Command_Delete(&view, DIR_RIGHT); - } - - for (int c = GetCharPressed(); c; c = GetCharPressed()) { - // we interpret 2 byte sequences as 1 byte when rendering but we still - // want to read them properly. - String16 string = L"?"; - UTF16Result result = UTF32ToUTF16((uint32_t)c); - if (!result.error) string = {(wchar_t *)result.out_str, result.len}; - Command_Replace(&view, string); - } - - { - ProfileScope(mouse); - - Vec2 _mouse = GetMousePosition(); - bool mouse_in_view = CheckCollisionPointRec(_mouse, ToRectangle(window->document_rect)); - bool mouse_in_scrollbar = CheckCollisionPointRec(_mouse, ToRectangle(window->scrollbar_rect)); - Vec2I mouse = ToVec2I(_mouse); - - if (!(mouse_in_scrollbar || window->mouse_selecting_scrollbar) && (mouse_in_view || window->mouse_selecting)) { - Vec2I mworld = mouse - window->document_rect.min + view.scroll; - Vec2I pos = mworld / Vec2I{FontCharSpacing, FontLineSpacing}; - XY xy = {(Int)(pos.x), (Int)(pos.y)}; - Int p = XYToPosWithoutNL(*buffer, xy); - - if (mouse_in_view && IsMouseButtonDown(MOUSE_BUTTON_LEFT)) { - if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { - if (IsDoubleClick()) { - Caret *c = GetLast(view.carets); - if (InBounds({c->range.min - 1, c->range.max + 1}, p)) { - c->range = EncloseWord(*buffer, p); - view.selection_anchor = c->range; - } - } else { - if (!IsKeyDown(KEY_LEFT_CONTROL)) { - view.carets.len = 0; - } - Add(&view.carets, MakeCaret(p, p)); - view.selection_anchor = GetLast(view.carets)->range; - } - - MergeCarets(&view.carets); - } else if (IsMouseButtonDown(MOUSE_BUTTON_LEFT)) { - window->mouse_selecting = true; - } - } - - if (window->mouse_selecting) { - if (!IsMouseButtonDown(MOUSE_BUTTON_LEFT)) window->mouse_selecting = false; - Caret &caret = *GetLast(view.carets); - if (view.selection_anchor.min > p) { - caret = MakeCaret(p, view.selection_anchor.max); - } else if (view.selection_anchor.max < p) { - caret = MakeCaret(p, view.selection_anchor.min); - } else { - caret = MakeCaret(view.selection_anchor.max, view.selection_anchor.min); - } - MergeCarets(&view.carets, &view.selection_anchor); - } - } else if (!(mouse_in_view || window->mouse_selecting) && mouse_in_scrollbar || window->mouse_selecting_scrollbar) { - Scroller s = ComputeScrollerRect(*window); - double size_y = (double)GetSize(window->scrollbar_rect).y; - double p = _mouse.y - window->scrollbar_rect.min.y; - - if (IsMouseButtonDown(MOUSE_BUTTON_LEFT)) { - window->mouse_selecting_scrollbar = true; - } else if (!IsMouseButtonDown(MOUSE_BUTTON_LEFT)) { - window->mouse_selecting_scrollbar = false; - } - - if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { - if (_mouse.y < s.rect.min.y || _mouse.y > s.rect.max.y) { - view.scroll.y = (Int)(p / size_y * (double)s.line_count * (double)FontLineSpacing); - window->mouse_scroller_offset = -(double)GetSize(s.rect).y / 2.0 / size_y; - } else { - window->mouse_scroller_offset = (s.rect.min.y - p) / size_y; - } - } - - if (window->mouse_selecting_scrollbar) { - double v = p / size_y; - v = v + (window->mouse_scroller_offset); - view.scroll.y = (Int)(v * (double)s.line_count * (double)FontLineSpacing); - } - } - } - - // Scrolling with caret - if (!AreEqual(main_caret_on_begin_frame, view.carets[0]) && !dont_update_scroll) { - Caret c = view.carets[0]; - Int front = GetFront(c); - XY xy = PosToXY(*buffer, front); - - Rect2I visible = GetVisibleCells(*window); - Vec2I visible_cells = GetSize(visible); - Vec2I visible_size = visible_cells * Vec2I{FontCharSpacing, FontLineSpacing}; - Vec2I rect_size = GetSize(window->document_rect); - - if (xy.line > visible.max.y - 2) { - Int set_view_at_line = xy.line - (visible_cells.y - 1); - Int cut_off_y = Max((Int)0, visible_size.y - rect_size.y); - view.scroll.y = (set_view_at_line * FontLineSpacing) + cut_off_y; - } - - if (xy.line < visible.min.y + 1) { - view.scroll.y = xy.line * FontLineSpacing; - } - - if (xy.col >= visible.max.x - 1) { - Int set_view_at_line = xy.col - (visible_cells.x - 1); - Int cut_off_x = Max((Int)0, visible_size.x - rect_size.x); - view.scroll.x = (set_view_at_line * FontCharSpacing) + cut_off_x; - } - - if (xy.col <= visible.min.x) { - view.scroll.x = xy.col * FontCharSpacing; - } - } - } - - // Clip scroll - { - ProfileScope(clip_scroll); - Int last_line = LastLine(*buffer); - view.scroll.y = Clamp(view.scroll.y, (Int)0, Max((Int)0, (last_line - 1) * FontLineSpacing)); - - // @note: - // GetCharCountOfLongestLine is a bottleneck, there is probably an algorithm for - // calculating this value incrementally but do we even need X scrollbar or x clipping? - view.scroll.x = ClampBottom(view.scroll.x, (Int)0); - } -} \ No newline at end of file diff --git a/src/text_editor/windows.cpp b/src/text_editor/window.cpp similarity index 51% rename from src/text_editor/windows.cpp rename to src/text_editor/window.cpp index 82f266e..c858cd7 100644 --- a/src/text_editor/windows.cpp +++ b/src/text_editor/window.cpp @@ -42,8 +42,39 @@ void SetMouseCursor() { Array GetWindowZOrder(Allocator allocator) { Array order = {allocator}; - For(Windows) if (it.z == 2) Add(&order, GetIndex(Windows, it)); - For(Windows) if (it.z == 1) Add(&order, GetIndex(Windows, it)); - For(Windows) if (it.z == 0) Add(&order, GetIndex(Windows, it)); + For(Windows) if (it.z == 2 && it.visible) Add(&order, GetIndex(Windows, it)); + For(Windows) if (it.z == 1 && it.visible) Add(&order, GetIndex(Windows, it)); + For(Windows) if (it.z == 0 && it.visible) Add(&order, GetIndex(Windows, it)); return order; -} \ No newline at end of file +} + +void SetActiveWindow(WindowID window) { + if (LastFrameIDWhenSwitchedActiveWindow != FrameID) { + LastActiveWindow = ActiveWindow; + ActiveWindow = window; + LastFrameIDWhenSwitchedActiveWindow = FrameID; + } +} + +void ChangeActiveWindowAndScroll(Array &order) { + For(order) { + Window *window = &Windows[it]; + View *view = GetActiveView(window); + + Vec2 mouse = GetMousePosition(); + bool mouse_in_window = CheckCollisionPointRec(mouse, ToRectangle(window->total_rect)); + if (mouse_in_window) { + if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { + SetActiveWindow(window->id); + } + if (LastFrameIDWhenSwitchedActiveWindow != FrameID) { + if (IsKeyDown(KEY_F1)) { + view->scroll.x -= (Int)(GetMouseWheelMove() * 48); + } else { + view->scroll.y -= (Int)(GetMouseWheelMove() * 48); + } + LastFrameIDWhenSwitchedActiveWindow = FrameID; + } + } + } +} diff --git a/src/text_editor/view_draw.cpp b/src/text_editor/window_draw.cpp similarity index 100% rename from src/text_editor/view_draw.cpp rename to src/text_editor/window_draw.cpp