File Browser: scroll selected files into view

Add operator `FILE_OT_view_selected` to the file browser (and thus also
to the asset browser) that scrolls selected files into view.

This includes the active file, even though it is not selected. In
certain cases the active file can loose its selected state (clicking
next to it, or refreshing the asset browser), but then it's still shown
in the right-hand sidebar. Because of this, I found it important to take
it into account when scrolling.

This also includes a change to the keymaps:
- Blender default: {key NUMPAD_PERIOD} is removed from the "reload"
  operator, and assigned to the new "view selected files" operator. The
  reload operator was already doubly bound, and now {key R} is the only
  remaining hotkey for it.
- Industry compatible: {key F} is assigned to the new "view selected
  files" operator. This is consistent with the other "view selected"
  operators in other editors.

Reviewed By: Severin

Differential Revision: https://developer.blender.org/D10583
This commit is contained in:
Sybren A. Stüvel 2021-03-08 13:57:15 +01:00
parent e20b31504a
commit 5a67407d5a
8 changed files with 114 additions and 11 deletions

View File

@ -2011,7 +2011,6 @@ def km_file_browser_main(params):
# operator (i.e. in regular editor mode).
("file.select", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'},
{"properties": [("open", True), ("deselect_all", not params.legacy)]}),
("file.refresh", {"type": 'NUMPAD_PERIOD', "value": 'PRESS'}, None),
("file.select", {"type": 'LEFTMOUSE', "value": 'PRESS'},
{"properties": [("open", False), ("deselect_all", not params.legacy)]}),
("file.select", {"type": 'LEFTMOUSE', "value": 'CLICK', "ctrl": True},
@ -2053,6 +2052,7 @@ def km_file_browser_main(params):
{"properties": [("mode", 'SUB')]}),
("file.highlight", {"type": 'MOUSEMOVE', "value": 'ANY', "any": True}, None),
("file.sort_column_ui_context", {"type": 'LEFTMOUSE', "value": 'PRESS', "any": True}, None),
("file.view_selected", {"type": 'NUMPAD_PERIOD', "value": 'PRESS'}, None),
])
return keymap

View File

@ -1309,6 +1309,7 @@ def km_file_browser_main(params):
{"properties": [("mode", 'ADD')]}),
("file.highlight", {"type": 'MOUSEMOVE', "value": 'ANY', "any": True}, None),
("file.sort_column_ui_context", {"type": 'LEFTMOUSE', "value": 'PRESS', "any": True}, None),
("file.view_selected", {"type": 'F', "value": 'PRESS'}, None),
])
return keymap

View File

@ -494,6 +494,7 @@ class FILEBROWSER_MT_view(Menu):
layout.prop(st, "show_region_toolbar", text="Source List")
layout.prop(st, "show_region_ui", text="File Path")
layout.operator("file.view_selected")
layout.separator()

View File

@ -75,6 +75,7 @@ void FILE_OT_rename(struct wmOperatorType *ot);
void FILE_OT_smoothscroll(struct wmOperatorType *ot);
void FILE_OT_filepath_drop(struct wmOperatorType *ot);
void FILE_OT_start_filter(struct wmOperatorType *ot);
void FILE_OT_view_selected(struct wmOperatorType *ot);
void file_directory_enter_handle(bContext *C, void *arg_unused, void *arg_but);
void file_filename_enter_handle(bContext *C, void *arg_unused, void *arg_but);

View File

