Basic cursor movement, handling end of buffer, end of line in drawing

This commit is contained in:
Krzosa Karol
2024-06-22 16:09:26 +02:00
parent 7b818c5cde
commit 5ba9975c10
3 changed files with 85 additions and 33 deletions

View File

@@ -252,17 +252,22 @@ Line FindLine(Buffer &buffer, int64_t pos) {
return {}; return {};
} }
int64_t AdjustUTF8Pos(const Buffer &buffer, int64_t pos, int64_t direction = 1) { int64_t AdjustUTF8Pos(String string, int64_t pos, int64_t direction) {
int64_t result = pos; for (; pos >= 0 && pos < string.len;) {
for (; result >= 0 && result < buffer.len;) { if (IsUTF8ContinuationByte(string.data[pos])) {
char c = GetChar(buffer, pos); pos += direction;
if (IsUTF8ContinuationByte(c)) {
result += direction;
} else { } else {
break; break;
} }
} }
return result; return pos;
}
int64_t AdjustUTF8Pos(const Buffer &buffer, int64_t pos, int64_t direction = 1, bool clamp = true) {
String string = GetString(buffer);
pos = AdjustUTF8Pos(string, pos, direction);
if (clamp) pos = Clamp(buffer, pos);
return pos;
} }
uint32_t GetUTF32(Buffer &buffer, int64_t pos, int64_t *codepoint_size) { uint32_t GetUTF32(Buffer &buffer, int64_t pos, int64_t *codepoint_size) {
@@ -317,7 +322,7 @@ void Advance(BufferIter *iter) {
if (iter->direction == ITERATE_FORWARD) { if (iter->direction == ITERATE_FORWARD) {
iter->pos += iter->utf8_codepoint_size; iter->pos += iter->utf8_codepoint_size;
} else { } else {
iter->pos = AdjustUTF8Pos(*iter->buffer, iter->pos - 1, ITERATE_BACKWARD); iter->pos = AdjustUTF8Pos(*iter->buffer, iter->pos - 1, ITERATE_BACKWARD, false);
} }
if (!IsValid(*iter)) return; if (!IsValid(*iter)) return;

View File

@@ -68,6 +68,18 @@ void DrawString(Font font, String text, Vector2 position, float fontSize, float
} }
} }
int64_t MoveRight(Buffer &buffer, int64_t pos) {
int64_t result = pos + 1;
result = AdjustUTF8Pos(buffer, result);
return result;
}
int64_t MoveLeft(Buffer &buffer, int64_t pos) {
int64_t result = pos - 1;
result = AdjustUTF8Pos(buffer, result, -1);
return result;
}
int main() { int main() {
InitScratch(); InitScratch();
RunBufferTests(); RunBufferTests();
@@ -76,9 +88,10 @@ int main() {
SetTargetFPS(60); SetTargetFPS(60);
InitArena(&FrameArena); InitArena(&FrameArena);
float font_size = 25; float font_size = 64;
float font_spacing = 1; float font_spacing = 1;
Font font = LoadFontEx("C:/Windows/Fonts/times.ttf", (int)font_size, NULL, 250); Font font = LoadFontEx("C:/Windows/Fonts/times.ttf", (int)font_size, NULL, 250);
if (0) font = LoadFontEx("C:/Windows/Fonts/consola.ttf", (int)font_size, NULL, 250);
Array<Window> windows = {}; Array<Window> windows = {};
windows.add({GetScreenRectRenderUnits()}); windows.add({GetScreenRectRenderUnits()});
@@ -86,11 +99,13 @@ int main() {
Window *window = &windows[0]; Window *window = &windows[0];
Buffer *buffer = &window->buffer; Buffer *buffer = &window->buffer;
InitBuffer(buffer); InitBuffer(buffer);
if (0) {
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({}); window->cursors.add({});
} }
@@ -100,7 +115,7 @@ int main() {
{ {
Window *focused_window = &windows[0]; Window *focused_window = &windows[0];
if (IsMouseButtonDown(MOUSE_BUTTON_LEFT) || IsKeyDown(KEY_SPACE)) { if (IsKeyDown(KEY_F1)) {
camera_offset_world_to_render_units = Vector2Subtract(camera_offset_world_to_render_units, GetMouseDelta()); camera_offset_world_to_render_units = Vector2Subtract(camera_offset_world_to_render_units, GetMouseDelta());
} }
@@ -116,14 +131,21 @@ int main() {
} }
Array<Edit> edits = {FrameArena}; Array<Edit> edits = {FrameArena};
For(focused_window->cursors) { For(focused_window->cursors) AddEdit(&edits, it, string);
AddEdit(&edits, it, string);
}
ApplyEdits(&focused_window->buffer, edits); ApplyEdits(&focused_window->buffer, edits);
For(focused_window->cursors)
it.max = it.min = MoveRight(focused_window->buffer, it.min);
} }
if (IsKeyPressed(KEY_F1)) { if (IsKeyPressed(KEY_LEFT) || IsKeyPressedRepeat(KEY_LEFT)) {
font = LoadFontEx("C:/Windows/Fonts/consola.ttf", (int)font_size, NULL, 250); For(focused_window->cursors) {
it.max = it.min = MoveLeft(focused_window->buffer, it.min);
}
}
if (IsKeyPressed(KEY_RIGHT) || IsKeyPressedRepeat(KEY_RIGHT)) {
For(focused_window->cursors) {
it.max = it.min = MoveRight(focused_window->buffer, it.min);
}
} }
} }
@@ -131,7 +153,6 @@ int main() {
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),
@@ -170,30 +191,41 @@ int main() {
int64_t line; int64_t line;
}; };
// Compute visible rows and cells
Array<CellRow> rows = {FrameArena}; 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);
CellRow row = {};
row.line = line;
row.cells.allocator = FrameArena;
row.rect.min = text_position_in_render_units; row.rect.min = text_position_in_render_units;
// Iterate the string and compute cells for current font
// we are also incorporating - new line and end of buffer glyphs
// into the stream!
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 (int64_t i = 0; i < text.len;) { for (int64_t i = 0;;) {
int codepointByteCount = 0; bool end_of_buffer = i == window.buffer.len;
int codepoint = GetCodepointNext(&text.data[i], &codepointByteCount); bool new_line = i == line_range.max && i != window.buffer.len;
bool in_range = i < text.len;
bool continue_looping = end_of_buffer || new_line || in_range;
if (!continue_looping) break;
int codepointByteCount = 1;
int codepoint = '\n';
if (in_range) codepoint = GetCodepointNext(&text.data[i], &codepointByteCount);
int index = GetGlyphIndex(font, codepoint); int index = GetGlyphIndex(font, codepoint);
GlyphInfo *glyph = font.glyphs + index; GlyphInfo *glyph = font.glyphs + index;
Vec2 glyph_position = {text_position_in_render_units.x + textOffsetX, text_position_in_render_units.y}; Vec2 glyph_position = {text_position_in_render_units.x + textOffsetX, text_position_in_render_units.y};
@@ -204,6 +236,8 @@ int main() {
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); Rectangle cell_rectangle = ToRectangle(cell_rect);
// Clip everything that is outside the window and screen
if (CheckCollisionRecs(cell_rectangle, ToRectangle(window_rect_in_render_units_clamped_to_screen))) { if (CheckCollisionRecs(cell_rectangle, ToRectangle(window_rect_in_render_units_clamped_to_screen))) {
row.cells.add({cell_rect, codepoint, line_range.min + i}); row.cells.add({cell_rect, codepoint, line_range.min + i});
row.rect.max = cell_rect.max; row.rect.max = cell_rect.max;
@@ -231,16 +265,21 @@ int main() {
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); 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) { ForItem(row, rows) {
For(row.cells) { For(row.cells) {
if ((it.codepoint != ' ') && (it.codepoint != '\t')) { if (it.codepoint == '\n') {
Vec2 mid = GetMid(it.rect);
DrawCircle((int)mid.x, (int)mid.y, font_size / 8, {0, 0, 0, 120});
} else if ((it.codepoint != ' ') && (it.codepoint != '\t')) {
DrawTextCodepoint(font, it.codepoint, it.rect.min, font_size, BLACK); DrawTextCodepoint(font, it.codepoint, it.rect.min, font_size, BLACK);
} }
} }
} }
// Draw the cursor
ForItem(cursor, window.cursors) { ForItem(cursor, window.cursors) {
ForItem(row, rows) { ForItem(row, rows) {
Range line_range = window.buffer.lines[row.line]; Range line_range = window.buffer.lines[row.line];
if (cursor.min >= line_range.min && cursor.min <= line_range.max) { if (cursor.min >= line_range.min && cursor.min <= line_range.max) {
ForItem(cell, row.cells) { ForItem(cell, row.cells) {
if (cursor.min == cell.pos) { if (cursor.min == cell.pos) {
Rect2 rect = cell.rect; Rect2 rect = cell.rect;

View File

@@ -22,6 +22,14 @@ Rect2 Shrink(Rect2 result, float v) {
return result; return result;
} }
Vec2 GetMid(Rect2 r) {
Vec2 size = GetSize(r);
size.x /= 2.f;
size.y /= 2.f;
Vec2 result = Vector2Add(r.min, size);
return result;
}
Rect2 CutLeft(Rect2 *r, float value) { Rect2 CutLeft(Rect2 *r, float value) {
float minx = r->min.x; float minx = r->min.x;
r->min.x = Min(r->min.x + value, r->max.x); r->min.x = Min(r->min.x + value, r->max.x);