/* * events.cpp * Mouse and keyboard handling for the embedded desktop file manager * Copyright (c) 2026 Daniel Hammer */ #include "filemanager_internal.hpp" namespace filemanager { void filemanager_on_mouse(Window* win, MouseEvent& ev) { FileManagerState* fm = (FileManagerState*)win->app_data; if (!fm) return; Rect cr = win->content_rect(); int local_x = ev.x - cr.x; int local_y = ev.y - cr.y; int cw = cr.w; // Scrollbar interaction MouseEvent local_ev = ev; local_ev.x = local_x; local_ev.y = local_y; fm->scrollbar.handle_mouse(local_ev); // ---- Context menu interaction ---- if (fm->ctx_open) { mtk::ContextMenuState state = filemanager_context_state(fm); mtk::ContextMenuItem items[8]; filemanager_build_context_items(fm, items, 8); int action = mtk::context_menu_handle_mouse(state, items, fm->ctx_item_count, local_x, local_y, ev.buttons, ev.prev_buttons, cw, win->content_h); filemanager_store_context_state(fm, state); if (action >= 0) { int target = fm->ctx_target_idx; // Select target if needed if (target >= 0 && target < fm->entry_count) fm->selected = target; switch (action) { case CTX_OPEN: filemanager_open_entry(fm, target); break; case CTX_COPY: filemanager_do_copy(fm); break; case CTX_CUT: filemanager_do_cut(fm); break; case CTX_PASTE: filemanager_do_paste(fm); break; case CTX_RENAME: filemanager_start_rename(fm); break; case CTX_DELETE: filemanager_delete_selected(fm); break; case CTX_NEW_FOLDER: filemanager_new_folder(fm); break; case CTX_PROPERTIES: filemanager_show_properties(fm, target); break; } return; } if (action == mtk::CONTEXT_MENU_DISMISSED) return; return; } // ---- Cancel rename on click outside rename area ---- if (fm->rename_active && ev.left_pressed()) { filemanager_finish_rename(fm); } if (ev.left_pressed()) { // Click on path bar area if (local_y >= FM_TOOLBAR_H && local_y < FM_TOOLBAR_H + FM_PATHBAR_H) { if (!fm->pathbar_editing) filemanager_start_pathbar(fm); return; } // Clicking anywhere else while pathbar is editing commits the path if (fm->pathbar_editing) { filemanager_commit_pathbar(fm); } // Toolbar button clicks if (local_y < FM_TOOLBAR_H) { // Navigation buttons if (local_x >= 4 && local_x < 28) filemanager_go_back(fm); else if (local_x >= 32 && local_x < 56) filemanager_go_forward(fm); else if (local_x >= 60 && local_x < 84) filemanager_go_up(fm); else if (local_x >= 88 && local_x < 112) filemanager_go_home(fm); else if (local_x >= 120 && local_x < 144) { fm->grid_view = !fm->grid_view; fm->scrollbar.scroll_offset = 0; } // Action buttons else if (local_x >= 160 && local_x < 184) filemanager_do_copy(fm); else if (local_x >= 188 && local_x < 212) filemanager_do_cut(fm); else if (local_x >= 216 && local_x < 240) filemanager_do_paste(fm); else if (local_x >= 244 && local_x < 268) filemanager_start_rename(fm); else if (local_x >= 272 && local_x < 296) filemanager_new_folder(fm); else if (local_x >= 300 && local_x < 324) filemanager_delete_selected(fm); return; } // File clicks (grid vs list) if (fm->grid_view) { int list_y = FM_TOOLBAR_H + FM_PATHBAR_H; if (local_y >= list_y && local_x < cw - FM_SCROLLBAR_W) { int cols = (cw - FM_SCROLLBAR_W) / FM_GRID_CELL_W; if (cols < 1) cols = 1; int rel_y = local_y - list_y + fm->scrollbar.scroll_offset; int clicked_idx = filemanager_grid_index_at(fm, cols, local_x, rel_y); if (clicked_idx >= 0 && clicked_idx < fm->entry_count) { uint64_t now = montauk::get_milliseconds(); if (fm->last_click_item == clicked_idx && (now - fm->last_click_time) < 400) { filemanager_open_entry(fm, clicked_idx); fm->last_click_item = -1; fm->last_click_time = 0; } else { fm->selected = clicked_idx; fm->last_click_item = clicked_idx; fm->last_click_time = now; } } else { fm->selected = -1; } } } else { // List view clicks int list_y = FM_TOOLBAR_H + FM_PATHBAR_H + FM_HEADER_H; if (local_y >= list_y && local_x < cw - FM_SCROLLBAR_W) { int rel_y = local_y - list_y + fm->scrollbar.scroll_offset; int clicked_idx = rel_y / FM_ITEM_H; if (clicked_idx >= 0 && clicked_idx < fm->entry_count) { uint64_t now = montauk::get_milliseconds(); // Double-click detection if (fm->last_click_item == clicked_idx && (now - fm->last_click_time) < 400) { filemanager_open_entry(fm, clicked_idx); fm->last_click_item = -1; fm->last_click_time = 0; } else { fm->selected = clicked_idx; fm->last_click_item = clicked_idx; fm->last_click_time = now; } } else { fm->selected = -1; } } } } // ---- Right-click: open context menu ---- if (ev.right_pressed()) { filemanager_cancel_rename(fm); // Determine what was right-clicked int target_idx = -1; if (local_y >= FM_TOOLBAR_H + FM_PATHBAR_H) { if (fm->grid_view) { int list_y = FM_TOOLBAR_H + FM_PATHBAR_H; if (local_x < cw - FM_SCROLLBAR_W) { int cols = (cw - FM_SCROLLBAR_W) / FM_GRID_CELL_W; if (cols < 1) cols = 1; int rel_y = local_y - list_y + fm->scrollbar.scroll_offset; int idx = filemanager_grid_index_at(fm, cols, local_x, rel_y); if (idx >= 0 && idx < fm->entry_count) { target_idx = idx; fm->selected = idx; } } } else { int list_y = FM_TOOLBAR_H + FM_PATHBAR_H + FM_HEADER_H; if (local_y >= list_y && local_x < cw - FM_SCROLLBAR_W) { int rel_y = local_y - list_y + fm->scrollbar.scroll_offset; int idx = rel_y / FM_ITEM_H; if (idx >= 0 && idx < fm->entry_count) { target_idx = idx; fm->selected = idx; } } } } filemanager_open_ctx_menu(fm, local_x, local_y, target_idx); } // Scroll handling if (ev.scroll != 0) { int list_y_start = fm->grid_view ? FM_TOOLBAR_H + FM_PATHBAR_H : FM_TOOLBAR_H + FM_PATHBAR_H + FM_HEADER_H; int scroll_step = fm->grid_view ? FM_GRID_CELL_H : FM_ITEM_H; if (local_y >= list_y_start) { fm->scrollbar.scroll_offset -= ev.scroll * scroll_step; int ms = fm->scrollbar.max_scroll(); if (fm->scrollbar.scroll_offset < 0) fm->scrollbar.scroll_offset = 0; if (fm->scrollbar.scroll_offset > ms) fm->scrollbar.scroll_offset = ms; } } } void filemanager_on_key(Window* win, const Montauk::KeyEvent& key) { FileManagerState* fm = (FileManagerState*)win->app_data; if (!fm || !key.pressed) return; // ---- Path bar editing key handling ---- if (fm->pathbar_editing) { if (key.ascii == '\n' || key.ascii == '\r') { filemanager_commit_pathbar(fm); } else if (key.scancode == 0x01) { filemanager_cancel_pathbar(fm); } else if (key.ascii == '\b' || key.scancode == 0x0E) { if (fm->pathbar_cursor > 0) { for (int i = fm->pathbar_cursor - 1; i < fm->pathbar_len - 1; i++) fm->pathbar_buf[i] = fm->pathbar_buf[i + 1]; fm->pathbar_len--; fm->pathbar_cursor--; fm->pathbar_buf[fm->pathbar_len] = '\0'; } } else if (key.scancode == 0x53) { if (fm->pathbar_cursor < fm->pathbar_len) { for (int i = fm->pathbar_cursor; i < fm->pathbar_len - 1; i++) fm->pathbar_buf[i] = fm->pathbar_buf[i + 1]; fm->pathbar_len--; fm->pathbar_buf[fm->pathbar_len] = '\0'; } } else if (key.scancode == 0x4B) { if (fm->pathbar_cursor > 0) fm->pathbar_cursor--; } else if (key.scancode == 0x4D) { if (fm->pathbar_cursor < fm->pathbar_len) fm->pathbar_cursor++; } else if (key.scancode == 0x47) { fm->pathbar_cursor = 0; } else if (key.scancode == 0x4F) { fm->pathbar_cursor = fm->pathbar_len; } else if (key.ascii >= 32 && key.ascii < 127 && fm->pathbar_len < 254) { for (int i = fm->pathbar_len; i > fm->pathbar_cursor; i--) fm->pathbar_buf[i] = fm->pathbar_buf[i - 1]; fm->pathbar_buf[fm->pathbar_cursor] = key.ascii; fm->pathbar_cursor++; fm->pathbar_len++; fm->pathbar_buf[fm->pathbar_len] = '\0'; } return; } // ---- Rename mode key handling ---- if (fm->rename_active) { if (key.ascii == '\n' || key.ascii == '\r') { filemanager_finish_rename(fm); } else if (key.scancode == 0x01) { // Escape filemanager_cancel_rename(fm); } else if (key.ascii == '\b' || key.scancode == 0x0E) { // Backspace if (fm->rename_cursor > 0) { for (int i = fm->rename_cursor - 1; i < fm->rename_len - 1; i++) fm->rename_buf[i] = fm->rename_buf[i + 1]; fm->rename_len--; fm->rename_cursor--; fm->rename_buf[fm->rename_len] = '\0'; } } else if (key.scancode == 0x53) { // Delete key if (fm->rename_cursor < fm->rename_len) { for (int i = fm->rename_cursor; i < fm->rename_len - 1; i++) fm->rename_buf[i] = fm->rename_buf[i + 1]; fm->rename_len--; fm->rename_buf[fm->rename_len] = '\0'; } } else if (key.scancode == 0x4B) { // Left arrow if (fm->rename_cursor > 0) fm->rename_cursor--; } else if (key.scancode == 0x4D) { // Right arrow if (fm->rename_cursor < fm->rename_len) fm->rename_cursor++; } else if (key.scancode == 0x47) { // Home fm->rename_cursor = 0; } else if (key.scancode == 0x4F) { // End fm->rename_cursor = fm->rename_len; } else if (key.ascii >= 32 && key.ascii < 127) { // Printable character (reject / and other FS-unsafe chars) if (key.ascii != '/' && key.ascii != '\\' && fm->rename_len < 62) { for (int i = fm->rename_len; i > fm->rename_cursor; i--) fm->rename_buf[i] = fm->rename_buf[i - 1]; fm->rename_buf[fm->rename_cursor] = key.ascii; fm->rename_cursor++; fm->rename_len++; fm->rename_buf[fm->rename_len] = '\0'; } } return; } // ---- Context menu: Escape to close ---- if (fm->ctx_open && key.scancode == 0x01) { filemanager_close_ctx_menu(fm); return; } // ---- Keyboard shortcuts ---- if (key.ctrl) { if (key.ascii == 'c' || key.ascii == 'C') { filemanager_do_copy(fm); return; } if (key.ascii == 'x' || key.ascii == 'X') { filemanager_do_cut(fm); return; } if (key.ascii == 'v' || key.ascii == 'V') { filemanager_do_paste(fm); return; } } // F2 = rename if (key.scancode == 0x3C) { filemanager_start_rename(fm); return; } if (key.ascii == '\b' || key.scancode == 0x0E) { filemanager_go_up(fm); } else if (key.scancode == 0x48) { // Up arrow if (fm->grid_view) { Rect cr_r = win->content_rect(); int cols = (cr_r.w - FM_SCROLLBAR_W) / FM_GRID_CELL_W; if (cols < 1) cols = 1; if (fm->selected >= cols) fm->selected -= cols; } else { if (fm->selected > 0) fm->selected--; } } else if (key.scancode == 0x50) { // Down arrow if (fm->grid_view) { Rect cr_r = win->content_rect(); int cols = (cr_r.w - FM_SCROLLBAR_W) / FM_GRID_CELL_W; if (cols < 1) cols = 1; if (fm->selected + cols < fm->entry_count) fm->selected += cols; } else { if (fm->selected < fm->entry_count - 1) fm->selected++; } } else if (key.scancode == 0x4B && !key.alt && fm->grid_view) { // Left arrow (grid view only) if (fm->selected > 0) fm->selected--; } else if (key.scancode == 0x4D && !key.alt && fm->grid_view) { // Right arrow (grid view only) if (fm->selected < fm->entry_count - 1) fm->selected++; } else if (key.ascii == '\n' || key.ascii == '\r') { if (fm->selected >= 0 && fm->selected < fm->entry_count) filemanager_open_entry(fm, fm->selected); } else if (key.scancode == 0x53) { // Delete key filemanager_delete_selected(fm); } else if (key.alt && key.scancode == 0x4B) { // Alt+Left: go back filemanager_go_back(fm); } else if (key.alt && key.scancode == 0x4D) { // Alt+Right: go forward filemanager_go_forward(fm); } } void filemanager_on_close(Window* win) { if (win->app_data) { FileManagerState* fm = (FileManagerState*)win->app_data; filemanager_free_app_icons(fm); montauk::mfree(win->app_data); win->app_data = nullptr; } } } // namespace filemanager