430 lines
13 KiB
C++
430 lines
13 KiB
C++
WindowID AllocWindowID(Window *window) {
|
|
return {WindowIDs.id++, window};
|
|
}
|
|
|
|
Window *CreateWind() {
|
|
Allocator allocator = GetSystemAllocator();
|
|
Window *w = AllocType(allocator, Window);
|
|
w->font = &PrimaryFont;
|
|
w->visible = true;
|
|
w->primary = true;
|
|
w->draw_scrollbar = true;
|
|
w->draw_line_numbers = true;
|
|
w->draw_line_highlight = true;
|
|
w->jump_history = true;
|
|
w->id = AllocWindowID(w);
|
|
w->weight = 1.0;
|
|
Add(&Windows, w);
|
|
return w;
|
|
}
|
|
|
|
Window *GetWindow(WindowID id, Window *default_window = Windows[0]) {
|
|
For(Windows) {
|
|
if (it->id == id) return it;
|
|
}
|
|
return default_window;
|
|
}
|
|
|
|
void Close(WindowID id) {
|
|
Window *window = GetWindow(id);
|
|
if (window->id.id == 0) {
|
|
return;
|
|
}
|
|
window->close = true;
|
|
RunGCThisFrame = true;
|
|
}
|
|
|
|
Window *FindWindow(ViewID view_id, Window *default_window = NULL) {
|
|
For(Windows) {
|
|
if (it->active_view == view_id) {
|
|
return it;
|
|
}
|
|
}
|
|
return default_window;
|
|
}
|
|
|
|
Window *FindWindow(String buffer_name, Window *default_window = NULL) {
|
|
For(Windows) {
|
|
View *it_view = GetView(it->active_view);
|
|
Buffer *it_buffer = GetBuffer(it_view->active_buffer);
|
|
if (it_buffer->name == buffer_name) {
|
|
return it;
|
|
}
|
|
}
|
|
return default_window;
|
|
}
|
|
|
|
Window *FindWindow(BufferID buffer_id) {
|
|
For(Windows) {
|
|
View *view = GetView(it->active_view);
|
|
if (view->active_buffer == buffer_id) return it;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
Window *GetActiveWind() {
|
|
return GetWindow(ActiveWindowID);
|
|
}
|
|
|
|
bool IsDocumentSelectionValid() {
|
|
if (DocumentSelected.id == -1) return false;
|
|
return true;
|
|
}
|
|
|
|
bool IsScrollbarSelectionValid() {
|
|
if (ScrollbarSelected.id == -1) return false;
|
|
return true;
|
|
}
|
|
|
|
Array<Window *> GetWindowZOrder(Allocator allocator) {
|
|
Array<Window *> order = {allocator};
|
|
For(Windows) if (it->z == 2) Add(&order, it);
|
|
For(Windows) if (it->z == 1) Add(&order, it);
|
|
For(Windows) if (it->z == 0) Add(&order, it);
|
|
return order;
|
|
}
|
|
|
|
Int GetExpandingBarSize(Window *window) {
|
|
View *view = GetView(window->active_view);
|
|
Buffer *buffer = GetBuffer(view->active_buffer);
|
|
float result = (float)buffer->line_starts.len * window->font->line_spacing;
|
|
return (Int)result;
|
|
}
|
|
|
|
View *WindowOpenBufferView(Window *new_parent_window, String name) {
|
|
View *view = FindView(name);
|
|
if (!view) {
|
|
View *result = OpenBufferView(name);
|
|
new_parent_window->active_view = result->id;
|
|
return result;
|
|
}
|
|
|
|
Window *window = FindWindow(view->id);
|
|
if (!window) {
|
|
new_parent_window->active_view = view->id;
|
|
return view;
|
|
}
|
|
if (window == new_parent_window) {
|
|
return view;
|
|
}
|
|
|
|
Assert(window->active_view.id == view->id.id);
|
|
View *result = OpenBufferView(name);
|
|
new_parent_window->active_view = result->id;
|
|
return result;
|
|
}
|
|
|
|
void CalcNiceties(Window *n) {
|
|
float scrollbar_size = (10.f * DPIScale);
|
|
float line_numbers_size = (float)n->font->char_spacing * 10.f;
|
|
if (DrawScrollbar && n->draw_scrollbar) n->scrollbar_rect = CutRight(&n->document_rect, (Int)scrollbar_size);
|
|
if (DrawLineNumbers && n->draw_line_numbers) n->line_numbers_rect = CutLeft(&n->document_rect, (Int)line_numbers_size);
|
|
}
|
|
|
|
double WindowCalcEvenResizerValue(Int screen_size_x, Int *out_count = NULL) {
|
|
double w = 0;
|
|
Int c = 0;
|
|
ForItem(n, Windows) {
|
|
if (n->primary) {
|
|
w += n->weight;
|
|
c += 1;
|
|
}
|
|
}
|
|
if (out_count) *out_count = c;
|
|
return (double)screen_size_x / w;
|
|
}
|
|
|
|
Window *GetOverlappingWindow(Vec2I p, Window *default_window = NULL) {
|
|
For(Windows) {
|
|
if (AreOverlapping(p, it->total_rect)) {
|
|
return it;
|
|
}
|
|
}
|
|
return default_window;
|
|
}
|
|
|
|
Vec2I GetSideOfWindow(Window *window, int direction) {
|
|
Vec2I p = {};
|
|
Rect2I rect = window->total_rect;
|
|
float resizer_size = (float)window->font->char_spacing*0.5f; // @check_codebase_when_changing
|
|
if (direction == DIR_LEFT) {
|
|
p.x = rect.min.x - (Int)(resizer_size + window->font->char_spacing);
|
|
p.y = rect.min.y + (rect.max.y / 2);
|
|
} else if (direction == DIR_RIGHT) {
|
|
p.x = rect.max.x + (Int)(resizer_size + window->font->char_spacing);
|
|
p.y = rect.min.y + (rect.max.y / 2);
|
|
} else if (direction == DIR_UP) {
|
|
p.x = rect.min.x + (rect.max.x / 2);
|
|
p.y = rect.min.y - (Int)(resizer_size + window->font->line_spacing);
|
|
} else {
|
|
Assert(direction == DIR_DOWN);
|
|
p.x = rect.min.x + (rect.max.x / 2);
|
|
p.y = rect.max.y + (Int)(resizer_size + window->font->line_spacing);
|
|
}
|
|
return p;
|
|
}
|
|
|
|
Window *SwitchWindow(int direction) {
|
|
Window *window = GetWindow(ActiveWindowID);
|
|
Vec2I p = GetSideOfWindow(window, direction);
|
|
Window *result = GetOverlappingWindow(p, window);
|
|
return result;
|
|
}
|
|
|
|
Int ScreenSpaceToBufferPos(Window *window, View *view, Buffer *buffer, Vec2I mouse) {
|
|
Vec2I mworld = mouse - window->document_rect.min + view->scroll;
|
|
double px = (double)mworld.x / (double)window->font->char_spacing;
|
|
double py = (double)mworld.y / (double)window->font->line_spacing;
|
|
XY xy = {(Int)round(px), (Int)floor(py)};
|
|
Int result = XYToPosWithoutNL(buffer, xy);
|
|
return result;
|
|
}
|
|
|
|
Int ScreenSpaceToBufferPosErrorOutOfBounds(Window *window, View *view, Buffer *buffer, Vec2I mouse) {
|
|
Vec2I mworld = mouse - window->document_rect.min + view->scroll;
|
|
double px = (double)mworld.x / (double)window->font->char_spacing;
|
|
double py = (double)mworld.y / (double)window->font->line_spacing;
|
|
XY xy = {(Int)round(px), (Int)floor(py)};
|
|
Int result = XYToPosErrorOutOfBounds(buffer, xy);
|
|
return result;
|
|
}
|
|
|
|
GotoCrumb PopCrumb(Array<GotoCrumb> *cr) {
|
|
for (; cr->len;) {
|
|
GotoCrumb c = Pop(cr);
|
|
View *view = FindView(c.view_id, NULL);
|
|
if (view) {
|
|
return c;
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
View *GetLastValidView(Window *window) {
|
|
For (IterateInReverse(&window->goto_redo)) {
|
|
if (it.view_id == window->active_view) continue;
|
|
View *view = FindView(it.view_id, NULL);
|
|
if (view) return view;
|
|
}
|
|
For (IterateInReverse(&window->goto_history)) {
|
|
if (it.view_id == window->active_view) continue;
|
|
View *view = FindView(it.view_id, NULL);
|
|
if (view) return view;
|
|
}
|
|
return Views[0];
|
|
}
|
|
|
|
View *JumpToLastValidView(Window *window) {
|
|
View *view = GetLastValidView(window);
|
|
window->active_view = view->id;
|
|
return view;
|
|
}
|
|
|
|
void JumpBack(Window *window) {
|
|
if (window->jump_history == false) return;
|
|
if (window->goto_history.len <= 0) return;
|
|
BSet set = GetBSet(window);
|
|
Add(&window->goto_redo, {set.view->id, set.view->carets[0], GetTimeSeconds()});
|
|
|
|
GotoCrumb c = PopCrumb(&window->goto_history);
|
|
window->active_view = c.view_id;
|
|
View *view = GetView(c.view_id);
|
|
view->carets[0] = c.caret;
|
|
CenterView(window->id);
|
|
|
|
if (window->goto_history.len) {
|
|
GotoCrumb *next = GetLast(window->goto_history);
|
|
if (c.view_id == next->view_id && ((c.time - next->time) <= JumpHistoryMergeTime)) {
|
|
JumpBack(window);
|
|
}
|
|
}
|
|
}
|
|
|
|
void JumpForward(Window *window) {
|
|
if (window->jump_history == false) return;
|
|
if (window->goto_redo.len <= 0) return;
|
|
BSet set = GetBSet(window);
|
|
Add(&window->goto_history, {set.view->id, set.view->carets[0], GetTimeSeconds()});
|
|
|
|
GotoCrumb c = PopCrumb(&window->goto_redo);
|
|
window->active_view = c.view_id;
|
|
View *view = GetView(c.view_id);
|
|
view->carets[0] = c.caret;
|
|
CenterView(window->id);
|
|
|
|
if (window->goto_redo.len) {
|
|
GotoCrumb *next = GetLast(window->goto_redo);
|
|
if ((c.view_id == next->view_id) && ((next->time - c.time) <= JumpHistoryMergeTime)) {
|
|
JumpForward(window);
|
|
}
|
|
}
|
|
}
|
|
|
|
void GotoNextInList(Window *window, Int line_offset = 1) {
|
|
Assert(line_offset == 1 || line_offset == -1);
|
|
View *active_view = GetView(window->active_view);
|
|
|
|
View *view_goto = GetView(window->active_goto_list);
|
|
window->active_view = view_goto->id;
|
|
|
|
Buffer *buffer_goto = GetBuffer(view_goto->active_buffer);
|
|
int64_t pos = window->goto_list_pos;
|
|
Int line = PosToLine(buffer_goto, pos);
|
|
|
|
bool opened = false;
|
|
for (Int i = line + line_offset; i >= 0 && i < buffer_goto->line_starts.len; i += line_offset) {
|
|
Range line_range = GetLineRangeWithoutNL(buffer_goto, i);
|
|
String16 line = GetString(buffer_goto, line_range);
|
|
|
|
{
|
|
Int idx = 0;
|
|
String16 delim = u"||>";
|
|
if (Seek(line, delim, &idx, SeekFlag_None)) {
|
|
line = Skip(line, idx + delim.len);
|
|
}
|
|
}
|
|
|
|
view_goto->carets[0] = MakeCaret(line_range.min);
|
|
window->goto_list_pos = line_range.min;
|
|
line = Trim(line);
|
|
|
|
MergeCarets(buffer_goto, &view_goto->carets);
|
|
IF_DEBUG(AssertRanges(view_goto->carets));
|
|
if (line.len == 0) {
|
|
continue;
|
|
}
|
|
|
|
Buffer *active_view_buffer = GetBuffer(active_view->active_buffer);
|
|
Int p = active_view->carets[0].range.min;
|
|
Int active_view_line = PosToLine(active_view_buffer, p);
|
|
|
|
BSet set = Open(line, ResolveOpenMeta_DontError | ResolveOpenMeta_DontExec);
|
|
if (set.window == NULL) {
|
|
continue;
|
|
}
|
|
|
|
if (set.view == active_view) {
|
|
Int new_line = PosToLine(set.buffer, set.view->carets[0].range.min);
|
|
if (active_view_line == new_line) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
opened = true;
|
|
break;
|
|
}
|
|
|
|
if (!opened) window->active_view = active_view->id;
|
|
}
|
|
|
|
void UpdateScroll(Window *window, bool update_caret_scrolling) {
|
|
ProfileFunction();
|
|
BSet set = GetBSet(window);
|
|
|
|
// Scrolling with caret
|
|
if (update_caret_scrolling) {
|
|
Caret c = set.view->carets[0];
|
|
Int front = GetFront(c);
|
|
XY xy = PosToXY(set.buffer, front);
|
|
|
|
Rect2I visible = GetVisibleCells(window);
|
|
Vec2I visible_cells = GetSize(visible);
|
|
Vec2I visible_size = visible_cells * Vec2I{window->font->char_spacing, window->font->line_spacing};
|
|
Vec2I rect_size = GetSize(window->document_rect);
|
|
|
|
if (xy.line >= visible.max.y - 2) {
|
|
Int set_view_at_line = xy.line - (visible_cells.y - 1);
|
|
Int cut_off_y = Max((Int)0, visible_size.y - rect_size.y);
|
|
set.view->scroll.y = (set_view_at_line * window->font->line_spacing) + cut_off_y;
|
|
}
|
|
|
|
if (xy.line < visible.min.y + 1) {
|
|
set.view->scroll.y = xy.line * window->font->line_spacing;
|
|
}
|
|
|
|
if (xy.col >= visible.max.x - 1) {
|
|
Int set_view_at_line = xy.col - (visible_cells.x - 1);
|
|
Int cut_off_x = Max((Int)0, visible_size.x - rect_size.x);
|
|
set.view->scroll.x = (set_view_at_line * window->font->char_spacing) + cut_off_x;
|
|
}
|
|
|
|
if (xy.col <= visible.min.x) {
|
|
set.view->scroll.x = xy.col * window->font->char_spacing;
|
|
}
|
|
}
|
|
|
|
// Clip scroll
|
|
{
|
|
Int last_line = LastLine(set.buffer);
|
|
set.view->scroll.y = Clamp(set.view->scroll.y, (Int)0, Max((Int)0, (last_line - 1) * window->font->line_spacing));
|
|
|
|
// @note:
|
|
// GetCharCountOfLongestLine is a bottleneck, there is probably an algorithm for
|
|
// calculating this value incrementally but do we even need X scrollbar or x clipping?
|
|
set.view->scroll.x = ClampBottom(set.view->scroll.x, (Int)0);
|
|
}
|
|
}
|
|
|
|
void CenterView(WindowID window) {
|
|
BSet set = GetBSet(window);
|
|
Caret c = set.view->carets[0];
|
|
Int front = GetFront(c);
|
|
XY xy = PosToXY(set.buffer, front);
|
|
Vec2I size = GetSize(set.window->document_rect);
|
|
Int y = size.y / 2 / set.window->font->line_spacing;
|
|
set.view->scroll.x = 0;
|
|
if (xy.line > y) {
|
|
set.view->scroll.y = (xy.line) * set.window->font->line_spacing - (size.y / 2);
|
|
}
|
|
}
|
|
|
|
BSet GetBSet(Window *window) {
|
|
BSet set = {window};
|
|
set.view = GetView(set.window->active_view);
|
|
set.buffer = GetBuffer(set.view->active_buffer);
|
|
return set;
|
|
}
|
|
|
|
BSet GetBSet(WindowID window_id) {
|
|
Window *window = GetWindow(window_id);
|
|
BSet result = GetBSet(window);
|
|
return result;
|
|
}
|
|
|
|
String GetPrimaryDirectory() {
|
|
BSet main = GetBSet(PrimaryWindowID);
|
|
return GetDirectory(main.buffer);
|
|
}
|
|
|
|
String GetDirectory(Window *window) {
|
|
BSet main = GetBSet(PrimaryWindowID);
|
|
return GetDirectory(main.buffer);
|
|
}
|
|
|
|
void MoveCursorByPageSize(Window *window, int direction, bool shift = false) {
|
|
Assert(direction == DIR_UP || direction == DIR_DOWN);
|
|
BSet set = GetBSet(window);
|
|
|
|
Rect2I visible_cells_rect = GetVisibleCells(window);
|
|
Int y = GetSize(visible_cells_rect).y - 2;
|
|
if (direction == DIR_UP) y = -y;
|
|
|
|
For(set.view->carets) {
|
|
XY xy = PosToXY(set.buffer, GetFront(it));
|
|
if (direction == DIR_DOWN && xy.line == set.buffer->line_starts.len - 1) {
|
|
Range line_range = GetLineRange(set.buffer, xy.line);
|
|
xy.col = line_range.max - line_range.min;
|
|
} else if (direction == DIR_UP && xy.line == 0) {
|
|
xy.col = 0;
|
|
}
|
|
xy.line += y;
|
|
|
|
Int pos = XYToPos(set.buffer, xy);
|
|
if (shift) {
|
|
it = SetFront(it, pos);
|
|
} else {
|
|
it = MakeCaret(pos);
|
|
}
|
|
}
|
|
}
|