mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-28 19:21:20 -06:00
Merge remote-tracking branch 'remotes/origin/vb_undo_redo'
This commit is contained in:
commit
ab7ecc1819
60 changed files with 2799 additions and 578 deletions
|
|
@ -1188,6 +1188,8 @@ wxDEFINE_EVENT(EVT_GLCANVAS_TAB, SimpleEvent);
|
|||
wxDEFINE_EVENT(EVT_GLCANVAS_RESETGIZMOS, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent);
|
||||
|
||||
GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar)
|
||||
: m_canvas(canvas)
|
||||
|
|
@ -1791,7 +1793,7 @@ std::vector<int> GLCanvas3D::load_object(const Model& model, int obj_idx)
|
|||
void GLCanvas3D::mirror_selection(Axis axis)
|
||||
{
|
||||
m_selection.mirror(axis);
|
||||
do_mirror();
|
||||
do_mirror("Mirror Object");
|
||||
wxGetApp().obj_manipul()->set_dirty();
|
||||
}
|
||||
|
||||
|
|
@ -1812,14 +1814,14 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
|||
struct ModelVolumeState {
|
||||
ModelVolumeState(const GLVolume *volume) :
|
||||
model_volume(nullptr), geometry_id(volume->geometry_id), volume_idx(-1) {}
|
||||
ModelVolumeState(const ModelVolume *model_volume, const ModelID &instance_id, const GLVolume::CompositeID &composite_id) :
|
||||
ModelVolumeState(const ModelVolume *model_volume, const ObjectID &instance_id, const GLVolume::CompositeID &composite_id) :
|
||||
model_volume(model_volume), geometry_id(std::make_pair(model_volume->id().id, instance_id.id)), composite_id(composite_id), volume_idx(-1) {}
|
||||
ModelVolumeState(const ModelID &volume_id, const ModelID &instance_id) :
|
||||
ModelVolumeState(const ObjectID &volume_id, const ObjectID &instance_id) :
|
||||
model_volume(nullptr), geometry_id(std::make_pair(volume_id.id, instance_id.id)), volume_idx(-1) {}
|
||||
bool new_geometry() const { return this->volume_idx == size_t(-1); }
|
||||
const ModelVolume *model_volume;
|
||||
// ModelID of ModelVolume + ModelID of ModelInstance
|
||||
// or timestamp of an SLAPrintObjectStep + ModelID of ModelInstance
|
||||
// ObjectID of ModelVolume + ObjectID of ModelInstance
|
||||
// or timestamp of an SLAPrintObjectStep + ObjectID of ModelInstance
|
||||
std::pair<size_t, size_t> geometry_id;
|
||||
GLVolume::CompositeID composite_id;
|
||||
// Volume index in the new GLVolume vector.
|
||||
|
|
@ -2316,6 +2318,9 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
|
|||
return;
|
||||
}
|
||||
|
||||
if ((keyCode == WXK_ESCAPE) && _deactivate_undo_redo_toolbar_items())
|
||||
return;
|
||||
|
||||
if (m_gizmos.on_char(evt, *this))
|
||||
return;
|
||||
|
||||
|
|
@ -2348,6 +2353,25 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
|
|||
#endif /* __APPLE__ */
|
||||
post_event(SimpleEvent(EVT_GLTOOLBAR_PASTE));
|
||||
break;
|
||||
|
||||
|
||||
#ifdef __APPLE__
|
||||
case 'y':
|
||||
case 'Y':
|
||||
#else /* __APPLE__ */
|
||||
case WXK_CONTROL_Y:
|
||||
#endif /* __APPLE__ */
|
||||
post_event(SimpleEvent(EVT_GLCANVAS_REDO));
|
||||
break;
|
||||
#ifdef __APPLE__
|
||||
case 'z':
|
||||
case 'Z':
|
||||
#else /* __APPLE__ */
|
||||
case WXK_CONTROL_Z:
|
||||
#endif /* __APPLE__ */
|
||||
post_event(SimpleEvent(EVT_GLCANVAS_UNDO));
|
||||
break;
|
||||
|
||||
#ifdef __APPLE__
|
||||
case WXK_BACK: // the low cost Apple solutions are not equipped with a Delete key, use Backspace instead.
|
||||
#else /* __APPLE__ */
|
||||
|
|
@ -2368,7 +2392,6 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
|
|||
#endif /* __APPLE__ */
|
||||
post_event(SimpleEvent(EVT_GLTOOLBAR_DELETE));
|
||||
break;
|
||||
|
||||
case WXK_ESCAPE: { deselect_all(); break; }
|
||||
case '0': { select_view("iso"); break; }
|
||||
case '1': { select_view("top"); break; }
|
||||
|
|
@ -2715,12 +2738,17 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
}
|
||||
else if (evt.Leaving())
|
||||
{
|
||||
_deactivate_undo_redo_toolbar_items();
|
||||
|
||||
// to remove hover on objects when the mouse goes out of this canvas
|
||||
m_mouse.position = Vec2d(-1.0, -1.0);
|
||||
m_dirty = true;
|
||||
}
|
||||
else if (evt.LeftDown() || evt.RightDown())
|
||||
else if (evt.LeftDown() || evt.RightDown() || evt.MiddleDown())
|
||||
{
|
||||
if (_deactivate_undo_redo_toolbar_items())
|
||||
return;
|
||||
|
||||
// If user pressed left or right button we first check whether this happened
|
||||
// on a volume or not.
|
||||
m_layers_editing.state = LayersEditing::Unknown;
|
||||
|
|
@ -2918,9 +2946,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
else if ((m_mouse.drag.move_volume_idx != -1) && m_mouse.dragging)
|
||||
{
|
||||
m_regenerate_volumes = false;
|
||||
do_move();
|
||||
do_move("Move Object");
|
||||
wxGetApp().obj_manipul()->set_dirty();
|
||||
// Let the platter know that the dragging finished, so a delayed refresh
|
||||
// Let the plater know that the dragging finished, so a delayed refresh
|
||||
// of the scene with the background processing data should be performed.
|
||||
post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED));
|
||||
}
|
||||
|
|
@ -3077,11 +3105,14 @@ void GLCanvas3D::set_tooltip(const std::string& tooltip) const
|
|||
}
|
||||
|
||||
|
||||
void GLCanvas3D::do_move()
|
||||
void GLCanvas3D::do_move(const std::string& snapshot_type)
|
||||
{
|
||||
if (m_model == nullptr)
|
||||
return;
|
||||
|
||||
if (!snapshot_type.empty())
|
||||
wxGetApp().plater()->take_snapshot(_(L(snapshot_type)));
|
||||
|
||||
std::set<std::pair<int, int>> done; // keeps track of modified instances
|
||||
bool object_moved = false;
|
||||
Vec3d wipe_tower_origin = Vec3d::Zero();
|
||||
|
|
@ -3132,13 +3163,18 @@ void GLCanvas3D::do_move()
|
|||
|
||||
if (wipe_tower_origin != Vec3d::Zero())
|
||||
post_event(Vec3dEvent(EVT_GLCANVAS_WIPETOWER_MOVED, std::move(wipe_tower_origin)));
|
||||
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
void GLCanvas3D::do_rotate()
|
||||
void GLCanvas3D::do_rotate(const std::string& snapshot_type)
|
||||
{
|
||||
if (m_model == nullptr)
|
||||
return;
|
||||
|
||||
if (!snapshot_type.empty())
|
||||
wxGetApp().plater()->take_snapshot(_(L(snapshot_type)));
|
||||
|
||||
std::set<std::pair<int, int>> done; // keeps track of modified instances
|
||||
|
||||
Selection::EMode selection_mode = m_selection.get_mode();
|
||||
|
|
@ -3187,13 +3223,18 @@ void GLCanvas3D::do_rotate()
|
|||
|
||||
if (!done.empty())
|
||||
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_ROTATED));
|
||||
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
void GLCanvas3D::do_scale()
|
||||
void GLCanvas3D::do_scale(const std::string& snapshot_type)
|
||||
{
|
||||
if (m_model == nullptr)
|
||||
return;
|
||||
|
||||
if (!snapshot_type.empty())
|
||||
wxGetApp().plater()->take_snapshot(_(L(snapshot_type)));
|
||||
|
||||
std::set<std::pair<int, int>> done; // keeps track of modified instances
|
||||
|
||||
Selection::EMode selection_mode = m_selection.get_mode();
|
||||
|
|
@ -3239,18 +3280,27 @@ void GLCanvas3D::do_scale()
|
|||
|
||||
if (!done.empty())
|
||||
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_ROTATED));
|
||||
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
void GLCanvas3D::do_flatten()
|
||||
void GLCanvas3D::do_flatten(const Vec3d& normal, const std::string& snapshot_type)
|
||||
{
|
||||
do_rotate();
|
||||
if (!snapshot_type.empty())
|
||||
wxGetApp().plater()->take_snapshot(_(L(snapshot_type)));
|
||||
|
||||
m_selection.flattening_rotate(normal);
|
||||
do_rotate(""); // avoid taking another snapshot
|
||||
}
|
||||
|
||||
void GLCanvas3D::do_mirror()
|
||||
void GLCanvas3D::do_mirror(const std::string& snapshot_type)
|
||||
{
|
||||
if (m_model == nullptr)
|
||||
return;
|
||||
|
||||
if (!snapshot_type.empty())
|
||||
wxGetApp().plater()->take_snapshot(_(L(snapshot_type)));
|
||||
|
||||
std::set<std::pair<int, int>> done; // keeps track of modified instances
|
||||
|
||||
Selection::EMode selection_mode = m_selection.get_mode();
|
||||
|
|
@ -3289,6 +3339,8 @@ void GLCanvas3D::do_mirror()
|
|||
}
|
||||
|
||||
post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
|
||||
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
void GLCanvas3D::set_camera_zoom(double zoom)
|
||||
|
|
@ -3412,6 +3464,40 @@ bool GLCanvas3D::_is_shown_on_screen() const
|
|||
return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false;
|
||||
}
|
||||
|
||||
// Getter for the const char*[]
|
||||
static bool string_getter(const bool is_undo, int idx, const char** out_text)
|
||||
{
|
||||
return wxGetApp().plater()->undo_redo_string_getter(is_undo, idx, out_text);
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_undo_redo_stack(const bool is_undo, float pos_x)
|
||||
{
|
||||
const wxString& stack_name = _(is_undo ? L("Undo") : L("Redo"));
|
||||
ImGuiWrapper* imgui = wxGetApp().imgui();
|
||||
|
||||
const float x = pos_x * (float)get_camera().get_zoom() + 0.5f * (float)get_canvas_size().get_width();
|
||||
imgui->set_next_window_pos(x, m_toolbar.get_height(), ImGuiCond_Always, 0.5f, 0.0f);
|
||||
imgui->set_next_window_bg_alpha(0.5f);
|
||||
imgui->begin(wxString::Format(_(L("%s Stack")), stack_name),
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
|
||||
|
||||
int hovered = m_imgui_undo_redo_hovered_pos;
|
||||
int selected = -1;
|
||||
const float em = static_cast<float>(wxGetApp().em_unit());
|
||||
|
||||
if (imgui->undo_redo_list(ImVec2(12 * em, 20 * em), is_undo, &string_getter, hovered, selected))
|
||||
m_imgui_undo_redo_hovered_pos = hovered;
|
||||
else
|
||||
m_imgui_undo_redo_hovered_pos = -1;
|
||||
|
||||
if (selected >= 0)
|
||||
is_undo ? wxGetApp().plater()->undo_to(selected) : wxGetApp().plater()->redo_to(selected);
|
||||
|
||||
imgui->text(wxString::Format(_(L("%s %d Action")), stack_name, hovered + 1));
|
||||
|
||||
imgui->end();
|
||||
}
|
||||
|
||||
bool GLCanvas3D::_init_toolbar()
|
||||
{
|
||||
if (!m_toolbar.is_enabled())
|
||||
|
|
@ -3444,7 +3530,7 @@ bool GLCanvas3D::_init_toolbar()
|
|||
item.icon_filename = "add.svg";
|
||||
item.tooltip = _utf8(L("Add...")) + " [" + GUI::shortkey_ctrl_prefix() + "I]";
|
||||
item.sprite_id = 0;
|
||||
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_ADD)); };
|
||||
item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_ADD)); };
|
||||
if (!m_toolbar.add_item(item))
|
||||
return false;
|
||||
|
||||
|
|
@ -3452,8 +3538,8 @@ bool GLCanvas3D::_init_toolbar()
|
|||
item.icon_filename = "remove.svg";
|
||||
item.tooltip = _utf8(L("Delete")) + " [Del]";
|
||||
item.sprite_id = 1;
|
||||
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_DELETE)); };
|
||||
item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_delete(); };
|
||||
item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_DELETE)); };
|
||||
item.enabling_callback = []()->bool { return wxGetApp().plater()->can_delete(); };
|
||||
if (!m_toolbar.add_item(item))
|
||||
return false;
|
||||
|
||||
|
|
@ -3461,8 +3547,8 @@ bool GLCanvas3D::_init_toolbar()
|
|||
item.icon_filename = "delete_all.svg";
|
||||
item.tooltip = _utf8(L("Delete all")) + " [" + GUI::shortkey_ctrl_prefix() + "Del]";
|
||||
item.sprite_id = 2;
|
||||
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_DELETE_ALL)); };
|
||||
item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_delete_all(); };
|
||||
item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_DELETE_ALL)); };
|
||||
item.enabling_callback = []()->bool { return wxGetApp().plater()->can_delete_all(); };
|
||||
if (!m_toolbar.add_item(item))
|
||||
return false;
|
||||
|
||||
|
|
@ -3470,8 +3556,8 @@ bool GLCanvas3D::_init_toolbar()
|
|||
item.icon_filename = "arrange.svg";
|
||||
item.tooltip = _utf8(L("Arrange")) + " [A]";
|
||||
item.sprite_id = 3;
|
||||
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_ARRANGE)); };
|
||||
item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_arrange(); };
|
||||
item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_ARRANGE)); };
|
||||
item.enabling_callback = []()->bool { return wxGetApp().plater()->can_arrange(); };
|
||||
if (!m_toolbar.add_item(item))
|
||||
return false;
|
||||
|
||||
|
|
@ -3482,8 +3568,8 @@ bool GLCanvas3D::_init_toolbar()
|
|||
item.icon_filename = "copy.svg";
|
||||
item.tooltip = _utf8(L("Copy")) + " [" + GUI::shortkey_ctrl_prefix() + "C]";
|
||||
item.sprite_id = 4;
|
||||
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_COPY)); };
|
||||
item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_copy_to_clipboard(); };
|
||||
item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_COPY)); };
|
||||
item.enabling_callback = []()->bool { return wxGetApp().plater()->can_copy_to_clipboard(); };
|
||||
if (!m_toolbar.add_item(item))
|
||||
return false;
|
||||
|
||||
|
|
@ -3491,8 +3577,8 @@ bool GLCanvas3D::_init_toolbar()
|
|||
item.icon_filename = "paste.svg";
|
||||
item.tooltip = _utf8(L("Paste")) + " [" + GUI::shortkey_ctrl_prefix() + "V]";
|
||||
item.sprite_id = 5;
|
||||
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_PASTE)); };
|
||||
item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_paste_from_clipboard(); };
|
||||
item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_PASTE)); };
|
||||
item.enabling_callback = []()->bool { return wxGetApp().plater()->can_paste_from_clipboard(); };
|
||||
if (!m_toolbar.add_item(item))
|
||||
return false;
|
||||
|
||||
|
|
@ -3503,9 +3589,10 @@ bool GLCanvas3D::_init_toolbar()
|
|||
item.icon_filename = "instance_add.svg";
|
||||
item.tooltip = _utf8(L("Add instance")) + " [+]";
|
||||
item.sprite_id = 6;
|
||||
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_MORE)); };
|
||||
item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_MORE)); };
|
||||
item.visibility_callback = []()->bool { return wxGetApp().get_mode() != comSimple; };
|
||||
item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_increase_instances(); };
|
||||
item.enabling_callback = []()->bool { return wxGetApp().plater()->can_increase_instances(); };
|
||||
|
||||
if (!m_toolbar.add_item(item))
|
||||
return false;
|
||||
|
||||
|
|
@ -3513,9 +3600,9 @@ bool GLCanvas3D::_init_toolbar()
|
|||
item.icon_filename = "instance_remove.svg";
|
||||
item.tooltip = _utf8(L("Remove instance")) + " [-]";
|
||||
item.sprite_id = 7;
|
||||
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_FEWER)); };
|
||||
item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_FEWER)); };
|
||||
item.visibility_callback = []()->bool { return wxGetApp().get_mode() != comSimple; };
|
||||
item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_decrease_instances(); };
|
||||
item.enabling_callback = []()->bool { return wxGetApp().plater()->can_decrease_instances(); };
|
||||
if (!m_toolbar.add_item(item))
|
||||
return false;
|
||||
|
||||
|
|
@ -3526,9 +3613,9 @@ bool GLCanvas3D::_init_toolbar()
|
|||
item.icon_filename = "split_objects.svg";
|
||||
item.tooltip = _utf8(L("Split to objects"));
|
||||
item.sprite_id = 8;
|
||||
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_OBJECTS)); };
|
||||
item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_OBJECTS)); };
|
||||
item.visibility_callback = GLToolbarItem::Default_Visibility_Callback;
|
||||
item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_split_to_objects(); };
|
||||
item.enabling_callback = []()->bool { return wxGetApp().plater()->can_split_to_objects(); };
|
||||
if (!m_toolbar.add_item(item))
|
||||
return false;
|
||||
|
||||
|
|
@ -3536,9 +3623,9 @@ bool GLCanvas3D::_init_toolbar()
|
|||
item.icon_filename = "split_parts.svg";
|
||||
item.tooltip = _utf8(L("Split to parts"));
|
||||
item.sprite_id = 9;
|
||||
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_VOLUMES)); };
|
||||
item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_VOLUMES)); };
|
||||
item.visibility_callback = []()->bool { return wxGetApp().get_mode() != comSimple; };
|
||||
item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_split_to_volumes(); };
|
||||
item.enabling_callback = []()->bool { return wxGetApp().plater()->can_split_to_volumes(); };
|
||||
if (!m_toolbar.add_item(item))
|
||||
return false;
|
||||
|
||||
|
|
@ -3549,10 +3636,42 @@ bool GLCanvas3D::_init_toolbar()
|
|||
item.icon_filename = "layers_white.svg";
|
||||
item.tooltip = _utf8(L("Layers editing"));
|
||||
item.sprite_id = 10;
|
||||
item.is_toggable = true;
|
||||
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); };
|
||||
item.left.toggable = true;
|
||||
item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); };
|
||||
item.visibility_callback = [this]()->bool { return m_process->current_printer_technology() == ptFFF; };
|
||||
item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_layers_editing(); };
|
||||
item.enabling_callback = []()->bool { return wxGetApp().plater()->can_layers_editing(); };
|
||||
if (!m_toolbar.add_item(item))
|
||||
return false;
|
||||
|
||||
if (!m_toolbar.add_separator())
|
||||
return false;
|
||||
|
||||
item.name = "undo";
|
||||
#if ENABLE_SVG_ICONS
|
||||
item.icon_filename = "undo_toolbar.svg";
|
||||
#endif // ENABLE_SVG_ICONS
|
||||
item.tooltip = _utf8(L("Undo")) + " [" + GUI::shortkey_ctrl_prefix() + "Z]";
|
||||
item.sprite_id = 11;
|
||||
item.left.toggable = false;
|
||||
item.left.action_callback = [this]() { post_event(SimpleEvent(EVT_GLCANVAS_UNDO)); };
|
||||
item.right.toggable = true;
|
||||
item.right.action_callback = [this]() { m_imgui_undo_redo_hovered_pos = -1; };
|
||||
item.right.render_callback = [this](float left, float right, float, float) { if (m_canvas != nullptr) _render_undo_redo_stack(true, 0.5f * (left + right)); };
|
||||
item.visibility_callback = []()->bool { return true; };
|
||||
item.enabling_callback = [this]()->bool { return wxGetApp().plater()->can_undo(); };
|
||||
if (!m_toolbar.add_item(item))
|
||||
return false;
|
||||
|
||||
item.name = "redo";
|
||||
#if ENABLE_SVG_ICONS
|
||||
item.icon_filename = "redo_toolbar.svg";
|
||||
#endif // ENABLE_SVG_ICONS
|
||||
item.tooltip = _utf8(L("Redo")) + " [" + GUI::shortkey_ctrl_prefix() + "Y]";
|
||||
item.sprite_id = 12;
|
||||
item.left.action_callback = [this]() { post_event(SimpleEvent(EVT_GLCANVAS_REDO)); };
|
||||
item.right.action_callback = [this]() { m_imgui_undo_redo_hovered_pos = -1; };
|
||||
item.right.render_callback = [this](float left, float right, float, float) { if (m_canvas != nullptr) _render_undo_redo_stack(false, 0.5f * (left + right)); };
|
||||
item.enabling_callback = [this]()->bool { return wxGetApp().plater()->can_redo(); };
|
||||
if (!m_toolbar.add_item(item))
|
||||
return false;
|
||||
|
||||
|
|
@ -5551,6 +5670,22 @@ void GLCanvas3D::_update_selection_from_hover()
|
|||
m_dirty = true;
|
||||
}
|
||||
|
||||
bool GLCanvas3D::_deactivate_undo_redo_toolbar_items()
|
||||
{
|
||||
if (m_toolbar.is_item_pressed("undo"))
|
||||
{
|
||||
m_toolbar.force_right_action(m_toolbar.get_item_id("undo"), *this);
|
||||
return true;
|
||||
}
|
||||
else if (m_toolbar.is_item_pressed("redo"))
|
||||
{
|
||||
m_toolbar.force_right_action(m_toolbar.get_item_id("redo"), *this);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const Print* GLCanvas3D::fff_print() const
|
||||
{
|
||||
return (m_process == nullptr) ? nullptr : m_process->fff_print();
|
||||
|
|
|
|||
|
|
@ -127,6 +127,8 @@ wxDECLARE_EVENT(EVT_GLCANVAS_TAB, SimpleEvent);
|
|||
wxDECLARE_EVENT(EVT_GLCANVAS_RESETGIZMOS, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent);
|
||||
|
||||
class GLCanvas3D
|
||||
{
|
||||
|
|
@ -485,6 +487,8 @@ private:
|
|||
RenderStats m_render_stats;
|
||||
#endif // ENABLE_RENDER_STATISTICS
|
||||
|
||||
int m_imgui_undo_redo_hovered_pos{ -1 };
|
||||
|
||||
public:
|
||||
GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar);
|
||||
~GLCanvas3D();
|
||||
|
|
@ -596,11 +600,12 @@ public:
|
|||
|
||||
void set_tooltip(const std::string& tooltip) const;
|
||||
|
||||
void do_move();
|
||||
void do_rotate();
|
||||
void do_scale();
|
||||
void do_flatten();
|
||||
void do_mirror();
|
||||
// the following methods add a snapshot to the undo/redo stack, unless the given string is empty
|
||||
void do_move(const std::string& snapshot_type);
|
||||
void do_rotate(const std::string& snapshot_type);
|
||||
void do_scale(const std::string& snapshot_type);
|
||||
void do_flatten(const Vec3d& normal, const std::string& snapshot_type);
|
||||
void do_mirror(const std::string& snapshot_type);
|
||||
|
||||
void set_camera_zoom(double zoom);
|
||||
|
||||
|
|
@ -673,6 +678,7 @@ private:
|
|||
#endif // ENABLE_SHOW_CAMERA_TARGET
|
||||
void _render_sla_slices() const;
|
||||
void _render_selection_sidebar_hints() const;
|
||||
void _render_undo_redo_stack(const bool is_undo, float pos_x);
|
||||
|
||||
void _update_volumes_hover_state() const;
|
||||
|
||||
|
|
@ -731,6 +737,8 @@ private:
|
|||
// updates the selection from the content of m_hover_volume_idxs
|
||||
void _update_selection_from_hover();
|
||||
|
||||
bool _deactivate_undo_redo_toolbar_items();
|
||||
|
||||
static std::vector<float> _parse_colors(const std::vector<std::string>& colors);
|
||||
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -34,18 +34,24 @@ wxDEFINE_EVENT(EVT_GLVIEWTOOLBAR_PREVIEW, SimpleEvent);
|
|||
|
||||
const GLToolbarItem::ActionCallback GLToolbarItem::Default_Action_Callback = [](){};
|
||||
const GLToolbarItem::VisibilityCallback GLToolbarItem::Default_Visibility_Callback = []()->bool { return true; };
|
||||
const GLToolbarItem::EnabledStateCallback GLToolbarItem::Default_Enabled_State_Callback = []()->bool { return true; };
|
||||
const GLToolbarItem::EnablingCallback GLToolbarItem::Default_Enabling_Callback = []()->bool { return true; };
|
||||
const GLToolbarItem::RenderCallback GLToolbarItem::Default_Render_Callback = [](float, float, float, float){};
|
||||
|
||||
GLToolbarItem::Data::Option::Option()
|
||||
: toggable(false)
|
||||
, action_callback(Default_Action_Callback)
|
||||
, render_callback(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
GLToolbarItem::Data::Data()
|
||||
: name("")
|
||||
, icon_filename("")
|
||||
, tooltip("")
|
||||
, sprite_id(-1)
|
||||
, is_toggable(false)
|
||||
, visible(true)
|
||||
, action_callback(Default_Action_Callback)
|
||||
, visibility_callback(Default_Visibility_Callback)
|
||||
, enabled_state_callback(Default_Enabled_State_Callback)
|
||||
, enabling_callback(Default_Enabling_Callback)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -53,6 +59,7 @@ GLToolbarItem::GLToolbarItem(GLToolbarItem::EType type, const GLToolbarItem::Dat
|
|||
: m_type(type)
|
||||
, m_state(Normal)
|
||||
, m_data(data)
|
||||
, m_last_action_type(Undefined)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -68,7 +75,7 @@ bool GLToolbarItem::update_visibility()
|
|||
|
||||
bool GLToolbarItem::update_enabled_state()
|
||||
{
|
||||
bool enabled = m_data.enabled_state_callback();
|
||||
bool enabled = m_data.enabling_callback();
|
||||
bool ret = (is_enabled() != enabled);
|
||||
if (ret)
|
||||
m_state = enabled ? GLToolbarItem::Normal : GLToolbarItem::Disabled;
|
||||
|
|
@ -79,6 +86,14 @@ bool GLToolbarItem::update_enabled_state()
|
|||
void GLToolbarItem::render(unsigned int tex_id, float left, float right, float bottom, float top, unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const
|
||||
{
|
||||
GLTexture::render_sub_texture(tex_id, left, right, bottom, top, get_uvs(tex_width, tex_height, icon_size));
|
||||
|
||||
if (is_pressed())
|
||||
{
|
||||
if ((m_last_action_type == Left) && m_data.left.can_render())
|
||||
m_data.left.render_callback(left, right, bottom, top);
|
||||
else if ((m_last_action_type == Right) && m_data.right.can_render())
|
||||
m_data.right.render_callback(left, right, bottom, top);
|
||||
}
|
||||
}
|
||||
|
||||
GLTexture::Quad_UVs GLToolbarItem::get_uvs(unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const
|
||||
|
|
@ -136,6 +151,7 @@ GLToolbar::GLToolbar(GLToolbar::EType type, const std::string& name)
|
|||
, m_enabled(false)
|
||||
, m_icons_texture_dirty(true)
|
||||
, m_tooltip("")
|
||||
, m_pressed_toggable_id(-1)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -295,7 +311,7 @@ void GLToolbar::select_item(const std::string& name)
|
|||
|
||||
bool GLToolbar::is_item_pressed(const std::string& name) const
|
||||
{
|
||||
for (GLToolbarItem* item : m_items)
|
||||
for (const GLToolbarItem* item : m_items)
|
||||
{
|
||||
if (item->get_name() == name)
|
||||
return item->is_pressed();
|
||||
|
|
@ -306,7 +322,7 @@ bool GLToolbar::is_item_pressed(const std::string& name) const
|
|||
|
||||
bool GLToolbar::is_item_disabled(const std::string& name) const
|
||||
{
|
||||
for (GLToolbarItem* item : m_items)
|
||||
for (const GLToolbarItem* item : m_items)
|
||||
{
|
||||
if (item->get_name() == name)
|
||||
return item->is_disabled();
|
||||
|
|
@ -317,7 +333,7 @@ bool GLToolbar::is_item_disabled(const std::string& name) const
|
|||
|
||||
bool GLToolbar::is_item_visible(const std::string& name) const
|
||||
{
|
||||
for (GLToolbarItem* item : m_items)
|
||||
for (const GLToolbarItem* item : m_items)
|
||||
{
|
||||
if (item->get_name() == name)
|
||||
return item->is_visible();
|
||||
|
|
@ -326,11 +342,46 @@ bool GLToolbar::is_item_visible(const std::string& name) const
|
|||
return false;
|
||||
}
|
||||
|
||||
bool GLToolbar::is_any_item_pressed() const
|
||||
{
|
||||
for (const GLToolbarItem* item : m_items)
|
||||
{
|
||||
if (item->is_pressed())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int GLToolbar::get_item_id(const std::string& name) const
|
||||
{
|
||||
for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i)
|
||||
{
|
||||
if (m_items[i]->get_name() == name)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void GLToolbar::force_left_action(unsigned int item_id, GLCanvas3D& parent)
|
||||
{
|
||||
do_action(GLToolbarItem::Left, item_id, parent, false);
|
||||
}
|
||||
|
||||
void GLToolbar::force_right_action(unsigned int item_id, GLCanvas3D& parent)
|
||||
{
|
||||
do_action(GLToolbarItem::Right, item_id, parent, false);
|
||||
}
|
||||
|
||||
bool GLToolbar::update_items_state()
|
||||
{
|
||||
bool ret = false;
|
||||
ret |= update_items_visibility();
|
||||
ret |= update_items_enabled_state();
|
||||
if (!is_any_item_pressed())
|
||||
m_pressed_toggable_id = -1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -392,10 +443,11 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent)
|
|||
m_mouse_capture.left = true;
|
||||
m_mouse_capture.parent = &parent;
|
||||
processed = true;
|
||||
if ((item_id != -2) && !m_items[item_id]->is_separator())
|
||||
if ((item_id != -2) && !m_items[item_id]->is_separator() && ((m_pressed_toggable_id == -1) || (m_items[item_id]->get_last_action_type() == GLToolbarItem::Left)))
|
||||
{
|
||||
// mouse is inside an icon
|
||||
do_action((unsigned int)item_id, parent);
|
||||
do_action(GLToolbarItem::Left, (unsigned int)item_id, parent, true);
|
||||
parent.set_as_dirty();
|
||||
}
|
||||
}
|
||||
else if (evt.MiddleDown())
|
||||
|
|
@ -407,6 +459,13 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent)
|
|||
{
|
||||
m_mouse_capture.right = true;
|
||||
m_mouse_capture.parent = &parent;
|
||||
processed = true;
|
||||
if ((item_id != -2) && !m_items[item_id]->is_separator() && ((m_pressed_toggable_id == -1) || (m_items[item_id]->get_last_action_type() == GLToolbarItem::Right)))
|
||||
{
|
||||
// mouse is inside an icon
|
||||
do_action(GLToolbarItem::Right, (unsigned int)item_id, parent, true);
|
||||
parent.set_as_dirty();
|
||||
}
|
||||
}
|
||||
else if (evt.LeftUp())
|
||||
processed = true;
|
||||
|
|
@ -477,38 +536,61 @@ float GLToolbar::get_main_size() const
|
|||
return size * m_layout.scale;
|
||||
}
|
||||
|
||||
void GLToolbar::do_action(unsigned int item_id, GLCanvas3D& parent)
|
||||
void GLToolbar::do_action(GLToolbarItem::EActionType type, unsigned int item_id, GLCanvas3D& parent, bool check_hover)
|
||||
{
|
||||
if (item_id < (unsigned int)m_items.size())
|
||||
if ((m_pressed_toggable_id == -1) || (m_pressed_toggable_id == item_id))
|
||||
{
|
||||
GLToolbarItem* item = m_items[item_id];
|
||||
if ((item != nullptr) && !item->is_separator() && item->is_hovered())
|
||||
if (item_id < (unsigned int)m_items.size())
|
||||
{
|
||||
if (item->is_toggable())
|
||||
GLToolbarItem* item = m_items[item_id];
|
||||
if ((item != nullptr) && !item->is_separator() && (!check_hover || item->is_hovered()))
|
||||
{
|
||||
GLToolbarItem::EState state = item->get_state();
|
||||
if (state == GLToolbarItem::Hover)
|
||||
item->set_state(GLToolbarItem::HoverPressed);
|
||||
else if (state == GLToolbarItem::HoverPressed)
|
||||
item->set_state(GLToolbarItem::Hover);
|
||||
|
||||
parent.render();
|
||||
item->do_action();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_type == Radio)
|
||||
select_item(item->get_name());
|
||||
else
|
||||
item->set_state(GLToolbarItem::HoverPressed);
|
||||
|
||||
parent.render();
|
||||
item->do_action();
|
||||
if ((m_type == Normal) && (item->get_state() != GLToolbarItem::Disabled))
|
||||
if (((type == GLToolbarItem::Right) && item->is_right_toggable()) ||
|
||||
((type == GLToolbarItem::Left) && item->is_left_toggable()))
|
||||
{
|
||||
// the item may get disabled during the action, if not, set it back to hover state
|
||||
item->set_state(GLToolbarItem::Hover);
|
||||
GLToolbarItem::EState state = item->get_state();
|
||||
if (state == GLToolbarItem::Hover)
|
||||
item->set_state(GLToolbarItem::HoverPressed);
|
||||
else if (state == GLToolbarItem::HoverPressed)
|
||||
item->set_state(GLToolbarItem::Hover);
|
||||
else if (state == GLToolbarItem::Pressed)
|
||||
item->set_state(GLToolbarItem::Normal);
|
||||
else if (state == GLToolbarItem::Normal)
|
||||
item->set_state(GLToolbarItem::Pressed);
|
||||
|
||||
m_pressed_toggable_id = item->is_pressed() ? item_id : -1;
|
||||
item->reset_last_action_type();
|
||||
|
||||
parent.render();
|
||||
switch (type)
|
||||
{
|
||||
default:
|
||||
case GLToolbarItem::Left: { item->do_left_action(); break; }
|
||||
case GLToolbarItem::Right: { item->do_right_action(); break; }
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_type == Radio)
|
||||
select_item(item->get_name());
|
||||
else
|
||||
item->set_state(item->is_hovered() ? GLToolbarItem::HoverPressed : GLToolbarItem::Pressed);
|
||||
|
||||
item->reset_last_action_type();
|
||||
parent.render();
|
||||
switch (type)
|
||||
{
|
||||
default:
|
||||
case GLToolbarItem::Left: { item->do_left_action(); break; }
|
||||
case GLToolbarItem::Right: { item->do_right_action(); break; }
|
||||
}
|
||||
|
||||
if ((m_type == Normal) && (item->get_state() != GLToolbarItem::Disabled))
|
||||
{
|
||||
// the item may get disabled during the action, if not, set it back to hover state
|
||||
item->set_state(GLToolbarItem::Hover);
|
||||
parent.render();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1212,9 +1294,15 @@ bool GLToolbar::update_items_enabled_state()
|
|||
{
|
||||
bool ret = false;
|
||||
|
||||
for (GLToolbarItem* item : m_items)
|
||||
for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i)
|
||||
{
|
||||
GLToolbarItem* item = m_items[i];
|
||||
ret |= item->update_enabled_state();
|
||||
if (item->is_enabled() && (m_pressed_toggable_id != -1) && (m_pressed_toggable_id != i))
|
||||
{
|
||||
ret = true;
|
||||
item->set_state(GLToolbarItem::Disabled);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret)
|
||||
|
|
|
|||
|
|
@ -36,7 +36,8 @@ class GLToolbarItem
|
|||
public:
|
||||
typedef std::function<void()> ActionCallback;
|
||||
typedef std::function<bool()> VisibilityCallback;
|
||||
typedef std::function<bool()> EnabledStateCallback;
|
||||
typedef std::function<bool()> EnablingCallback;
|
||||
typedef std::function<void(float, float, float, float)> RenderCallback;
|
||||
|
||||
enum EType : unsigned char
|
||||
{
|
||||
|
|
@ -45,6 +46,14 @@ public:
|
|||
Num_Types
|
||||
};
|
||||
|
||||
enum EActionType : unsigned char
|
||||
{
|
||||
Undefined,
|
||||
Left,
|
||||
Right,
|
||||
Num_Action_Types
|
||||
};
|
||||
|
||||
enum EState : unsigned char
|
||||
{
|
||||
Normal,
|
||||
|
|
@ -57,27 +66,42 @@ public:
|
|||
|
||||
struct Data
|
||||
{
|
||||
struct Option
|
||||
{
|
||||
bool toggable;
|
||||
ActionCallback action_callback;
|
||||
RenderCallback render_callback;
|
||||
|
||||
Option();
|
||||
|
||||
bool can_render() const { return toggable && (render_callback != nullptr); }
|
||||
};
|
||||
|
||||
std::string name;
|
||||
std::string icon_filename;
|
||||
std::string tooltip;
|
||||
unsigned int sprite_id;
|
||||
bool is_toggable;
|
||||
// mouse left click
|
||||
Option left;
|
||||
// mouse right click
|
||||
Option right;
|
||||
bool visible;
|
||||
ActionCallback action_callback;
|
||||
VisibilityCallback visibility_callback;
|
||||
EnabledStateCallback enabled_state_callback;
|
||||
EnablingCallback enabling_callback;
|
||||
|
||||
Data();
|
||||
};
|
||||
|
||||
static const ActionCallback Default_Action_Callback;
|
||||
static const VisibilityCallback Default_Visibility_Callback;
|
||||
static const EnabledStateCallback Default_Enabled_State_Callback;
|
||||
static const EnablingCallback Default_Enabling_Callback;
|
||||
static const RenderCallback Default_Render_Callback;
|
||||
|
||||
private:
|
||||
EType m_type;
|
||||
EState m_state;
|
||||
Data m_data;
|
||||
EActionType m_last_action_type;
|
||||
|
||||
public:
|
||||
GLToolbarItem(EType type, const Data& data);
|
||||
|
|
@ -89,17 +113,25 @@ public:
|
|||
const std::string& get_icon_filename() const { return m_data.icon_filename; }
|
||||
const std::string& get_tooltip() const { return m_data.tooltip; }
|
||||
|
||||
void do_action() { m_data.action_callback(); }
|
||||
void do_left_action() { m_last_action_type = Left; m_data.left.action_callback(); }
|
||||
void do_right_action() { m_last_action_type = Right; m_data.right.action_callback(); }
|
||||
|
||||
bool is_enabled() const { return m_state != Disabled; }
|
||||
bool is_disabled() const { return m_state == Disabled; }
|
||||
bool is_hovered() const { return (m_state == Hover) || (m_state == HoverPressed); }
|
||||
bool is_pressed() const { return (m_state == Pressed) || (m_state == HoverPressed); }
|
||||
|
||||
bool is_toggable() const { return m_data.is_toggable; }
|
||||
bool is_visible() const { return m_data.visible; }
|
||||
bool is_separator() const { return m_type == Separator; }
|
||||
|
||||
bool is_left_toggable() const { return m_data.left.toggable; }
|
||||
bool is_right_toggable() const { return m_data.right.toggable; }
|
||||
|
||||
bool has_left_render_callback() const { return m_data.left.render_callback != nullptr; }
|
||||
bool has_right_render_callback() const { return m_data.right.render_callback != nullptr; }
|
||||
|
||||
EActionType get_last_action_type() const { return m_last_action_type; }
|
||||
void reset_last_action_type() { m_last_action_type = Undefined; }
|
||||
|
||||
// returns true if the state changes
|
||||
bool update_visibility();
|
||||
// returns true if the state changes
|
||||
|
|
@ -212,6 +244,7 @@ private:
|
|||
|
||||
MouseCapture m_mouse_capture;
|
||||
std::string m_tooltip;
|
||||
unsigned int m_pressed_toggable_id;
|
||||
|
||||
public:
|
||||
GLToolbar(EType type, const std::string& name);
|
||||
|
|
@ -246,6 +279,13 @@ public:
|
|||
bool is_item_disabled(const std::string& name) const;
|
||||
bool is_item_visible(const std::string& name) const;
|
||||
|
||||
bool is_any_item_pressed() const;
|
||||
|
||||
unsigned int get_item_id(const std::string& name) const;
|
||||
|
||||
void force_left_action(unsigned int item_id, GLCanvas3D& parent);
|
||||
void force_right_action(unsigned int item_id, GLCanvas3D& parent);
|
||||
|
||||
const std::string& get_tooltip() const { return m_tooltip; }
|
||||
|
||||
// returns true if any item changed its state
|
||||
|
|
@ -262,7 +302,7 @@ private:
|
|||
float get_height_horizontal() const;
|
||||
float get_height_vertical() const;
|
||||
float get_main_size() const;
|
||||
void do_action(unsigned int item_id, GLCanvas3D& parent);
|
||||
void do_action(GLToolbarItem::EActionType type, unsigned int item_id, GLCanvas3D& parent, bool check_hover);
|
||||
std::string update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent);
|
||||
std::string update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent);
|
||||
std::string update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent);
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ namespace GUI
|
|||
wxDEFINE_EVENT(EVT_OBJ_LIST_OBJECT_SELECT, SimpleEvent);
|
||||
|
||||
// pt_FFF
|
||||
FreqSettingsBundle FREQ_SETTINGS_BUNDLE_FFF =
|
||||
SettingsBundle FREQ_SETTINGS_BUNDLE_FFF =
|
||||
{
|
||||
{ L("Layers and Perimeters"), { "layer_height" , "perimeters", "top_solid_layers", "bottom_solid_layers" } },
|
||||
{ L("Infill") , { "fill_density", "fill_pattern" } },
|
||||
|
|
@ -36,7 +36,7 @@ FreqSettingsBundle FREQ_SETTINGS_BUNDLE_FFF =
|
|||
};
|
||||
|
||||
// pt_SLA
|
||||
FreqSettingsBundle FREQ_SETTINGS_BUNDLE_SLA =
|
||||
SettingsBundle FREQ_SETTINGS_BUNDLE_SLA =
|
||||
{
|
||||
{ L("Pad and Support") , { "supports_enable", "pad_enable" } }
|
||||
};
|
||||
|
|
@ -66,6 +66,14 @@ static int extruders_count()
|
|||
return wxGetApp().extruders_cnt();
|
||||
}
|
||||
|
||||
static void take_snapshot(const wxString& snapshot_name)
|
||||
{
|
||||
wxGetApp().plater()->take_snapshot(snapshot_name);
|
||||
}
|
||||
|
||||
static void suppress_snapshots(){ wxGetApp().plater()->suppress_snapshots(); }
|
||||
static void allow_snapshots() { wxGetApp().plater()->allow_snapshots(); }
|
||||
|
||||
ObjectList::ObjectList(wxWindow* parent) :
|
||||
wxDataViewCtrl(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxDV_MULTIPLE),
|
||||
m_parent(parent)
|
||||
|
|
@ -138,20 +146,24 @@ ObjectList::ObjectList(wxWindow* parent) :
|
|||
// Bind(wxEVT_KEY_DOWN, &ObjectList::OnChar, this);
|
||||
{
|
||||
// Accelerators
|
||||
wxAcceleratorEntry entries[6];
|
||||
wxAcceleratorEntry entries[8];
|
||||
entries[0].Set(wxACCEL_CTRL, (int) 'C', wxID_COPY);
|
||||
entries[1].Set(wxACCEL_CTRL, (int) 'X', wxID_CUT);
|
||||
entries[2].Set(wxACCEL_CTRL, (int) 'V', wxID_PASTE);
|
||||
entries[3].Set(wxACCEL_CTRL, (int) 'A', wxID_SELECTALL);
|
||||
entries[4].Set(wxACCEL_NORMAL, WXK_DELETE, wxID_DELETE);
|
||||
entries[5].Set(wxACCEL_NORMAL, WXK_BACK, wxID_DELETE);
|
||||
wxAcceleratorTable accel(6, entries);
|
||||
entries[4].Set(wxACCEL_CTRL, (int) 'Z', wxID_UNDO);
|
||||
entries[5].Set(wxACCEL_CTRL, (int) 'Y', wxID_REDO);
|
||||
entries[6].Set(wxACCEL_NORMAL, WXK_DELETE, wxID_DELETE);
|
||||
entries[7].Set(wxACCEL_NORMAL, WXK_BACK, wxID_DELETE);
|
||||
wxAcceleratorTable accel(8, entries);
|
||||
SetAcceleratorTable(accel);
|
||||
|
||||
this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->copy(); }, wxID_COPY);
|
||||
this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->paste(); }, wxID_PASTE);
|
||||
this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->select_item_all_children(); }, wxID_SELECTALL);
|
||||
this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->remove(); }, wxID_DELETE);
|
||||
this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->undo(); }, wxID_UNDO);
|
||||
this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->redo(); }, wxID_REDO);
|
||||
}
|
||||
#else __WXOSX__
|
||||
Bind(wxEVT_CHAR, [this](wxKeyEvent& event) { key_event(event); }); // doesn't work on OSX
|
||||
|
|
@ -655,7 +667,6 @@ void ObjectList::paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& vol
|
|||
if (volumes.empty())
|
||||
return;
|
||||
|
||||
ModelObject& model_object = *(*m_objects)[obj_idx];
|
||||
const auto object_item = m_objects_model->GetItemById(obj_idx);
|
||||
|
||||
wxDataViewItemArray items;
|
||||
|
|
@ -665,10 +676,7 @@ void ObjectList::paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& vol
|
|||
const wxDataViewItem& vol_item = m_objects_model->AddVolumeChild(object_item, wxString::FromUTF8(volume->name.c_str()), volume->type(),
|
||||
volume->get_mesh_errors_count()>0 ,
|
||||
volume->config.has("extruder") ? volume->config.option<ConfigOptionInt>("extruder")->value : 0);
|
||||
auto opt_keys = volume->config.keys();
|
||||
if (!opt_keys.empty() && !((opt_keys.size() == 1) && (opt_keys[0] == "extruder")))
|
||||
select_item(m_objects_model->AddSettingsChild(vol_item));
|
||||
|
||||
add_settings_item(vol_item, &volume->config);
|
||||
items.Add(vol_item);
|
||||
}
|
||||
|
||||
|
|
@ -805,6 +813,16 @@ void ObjectList::paste()
|
|||
wxPostEvent((wxEvtHandler*)wxGetApp().plater()->canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_PASTE));
|
||||
}
|
||||
|
||||
void ObjectList::undo()
|
||||
{
|
||||
wxGetApp().plater()->undo();
|
||||
}
|
||||
|
||||
void ObjectList::redo()
|
||||
{
|
||||
wxGetApp().plater()->redo();
|
||||
}
|
||||
|
||||
#ifndef __WXOSX__
|
||||
void ObjectList::key_event(wxKeyEvent& event)
|
||||
{
|
||||
|
|
@ -823,6 +841,10 @@ void ObjectList::key_event(wxKeyEvent& event)
|
|||
copy();
|
||||
else if (wxGetKeyState(wxKeyCode('V')) && wxGetKeyState(WXK_CONTROL))
|
||||
paste();
|
||||
else if (wxGetKeyState(wxKeyCode('Y')) && wxGetKeyState(WXK_CONTROL))
|
||||
redo();
|
||||
else if (wxGetKeyState(wxKeyCode('Z')) && wxGetKeyState(WXK_CONTROL))
|
||||
undo();
|
||||
else
|
||||
event.Skip();
|
||||
}
|
||||
|
|
@ -908,6 +930,7 @@ void ObjectList::OnDrop(wxDataViewEvent &event)
|
|||
|
||||
if (m_dragged_data.type() == itInstance)
|
||||
{
|
||||
take_snapshot(_(L("Instances to Separated Objects")));
|
||||
instances_to_separated_object(m_dragged_data.obj_idx(), m_dragged_data.inst_idxs());
|
||||
m_dragged_data.clear();
|
||||
return;
|
||||
|
|
@ -925,6 +948,8 @@ void ObjectList::OnDrop(wxDataViewEvent &event)
|
|||
// if (to_volume_id > from_volume_id) to_volume_id--;
|
||||
// #endif // __WXGTK__
|
||||
|
||||
take_snapshot(_(L("Remov Volume(s)")));
|
||||
|
||||
auto& volumes = (*m_objects)[m_dragged_data.obj_idx()]->volumes;
|
||||
auto delta = to_volume_id < from_volume_id ? -1 : 1;
|
||||
int cnt = 0;
|
||||
|
|
@ -963,7 +988,7 @@ std::vector<std::string> ObjectList::get_options(const bool is_part)
|
|||
|
||||
const std::vector<std::string>& ObjectList::get_options_for_bundle(const wxString& bundle_name)
|
||||
{
|
||||
const FreqSettingsBundle& bundle = printer_technology() == ptSLA ?
|
||||
const SettingsBundle& bundle = printer_technology() == ptSLA ?
|
||||
FREQ_SETTINGS_BUNDLE_SLA : FREQ_SETTINGS_BUNDLE_FFF;
|
||||
|
||||
for (auto& it : bundle)
|
||||
|
|
@ -973,7 +998,7 @@ const std::vector<std::string>& ObjectList::get_options_for_bundle(const wxStrin
|
|||
}
|
||||
#if 0
|
||||
// if "Quick menu" is selected
|
||||
FreqSettingsBundle& bundle_quick = printer_technology() == ptSLA ?
|
||||
SettingsBundle& bundle_quick = printer_technology() == ptSLA ?
|
||||
m_freq_settings_sla: m_freq_settings_fff;
|
||||
|
||||
for (auto& it : bundle_quick)
|
||||
|
|
@ -1049,7 +1074,7 @@ void ObjectList::get_settings_choice(const wxString& category_name)
|
|||
if (selection_cnt > 0)
|
||||
{
|
||||
// Add selected items to the "Quick menu"
|
||||
FreqSettingsBundle& freq_settings = printer_technology() == ptSLA ?
|
||||
SettingsBundle& freq_settings = printer_technology() == ptSLA ?
|
||||
m_freq_settings_sla : m_freq_settings_fff;
|
||||
bool changed_existing = false;
|
||||
|
||||
|
|
@ -1090,6 +1115,8 @@ void ObjectList::get_settings_choice(const wxString& category_name)
|
|||
}
|
||||
#endif
|
||||
|
||||
take_snapshot(wxString::Format(_(L("Add Settings for %s")), is_part ? _(L("Sub-object")) : _(L("Object"))));
|
||||
|
||||
std::vector <std::string> selected_options;
|
||||
selected_options.reserve(selection_cnt);
|
||||
for (auto sel : selections)
|
||||
|
|
@ -1119,8 +1146,8 @@ void ObjectList::get_settings_choice(const wxString& category_name)
|
|||
}
|
||||
|
||||
|
||||
// Add settings item for object
|
||||
update_settings_item();
|
||||
// Add settings item for object/sub-object and show them
|
||||
show_settings(add_settings_item(GetSelection(), m_config));
|
||||
}
|
||||
|
||||
void ObjectList::get_freq_settings_choice(const wxString& bundle_name)
|
||||
|
|
@ -1140,6 +1167,8 @@ void ObjectList::get_freq_settings_choice(const wxString& bundle_name)
|
|||
assert(m_config);
|
||||
auto opt_keys = m_config->keys();
|
||||
|
||||
take_snapshot(wxString::Format(_(L("Add Settings Bundle for %s")), m_objects_model->GetItemType(GetSelection()) & itObject ? _(L("Object")) : _(L("Sub-object"))));
|
||||
|
||||
const DynamicPrintConfig& from_config = wxGetApp().preset_bundle->prints.get_edited_preset().config;
|
||||
for (auto& opt_key : options)
|
||||
{
|
||||
|
|
@ -1154,13 +1183,21 @@ void ObjectList::get_freq_settings_choice(const wxString& bundle_name)
|
|||
}
|
||||
}
|
||||
|
||||
// Add settings item for object
|
||||
update_settings_item();
|
||||
// Add settings item for object/sub-object and show them
|
||||
show_settings(add_settings_item(GetSelection(), m_config));
|
||||
}
|
||||
|
||||
void ObjectList::update_settings_item()
|
||||
void ObjectList::show_settings(const wxDataViewItem settings_item)
|
||||
{
|
||||
auto item = GetSelection();
|
||||
if (!settings_item)
|
||||
return;
|
||||
|
||||
select_item(settings_item);
|
||||
|
||||
// update object selection on Plater
|
||||
if (!m_prevent_canvas_selection_update)
|
||||
update_selections_on_canvas();
|
||||
/* auto item = GetSelection();
|
||||
if (item) {
|
||||
if (m_objects_model->GetItemType(item) == itInstance)
|
||||
item = m_objects_model->GetTopParent(item);
|
||||
|
|
@ -1172,12 +1209,14 @@ void ObjectList::update_settings_item()
|
|||
if (!m_prevent_canvas_selection_update)
|
||||
update_selections_on_canvas();
|
||||
}
|
||||
else {
|
||||
else {
|
||||
//# ys_FIXME ??? use case ???
|
||||
auto panel = wxGetApp().sidebar().scrolled_panel();
|
||||
panel->Freeze();
|
||||
wxGetApp().obj_settings()->UpdateAndShow(true);
|
||||
panel->Thaw();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
wxMenu* ObjectList::append_submenu_add_generic(wxMenu* menu, const ModelVolumeType type) {
|
||||
|
|
@ -1488,7 +1527,7 @@ wxMenu* ObjectList::create_settings_popupmenu(wxMenu *parent_menu)
|
|||
void ObjectList::create_freq_settings_popupmenu(wxMenu *menu)
|
||||
{
|
||||
// Add default settings bundles
|
||||
const FreqSettingsBundle& bundle = printer_technology() == ptFFF ?
|
||||
const SettingsBundle& bundle = printer_technology() == ptFFF ?
|
||||
FREQ_SETTINGS_BUNDLE_FFF : FREQ_SETTINGS_BUNDLE_SLA;
|
||||
|
||||
const int extruders_cnt = extruders_count();
|
||||
|
|
@ -1503,7 +1542,7 @@ void ObjectList::create_freq_settings_popupmenu(wxMenu *menu)
|
|||
}
|
||||
#if 0
|
||||
// Add "Quick" settings bundles
|
||||
const FreqSettingsBundle& bundle_quick = printer_technology() == ptFFF ?
|
||||
const SettingsBundle& bundle_quick = printer_technology() == ptFFF ?
|
||||
m_freq_settings_fff : m_freq_settings_sla;
|
||||
|
||||
for (auto& it : bundle_quick) {
|
||||
|
|
@ -1539,6 +1578,8 @@ void ObjectList::load_subobject(ModelVolumeType type)
|
|||
if (m_objects_model->GetItemType(item)&itInstance)
|
||||
item = m_objects_model->GetItemById(obj_idx);
|
||||
|
||||
take_snapshot(_(L("Load Part")));
|
||||
|
||||
std::vector<std::pair<wxString, bool>> volumes_info;
|
||||
load_part((*m_objects)[obj_idx], volumes_info, type);
|
||||
|
||||
|
|
@ -1614,6 +1655,8 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode
|
|||
if (instance_idx == -1)
|
||||
return;
|
||||
|
||||
take_snapshot(_(L("Add Generic Subobject")));
|
||||
|
||||
// Selected object
|
||||
ModelObject &model_object = *(*m_objects)[obj_idx];
|
||||
// Bounding box of the selected instance in world coordinate system including the translation, without modifiers.
|
||||
|
|
@ -1720,6 +1763,8 @@ void ObjectList::del_settings_from_config(const wxDataViewItem& parent_item)
|
|||
is_layer_settings && opt_cnt == 2 && m_config->has("extruder") && m_config->has("layer_height"))
|
||||
return;
|
||||
|
||||
take_snapshot(_(L("Delete Settings")));
|
||||
|
||||
int extruder = -1;
|
||||
if (m_config->has("extruder"))
|
||||
extruder = m_config->option<ConfigOptionInt>("extruder")->value;
|
||||
|
|
@ -1742,6 +1787,8 @@ void ObjectList::del_instances_from_object(const int obj_idx)
|
|||
if (instances.size() <= 1)
|
||||
return;
|
||||
|
||||
take_snapshot(_(L("Delete All Instances from Object")));
|
||||
|
||||
while ( instances.size()> 1)
|
||||
instances.pop_back();
|
||||
|
||||
|
|
@ -1755,6 +1802,8 @@ void ObjectList::del_layer_from_object(const int obj_idx, const t_layer_height_r
|
|||
const auto del_range = object(obj_idx)->layer_config_ranges.find(layer_range);
|
||||
if (del_range == object(obj_idx)->layer_config_ranges.end())
|
||||
return;
|
||||
|
||||
take_snapshot(_(L("Delete Layers Range")));
|
||||
|
||||
object(obj_idx)->layer_config_ranges.erase(del_range);
|
||||
|
||||
|
|
@ -1789,6 +1838,8 @@ bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, con
|
|||
return false;
|
||||
}
|
||||
|
||||
take_snapshot(_(L("Delete Subobject")));
|
||||
|
||||
object->delete_volume(idx);
|
||||
|
||||
if (object->volumes.size() == 1)
|
||||
|
|
@ -1805,6 +1856,8 @@ bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, con
|
|||
Slic3r::GUI::show_error(nullptr, _(L("You can't delete the last intance from object.")));
|
||||
return false;
|
||||
}
|
||||
|
||||
take_snapshot(_(L("Delete Instance")));
|
||||
object->delete_instance(idx);
|
||||
}
|
||||
else
|
||||
|
|
@ -1832,6 +1885,8 @@ void ObjectList::split()
|
|||
return;
|
||||
}
|
||||
|
||||
take_snapshot(_(L("Split to Parts")));
|
||||
|
||||
wxBusyCursor wait;
|
||||
|
||||
auto model_object = (*m_objects)[obj_idx];
|
||||
|
|
@ -1850,11 +1905,7 @@ void ObjectList::split()
|
|||
volume->config.option<ConfigOptionInt>("extruder")->value : 0,
|
||||
false);
|
||||
// add settings to the part, if it has those
|
||||
auto opt_keys = volume->config.keys();
|
||||
if ( !(opt_keys.size() == 1 && opt_keys[0] == "extruder") ) {
|
||||
select_item(m_objects_model->AddSettingsChild(vol_item));
|
||||
Expand(vol_item);
|
||||
}
|
||||
add_settings_item(vol_item, &volume->config);
|
||||
}
|
||||
|
||||
if (parent == item)
|
||||
|
|
@ -1879,8 +1930,10 @@ void ObjectList::layers_editing()
|
|||
t_layer_config_ranges& ranges = object(obj_idx)->layer_config_ranges;
|
||||
|
||||
// set some default value
|
||||
if (ranges.empty())
|
||||
if (ranges.empty()) {
|
||||
take_snapshot(_(L("Add Layers")));
|
||||
ranges[{ 0.0f, 2.0f }] = get_default_layer_config(obj_idx);
|
||||
}
|
||||
|
||||
// create layer root item
|
||||
layers_item = add_layer_root_item(obj_item);
|
||||
|
|
@ -1912,6 +1965,7 @@ wxDataViewItem ObjectList::add_layer_root_item(const wxDataViewItem obj_item)
|
|||
for (const auto range : object(obj_idx)->layer_config_ranges)
|
||||
add_layer_item(range.first, layers_item);
|
||||
|
||||
Expand(layers_item);
|
||||
return layers_item;
|
||||
}
|
||||
|
||||
|
|
@ -2097,7 +2151,79 @@ void ObjectList::part_selection_changed()
|
|||
panel.Thaw();
|
||||
}
|
||||
|
||||
void ObjectList::add_object_to_list(size_t obj_idx)
|
||||
SettingsBundle ObjectList::get_item_settings_bundle(const DynamicPrintConfig* config, const bool is_layers_range_settings)
|
||||
{
|
||||
auto opt_keys = config->keys();
|
||||
if (opt_keys.empty())
|
||||
return SettingsBundle();
|
||||
|
||||
update_opt_keys(opt_keys); // update options list according to print technology
|
||||
|
||||
if (opt_keys.size() == 1 && opt_keys[0] == "extruder" ||
|
||||
is_layers_range_settings && opt_keys.size() == 2)
|
||||
return SettingsBundle();
|
||||
|
||||
const int extruders_cnt = wxGetApp().extruders_edited_cnt();
|
||||
|
||||
SettingsBundle bundle;
|
||||
for (auto& opt_key : opt_keys)
|
||||
{
|
||||
auto category = config->def()->get(opt_key)->category;
|
||||
if (category.empty() || (category == "Extruders" && extruders_cnt == 1))
|
||||
continue;
|
||||
|
||||
std::vector< std::string > new_category;
|
||||
|
||||
auto& cat_opt = bundle.find(category) == bundle.end() ? new_category : bundle.at(category);
|
||||
cat_opt.push_back(opt_key);
|
||||
if (cat_opt.size() == 1)
|
||||
bundle[category] = cat_opt;
|
||||
}
|
||||
|
||||
return bundle;
|
||||
}
|
||||
|
||||
// Add new SettingsItem for parent_item if it doesn't exist, or just update a digest according to new config
|
||||
wxDataViewItem ObjectList::add_settings_item(wxDataViewItem parent_item, const DynamicPrintConfig* config)
|
||||
{
|
||||
wxDataViewItem ret = wxDataViewItem(0);
|
||||
|
||||
if (!parent_item)
|
||||
return ret;
|
||||
|
||||
const bool is_layers_range_settings = m_objects_model->GetItemType(parent_item) == itLayer;
|
||||
SettingsBundle cat_options = get_item_settings_bundle(config, is_layers_range_settings);
|
||||
if (cat_options.empty())
|
||||
return ret;
|
||||
|
||||
std::vector<std::string> categories;
|
||||
categories.reserve(cat_options.size());
|
||||
for (auto& cat : cat_options)
|
||||
{
|
||||
if (cat.second.size() == 1 &&
|
||||
(cat.second[0] == "extruder" || is_layers_range_settings && cat.second[0] == "layer_height"))
|
||||
continue;
|
||||
|
||||
categories.push_back(cat.first);
|
||||
}
|
||||
|
||||
if (categories.empty())
|
||||
return ret;
|
||||
|
||||
if (m_objects_model->GetItemType(parent_item) & itInstance)
|
||||
parent_item = m_objects_model->GetTopParent(parent_item);
|
||||
|
||||
ret = m_objects_model->IsSettingsItem(parent_item) ? parent_item : m_objects_model->GetSettingsItem(parent_item);
|
||||
|
||||
if (!ret) ret = m_objects_model->AddSettingsChild(parent_item);
|
||||
|
||||
m_objects_model->UpdateSettingsDigest(ret, categories);
|
||||
Expand(parent_item);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed)
|
||||
{
|
||||
auto model_object = (*m_objects)[obj_idx];
|
||||
const wxString& item_name = from_u8(model_object->name);
|
||||
|
|
@ -2116,11 +2242,7 @@ void ObjectList::add_object_to_list(size_t obj_idx)
|
|||
!volume->config.has("extruder") ? 0 :
|
||||
volume->config.option<ConfigOptionInt>("extruder")->value,
|
||||
false);
|
||||
auto opt_keys = volume->config.keys();
|
||||
if (!opt_keys.empty() && !(opt_keys.size() == 1 && opt_keys[0] == "extruder")) {
|
||||
select_item(m_objects_model->AddSettingsChild(vol_item));
|
||||
Expand(vol_item);
|
||||
}
|
||||
add_settings_item(vol_item, &volume->config);
|
||||
}
|
||||
Expand(item);
|
||||
}
|
||||
|
|
@ -2130,17 +2252,14 @@ void ObjectList::add_object_to_list(size_t obj_idx)
|
|||
increase_object_instances(obj_idx, model_object->instances.size());
|
||||
|
||||
// add settings to the object, if it has those
|
||||
auto opt_keys = model_object->config.keys();
|
||||
if (!opt_keys.empty() && !(opt_keys.size() == 1 && opt_keys[0] == "extruder")) {
|
||||
select_item(m_objects_model->AddSettingsChild(item));
|
||||
Expand(item);
|
||||
}
|
||||
add_settings_item(item, &model_object->config);
|
||||
|
||||
// Add layers if it has
|
||||
add_layer_root_item(item);
|
||||
|
||||
#ifndef __WXOSX__
|
||||
selection_changed();
|
||||
if (call_selection_changed)
|
||||
selection_changed();
|
||||
#endif //__WXMSW__
|
||||
}
|
||||
|
||||
|
|
@ -2175,6 +2294,8 @@ void ObjectList::delete_from_model_and_list(const ItemType type, const int obj_i
|
|||
if ( !(type&(itObject|itVolume|itInstance)) )
|
||||
return;
|
||||
|
||||
take_snapshot(_(L("Delete Selected Item")));
|
||||
|
||||
if (type&itObject) {
|
||||
del_object(obj_idx);
|
||||
delete_object_from_list(obj_idx);
|
||||
|
|
@ -2285,6 +2406,9 @@ void ObjectList::remove()
|
|||
|
||||
wxDataViewItem parent = wxDataViewItem(0);
|
||||
|
||||
take_snapshot(_(L("Delete Selected")));
|
||||
suppress_snapshots();
|
||||
|
||||
for (auto& item : sels)
|
||||
{
|
||||
if (m_objects_model->GetParent(item) == wxDataViewItem(0))
|
||||
|
|
@ -2305,6 +2429,8 @@ void ObjectList::remove()
|
|||
|
||||
if (parent)
|
||||
select_item(parent);
|
||||
|
||||
allow_snapshots();
|
||||
}
|
||||
|
||||
void ObjectList::del_layer_range(const t_layer_height_range& range)
|
||||
|
|
@ -2350,6 +2476,8 @@ void ObjectList::add_layer_range_after_current(const t_layer_height_range& curre
|
|||
|
||||
if (current_range == last_range)
|
||||
{
|
||||
take_snapshot(_(L("Add New Layers Range")));
|
||||
|
||||
const t_layer_height_range& new_range = { last_range.second, last_range.second + 2.0f };
|
||||
ranges[new_range] = get_default_layer_config(obj_idx);
|
||||
add_layer_item(new_range, layers_item);
|
||||
|
|
@ -2377,22 +2505,28 @@ void ObjectList::add_layer_range_after_current(const t_layer_height_range& curre
|
|||
|
||||
t_layer_height_range new_range = { midl_layer, next_range.second };
|
||||
|
||||
take_snapshot(_(L("Add New Layers Range")));
|
||||
suppress_snapshots();
|
||||
|
||||
// create new 2 layers instead of deleted one
|
||||
|
||||
// delete old layer
|
||||
|
||||
wxDataViewItem layer_item = m_objects_model->GetItemByLayerRange(obj_idx, next_range);
|
||||
del_subobject_item(layer_item);
|
||||
|
||||
// create new 2 layers instead of deleted one
|
||||
|
||||
ranges[new_range] = old_config;
|
||||
add_layer_item(new_range, layers_item, layer_idx);
|
||||
|
||||
new_range = { current_range.second, midl_layer };
|
||||
ranges[new_range] = get_default_layer_config(obj_idx);
|
||||
add_layer_item(new_range, layers_item, layer_idx);
|
||||
allow_snapshots();
|
||||
}
|
||||
else
|
||||
{
|
||||
take_snapshot(_(L("Add New Layers Range")));
|
||||
|
||||
const t_layer_height_range new_range = { current_range.second, next_range.first };
|
||||
ranges[new_range] = get_default_layer_config(obj_idx);
|
||||
add_layer_item(new_range, layers_item, layer_idx);
|
||||
|
|
@ -2420,9 +2554,7 @@ void ObjectList::add_layer_item(const t_layer_height_range& range,
|
|||
range,
|
||||
config.opt_int("extruder"),
|
||||
layer_idx);
|
||||
|
||||
if (config.keys().size() > 2)
|
||||
select_item(m_objects_model->AddSettingsChild(layer_item));
|
||||
add_settings_item(layer_item, &config);
|
||||
}
|
||||
|
||||
bool ObjectList::edit_layer_range(const t_layer_height_range& range, coordf_t layer_height)
|
||||
|
|
@ -2452,6 +2584,8 @@ bool ObjectList::edit_layer_range(const t_layer_height_range& range, const t_lay
|
|||
const int obj_idx = get_selected_obj_idx();
|
||||
if (obj_idx < 0) return false;
|
||||
|
||||
take_snapshot(_(L("Edit Layers Range")));
|
||||
|
||||
const ItemType sel_type = m_objects_model->GetItemType(GetSelection());
|
||||
|
||||
t_layer_config_ranges& ranges = object(obj_idx)->layer_config_ranges;
|
||||
|
|
@ -2918,6 +3052,8 @@ void ObjectList::change_part_type()
|
|||
if (new_type == type || new_type == ModelVolumeType::INVALID)
|
||||
return;
|
||||
|
||||
take_snapshot(_(L("Paste from Clipboard")));
|
||||
|
||||
const auto item = GetSelection();
|
||||
volume->set_type(new_type);
|
||||
m_objects_model->SetVolumeType(item, new_type);
|
||||
|
|
@ -2933,18 +3069,17 @@ void ObjectList::change_part_type()
|
|||
}
|
||||
else if (!settings_item &&
|
||||
(new_type == ModelVolumeType::MODEL_PART || new_type == ModelVolumeType::PARAMETER_MODIFIER)) {
|
||||
select_item(m_objects_model->AddSettingsChild(item));
|
||||
add_settings_item(item, &volume->config);
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectList::last_volume_is_deleted(const int obj_idx)
|
||||
{
|
||||
|
||||
if (obj_idx < 0 || m_objects->empty() ||
|
||||
obj_idx <= m_objects->size() ||
|
||||
(*m_objects)[obj_idx]->volumes.empty())
|
||||
if (obj_idx < 0 || obj_idx >= m_objects->size() || (*m_objects)[obj_idx]->volumes.empty())
|
||||
return;
|
||||
auto volume = (*m_objects)[obj_idx]->volumes[0];
|
||||
|
||||
auto volume = (*m_objects)[obj_idx]->volumes.front();
|
||||
|
||||
// clear volume's config values
|
||||
volume->config.clear();
|
||||
|
|
@ -2966,6 +3101,7 @@ bool ObjectList::has_multi_part_objects()
|
|||
return false;
|
||||
}
|
||||
|
||||
/* #lm_FIXME_delete_after_testing
|
||||
void ObjectList::update_settings_items()
|
||||
{
|
||||
m_prevent_canvas_selection_update = true;
|
||||
|
|
@ -2991,22 +3127,52 @@ void ObjectList::update_settings_items()
|
|||
SetSelections(sel);
|
||||
m_prevent_canvas_selection_update = false;
|
||||
}
|
||||
*/
|
||||
void ObjectList::update_and_show_object_settings_item()
|
||||
{
|
||||
const wxDataViewItem item = GetSelection();
|
||||
if (!item) return;
|
||||
|
||||
const wxDataViewItem& obj_item = m_objects_model->IsSettingsItem(item) ? m_objects_model->GetParent(item) : item;
|
||||
select_item(add_settings_item(obj_item, &get_item_config(obj_item)));
|
||||
}
|
||||
|
||||
// Update settings item for item had it
|
||||
void ObjectList::update_settings_item_and_selection(wxDataViewItem item, wxDataViewItemArray& selections)
|
||||
{
|
||||
const wxDataViewItem& settings_item = m_objects_model->GetSettingsItem(item);
|
||||
select_item(settings_item ? settings_item : m_objects_model->AddSettingsChild(item));
|
||||
const wxDataViewItem old_settings_item = m_objects_model->GetSettingsItem(item);
|
||||
const wxDataViewItem new_settings_item = add_settings_item(item, &get_item_config(item));
|
||||
|
||||
// If settings item was deleted from the list,
|
||||
// it's need to be deleted from selection array, if it was there
|
||||
if (settings_item != m_objects_model->GetSettingsItem(item) &&
|
||||
selections.Index(settings_item) != wxNOT_FOUND) {
|
||||
selections.Remove(settings_item);
|
||||
if (!new_settings_item && old_settings_item)
|
||||
m_objects_model->Delete(old_settings_item);
|
||||
|
||||
// Select item, if settings_item doesn't exist for item anymore, but was selected
|
||||
if (selections.Index(item) == wxNOT_FOUND)
|
||||
selections.Add(item);
|
||||
// if ols settings item was is selected area
|
||||
if (selections.Index(old_settings_item) != wxNOT_FOUND)
|
||||
{
|
||||
// If settings item was just updated
|
||||
if (old_settings_item == new_settings_item)
|
||||
{
|
||||
Sidebar& panel = wxGetApp().sidebar();
|
||||
panel.Freeze();
|
||||
|
||||
// update settings list
|
||||
wxGetApp().obj_settings()->UpdateAndShow(true);
|
||||
|
||||
panel.Layout();
|
||||
panel.Thaw();
|
||||
}
|
||||
else
|
||||
// If settings item was deleted from the list,
|
||||
// it's need to be deleted from selection array, if it was there
|
||||
{
|
||||
selections.Remove(old_settings_item);
|
||||
|
||||
// Select item, if settings_item doesn't exist for item anymore, but was selected
|
||||
if (selections.Index(item) == wxNOT_FOUND) {
|
||||
selections.Add(item);
|
||||
select_item(item); // to correct update of the SettingsList and ManipulationPanel sizers
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3360,6 +3526,35 @@ void ObjectList::set_extruder_for_selected_items(const int extruder) const
|
|||
wxGetApp().plater()->update();
|
||||
}
|
||||
|
||||
void ObjectList::update_after_undo_redo()
|
||||
{
|
||||
m_prevent_list_events = true;
|
||||
m_prevent_canvas_selection_update = true;
|
||||
|
||||
suppress_snapshots();
|
||||
|
||||
// Unselect all objects before deleting them, so that no change of selection is emitted during deletion.
|
||||
this->UnselectAll();
|
||||
m_objects_model->DeleteAll();
|
||||
|
||||
size_t obj_idx = 0;
|
||||
while (obj_idx < m_objects->size()) {
|
||||
add_object_to_list(obj_idx, false);
|
||||
++obj_idx;
|
||||
}
|
||||
|
||||
allow_snapshots();
|
||||
|
||||
#ifndef __WXOSX__
|
||||
selection_changed();
|
||||
#endif /* __WXOSX__ */
|
||||
|
||||
update_selections();
|
||||
|
||||
m_prevent_canvas_selection_update = false;
|
||||
m_prevent_list_events = false;
|
||||
}
|
||||
|
||||
ModelObject* ObjectList::object(const int obj_idx) const
|
||||
{
|
||||
if (obj_idx < 0)
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ enum class ModelVolumeType : int;
|
|||
// FIXME: broken build on mac os because of this is missing:
|
||||
typedef std::vector<std::string> t_config_option_keys;
|
||||
|
||||
typedef std::map<std::string, std::vector<std::string>> FreqSettingsBundle;
|
||||
typedef std::map<std::string, std::vector<std::string>> SettingsBundle;
|
||||
|
||||
// category -> vector ( option ; label )
|
||||
typedef std::map< std::string, std::vector< std::pair<std::string, std::string> > > settings_menu_hierarchy;
|
||||
|
|
@ -152,8 +152,8 @@ class ObjectList : public wxDataViewCtrl
|
|||
wxDataViewItem m_last_selected_item {nullptr};
|
||||
|
||||
#if 0
|
||||
FreqSettingsBundle m_freq_settings_fff;
|
||||
FreqSettingsBundle m_freq_settings_sla;
|
||||
SettingsBundle m_freq_settings_fff;
|
||||
SettingsBundle m_freq_settings_sla;
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
|
@ -204,10 +204,12 @@ public:
|
|||
|
||||
void copy();
|
||||
void paste();
|
||||
void undo();
|
||||
void redo();
|
||||
|
||||
void get_settings_choice(const wxString& category_name);
|
||||
void get_freq_settings_choice(const wxString& bundle_name);
|
||||
void update_settings_item();
|
||||
void show_settings(const wxDataViewItem settings_item);
|
||||
|
||||
wxMenu* append_submenu_add_generic(wxMenu* menu, const ModelVolumeType type);
|
||||
void append_menu_items_add_volume(wxMenu* menu);
|
||||
|
|
@ -245,6 +247,7 @@ public:
|
|||
void layers_editing();
|
||||
|
||||
wxDataViewItem add_layer_root_item(const wxDataViewItem obj_item);
|
||||
wxDataViewItem add_settings_item(wxDataViewItem parent_item, const DynamicPrintConfig* config);
|
||||
|
||||
DynamicPrintConfig get_default_layer_config(const int obj_idx);
|
||||
bool get_volume_by_item(const wxDataViewItem& item, ModelVolume*& volume);
|
||||
|
|
@ -256,12 +259,13 @@ public:
|
|||
wxBoxSizer* get_sizer() {return m_sizer;}
|
||||
int get_selected_obj_idx() const;
|
||||
DynamicPrintConfig& get_item_config(const wxDataViewItem& item) const;
|
||||
SettingsBundle get_item_settings_bundle(const DynamicPrintConfig* config, const bool is_layers_range_settings);
|
||||
|
||||
void changed_object(const int obj_idx = -1) const;
|
||||
void part_selection_changed();
|
||||
|
||||
// Add object to the list
|
||||
void add_object_to_list(size_t obj_idx);
|
||||
void add_object_to_list(size_t obj_idx, bool call_selection_changed = true);
|
||||
// Delete object from the list
|
||||
void delete_object_from_list();
|
||||
void delete_object_from_list(const size_t obj_idx);
|
||||
|
|
@ -315,6 +319,7 @@ public:
|
|||
void last_volume_is_deleted(const int obj_idx);
|
||||
bool has_multi_part_objects();
|
||||
void update_settings_items();
|
||||
void update_and_show_object_settings_item();
|
||||
void update_settings_item_and_selection(wxDataViewItem item, wxDataViewItemArray& selections);
|
||||
void update_object_list_by_printer_technology();
|
||||
void update_object_menu();
|
||||
|
|
@ -333,6 +338,8 @@ public:
|
|||
|
||||
void msw_rescale();
|
||||
|
||||
void update_after_undo_redo();
|
||||
|
||||
private:
|
||||
#ifdef __WXOSX__
|
||||
// void OnChar(wxKeyEvent& event);
|
||||
|
|
|
|||
|
|
@ -242,8 +242,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
|||
selection.synchronize_unselected_instances(Selection::SYNC_ROTATION_GENERAL);
|
||||
selection.synchronize_unselected_volumes();
|
||||
// Copy mirroring values from GLVolumes into Model (ModelInstance / ModelVolume), trigger background processing.
|
||||
canvas->do_mirror();
|
||||
canvas->set_as_dirty();
|
||||
canvas->do_mirror("Set Mirror");
|
||||
UpdateAndShow(true);
|
||||
});
|
||||
return sizer;
|
||||
|
|
@ -324,7 +323,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
|||
selection.synchronize_unselected_instances(Selection::SYNC_ROTATION_GENERAL);
|
||||
selection.synchronize_unselected_volumes();
|
||||
// Copy rotation values from GLVolumes into Model (ModelInstance / ModelVolume), trigger background processing.
|
||||
canvas->do_rotate();
|
||||
canvas->do_rotate("Set Rotation");
|
||||
|
||||
UpdateAndShow(true);
|
||||
});
|
||||
|
|
@ -710,7 +709,7 @@ void ObjectManipulation::change_position_value(int axis, double value)
|
|||
Selection& selection = canvas->get_selection();
|
||||
selection.start_dragging();
|
||||
selection.translate(position - m_cache.position, selection.requires_local_axes());
|
||||
canvas->do_move();
|
||||
canvas->do_move("Set Position");
|
||||
|
||||
m_cache.position = position;
|
||||
m_cache.position_rounded(axis) = DBL_MAX;
|
||||
|
|
@ -741,7 +740,7 @@ void ObjectManipulation::change_rotation_value(int axis, double value)
|
|||
selection.rotate(
|
||||
(M_PI / 180.0) * (transformation_type.absolute() ? rotation : rotation - m_cache.rotation),
|
||||
transformation_type);
|
||||
canvas->do_rotate();
|
||||
canvas->do_rotate("Set Orientation");
|
||||
|
||||
m_cache.rotation = rotation;
|
||||
m_cache.rotation_rounded(axis) = DBL_MAX;
|
||||
|
|
@ -806,7 +805,7 @@ void ObjectManipulation::do_scale(int axis, const Vec3d &scale) const
|
|||
|
||||
selection.start_dragging();
|
||||
selection.scale(scaling_factor * 0.01, transformation_type);
|
||||
wxGetApp().plater()->canvas3D()->do_scale();
|
||||
wxGetApp().plater()->canvas3D()->do_scale("Set Scale");
|
||||
}
|
||||
|
||||
void ObjectManipulation::on_change(t_config_option_key opt_key, const boost::any& value)
|
||||
|
|
|
|||
|
|
@ -63,20 +63,28 @@ ObjectSettings::ObjectSettings(wxWindow* parent) :
|
|||
m_bmp_delete = ScalableBitmap(parent, "cross");
|
||||
}
|
||||
|
||||
void ObjectSettings::update_settings_list()
|
||||
bool ObjectSettings::update_settings_list()
|
||||
{
|
||||
m_settings_list_sizer->Clear(true);
|
||||
m_og_settings.resize(0);
|
||||
|
||||
auto objects_ctrl = wxGetApp().obj_list();
|
||||
auto objects_model = wxGetApp().obj_list()->GetModel();
|
||||
auto config = wxGetApp().obj_list()->config();
|
||||
|
||||
const auto item = objects_ctrl->GetSelection();
|
||||
const bool is_layers_range_settings = objects_model->GetItemType(objects_model->GetParent(item)) == itLayer;
|
||||
|
||||
if (!item || !objects_model->IsSettingsItem(item) || !config || objects_ctrl->multiple_selection())
|
||||
return false;
|
||||
|
||||
const bool is_layers_range_settings = objects_model->GetItemType(objects_model->GetParent(item)) == itLayer;
|
||||
SettingsBundle cat_options = objects_ctrl->get_item_settings_bundle(config, is_layers_range_settings);
|
||||
|
||||
if (!cat_options.empty())
|
||||
{
|
||||
std::vector<std::string> categories;
|
||||
categories.reserve(cat_options.size());
|
||||
|
||||
if (item && !objects_ctrl->multiple_selection() &&
|
||||
config && objects_model->IsSettingsItem(item))
|
||||
{
|
||||
auto extra_column = [config, this](wxWindow* parent, const Line& line)
|
||||
{
|
||||
auto opt_key = (line.get_options())[0].opt_id; //we assume that we have one option per line
|
||||
|
|
@ -96,86 +104,61 @@ void ObjectSettings::update_settings_list()
|
|||
return btn;
|
||||
};
|
||||
|
||||
std::map<std::string, std::vector<std::string>> cat_options;
|
||||
auto opt_keys = config->keys();
|
||||
objects_ctrl->update_opt_keys(opt_keys); // update options list according to print technology
|
||||
|
||||
m_og_settings.resize(0);
|
||||
std::vector<std::string> categories;
|
||||
if (!(opt_keys.size() == 1 && opt_keys[0] == "extruder"))// return;
|
||||
for (auto& cat : cat_options)
|
||||
{
|
||||
const int extruders_cnt = wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA ? 1 :
|
||||
wxGetApp().preset_bundle->printers.get_edited_preset().config.option<ConfigOptionFloats>("nozzle_diameter")->values.size();
|
||||
if (cat.second.size() == 1 &&
|
||||
(cat.second[0] == "extruder" || is_layers_range_settings && cat.second[0] == "layer_height"))
|
||||
continue;
|
||||
|
||||
for (auto& opt_key : opt_keys) {
|
||||
auto category = config->def()->get(opt_key)->category;
|
||||
if (category.empty() ||
|
||||
(category == "Extruders" && extruders_cnt == 1)) continue;
|
||||
categories.push_back(cat.first);
|
||||
|
||||
std::vector< std::string > new_category;
|
||||
auto optgroup = std::make_shared<ConfigOptionsGroup>(m_og->ctrl_parent(), _(cat.first), config, false, extra_column);
|
||||
optgroup->label_width = 15;
|
||||
optgroup->sidetext_width = 5.5;
|
||||
|
||||
auto& cat_opt = cat_options.find(category) == cat_options.end() ? new_category : cat_options.at(category);
|
||||
cat_opt.push_back(opt_key);
|
||||
if (cat_opt.size() == 1)
|
||||
cat_options[category] = cat_opt;
|
||||
}
|
||||
optgroup->m_on_change = [](const t_config_option_key& opt_id, const boost::any& value) {
|
||||
wxGetApp().obj_list()->changed_object(); };
|
||||
|
||||
for (auto& cat : cat_options) {
|
||||
if (cat.second.size() == 1 &&
|
||||
(cat.second[0] == "extruder" || is_layers_range_settings && cat.second[0] == "layer_height"))
|
||||
// call back for rescaling of the extracolumn control
|
||||
optgroup->rescale_extra_column_item = [this](wxWindow* win) {
|
||||
auto *ctrl = dynamic_cast<ScalableButton*>(win);
|
||||
if (ctrl == nullptr)
|
||||
return;
|
||||
ctrl->SetBitmap_(m_bmp_delete);
|
||||
};
|
||||
|
||||
const bool is_extruders_cat = cat.first == "Extruders";
|
||||
for (auto& opt : cat.second)
|
||||
{
|
||||
if (opt == "extruder" || is_layers_range_settings && opt == "layer_height")
|
||||
continue;
|
||||
|
||||
auto optgroup = std::make_shared<ConfigOptionsGroup>(m_og->ctrl_parent(), _(cat.first), config, false, extra_column);
|
||||
optgroup->label_width = 15;
|
||||
optgroup->sidetext_width = 5.5;
|
||||
|
||||
optgroup->m_on_change = [](const t_config_option_key& opt_id, const boost::any& value) {
|
||||
wxGetApp().obj_list()->changed_object(); };
|
||||
|
||||
const bool is_extruders_cat = cat.first == "Extruders";
|
||||
for (auto& opt : cat.second)
|
||||
{
|
||||
if (opt == "extruder" || is_layers_range_settings && opt == "layer_height")
|
||||
continue;
|
||||
Option option = optgroup->get_option(opt);
|
||||
option.opt.width = 12;
|
||||
if (is_extruders_cat)
|
||||
option.opt.max = wxGetApp().extruders_cnt();
|
||||
optgroup->append_single_option_line(option);
|
||||
}
|
||||
optgroup->reload_config();
|
||||
m_settings_list_sizer->Add(optgroup->sizer, 0, wxEXPAND | wxALL, 0);
|
||||
|
||||
// call back for rescaling of the extracolumn control
|
||||
optgroup->rescale_extra_column_item = [this](wxWindow* win) {
|
||||
auto *ctrl = dynamic_cast<ScalableButton*>(win);
|
||||
if (ctrl == nullptr)
|
||||
return;
|
||||
ctrl->SetBitmap_(m_bmp_delete);
|
||||
};
|
||||
|
||||
m_og_settings.push_back(optgroup);
|
||||
|
||||
categories.push_back(cat.first);
|
||||
Option option = optgroup->get_option(opt);
|
||||
option.opt.width = 12;
|
||||
if (is_extruders_cat)
|
||||
option.opt.max = wxGetApp().extruders_edited_cnt();
|
||||
optgroup->append_single_option_line(option);
|
||||
}
|
||||
optgroup->reload_config();
|
||||
|
||||
m_settings_list_sizer->Add(optgroup->sizer, 0, wxEXPAND | wxALL, 0);
|
||||
m_og_settings.push_back(optgroup);
|
||||
}
|
||||
|
||||
if (m_og_settings.empty()) {
|
||||
objects_ctrl->select_item(objects_model->Delete(item));
|
||||
}
|
||||
else {
|
||||
if (!categories.empty())
|
||||
objects_model->UpdateSettingsDigest(item, categories);
|
||||
}
|
||||
}
|
||||
if (!categories.empty())
|
||||
objects_model->UpdateSettingsDigest(item, categories);
|
||||
}
|
||||
else
|
||||
{
|
||||
objects_ctrl->select_item(objects_model->Delete(item));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ObjectSettings::UpdateAndShow(const bool show)
|
||||
{
|
||||
if (show)
|
||||
update_settings_list();
|
||||
|
||||
OG_Settings::UpdateAndShow(show);
|
||||
OG_Settings::UpdateAndShow(show ? update_settings_list() : false);
|
||||
}
|
||||
|
||||
void ObjectSettings::msw_rescale()
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ public:
|
|||
ObjectSettings(wxWindow* parent);
|
||||
~ObjectSettings() {}
|
||||
|
||||
void update_settings_list();
|
||||
bool update_settings_list();
|
||||
void UpdateAndShow(const bool show) override;
|
||||
void msw_rescale();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -379,7 +379,7 @@ bool GLGizmoSlaSupports::is_point_clipped(const Vec3d& point) const
|
|||
bool GLGizmoSlaSupports::is_mesh_update_necessary() const
|
||||
{
|
||||
return ((m_state == On) && (m_model_object != nullptr) && !m_model_object->instances.empty())
|
||||
&& ((m_model_object->id() != m_current_mesh_model_id) || m_its == nullptr);
|
||||
&& ((m_model_object->id() != m_current_mesh_object_id) || m_its == nullptr);
|
||||
}
|
||||
|
||||
void GLGizmoSlaSupports::update_mesh()
|
||||
|
|
@ -389,7 +389,7 @@ void GLGizmoSlaSupports::update_mesh()
|
|||
// This mesh does not account for the possible Z up SLA offset.
|
||||
m_mesh = &m_model_object->volumes.front()->mesh();
|
||||
m_its = &m_mesh->its;
|
||||
m_current_mesh_model_id = m_model_object->id();
|
||||
m_current_mesh_object_id = m_model_object->id();
|
||||
m_editing_mode = false;
|
||||
|
||||
m_AABB.deinit();
|
||||
|
|
@ -923,10 +923,12 @@ RENDER_AGAIN:
|
|||
}
|
||||
|
||||
if (value_changed) { // Update side panel
|
||||
wxTheApp->CallAfter([]() {
|
||||
wxGetApp().obj_settings()->UpdateAndShow(true);
|
||||
wxGetApp().obj_list()->update_settings_items();
|
||||
});
|
||||
/* wxTheApp->CallAfter([]() {
|
||||
* wxGetApp().obj_settings()->UpdateAndShow(true);
|
||||
* wxGetApp().obj_list()->update_settings_items();
|
||||
* });
|
||||
* #lm_FIXME_delete_after_testing */
|
||||
wxGetApp().obj_list()->update_and_show_object_settings_item();
|
||||
}
|
||||
|
||||
bool generate = m_imgui->button(m_desc.at("auto_generate"));
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class GLGizmoSlaSupports : public GLGizmoBase
|
|||
{
|
||||
private:
|
||||
ModelObject* m_model_object = nullptr;
|
||||
ModelID m_current_mesh_model_id = 0;
|
||||
ObjectID m_current_mesh_object_id = 0;
|
||||
int m_active_instance = -1;
|
||||
float m_active_instance_bb_radius; // to cache the bb
|
||||
mutable float m_z_shift = 0.f;
|
||||
|
|
|
|||
|
|
@ -542,8 +542,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas)
|
|||
if (m_current == Flatten)
|
||||
{
|
||||
// Rotate the object so the normal points downward:
|
||||
selection.flattening_rotate(get_flattening_normal());
|
||||
canvas.do_flatten();
|
||||
canvas.do_flatten(get_flattening_normal(), "Place on Face");
|
||||
wxGetApp().obj_manipul()->set_dirty();
|
||||
}
|
||||
|
||||
|
|
@ -616,17 +615,17 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas)
|
|||
case Move:
|
||||
{
|
||||
canvas.disable_regenerate_volumes();
|
||||
canvas.do_move();
|
||||
canvas.do_move("Gizmo-Move Object");
|
||||
break;
|
||||
}
|
||||
case Scale:
|
||||
{
|
||||
canvas.do_scale();
|
||||
canvas.do_scale("Gizmo-Scale Object");
|
||||
break;
|
||||
}
|
||||
case Rotate:
|
||||
{
|
||||
canvas.do_rotate();
|
||||
canvas.do_rotate("Gizmo-Rotate Object");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -233,9 +233,9 @@ ImVec2 ImGuiWrapper::calc_text_size(const wxString &text)
|
|||
return size;
|
||||
}
|
||||
|
||||
void ImGuiWrapper::set_next_window_pos(float x, float y, int flag)
|
||||
void ImGuiWrapper::set_next_window_pos(float x, float y, int flag, float pivot_x, float pivot_y)
|
||||
{
|
||||
ImGui::SetNextWindowPos(ImVec2(x, y), (ImGuiCond)flag);
|
||||
ImGui::SetNextWindowPos(ImVec2(x, y), (ImGuiCond)flag, ImVec2(pivot_x, pivot_y));
|
||||
ImGui::SetNextWindowSize(ImVec2(0.0, 0.0));
|
||||
}
|
||||
|
||||
|
|
@ -342,6 +342,32 @@ bool ImGuiWrapper::combo(const wxString& label, const std::vector<std::string>&
|
|||
return res;
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool , int , const char**), int& hovered, int& selected)
|
||||
{
|
||||
bool is_hovered = false;
|
||||
ImGui::ListBoxHeader("", size);
|
||||
|
||||
int i=0;
|
||||
const char* item_text;
|
||||
while (items_getter(is_undo, i, &item_text))
|
||||
{
|
||||
ImGui::Selectable(item_text, i < hovered);
|
||||
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(item_text);
|
||||
hovered = i;
|
||||
is_hovered = true;
|
||||
}
|
||||
|
||||
if (ImGui::IsItemClicked())
|
||||
selected = i;
|
||||
i++;
|
||||
}
|
||||
|
||||
ImGui::ListBoxFooter();
|
||||
return is_hovered;
|
||||
}
|
||||
|
||||
void ImGuiWrapper::disabled_begin(bool disabled)
|
||||
{
|
||||
wxCHECK_RET(!m_disabled, "ImGUI: Unbalanced disabled_begin() call");
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ public:
|
|||
ImVec2 scaled(float x, float y) const { return ImVec2(x * m_font_size, y * m_font_size); }
|
||||
ImVec2 calc_text_size(const wxString &text);
|
||||
|
||||
void set_next_window_pos(float x, float y, int flag);
|
||||
void set_next_window_pos(float x, float y, int flag, float pivot_x = 0.0f, float pivot_y = 0.0f);
|
||||
void set_next_window_bg_alpha(float alpha);
|
||||
|
||||
bool begin(const std::string &name, int flags = 0);
|
||||
|
|
@ -67,6 +67,7 @@ public:
|
|||
void text(const std::string &label);
|
||||
void text(const wxString &label);
|
||||
bool combo(const wxString& label, const std::vector<std::string>& options, int& selection); // Use -1 to not mark any option as selected
|
||||
bool undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool, int, const char**), int& hovered, int& selected);
|
||||
|
||||
void disabled_begin(bool disabled);
|
||||
void disabled_end();
|
||||
|
|
|
|||
|
|
@ -537,6 +537,14 @@ void MainFrame::init_menubar()
|
|||
_(L("Deletes all objects")), [this](wxCommandEvent&) { m_plater->reset_with_confirm(); },
|
||||
menu_icon("delete_all_menu"), nullptr, [this](){return can_delete_all(); }, this);
|
||||
|
||||
editMenu->AppendSeparator();
|
||||
append_menu_item(editMenu, wxID_ANY, _(L("&Undo")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "Z",
|
||||
_(L("Undo")), [this](wxCommandEvent&) { m_plater->undo(); },
|
||||
"undo", nullptr, [this](){return m_plater->can_undo(); }, this);
|
||||
append_menu_item(editMenu, wxID_ANY, _(L("&Redo")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "Y",
|
||||
_(L("Redo")), [this](wxCommandEvent&) { m_plater->redo(); },
|
||||
"redo", nullptr, [this](){return m_plater->can_redo(); }, this);
|
||||
|
||||
editMenu->AppendSeparator();
|
||||
append_menu_item(editMenu, wxID_ANY, _(L("&Copy")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "C",
|
||||
_(L("Copy selection to clipboard")), [this](wxCommandEvent&) { m_plater->copy_selection_to_clipboard(); },
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@
|
|||
#include "../Utils/ASCIIFolding.hpp"
|
||||
#include "../Utils/PrintHost.hpp"
|
||||
#include "../Utils/FixModelByWin10.hpp"
|
||||
#include "../Utils/UndoRedo.hpp"
|
||||
|
||||
#include <wx/glcanvas.h> // Needs to be last because reasons :-/
|
||||
#include "WipeTowerDialog.hpp"
|
||||
|
|
@ -1254,6 +1255,8 @@ const std::regex PlaterDropTarget::pattern_drop(".*[.](stl|obj|amf|3mf|prusa)",
|
|||
|
||||
bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &filenames)
|
||||
{
|
||||
plater->take_snapshot(_(L("Load Files")));
|
||||
|
||||
std::vector<fs::path> paths;
|
||||
|
||||
for (const auto &filename : filenames) {
|
||||
|
|
@ -1319,7 +1322,12 @@ struct Plater::priv
|
|||
Slic3r::Model model;
|
||||
PrinterTechnology printer_technology = ptFFF;
|
||||
Slic3r::GCodePreviewData gcode_preview_data;
|
||||
|
||||
Slic3r::UndoRedo::Stack undo_redo_stack;
|
||||
int m_prevent_snapshots = 0; /* Used for avoid of excess "snapshoting".
|
||||
* Like for "delete selected" or "set numbers of copies"
|
||||
* we should call tack_snapshot just ones
|
||||
* instead of calls for each action separately
|
||||
* */
|
||||
// GUI elements
|
||||
wxSizer* panel_sizer{ nullptr };
|
||||
wxPanel* current_panel{ nullptr };
|
||||
|
|
@ -1619,6 +1627,23 @@ struct Plater::priv
|
|||
void split_object();
|
||||
void split_volume();
|
||||
void scale_selection_to_fit_print_volume();
|
||||
|
||||
void take_snapshot(const std::string& snapshot_name)
|
||||
{
|
||||
if (this->m_prevent_snapshots > 0)
|
||||
return;
|
||||
assert(this->m_prevent_snapshots >= 0);
|
||||
this->undo_redo_stack.take_snapshot(snapshot_name, model, view3D->get_canvas3d()->get_selection());
|
||||
}
|
||||
void take_snapshot(const wxString& snapshot_name) { this->take_snapshot(std::string(snapshot_name.ToUTF8().data())); }
|
||||
int get_active_snapshot_index();
|
||||
void undo();
|
||||
void redo();
|
||||
void undo_to(size_t time_to_load);
|
||||
void redo_to(size_t time_to_load);
|
||||
void suppress_snapshots() { this->m_prevent_snapshots++; }
|
||||
void allow_snapshots() { this->m_prevent_snapshots--; }
|
||||
|
||||
bool background_processing_enabled() const { return this->get_config("background_processing") == "1"; }
|
||||
void update_print_volume_state();
|
||||
void schedule_background_process();
|
||||
|
|
@ -1709,6 +1734,7 @@ private:
|
|||
|
||||
void update_fff_scene();
|
||||
void update_sla_scene();
|
||||
void update_after_undo_redo();
|
||||
|
||||
// path to project file stored with no extension
|
||||
wxString m_project_filename;
|
||||
|
|
@ -1811,6 +1837,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
|||
view3D_canvas->Bind(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, &priv::on_3dcanvas_mouse_dragging_finished, this);
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_TAB, [this](SimpleEvent&) { select_next_view_3D(); });
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_RESETGIZMOS, [this](SimpleEvent&) { reset_all_gizmos(); });
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_UNDO, [this](SimpleEvent&) { this->undo(); });
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_REDO, [this](SimpleEvent&) { this->redo(); });
|
||||
|
||||
// 3DScene/Toolbar:
|
||||
view3D_canvas->Bind(EVT_GLTOOLBAR_ADD, &priv::on_action_add, this);
|
||||
|
|
@ -1849,6 +1877,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
|||
|
||||
// updates camera type from .ini file
|
||||
camera.set_type(get_config("use_perspective_camera"));
|
||||
|
||||
// Initialize the Undo / Redo stack with a first snapshot.
|
||||
this->take_snapshot(_(L("New Project")));
|
||||
}
|
||||
|
||||
Plater::priv::~priv()
|
||||
|
|
@ -2219,7 +2250,7 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &mode
|
|||
// the size of the object is too big -> this could lead to overflow when moving to clipper coordinates,
|
||||
// so scale down the mesh
|
||||
double inv = 1. / max_ratio;
|
||||
object->scale_mesh(Vec3d(inv, inv, inv));
|
||||
object->scale_mesh_after_creation(Vec3d(inv, inv, inv));
|
||||
object->origin_translation = Vec3d::Zero();
|
||||
object->center_around_origin();
|
||||
scaled_down = true;
|
||||
|
|
@ -2398,12 +2429,15 @@ void Plater::priv::object_list_changed()
|
|||
|
||||
void Plater::priv::select_all()
|
||||
{
|
||||
// this->take_snapshot(_(L("Select All")));
|
||||
|
||||
view3D->select_all();
|
||||
this->sidebar->obj_list()->update_selections();
|
||||
}
|
||||
|
||||
void Plater::priv::deselect_all()
|
||||
{
|
||||
// this->take_snapshot(_(L("Deselect All")));
|
||||
view3D->deselect_all();
|
||||
}
|
||||
|
||||
|
|
@ -2425,6 +2459,7 @@ void Plater::priv::remove(size_t obj_idx)
|
|||
|
||||
void Plater::priv::delete_object_from_model(size_t obj_idx)
|
||||
{
|
||||
this->take_snapshot(_(L("Delete Object")));
|
||||
model.delete_object(obj_idx);
|
||||
update();
|
||||
object_list_changed();
|
||||
|
|
@ -2432,6 +2467,8 @@ void Plater::priv::delete_object_from_model(size_t obj_idx)
|
|||
|
||||
void Plater::priv::reset()
|
||||
{
|
||||
this->take_snapshot(_(L("Reset Project")));
|
||||
|
||||
set_project_filename(wxEmptyString);
|
||||
|
||||
// Prevent toolpaths preview from rendering while we modify the Print object
|
||||
|
|
@ -2457,17 +2494,20 @@ void Plater::priv::reset()
|
|||
|
||||
void Plater::priv::mirror(Axis axis)
|
||||
{
|
||||
this->take_snapshot(_(L("Mirror")));
|
||||
view3D->mirror_selection(axis);
|
||||
}
|
||||
|
||||
void Plater::priv::arrange()
|
||||
{
|
||||
this->take_snapshot(_(L("Arrange")));
|
||||
m_ui_jobs.start(Jobs::Arrange);
|
||||
}
|
||||
|
||||
// This method will find an optimal orientation for the currently selected item
|
||||
// Very similar in nature to the arrange method above...
|
||||
void Plater::priv::sla_optimize_rotation() {
|
||||
this->take_snapshot(_(L("Optimize Rotation")));
|
||||
m_ui_jobs.start(Jobs::Rotoptimize);
|
||||
}
|
||||
|
||||
|
|
@ -2623,6 +2663,8 @@ void Plater::priv::split_object()
|
|||
Slic3r::GUI::warning_catcher(q, _(L("The selected object couldn't be split because it contains only one part.")));
|
||||
else
|
||||
{
|
||||
this->take_snapshot(_(L("Split to Objects")));
|
||||
|
||||
unsigned int counter = 1;
|
||||
for (ModelObject* m : new_objects)
|
||||
m->name = current_model_object->name + "_" + std::to_string(counter++);
|
||||
|
|
@ -2861,6 +2903,8 @@ void Plater::priv::update_sla_scene()
|
|||
|
||||
void Plater::priv::reload_from_disk()
|
||||
{
|
||||
this->take_snapshot(_(L("Reload from Disk")));
|
||||
|
||||
const auto &selection = get_selection();
|
||||
const auto obj_orig_idx = selection.get_object_idx();
|
||||
if (selection.is_wipe_tower() || obj_orig_idx == -1) { return; }
|
||||
|
|
@ -2894,6 +2938,9 @@ void Plater::priv::fix_through_netfabb(const int obj_idx, const int vol_idx/* =
|
|||
{
|
||||
if (obj_idx < 0)
|
||||
return;
|
||||
|
||||
this->take_snapshot(_(L("Fix Throught NetFabb")));
|
||||
|
||||
fix_model_by_win10_sdk_gui(*model.objects[obj_idx], vol_idx);
|
||||
this->update();
|
||||
this->object_list_changed();
|
||||
|
|
@ -3139,6 +3186,8 @@ void Plater::priv::on_action_layersediting(SimpleEvent&)
|
|||
|
||||
void Plater::priv::on_object_select(SimpleEvent& evt)
|
||||
{
|
||||
// this->take_snapshot(_(L("Object Selection")));
|
||||
|
||||
wxGetApp().obj_list()->update_selections();
|
||||
selection_changed();
|
||||
}
|
||||
|
|
@ -3414,8 +3463,7 @@ void Plater::priv::init_view_toolbar()
|
|||
item.icon_filename = "editor.svg";
|
||||
item.tooltip = _utf8(L("3D editor view")) + " [" + GUI::shortkey_ctrl_prefix() + "5]";
|
||||
item.sprite_id = 0;
|
||||
item.action_callback = [this]() { if (this->q != nullptr) wxPostEvent(this->q, SimpleEvent(EVT_GLVIEWTOOLBAR_3D)); };
|
||||
item.is_toggable = false;
|
||||
item.left.action_callback = [this]() { if (this->q != nullptr) wxPostEvent(this->q, SimpleEvent(EVT_GLVIEWTOOLBAR_3D)); };
|
||||
if (!view_toolbar.add_item(item))
|
||||
return;
|
||||
|
||||
|
|
@ -3423,8 +3471,7 @@ void Plater::priv::init_view_toolbar()
|
|||
item.icon_filename = "preview.svg";
|
||||
item.tooltip = _utf8(L("Preview")) + " [" + GUI::shortkey_ctrl_prefix() + "6]";
|
||||
item.sprite_id = 1;
|
||||
item.action_callback = [this]() { if (this->q != nullptr) wxPostEvent(this->q, SimpleEvent(EVT_GLVIEWTOOLBAR_PREVIEW)); };
|
||||
item.is_toggable = false;
|
||||
item.left.action_callback = [this]() { if (this->q != nullptr) wxPostEvent(this->q, SimpleEvent(EVT_GLVIEWTOOLBAR_PREVIEW)); };
|
||||
if (!view_toolbar.add_item(item))
|
||||
return;
|
||||
|
||||
|
|
@ -3554,6 +3601,51 @@ void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const
|
|||
}
|
||||
}
|
||||
|
||||
int Plater::priv::get_active_snapshot_index()
|
||||
{
|
||||
const size_t& active_snapshot_time = this->undo_redo_stack.active_snapshot_time();
|
||||
const std::vector<UndoRedo::Snapshot>& ss_stack = this->undo_redo_stack.snapshots();
|
||||
const auto it = std::lower_bound(ss_stack.begin(), ss_stack.end(), UndoRedo::Snapshot(active_snapshot_time));
|
||||
return it - ss_stack.begin();
|
||||
}
|
||||
|
||||
void Plater::priv::undo()
|
||||
{
|
||||
if (this->undo_redo_stack.undo(model, this->view3D->get_canvas3d()->get_selection()))
|
||||
this->update_after_undo_redo();
|
||||
}
|
||||
|
||||
void Plater::priv::redo()
|
||||
{
|
||||
if (this->undo_redo_stack.redo(model))
|
||||
this->update_after_undo_redo();
|
||||
}
|
||||
|
||||
void Plater::priv::undo_to(size_t time_to_load)
|
||||
{
|
||||
if (this->undo_redo_stack.undo(model, this->view3D->get_canvas3d()->get_selection(), time_to_load))
|
||||
this->update_after_undo_redo();
|
||||
}
|
||||
|
||||
void Plater::priv::redo_to(size_t time_to_load)
|
||||
{
|
||||
if (this->undo_redo_stack.redo(model, time_to_load))
|
||||
this->update_after_undo_redo();
|
||||
}
|
||||
|
||||
void Plater::priv::update_after_undo_redo()
|
||||
{
|
||||
this->view3D->get_canvas3d()->get_selection().clear();
|
||||
this->update(false); // update volumes from the deserializd model
|
||||
//YS_FIXME update obj_list from the deserialized model (maybe store ObjectIDs into the tree?) (no selections at this point of time)
|
||||
this->view3D->get_canvas3d()->get_selection().set_deserialized(GUI::Selection::EMode(this->undo_redo_stack.selection_deserialized().mode), this->undo_redo_stack.selection_deserialized().volumes_and_instances);
|
||||
|
||||
wxGetApp().obj_list()->update_after_undo_redo();
|
||||
|
||||
//FIXME what about the state of the manipulators?
|
||||
//FIXME what about the focus? Cursor in the side panel?
|
||||
}
|
||||
|
||||
void Sidebar::set_btn_label(const ActionButtonType btn_type, const wxString& label) const
|
||||
{
|
||||
switch (btn_type)
|
||||
|
|
@ -3591,6 +3683,8 @@ void Plater::new_project()
|
|||
|
||||
void Plater::load_project()
|
||||
{
|
||||
this->take_snapshot(_(L("Load Project")));
|
||||
|
||||
wxString input_file;
|
||||
wxGetApp().load_project(this, input_file);
|
||||
load_project(input_file);
|
||||
|
|
@ -3616,6 +3710,8 @@ void Plater::add_model()
|
|||
if (input_files.empty())
|
||||
return;
|
||||
|
||||
this->take_snapshot(_(L("Add object(s)")));
|
||||
|
||||
std::vector<fs::path> input_paths;
|
||||
for (const auto &file : input_files) {
|
||||
input_paths.push_back(into_path(file));
|
||||
|
|
@ -3673,13 +3769,18 @@ void Plater::delete_object_from_model(size_t obj_idx) { p->delete_object_from_mo
|
|||
|
||||
void Plater::remove_selected()
|
||||
{
|
||||
this->take_snapshot(_(L("Delete Selected Objects")));
|
||||
this->suppress_snapshots();
|
||||
this->p->view3D->delete_selected();
|
||||
this->allow_snapshots();
|
||||
}
|
||||
|
||||
void Plater::increase_instances(size_t num)
|
||||
{
|
||||
if (! can_increase_instances()) { return; }
|
||||
|
||||
this->take_snapshot(_(L("Increase Instances")));
|
||||
|
||||
int obj_idx = p->get_selected_object_idx();
|
||||
|
||||
ModelObject* model_object = p->model.objects[obj_idx];
|
||||
|
|
@ -3714,6 +3815,8 @@ void Plater::decrease_instances(size_t num)
|
|||
{
|
||||
if (! can_decrease_instances()) { return; }
|
||||
|
||||
this->take_snapshot(_(L("Decrease Instances")));
|
||||
|
||||
int obj_idx = p->get_selected_object_idx();
|
||||
|
||||
ModelObject* model_object = p->model.objects[obj_idx];
|
||||
|
|
@ -3748,11 +3851,16 @@ void Plater::set_number_of_copies(/*size_t num*/)
|
|||
if (num < 0)
|
||||
return;
|
||||
|
||||
this->take_snapshot(wxString::Format(_(L("Set numbers of copies to %d")), num));
|
||||
this->suppress_snapshots();
|
||||
|
||||
int diff = (int)num - (int)model_object->instances.size();
|
||||
if (diff > 0)
|
||||
increase_instances(diff);
|
||||
else if (diff < 0)
|
||||
decrease_instances(-diff);
|
||||
|
||||
this->allow_snapshots();
|
||||
}
|
||||
|
||||
bool Plater::is_selection_empty() const
|
||||
|
|
@ -3776,6 +3884,8 @@ void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_uppe
|
|||
return;
|
||||
}
|
||||
|
||||
this->take_snapshot(_(L("Cut")));
|
||||
|
||||
wxBusyCursor wait;
|
||||
const auto new_objects = object->cut(instance_idx, z, keep_upper, keep_lower, rotate_lower);
|
||||
|
||||
|
|
@ -4065,6 +4175,45 @@ void Plater::send_gcode()
|
|||
}
|
||||
}
|
||||
|
||||
void Plater::take_snapshot(const std::string &snapshot_name) { p->take_snapshot(snapshot_name); }
|
||||
void Plater::take_snapshot(const wxString &snapshot_name) { p->take_snapshot(snapshot_name); }
|
||||
void Plater::suppress_snapshots() { p->suppress_snapshots(); }
|
||||
void Plater::allow_snapshots() { p->allow_snapshots(); }
|
||||
void Plater::undo() { p->undo(); }
|
||||
void Plater::redo() { p->redo(); }
|
||||
void Plater::undo_to(int selection)
|
||||
{
|
||||
if (selection == 0) {
|
||||
p->undo();
|
||||
return;
|
||||
}
|
||||
|
||||
const int idx = p->get_active_snapshot_index() - selection - 1;
|
||||
p->undo_to(p->undo_redo_stack.snapshots()[idx].timestamp);
|
||||
}
|
||||
void Plater::redo_to(int selection)
|
||||
{
|
||||
if (selection == 0) {
|
||||
p->redo();
|
||||
return;
|
||||
}
|
||||
|
||||
const int idx = p->get_active_snapshot_index() + selection + 1;
|
||||
p->redo_to(p->undo_redo_stack.snapshots()[idx].timestamp);
|
||||
}
|
||||
bool Plater::undo_redo_string_getter(const bool is_undo, int idx, const char** out_text)
|
||||
{
|
||||
const std::vector<UndoRedo::Snapshot>& ss_stack = p->undo_redo_stack.snapshots();
|
||||
const int idx_in_ss_stack = p->get_active_snapshot_index() + (is_undo ? -(++idx) : idx);
|
||||
|
||||
if (0 < idx_in_ss_stack && idx_in_ss_stack < ss_stack.size() - 1) {
|
||||
*out_text = ss_stack[idx_in_ss_stack].name.c_str();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Plater::on_extruders_change(int num_extruders)
|
||||
{
|
||||
auto& choices = sidebar().combos_filament();
|
||||
|
|
@ -4290,8 +4439,11 @@ void Plater::copy_selection_to_clipboard()
|
|||
|
||||
void Plater::paste_from_clipboard()
|
||||
{
|
||||
if (can_paste_from_clipboard())
|
||||
p->view3D->get_canvas3d()->get_selection().paste_from_clipboard();
|
||||
if (!can_paste_from_clipboard())
|
||||
return;
|
||||
|
||||
this->take_snapshot(_(L("Paste From Clipboard")));
|
||||
p->view3D->get_canvas3d()->get_selection().paste_from_clipboard();
|
||||
}
|
||||
|
||||
void Plater::msw_rescale()
|
||||
|
|
@ -4356,6 +4508,16 @@ bool Plater::can_copy_to_clipboard() const
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Plater::can_undo() const
|
||||
{
|
||||
return p->undo_redo_stack.has_undo_snapshot();
|
||||
}
|
||||
|
||||
bool Plater::can_redo() const
|
||||
{
|
||||
return p->undo_redo_stack.has_redo_snapshot();
|
||||
}
|
||||
|
||||
SuppressBackgroundProcessingUpdate::SuppressBackgroundProcessingUpdate() :
|
||||
m_was_running(wxGetApp().plater()->is_background_process_running())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -184,6 +184,16 @@ public:
|
|||
void fix_through_netfabb(const int obj_idx, const int vol_idx = -1);
|
||||
void send_gcode();
|
||||
|
||||
void take_snapshot(const std::string &snapshot_name);
|
||||
void take_snapshot(const wxString &snapshot_name);
|
||||
void suppress_snapshots();
|
||||
void allow_snapshots();
|
||||
void undo();
|
||||
void redo();
|
||||
void undo_to(int selection);
|
||||
void redo_to(int selection);
|
||||
bool undo_redo_string_getter(const bool is_undo, int idx, const char** out_text);
|
||||
|
||||
void on_extruders_change(int extruders_count);
|
||||
void on_config_change(const DynamicPrintConfig &config);
|
||||
// On activating the parent window.
|
||||
|
|
@ -218,6 +228,8 @@ public:
|
|||
bool can_layers_editing() const;
|
||||
bool can_paste_from_clipboard() const;
|
||||
bool can_copy_to_clipboard() const;
|
||||
bool can_undo() const;
|
||||
bool can_redo() const;
|
||||
|
||||
void msw_rescale();
|
||||
|
||||
|
|
|
|||
|
|
@ -1366,7 +1366,7 @@ void PresetBundle::export_configbundle(const std::string &path, bool export_syst
|
|||
continue;
|
||||
c << std::endl << "[" << presets->section_name() << ":" << preset.name << "]" << std::endl;
|
||||
for (const std::string &opt_key : preset.config.keys())
|
||||
c << opt_key << " = " << preset.config.serialize(opt_key) << std::endl;
|
||||
c << opt_key << " = " << preset.config.opt_serialize(opt_key) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -312,6 +312,22 @@ void Selection::add_all()
|
|||
this->set_bounding_boxes_dirty();
|
||||
}
|
||||
|
||||
void Selection::set_deserialized(EMode mode, const std::vector<std::pair<size_t, size_t>> &volumes_and_instances)
|
||||
{
|
||||
if (! m_valid)
|
||||
return;
|
||||
|
||||
m_mode = mode;
|
||||
for (unsigned int i : m_list)
|
||||
(*m_volumes)[i]->selected = false;
|
||||
m_list.clear();
|
||||
for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++ i)
|
||||
if (std::binary_search(volumes_and_instances.begin(), volumes_and_instances.end(), (*m_volumes)[i]->geometry_id))
|
||||
this->do_add_volume(i);
|
||||
update_type();
|
||||
this->set_bounding_boxes_dirty();
|
||||
}
|
||||
|
||||
void Selection::clear()
|
||||
{
|
||||
if (!m_valid)
|
||||
|
|
@ -790,6 +806,8 @@ void Selection::scale_to_fit_print_volume(const DynamicPrintConfig& config)
|
|||
double s = std::min(sx, std::min(sy, sz));
|
||||
if (s != 1.0)
|
||||
{
|
||||
wxGetApp().plater()->take_snapshot(_(L("Scale To Fit")));
|
||||
|
||||
TransformationType type;
|
||||
type.set_world();
|
||||
type.set_relative();
|
||||
|
|
@ -798,12 +816,12 @@ void Selection::scale_to_fit_print_volume(const DynamicPrintConfig& config)
|
|||
// apply scale
|
||||
start_dragging();
|
||||
scale(s * Vec3d::Ones(), type);
|
||||
wxGetApp().plater()->canvas3D()->do_scale();
|
||||
wxGetApp().plater()->canvas3D()->do_scale(""); // avoid storing another snapshot
|
||||
|
||||
// center selection on print bed
|
||||
start_dragging();
|
||||
translate(print_volume.center() - get_bounding_box().center());
|
||||
wxGetApp().plater()->canvas3D()->do_move();
|
||||
wxGetApp().plater()->canvas3D()->do_move(""); // avoid storing another snapshot
|
||||
|
||||
wxGetApp().obj_manipul()->set_dirty();
|
||||
}
|
||||
|
|
@ -1177,7 +1195,7 @@ void Selection::copy_to_clipboard()
|
|||
ModelObject* dst_object = m_clipboard.add_object();
|
||||
dst_object->name = src_object->name;
|
||||
dst_object->input_file = src_object->input_file;
|
||||
dst_object->config = src_object->config;
|
||||
static_cast<DynamicPrintConfig&>(dst_object->config) = static_cast<const DynamicPrintConfig&>(src_object->config);
|
||||
dst_object->sla_support_points = src_object->sla_support_points;
|
||||
dst_object->sla_points_status = src_object->sla_points_status;
|
||||
dst_object->layer_config_ranges = src_object->layer_config_ranges; // #ys_FIXME_experiment
|
||||
|
|
@ -2016,6 +2034,10 @@ bool Selection::is_from_fully_selected_instance(unsigned int volume_idx) const
|
|||
|
||||
void Selection::paste_volumes_from_clipboard()
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
check_model_ids_validity(*m_model);
|
||||
#endif /* _DEBUG */
|
||||
|
||||
int dst_obj_idx = get_object_idx();
|
||||
if ((dst_obj_idx < 0) || ((int)m_model->objects.size() <= dst_obj_idx))
|
||||
return;
|
||||
|
|
@ -2057,6 +2079,9 @@ void Selection::paste_volumes_from_clipboard()
|
|||
}
|
||||
|
||||
volumes.push_back(dst_volume);
|
||||
#ifdef _DEBUG
|
||||
check_model_ids_validity(*m_model);
|
||||
#endif /* _DEBUG */
|
||||
}
|
||||
|
||||
// keeps relative position of multivolume selections
|
||||
|
|
@ -2070,10 +2095,18 @@ void Selection::paste_volumes_from_clipboard()
|
|||
|
||||
wxGetApp().obj_list()->paste_volumes_into_list(dst_obj_idx, volumes);
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
check_model_ids_validity(*m_model);
|
||||
#endif /* _DEBUG */
|
||||
}
|
||||
|
||||
void Selection::paste_objects_from_clipboard()
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
check_model_ids_validity(*m_model);
|
||||
#endif /* _DEBUG */
|
||||
|
||||
std::vector<size_t> object_idxs;
|
||||
const ModelObjectPtrs& src_objects = m_clipboard.get_objects();
|
||||
for (const ModelObject* src_object : src_objects)
|
||||
|
|
@ -2087,9 +2120,16 @@ void Selection::paste_objects_from_clipboard()
|
|||
}
|
||||
|
||||
object_idxs.push_back(m_model->objects.size() - 1);
|
||||
#ifdef _DEBUG
|
||||
check_model_ids_validity(*m_model);
|
||||
#endif /* _DEBUG */
|
||||
}
|
||||
|
||||
wxGetApp().obj_list()->paste_objects_into_list(object_idxs);
|
||||
|
||||
#ifdef _DEBUG
|
||||
check_model_ids_validity(*m_model);
|
||||
#endif /* _DEBUG */
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ private:
|
|||
Vec3d dragging_center;
|
||||
// Map from indices of ModelObject instances in Model::objects
|
||||
// to a set of indices of ModelVolume instances in ModelObject::instances
|
||||
// Here the index means a position inside the respective std::vector, not ModelID.
|
||||
// Here the index means a position inside the respective std::vector, not ObjectID.
|
||||
ObjectIdxsToInstanceIdxsMap content;
|
||||
};
|
||||
|
||||
|
|
@ -237,6 +237,9 @@ public:
|
|||
|
||||
void add_all();
|
||||
|
||||
// To be called after Undo or Redo once the volumes are updated.
|
||||
void set_deserialized(EMode mode, const std::vector<std::pair<size_t, size_t>> &volumes_and_instances);
|
||||
|
||||
// Update the selection based on the new instance IDs.
|
||||
void instances_changed(const std::vector<size_t> &instance_ids_selected);
|
||||
// Update the selection based on the map from old indices to new indices after m_volumes changed.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue