ded

Dramatic EDitor
Index Commits Files Refs README LICENSE
commit 983c71135214353f6c6b7544ea112cd3afd696a9
parent f62bf8c5814648e97aa8e1b08e6da193c836e516
Author: rexim <reximkut@gmail.com>
Date:   Fri, 13 Jan 2023 01:12:07 +0700

Add visual support for selections

Diffstat:
Msrc/common.h | 7+++++++
Msrc/editor.c | 142+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/editor.h | 11+++++++++++
Msrc/free_glyph.h | 2++
Msrc/main.c | 128++++++++++++++-----------------------------------------------------------------
5 files changed, 184 insertions(+), 106 deletions(-)
diff --git a/src/common.h b/src/common.h
@@ -4,8 +4,15 @@
 #include <stdlib.h>
 #include <stdio.h>
 
+#define SCREEN_WIDTH 800
+#define SCREEN_HEIGHT 600
+#define FPS 60
+#define DELTA_TIME (1.0f / FPS)
+
 typedef int Errno;
 
+#define SWAP(T, a, b) do { T t = a; a = b; b = t; } while (0)
+
 #define return_defer(value) do { result = (value); goto defer; } while (0)
 
 #define UNIMPLEMENTED(...)                                                      \
diff --git a/src/editor.c b/src/editor.c
@@ -150,3 +150,145 @@ void editor_recompute_lines(Editor *e)
     line.end = e->data.count;
     da_append(&e->lines, line);
 }
