Drawing the cursor and cell setup
This commit is contained in:
@@ -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
|
#define SRC_CACHE_ENTRY_COUNT 1024
|
||||||
|
|
||||||
struct SRC_CacheEntry {
|
struct SRC_CacheEntry {
|
||||||
|
|||||||
@@ -4,25 +4,10 @@
|
|||||||
#include "raymath.h"
|
#include "raymath.h"
|
||||||
|
|
||||||
#include "buffer.cpp"
|
#include "buffer.cpp"
|
||||||
|
#include "rect2.cpp"
|
||||||
|
|
||||||
Arena FrameArena;
|
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
|
// Render units - positions ready to draw, y
|
||||||
// World units - positions offset by screen movement
|
// World units - positions offset by screen movement
|
||||||
// Window units - positions inside the window (starts in left top of window)
|
// Window units - positions inside the window (starts in left top of window)
|
||||||
@@ -59,8 +44,6 @@ Rect2 GetScreenRectRenderUnits() {
|
|||||||
return result;
|
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) {
|
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
|
if (font.texture.id == 0) font = GetFontDefault(); // Security check in case of not valid font
|
||||||
|
|
||||||
@@ -100,13 +83,16 @@ int main() {
|
|||||||
Array<Window> windows = {};
|
Array<Window> windows = {};
|
||||||
windows.add({GetScreenRectRenderUnits()});
|
windows.add({GetScreenRectRenderUnits()});
|
||||||
{
|
{
|
||||||
Buffer *buffer = &windows[0].buffer;
|
Window *window = &windows[0];
|
||||||
|
Buffer *buffer = &window->buffer;
|
||||||
InitBuffer(buffer);
|
InitBuffer(buffer);
|
||||||
for (int i = 0; i < 100; i += 1) {
|
for (int i = 0; i < 100; i += 1) {
|
||||||
Array<Edit> edits = {FrameArena};
|
Array<Edit> edits = {FrameArena};
|
||||||
AddEdit(&edits, GetEnd(*buffer), Format(FrameArena, "line number: %d\n", i));
|
AddEdit(&edits, GetEnd(*buffer), Format(FrameArena, "line number: %d\n", i));
|
||||||
ApplyEdits(buffer, edits);
|
ApplyEdits(buffer, edits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window->cursors.add({});
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec2 camera_offset_world_to_render_units = {};
|
Vec2 camera_offset_world_to_render_units = {};
|
||||||
@@ -130,15 +116,22 @@ int main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Array<Edit> edits = {FrameArena};
|
Array<Edit> edits = {FrameArena};
|
||||||
AddEdit(&edits, {}, string);
|
For(focused_window->cursors) {
|
||||||
|
AddEdit(&edits, it, string);
|
||||||
|
}
|
||||||
ApplyEdits(&focused_window->buffer, edits);
|
ApplyEdits(&focused_window->buffer, edits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IsKeyPressed(KEY_F1)) {
|
||||||
|
font = LoadFontEx("C:/Windows/Fonts/consola.ttf", (int)font_size, NULL, 250);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BeginDrawing();
|
BeginDrawing();
|
||||||
ClearBackground(RAYWHITE);
|
ClearBackground(RAYWHITE);
|
||||||
|
|
||||||
ForItem(window, windows) {
|
ForItem(window, windows) {
|
||||||
|
// Draw the frame
|
||||||
Rect2 window_rect_in_render_units = {
|
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.min, camera_offset_world_to_render_units),
|
||||||
WorldToRenderUnits(window.rect_in_world_units.max, 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);
|
Rectangle rectangle_in_render_units = ToRectangle(window_rect_in_render_units);
|
||||||
DrawRectangleRec(rectangle_in_render_units, WHITE);
|
DrawRectangleRec(rectangle_in_render_units, WHITE);
|
||||||
|
|
||||||
//
|
Rect2 screen_rect_in_render_units = GetScreenRectRenderUnits();
|
||||||
// Line rendering
|
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);
|
||||||
// @todo: how to constrain 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);
|
||||||
// @optimize: I could clip to visible on screen window_rectangle but I couldn't get it right
|
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);
|
Vec2 s = GetSize(window_rect_in_render_units);
|
||||||
float line_offset = font_size;
|
float line_offset = font_size;
|
||||||
float _line_min_y = (window.window_world_to_window_units.y) / line_offset;
|
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_min_y = (int64_t)floorf(_line_min_y);
|
||||||
int64_t line_max_y = (int64_t)ceilf(_line_max_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);
|
struct Cell {
|
||||||
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);
|
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) {
|
for (int64_t line = line_min_y; line < line_max_y; line += 1) {
|
||||||
if (line < 0) break;
|
if (line < 0) break;
|
||||||
if (line >= window.buffer.lines.len) break;
|
if (line >= window.buffer.lines.len) break;
|
||||||
|
|
||||||
|
CellRow row = {};
|
||||||
|
row.line = line;
|
||||||
|
row.cells.allocator = FrameArena;
|
||||||
|
|
||||||
Range line_range = window.buffer.lines[line];
|
Range line_range = window.buffer.lines[line];
|
||||||
|
|
||||||
Vec2 text_position_in_world_window_units = {0, line_offset * (float)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_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_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);
|
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);
|
String text = GetString(window.buffer, line_range);
|
||||||
if (font.texture.id == 0) font = GetFontDefault();
|
if (font.texture.id == 0) font = GetFontDefault();
|
||||||
float textOffsetX = 0.0f;
|
float textOffsetX = 0.0f;
|
||||||
float scaleFactor = font_size / font.baseSize; // Character quad scaling factor
|
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 codepointByteCount = 0;
|
||||||
int codepoint = GetCodepointNext(&text.data[i], &codepointByteCount);
|
int codepoint = GetCodepointNext(&text.data[i], &codepointByteCount);
|
||||||
int index = GetGlyphIndex(font, codepoint);
|
int index = GetGlyphIndex(font, codepoint);
|
||||||
@@ -188,18 +201,57 @@ int main() {
|
|||||||
float x_to_offset_by = ((float)glyph->advanceX * scaleFactor + font_spacing);
|
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);
|
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};
|
Vec2 cell_size = {x_to_offset_by, font_size};
|
||||||
Rect2 cell_rect = {glyph_position, Vector2Add(glyph_position, cell_size)};
|
Rect2 cell_rect = {glyph_position, Vector2Add(glyph_position, cell_size)};
|
||||||
|
Rectangle cell_rectangle = ToRectangle(cell_rect);
|
||||||
DrawRectangleLinesEx(ToRectangle(cell_rect), 1, {255, 0, 0, 120});
|
if (CheckCollisionRecs(cell_rectangle, ToRectangle(window_rect_in_render_units_clamped_to_screen))) {
|
||||||
if ((codepoint != ' ') && (codepoint != '\t')) {
|
row.cells.add({cell_rect, codepoint, line_range.min + i});
|
||||||
DrawTextCodepoint(font, codepoint, glyph_position, font_size, BLACK);
|
row.rect.max = cell_rect.max;
|
||||||
}
|
}
|
||||||
|
|
||||||
textOffsetX += x_to_offset_by;
|
textOffsetX += x_to_offset_by;
|
||||||
i += codepointByteCount; // Move text bytes counter to next codepoint
|
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();
|
EndScissorMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
65
src/text_editor/rect2.cpp
Normal file
65
src/text_editor/rect2.cpp
Normal 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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user