ENH:Add shortcut keys and lists for objects search

JIRA: STUDIO-5157 STUDIO-5158 STUDIO-5240

Signed-off-by: Kunlong Ma <kunlong.ma@bambulab.com>
Change-Id: Ic7cfaaa9b4bb8a85208bafab7fe3bafdb78f0045
This commit is contained in:
Kunlong Ma 2023-11-15 14:40:42 +08:00 committed by Lane.Wei
parent 310e68c34c
commit 5bcd363a25
9 changed files with 292 additions and 103 deletions

View File

@ -779,58 +779,20 @@ void ObjectList::printable_state_changed(const std::vector<ObjectVolumeID>& ov_i
wxGetApp().plater()->update();
}
void ObjectList::search_object_list() {
void ObjectList::assembly_plate_object_name()
{
m_objects_model->assembly_name();
}
auto found_list = m_objects_model->get_search_list();
auto found_size = found_list.size();
if (cur_pos >= found_size) {
cur_pos = 0;
void ObjectList::selected_object(ObjectDataViewModelNode* item)
{
if (!item) {
return;
}
if (cur_pos < found_size) {
wxDataViewItem cur_item = found_list.Item(cur_pos);
select_item(cur_item);
cur_pos++;
this->SetFocus();
select_item(wxDataViewItem(item));
ensure_current_item_visible();
selection_changed();
}
}
void ObjectList::set_found_list(wxString current_search_text) {
PartPlateList& ppl = wxGetApp().plater()->get_partplate_list();
current_search_text = current_search_text.MakeLower();
m_objects_model->append_found_list(current_search_text);
if (current_search_text.empty()) {
if (ppl.get_plate_count() > 0) {
wxDataViewItem item = m_objects_model->GetItemByPlateId(0);
select_item(item);
ensure_current_item_visible();
selection_changed();
}
auto column = GetColumn(colName);
column->SetTitle(_L("Name"));
}
else {
auto found_list = m_objects_model->get_search_list();
auto column = GetColumn(colName);
wxString match_num = wxString::Format("%d", found_list.size());
wxString match_message = " (" + match_num + _L(" search results") + ")";
wxString column_name = _L("Name") + match_message;
column->SetTitle(column_name);
}
}
void ObjectList::set_cur_pos(int value) {
cur_pos = value;
}
void ObjectList::searchbar_kill_focus() {
auto column = GetColumn(colName);
column->SetTitle(_L("Name"));
}
void ObjectList::update_objects_list_filament_column(size_t filaments_count)

View File

@ -457,10 +457,9 @@ public:
void printable_state_changed(const std::vector<ObjectVolumeID>& ov_ids);
// search objectlist
void search_object_list();
void set_found_list(wxString current_search_text);
void set_cur_pos(int value);
void searchbar_kill_focus();
void assembly_plate_object_name();
void selected_object(ObjectDataViewModelNode* item);
private:
#ifdef __WXOSX__
// void OnChar(wxKeyEvent& event);
@ -488,7 +487,6 @@ private:
std::vector<int> m_columns_width;
wxSize m_last_size;
int cur_pos = 0;
};

View File

@ -630,7 +630,11 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, BORDERLESS_FRAME_
if (evt.CmdDown() && evt.GetKeyCode() == 'O') { m_plater->load_project(); return;}
if (evt.CmdDown() && evt.ShiftDown() && evt.GetKeyCode() == 'S') { if (can_save_as()) m_plater->save_project(true); return;}
else if (evt.CmdDown() && evt.GetKeyCode() == 'S') { if (can_save()) m_plater->save_project(); return;}
if (evt.CmdDown() && evt.GetKeyCode() == 'F') {
if (m_plater && (m_tabpanel->GetSelection() == TabPosition::tp3DEditor || m_tabpanel->GetSelection() == TabPosition::tpPreview)) {
m_plater->sidebar().can_search();
}
}
#ifdef __APPLE__
if (evt.CmdDown() && evt.GetKeyCode() == ',')
#else

View File

@ -1498,26 +1498,53 @@ void ObjectDataViewModel::UpdateItemNames()
ItemsChanged(changed_items);
}
void ObjectDataViewModel::append_found_list(wxString current_search_text)
void ObjectDataViewModel::assembly_name()
{
found_list.clear();
assembly_name_list.clear();
for (int i = 0; i < m_plates.size(); ++i) {
append_found(current_search_text, m_plates[i]);
assembly_name(m_plates[i], m_plates[i]->GetName());
}
search_found_list = assembly_name_list;
}
void ObjectDataViewModel::assembly_name(ObjectDataViewModelNode* item, wxString name)
{
auto type = this->GetItemType(wxDataViewItem(item));
if (type != itPlate) {
wxString str = name + ":" + item->GetName();
assembly_name_list.push_back(std::make_pair(item, str));
}
for (size_t i = 0; i < item->GetChildCount(); ++i) {
wxString str_name = name + ":" + item->GetName();
if (type == itPlate) {
str_name = name;
}
assembly_name(item->GetNthChild(i), str_name);
}
}
void ObjectDataViewModel::append_found(wxString current_search_text, ObjectDataViewModelNode* item)
void ObjectDataViewModel::search_object(wxString search_text)
{
wxString item_name = item->GetName();
item_name = item_name.MakeLower();
if (item_name.find(current_search_text) != wxString::npos) {
if(item != m_plate_outside)
found_list.Add(wxDataViewItem(item));
if (search_text.empty()) {
search_found_list = assembly_name_list;
}
else {
search_found_list.clear();
search_text = search_text.MakeLower();
for (size_t i = 0; i < item->GetChildCount(); ++i) {
append_found(current_search_text, item->GetNthChild(i));
for (auto pair : assembly_name_list) {
wxString need_str = pair.second.AfterFirst(':');
need_str = need_str.MakeLower();
size_t pos = need_str.find(search_text);
if ( pos != wxString::npos) {
size_t len = search_text.length();
size_t before_size = pair.second.BeforeFirst(':').length();
wxString new_search_str = "<b>" + pair.second.Mid(before_size + pos + 1, len) + "</b>";
wxString new_str = pair.second.Mid(0, before_size + pos + 1) + new_search_str + pair.second.Mid(before_size + pos + len + 1, wxString::npos);
search_found_list.push_back(std::make_pair(pair.first, new_str));
}
}
}
}

View File

@ -330,7 +330,9 @@ class ObjectDataViewModel :public wxDataViewModel
ObjectDataViewModelNode* m_plate_outside;
wxDataViewCtrl* m_ctrl { nullptr };
wxDataViewItemArray found_list;
std::vector<std::pair<ObjectDataViewModelNode*, wxString>> assembly_name_list;
std::vector<std::pair<ObjectDataViewModelNode*, wxString>> search_found_list;
public:
ObjectDataViewModel();
~ObjectDataViewModel();
@ -486,9 +488,12 @@ public:
// BBS
void UpdateItemNames();
void append_found_list(wxString current_search_text);
void append_found(wxString current_search_text, ObjectDataViewModelNode* item);
wxDataViewItemArray get_search_list() { return found_list; }
void assembly_name(ObjectDataViewModelNode* item, wxString name);
void assembly_name();
std::vector<std::pair<ObjectDataViewModelNode*, wxString>> get_assembly_name_list() { return assembly_name_list; }
void search_object(wxString search_text);
std::vector<std::pair<ObjectDataViewModelNode*, wxString>> get_found_list() { return search_found_list; }
void sys_color_changed();
private:

View File

@ -329,6 +329,7 @@ struct Sidebar::priv
ScalableButton* m_filament_icon = nullptr;
Button * m_flushing_volume_btn = nullptr;
wxSearchCtrl* m_search_bar = nullptr;
Search::SearchObjectDialog* dia = nullptr;
// BBS printer config
StaticBox* m_panel_printer_title = nullptr;
@ -355,9 +356,10 @@ struct Sidebar::priv
~priv();
void show_preset_comboboxes();
void on_search_enter();
void on_search_update();
void on_kill_focus();
void jump_to_object(ObjectDataViewModelNode* item);
void can_search();
#ifdef _WIN32
wxString btn_reslice_tip;
void show_rich_tip(const wxString& tooltip, wxButton* btn);
@ -397,21 +399,25 @@ void Sidebar::priv::show_preset_comboboxes()
scrolled->Refresh();
}
void Sidebar::priv::on_search_enter() {
m_object_list->search_object_list();
}
void Sidebar::priv::on_search_update() {
void Sidebar::priv::on_search_update()
{
m_object_list->assembly_plate_object_name();
wxString search_text = m_search_bar->GetValue();
m_object_list->set_found_list(search_text);
m_object_list->GetModel()->search_object(search_text);
dia->update_list();
}
void Sidebar::priv::on_kill_focus() {
m_object_list->searchbar_kill_focus();
void Sidebar::priv::jump_to_object(ObjectDataViewModelNode* item)
{
m_object_list->selected_object(item);
}
void Sidebar::priv::can_search()
{
if (m_search_bar->IsShown()) {
m_search_bar->SetFocus();
}
}
#ifdef _WIN32
@ -943,17 +949,21 @@ Sidebar::Sidebar(Plater *parent)
p->m_search_bar->ShowSearchButton(true);
p->m_search_bar->ShowCancelButton(true);
p->m_search_bar->SetDescriptiveText(_L("Search plate, object and part."));
p->m_search_bar->Bind(wxEVT_SET_FOCUS, [this](wxFocusEvent&) {
this->p->on_search_update();});
this->p->on_search_update();
wxPoint pos = this->p->m_search_bar->ClientToScreen(wxPoint(0, 0));
pos.y += this->p->m_search_bar->GetRect().height;
p->dia->SetPosition(pos);
p->dia->Popup();
});
p->m_search_bar->Bind(wxEVT_COMMAND_TEXT_UPDATED, [this](wxCommandEvent&) {
this->p->m_object_list->set_cur_pos(0);
this->p->on_search_update();
});
p->m_search_bar->Bind(wxEVT_KILL_FOCUS, [this](wxFocusEvent& e) {
this->p->on_kill_focus();
p->dia->Dismiss();
e.Skip();
});
p->m_search_bar->Bind(wxEVT_SEARCH, [this](wxCommandEvent&) {this->p->on_search_enter();});
p->m_object_list = new ObjectList(p->scrolled);
@ -964,6 +974,8 @@ Sidebar::Sidebar(Plater *parent)
p->m_search_bar->Hide();
// Frequently Object Settings
p->object_settings = new ObjectSettings(p->scrolled);
p->dia = new Search::SearchObjectDialog(p->m_object_list, p->m_search_bar);
#if !NEW_OBJECT_SETTING
p->object_settings->Hide();
p->sizer_params->Add(p->object_settings->get_sizer(), 0, wxEXPAND | wxTOP, 5 * em / 10);
@ -1852,7 +1864,15 @@ void Sidebar::auto_calc_flushing_volumes(const int modify_id) {
wxPostEvent(this, SimpleEvent(EVT_SCHEDULE_BACKGROUND_PROCESS, this));
}
// Plater::DropTarget
void Sidebar::jump_to_object(ObjectDataViewModelNode* item)
{
p->jump_to_object(item);
}
void Sidebar::can_search()
{
p->can_search();
}
class PlaterDropTarget : public wxFileDropTarget
{
@ -2588,6 +2608,12 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
q->Bind(EVT_SCHEDULE_BACKGROUND_PROCESS, [this](SimpleEvent&) { this->schedule_background_process(); });
// jump to found option from SearchDialog
q->Bind(wxCUSTOMEVT_JUMP_TO_OPTION, [this](wxCommandEvent& evt) { sidebar->jump_to_option(evt.GetInt()); });
q->Bind(wxCUSTOMEVT_JUMP_TO_OBJECT, [this](wxCommandEvent& evt) {
auto client_data = evt.GetClientData();
ObjectDataViewModelNode* data = static_cast<ObjectDataViewModelNode*>(client_data);
sidebar->jump_to_object(data);
}
);
}
wxGLCanvas* view3D_canvas = view3D->get_wxglcanvas();
@ -2761,11 +2787,11 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
q->Bind(EVT_GLCANVAS_PLATE_SELECT, &priv::on_plate_selected, this);
q->Bind(EVT_DOWNLOAD_PROJECT, &priv::on_action_download_project, this);
q->Bind(EVT_IMPORT_MODEL_ID, &priv::on_action_request_model_id, this);
q->Bind(EVT_PRINT_FINISHED, [q](wxCommandEvent &evt) { q->print_job_finished(evt); });
q->Bind(EVT_PRINT_FINISHED, [q](wxCommandEvent& evt) { q->print_job_finished(evt); });
q->Bind(EVT_SEND_CALIBRATION_FINISHED, [q](wxCommandEvent& evt) { q->send_calibration_job_finished(evt); });
q->Bind(EVT_SEND_FINISHED, [q](wxCommandEvent &evt) { q->send_job_finished(evt); });
q->Bind(EVT_PUBLISH_FINISHED, [q](wxCommandEvent &evt) { q->publish_job_finished(evt);});
q->Bind(EVT_OPEN_PLATESETTINGSDIALOG, [q](wxCommandEvent &evt) { q->open_platesettings_dialog(evt);});
q->Bind(EVT_SEND_FINISHED, [q](wxCommandEvent& evt) { q->send_job_finished(evt); });
q->Bind(EVT_PUBLISH_FINISHED, [q](wxCommandEvent& evt) { q->publish_job_finished(evt);});
q->Bind(EVT_OPEN_PLATESETTINGSDIALOG, [q](wxCommandEvent& evt) { q->open_platesettings_dialog(evt);});
//q->Bind(EVT_GLVIEWTOOLBAR_ASSEMBLE, [q](SimpleEvent&) { q->select_view_3D("Assemble"); });
}