+
+void editor_render(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer *sr, Editor *editor)
+{
+    int w, h;
+    SDL_GetWindowSize(window, &w, &h);
+
+    float max_line_len = 0.0f;
+
+    sr->resolution = vec2f(w, h);
+    sr->time = (float) SDL_GetTicks() / 1000.0f;
+
+    // Render text
+    {
+        simple_renderer_set_shader(sr, SHADER_FOR_COLOR);
+        if (editor->selection) {
+            for (size_t row = 0; row < editor->lines.count; ++row) {
+                size_t select_begin_chr = editor->select_begin;
+                size_t select_end_chr = editor->cursor;
+                if (select_begin_chr > select_end_chr) {
+                    SWAP(size_t, select_begin_chr, select_end_chr);
+                }
+
+                Line line_chr = editor->lines.items[row];
+
+                if (select_begin_chr < line_chr.begin) {
+                    select_begin_chr = line_chr.begin;
+                }
+
+                if (select_end_chr > line_chr.end) {
+                    select_end_chr = line_chr.end;
+                }
+
+                if (select_begin_chr <= select_end_chr) {
+                    Vec2f select_begin_scr = vec2f(0, -(float)row * FREE_GLYPH_FONT_SIZE);
+                    free_glyph_atlas_render_line_sized(
+                        atlas, sr, editor->data.items + line_chr.begin, select_begin_chr - line_chr.begin,
+                        &select_begin_scr,
+                        false);
+
+                    Vec2f select_end_scr = select_begin_scr;
+                    free_glyph_atlas_render_line_sized(
+                        atlas, sr, editor->data.items + select_begin_chr, select_end_chr - select_begin_chr,
+                        &select_end_scr,
+                        false);
+
+                    Vec4f selection_color = vec4f(.25, .25, .25, 1);
+                    simple_renderer_solid_rect(sr, select_begin_scr, vec2f(select_end_scr.x - select_begin_scr.x, FREE_GLYPH_FONT_SIZE), selection_color);
+                }
+            }
+        }
+        simple_renderer_flush(sr);
+
+        simple_renderer_set_shader(sr, SHADER_FOR_EPICNESS);
+        for (size_t row = 0; row < editor->lines.count; ++row) {
+            Line line = editor->lines.items[row];
+
+            const Vec2f begin = vec2f(0, -(float)row * FREE_GLYPH_FONT_SIZE);
+            Vec2f end = begin;
+            free_glyph_atlas_render_line_sized(
+                atlas, sr, editor->data.items + line.begin, line.end - line.begin,
+                &end,
+                true);
+            // TODO: the max_line_len should be calculated based on what's visible on the screen right now
+            float line_len = fabsf(end.x - begin.x);
+            if (line_len > max_line_len) {
+                max_line_len = line_len;
+            }
+        }
+
+        simple_renderer_flush(sr);
+    }
+
+    Vec2f cursor_pos = vec2fs(0.0f);
+    {
+        size_t cursor_row = editor_cursor_row(editor);
+        Line line = editor->lines.items[cursor_row];
+        size_t cursor_col = editor->cursor - line.begin;
+        cursor_pos.y = -(float) cursor_row * FREE_GLYPH_FONT_SIZE;
+        cursor_pos.x = free_glyph_atlas_cursor_pos(
+                           atlas,
+                           editor->data.items + line.begin, line.end - line.begin,
+                           vec2f(0.0, cursor_pos.y),
+                           cursor_col
+                       );
+    }
+
+    // Render cursor
+    simple_renderer_set_shader(sr, SHADER_FOR_COLOR);
+    {
+        float CURSOR_WIDTH = 5.0f;
+        Uint32 CURSOR_BLINK_THRESHOLD = 500;
+        Uint32 CURSOR_BLINK_PERIOD = 1000;
+        Uint32 t = SDL_GetTicks() - editor->last_stroke;
+
+        sr->verticies_count = 0;
+        if (t < CURSOR_BLINK_THRESHOLD || t/CURSOR_BLINK_PERIOD%2 != 0) {
+            simple_renderer_solid_rect(
+                sr,
+                cursor_pos, vec2f(CURSOR_WIDTH, FREE_GLYPH_FONT_SIZE),
+                vec4fs(1));
+        }
+
+        simple_renderer_flush(sr);
+    }
+
+    // Update camera
+    {
+        float target_scale = 3.0f;
+        if (max_line_len > 1000.0f) {
+            max_line_len = 1000.0f;
+        }
+        if (max_line_len > 0.0f) {
+            target_scale = SCREEN_WIDTH / max_line_len;
+        }
+
+        if (target_scale > 3.0f) {
+            target_scale = 3.0f;
+        }
+
+        sr->camera_vel = vec2f_mul(
+                             vec2f_sub(cursor_pos, sr->camera_pos),
+                             vec2fs(2.0f));
+        sr->camera_scale_vel = (target_scale - sr->camera_scale) * 2.0f;
+
+        sr->camera_pos = vec2f_add(sr->camera_pos, vec2f_mul(sr->camera_vel, vec2fs(DELTA_TIME)));
+        sr->camera_scale = sr->camera_scale + sr->camera_scale_vel * DELTA_TIME;
+    }
+}
+
+void editor_update_selection(Editor *e, bool shift)
+{
+    if (shift) {
+        if (!e->selection) {
+            e->selection = true;
+            e->select_begin = e->cursor;
+        }
+    } else {
+        if (e->selection) {
+            e->selection = false;
+        }
+    }
+}
diff --git a/src/editor.h b/src/editor.h
@@ -3,6 +3,10 @@
 
 #include <stdlib.h>
 #include "common.h"
