Simplify the Align/Distribute context menu

This commit is contained in:
SoftFever
2026-10-14 18:25:16 +08:00
parent 4e7fa93981
commit 48d07a831d
5 changed files with 45 additions and 375 deletions
+45 -234
View File
@@ -2334,243 +2334,54 @@ void MenuFactory::append_menu_item_center(wxMenu* menu)
void MenuFactory::append_menu_item_align_distribute(wxMenu *menu)
{
using AlignType = GLGizmoAlignment::AlignType;
auto can = [](AlignType type) {
if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D)
return false;
GLGizmoAlignment alignment_helper(*plater()->get_view3D_canvas3D());
return type >= AlignType::DISTRIBUTE_X ? alignment_helper.can_distribute(type) : alignment_helper.can_align(type);
};
auto run = [](AlignType type) {
GLGizmoAlignment alignment_helper(*plater()->get_view3D_canvas3D());
if (type >= AlignType::DISTRIBUTE_X)
alignment_helper.distribute_objects(type);
else
alignment_helper.align_objects(type);
};
// NONE adds a separator.
const std::pair<wxString, AlignType> items[] = {
{_L("Distribute left-right") + " (X)", AlignType::DISTRIBUTE_X},
{_L("Distribute front-back") + " (Y)", AlignType::DISTRIBUTE_Y},
{_L("Distribute top-bottom") + " (Z)", AlignType::DISTRIBUTE_Z},
{wxString(), AlignType::NONE},
{_L("Align left") + " (-X)", AlignType::X_MIN},
{_L("Align left-right center") + " (X)", AlignType::CENTER_X},
{_L("Align right") + " (+X)", AlignType::X_MAX},
{wxString(), AlignType::NONE},
{_L("Align front") + " (-Y)", AlignType::Y_MIN},
{_L("Align front-back center") + " (Y)", AlignType::CENTER_Y},
{_L("Align back") + " (+Y)", AlignType::Y_MAX},
{wxString(), AlignType::NONE},
{_L("Align bottom") + " (-Z)", AlignType::Z_MIN},
{_L("Align top-bottom center") + " (Z)", AlignType::CENTER_Z},
{_L("Align top") + " (+Z)", AlignType::Z_MAX},
};
wxMenu* align_distribute_menu = new wxMenu();
if (!align_distribute_menu)
return;
append_menu_item(
align_distribute_menu, wxID_ANY, _L("Distribute left-right") + " (X)", "",
[](wxCommandEvent &) {
auto canvas3d = plater()->get_view3D_canvas3D();
canvas3d->get_gizmos_manager().check_object_located_outside_plate(true);
plater()->distribute_selection_x();
},
"", nullptr,
[]() {
if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D)
return false;
auto canvas3d = plater()->get_view3D_canvas3D();
GLGizmoAlignment alignment_helper(*canvas3d);
return alignment_helper.can_distribute(GLGizmoAlignment::AlignType::DISTRIBUTE_X);
},
m_parent);
append_menu_item(
align_distribute_menu, wxID_ANY, _L("Distribute front-back") + " (Y)", "",
[](wxCommandEvent &) {
auto canvas3d = plater()->get_view3D_canvas3D();
canvas3d->get_gizmos_manager().check_object_located_outside_plate(true);
plater()->distribute_selection_y();
},
"", nullptr,
[]() {
if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D)
return false;
auto canvas3d = plater()->get_view3D_canvas3D();
GLGizmoAlignment alignment_helper(*canvas3d);
return alignment_helper.can_distribute(GLGizmoAlignment::AlignType::DISTRIBUTE_Y);
},
m_parent);
append_menu_item(
align_distribute_menu, wxID_ANY, _L("Distribute top-bottom") + " (Z)", "",
[](wxCommandEvent &) {
auto canvas3d = plater()->get_view3D_canvas3D();
canvas3d->get_gizmos_manager().check_object_located_outside_plate(true);
plater()->distribute_selection_z();
},
"", nullptr,
[]() {
if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D)
return false;
auto canvas3d = plater()->get_view3D_canvas3D();
GLGizmoAlignment alignment_helper(*canvas3d);
return alignment_helper.can_distribute(GLGizmoAlignment::AlignType::DISTRIBUTE_Z);
},
m_parent);
align_distribute_menu->AppendSeparator();
append_menu_item(
align_distribute_menu, wxID_ANY, _L("Align left") + " (-X)", "",
[](wxCommandEvent &) {
auto canvas3d = plater()->get_view3D_canvas3D();
canvas3d->get_gizmos_manager().check_object_located_outside_plate(true);
plater()->align_selection_x_min();
},
"", nullptr,
[]() {
if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D)
return false;
auto canvas3d = plater()->get_view3D_canvas3D();
GLGizmoAlignment alignment_helper(*canvas3d);
return alignment_helper.can_align(GLGizmoAlignment::AlignType::X_MIN);
},
m_parent);
append_menu_item(
align_distribute_menu, wxID_ANY, _L("Align left-right center") + " (X)", "",
[](wxCommandEvent &) {
auto canvas3d = plater()->get_view3D_canvas3D();
canvas3d->get_gizmos_manager().check_object_located_outside_plate(true);
plater()->align_selection_x_center();
},
"", nullptr,
[]() {
if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D)
return false;
auto canvas3d = plater()->get_view3D_canvas3D();
GLGizmoAlignment alignment_helper(*canvas3d);
return alignment_helper.can_align(GLGizmoAlignment::AlignType::CENTER_X);
},
m_parent);
append_menu_item(
align_distribute_menu, wxID_ANY, _L("Align right") + " (+X)", "",
[](wxCommandEvent &) {
auto canvas3d = plater()->get_view3D_canvas3D();
canvas3d->get_gizmos_manager().check_object_located_outside_plate(true);
plater()->align_selection_x_max();
},
"", nullptr,
[]() {
if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D)
return false;
auto canvas3d = plater()->get_view3D_canvas3D();
GLGizmoAlignment alignment_helper(*canvas3d);
return alignment_helper.can_align(GLGizmoAlignment::AlignType::X_MAX);
},
m_parent);
align_distribute_menu->AppendSeparator();
append_menu_item(
align_distribute_menu, wxID_ANY, _L("Align front") + " (-Y)", "",
[](wxCommandEvent &) {
auto canvas3d = plater()->get_view3D_canvas3D();
canvas3d->get_gizmos_manager().check_object_located_outside_plate(true);
plater()->align_selection_y_min();
},
"", nullptr,
[]() {
if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D)
return false;
auto canvas3d = plater()->get_view3D_canvas3D();
GLGizmoAlignment alignment_helper(*canvas3d);
return alignment_helper.can_align(GLGizmoAlignment::AlignType::Y_MIN);
},
m_parent);
append_menu_item(
align_distribute_menu, wxID_ANY, _L("Align front-back center") + " (Y)", "",
[](wxCommandEvent &) {
auto canvas3d = plater()->get_view3D_canvas3D();
canvas3d->get_gizmos_manager().check_object_located_outside_plate(true);
plater()->align_selection_y_center();
},
"", nullptr,
[]() {
if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D)
return false;
auto canvas3d = plater()->get_view3D_canvas3D();
GLGizmoAlignment alignment_helper(*canvas3d);
return alignment_helper.can_align(GLGizmoAlignment::AlignType::CENTER_Y);
},
m_parent);
append_menu_item(
align_distribute_menu, wxID_ANY, _L("Align back") + " (+Y)", "",
[](wxCommandEvent &) {
auto canvas3d = plater()->get_view3D_canvas3D();
canvas3d->get_gizmos_manager().check_object_located_outside_plate(true);
plater()->align_selection_y_max();
},
"", nullptr,
[]() {
if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D)
return false;
auto canvas3d = plater()->get_view3D_canvas3D();
GLGizmoAlignment alignment_helper(*canvas3d);
return alignment_helper.can_align(GLGizmoAlignment::AlignType::Y_MAX);
},
m_parent);
align_distribute_menu->AppendSeparator();
append_menu_item(
align_distribute_menu, wxID_ANY, _L("Align bottom") + " (-Z)", "",
[](wxCommandEvent &) {
auto canvas3d = plater()->get_view3D_canvas3D();
canvas3d->get_gizmos_manager().check_object_located_outside_plate(true);
plater()->align_selection_z_min();
},
"", nullptr,
[]() {
if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D)
return false;
auto canvas3d = plater()->get_view3D_canvas3D();
GLGizmoAlignment alignment_helper(*canvas3d);
return alignment_helper.can_align(GLGizmoAlignment::AlignType::Z_MIN);
},
m_parent);
append_menu_item(
align_distribute_menu, wxID_ANY, _L("Align top-bottom center") + " (Z)", "",
[](wxCommandEvent &) {
auto canvas3d = plater()->get_view3D_canvas3D();
canvas3d->get_gizmos_manager().check_object_located_outside_plate(true);
plater()->align_selection_z_center();
},
"", nullptr,
[]() {
if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D)
return false;
auto canvas3d = plater()->get_view3D_canvas3D();
GLGizmoAlignment alignment_helper(*canvas3d);
return alignment_helper.can_align(GLGizmoAlignment::AlignType::CENTER_Z);
},
m_parent);
append_menu_item(
align_distribute_menu, wxID_ANY, _L("Align top") + " (+Z)", "",
[](wxCommandEvent &) {
auto canvas3d = plater()->get_view3D_canvas3D();
canvas3d->get_gizmos_manager().check_object_located_outside_plate(true);
plater()->align_selection_z_max();
},
"", nullptr,
[]() {
if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D)
return false;
auto canvas3d = plater()->get_view3D_canvas3D();
GLGizmoAlignment alignment_helper(*canvas3d);
return alignment_helper.can_align(GLGizmoAlignment::AlignType::Z_MAX);
},
m_parent);
for (const auto &item : items) {
const AlignType type = item.second;
if (type == AlignType::NONE)
align_distribute_menu->AppendSeparator();
else
append_menu_item(
align_distribute_menu, wxID_ANY, item.first, "", [run, type](wxCommandEvent &) { run(type); }, "", nullptr,
[can, type]() { return can(type); }, m_parent);
}
// Anything that can be distributed can also be aligned, and aligning in X has no extra condition.
append_submenu(menu, align_distribute_menu, wxID_ANY, _L("Align/Distribute"), _L("Align and distribute objects"), "",
[]() {
if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D)
return false;
auto canvas3d = plater()->get_view3D_canvas3D();
GLGizmoAlignment alignment_helper(*canvas3d);
bool has_any_align = alignment_helper.can_align(GLGizmoAlignment::AlignType::CENTER_X) ||
alignment_helper.can_align(GLGizmoAlignment::AlignType::CENTER_Y) ||
alignment_helper.can_align(GLGizmoAlignment::AlignType::CENTER_Z) ||
alignment_helper.can_align(GLGizmoAlignment::AlignType::X_MAX) ||
alignment_helper.can_align(GLGizmoAlignment::AlignType::X_MIN) ||
alignment_helper.can_align(GLGizmoAlignment::AlignType::Y_MAX) ||
alignment_helper.can_align(GLGizmoAlignment::AlignType::Y_MIN) ||
alignment_helper.can_align(GLGizmoAlignment::AlignType::Z_MAX) ||
alignment_helper.can_align(GLGizmoAlignment::AlignType::Z_MIN);
bool has_any_distribute = alignment_helper.can_distribute(GLGizmoAlignment::AlignType::DISTRIBUTE_X) ||
alignment_helper.can_distribute(GLGizmoAlignment::AlignType::DISTRIBUTE_Y) ||
alignment_helper.can_distribute(GLGizmoAlignment::AlignType::DISTRIBUTE_Z);
return has_any_align || has_any_distribute;
}, m_parent);
[can]() { return can(AlignType::X_MIN); }, m_parent);
}
void MenuFactory::append_menu_item_drop(wxMenu* menu)
-31
View File
@@ -470,37 +470,6 @@ bool GLGizmosManager::check_gizmos_closed_except(EType type) const
return true;
}
// Switches the active plate to the one containing the first selected object,
// so that subsequent operations (align/distribute) act on a plate the user
// can actually see.
void GLGizmosManager::check_object_located_outside_plate(bool change_plate)
{
PartPlateList& plate_list = wxGetApp().plater()->get_partplate_list();
auto curr_plate_index = plate_list.get_curr_plate_index();
Selection& selection = m_parent.get_selection();
auto idxs = selection.get_volume_idxs();
if (idxs.empty())
return;
const GLVolume* v = selection.get_volume(*idxs.begin());
int object_idx = v->object_idx();
const Model* model = m_parent.get_model();
if (object_idx < 0 || object_idx >= (int) model->objects.size())
return;
ModelObject* model_object = model->objects[object_idx];
for (size_t i = 0; i < plate_list.get_plate_count(); ++i) {
auto plate = plate_list.get_plate(i);
for (auto object : plate->get_objects_on_this_plate()) {
if (model_object == object) {
if (change_plate && curr_plate_index != (int) i)
plate_list.select_plate(i);
return;
}
}
}
}
void GLGizmosManager::set_hover_id(int id)
{
// Measure and assembly handles hover by itself
@@ -261,7 +261,6 @@ public:
void reset_all_states();
bool open_gizmo(EType type);
bool check_gizmos_closed_except(EType) const;
void check_object_located_outside_plate(bool change_plate = true);
void set_hover_id(int id);
-97
View File
@@ -245,7 +245,6 @@
#include "Widgets/MultiNozzleSync.hpp" // NozzleOption, tryPopUpMultiNozzleDialog, setExtruderNozzleCount
#include "DeviceCore/DevNozzleSystem.h" // DevNozzle, GetExtNozzles / GetRackNozzles
#include "ProjectDirtyStateManager.hpp"
#include "Gizmos/GLGizmoAlignment.hpp"
#include "Gizmos/GLGizmoSimplify.hpp" // create suggestion notification
#include "Gizmos/GLGizmoSVG.hpp" // Drop SVG file
#include "Gizmos/GizmoObjectManipulation.hpp"
@@ -7485,18 +7484,6 @@ struct Plater::priv
void delete_all_objects_from_model();
void reset(bool apply_presets_change = false, bool reload_presets = true);
void center_selection();
void distribute_selection_x();
void distribute_selection_y();
void distribute_selection_z();
void align_selection_x_min();
void align_selection_x_center();
void align_selection_x_max();
void align_selection_y_min();
void align_selection_y_center();
void align_selection_y_max();
void align_selection_z_min();
void align_selection_z_center();
void align_selection_z_max();
void drop_selection();
void mirror(Axis axis);
void split_object(bool auto_drop = true);
@@ -10926,78 +10913,6 @@ void Plater::priv::center_selection()
view3D->center_selected();
}
void Plater::priv::distribute_selection_x()
{
GLGizmoAlignment alignment_helper(*view3D->get_canvas3d());
alignment_helper.distribute_objects(GLGizmoAlignment::AlignType::DISTRIBUTE_X);
}
void Plater::priv::distribute_selection_y()
{
GLGizmoAlignment alignment_helper(*view3D->get_canvas3d());
alignment_helper.distribute_objects(GLGizmoAlignment::AlignType::DISTRIBUTE_Y);
}
void Plater::priv::distribute_selection_z()
{
GLGizmoAlignment alignment_helper(*view3D->get_canvas3d());
alignment_helper.distribute_objects(GLGizmoAlignment::AlignType::DISTRIBUTE_Z);
}
void Plater::priv::align_selection_x_min()
{
GLGizmoAlignment alignment_helper(*view3D->get_canvas3d());
alignment_helper.align_objects(GLGizmoAlignment::AlignType::X_MIN);
}
void Plater::priv::align_selection_x_center()
{
GLGizmoAlignment alignment_helper(*view3D->get_canvas3d());
alignment_helper.align_objects(GLGizmoAlignment::AlignType::CENTER_X);
}
void Plater::priv::align_selection_x_max()
{
GLGizmoAlignment alignment_helper(*view3D->get_canvas3d());
alignment_helper.align_objects(GLGizmoAlignment::AlignType::X_MAX);
}
void Plater::priv::align_selection_y_min()
{
GLGizmoAlignment alignment_helper(*view3D->get_canvas3d());
alignment_helper.align_objects(GLGizmoAlignment::AlignType::Y_MIN);
}
void Plater::priv::align_selection_y_center()
{
GLGizmoAlignment alignment_helper(*view3D->get_canvas3d());
alignment_helper.align_objects(GLGizmoAlignment::AlignType::CENTER_Y);
}
void Plater::priv::align_selection_y_max()
{
GLGizmoAlignment alignment_helper(*view3D->get_canvas3d());
alignment_helper.align_objects(GLGizmoAlignment::AlignType::Y_MAX);
}
void Plater::priv::align_selection_z_min()
{
GLGizmoAlignment alignment_helper(*view3D->get_canvas3d());
alignment_helper.align_objects(GLGizmoAlignment::AlignType::Z_MIN);
}
void Plater::priv::align_selection_z_center()
{
GLGizmoAlignment alignment_helper(*view3D->get_canvas3d());
alignment_helper.align_objects(GLGizmoAlignment::AlignType::CENTER_Z);
}
void Plater::priv::align_selection_z_max()
{
GLGizmoAlignment alignment_helper(*view3D->get_canvas3d());
alignment_helper.align_objects(GLGizmoAlignment::AlignType::Z_MAX);
}
void Plater::priv::drop_selection()
{
view3D->drop_selected();
@@ -22311,18 +22226,6 @@ void Plater::suppress_background_process(const bool stop_background_process)
BackgroundSlicingProcess& Plater::background_process() { return p->background_process; }
void Plater::center_selection() { p->center_selection(); }
void Plater::distribute_selection_x() { p->distribute_selection_x(); }
void Plater::distribute_selection_y() { p->distribute_selection_y(); }
void Plater::distribute_selection_z() { p->distribute_selection_z(); }
void Plater::align_selection_x_min() { p->align_selection_x_min(); }
void Plater::align_selection_x_center() { p->align_selection_x_center(); }
void Plater::align_selection_x_max() { p->align_selection_x_max(); }
void Plater::align_selection_y_min() { p->align_selection_y_min(); }
void Plater::align_selection_y_center() { p->align_selection_y_center(); }
void Plater::align_selection_y_max() { p->align_selection_y_max(); }
void Plater::align_selection_z_min() { p->align_selection_z_min(); }
void Plater::align_selection_z_center() { p->align_selection_z_center(); }
void Plater::align_selection_z_max() { p->align_selection_z_max(); }
void Plater::drop_selection() { p->drop_selection(); }
void Plater::mirror(Axis axis) { p->mirror(axis); }
void Plater::split_object(bool auto_drop) { p->split_object(auto_drop); }
-12
View File
@@ -762,18 +762,6 @@ public:
//BBS: add clone logic
void clone_selection();
void center_selection();
void distribute_selection_x();
void distribute_selection_y();
void distribute_selection_z();
void align_selection_x_min();
void align_selection_x_center();
void align_selection_x_max();
void align_selection_y_min();
void align_selection_y_center();
void align_selection_y_max();
void align_selection_z_min();
void align_selection_z_center();
void align_selection_z_max();
void drop_selection();
void search(bool plater_is_active, Preset::Type type, wxWindow *tag, TextInput *etag, wxWindow *stag);
void mirror(Axis axis);