Skip to content

Commit

Permalink
Add GridColumn Expand, Min/MaxWidth, and HeaderTextAlignment
Browse files Browse the repository at this point in the history
Gtk/WinForms/Mac: Tree/GridView now can autosize to its content
Mac: Fix showing expanders when TreeGridView is initially populated (in some cases)
  • Loading branch information
cwensley committed Jan 25, 2021
1 parent ff35df2 commit 252d475
Show file tree
Hide file tree
Showing 16 changed files with 712 additions and 140 deletions.
17 changes: 15 additions & 2 deletions src/Eto.Gtk/Forms/Controls/GridColumnHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ public bool Editable
public int Width
{
get { return Control.Width; }
set
{
set
{
autoSize = value == -1;
Control.FixedWidth = value;
Control.Sizing = autoSize ? Gtk.TreeViewColumnSizing.GrowOnly : Gtk.TreeViewColumnSizing.Fixed;
Expand Down Expand Up @@ -182,6 +182,19 @@ Gtk.TreeViewColumn IGridColumnHandler.Control
{
get { return Control; }
}

public bool Expand
{
get => Control.Expand;
set => Control.Expand = value;
}
public TextAlignment HeaderTextAlignment
{
get => GtkConversions.ToEtoAlignment(Control.Alignment);
set => Control.Alignment = value.ToAlignment();
}
public int MinWidth { get => Control.MinWidth; set => Control.MinWidth = value; }
public int MaxWidth { get => Control.MaxWidth; set => Control.MaxWidth = value; }
}
}

49 changes: 47 additions & 2 deletions src/Eto.Gtk/Forms/Controls/GridHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,56 @@ public abstract class GridHandler<TWidget, TCallback> : GtkControl<Gtk.ScrolledW

public override Gtk.Widget DragControl => Tree;

class EtoScrolledWindow : Gtk.ScrolledWindow
{
WeakReference handler;
public IGtkControl Handler { get => handler?.Target as IGtkControl; set => handler = new WeakReference(value); }

#if GTKCORE

protected override void OnGetPreferredWidth(out int minimum_width, out int natural_width)
{
base.OnGetPreferredWidth(out minimum_width, out natural_width);

var h = Handler;
if (h != null)
{
var size = h.UserPreferredSize;
if (size.Width >= 0)
{
natural_width = Math.Min(size.Width, natural_width);
minimum_width = Math.Min(size.Width, minimum_width);
}
}
}

protected override void OnGetPreferredHeight(out int minimum_height, out int natural_height)
{
base.OnGetPreferredHeight(out minimum_height, out natural_height);
var h = Handler;
if (h != null)
{
var size = h.UserPreferredSize;
if (size.Height >= 0)
{
natural_height = Math.Min(size.Height, natural_height);
minimum_height = Math.Min(size.Height, minimum_height);
}
}
}
#endif
}

protected GridHandler()
{
Control = new Gtk.ScrolledWindow
Control = new EtoScrolledWindow
{
ShadowType = Gtk.ShadowType.In
Handler = this,
ShadowType = Gtk.ShadowType.In,
#if GTKCORE
PropagateNaturalHeight = true,
PropagateNaturalWidth = true
#endif
};
}

Expand Down
11 changes: 11 additions & 0 deletions src/Eto.Gtk/GtkConversions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,17 @@ public static Pango.Alignment ToPango(this TextAlignment alignment)
}
}

public static TextAlignment ToEtoAlignment(float align)
{
if (align == 0f)
return TextAlignment.Left;
else if (align == 0.5f)
return TextAlignment.Center;
else if (align == 1f)
return TextAlignment.Right;
return TextAlignment.Left;
}

public static float ToAlignment(this TextAlignment alignment)
{
switch (alignment)
Expand Down
48 changes: 44 additions & 4 deletions src/Eto.Mac/Forms/Controls/GridColumnHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,15 @@ public interface IDataViewHandler
bool IsCancellingEdit { get; }
}

public interface IDataColumnHandler
public interface IDataColumnHandler : GridColumn.IHandler
{
void Setup(IDataViewHandler handler, int column);

NSObject GetObjectValue(object dataItem);

void SetObjectValue(object dataItem, NSObject val);

GridColumn Widget { get; }
new GridColumn Widget { get; }

IDataViewHandler DataViewHandler { get; }

Expand All @@ -81,6 +81,7 @@ public interface IDataColumnHandler
void EnabledChanged(bool value);

nfloat GetPreferredWidth(NSRange? range = null);
void SizeToFit();
}

