void AddSlotToTable(Slot slot) { slotTable[InternalGetSlotName(slot)] = new SlotData() { Slot = slot }; foreach (var child in slot.Children) { AddSlotToTable(child); } }
public Slot Clone() { var clone = new Slot { Name = this.Name, Length = this.Length, MinLength = this.MinLength, MaxLength = this.MaxLength, Orientation = this.Orientation, }; foreach (var child in this.children) { clone.children.Add(child.Clone()); } return clone; }
Slot SplitSlot(Slot targetSlot, Dock dock) { Orientation orientation = (dock == Dock.Left || dock == Dock.Right) ? Orientation.Horizontal : Orientation.Vertical; Slot newSlot = new Slot() { Name = (this.nextSlotName++).ToString() }; // Splitting a slot by simply dividing the target slot can create an undesirable "split tree" effect, where slots with the same orientation // are in a tree rather than an array. This makes the splitters behave incorrectly because the side of the tree with (grand)children is sized as a // single unit and the change is proportionally distributed. Slot children that have the same orientation should all be parented by the same node. // // To avoid a "split tree", there are 3 cases to handle in a slot split. // 1: The slot HAS a parent slot with the same orientation as the requested split. The new slot is inserted into the parent slot's children. // 2: The slot IS a parent slot with the same orientation as the requested split. The new slot is appended/prepended to this slot's children. // 3: The slot gets replaced by a new parent slot with two children, one being the existing slot and the other being a newly created/returned slot. if (targetSlot != this.SlotDefinition && targetSlot.Parent.Orientation == orientation) { // This is case 1. Insert the new slot into the parent, either before or after the target slot based on dock. double newSize = targetSlot.Parent.Children.Average(n => n.Length.Value); newSlot.Length = new GridLength(newSize, GridUnitType.Star); int index = targetSlot.Parent.Children.IndexOf(targetSlot); if (dock == Dock.Right || dock == Dock.Bottom) { index += 1; } // Note, adding child slots to a parent automatically assigns the child's Parent property targetSlot.Parent.Children.Insert(index, newSlot); } else if (targetSlot.Children.Count > 0 && targetSlot.Orientation == orientation) { // This is case 2. Insert the new slot into the target slot (at the beginning or end based on dock). double newSize = targetSlot.Children.Average(n => n.Length.Value); newSlot.Length = new GridLength(newSize, GridUnitType.Star); if (dock == Dock.Left || dock == Dock.Top) { targetSlot.Children.Insert(0, newSlot); } else { targetSlot.Children.Add(newSlot); } } else { // This is case 3. Theoretically, we replace targetSlot with a new slot with two children, one being targetSlot, // and the other being the new slot created by the split. In practice, though, we don't actually *replace* targetSlot // because it may be the root. So what we *really* do is create two new slots: A (the new slot) and B (the new targetSlot). // Then rename targetSlot and move its children (if any) to slot B, and name slot B with targetSlot's old name. Slot newSlotB = new Slot { Name = targetSlot.Name, Orientation = targetSlot.Orientation }; // Add the children from targetSlot to newSlotB first, which re-assigns Parent for each of them... foreach (var child in targetSlot.Children) { newSlotB.Children.Add(child); } // And then remove them one-at-a-time (so the slot sees each one and disconnects. .Clear does a reset) while (targetSlot.Children.Count > 0) { targetSlot.Children.RemoveAt(targetSlot.Children.Count - 1); } // Rename targetSlot and set its orientation targetSlot.Name = (this.nextSlotName++).ToString(); targetSlot.Orientation = orientation; // Add the new children to targetSlot if (dock == Dock.Left || dock == Dock.Top) { targetSlot.Children.Add(newSlot); targetSlot.Children.Add(newSlotB); } else { targetSlot.Children.Add(newSlotB); targetSlot.Children.Add(newSlot); } } return newSlot; }
public static Slot LoadSlotFromState(XElement slotElement) { if (gridLengthConverter == null) { gridLengthConverter = TypeDescriptor.GetConverter(typeof(GridLength)); } Slot slot = new Slot(); slot.Name = slotElement.Attribute("Name").Value; slot.Length = (GridLength)gridLengthConverter.ConvertFromString(null, CultureInfo.InvariantCulture, slotElement.Attribute("Length").Value); slot.Orientation = (Orientation)Enum.Parse(typeof(Orientation), slotElement.Attribute("Orientation").Value); foreach (var child in slotElement.Elements("Slot")) { slot.Children.Add(LoadSlotFromState(child)); } return slot; }
public static XElement BuildSlotElement(Slot slot) { return new XElement("Slot", new XAttribute("Name", slot.Name), new XAttribute("Length", slot.Length), new XAttribute("Orientation", slot.Orientation), slot.Children.Select(c => BuildSlotElement(c))); }
public ViewSource AddViewSource(IViewCreationCommand creator, Slot targetSlot, Dock? dock) { ViewSource source; if (creator == null) { return null; } if (this.ViewSources.Count == 0) { source = new ViewSource(this, this.nextViewId++, this.SlotDefinition.Name, creator); this.ViewSources.Add(source); var handler = this.PlaceholderModified; // Note that this is the only place where this event could fire (going from 0 to 1 sources) if (this.IsNewPlaceholder && handler != null) { handler(this, EventArgs.Empty); } return source; } if (targetSlot == null) { return null; } Slot newSlot = targetSlot; if (dock.HasValue) { newSlot = SplitSlot(targetSlot, dock.Value); } source = new ViewSource(this, this.nextViewId++, newSlot.Name, creator); this.ViewSources.Add(source); RecomputeViewShortcutKeys(); return source; }
void UpdateHitState(Point pos) { LayoutControl targetLayout = this.CurrentLayout; bool ghostPositionSet = false; this.dockTargetSlot = null; this.dragGhostWindow.CancelReason = "To place the view, drag over an empty layout, an existing view, or a docking target."; if (targetLayout != null) { var posInLayout = Mouse.GetPosition(targetLayout); var rect = new Rect(0, 0, targetLayout.ActualWidth, targetLayout.ActualHeight); if (rect.Contains(posInLayout)) { this.dockTargetSlot = targetLayout.GetSlotUnderPoint(Mouse.GetPosition(targetLayout)); } else { targetLayout = null; } if (this.dockTargetSlot != null) { if (this.creatorBeingDragged != null) { if (this.creatorBeingDragged.Category.DocumentFactoryName != null && targetLayout.LayoutInstance.LayoutDefinition.DocumentFactoryName != this.creatorBeingDragged.Category.DocumentFactoryName) { // The creator being dragged is affinitized with a particular document type which is not the same type as the active // layout. Disallow the drop. this.dockTargetSlot = null; this.dragGhostWindow.CancelReason = string.Format(CultureInfo.InvariantCulture, "This view can only be placed in a {0} layout.", this.creatorBeingDragged.Category.DisplayName); } else if (this.creatorBeingDragged.Command.IsSingleInstancePerLayout && (targetLayout.LayoutInstance.LayoutDefinition.ViewSources.Any(vs => vs.ViewCreator == this.creatorBeingDragged.Command))) { // This is a single-instance-per-layout view that already exists in the layout. No dice. this.dockTargetSlot = null; this.dragGhostWindow.CancelReason = "Only one instance of this view type can exist in a layout."; } else if (this.creatorBeingDragged.Command.IsSingleInstance) { var existingSource = ToolsUIApplication.Instance.LayoutDefinitions.SelectMany(d => d.ViewSources).Where(vs => vs.ViewCreator == this.creatorBeingDragged.Command).FirstOrDefault(); if (existingSource != null) { // This is a single-instance view that already exists in a layout (perhaps not even this one). this.dockTargetSlot = null; this.dragGhostWindow.CancelReason = string.Format(CultureInfo.InvariantCulture, "This view already exists in the '{0}' layout. Only one instance of this view type can be created.", existingSource.Parent.Header); } } } } } if (this.dockTargetSlot != null) { this.layout = targetLayout; if (this.dockTargetWindow == null) { this.dockTargetWindow = new ViewDropTargetWindow(); } var rect = targetLayout.GetSlotScreenRect(this.dockTargetSlot); this.dockTargetWindow.Left = rect.X; this.dockTargetWindow.Top = rect.Y; this.dockTargetWindow.Width = rect.Width; this.dockTargetWindow.Height = rect.Height; this.dockTargetWindow.IsTabbedSpotVisible = true; this.dockTargetWindow.RootSlot = targetLayout.LayoutInstance.LayoutDefinition.SlotDefinition; this.dockTargetWindow.TargetSlot = this.dockTargetSlot; this.dockTargetWindow.AreDockSpotsVisible = targetLayout.LayoutInstance.LayoutDefinition.ViewSources.Count > 0; this.dockTargetWindow.Show(); this.hitTestSpot = null; this.tabHitTesting = false; VisualTreeHelper.HitTest(this.dockTargetWindow, this.HitTestFilter, this.HitTestResult, new PointHitTestParameters(Mouse.GetPosition(this.dockTargetWindow))); if (this.hitTestSpot != null) { var spot = this.hitTestSpot; if (spot != null && spot.DestinationSlot != null) { rect = targetLayout.GetSlotScreenRect(spot.DestinationSlot); this.dragGhostWindow.CancelMode = false; this.dragGhostWindow.CancelReason = null; if (spot.IsTabbed) { this.dragGhostWindow.Left = rect.X; this.dragGhostWindow.Top = rect.Y; this.dragGhostWindow.Width = rect.Width; this.dragGhostWindow.Height = rect.Height; } else { switch (spot.Dock) { case Dock.Top: this.dragGhostWindow.Left = rect.X; this.dragGhostWindow.Top = rect.Y; this.dragGhostWindow.Width = rect.Width; this.dragGhostWindow.Height = rect.Height / 2; break; case Dock.Bottom: this.dragGhostWindow.Width = rect.Width; this.dragGhostWindow.Height = rect.Height / 2; this.dragGhostWindow.Left = rect.X; this.dragGhostWindow.Top = rect.Y + this.dragGhostWindow.Height; break; case Dock.Left: this.dragGhostWindow.Left = rect.X; this.dragGhostWindow.Top = rect.Y; this.dragGhostWindow.Width = rect.Width / 2; this.dragGhostWindow.Height = rect.Height; break; case Dock.Right: this.dragGhostWindow.Width = rect.Width / 2; this.dragGhostWindow.Height = rect.Height; this.dragGhostWindow.Left = rect.X + this.dragGhostWindow.Width; this.dragGhostWindow.Top = rect.Y; break; } } ghostPositionSet = true; } } else { // Didn't even hit the tabbed spot (the transparent rect covering the whole slot), so must be in // the margin. Tear down the dock target window. (Won't flicker because it fades in...) this.dockTargetWindow.Close(); this.dockTargetWindow = null; this.dockTargetSlot = null; } } else { if (this.dockTargetWindow != null) { this.dockTargetWindow.Close(); this.dockTargetWindow = null; } this.layout = null; // See if the mouse is over a layout tab. If so, select it. this.tabHitTesting = true; this.hitTestTabItem = null; VisualTreeHelper.HitTest(this.tabControl, this.HitTestFilter, this.HitTestResult, new PointHitTestParameters(Mouse.GetPosition(this.tabControl))); if (this.hitTestTabItem != null) { var layoutDef = this.hitTestTabItem.DataContext as LayoutDefinition; if (layoutDef != null) { this.tabControl.SelectedItem = layoutDef; } } } if (!ghostPositionSet) { this.dragGhostWindow.Width = 200; this.dragGhostWindow.Height = 200; this.dragGhostWindow.Left = pos.X; this.dragGhostWindow.Top = pos.Y; this.dragGhostWindow.CancelMode = true; } }
public Rect GetSlotScreenRect(Slot slot) { if (this.slotPanel == null || slot == null) { return new Rect(); } var upperLeft = this.slotPanel.PointToScreenIndependent(slot.UpperLeft); var lowerRight = this.slotPanel.PointToScreenIndependent(new Point(slot.UpperLeft.X + slot.ActualSize.Width, slot.UpperLeft.Y + slot.ActualSize.Height)); return new Rect(upperLeft, lowerRight); }
Slot SplitSlot(Slot targetSlot, Dock dock) { Orientation orientation = (dock == Dock.Left || dock == Dock.Right) ? Orientation.Horizontal : Orientation.Vertical; Slot newSlot = new Slot() { Name = (this.nextSlotName++).ToString() }; // Splitting a slot by simply dividing the target slot can create an undesirable "split tree" effect, where slots with the same orientation // are in a tree rather than an array. This makes the splitters behave incorrectly because the side of the tree with (grand)children is sized as a // single unit and the change is proportionally distributed. Slot children that have the same orientation should all be parented by the same node. // // To avoid a "split tree", there are 3 cases to handle in a slot split. // 1: The slot HAS a parent slot with the same orientation as the requested split. The new slot is inserted into the parent slot's children. // 2: The slot IS a parent slot with the same orientation as the requested split. The new slot is appended/prepended to this slot's children. // 3: The slot gets replaced by a new parent slot with two children, one being the existing slot and the other being a newly created/returned slot. if (targetSlot != this.SlotDefinition && targetSlot.Parent.Orientation == orientation) { // This is case 1. Insert the new slot into the parent, either before or after the target slot based on dock. double newSize = targetSlot.Parent.Children.Average(n => n.Length.Value); newSlot.Length = new GridLength(newSize, GridUnitType.Star); int index = targetSlot.Parent.Children.IndexOf(targetSlot); if (dock == Dock.Right || dock == Dock.Bottom) { index += 1; } // Note, adding child slots to a parent automatically assigns the child's Parent property targetSlot.Parent.Children.Insert(index, newSlot); } else if (targetSlot.Children.Count > 0 && targetSlot.Orientation == orientation) { // This is case 2. Insert the new slot into the target slot (at the beginning or end based on dock). double newSize = targetSlot.Children.Average(n => n.Length.Value); newSlot.Length = new GridLength(newSize, GridUnitType.Star); if (dock == Dock.Left || dock == Dock.Top) { targetSlot.Children.Insert(0, newSlot); } else { targetSlot.Children.Add(newSlot); } } else { // This is case 3. Theoretically, we replace targetSlot with a new slot with two children, one being targetSlot, // and the other being the new slot created by the split. In practice, though, we don't actually *replace* targetSlot // because it may be the root. So what we *really* do is create two new slots: A (the new slot) and B (the new targetSlot). // Then rename targetSlot and move its children (if any) to slot B, and name slot B with targetSlot's old name. Slot newSlotB = new Slot { Name = targetSlot.Name, Orientation = targetSlot.Orientation }; // Add the children from targetSlot to newSlotB first, which re-assigns Parent for each of them... foreach (var child in targetSlot.Children) { newSlotB.Children.Add(child); } // And then remove them one-at-a-time (so the slot sees each one and disconnects. .Clear does a reset) while (targetSlot.Children.Count > 0) { targetSlot.Children.RemoveAt(targetSlot.Children.Count - 1); } // Rename targetSlot and set its orientation targetSlot.Name = (this.nextSlotName++).ToString(); targetSlot.Orientation = orientation; // Add the new children to targetSlot if (dock == Dock.Left || dock == Dock.Top) { targetSlot.Children.Add(newSlot); targetSlot.Children.Add(newSlotB); } else { targetSlot.Children.Add(newSlotB); targetSlot.Children.Add(newSlot); } } return(newSlot); }
private static string InternalGetSlotName(Slot child) { return child.Name ?? ""; }