diff --git a/resources/data/hints.ini b/resources/data/hints.ini index 2ebf9091f..81817f3ca 100644 --- a/resources/data/hints.ini +++ b/resources/data/hints.ini @@ -1,3 +1,54 @@ +# THIS DOCUMENT CONTAINS DATA FOR HINTS NOTIFICATIONS +# +# Each notification is divided by +# [hint:*name of notification*] +# +# Each notification MUST have text var in format: +# text = Headline of hint\nBody of hint. +# Headline is divided by new line (\n) from body. +# Headline is automaticaly printed as Bold. +# Body can contain bold marks: text to be bold (currently rendered as diffenert color, not bold due to font limitations) +# Body can contain hypertext: hypertext text +# Hypertext must be max one per notification and must be closed by +# +# Notification can have documentation link: +# documentation_link = https://help.prusa3d.com/en/article/name-of-article +# +# If notification contains hypertext, it needs to be specified by hypertext_type var. +# each type needs to be supported with one or more additional vars. +# These types are possible: +# +# Settings highlight (like search feature) +# hypertext_type = settings +# hypertext_settings_opt = name_of_settings (hover over settings value and copy last line of hover text) +# hypertext_settings_type = 1 (1 - 5 according to settings tab - to be channged to name of tabs instead of numbers) +# hypertext_settings_category = Infill (name of panel - written on left in settings) +# +# Plater top toolbar highlight +# hypertext_type = plater +# hypertext_plater_item = nameofbutton (internal name of GLToolbar items) +# +# Plater gizmos (left) toolbar highlight +# hypertext_type = gizmo +# hypertext_gizmo_item = name (name of svg icon of gizmo in resources without .svg suffix) +# +# Open preferences (might add item to highlight) +# hypertext_type = preferences +# hypertext_preferences_page = 0 (values 0-2 according to prefernces tab to be opened) +# +# Open gallery (no aditional var) +# 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. +# if there are both disabled and preferred, only preferred that are not in disabled are valid. + [hint:Perspective camera] text = Perspective camera\nDid you know that you can use the K key to quickly switch between an orthographic and perspective camera? @@ -45,12 +96,12 @@ hypertext_plater_item = arrange [hint:Reload from disk] text = Reload from disk\nDid you know that if you created a newer version of your model, you can simply reload it in PrusaSlicer? Right-click the model in the 3D view and choose Reload from disk. Read more in thedocumentation. hypertext_type = link -hypertext_link = https://help.prusa3d.com/en/article/reload-from-disk_120427 +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. hypertext_type = link -hypertext_link = https://help.prusa3d.com/en/article/per-model-settings_1674 +documentation_link = https://help.prusa3d.com/en/article/per-model-settings_1674 [hint:Solid infill threshold area] text = Solid infill threshold area\nDid you know that you can make parts of your model with a small cross-section be filled with solid infill automatically? Set theSolid infill threshold area.(Expert mode only.) @@ -91,7 +142,7 @@ text = Load config from G-code\nDid you know that you can use File-Import Config [hint:Ironing] text = Ironing\nDid you know that you can smooth top surfaces of prints using Ironing? The nozzle will run a special second infill phase at the same layer to fill in holes and flatten any lifted plastic. Read more in thedocumentation. (Requires Advanced or Expert mode.) hypertext_type = link -hypertext_link = https://help.prusa3d.com/en/article/ironing_177488 +documentation_link = https://help.prusa3d.com/en/article/ironing_177488 disabled_modes = SLA; simple [hint:Fuzzy skin] @@ -105,7 +156,7 @@ disabled_modes = SLA [hint:Negative volume] text = Negative volume\nDid you know that you can subtract one mesh from another using the Negative volume modifier? That way you can, for example, create easily resizable holes directly in PrusaSlicer. Read more in thedocumentation.(Requires Advanced or Expert mode.) hypertext_type = link -hypertext_link = https://help.prusa3d.com/en/article/negative-volume_238503 +documentation_link = https://help.prusa3d.com/en/article/negative-volume_238503 disabled_modes = SLA; simple [hint:Paint-on supports] @@ -123,17 +174,17 @@ disabled_modes = SLA; simple [hint:Insert Pause] text = Insert Pause\nDid you know that you can schedule the print to pause at a specific layer? Right-click the layer slider in the Preview and select Add pause print (M601). This can be used to insert magnets, weights or nuts into your prints. Read more in thedocumentation. hypertext_type = link -hypertext_link = https://help.prusa3d.com/en/article/insert-pause-or-custom-g-code-at-layer_120490#insert-pause-at-layer +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. hypertext_type = link -hypertext_link = https://help.prusa3d.com/en/article/insert-pause-or-custom-g-code-at-layer_120490#insert-custom-g-code-at-layer +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. hypertext_type = link -hypertext_link = https://help.prusa3d.com/en/article/configuration-snapshots_1776 +documentation_link = https://help.prusa3d.com/en/article/configuration-snapshots_1776 [hint:Minimum wall thickness] text = Minimum wall thickness\nDid you know that instead of the number of top and bottom layers, you can define theMinimum shell thicknessin millimeters? This feature is especially useful when using the variable layer height function. @@ -151,7 +202,7 @@ hypertext_preferences_page = 2 [hint:Adaptive infills] text = Adaptive infills\nDid you know that you can use the Adaptive cubic and Support cubic infills to decrease the print time and lower the filament consumption? Read more in thedocumentation. hypertext_type = link -hypertext_link = https://help.prusa3d.com/en/article/infill-patterns_177130 +documentation_link = https://help.prusa3d.com/en/article/infill-patterns_177130 [hint:Fullscreen mode] text = Fullscreen mode\nDid you know that you can switch PrusaSlicer to fullscreen mode? Use the F11 hotkey. @@ -159,7 +210,7 @@ text = Fullscreen mode\nDid you know that you can switch PrusaSlicer to fullscre [hint:Simplify mesh] text = Simplify mesh\nDid you know that you can reduce the number of triangles in a mesh using the Simplify mesh feature? Right-click the model and select Simplify model. Read more in thedocumentation. hypertext_type = link -hypertext_link = https://help.prusa3d.com/en/article/simplify-mesh_238941 +documentation_link = https://help.prusa3d.com/en/article/simplify-mesh_238941 #[hint:] #text = diff --git a/resources/icons/notification_clippy.svg b/resources/icons/notification_clippy.svg new file mode 100644 index 000000000..e7eb207fe --- /dev/null +++ b/resources/icons/notification_clippy.svg @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/notification_documentation.svg b/resources/icons/notification_documentation.svg new file mode 100644 index 000000000..21e7cfcb3 --- /dev/null +++ b/resources/icons/notification_documentation.svg @@ -0,0 +1,86 @@ + +image/svg+xml + + + + + + + + + + + + diff --git a/resources/icons/notification_documentation_hover.svg b/resources/icons/notification_documentation_hover.svg new file mode 100644 index 000000000..0c6b2e207 --- /dev/null +++ b/resources/icons/notification_documentation_hover.svg @@ -0,0 +1,97 @@ + +image/svg+xml + + + + + + + + + + + + diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h index dfb314d3c..713499a1b 100644 --- a/src/imgui/imconfig.h +++ b/src/imgui/imconfig.h @@ -124,32 +124,34 @@ namespace ImGui const char ColorMarkerEnd = 0x3; // ETX // Special ASCII characters are used here as an ikons markers - const char PrintIconMarker = 0x4; - const char PrinterIconMarker = 0x5; - const char PrinterSlaIconMarker = 0x6; - const char FilamentIconMarker = 0x7; - const char MaterialIconMarker = 0x8; - const char CloseNotifButton = 0xB; - const char CloseNotifHoverButton = 0xC; - const char MinimalizeButton = 0xE; - const char MinimalizeHoverButton = 0xF; - const char WarningMarker = 0x10; - const char ErrorMarker = 0x11; - const char EjectButton = 0x12; - const char EjectHoverButton = 0x13; - const char CancelButton = 0x14; - const char CancelHoverButton = 0x15; - const char VarLayerHeightMarker = 0x16; + const wchar_t PrintIconMarker = 0x4; + const wchar_t PrinterIconMarker = 0x5; + const wchar_t PrinterSlaIconMarker = 0x6; + const wchar_t FilamentIconMarker = 0x7; + const wchar_t MaterialIconMarker = 0x8; + const wchar_t CloseNotifButton = 0xB; + const wchar_t CloseNotifHoverButton = 0xC; + const wchar_t MinimalizeButton = 0xE; + const wchar_t MinimalizeHoverButton = 0xF; + const wchar_t WarningMarker = 0x10; + const wchar_t ErrorMarker = 0x11; + const wchar_t EjectButton = 0x12; + const wchar_t EjectHoverButton = 0x13; + const wchar_t CancelButton = 0x14; + const wchar_t CancelHoverButton = 0x15; + const wchar_t VarLayerHeightMarker = 0x16; - const char RightArrowButton = 0x18; - const char RightArrowHoverButton = 0x19; - const char PreferencesButton = 0x1A; - const char PreferencesHoverButton = 0x1B; - const char SinkingObjectMarker = 0x1C; - const char CustomSupportsMarker = 0x1D; - const char CustomSeamMarker = 0x1E; - const char MmuSegmentationMarker = 0x1F; - + const wchar_t RightArrowButton = 0x18; + const wchar_t RightArrowHoverButton = 0x19; + const wchar_t PreferencesButton = 0x1A; + const wchar_t PreferencesHoverButton = 0x1B; + const wchar_t SinkingObjectMarker = 0x1C; + const wchar_t CustomSupportsMarker = 0x1D; + const wchar_t CustomSeamMarker = 0x1E; + const wchar_t MmuSegmentationMarker = 0x1F; + const wchar_t DocumentationButton = 0x2600; + const wchar_t DocumentationHoverButton = 0x2601; + const wchar_t ClippyMarker = 0x2602; // void MyFunction(const char* name, const MyMatrix44& v); diff --git a/src/slic3r/GUI/HintNotification.cpp b/src/slic3r/GUI/HintNotification.cpp index fe4f3c9ab..a4ef5b963 100644 --- a/src/slic3r/GUI/HintNotification.cpp +++ b/src/slic3r/GUI/HintNotification.cpp @@ -31,9 +31,9 @@ inline void push_style_color(ImGuiCol idx, const ImVec4& col, bool fading_out, f ImGui::PushStyleColor(idx, col); } // return true if NOT in disabled mode. -inline bool disabled_modes_check(const std::string& disabled_modes) +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) { - if (disabled_modes.empty()) + if (disabled_mode.empty() && preferred_mode.empty() && disabled_tech.empty() && preferred_tech.empty()) return true; // simple / advanced / expert @@ -43,19 +43,37 @@ inline bool disabled_modes_check(const std::string& disabled_modes) 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) + 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; - // fff / sla + + // tchnology 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; + // 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; + } 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; + } } else { - if (disabled_modes.find("SLA") != std::string::npos) - return false; - } - + // 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; } } //namespace @@ -96,7 +114,11 @@ void HintDatabase::load_hints_from_file(const boost::filesystem::path& path) std::string text1; std::string hypertext_text; std::string follow_text; - std::string disabled_modes; + std::string disabled_mode; + std::string preferred_mode; + std::string disabled_tech; + std::string preferred_tech; + std::string documentation_link; unescape_string_cstyle(_utf8(dict["text"]), fulltext); // replace and for imgui markers std::string marker_s(1, ImGui::ColorMarkerStart); @@ -143,8 +165,20 @@ void HintDatabase::load_hints_from_file(const boost::filesystem::path& path) text1 = fulltext; } - if (dict.find("disabled_modes") != dict.end()) { - disabled_modes = dict["disabled_modes"]; + if (dict.find("disabled_mode") != dict.end()) { + disabled_mode = dict["disabled_mode"]; + } + 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("documentation_link") != dict.end()) { + documentation_link = dict["documentation_link"]; } // create HintData @@ -152,37 +186,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_modes, false, [hypertext_link]() { wxLaunchDefaultBrowser(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); } }; 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_modes, true, [opt, type, category]() { GUI::wxGetApp().sidebar().jump_to_option(opt, type, 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); } }; 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_modes, false, [page]() { wxGetApp().open_preferences(page); } }; + HintData hint_data{ text1, hypertext_text, follow_text, disabled_mode, preferred_mode, disabled_tech, preferred_tech, 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_modes, true, [item]() { wxGetApp().plater()->canvas3D()->highlight_toolbar_item(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); } }; 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); } }; + 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); } }; 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(); } }; + 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(); } }; m_loaded_hints.emplace_back(hint_data); } } else { // plain text without hypertext - HintData hint_data{ text1 }; + HintData hint_data{ text1, hypertext_text, follow_text, disabled_mode, preferred_mode, disabled_tech, preferred_tech, false, documentation_link }; m_loaded_hints.emplace_back(hint_data); } } @@ -194,6 +228,12 @@ HintData* HintDatabase::get_hint(bool up) init(); //return false; } + if (m_loaded_hints.empty()) + { + BOOST_LOG_TRIVIAL(error) << "There were no hints loaded from hints.ini file."; + return nullptr; + } + // 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(); @@ -220,12 +260,16 @@ void NotificationManager::HintNotification::count_spaces() std::string text; text = ImGui::WarningMarker; float picture_width = ImGui::CalcTextSize(text.c_str()).x; - m_left_indentation = picture_width + m_line_height / 2; + m_left_indentation = picture_width * 2 + 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 + if (m_documentation_link.empty()) + m_window_width_offset = m_left_indentation + m_line_height * 3.f; + else + m_window_width_offset = m_left_indentation + m_line_height * 5.5f; + m_window_width = m_line_height * 25; } @@ -387,12 +431,12 @@ void NotificationManager::HintNotification::set_next_window_size(ImGuiWrapper& i 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); + m_window_height = std::max((m_lines_count + 1.f) * m_line_height, 5.f * m_line_height); } bool NotificationManager::HintNotification::on_text_click() { - if (m_hypertext_callback != nullptr && (!m_runtime_disable || disabled_modes_check(m_disabled_modes))) + if (m_hypertext_callback != nullptr && (!m_runtime_disable || mode_and_tech_check(m_disabled_mode, m_preferred_mode, m_disabled_tech, m_preferred_tech))) m_hypertext_callback(); return false; } @@ -405,7 +449,7 @@ void NotificationManager::HintNotification::render_text(ImGuiWrapper& imgui, con 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 starting_y = (m_lines_count < 4 ? m_line_height / 2 * (4 - m_lines_count + 1) : m_line_height / 2); float shift_y = m_line_height; std::string line; @@ -425,8 +469,8 @@ void NotificationManager::HintNotification::render_text(ImGuiWrapper& imgui, con // regural line line = m_text1.substr(last_end, m_endlines[i] - last_end); } - // first line is headline - if (i == 0) { + // first line is headline (for hint notification it must be divided by \n) + if (m_text1.find('\n') >= m_endlines[i]) { line = ImGui::ColorMarkerStart + line + ImGui::ColorMarkerEnd; } // Add ImGui::ColorMarkerStart if there is ImGui::ColorMarkerEnd first (start was at prev line) @@ -534,6 +578,11 @@ void NotificationManager::HintNotification::render_close_button(ImGuiWrapper& im 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()) + { + render_documentation_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y); + } + } void NotificationManager::HintNotification::render_preferences_button(ImGuiWrapper& imgui, const float win_pos_x, const float win_pos_y) @@ -576,7 +625,6 @@ void NotificationManager::HintNotification::render_preferences_button(ImGuiWrapp // 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 @@ -621,12 +669,14 @@ void NotificationManager::HintNotification::render_logo(ImGuiWrapper& imgui, con 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); + std::wstring button_text; + button_text = ImGui::ClippyMarker;//LeftArrowButton; + std::string placeholder_text; + placeholder_text = ImGui::EjectButton; + + ImVec2 button_pic_size = ImGui::CalcTextSize(placeholder_text.c_str()); + ImVec2 button_size(button_pic_size.x * 1.25f * 2.f, button_pic_size.y * 1.25f * 2.f); + ImGui::SetCursorPosY(win_size.y / 2 - button_size.y * 1.1f); ImGui::SetCursorPosX(0); // shouldnt it render as text? if (imgui.button(button_text.c_str(), button_size.x, button_size.y)) @@ -639,10 +689,81 @@ void NotificationManager::HintNotification::render_logo(ImGuiWrapper& imgui, con ImGui::PopStyleColor(); ImGui::PopStyleColor(); } +void NotificationManager::HintNotification::render_documentation_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::wstring button_text; + button_text = ImGui::DocumentationButton; + std::string placeholder_text; + placeholder_text = ImGui::EjectButton; + + if (ImGui::IsMouseHoveringRect(ImVec2(win_pos.x - m_line_height * 5.f, win_pos.y), + ImVec2(win_pos.x - m_line_height * 2.5f, win_pos.y + win_size.y - 2 * m_line_height), + true)) + { + button_text = ImGui::DocumentationHoverButton; + // tooltip + + long time_now = wxGetLocalTime(); + if (m_hover_time > 0 && m_hover_time < time_now) { + ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BACKGROUND); + ImGui::BeginTooltip(); + imgui.text(_u8L("Open Documentation in web browser")); + ImGui::EndTooltip(); + ImGui::PopStyleColor(); + } + if (m_hover_time == 0) + m_hover_time = time_now; + + } + else + m_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); + ImGui::SetCursorPosX(win_size.x - m_line_height * 5.0f); + ImGui::SetCursorPosY(win_size.y / 2 - button_size.y); + if (imgui.button(button_text.c_str(), button_size.x, button_size.y)) + { + open_documentation(); + } + + //invisible large button + ImGui::SetCursorPosX(win_size.x - m_line_height * 4.625f); + ImGui::SetCursorPosY(0); + if (imgui.button(" ", m_line_height * 2.f, win_size.y - 2 * m_line_height)) + { + open_documentation(); + } + + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); +} + +void NotificationManager::HintNotification::open_documentation() +{ + if (!m_documentation_link.empty()) + { + wxLaunchDefaultBrowser(m_documentation_link); + } +} 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)) + 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)) { // Content for different user - retrieve another size_t count = HintDatabase::get_instance().get_count(); @@ -661,12 +782,15 @@ void NotificationManager::HintNotification::retrieve_data(size_t recursion_count 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_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_documentation_link = hint_data->documentation_link; m_has_hint_data = true; - + update(nd); } } } //namespace Slic3r diff --git a/src/slic3r/GUI/HintNotification.hpp b/src/slic3r/GUI/HintNotification.hpp index 125420fb6..ad0117a82 100644 --- a/src/slic3r/GUI/HintNotification.hpp +++ b/src/slic3r/GUI/HintNotification.hpp @@ -12,9 +12,13 @@ struct HintData std::string text; std::string hypertext; std::string follow_text; - std::string disabled_modes; + std::string disabled_mode; + std::string preferred_mode; + std::string disabled_tech; + std::string preferred_tech; bool runtime_disable; // if true - hyperlink will check before every click if not in disabled mode - std::function callback{ nullptr }; + std::string documentation_link; + std::function callback { nullptr }; }; class HintDatabase @@ -77,18 +81,27 @@ protected: 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_documentation_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); + void open_documentation(); bool m_has_hint_data { false }; std::function m_hypertext_callback; - std::string m_disabled_modes; + std::string m_disabled_mode; + std::string m_preferred_mode; + std::string m_disabled_tech; + std::string m_preferred_tech; 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 }; }; } //namespace Slic3r diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 79e34ba85..5b88ad9e7 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -36,7 +36,7 @@ namespace Slic3r { namespace GUI { -static const std::map font_icons = { +static const std::map font_icons = { {ImGui::PrintIconMarker , "cog" }, {ImGui::PrinterIconMarker , "printer" }, {ImGui::PrinterSlaIconMarker , "sla_printer" }, @@ -49,21 +49,27 @@ static const std::map font_icons = { {ImGui::PreferencesButton , "notification_preferences" }, {ImGui::PreferencesHoverButton , "notification_preferences_hover"}, }; -static const std::map font_icons_large = { - {ImGui::CloseNotifButton , "notification_close" }, - {ImGui::CloseNotifHoverButton , "notification_close_hover" }, - {ImGui::EjectButton , "notification_eject_sd" }, - {ImGui::EjectHoverButton , "notification_eject_sd_hover" }, - {ImGui::WarningMarker , "notification_warning" }, - {ImGui::ErrorMarker , "notification_error" }, - {ImGui::CancelButton , "notification_cancel" }, - {ImGui::CancelHoverButton , "notification_cancel_hover" }, - {ImGui::SinkingObjectMarker , "move" }, - {ImGui::CustomSupportsMarker , "fdm_supports" }, - {ImGui::CustomSeamMarker , "seam" }, - {ImGui::MmuSegmentationMarker , "move" }, - {ImGui::VarLayerHeightMarker , "layers" }, - +static const std::map font_icons_large = { + {ImGui::CloseNotifButton , "notification_close" }, + {ImGui::CloseNotifHoverButton , "notification_close_hover" }, + {ImGui::EjectButton , "notification_eject_sd" }, + {ImGui::EjectHoverButton , "notification_eject_sd_hover" }, + {ImGui::WarningMarker , "notification_warning" }, + {ImGui::ErrorMarker , "notification_error" }, + {ImGui::CancelButton , "notification_cancel" }, + {ImGui::CancelHoverButton , "notification_cancel_hover" }, + {ImGui::SinkingObjectMarker , "move" }, + {ImGui::CustomSupportsMarker , "fdm_supports" }, + {ImGui::CustomSeamMarker , "seam" }, + {ImGui::MmuSegmentationMarker , "move" }, + {ImGui::VarLayerHeightMarker , "layers" }, + {ImGui::DocumentationButton , "notification_documentation" }, + {ImGui::DocumentationHoverButton, "notification_documentation_hover"}, +}; + +static const std::map font_icons_extra_large = { + {ImGui::ClippyMarker , "notification_clippy" }, + }; const ImVec4 ImGuiWrapper::COL_GREY_DARK = { 0.333f, 0.333f, 0.333f, 1.0f }; @@ -989,6 +995,8 @@ void ImGuiWrapper::init_font(bool compress) io.Fonts->AddCustomRectFontGlyph(font, icon.first, icon_sz, icon_sz, 3.0 * font_scale + icon_sz); for (auto& icon : font_icons_large) io.Fonts->AddCustomRectFontGlyph(font, icon.first, icon_sz * 2, icon_sz * 2, 3.0 * font_scale + icon_sz * 2); + for (auto& icon : font_icons_extra_large) + io.Fonts->AddCustomRectFontGlyph(font, icon.first, icon_sz * 4, icon_sz * 4, 3.0 * font_scale + icon_sz * 4); // Build texture atlas unsigned char* pixels; @@ -1027,6 +1035,22 @@ void ImGuiWrapper::init_font(bool compress) rect_id++; } + icon_sz *= 2; // default size of extra large icon is 64 px + for (auto icon : font_icons_extra_large) { + if (const ImFontAtlas::CustomRect* rect = io.Fonts->GetCustomRectByIndex(rect_id)) { + assert(rect->Width == icon_sz); + assert(rect->Height == icon_sz); + std::vector raw_data = load_svg(icon.second, icon_sz, icon_sz); + const ImU32* pIn = (ImU32*)raw_data.data(); + for (int y = 0; y < icon_sz; y++) { + ImU32* pOut = (ImU32*)pixels + (rect->Y + y) * width + (rect->X); + for (int x = 0; x < icon_sz; x++) + *pOut++ = *pIn++; + } + } + rect_id++; + } + // Upload texture to graphics system GLint last_texture; glsafe(::glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture));