Compare commits

..

10 Commits

Author SHA1 Message Date
SoftFever
d8369e5f75 Fix faint toolbar icons on Wayland (#13723)
Use glBlendFuncSeparate in GLTexture::render_sub_texture so destination
  alpha stays at 1.0. The Wayland compositor honors framebuffer alpha for     window compositing; the previous straight-alpha blend reduced dst alpha
  at anti-aliased icon edges, making them semi-transparent against the
  desktop. RGB blending is unchanged, so X11/Windows/macOS are unaffected.
2026-05-19 01:38:37 +08:00
SoftFever
88b4a63228 Fix Linux data-view dropdown popup parenting (#13721)
Delay opening the object-list filament dropdown on wxGTK until the editor
  window is mapped, avoiding popup creation without a valid native toplevel.
  Also set the GTK transient parent for dropdown popups created from data-view
  cell editors.
2026-05-19 01:01:53 +08:00
yw4z
dc12126b78 Fix 2 Linux assertation errors on gtk_window_resize() (#13718)
Update DropDown.cpp
2026-05-18 19:01:42 +03:00
yw4z
054a173af7 Fix possible crash on startup because of low max volumetric speed on profiles (#13716)
* Update fdm_filament_common.json

* update

* Update fdm_filament_common.json
2026-05-18 19:00:48 +03:00
Kappa971
248a55abd0 Update Italian translation (#13674) 2026-05-18 10:29:38 -03:00
yw4z
b4aa070c40 MultiChooseDialog & CheckList class & improvements for Profile Dependencies (#9971)
* init

* fix

* fix

* update

* update

* update

* Update Tab.cpp

* Update CheckList.cpp
2026-05-18 19:46:47 +08:00
Thomas Henauer
b9ff15054f Fix wxGTK submenu popup parenting and sizing on Wayland (#13707) 2026-05-18 18:01:19 +08:00
CSLRDoesntGameDev
ddeaa4ba82 Add patches to the Anycubic Kobra X machine json (#13677)
add patched anycubic kobra x machine json

Co-authored-by: SoftFever <softfeverever@gmail.com>
2026-05-18 16:57:24 +08:00
Ian Chua
b333b04815 log: add logs surrounding logout to potentially catch any unwanted logouts or errors (#13713)
# Description

Currently, there is some suspicious behavior going on with the logout
flow, adding some logs to potentially catch any unwanted logouts or
unintentional behaviors.


[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
2026-05-18 16:39:59 +08:00
Ioannis Giannakas
e5ca01ba9e Fix per-volume "Only one wall" overrides being ignored on assembly parts (#13714) 2026-05-18 09:24:28 +01:00
57 changed files with 984 additions and 309 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><polygon points=".5 .5 5.5 7.5 5.5 13.5 9.5 15.5 9.5 7.5 14.5 .5 .5 .5" style="fill:none; stroke:#949494; stroke-linecap:round; stroke-linejoin:round;"/><line x1="8.64" y1="4.5" x2="10.19" y2="2.34" style="fill:none; stroke:#949494; stroke-linecap:round; stroke-linejoin:round;"/></svg>

After

Width:  |  Height:  |  Size: 414 B

View File

@@ -61,7 +61,7 @@
"nil" "nil"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

View File

@@ -58,7 +58,7 @@
"1.75" "1.75"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

File diff suppressed because one or more lines are too long

View File

@@ -58,7 +58,7 @@
"1.75" "1.75"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

View File

@@ -97,7 +97,7 @@
"nil" "nil"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_ramming_volumetric_speed": [ "filament_ramming_volumetric_speed": [
"-1" "-1"

View File

@@ -64,7 +64,7 @@
"1.75" "1.75"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

View File

@@ -64,7 +64,7 @@
"1.75" "1.75"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

View File

@@ -67,7 +67,7 @@
"1.75" "1.75"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

View File

@@ -64,7 +64,7 @@
"1.75" "1.75"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

View File

@@ -64,7 +64,7 @@
"1.75" "1.75"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

View File

@@ -64,7 +64,7 @@
"1.75" "1.75"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

View File

@@ -14,7 +14,7 @@
"15" "15"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "8"
], ],
"filament_type": [ "filament_type": [
"PETG" "PETG"

View File

@@ -6,7 +6,7 @@
"instantiation": "false", "instantiation": "false",
"fan_cooling_layer_time": "100", "fan_cooling_layer_time": "100",
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_type": [ "filament_type": [
"PLA" "PLA"

View File

@@ -14,7 +14,7 @@
"30" "30"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "3.6"
], ],
"filament_type": [ "filament_type": [
"TPU" "TPU"

View File

@@ -40,7 +40,7 @@
"1.75" "1.75"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

View File

@@ -64,7 +64,7 @@
"1.75" "1.75"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

View File

@@ -58,7 +58,7 @@
"1.75" "1.75"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

View File

@@ -58,7 +58,7 @@
"1.75" "1.75"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

View File

@@ -64,7 +64,7 @@
"1.75" "1.75"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

View File

@@ -38,7 +38,7 @@
"15" "15"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "8"
], ],
"filament_type": [ "filament_type": [
"PETG" "PETG"

View File

@@ -8,7 +8,7 @@
"100" "100"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_type": [ "filament_type": [
"PLA" "PLA"

View File

@@ -64,7 +64,7 @@
"1.75" "1.75"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

View File

@@ -64,7 +64,7 @@
"1.75" "1.75"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

View File

@@ -64,7 +64,7 @@
"1.75" "1.75"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

View File

@@ -64,7 +64,7 @@
"1.75" "1.75"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

View File

@@ -64,7 +64,7 @@
"1.75" "1.75"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

View File

@@ -64,7 +64,7 @@
"1.75" "1.75"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

View File

@@ -64,7 +64,7 @@
"1.75" "1.75"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

View File

@@ -64,7 +64,7 @@
"1.75" "1.75"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

View File

@@ -64,7 +64,7 @@
"1.75" "1.75"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

View File

@@ -70,7 +70,7 @@
"1.75" "1.75"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

View File

@@ -61,7 +61,7 @@
"nil" "nil"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

View File

@@ -58,7 +58,7 @@
"1.75" "1.75"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

View File

@@ -58,7 +58,7 @@
"1.75" "1.75"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

View File

@@ -58,7 +58,7 @@
"1.75" "1.75"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

View File

@@ -61,7 +61,7 @@
"1.75" "1.75"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

View File

@@ -64,7 +64,7 @@
"1.75" "1.75"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

View File

@@ -64,7 +64,7 @@
"1.75" "1.75"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

View File

@@ -67,7 +67,7 @@
"1.75" "1.75"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"0" "0"

View File

@@ -61,7 +61,7 @@
"nil" "nil"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

View File

@@ -64,7 +64,7 @@
"1.75" "1.75"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

View File

@@ -64,7 +64,7 @@
"1.75" "1.75"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

View File

@@ -61,7 +61,7 @@
"nil" "nil"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

View File

@@ -64,7 +64,7 @@
"2.85" "2.85"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"0" "12"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15" "15"

View File

@@ -159,6 +159,12 @@ bool Layer::is_perimeter_compatible(const PrintRegion& a, const PrintRegion& b)
&& config.detect_thin_wall == other_config.detect_thin_wall && config.detect_thin_wall == other_config.detect_thin_wall
&& config.infill_wall_overlap == other_config.infill_wall_overlap && config.infill_wall_overlap == other_config.infill_wall_overlap
&& config.top_bottom_infill_wall_overlap == other_config.top_bottom_infill_wall_overlap && config.top_bottom_infill_wall_overlap == other_config.top_bottom_infill_wall_overlap
// Orca: these flags directly change the effective wall count produced by the perimeter
// generator. If two regions disagree on any of them, merging their slices into one shared make_perimeters
// call would silently use the first region's flag for both.
&& config.only_one_wall_first_layer == other_config.only_one_wall_first_layer
&& config.only_one_wall_top == other_config.only_one_wall_top
&& config.min_width_top_surface == other_config.min_width_top_surface
&& config.seam_slope_type == other_config.seam_slope_type && config.seam_slope_type == other_config.seam_slope_type
&& config.seam_slope_conditional == other_config.seam_slope_conditional && config.seam_slope_conditional == other_config.seam_slope_conditional
&& config.scarf_angle_threshold == other_config.scarf_angle_threshold && config.scarf_angle_threshold == other_config.scarf_angle_threshold

View File

@@ -329,6 +329,8 @@ set(SLIC3R_GUI_SOURCES
GUI/Mouse3DController.hpp GUI/Mouse3DController.hpp
GUI/MsgDialog.cpp GUI/MsgDialog.cpp
GUI/MsgDialog.hpp GUI/MsgDialog.hpp
GUI/MultiChoiceDialog.hpp
GUI/MultiChoiceDialog.cpp
GUI/MultiMachine.cpp GUI/MultiMachine.cpp
GUI/MultiMachine.hpp GUI/MultiMachine.hpp
GUI/MultiMachineManagerPage.cpp GUI/MultiMachineManagerPage.cpp
@@ -496,6 +498,8 @@ set(SLIC3R_GUI_SOURCES
GUI/Widgets/Button.hpp GUI/Widgets/Button.hpp
GUI/Widgets/CheckBox.cpp GUI/Widgets/CheckBox.cpp
GUI/Widgets/CheckBox.hpp GUI/Widgets/CheckBox.hpp
GUI/Widgets/CheckList.cpp
GUI/Widgets/CheckList.hpp
GUI/Widgets/ComboBox.cpp GUI/Widgets/ComboBox.cpp
GUI/Widgets/ComboBox.hpp GUI/Widgets/ComboBox.hpp
GUI/Widgets/DialogButtons.cpp GUI/Widgets/DialogButtons.cpp

View File

@@ -324,9 +324,18 @@ wxWindow* BitmapChoiceRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelR
else else
c_editor->SetSelection(atoi(data.GetText().c_str()) - 1); c_editor->SetSelection(atoi(data.GetText().c_str()) - 1);
// Open the dropdown immediately when the editor is focused.
c_editor->Bind(wxEVT_SET_FOCUS, [c_editor](wxFocusEvent& evt) { c_editor->Bind(wxEVT_SET_FOCUS, [c_editor](wxFocusEvent& evt) {
#ifdef __WXGTK__
// On wxGTK the data-view editor may receive focus before its native
// window is mapped. Opening the popup one event later avoids creating
// the GTK popup without a valid toplevel parent.
c_editor->CallAfter([c_editor]() {
if (c_editor->IsShownOnScreen())
c_editor->ForceDropdownOpen();
});
#else
c_editor->ForceDropdownOpen(); c_editor->ForceDropdownOpen();
#endif
evt.Skip(); evt.Skip();
}); });
@@ -392,4 +401,3 @@ wxSize TextRenderer::GetSize() const
return GetTextExtent(m_value); return GetTextExtent(m_value);
} }

View File

@@ -663,7 +663,9 @@ void GLTexture::render_texture(unsigned int tex_id, float left, float right, flo
void GLTexture::render_sub_texture(unsigned int tex_id, float left, float right, float bottom, float top, const GLTexture::Quad_UVs& uvs) void GLTexture::render_sub_texture(unsigned int tex_id, float left, float right, float bottom, float top, const GLTexture::Quad_UVs& uvs)
{ {
glsafe(::glEnable(GL_BLEND)); glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); // Orca: fix washed-out toolbar icons on Wayland: keep destination alpha at 1.0 so the compositor
// does not treat anti-aliased icon edges as window transparency.
glsafe(::glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA));
glsafe(::glEnable(GL_TEXTURE_2D)); glsafe(::glEnable(GL_TEXTURE_2D));
glsafe(::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE)); glsafe(::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE));

View File

@@ -0,0 +1,58 @@
#include "MultiChoiceDialog.hpp"
#include "GUI_App.hpp"
#include "MainFrame.hpp"
namespace Slic3r { namespace GUI {
MultiChoiceDialog::MultiChoiceDialog(
wxWindow* parent,
const wxString& message,
const wxString& caption,
const wxArrayString& choices
)
: DPIDialog(parent ? parent : static_cast<wxWindow *>(wxGetApp().mainframe), wxID_ANY, caption, wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX)
{
SetBackgroundColour(*wxWHITE);
wxBoxSizer* w_sizer = new wxBoxSizer(wxVERTICAL);
if(!message.IsEmpty()){
wxStaticText *msg = new wxStaticText(this, wxID_ANY, message);
msg->SetFont(Label::Body_13);
msg->Wrap(-1);
w_sizer->Add(msg, 0, wxRIGHT | wxLEFT | wxTOP, FromDIP(10));
}
m_check_list = new CheckList(this, choices);
w_sizer->Add(m_check_list, 1, wxRIGHT | wxLEFT | wxTOP | wxEXPAND, FromDIP(10));
auto dlg_btns = new DialogButtons(this, {"OK", "Cancel"});
dlg_btns->GetOK()->Bind( wxEVT_BUTTON, [this](wxCommandEvent &e) {EndModal(wxID_OK);});
dlg_btns->GetCANCEL()->Bind(wxEVT_BUTTON, [this](wxCommandEvent &e) {EndModal(wxID_CANCEL);});
w_sizer->Add(dlg_btns, 0, wxEXPAND);
SetSizer(w_sizer);
Layout();
w_sizer->Fit(this);
wxGetApp().UpdateDlgDarkUI(this);
}
wxArrayInt MultiChoiceDialog::GetSelections() const
{
return m_check_list->GetSelections();
}
void MultiChoiceDialog::SetSelections(wxArrayInt sel_array)
{
m_check_list->SetSelections(sel_array);
}
MultiChoiceDialog::~MultiChoiceDialog() {}
void MultiChoiceDialog::on_dpi_changed(const wxRect &suggested_rect) {}
}} // namespace Slic3r::GUI

View File

@@ -0,0 +1,37 @@
#ifndef slic3r_GUI_MultiChoiceDialog_hpp_
#define slic3r_GUI_MultiChoiceDialog_hpp_
#include "Widgets/CheckList.hpp"
#include "Widgets/DialogButtons.hpp"
#include <wx/wx.h>
#include <vector>
#include <map>
namespace Slic3r { namespace GUI {
class MultiChoiceDialog : public DPIDialog
{
public:
MultiChoiceDialog(
wxWindow* parent = nullptr,
const wxString& message = wxEmptyString,
const wxString& caption = wxEmptyString,
const wxArrayString& choices = wxArrayString()
);
~MultiChoiceDialog();
wxArrayInt GetSelections() const;
void SetSelections(wxArrayInt sel_array);
protected:
CheckList* m_check_list;
wxArrayInt m_selected_indices;
void on_dpi_changed(const wxRect &suggested_rect) override;
};
}} // namespace Slic3r::GUI
#endif

View File

@@ -40,7 +40,7 @@
#include "UnsavedChangesDialog.hpp" #include "UnsavedChangesDialog.hpp"
#include "SavePresetDialog.hpp" #include "SavePresetDialog.hpp"
#include "EditGCodeDialog.hpp" #include "EditGCodeDialog.hpp"
#include "MultiChoiceDialog.hpp"
#include "MsgDialog.hpp" #include "MsgDialog.hpp"
#include "Notebook.hpp" #include "Notebook.hpp"
@@ -7002,7 +7002,15 @@ wxSizer* Tab::compatible_widget_create(wxWindow* parent, PresetDependencies &dep
presets.Add(from_u8(preset.name)); presets.Add(from_u8(preset.name));
} }
wxMultiChoiceDialog dlg(parent, deps.dialog_title, deps.dialog_label, presets); if(deps.type == Preset::TYPE_PRINTER){
deps.dialog_title = "Compatible printers";
deps.dialog_label = "Select printers";
}else{
deps.dialog_title = "Compatible process profiles";
deps.dialog_label = "Select profiles";
}
MultiChoiceDialog dlg(parent, deps.dialog_label, deps.dialog_title, presets);
wxGetApp().UpdateDlgDarkUI(&dlg); wxGetApp().UpdateDlgDarkUI(&dlg);
// Collect and set indices of depending_presets marked as compatible. // Collect and set indices of depending_presets marked as compatible.
wxArrayInt selections; wxArrayInt selections;
@@ -7015,13 +7023,16 @@ wxSizer* Tab::compatible_widget_create(wxWindow* parent, PresetDependencies &dep
break; break;
} }
dlg.SetSelections(selections); dlg.SetSelections(selections);
dlg.SetSize(FromDIP(wxSize(360, 480)));
std::vector<std::string> value; std::vector<std::string> value;
// Show the dialog. // Show the dialog.
if (dlg.ShowModal() == wxID_OK) { if (dlg.ShowModal() == wxID_OK) {
selections.Clear(); selections.Clear();
selections = dlg.GetSelections(); selections = dlg.GetSelections();
for (auto idx : selections) // leave list empty if all items checked. this will check "All" checkbox automatically. also fixes unnecessary config change
value.push_back(presets[idx].ToUTF8().data()); if(selections.GetCount() != presets.GetCount())
for (auto idx : selections)
value.push_back(presets[idx].ToUTF8().data());
if (value.empty()) { if (value.empty()) {
deps.checkbox->SetValue(1); deps.checkbox->SetValue(1);
deps.btn->Disable(); deps.btn->Disable();

View File

@@ -0,0 +1,225 @@
#include "CheckList.hpp"
#include "slic3r/GUI/GUI_App.hpp"
CheckList::CheckList(
wxWindow* parent,
const wxArrayString& choices,
long scroll_style
)
: wxWindow(parent, wxID_ANY)
, m_search(this, "search", 16)
, m_menu(this, "filter", 16)
, m_first_load(true)
{
Freeze();
w_sizer = new wxBoxSizer(wxVERTICAL);
f_sizer = new wxBoxSizer(wxHORIZONTAL);
f_bar = new wxPanel(this, wxID_ANY);
f_bar->SetBackgroundColour(parent->GetBackgroundColour());
m_filter_box = new TextInput(f_bar, "", "", "", wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER);
m_filter_box->SetIcon(m_search.bmp());
m_filter_box->SetMinSize(FromDIP(wxSize(200,24)));
m_filter_box->SetSize(FromDIP(wxSize(-1,24)));
m_filter_box->SetFocus();
m_filter_ctrl = m_filter_box->GetTextCtrl();
m_filter_ctrl->SetFont(Label::Body_13);
m_filter_ctrl->SetSize(wxSize(-1, FromDIP(16))); // Centers text vertically
m_filter_ctrl->SetHint(_L("Type to filter..."));
m_filter_ctrl->Bind(wxEVT_TEXT, [this](auto &e) {Filter(m_filter_ctrl->GetValue());});
m_filter_ctrl->Bind(wxEVT_TEXT_ENTER, [this](auto &e) {Filter(m_filter_ctrl->GetValue());});
m_filter_ctrl->Bind(wxEVT_SET_FOCUS, [this](auto &e) {Filter(m_filter_ctrl->GetValue());e.Skip();});
m_filter_ctrl->Bind(wxEVT_KILL_FOCUS, [this](auto &e) {Filter(m_filter_ctrl->GetValue());e.Skip();});
f_sizer->Add(m_filter_box, 1, wxEXPAND);
Bind(wxEVT_SET_FOCUS, [this](auto &e) {m_filter_box->SetFocus();});
fb_sizer = new wxBoxSizer(wxHORIZONTAL);
auto create_btn = [this] (wxString title, bool select){
auto btn = new wxStaticText(f_bar, wxID_ANY, title);
btn->SetForegroundColour("#009687");
btn->SetCursor(wxCURSOR_HAND);
btn->SetFont(Label::Body_13);
btn->Bind(wxEVT_LEFT_DOWN, [this, select](wxMouseEvent &e) {SelectAll(select);});
fb_sizer->Add(btn, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, FromDIP(10));
};
f_sizer->Add(fb_sizer,0 ,wxALIGN_CENTER_VERTICAL);
create_btn(_L("All") , true);
create_btn(_L("None"), false);
m_menu_button = new wxStaticBitmap(f_bar, wxID_ANY, m_menu.bmp());
m_menu_button->SetCursor(wxCURSOR_HAND);
m_menu_button->Bind(wxEVT_LEFT_DOWN, &CheckList::ShowMenu, this);
f_sizer->Add(m_menu_button,0 ,wxLEFT | wxALIGN_CENTER_VERTICAL, FromDIP(10));
f_bar->SetSizerAndFit(f_sizer);
w_sizer->Add(f_bar, 0, wxEXPAND);
auto spacer = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, FromDIP(3)));
spacer->SetBackgroundColour(parent->GetBackgroundColour());
w_sizer->Add(spacer, 0, wxEXPAND);
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->SetSizer(s_sizer);
m_scroll_area->SetBackgroundColour(parent->GetBackgroundColour());
m_scroll_area->Bind(wxEVT_RIGHT_DOWN, &CheckList::ShowMenu, this);
m_scroll_area->DisableFocusFromKeyboard();
m_info = new wxStaticText(m_scroll_area, wxID_ANY, "");
m_info->SetFont(Label::Body_13);
s_sizer->Add(m_info, 1, wxALIGN_CENTER_HORIZONTAL | wxALL, FromDIP(10));
m_info->Hide();
m_info_nonsel = _L("No selected items...");
m_info_allsel = _L("All items selected...");
m_info_empty = _L("No matching items...");
SetBackgroundColour(StateColor::darkModeColorFor("#DBDBDB")); // draws border on wxScrolledWindow
w_sizer->Add(m_scroll_area, 1, wxEXPAND | wxALL, FromDIP(1)); // 1 for border
s_sizer->Layout();
m_list_size = choices.size();
m_checks.reserve(m_list_size);
auto margin = FromDIP(2);
wxCheckBox* cb;
for (size_t i = 0; i < m_list_size; ++i){
cb = new wxCheckBox(m_scroll_area, wxID_ANY, choices[i]);
m_checks.emplace_back(cb);
s_sizer->Add(cb, 0, wxALL, margin);
}
m_scroll_area->FitInside();
s_sizer->Layout();
SetSizer(w_sizer);
Layout();
Thaw();
}
void CheckList::SetSelections(wxArrayInt sel_array){
if(!m_first_load){
for (size_t i = 0; i < m_list_size; ++i)
Check(i, false);
m_first_load = false;
}
for (int i : sel_array)
Check(i, true);
}
wxArrayInt CheckList::GetSelections()
{
wxArrayInt checks;
for (size_t i = 0; i < m_list_size; ++i)
if (m_checks[i]->GetValue())
checks.push_back(i);
return checks;
}
void CheckList::Check(int i, bool checked)
{
if (i > -1 && i < m_list_size)
m_checks[i]->SetValue(checked);
}
void CheckList::SelectAll(bool value)
{
for (size_t i = 0; i < m_list_size; ++i)
Check(i, value);
}
void CheckList::SelectVisible(bool value)
{
auto filter = m_filter_ctrl->GetValue().Lower();
if((!value && filter == "::unsel") || (value && filter == "::sel"))
m_filter_ctrl->SetValue("");
for (size_t i = 0; i < m_list_size; ++i)
if(m_checks[i]->IsShown())
Check(i, value);
}
bool CheckList::IsChecked(int i)
{
if (i > -1 && i < m_list_size)
return false;
return m_checks[i]->GetValue();
}
void CheckList::Filter(const wxString& filterText)
{
Freeze();
auto filter = filterText.Lower();
auto c_text = m_filter_ctrl->GetValue().Lower();
if(filter == "::sel" || filter == "::nonsel"){
if(c_text != filter){ // not text input
m_filter_ctrl->SetValue(filter);
m_filter_ctrl->SetSelection(0,-1);
}
fb_sizer->Show(false);
if (filter == "::sel")
for (auto& cb : m_checks) cb->Show(cb->GetValue());
else
for (auto& cb : m_checks) cb->Show(!cb->GetValue());
}
else{
bool clear = filterText.IsEmpty();
fb_sizer->Show(clear);
for (auto& cb : m_checks)
cb->Show(clear || cb->GetLabel().Lower().Contains(filter));
}
m_info->Show();
for (size_t i = 0; i < m_list_size; ++i) {
if (m_checks[i]->IsShown()){
m_info->Hide();
break;
}
}
if (m_info->IsShown())
m_info->SetLabel(filter == "::sel" ? m_info_nonsel : filter == "::nonsel" ? m_info_allsel : m_info_empty);
m_scroll_area->FitInside();
f_sizer->Layout();
Thaw();
}
void CheckList::ShowMenu(wxMouseEvent &evt)
{
bool filtering = !m_filter_ctrl->GetValue().IsEmpty();
bool list_empty = m_info->IsShown();
wxMenu m;
m.Append(wxID_FILE1, _L("Select All" ))->Enable(!filtering);
m.Append(wxID_FILE2, _L("Deselect All"))->Enable(!filtering);
m.AppendSeparator();
m.Append(wxID_FILE3, _L("Select visible" ))->Enable(!list_empty && filtering);
m.Append(wxID_FILE4, _L("Deselect visible"))->Enable(!list_empty && filtering);
m.AppendSeparator();
m.Append(wxID_FILE5, _L("Filter selected" ));
m.Append(wxID_FILE6, _L("Filter nonSelected"));
m.Bind(wxEVT_MENU, [this](wxCommandEvent& e) {
switch (e.GetId()){
case wxID_FILE1: SelectAll(true) ; break;
case wxID_FILE2: SelectAll(false) ; break;
case wxID_FILE3: SelectVisible(true) ; break;
case wxID_FILE4: SelectVisible(false); break;
case wxID_FILE5: Filter("::sel") ; break;
case wxID_FILE6: Filter("::nonsel") ; break;
default: break;
}
},wxID_FILE1, wxID_FILE6);
wxWindow* src = dynamic_cast<wxWindow*>(evt.GetEventObject());
if (!src) return;
wxPoint screen_pos = src->ClientToScreen(evt.GetPosition());
wxPoint local_pos = ScreenToClient(screen_pos);
PopupMenu(&m, local_pos);
}

View File

@@ -0,0 +1,59 @@
#ifndef slic3r_GUI_CHECKLIST_hpp_
#define slic3r_GUI_CHECKLIST_hpp_
#include "../wxExtensions.hpp"
#include "Label.hpp"
#include "TextInput.hpp"
#include <wx/wx.h>
#include <vector>
#include <wx/scrolwin.h>
#include <wx/menu.h>
class CheckList : public wxWindow
{
public:
CheckList(
wxWindow* parent,
const wxArrayString& choices = wxArrayString(),
long scroll_style = wxVSCROLL
);
wxArrayInt GetSelections();
void SetSelections(wxArrayInt sel_array);
void Check(int i, bool checked);
bool IsChecked(int i);
void Filter(const wxString& filterText);
void SelectAll(bool value);
void SelectVisible(bool value);
private:
void ShowMenu(wxMouseEvent &e);
std::vector<wxCheckBox*> m_checks;
int m_list_size;
bool m_first_load;
wxBoxSizer* w_sizer;
wxPanel* f_bar;
wxBoxSizer* f_sizer;
TextInput* m_filter_box;
wxTextCtrl* m_filter_ctrl;
wxBoxSizer* fb_sizer;
wxStaticBitmap* m_menu_button;
wxScrolledWindow* m_scroll_area;
wxBoxSizer* s_sizer;
wxStaticText* m_info;
wxString m_info_nonsel;
wxString m_info_allsel;
wxString m_info_empty;
ScalableBitmap m_search;
ScalableBitmap m_menu;
};
#endif // !slic3r_GUI_CheckList_hpp_

View File

@@ -167,6 +167,30 @@ bool DropDown::HasDismissLongTime()
(now - dismissTime).total_milliseconds() >= 20; (now - dismissTime).total_milliseconds() >= 20;
} }
void DropDown::Popup(wxWindow *focus)
{
#ifdef __WXGTK__
if (!mainDropDown && m_widget) {
// Data-view cell editors can receive focus before wxGTK infers a
// native popup parent, so provide the current toplevel explicitly.
GtkWindow *transient_parent = nullptr;
for (wxWindow *win = GetParent(); win; win = win->GetParent()) {
GtkWidget *widget = static_cast<GtkWidget *>(win->GetHandle());
if (!widget)
continue;
GtkWidget *top = gtk_widget_get_toplevel(widget);
if (GTK_IS_WINDOW(top)) {
transient_parent = GTK_WINDOW(top);
break;
}
}
if (transient_parent)
gtk_window_set_transient_for(GTK_WINDOW(m_widget), transient_parent);
}
#endif
PopupWindow::Popup(focus);
}
void DropDown::paintEvent(wxPaintEvent& evt) void DropDown::paintEvent(wxPaintEvent& evt)
{ {
// depending on your system you may need to look at double-buffered dcs // depending on your system you may need to look at double-buffered dcs
@@ -554,7 +578,8 @@ void DropDown::messureSize()
wxWindow::SetSize(szContent); wxWindow::SetSize(szContent);
#ifdef __WXGTK__ #ifdef __WXGTK__
// Gtk has a wrapper window for popup widget // Gtk has a wrapper window for popup widget
gtk_window_resize (GTK_WINDOW (m_widget), szContent.x, szContent.y); if (szContent.x > 0 && szContent.y > 0)
gtk_window_resize (GTK_WINDOW (m_widget), szContent.x, szContent.y);
#endif #endif
if (!groups.empty() && subDropDown == nullptr) { if (!groups.empty() && subDropDown == nullptr) {
subDropDown = new DropDown(items); subDropDown = new DropDown(items);
@@ -564,11 +589,6 @@ void DropDown::messureSize()
subDropDown->use_content_width = true; subDropDown->use_content_width = true;
subDropDown->Create(GetParent()); subDropDown->Create(GetParent());
#ifdef __WXGTK__ #ifdef __WXGTK__
// Orca: Keep the wx parent as the combobox so wxPopupTransientWindow installs
// its capture handlers on the main dropdown, but make the native GTK
// popup transient for the currently open popup to satisfy Wayland's
// xdg-shell rule that a popup's parent must be the topmost mapped popup.
gtk_window_set_transient_for(GTK_WINDOW(subDropDown->GetHandle()), GTK_WINDOW(GetHandle()));
// Orca: On Wayland, while the sub holds an xdg_popup grab, motion events for // Orca: On Wayland, while the sub holds an xdg_popup grab, motion events for
// the cursor over main may not be delivered (Mutter drops motion // the cursor over main may not be delivered (Mutter drops motion
// outside the grabbing surface). Poll on idle and synthesize a // outside the grabbing surface). Poll on idle and synthesize a
@@ -636,8 +656,11 @@ void DropDown::autoPosition()
// may exceed // may exceed
auto drect = wxDisplay(GetParent()).GetGeometry(); auto drect = wxDisplay(GetParent()).GetGeometry();
if (GetPosition().y + size.y + 10 > drect.GetBottom()) { if (GetPosition().y + size.y + 10 > drect.GetBottom()) {
int available_height = drect.GetBottom() - GetPosition().y - 10;
if (available_height < rowSize.y * 2)
return;
if (use_content_width && count <= 15) size.x += 6; if (use_content_width && count <= 15) size.x += 6;
size.y = drect.GetBottom() - GetPosition().y - 10; size.y = available_height;
wxWindow::SetSize(size); wxWindow::SetSize(size);
if (selection >= 0) { if (selection >= 0) {
if (offset.y + rowSize.y * (selection + 1) > size.y) if (offset.y + rowSize.y * (selection + 1) > size.y)
@@ -727,6 +750,13 @@ void DropDown::mouseMove(wxMouseEvent &event)
drop.group = items[-index - 2].group_key; drop.group = items[-index - 2].group_key;
drop.need_sync = true; drop.need_sync = true;
drop.messureSize(); drop.messureSize();
#ifdef __WXGTK__
// wxGTK wraps popup contents in a native GtkWindow. Make the submenu
// transient for the currently mapped parent popup window before
// positioning/showing it, so wlroots/Hyprland sees the topmost parent.
if (m_widget && drop.m_widget)
gtk_window_set_transient_for(GTK_WINDOW(drop.m_widget), GTK_WINDOW(m_widget));
#endif
drop.autoPosition(); drop.autoPosition();
drop.paintNow(); drop.paintNow();
if (!drop.IsShown()) if (!drop.IsShown())

View File

@@ -105,6 +105,8 @@ public:
bool HasDismissLongTime(); bool HasDismissLongTime();
void Popup(wxWindow *focus = nullptr) override;
protected: protected:
void Dismiss() override; void Dismiss() override;