public class GridColumnHandler : MacObject<NSTableColumn, GridColumn, GridColumn.ICallback>, GridColumn.IHandler, IDataColumnHandler
Expand Down Expand Up @@ -112,14 +113,18 @@ protected override void Initialize()
HeaderText = string.Empty;
Editable = false;
AutoSize = true;
Control.Width = Control.MinWidth;
DataCell = new TextBoxCell();
base.Initialize();
}

public void AutoSizeColumn(NSRange rowRange, bool force = false)
{
var handler = DataViewHandler;
if (AutoSize && handler != null)
if (handler == null)
return;

if (AutoSize)
{
var width = GetPreferredWidth(rowRange);
if (force || width > Control.Width)
Expand All @@ -131,9 +136,17 @@ public nfloat GetPreferredWidth(NSRange? range = null)
{
var handler = DataViewHandler;
nfloat width = 0;

if (!AutoSize)
return Width;

var outlineView = handler.Table as NSOutlineView;
if (handler.ShowHeader)
{
width = (nfloat)Math.Max(Control.HeaderCell.CellSizeForBounds(new CGRect(0, 0, int.MaxValue, int.MaxValue)).Width, width);
if (outlineView != null && Column == 0)
width += (float)outlineView.IndentationPerLevel;
}

if (dataCell != null)
{
Expand Down Expand Up @@ -217,9 +230,15 @@ public int Width
{
get => (int)Math.Ceiling(Control.Width) + 3;
set
{
{
AutoSize = value == -1;
Control.Width = Math.Max(0, value - 3);

var table = Control.TableView;
if (DataViewHandler != null && DataViewHandler.Loaded && table != null)
{
table.SizeToFit();
}
}
}

Expand Down Expand Up @@ -290,6 +309,25 @@ public Font Font
}
}

public bool Expand
{
get => Control.ResizingMask.HasFlag(NSTableColumnResizing.Autoresizing);
set
{
if (value)
Control.ResizingMask |= NSTableColumnResizing.Autoresizing;
else
Control.ResizingMask &= ~NSTableColumnResizing.Autoresizing;
}
}
public TextAlignment HeaderTextAlignment
{
get => Control.HeaderCell.Alignment.ToEto();
set => Control.HeaderCell.Alignment = value.ToNS();
}
public int MinWidth { get => (int)Control.MinWidth; set => Control.MinWidth = value; }
public int MaxWidth { get => (int)Control.MaxWidth; set => Control.MaxWidth = value; }

public void EnabledChanged(bool value)
{
if (dataCell != null)
Expand All @@ -298,6 +336,8 @@ public void EnabledChanged(bool value)
cellHandler.EnabledChanged(value);
}
}

public void SizeToFit() => Control.SizeToFit();
}
}

69 changes: 51 additions & 18 deletions src/Eto.Mac/Forms/Controls/GridHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public interface IGridHandler : IMacViewHandler

NSTableView Table { get; }

bool AutoSizeColumns(bool force);
bool AutoSizeColumns(bool force, bool forceNewSize = false);
void PerformLayout();
}

Expand All @@ -72,7 +72,7 @@ public override void SetFrameSize(CGSize newSize)

if (!autoSized)
{
autoSized = h.AutoSizeColumns(false);
autoSized = h.AutoSizeColumns(false, true);
}
h.OnSizeChanged(EventArgs.Empty);
h.Callback.OnSizeChanged(h.Widget, EventArgs.Empty);
Expand Down Expand Up @@ -345,18 +345,14 @@ public override void OnLoadComplete(EventArgs e)
base.OnLoadComplete(e);
UpdateColumns();

if (!Widget.Properties.Get<bool>(GridHandler.ScrolledToRow_Key))
// Yosemite bug: hides first row when DataStore is set before control is visible
Control.ScrollRowToVisible(0);
else
Widget.Properties.Remove(GridHandler.ScrolledToRow_Key);
var row = Widget.Properties.Get<int>(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);
}

NSRange autoSizeRange;

public bool AutoSizeColumns(bool force) => AutoSizeColumns(force, false);

