Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_gcode_viewer
This commit is contained in:
commit
813e268d7e
12
resources/icons/attention.svg
Normal file
12
resources/icons/attention.svg
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
|
||||
<g id="attention">
|
||||
<path fill="#ED0000" d="M8,1.85l5.29,3.53V7v3.62L8,14.15l-5.29-3.53V7V5.38L8,1.85 M8,1L2,5v2v4l6,4l6-4V7V5L8,1L8,1z"/>
|
||||
|
||||
<path fill="none" stroke="#ED0000" stroke-linecap="round" stroke-width="3" d="M8 4 L8 8" />
|
||||
|
||||
<circle fill="#ED0000" cx="8" cy="12" r="1.5"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 641 B |
16
resources/icons/collapse.svg
Normal file
16
resources/icons/collapse.svg
Normal file
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
|
||||
<g id="cross">
|
||||
<g><line fill="none" stroke="#ED6B21" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="12" y1="1" x2="15" y2="4"/></g>
|
||||
<g><line fill="none" stroke="#ED6B21" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="12" y1="7" x2="15" y2="4"/></g>
|
||||
<g><line fill="none" stroke="#ED6B21" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="8" y1="1" x2="11" y2="4"/></g>
|
||||
<g><line fill="none" stroke="#ED6B21" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="8" y1="7" x2="11" y2="4"/></g>
|
||||
|
||||
<g><line fill="none" stroke="#ED6B21" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="4" y1="9" x2="1" y2="12"/></g>
|
||||
<g><line fill="none" stroke="#ED6B21" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="4" y1="15" x2="1" y2="12"/></g>
|
||||
<g><line fill="none" stroke="#ED6B21" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="8" y1="9" x2="5" y2="12"/></g>
|
||||
<g><line fill="none" stroke="#ED6B21" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" x1="8" y1="15" x2="5" y2="12"/></g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
4
resources/icons/search.svg
Normal file
4
resources/icons/search.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24px" height="24px">
|
||||
<path fill="#808080" d="M 13.261719 14.867188 L 15.742188 17.347656 C 15.363281 18.070313 15.324219 18.789063 15.722656 19.1875 L 20.25 23.714844 C 20.820313 24.285156 22.0625 23.972656 23.015625 23.015625 C 23.972656 22.058594 24.285156 20.820313 23.714844 20.25 L 19.191406 15.722656 C 18.789063 15.324219 18.070313 15.363281 17.347656 15.738281 L 14.867188 13.261719 Z M 8.5 0 C 3.804688 0 0 3.804688 0 8.5 C 0 13.195313 3.804688 17 8.5 17 C 13.195313 17 17 13.195313 17 8.5 C 17 3.804688 13.195313 0 8.5 0 Z M 8.5 15 C 4.910156 15 2 12.089844 2 8.5 C 2 4.910156 4.910156 2 8.5 2 C 12.089844 2 15 4.910156 15 8.5 C 15 12.089844 12.089844 15 8.5 15 Z"/>
|
||||
<path fill="#ED6B21" d="M 13.261719 14.867188 L 19.191406 15.722656 C 18.789063 15.324219 18.070313 15.363281 17.347656 15.738281 M 8.5 0 C 3.804688 0 0 3.804688 0 8.5 C 0 13.195313 3.804688 17 8.5 17 C 13.195313 17 17 13.195313 17 8.5 C 17 3.804688 13.195313 0 8.5 0 Z M 8.5 15 C 4.910156 15 2 12.089844 2 8.5 C 2 4.910156 4.910156 2 8.5 2 C 12.089844 2 15 4.910156 15 8.5 C 15 12.089844 12.089844 15 8.5 15 Z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
4
resources/icons/search_.svg
Normal file
4
resources/icons/search_.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24px" height="24px">
|
||||
<path fill="#FFFFFF" d="M 13.261719 14.867188 L 15.742188 17.347656 C 15.363281 18.070313 15.324219 18.789063 15.722656 19.1875 L 20.25 23.714844 C 20.820313 24.285156 22.0625 23.972656 23.015625 23.015625 C 23.972656 22.058594 24.285156 20.820313 23.714844 20.25 L 19.191406 15.722656 C 18.789063 15.324219 18.070313 15.363281 17.347656 15.738281 L 14.867188 13.261719 Z M 8.5 0 C 3.804688 0 0 3.804688 0 8.5 C 0 13.195313 3.804688 17 8.5 17 C 13.195313 17 17 13.195313 17 8.5 C 17 3.804688 13.195313 0 8.5 0 Z M 8.5 15 C 4.910156 15 2 12.089844 2 8.5 C 2 4.910156 4.910156 2 8.5 2 C 12.089844 2 15 4.910156 15 8.5 C 15 12.089844 12.089844 15 8.5 15 Z"/>
|
||||
<path fill="#ED6B21" d="M 13.261719 14.867188 L 19.191406 15.722656 C 18.789063 15.324219 18.070313 15.363281 17.347656 15.738281 M 8.5 0 C 3.804688 0 0 3.804688 0 8.5 C 0 13.195313 3.804688 17 8.5 17 C 13.195313 17 17 13.195313 17 8.5 C 17 3.804688 13.195313 0 8.5 0 Z M 8.5 15 C 4.910156 15 2 12.089844 2 8.5 C 2 4.910156 4.910156 2 8.5 2 C 12.089844 2 15 4.910156 15 8.5 C 15 12.089844 12.089844 15 8.5 15 Z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
4
resources/icons/search_gray.svg
Normal file
4
resources/icons/search_gray.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24px" height="24px">
|
||||
<path fill="#808080" d="M 13.261719 14.867188 L 15.742188 17.347656 C 15.363281 18.070313 15.324219 18.789063 15.722656 19.1875 L 20.25 23.714844 C 20.820313 24.285156 22.0625 23.972656 23.015625 23.015625 C 23.972656 22.058594 24.285156 20.820313 23.714844 20.25 L 19.191406 15.722656 C 18.789063 15.324219 18.070313 15.363281 17.347656 15.738281 L 14.867188 13.261719 Z M 8.5 0 C 3.804688 0 0 3.804688 0 8.5 C 0 13.195313 3.804688 17 8.5 17 C 13.195313 17 17 13.195313 17 8.5 C 17 3.804688 13.195313 0 8.5 0 Z M 8.5 15 C 4.910156 15 2 12.089844 2 8.5 C 2 4.910156 4.910156 2 8.5 2 C 12.089844 2 15 4.910156 15 8.5 C 15 12.089844 12.089844 15 8.5 15 Z"/>
|
||||
<path fill="#808080" d="M 13.261719 14.867188 L 19.191406 15.722656 C 18.789063 15.324219 18.070313 15.363281 17.347656 15.738281 M 8.5 0 C 3.804688 0 0 3.804688 0 8.5 C 0 13.195313 3.804688 17 8.5 17 C 13.195313 17 17 13.195313 17 8.5 C 17 3.804688 13.195313 0 8.5 0 Z M 8.5 15 C 4.910156 15 2 12.089844 2 8.5 C 2 4.910156 4.910156 2 8.5 2 C 12.089844 2 15 4.910156 15 8.5 C 15 12.089844 12.089844 15 8.5 15 Z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
@ -97,9 +97,17 @@
|
||||
//#define IMGUI_DEBUG_PARANOID
|
||||
|
||||
//---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files.
|
||||
/*
|
||||
|
||||
namespace ImGui
|
||||
{
|
||||
void MyFunction(const char* name, const MyMatrix44& v);
|
||||
// Special ASCII character is used here as markup symbols for tokens to be highlighted as a for hovered item
|
||||
const char ColorMarkerHovered = 0x1; // STX
|
||||
|
||||
// Special ASCII characters STX and ETX are used here as markup symbols for tokens to be highlighted.
|
||||
const char ColorMarkerStart = 0x2; // STX
|
||||
const char ColorMarkerEnd = 0x3; // ETX
|
||||
|
||||
// void MyFunction(const char* name, const MyMatrix44& v);
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
|
@ -33,6 +33,7 @@ Index of this file:
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
#endif
|
||||
#include "imgui_internal.h"
|
||||
#include "imconfig.h"
|
||||
|
||||
#include <stdio.h> // vsnprintf, sscanf, printf
|
||||
#if !defined(alloca)
|
||||
@ -2991,6 +2992,15 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col
|
||||
ImDrawIdx* idx_write = draw_list->_IdxWritePtr;
|
||||
unsigned int vtx_current_idx = draw_list->_VtxCurrentIdx;
|
||||
|
||||
ImU32 defaultCol = col;
|
||||
ImU32 highlighCol = ImGui::GetColorU32(ImGuiCol_ButtonHovered);
|
||||
|
||||
// if text is started with ColorMarkerHovered symbol, we should use another color for a highlighting
|
||||
if (*s == ImGui::ColorMarkerHovered) {
|
||||
highlighCol = ImGui::GetColorU32(ImGuiCol_FrameBg);
|
||||
s += 1;
|
||||
}
|
||||
|
||||
while (s < text_end)
|
||||
{
|
||||
if (word_wrap_enabled)
|
||||
@ -3019,6 +3029,17 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col
|
||||
}
|
||||
}
|
||||
|
||||
if (*s == ImGui::ColorMarkerStart) {
|
||||
col = highlighCol;
|
||||
s += 1;
|
||||
}
|
||||
else if (*s == ImGui::ColorMarkerEnd) {
|
||||
col = defaultCol;
|
||||
s += 1;
|
||||
if (s == text_end)
|
||||
break;
|
||||
}
|
||||
|
||||
// Decode and advance source
|
||||
unsigned int c = (unsigned int)*s;
|
||||
if (c < 0x80)
|
||||
|
@ -67,7 +67,7 @@ struct ArrangeParams {
|
||||
|
||||
/// The minimum distance which is allowed for any
|
||||
/// pair of items on the print bed in any direction.
|
||||
coord_t min_obj_distance = 0.;
|
||||
coord_t min_obj_distance = 0;
|
||||
|
||||
/// The accuracy of optimization.
|
||||
/// Goes from 0.0 to 1.0 and scales performance as well
|
||||
|
@ -149,6 +149,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def->label = L("Other layers");
|
||||
def->tooltip = L("Bed temperature for layers after the first one. "
|
||||
"Set this to zero to disable bed temperature control commands in the output.");
|
||||
def->sidetext = L("°C");
|
||||
def->full_label = L("Bed temperature");
|
||||
def->min = 0;
|
||||
def->max = 300;
|
||||
@ -873,8 +874,10 @@ void PrintConfigDef::init_fff_params()
|
||||
|
||||
def = this->add("first_layer_bed_temperature", coInts);
|
||||
def->label = L("First layer");
|
||||
def->full_label = L("First layer bed temperature");
|
||||
def->tooltip = L("Heated build plate temperature for the first layer. Set this to zero to disable "
|
||||
"bed temperature control commands in the output.");
|
||||
def->sidetext = L("°C");
|
||||
def->max = 0;
|
||||
def->max = 300;
|
||||
def->set_default_value(new ConfigOptionInts { 0 });
|
||||
@ -915,8 +918,10 @@ void PrintConfigDef::init_fff_params()
|
||||
|
||||
def = this->add("first_layer_temperature", coInts);
|
||||
def->label = L("First layer");
|
||||
def->full_label = L("First layer extruder temperature");
|
||||
def->tooltip = L("Extruder temperature for first layer. If you want to control temperature manually "
|
||||
"during print, set this to zero to disable temperature control commands in the output file.");
|
||||
def->sidetext = L("°C");
|
||||
def->min = 0;
|
||||
def->max = max_temp;
|
||||
def->set_default_value(new ConfigOptionInts { 200 });
|
||||
@ -2125,7 +2130,8 @@ void PrintConfigDef::init_fff_params()
|
||||
def->label = L("Other layers");
|
||||
def->tooltip = L("Extruder temperature for layers after the first one. Set this to zero to disable "
|
||||
"temperature control commands in the output.");
|
||||
def->full_label = L("Temperature");
|
||||
def->sidetext = L("°C");
|
||||
def->full_label = L("Extruder temperature");
|
||||
def->min = 0;
|
||||
def->max = max_temp;
|
||||
def->set_default_value(new ConfigOptionInts { 200 });
|
||||
|
@ -165,6 +165,8 @@ set(SLIC3R_GUI_SOURCES
|
||||
GUI/ObjectDataViewModel.hpp
|
||||
GUI/InstanceCheck.cpp
|
||||
GUI/InstanceCheck.hpp
|
||||
GUI/Search.cpp
|
||||
GUI/Search.hpp
|
||||
Utils/Http.cpp
|
||||
Utils/Http.hpp
|
||||
Utils/FixModelByWin10.cpp
|
||||
|
@ -115,7 +115,9 @@ void CopyrightsDialog::fill_entries()
|
||||
{ "Icons for STL and GCODE files."
|
||||
, "Akira Yasuda" , "http://3dp0.com/icons-for-stl-and-gcode/" },
|
||||
{ "AppImage packaging for Linux using AppImageKit"
|
||||
, "2004-2019 Simon Peter and contributors" , "https://appimage.org/" }
|
||||
, "2004-2019 Simon Peter and contributors" , "https://appimage.org/" },
|
||||
{ "lib_fts"
|
||||
, "Forrest Smith" , "https://www.forrestthewoods.com/" }
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -57,6 +57,8 @@ void Field::PostInitialize()
|
||||
m_Undo_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent) { on_back_to_initial_value(); }));
|
||||
m_Undo_to_sys_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent) { on_back_to_sys_value(); }));
|
||||
|
||||
m_blinking_bmp = new BlinkingBitmap(m_parent);
|
||||
|
||||
switch (m_opt.type)
|
||||
{
|
||||
case coPercents:
|
||||
|
@ -230,6 +230,8 @@ public:
|
||||
static int def_width_wider() ;
|
||||
static int def_width_thinner() ;
|
||||
|
||||
BlinkingBitmap* blinking_bitmap() const { return m_blinking_bmp;}
|
||||
|
||||
protected:
|
||||
RevertButton* m_Undo_btn = nullptr;
|
||||
// Bitmap and Tooltip text for m_Undo_btn. The wxButton will be updated only if the new wxBitmap pointer differs from the currently rendered one.
|
||||
@ -240,6 +242,8 @@ protected:
|
||||
const ScalableBitmap* m_undo_to_sys_bitmap = nullptr;
|
||||
const wxString* m_undo_to_sys_tooltip = nullptr;
|
||||
|
||||
BlinkingBitmap* m_blinking_bmp{ nullptr };
|
||||
|
||||
wxStaticText* m_Label = nullptr;
|
||||
// Color for Label. The wxColour will be updated only if the new wxColour pointer differs from the currently rendered one.
|
||||
const wxColour* m_label_color = nullptr;
|
||||
@ -461,6 +465,7 @@ public:
|
||||
x_textctrl->Disable();
|
||||
y_textctrl->Disable(); }
|
||||
wxSizer* getSizer() override { return sizer; }
|
||||
wxWindow* getWindow() override { return dynamic_cast<wxWindow*>(x_textctrl); }
|
||||
};
|
||||
|
||||
class StaticText : public Field {
|
||||
|
@ -1537,6 +1537,7 @@ wxDEFINE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_COLLAPSE_SIDEBAR, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event<float>);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, HeightProfileSmoothEvent);
|
||||
@ -1562,6 +1563,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar
|
||||
#endif // !ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
, m_main_toolbar(GLToolbar::Normal, "Top")
|
||||
, m_undoredo_toolbar(GLToolbar::Normal, "Top")
|
||||
, m_collapse_toolbar(GLToolbar::Normal, "Top")
|
||||
, m_gizmos(*this)
|
||||
, m_use_clipping_planes(false)
|
||||
, m_sidebar_field("")
|
||||
@ -1981,6 +1983,11 @@ void GLCanvas3D::enable_undoredo_toolbar(bool enable)
|
||||
m_undoredo_toolbar.set_enabled(enable);
|
||||
}
|
||||
|
||||
void GLCanvas3D::enable_collapse_toolbar(bool enable)
|
||||
{
|
||||
m_collapse_toolbar.set_enabled(enable);
|
||||
}
|
||||
|
||||
void GLCanvas3D::enable_dynamic_background(bool enable)
|
||||
{
|
||||
m_dynamic_background_enabled = enable;
|
||||
@ -2213,6 +2220,9 @@ void GLCanvas3D::render()
|
||||
tooltip = m_undoredo_toolbar.get_tooltip();
|
||||
|
||||
if (tooltip.empty())
|
||||
tooltip = m_collapse_toolbar.get_tooltip();
|
||||
|
||||
if (tooltip.empty())
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
tooltip = wxGetApp().plater()->get_view_toolbar().get_tooltip();
|
||||
#else
|
||||
@ -2251,6 +2261,9 @@ void GLCanvas3D::render()
|
||||
if (tooltip.empty())
|
||||
tooltip = m_undoredo_toolbar.get_tooltip();
|
||||
|
||||
if (tooltip.empty())
|
||||
tooltip = m_collapse_toolbar.get_tooltip();
|
||||
|
||||
if (tooltip.empty())
|
||||
tooltip = m_view_toolbar.get_tooltip();
|
||||
|
||||
@ -3046,6 +3059,7 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt)
|
||||
|
||||
m_dirty |= m_main_toolbar.update_items_state();
|
||||
m_dirty |= m_undoredo_toolbar.update_items_state();
|
||||
m_dirty |= m_collapse_toolbar.update_items_state();
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
m_dirty |= wxGetApp().plater()->get_view_toolbar().update_items_state();
|
||||
bool mouse3d_controller_applied = wxGetApp().plater()->get_mouse3d_controller().apply(wxGetApp().plater()->get_camera());
|
||||
@ -3085,7 +3099,7 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
|
||||
return;
|
||||
}
|
||||
|
||||
if ((keyCode == WXK_ESCAPE) && _deactivate_undo_redo_toolbar_items())
|
||||
if ((keyCode == WXK_ESCAPE) && (_deactivate_undo_redo_toolbar_items() || _deactivate_search_toolbar_item()))
|
||||
return;
|
||||
|
||||
if (m_gizmos.on_char(evt))
|
||||
@ -3133,6 +3147,16 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
|
||||
break;
|
||||
|
||||
|
||||
#ifdef __APPLE__
|
||||
case 'f':
|
||||
case 'F':
|
||||
#else /* __APPLE__ */
|
||||
case WXK_CONTROL_F:
|
||||
#endif /* __APPLE__ */
|
||||
_activate_search_toolbar_item();
|
||||
break;
|
||||
|
||||
|
||||
#ifdef __APPLE__
|
||||
case 'y':
|
||||
case 'Y':
|
||||
@ -3544,6 +3568,15 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
|
||||
}
|
||||
}
|
||||
|
||||
// If the Search window or Undo/Redo list is opened,
|
||||
// update them according to the event
|
||||
if (m_main_toolbar.is_item_pressed("search") ||
|
||||
m_undoredo_toolbar.is_item_pressed("undo") ||
|
||||
m_undoredo_toolbar.is_item_pressed("redo")) {
|
||||
m_mouse_wheel = int((double)evt.GetWheelRotation() / (double)evt.GetWheelDelta());
|
||||
return;
|
||||
}
|
||||
|
||||
// Inform gizmos about the event so they have the opportunity to react.
|
||||
if (m_gizmos.on_mouse_wheel(evt))
|
||||
return;
|
||||
@ -3679,6 +3712,14 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_collapse_toolbar.on_mouse(evt, *this))
|
||||
{
|
||||
if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp())
|
||||
mouse_up_cleanup();
|
||||
m_mouse.set_start_position_3D_as_invalid();
|
||||
return;
|
||||
}
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
if (wxGetApp().plater()->get_view_toolbar().on_mouse(evt, *this))
|
||||
#else
|
||||
@ -3744,6 +3785,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
else if (evt.Leaving())
|
||||
{
|
||||
_deactivate_undo_redo_toolbar_items();
|
||||
_deactivate_search_toolbar_item();
|
||||
|
||||
// to remove hover on objects when the mouse goes out of this canvas
|
||||
m_mouse.position = Vec2d(-1.0, -1.0);
|
||||
@ -3751,7 +3793,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
}
|
||||
else if (evt.LeftDown() || evt.RightDown() || evt.MiddleDown())
|
||||
{
|
||||
if (_deactivate_undo_redo_toolbar_items())
|
||||
if (_deactivate_undo_redo_toolbar_items() || _deactivate_search_toolbar_item())
|
||||
return;
|
||||
|
||||
// If user pressed left or right button we first check whether this happened
|
||||
@ -4548,7 +4590,7 @@ bool GLCanvas3D::_render_undo_redo_stack(const bool is_undo, float pos_x) const
|
||||
em *= m_retina_helper->get_scale_factor();
|
||||
#endif
|
||||
|
||||
if (imgui->undo_redo_list(ImVec2(18 * em, 26 * em), is_undo, &string_getter, hovered, selected))
|
||||
if (imgui->undo_redo_list(ImVec2(18 * em, 26 * em), is_undo, &string_getter, hovered, selected, m_mouse_wheel))
|
||||
m_imgui_undo_redo_hovered_pos = hovered;
|
||||
else
|
||||
m_imgui_undo_redo_hovered_pos = -1;
|
||||
@ -4566,6 +4608,64 @@ bool GLCanvas3D::_render_undo_redo_stack(const bool is_undo, float pos_x) const
|
||||
return action_taken;
|
||||
}
|
||||
|
||||
// Getter for the const char*[] for the search list
|
||||
static bool search_string_getter(int idx, const char** label, const char** tooltip)
|
||||
{
|
||||
return wxGetApp().plater()->search_string_getter(idx, label, tooltip);
|
||||
}
|
||||
|
||||
bool GLCanvas3D::_render_search_list(float pos_x) const
|
||||
{
|
||||
bool action_taken = false;
|
||||
ImGuiWrapper* imgui = wxGetApp().imgui();
|
||||
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
const float x = pos_x * (float)wxGetApp().plater()->get_camera().get_zoom() + 0.5f * (float)get_canvas_size().get_width();
|
||||
#else
|
||||
const float x = pos_x * (float)get_camera().get_zoom() + 0.5f * (float)get_canvas_size().get_width();
|
||||
#endif
|
||||
imgui->set_next_window_pos(x, m_main_toolbar.get_height(), ImGuiCond_Always, 0.5f, 0.0f);
|
||||
std::string title = L("Search");
|
||||
imgui->begin(_(title), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
|
||||
|
||||
int selected = -1;
|
||||
bool edited = false;
|
||||
bool check_changed = false;
|
||||
float em = static_cast<float>(wxGetApp().em_unit());
|
||||
#if ENABLE_RETINA_GL
|
||||
em *= m_retina_helper->get_scale_factor();
|
||||
#endif
|
||||
|
||||
Sidebar& sidebar = wxGetApp().sidebar();
|
||||
|
||||
std::string& search_line = sidebar.get_search_line();
|
||||
char *s = new char[255];
|
||||
strcpy(s, search_line.empty() ? _u8L("Type here to search").c_str() : search_line.c_str());
|
||||
|
||||
imgui->search_list(ImVec2(45 * em, 30 * em), &search_string_getter, s,
|
||||
sidebar.get_searcher().view_params,
|
||||
selected, edited, m_mouse_wheel);
|
||||
|
||||
search_line = s;
|
||||
delete [] s;
|
||||
if (search_line == _u8L("Type here to search"))
|
||||
search_line.clear();
|
||||
|
||||
if (edited)
|
||||
sidebar.search();
|
||||
|
||||
if (selected != size_t(-1)) {
|
||||
// selected == 9999 means that Esc kye was pressed
|
||||
if (selected != 9999)
|
||||
sidebar.jump_to_option(selected);
|
||||
action_taken = true;
|
||||
}
|
||||
|
||||
imgui->end();
|
||||
|
||||
return action_taken;
|
||||
}
|
||||
|
||||
#define ENABLE_THUMBNAIL_GENERATOR_DEBUG_OUTPUT 0
|
||||
#if ENABLE_THUMBNAIL_GENERATOR_DEBUG_OUTPUT
|
||||
static void debug_output_thumbnail(const ThumbnailData& thumbnail_data)
|
||||
@ -4935,6 +5035,9 @@ bool GLCanvas3D::_init_toolbars()
|
||||
if (!_init_view_toolbar())
|
||||
return false;
|
||||
|
||||
if (!_init_collapse_toolbar())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -5092,6 +5195,26 @@ bool GLCanvas3D::_init_main_toolbar()
|
||||
if (!m_main_toolbar.add_item(item))
|
||||
return false;
|
||||
|
||||
if (!m_main_toolbar.add_separator())
|
||||
return false;
|
||||
|
||||
item.name = "search";
|
||||
item.icon_filename = "search_.svg";
|
||||
item.tooltip = _utf8(L("Search")) + " [" + GUI::shortkey_ctrl_prefix() + "F]";
|
||||
item.sprite_id = 11;
|
||||
item.left.render_callback = [this](float left, float right, float, float) {
|
||||
if (m_canvas != nullptr)
|
||||
{
|
||||
if (_render_search_list(0.5f * (left + right)))
|
||||
_deactivate_search_toolbar_item();
|
||||
}
|
||||
};
|
||||
item.left.action_callback = GLToolbarItem::Default_Action_Callback;
|
||||
item.visibility_callback = GLToolbarItem::Default_Visibility_Callback;
|
||||
item.enabling_callback = GLToolbarItem::Default_Enabling_Callback;
|
||||
if (!m_main_toolbar.add_item(item))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -5201,6 +5324,9 @@ bool GLCanvas3D::_init_undoredo_toolbar()
|
||||
if (!m_undoredo_toolbar.add_item(item))
|
||||
return false;
|
||||
|
||||
if (!m_undoredo_toolbar.add_separator())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -5209,6 +5335,106 @@ bool GLCanvas3D::_init_view_toolbar()
|
||||
return wxGetApp().plater()->init_view_toolbar();
|
||||
}
|
||||
|
||||
bool GLCanvas3D::_init_collapse_toolbar()
|
||||
{
|
||||
if (!m_collapse_toolbar.is_enabled())
|
||||
return true;
|
||||
|
||||
BackgroundTexture::Metadata background_data;
|
||||
background_data.filename = "toolbar_background.png";
|
||||
background_data.left = 16;
|
||||
background_data.top = 16;
|
||||
background_data.right = 16;
|
||||
background_data.bottom = 16;
|
||||
|
||||
if (!m_collapse_toolbar.init(background_data))
|
||||
{
|
||||
// unable to init the toolbar texture, disable it
|
||||
m_collapse_toolbar.set_enabled(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
m_collapse_toolbar.set_layout_type(GLToolbar::Layout::Vertical);
|
||||
m_collapse_toolbar.set_horizontal_orientation(GLToolbar::Layout::HO_Right);
|
||||
m_collapse_toolbar.set_vertical_orientation(GLToolbar::Layout::VO_Top);
|
||||
m_collapse_toolbar.set_border(5.0f);
|
||||
m_collapse_toolbar.set_separator_size(5);
|
||||
m_collapse_toolbar.set_gap_size(2);
|
||||
|
||||
GLToolbarItem::Data item;
|
||||
|
||||
item.name = "collapse_sidebar";
|
||||
item.icon_filename = "collapse.svg";
|
||||
item.tooltip = wxGetApp().plater()->is_sidebar_collapsed() ? _utf8(L("Expand right panel")) : _utf8(L("Collapse right panel"));
|
||||
item.sprite_id = 0;
|
||||
item.left.action_callback = [this, item]() {
|
||||
std::string new_tooltip = wxGetApp().plater()->is_sidebar_collapsed() ?
|
||||
_utf8(L("Collapse right panel")) : _utf8(L("Expand right panel"));
|
||||
|
||||
int id = m_collapse_toolbar.get_item_id("collapse_sidebar");
|
||||
m_collapse_toolbar.set_tooltip(id, new_tooltip);
|
||||
set_tooltip("");
|
||||
|
||||
wxGetApp().plater()->collapse_sidebar(!wxGetApp().plater()->is_sidebar_collapsed());
|
||||
};
|
||||
|
||||
if (!m_collapse_toolbar.add_item(item))
|
||||
return false;
|
||||
|
||||
if (!m_collapse_toolbar.add_separator())
|
||||
return false;
|
||||
|
||||
item.name = "print";
|
||||
item.icon_filename = "cog.svg";
|
||||
item.tooltip = _utf8(L("Switch to Print Settings")) + " [" + GUI::shortkey_ctrl_prefix() + "2]";
|
||||
item.sprite_id = 1;
|
||||
item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(/*0*/1); };
|
||||
|
||||
if (!m_collapse_toolbar.add_item(item))
|
||||
return false;
|
||||
|
||||
item.name = "filament";
|
||||
item.icon_filename = "spool.svg";
|
||||
item.tooltip = _utf8(L("Switch to Filament Settings")) + " [" + GUI::shortkey_ctrl_prefix() + "3]";
|
||||
item.sprite_id = 2;
|
||||
item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(/*1*/2); };
|
||||
item.visibility_callback = [this]() { return wxGetApp().plater()->printer_technology() == ptFFF; };
|
||||
|
||||
if (!m_collapse_toolbar.add_item(item))
|
||||
return false;
|
||||
|
||||
item.name = "printer";
|
||||
item.icon_filename = "printer.svg";
|
||||
item.tooltip = _utf8(L("Switch to Printer Settings")) + " [" + GUI::shortkey_ctrl_prefix() + "4]";
|
||||
item.sprite_id = 3;
|
||||
item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(/*2*/3); };
|
||||
|
||||
if (!m_collapse_toolbar.add_item(item))
|
||||
return false;
|
||||
|
||||
item.name = "resin";
|
||||
item.icon_filename = "resin.svg";
|
||||
item.tooltip = _utf8(L("Switch to SLA Material Settings")) + " [" + GUI::shortkey_ctrl_prefix() + "3]";
|
||||
item.sprite_id = 4;
|
||||
item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(/*1*/2); };
|
||||
item.visibility_callback = [this]() { return m_process->current_printer_technology() == ptSLA; };
|
||||
|
||||
if (!m_collapse_toolbar.add_item(item))
|
||||
return false;
|
||||
|
||||
item.name = "sla_printer";
|
||||
item.icon_filename = "sla_printer.svg";
|
||||
item.tooltip = _utf8(L("Switch to Printer Settings")) + " [" + GUI::shortkey_ctrl_prefix() + "4]";
|
||||
item.sprite_id = 5;
|
||||
item.left.action_callback = [this]() { wxGetApp().mainframe->select_tab(/*2*/3); };
|
||||
|
||||
if (!m_collapse_toolbar.add_item(item))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool GLCanvas3D::_set_current()
|
||||
{
|
||||
return m_context != nullptr && m_canvas->SetCurrent(*m_context);
|
||||
@ -5606,14 +5832,17 @@ void GLCanvas3D::_render_overlays() const
|
||||
const float scale = m_retina_helper->get_scale_factor() * wxGetApp().toolbar_icon_scale(true);
|
||||
m_main_toolbar.set_scale(scale);
|
||||
m_undoredo_toolbar.set_scale(scale);
|
||||
m_collapse_toolbar.set_scale(scale);
|
||||
#else
|
||||
const float size = int(GLToolbar::Default_Icons_Size * wxGetApp().toolbar_icon_scale(true));
|
||||
m_main_toolbar.set_icons_size(size);
|
||||
m_undoredo_toolbar.set_icons_size(size);
|
||||
m_collapse_toolbar.set_icons_size(size);
|
||||
#endif // ENABLE_RETINA_GL
|
||||
|
||||
_render_main_toolbar();
|
||||
_render_undoredo_toolbar();
|
||||
_render_collapse_toolbar();
|
||||
_render_view_toolbar();
|
||||
|
||||
if ((m_layers_editing.last_object_id >= 0) && (m_layers_editing.object_max_z() > 0.0f))
|
||||
@ -5746,6 +5975,27 @@ void GLCanvas3D::_render_undoredo_toolbar() const
|
||||
m_undoredo_toolbar.render(*this);
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_collapse_toolbar() const
|
||||
{
|
||||
if (!m_collapse_toolbar.is_enabled())
|
||||
return;
|
||||
|
||||
Size cnv_size = get_canvas_size();
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom();
|
||||
#else
|
||||
float inv_zoom = (float)m_camera.get_inv_zoom();
|
||||
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
|
||||
float band = m_layers_editing.is_enabled() ? (wxGetApp().imgui()->get_style_scaling() * LayersEditing::THICKNESS_BAR_WIDTH) : 0.0;
|
||||
|
||||
float top = 0.5f * (float)cnv_size.get_height() * inv_zoom;
|
||||
float left = (0.5f * (float)cnv_size.get_width() - (float)m_collapse_toolbar.get_width() - band) * inv_zoom;
|
||||
|
||||
m_collapse_toolbar.set_position(top, left);
|
||||
m_collapse_toolbar.render(*this);
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_view_toolbar() const
|
||||
{
|
||||
#if ENABLE_NON_STATIC_CANVAS_MANAGER
|
||||
@ -7277,6 +7527,39 @@ bool GLCanvas3D::_deactivate_undo_redo_toolbar_items()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GLCanvas3D::_deactivate_search_toolbar_item()
|
||||
{
|
||||
if (m_main_toolbar.is_item_pressed("search"))
|
||||
{
|
||||
m_main_toolbar.force_left_action(m_main_toolbar.get_item_id("search"), *this);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GLCanvas3D::_activate_search_toolbar_item()
|
||||
{
|
||||
if (!m_main_toolbar.is_item_pressed("search"))
|
||||
{
|
||||
m_main_toolbar.force_left_action(m_main_toolbar.get_item_id("search"), *this);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GLCanvas3D::_deactivate_collapse_toolbar_items()
|
||||
{
|
||||
if (m_collapse_toolbar.is_item_pressed("print"))
|
||||
{
|
||||
m_collapse_toolbar.force_left_action(m_collapse_toolbar.get_item_id("print"), *this);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const Print* GLCanvas3D::fff_print() const
|
||||
{
|
||||
return (m_process == nullptr) ? nullptr : m_process->fff_print();
|
||||
|
@ -114,6 +114,7 @@ wxDECLARE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_COLLAPSE_SIDEBAR, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event<float>);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, HeightProfileSmoothEvent);
|
||||
@ -163,8 +164,8 @@ private:
|
||||
Num_States
|
||||
};
|
||||
|
||||
private:
|
||||
static const float THICKNESS_BAR_WIDTH;
|
||||
private:
|
||||
|
||||
bool m_enabled;
|
||||
Shader m_shader;
|
||||
@ -463,6 +464,7 @@ private:
|
||||
mutable GLGizmosManager m_gizmos;
|
||||
mutable GLToolbar m_main_toolbar;
|
||||
mutable GLToolbar m_undoredo_toolbar;
|
||||
mutable GLToolbar m_collapse_toolbar;
|
||||
ClippingPlane m_clipping_planes[2];
|
||||
mutable ClippingPlane m_camera_clipping_plane;
|
||||
bool m_use_clipping_planes;
|
||||
@ -518,6 +520,7 @@ private:
|
||||
#endif // ENABLE_RENDER_STATISTICS
|
||||
|
||||
mutable int m_imgui_undo_redo_hovered_pos{ -1 };
|
||||
mutable int m_mouse_wheel {0};
|
||||
int m_selected_extruder;
|
||||
|
||||
Labels m_labels;
|
||||
@ -619,6 +622,7 @@ public:
|
||||
void enable_selection(bool enable);
|
||||
void enable_main_toolbar(bool enable);
|
||||
void enable_undoredo_toolbar(bool enable);
|
||||
void enable_collapse_toolbar(bool enable);
|
||||
void enable_dynamic_background(bool enable);
|
||||
void enable_labels(bool enable) { m_labels.enable(enable); }
|
||||
#if ENABLE_SLOPE_RENDERING
|
||||
@ -782,6 +786,7 @@ private:
|
||||
bool _init_main_toolbar();
|
||||
bool _init_undoredo_toolbar();
|
||||
bool _init_view_toolbar();
|
||||
bool _init_collapse_toolbar();
|
||||
|
||||
bool _set_current();
|
||||
void _resize(unsigned int w, unsigned int h);
|
||||
@ -815,6 +820,7 @@ private:
|
||||
void _render_gizmos_overlay() const;
|
||||
void _render_main_toolbar() const;
|
||||
void _render_undoredo_toolbar() const;
|
||||
void _render_collapse_toolbar() const;
|
||||
void _render_view_toolbar() const;
|
||||
#if ENABLE_SHOW_CAMERA_TARGET
|
||||
void _render_camera_target() const;
|
||||
@ -822,6 +828,7 @@ private:
|
||||
void _render_sla_slices() const;
|
||||
void _render_selection_sidebar_hints() const;
|
||||
bool _render_undo_redo_stack(const bool is_undo, float pos_x) const;
|
||||
bool _render_search_list(float pos_x) const;
|
||||
void _render_thumbnail_internal(ThumbnailData& thumbnail_data, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) const;
|
||||
// render thumbnail using an off-screen framebuffer
|
||||
void _render_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) const;
|
||||
@ -885,6 +892,9 @@ private:
|
||||
void _update_selection_from_hover();
|
||||
|
||||
bool _deactivate_undo_redo_toolbar_items();
|
||||
bool _deactivate_search_toolbar_item();
|
||||
bool _activate_search_toolbar_item();
|
||||
bool _deactivate_collapse_toolbar_items();
|
||||
|
||||
static std::vector<float> _parse_colors(const std::vector<std::string>& colors);
|
||||
|
||||
|
@ -406,6 +406,12 @@ void GLToolbar::set_additional_tooltip(int item_id, const std::string& text)
|
||||
m_items[item_id]->set_additional_tooltip(text);
|
||||
}
|
||||
|
||||
void GLToolbar::set_tooltip(int item_id, const std::string& text)
|
||||
{
|
||||
if (0 <= item_id && item_id < (int)m_items.size())
|
||||
m_items[item_id]->set_tooltip(text);
|
||||
}
|
||||
|
||||
bool GLToolbar::update_items_state()
|
||||
{
|
||||
bool ret = false;
|
||||
|
@ -118,6 +118,7 @@ public:
|
||||
const std::string& get_tooltip() const { return m_data.tooltip; }
|
||||
const std::string& get_additional_tooltip() const { return m_data.additional_tooltip; }
|
||||
void set_additional_tooltip(const std::string& text) { m_data.additional_tooltip = text; }
|
||||
void set_tooltip(const std::string& text) { m_data.tooltip = text; }
|
||||
|
||||
void do_left_action() { m_last_action_type = Left; m_data.left.action_callback(); }
|
||||
void do_right_action() { m_last_action_type = Right; m_data.right.action_callback(); }
|
||||
@ -317,6 +318,7 @@ public:
|
||||
|
||||
void get_additional_tooltip(int item_id, std::string& text);
|
||||
void set_additional_tooltip(int item_id, const std::string& text);
|
||||
void set_tooltip(int item_id, const std::string& text);
|
||||
|
||||
// returns true if any item changed its state
|
||||
bool update_items_state();
|
||||
|
@ -350,17 +350,15 @@ bool GUI_App::on_init_inner()
|
||||
// Slic3r::debugf "wxWidgets version %s, Wx version %s\n", wxVERSION_STRING, wxVERSION;
|
||||
|
||||
std::string msg = Http::tls_global_init();
|
||||
wxRichMessageDialog
|
||||
dlg(nullptr,
|
||||
wxString::Format(_(L("%s\nDo you want to continue?")), _(msg)),
|
||||
"PrusaSlicer", wxICON_QUESTION | wxYES_NO);
|
||||
|
||||
bool ssl_accept = app_config->get("tls_cert_store_accepted") == "yes";
|
||||
std::string ssl_cert_store = app_config->get("tls_accepted_cert_store_location");
|
||||
ssl_accept = ssl_accept && ssl_cert_store == Http::tls_system_cert_store();
|
||||
bool ssl_accept = app_config->get("tls_cert_store_accepted") == "yes" && ssl_cert_store == Http::tls_system_cert_store();
|
||||
|
||||
dlg.ShowCheckBox(_(L("Remember my choice")));
|
||||
if (!msg.empty() && !ssl_accept) {
|
||||
wxRichMessageDialog
|
||||
dlg(nullptr,
|
||||
wxString::Format(_(L("%s\nDo you want to continue?")), msg),
|
||||
"PrusaSlicer", wxICON_QUESTION | wxYES_NO);
|
||||
dlg.ShowCheckBox(_(L("Remember my choice")));
|
||||
if (dlg.ShowModal() != wxID_YES) return false;
|
||||
|
||||
app_config->set("tls_cert_store_accepted",
|
||||
@ -415,6 +413,8 @@ bool GUI_App::on_init_inner()
|
||||
if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr)
|
||||
wxImage::AddHandler(new wxPNGHandler());
|
||||
mainframe = new MainFrame();
|
||||
mainframe->switch_to(true); // hide settings tabs after first Layout
|
||||
|
||||
sidebar().obj_list()->init_objects(); // propagate model objects to object list
|
||||
// update_mode(); // !!! do that later
|
||||
SetTopWindow(mainframe);
|
||||
|
@ -97,6 +97,7 @@ bool View3D::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_
|
||||
m_canvas->enable_selection(true);
|
||||
m_canvas->enable_main_toolbar(true);
|
||||
m_canvas->enable_undoredo_toolbar(true);
|
||||
m_canvas->enable_collapse_toolbar(true);
|
||||
m_canvas->enable_labels(true);
|
||||
#if ENABLE_SLOPE_RENDERING
|
||||
m_canvas->enable_slope(true);
|
||||
@ -294,6 +295,7 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view
|
||||
m_canvas->set_process(m_process);
|
||||
m_canvas->enable_legend_texture(true);
|
||||
m_canvas->enable_dynamic_background(true);
|
||||
m_canvas->enable_collapse_toolbar(true);
|
||||
|
||||
m_double_slider_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
create_double_slider();
|
||||
|
@ -15,12 +15,17 @@
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
#ifndef IMGUI_DEFINE_MATH_OPERATORS
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
#endif
|
||||
#include <imgui/imgui_internal.h>
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "3DScene.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "Search.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
@ -374,7 +379,40 @@ bool ImGuiWrapper::combo(const wxString& label, const std::vector<std::string>&
|
||||
return res;
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool , int , const char**), int& hovered, int& selected)
|
||||
// Scroll up for one item
|
||||
static void scroll_up()
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiWindow* window = g.CurrentWindow;
|
||||
|
||||
float item_size_y = window->DC.PrevLineSize.y + g.Style.ItemSpacing.y;
|
||||
float win_top = window->Scroll.y;
|
||||
|
||||
ImGui::SetScrollY(win_top - item_size_y);
|
||||
}
|
||||
|
||||
// Scroll down for one item
|
||||
static void scroll_down()
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiWindow* window = g.CurrentWindow;
|
||||
|
||||
float item_size_y = window->DC.PrevLineSize.y + g.Style.ItemSpacing.y;
|
||||
float win_top = window->Scroll.y;
|
||||
|
||||
ImGui::SetScrollY(win_top + item_size_y);
|
||||
}
|
||||
|
||||
static void process_mouse_wheel(int& mouse_wheel)
|
||||
{
|
||||
if (mouse_wheel > 0)
|
||||
scroll_up();
|
||||
else if (mouse_wheel < 0)
|
||||
scroll_down();
|
||||
mouse_wheel = 0;
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool , int , const char**), int& hovered, int& selected, int& mouse_wheel)
|
||||
{
|
||||
bool is_hovered = false;
|
||||
ImGui::ListBoxHeader("", size);
|
||||
@ -396,10 +434,312 @@ bool ImGuiWrapper::undo_redo_list(const ImVec2& size, const bool is_undo, bool (
|
||||
i++;
|
||||
}
|
||||
|
||||
if (is_hovered)
|
||||
process_mouse_wheel(mouse_wheel);
|
||||
|
||||
ImGui::ListBoxFooter();
|
||||
return is_hovered;
|
||||
}
|
||||
|
||||
// It's a copy of IMGui::Selactable function.
|
||||
// But a little beat modified to change a label text.
|
||||
// If item is hovered we should use another color for highlighted letters.
|
||||
// To do that we push a ColorMarkerHovered symbol at the very beginning of the label
|
||||
// This symbol will be used to a color selection for the highlighted letters.
|
||||
// see imgui_draw.cpp, void ImFont::RenderText()
|
||||
static bool selectable(const char* label, bool selected, ImGuiSelectableFlags flags = 0, const ImVec2& size_arg = ImVec2(0, 0))
|
||||
{
|
||||
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||
if (window->SkipItems)
|
||||
return false;
|
||||
|
||||
ImGuiContext& g = *GImGui;
|
||||
const ImGuiStyle& style = g.Style;
|
||||
|
||||
if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.CurrentColumns) // FIXME-OPT: Avoid if vertically clipped.
|
||||
ImGui::PushColumnsBackground();
|
||||
|
||||
ImGuiID id = window->GetID(label);
|
||||
ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true);
|
||||
ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y);
|
||||
ImVec2 pos = window->DC.CursorPos;
|
||||
pos.y += window->DC.CurrLineTextBaseOffset;
|
||||
ImRect bb_inner(pos, pos + size);
|
||||
ImGui::ItemSize(size, 0.0f);
|
||||
|
||||
// Fill horizontal space.
|
||||
ImVec2 window_padding = window->WindowPadding;
|
||||
float max_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? ImGui::GetWindowContentRegionMax().x : ImGui::GetContentRegionMax().x;
|
||||
float w_draw = ImMax(label_size.x, window->Pos.x + max_x - window_padding.x - pos.x);
|
||||
ImVec2 size_draw((size_arg.x != 0 && !(flags & ImGuiSelectableFlags_DrawFillAvailWidth)) ? size_arg.x : w_draw, size_arg.y != 0.0f ? size_arg.y : size.y);
|
||||
ImRect bb(pos, pos + size_draw);
|
||||
if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_DrawFillAvailWidth))
|
||||
bb.Max.x += window_padding.x;
|
||||
|
||||
// Selectables are tightly packed together so we extend the box to cover spacing between selectable.
|
||||
const float spacing_x = style.ItemSpacing.x;
|
||||
const float spacing_y = style.ItemSpacing.y;
|
||||
const float spacing_L = IM_FLOOR(spacing_x * 0.50f);
|
||||
const float spacing_U = IM_FLOOR(spacing_y * 0.50f);
|
||||
bb.Min.x -= spacing_L;
|
||||
bb.Min.y -= spacing_U;
|
||||
bb.Max.x += (spacing_x - spacing_L);
|
||||
bb.Max.y += (spacing_y - spacing_U);
|
||||
|
||||
bool item_add;
|
||||
if (flags & ImGuiSelectableFlags_Disabled)
|
||||
{
|
||||
ImGuiItemFlags backup_item_flags = window->DC.ItemFlags;
|
||||
window->DC.ItemFlags |= ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNavDefaultFocus;
|
||||
item_add = ImGui::ItemAdd(bb, id);
|
||||
window->DC.ItemFlags = backup_item_flags;
|
||||
}
|
||||
else
|
||||
{
|
||||
item_add = ImGui::ItemAdd(bb, id);
|
||||
}
|
||||
if (!item_add)
|
||||
{
|
||||
if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.CurrentColumns)
|
||||
ImGui::PopColumnsBackground();
|
||||
return false;
|
||||
}
|
||||
|
||||
// We use NoHoldingActiveID on menus so user can click and _hold_ on a menu then drag to browse child entries
|
||||
ImGuiButtonFlags button_flags = 0;
|
||||
if (flags & ImGuiSelectableFlags_NoHoldingActiveID) { button_flags |= ImGuiButtonFlags_NoHoldingActiveId; }
|
||||
if (flags & ImGuiSelectableFlags_PressedOnClick) { button_flags |= ImGuiButtonFlags_PressedOnClick; }
|
||||
if (flags & ImGuiSelectableFlags_PressedOnRelease) { button_flags |= ImGuiButtonFlags_PressedOnRelease; }
|
||||
if (flags & ImGuiSelectableFlags_Disabled) { button_flags |= ImGuiButtonFlags_Disabled; }
|
||||
if (flags & ImGuiSelectableFlags_AllowDoubleClick) { button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; }
|
||||
if (flags & ImGuiSelectableFlags_AllowItemOverlap) { button_flags |= ImGuiButtonFlags_AllowItemOverlap; }
|
||||
|
||||
if (flags & ImGuiSelectableFlags_Disabled)
|
||||
selected = false;
|
||||
|
||||
const bool was_selected = selected;
|
||||
bool hovered, held;
|
||||
bool pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held, button_flags);
|
||||
|
||||
// Update NavId when clicking or when Hovering (this doesn't happen on most widgets), so navigation can be resumed with gamepad/keyboard
|
||||
if (pressed || (hovered && (flags & ImGuiSelectableFlags_SetNavIdOnHover)))
|
||||
{
|
||||
if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent)
|
||||
{
|
||||
g.NavDisableHighlight = true;
|
||||
ImGui::SetNavID(id, window->DC.NavLayerCurrent, window->DC.NavFocusScopeIdCurrent);
|
||||
}
|
||||
}
|
||||
if (pressed)
|
||||
ImGui::MarkItemEdited(id);
|
||||
|
||||
if (flags & ImGuiSelectableFlags_AllowItemOverlap)
|
||||
ImGui::SetItemAllowOverlap();
|
||||
|
||||
// In this branch, Selectable() cannot toggle the selection so this will never trigger.
|
||||
if (selected != was_selected) //-V547
|
||||
window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_ToggledSelection;
|
||||
|
||||
// Render
|
||||
if (held && (flags & ImGuiSelectableFlags_DrawHoveredWhenHeld))
|
||||
hovered = true;
|
||||
if (hovered || selected)
|
||||
{
|
||||
const ImU32 col = ImGui::GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
|
||||
ImGui::RenderFrame(bb.Min, bb.Max, col, false, 0.0f);
|
||||
ImGui::RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding);
|
||||
}
|
||||
|
||||
if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.CurrentColumns)
|
||||
{
|
||||
ImGui::PopColumnsBackground();
|
||||
bb.Max.x -= (ImGui::GetContentRegionMax().x - max_x);
|
||||
}
|
||||
|
||||
// mark a label with a ImGui::ColorMarkerHovered, if item is hovered
|
||||
char* marked_label = new char[255];
|
||||
if (hovered)
|
||||
sprintf(marked_label, "%c%s", ImGui::ColorMarkerHovered, label);
|
||||
else
|
||||
strcpy(marked_label, label);
|
||||
|
||||
if (flags & ImGuiSelectableFlags_Disabled) ImGui::PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]);
|
||||
ImGui::RenderTextClipped(bb_inner.Min, bb_inner.Max, marked_label, NULL, &label_size, style.SelectableTextAlign, &bb);
|
||||
if (flags & ImGuiSelectableFlags_Disabled) ImGui::PopStyleColor();
|
||||
|
||||
delete[] marked_label;
|
||||
|
||||
// Automatically close popups
|
||||
if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(window->DC.ItemFlags & ImGuiItemFlags_SelectableDontClosePopup)) ImGui::CloseCurrentPopup();
|
||||
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags);
|
||||
return pressed;
|
||||
}
|
||||
|
||||
// Scroll so that the hovered item is at the top of the window
|
||||
static void scroll_y(int hover_id)
|
||||
{
|
||||
if (hover_id < 0)
|
||||
return;
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiWindow* window = g.CurrentWindow;
|
||||
|
||||
float item_size_y = window->DC.PrevLineSize.y + g.Style.ItemSpacing.y;
|
||||
float item_delta = 0.5 * item_size_y;
|
||||
|
||||
float item_top = item_size_y * hover_id;
|
||||
float item_bottom = item_top + item_size_y;
|
||||
|
||||
float win_top = window->Scroll.y;
|
||||
float win_bottom = window->Scroll.y + window->Size.y;
|
||||
|
||||
if (item_bottom + item_delta >= win_bottom)
|
||||
ImGui::SetScrollY(win_top + item_size_y);
|
||||
else if (item_top - item_delta <= win_top)
|
||||
ImGui::SetScrollY(win_top - item_size_y);
|
||||
}
|
||||
|
||||
// Use this function instead of ImGui::IsKeyPressed.
|
||||
// ImGui::IsKeyPressed is related for *GImGui.IO.KeysDownDuration[user_key_index]
|
||||
// And after first key pressing IsKeyPressed() return "true" always even if key wasn't pressed
|
||||
static void process_key_down(ImGuiKey imgui_key, std::function<void()> f)
|
||||
{
|
||||
if (ImGui::IsKeyDown(ImGui::GetKeyIndex(imgui_key)))
|
||||
{
|
||||
f();
|
||||
// set KeysDown to false to avoid redundant key down processing
|
||||
ImGuiContext& g = *GImGui;
|
||||
g.IO.KeysDown[ImGui::GetKeyIndex(imgui_key)] = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiWrapper::search_list(const ImVec2& size_, bool (*items_getter)(int, const char** label, const char** tooltip), char* search_str,
|
||||
Search::OptionViewParameters& view_params, int& selected, bool& edited, int& mouse_wheel)
|
||||
{
|
||||
// ImGui::ListBoxHeader("", size);
|
||||
{
|
||||
// rewrote part of function to add a TextInput instead of label Text
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||
if (window->SkipItems)
|
||||
return ;
|
||||
|
||||
const ImGuiStyle& style = g.Style;
|
||||
|
||||
// Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
|
||||
ImVec2 size = ImGui::CalcItemSize(size_, ImGui::CalcItemWidth(), ImGui::GetTextLineHeightWithSpacing() * 7.4f + style.ItemSpacing.y);
|
||||
ImRect frame_bb(window->DC.CursorPos, ImVec2(window->DC.CursorPos.x + size.x, window->DC.CursorPos.y + size.y));
|
||||
|
||||
ImRect bb(frame_bb.Min, frame_bb.Max);
|
||||
window->DC.LastItemRect = bb; // Forward storage for ListBoxFooter.. dodgy.
|
||||
g.NextItemData.ClearFlags();
|
||||
|
||||
if (!ImGui::IsRectVisible(bb.Min, bb.Max))
|
||||
{
|
||||
ImGui::ItemSize(bb.GetSize(), style.FramePadding.y);
|
||||
ImGui::ItemAdd(bb, 0, &frame_bb);
|
||||
return ;
|
||||
}
|
||||
|
||||
ImGui::BeginGroup();
|
||||
|
||||
const ImGuiID id = ImGui::GetID(search_str);
|
||||
ImVec2 search_size = ImVec2(size.x, ImGui::GetTextLineHeightWithSpacing() + style.ItemSpacing.y);
|
||||
|
||||
if (!ImGui::IsAnyItemFocused() && !ImGui::IsAnyItemActive() && !ImGui::IsMouseClicked(0))
|
||||
ImGui::SetKeyboardFocusHere(0);
|
||||
|
||||
// The press on Esc key invokes editing of InputText (removes last changes)
|
||||
// So we should save previous value...
|
||||
std::string str = search_str;
|
||||
ImGui::InputTextEx("", NULL, search_str, 20, search_size, ImGuiInputTextFlags_AutoSelectAll, NULL, NULL);
|
||||
edited = ImGui::IsItemEdited();
|
||||
if (edited)
|
||||
view_params.hovered_id = -1;
|
||||
|
||||
process_key_down(ImGuiKey_Escape, [&selected, search_str, str]() {
|
||||
// use 9999 to mark selection as a Esc key
|
||||
selected = 9999;
|
||||
// ... and when Esc key was pressed, than revert search_str value
|
||||
strcpy(search_str, str.c_str());
|
||||
});
|
||||
|
||||
ImGui::BeginChildFrame(id, frame_bb.GetSize());
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
const char* item_text;
|
||||
const char* tooltip;
|
||||
int mouse_hovered = -1;
|
||||
int& hovered_id = view_params.hovered_id;
|
||||
|
||||
while (items_getter(i, &item_text, &tooltip))
|
||||
{
|
||||
selectable(item_text, i == hovered_id);
|
||||
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("%s", /*item_text*/tooltip);
|
||||
view_params.hovered_id = -1;
|
||||
mouse_hovered = i;
|
||||
}
|
||||
|
||||
if (ImGui::IsItemClicked())
|
||||
selected = i;
|
||||
i++;
|
||||
}
|
||||
|
||||
scroll_y(mouse_hovered);
|
||||
|
||||
// Process mouse wheel
|
||||
if (mouse_hovered > 0)
|
||||
process_mouse_wheel(mouse_wheel);
|
||||
|
||||
// process Up/DownArrows and Enter
|
||||
process_key_down(ImGuiKey_UpArrow, [&hovered_id, mouse_hovered]() {
|
||||
if (mouse_hovered > 0)
|
||||
scroll_up();
|
||||
else {
|
||||
if (hovered_id > 0 && hovered_id != size_t(-1))
|
||||
--hovered_id;
|
||||
scroll_y(hovered_id);
|
||||
}
|
||||
});
|
||||
|
||||
process_key_down(ImGuiKey_DownArrow, [&hovered_id, mouse_hovered, i]() {
|
||||
if (mouse_hovered > 0)
|
||||
scroll_down();
|
||||
else {
|
||||
if (hovered_id == size_t(-1))
|
||||
hovered_id = 0;
|
||||
else if (hovered_id < size_t(i - 1))
|
||||
++hovered_id;
|
||||
scroll_y(hovered_id);
|
||||
}
|
||||
});
|
||||
|
||||
process_key_down(ImGuiKey_Enter, [&selected, hovered_id]() {
|
||||
selected = hovered_id;
|
||||
});
|
||||
|
||||
ImGui::ListBoxFooter();
|
||||
|
||||
auto check_box = [&edited, this](const wxString& label, bool& check) {
|
||||
ImGui::SameLine();
|
||||
bool ch = check;
|
||||
checkbox(label, ch);
|
||||
if (ImGui::IsItemClicked()) {
|
||||
check = !check;
|
||||
edited = true;
|
||||
}
|
||||
};
|
||||
|
||||
// add checkboxes for show/hide Categories and Groups
|
||||
text(_L("Use for search")+":");
|
||||
check_box(_L("Type"), view_params.type);
|
||||
check_box(_L("Category"), view_params.category);
|
||||
check_box(_L("Group"), view_params.group);
|
||||
}
|
||||
|
||||
void ImGuiWrapper::disabled_begin(bool disabled)
|
||||
{
|
||||
wxCHECK_RET(!m_disabled, "ImGUI: Unbalanced disabled_begin() call");
|
||||
|
@ -8,6 +8,10 @@
|
||||
|
||||
#include "libslic3r/Point.hpp"
|
||||
|
||||
namespace Slic3r {namespace Search {
|
||||
struct OptionViewParameters;
|
||||
}}
|
||||
|
||||
class wxString;
|
||||
class wxMouseEvent;
|
||||
class wxKeyEvent;
|
||||
@ -73,7 +77,9 @@ public:
|
||||
bool slider_float(const std::string& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f);
|
||||
bool slider_float(const wxString& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f);
|
||||
bool combo(const wxString& label, const std::vector<std::string>& options, int& selection); // Use -1 to not mark any option as selected
|
||||
bool undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool, int, const char**), int& hovered, int& selected);
|
||||
bool undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool, int, const char**), int& hovered, int& selected, int& mouse_wheel);
|
||||
void search_list(const ImVec2& size, bool (*items_getter)(int, const char** label, const char** tooltip), char* search_str,
|
||||
Search::OptionViewParameters& view_params, int& selected, bool& edited, int& mouse_wheel);
|
||||
|
||||
void disabled_begin(bool disabled);
|
||||
void disabled_end();
|
||||
|
@ -134,6 +134,7 @@ void KBShortcutsDialog::fill_shortcuts()
|
||||
{ ctrl + "C", L("Copy to clipboard") },
|
||||
{ ctrl + "V", L("Paste from clipboard") },
|
||||
{ "F5", L("Reload plater from disk") },
|
||||
{ ctrl + "F", L("Search") },
|
||||
// Window
|
||||
{ ctrl + "1", L("Select Plater Tab") },
|
||||
{ ctrl + "2", L("Select Print Settings Tab") },
|
||||
|
@ -90,6 +90,8 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
|
||||
|
||||
// initialize layout
|
||||
auto sizer = new wxBoxSizer(wxVERTICAL);
|
||||
if (m_plater)
|
||||
sizer->Add(m_plater, 1, wxEXPAND);
|
||||
if (m_tabpanel)
|
||||
sizer->Add(m_tabpanel, 1, wxEXPAND);
|
||||
sizer->SetSizeHints(this);
|
||||
@ -306,11 +308,16 @@ void MainFrame::init_tabpanel()
|
||||
// before the MainFrame is fully set up.
|
||||
static_cast<Tab*>(panel)->OnActivate();
|
||||
}
|
||||
else
|
||||
select_tab(0);
|
||||
});
|
||||
|
||||
m_plater = new Slic3r::GUI::Plater(m_tabpanel, this);
|
||||
//! m_plater = new Slic3r::GUI::Plater(m_tabpanel, this);
|
||||
m_plater = new Plater(this, this);
|
||||
|
||||
wxGetApp().plater_ = m_plater;
|
||||
m_tabpanel->AddPage(m_plater, _(L("Plater")));
|
||||
// m_tabpanel->AddPage(m_plater, _(L("Plater")));
|
||||
m_tabpanel->AddPage(new wxPanel(m_tabpanel), _L("Plater")); // empty panel just for Plater tab
|
||||
|
||||
wxGetApp().obj_list()->create_popup_menus();
|
||||
|
||||
@ -336,6 +343,13 @@ void MainFrame::init_tabpanel()
|
||||
}
|
||||
}
|
||||
|
||||
void MainFrame::switch_to(bool plater)
|
||||
{
|
||||
this->m_plater->Show(plater);
|
||||
this->m_tabpanel->Show(!plater);
|
||||
this->Layout();
|
||||
}
|
||||
|
||||
void MainFrame::create_preset_tabs()
|
||||
{
|
||||
wxGetApp().update_label_colours_from_appconfig();
|
||||
@ -739,31 +753,37 @@ void MainFrame::init_menubar()
|
||||
append_menu_item(editMenu, wxID_ANY, _(L("Re&load from disk")) + sep + "F5",
|
||||
_(L("Reload the plater from disk")), [this](wxCommandEvent&) { m_plater->reload_all_from_disk(); },
|
||||
"", nullptr, [this]() {return !m_plater->model().objects.empty(); }, this);
|
||||
|
||||
editMenu->AppendSeparator();
|
||||
append_menu_item(editMenu, wxID_ANY, _(L("Searc&h")) + "\tCtrl+F",
|
||||
_(L("Find option")), [this](wxCommandEvent&) { m_plater->search(/*m_tabpanel->GetCurrentPage() == */m_plater->IsShown()); },
|
||||
"search", nullptr, [this]() {return true; }, this);
|
||||
}
|
||||
|
||||
// Window menu
|
||||
auto windowMenu = new wxMenu();
|
||||
{
|
||||
size_t tab_offset = 0;
|
||||
//! size_t tab_offset = 0;
|
||||
if (m_plater) {
|
||||
append_menu_item(windowMenu, wxID_HIGHEST + 1, _(L("&Plater Tab")) + "\tCtrl+1", _(L("Show the plater")),
|
||||
[this](wxCommandEvent&) { select_tab(0); }, "plater", nullptr,
|
||||
[this/*, tab_offset*/](wxCommandEvent&) { select_tab(/*(size_t)(-1)*/0); }, "plater", nullptr,
|
||||
[this]() {return true; }, this);
|
||||
tab_offset += 1;
|
||||
}
|
||||
if (tab_offset > 0) {
|
||||
//! tab_offset += 1;
|
||||
//! }
|
||||
//! if (tab_offset > 0) {
|
||||
windowMenu->AppendSeparator();
|
||||
}
|
||||
append_menu_item(windowMenu, wxID_HIGHEST + 2, _(L("P&rint Settings Tab")) + "\tCtrl+2", _(L("Show the print settings")),
|
||||
[this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 0); }, "cog", nullptr,
|
||||
[this/*, tab_offset*/](wxCommandEvent&) { select_tab(/*tab_offset + 0*/1); }, "cog", nullptr,
|
||||
[this]() {return true; }, this);
|
||||
wxMenuItem* item_material_tab = append_menu_item(windowMenu, wxID_HIGHEST + 3, _(L("&Filament Settings Tab")) + "\tCtrl+3", _(L("Show the filament settings")),
|
||||
[this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 1); }, "spool", nullptr,
|
||||
[this/*, tab_offset*/](wxCommandEvent&) { select_tab(/*tab_offset + 1*/2); }, "spool", nullptr,
|
||||
[this]() {return true; }, this);
|
||||
m_changeable_menu_items.push_back(item_material_tab);
|
||||
append_menu_item(windowMenu, wxID_HIGHEST + 4, _(L("Print&er Settings Tab")) + "\tCtrl+4", _(L("Show the printer settings")),
|
||||
[this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 2); }, "printer", nullptr,
|
||||
wxMenuItem* item_printer_tab = append_menu_item(windowMenu, wxID_HIGHEST + 4, _(L("Print&er Settings Tab")) + "\tCtrl+4", _(L("Show the printer settings")),
|
||||
[this/*, tab_offset*/](wxCommandEvent&) { select_tab(/*tab_offset + 2*/3); }, "printer", nullptr,
|
||||
[this]() {return true; }, this);
|
||||
m_changeable_menu_items.push_back(item_printer_tab);
|
||||
if (m_plater) {
|
||||
windowMenu->AppendSeparator();
|
||||
append_menu_item(windowMenu, wxID_HIGHEST + 5, _(L("3&D")) + "\tCtrl+5", _(L("Show the 3D editing view")),
|
||||
@ -830,6 +850,9 @@ void MainFrame::init_menubar()
|
||||
[this](wxCommandEvent&) { m_plater->show_view3D_labels(!m_plater->are_view3D_labels_shown()); }, this,
|
||||
[this]() { return m_plater->is_view3D_shown(); }, [this]() { return m_plater->are_view3D_labels_shown(); }, this);
|
||||
#endif // ENABLE_SLOPE_RENDERING
|
||||
append_menu_check_item(viewMenu, wxID_ANY, _(L("&Collapse sidebar")), _(L("Collapse sidebar")),
|
||||
[this](wxCommandEvent&) { m_plater->collapse_sidebar(!m_plater->is_sidebar_collapsed()); }, this,
|
||||
[this]() { return true; }, [this]() { return m_plater->is_sidebar_collapsed(); }, this);
|
||||
}
|
||||
|
||||
// Help menu
|
||||
@ -904,7 +927,9 @@ void MainFrame::update_menubar()
|
||||
m_changeable_menu_items[miSend] ->SetItemLabel((is_fff ? _(L("S&end G-code")) : _(L("S&end to print"))) + dots + "\tCtrl+Shift+G");
|
||||
|
||||
m_changeable_menu_items[miMaterialTab] ->SetItemLabel((is_fff ? _(L("&Filament Settings Tab")) : _(L("Mate&rial Settings Tab"))) + "\tCtrl+3");
|
||||
m_changeable_menu_items[miMaterialTab] ->SetBitmap(create_scaled_bitmap(is_fff ? "spool": "resin"));
|
||||
m_changeable_menu_items[miMaterialTab] ->SetBitmap(create_scaled_bitmap(is_fff ? "spool" : "resin"));
|
||||
|
||||
m_changeable_menu_items[miPrinterTab] ->SetBitmap(create_scaled_bitmap(is_fff ? "printer" : "sla_printer"));
|
||||
}
|
||||
|
||||
// To perform the "Quck Slice", "Quick Slice and Save As", "Repeat last Quick Slice" and "Slice to SVG".
|
||||
@ -1220,9 +1245,17 @@ void MainFrame::load_config(const DynamicPrintConfig& config)
|
||||
#endif
|
||||
}
|
||||
|
||||
void MainFrame::select_tab(size_t tab) const
|
||||
void MainFrame::select_tab(size_t tab)
|
||||
{
|
||||
m_tabpanel->SetSelection(tab);
|
||||
if (tab == /*(size_t)(-1)*/0) {
|
||||
if (m_plater && !m_plater->IsShown())
|
||||
this->switch_to(true);
|
||||
}
|
||||
else {
|
||||
if (m_plater && m_plater->IsShown())
|
||||
switch_to(false);
|
||||
m_tabpanel->SetSelection(tab);
|
||||
}
|
||||
}
|
||||
|
||||
// Set a camera direction, zoom to all objects.
|
||||
|
@ -86,6 +86,7 @@ class MainFrame : public DPIFrame
|
||||
miExport = 0, // Export G-code Export
|
||||
miSend, // Send G-code Send to print
|
||||
miMaterialTab, // Filament Settings Material Settings
|
||||
miPrinterTab, // Different bitmap for Printer Settings
|
||||
};
|
||||
|
||||
// vector of a MenuBar items changeable in respect to printer technology
|
||||
@ -108,6 +109,7 @@ public:
|
||||
void update_title();
|
||||
|
||||
void init_tabpanel();
|
||||
void switch_to(bool plater);
|
||||
void create_preset_tabs();
|
||||
void add_created_tab(Tab* panel);
|
||||
void init_menubar();
|
||||
@ -128,7 +130,7 @@ public:
|
||||
void export_configbundle();
|
||||
void load_configbundle(wxString file = wxEmptyString);
|
||||
void load_config(const DynamicPrintConfig& config);
|
||||
void select_tab(size_t tab) const;
|
||||
void select_tab(size_t tab);
|
||||
void select_view(const std::string& direction);
|
||||
// Propagate changed configuration from the Tab to the Plater and save changes to the AppConfig
|
||||
void on_config_changed(DynamicPrintConfig* cfg) const ;
|
||||
|
@ -108,6 +108,7 @@ void OptionsGroup::add_undo_buttuns_to_sizer(wxSizer* sizer, const t_field& fiel
|
||||
return;
|
||||
}
|
||||
|
||||
sizer->Add(field->m_blinking_bmp, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 2);
|
||||
sizer->Add(field->m_Undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL);
|
||||
sizer->Add(field->m_Undo_btn, 0, wxALIGN_CENTER_VERTICAL);
|
||||
}
|
||||
@ -378,6 +379,9 @@ Option ConfigOptionsGroup::get_option(const std::string& opt_key, int opt_index
|
||||
std::pair<std::string, int> pair(opt_key, opt_index);
|
||||
m_opt_map.emplace(opt_id, pair);
|
||||
|
||||
if (m_show_modified_btns) // fill group and category values just fro options from Settings Tab
|
||||
wxGetApp().sidebar().get_searcher().add_key(opt_id, title, config_category);
|
||||
|
||||
return Option(*m_config->def()->get(opt_key), opt_id);
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include "Field.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "I18N.hpp"
|
||||
|
||||
// Translate the ifdef
|
||||
#ifdef __WXOSX__
|
||||
@ -59,7 +60,7 @@ public:
|
||||
m_extra_widgets.push_back(widget);
|
||||
}
|
||||
Line(wxString label, wxString tooltip) :
|
||||
label(label), label_tooltip(tooltip) {}
|
||||
label(_(label)), label_tooltip(_(tooltip)) {}
|
||||
|
||||
const std::vector<widget_t>& get_extra_widgets() const {return m_extra_widgets;}
|
||||
const std::vector<Option>& get_options() const { return m_options; }
|
||||
@ -78,7 +79,7 @@ class OptionsGroup {
|
||||
wxStaticBox* stb;
|
||||
public:
|
||||
const bool staticbox {true};
|
||||
const wxString title {wxString("")};
|
||||
const wxString title;
|
||||
size_t label_width = 20 ;// {200};
|
||||
wxSizer* sizer {nullptr};
|
||||
column_t extra_column {nullptr};
|
||||
@ -175,7 +176,7 @@ public:
|
||||
m_show_modified_btns(is_tab_opt),
|
||||
staticbox(title!=""), extra_column(extra_clmn) {
|
||||
if (staticbox) {
|
||||
stb = new wxStaticBox(_parent, wxID_ANY, title);
|
||||
stb = new wxStaticBox(_parent, wxID_ANY, _(title));
|
||||
if (!wxOSX) stb->SetBackgroundStyle(wxBG_STYLE_PAINT);
|
||||
stb->SetFont(wxGetApp().bold_font());
|
||||
} else
|
||||
@ -194,6 +195,7 @@ public:
|
||||
#else
|
||||
sizer->Add(m_grid_sizer, 0, wxEXPAND | wxALL, wxOSX||!staticbox ? 0: 5);
|
||||
#endif /* __WXGTK__ */
|
||||
|
||||
}
|
||||
|
||||
wxGridSizer* get_grid_sizer() { return m_grid_sizer; }
|
||||
@ -247,6 +249,8 @@ public:
|
||||
bool m_full_labels {0};
|
||||
t_opt_map m_opt_map;
|
||||
|
||||
std::string config_category;
|
||||
|
||||
void set_config(DynamicPrintConfig* config) { m_config = config; }
|
||||
Option get_option(const std::string& opt_key, int opt_index = -1);
|
||||
Line create_single_option_line(const std::string& title, int idx = -1) /*const*/{
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/nowide/convert.hpp>
|
||||
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/stattext.h>
|
||||
@ -357,6 +358,9 @@ PresetBitmapComboBox(parent, wxSize(15 * wxGetApp().em_unit(), -1)),
|
||||
|
||||
wxGetApp().tab_panel()->ChangeSelection(page_id);
|
||||
|
||||
// Switch to Settings NotePad
|
||||
wxGetApp().mainframe->switch_to(false);
|
||||
|
||||
/* In a case of a multi-material printing, for editing another Filament Preset
|
||||
* it's needed to select this preset for the "Filament settings" Tab
|
||||
*/
|
||||
@ -716,6 +720,9 @@ struct Sidebar::priv
|
||||
ScalableButton *btn_remove_device;
|
||||
ScalableButton* btn_export_gcode_removable; //exports to removable drives (appears only if removable drive is connected)
|
||||
|
||||
bool is_collapsed {false};
|
||||
Search::OptionsSearcher searcher;
|
||||
|
||||
priv(Plater *plater) : plater(plater) {}
|
||||
~priv();
|
||||
|
||||
@ -764,6 +771,8 @@ Sidebar::Sidebar(Plater *parent)
|
||||
p->scrolled = new wxScrolledWindow(this);
|
||||
p->scrolled->SetScrollbars(0, 100, 1, 2);
|
||||
|
||||
SetFont(wxGetApp().normal_font());
|
||||
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||
|
||||
// Sizer in the scrolled area
|
||||
auto *scrolled_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
@ -1082,6 +1091,21 @@ void Sidebar::msw_rescale()
|
||||
p->scrolled->Layout();
|
||||
}
|
||||
|
||||
void Sidebar::search()
|
||||
{
|
||||
p->searcher.search();
|
||||
}
|
||||
|
||||
void Sidebar::jump_to_option(size_t selected)
|
||||
{
|
||||
const Search::Option& opt = p->searcher.get_option(selected);
|
||||
wxGetApp().get_tab(opt.type)->activate_option(boost::nowide::narrow(opt.opt_key), boost::nowide::narrow(opt.category));
|
||||
|
||||
// Switch to the Settings NotePad, if plater is shown
|
||||
if (p->plater->IsShown())
|
||||
wxGetApp().mainframe->switch_to(false);
|
||||
}
|
||||
|
||||
ObjectManipulation* Sidebar::obj_manipul()
|
||||
{
|
||||
return p->object_manipulation;
|
||||
@ -1339,6 +1363,23 @@ bool Sidebar::is_multifilament()
|
||||
return p->combos_filament.size() > 1;
|
||||
}
|
||||
|
||||
static std::vector<Search::InputInfo> get_search_inputs(ConfigOptionMode mode)
|
||||
{
|
||||
std::vector<Search::InputInfo> ret {};
|
||||
|
||||
auto& tabs_list = wxGetApp().tabs_list;
|
||||
auto print_tech = wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology();
|
||||
for (auto tab : tabs_list)
|
||||
if (tab->supports_printer_technology(print_tech))
|
||||
ret.emplace_back(Search::InputInfo {tab->get_config(), tab->type(), mode});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Sidebar::update_searcher()
|
||||
{
|
||||
p->searcher.init(get_search_inputs(m_mode));
|
||||
}
|
||||
|
||||
void Sidebar::update_mode()
|
||||
{
|
||||
@ -1346,6 +1387,7 @@ void Sidebar::update_mode()
|
||||
|
||||
update_reslice_btn_tooltip();
|
||||
update_mode_sizer();
|
||||
update_searcher();
|
||||
|
||||
wxWindowUpdateLocker noUpdates(this);
|
||||
|
||||
@ -1358,11 +1400,35 @@ void Sidebar::update_mode()
|
||||
Layout();
|
||||
}
|
||||
|
||||
bool Sidebar::is_collapsed() { return p->is_collapsed; }
|
||||
|
||||
void Sidebar::collapse(bool collapse)
|
||||
{
|
||||
p->is_collapsed = collapse;
|
||||
|
||||
this->Show(!collapse);
|
||||
p->plater->Layout();
|
||||
|
||||
// save collapsing state to the AppConfig
|
||||
wxGetApp().app_config->set("collapsed_sidebar", collapse ? "1" : "0");
|
||||
}
|
||||
|
||||
|
||||
std::vector<PresetComboBox*>& Sidebar::combos_filament()
|
||||
{
|
||||
return p->combos_filament;
|
||||
}
|
||||
|
||||
Search::OptionsSearcher& Sidebar::get_searcher()
|
||||
{
|
||||
return p->searcher;
|
||||
}
|
||||
|
||||
std::string& Sidebar::get_search_line()
|
||||
{
|
||||
return p->searcher.search_string();
|
||||
}
|
||||
|
||||
// Plater::DropTarget
|
||||
|
||||
class PlaterDropTarget : public wxFileDropTarget
|
||||
@ -1557,6 +1623,9 @@ struct Plater::priv
|
||||
bool are_view3D_labels_shown() const { return (current_panel == view3D) && view3D->get_canvas3d()->are_labels_shown(); }
|
||||
void show_view3D_labels(bool show) { if (current_panel == view3D) view3D->get_canvas3d()->show_labels(show); }
|
||||
|
||||
bool is_sidebar_collapsed() const { return sidebar->is_collapsed(); }
|
||||
void collapse_sidebar(bool show) { sidebar->collapse(show); }
|
||||
|
||||
#if ENABLE_SLOPE_RENDERING
|
||||
bool is_view3D_slope_shown() const { return (current_panel == view3D) && view3D->get_canvas3d()->is_slope_shown(); }
|
||||
void show_view3D_slope(bool show) { if (current_panel == view3D) view3D->get_canvas3d()->show_slope(show); }
|
||||
@ -1877,6 +1946,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_RESETGIZMOS, [this](SimpleEvent&) { reset_all_gizmos(); });
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_UNDO, [this](SimpleEvent&) { this->undo(); });
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_REDO, [this](SimpleEvent&) { this->redo(); });
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_COLLAPSE_SIDEBAR, [this](SimpleEvent&) { this->q->collapse_sidebar(!this->q->is_sidebar_collapsed()); });
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, [this](SimpleEvent&) { this->view3D->get_canvas3d()->reset_layer_height_profile(); });
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, [this](Event<float>& evt) { this->view3D->get_canvas3d()->adaptive_layer_height_profile(evt.data); });
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, [this](HeightProfileSmoothEvent& evt) { this->view3D->get_canvas3d()->smooth_layer_height_profile(evt.data); });
|
||||
@ -1987,6 +2057,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||
});
|
||||
wxGetApp().other_instance_message_handler()->init(this->q);
|
||||
|
||||
|
||||
// collapse sidebar according to saved value
|
||||
sidebar->collapse(wxGetApp().app_config->get("collapsed_sidebar") == "1");
|
||||
}
|
||||
|
||||
Plater::priv::~priv()
|
||||
@ -3245,13 +3318,14 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt)
|
||||
|
||||
// update plater with new config
|
||||
q->on_config_change(wxGetApp().preset_bundle->full_config());
|
||||
if (preset_type == Preset::TYPE_PRINTER) {
|
||||
/* Settings list can be changed after printer preset changing, so
|
||||
* update all settings items for all item had it.
|
||||
* Furthermore, Layers editing is implemented only for FFF printers
|
||||
* and for SLA presets they should be deleted
|
||||
*/
|
||||
if (preset_type == Preset::TYPE_PRINTER)
|
||||
wxGetApp().obj_list()->update_object_list_by_printer_technology();
|
||||
}
|
||||
}
|
||||
|
||||
void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
|
||||
@ -4354,6 +4428,9 @@ bool Plater::is_view3D_shown() const { return p->is_view3D_shown(); }
|
||||
bool Plater::are_view3D_labels_shown() const { return p->are_view3D_labels_shown(); }
|
||||
void Plater::show_view3D_labels(bool show) { p->show_view3D_labels(show); }
|
||||
|
||||
bool Plater::is_sidebar_collapsed() const { return p->is_sidebar_collapsed(); }
|
||||
void Plater::collapse_sidebar(bool show) { p->collapse_sidebar(show); }
|
||||
|
||||
#if ENABLE_SLOPE_RENDERING
|
||||
bool Plater::is_view3D_slope_shown() const { return p->is_view3D_slope_shown(); }
|
||||
void Plater::show_view3D_slope(bool show) { p->show_view3D_slope(show); }
|
||||
@ -4926,6 +5003,18 @@ void Plater::undo_redo_topmost_string_getter(const bool is_undo, std::string& ou
|
||||
out_text = "";
|
||||
}
|
||||
|
||||
bool Plater::search_string_getter(int idx, const char** label, const char** tooltip)
|
||||
{
|
||||
const Search::OptionsSearcher& search_list = p->sidebar->get_searcher();
|
||||
|
||||
if (0 <= idx && (size_t)idx < search_list.size()) {
|
||||
search_list[idx].get_marked_label_and_tooltip(label, tooltip);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Plater::on_extruders_change(size_t num_extruders)
|
||||
{
|
||||
auto& choices = sidebar().combos_filament();
|
||||
@ -4985,8 +5074,11 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
|
||||
}
|
||||
|
||||
p->config->set_key_value(opt_key, config.option(opt_key)->clone());
|
||||
if (opt_key == "printer_technology")
|
||||
if (opt_key == "printer_technology") {
|
||||
this->set_printer_technology(config.opt_enum<PrinterTechnology>(opt_key));
|
||||
// print technology is changed, so we should to update a search list
|
||||
p->sidebar->update_searcher();
|
||||
}
|
||||
else if ((opt_key == "bed_shape") || (opt_key == "bed_custom_texture") || (opt_key == "bed_custom_model")) {
|
||||
bed_shape_changed = true;
|
||||
update_scheduled = true;
|
||||
@ -5296,6 +5388,27 @@ void Plater::paste_from_clipboard()
|
||||
p->view3D->get_canvas3d()->get_selection().paste_from_clipboard();
|
||||
}
|
||||
|
||||
void Plater::search(bool plater_is_active)
|
||||
{
|
||||
if (plater_is_active) {
|
||||
wxKeyEvent evt;
|
||||
#ifdef __APPLE__
|
||||
evt.m_keyCode = 'f';
|
||||
#else /* __APPLE__ */
|
||||
evt.m_keyCode = WXK_CONTROL_F;
|
||||
#endif /* __APPLE__ */
|
||||
evt.SetControlDown(true);
|
||||
canvas3D()->on_char(evt);
|
||||
}
|
||||
else
|
||||
{
|
||||
wxPoint pos = this->ClientToScreen(wxPoint(0, 0));
|
||||
pos.x += em_unit(this) * 40;
|
||||
pos.y += em_unit(this) * 4;
|
||||
p->sidebar->get_searcher().search_dialog->Popup(pos);
|
||||
}
|
||||
}
|
||||
|
||||
void Plater::msw_rescale()
|
||||
{
|
||||
p->preview->msw_rescale();
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "libslic3r/BoundingBox.hpp"
|
||||
#include "Jobs/Job.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
#include "Search.hpp"
|
||||
|
||||
class wxButton;
|
||||
class ScalableButton;
|
||||
@ -106,6 +107,8 @@ public:
|
||||
void update_mode_sizer() const;
|
||||
void update_reslice_btn_tooltip() const;
|
||||
void msw_rescale();
|
||||
void search();
|
||||
void jump_to_option(size_t selected);
|
||||
|
||||
ObjectManipulation* obj_manipul();
|
||||
ObjectList* obj_list();
|
||||
@ -129,8 +132,14 @@ public:
|
||||
bool show_export_removable(bool show) const;
|
||||
bool is_multifilament();
|
||||
void update_mode();
|
||||
bool is_collapsed();
|
||||
void collapse(bool collapse);
|
||||
void update_searcher();
|
||||
|
||||
std::vector<PresetComboBox*>& combos_filament();
|
||||
Search::OptionsSearcher& get_searcher();
|
||||
std::string& get_search_line();
|
||||
|
||||
std::vector<PresetComboBox*>& combos_filament();
|
||||
private:
|
||||
struct priv;
|
||||
std::unique_ptr<priv> p;
|
||||
@ -178,6 +187,9 @@ public:
|
||||
bool are_view3D_labels_shown() const;
|
||||
void show_view3D_labels(bool show);
|
||||
|
||||
bool is_sidebar_collapsed() const;
|
||||
void collapse_sidebar(bool show);
|
||||
|
||||
#if ENABLE_SLOPE_RENDERING
|
||||
bool is_view3D_slope_shown() const;
|
||||
void show_view3D_slope(bool show);
|
||||
@ -233,6 +245,7 @@ public:
|
||||
void redo_to(int selection);
|
||||
bool undo_redo_string_getter(const bool is_undo, int idx, const char** out_text);
|
||||
void undo_redo_topmost_string_getter(const bool is_undo, std::string& out_text);
|
||||
bool search_string_getter(int idx, const char** label, const char** tooltip);
|
||||
// For the memory statistics.
|
||||
const Slic3r::UndoRedo::Stack& undo_redo_stack_main() const;
|
||||
// Enter / leave the Gizmos specific Undo / Redo stack. To be used by the SLA support point editing gizmo.
|
||||
@ -278,6 +291,7 @@ public:
|
||||
|
||||
void copy_selection_to_clipboard();
|
||||
void paste_from_clipboard();
|
||||
void search(bool plater_is_active);
|
||||
|
||||
bool can_delete() const;
|
||||
bool can_delete_all() const;
|
||||
|
585
src/slic3r/GUI/Search.cpp
Normal file
585
src/slic3r/GUI/Search.cpp
Normal file
@ -0,0 +1,585 @@
|
||||
#include "Search.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/nowide/convert.hpp>
|
||||
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "Tab.hpp"
|
||||
#include "PresetBundle.hpp"
|
||||
|
||||
#define FTS_FUZZY_MATCH_IMPLEMENTATION
|
||||
#include "fts_fuzzy_match.h"
|
||||
|
||||
#include "imgui/imconfig.h"
|
||||
|
||||
using boost::optional;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
using GUI::from_u8;
|
||||
using GUI::into_u8;
|
||||
|
||||
namespace Search {
|
||||
|
||||
static std::map<Preset::Type, std::string> NameByType = {
|
||||
{ Preset::TYPE_PRINT, L("Print") },
|
||||
{ Preset::TYPE_FILAMENT, L("Filament") },
|
||||
{ Preset::TYPE_SLA_MATERIAL, L("Material") },
|
||||
{ Preset::TYPE_SLA_PRINT, L("Print") },
|
||||
{ Preset::TYPE_PRINTER, L("Printer") }
|
||||
};
|
||||
|
||||
FMFlag Option::fuzzy_match(wchar_t const* search_pattern, int& outScore, std::vector<uint16_t> &out_matches) const
|
||||
{
|
||||
FMFlag flag = fmUndef;
|
||||
int score;
|
||||
|
||||
uint16_t matches[fts::max_matches + 1]; // +1 for the stopper
|
||||
auto save_matches = [&matches, &out_matches]() {
|
||||
size_t cnt = 0;
|
||||
for (; matches[cnt] != fts::stopper; ++cnt);
|
||||
out_matches.assign(matches, matches + cnt);
|
||||
};
|
||||
if (fts::fuzzy_match(search_pattern, label_local.c_str(), score, matches) && outScore < score) {
|
||||
outScore = score; flag = fmLabelLocal ; save_matches(); }
|
||||
if (fts::fuzzy_match(search_pattern, group_local.c_str(), score, matches) && outScore < score) {
|
||||
outScore = score; flag = fmGroupLocal ; save_matches(); }
|
||||
if (fts::fuzzy_match(search_pattern, category_local.c_str(), score, matches) && outScore < score) {
|
||||
outScore = score; flag = fmCategoryLocal; save_matches(); }
|
||||
if (fts::fuzzy_match(search_pattern, opt_key.c_str(), score, matches) && outScore < score) {
|
||||
outScore = score; flag = fmOptKey ; save_matches(); }
|
||||
if (fts::fuzzy_match(search_pattern, label.c_str(), score, matches) && outScore < score) {
|
||||
outScore = score; flag = fmLabel ; save_matches(); }
|
||||
if (fts::fuzzy_match(search_pattern, group.c_str(), score, matches) && outScore < score) {
|
||||
outScore = score; flag = fmGroup ; save_matches(); }
|
||||
if (fts::fuzzy_match(search_pattern, category.c_str(), score, matches) && outScore < score) {
|
||||
outScore = score; flag = fmCategory ; save_matches(); }
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
void FoundOption::get_marked_label_and_tooltip(const char** label_, const char** tooltip_) const
|
||||
{
|
||||
*label_ = marked_label.c_str();
|
||||
*tooltip_ = tooltip.c_str();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
//void change_opt_key(std::string& opt_key, DynamicPrintConfig* config)
|
||||
void change_opt_key(std::string& opt_key, DynamicPrintConfig* config, int& cnt)
|
||||
{
|
||||
T* opt_cur = static_cast<T*>(config->option(opt_key));
|
||||
cnt = opt_cur->values.size();
|
||||
return;
|
||||
|
||||
if (opt_cur->values.size() > 0)
|
||||
opt_key += "#" + std::to_string(0);
|
||||
}
|
||||
|
||||
void OptionsSearcher::append_options(DynamicPrintConfig* config, Preset::Type type, ConfigOptionMode mode)
|
||||
{
|
||||
auto emplace = [this, type](const std::string opt_key, const wxString& label)
|
||||
{
|
||||
const GroupAndCategory& gc = groups_and_categories[opt_key];
|
||||
if (gc.group.IsEmpty() || gc.category.IsEmpty())
|
||||
return;
|
||||
|
||||
wxString suffix;
|
||||
if (gc.category == "Machine limits")
|
||||
suffix = opt_key.back()=='1' ? L("Stealth") : L("Normal");
|
||||
|
||||
if (!label.IsEmpty())
|
||||
options.emplace_back(Option{ boost::nowide::widen(opt_key), type,
|
||||
(label+ " " + suffix).ToStdWstring(), (_(label)+ " " + _(suffix)).ToStdWstring(),
|
||||
gc.group.ToStdWstring(), _(gc.group).ToStdWstring(),
|
||||
gc.category.ToStdWstring(), _(gc.category).ToStdWstring() });
|
||||
};
|
||||
|
||||
for (std::string opt_key : config->keys())
|
||||
{
|
||||
const ConfigOptionDef& opt = config->def()->options.at(opt_key);
|
||||
if (opt.mode > mode)
|
||||
continue;
|
||||
|
||||
int cnt = 0;
|
||||
|
||||
if ( (type == Preset::TYPE_SLA_MATERIAL || type == Preset::TYPE_PRINTER) && opt_key != "bed_shape")
|
||||
switch (config->option(opt_key)->type())
|
||||
{
|
||||
case coInts: change_opt_key<ConfigOptionInts >(opt_key, config, cnt); break;
|
||||
case coBools: change_opt_key<ConfigOptionBools >(opt_key, config, cnt); break;
|
||||
case coFloats: change_opt_key<ConfigOptionFloats >(opt_key, config, cnt); break;
|
||||
case coStrings: change_opt_key<ConfigOptionStrings >(opt_key, config, cnt); break;
|
||||
case coPercents:change_opt_key<ConfigOptionPercents >(opt_key, config, cnt); break;
|
||||
case coPoints: change_opt_key<ConfigOptionPoints >(opt_key, config, cnt); break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
wxString label = opt.full_label.empty() ? opt.label : opt.full_label;
|
||||
|
||||
if (cnt == 0)
|
||||
emplace(opt_key, label);
|
||||
else
|
||||
for (int i = 0; i < cnt; ++i)
|
||||
emplace(opt_key + "#" + std::to_string(i), label);
|
||||
|
||||
/*const GroupAndCategory& gc = groups_and_categories[opt_key];
|
||||
if (gc.group.IsEmpty() || gc.category.IsEmpty())
|
||||
continue;
|
||||
|
||||
if (!label.IsEmpty())
|
||||
options.emplace_back(Option{opt_key, type,
|
||||
label, _(label),
|
||||
gc.group, _(gc.group),
|
||||
gc.category, _(gc.category) });*/
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap a string with ColorMarkerStart and ColorMarkerEnd symbols
|
||||
static wxString wrap_string(const wxString& str)
|
||||
{
|
||||
return wxString::Format("%c%s%c", ImGui::ColorMarkerStart, str, ImGui::ColorMarkerEnd);
|
||||
}
|
||||
|
||||
// Mark a string using ColorMarkerStart and ColorMarkerEnd symbols
|
||||
static std::wstring mark_string(const std::wstring &str, const std::vector<uint16_t> &matches)
|
||||
{
|
||||
std::wstring out;
|
||||
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);
|
||||
}
|
||||
|
||||
bool OptionsSearcher::search(const std::string& search, bool force/* = false*/)
|
||||
{
|
||||
if (search_line == search && !force)
|
||||
return false;
|
||||
|
||||
found.clear();
|
||||
|
||||
bool full_list = search.empty();
|
||||
wxString sep = " : ";
|
||||
|
||||
auto get_label = [this, sep](const Option& opt)
|
||||
{
|
||||
wxString label;
|
||||
if (view_params.type)
|
||||
label += _(NameByType[opt.type]) + sep;
|
||||
if (view_params.category)
|
||||
label += opt.category_local + sep;
|
||||
if (view_params.group)
|
||||
label += opt.group_local + sep;
|
||||
label += opt.label_local;
|
||||
return label;
|
||||
};
|
||||
|
||||
auto get_tooltip = [this, sep](const Option& opt)
|
||||
{
|
||||
return _(NameByType[opt.type]) + sep +
|
||||
opt.category_local + sep +
|
||||
opt.group_local + sep + opt.label_local;
|
||||
};
|
||||
|
||||
std::vector<uint16_t> matches;
|
||||
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));
|
||||
found.emplace_back(FoundOption{ label, label, into_u8(get_tooltip(opt)), i, fmUndef, 0 });
|
||||
continue;
|
||||
}
|
||||
|
||||
int score = 0;
|
||||
FMFlag fuzzy_match_flag = opt.fuzzy_match(boost::nowide::widen(search).c_str(), score, matches);
|
||||
if (fuzzy_match_flag != fmUndef)
|
||||
{
|
||||
wxString label;
|
||||
|
||||
if (view_params.type)
|
||||
label += _(NameByType[opt.type]) + sep;
|
||||
if (fuzzy_match_flag == fmCategoryLocal)
|
||||
label += mark_string(opt.category_local, matches) + sep;
|
||||
else if (view_params.category)
|
||||
label += opt.category_local + sep;
|
||||
if (fuzzy_match_flag == fmGroupLocal)
|
||||
label += mark_string(opt.group_local, matches) + sep;
|
||||
else if (view_params.group)
|
||||
label += opt.group_local + sep;
|
||||
label += ((fuzzy_match_flag == fmLabelLocal) ? mark_string(opt.label_local, matches) : opt.label_local) + sep;
|
||||
|
||||
switch (fuzzy_match_flag) {
|
||||
case fmLabelLocal:
|
||||
case fmGroupLocal:
|
||||
case fmCategoryLocal:
|
||||
break;
|
||||
case fmLabel: label = get_label(opt) + "(" + mark_string(opt.label, matches) + ")"; break;
|
||||
case fmGroup: label = get_label(opt) + "(" + mark_string(opt.group, matches) + ")"; break;
|
||||
case fmCategory: label = get_label(opt) + "(" + mark_string(opt.category, matches) + ")"; break;
|
||||
case fmOptKey: label = get_label(opt) + "(" + mark_string(opt.opt_key, matches) + ")"; break;
|
||||
case fmUndef: assert(false); break;
|
||||
}
|
||||
|
||||
std::string label_plain = into_u8(label);
|
||||
boost::erase_all(label_plain, std::wstring(1, wchar_t(ImGui::ColorMarkerStart)));
|
||||
boost::erase_all(label_plain, std::wstring(1, wchar_t(ImGui::ColorMarkerEnd)));
|
||||
found.emplace_back(FoundOption{ label_plain, into_u8(label), into_u8(get_tooltip(opt)), i, fuzzy_match_flag, score });
|
||||
}
|
||||
}
|
||||
|
||||
if (!full_list)
|
||||
sort_found();
|
||||
|
||||
if (search_line != search)
|
||||
search_line = search;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
OptionsSearcher::OptionsSearcher()
|
||||
{
|
||||
search_dialog = new SearchDialog(this);
|
||||
}
|
||||
|
||||
OptionsSearcher::~OptionsSearcher()
|
||||
{
|
||||
if (search_dialog)
|
||||
search_dialog->Destroy();
|
||||
}
|
||||
|
||||
void OptionsSearcher::init(std::vector<InputInfo> input_values)
|
||||
{
|
||||
options.clear();
|
||||
for (auto i : input_values)
|
||||
append_options(i.config, i.type, i.mode);
|
||||
sort_options();
|
||||
|
||||
search(search_line, true);
|
||||
}
|
||||
|
||||
void OptionsSearcher::apply(DynamicPrintConfig* config, Preset::Type type, ConfigOptionMode mode)
|
||||
{
|
||||
if (options.empty())
|
||||
return;
|
||||
|
||||
options.erase(std::remove_if(options.begin(), options.end(), [type](Option opt) {
|
||||
return opt.type == type;
|
||||
}), options.end());
|
||||
|
||||
append_options(config, type, mode);
|
||||
|
||||
sort_options();
|
||||
|
||||
search(search_line, true);
|
||||
}
|
||||
|
||||
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 options[found[pos_in_filter].option_idx];
|
||||
}
|
||||
|
||||
void OptionsSearcher::add_key(const std::string& opt_key, const wxString& group, const wxString& category)
|
||||
{
|
||||
groups_and_categories[opt_key] = GroupAndCategory{group, category};
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------
|
||||
// SearchComboPopup
|
||||
//------------------------------------------
|
||||
|
||||
|
||||
void SearchComboPopup::Init()
|
||||
{
|
||||
this->Bind(wxEVT_MOTION, &SearchComboPopup::OnMouseMove, this);
|
||||
this->Bind(wxEVT_LEFT_UP, &SearchComboPopup::OnMouseClick, this);
|
||||
this->Bind(wxEVT_KEY_DOWN, &SearchComboPopup::OnKeyDown, this);
|
||||
}
|
||||
|
||||
bool SearchComboPopup::Create(wxWindow* parent)
|
||||
{
|
||||
return wxListBox::Create(parent, 1, wxPoint(0, 0), wxDefaultSize);
|
||||
}
|
||||
|
||||
void SearchComboPopup::SetStringValue(const wxString& s)
|
||||
{
|
||||
int n = wxListBox::FindString(s);
|
||||
if (n >= 0 && n < int(wxListBox::GetCount()))
|
||||
wxListBox::Select(n);
|
||||
|
||||
// save a combo control's string
|
||||
m_input_string = s;
|
||||
}
|
||||
|
||||
void SearchComboPopup::ProcessSelection(int selection)
|
||||
{
|
||||
wxCommandEvent event(wxEVT_LISTBOX, GetId());
|
||||
event.SetInt(selection);
|
||||
event.SetEventObject(this);
|
||||
ProcessEvent(event);
|
||||
|
||||
Dismiss();
|
||||
}
|
||||
|
||||
void SearchComboPopup::OnMouseMove(wxMouseEvent& event)
|
||||
{
|
||||
wxPoint pt = wxGetMousePosition() - this->GetScreenPosition();
|
||||
int selection = this->HitTest(pt);
|
||||
wxListBox::Select(selection);
|
||||
}
|
||||
|
||||
void SearchComboPopup::OnMouseClick(wxMouseEvent&)
|
||||
{
|
||||
int selection = wxListBox::GetSelection();
|
||||
SetSelection(wxNOT_FOUND);
|
||||
ProcessSelection(selection);
|
||||
}
|
||||
|
||||
void SearchComboPopup::OnKeyDown(wxKeyEvent& event)
|
||||
{
|
||||
int key = event.GetKeyCode();
|
||||
|
||||
// change selected item in the list
|
||||
if (key == WXK_UP || key == WXK_DOWN)
|
||||
{
|
||||
int selection = wxListBox::GetSelection();
|
||||
|
||||
if (key == WXK_UP && selection > 0)
|
||||
selection--;
|
||||
if (key == WXK_DOWN && selection < int(wxListBox::GetCount() - 1))
|
||||
selection++;
|
||||
|
||||
wxListBox::Select(selection);
|
||||
}
|
||||
// send wxEVT_LISTBOX event if "Enter" was pushed
|
||||
else if (key == WXK_NUMPAD_ENTER || key == WXK_RETURN)
|
||||
ProcessSelection(wxListBox::GetSelection());
|
||||
else
|
||||
event.Skip(); // !Needed to have EVT_CHAR generated as well
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------
|
||||
// SearchDialog
|
||||
//------------------------------------------
|
||||
|
||||
SearchDialog::SearchDialog(OptionsSearcher* searcher)
|
||||
: GUI::DPIDialog(NULL, wxID_ANY, _L("Search"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
|
||||
searcher(searcher)
|
||||
{
|
||||
SetFont(GUI::wxGetApp().normal_font());
|
||||
wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
|
||||
SetBackgroundColour(bgr_clr);
|
||||
|
||||
default_string = _L("Type here to search");
|
||||
int border = 10;
|
||||
|
||||
search_line = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize);
|
||||
|
||||
// wxWANTS_CHARS style is neede for process Enter key press
|
||||
search_list = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxSize(em_unit() * 40, em_unit() * 30), 0, NULL, wxWANTS_CHARS);
|
||||
|
||||
wxBoxSizer* check_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
check_type = new wxCheckBox(this, wxID_ANY, _L("Type"));
|
||||
check_category = new wxCheckBox(this, wxID_ANY, _L("Category"));
|
||||
check_group = new wxCheckBox(this, wxID_ANY, _L("Group"));
|
||||
|
||||
wxStdDialogButtonSizer* cancel_btn = this->CreateStdDialogButtonSizer(wxCANCEL);
|
||||
|
||||
check_sizer->Add(check_type, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, border);
|
||||
check_sizer->Add(check_category, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, border);
|
||||
check_sizer->Add(check_group, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, border);
|
||||
check_sizer->AddStretchSpacer(border);
|
||||
check_sizer->Add(cancel_btn, 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
topSizer->Add(search_line, 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border);
|
||||
topSizer->Add(search_list, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border);
|
||||
topSizer->Add(check_sizer, 0, wxEXPAND | wxALL, border);
|
||||
|
||||
search_line->Bind(wxEVT_TEXT, &SearchDialog::OnInputText, this);
|
||||
search_line->Bind(wxEVT_LEFT_UP, &SearchDialog::OnLeftUpInTextCtrl, this);
|
||||
// process wxEVT_KEY_DOWN to navigate inside search_list, if ArrowUp/Down was pressed
|
||||
search_line->Bind(wxEVT_KEY_DOWN,&SearchDialog::OnKeyDown, this);
|
||||
|
||||
search_list->Bind(wxEVT_MOTION, &SearchDialog::OnMouseMove, this);
|
||||
search_list->Bind(wxEVT_LEFT_UP, &SearchDialog::OnMouseClick, this);
|
||||
search_list->Bind(wxEVT_KEY_DOWN,&SearchDialog::OnKeyDown, this);
|
||||
|
||||
check_type ->Bind(wxEVT_CHECKBOX, &SearchDialog::OnCheck, this);
|
||||
check_category->Bind(wxEVT_CHECKBOX, &SearchDialog::OnCheck, this);
|
||||
check_group ->Bind(wxEVT_CHECKBOX, &SearchDialog::OnCheck, this);
|
||||
|
||||
this->Bind(wxEVT_LISTBOX, &SearchDialog::OnSelect, this);
|
||||
|
||||
SetSizer(topSizer);
|
||||
topSizer->SetSizeHints(this);
|
||||
}
|
||||
|
||||
void SearchDialog::Popup(wxPoint position /*= wxDefaultPosition*/)
|
||||
{
|
||||
const std::string& line = searcher->search_string();
|
||||
search_line->SetValue(line.empty() ? default_string : from_u8(line));
|
||||
search_line->SetFocus();
|
||||
search_line->SelectAll();
|
||||
|
||||
update_list();
|
||||
|
||||
const OptionViewParameters& params = searcher->view_params;
|
||||
check_type->SetValue(params.type);
|
||||
check_category->SetValue(params.category);
|
||||
check_group->SetValue(params.group);
|
||||
|
||||
this->SetPosition(position);
|
||||
this->ShowModal();
|
||||
}
|
||||
|
||||
void SearchDialog::ProcessSelection(int selection)
|
||||
{
|
||||
if (selection < 0)
|
||||
return;
|
||||
|
||||
GUI::wxGetApp().sidebar().jump_to_option(selection);
|
||||
this->EndModal(wxID_CLOSE);
|
||||
}
|
||||
|
||||
void SearchDialog::OnInputText(wxCommandEvent&)
|
||||
{
|
||||
search_line->SetInsertionPointEnd();
|
||||
|
||||
wxString input_string = search_line->GetValue();
|
||||
if (input_string == default_string)
|
||||
input_string.Clear();
|
||||
|
||||
searcher->search(into_u8(input_string));
|
||||
|
||||
update_list();
|
||||
}
|
||||
|
||||
void SearchDialog::OnLeftUpInTextCtrl(wxEvent& event)
|
||||
{
|
||||
if (search_line->GetValue() == default_string)
|
||||
search_line->SetValue("");
|
||||
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
void SearchDialog::OnMouseMove(wxMouseEvent& event)
|
||||
{
|
||||
wxPoint pt = wxGetMousePosition() - search_list->GetScreenPosition();
|
||||
int selection = search_list->HitTest(pt);
|
||||
search_list->Select(selection);
|
||||
}
|
||||
|
||||
void SearchDialog::OnMouseClick(wxMouseEvent&)
|
||||
{
|
||||
int selection = search_list->GetSelection();
|
||||
search_list->SetSelection(wxNOT_FOUND);
|
||||
|
||||
wxCommandEvent event(wxEVT_LISTBOX, search_list->GetId());
|
||||
event.SetInt(selection);
|
||||
event.SetEventObject(search_list);
|
||||
ProcessEvent(event);
|
||||
}
|
||||
|
||||
void SearchDialog::OnSelect(wxCommandEvent& event)
|
||||
{
|
||||
int selection = event.GetSelection();
|
||||
ProcessSelection(selection);
|
||||
}
|
||||
|
||||
void SearchDialog::update_list()
|
||||
{
|
||||
search_list->Clear();
|
||||
|
||||
const std::vector<FoundOption>& filters = searcher->found_options();
|
||||
for (const FoundOption& item : filters)
|
||||
search_list->Append(from_u8(item.label));
|
||||
}
|
||||
|
||||
void SearchDialog::OnKeyDown(wxKeyEvent& event)
|
||||
{
|
||||
int key = event.GetKeyCode();
|
||||
|
||||
// change selected item in the list
|
||||
if (key == WXK_UP || key == WXK_DOWN)
|
||||
{
|
||||
int selection = search_list->GetSelection();
|
||||
|
||||
if (key == WXK_UP && selection > 0)
|
||||
selection--;
|
||||
if (key == WXK_DOWN && selection < int(search_list->GetCount() - 1))
|
||||
selection++;
|
||||
|
||||
search_list->Select(selection);
|
||||
// This function could be called from search_line,
|
||||
// So, for the next correct navigation, set focus on the search_list
|
||||
search_list->SetFocus();
|
||||
}
|
||||
// process "Enter" pressed
|
||||
else if (key == WXK_NUMPAD_ENTER || key == WXK_RETURN)
|
||||
ProcessSelection(search_list->GetSelection());
|
||||
else
|
||||
event.Skip(); // !Needed to have EVT_CHAR generated as well
|
||||
}
|
||||
|
||||
void SearchDialog::OnCheck(wxCommandEvent& event)
|
||||
{
|
||||
OptionViewParameters& params = searcher->view_params;
|
||||
params.type = check_type->GetValue();
|
||||
params.category = check_category->GetValue();
|
||||
params.group = check_group->GetValue();
|
||||
|
||||
searcher->search();
|
||||
update_list();
|
||||
}
|
||||
|
||||
void SearchDialog::on_dpi_changed(const wxRect& suggested_rect)
|
||||
{
|
||||
const int& em = em_unit();
|
||||
|
||||
msw_buttons_rescale(this, em, { wxID_CANCEL });
|
||||
|
||||
const wxSize& size = wxSize(40 * em, 30 * em);
|
||||
SetMinSize(size);
|
||||
|
||||
Fit();
|
||||
Refresh();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
} // namespace Slic3r::GUI
|
220
src/slic3r/GUI/Search.hpp
Normal file
220
src/slic3r/GUI/Search.hpp
Normal file
@ -0,0 +1,220 @@
|
||||
#ifndef slic3r_SearchComboBox_hpp_
|
||||
#define slic3r_SearchComboBox_hpp_
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include <wx/panel.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/listctrl.h>
|
||||
|
||||
#include <wx/combo.h>
|
||||
|
||||
#include <wx/checkbox.h>
|
||||
#include <wx/dialog.h>
|
||||
|
||||
#include "GUI_Utils.hpp"
|
||||
#include "Preset.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace Search{
|
||||
|
||||
class SearchDialog;
|
||||
|
||||
struct InputInfo
|
||||
{
|
||||
DynamicPrintConfig* config {nullptr};
|
||||
Preset::Type type {Preset::TYPE_INVALID};
|
||||
ConfigOptionMode mode {comSimple};
|
||||
};
|
||||
|
||||
struct GroupAndCategory {
|
||||
wxString group;
|
||||
wxString category;
|
||||
};
|
||||
|
||||
// fuzzy_match flag
|
||||
// Sorted by the order of importance. The outputs will be sorted by the importance if the match value given by fuzzy_match is equal.
|
||||
enum FMFlag
|
||||
{
|
||||
fmUndef = 0, // didn't find
|
||||
fmOptKey,
|
||||
fmLabel,
|
||||
fmLabelLocal,
|
||||
fmGroup,
|
||||
fmGroupLocal,
|
||||
fmCategory,
|
||||
fmCategoryLocal
|
||||
};
|
||||
|
||||
struct Option {
|
||||
bool operator<(const Option& other) const { return other.label > this->label; }
|
||||
bool operator>(const Option& other) const { return other.label < this->label; }
|
||||
|
||||
// Fuzzy matching works at a character level. Thus matching with wide characters is a safer bet than with short characters,
|
||||
// though for some languages (Chinese?) it may not work correctly.
|
||||
std::wstring opt_key;
|
||||
Preset::Type type {Preset::TYPE_INVALID};
|
||||
std::wstring label;
|
||||
std::wstring label_local;
|
||||
std::wstring group;
|
||||
std::wstring group_local;
|
||||
std::wstring category;
|
||||
std::wstring category_local;
|
||||
|
||||
FMFlag fuzzy_match(wchar_t const *search_pattern, int &outScore, std::vector<uint16_t> &out_matches) const;
|
||||
};
|
||||
|
||||
struct FoundOption {
|
||||
// UTF8 encoding, to be consumed by ImGUI by reference.
|
||||
std::string label;
|
||||
std::string marked_label;
|
||||
std::string tooltip;
|
||||
size_t option_idx {0};
|
||||
FMFlag category {fmUndef};
|
||||
int outScore {0};
|
||||
|
||||
// Returning pointers to contents of std::string members, to be used by ImGUI for rendering.
|
||||
void get_marked_label_and_tooltip(const char** label, const char** tooltip) const;
|
||||
};
|
||||
|
||||
struct OptionViewParameters
|
||||
{
|
||||
bool type {false};
|
||||
bool category {false};
|
||||
bool group {true };
|
||||
|
||||
int hovered_id {-1};
|
||||
};
|
||||
|
||||
class OptionsSearcher
|
||||
{
|
||||
std::string search_line;
|
||||
std::map<std::string, GroupAndCategory> groups_and_categories;
|
||||
|
||||
std::vector<Option> options {};
|
||||
std::vector<FoundOption> found {};
|
||||
|
||||
void append_options(DynamicPrintConfig* config, Preset::Type type, ConfigOptionMode mode);
|
||||
|
||||
void sort_options() {
|
||||
std::sort(options.begin(), options.end(), [](const Option& o1, const Option& o2) {
|
||||
return o1.label < o2.label; });
|
||||
}
|
||||
void sort_found() {
|
||||
std::sort(found.begin(), found.end(), [](const FoundOption& f1, const FoundOption& f2) {
|
||||
return f1.outScore > f2.outScore || (f1.outScore == f2.outScore && int(f1.category) < int(f2.category)); });
|
||||
};
|
||||
|
||||
size_t options_size() const { return options.size(); }
|
||||
size_t found_size() const { return found.size(); }
|
||||
|
||||
public:
|
||||
OptionViewParameters view_params;
|
||||
|
||||
SearchDialog* search_dialog { nullptr };
|
||||
|
||||
OptionsSearcher();
|
||||
~OptionsSearcher();
|
||||
|
||||
void init(std::vector<InputInfo> input_values);
|
||||
void apply(DynamicPrintConfig *config,
|
||||
Preset::Type type,
|
||||
ConfigOptionMode mode);
|
||||
bool search();
|
||||
bool search(const std::string& search, bool force = false);
|
||||
|
||||
void add_key(const std::string& opt_key, const wxString& group, const wxString& category);
|
||||
|
||||
size_t size() const { return found_size(); }
|
||||
|
||||
const FoundOption& operator[](const size_t pos) const noexcept { return found[pos]; }
|
||||
const Option& get_option(size_t pos_in_filter) const;
|
||||
|
||||
const std::vector<FoundOption>& found_options() { return found; }
|
||||
const GroupAndCategory& get_group_and_category (const std::string& opt_key) { return groups_and_categories[opt_key]; }
|
||||
std::string& search_string() { return search_line; }
|
||||
};
|
||||
|
||||
|
||||
class SearchComboPopup : public wxListBox, public wxComboPopup
|
||||
{
|
||||
public:
|
||||
// Initialize member variables
|
||||
void Init();
|
||||
|
||||
// Create popup control
|
||||
virtual bool Create(wxWindow* parent);
|
||||
// Return pointer to the created control
|
||||
virtual wxWindow* GetControl() { return this; }
|
||||
|
||||
// Translate string into a list selection
|
||||
virtual void SetStringValue(const wxString& s);
|
||||
// Get list selection as a string
|
||||
virtual wxString GetStringValue() const {
|
||||
// we shouldn't change a combo control's string
|
||||
return m_input_string;
|
||||
}
|
||||
|
||||
void ProcessSelection(int selection);
|
||||
|
||||
// Do mouse hot-tracking (which is typical in list popups)
|
||||
void OnMouseMove(wxMouseEvent& event);
|
||||
// On mouse left up, set the value and close the popup
|
||||
void OnMouseClick(wxMouseEvent& WXUNUSED(event));
|
||||
// process Up/Down arrows and Enter press
|
||||
void OnKeyDown(wxKeyEvent& event);
|
||||
|
||||
protected:
|
||||
wxString m_input_string;
|
||||
};
|
||||
|
||||
|
||||
//------------------------------------------
|
||||
// SearchDialog
|
||||
//------------------------------------------
|
||||
|
||||
class SearchDialog : public GUI::DPIDialog
|
||||
{
|
||||
wxString search_str;
|
||||
wxString default_string;
|
||||
|
||||
wxTextCtrl* search_line { nullptr };
|
||||
wxListBox* search_list { nullptr };
|
||||
wxCheckBox* check_type { nullptr };
|
||||
wxCheckBox* check_category { nullptr };
|
||||
wxCheckBox* check_group { nullptr };
|
||||
|
||||
OptionsSearcher* searcher;
|
||||
|
||||
void update_list();
|
||||
|
||||
void OnInputText(wxCommandEvent& event);
|
||||
void OnLeftUpInTextCtrl(wxEvent& event);
|
||||
|
||||
void OnMouseMove(wxMouseEvent& event);
|
||||
void OnMouseClick(wxMouseEvent& event);
|
||||
void OnSelect(wxCommandEvent& event);
|
||||
void OnKeyDown(wxKeyEvent& event);
|
||||
|
||||
void OnCheck(wxCommandEvent& event);
|
||||
|
||||
public:
|
||||
SearchDialog(OptionsSearcher* searcher);
|
||||
~SearchDialog() {}
|
||||
|
||||
void Popup(wxPoint position = wxDefaultPosition);
|
||||
void ProcessSelection(int selection);
|
||||
|
||||
protected:
|
||||
void on_dpi_changed(const wxRect& suggested_rect) override;
|
||||
};
|
||||
|
||||
|
||||
} // Search namespace
|
||||
}
|
||||
|
||||
#endif //slic3r_SearchComboBox_hpp_
|
File diff suppressed because it is too large
Load Diff
@ -49,7 +49,7 @@ class Page : public wxScrolledWindow
|
||||
wxBoxSizer* m_vsizer;
|
||||
bool m_show = true;
|
||||
public:
|
||||
Page(wxWindow* parent, const wxString title, const int iconID, const std::vector<ScalableBitmap>& mode_bmp_cache) :
|
||||
Page(wxWindow* parent, const wxString& title, const int iconID, const std::vector<ScalableBitmap>& mode_bmp_cache) :
|
||||
m_parent(parent),
|
||||
m_title(title),
|
||||
m_iconID(iconID),
|
||||
@ -121,6 +121,7 @@ protected:
|
||||
std::string m_name;
|
||||
const wxString m_title;
|
||||
PresetBitmapComboBox* m_presets_choice;
|
||||
ScalableButton* m_search_btn;
|
||||
ScalableButton* m_btn_save_preset;
|
||||
ScalableButton* m_btn_delete_preset;
|
||||
ScalableButton* m_btn_hide_incompatible_presets;
|
||||
@ -221,6 +222,21 @@ protected:
|
||||
bool m_completed { false };
|
||||
ConfigOptionMode m_mode = comExpert; // to correct first Tab update_visibility() set mode to Expert
|
||||
|
||||
struct Highlighter
|
||||
{
|
||||
void set_timer_owner(wxEvtHandler* owner, int timerid = wxID_ANY);
|
||||
void init(BlinkingBitmap* bmp);
|
||||
void blink();
|
||||
|
||||
private:
|
||||
void invalidate();
|
||||
|
||||
BlinkingBitmap* bbmp {nullptr};
|
||||
int blink_counter {0};
|
||||
wxTimer timer;
|
||||
}
|
||||
m_highlighter;
|
||||
|
||||
public:
|
||||
PresetBundle* m_preset_bundle;
|
||||
bool m_show_btn_incompatible_presets = false;
|
||||
@ -233,6 +249,10 @@ public:
|
||||
// Used for options which don't have corresponded field
|
||||
std::map<std::string, wxStaticText*> m_colored_Labels;
|
||||
|
||||
// map of option name -> BlinkingBitmap (blinking ikon, associated with option)
|
||||
// Used for options which don't have corresponded field
|
||||
std::map<std::string, BlinkingBitmap*> m_blinking_ikons;
|
||||
|
||||
// Counter for the updating (because of an update() function can have a recursive behavior):
|
||||
// 1. increase value from the very beginning of an update() function
|
||||
// 2. decrease value at the end of an update() function
|
||||
@ -299,6 +319,7 @@ public:
|
||||
void update_visibility();
|
||||
virtual void msw_rescale();
|
||||
Field* get_field(const t_config_option_key& opt_key, int opt_index = -1) const;
|
||||
Field* get_field(const t_config_option_key &opt_key, Page** selected_page, int opt_index = -1);
|
||||
bool set_value(const t_config_option_key& opt_key, const boost::any& value);
|
||||
wxSizer* description_line_widget(wxWindow* parent, ogStaticText** StaticText);
|
||||
bool current_preset_is_dirty();
|
||||
@ -310,6 +331,8 @@ public:
|
||||
void on_value_change(const std::string& opt_key, const boost::any& value);
|
||||
|
||||
void update_wiping_button_visibility();
|
||||
void activate_option(const std::string& opt_key, const wxString& category);
|
||||
void apply_searcher();
|
||||
|
||||
protected:
|
||||
void create_line_with_widget(ConfigOptionsGroup* optgroup, const std::string& opt_key, widget_t widget);
|
||||
|
@ -29,7 +29,7 @@ inline wxString format_wxstr(const wxString& fmt, TArgs&&... args) {
|
||||
}
|
||||
template<typename... TArgs>
|
||||
inline std::string format(const wxString& fmt, TArgs&&... args) {
|
||||
return format(fmt.ToUTF8().data(), std::forward<TArgs>(args)...);
|
||||
return Slic3r::format(fmt.ToUTF8().data(), std::forward<TArgs>(args)...);
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
|
245
src/slic3r/GUI/fts_fuzzy_match.h
Normal file
245
src/slic3r/GUI/fts_fuzzy_match.h
Normal file
@ -0,0 +1,245 @@
|
||||
// LICENSE
|
||||
//
|
||||
// This software is dual-licensed to the public domain and under the following
|
||||
// license: you are granted a perpetual, irrevocable license to copy, modify,
|
||||
// publish, and distribute this file as you see fit.
|
||||
//
|
||||
// VERSION
|
||||
// 0.2.0 (2017-02-18) Scored matches perform exhaustive search for best score
|
||||
// 0.1.0 (2016-03-28) Initial release
|
||||
//
|
||||
// AUTHOR
|
||||
// Forrest Smith
|
||||
//
|
||||
// NOTES
|
||||
// Compiling
|
||||
// You MUST add '#define FTS_FUZZY_MATCH_IMPLEMENTATION' before including this header in ONE source file to create implementation.
|
||||
//
|
||||
// fuzzy_match_simple(...)
|
||||
// Returns true if each character in pattern is found sequentially within str
|
||||
//
|
||||
// fuzzy_match(...)
|
||||
// Returns true if pattern is found AND calculates a score.
|
||||
// Performs exhaustive search via recursion to find all possible matches and match with highest score.
|
||||
// Scores values have no intrinsic meaning. Possible score range is not normalized and varies with pattern.
|
||||
// Recursion is limited internally (default=10) to prevent degenerate cases (pattern="aaaaaa" str="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
|
||||
// Uses uint8_t for match indices. Therefore patterns are limited to max_matches characters.
|
||||
// Score system should be tuned for YOUR use case. Words, sentences, file names, or method names all prefer different tuning.
|
||||
|
||||
|
||||
#ifndef FTS_FUZZY_MATCH_H
|
||||
#define FTS_FUZZY_MATCH_H
|
||||
|
||||
|
||||
#include <cstdint> // uint8_t
|
||||
#include <ctype.h> // ::tolower, ::toupper
|
||||
#include <cstring> // memcpy
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include "../Utils/ASCIIFolding.hpp"
|
||||
|
||||
// Public interface
|
||||
namespace fts {
|
||||
using char_type = wchar_t;
|
||||
using pos_type = uint16_t;
|
||||
static constexpr pos_type stopper = pos_type(-1);
|
||||
static constexpr int max_matches = 255;
|
||||
|
||||
static bool fuzzy_match(char_type const * pattern, char_type const * str, int & outScore);
|
||||
static bool fuzzy_match(char_type const * pattern, char_type const * str, int & outScore, pos_type * matches);
|
||||
}
|
||||
|
||||
#ifdef FTS_FUZZY_MATCH_IMPLEMENTATION
|
||||
namespace fts {
|
||||
|
||||
// Forward declarations for "private" implementation
|
||||
namespace fuzzy_internal {
|
||||
static bool fuzzy_match_recursive(const char_type * pattern, const char_type * str, int & outScore, const char_type * const strBegin,
|
||||
pos_type const * srcMatches, pos_type * newMatches, int nextMatch,
|
||||
int & recursionCount, int recursionLimit);
|
||||
static void copy_matches(pos_type * dst, pos_type const* src);
|
||||
}
|
||||
|
||||
// Public interface
|
||||
static bool fuzzy_match(char_type const * pattern, char_type const * str, int & outScore) {
|
||||
pos_type matches[max_matches + 1]; // with the room for the stopper
|
||||
matches[0] = stopper;
|
||||
return fuzzy_match(pattern, str, outScore, matches);
|
||||
}
|
||||
|
||||
static bool fuzzy_match(char_type const * pattern, char_type const * str, int & outScore, pos_type * matches) {
|
||||
int recursionCount = 0;
|
||||
static constexpr int recursionLimit = 10;
|
||||
return fuzzy_internal::fuzzy_match_recursive(pattern, str, outScore, str, nullptr, matches, 0, recursionCount, recursionLimit);
|
||||
}
|
||||
|
||||
// Private implementation
|
||||
static bool fuzzy_internal::fuzzy_match_recursive(
|
||||
// Pattern to match over str.
|
||||
const char_type * pattern,
|
||||
// Text to match the pattern over.
|
||||
const char_type * str,
|
||||
// Score of the pattern matching str. Output variable.
|
||||
int & outScore,
|
||||
// The very start of str, for calculating indices of matches and for calculating matches from the start of the input string.
|
||||
const char_type * const strBegin,
|
||||
// Matches when entering this function.
|
||||
pos_type const * srcMatches,
|
||||
// Output matches.
|
||||
pos_type * matches,
|
||||
// Number of matched characters stored in srcMatches when entering this function, also tracking the successive matches.
|
||||
int nextMatch,
|
||||
// Recursion count is input / output to track the maximum depth reached.
|
||||
// Was given by reference &recursionCount, see discussion in https://github.com/forrestthewoods/lib_fts/issues/21
|
||||
// int & recursionCount,
|
||||
int & recursionCount,
|
||||
int recursionLimit)
|
||||
{
|
||||
// Count recursions
|
||||
if (++ recursionCount >= recursionLimit)
|
||||
return false;
|
||||
|
||||
// Detect end of strings
|
||||
if (*pattern == '\0' || *str == '\0')
|
||||
return false;
|
||||
|
||||
// Recursion params
|
||||
bool recursiveMatch = false;
|
||||
pos_type bestRecursiveMatches[max_matches + 1]; // with the room for the stopper
|
||||
int bestRecursiveScore = 0;
|
||||
|
||||
// Loop through pattern and str looking for a match
|
||||
bool first_match = true;
|
||||
while (*pattern != '\0' && *str != '\0') {
|
||||
|
||||
int num_matched = std::tolower(*pattern) == std::tolower(*str) ? 1 : 0;
|
||||
bool folded_match = false;
|
||||
if (! num_matched) {
|
||||
char tmp[4];
|
||||
char *end = Slic3r::fold_to_ascii(*str, tmp);
|
||||
char *c = tmp;
|
||||
for (const wchar_t* d = pattern; c != end && *d != 0 && wchar_t(std::tolower(*c)) == std::tolower(*d); ++c, ++d);
|
||||
if (c == end) {
|
||||
folded_match = true;
|
||||
num_matched = end - tmp;
|
||||
}
|
||||
}
|
||||
|
||||
// Found match
|
||||
if (num_matched) {
|
||||
|
||||
// Supplied matches buffer was too short
|
||||
if (nextMatch + num_matched > max_matches)
|
||||
return false;
|
||||
|
||||
// "Copy-on-Write" srcMatches into matches
|
||||
if (first_match && srcMatches) {
|
||||
memcpy(matches, srcMatches, sizeof(pos_type) * (nextMatch + 1)); // including the stopper
|
||||
first_match = false;
|
||||
}
|
||||
|
||||
// Recursive call that "skips" this match
|
||||
pos_type recursiveMatches[max_matches + 1]; // with the room for the stopper
|
||||
int recursiveScore;
|
||||
if (fuzzy_match_recursive(pattern, str + 1, recursiveScore, strBegin, matches, recursiveMatches, nextMatch, recursionCount, recursionLimit)) {
|
||||
|
||||
// Pick best recursive score
|
||||
if (!recursiveMatch || recursiveScore > bestRecursiveScore) {
|
||||
copy_matches(bestRecursiveMatches, recursiveMatches);
|
||||
bestRecursiveScore = recursiveScore;
|
||||
}
|
||||
recursiveMatch = true;
|
||||
}
|
||||
|
||||
// Advance
|
||||
matches[nextMatch++] = (pos_type)(str - strBegin);
|
||||
// Write a stopper sign.
|
||||
matches[nextMatch] = stopper;
|
||||
// Advance pattern by the number of matched characters (could be more if ASCII folding triggers in).
|
||||
pattern += num_matched;
|
||||
}
|
||||
++str;
|
||||
}
|
||||
|
||||
// Determine if full pattern was matched
|
||||
bool matched = *pattern == '\0';
|
||||
|
||||
// Calculate score
|
||||
if (matched) {
|
||||
static constexpr int sequential_bonus = 15; // bonus for adjacent matches
|
||||
static constexpr int separator_bonus = 30; // bonus if match occurs after a separator
|
||||
static constexpr int camel_bonus = 30; // bonus if match is uppercase and prev is lower
|
||||
static constexpr int first_letter_bonus = 15; // bonus if the first letter is matched
|
||||
|
||||
static constexpr int leading_letter_penalty = -5; // penalty applied for every letter in str before the first match
|
||||
static constexpr int max_leading_letter_penalty = -15; // maximum penalty for leading letters
|
||||
static constexpr int unmatched_letter_penalty = -1; // penalty for every letter that doesn't matter
|
||||
|
||||
// Iterate str to end
|
||||
while (*str != '\0')
|
||||
++str;
|
||||
|
||||
// Initialize score
|
||||
outScore = 100;
|
||||
|
||||
// Apply leading letter penalty or bonus.
|
||||
outScore += matches[0] == 0 ?
|
||||
first_letter_bonus :
|
||||
std::max(matches[0] * leading_letter_penalty, max_leading_letter_penalty);
|
||||
|
||||
// Apply unmatched letters after the end penalty
|
||||
// outScore += (int(str - strBegin) - matches[nextMatch-1] + 1) * unmatched_letter_penalty;
|
||||
// Apply unmatched penalty
|
||||
outScore += (int(str - strBegin) - nextMatch) * unmatched_letter_penalty;
|
||||
|
||||
// Apply ordering bonuses
|
||||
for (int i = 0; i < nextMatch; ++i) {
|
||||
pos_type currIdx = matches[i];
|
||||
|
||||
// Check for bonuses based on neighbor character value
|
||||
if (currIdx > 0) {
|
||||
if (i > 0 && currIdx == matches[i - 1] + 1)
|
||||
// Sequential
|
||||
outScore += sequential_bonus;
|
||||
// Camel case
|
||||
char_type prev = strBegin[currIdx - 1];
|
||||
if (std::islower(prev) && std::isupper(strBegin[currIdx]))
|
||||
outScore += camel_bonus;
|
||||
// Separator
|
||||
if (prev == '_' || prev == ' ')
|
||||
outScore += separator_bonus;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return best result
|
||||
if (recursiveMatch && (!matched || bestRecursiveScore > outScore)) {
|
||||
// Recursive score is better than "this"
|
||||
copy_matches(matches, bestRecursiveMatches);
|
||||
outScore = bestRecursiveScore;
|
||||
return true;
|
||||
}
|
||||
else if (matched) {
|
||||
// "this" score is better than recursive
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
// no match
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy matches up to a stopper.
|
||||
static void fuzzy_internal::copy_matches(pos_type * dst, pos_type const* src)
|
||||
{
|
||||
while (*src != stopper)
|
||||
*dst++ = *src++;
|
||||
*dst = stopper;
|
||||
}
|
||||
|
||||
} // namespace fts
|
||||
|
||||
#endif // FTS_FUZZY_MATCH_IMPLEMENTATION
|
||||
|
||||
#endif // FTS_FUZZY_MATCH_H
|
@ -951,5 +951,40 @@ void ScalableButton::msw_rescale()
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// BlinkingBitmap
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
BlinkingBitmap::BlinkingBitmap(wxWindow* parent, const std::string& icon_name) :
|
||||
wxStaticBitmap(parent, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize(int(1.6 * Slic3r::GUI::wxGetApp().em_unit()), -1))
|
||||
{
|
||||
bmp = ScalableBitmap(parent, icon_name);
|
||||
}
|
||||
|
||||
void BlinkingBitmap::msw_rescale()
|
||||
{
|
||||
bmp.msw_rescale();
|
||||
this->SetSize(bmp.GetBmpSize());
|
||||
this->SetMinSize(bmp.GetBmpSize());
|
||||
}
|
||||
|
||||
void BlinkingBitmap::invalidate()
|
||||
{
|
||||
this->SetBitmap(wxNullBitmap);
|
||||
}
|
||||
|
||||
void BlinkingBitmap::activate()
|
||||
{
|
||||
this->SetBitmap(bmp.bmp());
|
||||
show = true;
|
||||
}
|
||||
|
||||
void BlinkingBitmap::blink()
|
||||
{
|
||||
show = !show;
|
||||
this->SetBitmap(show ? bmp.bmp() : wxNullBitmap);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/menu.h>
|
||||
#include <wx/bmpcbox.h>
|
||||
#include <wx/statbmp.h>
|
||||
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
@ -355,5 +356,28 @@ private:
|
||||
};
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// BlinkingBitmap
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
class BlinkingBitmap : public wxStaticBitmap
|
||||
{
|
||||
public:
|
||||
BlinkingBitmap() {};
|
||||
BlinkingBitmap(wxWindow* parent, const std::string& icon_name = "redo_toolbar");
|
||||
|
||||
~BlinkingBitmap() {}
|
||||
|
||||
void msw_rescale();
|
||||
void invalidate();
|
||||
void activate();
|
||||
void blink();
|
||||
|
||||
private:
|
||||
ScalableBitmap bmp;
|
||||
bool show {false};
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // slic3r_GUI_wxExtensions_hpp_
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -7,8 +7,22 @@ namespace Slic3r {
|
||||
|
||||
// If possible, remove accents from accented latin characters.
|
||||
// This function is useful for generating file names to be processed by legacy firmwares.
|
||||
extern std::string fold_utf8_to_ascii(const char *src);
|
||||
extern std::string fold_utf8_to_ascii(const std::string &src);
|
||||
extern std::string fold_utf8_to_ascii(const char *src);
|
||||
extern std::string fold_utf8_to_ascii(const std::string &src);
|
||||
|
||||
// Convert the input UNICODE character to a string of maximum 4 output ASCII characters.
|
||||
// Return the end of the string written to the output.
|
||||
// The output buffer must be at least 4 characters long.
|
||||
extern char* fold_to_ascii(wchar_t c, char *out);
|
||||
|
||||
template<typename OUTPUT_ITERATOR>
|
||||
void fold_to_ascii(wchar_t c, OUTPUT_ITERATOR out)
|
||||
{
|
||||
char tmp[4];
|
||||
char *end = fold_to_ascii(c, tmp);
|
||||
for (char *it = tmp; it != end; ++ it)
|
||||
*out = *it;
|
||||
}
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
|
@ -18,10 +18,10 @@
|
||||
#include <openssl/x509.h>
|
||||
#endif
|
||||
|
||||
#define L(s) s
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include <libslic3r/libslic3r.h>
|
||||
#include <libslic3r/Utils.hpp>
|
||||
#include <slic3r/GUI/I18N.hpp>
|
||||
#include <slic3r/GUI/format.hpp>
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
@ -70,26 +70,26 @@ struct CurlGlobalInit
|
||||
}
|
||||
|
||||
if (!bundle)
|
||||
message = L("Could not detect system SSL certificate store. "
|
||||
"PrusaSlicer will be unable to establish secure "
|
||||
"network connections.");
|
||||
message = _u8L("Could not detect system SSL certificate store. "
|
||||
"PrusaSlicer will be unable to establish secure "
|
||||
"network connections.");
|
||||
else
|
||||
message = string_printf(
|
||||
L("PrusaSlicer detected system SSL certificate store in: %s"),
|
||||
message = Slic3r::GUI::format(
|
||||
_L("PrusaSlicer detected system SSL certificate store in: %1%"),
|
||||
bundle);
|
||||
|
||||
message += string_printf(
|
||||
L("\nTo specify the system certificate store manually, please "
|
||||
"set the %s environment variable to the correct CA bundle "
|
||||
"and restart the application."),
|
||||
message += "\n" + Slic3r::GUI::format(
|
||||
_L("To specify the system certificate store manually, please "
|
||||
"set the %1% environment variable to the correct CA bundle "
|
||||
"and restart the application."),
|
||||
SSL_CA_FILE);
|
||||
}
|
||||
|
||||
#endif // OPENSSL_CERT_OVERRIDE
|
||||
|
||||
if (CURLcode ec = ::curl_global_init(CURL_GLOBAL_DEFAULT)) {
|
||||
message = L("CURL init has failed. PrusaSlicer will be unable to establish "
|
||||
"network connections. See logs for additional details.");
|
||||
message += _u8L("CURL init has failed. PrusaSlicer will be unable to establish "
|
||||
"network connections. See logs for additional details.");
|
||||
|
||||
BOOST_LOG_TRIVIAL(error) << ::curl_easy_strerror(ec);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user