Selecting text using mouse and new abstraction for cursors
This commit is contained in:
@@ -76,6 +76,13 @@ Range GetEnd(const Buffer &buffer) {
|
|||||||
return range;
|
return range;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Range MakeRange(int64_t a, int64_t b) {
|
||||||
|
Range result = {};
|
||||||
|
result.min = Min(a, b);
|
||||||
|
result.max = Max(a, b);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void AddEdit(Array<Edit> *edits, Range range, String string) {
|
void AddEdit(Array<Edit> *edits, Range range, String string) {
|
||||||
edits->add({range, string});
|
edits->add({range, string});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,14 +6,57 @@
|
|||||||
#include "buffer.cpp"
|
#include "buffer.cpp"
|
||||||
#include "rect2.cpp"
|
#include "rect2.cpp"
|
||||||
|
|
||||||
Arena FrameArena;
|
|
||||||
|
|
||||||
// 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)
|
||||||
// WindowBuffer units
|
// WindowBuffer units
|
||||||
// WindowBufferWorld units
|
// WindowBufferWorld units
|
||||||
|
|
||||||
|
struct Cursor {
|
||||||
|
union {
|
||||||
|
Range range;
|
||||||
|
int64_t pos[2];
|
||||||
|
};
|
||||||
|
int64_t ifront;
|
||||||
|
};
|
||||||
|
|
||||||
|
int64_t GetFront(Cursor cursor) {
|
||||||
|
int64_t result = cursor.pos[cursor.ifront];
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t GetBack(Cursor cursor) {
|
||||||
|
int64_t index = (cursor.ifront + 1) % 2;
|
||||||
|
int64_t result = cursor.pos[index];
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Cursor MakeCursor(int64_t front, int64_t back) {
|
||||||
|
Cursor result = {};
|
||||||
|
if (front >= back) {
|
||||||
|
result.range.min = back;
|
||||||
|
result.range.max = front;
|
||||||
|
result.ifront = 1;
|
||||||
|
} else {
|
||||||
|
result.range.min = front;
|
||||||
|
result.range.max = back;
|
||||||
|
result.ifront = 0;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Cursor ChangeBack(Cursor cursor, int64_t back) {
|
||||||
|
int64_t front = GetFront(cursor);
|
||||||
|
Cursor result = MakeCursor(front, back);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Cursor ChangeFront(Cursor cursor, int64_t front) {
|
||||||
|
int64_t back = GetBack(cursor);
|
||||||
|
Cursor result = MakeCursor(front, back);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
struct Window {
|
struct Window {
|
||||||
uint64_t flags;
|
uint64_t flags;
|
||||||
Rect2 rect_in_world_units;
|
Rect2 rect_in_world_units;
|
||||||
@@ -23,7 +66,7 @@ struct Window {
|
|||||||
float right_scroll_bar_pixel_size;
|
float right_scroll_bar_pixel_size;
|
||||||
float bottom_scroll_bar_pixel_size;
|
float bottom_scroll_bar_pixel_size;
|
||||||
|
|
||||||
Array<Range> cursors;
|
Array<Cursor> cursors;
|
||||||
Buffer buffer;
|
Buffer buffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -112,14 +155,16 @@ void UpdateCursorsAfterEdit(Window *window, Array<Edit> edits) {
|
|||||||
int64_t offset = insert_size - remove_size;
|
int64_t offset = insert_size - remove_size;
|
||||||
|
|
||||||
ForItem(cursor, window->cursors) {
|
ForItem(cursor, window->cursors) {
|
||||||
if (edit.range.min <= cursor.min) {
|
if (edit.range.min <= cursor.range.min) {
|
||||||
cursor.min += offset;
|
cursor.range.min += offset;
|
||||||
cursor.max = cursor.min;
|
cursor.range.max = cursor.range.min;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
For(window->cursors) it.range = Clamp(window->buffer, it.range);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Arena FrameArena;
|
||||||
int main() {
|
int main() {
|
||||||
InitScratch();
|
InitScratch();
|
||||||
RunBufferTests();
|
RunBufferTests();
|
||||||
@@ -179,12 +224,12 @@ int main() {
|
|||||||
if (IsKeyPressed(KEY_LEFT) || IsKeyPressedRepeat(KEY_LEFT)) {
|
if (IsKeyPressed(KEY_LEFT) || IsKeyPressedRepeat(KEY_LEFT)) {
|
||||||
For(focused_window->cursors) {
|
For(focused_window->cursors) {
|
||||||
if (IsKeyDown(KEY_LEFT_CONTROL)) {
|
if (IsKeyDown(KEY_LEFT_CONTROL)) {
|
||||||
it.max = it.min = Seek(focused_window->buffer, it.min, ITERATE_BACKWARD);
|
it.range.max = it.range.min = Seek(focused_window->buffer, it.range.min, ITERATE_BACKWARD);
|
||||||
} else {
|
} else {
|
||||||
if (IsKeyDown(KEY_LEFT_SHIFT)) {
|
if (IsKeyDown(KEY_LEFT_SHIFT)) {
|
||||||
it.min = MoveLeft(focused_window->buffer, it.min);
|
it.range.min = MoveLeft(focused_window->buffer, it.range.min);
|
||||||
} else {
|
} else {
|
||||||
it.max = it.min = MoveLeft(focused_window->buffer, it.min);
|
it.range.max = it.range.min = MoveLeft(focused_window->buffer, it.range.min);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -192,38 +237,38 @@ int main() {
|
|||||||
if (IsKeyPressed(KEY_RIGHT) || IsKeyPressedRepeat(KEY_RIGHT)) {
|
if (IsKeyPressed(KEY_RIGHT) || IsKeyPressedRepeat(KEY_RIGHT)) {
|
||||||
For(focused_window->cursors) {
|
For(focused_window->cursors) {
|
||||||
if (IsKeyDown(KEY_LEFT_CONTROL)) {
|
if (IsKeyDown(KEY_LEFT_CONTROL)) {
|
||||||
it.max = it.min = Seek(focused_window->buffer, it.min, ITERATE_FORWARD);
|
it.range.max = it.range.min = Seek(focused_window->buffer, it.range.min, ITERATE_FORWARD);
|
||||||
} else {
|
} else {
|
||||||
if (IsKeyDown(KEY_LEFT_SHIFT)) {
|
if (IsKeyDown(KEY_LEFT_SHIFT)) {
|
||||||
it.max = MoveRight(focused_window->buffer, it.max);
|
it.range.max = MoveRight(focused_window->buffer, it.range.max);
|
||||||
} else {
|
} else {
|
||||||
it.max = it.min = MoveRight(focused_window->buffer, it.min);
|
it.range.max = it.range.min = MoveRight(focused_window->buffer, it.range.min);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (IsKeyPressed(KEY_DOWN) || IsKeyPressedRepeat(KEY_DOWN)) {
|
if (IsKeyPressed(KEY_DOWN) || IsKeyPressedRepeat(KEY_DOWN)) {
|
||||||
For(focused_window->cursors) {
|
For(focused_window->cursors) {
|
||||||
it.max = it.min = MoveDown(focused_window->buffer, it.min);
|
it.range.max = it.range.min = MoveDown(focused_window->buffer, it.range.min);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (IsKeyPressed(KEY_UP) || IsKeyPressedRepeat(KEY_UP)) {
|
if (IsKeyPressed(KEY_UP) || IsKeyPressedRepeat(KEY_UP)) {
|
||||||
For(focused_window->cursors) {
|
For(focused_window->cursors) {
|
||||||
it.max = it.min = MoveUp(focused_window->buffer, it.min);
|
it.range.max = it.range.min = MoveUp(focused_window->buffer, it.range.min);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge cursors that overlap, this needs to be handled before any edits to
|
// Merge cursors that overlap, this needs to be handled before any edits to
|
||||||
// make sure overlapping edits won't happen.
|
// make sure overlapping edits won't happen.
|
||||||
for (int64_t cursor_i = 0; cursor_i < focused_window->cursors.len; cursor_i += 1) {
|
for (int64_t cursor_i = 0; cursor_i < focused_window->cursors.len; cursor_i += 1) {
|
||||||
Range &cursor = focused_window->cursors[cursor_i];
|
Cursor &cursor = focused_window->cursors[cursor_i];
|
||||||
IterRemove(focused_window->cursors) {
|
IterRemove(focused_window->cursors) {
|
||||||
IterRemovePrepare(focused_window->cursors);
|
IterRemovePrepare(focused_window->cursors);
|
||||||
if (&cursor == &it) continue;
|
if (&cursor == &it) continue;
|
||||||
|
|
||||||
if (cursor.max > it.min && cursor.max <= it.max) {
|
if (cursor.range.max > it.range.min && cursor.range.max <= it.range.max) {
|
||||||
remove_item = true;
|
remove_item = true;
|
||||||
cursor.max = it.max;
|
cursor.range.max = it.range.max;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -233,8 +278,8 @@ int main() {
|
|||||||
Array<Edit> edits = {FrameArena};
|
Array<Edit> edits = {FrameArena};
|
||||||
String string = {};
|
String string = {};
|
||||||
For(focused_window->cursors) {
|
For(focused_window->cursors) {
|
||||||
int64_t pos = MoveLeft(focused_window->buffer, it.min);
|
int64_t pos = MoveLeft(focused_window->buffer, it.range.min);
|
||||||
AddEdit(&edits, {pos, it.min}, string);
|
AddEdit(&edits, {pos, it.range.min}, string);
|
||||||
}
|
}
|
||||||
ApplyEdits(&focused_window->buffer, edits);
|
ApplyEdits(&focused_window->buffer, edits);
|
||||||
UpdateCursorsAfterEdit(focused_window, edits);
|
UpdateCursorsAfterEdit(focused_window, edits);
|
||||||
@@ -253,7 +298,7 @@ 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.range, string);
|
||||||
}
|
}
|
||||||
ApplyEdits(&focused_window->buffer, edits);
|
ApplyEdits(&focused_window->buffer, edits);
|
||||||
UpdateCursorsAfterEdit(focused_window, edits);
|
UpdateCursorsAfterEdit(focused_window, edits);
|
||||||
@@ -389,6 +434,27 @@ int main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mouse in text area
|
||||||
|
{
|
||||||
|
// @todo: test for focus
|
||||||
|
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)) {
|
||||||
|
window.cursors.clear();
|
||||||
|
window.cursors.add({cell.pos, cell.pos});
|
||||||
|
} else if (IsMouseButtonDown(MOUSE_BUTTON_LEFT)) {
|
||||||
|
window.cursors[0] = ChangeBack(window.cursors[0], cell.pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Draw the glyphs
|
// Draw the glyphs
|
||||||
Vec2 window_text_rect_in_render_units_clamped_to_screen_size = GetSize(window_text_rect_in_render_units_clamped_to_screen);
|
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);
|
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);
|
||||||
@@ -405,35 +471,32 @@ int main() {
|
|||||||
|
|
||||||
// Draw cursor stuff
|
// Draw cursor stuff
|
||||||
ForItem(cursor, window.cursors) {
|
ForItem(cursor, window.cursors) {
|
||||||
Line min_line = FindLine(window.buffer, cursor.min);
|
Line min_line = FindLine(window.buffer, cursor.range.min);
|
||||||
Line max_line = FindLine(window.buffer, cursor.max);
|
Line max_line = FindLine(window.buffer, cursor.range.max);
|
||||||
bool selecting = cursor.min != cursor.max;
|
bool selecting = cursor.range.min != cursor.range.max;
|
||||||
ForItem(row, rows) {
|
ForItem(row, rows) {
|
||||||
// Draw line highlights
|
// Draw line highlight
|
||||||
if (row.line == min_line.number) {
|
if (row.line == min_line.number) {
|
||||||
DrawRectangleRec(ToRectangle(row.rect), {255, 0, 0, 30});
|
DrawRectangleRec(ToRectangle(row.rect), {255, 0, 0, 30});
|
||||||
}
|
}
|
||||||
// if (row.line == max_line.number) {
|
|
||||||
// DrawRectangleRec(ToRectangle(row.rect), {0, 255, 0, 30});
|
|
||||||
// }
|
|
||||||
|
|
||||||
ForItem(cell, row.cells) {
|
ForItem(cell, row.cells) {
|
||||||
// Draw selection
|
// Draw selection
|
||||||
if (selecting) {
|
if (selecting) {
|
||||||
if (row.line >= min_line.number && row.line <= max_line.number) {
|
if (row.line >= min_line.number && row.line <= max_line.number) {
|
||||||
if (cell.pos >= cursor.min && cell.pos < cursor.max) {
|
if (cell.pos >= cursor.range.min && cell.pos < cursor.range.max) {
|
||||||
DrawRectangleRec(ToRectangle(cell.rect), BLUE);
|
DrawRectangleRec(ToRectangle(cell.rect), BLUE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw cursors
|
// Draw cursors
|
||||||
if (cell.pos == cursor.min) {
|
if (cell.pos == GetFront(cursor)) {
|
||||||
Rect2 rect = cell.rect;
|
Rect2 rect = cell.rect;
|
||||||
rect = CutLeft(&rect, 4);
|
rect = CutLeft(&rect, 4);
|
||||||
DrawRectangleRec(ToRectangle(rect), RED);
|
DrawRectangleRec(ToRectangle(rect), RED);
|
||||||
}
|
}
|
||||||
if (cell.pos == cursor.max) {
|
if (cell.pos == GetBack(cursor)) {
|
||||||
Rect2 rect = cell.rect;
|
Rect2 rect = cell.rect;
|
||||||
rect = CutLeft(&rect, 2);
|
rect = CutLeft(&rect, 2);
|
||||||
DrawRectangleRec(ToRectangle(rect), GREEN);
|
DrawRectangleRec(ToRectangle(rect), GREEN);
|
||||||
|
|||||||
Reference in New Issue
Block a user