From 09666024cff86f3c3c5b8fd754c63300e0841794 Mon Sep 17 00:00:00 2001 From: luboslenco Date: Wed, 15 Apr 2026 22:34:35 +0200 Subject: [PATCH] paint: keyboard navigation in browser --- paint/sources/functions.h | 2 ++ paint/sources/globals.h | 1 + paint/sources/tab_browser.c | 33 ++++++++++++++++++----- paint/sources/ui_files.c | 53 ++++++++++++++++++++++++++++++++++--- 4 files changed, 78 insertions(+), 11 deletions(-) diff --git a/paint/sources/functions.h b/paint/sources/functions.h index 52c2da00..43aa40c4 100644 --- a/paint/sources/functions.h +++ b/paint/sources/functions.h @@ -132,6 +132,8 @@ void import_texture_run(char *path, bool hdr_as_envmap); void ui_files_show(char *filters, bool is_save, bool open_multiple, void (*files_done)(char *)); char *ui_files_file_browser(ui_handle_t *handle, bool drag_files, char *search, bool refresh, void (*context_menu)(char *)); void ui_files_go_up(ui_handle_t *handle); +void ui_files_navigate(i32 dx, i32 dy); +void ui_files_enter_selected(ui_handle_t *handle); void base_init(); i32 base_w(); i32 base_view3d_w(); diff --git a/paint/sources/globals.h b/paint/sources/globals.h index 0704f0d3..8f8b2d36 100644 --- a/paint/sources/globals.h +++ b/paint/sources/globals.h @@ -221,6 +221,7 @@ i32 util_render_font_preview_size = 200; ui_handle_t *tab_browser_hpath; ui_handle_t *tab_browser_hsearch; bool tab_browser_refresh = false; +extern i32 ui_files_selected; any_map_t *util_mesh_unwrappers; i32 ui_header_default_h = 30; i32 ui_header_h; diff --git a/paint/sources/tab_browser.c b/paint/sources/tab_browser.c index 81a26d93..25485015 100644 --- a/paint/sources/tab_browser.c +++ b/paint/sources/tab_browser.c @@ -7,9 +7,9 @@ char *_tab_browser_draw_file; char *_tab_browser_draw_b; void tab_browser_show_directory(char *directory) { - tab_browser_hpath->text = string_copy(directory); - tab_browser_hsearch->text = ""; - ui_base_htabs->buffer[TAB_AREA_STATUS]->i = 0; + tab_browser_hpath->text = string_copy(directory); + tab_browser_hsearch->text = ""; + ui_base_htabs->buffer[TAB_AREA_STATUS]->i = 0; g_config->layout_tabs->buffer[TAB_AREA_STATUS] = 0; } @@ -37,10 +37,10 @@ void tab_browser_draw_set_as_color_id_map_on_next_frame(void *_) { if (asset_index != -1) { g_context->colorid_handle->i = asset_index; g_context->colorid_picked = false; - ui_toolbar_handle->redraws = 1; + ui_toolbar_handle->redraws = 1; if (g_context->tool == TOOL_TYPE_COLORID) { ui_header_handle->redraws = 2; - g_context->ddirty = 2; + g_context->ddirty = 2; } } } @@ -172,6 +172,8 @@ void tab_browser_draw(ui_handle_t *htab) { i32 bookmarks_w = math_floor(100 * UI_SCALE()); bool show_full = ui->_w > (500 * UI_SCALE()); + bool in_focus = ui->input_x > ui->_window_x && ui->input_x < ui->_window_x + ui->_window_w && ui->input_y > ui->_window_y && + ui->input_y < ui->_window_y + ui->_window_h; if (string_equals(tab_browser_hpath->text, "") && g_config->bookmarks->length > 0) { // Init to first bookmark tab_browser_hpath->text = string_copy(g_config->bookmarks->buffer[0]); @@ -221,8 +223,8 @@ void tab_browser_draw(ui_handle_t *htab) { } // Refresh - bool in_focus = ui->input_x > ui->_window_x && ui->input_x < ui->_window_x + ui->_window_w && ui->input_y > ui->_window_y && - ui->input_y < ui->_window_y + ui->_window_h; + in_focus = ui->input_x > ui->_window_x && ui->input_x < ui->_window_x + ui->_window_w && ui->input_y > ui->_window_y && + ui->input_y < ui->_window_y + ui->_window_h; if (ui_icon_button(tr("Refresh"), ICON_REFRESH, UI_ALIGN_CENTER) || (in_focus && ui->is_key_pressed && ui->key_code == KEY_CODE_F5)) { tab_browser_refresh = true; } @@ -362,5 +364,22 @@ void tab_browser_draw(ui_handle_t *htab) { ui->_y = bottom_y; } } + + if (in_focus) { + if (ui->is_key_pressed && ui->key_code == KEY_CODE_LEFT) + ui_files_navigate(-1, 0); + if (ui->is_key_pressed && ui->key_code == KEY_CODE_RIGHT) + ui_files_navigate(1, 0); + if (ui->is_key_pressed && ui->key_code == KEY_CODE_UP) + ui_files_navigate(0, -1); + if (ui->is_key_pressed && ui->key_code == KEY_CODE_DOWN) + ui_files_navigate(0, 1); + if (ui->is_key_pressed && ui->key_code == KEY_CODE_RETURN) + ui_files_enter_selected(tab_browser_hpath); + if (ui->is_key_pressed && ui->key_code == KEY_CODE_BACKSPACE && nested) + ui_files_go_up(tab_browser_hpath); + if (ui->is_escape_down) + ui_files_selected = -1; + } } } diff --git a/paint/sources/ui_files.c b/paint/sources/ui_files.c index 9b324631..1f1d9228 100644 --- a/paint/sources/ui_files.c +++ b/paint/sources/ui_files.c @@ -17,6 +17,8 @@ string_array_t *ui_files_files = NULL; any_map_t *ui_files_icon_map = NULL; any_map_t *ui_files_icon_file_map = NULL; i32 ui_files_selected = -1; +i32 ui_files_num_cols = 1; +char *ui_files_select_pending = NULL; bool ui_files_show_extensions = false; bool ui_files_offline = false; ui_handle_t *_ui_files_file_browser_handle; @@ -260,11 +262,24 @@ char *ui_files_file_browser(ui_handle_t *handle, bool drag_files, char *search, gc_root(ui_files_last_search); handle->changed = false; + if (ui_files_select_pending != NULL) { + ui_files_selected = -1; + for (i32 i = 0; i < ui_files_files->length; ++i) { + if (string_equals(ui_files_files->buffer[i], ui_files_select_pending)) { + ui_files_selected = i; + break; + } + } + gc_unroot(ui_files_select_pending); + ui_files_select_pending = NULL; + } + i32 slotw = math_floor(70 * UI_SCALE()); i32 num = math_floor(ui->_w / (float)slotw); if (num == 0) { return handle->text; } + ui_files_num_cols = num; ui->_y += 4; // Don't cut off the border around selected materials // Directory contents @@ -340,8 +355,7 @@ char *ui_files_file_browser(ui_handle_t *handle, bool drag_files, char *search, gc_root(_ui_files_file_browser_handle); any_map_set(ui_files_icon_file_map, icon_file, f); - file_cache_cloud(string("%s%s%s", handle->text, PATH_SEP, icon_file), &ui_files_file_browser_on_cache_cloud_done, - g_config->server); + file_cache_cloud(string("%s%s%s", handle->text, PATH_SEP, icon_file), &ui_files_file_browser_on_cache_cloud_done, g_config->server); } } } @@ -376,7 +390,7 @@ char *ui_files_file_browser(ui_handle_t *handle, bool drag_files, char *search, blob_path = string("%s%s", document_directory, blob_path); #endif - buffer_t *buffer = iron_load_blob(blob_path); + buffer_t *buffer = iron_load_blob(blob_path); project_t *raw; if (import_arm_is_old(buffer)) { raw = import_arm_from_old(buffer); @@ -553,8 +567,39 @@ char *ui_files_file_browser(ui_handle_t *handle, bool drag_files, char *search, return handle->text; } +void ui_files_enter_selected(ui_handle_t *handle) { + if (ui_files_files == NULL || ui_files_selected < 0 || ui_files_selected >= ui_files_files->length) { + return; + } + char *f = ui_files_files->buffer[ui_files_selected]; + if (!string_equals(char_at(handle->text, string_length(handle->text) - 1), PATH_SEP)) { + handle->text = string("%s%s", handle->text, PATH_SEP); + } + handle->text = string("%s%s", handle->text, f); + handle->changed = true; + ui_files_selected = -1; +} + +void ui_files_navigate(i32 dx, i32 dy) { + if (ui_files_files == NULL || ui_files_files->length == 0) { + return; + } + if (ui_files_selected < 0) { + ui_files_selected = 0; + return; + } + i32 next = ui_files_selected + dx + dy * ui_files_num_cols; + if (next >= 0 && next < ui_files_files->length) { + ui_files_selected = next; + } +} + void ui_files_go_up(ui_handle_t *handle) { - handle->text = string_copy(substring(handle->text, 0, string_last_index_of(handle->text, PATH_SEP))); + i32 sep = string_last_index_of(handle->text, PATH_SEP); + gc_unroot(ui_files_select_pending); + ui_files_select_pending = string_copy(substring(handle->text, sep + 1, string_length(handle->text))); + gc_root(ui_files_select_pending); + handle->text = string_copy(substring(handle->text, 0, sep)); // Drive root if (string_length(handle->text) == 2 && string_equals(char_at(handle->text, 1), ":")) { handle->text = string("%s%s", handle->text, PATH_SEP);