mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-08-26 03:17:48 +00:00
feat: add max visible pages to app config under Preferences -> General -> Plugins
This commit is contained in:
@@ -280,6 +280,20 @@ void AppConfig::set_defaults()
|
||||
set(SETTING_OPENGL_FPS_CAP, std::to_string(fps_cap));
|
||||
}
|
||||
|
||||
if (get(SETTING_PLUGIN_PAGES_VISIBLE_COUNT).empty())
|
||||
set(SETTING_PLUGIN_PAGES_VISIBLE_COUNT, std::to_string(PLUGIN_PAGES_VISIBLE_COUNT_DEFAULT));
|
||||
else {
|
||||
int visible_count = PLUGIN_PAGES_VISIBLE_COUNT_DEFAULT;
|
||||
try {
|
||||
visible_count = std::stoi(get(SETTING_PLUGIN_PAGES_VISIBLE_COUNT));
|
||||
}
|
||||
catch (...) {
|
||||
visible_count = PLUGIN_PAGES_VISIBLE_COUNT_DEFAULT;
|
||||
}
|
||||
visible_count = std::max(PLUGIN_PAGES_VISIBLE_COUNT_MIN, std::min(visible_count, PLUGIN_PAGES_VISIBLE_COUNT_MAX));
|
||||
set(SETTING_PLUGIN_PAGES_VISIBLE_COUNT, std::to_string(visible_count));
|
||||
}
|
||||
|
||||
if (get(SETTING_OPENGL_SHOW_FPS_OVERLAY).empty())
|
||||
set_bool(SETTING_OPENGL_SHOW_FPS_OVERLAY, false);
|
||||
|
||||
@@ -1630,6 +1644,28 @@ void AppConfig::set_network_plugin_version(const std::string& version)
|
||||
set(SETTING_NETWORK_PLUGIN_VERSION, version);
|
||||
}
|
||||
|
||||
int AppConfig::get_plugin_pages_visible_count() const
|
||||
{
|
||||
std::string value = get(SETTING_PLUGIN_PAGES_VISIBLE_COUNT);
|
||||
if (value.empty())
|
||||
return PLUGIN_PAGES_VISIBLE_COUNT_DEFAULT;
|
||||
|
||||
int visible_count = PLUGIN_PAGES_VISIBLE_COUNT_DEFAULT;
|
||||
try {
|
||||
visible_count = std::stoi(value);
|
||||
}
|
||||
catch (...) {
|
||||
return PLUGIN_PAGES_VISIBLE_COUNT_DEFAULT;
|
||||
}
|
||||
return std::max(PLUGIN_PAGES_VISIBLE_COUNT_MIN, std::min(visible_count, PLUGIN_PAGES_VISIBLE_COUNT_MAX));
|
||||
}
|
||||
|
||||
void AppConfig::set_plugin_pages_visible_count(int count)
|
||||
{
|
||||
count = std::max(PLUGIN_PAGES_VISIBLE_COUNT_MIN, std::min(count, PLUGIN_PAGES_VISIBLE_COUNT_MAX));
|
||||
set(SETTING_PLUGIN_PAGES_VISIBLE_COUNT, std::to_string(count));
|
||||
}
|
||||
|
||||
std::vector<std::string> AppConfig::get_skipped_network_versions() const
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
|
||||
@@ -41,6 +41,11 @@ using namespace nlohmann;
|
||||
#define SETTING_OPENGL_PHONG_SSAO "opengl_phong_ssao"
|
||||
#define SETTING_OPENGL_PHONG_SMOOTH_NORMALS "opengl_phong_smooth_normals"
|
||||
|
||||
#define SETTING_PLUGIN_PAGES_VISIBLE_COUNT "plugin_pages_visible_count"
|
||||
#define PLUGIN_PAGES_VISIBLE_COUNT_MIN 1
|
||||
#define PLUGIN_PAGES_VISIBLE_COUNT_DEFAULT 5
|
||||
#define PLUGIN_PAGES_VISIBLE_COUNT_MAX 10
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#define BAMBU_NETWORK_AGENT_VERSION_LEGACY "01.10.01.09"
|
||||
#else
|
||||
@@ -374,6 +379,11 @@ public:
|
||||
std::string get_network_plugin_version() const;
|
||||
void set_network_plugin_version(const std::string& version);
|
||||
|
||||
// Number of plugin pages shown as fixed tabs before the rest are collapsed into a
|
||||
// dropdown on the last tab.
|
||||
int get_plugin_pages_visible_count() const;
|
||||
void set_plugin_pages_visible_count(int count);
|
||||
|
||||
std::vector<std::string> get_skipped_network_versions() const;
|
||||
void add_skipped_network_version(const std::string& version);
|
||||
bool is_network_version_skipped(const std::string& version) const;
|
||||
|
||||
@@ -1748,11 +1748,26 @@ void PreferencesDialog::create_items()
|
||||
g_sizer->Add(item_pop_up_filament_map_dialog);
|
||||
#endif
|
||||
|
||||
//// GENERAL > Plugins
|
||||
g_sizer->Add(create_item_title(_L("Plugins")), 1, wxEXPAND);
|
||||
|
||||
auto item_plugin_pages_visible_count = create_item_spinctrl(
|
||||
_L("Visible plugin pages"),
|
||||
"",
|
||||
_L("pages"),
|
||||
_L("Number of plugin pages shown as fixed tabs before the remaining pages collapse into a dropdown on the last tab."),
|
||||
SETTING_PLUGIN_PAGES_VISIBLE_COUNT,
|
||||
PLUGIN_PAGES_VISIBLE_COUNT_MIN,
|
||||
PLUGIN_PAGES_VISIBLE_COUNT_MAX,
|
||||
[](int value) { wxGetApp().mainframe->plugin_pages().set_visible_page_count(value); }
|
||||
);
|
||||
g_sizer->Add(item_plugin_pages_visible_count);
|
||||
|
||||
g_sizer->AddSpacer(FromDIP(10));
|
||||
sizer_page->Add(g_sizer, 0, wxEXPAND);
|
||||
|
||||
//////////////////////////
|
||||
//// CONTROL TAB
|
||||
//// CONTROL TAB
|
||||
/////////////////////////////////////
|
||||
m_pref_tabs->AppendItem(_L("Control"));
|
||||
f_sizers.push_back(new wxFlexGridSizer(1, 1, v_gap, 0));
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "PluginPages.hpp"
|
||||
|
||||
#include "libslic3r/AppConfig.hpp"
|
||||
#include "slic3r/GUI/GUI.hpp"
|
||||
#include "slic3r/GUI/Notebook.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
@@ -10,12 +11,15 @@
|
||||
|
||||
#include <libslic3r/Utils.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <wx/bookctrl.h>
|
||||
#include <wx/choice.h>
|
||||
#include <wx/sizer.h>
|
||||
|
||||
#include <utility>
|
||||
@@ -194,9 +198,119 @@ void PluginPage::push_message(const std::string& message)
|
||||
WebView::RunScript(m_browser, script);
|
||||
}
|
||||
|
||||
class PluginPagesOverflowPanel : public wxPanel
|
||||
{
|
||||
public:
|
||||
explicit PluginPagesOverflowPanel(Notebook* parent)
|
||||
: wxPanel(parent, wxID_ANY)
|
||||
, m_notebook(parent)
|
||||
{
|
||||
auto* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
m_choice = new wxChoice(this, wxID_ANY);
|
||||
m_choice->Bind(wxEVT_CHOICE, &PluginPagesOverflowPanel::on_choice, this);
|
||||
sizer->Add(m_choice, wxSizerFlags().Expand().Border(wxALL, FromDIP(4)));
|
||||
|
||||
m_content_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
sizer->Add(m_content_sizer, wxSizerFlags().Expand().Proportion(1));
|
||||
|
||||
SetSizer(sizer);
|
||||
}
|
||||
|
||||
void add_entry(const PluginCapabilityId& id, PluginPage* page, const wxString& title)
|
||||
{
|
||||
page->Reparent(this);
|
||||
page->Hide();
|
||||
m_entries.push_back({id, page, title});
|
||||
m_choice->Append(title);
|
||||
if (m_entries.size() == 1)
|
||||
select_index(0);
|
||||
}
|
||||
|
||||
void select_entry(const PluginCapabilityId& id)
|
||||
{
|
||||
for (size_t i = 0; i < m_entries.size(); ++i) {
|
||||
if (m_entries[i].id == id) {
|
||||
select_index(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
if (m_shown_index != wxNOT_FOUND)
|
||||
m_entries[static_cast<size_t>(m_shown_index)].page->Hide();
|
||||
m_content_sizer->Clear(false);
|
||||
|
||||
for (const Entry& entry : m_entries)
|
||||
entry.page->Reparent(m_notebook);
|
||||
|
||||
m_entries.clear();
|
||||
m_choice->Clear();
|
||||
m_shown_index = wxNOT_FOUND;
|
||||
}
|
||||
|
||||
wxString current_title() const { return m_shown_index == wxNOT_FOUND ? wxString() : m_entries[static_cast<size_t>(m_shown_index)].title; }
|
||||
int current_image_id() const
|
||||
{
|
||||
return m_shown_index == wxNOT_FOUND ? wxBookCtrlBase::NO_IMAGE : m_entries[static_cast<size_t>(m_shown_index)].page->get_icon_image_id();
|
||||
}
|
||||
|
||||
private:
|
||||
struct Entry
|
||||
{
|
||||
PluginCapabilityId id;
|
||||
PluginPage* page;
|
||||
wxString title;
|
||||
};
|
||||
|
||||
void on_choice(wxCommandEvent&)
|
||||
{
|
||||
const int selection = m_choice->GetSelection();
|
||||
if (selection != wxNOT_FOUND)
|
||||
select_index(static_cast<size_t>(selection));
|
||||
}
|
||||
|
||||
void select_index(size_t index)
|
||||
{
|
||||
if (index >= m_entries.size())
|
||||
return;
|
||||
|
||||
if (m_shown_index != wxNOT_FOUND)
|
||||
m_entries[static_cast<size_t>(m_shown_index)].page->Hide();
|
||||
|
||||
m_content_sizer->Clear(false);
|
||||
m_content_sizer->Add(m_entries[index].page, wxSizerFlags().Expand().Proportion(1));
|
||||
m_entries[index].page->Show();
|
||||
Layout();
|
||||
|
||||
m_shown_index = static_cast<int>(index);
|
||||
m_choice->SetSelection(static_cast<int>(index));
|
||||
|
||||
const int tab_index = m_notebook->FindPage(this);
|
||||
if (tab_index != wxNOT_FOUND) {
|
||||
m_notebook->SetPageText(static_cast<size_t>(tab_index), m_entries[index].title);
|
||||
m_notebook->SetPageImage(static_cast<size_t>(tab_index), m_entries[index].page->get_icon_image_id());
|
||||
}
|
||||
}
|
||||
|
||||
Notebook* m_notebook{nullptr};
|
||||
wxChoice* m_choice{nullptr};
|
||||
wxBoxSizer* m_content_sizer{nullptr};
|
||||
std::vector<Entry> m_entries;
|
||||
int m_shown_index{wxNOT_FOUND};
|
||||
};
|
||||
|
||||
PluginPages::~PluginPages()
|
||||
{
|
||||
shutdown();
|
||||
// try {
|
||||
// } catch (const std::exception& error) {
|
||||
// BOOST_LOG_TRIVIAL(error) << "PluginPages::~PluginPages: shutdown() threw: " << error.what();
|
||||
// } catch (...) {
|
||||
// BOOST_LOG_TRIVIAL(error) << "PluginPages::~PluginPages: shutdown() threw a non-standard exception";
|
||||
// }
|
||||
}
|
||||
|
||||
void PluginPages::initialize(Notebook* parent)
|
||||
@@ -206,15 +320,17 @@ void PluginPages::initialize(Notebook* parent)
|
||||
if (m_parent == nullptr)
|
||||
return;
|
||||
|
||||
// Keep image-list indices stable for the lifetime of this notebook. Removing an image
|
||||
// would shift every later index, so deregistration only removes the page.
|
||||
m_visible_page_count = GUI::wxGetApp().app_config->get_plugin_pages_visible_count();
|
||||
m_notebook_base_index = static_cast<size_t>(m_parent->GetPageCount());
|
||||
|
||||
m_image_list = std::make_unique<wxImageList>(20, 20, true, 0);
|
||||
m_parent->SetImageList(m_image_list.get());
|
||||
|
||||
for (const auto& capability : PluginManager::instance().get_plugin_capabilities("", PluginCapabilityType::Pages)) {
|
||||
if (capability)
|
||||
on_cap_register(capability->identity());
|
||||
create_page(capability->identity());
|
||||
}
|
||||
relayout();
|
||||
}
|
||||
|
||||
void PluginPages::shutdown()
|
||||
@@ -225,6 +341,17 @@ void PluginPages::shutdown()
|
||||
m_parent->SetImageList(nullptr);
|
||||
m_image_list.reset();
|
||||
m_parent = nullptr;
|
||||
m_notebook_base_index = 0;
|
||||
}
|
||||
|
||||
void PluginPages::set_visible_page_count(int count)
|
||||
{
|
||||
const int clamped = std::max(PLUGIN_PAGES_VISIBLE_COUNT_MIN, std::min(count, PLUGIN_PAGES_VISIBLE_COUNT_MAX));
|
||||
if (clamped == m_visible_page_count)
|
||||
return;
|
||||
|
||||
m_visible_page_count = clamped;
|
||||
relayout();
|
||||
}
|
||||
|
||||
std::shared_ptr<PagesPluginCapability> PluginPages::get_pages_cap(const PluginCapabilityId& id, bool is_enabled) const
|
||||
@@ -236,14 +363,14 @@ std::shared_ptr<PagesPluginCapability> PluginPages::get_pages_cap(const PluginCa
|
||||
return std::dynamic_pointer_cast<PagesPluginCapability>(capability);
|
||||
}
|
||||
|
||||
void PluginPages::on_cap_register(const PluginCapabilityId& id)
|
||||
bool PluginPages::create_page(const PluginCapabilityId& id)
|
||||
{
|
||||
if (m_parent == nullptr || m_pages.find(id) != m_pages.end())
|
||||
return;
|
||||
if (m_pages.find(id) != m_pages.end())
|
||||
return false;
|
||||
|
||||
auto capability = get_pages_cap(id, true);
|
||||
if (!capability)
|
||||
return;
|
||||
return false;
|
||||
|
||||
std::string icon;
|
||||
try {
|
||||
@@ -257,11 +384,9 @@ void PluginPages::on_cap_register(const PluginCapabilityId& id)
|
||||
auto* page = new PluginPage(m_parent, std::move(capability));
|
||||
if (!page->is_valid()) {
|
||||
page->Destroy();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
const wxString title = wxString::FromUTF8(id.name);
|
||||
|
||||
int image_id = wxBookCtrlBase::NO_IMAGE;
|
||||
if (!icon.empty() && m_image_list) {
|
||||
try {
|
||||
@@ -281,14 +406,18 @@ void PluginPages::on_cap_register(const PluginCapabilityId& id)
|
||||
}
|
||||
|
||||
page->set_icon_image_id(image_id);
|
||||
if (!m_parent->AddPage(page, title, false, image_id)) {
|
||||
if (image_id != wxBookCtrlBase::NO_IMAGE && m_image_list && image_id == m_image_list->GetImageCount() - 1)
|
||||
m_image_list->Remove(image_id);
|
||||
page->Destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
m_pages.emplace(id, page);
|
||||
m_order.push_back(id);
|
||||
return true;
|
||||
}
|
||||
|
||||
void PluginPages::on_cap_register(const PluginCapabilityId& id)
|
||||
{
|
||||
if (m_parent == nullptr)
|
||||
return;
|
||||
|
||||
if (create_page(id))
|
||||
relayout();
|
||||
}
|
||||
|
||||
void PluginPages::on_cap_deregister(const PluginCapabilityId& id)
|
||||
@@ -327,39 +456,87 @@ void PluginPages::remove_page(const PluginCapabilityId& id)
|
||||
PluginPage* page = it->second;
|
||||
const int removed_image_id = page->get_icon_image_id();
|
||||
page->detach_capability();
|
||||
if (m_parent != nullptr) {
|
||||
const int index = m_parent->FindPage(page);
|
||||
if (index != wxNOT_FOUND)
|
||||
m_parent->RemovePage(static_cast<size_t>(index));
|
||||
}
|
||||
|
||||
m_pages.erase(it);
|
||||
m_order.erase(std::remove(m_order.begin(), m_order.end(), id), m_order.end());
|
||||
|
||||
if (m_image_list && removed_image_id != wxBookCtrlBase::NO_IMAGE &&
|
||||
removed_image_id >= 0 && removed_image_id < m_image_list->GetImageCount()) {
|
||||
m_image_list->Remove(removed_image_id);
|
||||
|
||||
// wxImageList IDs are positional. Removing one shifts all later images down by
|
||||
// one, so update both the page state and the notebook button for those pages.
|
||||
for (const auto& [other_id, other_page] : m_pages) {
|
||||
if (other_id == id)
|
||||
continue;
|
||||
|
||||
// wxImageList IDs are positional. Removing one shifts all later images down by one.
|
||||
for (auto& [other_id, other_page] : m_pages) {
|
||||
const int other_image_id = other_page->get_icon_image_id();
|
||||
if (other_image_id <= removed_image_id)
|
||||
continue;
|
||||
|
||||
const int updated_image_id = other_image_id - 1;
|
||||
other_page->set_icon_image_id(updated_image_id);
|
||||
|
||||
if (m_parent != nullptr) {
|
||||
const int other_index = m_parent->FindPage(other_page);
|
||||
if (other_index != wxNOT_FOUND)
|
||||
m_parent->SetPageImage(static_cast<size_t>(other_index), updated_image_id);
|
||||
}
|
||||
if (other_image_id > removed_image_id)
|
||||
other_page->set_icon_image_id(other_image_id - 1);
|
||||
}
|
||||
}
|
||||
|
||||
relayout();
|
||||
page->Destroy();
|
||||
m_pages.erase(it);
|
||||
}
|
||||
|
||||
wxString PluginPages::page_tab_id(const PluginCapabilityId& id)
|
||||
{
|
||||
return wxString::FromUTF8("plugin." + id.plugin_key + "." + id.name);
|
||||
}
|
||||
|
||||
void PluginPages::relayout()
|
||||
{
|
||||
if (m_parent == nullptr)
|
||||
return;
|
||||
|
||||
m_order.erase(std::remove_if(m_order.begin(), m_order.end(),
|
||||
[this](const PluginCapabilityId& id) {
|
||||
const bool orphaned = m_pages.find(id) == m_pages.end();
|
||||
if (orphaned)
|
||||
BOOST_LOG_TRIVIAL(error) << "PluginPages::relayout: '" << id.name << "' was in m_order but not m_pages, dropping";
|
||||
return orphaned;
|
||||
}),
|
||||
m_order.end());
|
||||
|
||||
wxString id_to_reselect = m_parent->GetSelectedPageName();
|
||||
|
||||
while (m_parent->GetPageCount() > m_notebook_base_index)
|
||||
m_parent->RemovePage(m_parent->GetPageCount() - 1);
|
||||
if (m_overflow_panel != nullptr)
|
||||
m_overflow_panel->clear();
|
||||
|
||||
const int visible_slots = std::max(1, m_visible_page_count);
|
||||
const bool need_overflow = static_cast<int>(m_order.size()) > visible_slots;
|
||||
const size_t individual_count = need_overflow ? static_cast<size_t>(visible_slots - 1) : m_order.size();
|
||||
|
||||
for (size_t i = 0; i < individual_count; ++i) {
|
||||
const PluginCapabilityId& id = m_order[i];
|
||||
PluginPage* page = m_pages.at(id);
|
||||
m_parent->InsertPage(m_parent->GetPageCount(), page_tab_id(id), page, wxString::FromUTF8(id.name), page->get_icon_image_id());
|
||||
}
|
||||
|
||||
if (need_overflow) {
|
||||
if (m_overflow_panel == nullptr)
|
||||
m_overflow_panel = new PluginPagesOverflowPanel(m_parent);
|
||||
|
||||
bool reselecting_overflow_entry = false;
|
||||
for (size_t i = individual_count; i < m_order.size(); ++i) {
|
||||
const PluginCapabilityId& id = m_order[i];
|
||||
m_overflow_panel->add_entry(id, m_pages.at(id), wxString::FromUTF8(id.name));
|
||||
if (page_tab_id(id) == id_to_reselect) {
|
||||
m_overflow_panel->select_entry(id);
|
||||
reselecting_overflow_entry = true;
|
||||
}
|
||||
}
|
||||
if (reselecting_overflow_entry)
|
||||
id_to_reselect = "plugin.__overflow__";
|
||||
|
||||
m_parent->InsertPage(m_parent->GetPageCount(), "plugin.__overflow__", m_overflow_panel,
|
||||
m_overflow_panel->current_title(), m_overflow_panel->current_image_id());
|
||||
} else if (m_overflow_panel != nullptr) {
|
||||
m_overflow_panel->Destroy();
|
||||
m_overflow_panel = nullptr;
|
||||
}
|
||||
|
||||
if (!id_to_reselect.empty())
|
||||
m_parent->SelectPageByName(id_to_reselect);
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <wx/bookctrl.h>
|
||||
#include <wx/imaglist.h>
|
||||
@@ -47,6 +48,8 @@ private:
|
||||
int m_icon_image_id = wxBookCtrlBase::NO_IMAGE;
|
||||
};
|
||||
|
||||
class PluginPagesOverflowPanel;
|
||||
|
||||
class PluginPages
|
||||
{
|
||||
public:
|
||||
@@ -64,14 +67,26 @@ public:
|
||||
void on_plugin_register(const std::string& plugin_key);
|
||||
void on_plugin_deregister(const std::string& plugin_key);
|
||||
|
||||
int get_visible_page_count() const { return m_visible_page_count; }
|
||||
void set_visible_page_count(int count);
|
||||
|
||||
private:
|
||||
std::shared_ptr<PagesPluginCapability> get_pages_cap(const PluginCapabilityId& id, bool is_enabled) const;
|
||||
bool create_page(const PluginCapabilityId& id);
|
||||
void remove_page(const PluginCapabilityId& id);
|
||||
|
||||
void relayout();
|
||||
static wxString page_tab_id(const PluginCapabilityId& id);
|
||||
|
||||
std::map<PluginCapabilityId, PluginPage*> m_pages;
|
||||
std::vector<PluginCapabilityId> m_order;
|
||||
Notebook* m_parent{nullptr};
|
||||
size_t m_notebook_base_index{0};
|
||||
|
||||
std::unique_ptr<wxImageList> m_image_list;
|
||||
int m_visible_page_count{0};
|
||||
|
||||
PluginPagesOverflowPanel* m_overflow_panel{nullptr};
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
Reference in New Issue
Block a user