diff --git a/resources/icons/attention.svg b/resources/icons/attention.svg new file mode 100644 index 000000000..934bd3b41 --- /dev/null +++ b/resources/icons/attention.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/resources/icons/collapse.svg b/resources/icons/collapse.svg new file mode 100644 index 000000000..c0d6f43d5 --- /dev/null +++ b/resources/icons/collapse.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + diff --git a/resources/icons/search.svg b/resources/icons/search.svg new file mode 100644 index 000000000..6421c7e05 --- /dev/null +++ b/resources/icons/search.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/resources/icons/search_.svg b/resources/icons/search_.svg new file mode 100644 index 000000000..679bb30f7 --- /dev/null +++ b/resources/icons/search_.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/resources/icons/search_gray.svg b/resources/icons/search_gray.svg new file mode 100644 index 000000000..043c2e3b2 --- /dev/null +++ b/resources/icons/search_gray.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h index 09bfd16c9..c425bbef2 100644 --- a/src/imgui/imconfig.h +++ b/src/imgui/imconfig.h @@ -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); + } -*/ + diff --git a/src/imgui/imgui_draw.cpp b/src/imgui/imgui_draw.cpp index 4bb91ccfe..4e6f1374b 100644 --- a/src/imgui/imgui_draw.cpp +++ b/src/imgui/imgui_draw.cpp @@ -33,6 +33,7 @@ Index of this file: #define IMGUI_DEFINE_MATH_OPERATORS #endif #include "imgui_internal.h" +#include "imconfig.h" #include // 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) diff --git a/src/libslic3r/Arrange.hpp b/src/libslic3r/Arrange.hpp index 352e9e1cf..8ff1e37a9 100644 --- a/src/libslic3r/Arrange.hpp +++ b/src/libslic3r/Arrange.hpp @@ -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 diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 43c83fe2b..1f0a6c3ae 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -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 }); diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 1ace0902c..c33a0af59 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -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 diff --git a/src/slic3r/GUI/AboutDialog.cpp b/src/slic3r/GUI/AboutDialog.cpp index 505c2eb2a..92f8d1fdb 100644 --- a/src/slic3r/GUI/AboutDialog.cpp +++ b/src/slic3r/GUI/AboutDialog.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/" } }; } diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 68193f84d..16d3f4123 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -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: diff --git a/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp index 5bdbfadc8..f41e3c7b0 100644 --- a/src/slic3r/GUI/Field.hpp +++ b/src/slic3r/GUI/Field.hpp @@ -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(x_textctrl); } }; class StaticText : public Field { diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 647994717..6881e86fd 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -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); 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(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(); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index f057ea7d6..be10e79a1 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -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); 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 _parse_colors(const std::vector& colors); diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index 4219fe482..0bd087814 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -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; diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp index b4aac9206..27b43fef6 100644 --- a/src/slic3r/GUI/GLToolbar.hpp +++ b/src/slic3r/GUI/GLToolbar.hpp @@ -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(); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index c04d7ca16..0774f3208 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -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); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 22b3d2c25..ce79a4bcd 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -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(); diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index a44e843b8..ea8b2afd3 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -15,12 +15,17 @@ #include +#ifndef IMGUI_DEFINE_MATH_OPERATORS +#define IMGUI_DEFINE_MATH_OPERATORS +#endif #include #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& 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 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"); diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index 417561881..6a1e27dcb 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -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& 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(); diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index 79a6cad20..9f31d23d8 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -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") }, diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 21aad8987..d3bd2720e 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -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(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. diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 6038e6d2f..8fc0ed1f2 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -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 ; diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index d561d8e2a..83eb6c76f 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -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 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); } diff --git a/src/slic3r/GUI/OptionsGroup.hpp b/src/slic3r/GUI/OptionsGroup.hpp index 536eb72a5..6128eb7ab 100644 --- a/src/slic3r/GUI/OptionsGroup.hpp +++ b/src/slic3r/GUI/OptionsGroup.hpp @@ -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& get_extra_widgets() const {return m_extra_widgets;} const std::vector