Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Control.UpdateLayout() API #2382

Merged
merged 1 commit into from
Jan 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion src/Eto.Gtk/Forms/GtkControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -939,7 +939,13 @@ public PointF PointToScreen(PointF point)

public Point Location
{
get { return Control.Allocation.Location.ToEto(); }
get
{
var pt = Control.Allocation.Location.ToEto();
if (pt.X == -1 && pt.Y == -1)
return Point.Empty;
return pt;
}
}

public virtual bool ShowBorder
Expand Down Expand Up @@ -1084,5 +1090,11 @@ public void Print()
{
// ContainerControl.Print
}

public virtual void UpdateLayout()
{
// is this the best way to force a layout pass? I can't find anything else..
ContainerControl.Toplevel?.SizeAllocate(ContainerControl.Toplevel.Allocation);
}
}
}
2 changes: 2 additions & 0 deletions src/Eto.Mac/Forms/MacContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ public abstract class MacContainer<TControl, TWidget, TCallback> :
public virtual void Update()
{
InvalidateMeasure();
// ContainerControl.Superview?.LayoutSubtreeIfNeeded();
ContainerControl.Window?.LayoutIfNeeded();
}

protected override bool ControlEnabled
Expand Down
5 changes: 5 additions & 0 deletions src/Eto.Mac/Forms/MacView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1533,6 +1533,11 @@ public bool TextInputImplemented
get => Widget.Properties.Get<bool>(MacView.TextInputImplemented_Key);
private set => Widget.Properties.Set(MacView.TextInputImplemented_Key, value);
}

public virtual void UpdateLayout()
{
ContainerControl?.Window?.LayoutIfNeeded();
}
}
}

2 changes: 2 additions & 0 deletions src/Eto.WinForms/Forms/HwndFormHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -630,5 +630,7 @@ public SizeF GetPreferredSize(SizeF availableSize)
}

public void Print() => throw new NotImplementedException();

public void UpdateLayout() => throw new NotImplementedException();
}
}
2 changes: 2 additions & 0 deletions src/Eto.WinForms/Forms/WindowsControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1039,5 +1039,7 @@ public void Print()
{

}

public void UpdateLayout() => ContainerControl.PerformLayout();
}
}
9 changes: 9 additions & 0 deletions src/Eto.Wpf/Forms/WpfFrameworkElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1155,5 +1155,14 @@ internal bool PerformOnLoad(Action action)
actionList.Add(action);
return true;
}

public void UpdateLayout()
{
// allow WPF controls to actually get their Loaded event fired.
ContainerControl.Dispatcher.Invoke(new Action(() => { }), sw.Threading.DispatcherPriority.ApplicationIdle, null);

// update the layout
ContainerControl.UpdateLayout();
}
}
}
24 changes: 24 additions & 0 deletions src/Eto/Forms/Controls/Control.cs
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,19 @@ public void Invalidate(Rectangle rect, bool invalidateChildren)
Handler.Invalidate(rect, invalidateChildren);
}

/// <summary>
/// Updates the layout of this control if necessary.
/// </summary>
/// <remarks>
/// This will ensure the control has had all of its layout applied so you can use its position and size right after this call.
/// Most platforms (except WinForms) use a deferred layout system so that after adding your control to the form dynamically it won't
/// get laid out until the next idle loop.
/// This is useful when you need to know the dimensions of the control immediately.
/// Note that this can be an expensive operation, so it is recommended to only call this method when necessary and after all of the
/// controls have been added/updated.
/// </remarks>
public void UpdateLayout() => Handler.UpdateLayout();

/// <summary>
/// Gets or sets the size of the control. Use -1 to specify auto sizing for either the width and/or height.
/// </summary>
Expand Down Expand Up @@ -2007,6 +2020,17 @@ public void OnEnabledChanged(Control widget, EventArgs e)
/// <param name="availableSize">The available size to determine the preferred size</param>
/// <returns>The preferred size this control would like to be, which can be larger than the specified <paramref name="availableSize" />.</returns>
SizeF GetPreferredSize(SizeF availableSize);

/// <summary>
/// Updates the layout of this control if necessary.
/// </summary>
/// <remarks>
/// This will ensure the control has had all of its layout applied so you can use its position and size right after this call.
/// Most platforms (except WinForms) use a deferred layout system so that after adding your control to the form dynamically it won't
/// get laid out until the next idle loop.
/// This is useful when you need to know the dimensions of the control immediately.
/// </remarks>
void UpdateLayout();
}
#endregion
}
Expand Down
3 changes: 3 additions & 0 deletions src/Eto/Forms/Controls/ThemedControlHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,9 @@ public override void AttachEvent(string id)
/// <inheritdoc />
public void Print() => Control.Print();

/// <inheritdoc />
public void UpdateLayout() => Control.UpdateLayout();

#endregion

}
Expand Down
82 changes: 82 additions & 0 deletions test/Eto.Test/UnitTests/Forms/Layout/LayoutTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using System;
using Eto.Drawing;
using Eto.Forms;
using NUnit.Framework;

namespace Eto.Test.UnitTests.Forms.Layout
{
[TestFixture]
public class LayoutTests : TestBase
{
[Test]
public void UpdateLayoutShouldSetAllSizes()
{
Panel holder = null;
TableLayout table = null;
Shown(form =>
{
holder = new Panel { Size = new Size(100, 100) };
table = new TableLayout
{
Rows =
{
new TableRow(new Panel { Size = new Size(100, 100)}),
new TableRow(new TableCell(), holder)
}
};
form.Content = table;
},
() =>
{
holder.SuspendLayout();

var control = new Panel();
control.Content = "Hello then!";
Assert.LessOrEqual(control.Width, 0, "#1.1");
Assert.LessOrEqual(control.Height, 0, "#1.2");
Assert.AreEqual(new Point(0, 0), control.Location, "#1.3");

holder.Content = control;

// layout is suspended or deferred so nothing is set up yet
// Gtk is annoying and returns 1,1 for size at this stage, others return 0,0.
Assert.LessOrEqual(control.Width, 1, "#2.1");
Assert.LessOrEqual(control.Height, 1, "#2.2");

// macOS gives us a "flipped" view of the location at this point..
// the value of Location isn't valid here anyway.
if (!Platform.Instance.IsMac)
Assert.AreEqual(new Point(0, 0), control.Location, "#2.3");

holder.ResumeLayout();

if (!Platform.Instance.IsWinForms)
{
// Gtk, Wpf, and Mac all use deferred layouts so it still isn't set up here.
Assert.LessOrEqual(control.Width, 1, "#3.1");
Assert.LessOrEqual(control.Height, 1, "#3.2");

if (!Platform.Instance.IsMac)
Assert.AreEqual(new Point(0, 0), control.Location, "#3.3");
}
else
{
// At this point WinForms is all set up.
Assert.AreEqual(100, control.Width, "#3.1");
Assert.AreEqual(100, control.Height, "#3.2");
Assert.AreEqual(new Point(0, 0), control.Location, "#3.3");
Assert.AreEqual(new Point(100, 100), Point.Round(table.PointFromScreen(control.PointToScreen(PointF.Empty))), "#3.4");
}

// force a layout
holder.UpdateLayout();

// everything should now be set up as we expect.
Assert.AreEqual(100, control.Width, "#4.1");
Assert.AreEqual(100, control.Height, "#4.2");
Assert.AreEqual(new Point(0, 0), control.Location, "#4.3");
Assert.AreEqual(new Point(100, 100), Point.Round(table.PointFromScreen(control.PointToScreen(PointF.Empty))), "#4.4");
});
}
}
}