|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace System.Windows.Forms.Design.Behavior;
/// <summary>
/// The SnapLine class represents a UI-guideline that will be rendered
/// during control movement (drag, keyboard, and resize) operations.
/// SnapLines will assist a user in aligning controls relative to one
/// one another. Each SnapLine will have a type: top, bottom, etc...
/// Only SnapLines of like-types are allowed to align with each other.
/// The 'offset' will represent the distance from the origin (upper-left
/// corner) of the control to where the SnapLine is located. And finally
/// the 'filter' is a string used to define custom types of SnapLines.
/// This enables a SnapLine with a filter of "TypeX" to only snap to
/// other "TypeX" filtered lines.
/// </summary>
public sealed class SnapLine
{
// These are used in the SnapLine filter to define custom margin/padding SnapLines.
// Margins will have special rules of equality, basically opposites will attract one another
// (ex: margin right == margin left) and paddings will be attracted to like-margins.
internal const string Margin = "Margin";
internal const string MarginRight = Margin + ".Right";
internal const string MarginLeft = Margin + ".Left";
internal const string MarginBottom = Margin + ".Bottom";
internal const string MarginTop = Margin + ".Top";
internal const string Padding = "Padding";
internal const string PaddingRight = Padding + ".Right";
internal const string PaddingLeft = Padding + ".Left";
internal const string PaddingBottom = Padding + ".Bottom";
internal const string PaddingTop = Padding + ".Top";
/// <summary>
/// SnapLine constructor that takes the type and offset of SnapLine.
/// </summary>
public SnapLine(SnapLineType type, int offset)
: this(type, offset, filter: null, SnapLinePriority.Low)
{
}
/// <summary>
/// SnapLine constructor that takes the type, offset and filter of SnapLine.
/// </summary>
public SnapLine(SnapLineType type, int offset, string? filter)
: this(type, offset, filter, SnapLinePriority.Low)
{
}
/// <summary>
/// SnapLine constructor that takes the type, offset, and priority of SnapLine.
/// </summary>
public SnapLine(SnapLineType type, int offset, SnapLinePriority priority)
: this(type, offset, filter: null, priority)
{
}
/// <summary>
/// SnapLine constructor that takes the type, offset, filter, and priority of the SnapLine.
/// </summary>
public SnapLine(SnapLineType type, int offset, string? filter, SnapLinePriority priority)
{
SnapLineType = type;
Offset = offset;
Filter = filter;
Priority = priority;
}
/// <summary>
/// This property returns a string representing an optional user-defined filter.
/// Setting this filter will allow only those SnapLines with similar filters to align
/// to one another.
/// </summary>
public string? Filter { get; }
/// <summary>
/// Returns true if the SnapLine is of a horizontal type.
/// </summary>
public bool IsHorizontal => SnapLineType is SnapLineType.Top
or SnapLineType.Bottom
or SnapLineType.Horizontal
or SnapLineType.Baseline;
/// <summary>
/// Returns true if the SnapLine is of a vertical type.
/// </summary>
public bool IsVertical => SnapLineType is SnapLineType.Left
or SnapLineType.Right
or SnapLineType.Vertical;
/// <summary>
/// Read-only property that returns the distance from the origin to where this SnapLine is defined.
/// </summary>
public int Offset { get; private set; }
/// <summary>
/// Read-only property that returns the priority of the SnapLine.
/// </summary>
public SnapLinePriority Priority { get; }
/// <summary>
/// Read-only property that represents the 'type' of SnapLine.
/// </summary>
public SnapLineType SnapLineType { get; }
/// <summary>
/// Adjusts the offset property of the SnapLine.
/// </summary>
public void AdjustOffset(int adjustment)
{
Offset += adjustment;
}
/// <summary>
/// Returns true if SnapLine s1 should snap to SnapLine s2.
/// </summary>
public static bool ShouldSnap(SnapLine line1, SnapLine line2)
{
// types must first be equal
if (line1.SnapLineType != line2.SnapLineType)
{
return false;
}
// if the filters are both null - then return true
if (line1.Filter is null && line2.Filter is null)
{
return true;
}
// at least one filter is non-null so if the other is null
// then we don't have a match
if (line1.Filter is null || line2.Filter is null)
{
return false;
}
// check for our special-cased margin filter
if (line1.Filter.Contains(Margin))
{
if ((line1.Filter.Equals(MarginRight) && (line2.Filter.Equals(MarginLeft) || line2.Filter.Equals(PaddingRight))) ||
(line1.Filter.Equals(MarginLeft) && (line2.Filter.Equals(MarginRight) || line2.Filter.Equals(PaddingLeft))) ||
(line1.Filter.Equals(MarginTop) && (line2.Filter.Equals(MarginBottom) || line2.Filter.Equals(PaddingTop))) ||
(line1.Filter.Equals(MarginBottom) && (line2.Filter.Equals(MarginTop) || line2.Filter.Equals(PaddingBottom))))
{
return true;
}
return false;
}
// check for padding & margins
if (line1.Filter.Contains(Padding))
{
if ((line1.Filter.Equals(PaddingLeft) && line2.Filter.Equals(MarginLeft)) ||
(line1.Filter.Equals(PaddingRight) && line2.Filter.Equals(MarginRight)) ||
(line1.Filter.Equals(PaddingTop) && line2.Filter.Equals(MarginTop)) ||
(line1.Filter.Equals(PaddingBottom) && line2.Filter.Equals(MarginBottom)))
{
return true;
}
return false;
}
// basic filter equality
if (line1.Filter.Equals(line2.Filter))
{
return true;
}
// not equal!
return false;
}
/// <summary>
/// ToString implementation for SnapLines.
/// </summary>
public override string ToString()
{
return $"SnapLine: {{type = {SnapLineType}, offset = {Offset}, priority = {Priority}, filter = {Filter ?? "<null>"}}}";
}
}
|