Adding scrollbars and titlebars, laying it out
This commit is contained in:
@@ -11,18 +11,30 @@ Arena FrameArena;
|
||||
// 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
|
||||
// WindowBuffer units
|
||||
// WindowBufferWorld units
|
||||
|
||||
const uint64_t WindowFlag_DrawTitleBar = 1 << 1;
|
||||
const uint64_t WindowFlag_DrawLineNumbers = 1 << 2;
|
||||
const uint64_t WindowFlag_DrawRightScrollBar = 1 << 3;
|
||||
const uint64_t WindowFlag_DrawBottomScrollBar = 1 << 4;
|
||||
|
||||
struct Window {
|
||||
Rect2 rect_in_world_units;
|
||||
Vec2 window_world_to_window_units;
|
||||
uint64_t flags;
|
||||
Rect2 rect_in_world_units;
|
||||
Vec2 scroll; // 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);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -108,21 +120,24 @@ int main() {
|
||||
if (0) font = LoadFontEx("C:/Windows/Fonts/consola.ttf", (int)font_size, NULL, 250);
|
||||
|
||||
Array<Window> windows = {};
|
||||
windows.add({GetScreenRectRenderUnits()});
|
||||
{
|
||||
Window *window = &windows[0];
|
||||
Buffer *buffer = &window->buffer;
|
||||
InitBuffer(buffer);
|
||||
Window window = {};
|
||||
window.flags = WindowFlag_DrawTitleBar | WindowFlag_DrawLineNumbers;
|
||||
window.rect_in_world_units = GetScreenRectRenderUnits();
|
||||
window.rect_in_world_units.max.x *= 2;
|
||||
window.rect_in_world_units.max.y *= 2;
|
||||
InitBuffer(&window.buffer);
|
||||
if (1) {
|
||||
for (int i = 0; i < 5; i += 1) {
|
||||
Array<Edit> edits = {FrameArena};
|
||||
AddEdit(&edits, GetEnd(*buffer), Format(FrameArena, "line number: %d\n", i));
|
||||
ApplyEdits(buffer, edits);
|
||||
AddEdit(&edits, GetEnd(window.buffer), Format(FrameArena, "line number: %d\n", i));
|
||||
ApplyEdits(&window.buffer, edits);
|
||||
}
|
||||
}
|
||||
|
||||
window->cursors.add({});
|
||||
window->cursors.add(GetLine(*buffer, 1).range);
|
||||
window.cursors.add({});
|
||||
window.cursors.add(GetLine(window.buffer, 1).range);
|
||||
windows.add(window);
|
||||
}
|
||||
|
||||
Vec2 camera_offset_world_to_render_units = {};
|
||||
@@ -135,8 +150,8 @@ int main() {
|
||||
}
|
||||
|
||||
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);
|
||||
focused_window->scroll.y -= mouse_wheel;
|
||||
focused_window->scroll.y = ClampBottom(focused_window->scroll.y, 0.f);
|
||||
|
||||
if (IsKeyPressed(KEY_LEFT) || IsKeyPressedRepeat(KEY_LEFT)) {
|
||||
For(focused_window->cursors) {
|
||||
@@ -208,24 +223,33 @@ int main() {
|
||||
Rectangle rectangle_in_render_units = ToRectangle(window_rect_in_render_units);
|
||||
DrawRectangleRec(rectangle_in_render_units, WHITE);
|
||||
|
||||
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});
|
||||
}
|
||||
Rect2 window_text_rect_in_render_units = window_rect_in_render_units;
|
||||
|
||||
// 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;
|
||||
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);
|
||||
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);
|
||||
|
||||
float title_bar_size = 20;
|
||||
float line_number_bar_size = 40;
|
||||
float scroll_bar_size = 30;
|
||||
Vec2 window_buffer_to_window_units = {line_number_bar_size, title_bar_size};
|
||||
Rect2 title_bar_in_render_units = CutTop(&window_text_rect_in_render_units_clamped_to_screen, title_bar_size);
|
||||
Rect2 line_number_bar_in_render_units = CutLeft(&window_text_rect_in_render_units_clamped_to_screen, line_number_bar_size);
|
||||
Rect2 bottom_scroll_bar_in_render_units = CutBottom(&window_text_rect_in_render_units_clamped_to_screen, scroll_bar_size);
|
||||
Rect2 right_scroll_bar_in_render_units = CutRight(&window_text_rect_in_render_units_clamped_to_screen, scroll_bar_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);
|
||||
|
||||
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;
|
||||
@@ -241,61 +265,72 @@ int main() {
|
||||
|
||||
// Compute visible rows and cells
|
||||
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;
|
||||
Range line_range = window.buffer.lines[line];
|
||||
{
|
||||
// Figure out which lines to draw
|
||||
Vec2 s = GetSize(window_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);
|
||||
|
||||
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);
|
||||
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];
|
||||
|
||||
CellRow row = {};
|
||||
row.line = line;
|
||||
row.cells.allocator = FrameArena;
|
||||
row.rect.min = text_position_in_render_units;
|
||||
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);
|
||||
|
||||
// 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
|
||||
CellRow row = {};
|
||||
row.line = line;
|
||||
row.cells.allocator = FrameArena;
|
||||
row.rect.min = text_position_in_render_units;
|
||||
|
||||
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;
|
||||
// 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
|
||||
|
||||
int codepoint = '\n';
|
||||
if (in_range) codepoint = iter.item;
|
||||
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 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};
|
||||
int codepoint = '\n';
|
||||
if (in_range) codepoint = iter.item;
|
||||
|
||||
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);
|
||||
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};
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
// Clip everything that is outside the window and screen
|
||||
if (CheckCollisionRecs(cell_rectangle, ToRectangle(window_rect_in_render_units_clamped_to_screen))) {
|
||||
row.cells.add({cell_rect, codepoint, iter.pos});
|
||||
row.rect.max = cell_rect.max;
|
||||
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.pos});
|
||||
row.rect.max = cell_rect.max;
|
||||
}
|
||||
|
||||
textOffsetX += x_to_offset_by;
|
||||
if (end_of_buffer || new_line) break;
|
||||
}
|
||||
|
||||
textOffsetX += x_to_offset_by;
|
||||
if (end_of_buffer || new_line) break;
|
||||
if (row.cells.len) rows.add(row);
|
||||
}
|
||||
|
||||
if (row.cells.len) rows.add(row);
|
||||
}
|
||||
|
||||
// Draw debug markers
|
||||
@@ -309,8 +344,8 @@ int main() {
|
||||
}
|
||||
|
||||
// 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);
|
||||
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') {
|
||||
|
||||
Reference in New Issue
Block a user