mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-20 19:41:40 -04:00
fix(desktop): pick up a drawing on mouse down in the parts list
Choosing a part from the editor's drawing list armed the pickup only in Click. Moving the mouse more than 3 px before release started a drag-and-drop, which takes mouse capture, so the list never raised Click and nothing attached to the cursor on the plate. The list now raises DrawingPickedUp on a single left press over a row and the editor arms the pickup there. Dragging onto the plate still drops a part at the release point; a completed drop clears the pickup so a second copy does not follow the cursor. Presses below the last row, right presses and the second press of a double-click do not pick up.
This commit is contained in:
1 parent
720c3d9b7a
commit
8834abc854
4 files changed
+208
-6
No files matched your search
@@ -0,0 +1,148 @@
|
||||
using System.Drawing;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
using OpenNest.Actions;
|
||||
using OpenNest.CNC;
|
||||
using OpenNest.Controls;
|
||||
using OpenNest.Forms;
|
||||
|
||||
namespace OpenNest.WinForms.Tests.Controls;
|
||||
|
||||
[Collection("Fill operation lifetime")]
|
||||
public class DrawingListPickupTests
|
||||
{
|
||||
private const int WM_LBUTTONDOWN = 0x0201;
|
||||
private const int WM_LBUTTONUP = 0x0202;
|
||||
private const int MK_LBUTTON = 0x0001;
|
||||
|
||||
[Fact]
|
||||
public void PressingADrawingRowPicksItUpWithoutWaitingForClick() => RunSta(() =>
|
||||
{
|
||||
var (form, list, second) = CreateEditor();
|
||||
using (form)
|
||||
{
|
||||
var center = RowCenter(list, list.Items.IndexOf(second));
|
||||
|
||||
// Only the press: a mouse that moves before release never produces Click.
|
||||
SendMessage(list.Handle, WM_LBUTTONDOWN, MK_LBUTTON, MakeLParam(center));
|
||||
try
|
||||
{
|
||||
EnterPlateView(form);
|
||||
|
||||
var clone = Assert.IsType<ActionClone>(form.PlateView.Actions.CurrentAction);
|
||||
Assert.Same(second, ClonedDrawing(clone));
|
||||
}
|
||||
finally
|
||||
{
|
||||
SendMessage(list.Handle, WM_LBUTTONUP, 0, MakeLParam(center));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
[Fact]
|
||||
public void CompletedDragDropDoesNotAttachASecondCopy() => RunSta(() =>
|
||||
{
|
||||
var (form, list, second) = CreateEditor();
|
||||
using (form)
|
||||
{
|
||||
var center = RowCenter(list, list.Items.IndexOf(second));
|
||||
SendMessage(list.Handle, WM_LBUTTONDOWN, MK_LBUTTON, MakeLParam(center));
|
||||
SendMessage(list.Handle, WM_LBUTTONUP, 0, MakeLParam(center));
|
||||
|
||||
typeof(EditNestForm)
|
||||
.GetMethod("drawingListBox1_DrawingDropped", BindingFlags.Instance | BindingFlags.NonPublic)!
|
||||
.Invoke(form, [list, second]);
|
||||
EnterPlateView(form);
|
||||
|
||||
Assert.IsType<ActionSelect>(form.PlateView.Actions.CurrentAction);
|
||||
}
|
||||
});
|
||||
|
||||
[Theory]
|
||||
[InlineData(MouseButtons.Right, 1, false)]
|
||||
[InlineData(MouseButtons.Left, 2, false)]
|
||||
[InlineData(MouseButtons.Left, 1, true)]
|
||||
public void OnlyASingleLeftPressOnARowPicksUp(MouseButtons button, int clicks, bool belowRows) =>
|
||||
RunSta(() =>
|
||||
{
|
||||
using var list = new PressableDrawingListBox { Size = new Size(260, 400) };
|
||||
var drawing = new Drawing("only", Square());
|
||||
list.Items.Add(drawing);
|
||||
_ = list.Handle;
|
||||
var picked = new List<Drawing>();
|
||||
list.DrawingPickedUp += (_, d) => picked.Add(d);
|
||||
|
||||
var point = belowRows
|
||||
? new Point(20, list.GetItemRectangle(0).Bottom + 20)
|
||||
: RowCenter(list, 0);
|
||||
list.Press(new MouseEventArgs(button, clicks, point.X, point.Y, 0));
|
||||
|
||||
Assert.Empty(picked);
|
||||
|
||||
list.Press(new MouseEventArgs(MouseButtons.Left, 1, RowCenter(list, 0).X, RowCenter(list, 0).Y, 0));
|
||||
Assert.Same(drawing, Assert.Single(picked));
|
||||
});
|
||||
|
||||
private static (EditNestForm Form, DrawingListBox List, Drawing Second) CreateEditor()
|
||||
{
|
||||
var nest = new Nest("pickup");
|
||||
nest.Drawings.Add(new Drawing("a", Square()));
|
||||
var second = new Drawing("b", Square());
|
||||
nest.Drawings.Add(second);
|
||||
var form = new EditNestForm(nest) { Width = 1100, Height = 700 };
|
||||
form.PlateView.SetOverlapAutoCheck(null);
|
||||
form.Show();
|
||||
Application.DoEvents();
|
||||
|
||||
var list = (DrawingListBox)typeof(EditNestForm)
|
||||
.GetField("drawingListBox1", BindingFlags.Instance | BindingFlags.NonPublic)!
|
||||
.GetValue(form)!;
|
||||
Assert.Equal(2, list.Items.Count);
|
||||
Assert.IsType<ActionSelect>(form.PlateView.Actions.CurrentAction);
|
||||
return (form, list, second);
|
||||
}
|
||||
|
||||
private static void EnterPlateView(EditNestForm form) =>
|
||||
typeof(EditNestForm)
|
||||
.GetMethod("PlateView_Enter", BindingFlags.Instance | BindingFlags.NonPublic)!
|
||||
.Invoke(form, [form.PlateView, EventArgs.Empty]);
|
||||
|
||||
private static Drawing ClonedDrawing(ActionClone clone)
|
||||
{
|
||||
var parts = (List<Part>)typeof(ActionClone)
|
||||
.GetField("sourceParts", BindingFlags.Instance | BindingFlags.NonPublic)!
|
||||
.GetValue(clone)!;
|
||||
return Assert.Single(parts).BaseDrawing;
|
||||
}
|
||||
|
||||
private static Point RowCenter(ListBox list, int index)
|
||||
{
|
||||
var row = list.GetItemRectangle(index);
|
||||
return new Point(row.Left + row.Width / 2, row.Top + row.Height / 2);
|
||||
}
|
||||
|
||||
private static IntPtr MakeLParam(Point point) => (IntPtr)((point.Y << 16) | (point.X & 0xFFFF));
|
||||
|
||||
private static Program Square()
|
||||
{
|
||||
var program = new Program();
|
||||
program.MoveTo(0, 0);
|
||||
program.LineTo(0, 10);
|
||||
program.LineTo(10, 10);
|
||||
program.LineTo(10, 0);
|
||||
program.LineTo(0, 0);
|
||||
return program;
|
||||
}
|
||||
|
||||
private static void RunSta(System.Action action) =>
|
||||
StaTestThread.Run(action, TimeSpan.FromMinutes(3), "The STA test did not complete.");
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, IntPtr lParam);
|
||||
|
||||
private sealed class PressableDrawingListBox : DrawingListBox
|
||||
{
|
||||
public void Press(MouseEventArgs e) => OnMouseDown(e);
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ namespace OpenNest.Controls
|
||||
private readonly Size imageSize;
|
||||
private readonly Font nameFont;
|
||||
private Point lastClickLocation;
|
||||
private Drawing pressedDrawing;
|
||||
|
||||
public DrawingListBox()
|
||||
{
|
||||
@@ -39,6 +40,16 @@ namespace OpenNest.Controls
|
||||
|
||||
public event EventHandler<Drawing> DeleteRequested;
|
||||
|
||||
/// <summary>
|
||||
/// Raised when the left button is pressed on a drawing's row.
|
||||
/// </summary>
|
||||
public event EventHandler<Drawing> DrawingPickedUp;
|
||||
|
||||
/// <summary>
|
||||
/// Raised when a drawing dragged out of the list was accepted by a drop target.
|
||||
/// </summary>
|
||||
public event EventHandler<Drawing> DrawingDropped;
|
||||
|
||||
public Units Units { get; set; }
|
||||
|
||||
public bool HideDepletedParts { get; set; }
|
||||
@@ -152,19 +163,53 @@ namespace OpenNest.Controls
|
||||
{
|
||||
base.OnMouseMove(e);
|
||||
|
||||
var item = SelectedItem as Drawing;
|
||||
var drawing = pressedDrawing;
|
||||
|
||||
if (item == null)
|
||||
if (drawing == null || e.Button != MouseButtons.Left)
|
||||
return;
|
||||
|
||||
if (e.Button == MouseButtons.Left && e.Location.DistanceTo(lastClickLocation) > 3)
|
||||
DoDragDrop(item, DragDropEffects.Copy);
|
||||
if (e.Location.DistanceTo(lastClickLocation) <= 3)
|
||||
return;
|
||||
|
||||
pressedDrawing = null;
|
||||
|
||||
if (DoDragDrop(drawing, DragDropEffects.Copy) != DragDropEffects.None)
|
||||
DrawingDropped?.Invoke(this, drawing);
|
||||
}
|
||||
|
||||
protected override void OnMouseDown(MouseEventArgs e)
|
||||
{
|
||||
base.OnMouseDown(e);
|
||||
lastClickLocation = e.Location;
|
||||
|
||||
// Pick the part up on the press. Waiting for Click loses the pickup
|
||||
// whenever the mouse moves a few pixels first: the drag that starts
|
||||
// takes mouse capture and the list never raises Click.
|
||||
pressedDrawing =
|
||||
e.Button == MouseButtons.Left && e.Clicks == 1 ? DrawingAt(e.Location) : null;
|
||||
|
||||
if (pressedDrawing != null)
|
||||
DrawingPickedUp?.Invoke(this, pressedDrawing);
|
||||
}
|
||||
|
||||
protected override void OnMouseUp(MouseEventArgs e)
|
||||
{
|
||||
pressedDrawing = null;
|
||||
base.OnMouseUp(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The drawing whose row contains <paramref name="location"/>, or null
|
||||
/// when the point is in the empty area below the last row.
|
||||
/// </summary>
|
||||
internal Drawing DrawingAt(Point location)
|
||||
{
|
||||
var index = IndexFromPoint(location);
|
||||
|
||||
if (index < 0 || index >= Items.Count)
|
||||
return null;
|
||||
|
||||
return GetItemRectangle(index).Contains(location) ? Items[index] as Drawing : null;
|
||||
}
|
||||
|
||||
protected override void WndProc(ref Message m)
|
||||
|
||||
Generated
-1
@@ -213,7 +213,6 @@
|
||||
drawingListBox1.Size = new System.Drawing.Size(265, 394);
|
||||
drawingListBox1.TabIndex = 1;
|
||||
drawingListBox1.Units = Units.Inches;
|
||||
drawingListBox1.Click += drawingListBox1_Click;
|
||||
drawingListBox1.DoubleClick += drawingListBox1_DoubleClick;
|
||||
//
|
||||
// toolStrip2
|
||||
|
||||
@@ -432,6 +432,8 @@ namespace OpenNest.Forms
|
||||
Text = Nest.Name;
|
||||
drawingListBox1.Units = Nest.Units;
|
||||
drawingListBox1.DeleteRequested += drawingListBox1_DeleteRequested;
|
||||
drawingListBox1.DrawingPickedUp += drawingListBox1_DrawingPickedUp;
|
||||
drawingListBox1.DrawingDropped += drawingListBox1_DrawingDropped;
|
||||
}
|
||||
|
||||
public void UpdatePlateList()
|
||||
@@ -1423,11 +1425,19 @@ namespace OpenNest.Forms
|
||||
PlateView.Invalidate();
|
||||
}
|
||||
|
||||
private void drawingListBox1_Click(object sender, EventArgs e)
|
||||
private void drawingListBox1_DrawingPickedUp(object sender, Drawing drawing)
|
||||
{
|
||||
// Armed on the press, so moving the mouse before release still
|
||||
// attaches the part when the pointer reaches the plate.
|
||||
addPart = true;
|
||||
}
|
||||
|
||||
private void drawingListBox1_DrawingDropped(object sender, Drawing drawing)
|
||||
{
|
||||
// The drop already placed the part; don't attach another copy.
|
||||
addPart = false;
|
||||
}
|
||||
|
||||
private void PlateView_MouseEnter(object sender, EventArgs e)
|
||||
{
|
||||
if (!PlateView.Focused)
|
||||
|
||||
Reference in new issue
Block a user