View File

@ -163,6 +163,8 @@ public:
bool show_object_list(bool show) const;
void finish_param_edit();
void auto_calc_flushing_volumes(const int modify_id);
void jump_to_object(ObjectDataViewModelNode* item);
void can_search();
#ifdef _MSW_DARK_MODE
void show_mode_sizer(bool show);
#endif

View File

@ -28,6 +28,7 @@ namespace Slic3r {
wxDEFINE_EVENT(wxCUSTOMEVT_JUMP_TO_OPTION, wxCommandEvent);
wxDEFINE_EVENT(wxCUSTOMEVT_EXIT_SEARCH, wxCommandEvent);
wxDEFINE_EVENT(wxCUSTOMEVT_JUMP_TO_OBJECT, wxCommandEvent);
using GUI::from_u8;
using GUI::into_u8;
@ -394,10 +395,11 @@ void OptionsSearcher::add_key(const std::string &opt_key, Preset::Type type, con
// SearchItem
//------------------------------------------
SearchItem::SearchItem(wxWindow *parent, wxString text, int index, SearchDialog* sdialog)
SearchItem::SearchItem(wxWindow *parent, wxString text, int index, SearchDialog* sdialog, SearchObjectDialog* search_dialog)
: wxWindow(parent, wxID_ANY, wxDefaultPosition, wxSize(parent->GetSize().GetWidth(), 3 * GUI::wxGetApp().em_unit()))
{
m_sdialog = sdialog;
m_search_object_dialog = search_dialog;
m_text = text;
m_index = index;
@ -511,10 +513,19 @@ void SearchItem::on_mouse_left_up(wxMouseEvent &evt)
//if (m_sdialog->prevent_list_events) return;
// if (wxGetMouseState().LeftIsDown())
if (m_sdialog) {
m_sdialog->Die();
wxCommandEvent event(wxCUSTOMEVT_JUMP_TO_OPTION);
event.SetInt(m_index);
wxPostEvent(GUI::wxGetApp().plater(), event);
}
if (m_search_object_dialog) {
m_search_object_dialog->Dismiss();
wxCommandEvent event(wxCUSTOMEVT_JUMP_TO_OBJECT);
event.SetClientData(m_item);
wxPostEvent(GUI::wxGetApp().plater(), event);
}
}
//------------------------------------------
@ -894,6 +905,121 @@ void SearchListModel::GetValueByRow(wxVariant &variant, unsigned int row, unsign
}
}
SearchObjectDialog::SearchObjectDialog(GUI::ObjectList* object_list, wxWindow* parent)
: PopupWindow(parent, wxBORDER_NONE), m_object_list(object_list)
{
Freeze();
SetBackgroundColour(wxColour(238, 238, 238));
em = GUI::wxGetApp().em_unit();
m_text_color = wxColour(38, 46, 48);
m_bg_color = wxColour(255, 255, 255);
m_thumb_color = wxColour(196, 196, 196);
SetFont(GUI::wxGetApp().normal_font());
SetSizeHints(wxDefaultSize, wxDefaultSize);
m_sizer_border = new wxBoxSizer(wxVERTICAL);
m_sizer_main = new wxBoxSizer(wxVERTICAL);
m_sizer_body = new wxBoxSizer(wxVERTICAL);
// border
m_border_panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(POPUP_WIDTH * em, POPUP_HEIGHT * em), wxTAB_TRAVERSAL);
m_border_panel->SetBackgroundColour(m_bg_color);
// client
m_client_panel = new wxPanel(m_border_panel, wxID_ANY, wxDefaultPosition, wxSize(POPUP_WIDTH * em, POPUP_HEIGHT * em), wxTAB_TRAVERSAL);
m_client_panel->SetBackgroundColour(m_bg_color);
// scroll window
m_scrolledWindow = new ScrolledWindow(m_client_panel, wxID_ANY, wxDefaultPosition, wxSize(POPUP_WIDTH * em - (em + em / 2), POPUP_HEIGHT * em), wxVSCROLL, 6, 6);
m_scrolledWindow->SetMarginColor(m_bg_color);
m_scrolledWindow->SetScrollbarColor(m_thumb_color);
m_scrolledWindow->SetBackgroundColour(m_bg_color);
auto m_listsizer = new wxBoxSizer(wxVERTICAL);
auto m_listPanel = new wxWindow(m_scrolledWindow->GetPanel(), -1);
m_listPanel->SetBackgroundColour(m_bg_color);
m_listPanel->SetSize(wxSize(m_scrolledWindow->GetSize().GetWidth(), -1));
m_listPanel->SetSizer(m_listsizer);
m_listPanel->Fit();
m_scrolledWindow->SetScrollbars(1, 1, 0, m_listPanel->GetSize().GetHeight());
m_sizer_body->Add(m_scrolledWindow, 0, wxEXPAND | wxALL, em);
m_client_panel->SetSizer(m_sizer_body);
m_client_panel->Layout();
m_sizer_body->Fit(m_client_panel);
m_sizer_main->Add(m_client_panel, 1, wxEXPAND, 0);
m_border_panel->SetSizer(m_sizer_main);
m_border_panel->Layout();
m_sizer_border->Add(m_border_panel, 1, wxEXPAND | wxALL, 1);
SetSizer(m_sizer_border);
Layout();
m_sizer_border->Fit(this);
Thaw();
GUI::wxGetApp().UpdateDarkUIWin(this);
}
SearchObjectDialog::~SearchObjectDialog() {}
void SearchObjectDialog::Popup(wxPoint position /*= wxDefaultPosition*/)
{
update_list();
PopupWindow::Popup();
}
void SearchObjectDialog::Dismiss()
{
auto focus_window = this->GetParent()->HasFocus();
if (!focus_window)
PopupWindow::Dismiss();
}
void SearchObjectDialog::update_list()
{
#ifndef __WXGTK__
Freeze();
#endif
m_scrolledWindow->Destroy();
m_scrolledWindow = new ScrolledWindow(m_client_panel, wxID_ANY, wxDefaultPosition, wxSize(POPUP_WIDTH * em - (em + em / 2), POPUP_HEIGHT * em - em), wxVSCROLL, 6, 6);
m_scrolledWindow->SetMarginColor(StateColor::darkModeColorFor(m_bg_color));
m_scrolledWindow->SetScrollbarColor(StateColor::darkModeColorFor(m_thumb_color));
m_scrolledWindow->SetBackgroundColour(StateColor::darkModeColorFor(m_bg_color));
auto m_listsizer = new wxBoxSizer(wxVERTICAL);
auto m_listPanel = new wxWindow(m_scrolledWindow->GetPanel(), -1);
m_listPanel->SetBackgroundColour(StateColor::darkModeColorFor(m_bg_color));
m_listPanel->SetSize(wxSize(m_scrolledWindow->GetSize().GetWidth(), -1));
const std::vector<std::pair<GUI::ObjectDataViewModelNode*, wxString>>& found = m_object_list->GetModel()->get_found_list();
auto index = 0;
for (const auto& item : found) {
GUI::ObjectDataViewModelNode* data_item = item.first;
wxString data_str = item.second;
auto tmp = new SearchItem(m_listPanel, data_str, index, nullptr, this);
tmp->m_item = data_item;
m_listsizer->Add(tmp, 0, wxEXPAND, 0);
index++;
}
m_listPanel->SetSizer(m_listsizer);
m_listPanel->Fit();
m_scrolledWindow->SetScrollbars(1, 1, 0, m_listPanel->GetSize().GetHeight());
m_sizer_body->Add(m_scrolledWindow, 0, wxEXPAND | wxALL, em);
m_sizer_body->Fit(m_client_panel);
m_sizer_body->Layout();
#ifndef __WXGTK__
Thaw();
#endif
}
} // namespace Search
} // namespace Slic3r

