Files
imgui_transcript_browser/src/text_editor/main.cpp
2024-06-22 12:29:40 +02:00

208 lines
8.3 KiB
C++

#define BASIC_IMPL
#include "../basic/basic.h"
#include "raylib.h"
#include "raymath.h"
#include "buffer.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)
// WindowWorld units - positions offset by a position inside the buffer
struct Window {
Rect2 rect_in_world_units;
Vec2 window_world_to_window_units;
Array<Range> cursors;
Buffer buffer;
};
Vec2 WindowWorldToWindowUnits(Vec2 value, const Window &window) {
Vec2 result = Vector2Subtract(value, window.window_world_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 result = {
{ 0, 0},
{(float)GetRenderWidth(), (float)GetRenderHeight()}
};
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
float textOffsetX = 0.0f; // Offset X to next character to draw
float scaleFactor = fontSize / font.baseSize; // Character quad scaling factor
for (int i = 0; i < text.len;) {
// Get next codepoint from byte string and glyph index in font
int codepointByteCount = 0;
int codepoint = GetCodepointNext(&text.data[i], &codepointByteCount);
int index = GetGlyphIndex(font, codepoint);
if ((codepoint != ' ') && (codepoint != '\t')) {
DrawTextCodepoint(font, codepoint, {position.x + textOffsetX, position.y}, fontSize, tint);
}
if (font.glyphs[index].advanceX == 0) textOffsetX += ((float)font.recs[index].width * scaleFactor + spacing);
else textOffsetX += ((float)font.glyphs[index].advanceX * scaleFactor + spacing);
i += codepointByteCount; // Move text bytes counter to next codepoint
}
}
int main() {
InitScratch();
RunBufferTests();
InitWindow(800, 600, "Hello");
SetTargetFPS(60);
InitArena(&FrameArena);
float font_size = 25;
float font_spacing = 1;
Font font = LoadFontEx("C:/Windows/Fonts/times.ttf", (int)font_size, NULL, 250);
Array<Window> windows = {};
windows.add({GetScreenRectRenderUnits()});
{
Buffer *buffer = &windows[0].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);
}
}
Vec2 camera_offset_world_to_render_units = {};
while (!WindowShouldClose()) {
{
Window *focused_window = &windows[0];
if (IsMouseButtonDown(MOUSE_BUTTON_LEFT) || IsKeyDown(KEY_SPACE)) {
camera_offset_world_to_render_units = Vector2Subtract(camera_offset_world_to_render_units, GetMouseDelta());
}
float mouse_wheel = GetMouseWheelMove() * 48;
focused_window->window_world_to_window_units.y -= mouse_wheel;
focused_window->window_world_to_window_units.y = ClampBottom(focused_window->window_world_to_window_units.y, 0.f);
for (int c = GetCharPressed(); c; c = GetCharPressed()) {
String string = "?";
UTF8Result utf8 = UTF32ToUTF8((uint32_t)c);
if (utf8.error == 0) {
string = {(char *)utf8.out_str, (int64_t)utf8.len};
}
Array<Edit> edits = {FrameArena};
AddEdit(&edits, {}, string);
ApplyEdits(&focused_window->buffer, edits);
}
}
BeginDrawing();
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);
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
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;
float _line_max_y = (s.y + window.window_world_to_window_units.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);
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);
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_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);
//
// 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;) {
int codepointByteCount = 0;
int codepoint = GetCodepointNext(&text.data[i], &codepointByteCount);
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)};
DrawRectangleLinesEx(ToRectangle(cell_rect), 1, {255, 0, 0, 120});
if ((codepoint != ' ') && (codepoint != '\t')) {
DrawTextCodepoint(font, codepoint, glyph_position, font_size, BLACK);
}
textOffsetX += x_to_offset_by;
i += codepointByteCount; // Move text bytes counter to next codepoint
}
}
EndScissorMode();
}
EndDrawing();
}
}