Rulers: confine box/ellipse to square/circle with Ctrl, center box/ellipse with Ctrl - same as for drawing boxes

This commit is contained in:
Matthias Koefferlein 2026-01-18 21:12:26 +01:00
parent 18c2f5dfa4
commit ddee74ab78
4 changed files with 143 additions and 14 deletions

View File

@ -50,6 +50,10 @@ ToolkitWidget::ToolkitWidget (lay::LayoutViewBase *view, lay::Dispatcher *dispat
mp_y_le->set_label ("dy:");
mp_layout->addWidget (mp_y_le);
mp_d_le = new lay::DecoratedLineEdit (this);
mp_d_le->set_label ("d:");
mp_layout->addWidget (mp_d_le);
mp_layout->addStretch (1);
hide ();
@ -86,12 +90,24 @@ ToolkitWidget::commit (lay::Dispatcher *dispatcher)
{
try {
double dx = 0.0, dy = 0.0;
if (mp_d_le->hasFocus ()) {
tl::from_string (tl::to_string (mp_x_le->text ()), dx);
tl::from_string (tl::to_string (mp_y_le->text ()), dy);
double d = 0.0;
dispatcher->call_function (ant::Service::function_name (), db::DVector (dx, dy).to_string ());
tl::from_string (tl::to_string (mp_d_le->text ()), d);
dispatcher->call_function (ant::Service::d_function_name (), tl::to_string (d));
} else {
double dx = 0.0, dy = 0.0;
tl::from_string (tl::to_string (mp_x_le->text ()), dx);
tl::from_string (tl::to_string (mp_y_le->text ()), dy);
dispatcher->call_function (ant::Service::xy_function_name (), db::DVector (dx, dy).to_string ());
}
} catch (...) {
}
@ -100,7 +116,7 @@ ToolkitWidget::commit (lay::Dispatcher *dispatcher)
void
ToolkitWidget::configure (const std::string &name, const std::string &value)
{
if (name == ant::Service::configure_name () && ! mp_x_le->hasFocus () && ! mp_y_le->hasFocus ()) {
if (name == ant::Service::xy_configure_name () && ! mp_x_le->hasFocus () && ! mp_y_le->hasFocus ()) {
try {
@ -113,6 +129,18 @@ ToolkitWidget::configure (const std::string &name, const std::string &value)
} catch (...) {
}
} else if (name == ant::Service::d_configure_name () && ! mp_x_le->hasFocus () && ! mp_y_le->hasFocus ()) {
try {
double d;
tl::from_string (value, d);
mp_d_le->setText (tl::to_qstring (tl::micron_to_string (d)));
} catch (...) {
}
}
}

View File

@ -58,7 +58,7 @@ public:
private:
QHBoxLayout *mp_layout;
lay::DecoratedLineEdit *mp_x_le, *mp_y_le;
lay::DecoratedLineEdit *mp_x_le, *mp_y_le, *mp_d_le;
};
}

View File

@ -1043,8 +1043,10 @@ View::render (const lay::Viewport &vp, lay::ViewObjectCanvas &canvas)
// ant::Service implementation
const char *Service::editor_options_name () { return "ant-toolkit-widget-name"; }
const char *Service::configure_name () { return "ant-toolkit-widget-value"; }
const char *Service::function_name () { return "ant-toolkit-widget-commit"; }
const char *Service::xy_configure_name () { return "ant-toolkit-widget-xy-value"; }
const char *Service::d_configure_name () { return "ant-toolkit-widget-d-value"; }
const char *Service::xy_function_name () { return "ant-toolkit-widget-xy-commit"; }
const char *Service::d_function_name () { return "ant-toolkit-widget-d-commit"; }
Service::Service (db::Manager *manager, lay::LayoutViewBase *view)
: lay::EditorServiceBase (view),
@ -1061,6 +1063,9 @@ Service::Service (db::Manager *manager, lay::LayoutViewBase *view)
m_drawing (false), m_current (),
m_move_mode (MoveNone),
m_seg_index (0),
m_length_confined (false),
m_length (0.0),
m_centered (false),
m_current_template (0),
m_hover (false),
m_hover_wait (false),
@ -1906,6 +1911,7 @@ Service::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio
// cancel any edit operations so far
m_move_mode = MoveNone;
m_length_confined = false;
// reset selection
clear_selection ();
@ -2023,6 +2029,7 @@ Service::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio
pts.push_back (m_p1);
m_current.set_points_exact (pts);
m_length_confined = false;
}
@ -2058,7 +2065,7 @@ Service::create_measure_ruler (const db::DPoint &pt, lay::angle_constraint_type
void
Service::function (const std::string &name, const std::string &value)
{
if (name == function_name ()) {
if (name == xy_function_name ()) {
try {
@ -2109,6 +2116,59 @@ Service::function (const std::string &name, const std::string &value)
} catch (...) {
}
} else if (name == d_function_name ()) {
try {
double s = 0.0;
tl::from_string (value, s);
if (m_drawing) {
m_length_confined = true;
m_length = s;
ant::Object::point_list pts = m_current.points ();
confine_length (pts);
m_current.set_points_exact (pts);
}
} catch (...) {
}
}
}
void
Service::confine_length (ant::Object::point_list &pts)
{
if (m_length_confined && pts.size () >= 2) {
const ant::Template &tpl = current_template ();
bool is_box_style = (tpl.outline () == ant::Object::OL_box || tpl.outline () == ant::Object::OL_ellipse);
db::DPoint p1 = m_centered ? m_p1 : pts [pts.size () - 2];
db::DVector s = pts.back () - p1;
if (is_box_style) {
db::DVector snew = s;
double l = m_centered ? m_length * 0.5 : m_length;
if (fabs (s.x ()) < fabs (s.y ()) + db::epsilon) {
snew.set_y (l * (s.y () < 0 ? -1.0 : 1.0));
}
if (fabs (s.y ()) < fabs (s.x ()) + db::epsilon) {
snew.set_x (l * (s.x () < 0 ? -1.0 : 1.0));
}
s = snew;
} else {
double l = s.double_length ();
if (l > db::epsilon) {
s *= m_length / l;
}
}
pts.back () = p1 + s;
}
}
@ -2131,11 +2191,31 @@ Service::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio)
}
const ant::Template &tpl = current_template ();
// for normal rulers with box or ellipse rendering we use a different button scheme:
// Shift will keep the center, Ctrl will confine the box to a square/ellipse to a circle
bool snap_square = false;
bool is_box_style = (tpl.outline () == ant::Object::OL_box || tpl.outline () == ant::Object::OL_ellipse);
if (tpl.mode () == ant::Template::RulerNormal && is_box_style) {
snap_square = (buttons & lay::ControlButton) != 0;
m_centered = (buttons & lay::ShiftButton) != 0;
} else {
m_centered = false;
}
lay::PointSnapToObjectResult snap_details;
if (m_drawing) {
snap_details = snap2_details (m_p1, p, mp_active_ruler->ruler (), ac_from_buttons (buttons));
lay::angle_constraint_type ac;
if (snap_square) {
ac = lay::AC_DiagonalOnly;
} else if (is_box_style) {
ac = lay::AC_Any;
} else {
ac = ac_from_buttons (buttons);
}
snap_details = snap2_details (m_p1, p, mp_active_ruler->ruler (), ac);
} else {
const ant::Template &tpl = current_template ();
snap_details = snap1_details (p, m_obj_snap && tpl.snap () && (tpl.mode () != ant::Template::RulerAutoMetricEdge || ! view ()->transient_selection_mode ()));
}
@ -2149,8 +2229,19 @@ Service::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio)
// otherwise we risk manipulating p1 too.
ant::Object::point_list pts = m_current.points ();
if (! pts.empty ()) {
pts.back () = snap_details.snapped_point;
confine_length (pts);
if (m_centered) {
pts.front () = m_p1 - (pts.back () - m_p1);
} else {
pts.front () = m_p1;
}
}
m_current.set_points_exact (pts);
db::DVector delta;
@ -2161,7 +2252,9 @@ Service::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio)
lay::EditorOptionsPage *tb = toolbox_widget ();
if (tb) {
tb->configure (configure_name (), delta.to_string ());
double d = is_box_style ? std::min (fabs (delta.x ()), fabs (delta.y ())) : delta.length ();
tb->configure (xy_configure_name (), delta.to_string ());
tb->configure (d_configure_name (), tl::to_string (d));
}
mp_active_ruler->redraw ();

View File

@ -199,8 +199,10 @@ public:
// for communicating with the toolbox widget
static const char *editor_options_name ();
static const char *configure_name ();
static const char *function_name ();
static const char *xy_configure_name ();
static const char *d_configure_name ();
static const char *xy_function_name ();
static const char *d_function_name ();
/**
* The current move mode:
@ -598,6 +600,11 @@ private:
MoveMode m_move_mode;
// The currently moving segment
size_t m_seg_index;
// When set to true, the length is confined to the value given by m_length
bool m_length_confined;
double m_length;
// When set to true, the last point was established in centered fashion
bool m_centered;
// The ruler template
std::vector<ant::Template> m_ruler_templates;
unsigned int m_current_template;
@ -618,6 +625,7 @@ private:
db::DPoint snap2_visual (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *obj, lay::angle_constraint_type ac);
lay::PointSnapToObjectResult snap2_details (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *obj, lay::angle_constraint_type ac);
lay::TwoPointSnapToObjectResult auto_measure (const db::DPoint &p, lay::angle_constraint_type ac, const ant::Template &tpl);
void confine_length (ant::Object::point_list &pts);
const ant::Template &current_template () const;