feat: Intel GPU fixes, add tcc compiler, spreadsheet improvements, paint app, file manager improvements
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -11,14 +11,15 @@
|
||||
// Text Editor state
|
||||
// ============================================================================
|
||||
|
||||
static constexpr int TE_TOOLBAR_H = 36;
|
||||
static constexpr int TE_PATHBAR_H = 32;
|
||||
static constexpr int TE_STATUS_H = 24;
|
||||
static constexpr int TE_LINE_NUM_W = 48;
|
||||
static constexpr int TE_INIT_CAP = 4096;
|
||||
static constexpr int TE_MAX_CAP = 262144; // 256KB
|
||||
static constexpr int TE_MAX_LINES = 16384;
|
||||
static constexpr int TE_TAB_WIDTH = 4;
|
||||
static constexpr int TE_TOOLBAR_H = 36;
|
||||
static constexpr int TE_PATHBAR_H = 32;
|
||||
static constexpr int TE_STATUS_H = 24;
|
||||
static constexpr int TE_LINE_NUM_W = 48;
|
||||
static constexpr int TE_SCROLLBAR_W = 12;
|
||||
static constexpr int TE_INIT_CAP = 4096;
|
||||
static constexpr int TE_MAX_CAP = 262144; // 256KB
|
||||
static constexpr int TE_MAX_LINES = 16384;
|
||||
static constexpr int TE_TAB_WIDTH = 4;
|
||||
|
||||
struct TextEditorState {
|
||||
char* buffer;
|
||||
@@ -32,10 +33,20 @@ struct TextEditorState {
|
||||
int scroll_y; // first visible line
|
||||
int scroll_x; // horizontal scroll in pixels
|
||||
bool modified;
|
||||
bool cursor_moved; // set when cursor moves, triggers scroll-to-cursor
|
||||
char filepath[256];
|
||||
char filename[64];
|
||||
DesktopState* desktop;
|
||||
|
||||
// Selection
|
||||
int sel_anchor; // byte position where selection started
|
||||
int sel_end; // byte position where selection extends to
|
||||
bool has_selection;
|
||||
bool mouse_selecting;
|
||||
|
||||
// Scrollbar
|
||||
Scrollbar scrollbar;
|
||||
|
||||
bool show_pathbar;
|
||||
char pathbar_text[256];
|
||||
int pathbar_cursor;
|
||||
@@ -71,6 +82,7 @@ static void te_update_cursor_pos(TextEditorState* te) {
|
||||
}
|
||||
}
|
||||
te->cursor_col = te->cursor_pos - te->line_offsets[te->cursor_line];
|
||||
te->cursor_moved = true;
|
||||
}
|
||||
|
||||
static int te_line_length(TextEditorState* te, int line) {
|
||||
@@ -206,11 +218,65 @@ static void te_move_end(TextEditorState* te) {
|
||||
te_update_cursor_pos(te);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Selection
|
||||
// ============================================================================
|
||||
|
||||
static void te_clear_selection(TextEditorState* te) {
|
||||
te->has_selection = false;
|
||||
te->sel_anchor = 0;
|
||||
te->sel_end = 0;
|
||||
}
|
||||
|
||||
static void te_sel_range(TextEditorState* te, int* out_start, int* out_end) {
|
||||
if (te->sel_anchor < te->sel_end) {
|
||||
*out_start = te->sel_anchor;
|
||||
*out_end = te->sel_end;
|
||||
} else {
|
||||
*out_start = te->sel_end;
|
||||
*out_end = te->sel_anchor;
|
||||
}
|
||||
}
|
||||
|
||||
static void te_start_selection(TextEditorState* te) {
|
||||
if (!te->has_selection) {
|
||||
te->sel_anchor = te->cursor_pos;
|
||||
te->sel_end = te->cursor_pos;
|
||||
te->has_selection = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void te_update_selection(TextEditorState* te) {
|
||||
te->sel_end = te->cursor_pos;
|
||||
if (te->sel_anchor == te->sel_end)
|
||||
te->has_selection = false;
|
||||
}
|
||||
|
||||
static void te_delete_selection(TextEditorState* te) {
|
||||
if (!te->has_selection) return;
|
||||
|
||||
int sel_s, sel_e;
|
||||
te_sel_range(te, &sel_s, &sel_e);
|
||||
int count = sel_e - sel_s;
|
||||
if (count <= 0) { te_clear_selection(te); return; }
|
||||
|
||||
// Remove bytes [sel_s, sel_e) from buffer
|
||||
for (int i = sel_s; i < te->buf_len - count; i++)
|
||||
te->buffer[i] = te->buffer[i + count];
|
||||
te->buf_len -= count;
|
||||
|
||||
te->cursor_pos = sel_s;
|
||||
te->modified = true;
|
||||
te_clear_selection(te);
|
||||
te_recompute_lines(te);
|
||||
te_update_cursor_pos(te);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Scrolling
|
||||
// ============================================================================
|
||||
|
||||
static void te_ensure_cursor_visible(TextEditorState* te, int visible_lines, int content_w) {
|
||||
static void te_ensure_cursor_visible(TextEditorState* te, int visible_lines, int text_area_w) {
|
||||
if (te->cursor_line < te->scroll_y) {
|
||||
te->scroll_y = te->cursor_line;
|
||||
}
|
||||
@@ -218,10 +284,16 @@ static void te_ensure_cursor_visible(TextEditorState* te, int visible_lines, int
|
||||
te->scroll_y = te->cursor_line - visible_lines + 1;
|
||||
}
|
||||
|
||||
// Clamp scroll_y
|
||||
int max_scroll_y = te->line_count - visible_lines;
|
||||
if (max_scroll_y < 0) max_scroll_y = 0;
|
||||
if (te->scroll_y > max_scroll_y) te->scroll_y = max_scroll_y;
|
||||
if (te->scroll_y < 0) te->scroll_y = 0;
|
||||
|
||||
// Horizontal scroll
|
||||
int cell_w = mono_cell_width();
|
||||
int cursor_px = te->cursor_col * cell_w;
|
||||
int view_w = content_w - TE_LINE_NUM_W;
|
||||
int view_w = text_area_w - TE_LINE_NUM_W - TE_SCROLLBAR_W;
|
||||
if (cursor_px - te->scroll_x > view_w - cell_w * 2) {
|
||||
te->scroll_x = cursor_px - view_w + cell_w * 4;
|
||||
}
|
||||
@@ -375,8 +447,24 @@ static void texteditor_on_draw(Window* win, Framebuffer& fb) {
|
||||
int text_area_h = c.h - editor_y_start - TE_STATUS_H;
|
||||
int visible_lines = text_area_h / cell_h;
|
||||
if (visible_lines < 1) visible_lines = 1;
|
||||
int text_area_w = c.w - TE_SCROLLBAR_W;
|
||||
|
||||
te_ensure_cursor_visible(te, visible_lines, c.w);
|
||||
// Only scroll-to-cursor when the cursor actually moved
|
||||
if (te->cursor_moved) {
|
||||
te_ensure_cursor_visible(te, visible_lines, c.w);
|
||||
te->cursor_moved = false;
|
||||
}
|
||||
|
||||
// Update scrollbar
|
||||
te->scrollbar.bounds = {c.w - TE_SCROLLBAR_W, editor_y_start, TE_SCROLLBAR_W, text_area_h};
|
||||
te->scrollbar.content_height = te->line_count * cell_h;
|
||||
te->scrollbar.view_height = text_area_h;
|
||||
te->scrollbar.scroll_offset = te->scroll_y * cell_h;
|
||||
{
|
||||
int ms = te->scrollbar.max_scroll();
|
||||
if (te->scrollbar.scroll_offset > ms) te->scrollbar.scroll_offset = ms;
|
||||
if (te->scrollbar.scroll_offset < 0) te->scrollbar.scroll_offset = 0;
|
||||
}
|
||||
|
||||
// Line number gutter background
|
||||
c.fill_rect(0, editor_y_start, TE_LINE_NUM_W, text_area_h, Color::from_rgb(0xF0, 0xF0, 0xF0));
|
||||
@@ -384,6 +472,11 @@ static void texteditor_on_draw(Window* win, Framebuffer& fb) {
|
||||
// Gutter separator
|
||||
c.vline(TE_LINE_NUM_W, editor_y_start, text_area_h, colors::BORDER);
|
||||
|
||||
// Selection range
|
||||
int sel_s = 0, sel_e = 0;
|
||||
if (te->has_selection) te_sel_range(te, &sel_s, &sel_e);
|
||||
Color sel_bg = Color::from_rgb(0xB0, 0xD0, 0xF0);
|
||||
|
||||
// Draw lines
|
||||
Color linenum_color = Color::from_rgb(0x99, 0x99, 0x99);
|
||||
Color cursor_line_color = Color::from_rgb(0xFF, 0xFD, 0xE8);
|
||||
@@ -398,11 +491,11 @@ static void texteditor_on_draw(Window* win, Framebuffer& fb) {
|
||||
int py = editor_y_start + vis * cell_h;
|
||||
if (py >= editor_y_start + text_area_h) break;
|
||||
|
||||
// Cursor line highlighting
|
||||
if (line == te->cursor_line) {
|
||||
// Cursor line highlighting (only when no selection)
|
||||
if (line == te->cursor_line && !te->has_selection) {
|
||||
int hl_h = gui_min(cell_h, editor_y_start + text_area_h - py);
|
||||
if (hl_h > 0)
|
||||
c.fill_rect(TE_LINE_NUM_W + 1, py, c.w - TE_LINE_NUM_W - 1, hl_h, cursor_line_color);
|
||||
c.fill_rect(TE_LINE_NUM_W + 1, py, text_area_w - TE_LINE_NUM_W - 1, hl_h, cursor_line_color);
|
||||
}
|
||||
|
||||
// Line number
|
||||
@@ -417,9 +510,18 @@ static void texteditor_on_draw(Window* win, Framebuffer& fb) {
|
||||
for (int ci = 0; ci < line_len; ci++) {
|
||||
int px = text_start_x + ci * cell_w - te->scroll_x;
|
||||
if (px + cell_w <= TE_LINE_NUM_W + 1) continue;
|
||||
if (px >= c.w) break;
|
||||
if (px >= text_area_w) break;
|
||||
|
||||
int byte_pos = line_start + ci;
|
||||
char ch = te->buffer[byte_pos];
|
||||
|
||||
// Draw selection highlight behind text
|
||||
if (te->has_selection && byte_pos >= sel_s && byte_pos < sel_e) {
|
||||
int hl_h = gui_min(cell_h, editor_y_start + text_area_h - py);
|
||||
if (hl_h > 0)
|
||||
c.fill_rect(px, py, cell_w, hl_h, sel_bg);
|
||||
}
|
||||
|
||||
char ch = te->buffer[line_start + ci];
|
||||
if (ch >= 32 || ch < 0) {
|
||||
if (fonts::mono && fonts::mono->valid) {
|
||||
GlyphCache* gc = fonts::mono->get_cache(fonts::TERM_SIZE);
|
||||
@@ -436,7 +538,7 @@ static void texteditor_on_draw(Window* win, Framebuffer& fb) {
|
||||
if (bits & (0x80 >> fx)) {
|
||||
int dx = px + fx;
|
||||
int dy = py + fy;
|
||||
if (dx > TE_LINE_NUM_W && dx < c.w && dy >= 0 && dy < c.h)
|
||||
if (dx > TE_LINE_NUM_W && dx < text_area_w && dy >= 0 && dy < c.h)
|
||||
c.pixels[dy * c.w + dx] = text_px;
|
||||
}
|
||||
}
|
||||
@@ -445,10 +547,23 @@ static void texteditor_on_draw(Window* win, Framebuffer& fb) {
|
||||
}
|
||||
}
|
||||
|
||||
// Draw selection highlight for newline at end of line (visual feedback)
|
||||
if (te->has_selection && line + 1 < te->line_count) {
|
||||
int nl_pos = line_start + line_len; // the newline byte
|
||||
if (nl_pos >= sel_s && nl_pos < sel_e) {
|
||||
int px = text_start_x + line_len * cell_w - te->scroll_x;
|
||||
if (px > TE_LINE_NUM_W && px < text_area_w) {
|
||||
int hl_h = gui_min(cell_h, editor_y_start + text_area_h - py);
|
||||
if (hl_h > 0)
|
||||
c.fill_rect(px, py, cell_w / 2, hl_h, sel_bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw cursor
|
||||
if (line == te->cursor_line) {
|
||||
int cx = text_start_x + te->cursor_col * cell_w - te->scroll_x;
|
||||
if (cx > TE_LINE_NUM_W && cx + 2 <= c.w) {
|
||||
if (cx > TE_LINE_NUM_W && cx + 2 <= text_area_w) {
|
||||
int cur_h = gui_min(cell_h, editor_y_start + text_area_h - py);
|
||||
if (cur_h > 0)
|
||||
c.fill_rect(cx, py, 2, cur_h, colors::ACCENT);
|
||||
@@ -456,13 +571,33 @@ static void texteditor_on_draw(Window* win, Framebuffer& fb) {
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Scrollbar ----
|
||||
if (te->scrollbar.content_height > te->scrollbar.view_height) {
|
||||
Color sb_fg = (te->scrollbar.hovered || te->scrollbar.dragging)
|
||||
? te->scrollbar.hover_fg : te->scrollbar.fg;
|
||||
int sbx = te->scrollbar.bounds.x;
|
||||
int sby = te->scrollbar.bounds.y;
|
||||
int sbw = te->scrollbar.bounds.w;
|
||||
int sbh = te->scrollbar.bounds.h;
|
||||
c.fill_rect(sbx, sby, sbw, sbh, colors::SCROLLBAR_BG);
|
||||
int th = te->scrollbar.thumb_height();
|
||||
int tty = te->scrollbar.thumb_y();
|
||||
c.fill_rect(sbx + 1, tty, sbw - 2, th, sb_fg);
|
||||
}
|
||||
|
||||
// ---- Status bar ----
|
||||
int status_y = c.h - TE_STATUS_H;
|
||||
c.fill_rect(0, status_y, c.w, TE_STATUS_H, Color::from_rgb(0x2B, 0x3E, 0x50));
|
||||
|
||||
// Cursor position (right side)
|
||||
char status_right[32];
|
||||
snprintf(status_right, 32, "Ln %d, Col %d ", te->cursor_line + 1, te->cursor_col + 1);
|
||||
char status_right[48];
|
||||
if (te->has_selection) {
|
||||
int ss, se;
|
||||
te_sel_range(te, &ss, &se);
|
||||
snprintf(status_right, 48, "%d selected Ln %d, Col %d ", se - ss, te->cursor_line + 1, te->cursor_col + 1);
|
||||
} else {
|
||||
snprintf(status_right, 48, "Ln %d, Col %d ", te->cursor_line + 1, te->cursor_col + 1);
|
||||
}
|
||||
int sr_w = text_width(status_right);
|
||||
int status_text_y = status_y + (TE_STATUS_H - sfh) / 2;
|
||||
c.text(c.w - sr_w - 4, status_text_y, status_right, colors::PANEL_TEXT);
|
||||
@@ -481,6 +616,22 @@ static void texteditor_on_draw(Window* win, Framebuffer& fb) {
|
||||
// Mouse handling
|
||||
// ============================================================================
|
||||
|
||||
static int te_hit_test(TextEditorState* te, int local_x, int local_y, int editor_y_start) {
|
||||
int cell_w = mono_cell_width();
|
||||
int cell_h = mono_cell_height();
|
||||
|
||||
int clicked_line = te->scroll_y + (local_y - editor_y_start) / cell_h;
|
||||
if (clicked_line >= te->line_count) clicked_line = te->line_count - 1;
|
||||
if (clicked_line < 0) clicked_line = 0;
|
||||
|
||||
int clicked_col = (local_x - TE_LINE_NUM_W - 4 + te->scroll_x + cell_w / 2) / cell_w;
|
||||
if (clicked_col < 0) clicked_col = 0;
|
||||
int line_len = te_line_length(te, clicked_line);
|
||||
if (clicked_col > line_len) clicked_col = line_len;
|
||||
|
||||
return te->line_offsets[clicked_line] + clicked_col;
|
||||
}
|
||||
|
||||
static void texteditor_on_mouse(Window* win, MouseEvent& ev) {
|
||||
TextEditorState* te = (TextEditorState*)win->app_data;
|
||||
if (!te) return;
|
||||
@@ -489,19 +640,32 @@ static void texteditor_on_mouse(Window* win, MouseEvent& ev) {
|
||||
int local_x = ev.x - cr.x;
|
||||
int local_y = ev.y - cr.y;
|
||||
|
||||
int cell_w = mono_cell_width();
|
||||
int cell_h = mono_cell_height();
|
||||
|
||||
int editor_y_start = TE_TOOLBAR_H + (te->show_pathbar ? TE_PATHBAR_H : 0);
|
||||
int text_area_h = cr.h - editor_y_start - TE_STATUS_H;
|
||||
|
||||
// ---- Scrollbar ----
|
||||
{
|
||||
MouseEvent local_ev = ev;
|
||||
local_ev.x = local_x;
|
||||
local_ev.y = local_y;
|
||||
bool was_dragging = te->scrollbar.dragging;
|
||||
te->scrollbar.handle_mouse(local_ev);
|
||||
if (te->scrollbar.dragging || was_dragging) {
|
||||
// Convert pixel scroll offset back to line index
|
||||
if (cell_h > 0)
|
||||
te->scroll_y = te->scrollbar.scroll_offset / cell_h;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Toolbar clicks ----
|
||||
if (ev.left_pressed() && local_y < TE_TOOLBAR_H) {
|
||||
// Open button
|
||||
if (local_x >= 4 && local_x < 28 && local_y >= 6 && local_y < 30) {
|
||||
te->show_pathbar = !te->show_pathbar;
|
||||
if (te->show_pathbar) {
|
||||
// Pre-fill with current filepath
|
||||
montauk::strncpy(te->pathbar_text, te->filepath, 255);
|
||||
te->pathbar_len = montauk::slen(te->pathbar_text);
|
||||
te->pathbar_cursor = te->pathbar_len;
|
||||
@@ -519,14 +683,12 @@ static void texteditor_on_mouse(Window* win, MouseEvent& ev) {
|
||||
// ---- Path bar clicks ----
|
||||
if (te->show_pathbar && local_y >= TE_TOOLBAR_H && local_y < TE_TOOLBAR_H + TE_PATHBAR_H) {
|
||||
if (ev.left_pressed()) {
|
||||
// Check "Open" button region
|
||||
int btn_w = 56;
|
||||
int inp_w = cr.w - 8 - btn_w - 12;
|
||||
int ob_x = 8 + inp_w + 6;
|
||||
if (local_x >= ob_x && local_x < ob_x + btn_w) {
|
||||
if (te->pathbar_text[0]) {
|
||||
te_load_file(te, te->pathbar_text);
|
||||
// Update window title
|
||||
char title[64];
|
||||
snprintf(title, 64, "%s - Editor", te->filename);
|
||||
montauk::strncpy(win->title, title, 63);
|
||||
@@ -537,26 +699,42 @@ static void texteditor_on_mouse(Window* win, MouseEvent& ev) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ---- Editor area clicks ----
|
||||
if (ev.left_pressed() && local_y >= editor_y_start && local_y < editor_y_start + text_area_h && local_x > TE_LINE_NUM_W) {
|
||||
int clicked_line = te->scroll_y + (local_y - editor_y_start) / cell_h;
|
||||
if (clicked_line >= te->line_count) clicked_line = te->line_count - 1;
|
||||
if (clicked_line < 0) clicked_line = 0;
|
||||
|
||||
int clicked_col = (local_x - TE_LINE_NUM_W - 4 + te->scroll_x + cell_w / 2) / cell_w;
|
||||
if (clicked_col < 0) clicked_col = 0;
|
||||
int line_len = te_line_length(te, clicked_line);
|
||||
if (clicked_col > line_len) clicked_col = line_len;
|
||||
|
||||
te->cursor_pos = te->line_offsets[clicked_line] + clicked_col;
|
||||
// ---- Editor area: mouse press - start selection or place cursor ----
|
||||
if (ev.left_pressed() && local_y >= editor_y_start && local_y < editor_y_start + text_area_h
|
||||
&& local_x > TE_LINE_NUM_W && local_x < cr.w - TE_SCROLLBAR_W) {
|
||||
int pos = te_hit_test(te, local_x, local_y, editor_y_start);
|
||||
te->cursor_pos = pos;
|
||||
te_update_cursor_pos(te);
|
||||
|
||||
te->sel_anchor = pos;
|
||||
te->sel_end = pos;
|
||||
te->has_selection = false;
|
||||
te->mouse_selecting = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// ---- Editor area: mouse drag - extend selection ----
|
||||
if (te->mouse_selecting && ev.left_held() && local_y >= editor_y_start - 20) {
|
||||
int pos = te_hit_test(te, local_x, local_y, editor_y_start);
|
||||
te->sel_end = pos;
|
||||
te->cursor_pos = pos;
|
||||
te_update_cursor_pos(te);
|
||||
te->has_selection = (te->sel_anchor != te->sel_end);
|
||||
return;
|
||||
}
|
||||
|
||||
// ---- Mouse release: end selection ----
|
||||
if (te->mouse_selecting && ev.left_released()) {
|
||||
te->mouse_selecting = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// ---- Scroll ----
|
||||
if (ev.scroll != 0 && local_y >= editor_y_start && local_y < editor_y_start + text_area_h) {
|
||||
te->scroll_y -= ev.scroll * 3;
|
||||
if (te->scroll_y < 0) te->scroll_y = 0;
|
||||
int max_scroll = te->line_count - (text_area_h / cell_h) + 1;
|
||||
int visible_lines = text_area_h / cell_h;
|
||||
int max_scroll = te->line_count - visible_lines;
|
||||
if (max_scroll < 0) max_scroll = 0;
|
||||
if (te->scroll_y > max_scroll) te->scroll_y = max_scroll;
|
||||
}
|
||||
@@ -635,33 +813,126 @@ static void texteditor_on_key(Window* win, const Montauk::KeyEvent& key) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Arrow keys
|
||||
if (key.scancode == 0x48) { te_move_up(te); return; }
|
||||
if (key.scancode == 0x50) { te_move_down(te); return; }
|
||||
if (key.scancode == 0x4B) { te_move_left(te); return; }
|
||||
if (key.scancode == 0x4D) { te_move_right(te); return; }
|
||||
// Ctrl+A: select all
|
||||
if (key.ctrl && (key.ascii == 'a' || key.ascii == 'A')) {
|
||||
te->sel_anchor = 0;
|
||||
te->sel_end = te->buf_len;
|
||||
te->has_selection = (te->buf_len > 0);
|
||||
te->cursor_pos = te->buf_len;
|
||||
te_update_cursor_pos(te);
|
||||
return;
|
||||
}
|
||||
|
||||
// Arrow keys (Shift extends selection)
|
||||
if (key.scancode == 0x48) { // Up
|
||||
if (key.shift) te_start_selection(te);
|
||||
else if (te->has_selection) te_clear_selection(te);
|
||||
te_move_up(te);
|
||||
if (key.shift) te_update_selection(te);
|
||||
return;
|
||||
}
|
||||
if (key.scancode == 0x50) { // Down
|
||||
if (key.shift) te_start_selection(te);
|
||||
else if (te->has_selection) te_clear_selection(te);
|
||||
te_move_down(te);
|
||||
if (key.shift) te_update_selection(te);
|
||||
return;
|
||||
}
|
||||
if (key.scancode == 0x4B) { // Left
|
||||
if (key.shift) {
|
||||
te_start_selection(te);
|
||||
te_move_left(te);
|
||||
te_update_selection(te);
|
||||
} else if (te->has_selection) {
|
||||
int s, e; te_sel_range(te, &s, &e);
|
||||
te->cursor_pos = s;
|
||||
te_update_cursor_pos(te);
|
||||
te_clear_selection(te);
|
||||
} else {
|
||||
te_move_left(te);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (key.scancode == 0x4D) { // Right
|
||||
if (key.shift) {
|
||||
te_start_selection(te);
|
||||
te_move_right(te);
|
||||
te_update_selection(te);
|
||||
} else if (te->has_selection) {
|
||||
int s, e; te_sel_range(te, &s, &e);
|
||||
te->cursor_pos = e;
|
||||
te_update_cursor_pos(te);
|
||||
te_clear_selection(te);
|
||||
} else {
|
||||
te_move_right(te);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Home
|
||||
if (key.scancode == 0x47) { te_move_home(te); return; }
|
||||
if (key.scancode == 0x47) {
|
||||
if (key.shift) te_start_selection(te);
|
||||
else if (te->has_selection) te_clear_selection(te);
|
||||
te_move_home(te);
|
||||
if (key.shift) te_update_selection(te);
|
||||
return;
|
||||
}
|
||||
// End
|
||||
if (key.scancode == 0x4F) { te_move_end(te); return; }
|
||||
if (key.scancode == 0x4F) {
|
||||
if (key.shift) te_start_selection(te);
|
||||
else if (te->has_selection) te_clear_selection(te);
|
||||
te_move_end(te);
|
||||
if (key.shift) te_update_selection(te);
|
||||
return;
|
||||
}
|
||||
|
||||
// Page Up
|
||||
if (key.scancode == 0x49) {
|
||||
if (key.shift) te_start_selection(te);
|
||||
else if (te->has_selection) te_clear_selection(te);
|
||||
int cell_h = mono_cell_height();
|
||||
int visible = (te->scrollbar.view_height > 0 && cell_h > 0)
|
||||
? te->scrollbar.view_height / cell_h : 20;
|
||||
for (int i = 0; i < visible; i++) te_move_up(te);
|
||||
if (key.shift) te_update_selection(te);
|
||||
return;
|
||||
}
|
||||
// Page Down
|
||||
if (key.scancode == 0x51) {
|
||||
if (key.shift) te_start_selection(te);
|
||||
else if (te->has_selection) te_clear_selection(te);
|
||||
int cell_h = mono_cell_height();
|
||||
int visible = (te->scrollbar.view_height > 0 && cell_h > 0)
|
||||
? te->scrollbar.view_height / cell_h : 20;
|
||||
for (int i = 0; i < visible; i++) te_move_down(te);
|
||||
if (key.shift) te_update_selection(te);
|
||||
return;
|
||||
}
|
||||
|
||||
// Delete
|
||||
if (key.scancode == 0x53) { te_delete_char(te); return; }
|
||||
if (key.scancode == 0x53) {
|
||||
if (te->has_selection) te_delete_selection(te);
|
||||
else te_delete_char(te);
|
||||
return;
|
||||
}
|
||||
|
||||
// Backspace
|
||||
if (key.ascii == '\b' || key.scancode == 0x0E) {
|
||||
te_backspace(te);
|
||||
if (te->has_selection) te_delete_selection(te);
|
||||
else te_backspace(te);
|
||||
return;
|
||||
}
|
||||
|
||||
// Enter
|
||||
if (key.ascii == '\n' || key.ascii == '\r') {
|
||||
if (te->has_selection) te_delete_selection(te);
|
||||
te_insert_char(te, '\n');
|
||||
return;
|
||||
}
|
||||
|
||||
// Tab
|
||||
if (key.ascii == '\t') {
|
||||
if (te->has_selection) te_delete_selection(te);
|
||||
for (int i = 0; i < TE_TAB_WIDTH; i++) {
|
||||
te_insert_char(te, ' ');
|
||||
}
|
||||
@@ -670,6 +941,7 @@ static void texteditor_on_key(Window* win, const Montauk::KeyEvent& key) {
|
||||
|
||||
// Printable characters
|
||||
if (key.ascii >= 32 && key.ascii < 127) {
|
||||
if (te->has_selection) te_delete_selection(te);
|
||||
te_insert_char(te, key.ascii);
|
||||
return;
|
||||
}
|
||||
@@ -706,6 +978,12 @@ void open_texteditor(DesktopState* ds) {
|
||||
te->pathbar_text[0] = '\0';
|
||||
te->pathbar_cursor = 0;
|
||||
te->pathbar_len = 0;
|
||||
te->has_selection = false;
|
||||
te->mouse_selecting = false;
|
||||
te->sel_anchor = 0;
|
||||
te->sel_end = 0;
|
||||
te->cursor_moved = true;
|
||||
te->scrollbar.init(0, 0, TE_SCROLLBAR_W, 100);
|
||||
|
||||
te_recompute_lines(te);
|
||||
te_update_cursor_pos(te);
|
||||
@@ -718,7 +996,6 @@ void open_texteditor(DesktopState* ds) {
|
||||
}
|
||||
|
||||
void open_texteditor_with_file(DesktopState* ds, const char* path) {
|
||||
// Extract filename for window title
|
||||
const char* name = path;
|
||||
for (int i = 0; path[i]; i++) {
|
||||
if (path[i] == '/') name = path + i + 1;
|
||||
@@ -743,9 +1020,12 @@ void open_texteditor_with_file(DesktopState* ds, const char* path) {
|
||||
te->pathbar_text[0] = '\0';
|
||||
te->pathbar_cursor = 0;
|
||||
te->pathbar_len = 0;
|
||||
te->has_selection = false;
|
||||
te->mouse_selecting = false;
|
||||
te->sel_anchor = 0;
|
||||
te->sel_end = 0;
|
||||
te->scrollbar.init(0, 0, TE_SCROLLBAR_W, 100);
|
||||
|
||||
// Initialize line index for empty document first (ensures line_offsets
|
||||
// is non-null even if te_load_file fails to open the file)
|
||||
te_recompute_lines(te);
|
||||
te_update_cursor_pos(te);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user