|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Specialized;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms.Layout;
namespace System.Windows.Forms;
[Designer($"System.Windows.Forms.Design.ToolStripPanelDesigner, {AssemblyRef.SystemDesign}")]
[ToolboxBitmap(typeof(ToolStripPanel), "ToolStripPanel_standalone")]
public partial class ToolStripPanel : ContainerControl, IArrangedElement
{
private Orientation _orientation = Orientation.Horizontal;
private Padding _rowMargin;
private ToolStripRendererSwitcher? _rendererSwitcher;
private BitVector32 _state;
private readonly ToolStripContainer? _owner;
private static readonly int s_propToolStripPanelRowCollection = PropertyStore.CreateKey();
private static readonly int s_stateLocked = BitVector32.CreateMask();
private static readonly int s_stateBeginInit = BitVector32.CreateMask(s_stateLocked);
private static readonly int s_stateChangingZOrder = BitVector32.CreateMask(s_stateBeginInit);
private static readonly int s_stateInJoin = BitVector32.CreateMask(s_stateChangingZOrder);
private static readonly int s_stateEndInit = BitVector32.CreateMask(s_stateInJoin);
private static readonly int s_stateLayoutSuspended = BitVector32.CreateMask(s_stateEndInit);
private static readonly int s_stateRightToLeftChanged = BitVector32.CreateMask(s_stateLayoutSuspended);
internal static readonly Padding s_dragMargin = new(10);
private static readonly object s_eventRendererChanged = new();
public ToolStripPanel()
{
const int LogicalRowLeftMargin = 3;
_rowMargin = new(ScaleHelper.ScaleToDpi(LogicalRowLeftMargin, ScaleHelper.InitialSystemDpi), 0, 0, 0);
SuspendLayout();
AutoScaleMode = AutoScaleMode.None;
InitFlowLayout();
AutoSize = true;
MinimumSize = Size.Empty; // consider 1,1
_state[s_stateLocked | s_stateBeginInit | s_stateChangingZOrder] = false;
TabStop = false;
ToolStripManager.ToolStripPanels.Add(this);
// not setting ControlStyles.AllPaintingInWmPaint as we don't do any painting in OnPaint... all
// is done in OnPaintBackground... so its better to show the rafting container in WM_ERASEBACKGROUND.
SetStyle(ControlStyles.ResizeRedraw | ControlStyles.OptimizedDoubleBuffer | /*ControlStyles.AllPaintingInWmPaint |*/ControlStyles.SupportsTransparentBackColor, true);
SetStyle(ControlStyles.Selectable, false);
ResumeLayout(true);
}
internal ToolStripPanel(ToolStripContainer owner)
: this()
{
_owner = owner;
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override bool AllowDrop
{
get => base.AllowDrop;
set => base.AllowDrop = value;
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override bool AutoScroll
{
get => base.AutoScroll;
set => base.AutoScroll = value;
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new Size AutoScrollMargin
{
get => base.AutoScrollMargin;
set => base.AutoScrollMargin = value;
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new Size AutoScrollMinSize
{
get => base.AutoScrollMinSize;
set => base.AutoScrollMinSize = value;
}
[DefaultValue(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public override bool AutoSize
{
get => base.AutoSize;
set => base.AutoSize = value;
}
/// Override base AutoSizeChanged to we can change visibility/browsability attributes
[Browsable(true)]
[EditorBrowsable(EditorBrowsableState.Always)]
public new event EventHandler? AutoSizeChanged
{
add => base.AutoSizeChanged += value;
remove => base.AutoSizeChanged -= value;
}
protected override Padding DefaultPadding
{
get { return Padding.Empty; }
}
protected override Padding DefaultMargin
{
get { return Padding.Empty; }
}
public Padding RowMargin
{
get { return _rowMargin; }
set
{
_rowMargin = value;
LayoutTransaction.DoLayout(this, this, "RowMargin");
}
}
public override DockStyle Dock
{
get => base.Dock;
set
{
base.Dock = value;
if (value is DockStyle.Left or DockStyle.Right)
{
Orientation = Orientation.Vertical;
}
else
{
Orientation = Orientation.Horizontal;
}
}
}
internal Rectangle DragBounds
{
get
{
return LayoutUtils.InflateRect(ClientRectangle, s_dragMargin);
}
}
internal bool IsInDesignMode
{
get
{
return DesignMode;
}
}
public override LayoutEngine LayoutEngine
{
get
{
return FlowLayout.Instance;
}
}
[DefaultValue(false)]
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Advanced)]
public bool Locked
{
get
{
return _state[s_stateLocked];
}
set
{
_state[s_stateLocked] = value;
}
}
public Orientation Orientation
{
get
{
return _orientation;
}
set
{
if (_orientation != value)
{
_orientation = value;
_rowMargin = LayoutUtils.FlipPadding(_rowMargin);
InitFlowLayout();
foreach (ToolStripPanelRow row in RowsInternal)
{
row.OnOrientationChanged();
}
}
}
}
private ToolStripRendererSwitcher RendererSwitcher
{
get
{
if (_rendererSwitcher is null)
{
_rendererSwitcher = new ToolStripRendererSwitcher(this);
HandleRendererChanged(this, EventArgs.Empty);
_rendererSwitcher.RendererChanged += HandleRendererChanged;
}
return _rendererSwitcher;
}
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public ToolStripRenderer Renderer
{
get
{
return RendererSwitcher.Renderer;
}
set
{
RendererSwitcher.Renderer = value;
}
}
[SRDescription(nameof(SR.ToolStripRenderModeDescr))]
[SRCategory(nameof(SR.CatAppearance))]
public ToolStripRenderMode RenderMode
{
get
{
return RendererSwitcher.RenderMode;
}
set
{
RendererSwitcher.RenderMode = value;
}
}
[SRCategory(nameof(SR.CatAppearance))]
[SRDescription(nameof(SR.ToolStripRendererChanged))]
public event EventHandler? RendererChanged
{
add => Events.AddHandler(s_eventRendererChanged, value);
remove => Events.RemoveHandler(s_eventRendererChanged, value);
}
/// <summary>
/// Collection of child controls.
/// </summary>
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[SRDescription(nameof(SR.ToolStripPanelRowsDescr))]
internal ToolStripPanelRowCollection RowsInternal
{
get
{
if (!Properties.TryGetValue(s_propToolStripPanelRowCollection, out ToolStripPanelRowCollection? rowCollection))
{
rowCollection = Properties.AddValue(s_propToolStripPanelRowCollection, CreateToolStripPanelRowCollection());
}
return rowCollection;
}
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[SRDescription(nameof(SR.ToolStripPanelRowsDescr))]
public ToolStripPanelRow[] Rows
{
get
{
ToolStripPanelRow[] rows = new ToolStripPanelRow[RowsInternal.Count];
RowsInternal.CopyTo(rows, 0);
return rows;
}
}
internal override bool SupportsUiaProviders => true;
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new int TabIndex
{
get => base.TabIndex;
set => base.TabIndex = value;
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public new event EventHandler? TabIndexChanged
{
add => base.TabIndexChanged += value;
remove => base.TabIndexChanged -= value;
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public new bool TabStop
{
get => base.TabStop;
set
{
SetStyle(ControlStyles.Selectable, value);
base.TabStop = value;
}
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public new event EventHandler? TabStopChanged
{
add => base.TabStopChanged += value;
remove => base.TabStopChanged -= value;
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[AllowNull]
public override string Text
{
get => base.Text;
set => base.Text = value;
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public new event EventHandler? TextChanged
{
add => base.TextChanged += value;
remove => base.TextChanged -= value;
}
#region ISupportInitialize
public void BeginInit()
{
_state[s_stateBeginInit] = true;
}
public void EndInit()
{
_state[s_stateBeginInit] = false;
_state[s_stateEndInit] = true;
try
{
if (!_state[s_stateInJoin])
{
JoinControls();
}
}
finally
{
_state[s_stateEndInit] = false;
}
}
#endregion ISupportInitialize
private ToolStripPanelRowCollection CreateToolStripPanelRowCollection() => new(this);
protected override AccessibleObject CreateAccessibilityInstance() => new ToolStripPanelAccessibleObject(this);
protected override ControlCollection CreateControlsInstance() => new ToolStripPanelControlCollection(this);
/// <summary>
/// Disposes of the resources (other than memory) used by
/// the <see cref="ContainerControl"/>
/// .
/// </summary>
protected override void Dispose(bool disposing)
{
if (disposing)
{
ToolStripManager.ToolStripPanels.Remove(this);
}
base.Dispose(disposing);
}
private void InitFlowLayout()
{
if (Orientation == Orientation.Horizontal)
{
FlowLayout.SetFlowDirection(this, FlowDirection.TopDown);
}
else
{
FlowLayout.SetFlowDirection(this, FlowDirection.LeftToRight);
}
FlowLayout.SetWrapContents(this, false);
}
private Point GetStartLocation(ToolStrip toolStripToDrag)
{
if (toolStripToDrag.IsCurrentlyDragging
&& Orientation == Orientation.Horizontal
&& toolStripToDrag.RightToLeft == RightToLeft.Yes)
{
// the grip is on the right side, not left.
return new Point(toolStripToDrag.Right, toolStripToDrag.Top);
}
return toolStripToDrag.Location;
}
private void HandleRendererChanged(object? sender, EventArgs e)
{
OnRendererChanged(e);
}
[EditorBrowsable(EditorBrowsableState.Advanced)]
protected override void OnPaintBackground(PaintEventArgs e)
{
ToolStripPanelRenderEventArgs rea = new(e.Graphics, this);
Renderer.DrawToolStripPanelBackground(rea);
if (!rea.Handled)
{
base.OnPaintBackground(e);
}
}
protected override void OnControlAdded(ControlEventArgs e)
{
base.OnControlAdded(e);
if (!_state[s_stateBeginInit] && !_state[s_stateInJoin])
{
if (!_state[s_stateLayoutSuspended])
{
if (e.Control is not null)
{
var toolStrip = e.Control as ToolStrip;
if (toolStrip is not null)
{
Join(toolStrip, e.Control.Location);
}
}
}
else
{
BeginInit();
}
}
}
protected override void OnControlRemoved(ControlEventArgs e)
{
if (e.Control is ISupportToolStripPanel controlToBeDragged)
{
controlToBeDragged.ToolStripPanelRow?.ControlsInternal.Remove(e.Control);
}
base.OnControlRemoved(e);
}
protected override void OnLayout(LayoutEventArgs e)
{
if (e.AffectedComponent != ParentInternal && e.AffectedComponent as Control is not null)
{
if (e.AffectedComponent is ISupportToolStripPanel draggedControl && RowsInternal.Contains(draggedControl.ToolStripPanelRow!))
{
// there's a problem in the base onlayout... if toolstrip needs more space it talks to us
// not the row that needs layout.
LayoutTransaction.DoLayout(draggedControl.ToolStripPanelRow, e.AffectedComponent as IArrangedElement, e.AffectedProperty);
}
}
base.OnLayout(e);
}
internal override void OnLayoutSuspended()
{
base.OnLayoutSuspended();
_state[s_stateLayoutSuspended] = true;
}
internal override void OnLayoutResuming(bool resumeLayout)
{
base.OnLayoutResuming(resumeLayout);
_state[s_stateLayoutSuspended] = false;
if (_state[s_stateBeginInit])
{
EndInit();
}
}
protected override void OnRightToLeftChanged(EventArgs e)
{
base.OnRightToLeftChanged(e);
if (!_state[s_stateBeginInit])
{
if (Controls.Count > 0)
{
// rejoin the controls on the other side of the toolstrippanel.
SuspendLayout();
Control[] controls = new Control[Controls.Count];
Point[] controlLocations = new Point[Controls.Count];
int j = 0;
foreach (ToolStripPanelRow row in RowsInternal)
{
foreach (Control control in row.ControlsInternal)
{
controls[j] = control;
controlLocations[j] = new Point(row.Bounds.Width - control.Right, control.Top);
j++;
}
}
Controls.Clear();
for (int i = 0; i < controls.Length; i++)
{
var toolStrip = controls[i] as ToolStrip;
if (toolStrip is not null)
{
Join(toolStrip, controlLocations[i]);
}
}
ResumeLayout(true);
}
}
else
{
_state[s_stateRightToLeftChanged] = true;
}
}
protected virtual void OnRendererChanged(EventArgs e)
{
Renderer.InitializePanel(this);
Invalidate();
((EventHandler?)Events[s_eventRendererChanged])?.Invoke(this, e);
}
/// <summary>
/// We want to Set ToolStripPanel at DesignTime when the ToolStripPanel is added to the Form,
/// </summary>
protected override void OnParentChanged(EventArgs e)
{
PerformUpdate();
base.OnParentChanged(e);
}
protected override void OnDockChanged(EventArgs e)
{
PerformUpdate();
base.OnDockChanged(e);
}
internal void PerformUpdate()
{
PerformUpdate(false);
}
internal void PerformUpdate(bool forceLayout)
{
if (!_state[s_stateBeginInit] && !_state[s_stateInJoin])
{
JoinControls(forceLayout);
}
}
private void ResetRenderMode()
{
RendererSwitcher.ResetRenderMode();
}
private bool ShouldSerializeRenderMode()
{
return RendererSwitcher.ShouldSerializeRenderMode();
}
private bool ShouldSerializeDock()
{
return _owner is null && (Dock != DockStyle.None);
}
private void JoinControls()
{
JoinControls(false);
}
private void JoinControls(bool forceLayout)
{
// undone: config - shift to other container
ToolStripPanelControlCollection? controls = Controls as ToolStripPanelControlCollection;
if (controls is not null && controls.Count > 0)
{
controls.Sort();
// since Join is going to mess with our order - make a copy. (ugh).
Control[] controlArray = new Control[controls.Count];
controls.CopyTo(controlArray, 0);
for (int i = 0; i < controlArray.Length; i++)
{
int numRows = RowsInternal.Count;
if (controlArray[i] is ISupportToolStripPanel draggedControl
&& draggedControl.ToolStripPanelRow is not null
&& !draggedControl.IsCurrentlyDragging)
{
ToolStripPanelRow row = draggedControl.ToolStripPanelRow;
if (row.Bounds.Contains(controlArray[i].Location))
{
// this toolstrip does not require join.
continue;
}
}
if (controlArray[i].AutoSize)
{
controlArray[i].Size = controlArray[i].PreferredSize;
}
var toolStrip = controlArray[i] as ToolStrip;
if (toolStrip is not null)
{
Join(toolStrip, controlArray[i].Location);
}
if (numRows < RowsInternal.Count || forceLayout)
{
// OK this is weird but here we're in the midst of a suspend layout.
// the only way we can deterministically place these guys is to force a layout
// each time we've added a new row. Otherwise we won't find the correct
// row to add the control to (PointToRow will fail as Row.Bounds isn't set yet)
OnLayout(new LayoutEventArgs(this, PropertyNames.Rows));
}
}
}
_state[s_stateRightToLeftChanged] = false;
}
[ThreadStatic]
private static FeedbackRectangle? t_feedbackRect;
private void GiveToolStripPanelFeedback(ToolStrip toolStripToDrag, Point screenLocation)
{
if (Orientation == Orientation.Horizontal && RightToLeft == RightToLeft.Yes)
{
// paint the feedback in the correct location when RTL.Yes
screenLocation.Offset(-toolStripToDrag.Width, 0);
}
CurrentFeedbackRect ??= new FeedbackRectangle(toolStripToDrag.ClientRectangle);
if (!CurrentFeedbackRect.Visible)
{
toolStripToDrag.SuspendCaptureMode();
try
{
CurrentFeedbackRect.Show(screenLocation);
toolStripToDrag.Capture = true;
}
finally
{
toolStripToDrag.ResumeCaptureMode();
}
}
else
{
CurrentFeedbackRect.Move(screenLocation);
}
}
internal static void ClearDragFeedback()
{
FeedbackRectangle? oldFeedback = t_feedbackRect;
t_feedbackRect = null;
oldFeedback?.Dispose();
}
private static FeedbackRectangle? CurrentFeedbackRect
{
get => t_feedbackRect;
set => t_feedbackRect = value;
}
public void Join(ToolStrip toolStripToDrag)
{
Join(toolStripToDrag, Point.Empty);
}
public void Join(ToolStrip toolStripToDrag, int row)
{
ArgumentOutOfRangeException.ThrowIfNegative(row);
Rectangle dragRect;
if (row >= RowsInternal.Count)
{
dragRect = DragBounds;
}
else
{
dragRect = RowsInternal[row].DragBounds;
}
Point location;
if (Orientation == Orientation.Horizontal)
{
location = new Point(0, dragRect.Bottom - 1);
}
else
{
location = new Point(dragRect.Right - 1, 0);
}
Join(toolStripToDrag, location);
}
public void Join(ToolStrip toolStripToDrag, int x, int y)
{
Join(toolStripToDrag, new Point(x, y));
}
public void Join(ToolStrip toolStripToDrag, Point location)
{
ArgumentNullException.ThrowIfNull(toolStripToDrag);
if (!_state[s_stateBeginInit] && !_state[s_stateInJoin])
{
try
{
_state[s_stateInJoin] = true;
toolStripToDrag.ParentInternal = this;
MoveInsideContainer(toolStripToDrag, location);
}
finally
{
_state[s_stateInJoin] = false;
}
}
else
{
Controls.Add(toolStripToDrag);
toolStripToDrag.Location = location;
}
}
internal void MoveControl(ToolStrip? toolStripToDrag, Point screenLocation)
{
if (toolStripToDrag is not ISupportToolStripPanel)
{
Debug.Fail("Move called on immovable object.");
return;
}
Point clientLocation = PointToClient(screenLocation);
if (!DragBounds.Contains(clientLocation))
{
MoveOutsideContainer(toolStripToDrag, screenLocation);
return;
}
else
{
Join(toolStripToDrag, clientLocation);
}
}
private void MoveInsideContainer(ToolStrip toolStripToDrag, Point clientLocation)
{
ISupportToolStripPanel draggedControl = toolStripToDrag;
// If the point is not in this rafting container forward on to the appropriate container.
if (draggedControl.IsCurrentlyDragging && !DragBounds.Contains(clientLocation))
{
return;
}
// We know we're moving inside the container.
bool changedRow = false;
ClearDragFeedback();
// In design mode we get bogus values for client location.
if (toolStripToDrag.Site is not null && toolStripToDrag.Site.DesignMode && IsHandleCreated)
{
if (clientLocation.X < 0 || clientLocation.Y < 0)
{
Point currentCursorLoc = PointToClient(WindowsFormsUtils.LastCursorPoint);
if (ClientRectangle.Contains(currentCursorLoc))
{
clientLocation = currentCursorLoc;
}
}
}
//
// Point INSIDE this rafting container
//
ToolStripPanelRow? currentToolStripPanelRow = draggedControl.ToolStripPanelRow;
#if DEBUG
bool debugModeOnly_ChangedContainers = currentToolStripPanelRow is null || (currentToolStripPanelRow.ToolStripPanel != this);
#endif
bool pointInCurrentRow = false;
if (currentToolStripPanelRow is not null && currentToolStripPanelRow.Visible && currentToolStripPanelRow.ToolStripPanel == this)
{
if (toolStripToDrag.IsCurrentlyDragging)
{
// Dragging with mouse, use DragBounds to check
pointInCurrentRow = currentToolStripPanelRow.DragBounds.Contains(clientLocation);
}
else
{
// Location is set directly, use normal Bounds to check
pointInCurrentRow = currentToolStripPanelRow.Bounds.Contains(clientLocation);
}
}
if (pointInCurrentRow)
{
// Point INSIDE same rafting row
draggedControl.ToolStripPanelRow?.MoveControl(toolStripToDrag, GetStartLocation(toolStripToDrag), clientLocation);
}
else
{
// Point OUTSIDE current rafting row.
ToolStripPanelRow? row = PointToRow(clientLocation);
if (row is null)
{
// There's no row at this point so lets create one.
int index = RowsInternal.Count;
if (Orientation == Orientation.Horizontal)
{
// If it's above the first row, insert at the front.
index = (clientLocation.Y <= Padding.Left) ? 0 : index;
}
else
{
// Orientation.Vertical
// if it's before the first row, insert at the front.
index = (clientLocation.X <= Padding.Left) ? 0 : index;
}
ToolStripPanelRow? previousRow = null;
if (RowsInternal.Count > 0)
{
if (index == 0)
{
previousRow = RowsInternal[0];
}
else if (index > 0)
{
previousRow = RowsInternal[index - 1];
}
}
if (previousRow is not null
&& previousRow.ControlsInternal.Count == 1
&& previousRow.ControlsInternal.Contains(toolStripToDrag))
{
// If the previous row already contains this control it's futile to create a new row, we're just
// going to wind up disposing this one and causing great amounts of flicker.
row = previousRow;
// Move the ToolStrip to the new Location in the existing row.
if (toolStripToDrag.IsInDesignMode)
{
Point endLocation = (Orientation == Orientation.Horizontal) ? new Point(clientLocation.X, row.Bounds.Y) : new Point(row.Bounds.X, clientLocation.Y);
draggedControl.ToolStripPanelRow?.MoveControl(toolStripToDrag, GetStartLocation(toolStripToDrag), endLocation);
}
}
else
{
// Create a new row and insert it.
row = new ToolStripPanelRow(this);
RowsInternal.Insert(index, row);
}
}
else if (!row.CanMove(toolStripToDrag))
{
// There was a row, but we can't add the control to it, creating/inserting new row.
int index = RowsInternal.IndexOf(row);
if (currentToolStripPanelRow is not null && currentToolStripPanelRow.ControlsInternal.Count == 1)
{
if (index > 0 && index - 1 == RowsInternal.IndexOf(currentToolStripPanelRow))
{
// Attempts to leave the current row failed as there's no space in the next row.
// Since there's only one control, just keep the row.
return;
}
}
row = new ToolStripPanelRow(this);
RowsInternal.Insert(index, row);
clientLocation.Y = row.Bounds.Y;
}
changedRow = (currentToolStripPanelRow != row);
if (!changedRow)
{
if (currentToolStripPanelRow is not null && currentToolStripPanelRow.ControlsInternal.Count > 1)
{
// force a leave/re-enter to occur.
currentToolStripPanelRow.LeaveRow(toolStripToDrag);
currentToolStripPanelRow = null;
changedRow = true;
}
}
if (changedRow)
{
currentToolStripPanelRow?.LeaveRow(toolStripToDrag);
row.JoinRow(toolStripToDrag, clientLocation);
}
if (changedRow && draggedControl.IsCurrentlyDragging)
{
// Force the layout of the new row.
for (int i = 0; i < RowsInternal.Count; i++)
{
LayoutTransaction.DoLayout(RowsInternal[i], this, PropertyNames.Rows);
}
if (RowsInternal.IndexOf(row) > 0)
{
// When joining a new row, move the cursor to to the location of
// the grip, otherwise budging the mouse can pull it down into the next row.
Point cursorLoc = toolStripToDrag.PointToScreen(toolStripToDrag.GripRectangle.Location);
if (Orientation == Orientation.Vertical)
{
cursorLoc.X += toolStripToDrag.GripRectangle.Width / 2;
cursorLoc.Y = Cursor.Position.Y;
}
else
{
cursorLoc.Y += toolStripToDrag.GripRectangle.Height / 2;
cursorLoc.X = Cursor.Position.X;
}
Cursor.Position = cursorLoc;
}
}
}
#if DEBUG
Debug_VerifyOneToOneCellRowControlMatchup();
if (draggedControl.IsCurrentlyDragging && changedRow && !debugModeOnly_ChangedContainers)
{
// if we have changed containers, we're in a SuspendLayout.
Debug_VerifyNoOverlaps();
}
#endif
}
private void MoveOutsideContainer(ToolStrip toolStripToDrag, Point screenLocation)
{
// look for another rafting container.
ToolStripPanel? panel = ToolStripManager.ToolStripPanelFromPoint(toolStripToDrag, screenLocation);
if (panel is not null)
{
using (new LayoutTransaction(panel, panel, null))
{
panel.MoveControl(toolStripToDrag, screenLocation);
}
toolStripToDrag.PerformLayout();
#if DEBUG
ToolStrip draggedControl = toolStripToDrag;
if (draggedControl.IsCurrentlyDragging)
{
Debug_VerifyNoOverlaps();
}
#endif
}
else
{
GiveToolStripPanelFeedback(toolStripToDrag, screenLocation);
}
}
/// <summary>
/// Given a point within the ToolStripPanel client area -
/// it returns the row. If no such row exists, returns null
/// </summary>
public ToolStripPanelRow? PointToRow(Point clientLocation)
{
// PERF: since we're using the PropertyStore for this.RowsInternal, its actually
// faster to use foreach.
foreach (ToolStripPanelRow row in RowsInternal)
{
Rectangle bounds = LayoutUtils.InflateRect(row.Bounds, row.Margin);
// at this point we may not be sized correctly. Guess.
if (ParentInternal is not null)
{
if (Orientation == Orientation.Horizontal && (bounds.Width == 0))
{
bounds.Width = ParentInternal.DisplayRectangle.Width;
}
else if (Orientation == Orientation.Vertical && (bounds.Height == 0))
{
bounds.Height = ParentInternal.DisplayRectangle.Height;
}
}
if (bounds.Contains(clientLocation))
{
return row;
}
}
return null;
}
#if DEBUG
[Conditional("DEBUG")]
private void Debug_VerifyOneToOneCellRowControlMatchup()
{
for (int i = 0; i < RowsInternal.Count; i++)
{
ToolStripPanelRow row = RowsInternal[i];
foreach (ToolStripPanelCell cell in row.Cells)
{
if (cell.Control is not null)
{
ToolStripPanelRow? currentlyAssignedRow = ((ISupportToolStripPanel)cell.Control).ToolStripPanelRow;
if (currentlyAssignedRow != row)
{
int goodRowIndex = (currentlyAssignedRow is not null) ? RowsInternal.IndexOf(currentlyAssignedRow) : -1;
if (goodRowIndex == -1)
{
Debug.Fail($"ToolStripPanelRow has not been assigned! Should be set to {i}.");
}
else
{
Debug.Fail($"Detected orphan cell! {cell.Control.Name} is in row {goodRowIndex}. It shouldn't have a cell in {i}! \r\n\r\nTurn on DEBUG_PAINT in ToolStripPanel and ToolStripPanelRow to investigate.");
}
}
}
else
{
Debug.Fail("why do we have a cell with a null control in this row?");
}
}
}
}
[Conditional("DEBUG")]
private void Debug_VerifyNoOverlaps()
{
foreach (Control c1 in Controls)
{
foreach (Control c2 in Controls)
{
if (c1 == c2)
{
continue;
}
Rectangle intersection = c1.Bounds;
intersection.Intersect(c2.Bounds);
if (!LayoutUtils.IsZeroWidthOrHeight(intersection))
{
ISupportToolStripPanel draggedToolStrip1 = (c1 as ISupportToolStripPanel)!;
ISupportToolStripPanel draggedToolStrip2 = (c2 as ISupportToolStripPanel)!;
static object GetRow(ISupportToolStripPanel draggedToolStrip, ToolStripPanelRowCollection rows)
{
int rowIndex = rows.IndexOf(draggedToolStrip.ToolStripPanelRow!);
return rowIndex == -1
? "unknown"
: rowIndex;
}
Debug.Fail($@"OVERLAP detection:
{c1.Name ?? ""}: {c1.Bounds} row {GetRow(draggedToolStrip1, RowsInternal)} row bounds {draggedToolStrip1.ToolStripPanelRow?.Bounds}
{c2.Name ?? ""}: {c2.Bounds} row {GetRow(draggedToolStrip2, RowsInternal)} row bounds {draggedToolStrip2.ToolStripPanelRow?.Bounds}");
}
}
}
}
#endif
ArrangedElementCollection IArrangedElement.Children => RowsInternal;
}
|