View File

@ -14,6 +14,7 @@
#include <wx/checkbox.h>
#include <wx/dialog.h>
#include <wx/srchctrl.h>
#include "wxExtensions.hpp"
#include "GUI_Utils.hpp"
@ -21,12 +22,13 @@
#include "Widgets/ScrolledWindow.hpp"
#include "Widgets/TextInput.hpp"
#include "Widgets/PopupWindow.hpp"
#include "GUI_ObjectList.hpp"
namespace Slic3r {
wxDECLARE_EVENT(wxCUSTOMEVT_JUMP_TO_OPTION, wxCommandEvent);
wxDECLARE_EVENT(wxCUSTOMEVT_EXIT_SEARCH, wxCommandEvent);
wxDECLARE_EVENT(wxCUSTOMEVT_JUMP_TO_OBJECT, wxCommandEvent);
namespace Search {
@ -154,14 +156,17 @@ public:
// SearchDialog
//------------------------------------------
class SearchDialog;
class SearchObjectDialog;
class SearchItem : public wxWindow
{
public:
wxString m_text;
int m_index;
SearchDialog *m_sdialog;
SearchDialog* m_sdialog{ nullptr };
SearchObjectDialog* m_search_object_dialog{ nullptr };
GUI::ObjectDataViewModelNode* m_item{ nullptr };
SearchItem(wxWindow *parent, wxString text, int index, SearchDialog *sdialog);
SearchItem(wxWindow *parent, wxString text, int index, SearchDialog *sdialog = nullptr, SearchObjectDialog* search_dialog = nullptr);
~SearchItem(){};
wxSize DrawTextString(wxDC &dc, const wxString &text, const wxPoint &pt, bool bold);
@ -276,6 +281,40 @@ public:
bool SetValueByRow(const wxVariant &variant, unsigned int row, unsigned int col) override { return false; }
};
class SearchObjectDialog : public PopupWindow
{
public:
SearchObjectDialog(GUI::ObjectList* object_list, wxWindow* parent);
~SearchObjectDialog();
void Popup(wxPoint position = wxDefaultPosition);
void Dismiss();
void update_list();
public:
GUI::ObjectList* m_object_list{ nullptr };
int em;
const int POPUP_WIDTH = 41;
const int POPUP_HEIGHT = 45;
ScrolledWindow* m_scrolledWindow{ nullptr };
wxColour m_text_color;
wxColour m_bg_color;
wxColour m_thumb_color;
wxColour m_bold_color;
wxBoxSizer* m_sizer_body{ nullptr };
wxBoxSizer* m_sizer_main{ nullptr };
wxBoxSizer* m_sizer_border{ nullptr };
wxWindow* m_border_panel{ nullptr };
wxWindow* m_client_panel{ nullptr };
wxWindow* m_listPanel{ nullptr };
};
} // namespace Search
} // namespace Slic3r