public static bool RecurseWindowlessControls(IWindowlessControl control, WindowlessControlRecursionHandler pre, WindowlessControlRecursionHandler post) { if (pre != null) { if (!pre(control)) { return(false); } } if (control.Controls != null) { foreach (IWindowlessControl child in control.Controls) { if (!RecurseWindowlessControls(child, pre, post)) { return(false); } } } if (post != null) { if (!post(control)) { return(false); } } return(true); }
public void Connect(IWindowlessControl control) { Type type = GetType(); WindowlessControlHost.RecurseWindowlessControls(control, (current) => { if (string.IsNullOrEmpty(current.Name)) { return(true); } PropertyInfo prop = type.GetProperty(current.Name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); if (prop != null) { if (prop.GetValue(this, null) != null) { throw new InvalidOperationException(string.Format("Unable to connect to property {0}. The value is non-null", current.Name)); } prop.SetValue(this, current, null); return(true); } FieldInfo field = type.GetField(current.Name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); if (field != null) { if (field.GetValue(this) != null) { throw new InvalidOperationException(string.Format("Unable to connect to field {0}. The value is non-null", current.Name)); } field.SetValue(this, current); } return(true); }, null); }
protected void LayoutControl(IWindowlessControl control, Rectangle rect) { switch (control.HorizontalAlignment) { case HorizontalAlignment.Stretch: case HorizontalAlignment.Left: control.Left = rect.Left; break; case HorizontalAlignment.Center: control.Left = (rect.Left + rect.Right - control.Width) / 2; break; case HorizontalAlignment.Right: control.Left = rect.Right - control.Width; break; } switch (control.VerticalAlignment) { case VerticalAlignment.Stretch: case VerticalAlignment.Top: control.Top = rect.Top; break; case VerticalAlignment.Center: control.Top = (rect.Top + rect.Bottom - control.Height) / 2; break; case VerticalAlignment.Bottom: control.Top = rect.Bottom - control.Height; break; } }
public void Connect(IWindowlessControl control) { Type type = GetType(); WindowlessControlHost.RecurseWindowlessControls(control, (current) => { if (string.IsNullOrEmpty(current.Name)) return true; PropertyInfo prop = type.GetProperty(current.Name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); if (prop != null) { if (prop.GetValue(this, null) != null) throw new InvalidOperationException(string.Format("Unable to connect to property {0}. The value is non-null", current.Name)); prop.SetValue(this, current, null); return true; } FieldInfo field = type.GetField(current.Name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); if (field != null) { if (field.GetValue(this) != null) throw new InvalidOperationException(string.Format("Unable to connect to field {0}. The value is non-null", current.Name)); field.SetValue(this, current); } return true; }, null); }
public RelativeLayout(IWindowlessControl relative, float x, float y, float width, float height) { myWidth = width; myHeight = height; myX = x; myY = y; myRelative = relative; }
public static WindowlessControlHost GetHost(IWindowlessControl control) { control = control.Parent; while (control != null && !(control is WindowlessControlHost)) { control = control.Parent; } return(control as WindowlessControlHost); }
protected override void OnWindowlessMouseDown(WindowlessControlHost sender, WindowlessMouseEventArgs e) { base.OnWindowlessMouseDown(sender, e); IWindowlessControl control = FindControlAtPoint(new Point(e.X, e.Y)); if (control == null && control != myCurrentSelection) { return; } e.Handled = true; ChangeSelection(control as IInteractiveContentPresenter); OnClickDown(); }
protected override void OnWindowlessMouseFocus(WindowlessControlHost sender, MouseEventArgs e) { base.OnWindowlessMouseFocus(sender, e); if (sender != this) { return; } IWindowlessControl control = FindControlAtPoint(new Point(e.X, e.Y)); if (control == null && control != myCurrentSelection) { return; } ChangeSelection(control as IInteractiveContentPresenter); }
void HorizontalLayoutControls(int top, int maxWidth, int maxHeight, int start, int end) { int offset = 0; for (int i = start; i < end; i++) { IWindowlessControl control = Controls[i]; if (!control.Visible) { continue; } int controlWidth = WrapStyle == WrapStyle.Greedy ? control.Width : maxWidth; LayoutControl(control, new Rectangle(Margin.Left + offset, top, controlWidth, maxHeight)); offset += controlWidth; } }
public static void WindowlessInvalidate(IWindowlessControl sender) { Point origin = Point.Empty; IWindowlessControl parent = sender; while (!(parent is WindowlessControlHost) && parent != null) { origin.X += parent.Left; origin.Y += parent.Top; parent = parent.Parent; } WindowlessControlHost host = parent as WindowlessControlHost; if (host != null) { host.WindowlessInvalidate(new Rectangle(origin.X, origin.Y, sender.Width, sender.Height)); } }
public static Point WindowlessPointToHost(IWindowlessControl control, Point point) { Point ret = point; if (control.Parent == null) { return(point); } IWindowlessControl host = WindowlessControlHost.GetHost(control); while (control != host) { ret.X += control.Left; ret.Y += control.Top; control = control.Parent; } return(ret); }
IWindowlessControl Load(Stream stream) { List <Type> additionalControls = new List <Type>(); AddAssemblyControlsToList(Assembly.GetExecutingAssembly(), additionalControls); XmlAttributeOverrides overrides = new XmlAttributeOverrides(); XmlAttributes xmlArrayItems = new XmlAttributes(); xmlArrayItems.XmlArray = new XmlArrayAttribute("Controls"); overrides.Add(typeof(WindowlessControlHost), "SerializableControls", xmlArrayItems); overrides.Add(typeof(WindowlessControl), "Controls", xmlArrayItems); XmlIgnoreAttributeOverrides(overrides, typeof(WindowlessControlHost).BaseType); List <Type> additionalTypes = new List <Type>(); additionalTypes.Add(typeof(SmartColor)); OnCreateXmlSerializer(overrides, additionalTypes, additionalControls); foreach (Type type in additionalControls) { xmlArrayItems.XmlArrayItems.Add(new XmlArrayItemAttribute(type)); } XmlSerializer ser = new XmlSerializer(typeof(WindowlessControlHost), overrides, additionalTypes.ToArray(), null, null); //{ // WindowlessControlHost test = new WindowlessControlHost(); // test.Orientation = WindowlessControls.Orientation.Vertical; // test.Control = new StackPanel(); // test.Control.Controls.Add(new WindowlessLabel("hello world")); // MemoryStream mem = new MemoryStream(); // ser.Serialize(mem, test); // mem.Seek(0, SeekOrigin.Begin); // string text = new StreamReader(mem).ReadToEnd(); //} IWindowlessControl ret = ser.Deserialize(stream) as IWindowlessControl; Connect(ret); return(ret); }
public static void WindowlessBringIntoView(IWindowlessControl control) { WindowlessControlHost host = GetHost(control); host.SyncWindows(); Point p = WindowlessPointToHost(control, Point.Empty); Rectangle rect = new Rectangle(p.X, p.Y, control.Width, control.Height); while (host != null) { if (host.AutoScroll) { Point newScroll = host.AutoScrollPosition; // we favor top and left over bottom and right if (rect.Top < 0) { newScroll.Y -= rect.Top; } else if (rect.Bottom > host.Height && rect.Height <= host.Height) { newScroll.Y += (host.Height - rect.Bottom); } if (rect.Left < 0) { newScroll.X -= rect.Left; } else if (rect.Right > host.Width && rect.Width <= host.Width) { newScroll.Y += (host.Width - rect.Right); } // for some reason you need to pass in positive values to autoscroll, and it converts them to negative values... if (newScroll != host.AutoScrollPosition) { host.AutoScrollPosition = new Point(Math.Abs(newScroll.X), Math.Abs(newScroll.Y)); host.SyncWindows(); } } rect.X += host.Left; rect.Y += host.Top; host = host.Parent as WindowlessControlHost; } }
public DropdownControl(WindowlessControlHost dropdownSource, IWindowlessControl dropdownContent) { AutoScroll = true; Orientation = Orientation.Vertical; myDropdownSource = dropdownSource; OverlayPanel overlay = new OverlayPanel(); WindowlessRectangle rectangle = new WindowlessRectangle(); rectangle.Color = SystemColors.ControlDarkDark; rectangle.Filled = false; overlay.Controls.Add(rectangle); OverlayPanel inner = new OverlayPanel(); inner.Margin = new Thickness(1, 1, 1, 1); inner.Controls.Add(dropdownContent); overlay.Controls.Add(inner); overlay.FitHeightControl = overlay.FitWidthControl = inner; Control = overlay; }
int FindOrInsertAncestor(IWindowlessControl control, List <IWindowlessControl> hierarchySorted) { int index = hierarchySorted.IndexOf(control); if (index != -1) { return(index); } RelativeLayout layout = control.Layout as RelativeLayout; // if this is a root, just insert it at the head and go if (layout.Relative == null) { hierarchySorted.Insert(0, control); return(0); } index = FindOrInsertAncestor(layout.Relative, hierarchySorted) + 1; hierarchySorted.Insert(index, control); return(index); }
protected override void OnWindowlessKeyDown(Control sender, System.Windows.Forms.KeyEventArgs e) { if (e.Handled) { return; } switch (e.KeyCode) { case Keys.Enter: OnClickDown(); break; case Keys.F19: case Keys.F20: { int firstIndex = -1; int prevIndex = -1; int nextIndex = -1; int lastIndex = -1; bool found = false; for (int i = 0; i < Control.Controls.Count; i++) { IWindowlessControl current = Control.Controls[i]; if (current == myCurrentSelection) { found = true; continue; } if (!current.Visible) { continue; } if (firstIndex != -1) { firstIndex = i; } if (!found) { prevIndex = i; } if (found && nextIndex == -1) { nextIndex = i; } lastIndex = i; } int newIndex = -1; if (e.KeyCode == Keys.F19) { newIndex = prevIndex; } else { newIndex = nextIndex; } if (newIndex >= 0 && newIndex < Items.Count && newIndex != SelectedIndex) { e.Handled = true; SelectedIndex = newIndex; } } break; case Keys.Up: case Keys.Down: case Keys.Right: case Keys.Left: if (CurrentSelection != null) { IWindowlessControl control = Control.Controls[Items.IndexOf(CurrentSelection)]; Rectangle rect = new Rectangle(control.Left, control.Top, control.Width, control.Height); e.Handled = NavigateSelection(rect, e.KeyCode); } else if (Items.Count != 0) { SelectedIndex = 0; } break; } base.OnWindowlessKeyDown(sender, e); }
void WindowlessPaintHost(IWindowlessControl control, Graphics graphics, Point origin, Rectangle destRect) { if (!control.Visible) { return; } // paint everything in a clip rectangle belonging to a host tree IWindowlessPaintControl paintControl = control as IWindowlessPaintControl; if (paintControl != null) { if (paintControl.PaintSelf) { WindowlessPaintEventArgs we = myPaintEventArgs; we.Graphics = graphics; we.ClipRectangle = destRect; we.Origin = origin; Rectangle clip = we.ClipRectangle; clip.X += we.Origin.X; clip.Y += we.Origin.Y; if (paintControl.ClipToBounds) { myDummyRegion.MakeEmpty(); myDummyRegion.Union(clip); myDummyRegion.Intersect(graphics.Clip); } else { myDummyRegion.MakeInfinite(); } Region oldClip = we.Graphics.Clip; we.Graphics.Clip = myDummyRegion; paintControl.OnWindowlessPaint(we); we.Graphics.Clip = oldClip; } if (!paintControl.PaintChildren) { return; } } if (control.Controls == null) { return; } foreach (IWindowlessControl child in control.Controls) { if (child is WindowlessControlHost) { continue; } Point childOrigin = new Point(origin.X + child.Left, origin.Y + child.Top); Rectangle childRect = new Rectangle(child.Left, child.Top, child.Width, child.Height); Rectangle clipRect = childRect; clipRect.Location = childOrigin; if (!graphics.Clip.IsVisible(clipRect)) { continue; } childRect.Intersect(destRect); if (childRect.Width <= 0 || childRect.Height <= 0) { continue; } childRect.X -= child.Left; childRect.Y -= child.Top; WindowlessPaintHost(child, graphics, childOrigin, childRect); } }
public override bool MeasureUnpadded(Size bounds, bool boundsChange) { // todo: this method is f****n huge. // can refactor by using reflection so the width/height crap can be done in a single spot. if (!(bounds.Width != Int32.MaxValue ^ bounds.Height != Int32.MaxValue)) { throw new Exception("WrapPanel needs to have one infinite dimension."); } bool layoutChange = false; int maxWidth = 0; int maxHeight = 0; foreach (IWindowlessControl control in Controls) { if (!control.Visible) { continue; } Rectangle oldRect = new Rectangle(control.Left, control.Top, control.Width, control.Height); if (control.NeedsMeasure || boundsChange) { control.Measure(bounds); } maxWidth = Math.Max(maxWidth, control.Width); maxHeight = Math.Max(maxHeight, control.Height); layoutChange |= oldRect != new Rectangle(control.Left, control.Top, control.Width, control.Height); } int newWidth = 0; int newHeight = 0; if (bounds.Width == Int32.MaxValue) { // vertical wrapping int left = 0; int top = 0; int maxColumnWidth = 0; //if (WrapStyle == WrapStyle.UniformGrid) //{ // maxHeight = bounds.Height / (bounds.Height / maxHeight); //} foreach (IWindowlessControl control in Controls) { if (!control.Visible) { continue; } int controlHeight = WrapStyle == WrapStyle.Greedy ? control.Height : maxHeight; // start a new column if necessary if (controlHeight + top > bounds.Height) { top = 0; left += WrapStyle == WrapStyle.UniformGrid ? maxWidth : maxColumnWidth; maxColumnWidth = 0; newHeight = Math.Max(newHeight, top); } control.Top = top + Margin.Top; control.Left = left + Margin.Left; top += controlHeight; maxColumnWidth = Math.Max(maxColumnWidth, control.Width); } newWidth = left + maxColumnWidth; newHeight = Math.Max(newHeight, top); } else { // horizontal wrapping int left = 0; int top = 0; int maxRowHeight = 0; if (WrapStyle == WrapStyle.UniformGrid && HorizontalAlignment == HorizontalAlignment.Stretch) { maxWidth = bounds.Width / (bounds.Width / maxWidth); } //foreach (IWindowlessControl control in Controls) int start = 0; int end = 0; for (int i = 0; i < Controls.Count; i++) { IWindowlessControl control = Controls[i]; if (!control.Visible) { continue; } int controlWidth = WrapStyle == WrapStyle.Greedy ? control.Width : maxWidth; // start a new column if necessary if (controlWidth + left > bounds.Width) { HorizontalLayoutControls(top + Margin.Top, maxWidth, WrapStyle == WrapStyle.Greedy ? maxHeight : maxRowHeight, start, end); start = end; end = i; newWidth = Math.Max(newWidth, left); left = 0; top += WrapStyle == WrapStyle.UniformGrid ? maxHeight : maxRowHeight; maxRowHeight = 0; } end++; //control.Top = top + Margin.Top; //control.Left = left + Margin.Left; left += controlWidth; maxRowHeight = Math.Max(maxRowHeight, control.Height); } if (end <= Controls.Count) { HorizontalLayoutControls(top + Margin.Top, maxWidth, WrapStyle == WrapStyle.Greedy ? maxHeight : maxRowHeight, start, Controls.Count); } newHeight = top + maxRowHeight; newWidth = Math.Max(newWidth, left); } if (HorizontalAlignment == HorizontalAlignment.Stretch && bounds.Width != Int32.MaxValue) { ClientWidth = bounds.Width; } else { ClientWidth = newWidth; } if (VerticalAlignment == VerticalAlignment.Stretch && bounds.Height != Int32.MaxValue) { ClientHeight = bounds.Height; } else { ClientHeight = newHeight; } return(layoutChange); }
void SyncWindows() { if (Control != null) { if (AutoScrollPosition.Y != Control.Top || AutoScrollPosition.X != Control.Left) { Control.Left = AutoScrollPosition.X; Control.Top = AutoScrollPosition.Y; WindowlessInvalidate(); } } foreach (Control control in Controls) { IWindowlessControl wc = control as IWindowlessControl; if (wc == null) { continue; } Point p = WindowlessControlHost.WindowlessPointToHost(wc, Point.Empty); if (control.Left != p.X) { control.Left = p.X; } if (control.Top != p.Y) { control.Top = p.Y; } } if (myAutoScroll) { if (Control.Width <= ClientSize.Width && Control.Height <= ClientSize.Height) { if (base.AutoScroll) { base.AutoScroll = false; } } else { if (!base.AutoScroll) { base.AutoScroll = true; } } } // note: this code causes infinite loop // note: this code no longer seems necessary, no wierd scrollbars as of late... // see if we got scroll bars // fix a bug with windows being horrible at making scrollbars disappear. //if (AutoScroll) //{ // SuspendRemeasure(); // foreach (Control control in Controls) // { // if (control.Bottom > ClientSize.Height || control.Right > ClientSize.Width || control.Left < 0 || control.Top < 0 && control.Visible) // { // control.Visible = false; // control.Visible = true; // break; // } // } // ResumeRemeasure(); //} }
void WindowlessPaintHost(IWindowlessControl control, Graphics graphics, Point origin, Rectangle destRect) { if (!control.Visible) return; // paint everything in a clip rectangle belonging to a host tree IWindowlessPaintControl paintControl = control as IWindowlessPaintControl; if (paintControl != null) { if (paintControl.PaintSelf) { WindowlessPaintEventArgs we = myPaintEventArgs; we.Graphics = graphics; we.ClipRectangle = destRect; we.Origin = origin; Rectangle clip = we.ClipRectangle; clip.X += we.Origin.X; clip.Y += we.Origin.Y; if (paintControl.ClipToBounds) { myDummyRegion.MakeEmpty(); myDummyRegion.Union(clip); myDummyRegion.Intersect(graphics.Clip); } else { myDummyRegion.MakeInfinite(); } Region oldClip = we.Graphics.Clip; we.Graphics.Clip = myDummyRegion; paintControl.OnWindowlessPaint(we); we.Graphics.Clip = oldClip; } if (!paintControl.PaintChildren) return; } if (control.Controls == null) return; foreach (IWindowlessControl child in control.Controls) { if (child is WindowlessControlHost) continue; Point childOrigin = new Point(origin.X + child.Left, origin.Y + child.Top); Rectangle childRect = new Rectangle(child.Left, child.Top, child.Width, child.Height); Rectangle clipRect = childRect; clipRect.Location = childOrigin; if (!graphics.Clip.IsVisible(clipRect)) continue; childRect.Intersect(destRect); if (childRect.Width <= 0 || childRect.Height <= 0) continue; childRect.X -= child.Left; childRect.Y -= child.Top; WindowlessPaintHost(child, graphics, childOrigin, childRect); } }
static Control GetBestNavigationHost(Rectangle rect, Keys key, Control parent, Control exclude, out ulong bestScore) { // we are in this function because the parent control does not want focus bestScore = ulong.MaxValue; if (parent.Controls == null || parent == exclude) { return(null); } Control best = null; foreach (Control control in parent.Controls) { // don't try to refocus onto something within self IWindowlessControl host = control as IWindowlessControl; bool visible = host != null ? host.Visible : control.Visible; if (control == exclude || !visible) { continue; } if (IsFocusable(control)) { long distance; bool positiveDirection = false; if (key == Keys.Up) { distance = GetEdgeDistance(control.Bottom, control.Left, control.Width, rect.Top, rect.Left, rect.Width, positiveDirection); } else if (key == Keys.Down) { positiveDirection = true; distance = GetEdgeDistance(control.Top, control.Left, control.Width, rect.Bottom, rect.Left, rect.Width, positiveDirection); } else if (key == Keys.Left) { distance = GetEdgeDistance(control.Right, control.Top, control.Height, rect.Left, rect.Top, rect.Height, positiveDirection); } else { positiveDirection = true; distance = GetEdgeDistance(control.Left, control.Top, control.Height, rect.Right, rect.Top, rect.Height, positiveDirection); } ulong score = ScoreDistance(distance, positiveDirection); if (score < bestScore) { bestScore = score; best = control; } } Rectangle childRect = rect; childRect.X -= control.Left; childRect.Y -= control.Top; // if the control is scrollable, the rect needs to be put into the space of the autoscroll's topleft, // not just the client topleft. ScrollableControl scrollControl = control as ScrollableControl; if (scrollControl != null && control != exclude.Parent) { childRect.X += scrollControl.AutoScrollPosition.X; childRect.Y += scrollControl.AutoScrollPosition.Y; } ulong bestChildScore; Control bestChild = GetBestNavigationHost(childRect, key, control, exclude, out bestChildScore); if (bestChild != null && bestChildScore < bestScore) { bestScore = bestChildScore; best = bestChild; } } // don't return this value, since it would return something in the wrong direction. if (bestScore >= long.MaxValue) { return(null); } return(best); }
int FindOrInsertAncestor(IWindowlessControl control, List<IWindowlessControl> hierarchySorted) { int index = hierarchySorted.IndexOf(control); if (index != -1) return index; RelativeLayout layout = control.Layout as RelativeLayout; // if this is a root, just insert it at the head and go if (layout.Relative == null) { hierarchySorted.Insert(0, control); return 0; } index = FindOrInsertAncestor(layout.Relative, hierarchySorted) + 1; hierarchySorted.Insert(index, control); return index; }
public SerializableControlCollection(IWindowlessControl control) : base(control) { }
bool NavigateSelection(Rectangle rect, Keys key) { int index = Items.IndexOf(CurrentSelection); IWindowlessControl selected = null; if (index != -1) { selected = Control.Controls[index]; } ulong bestScore = UInt32.MaxValue; IWindowlessControl best = null; foreach (IWindowlessControl control in Control.Controls) { if (control == selected || !control.Visible) { continue; } long distance; bool positiveDirection = false; if (key == Keys.Up) { distance = GetEdgeDistance(control.Top + control.Height, control.Left, control.Width, rect.Top, rect.Left, rect.Width, positiveDirection); } else if (key == Keys.Down) { positiveDirection = true; distance = GetEdgeDistance(control.Top, control.Left, control.Width, rect.Bottom, rect.Left, rect.Width, positiveDirection); } else if (key == Keys.Left) { distance = GetEdgeDistance(control.Left + control.Width, control.Top, control.Height, rect.Left, rect.Top, rect.Height, positiveDirection); } else { positiveDirection = true; distance = GetEdgeDistance(control.Left, control.Top, control.Height, rect.Right, rect.Top, rect.Height, positiveDirection); } ulong score = ScoreDistance(distance, positiveDirection); if (score < bestScore) { bestScore = score; best = control; } } if (bestScore >= Int32.MaxValue) { return(false); } if (best == null) { return(false); } CurrentSelection = Items[Control.Controls.IndexOf(best)]; return(true); }
public static void WindowlessInvalidate(IWindowlessControl sender) { Point origin = Point.Empty; IWindowlessControl parent = sender; while (!(parent is WindowlessControlHost) && parent != null) { origin.X += parent.Left; origin.Y += parent.Top; parent = parent.Parent; } WindowlessControlHost host = parent as WindowlessControlHost; if (host != null) host.WindowlessInvalidate(new Rectangle(origin.X, origin.Y, sender.Width, sender.Height)); }
public override bool MeasureUnpadded(System.Drawing.Size bounds, bool boundsChange) { // prime the root controls and make a list of the leaf controls List <IWindowlessControl> leaves = new List <IWindowlessControl>(Controls); List <IWindowlessControl> roots = new List <IWindowlessControl>(); foreach (IWindowlessControl control in Controls) { RelativeLayout layout = control.Layout as RelativeLayout; if (layout == null) { control.Layout = layout = new RelativeLayout(); } if (layout.Relative == null) { layout.RelativeWidth = layout.RelativeHeight = 1; layout.RelativeX = layout.RelativeY = 0; roots.Add(control); } else { leaves.Remove(layout.Relative); } } List <IWindowlessControl> hierarchySort = HierarchySort(leaves); // compute the relative layouts for all but the root controls foreach (IWindowlessControl control in Controls) { RelativeLayout layout = control.Layout as RelativeLayout; if (layout.Relative == null) { continue; } layout.RelativeWidth = layout.Width; layout.RelativeHeight = layout.Height; layout.RelativeX = layout.X; layout.RelativeY = layout.Y; // climb the lineage and calculate the relative layouts IWindowlessControl relative = layout.Relative; RelativeLayout relativeLayout = null; while (relative != null) { relativeLayout = relative.Layout as RelativeLayout; layout.RelativeX = layout.X * relativeLayout.Width + relativeLayout.X; layout.RelativeY = layout.Y * relativeLayout.Height + relativeLayout.Y; layout.RelativeWidth *= relativeLayout.Width; layout.RelativeHeight *= relativeLayout.Height; relative = relativeLayout.Relative; } // the last thing we should have now is the root controls layout // if anything is out of bounds, make note of it in the root control relativeLayout.RelativeX = Math.Min(relativeLayout.RelativeX, layout.RelativeX); relativeLayout.RelativeY = Math.Min(relativeLayout.RelativeY, layout.RelativeY); // for root controls, i am using the relative width/height to record the bottom right corner // of the control tree relativeLayout.RelativeWidth = Math.Max(layout.RelativeX + layout.RelativeWidth, relativeLayout.RelativeWidth); relativeLayout.RelativeHeight = Math.Max(layout.RelativeY + layout.RelativeHeight, relativeLayout.RelativeHeight); } float maxWidth = 0; float maxHeight = 0; bool layoutChange = false; // measure all the root controls, get the max dims and use that as a reference from // here on out foreach (IWindowlessControl control in roots) { RelativeLayout layout = control.Layout as RelativeLayout; Rectangle oldRect = new Rectangle(control.Left, control.Top, control.Width, control.Height); // always measure, regardless of bounds change or the control needing measure // size is relative to other controls... Size treeBounds = new Size(); if (bounds.Width == Int32.MaxValue) { treeBounds.Width = Int32.MaxValue; } else { treeBounds.Width = (int)Math.Round(bounds.Width / (layout.RelativeWidth - layout.RelativeX)); } if (bounds.Height == Int32.MaxValue) { treeBounds.Height = Int32.MaxValue; } else { treeBounds.Height = (int)Math.Round(bounds.Height / (layout.RelativeHeight - layout.RelativeY)); } control.Measure(treeBounds); float treeWidth = (layout.RelativeWidth - layout.RelativeX) * control.Width; float treeHeight = (layout.RelativeHeight - layout.RelativeX) * control.Height; maxWidth = Math.Max(maxWidth, treeWidth); maxHeight = Math.Max(maxHeight, treeHeight); layoutChange |= oldRect != new Rectangle(control.Left, control.Top, control.Width, control.Height); } ClientWidth = (int)Math.Round(maxWidth); ClientHeight = (int)Math.Round(maxHeight); // lay out the roots foreach (IWindowlessControl control in roots) { RelativeLayout layout = control.Layout as RelativeLayout; Rectangle rootRect = new Rectangle(); // todo: add something to the layout class to persist this? float treeWidth = layout.RelativeWidth - layout.RelativeX; float treeHeight = layout.RelativeHeight - layout.RelativeY; rootRect.Width = (int)Math.Round(ClientWidth / treeWidth); rootRect.Height = (int)Math.Round(ClientHeight / treeHeight); rootRect.X = (int)Math.Round(-(layout.RelativeX / treeWidth) * ClientWidth) + ClientLeft; rootRect.Y = (int)Math.Round(-(layout.RelativeY / treeHeight) * ClientHeight) + ClientTop; LayoutControl(control, rootRect); } // measure out all child controls and layout all controls foreach (IWindowlessControl control in hierarchySort) { RelativeLayout layout = control.Layout as RelativeLayout; IWindowlessControl relative = layout.Relative; if (relative == null) { continue; } Size childBounds = new Size(); // calcuate child bounds childBounds.Width = (int)Math.Round(layout.RelativeWidth * relative.Width); childBounds.Height = (int)Math.Round(layout.RelativeHeight * relative.Height); // calculate the layout rect Rectangle childRect = new Rectangle(); childRect.X = (int)Math.Round(layout.RelativeX * relative.Width + relative.Left); childRect.Y = (int)Math.Round(layout.RelativeY * relative.Height + relative.Top); childRect.Size = childBounds; // lay it out Rectangle oldRect = new Rectangle(control.Left, control.Top, control.Width, control.Height); control.Measure(childBounds); LayoutControl(control, childRect); layoutChange |= oldRect != new Rectangle(control.Left, control.Top, control.Width, control.Height); } return(layoutChange); }