diff --git a/resources/data/hints.ini b/resources/data/hints.ini
new file mode 100644
index 000000000..2ebf9091f
--- /dev/null
+++ b/resources/data/hints.ini
@@ -0,0 +1,168 @@
+
+[hint:Perspective camera]
+text = Perspective camera\nDid you know that you can use the K key to quickly switch between an orthographic and perspective camera?
+
+[hint:Camera Views]
+text = Camera Views\nDid you know that you can use the number keys 0-6 to quickly switch between predefined camera angles?
+
+[hint:Place on face]
+text = Place on face\nDid you know that you can quickly orient a model so that one of its faces sits on the print bed? Select thePlace on facefunction or press the F key.
+hypertext_type = gizmo
+hypertext_gizmo_item = place
+
+[hint:Set number of instances]
+text = Set number of instances\nDid you know that you can right-click a model and set an exact number of instances instead of copy-pasting it several times?
+
+[hint:Combine infill]
+text = Combine infill\nDid you know that you can print the infill with a higher layer height compared to perimeters to save print time using the settingCombine infill every.
+hypertext_type = settings
+hypertext_settings_opt = infill_every_layers
+hypertext_settings_type = 1
+hypertext_settings_category = Infill
+disabled_modes = SLA; simple
+
+[hint:Hiding sidebar]
+text = Hiding sidebar\nDid you know that you can hide the right sidebar using the shortcut Shift+Tab? You can also enable the icon for this from thePreferences.
+hypertext_type = preferences
+hypertext_preferences_page = 2
+
+[hint:Variable layer height]
+text = Variable layer height\nDid you know that you can print different regions of your model with a different layer height and smooth the transitions between them? Try theVariable layer height tool.(Not available for SLA printers.)
+hypertext_type = plater
+hypertext_plater_item = layersediting
+disabled_modes = SLA
+
+[hint:Undo/redo history]
+text = Undo/redo history\nDid you know that you can right-click theundo/redo arrowsto see the history of changes and to undo or redo several actions at once?
+hypertext_type = plater
+hypertext_plater_item = undo
+
+[hint:Auto-arrange settings]
+text = Auto-arrange settings\nDid you know that you can right-click theauto-arrange iconto adjust the size of the gap between objects and to allow automatic rotations?
+hypertext_type = plater
+hypertext_plater_item = arrange
+
+[hint:Reload from disk]
+text = Reload from disk\nDid you know that if you created a newer version of your model, you can simply reload it in PrusaSlicer? Right-click the model in the 3D view and choose Reload from disk. Read more in thedocumentation.
+hypertext_type = link
+hypertext_link = https://help.prusa3d.com/en/article/reload-from-disk_120427
+
+[hint:Different layer height for each model]
+text = Different layer height for each model\nDid you know that you can print each model on the plater with a different layer height? Right-click the model in the 3D view, choose Layers and Perimeters and adjust the values in the right panel. Read more in thedocumentation.
+hypertext_type = link
+hypertext_link = https://help.prusa3d.com/en/article/per-model-settings_1674
+
+[hint:Solid infill threshold area]
+text = Solid infill threshold area\nDid you know that you can make parts of your model with a small cross-section be filled with solid infill automatically? Set theSolid infill threshold area.(Expert mode only.)
+hypertext_type = settings
+hypertext_settings_opt = solid_infill_below_area
+hypertext_settings_type = 1
+hypertext_settings_category = Infill
+disabled_modes = SLA; simple; advanced
+
+[hint:Search functionality]
+text = Search functionality\n Did you know that you use theSearchtool to quickly find a specific PrusaSlicer setting? Or use the familiar shortcut Ctrl+F.
+hypertext_type = plater
+hypertext_plater_item = search
+
+[hint:Box selection]
+text = Box selection\nDid you know that you can do a box selection with Shift+Mouse drag? You can also box-deselect objects with Alt+Mouse drag.
+
+[hint:Zoom on selected objects or on all objects if none selected]
+text =Zoom on selected objects or on all objects if none selected\nDid you know that you can zoom in on selected objects by pressing the Z key? If none are selected, the camera will zoom on all objects in the scene.
+
+[hint:Shapes gallery]
+text = Shapes gallery\nDid you know that PrusaSlicer has a Shapes Gallery? You can use the included models as modifiers, negative volumes or as printable objects. Right-click the platter and selectAdd Shape - Gallery.
+hypertext_type = gallery
+disable_modes = simple
+
+[hint:Printable toggle]
+text = Printable toggle\nDid you know that you can disable the G-code generation for the selected model without having to move or delete it? Toggle the Printable property of a model from the Right-click context menu.
+
+[hint:Mirror]
+text = Mirror\nDid you know that you can mirror the selected model to create a reversed version of it? Right-click the model, select Mirror and pick the mirror axis.
+
+[hint:PageUp / PageDown quick rotation by 45 degrees]
+text = PageUp / PageDown quick rotation by 45 degrees\nDid you know that you can quickly rotate selected models by 45 degrees around the Z-axis clockwise or counter-clockwise by pressing Page Up or Page Down respectively?
+
+[hint:Load config from G-code]
+text = Load config from G-code\nDid you know that you can use File-Import Config to load print, filament and printer profiles from an existing G-code file? Similarly, you can use File-Import SL1 archive, which also lets you reconstruct 3D models from the voxel data.
+
+[hint:Ironing]
+text = Ironing\nDid you know that you can smooth top surfaces of prints using Ironing? The nozzle will run a special second infill phase at the same layer to fill in holes and flatten any lifted plastic. Read more in thedocumentation. (Requires Advanced or Expert mode.)
+hypertext_type = link
+hypertext_link = https://help.prusa3d.com/en/article/ironing_177488
+disabled_modes = SLA; simple
+
+[hint:Fuzzy skin]
+text = Fuzzy skin\nDid you know that you can create rough fibre-like texture on the sides of your models using theFuzzy skinfeature? You can also use modifiers to apply fuzzy-skin only to a portion of your model.
+hypertext_type = settings
+hypertext_settings_opt = fuzzy_skin
+hypertext_settings_type = 1
+hypertext_settings_category = Layers and perimeters
+disabled_modes = SLA
+
+[hint:Negative volume]
+text = Negative volume\nDid you know that you can subtract one mesh from another using the Negative volume modifier? That way you can, for example, create easily resizable holes directly in PrusaSlicer. Read more in thedocumentation.(Requires Advanced or Expert mode.)
+hypertext_type = link
+hypertext_link = https://help.prusa3d.com/en/article/negative-volume_238503
+disabled_modes = SLA; simple
+
+[hint:Paint-on supports]
+text = Paint-on supports\nDid you know that you can paint directly on the object and select areas, where supports should be enforced or blocked? Try thePaint-on supportsfeature. (Requires Advanced or Expert mode.)
+hypertext_type = gizmo
+hypertext_gizmo_item = fdm_supports
+disabled_modes = SLA; simple
+
+[hint:Paint-on seam]
+text = Paint-on seam\nDid you know that you can paint directly on the object and select where to place the start/endpoint of each perimeter loop? Try theSeam paintingfeature. (Requires Advanced or Expert mode.)
+hypertext_type = gizmo
+hypertext_gizmo_item = seam
+disabled_modes = SLA; simple
+
+[hint:Insert Pause]
+text = Insert Pause\nDid you know that you can schedule the print to pause at a specific layer? Right-click the layer slider in the Preview and select Add pause print (M601). This can be used to insert magnets, weights or nuts into your prints. Read more in thedocumentation.
+hypertext_type = link
+hypertext_link = https://help.prusa3d.com/en/article/insert-pause-or-custom-g-code-at-layer_120490#insert-pause-at-layer
+
+[hint:Insert Custom G-code]
+text = Insert Custom G-code\nDid you know that you can insert a custom G-code at a specific layer? Right-click the layer in the Preview and select Add custom G-code. With this function you can, for example, create a temperature tower. Read more in thedocumentation.
+hypertext_type = link
+hypertext_link = https://help.prusa3d.com/en/article/insert-pause-or-custom-g-code-at-layer_120490#insert-custom-g-code-at-layer
+
+[hint:Configuration snapshots]
+text = Configuration snapshots\nDid you know that roll back to a complete backup of all system and user profiles? You can view and move back and forth between snapshots using the Configuration - Configuration snapshots menu. Read more in thedocumentation.
+hypertext_type = link
+hypertext_link = https://help.prusa3d.com/en/article/configuration-snapshots_1776
+
+[hint:Minimum wall thickness]
+text = Minimum wall thickness\nDid you know that instead of the number of top and bottom layers, you can define theMinimum shell thicknessin millimeters? This feature is especially useful when using the variable layer height function.
+hypertext_type = settings
+hypertext_settings_opt = top_solid_min_thickness
+hypertext_settings_type = 1
+hypertext_settings_category = Layers and perimeters
+disabled_modes = SLA
+
+[hint:Settings in non-modal window]
+text = Settings in non-modal window\nDid you know that you can open the Settings in a new non-modal window? This means you can have settings open on one screen and the G-code Preview on the other. Go to thePreferencesand select Settings in non-modal window.
+hypertext_type = preferences
+hypertext_preferences_page = 2
+
+[hint:Adaptive infills]
+text = Adaptive infills\nDid you know that you can use the Adaptive cubic and Support cubic infills to decrease the print time and lower the filament consumption? Read more in thedocumentation.
+hypertext_type = link
+hypertext_link = https://help.prusa3d.com/en/article/infill-patterns_177130
+
+[hint:Fullscreen mode]
+text = Fullscreen mode\nDid you know that you can switch PrusaSlicer to fullscreen mode? Use the F11 hotkey.
+
+[hint:Simplify mesh]
+text = Simplify mesh\nDid you know that you can reduce the number of triangles in a mesh using the Simplify mesh feature? Right-click the model and select Simplify model. Read more in thedocumentation.
+hypertext_type = link
+hypertext_link = https://help.prusa3d.com/en/article/simplify-mesh_238941
+
+#[hint:]
+#text =
+#hypertext =
+#follow_text =
+
diff --git a/resources/icons/notification_left.svg b/resources/icons/notification_left.svg
new file mode 100644
index 000000000..688f9a32b
--- /dev/null
+++ b/resources/icons/notification_left.svg
@@ -0,0 +1,68 @@
+
+
diff --git a/resources/icons/notification_left_hover.svg b/resources/icons/notification_left_hover.svg
new file mode 100644
index 000000000..66046ee50
--- /dev/null
+++ b/resources/icons/notification_left_hover.svg
@@ -0,0 +1,68 @@
+
+
diff --git a/resources/icons/notification_preferences.svg b/resources/icons/notification_preferences.svg
new file mode 100644
index 000000000..979101e58
--- /dev/null
+++ b/resources/icons/notification_preferences.svg
@@ -0,0 +1,67 @@
+
+
diff --git a/resources/icons/notification_preferences_hover.svg b/resources/icons/notification_preferences_hover.svg
new file mode 100644
index 000000000..09f229ba9
--- /dev/null
+++ b/resources/icons/notification_preferences_hover.svg
@@ -0,0 +1,70 @@
+
+
diff --git a/resources/icons/notification_right.svg b/resources/icons/notification_right.svg
new file mode 100644
index 000000000..58db9cc3c
--- /dev/null
+++ b/resources/icons/notification_right.svg
@@ -0,0 +1,68 @@
+
+
diff --git a/resources/icons/notification_right_hover.svg b/resources/icons/notification_right_hover.svg
new file mode 100644
index 000000000..ede2eb677
--- /dev/null
+++ b/resources/icons/notification_right_hover.svg
@@ -0,0 +1,68 @@
+
+
diff --git a/resources/icons/toolbar_arrow.png b/resources/icons/toolbar_arrow.png
new file mode 100644
index 000000000..65905a727
Binary files /dev/null and b/resources/icons/toolbar_arrow.png differ
diff --git a/resources/icons/toolbar_arrow.svg b/resources/icons/toolbar_arrow.svg
new file mode 100644
index 000000000..a1476bcd9
--- /dev/null
+++ b/resources/icons/toolbar_arrow.svg
@@ -0,0 +1,79 @@
+
+
diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h
index 5dd6ec608..6416e1fc2 100644
--- a/src/imgui/imconfig.h
+++ b/src/imgui/imconfig.h
@@ -131,8 +131,6 @@ namespace ImGui
const char MaterialIconMarker = 0x8;
const char CloseNotifButton = 0xB;
const char CloseNotifHoverButton = 0xC;
-// const char TimerDotMarker = 0xE;
-// const char TimerDotEmptyMarker = 0xF;
const char MinimalizeButton = 0xE;
const char MinimalizeHoverButton = 0xF;
const char WarningMarker = 0x10;
@@ -141,6 +139,12 @@ namespace ImGui
const char EjectHoverButton = 0x13;
const char CancelButton = 0x14;
const char CancelHoverButton = 0x15;
+ const char LeftArrowButton = 0x16;
+ const char LeftArrowHoverButton = 0x17;
+ const char RightArrowButton = 0x18;
+ const char RightArrowHoverButton = 0x19;
+ const char PreferencesButton = 0x1A;
+ const char PreferencesHoverButton = 0x1B;
// void MyFunction(const char* name, const MyMatrix44& v);
}
diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp
index dc720db48..cc5439b8d 100644
--- a/src/libslic3r/AppConfig.cpp
+++ b/src/libslic3r/AppConfig.cpp
@@ -170,6 +170,12 @@ void AppConfig::set_defaults()
if (get("show_splash_screen").empty())
set("show_splash_screen", "1");
+ if (get("last_hint").empty())
+ set("last_hint", "0");
+
+ if (get("show_hints").empty())
+ set("show_hints", "1");
+
#ifdef _WIN32
if (get("use_legacy_3DConnexion").empty())
set("use_legacy_3DConnexion", "0");
diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt
index 64d09674c..d21f55d15 100644
--- a/src/slic3r/CMakeLists.txt
+++ b/src/slic3r/CMakeLists.txt
@@ -201,6 +201,8 @@ set(SLIC3R_GUI_SOURCES
GUI/ProjectDirtyStateManager.cpp
GUI/DesktopIntegrationDialog.cpp
GUI/DesktopIntegrationDialog.hpp
+ GUI/HintNotification.cpp
+ GUI/HintNotification.hpp
Utils/Http.cpp
Utils/Http.hpp
Utils/FixModelByWin10.cpp
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 6f34f4051..e089621f2 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -900,6 +900,8 @@ wxDEFINE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event);
wxDEFINE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, HeightProfileSmoothEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_RELOAD_FROM_DISK, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_RENDER_TIMER, wxTimerEvent/*RenderTimerEvent*/);
+wxDEFINE_EVENT(EVT_GLCANVAS_TOOLBAR_HIGHLIGHTER_TIMER, wxTimerEvent);
+wxDEFINE_EVENT(EVT_GLCANVAS_GIZMO_HIGHLIGHTER_TIMER, wxTimerEvent);
const double GLCanvas3D::DefaultCameraZoomToBoxMarginFactor = 1.25;
@@ -2164,6 +2166,10 @@ void GLCanvas3D::bind_event_handlers()
m_canvas->Bind(wxEVT_MOUSEWHEEL, &GLCanvas3D::on_mouse_wheel, this);
m_canvas->Bind(wxEVT_TIMER, &GLCanvas3D::on_timer, this);
m_canvas->Bind(EVT_GLCANVAS_RENDER_TIMER, &GLCanvas3D::on_render_timer, this);
+ m_toolbar_highlighter.set_timer_owner(m_canvas, 0);
+ m_canvas->Bind(EVT_GLCANVAS_TOOLBAR_HIGHLIGHTER_TIMER, [this](wxTimerEvent&) { m_toolbar_highlighter.blink(); });
+ m_gizmo_highlighter.set_timer_owner(m_canvas, 0);
+ m_canvas->Bind(EVT_GLCANVAS_GIZMO_HIGHLIGHTER_TIMER, [this](wxTimerEvent&) { m_gizmo_highlighter.blink(); });
m_canvas->Bind(wxEVT_LEFT_DOWN, &GLCanvas3D::on_mouse, this);
m_canvas->Bind(wxEVT_LEFT_UP, &GLCanvas3D::on_mouse, this);
m_canvas->Bind(wxEVT_MIDDLE_DOWN, &GLCanvas3D::on_mouse, this);
@@ -4399,6 +4405,29 @@ bool GLCanvas3D::_init_main_toolbar()
m_main_toolbar.set_enabled(false);
return true;
}
+ // init arrow
+ BackgroundTexture::Metadata arrow_data;
+ arrow_data.filename = "toolbar_arrow.png";
+// arrow_data.filename = "toolbar_arrow.svg";
+ //arrow_data.left = 16;
+ //arrow_data.top = 16;
+ //arrow_data.right = 16;
+ //arrow_data.bottom = 16;
+
+ arrow_data.left = 0;
+ arrow_data.top = 0;
+ arrow_data.right = 0;
+ arrow_data.bottom = 0;
+
+ if (!m_main_toolbar.init_arrow(arrow_data))
+ {
+ BOOST_LOG_TRIVIAL(error) << "Main toolbar failed to load arrow texture.";
+ }
+
+ if (!m_gizmos.init_arrow(arrow_data))
+ {
+ BOOST_LOG_TRIVIAL(error) << "Gizmos manager failed to load arrow texture.";
+ }
// m_main_toolbar.set_layout_type(GLToolbar::Layout::Vertical);
m_main_toolbar.set_layout_type(GLToolbar::Layout::Horizontal);
@@ -5313,6 +5342,11 @@ void GLCanvas3D::_render_gizmos_overlay()
#endif /* __WXMSW__ */
m_gizmos.render_overlay();
+
+ if (m_gizmo_highlighter.m_render_arrow)
+ {
+ m_gizmos.render_arrow(*this, m_gizmo_highlighter.m_gizmo_type);
+ }
}
void GLCanvas3D::_render_main_toolbar()
@@ -5330,6 +5364,10 @@ void GLCanvas3D::_render_main_toolbar()
m_main_toolbar.set_position(top, left);
m_main_toolbar.render(*this);
+ if (m_toolbar_highlighter.m_render_arrow)
+ {
+ m_main_toolbar.render_arrow(*this, m_toolbar_highlighter.m_toolbar_item);
+ }
}
void GLCanvas3D::_render_undoredo_toolbar()
@@ -6497,6 +6535,24 @@ bool GLCanvas3D::_deactivate_collapse_toolbar_items()
return false;
}
+void GLCanvas3D::highlight_toolbar_item(const std::string& item_name)
+{
+ GLToolbarItem* item = m_main_toolbar.get_item(item_name);
+ if (!item)
+ item = m_undoredo_toolbar.get_item(item_name);
+ if (!item || !item->is_visible())
+ return;
+ m_toolbar_highlighter.init(item, this);
+}
+
+void GLCanvas3D::highlight_gizmo(const std::string& gizmo_name)
+{
+ GLGizmosManager::EType gizmo = m_gizmos.get_gizmo_from_name(gizmo_name);
+ if(gizmo == GLGizmosManager::EType::Undefined)
+ return;
+ m_gizmo_highlighter.init(&m_gizmos, gizmo, this);
+}
+
const Print* GLCanvas3D::fff_print() const
{
return (m_process == nullptr) ? nullptr : m_process->fff_print();
@@ -6516,10 +6572,119 @@ void GLCanvas3D::WipeTowerInfo::apply_wipe_tower() const
wxGetApp().get_tab(Preset::TYPE_PRINT)->load_config(cfg);
}
-
-void GLCanvas3D::RenderTimer::Notify()
+void GLCanvas3D::RenderTimer::Notify()
{
wxPostEvent((wxEvtHandler*)GetOwner(), RenderTimerEvent( EVT_GLCANVAS_RENDER_TIMER, *this));
}
+
+void GLCanvas3D::ToolbarHighlighterTimer::Notify()
+{
+ wxPostEvent((wxEvtHandler*)GetOwner(), ToolbarHighlighterTimerEvent(EVT_GLCANVAS_TOOLBAR_HIGHLIGHTER_TIMER, *this));
+}
+
+void GLCanvas3D::GizmoHighlighterTimer::Notify()
+{
+ wxPostEvent((wxEvtHandler*)GetOwner(), GizmoHighlighterTimerEvent(EVT_GLCANVAS_GIZMO_HIGHLIGHTER_TIMER, *this));
+}
+
+void GLCanvas3D::ToolbarHighlighter::set_timer_owner(wxEvtHandler* owner, int timerid/* = wxID_ANY*/)
+{
+ m_timer.SetOwner(owner, timerid);
+}
+
+void GLCanvas3D::ToolbarHighlighter::init(GLToolbarItem* toolbar_item, GLCanvas3D* canvas)
+{
+ if (m_timer.IsRunning())
+ invalidate();
+ if (!toolbar_item || !canvas)
+ return;
+
+ m_timer.Start(300, false);
+
+ m_toolbar_item = toolbar_item;
+ m_canvas = canvas;
+}
+
+void GLCanvas3D::ToolbarHighlighter::invalidate()
+{
+ m_timer.Stop();
+
+ if (m_toolbar_item) {
+ m_toolbar_item->set_highlight(GLToolbarItem::EHighlightState::NotHighlighted);
+ }
+ m_toolbar_item = nullptr;
+ m_blink_counter = 0;
+ m_render_arrow = false;
+}
+
+void GLCanvas3D::ToolbarHighlighter::blink()
+{
+ if (m_toolbar_item) {
+ char state = m_toolbar_item->get_highlight();
+ if (state != (char)GLToolbarItem::EHighlightState::HighlightedShown)
+ m_toolbar_item->set_highlight(GLToolbarItem::EHighlightState::HighlightedShown);
+ else
+ m_toolbar_item->set_highlight(GLToolbarItem::EHighlightState::HighlightedHidden);
+
+ m_render_arrow = !m_render_arrow;
+ m_canvas->set_as_dirty();
+ }
+ else
+ invalidate();
+
+ if ((++m_blink_counter) >= 11)
+ invalidate();
+}
+
+void GLCanvas3D::GizmoHighlighter::set_timer_owner(wxEvtHandler* owner, int timerid/* = wxID_ANY*/)
+{
+ m_timer.SetOwner(owner, timerid);
+}
+
+void GLCanvas3D::GizmoHighlighter::init(GLGizmosManager* manager, GLGizmosManager::EType gizmo, GLCanvas3D* canvas)
+{
+ if (m_timer.IsRunning())
+ invalidate();
+ if (!gizmo || !canvas)
+ return;
+
+ m_timer.Start(300, false);
+
+ m_gizmo_manager = manager;
+ m_gizmo_type = gizmo;
+ m_canvas = canvas;
+}
+
+void GLCanvas3D::GizmoHighlighter::invalidate()
+{
+ m_timer.Stop();
+
+ if (m_gizmo_manager) {
+ m_gizmo_manager->set_highlight(GLGizmosManager::EType::Undefined, false);
+ }
+ m_gizmo_manager = nullptr;
+ m_gizmo_type = GLGizmosManager::EType::Undefined;
+ m_blink_counter = 0;
+ m_render_arrow = false;
+}
+
+void GLCanvas3D::GizmoHighlighter::blink()
+{
+ if (m_gizmo_manager) {
+ if (m_blink_counter % 2 == 0)
+ m_gizmo_manager->set_highlight(m_gizmo_type, true);
+ else
+ m_gizmo_manager->set_highlight(m_gizmo_type, false);
+
+ m_render_arrow = !m_render_arrow;
+ m_canvas->set_as_dirty();
+ }
+ else
+ invalidate();
+
+ if ((++m_blink_counter) >= 11)
+ invalidate();
+}
+
} // namespace GUI
} // namespace Slic3r
diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp
index b5fa86235..d190c0819 100644
--- a/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/src/slic3r/GUI/GLCanvas3D.hpp
@@ -93,6 +93,43 @@ private:
wxTimer* m_timer;
};
+class ToolbarHighlighterTimerEvent : public wxEvent
+{
+public:
+ ToolbarHighlighterTimerEvent(wxEventType type, wxTimer& timer)
+ : wxEvent(timer.GetId(), type),
+ m_timer(&timer)
+ {
+ SetEventObject(timer.GetOwner());
+ }
+ int GetInterval() const { return m_timer->GetInterval(); }
+ wxTimer& GetTimer() const { return *m_timer; }
+
+ virtual wxEvent* Clone() const { return new ToolbarHighlighterTimerEvent(*this); }
+ virtual wxEventCategory GetEventCategory() const { return wxEVT_CATEGORY_TIMER; }
+private:
+ wxTimer* m_timer;
+};
+
+
+class GizmoHighlighterTimerEvent : public wxEvent
+{
+public:
+ GizmoHighlighterTimerEvent(wxEventType type, wxTimer& timer)
+ : wxEvent(timer.GetId(), type),
+ m_timer(&timer)
+ {
+ SetEventObject(timer.GetOwner());
+ }
+ int GetInterval() const { return m_timer->GetInterval(); }
+ wxTimer& GetTimer() const { return *m_timer; }
+
+ virtual wxEvent* Clone() const { return new GizmoHighlighterTimerEvent(*this); }
+ virtual wxEventCategory GetEventCategory() const { return wxEVT_CATEGORY_TIMER; }
+private:
+ wxTimer* m_timer;
+};
+
wxDECLARE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent);
@@ -137,6 +174,8 @@ wxDECLARE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event);
wxDECLARE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, HeightProfileSmoothEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_RELOAD_FROM_DISK, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_RENDER_TIMER, wxTimerEvent/*RenderTimerEvent*/);
+wxDECLARE_EVENT(EVT_GLCANVAS_TOOLBAR_HIGHLIGHTER_TIMER, wxTimerEvent);
+wxDECLARE_EVENT(EVT_GLCANVAS_GIZMO_HIGHLIGHTER_TIMER, wxTimerEvent);
class GLCanvas3D
{
@@ -378,6 +417,16 @@ class GLCanvas3D
virtual void Notify() override;
};
+ class ToolbarHighlighterTimer : public wxTimer {
+ private:
+ virtual void Notify() override;
+ };
+
+ class GizmoHighlighterTimer : public wxTimer {
+ private:
+ virtual void Notify() override;
+ };
+
public:
enum ECursorType : unsigned char
{
@@ -517,6 +566,38 @@ private:
SequentialPrintClearance m_sequential_print_clearance;
bool m_sequential_print_clearance_first_displacement{ true };
+ struct ToolbarHighlighter
+ {
+ void set_timer_owner(wxEvtHandler* owner, int timerid = wxID_ANY);
+ void init(GLToolbarItem* toolbar_item, GLCanvas3D* canvas);
+ void blink();
+ void invalidate();
+ bool m_render_arrow{ false };
+ GLToolbarItem* m_toolbar_item{ nullptr };
+ private:
+ GLCanvas3D* m_canvas{ nullptr };
+ int m_blink_counter{ 0 };
+ ToolbarHighlighterTimer m_timer;
+ }
+ m_toolbar_highlighter;
+
+ struct GizmoHighlighter
+ {
+ void set_timer_owner(wxEvtHandler* owner, int timerid = wxID_ANY);
+ void init(GLGizmosManager* manager, GLGizmosManager::EType gizmo, GLCanvas3D* canvas);
+ void blink();
+ void invalidate();
+ bool m_render_arrow{ false };
+ GLGizmosManager::EType m_gizmo_type;
+ private:
+ GLGizmosManager* m_gizmo_manager{ nullptr };
+ GLCanvas3D* m_canvas{ nullptr };
+ int m_blink_counter{ 0 };
+ GizmoHighlighterTimer m_timer;
+
+ }
+ m_gizmo_highlighter;
+
public:
explicit GLCanvas3D(wxGLCanvas* canvas);
~GLCanvas3D();
@@ -744,6 +825,9 @@ public:
void use_slope(bool use) { m_slope.use(use); }
void set_slope_normal_angle(float angle_in_deg) { m_slope.set_normal_angle(angle_in_deg); }
+ void highlight_toolbar_item(const std::string& item_name);
+ void highlight_gizmo(const std::string& gizmo_name);
+
ArrangeSettings get_arrange_settings() const {
const ArrangeSettings &settings = get_arrange_settings(this);
ArrangeSettings ret = settings;
diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp
index 178c17d15..0167ba3ce 100644
--- a/src/slic3r/GUI/GLToolbar.cpp
+++ b/src/slic3r/GUI/GLToolbar.cpp
@@ -61,6 +61,7 @@ GLToolbarItem::GLToolbarItem(GLToolbarItem::EType type, const GLToolbarItem::Dat
, m_state(Normal)
, m_data(data)
, m_last_action_type(Undefined)
+ , m_highlight_state(NotHighlighted)
{
}
@@ -91,7 +92,8 @@ void GLToolbarItem::render(unsigned int tex_id, float left, float right, float b
assert((tex_width != 0) && (tex_height != 0));
GLTexture::Quad_UVs ret;
// tiles in the texture are spaced by 1 pixel
- float icon_size_px = (float)(tex_width - 1) / (float)Num_States;
+ float icon_size_px = (float)(tex_width - 1) / ((float)Num_States + (float)Num_Rendered_Highlight_States);
+ char render_state = (m_highlight_state == NotHighlighted ? m_state : Num_States + m_highlight_state);
float inv_tex_width = 1.0f / (float)tex_width;
float inv_tex_height = 1.0f / (float)tex_height;
// tiles in the texture are spaced by 1 pixel
@@ -99,7 +101,7 @@ void GLToolbarItem::render(unsigned int tex_id, float left, float right, float b
float v_offset = 1.0f * inv_tex_height;
float du = icon_size_px * inv_tex_width;
float dv = icon_size_px * inv_tex_height;
- float left = u_offset + (float)m_state * du;
+ float left = u_offset + (float)render_state * du;
float right = left + du - u_offset;
float top = v_offset + (float)m_data.sprite_id * dv;
float bottom = top + dv - v_offset;
@@ -183,6 +185,24 @@ bool GLToolbar::init(const BackgroundTexture::Metadata& background_texture)
return res;
}
+bool GLToolbar::init_arrow(const BackgroundTexture::Metadata& arrow_texture)
+{
+ if (m_arrow_texture.texture.get_id() != 0)
+ return true;
+
+ std::string path = resources_dir() + "/icons/";
+ bool res = false;
+
+ if (!arrow_texture.filename.empty())
+ res = m_arrow_texture.texture.load_from_file(path + arrow_texture.filename, false, GLTexture::SingleThreaded, false);
+// res = m_arrow_texture.texture.load_from_svg_file(path + arrow_texture.filename, false, true, false, 100);
+
+ if (res)
+ m_arrow_texture.metadata = arrow_texture;
+
+ return res;
+}
+
GLToolbar::Layout::EType GLToolbar::get_layout_type() const
{
return m_layout.type;
@@ -419,6 +439,8 @@ void GLToolbar::render(const GLCanvas3D& parent)
}
}
+
+
bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent)
{
if (!m_enabled)
@@ -869,6 +891,21 @@ void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D&
}
}
+GLToolbarItem* GLToolbar::get_item(const std::string& item_name)
+{
+ if (!m_enabled)
+ return nullptr;
+
+ for (GLToolbarItem* item : m_items)
+ {
+ if (item->get_name() == item_name)
+ {
+ return item;
+ }
+ }
+ return nullptr;
+}
+
int GLToolbar::contains_mouse(const Vec2d& mouse_pos, const GLCanvas3D& parent) const
{
if (!m_enabled)
@@ -1105,6 +1142,63 @@ void GLToolbar::render_background(float left, float top, float right, float bott
}
}
+void GLToolbar::render_arrow(const GLCanvas3D& parent, GLToolbarItem* highlighted_item)
+{
+ float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom();
+ float factor = inv_zoom * m_layout.scale;
+
+ float scaled_icons_size = m_layout.icons_size * factor;
+ float scaled_separator_size = m_layout.separator_size * factor;
+ float scaled_gap_size = m_layout.gap_size * factor;
+ float border = m_layout.border * factor;
+
+ float separator_stride = scaled_separator_size + scaled_gap_size;
+ float icon_stride = scaled_icons_size + scaled_gap_size;
+
+ float left = m_layout.left;
+ float top = m_layout.top - icon_stride;
+
+ for (const GLToolbarItem* item : m_items) {
+ if (!item->is_visible())
+ continue;
+
+ if (item->is_separator())
+ left += separator_stride;
+ else {
+ if (item->get_name() == highlighted_item->get_name())
+ break;
+ left += icon_stride;
+ }
+ }
+
+ left += border;
+ top -= separator_stride;
+ float right = left + scaled_icons_size;
+
+ unsigned int tex_id = m_arrow_texture.texture.get_id();
+ float tex_width = (float)m_icons_texture.get_width();
+ float tex_height = (float)m_icons_texture.get_height();
+
+ if ((tex_id != 0) && (tex_width > 0) && (tex_height > 0)) {
+ float inv_tex_width = (tex_width != 0.0f) ? 1.0f / tex_width : 0.0f;
+ float inv_tex_height = (tex_height != 0.0f) ? 1.0f / tex_height : 0.0f;
+
+ float internal_left = left + border - scaled_icons_size / 2; // add half scaled_icons_size for huge arrow
+ float internal_right = right - border + scaled_icons_size / 2;
+ float internal_top = top - border;
+ // bottom is not moving and should be calculated from arrow texture sides ratio
+ float arrow_sides_ratio = (float)m_arrow_texture.texture.get_height() / (float)m_arrow_texture.texture.get_width();
+ float internal_bottom = internal_top - (internal_right - internal_left) * arrow_sides_ratio;
+
+ float internal_left_uv = (float)m_arrow_texture.metadata.left * inv_tex_width;
+ float internal_right_uv = 1.0f - (float)m_arrow_texture.metadata.right * inv_tex_width;
+ float internal_top_uv = 1.0f - (float)m_arrow_texture.metadata.top * inv_tex_height;
+ float internal_bottom_uv = (float)m_arrow_texture.metadata.bottom * inv_tex_height;
+
+ GLTexture::render_sub_texture(tex_id, internal_left, internal_right, internal_bottom, internal_top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } });
+ }
+}
+
void GLToolbar::render_horizontal(const GLCanvas3D& parent)
{
unsigned int tex_id = m_icons_texture.get_id();
@@ -1217,6 +1311,8 @@ bool GLToolbar::generate_icons_texture()
states.push_back({ 0, false }); // Hover
states.push_back({ 0, false }); // HoverPressed
states.push_back({ 2, false }); // HoverDisabled
+ states.push_back({ 0, false }); // HighlightedShown
+ states.push_back({ 2, false }); // HighlightedHidden
}
else {
states.push_back({ 1, false }); // Normal
@@ -1225,6 +1321,8 @@ bool GLToolbar::generate_icons_texture()
states.push_back({ 0, false }); // Hover
states.push_back({ 1, true }); // HoverPressed
states.push_back({ 1, false }); // HoverDisabled
+ states.push_back({ 0, false }); // HighlightedShown
+ states.push_back({ 1, false }); // HighlightedHidden
}
unsigned int sprite_size_px = (unsigned int)(m_layout.icons_size * m_layout.scale);
diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp
index 92df63bfb..5740db3e6 100644
--- a/src/slic3r/GUI/GLToolbar.hpp
+++ b/src/slic3r/GUI/GLToolbar.hpp
@@ -65,6 +65,14 @@ public:
Num_States
};
+ enum EHighlightState : unsigned char
+ {
+ HighlightedShown,
+ HighlightedHidden,
+ Num_Rendered_Highlight_States,
+ NotHighlighted
+ };
+
struct Data
{
struct Option
@@ -104,13 +112,16 @@ private:
EState m_state;
Data m_data;
EActionType m_last_action_type;
-
+ EHighlightState m_highlight_state;
public:
GLToolbarItem(EType type, const Data& data);
EState get_state() const { return m_state; }
void set_state(EState state) { m_state = state; }
+ EHighlightState get_highlight() const { return m_highlight_state; }
+ void set_highlight(EHighlightState state) { m_highlight_state = state; }
+
const std::string& get_name() const { return m_data.name; }
const std::string& get_icon_filename() const { return m_data.icon_filename; }
const std::string& get_tooltip() const { return m_data.tooltip; }
@@ -143,7 +154,6 @@ public:
bool update_enabled_state();
void render(unsigned int tex_id, float left, float right, float bottom, float top, unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const;
-
private:
void set_visible(bool visible) { m_data.visible = visible; }
@@ -236,6 +246,7 @@ private:
GLTexture m_icons_texture;
bool m_icons_texture_dirty;
BackgroundTexture m_background_texture;
+ BackgroundTexture m_arrow_texture;
Layout m_layout;
ItemsList m_items;
@@ -262,6 +273,8 @@ public:
bool init(const BackgroundTexture::Metadata& background_texture);
+ bool init_arrow(const BackgroundTexture::Metadata& arrow_texture);
+
Layout::EType get_layout_type() const;
void set_layout_type(Layout::EType type);
Layout::EHorizontalOrientation get_horizontal_orientation() const { return m_layout.horizontal_orientation; }
@@ -310,9 +323,11 @@ public:
bool update_items_state();
void render(const GLCanvas3D& parent);
+ void render_arrow(const GLCanvas3D& parent, GLToolbarItem* highlighted_item);
bool on_mouse(wxMouseEvent& evt, GLCanvas3D& parent);
-
+ // get item pointer for highlighter timer
+ GLToolbarItem* get_item(const std::string& item_name);
private:
void calc_layout();
float get_width_horizontal() const;
diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp
index a73ca880a..40f375664 100644
--- a/src/slic3r/GUI/GUI_App.cpp
+++ b/src/slic3r/GUI/GUI_App.cpp
@@ -661,6 +661,10 @@ void GUI_App::post_init()
this->mainframe->load_config(this->init_params->extra_config);
}
+ // show "Did you know" notification
+ if (app_config->get("show_hints") == "1")
+ plater_->get_notification_manager()->push_hint_notification();
+
// The extra CallAfter() is needed because of Mac, where this is the only way
// to popup a modal dialog on start without screwing combo boxes.
// This is ugly but I honestly found no better way to do it.
@@ -1981,6 +1985,43 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
menu->Append(local_menu, _L("&Configuration"));
}
+void GUI_App::open_preferences(size_t open_on_tab)
+{
+ bool app_layout_changed = false;
+ {
+ // the dialog needs to be destroyed before the call to recreate_GUI()
+ // or sometimes the application crashes into wxDialogBase() destructor
+ // so we put it into an inner scope
+ PreferencesDialog dlg(mainframe, open_on_tab);
+ dlg.ShowModal();
+ app_layout_changed = dlg.settings_layout_changed();
+#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
+ if (dlg.seq_top_layer_only_changed() || dlg.seq_seq_top_gcode_indices_changed())
+#else
+ if (dlg.seq_top_layer_only_changed())
+#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
+ this->plater_->refresh_print();
+#ifdef _WIN32
+ if (is_editor()) {
+ if (app_config->get("associate_3mf") == "1")
+ associate_3mf_files();
+ if (app_config->get("associate_stl") == "1")
+ associate_stl_files();
+ }
+ else {
+ if (app_config->get("associate_gcode") == "1")
+ associate_gcode_files();
+ }
+#endif // _WIN32
+ }
+ if (app_layout_changed) {
+ // hide full main_sizer for mainFrame
+ mainframe->GetSizer()->Show(false);
+ mainframe->update_layout();
+ mainframe->select_tab(size_t(0));
+ }
+}
+
#if ENABLE_PROJECT_DIRTY_STATE
bool GUI_App::has_unsaved_preset_changes() const
{
diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp
index 92bfd67c4..0ca73cde9 100644
--- a/src/slic3r/GUI/GUI_App.hpp
+++ b/src/slic3r/GUI/GUI_App.hpp
@@ -256,6 +256,8 @@ public:
wxString current_language_code_safe() const;
bool is_localized() const { return m_wxLocale->GetLocale() != "English"; }
+ void open_preferences(size_t open_on_tab = 0);
+
virtual bool OnExceptionInMainLoop() override;
#ifdef __APPLE__
diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
index f06acf28d..dbfeaaa80 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
@@ -123,10 +123,28 @@ bool GLGizmosManager::init()
m_current = Undefined;
m_hover = Undefined;
+ m_highlight = std::pair(Undefined, false);
return true;
}
+bool GLGizmosManager::init_arrow(const BackgroundTexture::Metadata& arrow_texture)
+{
+ if (m_arrow_texture.texture.get_id() != 0)
+ return true;
+
+ std::string path = resources_dir() + "/icons/";
+ bool res = false;
+
+ if (!arrow_texture.filename.empty())
+ res = m_arrow_texture.texture.load_from_file(path + arrow_texture.filename, false, GLTexture::SingleThreaded, false);
+// res = m_arrow_texture.texture.load_from_svg_file(path + arrow_texture.filename, false, true, false, 100);
+ if (res)
+ m_arrow_texture.metadata = arrow_texture;
+
+ return res;
+}
+
void GLGizmosManager::set_overlay_icon_size(float size)
{
if (m_layout.icons_size != size)
@@ -973,6 +991,46 @@ void GLGizmosManager::render_background(float left, float top, float right, floa
}
}
+void GLGizmosManager::render_arrow(const GLCanvas3D& parent, EType highlighted_type) const
+{
+
+ std::vector selectable_idxs = get_selectable_idxs();
+ if (selectable_idxs.empty())
+ return;
+ float cnv_w = (float)m_parent.get_canvas_size().get_width();
+ float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom();
+ float height = get_scaled_total_height();
+ float zoomed_border = m_layout.scaled_border() * inv_zoom;
+ float zoomed_top_x = (-0.5f * cnv_w) * inv_zoom;
+ float zoomed_top_y = (0.5f * height) * inv_zoom;
+ zoomed_top_x += zoomed_border;
+ zoomed_top_y -= zoomed_border;
+ float icons_size = m_layout.scaled_icons_size();
+ float zoomed_icons_size = icons_size * inv_zoom;
+ float zoomed_stride_y = m_layout.scaled_stride_y() * inv_zoom;
+ for (size_t idx : selectable_idxs)
+ {
+ if (idx == highlighted_type) {
+ int tex_width = m_icons_texture.get_width();
+ int tex_height = m_icons_texture.get_height();
+ unsigned int tex_id = m_arrow_texture.texture.get_id();
+ float inv_tex_width = (tex_width != 0.0f) ? 1.0f / tex_width : 0.0f;
+ float inv_tex_height = (tex_height != 0.0f) ? 1.0f / tex_height : 0.0f;
+
+ float internal_left_uv = (float)m_arrow_texture.metadata.left * inv_tex_width;
+ float internal_right_uv = 1.0f - (float)m_arrow_texture.metadata.right * inv_tex_width;
+ float internal_top_uv = 1.0f - (float)m_arrow_texture.metadata.top * inv_tex_height;
+ float internal_bottom_uv = (float)m_arrow_texture.metadata.bottom * inv_tex_height;
+
+ float arrow_sides_ratio = (float)m_arrow_texture.texture.get_height() / (float)m_arrow_texture.texture.get_width();
+
+ GLTexture::render_sub_texture(tex_id, zoomed_top_x + zoomed_icons_size * 1.2f, zoomed_top_x + zoomed_icons_size * 1.2f + zoomed_icons_size * arrow_sides_ratio, zoomed_top_y - zoomed_icons_size, zoomed_top_y, { { internal_left_uv, internal_top_uv }, { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv } });
+ break;
+ }
+ zoomed_top_y -= zoomed_stride_y;
+ }
+}
+
void GLGizmosManager::do_render_overlay() const
{
std::vector selectable_idxs = get_selectable_idxs();
@@ -1012,7 +1070,7 @@ void GLGizmosManager::do_render_overlay() const
if ((icons_texture_id == 0) || (tex_width <= 1) || (tex_height <= 1))
return;
- float du = (float)(tex_width - 1) / (4.0f * (float)tex_width); // 4 is the number of possible states if the icons
+ float du = (float)(tex_width - 1) / (6.0f * (float)tex_width); // 6 is the number of possible states if the icons
float dv = (float)(tex_height - 1) / (float)(m_gizmos.size() * tex_height);
// tiles in the texture are spaced by 1 pixel
@@ -1022,9 +1080,9 @@ void GLGizmosManager::do_render_overlay() const
for (size_t idx : selectable_idxs)
{
GLGizmoBase* gizmo = m_gizmos[idx].get();
-
unsigned int sprite_id = gizmo->get_sprite_id();
- int icon_idx = (m_current == idx) ? 2 : ((m_hover == idx) ? 1 : (gizmo->is_activable()? 0 : 3));
+ // higlighted state needs to be decided first so its highlighting in every other state
+ int icon_idx = (m_highlight.first == idx ? (m_highlight.second ? 4 : 5) : (m_current == idx) ? 2 : ((m_hover == idx) ? 1 : (gizmo->is_activable()? 0 : 3)));
float v_top = v_offset + sprite_id * dv;
float u_left = u_offset + icon_idx * du;
@@ -1055,13 +1113,26 @@ GLGizmoBase* GLGizmosManager::get_current() const
return ((m_current == Undefined) || m_gizmos.empty()) ? nullptr : m_gizmos[m_current].get();
}
+GLGizmosManager::EType GLGizmosManager::get_gizmo_from_name(const std::string& gizmo_name) const
+{
+ std::vector selectable_idxs = get_selectable_idxs();
+ for (size_t idx = 0; idx < selectable_idxs.size(); ++idx)
+ {
+ std::string filename = m_gizmos[selectable_idxs[idx]]->get_icon_filename();
+ filename = filename.substr(0, filename.find_first_of('.'));
+ if (filename == gizmo_name)
+ return (GLGizmosManager::EType)selectable_idxs[idx];
+ }
+ return GLGizmosManager::EType::Undefined;
+}
+
bool GLGizmosManager::generate_icons_texture() const
{
std::string path = resources_dir() + "/icons/";
std::vector filenames;
for (size_t idx=0; idxget_icon_filename();
if (!icon_filename.empty())
@@ -1074,6 +1145,8 @@ bool GLGizmosManager::generate_icons_texture() const
states.push_back(std::make_pair(0, false)); // Hovered
states.push_back(std::make_pair(0, true)); // Selected
states.push_back(std::make_pair(2, false)); // Disabled
+ states.push_back(std::make_pair(0, false)); // HighlightedShown
+ states.push_back(std::make_pair(2, false)); // HighlightedHidden
unsigned int sprite_size_px = (unsigned int)m_layout.scaled_icons_size();
// // force even size
diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp
index 01d7ea85c..64780a2bc 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp
@@ -94,9 +94,11 @@ private:
mutable GLTexture m_icons_texture;
mutable bool m_icons_texture_dirty;
BackgroundTexture m_background_texture;
+ BackgroundTexture m_arrow_texture;
Layout m_layout;
EType m_current;
EType m_hover;
+ std::pair m_highlight; // bool true = higlightedShown, false = highlightedHidden
std::vector get_selectable_idxs() const;
std::vector get_activable_idxs() const;
@@ -128,6 +130,8 @@ public:
bool init();
+ bool init_arrow(const BackgroundTexture::Metadata& arrow_texture);
+
template
void load(Archive& ar)
{
@@ -182,6 +186,7 @@ public:
EType get_current_type() const { return m_current; }
GLGizmoBase* get_current() const;
+ EType get_gizmo_from_name(const std::string& gizmo_name) const;
bool is_running() const;
bool handle_shortcut(int key);
@@ -220,6 +225,8 @@ public:
void render_overlay() const;
+ void render_arrow(const GLCanvas3D& parent, EType highlighted_type) const;
+
std::string get_tooltip() const;
bool on_mouse(wxMouseEvent& evt);
@@ -232,8 +239,13 @@ public:
int get_selectable_icons_cnt() const { return get_selectable_idxs().size(); }
int get_shortcut_key(GLGizmosManager::EType) const;
+ // To end highlight set gizmo = undefined
+ void set_highlight(EType gizmo, bool highlight_shown) { m_highlight = std::pair(gizmo, highlight_shown); }
+ bool get_highlight_state() const { return m_highlight.second; }
+
private:
void render_background(float left, float top, float right, float bottom, float border) const;
+
void do_render_overlay() const;
float get_scaled_total_height() const;
diff --git a/src/slic3r/GUI/HintNotification.cpp b/src/slic3r/GUI/HintNotification.cpp
new file mode 100644
index 000000000..5e8c3014f
--- /dev/null
+++ b/src/slic3r/GUI/HintNotification.cpp
@@ -0,0 +1,671 @@
+#include "HintNotification.hpp"
+#include "ImGuiWrapper.hpp"
+#include "format.hpp"
+#include "I18N.hpp"
+#include "GUI_ObjectList.hpp"
+#include "libslic3r/AppConfig.hpp"
+#include "libslic3r/Utils.hpp"
+#include "libslic3r/Config.hpp"
+#include "libslic3r/PresetBundle.hpp"
+
+#include
+#include
+#include