From c0f5a443620b069416a694048a68f1284d376747 Mon Sep 17 00:00:00 2001 From: codereader Date: Sun, 10 Jan 2021 06:09:27 +0100 Subject: [PATCH] #3250: Try to make the filter search feel more alive by focusing on the first matching item (unless the selected item already matches the filter text). Tweak the toolbar a bit. --- libs/wxutil/dataview/ResourceTreeView.cpp | 51 +++++++++++++++++-- libs/wxutil/dataview/ResourceTreeView.h | 2 + .../wxutil/dataview/ResourceTreeViewToolbar.h | 30 ++++++++--- libs/wxutil/dataview/TreeView.h | 3 +- radiant/ui/mediabrowser/MediaBrowser.cpp | 2 +- 5 files changed, 74 insertions(+), 14 deletions(-) diff --git a/libs/wxutil/dataview/ResourceTreeView.cpp b/libs/wxutil/dataview/ResourceTreeView.cpp index 4b89caa281..379291cc95 100644 --- a/libs/wxutil/dataview/ResourceTreeView.cpp +++ b/libs/wxutil/dataview/ResourceTreeView.cpp @@ -204,11 +204,50 @@ void ResourceTreeView::SetFilterText(const std::string& filterText) { // We use the lower-case copy of the given filter text _filterText = string::to_lower_copy(filterText); + + wxDataViewItem item = GetSelection(); + + Rebuild(); + + // Keep the previous selection if not filtered out and is meaningful + if (item.IsOk() && _treeModelFilter->ItemIsVisible(item)) + { + TreeModel::Row row(item, *GetModel()); + + if (!_filterText.empty() && !RowContainsSearchString(row)) + { + // The selected row is not relevant anymore + JumpToFirstFilterMatch(); + return; + } + + // Try to keep whatever selection we had before + Select(item); + EnsureVisible(item); + } + else + { + JumpToFirstFilterMatch(); + } +} + +void ResourceTreeView::JumpToFirstFilterMatch() +{ + if (_filterText.empty()) return; + + auto item = _treeModelFilter->FindNextString(_filterText, _colsToSearch); + + if (item.IsOk()) + { + JumpToSearchMatch(item); + } } void ResourceTreeView::ClearFilterText() { _filterText.clear(); + + Rebuild(); } std::string ResourceTreeView::GetSelectedFullname() @@ -456,17 +495,21 @@ bool ResourceTreeView::IsTreeModelRowVisible(wxutil::TreeModel::Row& row) return !IsTreeModelRowFilteredRecursively(row); } -bool ResourceTreeView::IsTreeModelRowFilteredRecursively(wxutil::TreeModel::Row& row) +bool ResourceTreeView::RowContainsSearchString(wxutil::TreeModel::Row& row) { wxDataViewIconText iconAndName = row[_columns.iconAndName]; auto displayString = iconAndName.GetText().ToStdString(); string::to_lower(displayString); - //rMessage() << "displayString: " << displayString << ": " << displayString.find(_filterText) << std::endl; - if (displayString.find(_filterText) != std::string::npos) + return displayString.find(_filterText) != std::string::npos; +} + +bool ResourceTreeView::IsTreeModelRowFilteredRecursively(wxutil::TreeModel::Row& row) +{ + if (RowContainsSearchString(row)) { - return false; // row itself is visible, no need to check child nodes + return false; } // This node might also be visible if a single child node is visible, dive into it diff --git a/libs/wxutil/dataview/ResourceTreeView.h b/libs/wxutil/dataview/ResourceTreeView.h index fc5a00b66b..e7e4a67693 100644 --- a/libs/wxutil/dataview/ResourceTreeView.h +++ b/libs/wxutil/dataview/ResourceTreeView.h @@ -152,6 +152,8 @@ class ResourceTreeView : // Returns true if the given row is filtered (i.e. node and all child nodes are invisible) bool IsTreeModelRowFilteredRecursively(wxutil::TreeModel::Row& row); + bool RowContainsSearchString(wxutil::TreeModel::Row& row); + void JumpToFirstFilterMatch(); void _onContextMenu(wxDataViewEvent& ev); void _onTreeStorePopulationProgress(TreeModel::PopulationProgressEvent& ev); diff --git a/libs/wxutil/dataview/ResourceTreeViewToolbar.h b/libs/wxutil/dataview/ResourceTreeViewToolbar.h index 7bdd0fc112..d393eb3ba0 100644 --- a/libs/wxutil/dataview/ResourceTreeViewToolbar.h +++ b/libs/wxutil/dataview/ResourceTreeViewToolbar.h @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include #include "ResourceTreeView.h" @@ -29,29 +31,41 @@ class ResourceTreeViewToolbar : _showAll(nullptr), _showFavourites(nullptr) { - // Hbox for the favourites selection widgets - SetSizer(new wxBoxSizer(wxHORIZONTAL)); + auto* grid = new wxFlexGridSizer(2); + grid->AddGrowableCol(1); + + SetSizer(grid); + // Hbox for the favourites selection widgets + auto* favourites = new wxBoxSizer(wxHORIZONTAL); _showAll = new wxRadioButton(this, wxID_ANY, _("Show All"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP); _showFavourites = new wxRadioButton(this, wxID_ANY, _("Show Favourites")); _showAll->Bind(wxEVT_RADIOBUTTON, &ResourceTreeViewToolbar::_onFilterButtonToggled, this); _showFavourites->Bind(wxEVT_RADIOBUTTON, &ResourceTreeViewToolbar::_onFilterButtonToggled, this); - GetSizer()->Add(_showAll, 0, wxRIGHT, 0); - GetSizer()->Add(_showFavourites, 0, wxLEFT, 6); + favourites->Add(_showAll, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 0); + favourites->Add(_showFavourites, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, 6); // Filter text entry box - auto* filterBox = new wxTextCtrl(this, wxID_ANY); - filterBox->SetMinSize(wxSize(60, -1)); - filterBox->Bind(wxEVT_TEXT, [this](wxCommandEvent& ev) + auto* filterBox = new wxBoxSizer(wxHORIZONTAL); + + auto* filterImage = new wxStaticBitmap(this, wxID_ANY, wxArtProvider::GetBitmap(wxART_FIND, wxART_TOOLBAR, wxSize(16, 16))); + + auto* filterEntry = new wxTextCtrl(this, wxID_ANY); + filterEntry->SetMinSize(wxSize(100, -1)); + filterEntry->Bind(wxEVT_TEXT, [this](wxCommandEvent& ev) { if (_treeView != nullptr) { _treeView->SetFilterText(ev.GetString().ToStdString()); } }); - GetSizer()->Add(filterBox, 1, wxLEFT, 6); + filterBox->Add(filterImage, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 6); + filterBox->Add(filterEntry, 0, wxALIGN_CENTER_VERTICAL, 6); + + grid->Add(favourites, 0, wxALIGN_CENTER_VERTICAL| wxALIGN_LEFT | wxRIGHT, 6); + grid->Add(filterBox, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT, 6); AssociateToTreeView(treeView); } diff --git a/libs/wxutil/dataview/TreeView.h b/libs/wxutil/dataview/TreeView.h index 8a02e6782a..1cb1ca9e83 100644 --- a/libs/wxutil/dataview/TreeView.h +++ b/libs/wxutil/dataview/TreeView.h @@ -88,11 +88,12 @@ class TreeView : // Synthesises a selection change event to notify any handlers void SendSelectionChangeEvent(const wxDataViewItem& item); + void JumpToSearchMatch(const wxDataViewItem& item); + private: void CollapseChildren(const wxDataViewItem& item); void CloseSearch(); - void JumpToSearchMatch(const wxDataViewItem& item); void _onItemCollapsing(wxDataViewEvent& ev); void _onItemExpanded(wxDataViewEvent& ev); diff --git a/radiant/ui/mediabrowser/MediaBrowser.cpp b/radiant/ui/mediabrowser/MediaBrowser.cpp index 3810ca9fcb..a44bc265b1 100644 --- a/radiant/ui/mediabrowser/MediaBrowser.cpp +++ b/radiant/ui/mediabrowser/MediaBrowser.cpp @@ -60,7 +60,7 @@ void MediaBrowser::construct() _treeView = new MediaBrowserTreeView(_mainWidget); auto* toolbar = new wxutil::ResourceTreeViewToolbar(_mainWidget, _treeView); - _mainWidget->GetSizer()->Add(toolbar, 0, wxALIGN_LEFT | wxALL, 6); + _mainWidget->GetSizer()->Add(toolbar, 0, wxALIGN_LEFT | wxEXPAND | wxALL, 6); _mainWidget->GetSizer()->Add(_treeView, 1, wxEXPAND); // Connect up the selection changed callback