From f09e5617b5960059193415c31836037fcc70eaec Mon Sep 17 00:00:00 2001 From: v-leafshi Date: Thu, 20 Nov 2025 17:51:19 +0800 Subject: [PATCH 1/4] Fix 14062: Exclude sizing grip width from DisplayRectangle when SizingGrip is enabled --- .../PublicAPI.Unshipped.txt | 1 + .../Forms/Controls/ToolStrips/StatusStrip.cs | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/System.Windows.Forms/PublicAPI.Unshipped.txt b/src/System.Windows.Forms/PublicAPI.Unshipped.txt index 3348c917404..4f04d8d271c 100644 --- a/src/System.Windows.Forms/PublicAPI.Unshipped.txt +++ b/src/System.Windows.Forms/PublicAPI.Unshipped.txt @@ -1,3 +1,4 @@ +override System.Windows.Forms.StatusStrip.DisplayRectangle.get -> System.Drawing.Rectangle static System.Windows.Forms.Application.SetColorMode(System.Windows.Forms.SystemColorMode systemColorMode) -> void static System.Windows.Forms.TaskDialog.ShowDialogAsync(nint hwndOwner, System.Windows.Forms.TaskDialogPage! page, System.Windows.Forms.TaskDialogStartupLocation startupLocation = System.Windows.Forms.TaskDialogStartupLocation.CenterOwner) -> System.Threading.Tasks.Task! static System.Windows.Forms.TaskDialog.ShowDialogAsync(System.Windows.Forms.IWin32Window! owner, System.Windows.Forms.TaskDialogPage! page, System.Windows.Forms.TaskDialogStartupLocation startupLocation = System.Windows.Forms.TaskDialogStartupLocation.CenterOwner) -> System.Threading.Tasks.Task! diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/StatusStrip.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/StatusStrip.cs index 7a1b4d7550c..a1795b0caff 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/StatusStrip.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/StatusStrip.cs @@ -309,6 +309,33 @@ internal override Size GetPreferredSizeCore(Size proposedSize) return base.GetPreferredSizeCore(proposedSize); } + public override Rectangle DisplayRectangle + { + get + { + var baseRectangle = base.DisplayRectangle; + + if (!SizingGrip) + { + return baseRectangle; + } + + int scaleGripWidth = ScaleHelper.ScaleToDpi(GripWidth, DeviceDpi); + + if (RightToLeft == RightToLeft.Yes) + { + baseRectangle.X += scaleGripWidth; + baseRectangle.Width = Math.Max(0, baseRectangle.Width - scaleGripWidth); + } + else + { + baseRectangle.Width = Math.Max(0, baseRectangle.Width - scaleGripWidth); + } + + return baseRectangle; + } + } + protected override void OnPaintBackground(PaintEventArgs e) { base.OnPaintBackground(e); From a7fcf682ff46477931486d9964a1a1e81b65b5ad Mon Sep 17 00:00:00 2001 From: v-leafshi Date: Fri, 21 Nov 2025 17:07:21 +0800 Subject: [PATCH 2/4] Update test cases --- .../Forms/Controls/ToolStrips/StatusStrip.cs | 26 ++++++++++++------- ...sStrip.StatusStripAccessibleObjectTests.cs | 2 +- .../System/Windows/Forms/StatusStripTests.cs | 4 +-- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/StatusStrip.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/StatusStrip.cs index a1795b0caff..217caada700 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/StatusStrip.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/StatusStrip.cs @@ -309,30 +309,36 @@ internal override Size GetPreferredSizeCore(Size proposedSize) return base.GetPreferredSizeCore(proposedSize); } + /// + /// Returns the client rect of the display area of the control. + /// When SizingGrip is enabled, `DisplayRectangle` excludes the sizing grip width. + /// public override Rectangle DisplayRectangle { get { - var baseRectangle = base.DisplayRectangle; + Rectangle rectangle = base.DisplayRectangle; if (!SizingGrip) { - return baseRectangle; + return rectangle; } - int scaleGripWidth = ScaleHelper.ScaleToDpi(GripWidth, DeviceDpi); - - if (RightToLeft == RightToLeft.Yes) + Rectangle grip = SizeGripBounds; + int remainingWidth = rectangle.Width - grip.Width; + if (grip.IsEmpty || remainingWidth <= 0) { - baseRectangle.X += scaleGripWidth; - baseRectangle.Width = Math.Max(0, baseRectangle.Width - scaleGripWidth); + return rectangle; } - else + + if (RightToLeft == RightToLeft.Yes) { - baseRectangle.Width = Math.Max(0, baseRectangle.Width - scaleGripWidth); + rectangle.X += grip.Width; } - return baseRectangle; + rectangle.Width = remainingWidth; + + return rectangle; } } diff --git a/src/test/unit/System.Windows.Forms/System/Windows/Forms/AccessibleObjects/StatusStrip.StatusStripAccessibleObjectTests.cs b/src/test/unit/System.Windows.Forms/System/Windows/Forms/AccessibleObjects/StatusStrip.StatusStripAccessibleObjectTests.cs index b483cce09bd..a1c56c04f3b 100644 --- a/src/test/unit/System.Windows.Forms/System/Windows/Forms/AccessibleObjects/StatusStrip.StatusStripAccessibleObjectTests.cs +++ b/src/test/unit/System.Windows.Forms/System/Windows/Forms/AccessibleObjects/StatusStrip.StatusStripAccessibleObjectTests.cs @@ -933,7 +933,7 @@ static ToolStripItem CreateStatusStripItem() return new ToolStripStatusLabel() { AutoSize = false, - Size = new Size(50, 25) + Size = new Size(40, 25) }; } } diff --git a/src/test/unit/System.Windows.Forms/System/Windows/Forms/StatusStripTests.cs b/src/test/unit/System.Windows.Forms/System/Windows/Forms/StatusStripTests.cs index 5931da26e5b..6de09c15ec2 100644 --- a/src/test/unit/System.Windows.Forms/System/Windows/Forms/StatusStripTests.cs +++ b/src/test/unit/System.Windows.Forms/System/Windows/Forms/StatusStripTests.cs @@ -65,7 +65,7 @@ public void StatusStrip_Ctor_Default() Assert.False(control.DesignMode); Assert.Empty(control.DisplayedItems); Assert.Same(control.DisplayedItems, control.DisplayedItems); - Assert.Equal(new Rectangle(1, 0, 185, 22), control.DisplayRectangle); + Assert.Equal(new Rectangle(1, 0, 173, 22), control.DisplayRectangle); Assert.Equal(DockStyle.Bottom, control.Dock); Assert.NotNull(control.DockPadding); Assert.Same(control.DockPadding, control.DockPadding); @@ -108,7 +108,7 @@ public void StatusStrip_Ctor_Default() Assert.Equal(Point.Empty, control.Location); Assert.Equal(Padding.Empty, control.Margin); Assert.Equal(Size.Empty, control.MaximumSize); - Assert.Equal(new Size(185, 22), control.MaxItemSize); + Assert.Equal(new Size(173, 22), control.MaxItemSize); Assert.Equal(Size.Empty, control.MinimumSize); Assert.Equal(Orientation.Horizontal, control.Orientation); Assert.NotNull(control.OverflowButton); From 7f3332bf1f66bb0867a42e897e9f85c80b6d0d4f Mon Sep 17 00:00:00 2001 From: v-leafshi Date: Wed, 26 Nov 2025 18:40:18 +0800 Subject: [PATCH 3/4] Update DisplayRectangle --- .../Forms/Controls/ToolStrips/StatusStrip.cs | 36 +++++++++++++------ .../Forms/Controls/ToolStrips/ToolStrip.cs | 7 +++- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/StatusStrip.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/StatusStrip.cs index 217caada700..50bf5d9d730 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/StatusStrip.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/StatusStrip.cs @@ -317,28 +317,44 @@ public override Rectangle DisplayRectangle { get { - Rectangle rectangle = base.DisplayRectangle; + Rectangle rect = base.DisplayRectangle; if (!SizingGrip) { - return rectangle; + return rect; } Rectangle grip = SizeGripBounds; - int remainingWidth = rectangle.Width - grip.Width; - if (grip.IsEmpty || remainingWidth <= 0) + + if (grip.IsEmpty) { - return rectangle; + return rect; } - if (RightToLeft == RightToLeft.Yes) + if (Dock is DockStyle.Bottom or DockStyle.Top or DockStyle.Fill) { - rectangle.X += grip.Width; - } + int remainingWidth = rect.Width - grip.Width; + if (remainingWidth > 0) + { + if (RightToLeft == RightToLeft.Yes) + { + rect.X += grip.Width; + } - rectangle.Width = remainingWidth; + rect.Width = remainingWidth; + } + } + else if (Dock is DockStyle.Left or DockStyle.Right) + { + int remainingHeight = rect.Height - grip.Height; + if (remainingHeight > 0) + { + rect.Y += grip.Height; + rect.Height = remainingHeight; + } + } - return rectangle; + return rect; } } diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/ToolStrip.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/ToolStrip.cs index 893d5ad4c31..892d653508c 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/ToolStrip.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/ToolStrip.cs @@ -728,6 +728,11 @@ public override Rectangle DisplayRectangle { Rectangle rect = base.DisplayRectangle; + if (this is StatusStrip) + { + return rect; + } + if ((LayoutEngine is ToolStripSplitStackLayout) && (GripStyle == ToolStripGripStyle.Visible)) { if (Orientation == Orientation.Horizontal) @@ -3940,7 +3945,7 @@ internal override void ReleaseUiaProvider(HWND handle) /// contains ToolStrip or ToolStripDropDown items to disconnect internal virtual void ReleaseToolStripItemsProviders(ToolStripItemCollection items) { - ToolStripItem[] itemsArray = [..items.Cast()]; + ToolStripItem[] itemsArray = [.. items.Cast()]; foreach (ToolStripItem toolStripItem in itemsArray) { if (toolStripItem is ToolStripDropDownItem dropDownItem && dropDownItem.DropDownItems.Count > 0) From f5e4bad3b27a97abacc89f725261a1f020f7a90b Mon Sep 17 00:00:00 2001 From: v-leafshi Date: Fri, 28 Nov 2025 17:39:51 +0800 Subject: [PATCH 4/4] =?UTF-8?q?Collision=20detection=20has=20been=20optimi?= =?UTF-8?q?zed;=20items=20are=20only=20hidden=20when=20the=20area=20of=20?= =?UTF-8?q?=E2=80=8B=E2=80=8Bintersection=20between=20an=20item=20and=20a?= =?UTF-8?q?=20SizingGrip=20exceeds=20a=20certain=20threshold=20(e.g.,=20>5?= =?UTF-8?q?0%).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Forms/Controls/ToolStrips/StatusStrip.cs | 81 +++++++------------ .../Forms/Controls/ToolStrips/ToolStrip.cs | 7 +- 2 files changed, 31 insertions(+), 57 deletions(-) diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/StatusStrip.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/StatusStrip.cs index 50bf5d9d730..7d9839bd2ad 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/StatusStrip.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/StatusStrip.cs @@ -309,55 +309,6 @@ internal override Size GetPreferredSizeCore(Size proposedSize) return base.GetPreferredSizeCore(proposedSize); } - /// - /// Returns the client rect of the display area of the control. - /// When SizingGrip is enabled, `DisplayRectangle` excludes the sizing grip width. - /// - public override Rectangle DisplayRectangle - { - get - { - Rectangle rect = base.DisplayRectangle; - - if (!SizingGrip) - { - return rect; - } - - Rectangle grip = SizeGripBounds; - - if (grip.IsEmpty) - { - return rect; - } - - if (Dock is DockStyle.Bottom or DockStyle.Top or DockStyle.Fill) - { - int remainingWidth = rect.Width - grip.Width; - if (remainingWidth > 0) - { - if (RightToLeft == RightToLeft.Yes) - { - rect.X += grip.Width; - } - - rect.Width = remainingWidth; - } - } - else if (Dock is DockStyle.Left or DockStyle.Right) - { - int remainingHeight = rect.Height - grip.Height; - if (remainingHeight > 0) - { - rect.Y += grip.Height; - rect.Height = remainingHeight; - } - } - - return rect; - } - } - protected override void OnPaintBackground(PaintEventArgs e) { base.OnPaintBackground(e); @@ -428,6 +379,7 @@ protected override void SetDisplayedItems() Rectangle lastItemBounds = Rectangle.Empty; ToolStripItem? lastItem = null; + const double HideIntersectionRatioThreshold = 0.5; for (int i = 0; i < Items.Count; i++) { ToolStripItem item = Items[i]; @@ -436,9 +388,36 @@ protected override void SetDisplayedItems() // visible. if (overflow || ((IArrangedElement)item).ParticipatesInLayout) { - if (overflow || (SizingGrip && item.Bounds.IntersectsWith(SizeGripBounds))) + bool hide = false; + + // Check for collisions with SizingGrip when SizingGrip is enabled. + if (!SizeGripBounds.IsEmpty) + { + Rectangle itemBounds = item.Bounds; + + if (itemBounds.IntersectsWith(SizeGripBounds)) + { + Rectangle intersect = Rectangle.Intersect(itemBounds, SizeGripBounds); + double itemArea = Math.Max(1, itemBounds.Width * itemBounds.Height); + double intersectArea = Math.Max(0, intersect.Width * intersect.Height); + double ratio = intersectArea / itemArea; + + // Only hide intersections when the intersection ratio exceeds a threshold, + // otherwise, slight overlap is allowed. + if (ratio >= HideIntersectionRatioThreshold) + { + hide = true; + } + } + } + + if (overflow) + { + hide = true; + } + + if (hide) { - // if the item collides with the size grip, set the location to nomansland. SetItemLocation(item, noMansLand); item.SetPlacement(ToolStripItemPlacement.None); } diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/ToolStrip.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/ToolStrip.cs index 892d653508c..893d5ad4c31 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/ToolStrip.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/ToolStrips/ToolStrip.cs @@ -728,11 +728,6 @@ public override Rectangle DisplayRectangle { Rectangle rect = base.DisplayRectangle; - if (this is StatusStrip) - { - return rect; - } - if ((LayoutEngine is ToolStripSplitStackLayout) && (GripStyle == ToolStripGripStyle.Visible)) { if (Orientation == Orientation.Horizontal) @@ -3945,7 +3940,7 @@ internal override void ReleaseUiaProvider(HWND handle) /// contains ToolStrip or ToolStripDropDown items to disconnect internal virtual void ReleaseToolStripItemsProviders(ToolStripItemCollection items) { - ToolStripItem[] itemsArray = [.. items.Cast()]; + ToolStripItem[] itemsArray = [..items.Cast()]; foreach (ToolStripItem toolStripItem in itemsArray) { if (toolStripItem is ToolStripDropDownItem dropDownItem && dropDownItem.DropDownItems.Count > 0)