text_list;
- text_list.push_back(_L("OrcaSlicer is based on BambuStudio, PrusaSlicer, and SuperSlicer."));
- text_list.push_back(_L("BambuStudio is originally based on PrusaSlicer by PrusaResearch."));
- text_list.push_back(_L("PrusaSlicer is originally based on Slic3r by Alessandro Ranellucci."));
- text_list.push_back(_L("Slic3r was created by Alessandro Ranellucci with the help of many other contributors."));
+ text_list.push_back(_L("Open-source slicing stands on a tradition of collaboration and attribution. Slic3r, created by Alessandro Ranellucci and the RepRap community, laid the foundation. PrusaSlicer by Prusa Research built on that work, Bambu Studio forked from PrusaSlicer, and SuperSlicer extended it with community-driven enhancements. Each project carried the work of its predecessors forward, crediting those who came before."));
+ text_list.push_back(_L("OrcaSlicer began in that same spirit, drawing from PrusaSlicer, BambuStudio, SuperSlicer, and CuraSlicer. But it has since grown far beyond its origins — introducing advanced calibration tools, precise wall and seam control and hundreds of other features."));
+ text_list.push_back(_L("Today, OrcaSlicer is the most widely used and actively developed open-source slicer in the 3D printing community. Many of its innovations have been adopted by other slicers, making it a driving force for the entire industry."));
text_sizer->Add( 0, 0, 0, wxTOP, FromDIP(33));
bool is_zh = wxGetApp().app_config->get("language") == "zh_CN";
@@ -316,7 +315,7 @@ AboutDialog::AboutDialog()
copyright_hor_sizer->Add(copyright_ver_sizer, 0, wxLEFT, FromDIP(20));
- wxStaticText *html_text = new wxStaticText(this, wxID_ANY, "Copyright(C) 2022-2025 Li Jiang All Rights Reserved", wxDefaultPosition, wxDefaultSize);
+ wxStaticText *html_text = new wxStaticText(this, wxID_ANY, "Copyright(C) 2026 OrcaSlicer Pte Ltd All Rights Reserved", wxDefaultPosition, wxDefaultSize);
html_text->SetForegroundColour(wxColour(107, 107, 107));
copyright_ver_sizer->Add(html_text, 0, wxALL , 0);
@@ -333,7 +332,7 @@ AboutDialog::AboutDialog()
(boost::format(
""
""
- "https://github.com/OrcaSlicer/OrcaSlicer a>
"
+ "https://www.orcaslicer.com a>
"
""
"")
).str());
diff --git a/src/slic3r/GUI/BBLTopbar.cpp b/src/slic3r/GUI/BBLTopbar.cpp
index 4ac9a7aa29..65fcb2958c 100644
--- a/src/slic3r/GUI/BBLTopbar.cpp
+++ b/src/slic3r/GUI/BBLTopbar.cpp
@@ -36,42 +36,67 @@ enum CUSTOM_ID
ID_AMS_NOTEBOOK,
};
+CenteredTitle::CenteredTitle(wxWindow* parent)
+ : wxControl()
+{
+ SetBackgroundStyle(wxBG_STYLE_PAINT);
+ Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE);
+ Bind(wxEVT_PAINT, [this](wxPaintEvent&) {
+ wxBufferedPaintDC dc(this);
+ dc.SetBackground(wxBrush(wxColour(38, 46, 48)));
+ dc.Clear();
+
+ dc.SetTextForeground(*wxWHITE);
+
+ wxFontMetrics fm = dc.GetFontMetrics();
+ int textHeight = fm.ascent + fm.descent;
+
+ wxRect rect = GetClientRect();
+ wxString ellipsized = wxControl::Ellipsize(m_title, dc, wxELLIPSIZE_END, wxMax(0, rect.GetWidth() - FromDIP(8)));
+
+ int y = rect.y + (rect.height - textHeight) / 2;
+ int x = rect.x + (ellipsized != m_title) // is ellipsized
+ ? FromDIP(4) // align to left when clipped
+ : (rect.width - dc.GetTextExtent(m_title).GetWidth()) / 2; // centered when has available space
+
+ dc.DrawText(ellipsized, x, y);
+ });
+
+ // repaint for Ellipsize
+ Bind(wxEVT_SIZE, [this](wxSizeEvent& e) {
+ Refresh();
+ e.Skip();
+ });
+
+ auto forwardMouseEvent = [this](wxMouseEvent& e) {
+ if (e.LeftDown() && e.GetClickCount() > 1) return; // prevent duplicate event
+ e.SetPosition(GetParent()->ScreenToClient(ClientToScreen(e.GetPosition())));
+ GetParent()->GetEventHandler()->ProcessEvent(e);
+ };
+
+ Bind(wxEVT_LEFT_DOWN, forwardMouseEvent);
+ Bind(wxEVT_LEFT_DCLICK, forwardMouseEvent);
+}
+
+void CenteredTitle::SetTitle(const wxString& title) {
+ m_title = title;
+ Refresh();
+}
+
+// required for proper height
+wxSize CenteredTitle::DoGetBestSize() const
+{
+ return wxSize(10, FromDIP(30));
+}
+
+
class BBLTopbarArt : public wxAuiDefaultToolBarArt
{
public:
- virtual void DrawLabel(wxDC& dc, wxWindow* wnd, const wxAuiToolBarItem& item, const wxRect& rect) wxOVERRIDE;
virtual void DrawBackground(wxDC& dc, wxWindow* wnd, const wxRect& rect) wxOVERRIDE;
virtual void DrawButton(wxDC& dc, wxWindow* wnd, const wxAuiToolBarItem& item, const wxRect& rect) wxOVERRIDE;
};
-void BBLTopbarArt::DrawLabel(wxDC& dc, wxWindow* wnd, const wxAuiToolBarItem& item, const wxRect& rect)
-{
- dc.SetFont(m_font);
-#ifdef __WINDOWS__
- dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
-#else
- dc.SetTextForeground(*wxWHITE);
-#endif
-
- int textWidth = 0, textHeight = 0;
- dc.GetTextExtent(item.GetLabel(), &textWidth, &textHeight);
-
- wxRect clipRect = rect;
- clipRect.width -= 1;
- dc.SetClippingRegion(clipRect);
-
- int textX, textY;
- if (textWidth < rect.GetWidth()) {
- textX = rect.x + 1 + (rect.width - textWidth) / 2;
- }
- else {
- textX = rect.x + 1;
- }
- textY = rect.y + (rect.height - textHeight) / 2;
- dc.DrawText(item.GetLabel(), textX, textY);
- dc.DestroyClippingRegion();
-}
-
void BBLTopbarArt::DrawBackground(wxDC& dc, wxWindow* wnd, const wxRect& rect)
{
dc.SetBrush(wxBrush(wxColour(38, 46, 48)));
@@ -257,21 +282,22 @@ void BBLTopbar::Init(wxFrame* parent)
m_calib_item = this->AddTool(ID_CALIB, _L("Calibration"), calib_bitmap);
m_calib_item->SetDisabledBitmap(calib_bitmap_inactive);
- this->AddSpacer(FromDIP(10));
- this->AddStretchSpacer(1);
+ this->AddSpacer(FromDIP(25));
+ //this->AddStretchSpacer(1);
- m_title_item = this->AddLabel(ID_TITLE, "", FromDIP(TOPBAR_TITLE_WIDTH));
- m_title_item->SetAlignment(wxALIGN_CENTRE);
+ m_title_ctrl = new CenteredTitle(this);
+ wxAuiToolBarItem* title_item = this->AddControl(m_title_ctrl, "");
+ title_item->SetProportion(1);
- this->AddSpacer(FromDIP(10));
- this->AddStretchSpacer(1);
+ this->AddSpacer(FromDIP(25));
+ //this->AddStretchSpacer(1);
- m_publish_bitmap = create_scaled_bitmap("topbar_publish", nullptr, TOPBAR_ICON_SIZE);
- m_publish_item = this->AddTool(ID_PUBLISH, "", m_publish_bitmap);
- m_publish_disable_bitmap = create_scaled_bitmap("topbar_publish_disable", nullptr, TOPBAR_ICON_SIZE);
- m_publish_item->SetDisabledBitmap(m_publish_disable_bitmap);
- this->EnableTool(m_publish_item->GetId(), false);
- this->AddSpacer(FromDIP(4));
+ //m_publish_bitmap = create_scaled_bitmap("topbar_publish", nullptr, TOPBAR_ICON_SIZE);
+ //m_publish_item = this->AddTool(ID_PUBLISH, "", m_publish_bitmap);
+ //m_publish_disable_bitmap = create_scaled_bitmap("topbar_publish_disable", nullptr, TOPBAR_ICON_SIZE);
+ //m_publish_item->SetDisabledBitmap(m_publish_disable_bitmap);
+ //this->EnableTool(m_publish_item->GetId(), false);
+ //this->AddSpacer(FromDIP(4));
/*wxBitmap model_store_bitmap = create_scaled_bitmap("topbar_store", nullptr, TOPBAR_ICON_SIZE);
m_model_store_item = this->AddTool(ID_MODEL_STORE, "", model_store_bitmap);
@@ -279,7 +305,7 @@ void BBLTopbar::Init(wxFrame* parent)
*/
//this->AddSeparator();
- this->AddSpacer(FromDIP(4));
+ //this->AddSpacer(FromDIP(4));
wxBitmap iconize_bitmap = create_scaled_bitmap("topbar_min", nullptr, TOPBAR_ICON_SIZE);
wxAuiToolBarItem* iconize_btn = this->AddTool(wxID_ICONIZE_FRAME, "", iconize_bitmap);
@@ -324,7 +350,7 @@ void BBLTopbar::Init(wxFrame* parent)
this->Bind(wxEVT_AUITOOLBAR_TOOL_DROPDOWN, &BBLTopbar::OnRedo, this, wxID_REDO);
this->Bind(wxEVT_AUITOOLBAR_TOOL_DROPDOWN, &BBLTopbar::OnUndo, this, wxID_UNDO);
//this->Bind(wxEVT_AUITOOLBAR_TOOL_DROPDOWN, &BBLTopbar::OnModelStoreClicked, this, ID_MODEL_STORE);
- this->Bind(wxEVT_AUITOOLBAR_TOOL_DROPDOWN, &BBLTopbar::OnPublishClicked, this, ID_PUBLISH);
+ //this->Bind(wxEVT_AUITOOLBAR_TOOL_DROPDOWN, &BBLTopbar::OnPublishClicked, this, ID_PUBLISH);
}
BBLTopbar::~BBLTopbar()
@@ -341,11 +367,11 @@ void BBLTopbar::OnOpenProject(wxAuiToolBarEvent& event)
plater->load_project();
}
-void BBLTopbar::show_publish_button(bool show)
-{
- this->EnableTool(m_publish_item->GetId(), show);
- Refresh();
-}
+//void BBLTopbar::show_publish_button(bool show)
+//{
+// this->EnableTool(m_publish_item->GetId(), show);
+// Refresh();
+//}
void BBLTopbar::OnSaveProject(wxAuiToolBarEvent& event)
{
@@ -446,12 +472,9 @@ wxMenu* BBLTopbar::GetCalibMenu()
void BBLTopbar::SetTitle(wxString title)
{
- wxGCDC dc(this);
- title = wxControl::Ellipsize(title, dc, wxELLIPSIZE_END, FromDIP(TOPBAR_TITLE_WIDTH));
-
- m_title_item->SetLabel(title);
- m_title_item->SetAlignment(wxALIGN_CENTRE);
- this->Refresh();
+ m_titleText = title;
+ if (m_title_ctrl)
+ m_title_ctrl->SetTitle(title);
}
void BBLTopbar::SetMaximizedSize()
@@ -500,7 +523,8 @@ void BBLTopbar::Rescale() {
item->SetBitmap(create_scaled_bitmap("calib_sf", this, TOPBAR_ICON_SIZE));
item->SetDisabledBitmap(create_scaled_bitmap("calib_sf_inactive", this, TOPBAR_ICON_SIZE));
- item = this->FindTool(ID_TITLE);
+ if (m_title_ctrl)
+ m_title_ctrl->SetTitle(m_titleText);
/*item = this->FindTool(ID_PUBLISH);
item->SetBitmap(create_scaled_bitmap("topbar_publish", this, TOPBAR_ICON_SIZE));
@@ -565,9 +589,9 @@ void BBLTopbar::OnCloseFrame(wxAuiToolBarEvent& event)
void BBLTopbar::OnMouseLeftDClock(wxMouseEvent& mouse)
{
wxPoint mouse_pos = ::wxGetMousePosition();
+ wxAuiToolBarItem* item = this->FindToolByCurrentPosition();
// check whether mouse is not on any tool item
- if (this->FindToolByCurrentPosition() != NULL &&
- this->FindToolByCurrentPosition() != m_title_item) {
+ if (item != NULL && item->GetWindow() != m_title_ctrl) {
mouse.Skip();
return;
}
@@ -635,10 +659,10 @@ void BBLTopbar::OnMouseLeftDown(wxMouseEvent& event)
{
wxPoint mouse_pos = ::wxGetMousePosition();
wxPoint frame_pos = m_frame->GetScreenPosition();
+ wxAuiToolBarItem* item = this->FindToolByCurrentPosition();
m_delta = mouse_pos - frame_pos;
- if (FindToolByCurrentPosition() == NULL
- || this->FindToolByCurrentPosition() == m_title_item)
+ if (item == NULL || item->GetWindow() == m_title_ctrl)
{
#ifdef __WXMSW__
CaptureMouse();
@@ -728,8 +752,8 @@ WXLRESULT BBLTopbar::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam
{
switch (nMsg) {
case WM_NCHITTEST: {
- const wxAuiToolBarItem* current_item = this->FindToolByCurrentPosition();
- if (current_item != nullptr && current_item != m_title_item) {
+ wxAuiToolBarItem* item = this->FindToolByCurrentPosition();
+ if (item != NULL && item->GetWindow() != m_title_ctrl) {
break;
}
diff --git a/src/slic3r/GUI/BBLTopbar.hpp b/src/slic3r/GUI/BBLTopbar.hpp
index b5f4673a44..8fc77e774e 100644
--- a/src/slic3r/GUI/BBLTopbar.hpp
+++ b/src/slic3r/GUI/BBLTopbar.hpp
@@ -6,9 +6,22 @@
#include "SelectMachine.hpp"
#include "DeviceManager.hpp"
+#include
using namespace Slic3r::GUI;
+class CenteredTitle : public wxControl
+{
+public:
+ CenteredTitle(wxWindow* parent);
+ void SetTitle(const wxString& title);
+
+ wxSize DoGetBestSize() const override;
+
+private:
+ wxString m_title;
+};
+
class BBLTopbar : public wxAuiToolBar
{
public:
@@ -31,7 +44,7 @@ public:
void OnMouseCaptureLost(wxMouseCaptureLostEvent& event);
void OnMenuClose(wxMenuEvent& event);
void OnOpenProject(wxAuiToolBarEvent& event);
- void show_publish_button(bool show);
+ //void show_publish_button(bool show);
void OnSaveProject(wxAuiToolBarEvent& event);
void OnUndo(wxAuiToolBarEvent& event);
void OnRedo(wxAuiToolBarEvent& event);
@@ -70,11 +83,14 @@ private:
wxMenu m_top_menu;
wxMenu* m_file_menu;
wxMenu m_calib_menu;
- wxAuiToolBarItem* m_title_item;
+
+ CenteredTitle* m_title_ctrl { nullptr };
+ wxString m_titleText;
+
wxAuiToolBarItem* m_account_item;
wxAuiToolBarItem* m_model_store_item;
- wxAuiToolBarItem *m_publish_item;
+ //wxAuiToolBarItem *m_publish_item;
wxAuiToolBarItem* m_undo_item;
wxAuiToolBarItem* m_redo_item;
wxAuiToolBarItem* m_calib_item;
diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp
index 919aeaf4b5..c1624f4a4e 100644
--- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp
+++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp
@@ -681,7 +681,6 @@ StringObjectException BackgroundSlicingProcess::validate(StringObjectException *
assert(m_print == m_fff_print);
m_fff_print->is_BBL_printer() = wxGetApp().preset_bundle->is_bbl_vendor();
- m_fff_print->is_QIDI_printer() = wxGetApp().preset_bundle->is_qidi_vendor();
return m_print->validate(warning, collison_polygons, height_polygons);
}
diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp
index 2ff580717f..f3f98a6fad 100644
--- a/src/slic3r/GUI/ConfigManipulation.cpp
+++ b/src/slic3r/GUI/ConfigManipulation.cpp
@@ -807,6 +807,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
toggle_field("single_extruder_multi_material", !is_BBL_Printer);
auto bSEMM = preset_bundle->printers.get_edited_preset().config.opt_bool("single_extruder_multi_material");
+ const bool supports_wipe_tower_2 = !is_BBL_Printer && preset_bundle->printers.get_edited_preset().config.opt_enum("wipe_tower_type") == WipeTowerType::Type2;
toggle_field("ooze_prevention", !bSEMM);
bool have_ooze_prevention = config->opt_bool("ooze_prevention");
@@ -831,17 +832,17 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
"wipe_tower_extra_spacing", "wipe_tower_max_purge_speed",
"wipe_tower_bridging", "wipe_tower_extra_flow",
"wipe_tower_no_sparse_layers"})
- toggle_line(el, have_prime_tower && !is_BBL_Printer);
+ toggle_line(el, have_prime_tower && supports_wipe_tower_2);
WipeTowerWallType wipe_tower_wall_type = config->opt_enum("wipe_tower_wall_type");
bool have_rib_wall = (wipe_tower_wall_type == WipeTowerWallType::wtwRib)&&have_prime_tower;
- toggle_line("wipe_tower_cone_angle", have_prime_tower && !is_BBL_Printer && wipe_tower_wall_type == WipeTowerWallType::wtwCone);
+ toggle_line("wipe_tower_cone_angle", have_prime_tower && supports_wipe_tower_2 && wipe_tower_wall_type == WipeTowerWallType::wtwCone);
toggle_line("wipe_tower_extra_rib_length", have_rib_wall);
toggle_line("wipe_tower_rib_width", have_rib_wall);
toggle_line("wipe_tower_fillet_wall", have_rib_wall);
- toggle_field("prime_tower_width", have_prime_tower && !(is_BBL_Printer && have_rib_wall));
+ toggle_field("prime_tower_width", have_prime_tower && (supports_wipe_tower_2 || have_rib_wall));
- toggle_line("single_extruder_multi_material_priming", !bSEMM && have_prime_tower && !is_BBL_Printer);
+ toggle_line("single_extruder_multi_material_priming", !bSEMM && have_prime_tower && supports_wipe_tower_2);
toggle_line("prime_volume",have_prime_tower && (!purge_in_primetower || !bSEMM));
diff --git a/src/slic3r/GUI/CreatePresetsDialog.cpp b/src/slic3r/GUI/CreatePresetsDialog.cpp
index b34b5df3ac..9a42319bf1 100644
--- a/src/slic3r/GUI/CreatePresetsDialog.cpp
+++ b/src/slic3r/GUI/CreatePresetsDialog.cpp
@@ -103,7 +103,7 @@ static const std::unordered_map> printer_m
"Creality Hi"}},
{"DeltaMaker", {"DeltaMaker 2", "DeltaMaker 2T", "DeltaMaker 2XT"}},
{"Dremel", {"Dremel 3D20", "Dremel 3D40", "Dremel 3D45"}},
- {"Elegoo", {"Elegoo Centauri Carbon", "Elegoo Centauri", "Elegoo Neptune", "Elegoo Neptune X", "Elegoo Neptune 2",
+ {"Elegoo", {"Elegoo Centauri Carbon 2", "Elegoo Centauri Carbon", "Elegoo Centauri", "Elegoo Neptune", "Elegoo Neptune X", "Elegoo Neptune 2",
"Elegoo Neptune 2S", "Elegoo Neptune 2D", "Elegoo Neptune 3", "Elegoo Neptune 3 Pro", "Elegoo Neptune 3 Plus",
"Elegoo Neptune 3 Max", "Elegoo Neptune 4 Pro", "Elegoo Neptune 4", "Elegoo Neptune 4 Max", "Elegoo Neptune 4 Plus",
"Elegoo OrangeStorm Giga"}},
diff --git a/src/slic3r/GUI/DeviceTab/CMakeLists.txt b/src/slic3r/GUI/DeviceTab/CMakeLists.txt
index 2d62d59a0d..40f192ef4e 100644
--- a/src/slic3r/GUI/DeviceTab/CMakeLists.txt
+++ b/src/slic3r/GUI/DeviceTab/CMakeLists.txt
@@ -1,7 +1,3 @@
-# GUI/DeviceTab
-# usage -- GUI about device tab for BambuStudio
-# date -- 2025.01.01
-# status -- Building
list(APPEND SLIC3R_GUI_SOURCES
GUI/DeviceTab/uiAmsHumidityPopup.h
diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp
index 4da5f8b092..93db4c32d2 100644
--- a/src/slic3r/GUI/Field.cpp
+++ b/src/slic3r/GUI/Field.cpp
@@ -101,43 +101,79 @@ ThumbnailErrors validate_thumbnails_string(wxString& str, const wxString& def_ex
return errors;
}
+// Orca
wxString get_formatted_tooltip_text(const ConfigOptionDef& opt, const t_config_option_key& id)
{
wxString tooltip = _(opt.tooltip);
- if (tooltip.length() > 0) {
- edit_tooltip(tooltip);
+ std::string opt_id = id;
+ auto hash_pos = opt_id.find("#");
+ if (hash_pos != std::string::npos) {
+ opt_id.replace(hash_pos, 1,"[");
+ opt_id += "]";
+ }
- std::string opt_id = id;
- auto hash_pos = opt_id.find("#");
- if (hash_pos != std::string::npos) {
- opt_id.replace(hash_pos, 1,"[");
- opt_id += "]";
- }
+ tooltip += (tooltip.empty() ? "" : "\n\n") + _(L("parameter name")) + ": " + opt_id;
- tooltip += "\n\n" + _(L("parameter name")) + ": " + opt_id;
+ // Orca:
+ // We can't use Orca's default values as-is because they sometimes depend on other values.
+ // Parent preset configuration values will be used instead.
+ if (const Preset* print_parent_preset = wxGetApp().preset_bundle->prints.get_selected_preset_parent()) {
+ const DynamicPrintConfig& parent_config = print_parent_preset->config;
- if (opt.type == coFloat || opt.type == coInt) {
+ if (!parent_config.has(opt_id))
+ return tooltip;
+
+ wxString side_text = from_u8(opt.sidetext);
+
+ // Orca: a small hack for `layers` side text: adding a space before it for better looking text
+ if (opt.sidetext == L("layers"))
+ side_text = " " + _(side_text);
+
+ if (opt.type == coFloat || opt.type == coInt || opt.type == coPercent || opt.type == coFloatOrPercent) {
double default_value = 0.;
if (opt.type == coFloat)
- default_value = opt.get_default_value()->value;
+ default_value = parent_config.option(opt_id)->value;
else if (opt.type == coInt)
- default_value = opt.get_default_value()->value;
+ default_value = parent_config.option(opt_id)->value;
+ else if (opt.type == coPercent)
+ default_value = parent_config.option(opt_id)->value;
+ else if (opt.type == coFloatOrPercent) {
+ default_value = parent_config.option(opt_id)->value;
+ if (parent_config.option(opt_id)->percent)
+ side_text = "%";
+ else if (!side_text.empty()) {
+ static std::string postfix = " or %";
+ auto postfix_pos = side_text.find(postfix);
+ if (postfix_pos != std::string::npos)
+ side_text.erase(postfix_pos, postfix.length());
+ }
+ }
- tooltip += "\n\n" + _(L("Default")) + ": " + _(double_to_string(default_value));
+ tooltip += "\n\n" + _(L("Default")) + ": " + _(double_to_string(default_value)) + _(side_text);
if (opt.min > -FLT_MAX && opt.max < FLT_MAX) {
tooltip += "\n" + _(L("Range")) + ": [" +
- _(double_to_string(opt.min)) + ", " +
- _(double_to_string(opt.max)) + "]";
+ _(double_to_string(opt.min)) + _(side_text) + ", " +
+ _(double_to_string(opt.max)) + _(side_text) + "]";
}
- }
+ } else if (opt.type == coBool || opt.type == coString) {
+ std::string default_value = "";
- return tooltip;
+ if (opt.type == coString)
+ default_value = parent_config.option(opt_id)->value;
+ else if (opt.type == coBool)
+ default_value = parent_config.option(opt_id)->value ? "true" : "false";
+
+ tooltip += "\n\n" + _(L("Default")) + ": " +
+ (default_value.empty() ? _(L("Empty string")) : _(default_value) + _(side_text));
+ }
}
- return "";
+ edit_tooltip(tooltip);
+
+ return tooltip;
}
Field::~Field()
diff --git a/src/slic3r/GUI/FilamentMapDialog.cpp b/src/slic3r/GUI/FilamentMapDialog.cpp
index c1cb16c545..c0120c2c80 100644
--- a/src/slic3r/GUI/FilamentMapDialog.cpp
+++ b/src/slic3r/GUI/FilamentMapDialog.cpp
@@ -207,6 +207,17 @@ FilamentMapDialog::FilamentMapDialog(wxWindow *parent,
m_ok_btn->Bind(wxEVT_BUTTON, &FilamentMapDialog::on_ok, this);
m_cancel_btn->Bind(wxEVT_BUTTON, &FilamentMapDialog::on_cancle, this);
+ SetEscapeId(wxID_CANCEL);
+ Bind(wxEVT_CHAR_HOOK, [this](wxKeyEvent& e) {
+ if (e.GetKeyCode() == WXK_ESCAPE) {
+ if (IsModal())
+ EndModal(wxID_CANCEL);
+ else
+ Close();
+ return;
+ }
+ e.Skip();
+ });
m_auto_btn->Bind(wxEVT_BUTTON, &FilamentMapDialog::on_switch_mode, this);
m_manual_btn->Bind(wxEVT_BUTTON, &FilamentMapDialog::on_switch_mode, this);
diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp
index 443757a117..fe47b581c3 100644
--- a/src/slic3r/GUI/GCodeViewer.cpp
+++ b/src/slic3r/GUI/GCodeViewer.cpp
@@ -1629,7 +1629,29 @@ void GCodeViewer::update_sequential_view_current(unsigned int first, unsigned in
levels.push_back(std::make_pair(value, libvgcode::convert(color_range.get_color_at(value))));
levels.back().second.a(0.5f);
}
- m_sequential_view.marker.set_actual_speed_data(actual_speed_data);
+
+ // ORCA Compress consecutive duplicate speeds with 0.1 precision
+ auto sameSpeed = [](float a, float b) {
+ return static_cast(std::roundf(a * 10.0f)) == static_cast(std::roundf(b * 10.0f));
+ };
+ std::vector compressed;
+ if (!actual_speed_data.empty()) {
+ compressed.push_back(actual_speed_data[0]);
+ for (int i = 1; i < (int)actual_speed_data.size(); ++i) {
+ const bool same_as_prev = sameSpeed(actual_speed_data[i].speed, actual_speed_data[i - 1].speed);
+ const bool same_as_next = (i + 1 < (int)actual_speed_data.size()) && sameSpeed(actual_speed_data[i].speed, actual_speed_data[i + 1].speed);
+ if (!same_as_prev) {
+ if (!sameSpeed(compressed.back().speed, actual_speed_data[i - 1].speed))
+ compressed.push_back(actual_speed_data[i - 1]);
+ compressed.push_back(actual_speed_data[i]);
+ } else if (!same_as_next)
+ compressed.push_back(actual_speed_data[i]);
+ }
+ if (compressed.back().pos != actual_speed_data.back().pos)
+ compressed.push_back(actual_speed_data.back());
+ }
+
+ m_sequential_view.marker.set_actual_speed_data(compressed);
m_sequential_view.marker.set_actual_speed_y_range(std::make_pair(interval[0], interval[1]));
m_sequential_view.marker.set_actual_speed_levels(levels);
}
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index ee7fedea11..b9becfa63c 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -38,7 +38,6 @@
#include "slic3r/Utils/UndoRedo.hpp"
#include "slic3r/Utils/MacDarkMode.hpp"
-#include
#include
#if ENABLE_RETINA_GL
@@ -2837,7 +2836,6 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
float y = dynamic_cast(proj_cfg.option("wipe_tower_y"))->get_at(plate_id);
float w = dynamic_cast(m_config->option("prime_tower_width"))->value;
float a = dynamic_cast(proj_cfg.option("wipe_tower_rotation_angle"))->value;
-
// BBS
float v = dynamic_cast(m_config->option("prime_volume"))->value;
Vec3d plate_origin = ppl.get_plate(plate_id)->get_origin();
@@ -2864,29 +2862,9 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
}
coordf_t plate_bbox_x_min_local_coord = plate_bbox_2d.min(0) - plate_origin(0);
- coordf_t plate_bbox_y_min_local_coord = plate_bbox_2d.min(1) - plate_origin(1);
coordf_t plate_bbox_x_max_local_coord = plate_bbox_2d.max(0) - plate_origin(0);
coordf_t plate_bbox_y_max_local_coord = plate_bbox_2d.max(1) - plate_origin(1);
- const float tower_w = (float) wipe_tower_size(0);
- const float tower_h = (float) wipe_tower_size(1);
- const float min_x = (float) plate_bbox_x_min_local_coord + margin;
- const float max_x = (float) plate_bbox_x_max_local_coord - margin;
- const float min_y = (float) plate_bbox_y_min_local_coord + margin;
- const float max_y = (float) plate_bbox_y_max_local_coord - margin;
-
- // snap wipe tower back to nearest edge if it was initially loaded outside the plate boundary
- float new_x = (x < min_x) ? min_x : ((x + tower_w > max_x) ? (max_x - tower_w) : x);
- float new_y = (y < min_y) ? min_y : ((y + tower_h > max_y) ? (max_y - tower_h) : y);
-
- if (new_x != x || new_y != y) {
- // do notification
- _set_warning_notification(EWarning::PreviewPrimeTowerOutside, true);
- x = new_x;
- y = new_y;
- }
-
-
if (!current_print->is_step_done(psWipeTower) || !current_print->wipe_tower_data().wipe_tower_mesh_data) {
// update for wipe tower position
{
@@ -4277,15 +4255,16 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_canvas->SetFocus();
if (evt.Entering()) {
-//#if defined(__WXMSW__) || defined(__linux__)
-// // On Windows and Linux needs focus in order to catch key events
- // Set focus in order to remove it from sidebar fields
+ // Set focus in order to remove it from sidebar fields and ensure hotkeys work
if (m_canvas != nullptr) {
- // Only set focus, if the top level window of this canvas is active.
+ // Only set focus if the top level window of this canvas is active.
auto p = dynamic_cast(evt.GetEventObject());
while (p->GetParent())
p = p->GetParent();
auto *top_level_wnd = dynamic_cast(p);
+ //Orca: Set focus so hotkeys like 'tab' work when a notification is shown.
+ if (top_level_wnd != nullptr && top_level_wnd->IsActive())
+ m_canvas->SetFocus();
m_mouse.position = pos.cast();
m_tooltip_enabled = false;
// 1) forces a frame render to ensure that m_hover_volume_idxs is updated even when the user right clicks while
@@ -4297,7 +4276,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_tooltip_enabled = true;
}
m_mouse.set_start_position_2D_as_invalid();
-//#endif
}
else if (evt.Leaving()) {
// to remove hover on objects when the mouse goes out of this canvas
@@ -8545,9 +8523,11 @@ void GLCanvas3D::_render_canvas_toolbar()
zoom_to_selection();
}
} else if (ImGui::IsItemHovered()) {
- auto tooltip = _L("Fit camera to scene or selected object.");
- auto width = ImGui::CalcTextSize(tooltip.c_str()).x + imgui.scaled(2.0f);
- imgui.tooltip(tooltip, width);
+ auto tooltip_str_wx = _L("Fit camera to scene or selected object.");
+ std::string tooltip_str = tooltip_str_wx.ToUTF8().data();
+
+ float width = ImGui::CalcTextSize(tooltip_str.c_str()).x + imgui.scaled(2.0f);
+ imgui.tooltip(tooltip_str, width);
}
}
@@ -9663,9 +9643,6 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state)
case EWarning::PrimeTowerOutside:
text = _u8L("The prime tower extends beyond the plate boundary.");
break;
- case EWarning::PreviewPrimeTowerOutside:
- text = _u8L("Prime tower position exceeded build plate boundaries and was repositioned to the nearest valid edge.");
- break;
case EWarning::NozzleFilamentIncompatible: {
text = _u8L(get_nozzle_filament_incompatible_text());
break;
@@ -9820,6 +9797,10 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state)
bool GLCanvas3D::is_flushing_matrix_error() {
+ // Flushing volumes only apply to single-extruder multi-material (SEMM) and BBL printers
+ if (!Sidebar::should_show_SEMM_buttons())
+ return false;
+
const auto &project_config = wxGetApp().preset_bundle->project_config;
const std::vector &config_matrix = (project_config.option("flush_volumes_matrix"))->values;
const std::vector &config_multiplier = (project_config.option("flush_multiplier"))->values;
diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp
index 6434ab4389..17a388c731 100644
--- a/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/src/slic3r/GUI/GLCanvas3D.hpp
@@ -381,14 +381,13 @@ class GLCanvas3D
ToolHeightOutside,
TPUPrintableError,
FilamentPrintableError,
- LeftExtruderPrintableError, // before slice
- RightExtruderPrintableError, // before slice
- MultiExtruderPrintableError, // after slice
- MultiExtruderHeightOutside, // after slice
+ LeftExtruderPrintableError, // before slice
+ RightExtruderPrintableError, // before slice
+ MultiExtruderPrintableError, // after slice
+ MultiExtruderHeightOutside, // after slice
FilamentUnPrintableOnFirstLayer,
MixUsePLAAndPETG,
- PrimeTowerOutside, // after slice
- PreviewPrimeTowerOutside, // before slice
+ PrimeTowerOutside,
NozzleFilamentIncompatible,
MixtureFilamentIncompatible,
FlushingVolumeZero
diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp
index 731b89e714..fe7cfbd255 100644
--- a/src/slic3r/GUI/GUI_App.cpp
+++ b/src/slic3r/GUI/GUI_App.cpp
@@ -372,16 +372,6 @@ public:
// Dynamic Text
m_action_line_y_position = int(height * 0.83);
-
- // Based on Text
- memDc.SetFont(m_constant_text.based_on_font);
- auto bs_version = wxString::Format(_L("Based on PrusaSlicer and BambuStudio")).ToStdString();
- wxSize based_on_ext = memDc.GetTextExtent(bs_version);
- wxRect based_on_rect(
- wxPoint(0, height - based_on_ext.GetHeight() * 2),
- wxPoint(width, height - based_on_ext.GetHeight())
- );
- memDc.DrawLabel(bs_version, based_on_rect, wxALIGN_CENTER);
}
static wxBitmap MakeBitmap()
@@ -484,6 +474,37 @@ private:
};
#ifdef __linux__
+static void migrate_flatpak_legacy_datadir(const boost::filesystem::path &data_dir_path)
+{
+ if(!boost::filesystem::exists("/.flatpak-info"))
+ return; // Not running as a Flatpak, nothing to migrate.
+
+ namespace fs = boost::filesystem;
+
+ if (fs::exists(data_dir_path)){
+ std::cerr << "New Flatpak data dir: " << data_dir_path << std::endl;
+ return;
+ }
+ std::cerr << "Migrating Flatpak data dir: " << data_dir_path << std::endl;
+
+ std::string legacy_data_dir_str = data_dir_path.string();
+ boost::replace_first(legacy_data_dir_str, "com.orcaslicer.OrcaSlicer", "io.github.orcaslicer.OrcaSlicer");
+ const fs::path legacy_data_dir(legacy_data_dir_str);
+
+ std::cerr << "Legacy Flatpak data dir: " << legacy_data_dir << std::endl;
+
+ if ( ! fs::exists(legacy_data_dir) || ! fs::is_directory(legacy_data_dir))
+ return;
+ std::cerr << "Legacy Flatpak data dir exists: " << legacy_data_dir << std::endl;
+
+ try {
+ std::cerr << "Migrating Flatpak data dir from " << legacy_data_dir << " to " << data_dir_path << std::endl;
+ copy_directory_recursively(legacy_data_dir, data_dir_path);
+ } catch (const std::exception &ex) {
+ std::cerr << "Failed to migrate Flatpak data dir from " << legacy_data_dir << " to " << data_dir_path << ": " << ex.what() << std::endl;
+ }
+}
+
bool static check_old_linux_datadir(const wxString& app_name) {
// If we are on Linux and the datadir does not exist yet, look into the old
// location where the datadir was before version 2.3. If we find it there,
@@ -873,7 +894,9 @@ void GUI_App::post_init()
}
if (!switch_to_3d) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", begin load_gl_resources";
+#ifndef __linux__
mainframe->Freeze();
+#endif
plater_->canvas3D()->enable_render(false);
mainframe->select_tab(size_t(MainFrame::tp3DEditor));
plater_->select_view_3D("3D");
@@ -910,7 +933,9 @@ void GUI_App::post_init()
mainframe->select_tab(size_t(0));
if (app_config->get("default_page") == "1")
mainframe->select_tab(size_t(1));
+#ifndef __linux__
mainframe->Thaw();
+#endif
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", end load_gl_resources";
}
@@ -2397,8 +2422,9 @@ void GUI_App::init_app_config()
wxString dir;
if (! wxGetEnv(wxS("XDG_CONFIG_HOME"), &dir) || dir.empty() )
dir = wxFileName::GetHomeDir() + wxS("/.config");
- set_data_dir((dir + "/" + GetAppName()).ToUTF8().data());
- data_dir_path = boost::filesystem::path(data_dir());
+ data_dir_path = boost::filesystem::path((dir + "/" + GetAppName()).ToUTF8().data());
+ migrate_flatpak_legacy_datadir(data_dir_path);
+ set_data_dir(data_dir_path.string());
#endif
if (!boost::filesystem::exists(data_dir_path)){
boost::filesystem::create_directory(data_dir_path);
@@ -6195,7 +6221,7 @@ static const wxLanguageInfo* linux_get_existing_locale_language(const wxLanguage
if (! it->empty()) {
const std::string &locale = *it;
const wxLanguageInfo* lang = wxLocale::FindLanguageInfo(from_u8(locale));
- if (wxLocale::IsAvailable(lang->Language))
+ if (lang != nullptr && wxLocale::IsAvailable(lang->Language))
return lang;
}
return language;
@@ -6237,7 +6263,10 @@ bool GUI_App::select_language()
names.Alloc(language_infos.size());
// Some valid language should be selected since the application start up.
- const wxLanguage current_language = wxLanguage(m_wxLocale->GetLanguage());
+ const wxString active_language_code = current_language_code();
+ const wxLanguageInfo* active_language_info = wxLocale::FindLanguageInfo(active_language_code);
+ const wxLanguage current_language = active_language_info != nullptr ? wxLanguage(active_language_info->Language) : wxLanguage(m_wxLocale->GetLanguage());
+ const wxString active_lang_prefix = active_language_code.BeforeFirst('_');
int init_selection = -1;
int init_selection_alt = -1;
int init_selection_default = -1;
@@ -6245,9 +6274,9 @@ bool GUI_App::select_language()
if (wxLanguage(language_infos[i]->Language) == current_language)
// The dictionary matches the active language and country.
init_selection = i;
- else if ((language_infos[i]->CanonicalName.BeforeFirst('_') == m_wxLocale->GetCanonicalName().BeforeFirst('_')) ||
+ else if ((language_infos[i]->CanonicalName.BeforeFirst('_') == active_lang_prefix) ||
// if the active language is Slovak, mark the Czech language as active.
- (language_infos[i]->CanonicalName.BeforeFirst('_') == "cs" && m_wxLocale->GetCanonicalName().BeforeFirst('_') == "sk"))
+ (language_infos[i]->CanonicalName.BeforeFirst('_') == "cs" && active_lang_prefix == "sk"))
// The dictionary matches the active language, it does not necessarily match the country.
init_selection_alt = i;
if (language_infos[i]->CanonicalName.BeforeFirst('_') == "en")
@@ -6365,7 +6394,10 @@ bool GUI_App::load_language(wxString language, bool initial)
language_info = wxLocale::GetLanguageInfo(wxLANGUAGE_ENGLISH_US);
}
- BOOST_LOG_TRIVIAL(trace) << boost::format("Switching wxLocales to %1%") % language_info->CanonicalName.ToUTF8().data();
+ const wxLanguageInfo *translation_language_info = language_info;
+ const wxString requested_language_code = translation_language_info->CanonicalName;
+ const wxLanguageInfo *locale_language_info = translation_language_info;
+ BOOST_LOG_TRIVIAL(trace) << boost::format("Requested translation language %1%") % requested_language_code.ToUTF8().data();
// Select language for locales. This language may be different from the language of the dictionary.
//if (language_info == m_language_info_best || language_info == m_language_info_system) {
@@ -6378,8 +6410,8 @@ bool GUI_App::load_language(wxString language, bool initial)
// language_info = m_language_info_system;
// Alternate language code.
- wxLanguage language_dict = wxLanguage(language_info->Language);
- if (language_info->CanonicalName.BeforeFirst('_') == "sk") {
+ wxLanguage language_dict = wxLanguage(translation_language_info->Language);
+ if (translation_language_info->CanonicalName.BeforeFirst('_') == "sk") {
// Slovaks understand Czech well. Give them the Czech translation.
language_dict = wxLANGUAGE_CZECH;
BOOST_LOG_TRIVIAL(info) << "Using Czech dictionaries for Slovak language";
@@ -6388,19 +6420,34 @@ bool GUI_App::load_language(wxString language, bool initial)
#ifdef __linux__
// If we can't find this locale , try to use different one for the language
// instead of just reporting that it is impossible to switch.
- if (! wxLocale::IsAvailable(language_info->Language) && m_language_info_system) {
- std::string original_lang = into_u8(language_info->CanonicalName);
- language_info = linux_get_existing_locale_language(language_info, m_language_info_system);
- BOOST_LOG_TRIVIAL(info) << boost::format("Can't switch language to %1% (missing locales). Using %2% instead.")
- % original_lang % language_info->CanonicalName.ToUTF8().data();
+ if (!wxLocale::IsAvailable(locale_language_info->Language) && m_language_info_system) {
+ std::string original_lang = into_u8(locale_language_info->CanonicalName);
+ locale_language_info = linux_get_existing_locale_language(locale_language_info, m_language_info_system);
+ if (locale_language_info != nullptr && locale_language_info != translation_language_info) {
+ BOOST_LOG_TRIVIAL(info) << boost::format("Can't use locale %1% directly (missing locales). Using locale %2% instead.")
+ % original_lang % locale_language_info->CanonicalName.ToUTF8().data();
+ }
+ }
+
+ if (locale_language_info == nullptr || !wxLocale::IsAvailable(locale_language_info->Language)) {
+ auto try_locale = [](const wxLanguageInfo* candidate) -> const wxLanguageInfo* {
+ return (candidate && wxLocale::IsAvailable(candidate->Language)) ? candidate : nullptr;
+ };
+ const wxLanguageInfo* fallback_locale_info =
+ try_locale(m_wxLocale ? wxLocale::GetLanguageInfo(wxLanguage(m_wxLocale->GetLanguage())) : nullptr);
+ if (!fallback_locale_info) fallback_locale_info = try_locale(m_language_info_system);
+ if (!fallback_locale_info) fallback_locale_info = try_locale(m_language_info_best);
+ if (!fallback_locale_info) fallback_locale_info = try_locale(wxLocale::GetLanguageInfo(wxLANGUAGE_ENGLISH_US));
+ if (!fallback_locale_info) fallback_locale_info = try_locale(wxLocale::GetLanguageInfo(wxLANGUAGE_ENGLISH_UK));
+ if (fallback_locale_info != nullptr) {
+ BOOST_LOG_TRIVIAL(info) << boost::format("Using fallback locale %1% while keeping translation dictionary %2%.")
+ % fallback_locale_info->CanonicalName.ToUTF8().data() % requested_language_code.ToUTF8().data();
+ locale_language_info = fallback_locale_info;
+ }
}
#endif
- if (! wxLocale::IsAvailable(language_info->Language)&&initial) {
- language_info = wxLocale::GetLanguageInfo(wxLANGUAGE_ENGLISH_UK);
- app_config->set("language", language_info->CanonicalName.ToUTF8().data());
- }
- else if (initial) {
+ if (initial) {
// bbs supported languages
//TODO: use a global one with Preference
//wxLanguage supported_languages[]{
@@ -6434,9 +6481,11 @@ bool GUI_App::load_language(wxString language, bool initial)
//}
}
- if (! wxLocale::IsAvailable(language_info->Language)) {
+ BOOST_LOG_TRIVIAL(trace) << boost::format("Switching wxLocales to %1%") % locale_language_info->CanonicalName.ToUTF8().data();
+
+ if (!wxLocale::IsAvailable(locale_language_info->Language)) {
// Loading the language dictionary failed.
- wxString message = "Switching Orca Slicer to language " + language_info->CanonicalName + " failed.";
+ wxString message = "Switching Orca Slicer to language " + requested_language_code + " failed.";
#if !defined(_WIN32) && !defined(__APPLE__)
// likely some linux system
message += "\nYou may need to reconfigure the missing locales, likely by running the \"locale-gen\" and \"dpkg-reconfigure locales\" commands.\n";
@@ -6454,12 +6503,13 @@ bool GUI_App::load_language(wxString language, bool initial)
//FIXME wxWidgets cause havoc if the current locale is deleted. We just forget it causing memory leaks for now.
m_wxLocale.release();
m_wxLocale = Slic3r::make_unique();
- m_wxLocale->Init(language_info->Language);
+ m_wxLocale->Init(locale_language_info->Language);
// Override language at the active wxTranslations class (which is stored in the active m_wxLocale)
// to load possibly different dictionary, for example, load Czech dictionary for Slovak language.
wxTranslations::Get()->SetLanguage(language_dict);
m_wxLocale->AddCatalog(SLIC3R_APP_KEY);
- m_imgui->set_language(into_u8(language_info->CanonicalName));
+ m_active_language_code = requested_language_code;
+ m_imgui->set_language(into_u8(requested_language_code));
//FIXME This is a temporary workaround, the correct solution is to switch to "C" locale during file import / export only.
//wxSetlocale(LC_NUMERIC, "C");
@@ -6747,49 +6797,57 @@ void GUI_App::show_ip_address_enter_dialog_handler(wxCommandEvent& evt)
void GUI_App::open_preferences(size_t open_on_tab, const std::string& highlight_option)
{
- bool app_layout_changed = false;
+ bool need_recreate_gui = false;
+ std::string pending_language;
{
// the dialog needs to be destroyed before the call to recreate_GUI()
// or sometimes the application crashes into wxDialogBase() destructor
// so we put it into an inner scope
PreferencesDialog dlg(mainframe, open_on_tab, highlight_option);
dlg.ShowModal();
- this->plater_->get_current_canvas3D()->force_set_focus();
- // BBS
- //app_layout_changed = dlg.settings_layout_changed();
+ need_recreate_gui = dlg.recreate_GUI();
+ pending_language = dlg.pending_language();
+ if (!need_recreate_gui) {
+ this->plater_->get_current_canvas3D()->force_set_focus();
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
- if (dlg.seq_top_layer_only_changed() || dlg.seq_seq_top_gcode_indices_changed())
+ if (dlg.seq_top_layer_only_changed() || dlg.seq_seq_top_gcode_indices_changed())
#else
- if (dlg.seq_top_layer_only_changed())
+ if (dlg.seq_top_layer_only_changed())
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
- this->plater_->reload_print();
+ this->plater_->reload_print();
#ifdef _WIN32
- if (is_editor()) {
- if (app_config->get("associate_3mf") == "true")
- associate_files(L"3mf");
- if (app_config->get("associate_stl") == "true")
- associate_files(L"stl");
- if (app_config->get("associate_step") == "true") {
- associate_files(L"step");
- associate_files(L"stp");
+ if (is_editor()) {
+ if (app_config->get("associate_3mf") == "true")
+ associate_files(L"3mf");
+ if (app_config->get("associate_stl") == "true")
+ associate_files(L"stl");
+ if (app_config->get("associate_step") == "true") {
+ associate_files(L"step");
+ associate_files(L"stp");
+ }
+ associate_url(L"orcaslicer");
+ }
+ else {
+ if (app_config->get("associate_gcode") == "true")
+ associate_files(L"gcode");
}
- associate_url(L"orcaslicer");
- }
- else {
- if (app_config->get("associate_gcode") == "true")
- associate_files(L"gcode");
- }
#endif // _WIN32
+ }
}
- // BBS
- /*
- if (app_layout_changed) {
- // hide full main_sizer for mainFrame
- mainframe->GetSizer()->Show(false);
- mainframe->update_layout();
- mainframe->select_tab(size_t(0));
- }*/
+ if (!pending_language.empty()) {
+ const std::string previous_language = app_config->get("language");
+ app_config->set("language", pending_language);
+ if (!load_language(wxString::FromUTF8(pending_language), false)) {
+ app_config->set("language", previous_language);
+ if (this->plater_)
+ this->plater_->get_current_canvas3D()->force_set_focus();
+ return;
+ }
+ }
+
+ if (need_recreate_gui)
+ recreate_GUI(_L("Changing application language"));
}
bool GUI_App::has_unsaved_preset_changes() const
diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp
index ae51add264..f89f583873 100644
--- a/src/slic3r/GUI/GUI_App.hpp
+++ b/src/slic3r/GUI/GUI_App.hpp
@@ -273,6 +273,7 @@ private:
const wxLanguageInfo *m_language_info_system = nullptr;
// Best translation language, provided by Windows or OSX, owned by wxWidgets.
const wxLanguageInfo *m_language_info_best = nullptr;
+ wxString m_active_language_code;
OpenGLManager m_opengl_mgr;
std::unique_ptr m_removable_drive_manager;
@@ -563,7 +564,7 @@ public:
void preset_deleted_from_cloud(std::string setting_id);
wxString filter_string(wxString str);
- wxString current_language_code() const { return m_wxLocale->GetCanonicalName(); }
+ wxString current_language_code() const { return m_active_language_code.empty() && m_wxLocale ? m_wxLocale->GetCanonicalName() : m_active_language_code; }
// Translate the language code to a code, for which Prusa Research maintains translations. Defaults to "en_US".
wxString current_language_code_safe() const;
bool is_localized() const { return m_wxLocale->GetLocale() != "English"; }
diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp
index 58e058cc0c..7e4853d19f 100644
--- a/src/slic3r/GUI/GUI_ObjectList.cpp
+++ b/src/slic3r/GUI/GUI_ObjectList.cpp
@@ -5947,7 +5947,7 @@ void GUI::ObjectList::smooth_mesh()
if (cur_face_count > limit_face_count) {
auto name_str = wxString::FromUTF8(name);
auto content = wxString::Format(_L("\"%s\" will exceed 1 million faces after this subdivision, which may increase slicing time. Do you want to continue?"), name_str);
- WarningDialog dlg(static_cast(wxGetApp().mainframe), (is_part ? _L("Part") : _L("Object")) + " " + content, _L("BambuStudio warning"), wxYES_NO);
+ WarningDialog dlg(static_cast(wxGetApp().mainframe), (is_part ? _L("Part") : _L("Object")) + " " + content, wxEmptyString, wxYES_NO);
if (dlg.ShowModal() == wxID_NO) {
return true;
}
@@ -5958,7 +5958,7 @@ void GUI::ObjectList::smooth_mesh()
auto show_smooth_mesh_error_dlg = [this](std::string name) {
auto name_str = wxString::FromUTF8(name);
auto content = wxString::Format(_L("\"%s\" part's mesh contains errors. Please repair it first."), name_str);
- WarningDialog dlg(static_cast(wxGetApp().mainframe), content, _L("BambuStudio warning"), wxOK);
+ WarningDialog dlg(static_cast(wxGetApp().mainframe), content, wxEmptyString, wxOK);
dlg.ShowModal();
};
bool has_show_smooth_mesh_error_dlg = false;
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp
index eeeb3da285..8a6bed9bca 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp
@@ -678,7 +678,7 @@ void GLGizmoMeasure::on_render()
m_mesh_raycaster_map[m_last_hit_volume]->get_transform(), m_only_select_plane) :
std::nullopt;
}
- if (m_measure_mode == EMeasureMode::ONLY_ASSEMBLY) {
+ if (m_measure_mode == EMeasureMode::ONLY_ASSEMBLY && curr_feature.has_value()) {
if (m_assembly_mode == AssemblyMode::FACE_FACE) {
if (curr_feature->get_type() != Measure::SurfaceFeatureType::Plane) {
curr_feature.reset();
diff --git a/src/slic3r/GUI/InstanceCheck.cpp b/src/slic3r/GUI/InstanceCheck.cpp
index 09d4da31c5..fcdd1a9c88 100644
--- a/src/slic3r/GUI/InstanceCheck.cpp
+++ b/src/slic3r/GUI/InstanceCheck.cpp
@@ -236,11 +236,9 @@ namespace instance_check_internal
DBusError err;
dbus_uint32_t serial = 0;
const char* sigval = message_text.c_str();
- //std::string interface_name = "com.prusa3d.prusaslicer.InstanceCheck";
- std::string interface_name = "com.softfever3d.orca-slicer.InstanceCheck.Object" + version;
+ std::string interface_name = "com.orcaslicer.OrcaSlicer.InstanceCheck.Object" + version;
std::string method_name = "AnotherInstance";
- //std::string object_name = "/com/prusa3d/prusaslicer/InstanceCheck";
- std::string object_name = "/com/softfever3d/OrcaSlicer/InstanceCheck/Object" + version;
+ std::string object_name = "/com/orcaslicer/OrcaSlicer/InstanceCheck/Object" + version;
// initialise the error value
@@ -551,7 +549,7 @@ namespace MessageHandlerDBusInternal
" "
" "
" "
- " "
+ " "
" "
" "
" "
@@ -589,7 +587,7 @@ namespace MessageHandlerDBusInternal
{
const char* interface_name = dbus_message_get_interface(message);
const char* member_name = dbus_message_get_member(message);
- std::string our_interface = "com.softfever3d.orca-slicer.InstanceCheck.Object" + wxGetApp().get_instance_hash_string();
+ std::string our_interface = "com.orcaslicer.OrcaSlicer.InstanceCheck.Object" + wxGetApp().get_instance_hash_string();
BOOST_LOG_TRIVIAL(trace) << "DBus message received: interface: " << interface_name << ", member: " << member_name;
if (0 == strcmp("org.freedesktop.DBus.Introspectable", interface_name) && 0 == strcmp("Introspect", member_name)) {
respond_to_introspect(connection, message);
@@ -609,8 +607,8 @@ void OtherInstanceMessageHandler::listen()
int name_req_val;
DBusObjectPathVTable vtable;
std::string instance_hash = wxGetApp().get_instance_hash_string();
- std::string interface_name = "com.softfever3d.orca-slicer.InstanceCheck.Object" + instance_hash;
- std::string object_name = "/com/softfever3d/OrcaSlicer/InstanceCheck/Object" + instance_hash;
+ std::string interface_name = "com.orcaslicer.OrcaSlicer.InstanceCheck.Object" + instance_hash;
+ std::string object_name = "/com/orcaslicer/OrcaSlicer/InstanceCheck/Object" + instance_hash;
//BOOST_LOG_TRIVIAL(debug) << "init dbus listen " << interface_name << " " << object_name;
dbus_error_init(&err);
diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp
index ae01c139bc..7d346571bb 100644
--- a/src/slic3r/GUI/KBShortcutsDialog.cpp
+++ b/src/slic3r/GUI/KBShortcutsDialog.cpp
@@ -211,9 +211,9 @@ void KBShortcutsDialog::fill_shortcuts()
bool swap_mouse_buttons = wxGetApp().app_config->get_bool("swap_mouse_buttons");
Shortcuts plater_shortcuts = {
- { L("Left mouse button"), swap_mouse_buttons ? L("Pan View") : L("Rotate View") },
- { L("Right mouse button"), swap_mouse_buttons ? L("Rotate View") : L("Pan View") },
- { L("Mouse wheel"), L("Zoom View") },
+ { L("Left mouse button"), swap_mouse_buttons ? L("Pan view") : L("Rotate view") },
+ { L("Right mouse button"), swap_mouse_buttons ? L("Rotate view") : L("Pan view") },
+ { L("Mouse wheel"), L("Zoom view") },
{ "A", L("Arrange all objects") },
{ shift + "A", L("Arrange objects on selected plates") },
diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp
index d7c0da0127..7e7bdb04dc 100644
--- a/src/slic3r/GUI/MainFrame.cpp
+++ b/src/slic3r/GUI/MainFrame.cpp
@@ -105,138 +105,109 @@ enum class ERescaleTarget
};
#ifdef __WXGTK__
-// Intercepts mouse events globally to provide resize handles for the
-// borderless (CSD) window. When the cursor is within BORDER_PX of a
-// window edge, a resize cursor is shown and left-click initiates a
-// WM-integrated resize via gtk_window_begin_resize_drag().
-class MainFrame::GtkResizeBorderHandler : public wxEventFilter
+// A thin transparent panel placed at a window edge to handle resize.
+// Works regardless of underlying content (GLCanvas3D, wxWebView, etc.)
+// because these panels are Raise()'d above all siblings, so their GDK
+// windows receive pointer events even over WebKit2GTK or GL surfaces.
+class ResizeEdgePanel : public wxPanel
{
public:
- static constexpr int BORDER_PX = 8;
+ enum Edge { Bottom, Left, Right };
- explicit GtkResizeBorderHandler(MainFrame* frame)
- : m_frame(frame)
+ static constexpr int BORDER_PX = 5;
+
+ ResizeEdgePanel(MainFrame* frame, Edge edge)
+ : wxPanel(frame, wxID_ANY, wxDefaultPosition, wxDefaultSize,
+ wxBORDER_NONE | wxTRANSPARENT_WINDOW)
+ , m_frame(frame)
+ , m_edge(edge)
{
- wxEvtHandler::AddFilter(this);
- }
-
- ~GtkResizeBorderHandler() override
- {
- wxEvtHandler::RemoveFilter(this);
- }
-
- int FilterEvent(wxEvent& event) override
- {
- const wxEventType t = event.GetEventType();
- if (t != wxEVT_MOTION && t != wxEVT_LEFT_DOWN)
- return Event_Skip;
-
- if (!m_frame || !m_frame->IsShown() || m_frame->IsMaximized() || m_frame->IsFullScreen())
- return Event_Skip;
-
- if (!gtk_widget_get_realized(m_frame->m_widget) || gtk_widget_get_window(m_frame->m_widget) == nullptr)
- return Event_Skip;
-
- wxPoint mouse = ::wxGetMousePosition();
- wxRect rect = m_frame->GetScreenRect();
-
- // Don't steal interactions from the custom top bar area.
- // Keep corner resizing available by only excluding the pure top edge.
- if (m_frame->topbar() != nullptr) {
- const wxRect topbar_rect = m_frame->topbar()->GetScreenRect();
- if (topbar_rect.Contains(mouse)) {
- const bool near_left_corner = mouse.x < rect.x + BORDER_PX;
- const bool near_right_corner = mouse.x > rect.x + rect.width - BORDER_PX;
- if (!near_left_corner && !near_right_corner)
- return Event_Skip;
- }
- }
-
- GdkWindowEdge edge;
- if (!hit_test(mouse, rect, edge)) {
- // Cursor is not near any edge — restore default cursor if we changed it.
- if (m_cursor_set) {
- gdk_window_set_cursor(gtk_widget_get_window(m_frame->m_widget), NULL);
- m_cursor_set = false;
- m_last_edge_valid = false;
- }
- return Event_Skip;
- }
-
- // Set the appropriate resize cursor.
- set_cursor_for_edge(edge);
-
- if (t == wxEVT_LEFT_DOWN) {
- gtk_window_begin_resize_drag(
- GTK_WINDOW(m_frame->m_widget),
- edge,
- 1, // left mouse button
- mouse.x, mouse.y,
- gtk_get_current_event_time());
- return Event_Processed;
- }
-
- // For motion, keep app interaction working (menus, hover, etc.).
- return Event_Skip;
+ SetBackgroundStyle(wxBG_STYLE_TRANSPARENT);
+ Bind(wxEVT_MOTION, &ResizeEdgePanel::OnCursorUpdate, this);
+ Bind(wxEVT_ENTER_WINDOW, &ResizeEdgePanel::OnCursorUpdate, this);
+ Bind(wxEVT_LEFT_DOWN, &ResizeEdgePanel::OnLeftDown, this);
+ Bind(wxEVT_LEAVE_WINDOW, &ResizeEdgePanel::OnLeave, this);
+ Bind(wxEVT_PAINT, &ResizeEdgePanel::OnPaint, this);
}
private:
- bool hit_test(const wxPoint& mouse, const wxRect& rect, GdkWindowEdge& edge) const
+ void OnPaint(wxPaintEvent&)
{
- bool left = mouse.x >= rect.x && mouse.x < rect.x + BORDER_PX;
- bool right = mouse.x > rect.x + rect.width - BORDER_PX && mouse.x <= rect.x + rect.width;
- bool top = mouse.y >= rect.y && mouse.y < rect.y + BORDER_PX;
- bool bottom = mouse.y > rect.y + rect.height - BORDER_PX && mouse.y <= rect.y + rect.height;
-
- if (!left && !right && !top && !bottom)
- return false;
-
- if (top && left) edge = GDK_WINDOW_EDGE_NORTH_WEST;
- else if (top && right) edge = GDK_WINDOW_EDGE_NORTH_EAST;
- else if (bottom && left) edge = GDK_WINDOW_EDGE_SOUTH_WEST;
- else if (bottom && right) edge = GDK_WINDOW_EDGE_SOUTH_EAST;
- else if (top) edge = GDK_WINDOW_EDGE_NORTH;
- else if (bottom) edge = GDK_WINDOW_EDGE_SOUTH;
- else if (left) edge = GDK_WINDOW_EDGE_WEST;
- else edge = GDK_WINDOW_EDGE_EAST;
- return true;
+ wxPaintDC dc(this);
+ // Transparent — draw nothing
}
- void set_cursor_for_edge(GdkWindowEdge edge)
+ GdkWindowEdge get_gdk_edge(const wxPoint& pos) const
{
- if (m_last_edge_valid && m_cursor_set && edge == m_last_edge)
- return;
-
- const char* cursor_name = nullptr;
- switch (edge) {
- case GDK_WINDOW_EDGE_NORTH: cursor_name = "n-resize"; break;
- case GDK_WINDOW_EDGE_SOUTH: cursor_name = "s-resize"; break;
- case GDK_WINDOW_EDGE_WEST: cursor_name = "w-resize"; break;
- case GDK_WINDOW_EDGE_EAST: cursor_name = "e-resize"; break;
- case GDK_WINDOW_EDGE_NORTH_WEST: cursor_name = "nw-resize"; break;
- case GDK_WINDOW_EDGE_NORTH_EAST: cursor_name = "ne-resize"; break;
- case GDK_WINDOW_EDGE_SOUTH_WEST: cursor_name = "sw-resize"; break;
- case GDK_WINDOW_EDGE_SOUTH_EAST: cursor_name = "se-resize"; break;
- default: return;
+ wxSize size = GetSize();
+ switch (m_edge) {
+ case Bottom:
+ if (pos.x < BORDER_PX) return GDK_WINDOW_EDGE_SOUTH_WEST;
+ if (pos.x > size.x - BORDER_PX) return GDK_WINDOW_EDGE_SOUTH_EAST;
+ return GDK_WINDOW_EDGE_SOUTH;
+ case Left:
+ if (pos.y < BORDER_PX) return GDK_WINDOW_EDGE_NORTH_WEST;
+ if (pos.y > size.y - BORDER_PX) return GDK_WINDOW_EDGE_SOUTH_WEST;
+ return GDK_WINDOW_EDGE_WEST;
+ case Right:
+ if (pos.y < BORDER_PX) return GDK_WINDOW_EDGE_NORTH_EAST;
+ if (pos.y > size.y - BORDER_PX) return GDK_WINDOW_EDGE_SOUTH_EAST;
+ return GDK_WINDOW_EDGE_EAST;
}
+ return GDK_WINDOW_EDGE_SOUTH;
+ }
- GdkDisplay* display = gtk_widget_get_display(m_frame->m_widget);
- GdkCursor* cursor = gdk_cursor_new_from_name(display, cursor_name);
- if (!cursor)
+ void OnCursorUpdate(wxMouseEvent& evt)
+ {
+ GdkWindowEdge edge = get_gdk_edge(evt.GetPosition());
+ const char* name;
+ switch (edge) {
+ case GDK_WINDOW_EDGE_NORTH: name = "n-resize"; break;
+ case GDK_WINDOW_EDGE_SOUTH: name = "s-resize"; break;
+ case GDK_WINDOW_EDGE_WEST: name = "w-resize"; break;
+ case GDK_WINDOW_EDGE_EAST: name = "e-resize"; break;
+ case GDK_WINDOW_EDGE_NORTH_WEST: name = "nw-resize"; break;
+ case GDK_WINDOW_EDGE_NORTH_EAST: name = "ne-resize"; break;
+ case GDK_WINDOW_EDGE_SOUTH_WEST: name = "sw-resize"; break;
+ case GDK_WINDOW_EDGE_SOUTH_EAST: name = "se-resize"; break;
+ default: name = "s-resize"; break;
+ }
+ if (name == m_last_cursor_name) return;
+ m_last_cursor_name = name;
+
+ GdkDisplay* display = gtk_widget_get_display(m_widget);
+ GdkCursor* cursor = gdk_cursor_new_from_name(display, name);
+ if (cursor) {
+ gdk_window_set_cursor(gtk_widget_get_window(m_widget), cursor);
+ g_object_unref(cursor);
+ }
+ }
+
+ void OnLeave(wxMouseEvent&)
+ {
+ m_last_cursor_name = nullptr;
+ gdk_window_set_cursor(gtk_widget_get_window(m_widget), nullptr);
+ }
+
+ void OnLeftDown(wxMouseEvent& evt)
+ {
+ if (m_frame->IsMaximized() || m_frame->IsFullScreen())
return;
- gdk_window_set_cursor(gtk_widget_get_window(m_frame->m_widget), cursor);
- g_object_unref(cursor);
+ GdkWindowEdge edge = get_gdk_edge(evt.GetPosition());
+ wxPoint mouse = ClientToScreen(evt.GetPosition());
- m_cursor_set = true;
- m_last_edge = edge;
- m_last_edge_valid = true;
+ gtk_window_begin_resize_drag(
+ GTK_WINDOW(m_frame->m_widget),
+ edge,
+ 1, // left button
+ mouse.x, mouse.y,
+ gtk_get_current_event_time());
}
MainFrame* m_frame;
- bool m_cursor_set{false};
- GdkWindowEdge m_last_edge{};
- bool m_last_edge_valid{false};
+ Edge m_edge;
+ const char* m_last_cursor_name{nullptr};
};
#endif // __WXGTK__
@@ -332,15 +303,11 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, BORDERLESS_FRAME_
#endif
#ifdef __WXGTK__
- // Zero out the decoration hints that wxGTK computed from the window style.
- // When GTKHandleRealized() later calls gdk_window_set_decorations(), it
- // will apply zero decorations (no WM title bar).
m_gdkDecor = 0;
- // Install app-level resize border handler as a fallback.
- // This keeps resize behavior for undecorated windows without using GTK CSD,
- // avoiding CSD repaint/maximize artifacts on some WMs.
- m_resize_border_handler = new GtkResizeBorderHandler(this);
+ m_edge_bottom = new ResizeEdgePanel(this, ResizeEdgePanel::Bottom);
+ m_edge_left = new ResizeEdgePanel(this, ResizeEdgePanel::Left);
+ m_edge_right = new ResizeEdgePanel(this, ResizeEdgePanel::Right);
#endif
if (!wxGetApp().app_config->has("user_mode")) {
@@ -504,8 +471,13 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, BORDERLESS_FRAME_
#endif
Refresh();
Layout();
+#ifdef __WXGTK__
+ update_edge_panels();
+#endif
wxQueueEvent(wxGetApp().plater(), new SimpleEvent(EVT_NOTICE_CHILDE_SIZE_CHANGED));
- });
+
+ fit_tab_labels(); // ORCA on resize
+ });
//BBS
Bind(EVT_SELECT_TAB, [this](wxCommandEvent&evt) {
@@ -1085,13 +1057,38 @@ void MainFrame::update_layout()
Thaw();
}
+#ifdef __WXGTK__
+void MainFrame::update_edge_panels()
+{
+ if (!m_edge_bottom) return;
+
+ bool hide = IsMaximized() || IsFullScreen();
+ m_edge_bottom->Show(!hide);
+ m_edge_left->Show(!hide);
+ m_edge_right->Show(!hide);
+ if (hide) return;
+
+ constexpr int B = ResizeEdgePanel::BORDER_PX;
+ wxSize cs = GetClientSize();
+ m_edge_bottom->SetSize(0, cs.y - B, cs.x, B);
+ m_edge_left->SetSize(0, 0, B, cs.y);
+ m_edge_right->SetSize(cs.x - B, 0, B, cs.y);
+
+ m_edge_bottom->Raise();
+ m_edge_left->Raise();
+ m_edge_right->Raise();
+}
+#endif
+
// Called when closing the application and when switching the application language.
void MainFrame::shutdown()
{
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "MainFrame::shutdown enter";
#ifdef __WXGTK__
- delete m_resize_border_handler;
- m_resize_border_handler = nullptr;
+ // Edge panels are child windows — wxWidgets destroys them automatically.
+ m_edge_bottom = nullptr;
+ m_edge_left = nullptr;
+ m_edge_right = nullptr;
#endif
// BBS: backup
Slic3r::set_backup_callback(nullptr);
@@ -1247,6 +1244,7 @@ void MainFrame::init_tabpanel() {
wxPostEvent(m_plater, SimpleEvent(EVT_GLVIEWTOOLBAR_PREVIEW));
m_param_panel->OnActivate();
}
+ fit_tab_labels(); // ORCA on switching prepare / preview
}
//else if (panel == m_param_panel)
// m_param_panel->OnActivate();
@@ -1350,8 +1348,10 @@ void MainFrame::init_tabpanel() {
void MainFrame::show_device(bool bBBLPrinter) {
auto idx = -1;
if (bBBLPrinter) {
- if (m_tabpanel->FindPage(m_monitor) != wxNOT_FOUND)
+ if (m_tabpanel->FindPage(m_monitor) != wxNOT_FOUND) {
+ fit_tab_labels(); // ORCA on printer change - same button layout
return;
+ }
// Remove printer view
if ((idx = m_tabpanel->FindPage(m_printer_view)) != wxNOT_FOUND) {
m_printer_view->Show(false);
@@ -1391,9 +1391,10 @@ void MainFrame::show_device(bool bBBLPrinter) {
#endif // _MSW_DARK_MODE
} else {
- if (m_tabpanel->FindPage(m_printer_view) != wxNOT_FOUND)
+ if (m_tabpanel->FindPage(m_printer_view) != wxNOT_FOUND) {
+ fit_tab_labels(); // ORCA on printer change - same button layout
return;
-
+ }
if ((idx = m_tabpanel->FindPage(m_calibration)) != wxNOT_FOUND) {
m_calibration->Show(false);
m_tabpanel->RemovePage(idx);
@@ -1419,6 +1420,33 @@ void MainFrame::show_device(bool bBBLPrinter) {
m_tabpanel->InsertPage(tpMonitor, m_printer_view, _L("Device"), std::string("tab_monitor_active"),
std::string("tab_monitor_active"));
}
+ fit_tab_labels(); // ORCA on printer change
+}
+
+void MainFrame::fit_tab_labels()
+{
+ if (!m_tabpanel || !m_slice_option_btn) // ignore layout change while slice/print buttons not visible
+ return;
+
+ auto* ctrl = m_tabpanel->GetBtnsListCtrl();
+ auto* sizer = ctrl->GetBtnsSizer();
+ int count = sizer->GetItemCount();
+
+ // Restore all
+ for (size_t i = 1; i < count; ++i)
+ ctrl->SetCompact(i, false);
+ m_tabpanel->Refresh();
+ Layout();
+
+ // Compact (last to first)
+ for (size_t i = count - 1; i >= 1; --i) {
+ int right = ScreenToClient(m_slice_option_btn->ClientToScreen({})).x;
+ int left = sizer->GetSize().GetWidth();
+ if (right - left - FromDIP(15) > 0) return;
+ ctrl->SetCompact(i, true);
+ m_tabpanel->Refresh();
+ Layout();
+ }
}
bool MainFrame::preview_only_hint()
@@ -1795,7 +1823,7 @@ wxBoxSizer* MainFrame::create_side_tools()
update_side_button_style();
m_slice_option_btn->Enable();
m_print_option_btn->Enable();
- sizer->Add(FromDIP(15), 0, 0, 0, 0);
+ //sizer->Add(FromDIP(15), 0, 0, 0, 0);
sizer->Add(slice_panel);
sizer->Add(FromDIP(15), 0, 0, 0, 0);
sizer->Add(print_panel);
@@ -1852,7 +1880,9 @@ wxBoxSizer* MainFrame::create_side_tools()
auto curr_plate = m_plater->get_partplate_list().get_curr_plate();
#ifdef __linux__
- slice = try_pop_up_before_slice(m_slice_select == eSliceAll, m_plater, curr_plate, true);
+ PresetBundle* preset = wxGetApp().preset_bundle;
+ bool force_show_fila_group_dlg = (preset && preset->is_bbl_vendor() && preset->get_printer_extruder_count() == 2);
+ slice = try_pop_up_before_slice(m_slice_select == eSliceAll, m_plater, curr_plate, force_show_fila_group_dlg);
#else
slice = try_pop_up_before_slice(m_slice_select == eSliceAll, m_plater, curr_plate, false);
#endif
@@ -1918,6 +1948,7 @@ wxBoxSizer* MainFrame::create_side_tools()
m_slice_enable = get_enable_slice_status();
m_slice_btn->Enable(m_slice_enable);
this->Layout();
+ fit_tab_labels(); // ORCA on label change
if(m_slice_option_pop_up)
m_slice_option_pop_up->Dismiss();
});
@@ -1928,6 +1959,7 @@ wxBoxSizer* MainFrame::create_side_tools()
m_slice_enable = get_enable_slice_status();
m_slice_btn->Enable(m_slice_enable);
this->Layout();
+ fit_tab_labels(); // ORCA on label change
if(m_slice_option_pop_up)
m_slice_option_pop_up->Dismiss();
});
@@ -1952,6 +1984,7 @@ wxBoxSizer* MainFrame::create_side_tools()
m_print_enable = get_enable_print_status();
m_print_btn->Enable(m_print_enable);
this->Layout();
+ fit_tab_labels(); // ORCA on label change
p->Dismiss();
});
@@ -1964,6 +1997,7 @@ wxBoxSizer* MainFrame::create_side_tools()
m_print_enable = get_enable_print_status();
m_print_btn->Enable(m_print_enable);
this->Layout();
+ fit_tab_labels(); // ORCA on label change
p->Dismiss();
});
@@ -1990,6 +2024,7 @@ wxBoxSizer* MainFrame::create_side_tools()
m_print_enable = get_enable_print_status();
m_print_btn->Enable(m_print_enable);
this->Layout();
+ fit_tab_labels(); // ORCA on label change
p->Dismiss();
});
@@ -2001,6 +2036,7 @@ wxBoxSizer* MainFrame::create_side_tools()
m_print_enable = get_enable_print_status();
m_print_btn->Enable(m_print_enable);
this->Layout();
+ fit_tab_labels(); // ORCA on label change
p->Dismiss();
});
@@ -2010,6 +2046,7 @@ wxBoxSizer* MainFrame::create_side_tools()
m_print_enable = get_enable_print_status();
m_print_btn->Enable(m_print_enable);
this->Layout();
+ fit_tab_labels(); // ORCA on label change
p->Dismiss();
});
@@ -2021,6 +2058,7 @@ wxBoxSizer* MainFrame::create_side_tools()
m_print_enable = get_enable_print_status();
m_print_btn->Enable(m_print_enable);
this->Layout();
+ fit_tab_labels(); // ORCA on label change
p->Dismiss();
});
@@ -2030,6 +2068,7 @@ wxBoxSizer* MainFrame::create_side_tools()
m_print_enable = get_enable_print_status();
m_print_btn->Enable(m_print_enable);
this->Layout();
+ fit_tab_labels(); // ORCA on label change
p->Dismiss();
});
@@ -2039,6 +2078,7 @@ wxBoxSizer* MainFrame::create_side_tools()
m_print_enable = get_enable_print_status();
m_print_btn->Enable(m_print_enable);
this->Layout();
+ fit_tab_labels(); // ORCA on label change
p->Dismiss();
});
@@ -2077,6 +2117,7 @@ wxBoxSizer* MainFrame::create_side_tools()
m_print_enable = get_enable_print_status();
m_print_btn->Enable(m_print_enable);
this->Layout();
+ fit_tab_labels(); // ORCA on label change
p->Dismiss();
});
p->append_button(print_multi_machine_btn);
@@ -2091,6 +2132,7 @@ wxBoxSizer* MainFrame::create_side_tools()
m_print_enable = get_enable_print_status();
m_print_btn->Enable(m_print_enable);
this->Layout();
+ fit_tab_labels(); // ORCA on label change
p->Dismiss();
});
p->append_button(export_gcode_btn);
@@ -2403,6 +2445,8 @@ void MainFrame::on_dpi_changed(const wxRect& suggested_rect)
this->SetSize(sz);
this->Maximize(is_maximized);
+
+ fit_tab_labels(); // ORCA
}
void MainFrame::on_sys_color_changed()
@@ -2488,7 +2532,7 @@ static wxMenu* generate_help_menu()
// //TODO
// });
// Check New Version
- append_menu_item(helpMenu, wxID_ANY, _L("Check for Update"), _L("Check for Update"),
+ append_menu_item(helpMenu, wxID_ANY, _L("Check for Updates"), _L("Check for Updates"),
[](wxCommandEvent&) {
wxGetApp().check_new_version_sf(true, 1);
}, "", nullptr, []() {
@@ -3148,15 +3192,7 @@ void MainFrame::init_menubar_as_editor()
append_menu_item(
parent_menu, wxID_ANY, _L("Preferences") + "\t" + ctrl + ",", "",
[this](wxCommandEvent &) {
- PreferencesDialog dlg(this);
- dlg.ShowModal();
- plater()->get_current_canvas3D()->force_set_focus();
-#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
- if (dlg.seq_top_layer_only_changed() || dlg.seq_seq_top_gcode_indices_changed())
-#else
- if (dlg.seq_top_layer_only_changed())
-#endif
- plater()->reload_print();
+ wxGetApp().open_preferences();
},
"", nullptr, []() { return true; }, this, 1);
//parent_menu->Insert(1, preference_item);
@@ -3177,7 +3213,6 @@ void MainFrame::init_menubar_as_editor()
[this](wxCommandEvent &) {
// Orca: Use GUI_App::open_preferences instead of direct call so windows associations are updated on exit
wxGetApp().open_preferences();
- plater()->get_current_canvas3D()->force_set_focus();
},
"", nullptr, []() { return true; }, this);
//m_topbar->AddDropDownMenuItem(preference_item);
@@ -3218,20 +3253,20 @@ void MainFrame::init_menubar_as_editor()
// Flow rate (with submenu)
auto flowrate_menu = new wxMenu();
append_menu_item(
- flowrate_menu, wxID_ANY, _L("Pass 1"), _L("Flow rate test - Pass 1"),
+ flowrate_menu, wxID_ANY, _L("Pass 1"), _L("Flow ratio test - Pass 1"),
[this](wxCommandEvent&) { if (m_plater) m_plater->calib_flowrate(false, 1); }, "", nullptr,
[this]() {return m_plater->is_view3D_shown();; }, this);
- append_menu_item(flowrate_menu, wxID_ANY, _L("Pass 2"), _L("Flow rate test - Pass 2"),
+ append_menu_item(flowrate_menu, wxID_ANY, _L("Pass 2"), _L("Flow ratio test - Pass 2"),
[this](wxCommandEvent&) { if (m_plater) m_plater->calib_flowrate(false, 2); }, "", nullptr,
[this]() {return m_plater->is_view3D_shown();; }, this);
flowrate_menu->AppendSeparator();
- append_menu_item(flowrate_menu, wxID_ANY, _L("YOLO (Recommended)"), _L("Orca YOLO flowrate calibration, 0.01 step"),
+ append_menu_item(flowrate_menu, wxID_ANY, _L("YOLO (Recommended)"), _L("Orca YOLO flowratio calibration, 0.01 step"),
[this](wxCommandEvent&) { if (m_plater) m_plater->calib_flowrate(true, 1); }, "", nullptr,
[this]() {return m_plater->is_view3D_shown();; }, this);
- append_menu_item(flowrate_menu, wxID_ANY, _L("YOLO (perfectionist version)"), _L("Orca YOLO flowrate calibration, 0.005 step"),
+ append_menu_item(flowrate_menu, wxID_ANY, _L("YOLO (perfectionist version)"), _L("Orca YOLO flowratio calibration, 0.005 step"),
[this](wxCommandEvent&) { if (m_plater) m_plater->calib_flowrate(true, 2); }, "", nullptr,
[this]() {return m_plater->is_view3D_shown();; }, this);
- m_topbar->GetCalibMenu()->AppendSubMenu(flowrate_menu, _L("Flow rate"));
+ m_topbar->GetCalibMenu()->AppendSubMenu(flowrate_menu, _L("Flow ratio"));
// Retraction test
append_menu_item(m_topbar->GetCalibMenu(), wxID_ANY, _L("Retraction test"), _L("Retraction test"),
@@ -3335,19 +3370,19 @@ void MainFrame::init_menubar_as_editor()
// Flowrate (with submenu)
auto flowrate_menu = new wxMenu();
- append_menu_item(flowrate_menu, wxID_ANY, _L("Pass 1"), _L("Flow rate test - Pass 1"),
+ append_menu_item(flowrate_menu, wxID_ANY, _L("Pass 1"), _L("Flow ratio test - Pass 1"),
[this](wxCommandEvent&) { if (m_plater) m_plater->calib_flowrate(false, 1); }, "", nullptr,
[this]() {return m_plater->is_view3D_shown();; }, this);
- append_menu_item(flowrate_menu, wxID_ANY, _L("Pass 2"), _L("Flow rate test - Pass 2"),
+ append_menu_item(flowrate_menu, wxID_ANY, _L("Pass 2"), _L("Flow ratio test - Pass 2"),
[this](wxCommandEvent&) { if (m_plater) m_plater->calib_flowrate(false, 2); }, "", nullptr,
[this]() {return m_plater->is_view3D_shown();; }, this);
- append_submenu(calib_menu,flowrate_menu,wxID_ANY,_L("Flow rate"),_L("Flow rate"),"",
+ append_submenu(calib_menu,flowrate_menu,wxID_ANY,_L("Flow ratio"),_L("Flow ratio"),"",
[this]() {return m_plater->is_view3D_shown();; });
flowrate_menu->AppendSeparator();
- append_menu_item(flowrate_menu, wxID_ANY, _L("YOLO (Recommended)"), _L("Orca YOLO flowrate calibration, 0.01 step"),
+ append_menu_item(flowrate_menu, wxID_ANY, _L("YOLO (Recommended)"), _L("Orca YOLO flowratio calibration, 0.01 step"),
[this](wxCommandEvent&) { if (m_plater) m_plater->calib_flowrate(true, 1); }, "", nullptr,
[this]() {return m_plater->is_view3D_shown();; }, this);
- append_menu_item(flowrate_menu, wxID_ANY, _L("YOLO (perfectionist version)"), _L("Orca YOLO flowrate calibration, 0.005 step"),
+ append_menu_item(flowrate_menu, wxID_ANY, _L("YOLO (perfectionist version)"), _L("Orca YOLO flowratio calibration, 0.005 step"),
[this](wxCommandEvent&) { if (m_plater) m_plater->calib_flowrate(true, 2); }, "", nullptr,
[this]() {return m_plater->is_view3D_shown();; }, this);
diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp
index 8fa5454cae..8cb5961d5a 100644
--- a/src/slic3r/GUI/MainFrame.hpp
+++ b/src/slic3r/GUI/MainFrame.hpp
@@ -52,6 +52,9 @@ class PrintHostQueueDialog;
class Plater;
class MainFrame;
class ParamsDialog;
+#ifdef __WXGTK__
+class ResizeEdgePanel;
+#endif
enum QuickSlice
{
@@ -356,6 +359,7 @@ public:
//SoftFever
void show_device(bool bBBLPrinter);
+ void fit_tab_labels(); // ORCA
PA_Calibration_Dlg* m_pa_calib_dlg{ nullptr };
Temp_Calibration_Dlg* m_temp_calib_dlg{ nullptr };
@@ -424,8 +428,11 @@ public:
#endif // _WIN32
#ifdef __WXGTK__
- class GtkResizeBorderHandler;
- GtkResizeBorderHandler* m_resize_border_handler{nullptr};
+ friend class ResizeEdgePanel;
+ ResizeEdgePanel* m_edge_bottom{nullptr};
+ ResizeEdgePanel* m_edge_left{nullptr};
+ ResizeEdgePanel* m_edge_right{nullptr};
+ void update_edge_panels();
#endif // __WXGTK__
};
diff --git a/src/slic3r/GUI/MediaPlayCtrl.h b/src/slic3r/GUI/MediaPlayCtrl.h
index bb4e8e448f..f5e5dcddfc 100644
--- a/src/slic3r/GUI/MediaPlayCtrl.h
+++ b/src/slic3r/GUI/MediaPlayCtrl.h
@@ -67,10 +67,10 @@ private:
static bool get_stream_url(std::string *url = nullptr);
private:
- static const wxMediaState MEDIASTATE_IDLE = (wxMediaState) 3;
- static const wxMediaState MEDIASTATE_INITIALIZING = (wxMediaState) 4;
- static const wxMediaState MEDIASTATE_LOADING = (wxMediaState) 5;
- static const wxMediaState MEDIASTATE_BUFFERING = (wxMediaState) 6;
+ static inline const wxMediaState MEDIASTATE_IDLE = static_cast(3);
+ static inline const wxMediaState MEDIASTATE_INITIALIZING = static_cast(4);
+ static inline const wxMediaState MEDIASTATE_LOADING = static_cast(5);
+ static inline const wxMediaState MEDIASTATE_BUFFERING = static_cast(6);
// token
std::shared_ptr m_token = std::make_shared(0);
diff --git a/src/slic3r/GUI/Notebook.cpp b/src/slic3r/GUI/Notebook.cpp
index 84bbf9950a..89f00f9447 100644
--- a/src/slic3r/GUI/Notebook.cpp
+++ b/src/slic3r/GUI/Notebook.cpp
@@ -202,6 +202,7 @@ bool ButtonsListCtrl::InsertPage(size_t n, const wxString &text, bool bSelect /*
});
Slic3r::GUI::wxGetApp().UpdateDarkUI(btn);
m_pageButtons.insert(m_pageButtons.begin() + n, btn);
+ m_pageLabels.insert(m_pageLabels.begin() + n, text); // ORCA
m_buttons_sizer->Insert(n, new wxSizerItem(btn));
m_buttons_sizer->SetCols(m_buttons_sizer->GetCols() + 1);
m_sizer->Layout();
@@ -212,6 +213,7 @@ void ButtonsListCtrl::RemovePage(size_t n)
{
Button* btn = m_pageButtons[n];
m_pageButtons.erase(m_pageButtons.begin() + n);
+ m_pageLabels.erase(m_pageLabels.begin() + n); // ORCA
m_buttons_sizer->Remove(n);
#if __WXOSX__
RemoveChild(btn);
@@ -238,6 +240,17 @@ void ButtonsListCtrl::SetPageText(size_t n, const wxString& strText)
{
Button* btn = m_pageButtons[n];
btn->SetLabel(strText);
+ if(!strText.empty()) // ORCA
+ m_pageLabels[n] = strText;
+}
+
+// ORCA
+void ButtonsListCtrl::SetCompact(size_t n, bool compact)
+{
+ int em = em_unit(this);
+ Button* btn = m_pageButtons[n];
+ btn->SetMinSize({(compact ? 40 : 136) * em / 10, 36 * em / 10});
+ btn->SetLabel(compact ? "" : (" " + m_pageLabels[n]));
}
wxString ButtonsListCtrl::GetPageText(size_t n) const
diff --git a/src/slic3r/GUI/Notebook.hpp b/src/slic3r/GUI/Notebook.hpp
index 7e6e94da3a..10abfedb95 100644
--- a/src/slic3r/GUI/Notebook.hpp
+++ b/src/slic3r/GUI/Notebook.hpp
@@ -28,7 +28,9 @@ public:
void RemovePage(size_t n);
bool SetPageImage(size_t n, const std::string& bmp_name) const;
void SetPageText(size_t n, const wxString& strText);
+ void SetCompact(size_t n, bool compact); // ORCA
wxString GetPageText(size_t n) const;
+ wxFlexGridSizer* GetBtnsSizer(){return m_buttons_sizer;}; // ORCA
private:
wxFlexGridSizer* m_buttons_sizer;
@@ -39,6 +41,7 @@ private:
int m_btn_margin;
int m_line_margin;
//ModeSizer* m_mode_sizer {nullptr};
+ std::vector m_pageLabels; // ORCA
};
class Notebook: public wxBookCtrlBase
diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp
index 35318412c8..abd5a12373 100644
--- a/src/slic3r/GUI/NotificationManager.cpp
+++ b/src/slic3r/GUI/NotificationManager.cpp
@@ -322,6 +322,13 @@ void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float init
render_minimize_button(imgui, win_pos.x, win_pos.y);
render_close_button(imgui, win_size.x, win_size.y, win_pos.x, win_pos.y); // ORCA draw it after minimize button since its position related to minimize button
}
+
+ const bool gcode_window_visible = canvas.get_canvas_type() == GLCanvas3D::ECanvasType::CanvasPreview && wxGetApp().show_gcode_window();
+ if (!gcode_window_visible)
+ {
+ ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow());
+ }
+
imgui.end();
restore_default_theme();
diff --git a/src/slic3r/GUI/ObjColorDialog.cpp b/src/slic3r/GUI/ObjColorDialog.cpp
index 7575777507..ced23a8d37 100644
--- a/src/slic3r/GUI/ObjColorDialog.cpp
+++ b/src/slic3r/GUI/ObjColorDialog.cpp
@@ -48,14 +48,7 @@ wxBoxSizer* ObjColorDialog::create_btn_sizer(long flags,bool exist_error)
if (!exist_error) {
btn_sizer->AddSpacer(FromDIP(25));
auto *tips = new HyperLink(this, _L("Wiki Guide")); // ORCA
- tips->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &e) {
- bool is_zh = wxGetApp().app_config->get("language") == "zh_CN";
- if (is_zh) {
- wxLaunchDefaultBrowser("https://wiki.bambulab.com/zh/software/bambu-studio/import_obj");
- } else {
- wxLaunchDefaultBrowser("https://wiki.bambulab.com/en/software/bambu-studio/import_obj");
- }
- });
+ tips->SetURL("https://www.orcaslicer.com/wiki/general-settings/import_export.html#obj");
btn_sizer->Add(tips, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
}
btn_sizer->AddStretchSpacer();
@@ -924,7 +917,7 @@ wxBoxSizer *ObjColorPanel::create_color_icon_map_rgba_sizer(wxWindow *parent, in
icon_sizer->Add(icon, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 0); // wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM
icon_sizer->AddSpacer(FromDIP(10));
- wxStaticText *map_text = new wxStaticText(parent, wxID_ANY, u8"—> ");
+ wxStaticText *map_text = new wxStaticText(parent, wxID_ANY, _L(u8"—> "));
map_text->SetFont(Label::Head_12);
icon_sizer->Add(map_text, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 0);
diff --git a/src/slic3r/GUI/PartPlate.cpp b/src/slic3r/GUI/PartPlate.cpp
index 6f697996f4..98c285f46a 100644
--- a/src/slic3r/GUI/PartPlate.cpp
+++ b/src/slic3r/GUI/PartPlate.cpp
@@ -1485,7 +1485,12 @@ void PartPlate::register_raycasters_for_picking(GLCanvas3D &canvas)
canvas.remove_raycasters_for_picking(SceneRaycaster::EType::Bed, picking_id_component(6));
register_model_for_picking(canvas, m_plate_name_edit_icon, picking_id_component(6));
register_model_for_picking(canvas, m_move_front_icon, picking_id_component(7));
- register_model_for_picking(canvas, m_plate_filament_map_icon, picking_id_component(PLATE_FILAMENT_MAP_ID));
+
+ // Only register filament map button for H2D (dual-extruder Bambu Lab) printers
+ PresetBundle* preset = wxGetApp().preset_bundle;
+ bool dual_bbl = (preset && preset->is_bbl_vendor() && preset->get_printer_extruder_count() == 2);
+ if (dual_bbl)
+ register_model_for_picking(canvas, m_plate_filament_map_icon, picking_id_component(PLATE_FILAMENT_MAP_ID));
}
int PartPlate::picking_id_component(int idx) const
@@ -3074,53 +3079,42 @@ bool PartPlate::set_shape(const Pointfs& shape, const Pointfs& exclude_areas, co
calc_bounding_boxes();
- ExPolygon logo_poly;
- generate_logo_polygon(logo_poly);
- m_logo_triangles.reset();
- if (!init_model_from_poly(m_logo_triangles, logo_poly, GROUND_Z + 0.02f))
- BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":Unable to create logo triangles\n";
+ if (m_plater != nullptr) { // render data, skip in CLI mode where m_plater is null
+ ExPolygon logo_poly;
+ generate_logo_polygon(logo_poly);
+ m_logo_triangles.reset();
+ if (!init_model_from_poly(m_logo_triangles, logo_poly, GROUND_Z + 0.02f))
+ BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":Unable to create logo triangles\n";
- ExPolygon poly;
- /*for (const Vec2d& p : m_shape) {
- poly.contour.append({ scale_(p(0)), scale_(p(1)) });
- }*/
- generate_print_polygon(poly);
- calc_triangles(poly);
+ ExPolygon poly;
+ generate_print_polygon(poly);
+ calc_triangles(poly);
- // reset m_wrapping_detection_triangles when change printer
- m_print_polygon = poly;
- m_wrapping_detection_triangles.reset();
- init_raycaster_from_model(m_triangles);
+ // reset m_wrapping_detection_triangles when change printer
+ m_print_polygon = poly;
+ m_wrapping_detection_triangles.reset();
+ init_raycaster_from_model(m_triangles);
- ExPolygon exclude_poly;
- /*for (const Vec2d& p : m_exclude_area) {
- exclude_poly.contour.append({ scale_(p(0)), scale_(p(1)) });
- }*/
- generate_exclude_polygon(exclude_poly);
- calc_exclude_triangles(exclude_poly);
+ ExPolygon exclude_poly;
+ generate_exclude_polygon(exclude_poly);
+ calc_exclude_triangles(exclude_poly);
- const BoundingBox& pp_bbox = poly.contour.bounding_box();
- calc_gridlines(poly, pp_bbox);
+ const BoundingBox& pp_bbox = poly.contour.bounding_box();
+ calc_gridlines(poly, pp_bbox);
- //calc_vertex_for_icons_background(5, m_del_and_background_icon);
- //calc_vertex_for_icons(4, m_del_icon);
- calc_vertex_for_icons(0, m_del_icon);
- calc_vertex_for_icons(1, m_orient_icon);
- calc_vertex_for_icons(2, m_arrange_icon);
- calc_vertex_for_icons(3, m_lock_icon);
- calc_vertex_for_icons(4, m_plate_settings_icon);
- // ORCA also change bed_icon_count number in calc_vertex_for_icons() after adding or removing icons for circular shaped beds that uses vertical alingment for icons
- bool dual_bbl = false;
- if (m_plater) {
- PresetBundle* preset = wxGetApp().preset_bundle;
- dual_bbl = (preset->is_bbl_vendor() && preset->get_printer_extruder_count() == 2);
- }
- calc_vertex_for_icons(dual_bbl ? 5 : 6, m_plate_filament_map_icon);
- calc_vertex_for_icons(dual_bbl ? 6 : 5, m_move_front_icon);
+ calc_vertex_for_icons(0, m_del_icon);
+ calc_vertex_for_icons(1, m_orient_icon);
+ calc_vertex_for_icons(2, m_arrange_icon);
+ calc_vertex_for_icons(3, m_lock_icon);
+ calc_vertex_for_icons(4, m_plate_settings_icon);
+ // ORCA also change bed_icon_count number in calc_vertex_for_icons() after adding or removing icons for circular shaped beds that uses vertical alingment for icons
+ bool dual_bbl = false;
+ PresetBundle* preset = wxGetApp().preset_bundle;
+ dual_bbl = (preset->is_bbl_vendor() && preset->get_printer_extruder_count() == 2);
+ calc_vertex_for_icons(dual_bbl ? 5 : 6, m_plate_filament_map_icon);
+ calc_vertex_for_icons(dual_bbl ? 6 : 5, m_move_front_icon);
- //calc_vertex_for_number(0, (m_plate_index < 9), m_plate_idx_icon);
- calc_vertex_for_number(0, false, m_plate_idx_icon);
- if (m_plater) {
+ calc_vertex_for_number(0, false, m_plate_idx_icon);
// calc vertex for plate name
generate_plate_name_texture();
}
@@ -3761,11 +3755,6 @@ void PartPlateList::init()
m_plate_cols = 1;
m_current_plate = 0;
- if (m_plater) {
- // In GUI mode
- set_default_wipe_tower_pos_for_plate(0);
- }
-
select_plate(0);
unprintable_plate.set_index(1);
@@ -4064,7 +4053,7 @@ void PartPlateList::release_icon_textures()
}
}
-void PartPlateList::set_default_wipe_tower_pos_for_plate(int plate_idx)
+void PartPlateList::set_default_wipe_tower_pos_for_plate(int plate_idx, bool init_pos)
{
DynamicConfig & proj_cfg = wxGetApp().preset_bundle->project_config;
ConfigOptionFloats *wipe_tower_x = proj_cfg.opt("wipe_tower_x");
@@ -4074,12 +4063,69 @@ void PartPlateList::set_default_wipe_tower_pos_for_plate(int plate_idx)
auto printer_structure_opt = wxGetApp().preset_bundle->printers.get_edited_preset().config.option>("printer_structure");
// set the default position, the same with print config(left top)
- ConfigOptionFloat wt_x_opt(WIPE_TOWER_DEFAULT_X_POS);
- ConfigOptionFloat wt_y_opt(WIPE_TOWER_DEFAULT_Y_POS);
+ float x = WIPE_TOWER_DEFAULT_X_POS;
+ float y = WIPE_TOWER_DEFAULT_Y_POS;
if (printer_structure_opt && printer_structure_opt->value == PrinterStructure::psI3) {
- wt_x_opt = ConfigOptionFloat(I3_WIPE_TOWER_DEFAULT_X_POS);
- wt_y_opt = ConfigOptionFloat(I3_WIPE_TOWER_DEFAULT_Y_POS);
+ x = I3_WIPE_TOWER_DEFAULT_X_POS;
+ y = I3_WIPE_TOWER_DEFAULT_Y_POS;
}
+
+ PartPlate *part_plate = get_plate(plate_idx);
+ Vec3d plate_origin = part_plate->get_origin();
+ BoundingBoxf3 plate_bbox = part_plate->get_bounding_box();
+ BoundingBoxf plate_bbox_2d(Vec2d(plate_bbox.min(0), plate_bbox.min(1)), Vec2d(plate_bbox.max(0), plate_bbox.max(1)));
+ const std::vector &extruder_areas = part_plate->get_extruder_areas();
+ for (const Pointfs &points : extruder_areas) {
+ BoundingBoxf bboxf(points);
+ plate_bbox_2d.min = plate_bbox_2d.min(0) >= bboxf.min(0) ? plate_bbox_2d.min : bboxf.min;
+ plate_bbox_2d.max = plate_bbox_2d.max(0) <= bboxf.max(0) ? plate_bbox_2d.max : bboxf.max;
+ }
+
+ coordf_t plate_bbox_x_min_local_coord = plate_bbox_2d.min(0) - plate_origin(0);
+ coordf_t plate_bbox_x_max_local_coord = plate_bbox_2d.max(0) - plate_origin(0);
+ coordf_t plate_bbox_y_max_local_coord = plate_bbox_2d.max(1) - plate_origin(1);
+
+ std::vector filament_maps = part_plate->get_real_filament_maps(proj_cfg);
+ DynamicPrintConfig full_config = wxGetApp().preset_bundle->full_config(false, filament_maps);
+ const DynamicPrintConfig &print_cfg = wxGetApp().preset_bundle->prints.get_edited_preset().config;
+ float w = dynamic_cast(print_cfg.option("prime_tower_width"))->value;
+ float v = dynamic_cast(full_config.option("prime_volume"))->value;
+ bool enable_wrapping = false;
+ const ConfigOptionBool *wrapping_opt = dynamic_cast(full_config.option("enable_wrapping_detection"));
+ if (wrapping_opt) enable_wrapping = wrapping_opt->value;
+ int nozzle_nums = wxGetApp().preset_bundle->get_printer_extruder_count();
+ Vec3d wipe_tower_size = part_plate->estimate_wipe_tower_size(print_cfg, w, v, nozzle_nums, init_pos ? 2 : 0, false, enable_wrapping);
+
+ if (!init_pos && (is_approx(wipe_tower_size(0), 0.0) || is_approx(wipe_tower_size(1), 0.0))) {
+ wipe_tower_size = part_plate->estimate_wipe_tower_size(print_cfg, w, v, nozzle_nums, 2, false, enable_wrapping);
+ }
+
+ // Compute brim-aware margin: brim extends outward from tower position
+ float brim_width = 0.f;
+ const ConfigOptionFloat *brim_opt = print_cfg.option("prime_tower_brim_width");
+ if (brim_opt) {
+ brim_width = brim_opt->value;
+ if (brim_width < 0) brim_width = WipeTower::get_auto_brim_by_height((float) wipe_tower_size.z());
+ }
+ const float margin = WIPE_TOWER_MARGIN + brim_width;
+
+ // clamp wipe tower position within plate boundaries
+ {
+ if (x + margin + wipe_tower_size(0) > plate_bbox_x_max_local_coord) {
+ x = plate_bbox_x_max_local_coord - wipe_tower_size(0) - margin;
+ } else if (x < margin + plate_bbox_x_min_local_coord) {
+ x = margin + plate_bbox_x_min_local_coord;
+ }
+
+ if (y + margin + wipe_tower_size(1) > plate_bbox_y_max_local_coord) {
+ y = plate_bbox_y_max_local_coord - wipe_tower_size(1) - margin;
+ } else if (y < margin) {
+ y = margin;
+ }
+ }
+
+ ConfigOptionFloat wt_x_opt(x);
+ ConfigOptionFloat wt_y_opt(y);
dynamic_cast(proj_cfg.option("wipe_tower_x"))->set_at(&wt_x_opt, plate_idx, 0);
dynamic_cast(proj_cfg.option("wipe_tower_y"))->set_at(&wt_y_opt, plate_idx, 0);
}
@@ -4190,6 +4236,11 @@ void PartPlateList::reinit()
//re-calc the bounding boxes
calc_bounding_boxes();
+ if (m_plater) {
+ // In GUI mode
+ set_default_wipe_tower_pos_for_plate(0, true);
+ }
+
return;
}
@@ -4261,7 +4312,7 @@ int PartPlateList::create_plate(bool adjust_position)
// update wipe tower config
if (m_plater) {
// In GUI mode
- set_default_wipe_tower_pos_for_plate(new_index);
+ set_default_wipe_tower_pos_for_plate(new_index, true);
}
unprintable_plate.set_index(new_index+1);
diff --git a/src/slic3r/GUI/PartPlate.hpp b/src/slic3r/GUI/PartPlate.hpp
index 70e3ec8e9a..9585c7aa95 100644
--- a/src/slic3r/GUI/PartPlate.hpp
+++ b/src/slic3r/GUI/PartPlate.hpp
@@ -628,13 +628,12 @@ class PartPlateList : public ObjectBase
void generate_icon_textures();
void release_icon_textures();
- void set_default_wipe_tower_pos_for_plate(int plate_idx);
-
friend class cereal::access;
friend class UndoRedo::StackImpl;
friend class PartPlate;
public:
+ void set_default_wipe_tower_pos_for_plate(int plate_idx, bool init_pos = false);
class BedTextureInfo {
public:
class TexturePart {
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 40b6fd9be6..4534b0886a 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -469,7 +469,6 @@ struct Sidebar::priv
ScalableButton * m_bpButton_ams_filament;
ScalableButton * m_bpButton_set_filament;
int m_menu_filament_id = -1;
- int filament_area_height;
wxScrolledWindow* m_panel_filament_content;
wxScrolledWindow* m_scrolledWindow_filament_content;
wxStaticLine* m_staticline2;
@@ -2106,14 +2105,10 @@ Sidebar::Sidebar(Plater *parent)
bSizer39->AddSpacer(FromDIP(SidebarProps::TitlebarMargin()));
// add filament content
- // ORCA use a height with user preference
- int filament_count_user = std::stoi(wxGetApp().app_config->get("filaments_area_preferred_count"));
- p->filament_area_height = std::ceil(filament_count_user * 0.5) * (30 + SidebarProps::ElementSpacing()) - SidebarProps::ElementSpacing();
-
p->m_panel_filament_content = new wxScrolledWindow( p->scrolled, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
p->m_panel_filament_content->SetScrollbars(0, 100, 1, 2);
p->m_panel_filament_content->SetScrollRate(0, 5);
- p->m_panel_filament_content->SetMaxSize(wxSize{-1, FromDIP(p->filament_area_height)}); // ORCA
+ //p->m_panel_filament_content->SetMaxSize(wxSize{-1, FromDIP(174)});
p->m_panel_filament_content->SetBackgroundColour(wxColour(255, 255, 255));
//wxBoxSizer* bSizer_filament_content;
@@ -2135,10 +2130,9 @@ Sidebar::Sidebar(Plater *parent)
sizer_filaments2->Add(p->sizer_filaments, 0, wxEXPAND, 0);
p->m_panel_filament_content->SetSizer(sizer_filaments2);
p->m_panel_filament_content->Layout();
- auto min_size = sizer_filaments2->GetMinSize();
- if (min_size.y > p->m_panel_filament_content->GetMaxHeight())
- min_size.y = p->m_panel_filament_content->GetMaxHeight();
- p->m_panel_filament_content->SetMinSize(min_size);
+
+ update_filaments_area_height(); // ORCA
+
scrolled_sizer->Add(p->m_panel_filament_content, 0, wxEXPAND | wxTOP | wxBOTTOM, FromDIP(SidebarProps::ContentMarginV())); // ORCA use vertical margin on parent otherwise it shows scrollbar even on 1 filament
}
@@ -2776,6 +2770,23 @@ void Sidebar::change_top_border_for_mode_sizer(bool increase_border)
#endif
}
+void Sidebar::update_filaments_area_height()
+// ORCA
+{
+ // ORCA use a height with user preference
+ auto left_sizer = p->sizer_filaments->GetItem((size_t) 0)->GetSizer();
+ auto combo_sizer = left_sizer->GetItem((size_t) 0)->GetSizer();
+ int preferred_rows = std::ceil(0.5 * std::stoi(wxGetApp().app_config->get("filaments_area_preferred_count")));
+ auto height_with_borders = combo_sizer->GetSize().GetHeight(); // gets height from sizer instead static numbers
+ p->m_panel_filament_content->SetMaxSize(wxSize{-1, preferred_rows * height_with_borders});
+
+ // fixes wxScrolledWindow not shrinks its height to content size
+ auto min_size = p->m_panel_filament_content->GetSizer()->GetMinSize();
+ if (min_size.y > p->m_panel_filament_content->GetMaxHeight())
+ min_size.y = p->m_panel_filament_content->GetMaxHeight();
+ p->m_panel_filament_content->SetMinSize({-1, min_size.y});
+}
+
void Sidebar::msw_rescale()
{
SetMinSize(wxSize(42 * wxGetApp().em_unit(), -1));
@@ -2846,6 +2857,9 @@ void Sidebar::msw_rescale()
for (PlaterPresetComboBox* combo : p->combos_filament)
combo->msw_rescale();
+ p->m_panel_filament_content->Layout();
+ update_filaments_area_height(); // ORCA resize after combos scaled
+
// BBS
//p->frequently_changed_parameters->msw_rescale();
//obj_list()->msw_rescale();
@@ -3024,10 +3038,7 @@ void Sidebar::on_filament_count_change(size_t num_filaments)
}
}
- auto min_size = p->m_panel_filament_content->GetSizer()->GetMinSize();
- if (min_size.y > p->m_panel_filament_content->GetMaxHeight())
- min_size.y = p->m_panel_filament_content->GetMaxHeight();
- p->m_panel_filament_content->SetMinSize(min_size);
+ update_filaments_area_height(); // ORCA
Layout();
p->m_panel_filament_title->Refresh();
@@ -3087,10 +3098,7 @@ void Sidebar::on_filaments_delete(size_t filament_id)
p->combos_filament[idx]->update();
}
- auto min_size = p->m_panel_filament_content->GetSizer()->GetMinSize();
- if (min_size.y > p->m_panel_filament_content->GetMaxHeight())
- min_size.y = p->m_panel_filament_content->GetMaxHeight();
- p->m_panel_filament_content->SetMinSize(min_size);
+ update_filaments_area_height(); // ORCA
Layout();
p->m_panel_filament_title->Refresh();
@@ -3524,11 +3532,8 @@ void Sidebar::sync_ams_list(bool is_from_big_sync_btn)
for (auto& c : p->combos_filament)
c->update();
// Expand filament list
- p->m_panel_filament_content->SetMaxSize({-1, FromDIP(p->filament_area_height)}); // ORCA
- auto min_size = p->m_panel_filament_content->GetSizer()->GetMinSize();
- if (min_size.y > p->m_panel_filament_content->GetMaxHeight())
- min_size.y = p->m_panel_filament_content->GetMaxHeight();
- p->m_panel_filament_content->SetMinSize({-1, min_size.y});
+ update_filaments_area_height(); // ORCA
+
// BBS:Synchronized consumables information
// auto calculation of flushing volumes
for (int i = 0; i < p->combos_filament.size(); ++i) {
@@ -10416,13 +10421,13 @@ void Plater::priv::set_project_name(const wxString& project_name)
BOOST_LOG_TRIVIAL(trace) << __FUNCTION__ << __LINE__ << " project is:" << project_name;
m_project_name = project_name;
//update topbar title
-#ifdef __WINDOWS__
- wxGetApp().mainframe->SetTitle(m_project_name + " - OrcaSlicer");
- wxGetApp().mainframe->topbar()->SetTitle(m_project_name);
-#else
+#ifdef __APPLE__
wxGetApp().mainframe->SetTitle(m_project_name);
if (!m_project_name.IsEmpty())
wxGetApp().mainframe->update_title_colour_after_set_title();
+#else
+ wxGetApp().mainframe->SetTitle(m_project_name + " - OrcaSlicer");
+ wxGetApp().mainframe->topbar()->SetTitle(m_project_name);
#endif
}
@@ -10437,11 +10442,12 @@ void Plater::priv::update_title_dirty_status()
else
title = m_project_name;
-#ifdef __WINDOWS__
- wxGetApp().mainframe->topbar()->SetTitle(title);
+#ifdef __APPLE__
+ wxGetApp().mainframe->SetTitle(title);
+ wxGetApp().mainframe->update_title_colour_after_set_title();
#else
wxGetApp().mainframe->SetTitle(title);
- wxGetApp().mainframe->update_title_colour_after_set_title();
+ wxGetApp().mainframe->topbar()->SetTitle(title);
#endif
}
@@ -10499,11 +10505,16 @@ void Plater::priv::init_notification_manager()
void Plater::priv::update_objects_position_when_select_preset(const std::function &select_prest)
{
- // TODO: Orca hack
select_prest();
wxGetApp().obj_list()->update_object_list_by_printer_technology();
+ // Re-clamp wipe tower positions to new bed boundaries after preset change
+ PartPlateList &cur_plate_list = this->partplate_list;
+ for (size_t plate_id = 0; plate_id < cur_plate_list.get_plate_list().size(); ++plate_id) {
+ cur_plate_list.set_default_wipe_tower_pos_for_plate(plate_id);
+ }
+
update();
}
diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp
index 89b9f560c4..3708e52b42 100644
--- a/src/slic3r/GUI/Plater.hpp
+++ b/src/slic3r/GUI/Plater.hpp
@@ -172,6 +172,7 @@ public:
void set_bed_type_accord_combox(BedType bed_type);
bool reset_bed_type_combox_choices(bool is_sidebar_init = false);
void change_top_border_for_mode_sizer(bool increase_border);
+ void update_filaments_area_height();
void msw_rescale();
void sys_color_changed();
void search();
diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp
index d668fcb099..1a52f886ca 100644
--- a/src/slic3r/GUI/Preferences.cpp
+++ b/src/slic3r/GUI/Preferences.cpp
@@ -318,13 +318,10 @@ wxBoxSizer *PreferencesDialog::create_item_language_combobox(wxString title, wxS
m_current_language_selected = combobox->GetSelection();
if (m_current_language_selected >= 0 && m_current_language_selected < vlist.size()) {
- app_config->set(param, vlist[m_current_language_selected]->CanonicalName.ToUTF8().data());
-
- wxGetApp().load_language(vlist[m_current_language_selected]->CanonicalName, false);
- Close();
- // Reparent(nullptr);
- GetParent()->RemoveChild(this);
- wxGetApp().recreate_GUI(_L("Changing application language"));
+ m_pending_language = vlist[m_current_language_selected]->CanonicalName.ToUTF8().data();
+ m_recreate_GUI = true;
+ EndModal(wxID_OK);
+ return;
}
}
@@ -592,6 +589,14 @@ wxBoxSizer *PreferencesDialog::create_item_spinctrl(wxString title, wxString tit
e.Skip();
});
+ input->Bind(wxEVT_SPINCTRL, [this, param, input, onchange](wxCommandEvent& e) {
+ auto value = input->GetValue();
+ app_config->set(param, std::to_string(value));
+ app_config->save();
+ if (onchange != nullptr) onchange(value);
+ e.Skip();
+ });
+
input->Bind(wxEVT_KILL_FOCUS, [this, param, input, onchange](wxFocusEvent &e) {
auto value = input->GetValue();
app_config->set(param, std::to_string(value));
@@ -1398,7 +1403,13 @@ void PreferencesDialog::create_items()
"group_filament_presets", {_L("All"), _L("None"), _L("By type"), _L("By vendor")}, [](wxString value) {wxGetApp().plater()->sidebar().update_presets(Preset::TYPE_FILAMENT);});
g_sizer->Add(item_filament_preset_grouping);
- auto item_filament_area_height = create_item_spinctrl(_L("Optimize filaments area height for..."), _L("(Requires restart)"), _L("filaments"), _L("Optimizes filament area maximum height by chosen filament count."), "filaments_area_preferred_count", 8, 99);
+ // prevent burst calling on keyboard / spin events
+ m_filament_height_timer.Bind(wxEVT_TIMER, [this](wxTimerEvent&) {
+ wxGetApp().plater()->sidebar().update_filaments_area_height();
+ UpdateSidebarLayout();
+ });
+ auto item_filament_area_height = create_item_spinctrl(_L("Optimize filaments area height for..."), "", _L("filaments"), _L("Optimizes filament area maximum height by chosen filament count."),
+ "filaments_area_preferred_count", 8, 99, [this](int value) {m_filament_height_timer.StartOnce(500);});
g_sizer->Add(item_filament_area_height);
//// GENERAL > Features
@@ -1802,10 +1813,10 @@ void PreferencesDialog::create_shortcuts_page()
std::vector mouse_supported;
Split(app_config->get("mouse_supported"), "/", mouse_supported);
- auto item_rotate_view = create_item_multiple_combobox(_L("Rotate of view"), _L("Rotate of view"), "rotate_view", keyboard_supported,
+ auto item_rotate_view = create_item_multiple_combobox(_L("Rotate view"), _L("Rotate view"), "rotate_view", keyboard_supported,
mouse_supported);
- auto item_move_view = create_item_multiple_combobox(_L("Move of view"), _L("Move of view"), "move_view", keyboard_supported, mouse_supported);
- auto item_zoom_view = create_item_multiple_combobox(_L("Zoom of view"), _L("Zoom of view"), "rotate_view", keyboard_supported, mouse_supported);
+ auto item_move_view = create_item_multiple_combobox(_L("Pan view"), _L("Pan view"), "move_view", keyboard_supported, mouse_supported);
+ auto item_zoom_view = create_item_multiple_combobox(_L("Zoom view"), _L("Zoom view"), "rotate_view", keyboard_supported, mouse_supported);
auto title_other = create_item_title(_L("Other"));
auto item_other = create_item_checkbox(_L("Mouse wheel reverses when zooming"), _L("Mouse wheel reverses when zooming"), "mouse_wheel");
@@ -1950,4 +1961,22 @@ wxBoxSizer* PreferencesDialog::create_debug_page()
return bSizer;
}
+void PreferencesDialog::UpdateSidebarLayout()
+{
+ Plater* plater = wxGetApp().plater();
+ if (!plater) return;
+
+ Sidebar& sidebar = plater->sidebar();
+
+ sidebar.Freeze();
+
+ sidebar.Layout();
+ //plater->Layout();
+ //wxGetApp().mainframe->Layout();
+
+ sidebar.Thaw();
+
+ plater->PostSizeEvent();
+}
+
}} // namespace Slic3r::GUI
diff --git a/src/slic3r/GUI/Preferences.hpp b/src/slic3r/GUI/Preferences.hpp
index 886f5f7cec..73e61938d6 100644
--- a/src/slic3r/GUI/Preferences.hpp
+++ b/src/slic3r/GUI/Preferences.hpp
@@ -6,6 +6,7 @@
#include
#include
+#include
#include
#include
#include