diff --git a/resources/data/hints.ini b/resources/data/hints.ini index 81817f3ca..0de06f04a 100644 --- a/resources/data/hints.ini +++ b/resources/data/hints.ini @@ -40,13 +40,13 @@ # hypertext_type = gallery # # -# Each notification can have disabled and preferred modes and techs - divided by ; -# preferred_mode = simple -# disabled_mode = advanced; expert -# preferred_tech = SLA -# disabled_tech = FFF; MMU -# Algorithm shows hint only if in preffered mode / tech. -# Algorithm shows hint only if not in disabled mode / tech. +# Each notification can have disabled and enabled modes and techs - divided by ; and space +# enabled_tags = ... +# disabled_tags = ... +# supported tags are: simple; advanced; expert; FFF; MMU; SLA +# FFF is affirmative for both one or more extruder printers. +# Algorithm shows hint only if ALL enabled tags are affirmative. (so never do enabled_tags = FFF; SLA;) +# Algorithm shows hint only if not in all disabled tags. # if there are both disabled and preferred, only preferred that are not in disabled are valid. @@ -70,7 +70,7 @@ hypertext_type = settings hypertext_settings_opt = infill_every_layers hypertext_settings_type = 1 hypertext_settings_category = Infill -disabled_modes = SLA; simple +disabled_tags = 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. @@ -81,7 +81,7 @@ hypertext_preferences_page = 2 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 +disabled_tags = 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? @@ -94,12 +94,12 @@ 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. +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 the documentation. hypertext_type = link documentation_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. +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 the documentation. hypertext_type = link documentation_link = https://help.prusa3d.com/en/article/per-model-settings_1674 @@ -109,7 +109,7 @@ hypertext_type = settings hypertext_settings_opt = solid_infill_below_area hypertext_settings_type = 1 hypertext_settings_category = Infill -disabled_modes = SLA; simple; advanced +enabled_tags = FFF; expert [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. @@ -140,10 +140,10 @@ text = PageUp / PageDown quick rotation by 45 degrees\nDid you know that you can 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.) +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 the documentation. (Requires Advanced or Expert mode.) hypertext_type = link documentation_link = https://help.prusa3d.com/en/article/ironing_177488 -disabled_modes = SLA; simple +disabled_tags = 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. @@ -151,38 +151,38 @@ hypertext_type = settings hypertext_settings_opt = fuzzy_skin hypertext_settings_type = 1 hypertext_settings_category = Layers and perimeters -disabled_modes = SLA +disabled_tags = 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.) +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 the documentation. (Requires Advanced or Expert mode.) hypertext_type = link documentation_link = https://help.prusa3d.com/en/article/negative-volume_238503 -disabled_modes = SLA; simple +disabled_tags = 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 +disabled_tags = 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 +disabled_tags = 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. +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 the documentation. hypertext_type = link documentation_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. +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 the documentation. hypertext_type = link documentation_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. +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 the documentation. hypertext_type = link documentation_link = https://help.prusa3d.com/en/article/configuration-snapshots_1776 @@ -192,7 +192,7 @@ hypertext_type = settings hypertext_settings_opt = top_solid_min_thickness hypertext_settings_type = 1 hypertext_settings_category = Layers and perimeters -disabled_modes = SLA +disabled_tags = 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. @@ -200,7 +200,7 @@ 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. +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 the documentation. hypertext_type = link documentation_link = https://help.prusa3d.com/en/article/infill-patterns_177130 @@ -208,7 +208,7 @@ documentation_link = https://help.prusa3d.com/en/article/infill-patterns_177130 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. +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 the documentation. hypertext_type = link documentation_link = https://help.prusa3d.com/en/article/simplify-mesh_238941 diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 297869ab9..4fa80bf58 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -664,7 +664,7 @@ void GUI_App::post_init() // show "Did you know" notification if (app_config->get("show_hints") == "1" && ! is_gcode_viewer()) - plater_->get_notification_manager()->push_hint_notification(); + plater_->get_notification_manager()->push_hint_notification(true); // 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. diff --git a/src/slic3r/GUI/HintNotification.cpp b/src/slic3r/GUI/HintNotification.cpp index a4ef5b963..973359bcb 100644 --- a/src/slic3r/GUI/HintNotification.cpp +++ b/src/slic3r/GUI/HintNotification.cpp @@ -30,52 +30,116 @@ inline void push_style_color(ImGuiCol idx, const ImVec4& col, bool fading_out, f else ImGui::PushStyleColor(idx, col); } -// return true if NOT in disabled mode. -inline bool mode_and_tech_check(const std::string& disabled_mode, const std::string& preferred_mode, const std::string& disabled_tech, const std::string& preferred_tech) +enum TagCheckResult { - if (disabled_mode.empty() && preferred_mode.empty() && disabled_tech.empty() && preferred_tech.empty()) - return true; + TagCheckAffirmative, + TagCheckNegative, + TagCheckNotCompatible +}; +// returns if in mode defined by tag +inline TagCheckResult tag_check_mode(const std::string& tag) +{ + std::vector allowed_tags = {"simple", "advanced", "expert"}; + if (std::find(allowed_tags.begin(), allowed_tags.end(), tag) != allowed_tags.end()) + { + ConfigOptionMode config_mode = wxGetApp().get_mode(); + if (config_mode == ConfigOptionMode::comSimple) return (tag == "simple" ? TagCheckAffirmative : TagCheckNegative); + else if (config_mode == ConfigOptionMode::comAdvanced) return (tag == "advanced" ? TagCheckAffirmative : TagCheckNegative); + else if (config_mode == ConfigOptionMode::comExpert) return (tag == "expert" ? TagCheckAffirmative : TagCheckNegative); + } + return TagCheckNotCompatible; +} - // 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 (!preferred_mode.empty() && !mode_name.empty() && preferred_mode.find(mode_name) == std::string::npos) - return false; - if (!mode_name.empty() && disabled_mode.find(mode_name) != std::string::npos) - return false; - - - // tchnology - const PrinterTechnology tech = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology(); - if (tech == ptFFF) { - // MMU - bool is_mmu = wxGetApp().extruders_edited_cnt() > 1; - if (is_mmu) - { - if (!preferred_tech.empty() && preferred_tech.find("MMU") == std::string::npos) - return false; - if (is_mmu && disabled_tech.find("MMU") != std::string::npos) - return false; +inline TagCheckResult tag_check_tech(const std::string& tag) +{ + std::vector allowed_tags = { "FFF", "MMU", "SLA" }; + if (std::find(allowed_tags.begin(), allowed_tags.end(), tag) != allowed_tags.end()) { + const PrinterTechnology tech = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology(); + if (tech == ptFFF) { + // MMU / FFF + bool is_mmu = wxGetApp().extruders_edited_cnt() > 1; + if (tag == "MMU") return (is_mmu ? TagCheckAffirmative : TagCheckNegative); + return (tag == "FFF" ? TagCheckAffirmative : TagCheckNegative); } else { - // only FFF - does not show if MMU preffered - if (!preferred_tech.empty() && preferred_tech.find("FFF") == std::string::npos) - return false; - if (disabled_tech.find("FFF") != std::string::npos) - return false; + // SLA + return (tag == "SLA" ? TagCheckAffirmative : TagCheckNegative); + } + } + return TagCheckNotCompatible; +} + +// return true if NOT in disabled mode. +inline bool tags_check(const std::string& disabled_tags, const std::string& enabled_tags) +{ + if (disabled_tags.empty() && enabled_tags.empty()) + return true; + // enabled tags must ALL return affirmative or check fails + if (!enabled_tags.empty()) { + std::string tag; + for (size_t i = 0; i < enabled_tags.size(); i++) { + if (enabled_tags[i] == ' ') { + tag.erase(); + continue; + } + if (enabled_tags[i] != ';') { + tag += enabled_tags[i]; + } + if (enabled_tags[i] == ';' || i == enabled_tags.size() - 1) { + if (!tag.empty()) { + TagCheckResult result; + result = tag_check_mode(tag); + if (result == TagCheckResult::TagCheckNegative) + return false; + if (result == TagCheckResult::TagCheckAffirmative) + continue; + result = tag_check_tech(tag); + if (result == TagCheckResult::TagCheckNegative) + return false; + if (result == TagCheckResult::TagCheckAffirmative) + continue; + BOOST_LOG_TRIVIAL(error) << "Hint Notification: Tag " << tag << " in enabled_tags not compatible."; + // non compatible in enabled means return false since all enabled must be affirmative. + return false; + } + } + } + } + // disabled tags must all NOT return affirmative or check fails + if (!disabled_tags.empty()) { + std::string tag; + for (size_t i = 0; i < disabled_tags.size(); i++) { + if (disabled_tags[i] == ' ') { + tag.erase(); + continue; + } + if (disabled_tags[i] != ';') { + tag += disabled_tags[i]; + } + if (disabled_tags[i] == ';' || i == disabled_tags.size() - 1) { + if (!tag.empty()) { + TagCheckResult result; + result = tag_check_mode(tag); + if (result == TagCheckResult::TagCheckNegative) + continue; + if (result == TagCheckResult::TagCheckAffirmative) + return false; + result = tag_check_tech(tag); + if (result == TagCheckResult::TagCheckNegative) + continue; + if (result == TagCheckResult::TagCheckAffirmative) + return false; + BOOST_LOG_TRIVIAL(error) << "Hint Notification: Tag " << tag << " in disabled_tags not compatible."; + } + } } - } else { - // SLA - if (!preferred_tech.empty() && preferred_tech.find("SLA") == std::string::npos) - return false; - if (disabled_tech.find("SLA") != std::string::npos) - return false; } return true; } +inline void launch_browser_if_allowed(const std::string& url) +{ + if (wxGetApp().app_config->get("suppress_hyperlinks") != "1") + wxLaunchDefaultBrowser(url); +} } //namespace void HintDatabase::init() @@ -108,17 +172,15 @@ void HintDatabase::load_hints_from_file(const boost::filesystem::path& path) dict.emplace(data.first, data.second.data()); } - //unescaping a translating all texts - //unescape text1 + //unescaping and translating all texts and saving all data common for all hint types std::string fulltext; std::string text1; std::string hypertext_text; std::string follow_text; - std::string disabled_mode; - std::string preferred_mode; - std::string disabled_tech; - std::string preferred_tech; + std::string disabled_tags; + std::string enabled_tags; std::string documentation_link; + //unescape text1 unescape_string_cstyle(_utf8(dict["text"]), fulltext); // replace and for imgui markers std::string marker_s(1, ImGui::ColorMarkerStart); @@ -165,17 +227,11 @@ void HintDatabase::load_hints_from_file(const boost::filesystem::path& path) text1 = fulltext; } - if (dict.find("disabled_mode") != dict.end()) { - disabled_mode = dict["disabled_mode"]; + if (dict.find("disabled_tags") != dict.end()) { + disabled_tags = dict["disabled_tags"]; } - if (dict.find("preferred_mode") != dict.end()) { - preferred_mode = dict["preferred_mode"]; - } - if (dict.find("disabled_tech") != dict.end()) { - disabled_tech = dict["disabled_tech"]; - } - if (dict.find("preferred_tech") != dict.end()) { - preferred_tech = dict["preferred_tech"]; + if (dict.find("enabled_tags") != dict.end()) { + enabled_tags = dict["enabled_tags"]; } if (dict.find("documentation_link") != dict.end()) { documentation_link = dict["documentation_link"]; @@ -186,37 +242,37 @@ void HintDatabase::load_hints_from_file(const boost::filesystem::path& path) //link to internet if(dict["hypertext_type"] == "link") { std::string hypertext_link = dict["hypertext_link"]; - HintData hint_data{ text1, hypertext_text, follow_text, disabled_mode, preferred_mode, disabled_tech, preferred_tech, false, documentation_link, [hypertext_link]() { wxLaunchDefaultBrowser(hypertext_link); } }; + HintData hint_data{ text1, hypertext_text, follow_text, disabled_tags, enabled_tags, false, documentation_link, [hypertext_link]() { launch_browser_if_allowed(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(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_mode, preferred_mode, disabled_tech, preferred_tech, true, documentation_link, [opt, type, category]() { GUI::wxGetApp().sidebar().jump_to_option(opt, type, category); } }; + HintData hint_data{ text1, hypertext_text, follow_text, disabled_tags, enabled_tags, true, documentation_link, [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(std::atoi(dict["hypertext_preferences_page"].c_str())); - HintData hint_data{ text1, hypertext_text, follow_text, disabled_mode, preferred_mode, disabled_tech, preferred_tech, false, documentation_link, [page]() { wxGetApp().open_preferences(page); } }; + HintData hint_data{ text1, hypertext_text, follow_text, disabled_tags, enabled_tags, false, documentation_link, [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_mode, preferred_mode, disabled_tech, preferred_tech, true, documentation_link, [item]() { wxGetApp().plater()->canvas3D()->highlight_toolbar_item(item); } }; + HintData hint_data{ text1, hypertext_text, follow_text, disabled_tags, enabled_tags, true, documentation_link, [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_mode, preferred_mode, disabled_tech, preferred_tech, true, documentation_link, [item]() { wxGetApp().plater()->canvas3D()->highlight_gizmo(item); } }; + HintData hint_data{ text1, hypertext_text, follow_text, disabled_tags, enabled_tags, true, documentation_link, [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_mode, preferred_mode, disabled_tech, preferred_tech, false, documentation_link, []() { wxGetApp().obj_list()->load_shape_object_from_gallery(); } }; + HintData hint_data{ text1, hypertext_text, follow_text, disabled_tags, enabled_tags, false, documentation_link, []() { wxGetApp().obj_list()->load_shape_object_from_gallery(); } }; m_loaded_hints.emplace_back(hint_data); } } else { // plain text without hypertext - HintData hint_data{ text1, hypertext_text, follow_text, disabled_mode, preferred_mode, disabled_tech, preferred_tech, false, documentation_link }; + HintData hint_data{ text1, hypertext_text, follow_text, disabled_tags, enabled_tags, false, documentation_link }; m_loaded_hints.emplace_back(hint_data); } } @@ -235,7 +291,7 @@ HintData* HintDatabase::get_hint(bool up) } // 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 = (up ? m_hint_id + 1 : m_hint_id ); m_hint_id %= m_loaded_hints.size(); AppConfig* app_config = wxGetApp().app_config; @@ -307,7 +363,7 @@ void NotificationManager::HintNotification::count_lines() } // 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 + ImGui::CalcTextSize(text.substr(last_end, next_space - last_end).c_str()).x < (m_window_width - m_window_width_offset) / 5 * 3 ) { float width_of_a = ImGui::CalcTextSize("a").x; int letter_count = (int)((m_window_width - m_window_width_offset) / width_of_a); @@ -377,7 +433,7 @@ void NotificationManager::HintNotification::count_lines() } // 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 + ImGui::CalcTextSize(text.substr(last_end, next_space - last_end).c_str()).x + size_of_last_line < (m_window_width - m_window_width_offset) / 5 * 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); @@ -436,7 +492,7 @@ void NotificationManager::HintNotification::set_next_window_size(ImGuiWrapper& i bool NotificationManager::HintNotification::on_text_click() { - if (m_hypertext_callback != nullptr && (!m_runtime_disable || mode_and_tech_check(m_disabled_mode, m_preferred_mode, m_disabled_tech, m_preferred_tech))) + if (m_hypertext_callback != nullptr && (!m_runtime_disable || tags_check(m_disabled_tags, m_enabled_tags))) m_hypertext_callback(); return false; } @@ -575,10 +631,10 @@ void NotificationManager::HintNotification::render_close_button(ImGuiWrapper& im ImGui::PopStyleColor(); - render_right_arrow_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y); + //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); - if (!m_documentation_link.empty()) + if (!m_documentation_link.empty() && wxGetApp().app_config->get("suppress_hyperlinks") != "1") { render_documentation_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y); } @@ -597,12 +653,24 @@ void NotificationManager::HintNotification::render_preferences_button(ImGuiWrapp 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), + if (ImGui::IsMouseHoveringRect(ImVec2(win_pos_x - m_window_width / 15.f, win_pos_y + m_window_height - 1.75f * m_line_height), ImVec2(win_pos_x, win_pos_y + m_window_height), true)) { button_text = ImGui::PreferencesHoverButton; - } + // tooltip + long time_now = wxGetLocalTime(); + if (m_prefe_hover_time > 0 && m_prefe_hover_time < time_now) { + ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BACKGROUND); + ImGui::BeginTooltip(); + imgui.text(_u8L("Open Preferences.")); + ImGui::EndTooltip(); + ImGui::PopStyleColor(); + } + if (m_prefe_hover_time == 0) + m_prefe_hover_time = time_now; + } else + m_prefe_hover_time = 0; 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); @@ -710,21 +778,19 @@ void NotificationManager::HintNotification::render_documentation_button(ImGuiWra { button_text = ImGui::DocumentationHoverButton; // tooltip - long time_now = wxGetLocalTime(); - if (m_hover_time > 0 && m_hover_time < time_now) { + if (m_docu_hover_time > 0 && m_docu_hover_time < time_now) { ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BACKGROUND); ImGui::BeginTooltip(); - imgui.text(_u8L("Open Documentation in web browser")); + imgui.text(_u8L("Open Documentation in web browser.")); ImGui::EndTooltip(); ImGui::PopStyleColor(); } - if (m_hover_time == 0) - m_hover_time = time_now; - + if (m_docu_hover_time == 0) + m_docu_hover_time = time_now; } else - m_hover_time = 0; + m_docu_hover_time = 0; ImVec2 button_pic_size = ImGui::CalcTextSize(placeholder_text.c_str()); ImVec2 button_size(button_pic_size.x * 1.25f, button_pic_size.y * 1.25f); @@ -754,16 +820,16 @@ void NotificationManager::HintNotification::open_documentation() { if (!m_documentation_link.empty()) { - wxLaunchDefaultBrowser(m_documentation_link); + launch_browser_if_allowed(m_documentation_link); } } -void NotificationManager::HintNotification::retrieve_data(size_t recursion_counter) +void NotificationManager::HintNotification::retrieve_data(int recursion_counter) { - HintData* hint_data = HintDatabase::get_instance().get_hint(true); + HintData* hint_data = HintDatabase::get_instance().get_hint(recursion_counter >= 0 ? true : false); if (hint_data == nullptr) close(); - if (hint_data != nullptr && !mode_and_tech_check(hint_data->disabled_mode, hint_data->preferred_mode, hint_data->disabled_tech, hint_data->preferred_tech)) + if (hint_data != nullptr && !tags_check(hint_data->disabled_tags, hint_data->enabled_tags)) { // Content for different user - retrieve another size_t count = HintDatabase::get_instance().get_count(); @@ -783,13 +849,11 @@ void NotificationManager::HintNotification::retrieve_data(size_t recursion_count hint_data->hypertext, nullptr, hint_data->follow_text }; m_hypertext_callback = hint_data->callback; - m_disabled_mode = hint_data->disabled_mode; - m_preferred_mode = hint_data->preferred_mode; - m_disabled_tech = hint_data->disabled_tech; - m_preferred_tech = hint_data->preferred_tech; - m_runtime_disable = hint_data->runtime_disable; + m_disabled_tags = hint_data->disabled_tags; + m_enabled_tags = hint_data->enabled_tags; + m_runtime_disable = hint_data->runtime_disable; m_documentation_link = hint_data->documentation_link; - m_has_hint_data = true; + m_has_hint_data = true; update(nd); } } diff --git a/src/slic3r/GUI/HintNotification.hpp b/src/slic3r/GUI/HintNotification.hpp index ad0117a82..ff81c66a2 100644 --- a/src/slic3r/GUI/HintNotification.hpp +++ b/src/slic3r/GUI/HintNotification.hpp @@ -12,10 +12,8 @@ struct HintData std::string text; std::string hypertext; std::string follow_text; - std::string disabled_mode; - std::string preferred_mode; - std::string disabled_tech; - std::string preferred_tech; + std::string disabled_tags; + std::string enabled_tags; bool runtime_disable; // if true - hyperlink will check before every click if not in disabled mode std::string documentation_link; std::function callback { nullptr }; @@ -57,12 +55,13 @@ private: class NotificationManager::HintNotification : public NotificationManager::PopNotification { public: - HintNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler) + HintNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, bool new_hint) : PopNotification(n, id_provider, evt_handler) { - retrieve_data(); + retrieve_data(new_hint ? 0 : -1); } virtual void init() override; + void open_next() { retrieve_data(0); } protected: virtual void set_next_window_size(ImGuiWrapper& imgui) override; virtual void count_spaces() override; @@ -87,21 +86,21 @@ protected: 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); + // recursion counter -1 tells to retrieve same hint as last time + void retrieve_data(int recursion_counter = 0); void open_documentation(); bool m_has_hint_data { false }; std::function m_hypertext_callback; - std::string m_disabled_mode; - std::string m_preferred_mode; - std::string m_disabled_tech; - std::string m_preferred_tech; + std::string m_disabled_tags; + std::string m_enabled_tags; bool m_runtime_disable; std::string m_documentation_link; float m_close_b_y { 0 }; float m_close_b_w { 0 }; // hover of buttons - size_t m_hover_time { 0 }; + size_t m_docu_hover_time { 0 }; + size_t m_prefe_hover_time{ 0 }; }; } //namespace Slic3r diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 5cf0d8573..6a900714e 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -45,6 +45,7 @@ #include "GUI_Factories.hpp" #include "GUI_ObjectList.hpp" #include "GalleryDialog.hpp" +#include "NotificationManager.hpp" #ifdef _WIN32 #include @@ -1073,6 +1074,10 @@ static wxMenu* generate_help_menu() else append_menu_item(helpMenu, wxID_ANY, wxString::Format(_L("&About %s"), GCODEVIEWER_APP_NAME), _L("Show about dialog"), [](wxCommandEvent&) { Slic3r::GUI::about(); }); + append_menu_item(helpMenu, wxID_ANY, _L("Next Hint notification"), _L("Opens another Hint notification."), + [](wxCommandEvent&) { wxGetApp().plater()->get_notification_manager()->push_hint_notification(true); }); + append_menu_item(helpMenu, wxID_ANY, _L("Reopen Hint notification"), _L("Opens Hint notification in bottom right corner."), + [](wxCommandEvent&) { wxGetApp().plater()->get_notification_manager()->push_hint_notification(false); }); helpMenu->AppendSeparator(); append_menu_item(helpMenu, wxID_ANY, _L("Keyboard Shortcuts") + sep + "&?", _L("Show the list of the keyboard shortcuts"), [](wxCommandEvent&) { wxGetApp().keyboard_shortcuts(); }); diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 296c8bfd0..9620f803e 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -1347,14 +1347,30 @@ void NotificationManager::upload_job_notification_show_error(int id, const std:: } } } -void NotificationManager::push_hint_notification() +void NotificationManager::push_hint_notification(bool open_next) +{ + + for (std::unique_ptr& notification : m_pop_notifications) { + if (notification->get_type() == NotificationType::DidYouKnowHint) { + if (open_next) + (dynamic_cast(notification.get()))->open_next(); + else + notification->set_hovered(); + return; + } + } + + NotificationData data{ NotificationType::DidYouKnowHint, NotificationLevel::RegularNotification, 300, "" }; + push_notification_data(std::make_unique(data, m_id_provider, m_evt_handler, open_next), 0); +} + +bool NotificationManager::is_hint_notification_open() { for (std::unique_ptr& notification : m_pop_notifications) { if (notification->get_type() == NotificationType::DidYouKnowHint) - return; + return true; } - NotificationData data{ NotificationType::DidYouKnowHint, NotificationLevel::RegularNotification, 0, "" }; - push_notification_data(std::make_unique(data, m_id_provider, m_evt_handler), 0); + return false; } void NotificationManager::push_updated_item_info_notification(InfoItemType type) diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 2c5c85b19..72e867704 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -169,7 +169,8 @@ public: 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(); + void push_hint_notification(bool open_next); + bool is_hint_notification_open(); void push_updated_item_info_notification(InfoItemType type); // Close old notification ExportFinished. void new_export_began(bool on_removable);