mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-05-19 19:33:47 +00:00
Fix macOS object-list filament editor crash/glitch (#13700)
# Description Fixes a macOS object-list filament editor crash/glitch where the filament column could enter Cocoa native text editing and expose `wxCustomRendererObject: 0x...` or crash while committing an invalid `DataViewBitmapText` value. The visible `wxCustomRendererObject` glitch was observed on macOS through Cocoa `wxDataViewCtrl` editing. The underlying unsafe assignment/variant/editor assumptions are not inherently macOS-only, so the defensive data-path fixes are cross-platform. Changes: - Adds an explicit `DataViewBitmapText` assignment operator that avoids copying the `wxObject` base, matching the existing copy constructor. - Rejects unexpected variant types before reading object-list name/filament values as `DataViewBitmapText`. - Hardens filament editor value extraction against unexpected editor controls. - On macOS, routes filament-column editing through the custom bitmap-choice renderer instead of `wxDataViewCtrl::EditItem()`, avoiding native text editing of `wxCustomRendererObject`. Fixes https://github.com/OrcaSlicer/OrcaSlicer/issues/13682 Previous PR with conversation: [#13684](https://github.com/OrcaSlicer/OrcaSlicer/pull/13684) This should fix the crash observed by @Noisyfox as we're addressing the underlying problem of presenting the `wxCustomRendererObject`. ## Tests Validated on macOS arm64. Repeated double-click/edit attempts on an unselected object's filament cell no longer show `wxCustomRendererObject` and no crash reproduced. <!-- > A guide for users on how to download the artifacts from this PR. --> [How to Download Pull Requests Artifacts for Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
This commit is contained in:
@@ -343,7 +343,10 @@ wxWindow* BitmapChoiceRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelR
|
|||||||
|
|
||||||
bool BitmapChoiceRenderer::GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value)
|
bool BitmapChoiceRenderer::GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value)
|
||||||
{
|
{
|
||||||
::ComboBox*c = static_cast<::ComboBox *>(ctrl);
|
auto* c = dynamic_cast<::ComboBox*>(ctrl);
|
||||||
|
if (!c)
|
||||||
|
return false;
|
||||||
|
|
||||||
int selection = c->GetSelection();
|
int selection = c->GetSelection();
|
||||||
if (selection < 0)
|
if (selection < 0)
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -28,6 +28,15 @@ public:
|
|||||||
m_bmp(other.m_bmp)
|
m_bmp(other.m_bmp)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
DataViewBitmapText& operator=(const DataViewBitmapText &other)
|
||||||
|
{
|
||||||
|
if (this != &other) {
|
||||||
|
m_text = other.m_text;
|
||||||
|
m_bmp = other.m_bmp;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
void SetText(const wxString &text) { m_text = text; }
|
void SetText(const wxString &text) { m_text = text; }
|
||||||
wxString GetText() const { return m_text; }
|
wxString GetText() const { return m_text; }
|
||||||
void SetBitmap(const wxBitmap &bmp) { m_bmp = bmp; }
|
void SetBitmap(const wxBitmap &bmp) { m_bmp = bmp; }
|
||||||
|
|||||||
@@ -435,15 +435,25 @@ void ObjectList::create_objects_ctrl()
|
|||||||
|
|
||||||
// column Extruder of the view control:
|
// column Extruder of the view control:
|
||||||
BitmapChoiceRenderer* bmp_choice_renderer = new BitmapChoiceRenderer();
|
BitmapChoiceRenderer* bmp_choice_renderer = new BitmapChoiceRenderer();
|
||||||
bmp_choice_renderer->set_can_create_editor_ctrl_function([this]() {
|
const auto get_filament_context_item = [this]() {
|
||||||
return m_objects_model->GetItemType(GetSelection()) & (itVolume | itLayer | itObject);
|
#ifdef __WXOSX__
|
||||||
|
return m_filament_editor_item.IsOk() ? m_filament_editor_item : GetSelection();
|
||||||
|
#else
|
||||||
|
return GetSelection();
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
bmp_choice_renderer->set_can_create_editor_ctrl_function([this, get_filament_context_item]() {
|
||||||
|
const wxDataViewItem item = get_filament_context_item();
|
||||||
|
return m_objects_model->GetItemType(item) & (itVolume | itLayer | itObject);
|
||||||
});
|
});
|
||||||
bmp_choice_renderer->set_default_extruder_idx([this]() {
|
bmp_choice_renderer->set_default_extruder_idx([this, get_filament_context_item]() {
|
||||||
return m_objects_model->GetDefaultExtruderIdx(GetSelection());
|
const wxDataViewItem item = get_filament_context_item();
|
||||||
|
return m_objects_model->GetDefaultExtruderIdx(item);
|
||||||
});
|
});
|
||||||
bmp_choice_renderer->set_has_default_extruder([this]() {
|
bmp_choice_renderer->set_has_default_extruder([this, get_filament_context_item]() {
|
||||||
return m_objects_model->GetVolumeType(GetSelection()) == ModelVolumeType::PARAMETER_MODIFIER ||
|
const wxDataViewItem item = get_filament_context_item();
|
||||||
m_objects_model->GetItemType(GetSelection()) == itLayer;
|
return m_objects_model->GetVolumeType(item) == ModelVolumeType::PARAMETER_MODIFIER ||
|
||||||
|
m_objects_model->GetItemType(item) == itLayer;
|
||||||
});
|
});
|
||||||
AppendColumn(new wxDataViewColumn(_L("Fila."), bmp_choice_renderer,
|
AppendColumn(new wxDataViewColumn(_L("Fila."), bmp_choice_renderer,
|
||||||
colFilament, m_columns_width[colFilament] * em, wxALIGN_CENTER_HORIZONTAL, 0));
|
colFilament, m_columns_width[colFilament] * em, wxALIGN_CENTER_HORIZONTAL, 0));
|
||||||
@@ -461,11 +471,16 @@ void ObjectList::create_objects_ctrl()
|
|||||||
wxALIGN_CENTER_HORIZONTAL, 0);
|
wxALIGN_CENTER_HORIZONTAL, 0);
|
||||||
|
|
||||||
|
|
||||||
// Open filament editor faster
|
// On macOS, bypass wxDataViewCtrl::EditItem() for this custom renderer to avoid
|
||||||
|
// native text editing of wxCustomRendererObject.
|
||||||
this->Bind(wxEVT_DATAVIEW_ITEM_ACTIVATED, [this](wxDataViewEvent& event) {
|
this->Bind(wxEVT_DATAVIEW_ITEM_ACTIVATED, [this](wxDataViewEvent& event) {
|
||||||
if (event.GetColumn() == colFilament) {
|
if (event.GetColumn() == colFilament) {
|
||||||
|
#ifdef __WXOSX__
|
||||||
|
start_filament_editor(event.GetItem());
|
||||||
|
#else
|
||||||
// Trigger the editor opening manually
|
// Trigger the editor opening manually
|
||||||
this->EditItem(event.GetItem(), GetColumn(colFilament));
|
this->EditItem(event.GetItem(), GetColumn(colFilament));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1082,10 +1097,11 @@ void ObjectList::update_filament_in_config(const wxDataViewItem& item)
|
|||||||
if (m_prevent_update_filament_in_config)
|
if (m_prevent_update_filament_in_config)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
ModelConfig* config = nullptr;
|
||||||
const ItemType item_type = m_objects_model->GetItemType(item);
|
const ItemType item_type = m_objects_model->GetItemType(item);
|
||||||
if (item_type & itObject) {
|
if (item_type & itObject) {
|
||||||
const int obj_idx = m_objects_model->GetIdByItem(item);
|
const int obj_idx = m_objects_model->GetIdByItem(item);
|
||||||
m_config = &(*m_objects)[obj_idx]->config;
|
config = &(*m_objects)[obj_idx]->config;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetObject(item));
|
const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetObject(item));
|
||||||
@@ -1094,15 +1110,17 @@ void ObjectList::update_filament_in_config(const wxDataViewItem& item)
|
|||||||
if (obj_idx < 0 || ui_volume_idx < 0)
|
if (obj_idx < 0 || ui_volume_idx < 0)
|
||||||
return;
|
return;
|
||||||
int volume_in3d_idx = m_objects_model->get_real_volume_index_in_3d(obj_idx,ui_volume_idx);
|
int volume_in3d_idx = m_objects_model->get_real_volume_index_in_3d(obj_idx,ui_volume_idx);
|
||||||
m_config = &(*m_objects)[obj_idx]->volumes[volume_in3d_idx]->config;
|
config = &(*m_objects)[obj_idx]->volumes[volume_in3d_idx]->config;
|
||||||
}
|
}
|
||||||
else if (item_type & itLayer)
|
else if (item_type & itLayer)
|
||||||
m_config = &get_item_config(item);
|
config = &get_item_config(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_config)
|
if (!config)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
m_config = config;
|
||||||
|
|
||||||
take_snapshot("Change Filament");
|
take_snapshot("Change Filament");
|
||||||
|
|
||||||
const int extruder = m_objects_model->GetExtruderNumber(item);
|
const int extruder = m_objects_model->GetExtruderNumber(item);
|
||||||
@@ -6327,10 +6345,71 @@ void ObjectList::ItemValueChanged(wxDataViewEvent &event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __WXOSX__
|
||||||
|
bool ObjectList::is_live_model_item(wxDataViewItem item) const
|
||||||
|
{
|
||||||
|
if (!item.IsOk() || !m_objects_model)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
wxDataViewItemArray all_items;
|
||||||
|
m_objects_model->GetAllChildren(wxDataViewItem(nullptr), all_items);
|
||||||
|
for (const wxDataViewItem& live_item : all_items)
|
||||||
|
if (live_item.GetID() == item.GetID())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectList::start_filament_editor(wxDataViewItem item)
|
||||||
|
{
|
||||||
|
if (m_starting_filament_editor || !is_live_model_item(item))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const ItemType type = m_objects_model->GetItemType(item);
|
||||||
|
if (!(type & (itVolume | itLayer | itObject)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto* column = GetColumn(colFilament);
|
||||||
|
if (!column)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto* custom_renderer = dynamic_cast<wxDataViewCustomRenderer*>(column->GetRenderer());
|
||||||
|
if (!custom_renderer || custom_renderer->GetEditorCtrl())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_starting_filament_editor = true;
|
||||||
|
m_filament_editor_item = item;
|
||||||
|
const bool started = custom_renderer->StartEditing(item, GetItemRect(item, column));
|
||||||
|
m_filament_editor_item = wxDataViewItem(nullptr);
|
||||||
|
m_starting_filament_editor = false;
|
||||||
|
|
||||||
|
if (!started)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SetCustomRendererPtr(custom_renderer);
|
||||||
|
SetCustomRendererItem(item);
|
||||||
|
}
|
||||||
|
#endif // __WXOSX__
|
||||||
|
|
||||||
void GUI::ObjectList::OnStartEditing(wxDataViewEvent &event)
|
void GUI::ObjectList::OnStartEditing(wxDataViewEvent &event)
|
||||||
{
|
{
|
||||||
auto col = event.GetColumn();
|
auto col = event.GetColumn();
|
||||||
auto item = event.GetItem();
|
auto item = event.GetItem();
|
||||||
|
#ifdef __WXOSX__
|
||||||
|
if (col == colFilament) {
|
||||||
|
if (m_starting_filament_editor)
|
||||||
|
return;
|
||||||
|
|
||||||
|
event.Veto();
|
||||||
|
CallAfter([this, item] {
|
||||||
|
if (!is_live_model_item(item))
|
||||||
|
return;
|
||||||
|
start_filament_editor(item);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif // __WXOSX__
|
||||||
|
|
||||||
if (col == colName) {
|
if (col == colName) {
|
||||||
ObjectDataViewModelNode* node = (ObjectDataViewModelNode*)item.GetID();
|
ObjectDataViewModelNode* node = (ObjectDataViewModelNode*)item.GetID();
|
||||||
if (node->GetType() & itPlate) {
|
if (node->GetType() & itPlate) {
|
||||||
@@ -6398,6 +6477,12 @@ void ObjectList::OnEditingStarted(wxDataViewEvent &event)
|
|||||||
dynamic_cast<TabPrintModel*>(wxGetApp().get_model_tab(vol_idx >= 0))->reset_model_config();
|
dynamic_cast<TabPrintModel*>(wxGetApp().get_model_tab(vol_idx >= 0))->reset_model_config();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#ifdef __WXOSX__
|
||||||
|
if (col == colFilament) {
|
||||||
|
start_filament_editor(item);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif // __WXOSX__
|
||||||
if (col != colFilament && col != colName)
|
if (col != colFilament && col != colName)
|
||||||
return;
|
return;
|
||||||
auto column = GetColumn(col);
|
auto column = GetColumn(col);
|
||||||
|
|||||||
@@ -192,6 +192,13 @@ private:
|
|||||||
int m_last_selected_column = -1;
|
int m_last_selected_column = -1;
|
||||||
#endif /* __MSW__ */
|
#endif /* __MSW__ */
|
||||||
|
|
||||||
|
#ifdef __WXOSX__
|
||||||
|
bool m_starting_filament_editor = false;
|
||||||
|
wxDataViewItem m_filament_editor_item { nullptr };
|
||||||
|
bool is_live_model_item(wxDataViewItem item) const;
|
||||||
|
void start_filament_editor(wxDataViewItem item);
|
||||||
|
#endif // __WXOSX__
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
SettingsFactory::Bundle m_freq_settings_fff;
|
SettingsFactory::Bundle m_freq_settings_fff;
|
||||||
SettingsFactory::Bundle m_freq_settings_sla;
|
SettingsFactory::Bundle m_freq_settings_sla;
|
||||||
|
|||||||
@@ -332,12 +332,16 @@ bool ObjectDataViewModelNode::SetValue(const wxVariant& variant, unsigned col)
|
|||||||
m_variable_height_icon << variant;
|
m_variable_height_icon << variant;
|
||||||
return true;
|
return true;
|
||||||
case colName: {
|
case colName: {
|
||||||
|
if (variant.GetType() != wxT("DataViewBitmapText"))
|
||||||
|
return false;
|
||||||
DataViewBitmapText data;
|
DataViewBitmapText data;
|
||||||
data << variant;
|
data << variant;
|
||||||
m_bmp = data.GetBitmap();
|
m_bmp = data.GetBitmap();
|
||||||
m_name = data.GetText();
|
m_name = data.GetText();
|
||||||
return true; }
|
return true; }
|
||||||
case colFilament: {
|
case colFilament: {
|
||||||
|
if (variant.GetType() != wxT("DataViewBitmapText"))
|
||||||
|
return false;
|
||||||
DataViewBitmapText data;
|
DataViewBitmapText data;
|
||||||
data << variant;
|
data << variant;
|
||||||
m_extruder_bmp = data.GetBitmap();
|
m_extruder_bmp = data.GetBitmap();
|
||||||
|
|||||||
Reference in New Issue
Block a user