Merge branch 'main' into belt/baseChanges

This commit is contained in:
Joseph Robertson
2026-04-13 21:34:34 -05:00
committed by GitHub
94 changed files with 4312 additions and 378 deletions

View File

@@ -75,6 +75,9 @@
#include "GUI_App.hpp"
#include "GuiColor.hpp"
#include "GUI_ObjectList.hpp"
#ifdef __WXGTK__
#include "LinuxDisplayBackend.hpp"
#endif
#include "GUI_Utils.hpp"
#include "GUI_Factories.hpp"
#include "wxExtensions.hpp"
@@ -348,6 +351,57 @@ void SlicedInfo::SetTextAndShow(SlicedInfoIdx idx, const wxString& text, const w
static wxString temp_dir;
namespace {
#ifdef __WXGTK__
wxString sanitize_window_layout_for_wayland(const wxString& layout, bool* removed_floating_state = nullptr)
{
if (!Slic3r::GUI::is_running_on_wayland() || layout.empty()) {
if (removed_floating_state != nullptr)
*removed_floating_state = false;
return layout;
}
static const std::regex state_pattern(R"(state=(\d+);)");
constexpr unsigned int disabled_wayland_flags =
static_cast<unsigned int>(wxAuiPaneInfo::optionFloating) |
static_cast<unsigned int>(wxAuiPaneInfo::optionFloatable);
const std::string input = layout.utf8_string();
std::string output;
output.reserve(input.size());
bool modified = false;
std::smatch match;
auto search_start = input.cbegin();
while (std::regex_search(search_start, input.cend(), match, state_pattern)) {
output.append(search_start, match[0].first);
try {
const unsigned long state = std::stoul(match[1].str());
const unsigned long sanitized_state = state & ~static_cast<unsigned long>(disabled_wayland_flags);
modified = modified || sanitized_state != state;
output += "state=" + std::to_string(sanitized_state) + ";";
} catch (const std::exception&) {
output += match[0].str();
}
search_start = match[0].second;
}
output.append(search_start, input.cend());
if (removed_floating_state != nullptr)
*removed_floating_state = modified;
return modified ? wxString::FromUTF8(output) : layout;
}
#endif
} // namespace
// Sidebar / private
enum class ActionButtonType : int {
@@ -1781,7 +1835,11 @@ Sidebar::Sidebar(Plater *parent)
e.Skip();
});
w->Bind(wxEVT_LEAVE_WINDOW, [this, panel_color](wxMouseEvent &e) {
wxWindow* next_w = wxFindWindowAtPoint(wxGetMousePosition());
// Use event-relative coords instead of wxGetMousePosition() which
// returns (0,0) on Wayland for global screen coordinates.
wxWindow* evtObj = dynamic_cast<wxWindow*>(e.GetEventObject());
wxPoint screenPos = evtObj ? evtObj->ClientToScreen(e.GetPosition()) : wxGetMousePosition();
wxWindow* next_w = wxFindWindowAtPoint(screenPos);
if (!next_w || !p->panel_printer_preset->IsDescendant(next_w)){
if(!p->combo_printer->HasFocus())
p->panel_printer_preset->SetBorderColor(panel_color.bd_normal);
@@ -1843,7 +1901,11 @@ Sidebar::Sidebar(Plater *parent)
e.Skip();
});
w->Bind(wxEVT_LEAVE_WINDOW, [this, panel_color](wxMouseEvent &e) {
wxWindow* next_w = wxFindWindowAtPoint(wxGetMousePosition());
// Use event-relative coords instead of wxGetMousePosition() which
// returns (0,0) on Wayland for global screen coordinates.
wxWindow* evtObj = dynamic_cast<wxWindow*>(e.GetEventObject());
wxPoint screenPos = evtObj ? evtObj->ClientToScreen(e.GetPosition()) : wxGetMousePosition();
wxWindow* next_w = wxFindWindowAtPoint(screenPos);
if (!p->combo_nozzle_dia->HasFocus() && (!next_w || !p->panel_nozzle_dia->IsDescendant(next_w)))
p->panel_nozzle_dia->SetBorderColor(panel_color.bd_normal);
e.Skip();
@@ -1913,7 +1975,11 @@ Sidebar::Sidebar(Plater *parent)
e.Skip();
});
w->Bind(wxEVT_LEAVE_WINDOW, [this, w, panel_color](wxMouseEvent &e) {
wxWindow* next_w = wxFindWindowAtPoint(wxGetMousePosition());
// Use event-relative coords instead of wxGetMousePosition() which
// returns (0,0) on Wayland for global screen coordinates.
wxWindow* evtObj = dynamic_cast<wxWindow*>(e.GetEventObject());
wxPoint screenPos = evtObj ? evtObj->ClientToScreen(e.GetPosition()) : wxGetMousePosition();
wxWindow* next_w = wxFindWindowAtPoint(screenPos);
if (!p->combo_printer_bed->HasFocus() && (!next_w || !p->panel_printer_bed->IsDescendant(next_w)))
p->panel_printer_bed->SetBorderColor(panel_color.bd_normal);
if(w == p->image_printer_bed)
@@ -4835,8 +4901,16 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
{
m_is_dark = wxGetApp().app_config->get("dark_color_mode") == "1";
#ifdef __WXGTK__
const bool disable_wayland_floating = Slic3r::GUI::is_running_on_wayland();
#endif
m_aui_mgr.SetManagedWindow(q);
m_aui_mgr.SetDockSizeConstraint(1, 1);
#ifdef __WXGTK__
if (disable_wayland_floating)
m_aui_mgr.SetFlags(m_aui_mgr.GetFlags() & ~wxAUI_MGR_ALLOW_FLOATING);
#endif
//m_aui_mgr.GetArtProvider()->SetMetric(wxAUI_DOCKART_PANE_BORDER_SIZE, 0);
//m_aui_mgr.GetArtProvider()->SetMetric(wxAUI_DOCKART_SASH_SIZE, 2);
m_aui_mgr.GetArtProvider()->SetMetric(wxAUI_DOCKART_CAPTION_SIZE, 18);
@@ -4928,7 +5002,6 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
.CloseButton(false)
.TopDockable(false)
.BottomDockable(false)
.Floatable(true)
.BestSize(wxSize(39 * wxGetApp().em_unit(), 90 * wxGetApp().em_unit())));
auto* panel_sizer = new wxBoxSizer(wxHORIZONTAL);
@@ -4947,7 +5020,19 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
const auto cfg = wxGetApp().app_config;
wxString layout = wxString::FromUTF8(cfg->get("window_layout"));
if (!layout.empty()) {
m_aui_mgr.LoadPerspective(layout, false);
bool removed_floating_state = false;
#ifdef __WXGTK__
if (disable_wayland_floating)
layout = sanitize_window_layout_for_wayland(layout, &removed_floating_state);
#endif
if (!m_aui_mgr.LoadPerspective(layout, false)) {
BOOST_LOG_TRIVIAL(warning) << "Failed to restore saved window layout";
m_aui_mgr.LoadPerspective(m_default_window_layout, false);
} else if (removed_floating_state) {
BOOST_LOG_TRIVIAL(info) << "Removed floating AUI state from saved window layout for Wayland";
}
sidebar_layout.is_collapsed = !sidebar.IsShown();
}
@@ -8886,11 +8971,9 @@ void Plater::priv::set_current_panel(wxPanel* panel, bool no_slice)
wxPanel* old_panel = current_panel;
//#if BBL_HAS_FIRST_PAGE
if (!old_panel) {
//BBS: only switch to the first panel when visible
// Wayland may report the first canvas as not yet shown while the frame is still mapping.
// Keep the panel switch anyway so handlers are bound and the first paint can initialize GL later.
panel->Show();
//dynamic_cast<View3D *>(panel)->get_canvas3d()->render();
if (!panel->IsShownOnScreen())
return;
}
//#endif
current_panel = panel;
@@ -10050,6 +10133,9 @@ void Plater::priv::on_action_split_volumes(SimpleEvent&)
void Plater::priv::on_object_select(SimpleEvent& evt)
{
if (wxGetApp().is_closing())
return;
wxGetApp().obj_list()->update_selections();
selection_changed();
}