public bool AutoSizeColumns(bool force, bool forceNewSize)
public bool AutoSizeColumns(bool force, bool forceNewSize = false)
{
if (Widget.Loaded)
{
Expand Down Expand Up @@ -434,8 +430,13 @@ public virtual IEnumerable<int> SelectedRows
UnselectAll();
if (value != null)
{
var indexes = NSIndexSet.FromArray(value.ToArray());
Control.SelectRows(indexes, AllowMultipleSelection);
var rows = value.ToArray();
if (rows.Length > 0)
{
var indexes = NSIndexSet.FromArray(rows);
Control.SelectRows(indexes, AllowMultipleSelection);
ScrollToRow(rows[0]);
}
}
SuppressSelectionChanged--;
if (SuppressSelectionChanged == 0)
Expand Down Expand Up @@ -547,12 +548,19 @@ public CGRect GetVisibleRect()

protected override SizeF GetNaturalSize(SizeF availableSize)
{
var width = Widget.Columns.Sum(r => r.Width);
EnsureAutoSizedColumns();
var width = (int)ColumnHandlers.Sum(r => r.Width);
if (width == 0)
width = 100;
var height = RowHeight * 4;

if (Border != BorderType.None)
width += 4;

width -= 2; // it's okay for the last divider to be hidden

var height = (RowHeight + 2) * RowCount;
if (ShowHeader)
height += 2 + (int)Control.HeaderView.Frame.Height;
return new Size(width, height);
}

Expand All @@ -563,9 +571,10 @@ public void OnCellFormatting(GridCellFormatEventArgs args)

public void ScrollToRow(int row)
{
Control.ScrollRowToVisible(row);
if (!Widget.Loaded)
Widget.Properties[GridHandler.ScrolledToRow_Key] = true;
Widget.Properties[GridHandler.ScrolledToRow_Key] = row;
else
Control.ScrollRowToVisible(row);
}

public bool Loaded => Widget.Loaded;
Expand Down Expand Up @@ -629,14 +638,23 @@ void IDataViewHandler.OnCellEdited(GridViewCellEventArgs e)
bool hasAutoSizedColumns;
protected void ResetAutoSizedColumns() => hasAutoSizedColumns = false;

public void PerformLayout()
void EnsureAutoSizedColumns()
{
if (!hasAutoSizedColumns && Widget.Loaded && !Table.VisibleRect().IsNull())
if (!hasAutoSizedColumns && !Table.VisibleRect().IsEmpty)
{
AutoSizeColumns(true, true);
var width = ColumnHandlers.Sum(r => r.Width);
if (width < Table.Frame.Width && Widget.Columns.Any(r => r.Expand))
{
Control.SizeToFit();
}
hasAutoSizedColumns = true;
}
}

public void PerformLayout()
{
EnsureAutoSizedColumns();
}

public bool IsEditing => Widget.Properties.Get(GridHandler.IsEditing_Key, Control.EditedRow != -1 && Control.EditedColumn != -1);
Expand Down Expand Up @@ -676,6 +694,21 @@ public override void DoDragDrop(DataObject data, DragEffects allowedAction, Imag
base.DoDragDrop(data, allowedAction, image, origin);
}
}

protected void ColumnDidResize(NSNotification notification)
{
if (!IsAutoSizingColumns && Widget.Loaded && hasAutoSizedColumns)
{
// when the user resizes the column, don't autosize anymore when data/scroll changes
var column = notification.UserInfo["NSTableColumn"] as NSTableColumn;
if (column != null)
{
var colHandler = GetColumn(column);
colHandler.AutoSize = false;
InvalidateMeasure();
}
}
}
}
}

13 changes: 2 additions & 11 deletions src/Eto.Mac/Forms/Controls/GridViewHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ public EtoTableView(GridViewHandler handler)
FocusRingType = NSFocusRingType.None;
DataSource = new EtoTableViewDataSource { Handler = handler };
Delegate = new EtoTableDelegate { Handler = handler };
ColumnAutoresizingStyle = NSTableViewColumnAutoresizingStyle.None;
ColumnAutoresizingStyle = NSTableViewColumnAutoresizingStyle.Uniform;
SetDraggingSourceOperationMask(NSDragOperation.All, true);
SetDraggingSourceOperationMask(NSDragOperation.All, false);
}
Expand Down Expand Up @@ -416,16 +416,7 @@ public override void DidClickTableColumn(NSTableView tableView, NSTableColumn ta

public override void ColumnDidResize(NSNotification notification)
{
if (!Handler.IsAutoSizingColumns)
{
// when the user resizes the column, don't autosize anymore when data/scroll changes
var column = notification.UserInfo["NSTableColumn"] as NSTableColumn;
if (column != null)
{
var colHandler = Handler.GetColumn(column);
colHandler.AutoSize = false;
}
}
Handler?.ColumnDidResize(notification);
}

public override NSView GetViewForItem(NSTableView tableView, NSTableColumn tableColumn, nint row)
Expand Down
Loading

0 comments on commit 252d475

Please sign in to comment.