+#include "free_glyph.h"
+#include "simple_renderer.h"
+
+#include <SDL2/SDL.h>
 
 typedef struct {
     size_t begin;
@@ -19,7 +23,12 @@ typedef struct {
     String_Builder data;
     Lines lines;
     String_Builder file_path;
+
+    bool selection;
+    size_t select_begin;
     size_t cursor;
+
+    Uint32 last_stroke;
 } Editor;
 
 Errno editor_save_as(Editor *editor, const char *file_path);
@@ -35,5 +44,7 @@ void editor_move_char_left(Editor *e);
 void editor_move_char_right(Editor *e);
 void editor_insert_char(Editor *e, char x);
 void editor_recompute_lines(Editor *e);
+void editor_render(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer *sr, Editor *editor);
+void editor_update_selection(Editor *e, bool shift);
 
 #endif // EDITOR_H_
diff --git a/src/free_glyph.h b/src/free_glyph.h
@@ -15,6 +15,8 @@
 
 #include "simple_renderer.h"
 
+#define FREE_GLYPH_FONT_SIZE 64
+
 // https://en.wikibooks.org/wiki/OpenGL_Programming/Modern_OpenGL_Tutorial_Text_Rendering_02
 
 typedef struct {
diff --git a/src/main.c b/src/main.c
@@ -20,11 +20,6 @@
 #include "./simple_renderer.h"
 #include "./common.h"
 
-#define SCREEN_WIDTH 800
-#define SCREEN_HEIGHT 600
-#define FPS 60
-#define DELTA_TIME (1.0f / FPS)
-
 // TODO: Save file dialog
 // Needed when ded is ran without any file so it does not know where to save.
 // TODO: File Manager
@@ -54,9 +49,6 @@ static Free_Glyph_Atlas atlas = {0};
 static Simple_Renderer sr = {0};
 static Editor editor = {0};
 static File_Browser fb = {0};
-static Uint32 last_stroke = 0;
-
-#define FREE_GLYPH_FONT_SIZE 64
 
 void render_file_browser(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer *sr, const File_Browser *fb)
 {
@@ -121,95 +113,6 @@ void render_file_browser(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Ren
     }
 }
 
-void render_editor(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer *sr, Editor *editor)
-{
-    int w, h;
-    SDL_GetWindowSize(window, &w, &h);
-
-    float max_line_len = 0.0f;
-
-    sr->resolution = vec2f(w, h);
-    sr->time = (float) SDL_GetTicks() / 1000.0f;
-
-    // Render text
-    simple_renderer_set_shader(sr, SHADER_FOR_EPICNESS);
-    {
-        for (size_t row = 0; row < editor->lines.count; ++row) {
-            Line line = editor->lines.items[row];
-
-            const Vec2f begin = vec2f(0, -(float)row * FREE_GLYPH_FONT_SIZE);
-            Vec2f end = begin;
-            free_glyph_atlas_render_line_sized(
-                atlas, sr, editor->data.items + line.begin, line.end - line.begin,
-                &end,
-                true);
-            // TODO: the max_line_len should be calculated based on what's visible on the screen right now
-            float line_len = fabsf(end.x - begin.x);
-            if (line_len > max_line_len) {
-                max_line_len = line_len;
-            }
-        }
-
-        simple_renderer_flush(sr);
-    }
-
-    Vec2f cursor_pos = vec2fs(0.0f);
-    {
-        size_t cursor_row = editor_cursor_row(editor);
-        Line line = editor->lines.items[cursor_row];
-        size_t cursor_col = editor->cursor - line.begin;
-        cursor_pos.y = -(float) cursor_row * FREE_GLYPH_FONT_SIZE;
-        cursor_pos.x = free_glyph_atlas_cursor_pos(
-                           atlas,
-                           editor->data.items + line.begin, line.end - line.begin,
-                           vec2f(0.0, cursor_pos.y),
-                           cursor_col
-                       );
-    }
-
-    // Render cursor
-    simple_renderer_set_shader(sr, SHADER_FOR_COLOR);
-    {
-        float CURSOR_WIDTH = 5.0f;
-        Uint32 CURSOR_BLINK_THRESHOLD = 500;
-        Uint32 CURSOR_BLINK_PERIOD = 1000;
-        Uint32 t = SDL_GetTicks() - last_stroke;
-
-        sr->verticies_count = 0;
-        if (t < CURSOR_BLINK_THRESHOLD || t/CURSOR_BLINK_PERIOD%2 != 0) {
-            simple_renderer_solid_rect(
-                sr,
-                cursor_pos, vec2f(CURSOR_WIDTH, FREE_GLYPH_FONT_SIZE),
-                vec4fs(1));
-        }
-
-        simple_renderer_flush(sr);
-    }
-
-    // Update camera
-    {
-        float target_scale = 3.0f;
-        if (max_line_len > 1000.0f) {
-            max_line_len = 1000.0f;
-        }
-        if (max_line_len > 0.0f) {
-            target_scale = SCREEN_WIDTH / max_line_len;
-        }
-
-        if (target_scale > 3.0f) {
-            target_scale = 3.0f;
-        }
-
-        sr->camera_vel = vec2f_mul(
-                             vec2f_sub(cursor_pos, sr->camera_pos),
-                             vec2fs(2.0f));
-        sr->camera_scale_vel = (target_scale - sr->camera_scale) * 2.0f;
-
-        sr->camera_pos = vec2f_add(sr->camera_pos, vec2f_mul(sr->camera_vel, vec2fs(DELTA_TIME)));
-        sr->camera_scale = sr->camera_scale + sr->camera_scale_vel * DELTA_TIME;
-    }
-}
-
 // TODO: display errors reported via flash_error right in the text editor window somehow
 #define flash_error(...) fprintf(stderr, __VA_ARGS__)
 
@@ -380,7 +283,7 @@ int main(int argc, char **argv)
                     switch (event.key.keysym.sym) {
                     case SDLK_BACKSPACE: {
                         editor_backspace(&editor);
-                        last_stroke = SDL_GetTicks();
+                        editor.last_stroke = SDL_GetTicks();
                     }
                     break;
 
@@ -404,37 +307,50 @@ int main(int argc, char **argv)
 
                     case SDLK_RETURN: {
                         editor_insert_char(&editor, '\n');
-                        last_stroke = SDL_GetTicks();
+                        editor.last_stroke = SDL_GetTicks();
                     }
                     break;
 
                     case SDLK_DELETE: {
                         editor_delete(&editor);
-                        last_stroke = SDL_GetTicks();
+                        editor.last_stroke = SDL_GetTicks();
+                    }
+                    break;
+
+                    case SDLK_a: {
+                        if (event.key.keysym.mod & KMOD_CTRL) {
+                            editor.selection = true;
+                            editor.select_begin = 0;
+                            editor.cursor = editor.data.count;
+                        }
                     }
                     break;
 
                     case SDLK_UP: {
+                        editor_update_selection(&editor, event.key.keysym.mod & KMOD_SHIFT);
                         editor_move_line_up(&editor);
-                        last_stroke = SDL_GetTicks();
+                        editor.last_stroke = SDL_GetTicks();
                     }
                     break;
 
                     case SDLK_DOWN: {
+                        editor_update_selection(&editor, event.key.keysym.mod & KMOD_SHIFT);
                         editor_move_line_down(&editor);
-                        last_stroke = SDL_GetTicks();
+                        editor.last_stroke = SDL_GetTicks();
                     }
                     break;
 
                     case SDLK_LEFT: {
+                        editor_update_selection(&editor, event.key.keysym.mod & KMOD_SHIFT);
                         editor_move_char_left(&editor);
-                        last_stroke = SDL_GetTicks();
+                        editor.last_stroke = SDL_GetTicks();
                     }
                     break;
 
                     case SDLK_RIGHT: {
+                        editor_update_selection(&editor, event.key.keysym.mod & KMOD_SHIFT);
                         editor_move_char_right(&editor);
-                        last_stroke = SDL_GetTicks();
+                        editor.last_stroke = SDL_GetTicks();
                     }
                     break;
                     }
@@ -451,7 +367,7 @@ int main(int argc, char **argv)
                     for (size_t i = 0; i < text_len; ++i) {
                         editor_insert_char(&editor, text[i]);
                     }
-                    last_stroke = SDL_GetTicks();
+                    editor.last_stroke = SDL_GetTicks();
                 }
             }
             break;
@@ -471,7 +387,7 @@ int main(int argc, char **argv)
         if (file_browser) {
             render_file_browser(window, &atlas, &sr, &fb);
         } else {
-            render_editor(window, &atlas, &sr, &editor);
+            editor_render(window, &atlas, &sr, &editor);
         }
 
         SDL_GL_SwapWindow(window);