This commit is contained in:
enricoturri1966 2021-07-29 13:28:16 +02:00
commit 438439fd71
30 changed files with 2118 additions and 199 deletions

View file

@ -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

View file

@ -900,6 +900,8 @@ wxDEFINE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event<float>);
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

View file

@ -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<float>);
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;

View file

@ -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);

View file

@ -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;

View file

@ -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
{

View file

@ -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__

View file

@ -123,10 +123,28 @@ bool GLGizmosManager::init()
m_current = Undefined;
m_hover = Undefined;
m_highlight = std::pair<EType, bool>(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<size_t> 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<size_t> 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<size_t> 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<std::string> filenames;
for (size_t idx=0; idx<m_gizmos.size(); ++idx)
{
if (m_gizmos[idx] != nullptr)
if (m_gizmos[idx] != nullptr)
{
const std::string& icon_filename = m_gizmos[idx]->get_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

View file

@ -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<EType, bool> m_highlight; // bool true = higlightedShown, false = highlightedHidden
std::vector<size_t> get_selectable_idxs() const;
std::vector<size_t> get_activable_idxs() const;
@ -128,6 +130,8 @@ public:
bool init();
bool init_arrow(const BackgroundTexture::Metadata& arrow_texture);
template<class Archive>
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<EType, bool>(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;

View file

@ -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 <boost/filesystem.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <map>
namespace Slic3r {
namespace GUI {
const std::string BOLD_MARKER_START = "<b>";
const std::string BOLD_MARKER_END = "</b>";
const std::string HYPERTEXT_MARKER_START = "<a>";
const std::string HYPERTEXT_MARKER_END = "</a>";
namespace {
inline void push_style_color(ImGuiCol idx, const ImVec4& col, bool fading_out, float current_fade_opacity)
{
if (fading_out)
ImGui::PushStyleColor(idx, ImVec4(col.x, col.y, col.z, col.w * current_fade_opacity));
else
ImGui::PushStyleColor(idx, col);
}
// return true if NOT in disabled mode.
inline bool disabled_modes_check(const std::string& disabled_modes)
{
if (disabled_modes.empty())
return true;
// simple / advanced / expert
ConfigOptionMode config_mode = wxGetApp().get_mode();
std::string mode_name;
if (config_mode == ConfigOptionMode::comSimple) mode_name = "simple";
else if (config_mode == ConfigOptionMode::comAdvanced) mode_name = "advanced";
else if (config_mode == ConfigOptionMode::comExpert) mode_name = "expert";
if (!mode_name.empty() && disabled_modes.find(mode_name) != std::string::npos)
return false;
// fff / sla
const PrinterTechnology tech = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology();
if (tech == ptFFF) {
if (disabled_modes.find("FFF") != std::string::npos)
return false;
} else {
if (disabled_modes.find("SLA") != std::string::npos)
return false;
}
return true;
}
} //namespace
void HintDatabase::init()
{
load_hints_from_file(std::move(boost::filesystem::path(resources_dir()) / "data" / "hints.ini"));
const AppConfig* app_config = wxGetApp().app_config;
m_hint_id = std::atoi(app_config->get("last_hint").c_str());
m_initialized = true;
}
void HintDatabase::load_hints_from_file(const boost::filesystem::path& path)
{
namespace pt = boost::property_tree;
pt::ptree tree;
boost::nowide::ifstream ifs(path.string());
try {
pt::read_ini(ifs, tree);
}
catch (const boost::property_tree::ini_parser::ini_parser_error& err) {
throw Slic3r::RuntimeError(format("Failed loading hints file \"%1%\"\nError: \"%2%\" at line %3%", path, err.message(), err.line()).c_str());
}
for (const auto& section : tree) {
if (boost::starts_with(section.first, "hint:")) {
// create std::map with tree data
std::map<std::string, std::string> dict;
for (const auto& data : section.second) {
dict.emplace(data.first, data.second.data());
}
//unescaping a translating all texts
//unescape text1
std::string fulltext;
std::string text1;
std::string hypertext_text;
std::string follow_text;
std::string disabled_modes;
unescape_string_cstyle(_utf8(dict["text"]), fulltext);
// replace <b> and </b> for imgui markers
std::string marker_s(1, ImGui::ColorMarkerStart);
std::string marker_e(1, ImGui::ColorMarkerEnd);
// start marker
size_t marker_pos = fulltext.find(BOLD_MARKER_START);
while (marker_pos != std::string::npos) {
fulltext.replace(marker_pos, 3, marker_s);
marker_pos = fulltext.find(BOLD_MARKER_START, marker_pos);
}
// end marker
marker_pos = fulltext.find(BOLD_MARKER_END);
while (marker_pos != std::string::npos) {
fulltext.replace(marker_pos, 4, marker_e);
marker_pos = fulltext.find(BOLD_MARKER_END, marker_pos);
}
// divide fulltext
size_t hypertext_start = fulltext.find(HYPERTEXT_MARKER_START);
if (hypertext_start != std::string::npos) {
//hypertext exists
fulltext.erase(hypertext_start, HYPERTEXT_MARKER_START.size());
if (fulltext.find(HYPERTEXT_MARKER_START) != std::string::npos) {
// This must not happen - only 1 hypertext allowed
BOOST_LOG_TRIVIAL(error) << "Hint notification with multiple hypertexts: " << _utf8(dict["text"]);
continue;
}
size_t hypertext_end = fulltext.find(HYPERTEXT_MARKER_END);
if (hypertext_end == std::string::npos) {
// hypertext was not correctly ended
BOOST_LOG_TRIVIAL(error) << "Hint notification without hypertext end marker: " << _utf8(dict["text"]);
continue;
}
fulltext.erase(hypertext_end, HYPERTEXT_MARKER_END.size());
if (fulltext.find(HYPERTEXT_MARKER_END) != std::string::npos) {
// This must not happen - only 1 hypertext end allowed
BOOST_LOG_TRIVIAL(error) << "Hint notification with multiple hypertext end markers: " << _utf8(dict["text"]);
continue;
}
text1 = fulltext.substr(0, hypertext_start);
hypertext_text = fulltext.substr(hypertext_start, hypertext_end - hypertext_start);
follow_text = fulltext.substr(hypertext_end);
} else {
text1 = fulltext;
}
if (dict.find("disabled_modes") != dict.end()) {
disabled_modes = dict["disabled_modes"];
}
// create HintData
if (dict.find("hypertext_type") != dict.end()) {
//link to internet
if(dict["hypertext_type"] == "link") {
std::string hypertext_link = dict["hypertext_link"];
HintData hint_data{ text1, hypertext_text, follow_text, disabled_modes, false, [hypertext_link]() { wxLaunchDefaultBrowser(hypertext_link); } };
m_loaded_hints.emplace_back(hint_data);
// highlight settings
} else if (dict["hypertext_type"] == "settings") {
std::string opt = dict["hypertext_settings_opt"];
Preset::Type type = static_cast<Preset::Type>(std::atoi(dict["hypertext_settings_type"].c_str()));
std::wstring category = boost::nowide::widen(dict["hypertext_settings_category"]);
HintData hint_data{ text1, hypertext_text, follow_text, disabled_modes, true, [opt, type, category]() { GUI::wxGetApp().sidebar().jump_to_option(opt, type, category); } };
m_loaded_hints.emplace_back(hint_data);
// open preferences
} else if(dict["hypertext_type"] == "preferences") {
int page = static_cast<Preset::Type>(std::atoi(dict["hypertext_preferences_page"].c_str()));
HintData hint_data{ text1, hypertext_text, follow_text, disabled_modes, false, [page]() { wxGetApp().open_preferences(page); } };
m_loaded_hints.emplace_back(hint_data);
} else if (dict["hypertext_type"] == "plater") {
std::string item = dict["hypertext_plater_item"];
HintData hint_data{ text1, hypertext_text, follow_text, disabled_modes, true, [item]() { wxGetApp().plater()->canvas3D()->highlight_toolbar_item(item); } };
m_loaded_hints.emplace_back(hint_data);
} else if (dict["hypertext_type"] == "gizmo") {
std::string item = dict["hypertext_gizmo_item"];
HintData hint_data{ text1, hypertext_text, follow_text, disabled_modes, true, [item]() { wxGetApp().plater()->canvas3D()->highlight_gizmo(item); } };
m_loaded_hints.emplace_back(hint_data);
}
else if (dict["hypertext_type"] == "gallery") {
HintData hint_data{ text1, hypertext_text, follow_text, disabled_modes, false, []() { wxGetApp().obj_list()->load_shape_object_from_gallery(); } };
m_loaded_hints.emplace_back(hint_data);
}
} else {
// plain text without hypertext
HintData hint_data{ text1 };
m_loaded_hints.emplace_back(hint_data);
}
}
}
}
HintData* HintDatabase::get_hint(bool up)
{
if (! m_initialized) {
init();
//return false;
}
// shift id
m_hint_id = (up ? m_hint_id + 1 : (m_hint_id == 0 ? m_loaded_hints.size() - 1 : m_hint_id - 1));
m_hint_id %= m_loaded_hints.size();
AppConfig* app_config = wxGetApp().app_config;
app_config->set("last_hint", std::to_string(m_hint_id));
//data = &m_loaded_hints[m_hint_id];
/*
data.text = m_loaded_hints[m_hint_id].text;
data.hypertext = m_loaded_hints[m_hint_id].hypertext;
data.follow_text = m_loaded_hints[m_hint_id].follow_text;
data.callback = m_loaded_hints[m_hint_id].callback;
*/
return &m_loaded_hints[m_hint_id];
}
void NotificationManager::HintNotification::count_spaces()
{
//determine line width
m_line_height = ImGui::CalcTextSize("A").y;
std::string text;
text = ImGui::LeftArrowButton; // TODO change to left arrow
float picture_width = ImGui::CalcTextSize(text.c_str()).x;
m_left_indentation = picture_width + m_line_height / 2;
// no left button picture
//m_left_indentation = m_line_height;
m_window_width_offset = m_left_indentation + m_line_height * 3.f;// 5.5f; // no right arrow
m_window_width = m_line_height * 25;
}
void NotificationManager::HintNotification::count_lines()
{
std::string text = m_text1;
size_t last_end = 0;
m_lines_count = 0;
if (text.empty())
return;
m_endlines.clear();
while (last_end < text.length() - 1)
{
size_t next_hard_end = text.find_first_of('\n', last_end);
if (next_hard_end != std::string::npos && ImGui::CalcTextSize(text.substr(last_end, next_hard_end - last_end).c_str()).x < m_window_width - m_window_width_offset) {
//next line is ended by '/n'
m_endlines.push_back(next_hard_end);
last_end = next_hard_end + 1;
}
else {
// find next suitable endline
if (ImGui::CalcTextSize(text.substr(last_end).c_str()).x >= m_window_width - m_window_width_offset) {
// more than one line till end
size_t next_space = text.find_first_of(' ', last_end);
if (next_space > 0 && next_space < text.length()) {
size_t next_space_candidate = text.find_first_of(' ', next_space + 1);
while (next_space_candidate > 0 && ImGui::CalcTextSize(text.substr(last_end, next_space_candidate - last_end).c_str()).x < m_window_width - m_window_width_offset) {
next_space = next_space_candidate;
next_space_candidate = text.find_first_of(' ', next_space + 1);
}
} else {
next_space = text.length();
}
// when one word longer than line.
if (ImGui::CalcTextSize(text.substr(last_end, next_space - last_end).c_str()).x > m_window_width - m_window_width_offset ||
ImGui::CalcTextSize(text.substr(last_end, next_space - last_end).c_str()).x < (m_window_width - m_window_width_offset) / 4 * 3
) {
float width_of_a = ImGui::CalcTextSize("a").x;
int letter_count = (int)((m_window_width - m_window_width_offset) / width_of_a);
while (last_end + letter_count < text.size() && ImGui::CalcTextSize(text.substr(last_end, letter_count).c_str()).x < m_window_width - m_window_width_offset) {
letter_count++;
}
m_endlines.push_back(last_end + letter_count);
last_end += letter_count;
} else {
m_endlines.push_back(next_space);
last_end = next_space + 1;
}
}
else {
m_endlines.push_back(text.length());
last_end = text.length();
}
}
m_lines_count++;
}
int prev_end = m_endlines.size() > 1 ? m_endlines[m_endlines.size() - 2] : 0;
int size_of_last_line = ImGui::CalcTextSize(text.substr(prev_end, last_end - prev_end).c_str()).x;
// hypertext calculation
if (!m_hypertext.empty()) {
if (size_of_last_line + ImGui::CalcTextSize(m_hypertext.c_str()).x > m_window_width - m_window_width_offset) {
// hypertext on new line
size_of_last_line = ImGui::CalcTextSize((m_hypertext + " ").c_str()).x;
m_endlines.push_back(last_end);
m_lines_count++;
} else {
size_of_last_line += ImGui::CalcTextSize((m_hypertext + " ").c_str()).x;
}
}
if (!m_text2.empty()) {
text = m_text2;
last_end = 0;
m_endlines2.clear();
// if size_of_last_line too large to fit anything
size_t first_end = std::min(text.find_first_of('\n'), text.find_first_of(' '));
if (size_of_last_line >= m_window_width - m_window_width_offset - ImGui::CalcTextSize(text.substr(0, first_end).c_str()).x) {
m_endlines2.push_back(0);
size_of_last_line = 0;
}
while (last_end < text.length() - 1)
{
size_t next_hard_end = text.find_first_of('\n', last_end);
if (next_hard_end != std::string::npos && ImGui::CalcTextSize(text.substr(last_end, next_hard_end - last_end).c_str()).x < m_window_width - m_window_width_offset - size_of_last_line) {
//next line is ended by '/n'
m_endlines2.push_back(next_hard_end);
last_end = next_hard_end + 1;
}
else {
// find next suitable endline
if (ImGui::CalcTextSize(text.substr(last_end).c_str()).x >= m_window_width - m_window_width_offset - size_of_last_line) {
// more than one line till end
size_t next_space = text.find_first_of(' ', last_end);
if (next_space > 0) {
size_t next_space_candidate = text.find_first_of(' ', next_space + 1);
while (next_space_candidate > 0 && ImGui::CalcTextSize(text.substr(last_end, next_space_candidate - last_end).c_str()).x < m_window_width - m_window_width_offset - size_of_last_line) {
next_space = next_space_candidate;
next_space_candidate = text.find_first_of(' ', next_space + 1);
}
}
else {
next_space = text.length();
}
// when one word longer than line.
if (ImGui::CalcTextSize(text.substr(last_end, next_space - last_end).c_str()).x > m_window_width - m_window_width_offset - size_of_last_line ||
ImGui::CalcTextSize(text.substr(last_end, next_space - last_end).c_str()).x + size_of_last_line < (m_window_width - m_window_width_offset) / 4 * 3
) {
float width_of_a = ImGui::CalcTextSize("a").x;
int letter_count = (int)((m_window_width - m_window_width_offset - size_of_last_line) / width_of_a);
while (last_end + letter_count < text.size() && ImGui::CalcTextSize(text.substr(last_end, letter_count).c_str()).x < m_window_width - m_window_width_offset - size_of_last_line) {
letter_count++;
}
m_endlines2.push_back(last_end + letter_count);
last_end += letter_count;
}
else {
m_endlines2.push_back(next_space);
last_end = next_space + 1;
}
}
else {
m_endlines2.push_back(text.length());
last_end = text.length();
}
}
if (size_of_last_line == 0) // if first line is continuation of previous text, do not add to line count.
m_lines_count++;
size_of_last_line = 0; // should countain value only for first line (with hypertext)
}
}
}
void NotificationManager::HintNotification::init()
{
// Do not init closing notification
if (is_finished())
return;
count_spaces();
count_lines();
m_multiline = true;
m_notification_start = GLCanvas3D::timestamp_now();
if (m_state == EState::Unknown)
m_state = EState::Shown;
}
void NotificationManager::HintNotification::set_next_window_size(ImGuiWrapper& imgui)
{
/*
m_window_height = m_multiline ?
(m_lines_count + 1.f) * m_line_height :
4.f * m_line_height;
m_window_height += 1 * m_line_height; // top and bottom
*/
m_window_height = std::max((m_lines_count + 1.f) * m_line_height, 4.f * m_line_height);
}
bool NotificationManager::HintNotification::on_text_click()
{
if (m_hypertext_callback != nullptr && (!m_runtime_disable || disabled_modes_check(m_disabled_modes)))
m_hypertext_callback();
return false;
}
void NotificationManager::HintNotification::render_text(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
{
if (!m_has_hint_data) {
retrieve_data();
}
float x_offset = m_left_indentation;
int last_end = 0;
float starting_y = (m_lines_count == 2 ? win_size_y / 2 - m_line_height :(m_lines_count == 1 ? win_size_y / 2 - m_line_height / 2: m_line_height / 2));
float shift_y = m_line_height;
std::string line;
for (size_t i = 0; i < (m_multiline ? /*m_lines_count*/m_endlines.size() : 2); i++) {
line.clear();
ImGui::SetCursorPosX(x_offset);
ImGui::SetCursorPosY(starting_y + i * shift_y);
if (m_endlines.size() > i && m_text1.size() >= m_endlines[i]) {
if (i == 1 && m_endlines.size() > 2 && !m_multiline) {
// second line with "more" hypertext
line = m_text1.substr(m_endlines[0] + (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0), m_endlines[1] - m_endlines[0] - (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0));
while (ImGui::CalcTextSize(line.c_str()).x > m_window_width - m_window_width_offset - ImGui::CalcTextSize((".." + _u8L("More")).c_str()).x) {
line = line.substr(0, line.length() - 1);
}
line += "..";
} else {
// regural line
line = m_text1.substr(last_end, m_endlines[i] - last_end);
}
// first line is headline
if (i == 0) {
line = ImGui::ColorMarkerStart + line + ImGui::ColorMarkerEnd;
}
// Add ImGui::ColorMarkerStart if there is ImGui::ColorMarkerEnd first (start was at prev line)
if (line.find_first_of(ImGui::ColorMarkerEnd) < line.find_first_of(ImGui::ColorMarkerStart)) {
line = ImGui::ColorMarkerStart + line;
}
last_end = m_endlines[i];
if (m_text1.size() > m_endlines[i])
last_end += (m_text1[m_endlines[i]] == '\n' || m_text1[m_endlines[i]] == ' ' ? 1 : 0);
imgui.text(line.c_str());
}
}
//hyperlink text
if (!m_multiline && m_lines_count > 2) {
render_hypertext(imgui, x_offset + ImGui::CalcTextSize((line + " ").c_str()).x, starting_y + shift_y, _u8L("More"), true);
} else if (!m_hypertext.empty()) {
render_hypertext(imgui, x_offset + ImGui::CalcTextSize((line + (line.empty()? "": " ")).c_str()).x, starting_y + (m_endlines.size() - 1) * shift_y, m_hypertext);
}
// text2
if (!m_text2.empty() && m_multiline) {
starting_y += (m_endlines.size() - 1) * shift_y;
last_end = 0;
for (size_t i = 0; i < (m_multiline ? m_endlines2.size() : 2); i++) {
if (i == 0) //first line X is shifted by hypertext
ImGui::SetCursorPosX(x_offset + ImGui::CalcTextSize((line + m_hypertext + (line.empty() ? " " : " ")).c_str()).x);
else
ImGui::SetCursorPosX(x_offset);
ImGui::SetCursorPosY(starting_y + i * shift_y);
line.clear();
if (m_endlines2.size() > i && m_text2.size() >= m_endlines2[i]) {
// regural line
line = m_text2.substr(last_end, m_endlines2[i] - last_end);
// Add ImGui::ColorMarkerStart if there is ImGui::ColorMarkerEnd first (start was at prev line)
if (line.find_first_of(ImGui::ColorMarkerEnd) < line.find_first_of(ImGui::ColorMarkerStart)) {
line = ImGui::ColorMarkerStart + line;
}
last_end = m_endlines2[i];
if (m_text2.size() > m_endlines2[i])
last_end += (m_text2[m_endlines2[i]] == '\n' || m_text2[m_endlines2[i]] == ' ' ? 1 : 0);
imgui.text(line.c_str());
}
}
}
}
void NotificationManager::HintNotification::render_close_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
{
ImVec2 win_size(win_size_x, win_size_y);
ImVec2 win_pos(win_pos_x, win_pos_y);
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
std::string button_text;
button_text = ImGui::CloseNotifButton;
if (ImGui::IsMouseHoveringRect(ImVec2(win_pos.x - win_size.x / 10.f, win_pos.y),
ImVec2(win_pos.x, win_pos.y + win_size.y - 2 * m_line_height),
true))
{
button_text = ImGui::CloseNotifHoverButton;
}
ImVec2 button_pic_size = ImGui::CalcTextSize(button_text.c_str());
ImVec2 button_size(button_pic_size.x * 1.25f, button_pic_size.y * 1.25f);
m_close_b_w = button_size.y;
if (m_lines_count <= 3) {
m_close_b_y = win_size.y / 2 - button_size.y * 1.25f;
ImGui::SetCursorPosX(win_size.x - m_line_height * 2.75f);
ImGui::SetCursorPosY(m_close_b_y);
} else {
ImGui::SetCursorPosX(win_size.x - m_line_height * 2.75f);
ImGui::SetCursorPosY(win_size.y / 2 - button_size.y);
}
if (imgui.button(button_text.c_str(), button_size.x, button_size.y))
{
close();
}
//invisible large button
ImGui::SetCursorPosX(win_size.x - m_line_height * 2.35f);
ImGui::SetCursorPosY(0);
if (imgui.button(" ", m_line_height * 2.125, win_size.y - 2 * m_line_height))
{
close();
}
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
render_right_arrow_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
render_logo(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
render_preferences_button(imgui, win_pos_x, win_pos_y);
}
void NotificationManager::HintNotification::render_preferences_button(ImGuiWrapper& imgui, const float win_pos_x, const float win_pos_y)
{
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
push_style_color(ImGuiCol_ButtonActive, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
std::string button_text;
button_text = ImGui::PreferencesButton;
//hover
if (ImGui::IsMouseHoveringRect(ImVec2(win_pos_x - m_window_width / 10.f, win_pos_y + m_window_height - 2 * m_line_height + 1),
ImVec2(win_pos_x, win_pos_y + m_window_height),
true))
{
button_text = ImGui::PreferencesHoverButton;
}
ImVec2 button_pic_size = ImGui::CalcTextSize(button_text.c_str());
ImVec2 button_size(button_pic_size.x * 1.25f, button_pic_size.y * 1.25f);
ImGui::SetCursorPosX(m_window_width - m_line_height * 1.75f);
if (m_lines_count <= 3) {
ImGui::SetCursorPosY(m_close_b_y + m_close_b_w / 4.f * 7.f);
} else {
ImGui::SetCursorPosY(m_window_height - button_size.y - m_close_b_w / 4.f);
}
if (imgui.button(button_text.c_str(), button_size.x, button_size.y))
{
wxGetApp().open_preferences(2);
}
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
// preferences button is in place of minimize button
m_minimize_b_visible = true;
}
void NotificationManager::HintNotification::render_right_arrow_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
{
// Used for debuging
ImVec2 win_size(win_size_x, win_size_y);
ImVec2 win_pos(win_pos_x, win_pos_y);
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
std::string button_text;
button_text = ImGui::RightArrowButton;
ImVec2 button_pic_size = ImGui::CalcTextSize(button_text.c_str());
ImVec2 button_size(button_pic_size.x * 1.25f, button_pic_size.y * 1.25f);
ImGui::SetCursorPosX(m_window_width - m_line_height * 3.f);
if (m_lines_count <= 3)
ImGui::SetCursorPosY(m_close_b_y + m_close_b_w / 4.f * 7.f);
else
ImGui::SetCursorPosY(m_window_height - button_size.y - m_close_b_w / 4.f);
if (imgui.button(button_text.c_str(), button_size.x * 0.8f, button_size.y * 1.f))
{
retrieve_data();
}
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
}
void NotificationManager::HintNotification::render_logo(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
{
ImVec2 win_size(win_size_x, win_size_y);
ImVec2 win_pos(win_pos_x, win_pos_y);
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
std::string button_text;
button_text = ImGui::ErrorMarker;//LeftArrowButton;
ImVec2 button_pic_size = ImGui::CalcTextSize(button_text.c_str());
ImVec2 button_size(button_pic_size.x * 1.25f, button_pic_size.y * 1.25f);
ImGui::SetCursorPosY(win_size.y / 2 - button_size.y);
ImGui::SetCursorPosX(0);
// shouldnt it render as text?
if (imgui.button(button_text.c_str(), button_size.x, button_size.y))
{
}
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
}
void NotificationManager::HintNotification::retrieve_data(size_t recursion_counter)
{
HintData* hint_data = HintDatabase::get_instance().get_hint(true);
if (hint_data != nullptr && !disabled_modes_check(hint_data->disabled_modes))
{
// Content for different user - retrieve another
size_t count = HintDatabase::get_instance().get_count();
if (count < recursion_counter) {
BOOST_LOG_TRIVIAL(error) << "Hint notification failed to load data due to recursion counter.";
} else {
retrieve_data(recursion_counter + 1);
}
return;
}
if(hint_data != nullptr)
{
NotificationData nd { NotificationType::DidYouKnowHint,
NotificationLevel::RegularNotification,
0,
hint_data->text,
hint_data->hypertext, nullptr,
hint_data->follow_text };
update(nd);
m_hypertext_callback = hint_data->callback;
m_disabled_modes = hint_data->disabled_modes;
m_runtime_disable = hint_data->runtime_disable;
m_has_hint_data = true;
}
}
} //namespace Slic3r
} //namespace GUI

View file

@ -0,0 +1,97 @@
#ifndef slic3r_GUI_HintNotification_hpp_
#define slic3r_GUI_HintNotification_hpp_
#include "NotificationManager.hpp"
namespace Slic3r {
namespace GUI {
// Database of hints updatable
struct HintData
{
std::string text;
std::string hypertext;
std::string follow_text;
std::string disabled_modes;
bool runtime_disable; // if true - hyperlink will check before every click if not in disabled mode
std::function<void(void)> callback{ nullptr };
};
class HintDatabase
{
public:
static HintDatabase& get_instance()
{
static HintDatabase instance; // Guaranteed to be destroyed.
// Instantiated on first use.
return instance;
}
private:
HintDatabase()
: m_hint_id(0)
{}
public:
HintDatabase(HintDatabase const&) = delete;
void operator=(HintDatabase const&) = delete;
// return true if HintData filled;
HintData* get_hint(bool up = true);
size_t get_count() {
if (!m_initialized)
return 0;
return m_loaded_hints.size();
}
private:
void init();
void load_hints_from_file(const boost::filesystem::path& path);
size_t m_hint_id;
bool m_initialized { false };
std::vector<HintData> m_loaded_hints;
};
// Notification class - shows current Hint ("Did you know")
class NotificationManager::HintNotification : public NotificationManager::PopNotification
{
public:
HintNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler)
: PopNotification(n, id_provider, evt_handler)
{
retrieve_data();
}
virtual void init() override;
protected:
virtual void set_next_window_size(ImGuiWrapper& imgui) override;
virtual void count_spaces() override;
virtual void count_lines() override;
virtual bool on_text_click() override;
virtual void render_text(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y) override;
virtual void render_close_button(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y) override;
virtual void render_minimize_button(ImGuiWrapper& imgui,
const float win_pos_x, const float win_pos_y) override {}
void render_preferences_button(ImGuiWrapper& imgui,
const float win_pos_x, const float win_pos_y);
void render_right_arrow_button(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y);
void render_logo(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y);
void retrieve_data(size_t recursion_counter = 0);
bool m_has_hint_data { false };
std::function<void(void)> m_hypertext_callback;
std::string m_disabled_modes;
bool m_runtime_disable;
float m_close_b_y { 0 };
float m_close_b_w { 0 };
};
} //namespace Slic3r
} //namespace GUI
#endif //slic3r_GUI_HintNotification_hpp_

View file

@ -43,7 +43,11 @@ static const std::map<const char, std::string> font_icons = {
{ImGui::FilamentIconMarker , "spool" },
{ImGui::MaterialIconMarker , "resin" },
{ImGui::MinimalizeButton , "notification_minimalize" },
{ImGui::MinimalizeHoverButton , "notification_minimalize_hover" }
{ImGui::MinimalizeHoverButton , "notification_minimalize_hover" },
{ImGui::RightArrowButton , "notification_right" },
{ImGui::RightArrowHoverButton , "notification_right_hover" },
{ImGui::PreferencesButton , "notification_preferences" },
{ImGui::PreferencesHoverButton , "notification_preferences_hover"},
};
static const std::map<const char, std::string> font_icons_large = {
{ImGui::CloseNotifButton , "notification_close" },
@ -54,6 +58,8 @@ static const std::map<const char, std::string> font_icons_large = {
{ImGui::ErrorMarker , "notification_error" },
{ImGui::CancelButton , "notification_cancel" },
{ImGui::CancelHoverButton , "notification_cancel_hover" },
{ImGui::LeftArrowButton , "notification_left" },
{ImGui::LeftArrowHoverButton , "notification_left_hover" },
};
const ImVec4 ImGuiWrapper::COL_GREY_DARK = { 0.333f, 0.333f, 0.333f, 1.0f };

View file

@ -1,10 +1,11 @@
#include "NotificationManager.hpp"
#include "HintNotification.hpp"
#include "GUI.hpp"
#include "ImGuiWrapper.hpp"
#include "PrintHostDialogs.hpp"
#include "wxExtensions.hpp"
#include "libslic3r/Config.hpp"
#include "../Utils/PrintHost.hpp"
#include "libslic3r/Config.hpp"
@ -30,7 +31,37 @@ wxDEFINE_EVENT(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED, EjectDriveNotificationClicke
wxDEFINE_EVENT(EVT_EXPORT_GCODE_NOTIFICAION_CLICKED, ExportGcodeNotificationClickedEvent);
wxDEFINE_EVENT(EVT_PRESET_UPDATE_AVAILABLE_CLICKED, PresetUpdateAvailableClickedEvent);
namespace Notifications_Internal{
const NotificationManager::NotificationData NotificationManager::basic_notifications[] = {
{NotificationType::Mouse3dDisconnected, NotificationLevel::RegularNotification, 10, _u8L("3D Mouse disconnected.") },
{NotificationType::PresetUpdateAvailable, NotificationLevel::ImportantNotification, 20, _u8L("Configuration update is available."), _u8L("See more."),
[](wxEvtHandler* evnthndlr) {
if (evnthndlr != nullptr)
wxPostEvent(evnthndlr, PresetUpdateAvailableClickedEvent(EVT_PRESET_UPDATE_AVAILABLE_CLICKED));
return true;
}
},
{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotification, 20, _u8L("New version is available."), _u8L("See Releases page."), [](wxEvtHandler* evnthndlr) {
wxLaunchDefaultBrowser("https://github.com/prusa3d/PrusaSlicer/releases"); return true; }},
{NotificationType::EmptyColorChangeCode, NotificationLevel::RegularNotification, 10,
_u8L("You have just added a G-code for color change, but its value is empty.\n"
"To export the G-code correctly, check the \"Color Change G-code\" in \"Printer Settings > Custom G-code\"") },
{NotificationType::EmptyAutoColorChange, NotificationLevel::RegularNotification, 10,
_u8L("This model doesn't allow to automatically add the color changes") },
{NotificationType::DesktopIntegrationSuccess, NotificationLevel::RegularNotification, 10,
_u8L("Desktop integration was successful.") },
{NotificationType::DesktopIntegrationFail, NotificationLevel::WarningNotification, 10,
_u8L("Desktop integration failed.") },
{NotificationType::UndoDesktopIntegrationSuccess, NotificationLevel::RegularNotification, 10,
_u8L("Undo desktop integration was successful.") },
{NotificationType::UndoDesktopIntegrationFail, NotificationLevel::WarningNotification, 10,
_u8L("Undo desktop integration failed.") },
//{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotification, 20, _u8L("New vesion of PrusaSlicer is available.", _u8L("Download page.") },
//{NotificationType::LoadingFailed, NotificationLevel::RegularNotification, 20, _u8L("Loading of model has Failed") },
//{NotificationType::DeviceEjected, NotificationLevel::RegularNotification, 10, _u8L("Removable device has been safely ejected")} // if we want changeble text (like here name of device), we need to do it as CustomNotification
};
namespace {
/* // not used?
ImFont* add_default_font(float pixel_size)
{
ImGuiIO& io = ImGui::GetIO();
@ -41,8 +72,8 @@ namespace Notifications_Internal{
ImFont* font = io.Fonts->AddFontDefault(&config);
return font;
}
static inline void push_style_color(ImGuiCol idx, const ImVec4& col, bool fading_out, float current_fade_opacity)
*/
inline void push_style_color(ImGuiCol idx, const ImVec4& col, bool fading_out, float current_fade_opacity)
{
if (fading_out)
ImGui::PushStyleColor(idx, ImVec4(col.x, col.y, col.z, col.w * current_fade_opacity));
@ -129,8 +160,8 @@ NotificationManager::PopNotification::PopNotification(const NotificationData &n,
m_data (n)
, m_id_provider (id_provider)
, m_text1 (n.text1)
, m_hypertext (n.hypertext)
, m_text2 (n.text2)
, m_hypertext (n.hypertext)
, m_text2 (n.text2)
, m_evt_handler (evt_handler)
, m_notification_start (GLCanvas3D::timestamp_now())
{}
@ -184,8 +215,8 @@ void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float init
// color change based on fading out
if (m_state == EState::FadingOut) {
Notifications_Internal::push_style_color(ImGuiCol_WindowBg, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), true, m_current_fade_opacity);
Notifications_Internal::push_style_color(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_Text), true, m_current_fade_opacity);
push_style_color(ImGuiCol_WindowBg, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), true, m_current_fade_opacity);
push_style_color(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_Text), true, m_current_fade_opacity);
fading_pop = true;
}
@ -219,20 +250,20 @@ bool NotificationManager::PopNotification::push_background_color()
{
if (m_is_gray) {
ImVec4 backcolor(0.7f, 0.7f, 0.7f, 0.5f);
Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
return true;
}
if (m_data.level == NotificationLevel::ErrorNotification) {
ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
backcolor.x += 0.3f;
Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
return true;
}
if (m_data.level == NotificationLevel::WarningNotification) {
ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
backcolor.x += 0.3f;
backcolor.y += 0.15f;
Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
return true;
}
return false;
@ -259,6 +290,9 @@ void NotificationManager::PopNotification::count_lines()
size_t last_end = 0;
m_lines_count = 0;
if (text.empty())
return;
m_endlines.clear();
while (last_end < text.length() - 1)
{
@ -283,7 +317,9 @@ void NotificationManager::PopNotification::count_lines()
next_space = text.length();
}
// when one word longer than line.
if (ImGui::CalcTextSize(text.substr(last_end, next_space - last_end).c_str()).x > m_window_width - m_window_width_offset) {
if (ImGui::CalcTextSize(text.substr(last_end, next_space - last_end).c_str()).x > m_window_width - m_window_width_offset ||
ImGui::CalcTextSize(text.substr(last_end, next_space - last_end).c_str()).x < (m_window_width - m_window_width_offset) / 4 * 3
) {
float width_of_a = ImGui::CalcTextSize("a").x;
int letter_count = (int)((m_window_width - m_window_width_offset) / width_of_a);
while (last_end + letter_count < text.size() && ImGui::CalcTextSize(text.substr(last_end, letter_count).c_str()).x < m_window_width - m_window_width_offset) {
@ -312,6 +348,9 @@ void NotificationManager::PopNotification::count_lines()
m_lines_count++;
}
}
// m_text_2 (text after hypertext) is not used for regular notifications right now.
// its caluculation is in HintNotification::count_lines()
}
void NotificationManager::PopNotification::init()
@ -339,105 +378,45 @@ void NotificationManager::PopNotification::set_next_window_size(ImGuiWrapper& im
void NotificationManager::PopNotification::render_text(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
{
ImVec2 win_size(win_size_x, win_size_y);
float x_offset = m_left_indentation;
std::string fulltext = m_text1 + m_hypertext; //+ m_text2;
ImVec2 text_size = ImGui::CalcTextSize(fulltext.c_str());
// text posistions are calculated by lines count
// large texts has "more" button or are displayed whole
// smaller texts are divided as one liners and two liners
if (m_lines_count > 2) {
if (m_multiline) {
int last_end = 0;
float starting_y = m_line_height/2;
float shift_y = m_line_height;
std::string line;
float x_offset = m_left_indentation;
int last_end = 0;
float starting_y = (m_lines_count == 2 ? win_size_y / 2 - m_line_height : (m_lines_count == 1 ? win_size_y / 2 - m_line_height / 2 : m_line_height / 2));
float shift_y = m_line_height;
std::string line;
for (size_t i = 0; i < m_lines_count; i++) {
line.clear();
ImGui::SetCursorPosX(x_offset);
ImGui::SetCursorPosY(starting_y + i * shift_y);
if (m_endlines.size() > i && m_text1.size() >= m_endlines[i]) {
line = m_text1.substr(last_end, m_endlines[i] - last_end);
last_end = m_endlines[i];
if (m_text1.size() > m_endlines[i])
last_end += (m_text1[m_endlines[i]] == '\n' || m_text1[m_endlines[i]] == ' ' ? 1 : 0);
imgui.text(line.c_str());
}
}
//hyperlink text
if (!m_hypertext.empty()) {
render_hypertext(imgui, x_offset + ImGui::CalcTextSize((line + (line.empty() ? "" : " ")).c_str()).x, starting_y + (m_lines_count - 1) * shift_y, m_hypertext);
}
} else {
// line1
if (m_text1.size() >= m_endlines[0]) {
ImGui::SetCursorPosX(x_offset);
ImGui::SetCursorPosY(win_size.y / 2 - win_size.y / 6 - m_line_height / 2);
imgui.text(m_text1.substr(0, m_endlines[0]).c_str());
}
// line2
std::string line;
ImGui::SetCursorPosX(x_offset);
ImGui::SetCursorPosY(win_size.y / 2 + win_size.y / 6 - m_line_height / 2);
if (m_text1.size() >= m_endlines[1]) {
for (size_t i = 0; i < (m_multiline ? m_endlines.size() : std::min(m_endlines.size(), (size_t)2)); i++) {
line.clear();
ImGui::SetCursorPosX(x_offset);
ImGui::SetCursorPosY(starting_y + i * shift_y);
if (m_endlines.size() > i && m_text1.size() >= m_endlines[i]) {
if (i == 1 && m_endlines.size() > 2 && !m_multiline) {
// second line with "more" hypertext
line = m_text1.substr(m_endlines[0] + (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0), m_endlines[1] - m_endlines[0] - (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0));
if (ImGui::CalcTextSize(line.c_str()).x > m_window_width - m_window_width_offset - ImGui::CalcTextSize((".." + _u8L("More")).c_str()).x) {
line = line.substr(0, line.length() - 6);
line += "..";
} else
line += " ";
imgui.text(line.c_str());
while (ImGui::CalcTextSize(line.c_str()).x > m_window_width - m_window_width_offset - ImGui::CalcTextSize((".." + _u8L("More")).c_str()).x) {
line = line.substr(0, line.length() - 1);
}
line += "..";
}
// "More" hypertext
render_hypertext(imgui, x_offset + ImGui::CalcTextSize(line.c_str()).x, win_size.y / 2 + win_size.y / 6 - m_line_height / 2, _u8L("More"), true);
}
} else {
//text 1
float cursor_y = win_size.y / 2 - text_size.y / 2;
float cursor_x = x_offset;
if(m_lines_count > 1) {
// line1
if (m_text1.length() >= m_endlines[0]) { // could be equal than substr takes whole string
ImGui::SetCursorPosX(x_offset);
ImGui::SetCursorPosY(win_size.y / 2 - win_size.y / 6 - m_line_height / 2);
imgui.text(m_text1.substr(0, m_endlines[0]).c_str());
else {
// regural line
line = m_text1.substr(last_end, m_endlines[i] - last_end);
}
// line2
ImGui::SetCursorPosX(x_offset);
cursor_y = win_size.y / 2 + win_size.y / 6 - m_line_height / 2;
ImGui::SetCursorPosY(cursor_y);
if (m_text1.length() > m_endlines[0]) { // must be greater otherwise theres nothing to show and m_text1[m_endlines[0]] is beyond last letter
std::string line = m_text1.substr(m_endlines[0] + (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0));
imgui.text(line.c_str());
cursor_x = x_offset + ImGui::CalcTextSize(line.c_str()).x;
}
} else {
ImGui::SetCursorPosX(x_offset);
ImGui::SetCursorPosY(cursor_y);
imgui.text(m_text1.c_str());
cursor_x = x_offset + ImGui::CalcTextSize(m_text1.c_str()).x;
last_end = m_endlines[i];
if (m_text1.size() > m_endlines[i])
last_end += (m_text1[m_endlines[i]] == '\n' || m_text1[m_endlines[i]] == ' ' ? 1 : 0);
imgui.text(line.c_str());
}
//hyperlink text
if (!m_hypertext.empty()) {
render_hypertext(imgui, cursor_x + 4, cursor_y, m_hypertext);
}
//notification text 2
//text 2 is suposed to be after the hyperlink - currently it is not used
/*
if (!m_text2.empty())
{
ImVec2 part_size = ImGui::CalcTextSize(m_hypertext.c_str());
ImGui::SetCursorPosX(win_size.x / 2 + text_size.x / 2 - part_size.x + 8 - x_offset);
ImGui::SetCursorPosY(cursor_y);
imgui.text(m_text2.c_str());
}
*/
}
//hyperlink text
if (!m_multiline && m_lines_count > 2) {
render_hypertext(imgui, x_offset + ImGui::CalcTextSize((line + " ").c_str()).x, starting_y + shift_y, _u8L("More"), true);
}
else if (!m_hypertext.empty()) {
render_hypertext(imgui, x_offset + ImGui::CalcTextSize((line + (line.empty() ? "" : " ")).c_str()).x, starting_y + (m_endlines.size() - 1) * shift_y, m_hypertext);
}
// text2 (text after hypertext) is not rendered for regular notifications
// its rendering is in HintNotification::render_text
}
void NotificationManager::PopNotification::render_hypertext(ImGuiWrapper& imgui, const float text_x, const float text_y, const std::string text, bool more)
@ -470,7 +449,7 @@ void NotificationManager::PopNotification::render_hypertext(ImGuiWrapper& imgui,
orange_color.y += 0.2f;
//text
Notifications_Internal::push_style_color(ImGuiCol_Text, orange_color, m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_Text, orange_color, m_state == EState::FadingOut, m_current_fade_opacity);
ImGui::SetCursorPosX(text_x);
ImGui::SetCursorPosY(text_y);
imgui.text(text.c_str());
@ -491,8 +470,8 @@ void NotificationManager::PopNotification::render_close_button(ImGuiWrapper& img
ImVec2 win_pos(win_pos_x, win_pos_y);
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
@ -542,9 +521,9 @@ void NotificationManager::PopNotification::render_minimize_button(ImGuiWrapper&
{
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
Notifications_Internal::push_style_color(ImGuiCol_ButtonActive, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), m_state == EState::FadingOut, m_current_fade_opacity);
Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_ButtonActive, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
//button - if part if treggered
@ -762,8 +741,8 @@ void NotificationManager::ExportFinishedNotification::render_eject_button(ImGuiW
ImVec2 win_pos(win_pos_x, win_pos_y);
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
std::string button_text;
@ -818,7 +797,7 @@ void NotificationManager::ExportFinishedNotification::render_eject_button(ImGuiW
}
bool NotificationManager::ExportFinishedNotification::on_text_click()
{
Notifications_Internal::open_folder(m_export_dir_path);
open_folder(m_export_dir_path);
return false;
}
//------ProgressBar----------------
@ -961,7 +940,7 @@ bool NotificationManager::PrintHostUploadNotification::push_background_color()
if (m_uj_state == UploadJobState::PB_ERROR) {
ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
backcolor.x += 0.3f;
Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
return true;
}
return false;
@ -1031,8 +1010,8 @@ void NotificationManager::PrintHostUploadNotification::render_cancel_button(ImGu
ImVec2 win_pos(win_pos_x, win_pos_y);
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
std::string button_text;
@ -1088,10 +1067,10 @@ NotificationManager::NotificationManager(wxEvtHandler* evt_handler) :
}
void NotificationManager::push_notification(const NotificationType type, int timestamp)
{
auto it = std::find_if(basic_notifications.begin(), basic_notifications.end(),
auto it = std::find_if(std::begin(basic_notifications), std::end(basic_notifications),
boost::bind(&NotificationData::type, boost::placeholders::_1) == type);
assert(it != basic_notifications.end());
if (it != basic_notifications.end())
assert(it != std::end(basic_notifications));
if (it != std::end(basic_notifications))
push_notification_data(*it, timestamp);
}
void NotificationManager::push_notification(const std::string& text, int timestamp)
@ -1331,6 +1310,15 @@ void NotificationManager::upload_job_notification_show_error(int id, const std::
}
}
}
void NotificationManager::push_hint_notification()
{
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
if (notification->get_type() == NotificationType::DidYouKnowHint)
return;
}
NotificationData data{ NotificationType::DidYouKnowHint, NotificationLevel::RegularNotification, 0, "" };
push_notification_data(std::make_unique<NotificationManager::HintNotification>(data, m_id_provider, m_evt_handler), 0);
}
bool NotificationManager::push_notification_data(const NotificationData& notification_data, int timestamp)
{
return push_notification_data(std::make_unique<PopNotification>(notification_data, m_id_provider, m_evt_handler), timestamp);

View file

@ -89,8 +89,9 @@ enum class NotificationType
UndoDesktopIntegrationSuccess,
UndoDesktopIntegrationFail,
// Notification that a printer has more extruders than are supported by MM Gizmo/segmentation.
MmSegmentationExceededExtrudersLimit
MmSegmentationExceededExtrudersLimit,
// Did you know Notification appearing on startup with arrows to change hint
DidYouKnowHint
};
class NotificationManager
@ -160,6 +161,8 @@ public:
void set_upload_job_notification_percentage(int id, const std::string& filename, const std::string& host, float percentage);
void upload_job_notification_show_canceled(int id, const std::string& filename, const std::string& host);
void upload_job_notification_show_error(int id, const std::string& filename, const std::string& host);
// Hint (did you know) notification
void push_hint_notification();
// Close old notification ExportFinished.
void new_export_began(bool on_removable);
// finds ExportFinished notification and closes it if it was to removable device
@ -188,7 +191,7 @@ private:
// Callback for hypertext - returns true if notification should close after triggering
// Usually sends event to UI thread thru wxEvtHandler.
// Examples in basic_notifications.
std::function<bool(wxEvtHandler*)> callback { nullptr };
std::function<bool(wxEvtHandler*)> callback;
const std::string text2;
};
@ -237,7 +240,7 @@ private:
//returns top in actual frame
float get_current_top() const { return m_top_y; }
const NotificationType get_type() const { return m_data.type; }
const NotificationData get_data() const { return m_data; }
const NotificationData& get_data() const { return m_data; }
const bool is_gray() const { return m_is_gray; }
void set_gray(bool g) { m_is_gray = g; }
virtual bool compare_text(const std::string& text) const;
@ -318,7 +321,10 @@ private:
float m_top_y { 0.0f };
// Height of text - Used as basic scaling unit!
float m_line_height;
// endlines for text1, hypertext excluded
std::vector<size_t> m_endlines;
// endlines for text2
std::vector<size_t> m_endlines2;
// Gray are f.e. eorrors when its uknown if they are still valid
bool m_is_gray { false };
//if multiline = true, notification is showing all lines(>2)
@ -337,7 +343,7 @@ private:
void set_large(bool l);
bool get_large() { return m_is_large; }
void set_print_info(const std::string &info);
virtual void render(GLCanvas3D& canvas, float initial_y, bool move_from_overlay, float overlay_width) override
void render(GLCanvas3D& canvas, float initial_y, bool move_from_overlay, float overlay_width) override
{
// This notification is always hidden if !large (means side bar is collapsed)
if (!get_large() && !is_finished())
@ -345,7 +351,7 @@ private:
PopNotification::render(canvas, initial_y, move_from_overlay, overlay_width);
}
protected:
virtual void render_text(ImGuiWrapper& imgui,
void render_text(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y)
override;
@ -366,7 +372,7 @@ private:
{
public:
PlaterWarningNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler) : PopNotification(n, id_provider, evt_handler) {}
virtual void close() override { if(is_finished()) return; m_state = EState::Hidden; wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); }
void close() override { if(is_finished()) return; m_state = EState::Hidden; wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); }
void real_close() { m_state = EState::ClosePending; wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); }
void show() { m_state = EState::Unknown; }
};
@ -382,17 +388,17 @@ private:
virtual void init() override;
virtual void count_lines() override;
virtual void render_text(ImGuiWrapper& imgui,
virtual void render_text(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y) override;
virtual void render_bar(ImGuiWrapper& imgui,
virtual void render_bar(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y) ;
virtual void render_cancel_button(ImGuiWrapper& imgui,
virtual void render_cancel_button(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y)
{}
virtual void render_minimize_button(ImGuiWrapper& imgui,
void render_minimize_button(ImGuiWrapper& imgui,
const float win_pos_x, const float win_pos_y) override {}
float m_percentage;
@ -421,22 +427,22 @@ private:
m_has_cancel_button = true;
}
static std::string get_upload_job_text(int id, const std::string& filename, const std::string& host) { return /*"[" + std::to_string(id) + "] " + */filename + " -> " + host; }
virtual void set_percentage(float percent) override;
void set_percentage(float percent) override;
void cancel() { m_uj_state = UploadJobState::PB_CANCELLED; m_has_cancel_button = false; }
void error() { m_uj_state = UploadJobState::PB_ERROR; m_has_cancel_button = false; init(); }
bool compare_job_id(const int other_id) const { return m_job_id == other_id; }
virtual bool compare_text(const std::string& text) const override { return false; }
bool compare_text(const std::string& text) const override { return false; }
protected:
virtual void init() override;
virtual void count_spaces() override;
virtual bool push_background_color() override;
virtual void render_bar(ImGuiWrapper& imgui,
void init() override;
void count_spaces() override;
bool push_background_color() override;
void render_bar(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y) override;
virtual void render_cancel_button(ImGuiWrapper& imgui,
void render_cancel_button(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y) override;
virtual void render_left_sign(ImGuiWrapper& imgui) override;
void render_left_sign(ImGuiWrapper& imgui) override;
// Identifies job in cancel callback
int m_job_id;
// Size of uploaded size to be displayed in MB
@ -461,24 +467,27 @@ private:
std::string m_export_dir_path;
protected:
// Reserves space on right for more buttons
virtual void count_spaces() override;
virtual void render_text(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y) override;
void count_spaces() override;
void render_text(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y) override;
// Renders also button to open directory with exported path and eject removable media
virtual void render_close_button(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y) override;
void render_close_button(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y) override;
void render_eject_button(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y);
virtual void render_minimize_button(ImGuiWrapper& imgui, const float win_pos_x, const float win_pos_y) override
void render_minimize_button(ImGuiWrapper& imgui, const float win_pos_x, const float win_pos_y) override
{ m_minimize_b_visible = false; }
virtual bool on_text_click() override;
bool on_text_click() override;
// local time of last hover for showing tooltip
long m_hover_time { 0 };
};
// in HintNotification.hpp
class HintNotification;
//pushes notification into the queue of notifications that are rendered
//can be used to create custom notification
bool push_notification_data(const NotificationData& notification_data, int timestamp);
@ -506,34 +515,7 @@ private:
// Notification types that can be shown multiple types at once (compared by text)
const std::vector<NotificationType> m_multiple_types = { NotificationType::CustomNotification, NotificationType::PlaterWarning, NotificationType::ProgressBar, NotificationType::PrintHostUpload };
//prepared (basic) notifications
const std::vector<NotificationData> basic_notifications = {
{NotificationType::Mouse3dDisconnected, NotificationLevel::RegularNotification, 10, _u8L("3D Mouse disconnected.") },
{NotificationType::PresetUpdateAvailable, NotificationLevel::ImportantNotification, 20, _u8L("Configuration update is available."), _u8L("See more."),
[](wxEvtHandler* evnthndlr) {
if (evnthndlr != nullptr)
wxPostEvent(evnthndlr, PresetUpdateAvailableClickedEvent(EVT_PRESET_UPDATE_AVAILABLE_CLICKED));
return true;
}
},
{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotification, 20, _u8L("New version is available."), _u8L("See Releases page."), [](wxEvtHandler* evnthndlr){
wxLaunchDefaultBrowser("https://github.com/prusa3d/PrusaSlicer/releases"); return true; }},
{NotificationType::EmptyColorChangeCode, NotificationLevel::RegularNotification, 10,
_u8L("You have just added a G-code for color change, but its value is empty.\n"
"To export the G-code correctly, check the \"Color Change G-code\" in \"Printer Settings > Custom G-code\"") },
{NotificationType::EmptyAutoColorChange, NotificationLevel::RegularNotification, 10,
_u8L("This model doesn't allow to automatically add the color changes") },
{NotificationType::DesktopIntegrationSuccess, NotificationLevel::RegularNotification, 10,
_u8L("Desktop integration was successful.") },
{NotificationType::DesktopIntegrationFail, NotificationLevel::WarningNotification, 10,
_u8L("Desktop integration failed.") },
{NotificationType::UndoDesktopIntegrationSuccess, NotificationLevel::RegularNotification, 10,
_u8L("Undo desktop integration was successful.") },
{NotificationType::UndoDesktopIntegrationFail, NotificationLevel::WarningNotification, 10,
_u8L("Undo desktop integration failed.") },
//{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotification, 20, _u8L("New vesion of PrusaSlicer is available.", _u8L("Download page.") },
//{NotificationType::LoadingFailed, NotificationLevel::RegularNotification, 20, _u8L("Loading of model has Failed") },
//{NotificationType::DeviceEjected, NotificationLevel::RegularNotification, 10, _u8L("Removable device has been safely ejected")} // if we want changeble text (like here name of device), we need to do it as CustomNotification
};
static const NotificationData basic_notifications[];
};
}//namespace GUI

View file

@ -47,7 +47,7 @@ PresetForPrinter::PresetForPrinter(PhysicalPrinterDialog* parent, const std::str
{
m_sizer = new wxBoxSizer(wxVERTICAL);
m_delete_preset_btn = new ScalableButton(parent, wxID_ANY, "cross", "", wxDefaultSize, wxDefaultPosition, /*wxBU_LEFT | */wxBU_EXACTFIT);
m_delete_preset_btn = new ScalableButton(parent, wxID_ANY, "cross");
m_delete_preset_btn->SetFont(wxGetApp().normal_font());
m_delete_preset_btn->SetToolTip(_L("Delete this preset from this printer device"));
m_delete_preset_btn->Bind(wxEVT_BUTTON, &PresetForPrinter::DeletePreset, this);
@ -175,7 +175,7 @@ PhysicalPrinterDialog::PhysicalPrinterDialog(wxWindow* parent, wxString printer_
wxStaticText* label_top = new wxStaticText(this, wxID_ANY, _L("Descriptive name for the printer") + ":");
m_add_preset_btn = new ScalableButton(this, wxID_ANY, "add_copies", "", wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT);
m_add_preset_btn = new ScalableButton(this, wxID_ANY, "add_copies");
m_add_preset_btn->SetFont(wxGetApp().normal_font());
m_add_preset_btn->SetToolTip(_L("Add preset for this printer device"));
m_add_preset_btn->Bind(wxEVT_BUTTON, &PhysicalPrinterDialog::AddPreset, this);

View file

@ -1045,6 +1045,12 @@ void Sidebar::search()
p->searcher.search();
}
void Sidebar::jump_to_option(const std::string& opt_key, Preset::Type type, const std::wstring& category)
{
//const Search::Option& opt = p->searcher.get_option(opt_key, type);
wxGetApp().get_tab(type)->activate_option(opt_key, category);
}
void Sidebar::jump_to_option(size_t selected)
{
const Search::Option& opt = p->searcher.get_option(selected);

View file

@ -83,6 +83,7 @@ public:
void sys_color_changed();
void search();
void jump_to_option(size_t selected);
void jump_to_option(const std::string& opt_key, Preset::Type type, const std::wstring& category);
ObjectManipulation* obj_manipul();
ObjectList* obj_list();

View file

@ -11,14 +11,14 @@
namespace Slic3r {
namespace GUI {
PreferencesDialog::PreferencesDialog(wxWindow* parent) :
PreferencesDialog::PreferencesDialog(wxWindow* parent, int selected_tab) :
DPIDialog(parent, wxID_ANY, _L("Preferences"), wxDefaultPosition,
wxDefaultSize, wxDEFAULT_DIALOG_STYLE)
{
#ifdef __WXOSX__
isOSX = true;
#endif
build();
build(selected_tab);
}
static std::shared_ptr<ConfigOptionsGroup>create_options_tab(const wxString& title, wxBookCtrlBase* tabs)
@ -44,7 +44,7 @@ static void activate_options_tab(std::shared_ptr<ConfigOptionsGroup> optgroup)
sizer->Add(optgroup->sizer, 0, wxEXPAND | wxALL, 10);
}
void PreferencesDialog::build()
void PreferencesDialog::build(size_t selected_tab)
{
#ifdef _WIN32
wxGetApp().UpdateDarkUI(this);
@ -341,13 +341,20 @@ void PreferencesDialog::build()
option = Option(def, "tabs_as_menu");
m_optgroup_gui->append_single_option_line(option);
#endif
def.label = L("Show \"Did you know\" hints after start");
def.type = coBool;
def.tooltip = L("If enabled, useful hints are displayed at startup.");
def.set_default_value(new ConfigOptionBool{ app_config->get("show_hints") == "1" });
option = Option(def, "show_hints");
m_optgroup_gui->append_single_option_line(option);
def.label = L("Use custom size for toolbar icons");
def.type = coBool;
def.tooltip = L("If enabled, you can change size of toolbar icons manually.");
def.set_default_value(new ConfigOptionBool{ app_config->get("use_custom_toolbar_size") == "1" });
option = Option(def, "use_custom_toolbar_size");
m_optgroup_gui->append_single_option_line(option);
m_optgroup_gui->append_single_option_line(option);
}
activate_options_tab(m_optgroup_gui);
@ -379,6 +386,9 @@ void PreferencesDialog::build()
}
#endif // ENABLE_ENVIRONMENT_MAP
if (selected_tab < tabs->GetPageCount())
tabs->SetSelection(selected_tab);
auto sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(tabs, 1, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 5);

View file

@ -32,14 +32,14 @@ class PreferencesDialog : public DPIDialog
bool m_recreate_GUI{false};
public:
explicit PreferencesDialog(wxWindow* parent);
explicit PreferencesDialog(wxWindow* parent, int selected_tab = 0);
~PreferencesDialog() = default;
bool settings_layout_changed() const { return m_settings_layout_changed; }
bool seq_top_layer_only_changed() const { return m_seq_top_layer_only_changed; }
bool recreate_GUI() const { return m_recreate_GUI; }
void build();
void accept(wxEvent&);
void build(size_t selected_tab = 0);
void accept(wxEvent&);
protected:
void on_dpi_changed(const wxRect &suggested_rect) override;