@ -256,6 +256,33 @@ static bool file_is_any_selected(struct FileList *files)
return false;
}
static FileSelection file_current_selection_range_get(struct FileList *files)
{
const int numfiles = filelist_files_ensure(files);
FileSelection selection = {-1, -1};
/* Iterate over the files once but in two loops, one to find the first selected file, and the
* other to find the last. */
int file_index;
for (file_index = 0; file_index < numfiles; file_index++) {
if (filelist_entry_is_selected(files, file_index)) {
/* First selected entry found. */
selection.first = file_index;
break;
}
}
for (; file_index < numfiles; file_index++) {
if (filelist_entry_is_selected(files, file_index)) {
selection.last = file_index;
/* Keep looping, we may find more selected files. */
}
}
return selection;
}
/**
* If \a file is outside viewbounds, this adjusts view to make sure it's inside
*/
@ -299,6 +326,24 @@ static void file_ensure_inside_viewbounds(ARegion *region, SpaceFile *sfile, con
}
}
static void file_ensure_selection_inside_viewbounds(ARegion *region,
SpaceFile *sfile,
FileSelection *sel)
{
const FileLayout *layout = ED_fileselect_get_layout(sfile, region);
if (((layout->flag & FILE_LAYOUT_HOR) && region->winx <= (1.2f * layout->tile_w)) &&
((layout->flag & FILE_LAYOUT_VER) && region->winy <= (2.0f * layout->tile_h))) {
return;
}
/* Adjust view to display selection. Doing iterations for first and last
* selected item makes view showing as much of the selection possible.
* Not really useful if tiles are (almost) bigger than viewbounds though. */
file_ensure_inside_viewbounds(region, sfile, sel->last);
file_ensure_inside_viewbounds(region, sfile, sel->first);
}
static FileSelect file_select(
bContext *C, const rcti *rect, FileSelType select, bool fill, bool do_diropen)
{
@ -330,16 +375,7 @@ static FileSelect file_select(
}
else if (sel.last >= 0) {
ARegion *region = CTX_wm_region(C);
const FileLayout *layout = ED_fileselect_get_layout(sfile, region);
/* Adjust view to display selection. Doing iterations for first and last
* selected item makes view showing as much of the selection possible.
* Not really useful if tiles are (almost) bigger than viewbounds though. */
if (((layout->flag & FILE_LAYOUT_HOR) && region->winx > (1.2f * layout->tile_w)) ||
((layout->flag & FILE_LAYOUT_VER) && region->winy > (2.0f * layout->tile_h))) {
file_ensure_inside_viewbounds(region, sfile, sel.last);
file_ensure_inside_viewbounds(region, sfile, sel.first);
}
file_ensure_selection_inside_viewbounds(region, sfile, &sel);
}
/* update operator for name change event */
@ -925,6 +961,55 @@ void FILE_OT_select_all(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Select All Operator
* \{ */
static int file_view_selected_exec(bContext *C, wmOperator *UNUSED(op))
{
SpaceFile *sfile = CTX_wm_space_file(C);
FileSelection sel = file_current_selection_range_get(sfile->files);
FileSelectParams *params = ED_fileselect_get_active_params(sfile);
if (sel.first == -1 && sel.last == -1 && params->active_file == -1) {
/* Nothing was selected. */
return OPERATOR_CANCELLED;
}
/* Extend the selection area with the active file, as it may not be selected but still is
* important to have in view. */
if (sel.first == -1 || params->active_file < sel.first) {
sel.first = params->active_file;
}
if (sel.last == -1 || params->active_file > sel.last) {
sel.last = params->active_file;
}
ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
file_ensure_selection_inside_viewbounds(region, sfile, &sel);
file_draw_check(C);
WM_event_add_mousemove(CTX_wm_window(C));
ED_area_tag_redraw(area);
return OPERATOR_FINISHED;
}
void FILE_OT_view_selected(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Frame Selected";
ot->description = "Scroll the selected files into view";
ot->idname = "FILE_OT_view_selected";
/* api callbacks */
ot->exec = file_view_selected_exec;
ot->poll = ED_operator_file_active;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Select Bookmark Operator
* \{ */

View File

@ -2701,6 +2701,19 @@ uint filelist_entry_select_index_get(FileList *filelist, const int index, FileCh
return 0;
}
bool filelist_entry_is_selected(FileList *filelist, const int index)
{
BLI_assert(index >= 0 && index < filelist->filelist.nbr_entries_filtered);
FileListInternEntry *intern_entry = filelist->filelist_intern.filtered[index];
/* BLI_ghash_lookup returns NULL if not found, which gets mapped to 0, which gets mapped to
* "not selected". */
const uint selection_state = POINTER_AS_UINT(
BLI_ghash_lookup(filelist->selection_state, intern_entry->uuid));
return selection_state != 0;
}
/**
* Set selection of the '..' parent entry, but only if it's actually visible.
*/

View File

@ -128,6 +128,7 @@ unsigned int filelist_entry_select_get(struct FileList *filelist,
unsigned int filelist_entry_select_index_get(struct FileList *filelist,
const int index,
FileCheckType check);
bool filelist_entry_is_selected(struct FileList *filelist, const int index);
void filelist_entry_parent_select_set(struct FileList *filelist,
FileSelType select,
unsigned int flag,

View File

@ -659,6 +659,7 @@ static void file_operatortypes(void)
WM_operatortype_append(FILE_OT_smoothscroll);
WM_operatortype_append(FILE_OT_filepath_drop);
WM_operatortype_append(FILE_OT_start_filter);
WM_operatortype_append(FILE_OT_view_selected);
}
/* NOTE: do not add .blend file reading on this level */