mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-07 15:51:08 +00:00
151 using-directives, using-declarations, type aliases and namespace aliases in source and test files that nothing refers to: the name is never used, it duplicates a using already in scope, or the code sits inside the namespace it names. Each one was removed on its own and the file still compiled, both as it is and with every header-level using taken away, so none of them was only redundant because a header leaks the same name. With the using gone, 28 #include lines and one forward declaration had no other reference left in their file (boost/optional.hpp without any optional, property_tree headers without any ptree) and go with it. No header is touched.
905 lines
30 KiB
C++
905 lines
30 KiB
C++
#include "Search.hpp"
|
|
|
|
#include "slic3r/GUI/GUI.hpp"
|
|
#include "libslic3r/Preset.hpp"
|
|
#include "libslic3r/Config.hpp"
|
|
#include <cstdint>
|
|
#include "slic3r/GUI/SettingsIndex.hpp"
|
|
#include <boost/algorithm/string/trim.hpp>
|
|
#include <limits>
|
|
#include <utility>
|
|
#include <boost/algorithm/string/replace.hpp>
|
|
#include <cassert>
|
|
#include "slic3r/GUI/Widgets/TextInput.hpp"
|
|
#include "slic3r/GUI/Widgets/StateColor.hpp"
|
|
#include <map>
|
|
#include "slic3r/GUI/Widgets/PopupWindow.hpp"
|
|
#include "slic3r/GUI/Widgets/ScrolledWindow.hpp"
|
|
#include <tuple>
|
|
#include "slic3r/GUI/ObjectDataViewModel.hpp"
|
|
#include <vector>
|
|
#include <cstddef>
|
|
#include <string>
|
|
#include <boost/algorithm/string.hpp>
|
|
#include <boost/nowide/convert.hpp>
|
|
#include <wx/event.h>
|
|
#include <wx/gdicmn.h>
|
|
#include <wx/colour.h>
|
|
#include <wx/dc.h>
|
|
#include <wx/dcclient.h>
|
|
#include <wx/popupwin.h>
|
|
#include <wx/sizer.h>
|
|
#include <wx/panel.h>
|
|
#include <wx/string.h>
|
|
#include <wx/window.h>
|
|
#include <wx/variant.h>
|
|
#include <wx/debug.h>
|
|
|
|
#include "wx/dataview.h"
|
|
#include "wx/numformatter.h"
|
|
#include "Widgets/Label.hpp"
|
|
|
|
#include "libslic3r/PrintConfig.hpp"
|
|
#include "libslic3r/PresetBundle.hpp"
|
|
#include "GUI_App.hpp"
|
|
#include "Plater.hpp"
|
|
#include "Tab.hpp"
|
|
|
|
#define FTS_FUZZY_MATCH_IMPLEMENTATION
|
|
#include "fts_fuzzy_match.h"
|
|
|
|
#include "imgui/imconfig.h"
|
|
|
|
#if defined(__WXGTK__)
|
|
#include "LinuxDisplayBackend.hpp"
|
|
#endif
|
|
|
|
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;
|
|
|
|
namespace {
|
|
|
|
bool focus_left_popup(wxWindow* popup, wxWindow* focus_window, wxWindow* related_window_1 = nullptr,
|
|
wxWindow* related_window_2 = nullptr, wxWindow* related_window_3 = nullptr)
|
|
{
|
|
return focus_window != popup && !popup->IsDescendant(focus_window) && focus_window != related_window_1 &&
|
|
focus_window != related_window_2 && focus_window != related_window_3;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace Search {
|
|
|
|
static char marker_by_type(Preset::Type type, PrinterTechnology pt)
|
|
{
|
|
switch (type) {
|
|
case Preset::TYPE_PRINT:
|
|
case Preset::TYPE_SLA_PRINT: return ImGui::PrintIconMarker;
|
|
case Preset::TYPE_FILAMENT: return ImGui::FilamentIconMarker;
|
|
case Preset::TYPE_SLA_MATERIAL: return ImGui::MaterialIconMarker;
|
|
case Preset::TYPE_PRINTER: return pt == ptSLA ? ImGui::PrinterSlaIconMarker : ImGui::PrinterIconMarker;
|
|
default: return ' ';
|
|
}
|
|
}
|
|
|
|
void FoundOption::get_marked_label_and_tooltip(const char **label_, const char **tooltip_) const
|
|
{
|
|
*label_ = marked_label.c_str();
|
|
*tooltip_ = tooltip.c_str();
|
|
}
|
|
|
|
// Mark a string using ColorMarkerStart and ColorMarkerEnd symbols
|
|
static std::wstring mark_string(const std::wstring &str, const std::vector<uint16_t> &matches, Preset::Type type, PrinterTechnology pt)
|
|
{
|
|
std::wstring out;
|
|
out += marker_by_type(type, pt);
|
|
if (matches.empty())
|
|
out += str;
|
|
else {
|
|
out.reserve(str.size() * 2);
|
|
if (matches.front() > 0) out += str.substr(0, matches.front());
|
|
for (size_t i = 0;;) {
|
|
// Find the longest string of successive indices.
|
|
size_t j = i + 1;
|
|
while (j < matches.size() && matches[j] == matches[j - 1] + 1) ++j;
|
|
out += ImGui::ColorMarkerStart;
|
|
out += str.substr(matches[i], matches[j - 1] - matches[i] + 1);
|
|
out += ImGui::ColorMarkerEnd;
|
|
if (j == matches.size()) {
|
|
out += str.substr(matches[j - 1] + 1);
|
|
break;
|
|
}
|
|
out += str.substr(matches[j - 1] + 1, matches[j] - matches[j - 1] - 1);
|
|
i = j;
|
|
}
|
|
}
|
|
return out;
|
|
}
|
|
|
|
bool OptionsSearcher::search() { return search(search_line, true); }
|
|
|
|
static bool fuzzy_match(const std::wstring &search_pattern, const std::wstring &label, int &out_score, std::vector<uint16_t> &out_matches)
|
|
{
|
|
uint16_t matches[fts::max_matches + 1]; // +1 for the stopper
|
|
int score;
|
|
if (fts::fuzzy_match(search_pattern.c_str(), label.c_str(), score, matches)) {
|
|
size_t cnt = 0;
|
|
for (; matches[cnt] != fts::stopper; ++cnt)
|
|
;
|
|
out_matches.assign(matches, matches + cnt);
|
|
out_score = score;
|
|
return true;
|
|
} else
|
|
return false;
|
|
}
|
|
|
|
bool OptionsSearcher::search(const std::string &search, bool force /* = false*/, Preset::Type type/* = Preset::TYPE_INVALID*/)
|
|
{
|
|
if (search_line == search && search_type == type && !force) return false;
|
|
|
|
found.clear();
|
|
|
|
bool full_list = search.empty();
|
|
wxString sep = L" : ";
|
|
|
|
auto get_label = [this, &sep](const Option &opt, bool marked = true) {
|
|
std::wstring out;
|
|
if (marked) out += marker_by_type(opt.type, printer_technology);
|
|
const std::wstring *prev = nullptr;
|
|
for (const std::wstring *const s : {view_params.category || opt.multi_category ? &opt.category_local : nullptr, &opt.group_local, &opt.label_local})
|
|
if (s != nullptr && (prev == nullptr || *prev != *s)) {
|
|
if (out.size() > 2) out += sep;
|
|
out += *s;
|
|
prev = s;
|
|
}
|
|
return out;
|
|
};
|
|
|
|
auto get_label_english = [this, &sep](const Option &opt, bool marked = true) {
|
|
std::wstring out;
|
|
if (marked) out += marker_by_type(opt.type, printer_technology);
|
|
const std::wstring *prev = nullptr;
|
|
for (const std::wstring *const s : {view_params.category || opt.multi_category ? &opt.category : nullptr, &opt.group, &opt.label})
|
|
if (s != nullptr && (prev == nullptr || *prev != *s)) {
|
|
if (out.size() > 2) out += sep;
|
|
out += *s;
|
|
prev = s;
|
|
}
|
|
return out;
|
|
};
|
|
|
|
auto get_tooltip = [this, &sep](const Option &opt) {
|
|
return wxString(marker_by_type(opt.type, printer_technology)) + opt.category_local + sep + opt.group_local + sep + opt.label_local;
|
|
};
|
|
|
|
std::vector<uint16_t> matches, matches2;
|
|
const std::vector<Option> &options = m_index.options();
|
|
for (size_t i = 0; i < options.size(); i++) {
|
|
const Option &opt = options[i];
|
|
if (full_list) {
|
|
std::string label = into_u8(get_label(opt));
|
|
//all
|
|
if (type == Preset::TYPE_INVALID) {
|
|
found.emplace_back(FoundOption{label, label, into_u8(get_tooltip(opt)), i, 0});
|
|
} else if (type == opt.type){
|
|
found.emplace_back(FoundOption{label, label, into_u8(get_tooltip(opt)), i, 0});
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
std::wstring wsearch = boost::nowide::widen(search);
|
|
boost::trim_left(wsearch);
|
|
std::wstring label = get_label(opt, false);
|
|
std::wstring label_english = get_label_english(opt, false);
|
|
int score = std::numeric_limits<int>::min();
|
|
int score2;
|
|
matches.clear();
|
|
fuzzy_match(wsearch, label, score, matches);
|
|
// bbs hide the contents in parentheses
|
|
/* if (fuzzy_match(wsearch, opt.key, score2, matches2) && score2 > score) {
|
|
for (fts::pos_type &pos : matches2) pos += label.size() + 1;
|
|
label += L"(" + opt.key + L")";
|
|
append(matches, matches2);
|
|
score = score2;
|
|
}*/
|
|
if (view_params.english && fuzzy_match(wsearch, label_english, score2, matches2) && score2 > score) {
|
|
label = std::move(label_english);
|
|
matches = std::move(matches2);
|
|
score = score2;
|
|
}
|
|
if (score > 90 /*std::numeric_limits<int>::min()*/) {
|
|
label = mark_string(label, matches, opt.type, printer_technology);
|
|
//label += L" [" + std::to_wstring(score) + L"]"; // add score value
|
|
std::string label_u8 = into_u8(label);
|
|
std::string label_plain = label_u8;
|
|
|
|
#ifdef SUPPORTS_MARKUP
|
|
boost::replace_all(label_plain, std::string(1, char(ImGui::ColorMarkerStart)), "<b>");
|
|
boost::replace_all(label_plain, std::string(1, char(ImGui::ColorMarkerEnd)), "</b>");
|
|
#else
|
|
boost::erase_all(label_plain, std::string(1, char(ImGui::ColorMarkerStart)));
|
|
boost::erase_all(label_plain, std::string(1, char(ImGui::ColorMarkerEnd)));
|
|
#endif
|
|
|
|
if (type == Preset::TYPE_INVALID) {
|
|
found.emplace_back(FoundOption{label_plain, label_u8, into_u8(get_tooltip(opt)), i, score});
|
|
} else if (type == opt.type) {
|
|
found.emplace_back(FoundOption{label_plain, label_u8, into_u8(get_tooltip(opt)), i, score});
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if (!full_list) sort_found();
|
|
|
|
if (search_line != search) search_line = search;
|
|
if (search_type != type) search_type = type;
|
|
|
|
return true;
|
|
}
|
|
|
|
OptionsSearcher::OptionsSearcher() {}
|
|
|
|
OptionsSearcher::~OptionsSearcher() {}
|
|
|
|
void OptionsSearcher::init(std::vector<InputInfo> input_values)
|
|
{
|
|
m_index.init(std::move(input_values));
|
|
|
|
search(search_line, true, search_type);
|
|
}
|
|
|
|
void OptionsSearcher::apply(DynamicPrintConfig *config, Preset::Type type, ConfigOptionMode mode)
|
|
{
|
|
if (m_index.apply(config, type, mode))
|
|
search(search_line, true, search_type);
|
|
}
|
|
|
|
const Option &OptionsSearcher::get_option(size_t pos_in_filter) const
|
|
{
|
|
assert(pos_in_filter != size_t(-1) && found[pos_in_filter].option_idx != size_t(-1));
|
|
return m_index.option_at(found[pos_in_filter].option_idx);
|
|
}
|
|
|
|
void OptionsSearcher::show_dialog(Preset::Type type, wxWindow *parent, TextInput *input, wxWindow* ssearch_btn)
|
|
{
|
|
if (parent == nullptr || input == nullptr) return;
|
|
auto search_dialog = new SearchDialog(this, type, parent, input, ssearch_btn);
|
|
wxPoint pos = input->GetParent()->ClientToScreen(wxPoint(0, 0));
|
|
#ifndef __WXGTK__
|
|
pos.y += input->GetParent()->GetRect().height;
|
|
#else
|
|
input->GetParent()->Hide();
|
|
#endif
|
|
search_dialog->SetPosition(pos);
|
|
search_dialog->Popup();
|
|
}
|
|
|
|
void OptionsSearcher::dlg_sys_color_changed()
|
|
{
|
|
/*if (search_dialog)
|
|
search_dialog->on_sys_color_changed();*/
|
|
}
|
|
|
|
void OptionsSearcher::dlg_msw_rescale()
|
|
{
|
|
if (search_dialog) search_dialog->msw_rescale();
|
|
}
|
|
//------------------------------------------
|
|
// SearchItem
|
|
//------------------------------------------
|
|
|
|
SearchItem::SearchItem(wxWindow *parent, wxString text, int index, SearchDialog* sdialog, SearchObjectDialog* search_dialog, wxString tooltip)
|
|
: 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;
|
|
|
|
this->SetToolTip(tooltip);
|
|
|
|
SetBackgroundColour(StateColor::darkModeColorFor(wxColour("#FFFFFF")));
|
|
Bind(wxEVT_ENTER_WINDOW, &SearchItem::on_mouse_enter, this);
|
|
Bind(wxEVT_LEAVE_WINDOW, &SearchItem::on_mouse_leave, this);
|
|
Bind(wxEVT_LEFT_DOWN, &SearchItem::on_mouse_left_down, this);
|
|
Bind(wxEVT_LEFT_UP, &SearchItem::on_mouse_left_up, this);
|
|
Bind(wxEVT_PAINT, &SearchItem::OnPaint, this);
|
|
}
|
|
|
|
wxSize SearchItem::DrawTextString(wxDC &dc, const wxString &text, const wxPoint &pt, bool bold)
|
|
{
|
|
if (bold) {
|
|
dc.SetFont(Label::Head_14);
|
|
} else {
|
|
dc.SetFont(Label::Body_14);
|
|
}
|
|
|
|
dc.SetBackgroundMode(wxTRANSPARENT);
|
|
dc.SetTextForeground(StateColor::darkModeColorFor(wxColour("#323A3C")));
|
|
dc.DrawText(text, pt);
|
|
return dc.GetTextExtent(text);
|
|
}
|
|
|
|
void SearchItem::OnPaint(wxPaintEvent &event)
|
|
{
|
|
wxPaintDC dc(this);
|
|
auto top = 5;
|
|
int left = 20;
|
|
|
|
auto bold_pair = std::vector<std::pair<int, int>>();
|
|
|
|
auto index = 0;
|
|
|
|
auto b_first_list = std::vector<int>();
|
|
auto b_second_list = std::vector<int>();
|
|
|
|
auto position = 0;
|
|
while ((position = m_text.find("<b>", position)) != wxString::npos) {
|
|
b_first_list.push_back(position);
|
|
position++;
|
|
}
|
|
|
|
position = 0;
|
|
while ((position = m_text.find("</b>", position)) != wxString::npos) {
|
|
b_second_list.push_back(position + 3);
|
|
position++;
|
|
}
|
|
|
|
if (b_first_list.size() != b_second_list.size()) { return; }
|
|
|
|
for (auto i = 0; i < b_first_list.size(); i++) {
|
|
auto pair = std::make_pair(b_first_list[i], b_second_list[i]);
|
|
bold_pair.push_back(pair);
|
|
}
|
|
|
|
//DrawTextString(dc, m_text, wxPoint(left, top), false);
|
|
/*if (bold_pair.size() <= 0) {
|
|
DrawTextString(dc, m_text, wxPoint(left, top), false);
|
|
} else {
|
|
auto index = 0;
|
|
for (auto i = 0; i < bold_pair.size(); i++) { DrawTextString(dc, m_text.SubString(index, bold_pair[i].second), wxPoint(left, top), true); }
|
|
}*/
|
|
auto str = wxString("");
|
|
for (auto c = 0; c < m_text.length(); c++) {
|
|
str = m_text[c];
|
|
|
|
auto inset = false;
|
|
auto pair_index = 0;
|
|
for (auto o = 0; o < bold_pair.size(); o++) {
|
|
if (c >= bold_pair[o].first && c <= bold_pair[o].second) {
|
|
pair_index = o;
|
|
inset = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!inset) {
|
|
left += DrawTextString(dc, str, wxPoint(left, top), false).GetWidth();
|
|
} else {
|
|
//str = str.erase(bold_pair[pair_index].first, 3);
|
|
//str = str.erase(bold_pair[pair_index].second, 4);
|
|
if (c - bold_pair[pair_index].first >= 3 && bold_pair[pair_index].second - c > 3) {
|
|
left += DrawTextString(dc, str, wxPoint(left, top), true).GetWidth();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SearchItem::on_mouse_enter(wxMouseEvent &evt)
|
|
{
|
|
SetBackgroundColour(StateColor::darkModeColorFor(wxColour("#BFE1DE"))); // ORCA color with %25 opacity
|
|
Refresh();
|
|
}
|
|
|
|
void SearchItem::on_mouse_leave(wxMouseEvent &evt)
|
|
{
|
|
SetBackgroundColour(StateColor::darkModeColorFor(wxColour(255, 255, 255)));
|
|
Refresh();
|
|
}
|
|
|
|
void SearchItem::on_mouse_left_down(wxMouseEvent &evt)
|
|
{
|
|
SetBackgroundColour(StateColor::darkModeColorFor(wxColour("#BFE1DE"))); // ORCA color with %25 opacity
|
|
Refresh();
|
|
}
|
|
|
|
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->Die();
|
|
wxCommandEvent event(wxCUSTOMEVT_JUMP_TO_OBJECT);
|
|
event.SetClientData(m_item);
|
|
wxPostEvent(GUI::wxGetApp().plater(), event);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------
|
|
// SearchDialog
|
|
//------------------------------------------
|
|
|
|
static const std::map<const char, int> icon_idxs = {
|
|
{ImGui::PrintIconMarker, 0}, {ImGui::PrinterIconMarker, 1}, {ImGui::PrinterSlaIconMarker, 2}, {ImGui::FilamentIconMarker, 3}, {ImGui::MaterialIconMarker, 4},
|
|
};
|
|
|
|
SearchDialog::SearchDialog(OptionsSearcher *searcher, Preset::Type type, wxWindow *parent, TextInput *input, wxWindow *search_btn)
|
|
: PopupWindow(parent, wxBORDER_NONE | wxPU_CONTAINS_CONTROLS), searcher(searcher)
|
|
{
|
|
m_event_tag = parent;
|
|
search_line = input;
|
|
search_type = type;
|
|
|
|
m_search_item_tag = search_btn;
|
|
|
|
// set border color
|
|
Freeze();
|
|
SetBackgroundColour(wxColour(238, 238, 238));
|
|
|
|
em = GUI::wxGetApp().em_unit();
|
|
|
|
m_bg_colour = 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_colour);
|
|
|
|
// 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_colour);
|
|
|
|
// search line
|
|
//search_line = new wxTextCtrl(m_client_panel, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER);
|
|
#ifdef __WXGTK__
|
|
search_line = new TextInput(m_client_panel, wxEmptyString, wxEmptyString, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0);
|
|
search_line->SetBackgroundColour(wxColour(238, 238, 238));
|
|
search_line->SetForegroundColour(wxColour(43, 52, 54));
|
|
search_line->SetFont(GUI::wxGetApp().bold_font());
|
|
#endif
|
|
|
|
search_line->Bind(wxEVT_TEXT, &SearchDialog::OnInputText, this);
|
|
search_line->Bind(wxEVT_LEFT_UP, &SearchDialog::OnLeftUpInTextCtrl, this);
|
|
search_line2 = search_line->GetTextCtrl();
|
|
|
|
// 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_colour);
|
|
m_scrolledWindow->SetScrollbarColor(m_thumb_color);
|
|
m_scrolledWindow->SetBackgroundColour(m_bg_colour);
|
|
|
|
auto m_listsizer = new wxBoxSizer(wxVERTICAL);
|
|
auto m_listPanel = new wxWindow(m_scrolledWindow->GetPanel(), -1);
|
|
m_listPanel->SetBackgroundColour(m_bg_colour);
|
|
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());
|
|
|
|
#ifdef __WXGTK__
|
|
m_sizer_body->Add(search_line, 0, wxEXPAND | wxALL, em / 2);
|
|
search_line = input;
|
|
#endif
|
|
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();
|
|
|
|
// check_category->Bind(wxEVT_CHECKBOX, &SearchDialog::OnCheck, this);
|
|
// Bind(wxEVT_MOTION, &SearchDialog::OnMotion, this);
|
|
// Bind(wxEVT_LEFT_DOWN, &SearchDialog::OnLeftDown, this);
|
|
|
|
// SetSizer(topSizer);
|
|
// topSizer->SetSizeHints(this);
|
|
GUI::wxGetApp().UpdateDarkUIWin(this);
|
|
}
|
|
|
|
SearchDialog::~SearchDialog() {}
|
|
|
|
void SearchDialog::Popup(wxWindow *focus /*= nullptr*/)
|
|
{
|
|
/* const std::string& line = searcher->search_string();
|
|
search_line->SetValue(line.empty() ? default_string : from_u8(line));
|
|
|
|
update_list();
|
|
|
|
const OptionViewParameters& params = searcher->view_params;
|
|
check_category->SetValue(params.category);*/
|
|
|
|
//const std::string &line = searcher->search_string();
|
|
//search_line->SetValue(line.empty() ? default_string : from_u8(line));
|
|
search_line2->SetValue(wxString(""));
|
|
//const std::string &line = searcher->search_string();
|
|
//searcher->search(into_u8(line), true);
|
|
PopupWindow::Popup(focus);
|
|
search_line2->SetFocus();
|
|
update_list();
|
|
}
|
|
|
|
|
|
#ifdef __WXMSW__
|
|
void SearchDialog::MSWDismissUnfocusedPopup()
|
|
{
|
|
Dismiss();
|
|
OnDismiss();
|
|
}
|
|
#endif // __WXMSW__
|
|
|
|
void SearchDialog::OnDismiss() { }
|
|
|
|
void SearchDialog::Dismiss()
|
|
{
|
|
auto focus_window = wxWindow::FindFocus();
|
|
if (!focus_window) {
|
|
Die();
|
|
return;
|
|
}
|
|
#if defined(__WXGTK__)
|
|
// On Wayland, wxGetMousePosition() returns unreliable global coords.
|
|
// Rely on focus tracking instead: if focus moved to a window outside
|
|
// this dialog and its related controls, dismiss.
|
|
if (Slic3r::GUI::is_running_on_wayland()) {
|
|
if (focus_left_popup(this, focus_window, m_event_tag, m_search_item_tag, search_line)) {
|
|
Die();
|
|
}
|
|
return;
|
|
}
|
|
#endif
|
|
auto pos = wxGetMousePosition();
|
|
if (!m_event_tag->GetScreenRect().Contains(pos) && !this->GetScreenRect().Contains(pos) && !m_search_item_tag->GetScreenRect().Contains(pos)) {
|
|
Die();
|
|
}
|
|
}
|
|
|
|
void SearchDialog::Die()
|
|
{
|
|
PopupWindow::Dismiss();
|
|
wxCommandEvent event(wxCUSTOMEVT_EXIT_SEARCH);
|
|
wxPostEvent(search_line, event);
|
|
}
|
|
|
|
void SearchDialog::OnInputText(wxCommandEvent &)
|
|
{
|
|
search_line2->SetInsertionPointEnd();
|
|
wxString input_string = search_line2->GetValue();
|
|
if (input_string == wxEmptyString) input_string.Clear();
|
|
searcher->search(into_u8(input_string), true, search_type);
|
|
update_list();
|
|
}
|
|
|
|
void SearchDialog::OnLeftUpInTextCtrl(wxEvent &event)
|
|
{
|
|
if (search_line2->GetValue() == wxEmptyString) search_line2->SetValue("");
|
|
event.Skip();
|
|
}
|
|
|
|
void SearchDialog::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_colour));
|
|
m_scrolledWindow->SetScrollbarColor(StateColor::darkModeColorFor(m_thumb_color));
|
|
m_scrolledWindow->SetBackgroundColour(StateColor::darkModeColorFor(m_bg_colour));
|
|
|
|
auto m_listsizer = new wxBoxSizer(wxVERTICAL);
|
|
auto m_listPanel = new wxWindow(m_scrolledWindow->GetPanel(), -1);
|
|
m_listPanel->SetBackgroundColour(StateColor::darkModeColorFor(m_bg_colour));
|
|
m_listPanel->SetSize(wxSize(m_scrolledWindow->GetSize().GetWidth(), -1));
|
|
|
|
const std::vector<FoundOption> &filters = searcher->found_options();
|
|
auto index = 0;
|
|
for (const FoundOption &item : filters) {
|
|
wxString str = from_u8(item.label).Remove(0, 1);
|
|
auto tmp = new SearchItem(m_listPanel, str, index, this);
|
|
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
|
|
}
|
|
|
|
void SearchDialog::msw_rescale()
|
|
{
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// SearchListModel
|
|
// ----------------------------------------------------------------------------
|
|
|
|
SearchListModel::SearchListModel(wxWindow *parent) : wxDataViewVirtualListModel(0)
|
|
{
|
|
int icon_id = 0;
|
|
for (const std::string icon : {"cog", "printer", "printer", "spool", "blank_16"}) m_icon[icon_id++] = ScalableBitmap(parent, icon);
|
|
}
|
|
|
|
void SearchListModel::Clear()
|
|
{
|
|
m_values.clear();
|
|
Reset(0);
|
|
}
|
|
|
|
void SearchListModel::Prepend(const std::string &label)
|
|
{
|
|
const char icon_c = label.at(0);
|
|
int icon_idx = icon_idxs.at(icon_c);
|
|
wxString str = from_u8(label).Remove(0, 1);
|
|
|
|
m_values.emplace_back(str, icon_idx);
|
|
|
|
RowPrepended();
|
|
}
|
|
|
|
void SearchListModel::msw_rescale()
|
|
{
|
|
for (ScalableBitmap &bmp : m_icon) bmp.msw_rescale();
|
|
}
|
|
|
|
wxString SearchListModel::GetColumnType(unsigned int col) const
|
|
{
|
|
if (col == colIcon) return "wxBitmap";
|
|
return "string";
|
|
}
|
|
|
|
void SearchListModel::GetValueByRow(wxVariant &variant, unsigned int row, unsigned int col) const
|
|
{
|
|
switch (col) {
|
|
case colIcon: variant << m_icon[m_values[row].second].bmp(); break;
|
|
case colMarkedText: variant = m_values[row].first; break;
|
|
case colMax: wxFAIL_MSG("invalid column");
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
SearchObjectDialog::SearchObjectDialog(GUI::ObjectList* object_list, wxWindow* parent, TextInput* input)
|
|
: PopupWindow(parent, wxBORDER_NONE | wxPU_CONTAINS_CONTROLS), m_object_list(object_list)
|
|
{
|
|
search_line = input;
|
|
|
|
Freeze();
|
|
SetBackgroundColour(wxColour(238, 238, 238));
|
|
|
|
em = GUI::wxGetApp().em_unit();
|
|
|
|
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);
|
|
|
|
// search line
|
|
#ifdef __WXGTK__
|
|
search_line = new TextInput(m_client_panel, wxEmptyString, wxEmptyString, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0);
|
|
search_line->SetBackgroundColour(wxColour(238, 238, 238));
|
|
search_line->SetForegroundColour(wxColour(43, 52, 54));
|
|
search_line->SetFont(GUI::wxGetApp().bold_font());
|
|
#endif
|
|
|
|
search_line->Bind(wxEVT_TEXT, &SearchObjectDialog::OnInputText, this);
|
|
search_line->Bind(wxEVT_LEFT_UP, &SearchObjectDialog::OnLeftUpInTextCtrl, this);
|
|
search_line2 = search_line->GetTextCtrl();
|
|
|
|
|
|
// 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());
|
|
|
|
#ifdef __WXGTK__
|
|
m_sizer_body->Add(search_line, 0, wxEXPAND | wxALL, em / 2);
|
|
search_line = input;
|
|
#endif
|
|
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(wxWindow *focus /*= nullptr*/)
|
|
{
|
|
if (m_is_dismissing || this->IsShown()) {
|
|
return;
|
|
}
|
|
search_line2->SetValue(wxString(""));
|
|
#ifdef __WXOSX__
|
|
// On macOS we need to remove the focus from the text input before popping up the
|
|
// dropdown list, otherwise the text input won't be usable
|
|
m_object_list->SetFocus();
|
|
#endif
|
|
PopupWindow::Popup(focus);
|
|
search_line2->SetFocus();
|
|
|
|
m_object_list->assembly_plate_object_name();
|
|
m_object_list->GetModel()->search_object(wxEmptyString);
|
|
update_list();
|
|
}
|
|
|
|
#ifdef __WXMSW__
|
|
void SearchObjectDialog::MSWDismissUnfocusedPopup()
|
|
{
|
|
Dismiss();
|
|
OnDismiss();
|
|
}
|
|
#endif // __WXMSW__
|
|
|
|
void SearchObjectDialog::OnDismiss() {}
|
|
|
|
void SearchObjectDialog::Dismiss()
|
|
{
|
|
auto focus_window = wxWindow::FindFocus();
|
|
if (!focus_window) {
|
|
Die();
|
|
return;
|
|
}
|
|
#if defined(__WXGTK__)
|
|
// On Wayland, wxGetMousePosition() returns unreliable global coords.
|
|
// Rely on focus tracking instead: if focus moved to a window outside
|
|
// this dialog and its related controls, dismiss.
|
|
if (Slic3r::GUI::is_running_on_wayland()) {
|
|
if (focus_left_popup(this, focus_window, search_line)) {
|
|
Die();
|
|
}
|
|
return;
|
|
}
|
|
#endif
|
|
auto pos = wxGetMousePosition();
|
|
if (!search_line->GetScreenRect().Contains(pos) && !this->GetScreenRect().Contains(pos)) {
|
|
Die();
|
|
}
|
|
}
|
|
|
|
void SearchObjectDialog::Die()
|
|
{
|
|
m_is_dismissing = true;
|
|
m_object_list->SetFocus();
|
|
PopupWindow::Dismiss();
|
|
wxCommandEvent event(wxCUSTOMEVT_EXIT_SEARCH);
|
|
wxPostEvent(m_object_list, event);
|
|
m_is_dismissing = false;
|
|
}
|
|
|
|
void SearchObjectDialog::OnInputText(wxCommandEvent&)
|
|
{
|
|
search_line2->SetInsertionPointEnd();
|
|
wxString input_string = search_line2->GetValue();
|
|
if (input_string == wxEmptyString)
|
|
input_string.Clear();
|
|
|
|
m_object_list->assembly_plate_object_name();
|
|
m_object_list->GetModel()->search_object(input_string);
|
|
|
|
update_list();
|
|
}
|
|
|
|
void SearchObjectDialog::OnLeftUpInTextCtrl(wxEvent& event)
|
|
{
|
|
if (search_line2->GetValue() == wxEmptyString)
|
|
search_line2->SetValue("");
|
|
event.Skip();
|
|
}
|
|
|
|
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::tuple<GUI::ObjectDataViewModelNode*, wxString, wxString>>& found = m_object_list->GetModel()->get_found_list();
|
|
auto index = 0;
|
|
for (const auto& [model_node, name, tip] : found) {
|
|
auto tmp = new SearchItem(m_listPanel, name, index, nullptr, this, tip);
|
|
tmp->m_item = model_node;
|
|
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
|