bool AppIsRunning = true; bool WaitForEvents = true; enum { MOUSE_NONE, MOUSE_MOVE, MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE, MOUSE_LEFT_UP, MOUSE_RIGHT_UP, MOUSE_MIDDLE_UP, }; struct Event { SDL_Keycode key; int16_t xmouse; int16_t ymouse; struct { uint8_t shift : 1; uint8_t ctrl : 1; uint8_t alt : 1; uint8_t super : 1; }; uint8_t mouse; uint8_t mouse_double_click; Vec2 wheel; const char *text; }; #define Press(KEY) (event.key == KEY) #define Ctrl(KEY) (event.key == KEY && event.ctrl) #define Shift(KEY) (event.key == KEY && event.shift) #define Alt(KEY) (event.key == KEY && event.alt) #define CtrlShift(KEY) (event.key == KEY && event.ctrl && event.shift) #define CtrlAlt(KEY) (event.key == KEY && event.ctrl && event.alt) #define AltShift(KEY) (event.key == KEY && event.shift && event.alt) #define MouseVec2() \ Vec2 { (float)event.xmouse, (float)event.ymouse } #define MouseVec2I() \ Vec2I { (Int) event.xmouse, (Int)event.ymouse } #define Mouse(x) (event.mouse == MOUSE_##x) #define MousePress() (Mouse(LEFT) || Mouse(RIGHT) || Mouse(MIDDLE)) Int MoveOnWhitespaceBoundaryForward(Buffer &buffer, Int pos) { pos = Clamp(pos, (Int)0, buffer.len); bool standing_on_whitespace = IsWhitespace(buffer.str[pos]); bool standing_on_symbol = IsSymbol(buffer.str[pos]); bool standing_on_word = !standing_on_whitespace && !standing_on_symbol; bool seek_whitespace = standing_on_whitespace == false; bool seek_word = standing_on_whitespace || standing_on_symbol; bool seek_symbol = standing_on_whitespace || standing_on_word; Int result = buffer.len; Int prev_pos = pos; for (Int i = pos; i < buffer.len; i += 1) { bool is_whitespace = IsWhitespace(buffer.str[i]); bool is_symbol = IsSymbol(buffer.str[i]); bool is_word = !is_whitespace && !is_symbol; if (seek_word && is_word) { result = i; break; } if (seek_whitespace && is_whitespace) { result = i; break; } if (seek_symbol && is_symbol) { result = i; break; } prev_pos = i; } return result; } Int MoveOnWhitespaceBoundaryBackward(Buffer &buffer, Int pos) { pos = Clamp(pos - 1, (Int)0, buffer.len); bool standing_on_whitespace = IsWhitespace(buffer.str[pos]); bool standing_on_symbol = IsSymbol(buffer.str[pos]); bool standing_on_word = !standing_on_whitespace && !standing_on_symbol; bool seek_whitespace = standing_on_whitespace == false; bool seek_word = standing_on_whitespace || standing_on_symbol; bool seek_symbol = standing_on_whitespace || standing_on_word; Int result = 0; Int prev_pos = pos; for (Int i = pos; i >= 0; i -= 1) { bool is_whitespace = IsWhitespace(buffer.str[i]); bool is_symbol = IsSymbol(buffer.str[i]); bool is_word = !is_whitespace && !is_symbol; if (seek_word && is_word) { result = prev_pos; break; } if (seek_whitespace && is_whitespace) { result = prev_pos; break; } if (seek_symbol && is_symbol) { result = prev_pos; break; } prev_pos = i; } return result; } Int MoveOnWhitespaceBoundaryDown(Buffer &buffer, Int pos) { Int result = pos; Int next_line = PosToLine(buffer, pos) + 1; for (Int line = next_line; line < buffer.line_starts.len; line += 1) { Range line_range = GetLineRange(buffer, line); result = line_range.min; bool whitespace_line = true; for (Int i = line_range.min; i < line_range.max; i += 1) { if (!IsWhitespace(buffer.str[i])) { whitespace_line = false; break; } } if (whitespace_line) break; } return result; } Int MoveOnWhitespaceBoundaryUp(Buffer &buffer, Int pos) { Int result = pos; Int next_line = PosToLine(buffer, pos) - 1; for (Int line = next_line; line >= 0; line -= 1) { Range line_range = GetLineRange(buffer, line); result = line_range.min; bool whitespace_line = true; for (Int i = line_range.min; i < line_range.max; i += 1) { if (!IsWhitespace(buffer.str[i])) { whitespace_line = false; break; } } if (whitespace_line) break; } return result; } Int MovePosByXY(Buffer &buffer, Int pos, XY offset) { XY xy = PosToXY(buffer, pos); Int result = XYToPosWithoutNL(buffer, {xy.col + offset.col, xy.line + offset.line}); return result; } const int DIR_RIGHT = 0; const int DIR_LEFT = 1; const int DIR_DOWN = 2; const int DIR_UP = 3; const bool CTRL_PRESSED = true; Int MovePos(Buffer &buffer, Int pos, int direction, bool ctrl_pressed = false) { ProfileFunction(); Assert(direction >= 0 && direction <= 3); if (ctrl_pressed) { switch (direction) { case DIR_RIGHT: return MoveOnWhitespaceBoundaryForward(buffer, pos); case DIR_LEFT: return MoveOnWhitespaceBoundaryBackward(buffer, pos); case DIR_DOWN: return MoveOnWhitespaceBoundaryDown(buffer, pos); case DIR_UP: return MoveOnWhitespaceBoundaryUp(buffer, pos); default: return pos; } } else { switch (direction) { case DIR_RIGHT: return Clamp(pos + 1, (Int)0, buffer.len); case DIR_LEFT: return Clamp(pos - 1, (Int)0, buffer.len); case DIR_DOWN: return MovePosByXY(buffer, pos, {0, 1}); case DIR_UP: return MovePosByXY(buffer, pos, {0, -1}); default: return pos; } } } Range EncloseWord(Buffer &buffer, Int pos) { Range result = {}; result.min = MoveOnWhitespaceBoundaryBackward(buffer, pos); result.max = MoveOnWhitespaceBoundaryForward(buffer, pos); return result; } Caret FindInBuffer(Buffer *buffer, String16 needle, Caret caret, bool find_next = false) { Int pos = caret.range.min; String16 medium = GetString(*buffer, {pos, INT64_MAX}); Caret result = {}; Int index = 0; if (Seek(medium, needle, &index)) { result = MakeCaret(pos + index + needle.len, pos + index); } else { medium = GetString(*buffer); if (Seek(medium, needle, &index)) { result = MakeCaret(index + needle.len, index); } } if (find_next && AreEqual(result, caret)) { caret.range.min = Clamp(*buffer, caret.range.min + 1); caret.range.max = Clamp(*buffer, caret.range.max + 1); result = FindInBuffer(buffer, needle, caret, false); } return result; } Array FindAllInBuffer(Allocator allocator, Buffer *buffer, String16 needle) { Array result = {allocator}; String16 string_buffer = GetString(*buffer); for (Int pos = 0;;) { Int index = 0; String16 medium = Skip(string_buffer, pos); if (!Seek(medium, needle, &index)) { break; } Add(&result, Rng(pos + index, pos + index + needle.len)); pos += needle.len; } return result; } bool GlobalCommand(Event event) { ProfileFunction(); bool run_window_command = true; if (Mouse(MOVE)) { Vec2I mouse = MouseVec2I(); Window *window = GetActiveWindow(); bool mouse_in_document = CheckCollisionPointRec(mouse, window->document_rect); bool mouse_in_scrollbar = CheckCollisionPointRec(mouse, window->scrollbar_rect); bool mouse_in_line_numbers = CheckCollisionPointRec(mouse, window->line_numbers_rect); bool mouse_in_total = CheckCollisionPointRec(mouse, window->total_rect); window->mouse_in_scrollbar = mouse_in_scrollbar; static SDL_Cursor *SDL_MouseCursor; if (SDL_MouseCursor) SDL_DestroyCursor(SDL_MouseCursor); if (window->mouse_selecting || mouse_in_document) { SDL_MouseCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_TEXT); SDL_SetCursor(SDL_MouseCursor); } else if (mouse_in_scrollbar || window->mouse_selecting_scrollbar) { SDL_MouseCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_DEFAULT); SDL_SetCursor(SDL_MouseCursor); } else if (mouse_in_line_numbers) { SDL_MouseCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_EW_RESIZE); SDL_SetCursor(SDL_MouseCursor); } else { SDL_MouseCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_POINTER); SDL_SetCursor(SDL_MouseCursor); } } if (MousePress()) { Scratch scratch; Array order = GetWindowZOrder(scratch); Vec2I mouse = MouseVec2I(); For(order) { Window *window = &Windows[it]; if (!window->visible) continue; bool mouse_in_window = CheckCollisionPointRec(mouse, window->total_rect); if (ActiveWindow.id == window->id.id) { bool mouse_in_line_numbers = CheckCollisionPointRec(mouse, window->line_numbers_rect); if (mouse_in_line_numbers) { // if (Mouse(LEFT)) { // window->layout_value.x += 0.1f; // } else if (Mouse(RIGHT)) { // window->layout_value.x -= 0.1f; // } // window->layout_value.x = Clamp(window->layout_value.x, 0.05f, 0.95f); } } if (mouse_in_window) { SetActiveWindow(window->id); break; } } } if (event.wheel.x || event.wheel.y) { Scratch scratch; Array order = GetWindowZOrder(scratch); Vec2I mouse = MouseVec2I(); For(order) { Window *window = &Windows[it]; if (!window->visible) continue; bool mouse_in_window = CheckCollisionPointRec(mouse, window->total_rect); if (mouse_in_window) { View *view = GetView(window->active_view); view->scroll.y -= (Int)(event.wheel.y * 48); view->scroll.x += (Int)(event.wheel.x * 48); break; } } } if (Ctrl(SDLK_0)) { Window *window = GetWindow(DebugWindowID); window->visible = !window->visible; } if (Press(SDLK_F5)) { AppIsRunning = false; run_window_command = false; } if (Ctrl(SDLK_P)) { Window *command_window = GetWindow(CommandWindowID); if (command_window->visible) { SetActiveWindow(GetLastActiveWindow()); } else { SetActiveWindow(command_window->id); } run_window_command = false; } if (Ctrl(SDLK_F)) { Window *search_window = GetWindow(SearchWindowID); if (search_window->visible) { SetActiveWindow(GetLastActiveWindow()); } else { SetActiveWindow(search_window->id); View *view = GetActiveView(search_window); Command_SelectEntireBuffer(view); Command_Replace(view, {}); } run_window_command = false; } if (Ctrl(SDLK_1)) { SetActiveWindow({0}); run_window_command = false; } if (Ctrl(SDLK_2)) { SetActiveWindow({1}); run_window_command = false; } if (Ctrl(SDLK_3)) { SetActiveWindow({2}); run_window_command = false; } if (Ctrl(SDLK_MINUS)) { ReloadFont((int32_t)MainFont.size - 1); run_window_command = false; } else if (Ctrl(SDLK_EQUALS)) { ReloadFont((int32_t)MainFont.size + 1); run_window_command = false; } return run_window_command; } void ReportErrorf(const char *fmt, ...) { Scratch scratch; STRING_FORMAT(scratch, fmt, string); SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error!", string.data, NULL); Buffer *buffer = GetBuffer("*console*"); if (!buffer) return; String16 string16 = ToString16(scratch, string); ReplaceText(buffer, {}, string16); } void ReportConsolef(const char *fmt, ...) { Scratch scratch; STRING_FORMAT(scratch, fmt, string); String16 string16 = ToString16(scratch, string); Buffer *buffer = GetBuffer("*console*"); ReplaceText(buffer, {}, string16); ReplaceText(buffer, Rng(string16.len), L"\n"); } void ReportWarningf(const char *fmt, ...) { Scratch scratch; STRING_FORMAT(scratch, fmt, string); String16 string16 = ToString16(scratch, string); { Buffer *buffer = GetBuffer("*console*"); ReplaceText(buffer, {}, string16); ReplaceText(buffer, Rng(string16.len), L"\n"); } { Buffer *buffer = GetBuffer("*popup*"); ReplaceText(buffer, GetRange(*buffer), string16); SetActiveWindow(PopupWindowID); } }