Major refactor of text layout
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,3 +4,4 @@ x64/Release
|
||||
|
||||
src/external
|
||||
build/
|
||||
*.rdbg
|
||||
134
src/text_editor/layout.cpp
Normal file
134
src/text_editor/layout.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
struct LayoutColumn {
|
||||
Rect2 rect;
|
||||
int64_t pos;
|
||||
int codepoint;
|
||||
};
|
||||
|
||||
struct LayoutRow {
|
||||
Rect2 rect;
|
||||
Array<LayoutColumn> columns;
|
||||
};
|
||||
|
||||
struct Layout {
|
||||
Array<LayoutRow> rows;
|
||||
Vec2 buffer_world_pixel_size;
|
||||
};
|
||||
|
||||
struct Window {
|
||||
Font font;
|
||||
float font_size;
|
||||
float font_spacing;
|
||||
Rect2 rect;
|
||||
|
||||
Vec2 scroll; // window_world_to_window_units
|
||||
Cursor main_cursor_begin_frame;
|
||||
Array<Cursor> cursors;
|
||||
Buffer buffer;
|
||||
|
||||
Arena layout_arena;
|
||||
Layout layout;
|
||||
};
|
||||
|
||||
template <class T1, class T2>
|
||||
struct Tuple {
|
||||
T1 a;
|
||||
T2 b;
|
||||
};
|
||||
|
||||
Layout CalculateLayout(Arena *arena, Buffer &buffer, Font font, float font_size, float font_spacing) {
|
||||
Layout layout = {};
|
||||
layout.rows.allocator = *arena;
|
||||
|
||||
float scaleFactor = font_size / font.baseSize; // Character quad scaling factor
|
||||
float text_offset_y = 0;
|
||||
ForItem(line_range, buffer.lines) {
|
||||
float textOffsetX = 0.0f;
|
||||
|
||||
LayoutRow *row = layout.rows.alloc();
|
||||
row->columns.allocator = *arena;
|
||||
row->rect.min = {textOffsetX, text_offset_y};
|
||||
|
||||
BufferIter iter = Iterate(buffer, line_range);
|
||||
for (;; Advance(&iter)) {
|
||||
bool end_of_buffer = iter.pos == buffer.len;
|
||||
bool new_line = iter.pos == line_range.max;
|
||||
bool in_range = IsValid(iter);
|
||||
bool continue_looping = end_of_buffer || new_line || in_range;
|
||||
if (!continue_looping) break;
|
||||
|
||||
int codepoint = '\n'; // @todo: questionable choice
|
||||
if (in_range) codepoint = iter.item;
|
||||
|
||||
int index = GetGlyphIndex(font, codepoint);
|
||||
GlyphInfo *glyph = font.glyphs + index;
|
||||
Vec2 glyph_position = {textOffsetX, text_offset_y};
|
||||
|
||||
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)};
|
||||
row->columns.add({cell_rect, iter.pos, codepoint});
|
||||
row->rect.max = cell_rect.max;
|
||||
|
||||
textOffsetX += x_to_offset_by;
|
||||
if (end_of_buffer || new_line) break;
|
||||
}
|
||||
|
||||
layout.buffer_world_pixel_size.x = Max(layout.buffer_world_pixel_size.x, textOffsetX);
|
||||
text_offset_y += font_size;
|
||||
}
|
||||
layout.buffer_world_pixel_size.y = text_offset_y;
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
LayoutRow *GetLayoutRow(Window &window, float ypos_window_buffer_world_units) {
|
||||
float line_spacing = window.font_size;
|
||||
int64_t line = (int64_t)floorf(ypos_window_buffer_world_units / line_spacing);
|
||||
if (line >= 0 && line < window.layout.rows.len) {
|
||||
return window.layout.rows.data + line;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LayoutColumn *GetLayoutColumn(LayoutRow *row, float xpos_window_buffer_world_units) {
|
||||
if (!row) return NULL;
|
||||
For(row->columns) {
|
||||
if (xpos_window_buffer_world_units >= it.rect.min.x && xpos_window_buffer_world_units <= it.rect.max.x) {
|
||||
return ⁢
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Tuple<LayoutRow *, LayoutColumn *> GetRowCol(Window &window, Vec2 pos_buffer_world_units) {
|
||||
Tuple<LayoutRow *, LayoutColumn *> result = {};
|
||||
result.a = GetLayoutRow(window, pos_buffer_world_units.y);
|
||||
result.b = GetLayoutColumn(result.a, pos_buffer_world_units.x);
|
||||
return result;
|
||||
}
|
||||
|
||||
Tuple<LayoutRow *, LayoutColumn *> GetRowCol(Window &window, int64_t pos) {
|
||||
Tuple<LayoutRow *, LayoutColumn *> result = {};
|
||||
For(window.layout.rows) {
|
||||
if (window.layout.rows.len == 0) continue;
|
||||
int64_t first_pos = it.columns.first()->pos;
|
||||
int64_t last_pos = it.columns.last()->pos;
|
||||
if (pos >= first_pos && pos <= last_pos) {
|
||||
result.a = ⁢
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (result.a) {
|
||||
For(result.a->columns) {
|
||||
if (it.pos == pos) {
|
||||
result.b = ⁢
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -5,50 +5,11 @@
|
||||
|
||||
// @todo: add history (undo, redo)
|
||||
// @todo: add clipboard history?
|
||||
#include "buffer.cpp"
|
||||
#include "rect2.cpp"
|
||||
#include "buffer.cpp"
|
||||
#include "layout.cpp"
|
||||
|
||||
// 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)
|
||||
// WindowBuffer units
|
||||
// WindowBufferWorld units
|
||||
|
||||
struct Window {
|
||||
uint64_t flags;
|
||||
Rect2 rect_in_world_units;
|
||||
Vec2 scroll; // window_world_to_window_units
|
||||
float title_bar_pixel_size;
|
||||
float line_number_bar_pixel_size;
|
||||
float right_scroll_bar_pixel_size;
|
||||
float bottom_scroll_bar_pixel_size;
|
||||
|
||||
Cursor main_cursor_begin_frame;
|
||||
Array<Cursor> cursors;
|
||||
Buffer buffer;
|
||||
};
|
||||
|
||||
Vec2 WindowBufferWorldToWindowBufferUnits(Vec2 value, const Window &window) {
|
||||
Vec2 result = Vector2Subtract(value, window.scroll);
|
||||
return result;
|
||||
}
|
||||
|
||||
Vec2 WindowBufferToWindowUnits(Vec2 value, Vec2 window_buffer_to_window_units) {
|
||||
Vec2 result = Vector2Add(value, window_buffer_to_window_units);
|
||||
return result;
|
||||
}
|
||||
|
||||
Vec2 WorldToRenderUnits(Vec2 value, Vec2 camera_offset_world_to_render_units) {
|
||||
Vec2 result = Vector2Subtract(value, camera_offset_world_to_render_units);
|
||||
return result;
|
||||
}
|
||||
|
||||
Vec2 WindowToWorldUnits(Vec2 value, const Window &window) {
|
||||
Vector2 result = Vector2Add(value, window.rect_in_world_units.min);
|
||||
return result;
|
||||
}
|
||||
|
||||
Rect2 GetScreenRectRenderUnits() {
|
||||
Rect2 GetScreenRect() {
|
||||
Rect2 result = {
|
||||
{ 0, 0},
|
||||
{(float)GetRenderWidth(), (float)GetRenderHeight()}
|
||||
@@ -165,7 +126,7 @@ void BeforeEdit(Window *window) {
|
||||
}
|
||||
|
||||
void AfterEdit(Window *window, Array<Edit> edits) {
|
||||
// First offset all cursors by edits
|
||||
// Offset all cursors by edits
|
||||
ForItem(edit, edits) {
|
||||
int64_t remove_size = GetRangeSize(edit.range);
|
||||
int64_t insert_size = edit.string.len;
|
||||
@@ -188,7 +149,12 @@ void AfterEdit(Window *window, Array<Edit> edits) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure all cursors are in range
|
||||
For(window->cursors) it.range = Clamp(window->buffer, it.range);
|
||||
|
||||
Clear(&window->layout_arena);
|
||||
window->layout = CalculateLayout(&window->layout_arena, window->buffer, window->font, window->font_size, window->font_spacing);
|
||||
}
|
||||
|
||||
Arena FrameArena;
|
||||
@@ -210,26 +176,25 @@ int main() {
|
||||
|
||||
Array<Window> windows = {};
|
||||
{
|
||||
Window window = {};
|
||||
window.rect_in_world_units = GetScreenRectRenderUnits();
|
||||
window.rect_in_world_units.max.x *= 2;
|
||||
window.rect_in_world_units.max.y *= 2;
|
||||
|
||||
window.title_bar_pixel_size = 20;
|
||||
window.line_number_bar_pixel_size = 40;
|
||||
window.right_scroll_bar_pixel_size = 30;
|
||||
window.bottom_scroll_bar_pixel_size = 30;
|
||||
Window window = {};
|
||||
window.rect = GetScreenRect();
|
||||
window.font = font;
|
||||
window.font_size = font_size;
|
||||
window.font_spacing;
|
||||
InitArena(&window.layout_arena);
|
||||
|
||||
InitBuffer(&window.buffer);
|
||||
if (1) {
|
||||
for (int i = 0; i < 50; i += 1) {
|
||||
Array<Edit> edits = {FrameArena};
|
||||
AddEdit(&edits, GetEnd(window.buffer), Format(FrameArena, "line number sddddddddddddssssssssss faasda s: %d\n", i));
|
||||
AddEdit(&edits, GetEnd(window.buffer), Format(FrameArena, "line number: %d\n", i));
|
||||
ApplyEdits(&window.buffer, edits);
|
||||
}
|
||||
}
|
||||
|
||||
window.cursors.add({});
|
||||
AfterEdit(&window, {});
|
||||
|
||||
windows.add(window);
|
||||
}
|
||||
|
||||
@@ -487,139 +452,44 @@ int main() {
|
||||
ClearBackground(RAYWHITE);
|
||||
|
||||
ForItem(window, windows) {
|
||||
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),
|
||||
};
|
||||
Rectangle rectangle_in_render_units = ToRectangle(window_rect_in_render_units);
|
||||
Rectangle rectangle_in_render_units = ToRectangle(window.rect);
|
||||
DrawRectangleRec(rectangle_in_render_units, WHITE);
|
||||
|
||||
Rect2 window_text_rect_in_render_units = window_rect_in_render_units;
|
||||
Rect2 window_text_rect_in_render_units_clamped_to_screen = window_text_rect_in_render_units;
|
||||
Rect2 screen_rect_in_render_units = GetScreenRectRenderUnits();
|
||||
window_text_rect_in_render_units_clamped_to_screen.min.x = Clamp(window_text_rect_in_render_units_clamped_to_screen.min.x, screen_rect_in_render_units.min.x, screen_rect_in_render_units.max.x);
|
||||
window_text_rect_in_render_units_clamped_to_screen.max.x = Clamp(window_text_rect_in_render_units_clamped_to_screen.max.x, screen_rect_in_render_units.min.x, screen_rect_in_render_units.max.x);
|
||||
window_text_rect_in_render_units_clamped_to_screen.min.y = Clamp(window_text_rect_in_render_units_clamped_to_screen.min.y, screen_rect_in_render_units.min.y, screen_rect_in_render_units.max.y);
|
||||
window_text_rect_in_render_units_clamped_to_screen.max.y = Clamp(window_text_rect_in_render_units_clamped_to_screen.max.y, screen_rect_in_render_units.min.y, screen_rect_in_render_units.max.y);
|
||||
// Figure out which lines to draw
|
||||
Vec2 s = GetSize(window.rect);
|
||||
float line_offset = font_size;
|
||||
float _line_min_y = (window.scroll.y) / line_offset;
|
||||
float _line_max_y = (s.y + window.scroll.y) / line_offset;
|
||||
int64_t line_min_y = (int64_t)floorf(_line_min_y);
|
||||
int64_t line_max_y = (int64_t)ceilf(_line_max_y);
|
||||
int64_t visible_lines = line_max_y - line_min_y;
|
||||
|
||||
Vec2 window_buffer_to_window_units = {window.line_number_bar_pixel_size, window.title_bar_pixel_size};
|
||||
Rect2 title_bar_in_render_units = CutTop(&window_text_rect_in_render_units_clamped_to_screen, window.title_bar_pixel_size);
|
||||
Rect2 line_number_bar_in_render_units = CutLeft(&window_text_rect_in_render_units_clamped_to_screen, window.line_number_bar_pixel_size);
|
||||
Rect2 bottom_scroll_bar_in_render_units = CutBottom(&window_text_rect_in_render_units_clamped_to_screen, window.bottom_scroll_bar_pixel_size);
|
||||
Rect2 right_scroll_bar_in_render_units = CutRight(&window_text_rect_in_render_units_clamped_to_screen, window.right_scroll_bar_pixel_size);
|
||||
|
||||
DrawRectangleRec(ToRectangle(title_bar_in_render_units), GRAY);
|
||||
DrawRectangleRec(ToRectangle(line_number_bar_in_render_units), GRAY);
|
||||
DrawRectangleRec(ToRectangle(bottom_scroll_bar_in_render_units), GRAY);
|
||||
DrawRectangleRec(ToRectangle(right_scroll_bar_in_render_units), GRAY);
|
||||
DrawString(title_bar_font, "title bar :)", title_bar_in_render_units.min, title_bar_font_size, font_spacing, BLACK);
|
||||
|
||||
if (0) {
|
||||
window_text_rect_in_render_units_clamped_to_screen = Shrink(window_text_rect_in_render_units_clamped_to_screen, 10);
|
||||
DrawRectangleRec(ToRectangle(window_text_rect_in_render_units_clamped_to_screen), {255, 0, 0, 50});
|
||||
}
|
||||
|
||||
struct Cell {
|
||||
Rect2 rect;
|
||||
int codepoint;
|
||||
int64_t column;
|
||||
int64_t pos;
|
||||
};
|
||||
|
||||
struct CellRow {
|
||||
Array<Cell> cells;
|
||||
Rect2 rect;
|
||||
int64_t line;
|
||||
};
|
||||
|
||||
// Compute visible rows and cells
|
||||
Array<CellRow> rows = {FrameArena};
|
||||
{
|
||||
// Figure out which lines to draw
|
||||
Vec2 s = GetSize(window_text_rect_in_render_units);
|
||||
float line_offset = font_size;
|
||||
float _line_min_y = (window.scroll.y) / line_offset;
|
||||
float _line_max_y = (s.y + window.scroll.y) / line_offset;
|
||||
int64_t line_min_y = (int64_t)floorf(_line_min_y);
|
||||
int64_t line_max_y = (int64_t)ceilf(_line_max_y);
|
||||
|
||||
for (int64_t line = line_min_y; line < line_max_y; line += 1) {
|
||||
if (line < 0) break;
|
||||
if (line >= window.buffer.lines.len) break;
|
||||
Range line_range = window.buffer.lines[line];
|
||||
|
||||
Vec2 text_position_in_window_buffer_world_units = {0, line_offset * (float)line};
|
||||
Vec2 text_position_in_window_buffer_units = WindowBufferWorldToWindowBufferUnits(text_position_in_window_buffer_world_units, window);
|
||||
Vec2 text_position_in_window_units = WindowBufferToWindowUnits(text_position_in_window_buffer_units, window_buffer_to_window_units);
|
||||
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);
|
||||
|
||||
CellRow row = {};
|
||||
row.line = line;
|
||||
row.cells.allocator = FrameArena;
|
||||
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);
|
||||
if (font.texture.id == 0) font = GetFontDefault();
|
||||
float textOffsetX = 0.0f;
|
||||
float scaleFactor = font_size / font.baseSize; // Character quad scaling factor
|
||||
|
||||
for (BufferIter iter = Iterate(window.buffer, line_range);; Advance(&iter)) {
|
||||
bool end_of_buffer = iter.pos == window.buffer.len;
|
||||
bool new_line = iter.pos == line_range.max;
|
||||
bool in_range = IsValid(iter);
|
||||
bool continue_looping = end_of_buffer || new_line || in_range;
|
||||
if (!continue_looping) break;
|
||||
|
||||
int codepoint = '\n';
|
||||
if (in_range) codepoint = iter.item;
|
||||
|
||||
int index = GetGlyphIndex(font, codepoint);
|
||||
GlyphInfo *glyph = font.glyphs + index;
|
||||
Vec2 glyph_position = {text_position_in_render_units.x + textOffsetX, text_position_in_render_units.y};
|
||||
|
||||
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)};
|
||||
Rectangle cell_rectangle = ToRectangle(cell_rect);
|
||||
|
||||
// Clip everything that is outside the window and screen
|
||||
if (CheckCollisionRecs(cell_rectangle, ToRectangle(window_text_rect_in_render_units_clamped_to_screen))) {
|
||||
row.cells.add({cell_rect, codepoint, iter.codepoint_index, iter.pos});
|
||||
row.rect.max = cell_rect.max;
|
||||
}
|
||||
|
||||
textOffsetX += x_to_offset_by;
|
||||
if (end_of_buffer || new_line) break;
|
||||
Array<LayoutColumn> visible_columns = {FrameArena};
|
||||
for (int64_t line = line_min_y; line < line_max_y && line >= 0 && line < window.layout.rows.len; line += 1) {
|
||||
LayoutRow &row = window.layout.rows[line];
|
||||
For(row.columns) {
|
||||
if (CheckCollisionRecs(ToRectangle(it.rect), ToRectangle(window.rect))) {
|
||||
visible_columns.add(it);
|
||||
}
|
||||
|
||||
if (row.cells.len) rows.add(row);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the scroll based on first cursor
|
||||
if (!AreEqual(window.main_cursor_begin_frame, window.cursors[0])) {
|
||||
Vec2 rect_in_render_units = GetSize(window_text_rect_in_render_units_clamped_to_screen);
|
||||
float visible_cells_in_render_units = font_size * (float)rows.len;
|
||||
float cut_off_in_render_units = visible_cells_in_render_units - rect_in_render_units.y;
|
||||
Vec2 rect_size = GetSize(window.rect);
|
||||
float visible_cells_in_render_units = font_size * (float)visible_lines;
|
||||
float cut_off_in_render_units = visible_cells_in_render_units - rect_size.y;
|
||||
|
||||
Cursor cursor = window.cursors[0];
|
||||
int64_t front = GetFront(cursor);
|
||||
Line line = FindLine(window.buffer, front);
|
||||
|
||||
// Scroll Y
|
||||
int64_t min_line = rows[0].line;
|
||||
int64_t max_line = rows[rows.len - 1].line;
|
||||
if (line.number < min_line) {
|
||||
if (line.number < line_min_y) {
|
||||
window.scroll.y = line.number * font_size;
|
||||
} else if (line.number >= max_line) {
|
||||
int64_t diff = line.number - max_line;
|
||||
window.scroll.y = (min_line + diff) * font_size + cut_off_in_render_units;
|
||||
} else if (line.number >= line_max_y) {
|
||||
int64_t diff = line.number - line_max_y;
|
||||
window.scroll.y = (line_min_y + diff) * font_size + cut_off_in_render_units;
|
||||
}
|
||||
|
||||
// Scroll X
|
||||
@@ -631,7 +501,7 @@ int main() {
|
||||
|
||||
GlyphInfo info = GetGlyphInfo(font, ' ');
|
||||
float right_scroll_zone = (float)(info.image.width + info.advanceX) * 3;
|
||||
float window_buffer_world_right_edge = (rect_in_render_units.x + window.scroll.x) - right_scroll_zone;
|
||||
float window_buffer_world_right_edge = (rect_size.x + window.scroll.x) - right_scroll_zone;
|
||||
float window_buffer_world_left_edge = window.scroll.x;
|
||||
if (x_cursor_position_in_window_buffer_world_units >= window_buffer_world_right_edge) {
|
||||
float diff = x_cursor_position_in_window_buffer_world_units - window_buffer_world_right_edge;
|
||||
@@ -642,108 +512,73 @@ int main() {
|
||||
}
|
||||
}
|
||||
|
||||
// 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});
|
||||
}
|
||||
}
|
||||
|
||||
// Mouse in text area
|
||||
// Mouse selection @todo
|
||||
{
|
||||
// @todo: click twice to select word
|
||||
// @tood: scrolling when selecting (Y and X)
|
||||
// @todo: change cursors
|
||||
Vec2 mouse_in_render_units = GetMousePosition();
|
||||
if (CheckCollisionPointRec(mouse_in_render_units, ToRectangle(window_text_rect_in_render_units_clamped_to_screen))) {
|
||||
ForItem(row, rows) {
|
||||
ForItem(cell, row.cells) {
|
||||
if (CheckCollisionPointRec(mouse_in_render_units, ToRectangle(cell.rect))) {
|
||||
DrawRectangleRec(ToRectangle(cell.rect), {0, 0, 255, 40});
|
||||
if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) {
|
||||
if (IsKeyDown(KEY_LEFT_CONTROL)) {
|
||||
window.cursors.add({cell.pos, cell.pos});
|
||||
} else {
|
||||
window.cursors.clear();
|
||||
window.cursors.add({cell.pos, cell.pos});
|
||||
}
|
||||
} else if (IsMouseButtonDown(MOUSE_BUTTON_LEFT)) {
|
||||
Cursor *c = window.cursors.last();
|
||||
*c = ChangeBack(*c, cell.pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
Vec2 mouse = GetMousePosition();
|
||||
Vec2 mouse_lookup = Vector2Add(mouse, window.scroll);
|
||||
|
||||
Tuple<LayoutRow *, LayoutColumn *> rowcol = GetRowCol(window, mouse_lookup);
|
||||
if (rowcol.b) {
|
||||
Rect2 col_rect = rowcol.b->rect - window.scroll;
|
||||
Rectangle col_rectangle = ToRectangle(col_rect);
|
||||
if (CheckCollisionPointRec(mouse, col_rectangle)) {
|
||||
DrawRectangleRec(col_rectangle, {0, 0, 255, 40});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the glyphs
|
||||
Vec2 window_text_rect_in_render_units_clamped_to_screen_size = GetSize(window_text_rect_in_render_units_clamped_to_screen);
|
||||
BeginScissorMode((int)window_text_rect_in_render_units_clamped_to_screen.min.x, (int)window_text_rect_in_render_units_clamped_to_screen.min.y, (int)window_text_rect_in_render_units_clamped_to_screen_size.x, (int)window_text_rect_in_render_units_clamped_to_screen_size.y);
|
||||
ForItem(row, rows) {
|
||||
For(row.cells) {
|
||||
if (it.codepoint == '\n') {
|
||||
Vec2 mid = GetMid(it.rect);
|
||||
Vec2 window_rect_size = GetSize(window.rect);
|
||||
BeginScissorMode((int)window.rect.min.x, (int)window.rect.min.y, (int)window_rect_size.x, (int)window_rect_size.y);
|
||||
|
||||
for (int64_t line = line_min_y; line < line_max_y; line += 1) {
|
||||
if (line < 0 || line >= window.layout.rows.len) continue;
|
||||
LayoutRow &row = window.layout.rows[line];
|
||||
|
||||
ForItem(col, row.columns) {
|
||||
Vec2 p0 = Vector2Subtract(col.rect.min, window.scroll);
|
||||
Vec2 p1 = Vector2Subtract(col.rect.max, window.scroll);
|
||||
Rect2 rect = {p0, p1};
|
||||
|
||||
if (!CheckCollisionRecs(ToRectangle(rect), ToRectangle(window.rect))) {
|
||||
continue; // Clip everything that is outside the window and screen
|
||||
}
|
||||
|
||||
if (col.codepoint == '\n') {
|
||||
Vec2 mid = GetMid(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);
|
||||
} else if ((col.codepoint != ' ') && (col.codepoint != '\t')) {
|
||||
DrawTextCodepoint(font, col.codepoint, rect.min, font_size, BLACK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw cursor stuff
|
||||
// Draw cursor stuff @todo: draw selection
|
||||
ForItem(cursor, window.cursors) {
|
||||
Line min_line = FindLine(window.buffer, cursor.range.min);
|
||||
Line max_line = FindLine(window.buffer, cursor.range.max);
|
||||
bool selecting = cursor.range.min != cursor.range.max;
|
||||
ForItem(row, rows) {
|
||||
// Draw line highlight
|
||||
if (row.line == min_line.number) {
|
||||
DrawRectangleRec(ToRectangle(row.rect), {255, 0, 0, 30});
|
||||
}
|
||||
auto front = GetRowCol(window, GetFront(cursor));
|
||||
auto back = GetRowCol(window, GetBack(cursor));
|
||||
|
||||
ForItem(cell, row.cells) {
|
||||
// Draw selection
|
||||
if (selecting) {
|
||||
if (row.line >= min_line.number && row.line <= max_line.number) {
|
||||
if (cell.pos >= cursor.range.min && cell.pos < cursor.range.max) {
|
||||
DrawRectangleRec(ToRectangle(cell.rect), BLUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw cursors
|
||||
if (cell.pos == GetFront(cursor)) {
|
||||
Rect2 rect = cell.rect;
|
||||
rect = CutLeft(&rect, 4);
|
||||
DrawRectangleRec(ToRectangle(rect), RED);
|
||||
}
|
||||
if (cell.pos == GetBack(cursor)) {
|
||||
Rect2 rect = cell.rect;
|
||||
rect = CutLeft(&rect, 2);
|
||||
DrawRectangleRec(ToRectangle(rect), GREEN);
|
||||
}
|
||||
For(visible_columns) {
|
||||
if (it.pos >= cursor.range.min && it.pos < cursor.range.max) {
|
||||
DrawRectangleRec(ToRectangle(it.rect), {0, 50, 150, 50});
|
||||
}
|
||||
}
|
||||
|
||||
if (front.b) {
|
||||
Rect2 rect = front.b->rect;
|
||||
rect -= window.scroll;
|
||||
rect = CutLeft(&rect, 4);
|
||||
DrawRectangleRec(ToRectangle(rect), RED);
|
||||
}
|
||||
if (back.b) {
|
||||
Rect2 rect = back.b->rect;
|
||||
rect -= window.scroll;
|
||||
rect = CutLeft(&rect, 2);
|
||||
DrawRectangleRec(ToRectangle(rect), GREEN);
|
||||
}
|
||||
}
|
||||
|
||||
EndScissorMode();
|
||||
|
||||
// Draw the line numbers
|
||||
{
|
||||
Rectangle bar_rectangle = ToRectangle(line_number_bar_in_render_units);
|
||||
BeginScissorMode((int)bar_rectangle.x, (int)bar_rectangle.y, (int)bar_rectangle.width, (int)bar_rectangle.height);
|
||||
Vec2 text_position_in_render_units = line_number_bar_in_render_units.min;
|
||||
For(rows) {
|
||||
Assert(it.cells.len);
|
||||
text_position_in_render_units.y = it.cells[0].rect.min.y;
|
||||
String string_num = Format(FrameArena, "%lld", (long long)it.line);
|
||||
DrawString(font, string_num, text_position_in_render_units, font_size, font_spacing, BLACK);
|
||||
}
|
||||
EndScissorMode();
|
||||
}
|
||||
}
|
||||
|
||||
EndDrawing();
|
||||
|
||||
@@ -71,3 +71,24 @@ Rect2 CutTop(Rect2 *r, float value) {
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
Rect2 operator-(Rect2 r, float value) {
|
||||
r.min.x -= value;
|
||||
r.min.y -= value;
|
||||
r.max.x -= value;
|
||||
r.max.y -= value;
|
||||
return r;
|
||||
}
|
||||
|
||||
Rect2 operator-(Rect2 r, Vec2 value) {
|
||||
r.min.x -= value.x;
|
||||
r.min.y -= value.y;
|
||||
r.max.x -= value.x;
|
||||
r.max.y -= value.y;
|
||||
return r;
|
||||
}
|
||||
|
||||
Rect2 operator-=(Rect2 &r, Vec2 value) {
|
||||
r = r - value;
|
||||
return r;
|
||||
}
|
||||
Reference in New Issue
Block a user