commit 983c71135214353f6c6b7544ea112cd3afd696a9
parent f62bf8c5814648e97aa8e1b08e6da193c836e516
Author: rexim <reximkut@gmail.com>
Date: Fri, 13 Jan 2023 01:12:07 +0700
Add visual support for selections
Diffstat:
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);