Drawing the cursor and cell setup

This commit is contained in:
Krzosa Karol
2024-06-22 14:48:32 +02:00
parent 443e88b4a9
commit 7b818c5cde
3 changed files with 162 additions and 37 deletions

View File

@@ -1,3 +1,11 @@
/* @todo: I guess I'm overcomplicating a little:
https://github.com/Mango0x45/cbs/
Seems like you only need to compare timestamps of all files with the artifact timestamp,
if one of the files is newer then the artifact then it needs to be recompiled. Do we
even need to store anything then? Just supply artifact file in code and that's it?
*/
#define SRC_CACHE_ENTRY_COUNT 1024
struct SRC_CacheEntry {

View File

@@ -4,25 +4,10 @@
#include "raymath.h"
#include "buffer.cpp"
#include "rect2.cpp"
Arena FrameArena;
using Vec2 = Vector2;
struct Rect2 {
Vec2 min;
Vec2 max;
};
Vec2 GetSize(Rect2 r) {
Vec2 result = {r.max.x - r.min.x, r.max.y - r.min.y};
return result;
}
Rectangle ToRectangle(Rect2 r) {
Rectangle result = {r.min.x, r.min.y, r.max.x - r.min.x, r.max.y - r.min.y};
return result;
}
// Render units - positions ready to draw, y
// World units - positions offset by screen movement
// Window units - positions inside the window (starts in left top of window)
@@ -59,8 +44,6 @@ Rect2 GetScreenRectRenderUnits() {
return result;
}
// Draw text using Font
// NOTE: chars spacing is NOT proportional to fontSize
void DrawString(Font font, String text, Vector2 position, float fontSize, float spacing, Color tint) {
if (font.texture.id == 0) font = GetFontDefault(); // Security check in case of not valid font
@@ -100,13 +83,16 @@ int main() {
Array<Window> windows = {};
windows.add({GetScreenRectRenderUnits()});
{
Buffer *buffer = &windows[0].buffer;
Window *window = &windows[0];
Buffer *buffer = &window->buffer;
InitBuffer(buffer);
for (int i = 0; i < 100; i += 1) {
Array<Edit> edits = {FrameArena};
AddEdit(&edits, GetEnd(*buffer), Format(FrameArena, "line number: %d\n", i));
ApplyEdits(buffer, edits);
}
window->cursors.add({});
}
Vec2 camera_offset_world_to_render_units = {};
@@ -130,15 +116,22 @@ int main() {
}
Array<Edit> edits = {FrameArena};
AddEdit(&edits, {}, string);
For(focused_window->cursors) {
AddEdit(&edits, it, string);
}
ApplyEdits(&focused_window->buffer, edits);
}
if (IsKeyPressed(KEY_F1)) {
font = LoadFontEx("C:/Windows/Fonts/consola.ttf", (int)font_size, NULL, 250);
}
}
BeginDrawing();
ClearBackground(RAYWHITE);
ForItem(window, windows) {
// Draw the frame
Rect2 window_rect_in_render_units = {
WorldToRenderUnits(window.rect_in_world_units.min, camera_offset_world_to_render_units),
WorldToRenderUnits(window.rect_in_world_units.max, camera_offset_world_to_render_units),
@@ -146,11 +139,18 @@ int main() {
Rectangle rectangle_in_render_units = ToRectangle(window_rect_in_render_units);
DrawRectangleRec(rectangle_in_render_units, WHITE);
//
// Line rendering
//
// @todo: how to constrain x?
// @optimize: I could clip to visible on screen window_rectangle but I couldn't get it right
Rect2 screen_rect_in_render_units = GetScreenRectRenderUnits();
Rect2 window_rect_in_render_units_clamped_to_screen = window_rect_in_render_units;
window_rect_in_render_units_clamped_to_screen.min.x = Clamp(window_rect_in_render_units_clamped_to_screen.min.x, screen_rect_in_render_units.min.x, screen_rect_in_render_units.max.x);
window_rect_in_render_units_clamped_to_screen.max.x = Clamp(window_rect_in_render_units_clamped_to_screen.max.x, screen_rect_in_render_units.min.x, screen_rect_in_render_units.max.x);
window_rect_in_render_units_clamped_to_screen.min.y = Clamp(window_rect_in_render_units_clamped_to_screen.min.y, screen_rect_in_render_units.min.y, screen_rect_in_render_units.max.y);
window_rect_in_render_units_clamped_to_screen.max.y = Clamp(window_rect_in_render_units_clamped_to_screen.max.y, screen_rect_in_render_units.min.y, screen_rect_in_render_units.max.y);
if (0) {
window_rect_in_render_units_clamped_to_screen = Shrink(window_rect_in_render_units_clamped_to_screen, 10);
DrawRectangleRec(ToRectangle(window_rect_in_render_units_clamped_to_screen), {255, 0, 0, 50});
}
// Figure out which lines to draw
Vec2 s = GetSize(window_rect_in_render_units);
float line_offset = font_size;
float _line_min_y = (window.window_world_to_window_units.y) / line_offset;
@@ -158,27 +158,40 @@ int main() {
int64_t line_min_y = (int64_t)floorf(_line_min_y);
int64_t line_max_y = (int64_t)ceilf(_line_max_y);
Vec2 window_rect_in_render_units_size = GetSize(window_rect_in_render_units);
BeginScissorMode((int)window_rect_in_render_units.min.x, (int)window_rect_in_render_units.min.y, (int)window_rect_in_render_units_size.x, (int)window_rect_in_render_units_size.y);
struct Cell {
Rect2 rect;
int codepoint;
int64_t pos;
};
struct CellRow {
Array<Cell> cells;
Rect2 rect;
int64_t line;
};
Array<CellRow> rows = {FrameArena};
for (int64_t line = line_min_y; line < line_max_y; line += 1) {
if (line < 0) break;
if (line >= window.buffer.lines.len) break;
CellRow row = {};
row.line = line;
row.cells.allocator = FrameArena;
Range line_range = window.buffer.lines[line];
Vec2 text_position_in_world_window_units = {0, line_offset * (float)line};
Vec2 text_position_in_window_units = WindowWorldToWindowUnits(text_position_in_world_window_units, window);
Vec2 text_position_in_world_units = WindowToWorldUnits(text_position_in_window_units, window);
Vec2 text_position_in_render_units = WorldToRenderUnits(text_position_in_world_units, camera_offset_world_to_render_units);
row.rect.min = text_position_in_render_units;
//
// Glyph inside the line rendering
//
String text = GetString(window.buffer, line_range);
if (font.texture.id == 0) font = GetFontDefault();
float textOffsetX = 0.0f;
float scaleFactor = font_size / font.baseSize; // Character quad scaling factor
for (int i = 0; i < text.len;) {
for (int64_t i = 0; i < text.len;) {
int codepointByteCount = 0;
int codepoint = GetCodepointNext(&text.data[i], &codepointByteCount);
int index = GetGlyphIndex(font, codepoint);
@@ -188,18 +201,57 @@ int main() {
float x_to_offset_by = ((float)glyph->advanceX * scaleFactor + font_spacing);
if (glyph->advanceX == 0) x_to_offset_by = ((float)font.recs[index].width * scaleFactor + font_spacing);
Vec2 cell_size = {x_to_offset_by, font_size};
Rect2 cell_rect = {glyph_position, Vector2Add(glyph_position, cell_size)};
DrawRectangleLinesEx(ToRectangle(cell_rect), 1, {255, 0, 0, 120});
if ((codepoint != ' ') && (codepoint != '\t')) {
DrawTextCodepoint(font, codepoint, glyph_position, font_size, BLACK);
Vec2 cell_size = {x_to_offset_by, font_size};
Rect2 cell_rect = {glyph_position, Vector2Add(glyph_position, cell_size)};
Rectangle cell_rectangle = ToRectangle(cell_rect);
if (CheckCollisionRecs(cell_rectangle, ToRectangle(window_rect_in_render_units_clamped_to_screen))) {
row.cells.add({cell_rect, codepoint, line_range.min + i});
row.rect.max = cell_rect.max;
}
textOffsetX += x_to_offset_by;
i += codepointByteCount; // Move text bytes counter to next codepoint
}
if (row.cells.len) rows.add(row);
}
// Draw debug markers
if (0) {
ForItem(row, rows) {
For(row.cells) {
DrawRectangleLinesEx(ToRectangle(it.rect), 1, {255, 0, 0, 50});
}
DrawRectangleLinesEx(ToRectangle(row.rect), 1, {0, 190, 50, 255});
}
}
// Draw the glyphs
Vec2 window_rect_in_render_units_size = GetSize(window_rect_in_render_units);
BeginScissorMode((int)window_rect_in_render_units.min.x, (int)window_rect_in_render_units.min.y, (int)window_rect_in_render_units_size.x, (int)window_rect_in_render_units_size.y);
ForItem(row, rows) {
For(row.cells) {
if ((it.codepoint != ' ') && (it.codepoint != '\t')) {
DrawTextCodepoint(font, it.codepoint, it.rect.min, font_size, BLACK);
}
}
}
ForItem(cursor, window.cursors) {
ForItem(row, rows) {
Range line_range = window.buffer.lines[row.line];
if (cursor.min >= line_range.min && cursor.min <= line_range.max) {
ForItem(cell, row.cells) {
if (cursor.min == cell.pos) {
Rect2 rect = cell.rect;
rect = CutLeft(&rect, 4);
DrawRectangleRec(ToRectangle(rect), RED);
}
}
}
}
}
EndScissorMode();
}

65
src/text_editor/rect2.cpp Normal file
View File

@@ -0,0 +1,65 @@
using Vec2 = Vector2;
struct Rect2 {
Vec2 min;
Vec2 max;
};
Vec2 GetSize(Rect2 r) {
Vec2 result = {r.max.x - r.min.x, r.max.y - r.min.y};
return result;
}
Rectangle ToRectangle(Rect2 r) {
Rectangle result = {r.min.x, r.min.y, r.max.x - r.min.x, r.max.y - r.min.y};
return result;
}
Rect2 Shrink(Rect2 result, float v) {
result.min.x += v;
result.max.x -= v;
result.min.y += v;
result.max.y -= v;
return result;
}
Rect2 CutLeft(Rect2 *r, float value) {
float minx = r->min.x;
r->min.x = Min(r->min.x + value, r->max.x);
Rect2 result = {
{ minx, r->min.y},
{r->min.x, r->max.y}
};
return result;
}
Rect2 CutRight(Rect2 *r, float value) {
float maxx = r->max.x;
r->max.x = Max(r->min.x, r->max.x - value);
Rect2 result = {
{r->max.x, r->min.y},
{ maxx, r->max.y},
};
return result;
}
// Y is up
Rect2 CutBottom(Rect2 *r, float value) {
float maxy = r->max.y;
r->max.y = Max(r->min.y, r->max.y - value);
Rect2 result = {
{r->min.x, r->max.y},
{r->max.x, maxy},
};
return result;
}
// Y is up
Rect2 CutTop(Rect2 *r, float value) {
float miny = r->min.y;
r->min.y = Min(r->min.y + value, r->max.y);
Rect2 result = {
{r->min.x, miny},
{r->max.x, r->min.y},
};
return result;
}