mirror of
https://github.com/ckaczor/Common.Wpf.git
synced 2026-02-16 10:48:30 -05:00
Improvements to window snapping on move and resize
This commit is contained in:
@@ -21,6 +21,55 @@ namespace Common.Wpf.Windows
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
private class WindowBorderRectangles
|
||||||
|
{
|
||||||
|
public WindowBorderRectangles(Rectangle windowPosition, int width, bool inside)
|
||||||
|
{
|
||||||
|
var offset = inside ? width : 0;
|
||||||
|
var actualWidth = inside ? width * 2 : width;
|
||||||
|
|
||||||
|
Top = new Rectangle(windowPosition.Left, windowPosition.Top - offset, windowPosition.Width, actualWidth);
|
||||||
|
Bottom = new Rectangle(windowPosition.Left, windowPosition.Bottom - offset, windowPosition.Width, actualWidth);
|
||||||
|
Left = new Rectangle(windowPosition.Left - offset, windowPosition.Top, actualWidth, windowPosition.Height);
|
||||||
|
Right = new Rectangle(windowPosition.Right - offset, windowPosition.Top, actualWidth, windowPosition.Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WindowBorderRectangles(Structures.WindowPosition windowPosition, int width, bool inside)
|
||||||
|
{
|
||||||
|
var offset = inside ? width : 0;
|
||||||
|
var actualWidth = inside ? width * 2 : width;
|
||||||
|
|
||||||
|
Top = new Rectangle(windowPosition.Left, windowPosition.Top - offset, windowPosition.Width, actualWidth);
|
||||||
|
Bottom = new Rectangle(windowPosition.Left, windowPosition.Bottom - offset, windowPosition.Width, actualWidth);
|
||||||
|
Left = new Rectangle(windowPosition.Left - offset, windowPosition.Top, actualWidth, windowPosition.Height);
|
||||||
|
Right = new Rectangle(windowPosition.Right - offset, windowPosition.Top, actualWidth, windowPosition.Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rectangle Top { get; private set; }
|
||||||
|
public Rectangle Bottom { get; private set; }
|
||||||
|
public Rectangle Left { get; private set; }
|
||||||
|
public Rectangle Right { get; private set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ResizeSide
|
||||||
|
{
|
||||||
|
public ResizeSide(Structures.WindowPosition position1, Structures.WindowPosition position2)
|
||||||
|
{
|
||||||
|
if (position1.IsSameSize(position2))
|
||||||
|
return;
|
||||||
|
|
||||||
|
IsTop = (position1.Top != position2.Top);
|
||||||
|
IsBottom = (position1.Bottom != position2.Bottom);
|
||||||
|
IsLeft = (position1.Left != position2.Left);
|
||||||
|
IsRight = (position1.Right != position2.Right);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsTop { get; private set; }
|
||||||
|
public bool IsBottom { get; private set; }
|
||||||
|
public bool IsLeft { get; private set; }
|
||||||
|
public bool IsRight { get; private set; }
|
||||||
|
}
|
||||||
|
|
||||||
#region Enumerations
|
#region Enumerations
|
||||||
|
|
||||||
private enum SnapMode
|
private enum SnapMode
|
||||||
@@ -62,20 +111,6 @@ namespace Common.Wpf.Windows
|
|||||||
_hwndSource.AddHook(WndProc);
|
_hwndSource.AddHook(WndProc);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnContentRendered(EventArgs e)
|
|
||||||
{
|
|
||||||
base.OnContentRendered(e);
|
|
||||||
|
|
||||||
// Initialize the last window position
|
|
||||||
_lastWindowPosition = new Structures.WindowPosition
|
|
||||||
{
|
|
||||||
Left = (int) Left,
|
|
||||||
Width = (int) Width,
|
|
||||||
Height = (int) Height,
|
|
||||||
Top = (int) Top
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnClosed(EventArgs e)
|
protected override void OnClosed(EventArgs e)
|
||||||
{
|
{
|
||||||
base.OnClosed(e);
|
base.OnClosed(e);
|
||||||
@@ -91,12 +126,31 @@ namespace Common.Wpf.Windows
|
|||||||
|
|
||||||
protected virtual IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
|
protected virtual IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
|
||||||
{
|
{
|
||||||
if (msg == (int) Constants.WindowMessage.WindowPositionChanging)
|
switch (msg)
|
||||||
|
{
|
||||||
|
case (int) Constants.WindowMessage.WindowPositionChanging:
|
||||||
return OnWindowPositionChanging(lParam, ref handled);
|
return OnWindowPositionChanging(lParam, ref handled);
|
||||||
|
|
||||||
if (msg == (int) Constants.WindowMessage.EnterSizeMove)
|
case (int) Constants.WindowMessage.EnterSizeMove:
|
||||||
|
|
||||||
|
// Initialize the last window position
|
||||||
|
_lastWindowPosition = new Structures.WindowPosition
|
||||||
|
{
|
||||||
|
Left = (int) Left,
|
||||||
|
Width = (int) Width,
|
||||||
|
Height = (int) Height,
|
||||||
|
Top = (int) Top
|
||||||
|
};
|
||||||
|
|
||||||
|
// Store the current other windows
|
||||||
_otherWindows = OtherWindows;
|
_otherWindows = OtherWindows;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (int) Constants.WindowMessage.ExitSizeMove:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return IntPtr.Zero;
|
return IntPtr.Zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,7 +177,10 @@ namespace Common.Wpf.Windows
|
|||||||
return IntPtr.Zero;
|
return IntPtr.Zero;
|
||||||
|
|
||||||
// Figure out if the window is being moved or resized
|
// Figure out if the window is being moved or resized
|
||||||
SnapMode snapMode = (_lastWindowPosition.IsSameSize(windowPosition) ? SnapMode.Move : SnapMode.Resize);
|
var snapMode = (_lastWindowPosition.IsSameSize(windowPosition) ? SnapMode.Move : SnapMode.Resize);
|
||||||
|
|
||||||
|
// Figure out what side is resizing
|
||||||
|
var resizeSide = new ResizeSide(windowPosition, _lastWindowPosition);
|
||||||
|
|
||||||
// Get the screen the cursor is currently on
|
// Get the screen the cursor is currently on
|
||||||
Screen screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position);
|
Screen screen = Screen.FromPoint(System.Windows.Forms.Cursor.Position);
|
||||||
@@ -205,55 +262,178 @@ namespace Common.Wpf.Windows
|
|||||||
|
|
||||||
if (otherWindows != null && otherWindows.Count > 0)
|
if (otherWindows != null && otherWindows.Count > 0)
|
||||||
{
|
{
|
||||||
|
// Get the snap source rectangles for the window being changed
|
||||||
|
var sourceRectanges = new WindowBorderRectangles(windowPosition, 1, false);
|
||||||
|
|
||||||
// Loop over all other windows looking to see if we should stick
|
// Loop over all other windows looking to see if we should stick
|
||||||
foreach (var otherWindow in otherWindows)
|
foreach (var otherWindow in otherWindows)
|
||||||
{
|
{
|
||||||
// Get a rectangle with the bounds of the other window
|
// Get a rectangle with the bounds of the other window
|
||||||
var otherWindowRect = otherWindow.Location;
|
var otherWindowRect = otherWindow.Location;
|
||||||
|
|
||||||
// Check the current window left against the other window right
|
// Get the snap target rectangles for the current window
|
||||||
var otherWindowSnapBorder = new Rectangle(otherWindowRect.Right, otherWindowRect.Top, snapDistance, otherWindowRect.Height);
|
var targetRectangles = new WindowBorderRectangles(otherWindow.Location, snapDistance, true);
|
||||||
var thisWindowSnapBorder = new Rectangle(windowPosition.Left, windowPosition.Top, 1, windowPosition.Height);
|
|
||||||
|
|
||||||
if (thisWindowSnapBorder.IntersectsWith(otherWindowSnapBorder))
|
if (snapMode == SnapMode.Move)
|
||||||
|
{
|
||||||
|
if (sourceRectanges.Left.IntersectsWith(targetRectangles.Right))
|
||||||
{
|
{
|
||||||
windowPosition.Left = otherWindowRect.Right;
|
windowPosition.Left = otherWindowRect.Right;
|
||||||
CheckSnapTopAndBottom(ref windowPosition, otherWindowRect, snapMode);
|
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the current window right against the other window left
|
if (sourceRectanges.Right.IntersectsWith(targetRectangles.Left))
|
||||||
otherWindowSnapBorder = new Rectangle(otherWindowRect.Left - snapDistance + 1, otherWindowRect.Top, snapDistance, otherWindowRect.Height);
|
|
||||||
thisWindowSnapBorder = new Rectangle(windowPosition.Right, windowPosition.Top, 1, windowPosition.Height);
|
|
||||||
|
|
||||||
if (thisWindowSnapBorder.IntersectsWith(otherWindowSnapBorder))
|
|
||||||
{
|
{
|
||||||
windowPosition.Left = otherWindowRect.Left - windowPosition.Width;
|
windowPosition.Left = otherWindowRect.Left - windowPosition.Width;
|
||||||
CheckSnapTopAndBottom(ref windowPosition, otherWindowRect, snapMode);
|
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the current window bottom against the other window top
|
if (sourceRectanges.Top.IntersectsWith(targetRectangles.Bottom))
|
||||||
otherWindowSnapBorder = new Rectangle(otherWindowRect.Left, otherWindowRect.Top - snapDistance + 1, otherWindowRect.Width, snapDistance);
|
|
||||||
thisWindowSnapBorder = new Rectangle(windowPosition.Left, windowPosition.Bottom, windowPosition.Width, 1);
|
|
||||||
|
|
||||||
if (thisWindowSnapBorder.IntersectsWith(otherWindowSnapBorder))
|
|
||||||
{
|
|
||||||
windowPosition.Top = otherWindowRect.Top - windowPosition.Height;
|
|
||||||
CheckSnapLeftAndRight(ref windowPosition, otherWindowRect, snapMode);
|
|
||||||
updated = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check the current window top against the other window bottom
|
|
||||||
otherWindowSnapBorder = new Rectangle(otherWindowRect.Left, otherWindowRect.Bottom, otherWindowRect.Width, snapDistance);
|
|
||||||
thisWindowSnapBorder = new Rectangle(windowPosition.Left, windowPosition.Top, windowPosition.Width, 1);
|
|
||||||
|
|
||||||
if (thisWindowSnapBorder.IntersectsWith(otherWindowSnapBorder))
|
|
||||||
{
|
{
|
||||||
windowPosition.Top = otherWindowRect.Bottom;
|
windowPosition.Top = otherWindowRect.Bottom;
|
||||||
CheckSnapLeftAndRight(ref windowPosition, otherWindowRect, snapMode);
|
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sourceRectanges.Bottom.IntersectsWith(targetRectangles.Top))
|
||||||
|
{
|
||||||
|
windowPosition.Top = otherWindowRect.Top - windowPosition.Height;
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (windowPosition.Top == otherWindowRect.Bottom || windowPosition.Bottom == otherWindowRect.Top)
|
||||||
|
{
|
||||||
|
if (Math.Abs(windowPosition.Left - otherWindowRect.Left) <= snapDistance)
|
||||||
|
{
|
||||||
|
windowPosition.Left = otherWindowRect.Left;
|
||||||
|
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Math.Abs(windowPosition.Right - otherWindowRect.Right) <= snapDistance)
|
||||||
|
{
|
||||||
|
windowPosition.Left = otherWindowRect.Right - windowPosition.Width;
|
||||||
|
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (windowPosition.Left == otherWindowRect.Right || windowPosition.Right == otherWindowRect.Left)
|
||||||
|
{
|
||||||
|
if (Math.Abs(otherWindowRect.Bottom - windowPosition.Bottom) <= snapDistance)
|
||||||
|
{
|
||||||
|
windowPosition.Top = otherWindowRect.Bottom - windowPosition.Height;
|
||||||
|
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Math.Abs(otherWindowRect.Top - windowPosition.Top) <= snapDistance)
|
||||||
|
{
|
||||||
|
windowPosition.Top = otherWindowRect.Top;
|
||||||
|
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (resizeSide.IsLeft)
|
||||||
|
{
|
||||||
|
// Check the current window left against the other window right
|
||||||
|
if (sourceRectanges.Left.IntersectsWith(targetRectangles.Right))
|
||||||
|
{
|
||||||
|
var sign = windowPosition.Left < otherWindowRect.Right ? -1 : 1;
|
||||||
|
|
||||||
|
windowPosition.Width += Math.Abs(otherWindowRect.Right - windowPosition.Left) * sign;
|
||||||
|
windowPosition.Left = otherWindowRect.Right;
|
||||||
|
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
else if (windowPosition.Top == otherWindowRect.Bottom || windowPosition.Bottom == otherWindowRect.Top)
|
||||||
|
{
|
||||||
|
if (Math.Abs(windowPosition.Left - otherWindowRect.Left) <= snapDistance)
|
||||||
|
{
|
||||||
|
var sign = windowPosition.Left < otherWindowRect.Left ? -1 : 1;
|
||||||
|
|
||||||
|
windowPosition.Width += Math.Abs(otherWindowRect.Left - windowPosition.Left) * sign;
|
||||||
|
windowPosition.Left = otherWindowRect.Left;
|
||||||
|
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (resizeSide.IsRight)
|
||||||
|
{
|
||||||
|
// Check the current window right against the other window left
|
||||||
|
if (sourceRectanges.Right.IntersectsWith(targetRectangles.Left))
|
||||||
|
{
|
||||||
|
var sign = windowPosition.Right < otherWindowRect.Left ? 1 : -1;
|
||||||
|
|
||||||
|
windowPosition.Width += Math.Abs(otherWindowRect.Left - windowPosition.Right) * sign;
|
||||||
|
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
else if (windowPosition.Top == otherWindowRect.Bottom || windowPosition.Bottom == otherWindowRect.Top)
|
||||||
|
{
|
||||||
|
if (Math.Abs(windowPosition.Right - otherWindowRect.Right) <= snapDistance)
|
||||||
|
{
|
||||||
|
var sign = windowPosition.Right < otherWindowRect.Right ? 1 : -1;
|
||||||
|
|
||||||
|
windowPosition.Width += Math.Abs(otherWindowRect.Right - windowPosition.Right) * sign;
|
||||||
|
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resizeSide.IsBottom)
|
||||||
|
{
|
||||||
|
// Check the current window bottom against the other window top
|
||||||
|
if (sourceRectanges.Bottom.IntersectsWith(targetRectangles.Top))
|
||||||
|
{
|
||||||
|
var sign = windowPosition.Bottom < otherWindowRect.Top ? 1 : -1;
|
||||||
|
|
||||||
|
windowPosition.Height += Math.Abs(otherWindowRect.Top - windowPosition.Bottom) * sign;
|
||||||
|
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
else if (windowPosition.Left == otherWindowRect.Right || windowPosition.Right == otherWindowRect.Left)
|
||||||
|
{
|
||||||
|
if (Math.Abs(otherWindowRect.Bottom - windowPosition.Bottom) <= snapDistance)
|
||||||
|
{
|
||||||
|
var sign = windowPosition.Bottom < otherWindowRect.Bottom ? 1 : -1;
|
||||||
|
|
||||||
|
windowPosition.Height += Math.Abs(otherWindowRect.Bottom - windowPosition.Bottom) * sign;
|
||||||
|
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (resizeSide.IsTop)
|
||||||
|
{
|
||||||
|
// Check the current window top against the other window bottom
|
||||||
|
if (sourceRectanges.Top.IntersectsWith(targetRectangles.Bottom))
|
||||||
|
{
|
||||||
|
var sign = windowPosition.Top < otherWindowRect.Bottom ? 1 : -1;
|
||||||
|
|
||||||
|
windowPosition.Height -= Math.Abs(otherWindowRect.Bottom - windowPosition.Top) * sign;
|
||||||
|
windowPosition.Top = otherWindowRect.Bottom;
|
||||||
|
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
else if (windowPosition.Left == otherWindowRect.Right || windowPosition.Right == otherWindowRect.Left)
|
||||||
|
{
|
||||||
|
if (Math.Abs(otherWindowRect.Top - windowPosition.Top) <= snapDistance)
|
||||||
|
{
|
||||||
|
var sign = windowPosition.Top < otherWindowRect.Top ? -1 : 1;
|
||||||
|
|
||||||
|
windowPosition.Height += Math.Abs(otherWindowRect.Top - windowPosition.Top) * sign;
|
||||||
|
windowPosition.Top = otherWindowRect.Top;
|
||||||
|
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,60 +449,6 @@ namespace Common.Wpf.Windows
|
|||||||
return IntPtr.Zero;
|
return IntPtr.Zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CheckSnapTopAndBottom(ref Structures.WindowPosition windowPosition, Rectangle otherWindowRect, SnapMode snapMode)
|
|
||||||
{
|
|
||||||
int snapDistance = SnapDistance;
|
|
||||||
|
|
||||||
switch (snapMode)
|
|
||||||
{
|
|
||||||
case SnapMode.Move:
|
|
||||||
if (Math.Abs(windowPosition.Top - otherWindowRect.Top) <= snapDistance)
|
|
||||||
windowPosition.Top = otherWindowRect.Top;
|
|
||||||
else if (Math.Abs(windowPosition.Bottom - otherWindowRect.Bottom) <= snapDistance)
|
|
||||||
windowPosition.Top = otherWindowRect.Bottom - windowPosition.Height;
|
|
||||||
|
|
||||||
break;
|
|
||||||
case SnapMode.Resize:
|
|
||||||
if (Math.Abs(windowPosition.Top - otherWindowRect.Top) <= snapDistance)
|
|
||||||
{
|
|
||||||
windowPosition.Height += (windowPosition.Top - otherWindowRect.Top);
|
|
||||||
windowPosition.Top = otherWindowRect.Top;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
if (Math.Abs(windowPosition.Bottom - otherWindowRect.Bottom) <= snapDistance)
|
|
||||||
windowPosition.Height = otherWindowRect.Bottom - windowPosition.Top;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CheckSnapLeftAndRight(ref Structures.WindowPosition windowPosition, Rectangle otherWindowRect, SnapMode snapMode)
|
|
||||||
{
|
|
||||||
int snapDistance = SnapDistance;
|
|
||||||
|
|
||||||
switch (snapMode)
|
|
||||||
{
|
|
||||||
case SnapMode.Move:
|
|
||||||
if (Math.Abs(windowPosition.Left - otherWindowRect.Left) <= snapDistance)
|
|
||||||
windowPosition.Left = otherWindowRect.Left;
|
|
||||||
else if (Math.Abs(windowPosition.Right - otherWindowRect.Right) <= snapDistance)
|
|
||||||
windowPosition.Left = otherWindowRect.Right - windowPosition.Width;
|
|
||||||
|
|
||||||
break;
|
|
||||||
case SnapMode.Resize:
|
|
||||||
if (Math.Abs(windowPosition.Left - otherWindowRect.Left) <= snapDistance)
|
|
||||||
{
|
|
||||||
windowPosition.Width += (windowPosition.Left - otherWindowRect.Left);
|
|
||||||
windowPosition.Left = otherWindowRect.Left;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
if (Math.Abs(windowPosition.Right - otherWindowRect.Right) <= snapDistance)
|
|
||||||
windowPosition.Width = otherWindowRect.Right - windowPosition.Left;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user