Files
OrcaSlicer/src/slic3r/GUI/CloneDialog.cpp
T
5ede9711f5 Fix GTK3 dialog min size: SetSizer → SetSizerAndFit for dialogs without explicit SetMinSize (#14948)
* For dialog without explicitly `SetMinSize`, we should use `SetSizerAndFit` instead, otherwise the dialog will not show correctly on GTK3. (OrcaSlicer/OrcaSlicer#14561)
- and if `SetSizer` is called before the full layout has been built, then an extra `SetSizeHints` should be called before layout/fit so the min size can be properly set automatically based on children's min sizes accordingly.

* Fix GTK3 dialog min size: SetSizer → SetSizerAndFit for dialogs without explicit SetMinSize

Replace SetSizer() with SetSizerAndFit() in 11 dialog constructors that
neither call SetMinSize() nor SetSizeHints(), ensuring proper minimum
size propagation from child widgets on GTK3.

SetSizerAndFit internally calls sizer->SetSizeHints(window), which
sets the window's minimum size based on children — the same fix
applied to ProjectDropDialog in 8a7662083e.

Also drop sizer->Fit(this) calls where present, since they only
resize but don't set the min size hint needed by GTK3.

Co-Authored-By: Claude <noreply@anthropic.com>

* Update code style

* Update TroubleshootDialog.hpp

* Fix unsaved preset dialog layout

* Fix MsgDialog layout

* Fix other 3 instances in MsgDialog.cpp

* Fix a few more instances

* Fix printer option dialog too big on Windows

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: yw4z <ywsyildiz@gmail.com>
2026-07-28 14:32:55 +08:00

142 lines
5.0 KiB
C++

#include "CloneDialog.hpp"
#include "GUI_App.hpp"
#include "MainFrame.hpp"
namespace Slic3r { namespace GUI {
CloneDialog::CloneDialog(wxWindow *parent)
: DPIDialog(parent ? parent : static_cast<wxWindow *>(wxGetApp().mainframe), wxID_ANY, _L("Clone"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX)
{
SetBackgroundColour(*wxWHITE);
SetFont(Label::Body_14);
m_plater = wxGetApp().plater();
m_config = wxGetApp().app_config;
m_cancel_process = false;
auto v_sizer = new wxBoxSizer(wxVERTICAL);
auto f_sizer = new wxFlexGridSizer(2, 2, FromDIP(4), FromDIP(20));
auto count_label = new wxStaticText(this, wxID_ANY, _L("Number of copies:"), wxDefaultPosition, wxDefaultSize, 0);
m_count_spin = new SpinInput(this, wxEmptyString, "", wxDefaultPosition, wxSize(FromDIP(120), -1), wxSP_ARROW_KEYS, 1, 1000, 1);
m_count_spin->GetTextCtrl()->SetFocus();
m_count_spin->Bind(wxEVT_KILL_FOCUS, [this](wxFocusEvent &e) {
e.SetId(GetId());
ProcessEventLocally(e);
e.Skip();
});
f_sizer->Add(count_label , 0, wxEXPAND | wxALIGN_CENTER_VERTICAL);
f_sizer->Add(m_count_spin, 0, wxALIGN_CENTER_VERTICAL);
auto arrange_label = new wxStaticText(this, wxID_ANY, _L("Auto arrange plate after cloning") + ":", wxDefaultPosition, wxDefaultSize, 0);
arrange_label->Wrap(FromDIP(300));
m_arrange_cb = new ::CheckBox(this);
m_arrange_cb->SetValue(m_config->get("auto_arrange") == "true");
f_sizer->Add(arrange_label, 0, wxEXPAND | wxALIGN_CENTER_VERTICAL);
f_sizer->Add(m_arrange_cb , 0, wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM, FromDIP(5));
v_sizer->Add(f_sizer, 1, wxEXPAND | wxALL, FromDIP(10));
auto bottom_sizer = new wxBoxSizer(wxHORIZONTAL);
m_progress = new ProgressBar(this, wxID_ANY, 100);
m_progress->SetHeight(FromDIP(8));
m_progress->SetMaxSize(wxSize(-1, FromDIP(8)));
m_progress->SetProgressForedColour(StateColor::darkModeColorFor(wxColour("#DFDFDF")));
m_progress->SetDoubleBuffered(true);
m_progress->Hide();
bottom_sizer->Add(m_progress, 2, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(10));
auto dlg_btns = new DialogButtons(this, {"Fill", "OK", "Cancel"}, "", 1 /*left_aligned*/);
// Keep pointer to OK button so we can trigger it manually on Enter.
Button *ok_btn = dlg_btns->GetOK();
dlg_btns->GetFIRST()->SetToolTip(_L("Fill bed with copies"));
dlg_btns->GetFIRST()->Bind(wxEVT_BUTTON, [this](wxCommandEvent &e) {
m_plater->fill_bed_with_instances();
EndModal(wxID_OK);
});
dlg_btns->GetOK()->Bind(wxEVT_BUTTON, [this, dlg_btns, v_sizer](wxCommandEvent &e) {
m_count_spin->Disable(); // also ensures input box value applied with wxEVT_KILL_FOCUS
m_arrange_cb->Disable();
m_count = m_count_spin->GetValue();
m_progress->Show();
dlg_btns->GetOK()->Hide();
dlg_btns->GetFIRST()->Hide();
this->Layout();
v_sizer->Fit(this);
Refresh();
Selection& sel = m_plater->canvas3D()->get_selection();
m_plater->take_snapshot(std::string("Selection-clone"));
m_plater->Freeze(); // Better to stop rendering canvas while processing
sel.copy_to_clipboard();
for (int i = 0; i < m_count; i++) { // same method with Selection::clone()
m_progress->SetValue(static_cast<int>(static_cast<double>(i) / m_count * 100)); // pass 0 / 100
sel.paste_from_clipboard();
if (m_cancel_process) {
m_plater->undo();
return;
}
wxYield(); // Allow event loop to process updates
}
if (!m_cancel_process) {
if (m_arrange_cb->GetValue()) {
m_plater->set_prepare_state(Job::PREPARE_STATE_MENU);
m_plater->arrange();
}
m_plater->Thaw();
EndModal(wxID_OK);
}
});
dlg_btns->GetCANCEL()->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) {
m_cancel_process = true;
if (m_plater->IsFrozen())
m_plater->Thaw();
EndModal(wxID_CANCEL);
});
bottom_sizer->Add(dlg_btns, 1, wxEXPAND);
v_sizer->Add(bottom_sizer, 0, wxEXPAND);
this->SetSizerAndFit(v_sizer);
this->Layout();
wxGetApp().UpdateDlgDarkUI(this);
// ------------------ ENTER KEY OVERRIDE ------------------
// This makes Enter inside the spinbox behave EXACTLY like clicking OK.
Bind(wxEVT_CHAR_HOOK, [this, ok_btn](wxKeyEvent &e)
{
const int key = e.GetKeyCode();
if ((key == WXK_RETURN || key == WXK_NUMPAD_ENTER) &&
m_count_spin->GetTextCtrl()->HasFocus())
{
// Trigger OK button's handler manually
wxCommandEvent evt(wxEVT_BUTTON, ok_btn->GetId());
ok_btn->GetEventHandler()->ProcessEvent(evt);
} else {
// Not handled here, process normally
e.Skip();
}
});
// --------------------------------------------------------
}
CloneDialog::~CloneDialog() {}
}} // namespace Slic3r::GUI