From 4654d24f6f1191b30f7a3fd472b763e30994039b Mon Sep 17 00:00:00 2001 From: Lam Wei Lun Date: Wed, 2 Sep 2026 12:38:27 +0800 Subject: [PATCH] Add a OrcaSlicer badge in the thumbnail preview for published 3MF projects. Add a visual indicator in the publish dialog to show that something in the section is toggled --- resources/web/homepage/css/home.css | 16 +++++++++- resources/web/homepage/js/home.js | 3 +- src/slic3r/GUI/PublishSettingsDialog.cpp | 39 ++++++++++++++++++++++++ src/slic3r/GUI/PublishSettingsDialog.hpp | 5 +++ src/slic3r/GUI/Widgets/Button.cpp | 35 +++++++++++++++++++++ src/slic3r/GUI/Widgets/Button.hpp | 7 +++++ src/slic3r/GUI/Widgets/TabCtrl.cpp | 8 +++++ src/slic3r/GUI/Widgets/TabCtrl.hpp | 3 ++ 8 files changed, 114 insertions(+), 2 deletions(-) diff --git a/resources/web/homepage/css/home.css b/resources/web/homepage/css/home.css index ff04d3ba3d..f4ef1d5420 100644 --- a/resources/web/homepage/css/home.css +++ b/resources/web/homepage/css/home.css @@ -552,7 +552,8 @@ body background-color: #E4E4E4; border-radius: 8px; width: 184px; - height: 184px; + height: 184px; + position: relative; } .FileItem img @@ -564,6 +565,19 @@ body object-fit: cover; } +.FileImg .FileLogoBadge +{ + position: absolute; + top: 8px; + left: 8px; + width: 26px; + height: 26px; + padding: 2px; + box-sizing: border-box; + background-color: rgba(255,255,255,0.85); + border-radius: 50%; +} + .FileName { white-space: nowrap; diff --git a/resources/web/homepage/js/home.js b/resources/web/homepage/js/home.js index 3cb5ff4fb3..20eb96474b 100644 --- a/resources/web/homepage/js/home.js +++ b/resources/web/homepage/js/home.js @@ -226,10 +226,11 @@ function ShowRecentFileList( pList ) //let sShortName=sPath.substring(index+1,sPath.length); let sBadge=sPublished? 'PUB':''; + let sLogoBadge=sPublished? '':''; let TmpHtml='
'+ ''+ - '
No Image
'+ + '
No Image'+sLogoBadge+'
'+ '
'+sBadge+'
'+sName+'
'+ '
'+sTime+'
'+ '
'; diff --git a/src/slic3r/GUI/PublishSettingsDialog.cpp b/src/slic3r/GUI/PublishSettingsDialog.cpp index d4d9b5d5d2..340ca8d02e 100644 --- a/src/slic3r/GUI/PublishSettingsDialog.cpp +++ b/src/slic3r/GUI/PublishSettingsDialog.cpp @@ -881,6 +881,7 @@ void PublishSettingsDialog::build_option_model() m_outer_tabs->SelectItem(0); show_outer_page(0); } + refresh_tab_indicators(); } size_t PublishSettingsDialog::section_group_for(Section kind) @@ -1127,6 +1128,7 @@ void PublishSettingsDialog::add_row_ui(const std::string& key, Row& current = m_rows[row_index]; current.check = new wxCheckBox(category.scroll, wxID_ANY, label); current.check->SetFont(Label::Body_13); + current.check->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent&) { refresh_tab_indicators(); }); auto* row_sizer = new wxBoxSizer(wxHORIZONTAL); row_sizer->Add(current.check, 0, wxALIGN_CENTER_VERTICAL); // The value is read-only text (incl. the Type row: the published type is the slot's @@ -1161,6 +1163,7 @@ void PublishSettingsDialog::on_full_toggle(size_t category_index) const bool full = cat.full_check->GetValue(); for (size_t r : cat.rows) m_rows[r].check->Enable(!full); + refresh_tab_indicators(); } void PublishSettingsDialog::on_enable_toggle(size_t category_index) @@ -1195,6 +1198,7 @@ void PublishSettingsDialog::on_enable_toggle(size_t category_index) apply_visibility(); if (cat.page != nullptr) cat.page->GetSizer()->Layout(); + refresh_tab_indicators(); } void PublishSettingsDialog::add_mixed_visual(size_t category_index, const MixedVisualSpec& spec) @@ -1606,6 +1610,7 @@ void PublishSettingsDialog::select_all(bool value) if (m_categories[c].section == Section::Material) on_enable_toggle(c); apply_visibility(); + refresh_tab_indicators(); } bool PublishSettingsDialog::row_is_visible(const Row& row) const @@ -1637,6 +1642,7 @@ void PublishSettingsDialog::select_visible(bool value) m_filter_ctrl->ChangeValue(""); apply_filter(""); // resync visibility and the All/None bar } + refresh_tab_indicators(); } void PublishSettingsDialog::show_menu(wxMouseEvent& evt) @@ -1778,6 +1784,7 @@ void PublishSettingsDialog::apply_selection(const std::vector& publ } apply_visibility(); + refresh_tab_indicators(); } std::vector PublishSettingsDialog::GetPublishedKeys() const @@ -1932,6 +1939,38 @@ std::vector PublishSettingsDialog::GetPublishedM return out; } +bool PublishSettingsDialog::category_has_selection(const Category& cat) const +{ + // A material slot publishes (or not) as a whole, gated by "Enable". + if (cat.section == Section::Material) + return cat.enable_check != nullptr && cat.enable_check->GetValue(); + // Print/Printer categories have no gate: any checked row counts. + for (const size_t r : cat.rows) + if (m_rows[r].check->GetValue()) + return true; + return false; +} + +void PublishSettingsDialog::refresh_tab_indicators() +{ + for (size_t s = 0; s < m_sections.size(); ++s) { + SectionGroup& section = m_sections[s]; + bool any = false; + for (size_t i = 0; i < section.categories.size(); ++i) { + const bool on = category_has_selection(m_categories[section.categories[i]]); + section.tabs->SetItemIndicator(static_cast(i), on); + any = any || on; + } + if (section.mixed_tabs != nullptr) + for (size_t i = 0; i < section.mixed_categories.size(); ++i) { + const bool on = category_has_selection(m_categories[section.mixed_categories[i]]); + section.mixed_tabs->SetItemIndicator(static_cast(i), on); + any = any || on; + } + m_outer_tabs->SetItemIndicator(static_cast(s), any); + } +} + std::vector PublishSettingsDialog::full_keys_for_slot() const { // The canonical filament preset keys minus the structural keys the published overlay must diff --git a/src/slic3r/GUI/PublishSettingsDialog.hpp b/src/slic3r/GUI/PublishSettingsDialog.hpp index 8d9342fadb..f41b3e73a3 100644 --- a/src/slic3r/GUI/PublishSettingsDialog.hpp +++ b/src/slic3r/GUI/PublishSettingsDialog.hpp @@ -213,6 +213,11 @@ private: // "Enable" toggled on a material slot: reveals/hides everything below the header and, for a // mixed slot, auto-selects its component filaments' "Enable" + "Full Publish" toggles. void on_enable_toggle(size_t category_index); + // Whether a category currently publishes something, driving its tab's indicator dot. + // Print/Printer: any row checked. Material: the slot's "Enable" is on. + bool category_has_selection(const Category& cat) const; + // Recompute the indicator dot on every outer/inner tab from the current selection state. + void refresh_tab_indicators(); // Unmet dependencies of enabled mixed-filament slots, one record per (mix, component) pair: // "Enable" not checked on the component, or enabled with neither "Full Publish" nor the // "Type" requirement row checked. Colour is deliberately ignored (the receiver renders the diff --git a/src/slic3r/GUI/Widgets/Button.cpp b/src/slic3r/GUI/Widgets/Button.cpp index 5a5bd89403..22b1c34cab 100644 --- a/src/slic3r/GUI/Widgets/Button.cpp +++ b/src/slic3r/GUI/Widgets/Button.cpp @@ -162,6 +162,15 @@ void Button::SetCenter(bool isCenter) { this->isCenter = isCenter; } +void Button::SetIndicator(bool on) +{ + if (m_show_indicator == on) + return; + m_show_indicator = on; + messureSize(); + Refresh(); +} + void Button::SetVertical(bool vertical) { this->vertical = vertical; @@ -324,6 +333,13 @@ void Button::render(wxDC& dc) szContent.x -= d; } } + if (m_show_indicator) { + const int dot = FromDIP(6); + if (vertical) + szContent.y += dot + FromDIP(6); + else + szContent.x += dot + FromDIP(6); + } // move to center wxRect rcContent = { {0, 0}, size }; if (isCenter) { @@ -364,6 +380,17 @@ void Button::render(wxDC& dc) #endif dc.DrawText(text, pt); } + if (m_show_indicator) { + const int dot = FromDIP(6); // diameter + wxPoint dot_pt; + dot_pt.x = pt.x + (text.IsEmpty() ? 0 : textSize.x) + FromDIP(6) + dot / 2; + // Centre on the content vertically; a bitmap-only (empty-label) tab has no text row. + dot_pt.y = text.IsEmpty() ? rcContent.y + rcContent.height / 2 : pt.y + textSize.y / 2; + const wxColour c = StateColor::darkModeColorFor(m_indicator_color); + dc.SetBrush(wxBrush(c)); + dc.SetPen(wxPen(c)); + dc.DrawCircle(dot_pt, dot / 2); + } } void Button::messureSize() @@ -388,6 +415,14 @@ void Button::messureSize() if (szIcon.y > szContent.y) szContent.y = szIcon.y; } } + if (m_show_indicator) { + // Indicator dot sits to the right of the label: its diameter plus the gap from the text. + const int dot = FromDIP(6); + if (vertical) + szContent.y += dot + FromDIP(6); + else + szContent.x += dot + FromDIP(6); + } wxSize size = szContent + paddingSize * 2; if (minSize.GetHeight() > 0) size.SetHeight(minSize.GetHeight()); diff --git a/src/slic3r/GUI/Widgets/Button.hpp b/src/slic3r/GUI/Widgets/Button.hpp index 19dcd24938..758a9a4009 100644 --- a/src/slic3r/GUI/Widgets/Button.hpp +++ b/src/slic3r/GUI/Widgets/Button.hpp @@ -4,6 +4,7 @@ #include "../wxExtensions.hpp" #include "StaticBox.hpp" #include +#include class ButtonProps { @@ -44,6 +45,8 @@ class Button : public StaticBox bool canFocus = true; bool isCenter = true; bool vertical = false; + bool m_show_indicator = false; + wxColour m_indicator_color = wxColour("#009688"); static const int buttonWidth = 200; static const int buttonHeight = 50; @@ -75,6 +78,10 @@ public: void SetSelected(bool selected = true) { m_selected = selected; } + // Show a small coloured dot to the right of the label (used by TabCtrl tabs to flag that + // the tab's category has a selected/toggled setting). + void SetIndicator(bool on); + // Only meant to be used by inspector, not public API ButtonStyle GetStyle() const { return m_style; } ButtonType GetType() const { return m_type; } diff --git a/src/slic3r/GUI/Widgets/TabCtrl.cpp b/src/slic3r/GUI/Widgets/TabCtrl.cpp index 4090bf9c1b..34de109b8f 100644 --- a/src/slic3r/GUI/Widgets/TabCtrl.cpp +++ b/src/slic3r/GUI/Widgets/TabCtrl.cpp @@ -171,6 +171,14 @@ void TabCtrl::SetItemBitmap(unsigned int item, const wxBitmap& bitmap) relayout(); } +void TabCtrl::SetItemIndicator(unsigned int item, bool on) +{ + if (item >= btns.size()) + return; + btns[item]->SetIndicator(on); + relayout(); +} + bool TabCtrl::GetItemBold(unsigned int item) const { if (item >= btns.size()) diff --git a/src/slic3r/GUI/Widgets/TabCtrl.hpp b/src/slic3r/GUI/Widgets/TabCtrl.hpp index 0d3606aca7..493c4edee5 100644 --- a/src/slic3r/GUI/Widgets/TabCtrl.hpp +++ b/src/slic3r/GUI/Widgets/TabCtrl.hpp @@ -46,6 +46,9 @@ public: void SetItemText(unsigned int item, wxString const& value); void SetItemBitmap(unsigned int item, const wxBitmap& bitmap); + // Show/hide the small "has selection" dot next to a tab's text. + void SetItemIndicator(unsigned int item, bool on); + bool GetItemBold(unsigned int item) const; void SetItemBold(unsigned int item, bool bold);