Files
OpenNest/OpenNest/Actions/ActionSelect.cs
AJ Isaacs 2ed02c2dae feat: show selected part bounding box in status bar
Add SelectionChanged event to PlateView and display the selected part's
location and size in a new status bar label. Shows combined bounding box
when multiple parts are selected.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 09:38:48 -04:00

257 lines
7.0 KiB
C#

using OpenNest.Controls;
using OpenNest.Geometry;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace OpenNest.Actions
{
[DisplayName("Select")]
public class ActionSelect : Action
{
private readonly Pen borderPenContain;
private readonly Pen borderPenIntersect;
private readonly Brush fillBrushContain;
private readonly Brush fillBrushIntersect;
private Pen borderPen;
private Brush fillBrush;
public Vector Point1;
public Vector Point2;
private Status status;
public ActionSelect(PlateView plateView)
: base(plateView)
{
borderPenIntersect = new Pen(Color.FromArgb(50, 255, 50));
fillBrushIntersect = new SolidBrush(Color.FromArgb(50, 50, 255, 50));
borderPenContain = new Pen(Color.FromArgb(99, 162, 228));
fillBrushContain = new SolidBrush(Color.FromArgb(90, 150, 200, 255));
UpdateBrushAndPen();
status = Status.SetFirstPoint;
plateView.MouseDown += plateView_MouseDown;
plateView.MouseUp += plateView_MouseUp;
plateView.MouseMove += plateView_MouseMove;
plateView.Paint += plateView_Paint;
}
public override void DisconnectEvents()
{
plateView.MouseDown -= plateView_MouseDown;
plateView.MouseUp -= plateView_MouseUp;
plateView.MouseMove -= plateView_MouseMove;
plateView.Paint -= plateView_Paint;
}
public override void CancelAction()
{
status = Status.SetFirstPoint;
plateView.DeselectAll();
plateView.Invalidate();
}
public override bool IsBusy()
{
return status != Status.SetFirstPoint || plateView.SelectedParts.Count > 0;
}
private void plateView_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left)
return;
if (status == Status.SetSecondPoint)
{
Point2 = plateView.PointControlToWorld(e.Location);
UpdateBrushAndPen();
}
else
{
if (plateView.SelectedParts.Count == 0)
return;
var offset = plateView.CurrentPoint - plateView.LastPoint;
foreach (var part in plateView.SelectedParts)
part.Offset(offset.X, offset.Y);
}
plateView.Invalidate();
}
private void plateView_MouseDown(object sender, MouseEventArgs e)
{
if (!plateView.AllowSelect)
return;
if (e.Button == MouseButtons.Left)
{
if (plateView.SelectedParts.Count > 0)
{
var part = plateView.GetPartAtControlPoint(e.Location);
if (part == null)
CancelAction();
}
if (SelectPartAtCurrentPoint())
return;
if (status == Status.SetFirstPoint)
{
Point1 = plateView.PointControlToWorld(e.Location);
Point2 = Point1;
status = Status.SetSecondPoint;
}
}
else if (e.Button == MouseButtons.Right)
{
CancelAction();
}
}
private void plateView_MouseUp(object sender, MouseEventArgs e)
{
if (!plateView.AllowSelect)
return;
if (e.Button != MouseButtons.Left)
return;
if (status == Status.SetSecondPoint)
{
Point2 = plateView.PointControlToWorld(e.Location);
SelectPartsInWindow();
plateView.Invalidate();
status = Status.SetFirstPoint;
}
}
private void plateView_Paint(object sender, PaintEventArgs e)
{
if (status != Status.SetSecondPoint)
return;
var rect = GetRectangle();
e.Graphics.FillRectangle(fillBrush, rect);
e.Graphics.DrawRectangle(borderPen,
rect.X,
rect.Y,
rect.Width,
rect.Height);
}
private bool SelectPartAtCurrentPoint()
{
var part = plateView.GetPartAtPoint(plateView.CurrentPoint);
if (part == null)
{
plateView.DeselectAll();
status = Status.SetFirstPoint;
return false;
}
if (Control.ModifierKeys != Keys.Control && plateView.SelectedParts.Contains(part) == false)
plateView.DeselectAll();
if (plateView.SelectedParts.Contains(part) == false)
{
plateView.SelectedParts.Add(part);
part.IsSelected = true;
}
plateView.NotifySelectionChanged();
plateView.Invalidate();
return true;
}
private void SelectPartsInWindow()
{
var parts = plateView.GetPartsFromWindow(GetRectangle(), SelectionType);
foreach (var part in parts)
{
if (plateView.SelectedParts.Contains(part))
continue;
plateView.SelectedParts.Add(part);
part.IsSelected = true;
}
plateView.NotifySelectionChanged();
}
private void UpdateBrushAndPen()
{
if (SelectionType == SelectionType.Intersect)
{
borderPen = borderPenIntersect;
fillBrush = fillBrushIntersect;
}
else
{
borderPen = borderPenContain;
fillBrush = fillBrushContain;
}
}
private RectangleF GetRectangle()
{
var rect = new RectangleF();
var pt1 = plateView.PointWorldToGraph(Point1);
var pt2 = plateView.PointWorldToGraph(Point2);
if (pt1.X < pt2.X)
{
rect.X = pt1.X;
rect.Width = pt2.X - pt1.X;
}
else
{
rect.X = pt2.X;
rect.Width = pt1.X - pt2.X;
}
if (pt1.Y < pt2.Y)
{
rect.Y = pt1.Y;
rect.Height = pt2.Y - pt1.Y;
}
else
{
rect.Y = pt2.Y;
rect.Height = pt1.Y - pt2.Y;
}
return rect;
}
public SelectionType SelectionType
{
get
{
return Point1.X < Point2.X
? SelectionType.Contains
: SelectionType.Intersect;
}
}
public enum Status
{
SetFirstPoint,
SetSecondPoint
}
}
}