Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/xwt.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Testing/GtkTestRunner.csproj1
-rw-r--r--Testing/Tests/InternalChildrenTests.cs211
-rw-r--r--Xwt.Mac/Xwt.Mac/ViewBackend.cs4
-rw-r--r--Xwt/Xwt.Backends/BackendHost.cs2
-rw-r--r--Xwt/Xwt.Backends/ExtensionMethods.cs5
-rw-r--r--Xwt/Xwt/Canvas.cs15
-rw-r--r--Xwt/Xwt/FrameBox.cs9
-rw-r--r--Xwt/Xwt/Widget.cs132
8 files changed, 341 insertions, 38 deletions
diff --git a/Testing/GtkTestRunner.csproj b/Testing/GtkTestRunner.csproj
index 62ff5717..8e060068 100644
--- a/Testing/GtkTestRunner.csproj
+++ b/Testing/GtkTestRunner.csproj
@@ -106,6 +106,7 @@
<Compile Include="Tests\WidgetTests.cs" />
<Compile Include="Tests\WindowTests.cs" />
<Compile Include="Tests\XwtTest.cs" />
+ <Compile Include="Tests\InternalChildrenTests.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
diff --git a/Testing/Tests/InternalChildrenTests.cs b/Testing/Tests/InternalChildrenTests.cs
new file mode 100644
index 00000000..9a6a7a73
--- /dev/null
+++ b/Testing/Tests/InternalChildrenTests.cs
@@ -0,0 +1,211 @@
+//
+// InternalChildrenTests.cs
+//
+// Author:
+// Lluis Sanchez <lluis@xamarin.com>
+//
+// Copyright (c) 2013 Xamarin Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.Linq;
+using NUnit.Framework;
+
+namespace Xwt
+{
+ public class InternalChildrenTests
+ {
+ [Test]
+ public void ChildrenExcludesInternal ()
+ {
+ MyContainer co = new MyContainer ();
+ var c1 = new Label ("hi1");
+ var c2 = new Label ("hi2");
+ Assert.AreEqual (0, co.Surface.Children.Count ());
+ co.Add (c1);
+ Assert.AreEqual (1, co.Surface.Children.Count ());
+ co.AddInner (c2);
+ Assert.AreEqual (2, co.Surface.Children.Count ());
+ Assert.IsTrue (co.Surface.Children.Contains (c1));
+ Assert.IsTrue (co.Surface.Children.Contains (c2));
+ }
+
+ [Test]
+ public void ParentIsSet ()
+ {
+ MyContainer co = new MyContainer ();
+ var c1 = new Label ("hi1");
+ var c2 = new Label ("hi2");
+ co.Add (c1);
+ co.AddInner (c2);
+ Assert.AreSame (co, c1.Parent);
+ Assert.AreSame (co, c2.Parent);
+ }
+
+ [Test]
+ [ExpectedException (typeof(InvalidOperationException))]
+ public void InvalidAdd1 ()
+ {
+ MyContainer co = new MyContainer ();
+ var c1 = new Label ("hi1");
+ co.InternalAdd (c1);
+ }
+
+ [Test]
+ [ExpectedException (typeof(InvalidOperationException))]
+ public void InvalidAdd2 ()
+ {
+ MyContainer co = new MyContainer ();
+ var c1 = new Label ("hi1");
+ HBox b = new HBox ();
+ b.PackStart (c1);
+ co.InternalAdd (c1);
+ }
+
+ [Test]
+ [ExpectedException (typeof(InvalidOperationException))]
+ public void InvalidAdd3 ()
+ {
+ MyContainer co = new MyContainer ();
+ var c1 = new Label ("hi1");
+ HBox b = new HBox ();
+ b.PackStart (c1);
+ co.Add (c1);
+ }
+
+ [Test]
+ public void Remove ()
+ {
+ MyContainer co = new MyContainer ();
+ var c1 = new Label ("hi1");
+ var c2 = new Label ("hi2");
+ co.Add (c1);
+ co.AddInner (c2);
+ Assert.AreEqual (2, co.Surface.Children.Count ());
+ co.RemoveInner (c2);
+ Assert.AreEqual (1, co.Surface.Children.Count ());
+ Assert.IsNull (c2.Parent);
+ co.Remove (c1);
+ Assert.AreEqual (0, co.Surface.Children.Count ());
+ Assert.IsNull (c1.Parent);
+ }
+
+ [Test]
+ [ExpectedException (typeof(InvalidOperationException))]
+ public void InvalidRemove ()
+ {
+ MyContainer co = new MyContainer ();
+ var c1 = new Label ("hi1");
+ co.Add (c1);
+ co.WrongRemove (c1);
+ }
+
+ [Test]
+ public void InternalRemoveAdd ()
+ {
+ // The a child can be removed from internal containers and still
+ // be a child of the container
+
+ MyContainer co = new MyContainer ();
+ Assert.AreEqual (0, co.Surface.Children.Count ());
+
+ var c1 = new Label ("hi1");
+ co.Add (c1);
+ Assert.AreEqual (1, co.Surface.Children.Count ());
+
+ co.InternalRemove (c1);
+ Assert.AreEqual (1, co.Surface.Children.Count ());
+ Assert.AreSame (co, c1.Parent);
+
+ co.InternalAdd (c1);
+ Assert.AreEqual (1, co.Surface.Children.Count ());
+ Assert.AreSame (co, c1.Parent);
+ }
+
+ [Test]
+ [ExpectedException (typeof(InvalidOperationException))]
+ public void CantSetIternalBeforeAdding ()
+ {
+ new MyContainerWrong1 ();
+ }
+ }
+
+ class MyContainer: Widget
+ {
+ internal HBox box = new HBox ();
+ internal VBox inner = new VBox ();
+
+ public MyContainer ()
+ {
+ Content = SetInternalChild (box);
+ box.PackStart (SetInternalChild (inner));
+ }
+
+ public void Add (Widget w)
+ {
+ RegisterChild (w);
+ box.PackStart (w);
+ }
+
+ public void AddInner (Widget w)
+ {
+ RegisterChild (w);
+ inner.PackStart (w);
+ }
+
+ public void Remove (Widget w)
+ {
+ box.Remove (w);
+ UnregisterChild (w);
+ }
+
+ public void RemoveInner (Widget w)
+ {
+ inner.Remove (w);
+ UnregisterChild (w);
+ }
+
+ public void WrongRemove (Widget w)
+ {
+ UnregisterChild (w);
+ box.Remove (w);
+ }
+
+ public void InternalRemove (Widget w)
+ {
+ box.Remove (w);
+ }
+
+ public void InternalAdd (Widget w)
+ {
+ box.PackStart (w);
+ }
+ }
+
+ class MyContainerWrong1: Widget
+ {
+ public MyContainerWrong1 ()
+ {
+ HBox box = new HBox ();
+ Content = box;
+ SetInternalChild (box);
+ }
+ }
+}
+
diff --git a/Xwt.Mac/Xwt.Mac/ViewBackend.cs b/Xwt.Mac/Xwt.Mac/ViewBackend.cs
index 443fc25b..62bf29ad 100644
--- a/Xwt.Mac/Xwt.Mac/ViewBackend.cs
+++ b/Xwt.Mac/Xwt.Mac/ViewBackend.cs
@@ -327,8 +327,8 @@ namespace Xwt.Mac
public static void ReplaceSubview (NSView oldChild, NSView newChild)
{
var vo = oldChild as IViewObject;
- if (vo != null && vo.Backend.Frontend.Parent != null) {
- var ba = vo.Backend.Frontend.Parent.GetBackend () as ViewBackend;
+ if (vo != null && vo.Backend.Frontend.GetInternalParent () != null) {
+ var ba = vo.Backend.Frontend.GetInternalParent ().GetBackend () as ViewBackend;
if (ba != null) {
ba.ReplaceChild (oldChild, newChild);
return;
diff --git a/Xwt/Xwt.Backends/BackendHost.cs b/Xwt/Xwt.Backends/BackendHost.cs
index d7a07433..368b0912 100644
--- a/Xwt/Xwt.Backends/BackendHost.cs
+++ b/Xwt/Xwt.Backends/BackendHost.cs
@@ -99,7 +99,7 @@ namespace Xwt.Backends
return EngineBackend.CreateBackendForFrontend (Parent.GetType ());
}
- public void EnsureBackendLoaded ()
+ internal void EnsureBackendLoaded ()
{
if (backend == null)
LoadBackend ();
diff --git a/Xwt/Xwt.Backends/ExtensionMethods.cs b/Xwt/Xwt.Backends/ExtensionMethods.cs
index c24a5708..bba9ba4c 100644
--- a/Xwt/Xwt.Backends/ExtensionMethods.cs
+++ b/Xwt/Xwt.Backends/ExtensionMethods.cs
@@ -70,6 +70,11 @@ namespace Xwt.Backends
return 0;
}
}
+
+ public static Widget GetInternalParent (this Widget widget)
+ {
+ return widget.InternalParent;
+ }
}
}
diff --git a/Xwt/Xwt/Canvas.cs b/Xwt/Xwt/Canvas.cs
index 8bd6e512..1956dddc 100644
--- a/Xwt/Xwt/Canvas.cs
+++ b/Xwt/Xwt/Canvas.cs
@@ -115,8 +115,8 @@ namespace Xwt
positions [widget] = bounds;
var bk = (IWidgetBackend)Widget.GetBackend (widget);
- Backend.AddChild (bk, bounds);
RegisterChild (widget);
+ Backend.AddChild (bk, bounds);
}
/// <summary>
@@ -128,12 +128,9 @@ namespace Xwt
/// <exception cref="System.ArgumentException">If the widget is not a child of this canvas</exception>
public void RemoveChild (Widget widget)
{
- if (positions == null || widget.Parent != this)
- throw new ArgumentException ("Widget is not a child of the canvas");
-
+ UnregisterChild (widget);
positions.Remove (widget);
Backend.RemoveChild ((IWidgetBackend)Widget.GetBackend (widget));
- UnregisterChild (widget);
}
/// <summary>
@@ -148,7 +145,7 @@ namespace Xwt
/// <exception cref="System.ArgumentException">If the widget is not a child of this canvas</exception>
public void SetChildBounds (Widget widget, Rectangle bounds)
{
- if (positions == null || widget.Parent != this)
+ if (positions == null || !positions.ContainsKey (widget))
throw new ArgumentException ("Widget is not a child of the canvas");
positions [widget] = bounds;
@@ -178,11 +175,9 @@ namespace Xwt
public Rectangle GetChildBounds (Widget widget)
{
Rectangle rect;
- if (positions == null || widget.Parent != this)
- throw new ArgumentException ("Widget is not a child of the canvas");
- if (positions.TryGetValue (widget, out rect))
+ if (positions != null && positions.TryGetValue (widget, out rect))
return rect;
- return Rectangle.Zero;
+ throw new ArgumentException ("Widget is not a child of the canvas");
}
protected override BackendHost CreateBackendHost ()
diff --git a/Xwt/Xwt/FrameBox.cs b/Xwt/Xwt/FrameBox.cs
index 0f9c3e7d..26aad8fd 100644
--- a/Xwt/Xwt/FrameBox.cs
+++ b/Xwt/Xwt/FrameBox.cs
@@ -126,7 +126,8 @@ namespace Xwt
public FrameBox ()
{
- base.Content = canvas = new FrameCanvas ();
+ canvas = SetInternalChild (new FrameCanvas ());
+ base.Content = canvas;
}
public FrameBox (Widget content): this ()
@@ -231,7 +232,11 @@ namespace Xwt
[DefaultValue (null)]
public new Widget Content {
get { return canvas.Child; }
- set { canvas.Child = value; }
+ set {
+ UnregisterChild (canvas.Child);
+ RegisterChild (value);
+ canvas.Child = value;
+ }
}
}
}
diff --git a/Xwt/Xwt/Widget.cs b/Xwt/Xwt/Widget.cs
index f009d62d..f63ec40a 100644
--- a/Xwt/Xwt/Widget.cs
+++ b/Xwt/Xwt/Widget.cs
@@ -283,8 +283,10 @@ namespace Xwt
if (disposing) {
if (BackendHost.BackendCreated)
Backend.Dispose ();
- if (children != null)
- children.ForEach (c => c.Dispose ());
+ if (children != null) {
+ foreach (var c in DirectChildren)
+ c.Dispose ();
+ }
}
}
@@ -467,7 +469,15 @@ namespace Xwt
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
public Widget Parent { get; private set; }
-
+
+ internal Widget InternalParent { get; private set; }
+
+ bool IsInternalChild {
+ get { return ExternalParent != null; }
+ }
+
+ Widget ExternalParent { get; set; }
+
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
public IWidgetSurface Surface {
get { return this; }
@@ -1019,7 +1029,7 @@ namespace Xwt
OnReallocate ();
if (children != null && !BackendHost.EngineBackend.HandlesSizeNegotiation) {
- foreach (Widget w in children) {
+ foreach (Widget w in DirectChildren) {
if (w.Visible)
w.Surface.Reallocate ();
}
@@ -1053,7 +1063,7 @@ namespace Xwt
{
OnChildPreferredSizeChanged ();
- if (Parent != null && resizeRequestQueue.Contains (Parent)) {
+ if (InternalParent != null && resizeRequestQueue.Contains (InternalParent)) {
// Size for this widget will be checked when checking the parent
ResetCachedSizes ();
return;
@@ -1095,8 +1105,8 @@ namespace Xwt
internal void OnPlacementChanged ()
{
- if (Parent != null)
- Parent.OnChildPlacementChanged (this);
+ if (InternalParent != null)
+ InternalParent.OnChildPlacementChanged (this);
else if (parentWindow is Window)
((Window)parentWindow).OnChildPlacementChanged (this);
}
@@ -1126,8 +1136,8 @@ namespace Xwt
void NotifySizeChangeToParent ()
{
- if (Parent != null) {
- QueueForSizeCheck (Parent);
+ if (InternalParent != null) {
+ QueueForSizeCheck (InternalParent);
QueueDelayedResizeRequest ();
}
else if (parentWindow is Window) {
@@ -1220,16 +1230,16 @@ namespace Xwt
int Depth {
get {
- if (Parent != null)
- return Parent.Depth + 1;
+ if (InternalParent != null)
+ return InternalParent.Depth + 1;
return 0;
}
}
string GetWidgetDesc ()
{
- if (Parent != null) {
- int i = Parent.Surface.Children.ToList ().IndexOf (this);
+ if (InternalParent != null) {
+ int i = InternalParent.Surface.Children.ToList ().IndexOf (this);
return this + " [" + GetHashCode() + "] (" + i + ")";
}
else
@@ -1243,21 +1253,57 @@ namespace Xwt
IEnumerable<Widget> IWidgetSurface.Children {
get {
- return (IEnumerable<Widget>)children ?? (IEnumerable<Widget>) emptyList;
+ return ExternalChildren;
}
}
+ IEnumerable<Widget> DirectChildren {
+ get { return children != null ? children.Where (c => c.InternalParent == this) : emptyList; }
+ }
+
+ IEnumerable<Widget> ExternalChildren {
+ get { return children != null ? children.Where (c => !c.IsInternalChild) : emptyList; }
+ }
+
+ Widget FindExternalParent ()
+ {
+ if (IsInternalChild && Parent != null)
+ return Parent.FindExternalParent ();
+ else
+ return this;
+ }
+
protected void RegisterChild (Widget w)
{
- if (w.Parent != null)
- throw new InvalidOperationException ("Widget is already a child of another widget");
+ if (w == null)
+ return;
+
if (w.Surface.ToolkitEngine != Surface.ToolkitEngine)
throw new InvalidOperationException ("Widget belongs to a different toolkit");
+
+ var wback = w.Backend as XwtWidgetBackend;
+
+ if (IsInternalChild && !w.IsInternalChild) {
+ if (w.Parent == null)
+ throw new InvalidOperationException ("Widget must be registered as a child widget of " + FindExternalParent ());
+ if (w.Parent != ExternalParent)
+ throw new InvalidOperationException ("Widget is already a child of a widget of type " + w.Parent.GetType ());
+ w.InternalParent = this;
+ if (wback != null)
+ wback.InternalParent = this;
+ } else {
+ if (w.Parent != null)
+ throw new InvalidOperationException ("Widget is already a child of a widget of type " + w.Parent.GetType ());
+ w.Parent = this;
+ w.InternalParent = this;
+ if (wback != null) {
+ wback.Parent = this;
+ wback.InternalParent = this;
+ }
+ }
+
if (children == null)
children = new List<Widget> ();
- w.Parent = this;
- if (w.Backend is XwtWidgetBackend)
- ((XwtWidgetBackend)w.Backend).Parent = this;
children.Add (w);
// Make sure the widget is queued for reallocation
@@ -1266,11 +1312,51 @@ namespace Xwt
protected void UnregisterChild (Widget w)
{
- if (children == null || !children.Remove (w))
+ if (w == null)
+ return;
+
+ int i;
+ if (children == null || (i = children.IndexOf (w)) == -1)
throw new InvalidOperationException ("Widget is not a child of this widget");
- w.Parent = null;
- if (w.Backend is XwtWidgetBackend)
- ((XwtWidgetBackend)w.Backend).Parent = null;
+
+ var wback = w.Backend as XwtWidgetBackend;
+
+ if (w.Parent == this) {
+ if (w.InternalParent != this)
+ throw new InvalidOperationException ("Child widget must be removed from internal container before unregistering it");
+ w.Parent = null;
+ w.InternalParent = null;
+ } else {
+ w.InternalParent = w.Parent;
+ }
+
+ children.RemoveAt (i);
+
+ if (wback != null) {
+ wback.Parent = w.Parent;
+ wback.InternalParent = w.InternalParent;
+ }
+ }
+
+ /// <summary>
+ /// Flags a widget as an internal child of a container
+ /// </summary>
+ /// <param name="child">A widget</param>
+ /// <remarks>
+ /// This method must must be called before the child widget is added to any container.
+ /// Internal children of a widget are not returned in the Children list of the widget, and they
+ /// are not included in the Parent hierarchy chain.
+ /// </remarks>
+ protected T SetInternalChild<T> (T child) where T:Widget
+ {
+ if (child.ExternalParent == this)
+ return child;
+ if (child.ExternalParent != null)
+ throw new InvalidOperationException ("Widget is already an internal child of widget " + child.ExternalParent);
+ if (child.Parent != null)
+ throw new InvalidOperationException ("Widget must be flagged as internal child before being added to a container");
+ child.ExternalParent = this;
+ return child;
}
void IAnimatable.BatchBegin ()