From d976d9eb0cc483e6dfda12bba799c065934cb87f Mon Sep 17 00:00:00 2001 From: Fernando Marino` Date: Sun, 27 Sep 2026 13:19:27 +0200 Subject: [PATCH 1/5] Release the DC after GetDC in get_dpi_for_window and font enumeration (#15919) get_dpi_for_window's pre-8.1 fallback and get_font_list_by_enumeration both called GetDC without a matching ReleaseDC, leaking a GDI handle each call. get_dpi_for_window runs on every mouse-move over the 3D viewport, so the leak exhausts the per-process GDI handle limit and hangs the app within minutes on Windows 7/8. Co-authored-by: Fernando Marino Co-authored-by: Claude Sonnet 5 --- src/libslic3r/Emboss.cpp | 1 + src/slic3r/GUI/GUI_Utils.cpp | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/Emboss.cpp b/src/libslic3r/Emboss.cpp index 34d9a93590..38c035806c 100644 --- a/src/libslic3r/Emboss.cpp +++ b/src/libslic3r/Emboss.cpp @@ -990,6 +990,7 @@ EmbossStyles Emboss::get_font_list_by_enumeration() { std::vector font_names; EnumFontFamilies(hDC, (LPCTSTR) NULL, EnumFamCallBack, (LPARAM) &font_names); + ReleaseDC(NULL, hDC); EmbossStyles font_list; for (const std::wstring &font_name : font_names) { diff --git a/src/slic3r/GUI/GUI_Utils.cpp b/src/slic3r/GUI/GUI_Utils.cpp index 10dd29c9c1..5c55e341c9 100644 --- a/src/slic3r/GUI/GUI_Utils.cpp +++ b/src/slic3r/GUI/GUI_Utils.cpp @@ -247,7 +247,9 @@ int get_dpi_for_window(const wxWindow *window) const HDC hdc = GetDC(hwnd); if (hdc == NULL) { return DPI_DEFAULT; } - return GetDeviceCaps(hdc, LOGPIXELSX); + const int dpi = GetDeviceCaps(hdc, LOGPIXELSX); + ReleaseDC(hwnd, hdc); // GetDC's handle must be released, unlike GetWindowDC's + return dpi; } #elif defined __linux__ // TODO From bd65cce3f42bb705526a59c22c6331ce8e849e75 Mon Sep 17 00:00:00 2001 From: Kiss Lorand <50251547+kisslorand@users.noreply.github.com> Date: Sun, 27 Sep 2026 16:58:10 +0300 Subject: [PATCH 2/5] Unify GUI scroll rates (#15902) Use DPI-aware 20 DIP vertical scrolling for general-purpose GUI scroll areas. Keep list-based views aligned to their item height so one wheel increment follows the visible row rhythm. This makes Preferences and other dialogs scroll consistently across Windows DPI settings. --- src/slic3r/GUI/AmsMappingPopup.cpp | 8 ++++---- src/slic3r/GUI/Auxiliary.cpp | 2 +- src/slic3r/GUI/BindDialog.cpp | 2 +- src/slic3r/GUI/CAD/DesignPanel.cpp | 2 +- src/slic3r/GUI/CaliHistoryDialog.cpp | 2 +- src/slic3r/GUI/CalibrationPanel.cpp | 2 +- src/slic3r/GUI/CalibrationWizard.cpp | 2 +- src/slic3r/GUI/CalibrationWizardPage.cpp | 2 +- src/slic3r/GUI/ConfigWizard.cpp | 2 +- src/slic3r/GUI/CreatePresetsDialog.cpp | 10 +++++----- src/slic3r/GUI/DeviceErrorDialog.cpp | 2 +- src/slic3r/GUI/GUI_ObjectTable.cpp | 4 ++-- src/slic3r/GUI/HMSPanel.cpp | 2 +- src/slic3r/GUI/KBShortcutsDialog.cpp | 2 +- src/slic3r/GUI/MixedFilamentDialog.cpp | 2 +- src/slic3r/GUI/MsgDialog.cpp | 2 +- src/slic3r/GUI/MultiMachineManagerPage.cpp | 2 +- src/slic3r/GUI/MultiMachinePage.cpp | 2 +- src/slic3r/GUI/MultiTaskManagerPage.cpp | 4 ++-- src/slic3r/GUI/ObjColorDialog.cpp | 2 +- src/slic3r/GUI/ParamsPanel.cpp | 2 +- src/slic3r/GUI/PartSkipDialog.cpp | 2 +- src/slic3r/GUI/Plater.cpp | 6 ++---- src/slic3r/GUI/Preferences.cpp | 2 +- src/slic3r/GUI/PrintOptionsDialog.cpp | 2 +- src/slic3r/GUI/PublishSettingsDialog.cpp | 2 +- src/slic3r/GUI/ReleaseNote.cpp | 12 ++++++------ src/slic3r/GUI/SafetyOptionsDialog.cpp | 4 ++-- src/slic3r/GUI/SelectMachine.cpp | 4 ++-- src/slic3r/GUI/SelectMachinePop.cpp | 2 +- src/slic3r/GUI/SendMultiMachinePage.cpp | 4 ++-- src/slic3r/GUI/SendToPrinter.cpp | 2 +- src/slic3r/GUI/StatusPanel.cpp | 2 +- src/slic3r/GUI/SyncAmsInfoDialog.cpp | 2 +- src/slic3r/GUI/TextureImportDialog.cpp | 6 +++--- src/slic3r/GUI/UnsavedChangesDialog.cpp | 2 +- src/slic3r/GUI/UpdateDialogs.cpp | 2 +- src/slic3r/GUI/UpgradePanel.cpp | 2 +- src/slic3r/GUI/Widgets/CheckList.cpp | 4 ++-- src/slic3r/GUI/Widgets/ProgressDialog.cpp | 2 +- src/slic3r/GUI/wxExtensions.cpp | 2 +- 41 files changed, 62 insertions(+), 64 deletions(-) diff --git a/src/slic3r/GUI/AmsMappingPopup.cpp b/src/slic3r/GUI/AmsMappingPopup.cpp index 22ffaef034..32a580845d 100644 --- a/src/slic3r/GUI/AmsMappingPopup.cpp +++ b/src/slic3r/GUI/AmsMappingPopup.cpp @@ -819,7 +819,7 @@ AmsMapingPopup::AmsMapingPopup(wxWindow *parent, bool use_in_sync_dialog) : m_scrolled_window = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL | wxHSCROLL); m_scrolled_window->SetBackgroundColour(*wxWHITE); - m_scrolled_window->SetScrollRate(0, FromDIP(10)); + m_scrolled_window->SetScrollRate(0, FromDIP(20)); wxBoxSizer *title_sizer_h= new wxBoxSizer(wxHORIZONTAL); wxBoxSizer *title_sizer_v = new wxBoxSizer(wxVERTICAL); @@ -1000,13 +1000,13 @@ AmsMapingPopup::AmsMapingPopup(wxWindow *parent, bool use_in_sync_dialog) : show_pos.x = screen_size.GetLeft(); m_scrolled_window->SetMaxSize(wxSize(screen_size.GetWidth(), popup_height)); m_scrolled_window->SetMinSize(wxSize(screen_size.GetWidth(), popup_height)); - m_scrolled_window->SetScrollRate(FromDIP(10), FromDIP(10)); + m_scrolled_window->SetScrollRate(FromDIP(10), FromDIP(20)); } else { m_scrolled_window->SetMaxSize(wxSize(popup_width, popup_height)); m_scrolled_window->SetMinSize(wxSize(popup_width, popup_height)); - m_scrolled_window->SetScrollRate(0, FromDIP(10)); + m_scrolled_window->SetScrollRate(0, FromDIP(20)); } //Vertical Direction Processing @@ -2117,7 +2117,7 @@ void AmsReplaceMaterialDialog::create() identical_filament->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#009688"))); // Orca: accent teal (not brand green) m_scrollview_groups = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL | wxVSCROLL); - m_scrollview_groups->SetScrollRate(5, 5); + m_scrollview_groups->SetScrollRate(5, FromDIP(20)); //m_scrollview_groups->SetMinSize(wxSize(400, 400)); //m_scrollview_groups->SetMaxSize(wxSize(400, 400)); m_scrollview_sizer = new wxBoxSizer(wxVERTICAL); diff --git a/src/slic3r/GUI/Auxiliary.cpp b/src/slic3r/GUI/Auxiliary.cpp index 14b8d238a2..444daa4f91 100644 --- a/src/slic3r/GUI/Auxiliary.cpp +++ b/src/slic3r/GUI/Auxiliary.cpp @@ -606,7 +606,7 @@ AuFolderPanel::AuFolderPanel(wxWindow *parent, AuxiliaryFolderType type, wxWindo wxBoxSizer *sizer_main = new wxBoxSizer(wxVERTICAL); m_scrolledWindow = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL | wxVSCROLL); - m_scrolledWindow->SetScrollRate(5, 5); + m_scrolledWindow->SetScrollRate(5, FromDIP(20)); wxBoxSizer *sizer_body = new wxBoxSizer(wxVERTICAL); wxBoxSizer *sizer_top = new wxBoxSizer(wxHORIZONTAL); diff --git a/src/slic3r/GUI/BindDialog.cpp b/src/slic3r/GUI/BindDialog.cpp index 4c6d3c249d..883765751b 100644 --- a/src/slic3r/GUI/BindDialog.cpp +++ b/src/slic3r/GUI/BindDialog.cpp @@ -558,7 +558,7 @@ PingCodeBindDialog::~PingCodeBindDialog() { //show bind failed info m_sw_bind_failed_info = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(450), FromDIP(300)), wxVSCROLL); m_sw_bind_failed_info->SetBackgroundColour(*wxWHITE); - m_sw_bind_failed_info->SetScrollRate(5, 5); + m_sw_bind_failed_info->SetScrollRate(5, FromDIP(20)); m_sw_bind_failed_info->SetMinSize(wxSize(FromDIP(450), FromDIP(90))); m_sw_bind_failed_info->SetMaxSize(wxSize(FromDIP(450), FromDIP(90))); diff --git a/src/slic3r/GUI/CAD/DesignPanel.cpp b/src/slic3r/GUI/CAD/DesignPanel.cpp index d6e0329ef8..0b4f86a618 100644 --- a/src/slic3r/GUI/CAD/DesignPanel.cpp +++ b/src/slic3r/GUI/CAD/DesignPanel.cpp @@ -3521,7 +3521,7 @@ DesignPanel::DesignPanel(wxWindow* parent) cards->Show(m_box_mate, false, true); m_form->FitInside(); - m_form->SetScrollRate(0, 10); // vertical only, like Prepare's sidebar: never scroll labels out + m_form->SetScrollRate(0, FromDIP(20)); // vertical only, like Prepare's sidebar: never scroll labels out m_form->SetMinSize(wxSize(264, -1)); // Right column: a small view toolbar over the live 3D viewport that mirrors diff --git a/src/slic3r/GUI/CaliHistoryDialog.cpp b/src/slic3r/GUI/CaliHistoryDialog.cpp index 3ce8c1bb81..7055f441ac 100644 --- a/src/slic3r/GUI/CaliHistoryDialog.cpp +++ b/src/slic3r/GUI/CaliHistoryDialog.cpp @@ -115,7 +115,7 @@ HistoryWindow::HistoryWindow(wxWindow* parent, const std::vector& auto main_sizer = new wxBoxSizer(wxVERTICAL); auto scroll_window = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL | wxVSCROLL); - scroll_window->SetScrollRate(5, 5); + scroll_window->SetScrollRate(5, FromDIP(20)); scroll_window->SetBackgroundColour(*wxWHITE); scroll_window->SetMinSize(HISTORY_WINDOW_SIZE); scroll_window->SetSize(HISTORY_WINDOW_SIZE); diff --git a/src/slic3r/GUI/CalibrationPanel.cpp b/src/slic3r/GUI/CalibrationPanel.cpp index fca9fa2be9..d906a8211c 100644 --- a/src/slic3r/GUI/CalibrationPanel.cpp +++ b/src/slic3r/GUI/CalibrationPanel.cpp @@ -207,7 +207,7 @@ SelectMObjectPopup::SelectMObjectPopup(wxWindow* parent) m_scrolledWindow = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, SELECT_MACHINE_LIST_SIZE, wxHSCROLL | wxVSCROLL); m_scrolledWindow->SetBackgroundColour(*wxWHITE); m_scrolledWindow->SetMinSize(SELECT_MACHINE_LIST_SIZE); - m_scrolledWindow->SetScrollRate(0, 5); + m_scrolledWindow->SetScrollRate(0, SELECT_MACHINE_ITEM_SIZE.y); auto m_sizxer_scrolledWindow = new wxBoxSizer(wxVERTICAL); m_scrolledWindow->SetSizer(m_sizxer_scrolledWindow); m_scrolledWindow->Layout(); diff --git a/src/slic3r/GUI/CalibrationWizard.cpp b/src/slic3r/GUI/CalibrationWizard.cpp index fa42143f06..4d6e694380 100644 --- a/src/slic3r/GUI/CalibrationWizard.cpp +++ b/src/slic3r/GUI/CalibrationWizard.cpp @@ -81,7 +81,7 @@ CalibrationWizard::CalibrationWizard(wxWindow* parent, CalibMode mode, wxWindowI wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL); m_scrolledWindow = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL | wxVSCROLL); - m_scrolledWindow->SetScrollRate(5, 5); + m_scrolledWindow->SetScrollRate(5, FromDIP(20)); m_scrolledWindow->SetBackgroundColour(*wxWHITE); wxBoxSizer* padding_sizer = new wxBoxSizer(wxHORIZONTAL); diff --git a/src/slic3r/GUI/CalibrationWizardPage.cpp b/src/slic3r/GUI/CalibrationWizardPage.cpp index c117ec8857..ff02feab68 100644 --- a/src/slic3r/GUI/CalibrationWizardPage.cpp +++ b/src/slic3r/GUI/CalibrationWizardPage.cpp @@ -834,7 +834,7 @@ void CaliPageSendingPanel::create(wxWindow* parent) m_sw_print_failed_info = new wxScrolledWindow(parent, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(380), FromDIP(125)), wxVSCROLL); m_sw_print_failed_info->SetBackgroundColour(*wxWHITE); - m_sw_print_failed_info->SetScrollRate(0, 5); + m_sw_print_failed_info->SetScrollRate(0, FromDIP(20)); m_sw_print_failed_info->SetMinSize(wxSize(FromDIP(380), FromDIP(125))); m_sw_print_failed_info->SetMaxSize(wxSize(FromDIP(380), FromDIP(125))); diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index 412df19091..7aaa0e7865 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -2710,7 +2710,7 @@ ConfigWizard::ConfigWizard(wxWindow *parent) SetSizerAndFit(vsizer); // We can now enable scrolling on hscroll - p->hscroll->SetScrollRate(30, 30); + p->hscroll->SetScrollRate(30, FromDIP(20)); on_window_geometry(this, [this]() { p->init_dialog_size(); diff --git a/src/slic3r/GUI/CreatePresetsDialog.cpp b/src/slic3r/GUI/CreatePresetsDialog.cpp index e6fb6e0782..16b9a5765d 100644 --- a/src/slic3r/GUI/CreatePresetsDialog.cpp +++ b/src/slic3r/GUI/CreatePresetsDialog.cpp @@ -699,7 +699,7 @@ CreateFilamentPresetDialog::CreateFilamentPresetDialog(wxWindow *parent) m_scrolled_preset_panel = new wxScrolledWindow(this, wxID_ANY); m_scrolled_preset_panel->SetMaxSize(wxSize(-1, FromDIP(350))); m_scrolled_preset_panel->SetBackgroundColour(*wxWHITE); - m_scrolled_preset_panel->SetScrollRate(5, 5); + m_scrolled_preset_panel->SetScrollRate(5, FromDIP(20)); m_scrolled_sizer = new wxBoxSizer(wxVERTICAL); m_scrolled_sizer->Add(create_item(FilamentOptionType::PRESET_FOR_PRINTER), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(5)); m_scrolled_sizer->Add(0, 0, 0, wxTOP, FromDIP(5)); @@ -1593,7 +1593,7 @@ CreatePrinterPresetDialog::CreatePrinterPresetDialog(wxWindow *parent) m_page1 = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize); m_page1->SetBackgroundColour(*wxWHITE); - m_page1->SetScrollRate(5, 5); + m_page1->SetScrollRate(5, FromDIP(20)); m_page2 = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize); m_page2->SetBackgroundColour(*wxWHITE); create_printer_page1(m_page1); @@ -2652,7 +2652,7 @@ wxBoxSizer *CreatePrinterPresetDialog::create_presets_template_item(wxWindow *pa wxBoxSizer *vertical_sizer = new wxBoxSizer(wxVERTICAL); m_scrolled_preset_window = new wxScrolledWindow(parent); - m_scrolled_preset_window->SetScrollRate(5, 5); + m_scrolled_preset_window->SetScrollRate(5, FromDIP(20)); m_scrolled_preset_window->SetBackgroundColour(*wxWHITE); //m_scrolled_preset_window->SetMinSize(wxSize(FromDIP(1500), FromDIP(-1))); m_scrolled_preset_window->SetMaxSize(wxSize(FromDIP(1500), FromDIP(-1))); @@ -4297,7 +4297,7 @@ wxBoxSizer *ExportConfigsDialog::create_select_printer(wxWindow *parent) optionSizer->SetMinSize(OPTION_SIZE); horizontal_sizer->Add(optionSizer, 0, wxEXPAND | wxALL, FromDIP(10)); m_scrolled_preset_window = new wxScrolledWindow(parent); - m_scrolled_preset_window->SetScrollRate(5, 5); + m_scrolled_preset_window->SetScrollRate(5, FromDIP(20)); m_scrolled_preset_window->SetBackgroundColour(*wxWHITE); m_scrolled_preset_window->SetMaxSize(wxSize(FromDIP(660), FromDIP(400))); m_scrolled_preset_window->SetSize(wxSize(FromDIP(660), FromDIP(400))); @@ -4736,7 +4736,7 @@ wxBoxSizer *EditFilamentPresetDialog::create_preset_tree_sizer() { wxBoxSizer *filament_preset_tree_sizer = new wxBoxSizer(wxHORIZONTAL); m_preset_tree_window = new wxScrolledWindow(this); - m_preset_tree_window->SetScrollRate(5, 5); + m_preset_tree_window->SetScrollRate(5, FromDIP(20)); m_preset_tree_window->SetBackgroundColour(PRINTER_LIST_COLOUR); m_preset_tree_window->SetMinSize(wxSize(-1, FromDIP(400))); m_preset_tree_window->SetMaxSize(wxSize(-1, FromDIP(300))); diff --git a/src/slic3r/GUI/DeviceErrorDialog.cpp b/src/slic3r/GUI/DeviceErrorDialog.cpp index 9790db67bd..4e81135a75 100644 --- a/src/slic3r/GUI/DeviceErrorDialog.cpp +++ b/src/slic3r/GUI/DeviceErrorDialog.cpp @@ -39,7 +39,7 @@ DeviceErrorDialog::DeviceErrorDialog(MachineObject* obj, wxWindow* parent, wxWin m_line_top->SetBackgroundColour(wxColour(166, 169, 170)); m_scroll_area = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL); - m_scroll_area->SetScrollRate(0, 5); + m_scroll_area->SetScrollRate(0, FromDIP(20)); m_scroll_area->SetBackgroundColour(*wxWHITE); m_scroll_area->SetMinSize(wxSize(FromDIP(320), FromDIP(250))); diff --git a/src/slic3r/GUI/GUI_ObjectTable.cpp b/src/slic3r/GUI/GUI_ObjectTable.cpp index a496eca6d3..c3c3295800 100644 --- a/src/slic3r/GUI/GUI_ObjectTable.cpp +++ b/src/slic3r/GUI/GUI_ObjectTable.cpp @@ -2764,12 +2764,12 @@ ObjectTablePanel::ObjectTablePanel( wxWindow* parent, wxWindowID id, const wxPoi //m_object_grid->AssignTable(m_object_grid_table); m_side_window = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(440),FromDIP(480)), wxVSCROLL); - m_side_window->SetScrollRate( 0, 5 ); + m_side_window->SetScrollRate(0, FromDIP(20)); m_page_sizer = new wxBoxSizer(wxVERTICAL); //m_page_top_sizer = new wxBoxSizer(wxHORIZONTAL); m_side_window->SetBackgroundColour(wxColour(0xff, 0xff, 0xff)); m_side_window->SetSizer(m_page_sizer); - m_side_window->SetScrollbars(1, 20, 1, 2); + m_side_window->SetScrollbars(1, FromDIP(20), 1, 2); //m_side_window->ShowScrollbars(wxSHOW_SB_NEVER, wxSHOW_SB_NEVER); //m_side_window->EnableScrolling(false, true); diff --git a/src/slic3r/GUI/HMSPanel.cpp b/src/slic3r/GUI/HMSPanel.cpp index c2a8314bd5..1268cb8f7b 100644 --- a/src/slic3r/GUI/HMSPanel.cpp +++ b/src/slic3r/GUI/HMSPanel.cpp @@ -171,7 +171,7 @@ HMSPanel::HMSPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wx m_scrolledWindow = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL); m_scrolledWindow->SetBackgroundColour(*wxWHITE); - m_scrolledWindow->SetScrollRate(5, 5); + m_scrolledWindow->SetScrollRate(5, FromDIP(20)); m_top_sizer = new wxBoxSizer(wxVERTICAL); diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index c9fdfe4ab4..63580387a7 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -211,7 +211,7 @@ wxPanel* KBShortcutsDialog::create_page(wxWindow* parent, const Page& page) wxGetApp().UpdateDarkUI(scrollable_panel); const wxColour page_colour = StateColor::darkModeColorFor(*wxWHITE); scrollable_panel->SetBackgroundColour(page_colour); - scrollable_panel->SetScrollRate(0, 20); + scrollable_panel->SetScrollRate(0, FromDIP(20)); const int page_width = FromDIP(PAGE_WIDTH); scrollable_panel->SetInitialSize(wxSize(page_width, FromDIP(450))); diff --git a/src/slic3r/GUI/MixedFilamentDialog.cpp b/src/slic3r/GUI/MixedFilamentDialog.cpp index 8176a76202..246c6548c6 100644 --- a/src/slic3r/GUI/MixedFilamentDialog.cpp +++ b/src/slic3r/GUI/MixedFilamentDialog.cpp @@ -1125,7 +1125,7 @@ wxBoxSizer* MixedFilamentDialog::create_recommendation_grid() outer->Add(title_sizer, 0, wxEXPAND | wxBOTTOM, FromDIP(4)); m_recommendation_scroll = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(-1, FromDIP(116))); - m_recommendation_scroll->SetScrollRate(0, 5); + m_recommendation_scroll->SetScrollRate(0, FromDIP(20)); m_recommendation_scroll->SetBackgroundColour(StateColor::darkModeColorFor(wxColour("#F8F8F8"))); m_recommendation_grid = new wxWrapSizer(wxHORIZONTAL, wxREMOVE_LEADING_SPACES); diff --git a/src/slic3r/GUI/MsgDialog.cpp b/src/slic3r/GUI/MsgDialog.cpp index 7f1effd162..0f1d3f62d5 100644 --- a/src/slic3r/GUI/MsgDialog.cpp +++ b/src/slic3r/GUI/MsgDialog.cpp @@ -398,7 +398,7 @@ static void add_msg_content(wxWindow *parent, } wxScrolledWindow *scrolledWindow = new wxScrolledWindow(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL); scrolledWindow->SetBackgroundColour(*wxWHITE); - scrolledWindow->SetScrollRate(0, 20); + scrolledWindow->SetScrollRate(0, scrolledWindow->FromDIP(20)); scrolledWindow->EnableScrolling(false, true); wxBoxSizer *sizer_scrolled = new wxBoxSizer(wxHORIZONTAL); Label *wrapped_text = new Label(scrolledWindow, font, msg, LB_AUTO_WRAP, wxSize(info_width, -1)); diff --git a/src/slic3r/GUI/MultiMachineManagerPage.cpp b/src/slic3r/GUI/MultiMachineManagerPage.cpp index d5f9cbce8a..f68188a9f1 100644 --- a/src/slic3r/GUI/MultiMachineManagerPage.cpp +++ b/src/slic3r/GUI/MultiMachineManagerPage.cpp @@ -398,7 +398,7 @@ MultiMachineManagerPage::MultiMachineManagerPage(wxWindow* parent) m_machine_list = new wxScrolledWindow(m_main_panel, wxID_ANY, wxDefaultPosition, wxDefaultSize); m_machine_list->SetBackgroundColour(*wxWHITE); - m_machine_list->SetScrollRate(0, 5); + m_machine_list->SetScrollRate(0, FromDIP(DEVICE_ITEM_MAX_HEIGHT)); m_machine_list->SetMinSize(wxSize(FromDIP(DEVICE_ITEM_MAX_WIDTH), 10 * FromDIP(DEVICE_ITEM_MAX_HEIGHT))); m_machine_list->SetMaxSize(wxSize(FromDIP(DEVICE_ITEM_MAX_WIDTH), 10 * FromDIP(DEVICE_ITEM_MAX_HEIGHT))); diff --git a/src/slic3r/GUI/MultiMachinePage.cpp b/src/slic3r/GUI/MultiMachinePage.cpp index e48e402405..2b6fe75f34 100644 --- a/src/slic3r/GUI/MultiMachinePage.cpp +++ b/src/slic3r/GUI/MultiMachinePage.cpp @@ -323,7 +323,7 @@ MultiMachinePickPage::MultiMachinePickPage(Plater* plater /*= nullptr*/) scroll_macine_list->SetMinSize(wxSize(FromDIP(400), FromDIP(10 * 30))); scroll_macine_list->SetMaxSize(wxSize(FromDIP(400), FromDIP(10 * 30))); scroll_macine_list->SetBackgroundColour(*wxWHITE); - scroll_macine_list->SetScrollRate(0, 5); + scroll_macine_list->SetScrollRate(0, FromDIP(DEVICE_ITEM_MAX_HEIGHT)); sizer_machine_list = new wxBoxSizer(wxVERTICAL); scroll_macine_list->SetSizer(sizer_machine_list); diff --git a/src/slic3r/GUI/MultiTaskManagerPage.cpp b/src/slic3r/GUI/MultiTaskManagerPage.cpp index 48a7fbe43e..d063b34743 100644 --- a/src/slic3r/GUI/MultiTaskManagerPage.cpp +++ b/src/slic3r/GUI/MultiTaskManagerPage.cpp @@ -675,7 +675,7 @@ LocalTaskManagerPage::LocalTaskManagerPage(wxWindow* parent) m_task_list = new wxScrolledWindow(m_main_panel, wxID_ANY, wxDefaultPosition, wxDefaultSize); m_task_list->SetBackgroundColour(*wxWHITE); - m_task_list->SetScrollRate(0, 5); + m_task_list->SetScrollRate(0, FromDIP(DEVICE_ITEM_MAX_HEIGHT)); m_task_list->SetMinSize(wxSize(FromDIP(CLOUD_TASK_ITEM_MAX_WIDTH), FromDIP(DEVICE_ITEM_MAX_HEIGHT))); m_task_list->SetMaxSize(wxSize(FromDIP(CLOUD_TASK_ITEM_MAX_WIDTH), 10 * FromDIP(DEVICE_ITEM_MAX_HEIGHT))); @@ -1056,7 +1056,7 @@ CloudTaskManagerPage::CloudTaskManagerPage(wxWindow* parent) m_task_list = new wxScrolledWindow(m_main_panel, wxID_ANY, wxDefaultPosition, wxDefaultSize); m_task_list->SetBackgroundColour(*wxWHITE); - m_task_list->SetScrollRate(0, 5); + m_task_list->SetScrollRate(0, FromDIP(DEVICE_ITEM_MAX_HEIGHT)); m_task_list->SetMinSize(wxSize(FromDIP(CLOUD_TASK_ITEM_MAX_WIDTH), FromDIP(DEVICE_ITEM_MAX_HEIGHT))); m_task_list->SetMaxSize(wxSize(FromDIP(CLOUD_TASK_ITEM_MAX_WIDTH), 10 * FromDIP(DEVICE_ITEM_MAX_HEIGHT))); diff --git a/src/slic3r/GUI/ObjColorDialog.cpp b/src/slic3r/GUI/ObjColorDialog.cpp index ef5d4ba9d2..4388449bb5 100644 --- a/src/slic3r/GUI/ObjColorDialog.cpp +++ b/src/slic3r/GUI/ObjColorDialog.cpp @@ -355,7 +355,7 @@ ObjColorPanel::ObjColorPanel(wxWindow *parent, Slic3r::ObjDialogInOut &in_out, c //new color table m_scrolledWindow = new wxScrolledWindow(m_page_simple, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL); m_scrolledWindow->SetBackgroundColour(*wxWHITE); - m_scrolledWindow->SetScrollRate(0, 20); + m_scrolledWindow->SetScrollRate(0, FromDIP(20)); m_scrolledWindow->EnableScrolling(false, true); m_scrolledWindow->ShowScrollbars(wxScrollbarVisibility::wxSHOW_SB_NEVER, wxScrollbarVisibility::wxSHOW_SB_DEFAULT); draw_new_table(); diff --git a/src/slic3r/GUI/ParamsPanel.cpp b/src/slic3r/GUI/ParamsPanel.cpp index b4fd81706a..9e16fcf8df 100644 --- a/src/slic3r/GUI/ParamsPanel.cpp +++ b/src/slic3r/GUI/ParamsPanel.cpp @@ -391,7 +391,7 @@ ParamsPanel::ParamsPanel( wxWindow* parent, wxWindowID id, const wxPoint& pos, c m_page_sizer = new wxBoxSizer(wxVERTICAL); m_page_view->SetSizer(m_page_sizer); - m_page_view->SetScrollbars(1, 20, 1, 2); + m_page_view->SetScrollbars(1, FromDIP(20), 1, 2); //m_page_view->SetScrollRate( 5, 5 ); if (m_mode_region) diff --git a/src/slic3r/GUI/PartSkipDialog.cpp b/src/slic3r/GUI/PartSkipDialog.cpp index 9bd6687757..860779ead4 100644 --- a/src/slic3r/GUI/PartSkipDialog.cpp +++ b/src/slic3r/GUI/PartSkipDialog.cpp @@ -148,7 +148,7 @@ PartSkipDialog::PartSkipDialog(wxWindow *parent) : DPIDialog(parent, wxID_ANY, _ m_line->SetBackgroundColour(wxColor(238, 238, 238)); m_list_view = new wxScrolledWindow(m_book_third_panel, wxID_ANY, wxDefaultPosition, wxSize(267, -1), wxHSCROLL | wxVSCROLL); - m_list_view->SetScrollRate(5, 5); + m_list_view->SetScrollRate(5, FromDIP(30)); m_list_view->SetMinSize(wxSize(FromDIP(267), FromDIP(378))); m_list_view->SetMaxSize(wxSize(FromDIP(267), FromDIP(378))); m_list_view->SetBackgroundColour(*wxWHITE); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 607ec64174..bdb8340edc 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3049,8 +3049,7 @@ Sidebar::Sidebar(Plater *parent) // add filament content p->m_panel_filament_content = new wxScrolledWindow(p->m_filament_area_wrapper, 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->SetScrollRate(0, FromDIP(20)); //p->m_panel_filament_content->SetMaxSize(wxSize{-1, FromDIP(174)}); p->m_panel_filament_content->SetBackgroundColour(wxColour(255, 255, 255)); @@ -3142,8 +3141,7 @@ Sidebar::Sidebar(Plater *parent) // 3) Mixed filament rows, in their own scroll area so a long mixed list does not // push the physical filament list off screen. p->m_mixed_scroll_area = new wxScrolledWindow(p->m_filament_area_wrapper, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - p->m_mixed_scroll_area->SetScrollbars(0, 100, 1, 2); - p->m_mixed_scroll_area->SetScrollRate(0, 5); + p->m_mixed_scroll_area->SetScrollRate(0, FromDIP(20)); p->m_mixed_scroll_area->SetBackgroundColour(StateColor::darkModeColorFor(*wxWHITE)); { auto* mix_scroll_sizer = new wxBoxSizer(wxVERTICAL); diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index fd5012b5ef..055449c35d 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -1475,7 +1475,7 @@ void PreferencesDialog::create() app_config = get_app_config(); m_parent = new MyscrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL); - m_parent->SetScrollRate(5, 5); + m_parent->SetScrollRate(0, FromDIP(20)); m_parent->SetBackgroundColour(*wxWHITE); m_sizer_body = new wxBoxSizer(wxVERTICAL); diff --git a/src/slic3r/GUI/PrintOptionsDialog.cpp b/src/slic3r/GUI/PrintOptionsDialog.cpp index 349e75fa06..d68bd24ccc 100644 --- a/src/slic3r/GUI/PrintOptionsDialog.cpp +++ b/src/slic3r/GUI/PrintOptionsDialog.cpp @@ -32,7 +32,7 @@ PrintOptionsDialog::PrintOptionsDialog(wxWindow* parent) m_scrollwindow = new wxScrolledWindow(this, wxID_ANY); - m_scrollwindow->SetScrollRate(0, FromDIP(10)); + m_scrollwindow->SetScrollRate(0, FromDIP(20)); m_scrollwindow->SetBackgroundColour(*wxWHITE); m_scrollwindow->SetMinSize(wxSize(FromDIP(480), wxDefaultCoord)); m_scrollwindow->SetMaxSize(wxSize(FromDIP(480), wxDefaultCoord)); diff --git a/src/slic3r/GUI/PublishSettingsDialog.cpp b/src/slic3r/GUI/PublishSettingsDialog.cpp index bd5c045053..d231857f2c 100644 --- a/src/slic3r/GUI/PublishSettingsDialog.cpp +++ b/src/slic3r/GUI/PublishSettingsDialog.cpp @@ -1225,7 +1225,7 @@ size_t PublishSettingsDialog::category_index_for( } category.scroll = new wxScrolledWindow(category.page, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL); - category.scroll->SetScrollRate(0, 10); + category.scroll->SetScrollRate(0, FromDIP(20)); category.scroll->SetBackgroundColour(GetBackgroundColour()); category.list_sizer = new wxBoxSizer(wxVERTICAL); category.scroll->SetSizer(category.list_sizer); diff --git a/src/slic3r/GUI/ReleaseNote.cpp b/src/slic3r/GUI/ReleaseNote.cpp index 029310faf9..c8338dc79a 100644 --- a/src/slic3r/GUI/ReleaseNote.cpp +++ b/src/slic3r/GUI/ReleaseNote.cpp @@ -76,7 +76,7 @@ ReleaseNoteDialog::ReleaseNoteDialog(Plater *plater /*= nullptr*/) m_sizer_right->Add(0, 0, 1, wxTOP, FromDIP(15)); m_vebview_release_note = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(560), FromDIP(430)), wxVSCROLL); - m_vebview_release_note->SetScrollRate(5, 5); + m_vebview_release_note->SetScrollRate(5, FromDIP(20)); m_vebview_release_note->SetBackgroundColour(wxColour(0xF8, 0xF8, 0xF8)); m_vebview_release_note->SetMaxSize(wxSize(FromDIP(560), FromDIP(430))); @@ -142,7 +142,7 @@ UpdatePluginDialog::UpdatePluginDialog(wxWindow* parent /*= nullptr*/) operation_tips->SetMaxSize(wxSize(FromDIP(260), -1)); m_vebview_release_note = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL); - m_vebview_release_note->SetScrollRate(5, 5); + m_vebview_release_note->SetScrollRate(5, FromDIP(20)); m_vebview_release_note->SetBackgroundColour(wxColour(0xF8, 0xF8, 0xF8)); m_vebview_release_note->SetMinSize(wxSize(FromDIP(260), FromDIP(150))); m_vebview_release_note->SetMaxSize(wxSize(FromDIP(260), FromDIP(150))); @@ -277,7 +277,7 @@ UpdateVersionDialog::UpdateVersionDialog(wxWindow *parent) m_simplebook_release_note->SetBackgroundColour(wxColour(0xF8, 0xF8, 0xF8)); m_scrollwindows_release_note = new wxScrolledWindow(m_simplebook_release_note, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(560), FromDIP(430)), wxVSCROLL); - m_scrollwindows_release_note->SetScrollRate(5, 5); + m_scrollwindows_release_note->SetScrollRate(5, FromDIP(20)); m_scrollwindows_release_note->SetBackgroundColour(wxColour(0xF8, 0xF8, 0xF8)); //webview @@ -538,7 +538,7 @@ SecondaryCheckDialog::SecondaryCheckDialog(wxWindow* parent, wxWindowID id, cons m_sizer_right->Add(0, 0, 1, wxTOP, FromDIP(15)); m_vebview_release_note = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL); - m_vebview_release_note->SetScrollRate(0, 5); + m_vebview_release_note->SetScrollRate(0, FromDIP(20)); m_vebview_release_note->SetBackgroundColour(*wxWHITE); m_vebview_release_note->SetMinSize(wxSize(FromDIP(400), FromDIP(380))); m_sizer_right->Add(m_vebview_release_note, 0, wxEXPAND | wxRIGHT | wxLEFT, FromDIP(15)); @@ -823,7 +823,7 @@ PrintErrorDialog::PrintErrorDialog(wxWindow* parent, wxWindowID id, const wxStri m_sizer_right->Add(0, 0, 1, wxTOP, FromDIP(5)); m_vebview_release_note = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL); - m_vebview_release_note->SetScrollRate(0, 5); + m_vebview_release_note->SetScrollRate(0, FromDIP(20)); m_vebview_release_note->SetBackgroundColour(*wxWHITE); m_vebview_release_note->SetMinSize(wxSize(FromDIP(320), FromDIP(250))); m_sizer_right->Add(m_vebview_release_note, 0, wxEXPAND | wxRIGHT | wxLEFT, FromDIP(15)); @@ -1122,7 +1122,7 @@ ConfirmBeforeSendDialog::ConfirmBeforeSendDialog(wxWindow* parent, wxWindowID id m_sizer_right->Add(0, 0, 1, wxTOP, FromDIP(15)); m_vebview_release_note = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL); - m_vebview_release_note->SetScrollRate(0, 5); + m_vebview_release_note->SetScrollRate(0, FromDIP(20)); m_vebview_release_note->SetBackgroundColour(*wxWHITE); m_vebview_release_note->SetMinSize(wxSize(FromDIP(400), FromDIP(380))); m_sizer_right->Add(m_vebview_release_note, 0, wxEXPAND | wxRIGHT | wxLEFT, FromDIP(15)); diff --git a/src/slic3r/GUI/SafetyOptionsDialog.cpp b/src/slic3r/GUI/SafetyOptionsDialog.cpp index 6536666bb1..c633d2778e 100644 --- a/src/slic3r/GUI/SafetyOptionsDialog.cpp +++ b/src/slic3r/GUI/SafetyOptionsDialog.cpp @@ -31,7 +31,7 @@ SafetyOptionsDialog::SafetyOptionsDialog(wxWindow* parent) SetSize(FromDIP(480),FromDIP(320)); m_scrollwindow = new wxScrolledWindow(this, wxID_ANY); - m_scrollwindow->SetScrollRate(0, FromDIP(10)); + m_scrollwindow->SetScrollRate(0, FromDIP(20)); m_scrollwindow->SetBackgroundColour(*wxWHITE); m_scrollwindow->SetMinSize(wxSize(FromDIP(480), wxDefaultCoord)); m_scrollwindow->SetMaxSize(wxSize(FromDIP(480), wxDefaultCoord)); @@ -272,4 +272,4 @@ void SafetyOptionsDialog::show_idel_heating_toast(const wxString &text) m_idel_heating_toast_timer.StartOnce(3000); } -}} // namespace Slic3r::GUI \ No newline at end of file +}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/SelectMachine.cpp b/src/slic3r/GUI/SelectMachine.cpp index a9767d6c72..a3b83bd643 100644 --- a/src/slic3r/GUI/SelectMachine.cpp +++ b/src/slic3r/GUI/SelectMachine.cpp @@ -148,7 +148,7 @@ SelectMachineDialog::SelectMachineDialog(Plater *plater) wxBoxSizer* m_scroll_sizer = new wxBoxSizer(wxVERTICAL); m_scroll_area = new wxScrolledWindow(this); - m_scroll_area->SetScrollRate(0, 20); + m_scroll_area->SetScrollRate(0, FromDIP(20)); m_scroll_area->SetBackgroundColour(m_colour_def_color); m_scroll_area->SetMinSize(wxSize(FromDIP(700), FromDIP(600))); m_scroll_area->SetMaxSize(wxSize(FromDIP(700), FromDIP(600))); @@ -707,7 +707,7 @@ SelectMachineDialog::SelectMachineDialog(Plater *plater) //show bind failed info m_sw_print_failed_info = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(SELECT_MACHINE_DIALOG_SIMBOOK_SIZE2.x, FromDIP(125)), wxVSCROLL); m_sw_print_failed_info->SetBackgroundColour(*wxWHITE); - m_sw_print_failed_info->SetScrollRate(0, 5); + m_sw_print_failed_info->SetScrollRate(0, FromDIP(20)); m_sw_print_failed_info->SetMinSize(wxSize(SELECT_MACHINE_DIALOG_SIMBOOK_SIZE2.x, FromDIP(125))); m_sw_print_failed_info->SetMaxSize(wxSize(SELECT_MACHINE_DIALOG_SIMBOOK_SIZE2.x, FromDIP(125))); diff --git a/src/slic3r/GUI/SelectMachinePop.cpp b/src/slic3r/GUI/SelectMachinePop.cpp index 9b34e74f23..0046fd5f7f 100644 --- a/src/slic3r/GUI/SelectMachinePop.cpp +++ b/src/slic3r/GUI/SelectMachinePop.cpp @@ -327,7 +327,7 @@ SelectMachinePopup::SelectMachinePopup(wxWindow *parent) m_scrolledWindow = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, SELECT_MACHINE_LIST_SIZE, wxHSCROLL | wxVSCROLL); m_scrolledWindow->SetBackgroundColour(*wxWHITE); m_scrolledWindow->SetMinSize(SELECT_MACHINE_LIST_SIZE); - m_scrolledWindow->SetScrollRate(0, 5); + m_scrolledWindow->SetScrollRate(0, SELECT_MACHINE_ITEM_SIZE.y); auto m_sizxer_scrolledWindow = new wxBoxSizer(wxVERTICAL); m_scrolledWindow->SetSizer(m_sizxer_scrolledWindow); m_scrolledWindow->Layout(); diff --git a/src/slic3r/GUI/SendMultiMachinePage.cpp b/src/slic3r/GUI/SendMultiMachinePage.cpp index cafbb360e6..b386d1f451 100644 --- a/src/slic3r/GUI/SendMultiMachinePage.cpp +++ b/src/slic3r/GUI/SendMultiMachinePage.cpp @@ -278,7 +278,7 @@ SendMultiMachinePage::SendMultiMachinePage(Plater* plater) m_main_scroll = new ScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL); m_main_scroll->SetBackgroundColour(*wxWHITE); - m_main_scroll->SetScrollRate(5, 5); + m_main_scroll->SetScrollRate(5, FromDIP(20)); m_sizer_body = new wxBoxSizer(wxVERTICAL); m_main_page = create_page(); @@ -1353,7 +1353,7 @@ wxPanel* SendMultiMachinePage::create_page() scroll_macine_list = new wxScrolledWindow(main_page, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(800), FromDIP(300)), wxHSCROLL | wxVSCROLL); scroll_macine_list->SetBackgroundColour(*wxWHITE); - scroll_macine_list->SetScrollRate(5, 5); + scroll_macine_list->SetScrollRate(5, FromDIP(SEND_ITEM_MAX_HEIGHT)); scroll_macine_list->SetMinSize(wxSize(FromDIP(DEVICE_ITEM_MAX_WIDTH), 10 * FromDIP(SEND_ITEM_MAX_HEIGHT))); scroll_macine_list->SetMaxSize(wxSize(FromDIP(DEVICE_ITEM_MAX_WIDTH), 10 * FromDIP(SEND_ITEM_MAX_HEIGHT))); diff --git a/src/slic3r/GUI/SendToPrinter.cpp b/src/slic3r/GUI/SendToPrinter.cpp index c4db0423ab..9071c0affd 100644 --- a/src/slic3r/GUI/SendToPrinter.cpp +++ b/src/slic3r/GUI/SendToPrinter.cpp @@ -384,7 +384,7 @@ SendToPrinterDialog::SendToPrinterDialog(Plater *plater) //show bind failed info m_sw_print_failed_info = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(380), FromDIP(125)), wxVSCROLL); m_sw_print_failed_info->SetBackgroundColour(*wxWHITE); - m_sw_print_failed_info->SetScrollRate(0, 5); + m_sw_print_failed_info->SetScrollRate(0, FromDIP(20)); m_sw_print_failed_info->SetMinSize(wxSize(FromDIP(380), FromDIP(125))); m_sw_print_failed_info->SetMaxSize(wxSize(FromDIP(380), FromDIP(125))); diff --git a/src/slic3r/GUI/StatusPanel.cpp b/src/slic3r/GUI/StatusPanel.cpp index 218371f0b5..0b6e4b6577 100644 --- a/src/slic3r/GUI/StatusPanel.cpp +++ b/src/slic3r/GUI/StatusPanel.cpp @@ -1302,7 +1302,7 @@ void PrintingTaskPanel::set_star_count(int star_count) StatusBasePanel::StatusBasePanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, long style, const wxString &name) : wxScrolledWindow(parent, id, pos, size, wxHSCROLL | wxVSCROLL) { - this->SetScrollRate(25, 25); + SetScrollRate(25, FromDIP(20)); Slic3r::DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager(); if (!dev) return; obj = dev->get_selected_machine(); diff --git a/src/slic3r/GUI/SyncAmsInfoDialog.cpp b/src/slic3r/GUI/SyncAmsInfoDialog.cpp index 7f075dbbfa..19b1ddc568 100644 --- a/src/slic3r/GUI/SyncAmsInfoDialog.cpp +++ b/src/slic3r/GUI/SyncAmsInfoDialog.cpp @@ -700,7 +700,7 @@ SyncAmsInfoDialog::SyncAmsInfoDialog(wxWindow *parent, SyncInfo &info) : //wxBoxSizer *m_scroll_sizer = new wxBoxSizer(wxVERTICAL); m_scrolledWindow = new wxScrolledWindow(m_show_page, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL); m_scrolledWindow->SetBackgroundColour(*wxWHITE); - m_scrolledWindow->SetScrollRate(0, 20); + m_scrolledWindow->SetScrollRate(0, FromDIP(20)); m_scrolledWindow->SetMinSize(wxSize(-1, SyncAmsInfoDialogHeightMAX)); m_scrolledWindow->SetMaxSize(wxSize(-1, SyncAmsInfoDialogHeightMAX)); m_scrolledWindow->EnableScrolling(false,true); diff --git a/src/slic3r/GUI/TextureImportDialog.cpp b/src/slic3r/GUI/TextureImportDialog.cpp index 2e5c1145e1..7296a1ebeb 100644 --- a/src/slic3r/GUI/TextureImportDialog.cpp +++ b/src/slic3r/GUI/TextureImportDialog.cpp @@ -534,7 +534,7 @@ public: m_content = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL); m_content->SetBackgroundColour(pop_bg); - m_content->SetScrollRate(0, FromDIP(5)); + m_content->SetScrollRate(0, FromDIP(20)); auto* outer = new wxBoxSizer(wxVERTICAL); const int pop_w = std::max(FromDIP(213), popup_width); @@ -2274,7 +2274,7 @@ void TextureImportDialog::build_mapping_panel(wxWindow* parent, wxSizer* sizer) m_mapping_scroll = new wxScrolledWindow(parent, wxID_ANY, wxDefaultPosition, wxSize(-1, FromDIP(300))); - m_mapping_scroll->SetScrollRate(0, FromDIP(10)); + m_mapping_scroll->SetScrollRate(0, FromDIP(20)); m_mapping_scroll->SetBackgroundColour(StateColor::darkModeColorFor(*wxWHITE)); m_mapping_scroll->Bind(wxEVT_MOUSEWHEEL, &TextureImportDialog::dismiss_filament_popup_on_wheel, this); @@ -4335,7 +4335,7 @@ void TextureImportDialog::on_dpi_changed(const wxRect&) if (m_mapping_scroll) { m_mapping_scroll->SetMinSize(wxSize(-1, FromDIP(300))); - m_mapping_scroll->SetScrollRate(0, FromDIP(10)); + m_mapping_scroll->SetScrollRate(0, FromDIP(20)); } if (m_btn_skip) { diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index db64bdcc99..df809396f5 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -942,7 +942,7 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection *dependent_ m_sizer_tab->Add(m_table_top, 1, 0, 0); m_scrolledWindow = new wxScrolledWindow(m_panel_tab, wxID_ANY, wxDefaultPosition, UNSAVE_CHANGE_DIALOG_SCROLL_WINDOW_SIZE, wxNO_BORDER|wxVSCROLL); - m_scrolledWindow->SetScrollRate(0, 5); + m_scrolledWindow->SetScrollRate(0, FromDIP(20)); m_scrolledWindow->SetBackgroundColour(GREY200); m_sizer_bottom = new wxBoxSizer(wxVERTICAL); m_sizer_bottom->Add(m_scrolledWindow, 1, wxEXPAND, 0); diff --git a/src/slic3r/GUI/UpdateDialogs.cpp b/src/slic3r/GUI/UpdateDialogs.cpp index 0710e054f7..42383cd177 100644 --- a/src/slic3r/GUI/UpdateDialogs.cpp +++ b/src/slic3r/GUI/UpdateDialogs.cpp @@ -130,7 +130,7 @@ MsgUpdateConfig::MsgUpdateConfig(const std::vector &updates, bool force_ m_sizer_right->Add(0, 0, 1, wxTOP, FromDIP(15)); auto m_scrollwindw_release_note = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(560), FromDIP(430)),wxVSCROLL); - m_scrollwindw_release_note->SetScrollRate(0, 5); + m_scrollwindw_release_note->SetScrollRate(0, FromDIP(20)); m_scrollwindw_release_note->SetBackgroundColour(wxColour(0xF8, 0xF8, 0xF8)); m_scrollwindw_release_note->SetMaxSize(wxSize(FromDIP(560), FromDIP(430))); m_scrollwindw_release_note->SetWindowStyle(wxVSCROLL); diff --git a/src/slic3r/GUI/UpgradePanel.cpp b/src/slic3r/GUI/UpgradePanel.cpp index d31fde8613..5f168025ed 100644 --- a/src/slic3r/GUI/UpgradePanel.cpp +++ b/src/slic3r/GUI/UpgradePanel.cpp @@ -1629,7 +1629,7 @@ UpgradePanel::UpgradePanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, auto m_main_sizer = new wxBoxSizer(wxVERTICAL); m_scrolledWindow = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL); - m_scrolledWindow->SetScrollRate(5, 25); + m_scrolledWindow->SetScrollRate(5, FromDIP(20)); m_machine_list_sizer = new wxBoxSizer(wxVERTICAL); diff --git a/src/slic3r/GUI/Widgets/CheckList.cpp b/src/slic3r/GUI/Widgets/CheckList.cpp index c0101441ff..ed404c62fe 100644 --- a/src/slic3r/GUI/Widgets/CheckList.cpp +++ b/src/slic3r/GUI/Widgets/CheckList.cpp @@ -62,7 +62,7 @@ CheckList::CheckList( s_sizer = new wxBoxSizer(wxVERTICAL); m_scroll_area = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, scroll_style); - m_scroll_area->SetScrollRate(0, 10); + m_scroll_area->SetScrollRate(0, FromDIP(20)); m_scroll_area->SetSizer(s_sizer); m_scroll_area->SetBackgroundColour(parent->GetBackgroundColour()); m_scroll_area->Bind(wxEVT_RIGHT_DOWN, &CheckList::ShowMenu, this); @@ -223,4 +223,4 @@ void CheckList::ShowMenu(wxMouseEvent &evt) wxPoint screen_pos = src->ClientToScreen(evt.GetPosition()); wxPoint local_pos = ScreenToClient(screen_pos); PopupMenu(&m, local_pos); -} \ No newline at end of file +} diff --git a/src/slic3r/GUI/Widgets/ProgressDialog.cpp b/src/slic3r/GUI/Widgets/ProgressDialog.cpp index c16f3bcf1f..6d42df1007 100644 --- a/src/slic3r/GUI/Widgets/ProgressDialog.cpp +++ b/src/slic3r/GUI/Widgets/ProgressDialog.cpp @@ -199,7 +199,7 @@ bool ProgressDialog::Create(const wxString &title, const wxString &message, int m_sizer_main->Add(m_simplebook, 1, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(28)); } else { m_msg_scrolledWindow = new wxScrolledWindow( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL ); - m_msg_scrolledWindow->SetScrollRate(0,5); + m_msg_scrolledWindow->SetScrollRate(0, FromDIP(20)); wxBoxSizer* m_msg_sizer= new wxBoxSizer(wxVERTICAL); m_msg = new wxStaticText(m_msg_scrolledWindow, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(PROGRESSDIALOG_SIMPLEBOOK_SIZE.x, -1), 0); diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 88e2df31c0..0857612ac3 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -1222,7 +1222,7 @@ ImageTransientPopup::ImageTransientPopup( wxWindow *parent, bool scrolled, wxBit m_panel->SetSize(300, 300); // And also actually enable them. - m_panel->SetScrollRate(10, 10); + m_panel->SetScrollRate(10, FromDIP(20)); } else { From ea471dc82de3ba540da1c16ad4cfddc8caf58f8c Mon Sep 17 00:00:00 2001 From: Chris Bennight Date: Sun, 27 Sep 2026 11:53:11 -0400 Subject: [PATCH 3/5] Fix model initialization without a wx application (#15866) Guard the smooth-normals preference read when CLI thumbnail generation initializes geometry without a wx application. Use the existing flat-normal path in that case and preserve GUI preferences. --- src/slic3r/GUI/GLModel.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index 3dd15c3fdd..92296030e1 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -456,8 +456,9 @@ void GLModel::init_from(const indexed_triangle_set& its) data.reserve_vertices(3 * its.indices.size()); data.reserve_indices(3 * its.indices.size()); - // Read user preference: smooth normals enabled - const bool smooth_normals_enabled = wxGetApp().app_config != nullptr && wxGetApp().app_config->get_bool(SETTING_OPENGL_PHONG_SMOOTH_NORMALS); + // CLI thumbnail generation can initialize models without a wx application. + const bool smooth_normals_enabled = wxApp::GetInstance() != nullptr && wxGetApp().app_config != nullptr && + wxGetApp().app_config->get_bool(SETTING_OPENGL_PHONG_SMOOTH_NORMALS); if (smooth_normals_enabled) { // Use per-corner smooth normals (via IGL) From 4964f49765f7963b9ee4b016fe5a3bc0581e1911 Mon Sep 17 00:00:00 2001 From: Kenneth Raplee <101818165+kenrap@users.noreply.github.com> Date: Sun, 27 Sep 2026 16:21:03 +0000 Subject: [PATCH 4/5] Fix size_t/%d format mismatch in MsgDialog::add_button (#15852) Fix size_t/%d mismatch in MsgDialog button keys `m_buttons.size()` is a `size_t`, which does not match the `%d` conversion in printf-style variadics. Build the key with `std::to_string` instead; exact for any size_t, no behavior change for realistic counts. Co-authored-by: yw4z --- src/slic3r/GUI/MsgDialog.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/MsgDialog.cpp b/src/slic3r/GUI/MsgDialog.cpp index 0f1d3f62d5..8c981a970f 100644 --- a/src/slic3r/GUI/MsgDialog.cpp +++ b/src/slic3r/GUI/MsgDialog.cpp @@ -194,9 +194,10 @@ Button* MsgDialog::add_button(wxWindowID btn_id, bool set_focus /*= false*/, con bd->button = btn; //bd->type = type; - mb->id = wxString::Format("%d", m_buttons.size()); + // m_buttons.size() is a size_t, which does not match the %d conversion + mb->id = wxString(std::to_string(m_buttons.size())); mb->buttondata = bd; - m_buttons[ wxString::Format("%d", m_buttons.size())] = mb; + m_buttons[ wxString(std::to_string(m_buttons.size()))] = mb; return btn; }; From 5298e49dd270e09077540000d779292b1e7d609f Mon Sep 17 00:00:00 2001 From: Kris Austin Date: Sun, 27 Sep 2026 17:08:27 -0500 Subject: [PATCH 5/5] fix: Klipper/Moonraker upload errors show a raw Python traceback (#14841) --- src/slic3r/Utils/PrintHost.cpp | 60 +++++++- tests/slic3rutils/CMakeLists.txt | 1 + tests/slic3rutils/test_printhost.cpp | 213 +++++++++++++++++++++++++++ 3 files changed, 273 insertions(+), 1 deletion(-) create mode 100644 tests/slic3rutils/test_printhost.cpp diff --git a/src/slic3r/Utils/PrintHost.cpp b/src/slic3r/Utils/PrintHost.cpp index 04709fef4c..f3333c731d 100644 --- a/src/slic3r/Utils/PrintHost.cpp +++ b/src/slic3r/Utils/PrintHost.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -115,10 +116,67 @@ std::string PrintHost::get_print_host_webui(DynamicPrintConfig* config) return webui_url; } +namespace { + +// Moonraker (Klipper's API server) reports a raised exception as { "error": { "code", "message", "traceback" } } +// under every host type that connects to it, often with the cause only in the traceback. Returns the reason to show, +// or empty for any other body. +std::string moonraker_error_reason(const std::string &body) +{ + const auto root = nlohmann::json::parse(body, nullptr, false); + const auto err = root.find("error"); + if (err == root.end()) + return {}; + const auto message = err->find("message"); + const auto traceback = err->find("traceback"); + if (message == err->end() || traceback == err->end() || !message->is_string() || !traceback->is_string()) + return {}; + + const auto &msg = message->get_ref(); + const auto &tb = traceback->get_ref(); + if (msg.empty()) + return {}; + const auto end = tb.find_last_not_of(" \t\r\n"); + if (end == std::string::npos) + return msg; + + // Chained exceptions each start a new traceback; the one that failed the request is the last. + const auto header = tb.rfind("Traceback (most recent call last):", end); + + // Tornado renders a raised HTTPError as "HTTP : [ ()]", and the detail may span lines. + const auto code = err->find("code"); + if (code != err->end() && code->is_number_integer()) { + const std::string marker = "HTTP " + std::to_string(code->get()) + ": "; + const auto pos = tb.rfind(marker, end); + if (pos != std::string::npos && (header == std::string::npos || pos > header) && pos + marker.size() <= end) { + const std::string reason = tb.substr(pos + marker.size(), end + 1 - pos - marker.size()); + // An HTTPError whose detail equals its reason, like HTTPError(401, "Unauthorized"), renders the phrase twice. + return reason == msg + " (" + msg + ")" ? msg : reason; + } + } + + // Any other exception's type and message are everything from the first unindented line after its frames. + auto begin = (header == std::string::npos) ? std::string::npos : tb.find('\n', header); + while (begin != std::string::npos && begin < end) { + ++begin; + if (tb[begin] != ' ' && tb[begin] != '\r' && tb[begin] != '\n') + break; + begin = tb.find('\n', begin); + } + if (begin == std::string::npos || begin > end) { + const auto nl = tb.rfind('\n', end); + begin = (nl == std::string::npos) ? 0 : nl + 1; + } + return msg + " (" + tb.substr(begin, end + 1 - begin) + ")"; +} + +} // namespace + wxString PrintHost::format_error(const std::string &body, const std::string &error, unsigned status) const { if (status != 0) { - auto wxbody = wxString::FromUTF8(body.data()); + const std::string reason = moonraker_error_reason(body); + auto wxbody = wxString::FromUTF8(reason.empty() ? body : reason); return wxString::Format("HTTP %u: %s", status, wxbody); } else { if (error.find("curl:Timeout was reached") != std::string::npos) { diff --git a/tests/slic3rutils/CMakeLists.txt b/tests/slic3rutils/CMakeLists.txt index 08f1a6dd8f..7f541f3701 100644 --- a/tests/slic3rutils/CMakeLists.txt +++ b/tests/slic3rutils/CMakeLists.txt @@ -18,6 +18,7 @@ add_executable(${_TEST_NAME}_tests test_plugin_install.cpp test_plugin_lifecycle.cpp test_plugin_printer_agent.cpp + test_printhost.cpp test_slicing_pipeline_bindings.cpp test_slicing_pipeline_config.cpp test_plugin_sort.cpp diff --git a/tests/slic3rutils/test_printhost.cpp b/tests/slic3rutils/test_printhost.cpp new file mode 100644 index 0000000000..557d4a5c89 --- /dev/null +++ b/tests/slic3rutils/test_printhost.cpp @@ -0,0 +1,213 @@ +#include + +#include + +#include "slic3r/Utils/PrintHost.hpp" + +using namespace Slic3r; + +namespace { + +class TestPrintHost : public PrintHost +{ +public: + using PrintHost::format_error; + + const char* get_name() const override { return "Test"; } + bool test(wxString&) const override { return true; } + wxString get_test_ok_msg() const override { return {}; } + wxString get_test_failed_msg(wxString&) const override { return {}; } + bool upload(PrintHostUpload, ProgressFn, ErrorFn, InfoFn) const override { return true; } + bool has_auto_discovery() const override { return false; } + bool can_test() const override { return false; } + PrintHostPostUploadActions get_post_upload_actions() const override { return {}; } + std::string get_host() const override { return {}; } +}; + +std::string format_error(const std::string& body, const std::string& error, unsigned status) +{ + return TestPrintHost().format_error(body, error, status).ToStdString(); +} + +std::string envelope(int code, const std::string& message, const std::string& traceback) +{ + return nlohmann::json{{"error", {{"code", code}, {"message", message}, {"traceback", traceback}}}}.dump(); +} + +std::string moonraker_error(int code, const std::string& message, const std::string& detail = {}) +{ + std::string line = "tornado.web.HTTPError: HTTP " + std::to_string(code) + ": " + message; + if (!detail.empty()) + line += " (" + detail + ")"; + return envelope(code, message, "Traceback (most recent call last):\n ...\n" + line + "\n"); +} + +// A real Moonraker body for uploading a file that is being printed. +constexpr const char* k_busy_file_403 = + R"JSON({"error": {"code": 403, "message": "Forbidden", "traceback": "Traceback (most recent call last):\n\n File \"/home/lava/moonraker/moonraker/components/file_manager/file_manager.py\", line 1017, in _finish_gcode_upload\n can_start = self._handle_operation_check(check_path)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nmoonraker.utils.exceptions.ServerError: File currently in use\n\nDuring handling of the above exception, another exception occurred:\n\nTraceback (most recent call last):\n\n File \"/home/lava/moonraker/moonraker/components/application.py\", line 1069, in post\n raise tornado.web.HTTPError(\ntornado.web.HTTPError: HTTP 403: Forbidden (File is loaded, upload not permitted)\n"}})JSON"; + +} // namespace + +TEST_CASE("A Klipper upload error shows its reason instead of a Python traceback", "[PrintHost][Regression]") +{ + const std::string msg = format_error(k_busy_file_403, "", 403); + INFO("actual: " << msg); + + CHECK(msg == "HTTP 403: Forbidden (File is loaded, upload not permitted)"); + CHECK_THAT(msg, !Catch::Matchers::ContainsSubstring("Traceback")); + CHECK_THAT(msg, !Catch::Matchers::ContainsSubstring("file_manager.py")); +} + +TEST_CASE("The specific cause is recovered from a file endpoint's traceback", "[PrintHost]") +{ + SECTION("a plain detail") + { + const std::string body = moonraker_error(403, "Forbidden", "File is loaded, upload not permitted"); + CHECK(format_error(body, "", 403) == "HTTP 403: Forbidden (File is loaded, upload not permitted)"); + } + + SECTION("a detail whose own parentheses nest (a filename)") + { + const std::string detail = "Directory does not exist (/home/pi/gcodes/plate (1).gcode)"; + const std::string body = moonraker_error(400, "Bad Request", detail); + CHECK(format_error(body, "", 400) == "HTTP 400: Bad Request (" + detail + ")"); + } + + SECTION("a detail that contains the reason phrase") + { + const std::string body = moonraker_error(403, "Forbidden", "Forbidden zone: access denied"); + CHECK(format_error(body, "", 403) == "HTTP 403: Forbidden (Forbidden zone: access denied)"); + } + + SECTION("a detail that spans lines") + { + const std::string detail = "Move out of range\nX=250.000 Y=10.000"; + const std::string body = moonraker_error(400, "Bad Request", detail); + CHECK(format_error(body, "", 400) == "HTTP 400: Bad Request (" + detail + ")"); + } + + SECTION("a detail that only repeats the reason phrase is dropped") + { + const std::string body = moonraker_error(401, "Unauthorized", "Unauthorized"); + CHECK(format_error(body, "", 401) == "HTTP 401: Unauthorized"); + } +} + +TEST_CASE("An unhandled exception shows its type and message", "[PrintHost]") +{ + const std::string frame = "Traceback (most recent call last):\n" + " File \"/home/pi/moonraker/moonraker/components/file_manager/file_manager.py\", line 1, in write\n" + " self._write(data)\n"; + + SECTION("a one-line message") + { + const std::string body = envelope(500, "Internal Server Error", frame + "OSError: [Errno 28] No space left on device\n"); + CHECK(format_error(body, "", 500) == "HTTP 500: Internal Server Error (OSError: [Errno 28] No space left on device)"); + } + + SECTION("a message that spans lines") + { + const std::string body = envelope(500, "Internal Server Error", frame + "ServerError: Klippy request failed\n see klippy.log\n"); + CHECK(format_error(body, "", 500) == "HTTP 500: Internal Server Error (ServerError: Klippy request failed\n see klippy.log)"); + } + + SECTION("raised while handling an HTTPError with the same code") + { + const std::string traceback = frame + "tornado.web.HTTPError: HTTP 500: Internal Server Error (Database locked)\n\n" + "During handling of the above exception, another exception occurred:\n\n" + + frame + "OSError: [Errno 5] Input/output error\n"; + const std::string body = envelope(500, "Internal Server Error", traceback); + CHECK(format_error(body, "", 500) == "HTTP 500: Internal Server Error (OSError: [Errno 5] Input/output error)"); + } + + SECTION("a traceback with no header") + { + const std::string body = envelope(500, "Internal Server Error", "OSError: [Errno 5] Input/output error"); + CHECK(format_error(body, "", 500) == "HTTP 500: Internal Server Error (OSError: [Errno 5] Input/output error)"); + } +} + +TEST_CASE("A reason already complete in message is shown unchanged", "[PrintHost]") +{ + SECTION("message is the whole reason, no trailing detail") + { + const std::string body = moonraker_error(503, "Klippy is not ready"); + CHECK(format_error(body, "", 503) == "HTTP 503: Klippy is not ready"); + } + + SECTION("a message that itself contains parentheses is not duplicated") + { + const std::string reason = "Requested blocks (0-5) are unavailable"; + const std::string body = moonraker_error(400, reason); + CHECK(format_error(body, "", 400) == "HTTP 400: " + reason); + } +} + +TEST_CASE("A Moonraker error with no usable detail shows just the reason phrase", "[PrintHost]") +{ + struct Case + { + const char* name; + const char* body; + unsigned status; + const char* expected; + }; + + const auto c = GENERATE( + Case{"an empty traceback", R"JSON({"error": {"code": 500, "message": "Internal Server Error", "traceback": ""}})JSON", 500, + "HTTP 500: Internal Server Error"}, + Case{"a traceback of only whitespace", R"JSON({"error": {"code": 500, "message": "Internal Server Error", "traceback": "\n \n"}})JSON", + 500, "HTTP 500: Internal Server Error"}); + + DYNAMIC_SECTION(c.name) { CHECK(format_error(c.body, "", c.status) == c.expected); } +} + +TEST_CASE("A percent sign in the reason is not a format specifier", "[PrintHost]") +{ + const std::string body = moonraker_error(507, "Insufficient Storage", "disk 100% full"); + CHECK(format_error(body, "", 507) == "HTTP 507: Insufficient Storage (disk 100% full)"); +} + +TEST_CASE("Error bodies that are not a Moonraker envelope are left unchanged", "[PrintHost]") +{ + SECTION("OctoPrint's string-valued error member") + { + const std::string body = R"JSON({"error": "File not found"})JSON"; + CHECK(format_error(body, "", 404) == "HTTP 404: " + body); + } + + SECTION("PrusaLink's top-level message, not under error") + { + const std::string body = R"JSON({"title": "Conflict", "message": "Printer is printing"})JSON"; + CHECK(format_error(body, "", 409) == "HTTP 409: " + body); + } + + SECTION("a body that is not JSON") + { + const std::string html = "502 Bad Gateway"; + CHECK(format_error(html, "", 502) == "HTTP 502: " + html); + } + + SECTION("an error object with no traceback") + { + const std::string body = R"JSON({"error": {"code": 500, "message": "Internal Server Error"}})JSON"; + CHECK(format_error(body, "", 500) == "HTTP 500: " + body); + } + + SECTION("an error object whose traceback is null") + { + const std::string body = R"JSON({"error": {"code": 500, "message": "Internal Server Error", "traceback": null}})JSON"; + CHECK(format_error(body, "", 500) == "HTTP 500: " + body); + } + + SECTION("an envelope whose reason phrase is empty") + { + const std::string body = envelope(403, "", "Traceback (most recent call last):\nOSError: denied\n"); + CHECK(format_error(body, "", 403) == "HTTP 403: " + body); + } + + SECTION("a transport error with no HTTP status") + { + CHECK(format_error("", "curl:Could not connect", 0) == "curl:Could not connect"); + } +}