From be6ddd8dd0a363f937388aac58852baaaa14186f Mon Sep 17 00:00:00 2001 From: Curtis Wensley Date: Wed, 7 Dec 2022 08:18:19 -0800 Subject: [PATCH] Mac: Fix grid column auto sizing when reusing the same control --- .../Forms/Controls/GridColumnHandler.cs | 10 +- src/Eto.Mac/Forms/Controls/GridHandler.cs | 129 ++++++++++-------- src/Eto.Mac/Forms/Controls/GridViewHandler.cs | 5 +- test/Eto.Test.Mac/UnitTests/GridViewTests.cs | 4 +- .../Sections/Controls/GridViewSection.cs | 2 +- 5 files changed, 85 insertions(+), 65 deletions(-) diff --git a/src/Eto.Mac/Forms/Controls/GridColumnHandler.cs b/src/Eto.Mac/Forms/Controls/GridColumnHandler.cs index add235e4c3..ace8e4696a 100644 --- a/src/Eto.Mac/Forms/Controls/GridColumnHandler.cs +++ b/src/Eto.Mac/Forms/Controls/GridColumnHandler.cs @@ -51,7 +51,7 @@ public interface IDataColumnHandler : GridColumn.IHandler void SetObjectValue(object dataItem, NSObject val); new GridColumn Widget { get; } IDataViewHandler DataViewHandler { get; } - void AutoSizeColumn(NSRange? rowRange, bool force = false); + bool AutoSizeColumn(NSRange? rowRange, bool force = false); void EnabledChanged(bool value); nfloat GetPreferredWidth(NSRange? range = null); void SizeToFit(); @@ -93,18 +93,22 @@ protected override void Initialize() base.Initialize(); } - public void AutoSizeColumn(NSRange? rowRange, bool force = false) + public bool AutoSizeColumn(NSRange? rowRange, bool force = false) { var handler = DataViewHandler; if (handler == null) - return; + return false; if (AutoSize) { var width = GetPreferredWidth(rowRange); if (force || width > Control.Width) + { Control.Width = (nfloat)Math.Ceiling(width); + return true; + } } + return false; } public nfloat GetPreferredWidth(NSRange? range = null) diff --git a/src/Eto.Mac/Forms/Controls/GridHandler.cs b/src/Eto.Mac/Forms/Controls/GridHandler.cs index 47b31542b5..f446adbfb2 100644 --- a/src/Eto.Mac/Forms/Controls/GridHandler.cs +++ b/src/Eto.Mac/Forms/Controls/GridHandler.cs @@ -331,6 +331,7 @@ protected override void Initialize() public override void OnLoad(EventArgs e) { base.OnLoad(e); + ResetAutoSizedColumns(); UpdateColumns(); } @@ -338,13 +339,6 @@ public override void OnLoadComplete(EventArgs e) { base.OnLoadComplete(e); - if (Widget.Columns.Any(r => r.Expand)) - { - // expanded columns need readjustment after initial size - AutoSizeColumns(true, false); - Control.SizeToFit(); - } - var row = Widget.Properties.Get(GridHandler.ScrolledToRow_Key, 0); // Yosemite bug: hides first row when DataStore is set before control is visible, so we always call this Control.ScrollRowToVisible(row); @@ -379,66 +373,92 @@ nfloat GetTableRowInsets() } } + int lastAutoSizeWidth; + public bool AutoSizeColumns(bool force, bool forceNewSize = false) { - if (Widget.Loaded) + if (!Widget.Loaded) + return false; + + var rect = Table.VisibleRect(); + if (rect.Width <= 0) + return false; + + bool resizeExpanded = forceNewSize; + bool changed = false; + var newRange = rect.IsEmpty ? null : (NSRange?)Table.RowsInRect(rect); + + if (lastAutoSizeWidth != (int)rect.Width) + { + resizeExpanded = true; + lastAutoSizeWidth = (int)rect.Width; + } + + if (force + || newRange == null + || (autoSizeRange.Location != newRange.Value.Location || autoSizeRange.Length != newRange.Value.Length)) { - var rect = Table.VisibleRect(); - var newRange = rect.IsEmpty ? null : (NSRange?)Table.RowsInRect(rect); - if (force - || newRange == null - || (autoSizeRange.Location != newRange.Value.Location || autoSizeRange.Length != newRange.Value.Length)) + IsAutoSizingColumns = true; + + int expandCount = 0; + nfloat requiredWidth = 0; + nfloat expandedWidth = 0; + + // remove all spacing that isn't part of column widths + var intercellSpacingWidth = Table.IntercellSpacing.Width; + rect.Width -= intercellSpacingWidth * (Table.ColumnCount - 1); + rect.Width -= GetTableRowInsets(); + + foreach (var col in ColumnHandlers) { - IsAutoSizingColumns = true; - - int expandCount = 0; - nfloat requiredWidth = 0; - nfloat expandedWidth = 0; - - // remove all spacing that isn't part of column widths - var intercellSpacingWidth = Table.IntercellSpacing.Width; - rect.Width -= intercellSpacingWidth * (Table.ColumnCount - 1); - rect.Width -= GetTableRowInsets(); + changed |= col.AutoSizeColumn(newRange, forceNewSize && !col.Expand); - foreach (var col in ColumnHandlers) + if (col.Expand) { - col.AutoSizeColumn(newRange, forceNewSize); - if (col.Expand) - { - expandCount++; - expandedWidth += col.Control.Width; - } - else - { - requiredWidth += col.Control.Width; - } + expandCount++; + expandedWidth += col.Control.Width; } - if (expandCount > 0 && !forceNewSize) + else { - var remaining = (nfloat)Math.Max(0, rect.Width - requiredWidth); - // System.Diagnostics.Debug.WriteLine($"Remaining: {remaining}, Required: {requiredWidth}, Width: {rect.Width}"); - if (remaining > 0) + requiredWidth += col.Control.Width; + } + } + + resizeExpanded |= changed; + + if (expandCount > 0 && resizeExpanded) + { + var remaining = (nfloat)Math.Max(0, rect.Width - requiredWidth); + // System.Diagnostics.Debug.WriteLine($"Remaining: {remaining}, Required: {requiredWidth}, Width: {rect.Width}"); + if (remaining > 0) + { + var each = remaining / expandCount; + + foreach (var col in ColumnHandlers) { - var each = remaining / expandCount; - - foreach (var col in ColumnHandlers) + if (col.Expand) { - if (col.Expand) - { - var existingWidth = col.Control.Width; - var weightedWidth = existingWidth / expandedWidth * remaining; - col.Control.Width = weightedWidth; - } + var existingWidth = col.Control.Width; + var weightedWidth = expandedWidth > 0 ? existingWidth / expandedWidth * remaining : each; + + changed |= existingWidth != weightedWidth; + + col.Control.Width = weightedWidth; } } } + } - if (newRange != null) - autoSizeRange = newRange.Value; - IsAutoSizingColumns = false; + if (newRange != null) + autoSizeRange = newRange.Value; + + IsAutoSizingColumns = false; + + if (forceNewSize && changed) + { InvalidateMeasure(); - return true; } + return true; } return false; } @@ -723,11 +743,8 @@ public void ResetAutoSizedColumns() void EnsureAutoSizedColumns() { - if (hasAutoSizedColumns != true && !Table.VisibleRect().IsEmpty) - { - AutoSizeColumns(true, hasAutoSizedColumns == null); - hasAutoSizedColumns = true; - } + AutoSizeColumns(true, hasAutoSizedColumns == null); + hasAutoSizedColumns = true; } public void PerformLayout() diff --git a/src/Eto.Mac/Forms/Controls/GridViewHandler.cs b/src/Eto.Mac/Forms/Controls/GridViewHandler.cs index 2f010fc9a9..3a41350bd7 100644 --- a/src/Eto.Mac/Forms/Controls/GridViewHandler.cs +++ b/src/Eto.Mac/Forms/Controls/GridViewHandler.cs @@ -100,11 +100,8 @@ public NSDragOperation DraggingSessionSourceOperationMask(NSDraggingSession sess public override void Layout() { - if (MacView.NewLayout) - base.Layout(); Handler?.PerformLayout(); - if (!MacView.NewLayout) - base.Layout(); + base.Layout(); } public override bool ValidateProposedFirstResponder(NSResponder responder, NSEvent forEvent) diff --git a/test/Eto.Test.Mac/UnitTests/GridViewTests.cs b/test/Eto.Test.Mac/UnitTests/GridViewTests.cs index 9cf4ca3e14..87f0f7eabc 100644 --- a/test/Eto.Test.Mac/UnitTests/GridViewTests.cs +++ b/test/Eto.Test.Mac/UnitTests/GridViewTests.cs @@ -27,9 +27,11 @@ class GridTestItem : TreeGridItem public IEnumerable CreateDataStore(int rows = 40) { var list = new TreeGridItemCollection(); + Image logo = TestIcons.Logo; + Image testImage = TestIcons.TestImage; for (int i = 0; i < rows; i++) { - Image image = i % 2 == 0 ? (Image)TestIcons.Logo : (Image)TestIcons.TestImage; + Image image = i % 2 == 0 ? logo : testImage; list.Add(new GridTestItem { Text = $"Item {i}", Image = image, Values = new[] { $"col {i}.2", $"col {i}.3", $"col {i}.4", $"col {i}.5" } }); } return list; diff --git a/test/Eto.Test/Sections/Controls/GridViewSection.cs b/test/Eto.Test/Sections/Controls/GridViewSection.cs index b83987b331..36f69cc593 100644 --- a/test/Eto.Test/Sections/Controls/GridViewSection.cs +++ b/test/Eto.Test/Sections/Controls/GridViewSection.cs @@ -467,7 +467,7 @@ void CreateGrid() LogEvents(grid); var dropDown = MyDropDown("DropDownKey"); - grid.Columns.Add(SetColumnState(0, new GridColumn { HeaderText = "ImageText", DataCell = new ImageTextCell("Image", "Text") })); + grid.Columns.Add(SetColumnState(0, new GridColumn { HeaderText = "ImageText", DataCell = new ImageTextCell("Image", "Text"), Expand = true })); grid.Columns.Add(SetColumnState(1, new GridColumn { DataCell = new CheckBoxCell("Check"), AutoSize = true, Resizable = false })); grid.Columns.Add(SetColumnState(2, new GridColumn { HeaderText = "Image", DataCell = new ImageViewCell("Image"), Resizable = false, HeaderToolTip = null })); grid.Columns.Add(SetColumnState(3, new GridColumn { HeaderText = "Text", DataCell = new TextBoxCell("Text"), Sortable = true, HeaderToolTip = "Some Tooltip", CellToolTipBinding = Binding.Property((MyGridItem i) => i.ToolTip) }));