diff --git a/source/Components/AvalonDock.Themes.Aero/Theme.xaml b/source/Components/AvalonDock.Themes.Aero/Theme.xaml index f47eccd9..11932e51 100644 --- a/source/Components/AvalonDock.Themes.Aero/Theme.xaml +++ b/source/Components/AvalonDock.Themes.Aero/Theme.xaml @@ -1336,7 +1336,7 @@ - @@ -1351,7 +1351,7 @@ VerticalContentAlignment="Center" HorizontalContentAlignment="Center" DropDownContextMenu="{Binding Model.Root.Manager.DocumentContextMenu, RelativeSource={RelativeSource TemplatedParent}}" - DropDownContextMenuDataContext="{Binding Path=RootDocumentLayoutItem, RelativeSource={RelativeSource TemplatedParent}}" + DropDownContextMenuDataContext="{Binding Path=SingleContentLayoutItem, RelativeSource={RelativeSource TemplatedParent}}" ToolTip="{x:Static avalonDockProperties:Resources.Document_CxMenu_Hint}"> - @@ -907,7 +907,7 @@ VerticalContentAlignment="Center" HorizontalContentAlignment="Center" DropDownContextMenu="{Binding Model.Root.Manager.DocumentContextMenu, RelativeSource={RelativeSource TemplatedParent}}" - DropDownContextMenuDataContext="{Binding Path=RootDocumentLayoutItem, RelativeSource={RelativeSource TemplatedParent}}" + DropDownContextMenuDataContext="{Binding Path=SingleContentLayoutItem, RelativeSource={RelativeSource TemplatedParent}}" ToolTip="{x:Static avalonDockProperties:Resources.Document_CxMenu_Hint}"> @@ -944,7 +944,7 @@ Focusable="False" Style="{StaticResource AvalonDock_Expression_ButtonStyle}" Visibility="{Binding Path=IsEnabled, RelativeSource={RelativeSource Self}, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" - Command="{Binding Path=RootDocumentLayoutItem.CloseCommand, RelativeSource={RelativeSource TemplatedParent}}" + Command="{Binding Path=SingleContentLayoutItem.CloseCommand, RelativeSource={RelativeSource TemplatedParent}}" ToolTip="{x:Static avalonDockProperties:Resources.Document_Close}" Grid.Column="3"> diff --git a/source/Components/AvalonDock.Themes.Metro/Theme.xaml b/source/Components/AvalonDock.Themes.Metro/Theme.xaml index 22ae9782..36bc5510 100644 --- a/source/Components/AvalonDock.Themes.Metro/Theme.xaml +++ b/source/Components/AvalonDock.Themes.Metro/Theme.xaml @@ -1394,7 +1394,7 @@ - @@ -1409,7 +1409,7 @@ VerticalContentAlignment="Center" HorizontalContentAlignment="Center" DropDownContextMenu="{Binding Model.Root.Manager.DocumentContextMenu, RelativeSource={RelativeSource TemplatedParent}}" - DropDownContextMenuDataContext="{Binding Path=RootDocumentLayoutItem, RelativeSource={RelativeSource TemplatedParent}}" + DropDownContextMenuDataContext="{Binding Path=SingleContentLayoutItem, RelativeSource={RelativeSource TemplatedParent}}" ToolTip="{x:Static avalonDockProperties:Resources.Document_CxMenu_Hint}"> - @@ -1491,7 +1491,7 @@ VerticalContentAlignment="Center" HorizontalContentAlignment="Center" DropDownContextMenu="{Binding Model.Root.Manager.DocumentContextMenu, RelativeSource={RelativeSource TemplatedParent}}" - DropDownContextMenuDataContext="{Binding Path=RootDocumentLayoutItem, RelativeSource={RelativeSource TemplatedParent}}" + DropDownContextMenuDataContext="{Binding Path=SingleContentLayoutItem, RelativeSource={RelativeSource TemplatedParent}}" ToolTip="{x:Static avalonDockProperties:Resources.Document_CxMenu_Hint}"> - ().FirstOrDefault(); switch (Type) { case DropTargetType.DocumentPaneDockBottom: #region DropTargetType.DocumentPaneDockBottom - { - var newLayoutDocumentPane = new LayoutDocumentPane(floatingWindow.RootDocument); - var parentModel = targetModel.Parent as LayoutDocumentPaneGroup; + { + var parentModel = targetModel.Parent as ILayoutGroup; + var parentModelOrientable = targetModel.Parent as ILayoutOrientableGroup; + int insertToIndex = parentModel.IndexOfChild(targetModel); - if (parentModel == null) + if (parentModelOrientable.Orientation != System.Windows.Controls.Orientation.Vertical && + parentModel.ChildrenCount == 1) + parentModelOrientable.Orientation = System.Windows.Controls.Orientation.Vertical; + + if (parentModelOrientable.Orientation == System.Windows.Controls.Orientation.Vertical) + { + var layoutDocumentPaneGroup = floatingWindow.RootPanel as LayoutDocumentPaneGroup; + if (layoutDocumentPaneGroup != null && + (layoutDocumentPaneGroup.Children.Count == 1 || + layoutDocumentPaneGroup.Orientation == System.Windows.Controls.Orientation.Vertical)) { - var parentContainer = targetModel.Parent as ILayoutContainer; - var newParentModel = new LayoutDocumentPaneGroup() { Orientation = System.Windows.Controls.Orientation.Vertical }; - parentContainer.ReplaceChild(targetModel, newParentModel); - newParentModel.Children.Add(targetModel as LayoutDocumentPane); - newParentModel.Children.Add(newLayoutDocumentPane); + var documentsToMove = layoutDocumentPaneGroup.Children.ToArray(); + for (int i = 0; i < documentsToMove.Length; i++) + parentModel.InsertChildAt(insertToIndex + 1 + i, documentsToMove[i]); } else + parentModel.InsertChildAt(insertToIndex + 1, floatingWindow.RootPanel); + } + else + { + var targetModelAsPositionableElement = targetModel as ILayoutPositionableElement; + var newOrientedPanel = new LayoutDocumentPaneGroup() { - var manager = parentModel.Root.Manager; - if (!manager.AllowMixedOrientation || parentModel.Orientation == System.Windows.Controls.Orientation.Vertical) - { - parentModel.Orientation = System.Windows.Controls.Orientation.Vertical; - int targetPaneIndex = parentModel.IndexOfChild(targetModel); - parentModel.Children.Insert(targetPaneIndex + 1, newLayoutDocumentPane); - } - else - { - LayoutDocumentPaneGroup newChildGroup = new LayoutDocumentPaneGroup(); - newChildGroup.Orientation = System.Windows.Controls.Orientation.Vertical; - parentModel.ReplaceChild(targetModel, newChildGroup); - newChildGroup.Children.Add(targetModel); - newChildGroup.Children.Add(newLayoutDocumentPane); - } + Orientation = System.Windows.Controls.Orientation.Vertical, + DockWidth = targetModelAsPositionableElement.DockWidth, + DockHeight = targetModelAsPositionableElement.DockHeight, + }; + + parentModel.InsertChildAt(insertToIndex, newOrientedPanel); + newOrientedPanel.Children.Add(targetModel); + newOrientedPanel.Children.Add(floatingWindow.RootPanel); - } } + } break; + #endregion case DropTargetType.DocumentPaneDockTop: #region DropTargetType.DocumentPaneDockTop - { - var newLayoutDocumentPane = new LayoutDocumentPane(floatingWindow.RootDocument); - var parentModel = targetModel.Parent as LayoutDocumentPaneGroup; + { + var parentModel = targetModel.Parent as ILayoutGroup; + var parentModelOrientable = targetModel.Parent as ILayoutOrientableGroup; + int insertToIndex = parentModel.IndexOfChild(targetModel); - if (parentModel == null) + if (parentModelOrientable.Orientation != System.Windows.Controls.Orientation.Vertical && + parentModel.ChildrenCount == 1) + parentModelOrientable.Orientation = System.Windows.Controls.Orientation.Vertical; + + if (parentModelOrientable.Orientation == System.Windows.Controls.Orientation.Vertical) + { + var layoutDocumentPaneGroup = floatingWindow.RootPanel as LayoutDocumentPaneGroup; + if (layoutDocumentPaneGroup != null && + (layoutDocumentPaneGroup.Children.Count == 1 || + layoutDocumentPaneGroup.Orientation == System.Windows.Controls.Orientation.Vertical)) { - var parentContainer = targetModel.Parent as ILayoutContainer; - var newParentModel = new LayoutDocumentPaneGroup() { Orientation = System.Windows.Controls.Orientation.Vertical }; - parentContainer.ReplaceChild(targetModel, newParentModel); - newParentModel.Children.Add(targetModel as LayoutDocumentPane); - newParentModel.Children.Insert(0, newLayoutDocumentPane); + var documentsToMove = layoutDocumentPaneGroup.Children.ToArray(); + for (int i = 0; i < documentsToMove.Length; i++) + parentModel.InsertChildAt(insertToIndex + i, documentsToMove[i]); } else + parentModel.InsertChildAt(insertToIndex, floatingWindow.RootPanel); + } + else + { + var targetModelAsPositionableElement = targetModel as ILayoutPositionableElement; + var newOrientedPanel = new LayoutDocumentPaneGroup() { - var manager = parentModel.Root.Manager; - if (!manager.AllowMixedOrientation || parentModel.Orientation == System.Windows.Controls.Orientation.Vertical) - { - parentModel.Orientation = System.Windows.Controls.Orientation.Vertical; - int targetPaneIndex = parentModel.IndexOfChild(targetModel); - parentModel.Children.Insert(targetPaneIndex, newLayoutDocumentPane); - } - else - { - LayoutDocumentPaneGroup newChildGroup = new LayoutDocumentPaneGroup(); - newChildGroup.Orientation = System.Windows.Controls.Orientation.Vertical; - parentModel.ReplaceChild(targetModel, newChildGroup); - newChildGroup.Children.Add(newLayoutDocumentPane); - newChildGroup.Children.Add(targetModel); - } + Orientation = System.Windows.Controls.Orientation.Vertical, + DockWidth = targetModelAsPositionableElement.DockWidth, + DockHeight = targetModelAsPositionableElement.DockHeight, + }; + + parentModel.InsertChildAt(insertToIndex, newOrientedPanel); + //the floating window must be added after the target modal as it could be raise a CollectGarbage call + newOrientedPanel.Children.Add(targetModel); + newOrientedPanel.Children.Insert(0, floatingWindow.RootPanel); - } } + } break; #endregion case DropTargetType.DocumentPaneDockLeft: #region DropTargetType.DocumentPaneDockLeft - { - var newLayoutDocumentPane = new LayoutDocumentPane(floatingWindow.RootDocument); - var parentModel = targetModel.Parent as LayoutDocumentPaneGroup; + { + var parentModel = targetModel.Parent as ILayoutGroup; + var parentModelOrientable = targetModel.Parent as ILayoutOrientableGroup; + int insertToIndex = parentModel.IndexOfChild(targetModel); - if (parentModel == null) + if (parentModelOrientable.Orientation != System.Windows.Controls.Orientation.Horizontal && + parentModel.ChildrenCount == 1) + parentModelOrientable.Orientation = System.Windows.Controls.Orientation.Horizontal; + + if (parentModelOrientable.Orientation == System.Windows.Controls.Orientation.Horizontal) + { + var layoutDocumentPaneGroup = floatingWindow.RootPanel as LayoutDocumentPaneGroup; + if (layoutDocumentPaneGroup != null && + (layoutDocumentPaneGroup.Children.Count == 1 || + layoutDocumentPaneGroup.Orientation == System.Windows.Controls.Orientation.Horizontal)) { - var parentContainer = targetModel.Parent as ILayoutContainer; - var newParentModel = new LayoutDocumentPaneGroup() { Orientation = System.Windows.Controls.Orientation.Horizontal }; - parentContainer.ReplaceChild(targetModel, newParentModel); - newParentModel.Children.Add(targetModel); - newParentModel.Children.Insert(0, newLayoutDocumentPane); + var documentsToMove = layoutDocumentPaneGroup.Children.ToArray(); + for (int i = 0; i < documentsToMove.Length; i++) + parentModel.InsertChildAt(insertToIndex + i, documentsToMove[i]); } else + parentModel.InsertChildAt(insertToIndex, floatingWindow.RootPanel); + } + else + { + var targetModelAsPositionableElement = targetModel as ILayoutPositionableElement; + var newOrientedPanel = new LayoutDocumentPaneGroup() { - var manager = parentModel.Root.Manager; - if (!manager.AllowMixedOrientation || parentModel.Orientation == System.Windows.Controls.Orientation.Horizontal) - { - parentModel.Orientation = System.Windows.Controls.Orientation.Horizontal; - int targetPaneIndex = parentModel.IndexOfChild(targetModel); - parentModel.Children.Insert(targetPaneIndex, newLayoutDocumentPane); - } - else - { - LayoutDocumentPaneGroup newChildGroup = new LayoutDocumentPaneGroup(); - newChildGroup.Orientation = System.Windows.Controls.Orientation.Horizontal; - parentModel.ReplaceChild(targetModel, newChildGroup); - newChildGroup.Children.Add(newLayoutDocumentPane); - newChildGroup.Children.Add(targetModel); - } - } + Orientation = System.Windows.Controls.Orientation.Horizontal, + DockWidth = targetModelAsPositionableElement.DockWidth, + DockHeight = targetModelAsPositionableElement.DockHeight, + }; + + parentModel.InsertChildAt(insertToIndex, newOrientedPanel); + //the floating window must be added after the target modal as it could be raise a CollectGarbage call + newOrientedPanel.Children.Add(targetModel); + newOrientedPanel.Children.Insert(0, floatingWindow.RootPanel); + } + } break; #endregion case DropTargetType.DocumentPaneDockRight: #region DropTargetType.DocumentPaneDockRight - { - var newLayoutDocumentPane = new LayoutDocumentPane(floatingWindow.RootDocument); - var parentModel = targetModel.Parent as LayoutDocumentPaneGroup; + { + var parentModel = targetModel.Parent as ILayoutGroup; + var parentModelOrientable = targetModel.Parent as ILayoutOrientableGroup; + int insertToIndex = parentModel.IndexOfChild(targetModel); - if (parentModel == null) + if (parentModelOrientable.Orientation != System.Windows.Controls.Orientation.Horizontal && + parentModel.ChildrenCount == 1) + parentModelOrientable.Orientation = System.Windows.Controls.Orientation.Horizontal; + + if (parentModelOrientable.Orientation == System.Windows.Controls.Orientation.Horizontal) + { + var layoutDocumentPaneGroup = floatingWindow.RootPanel as LayoutDocumentPaneGroup; + if (layoutDocumentPaneGroup != null && + (layoutDocumentPaneGroup.Children.Count == 1 || + layoutDocumentPaneGroup.Orientation == System.Windows.Controls.Orientation.Horizontal)) { - var parentContainer = targetModel.Parent as ILayoutContainer; - var newParentModel = new LayoutDocumentPaneGroup() { Orientation = System.Windows.Controls.Orientation.Horizontal }; - parentContainer.ReplaceChild(targetModel, newParentModel); - newParentModel.Children.Add(targetModel as LayoutDocumentPane); - newParentModel.Children.Add(newLayoutDocumentPane); + var documentToMove = layoutDocumentPaneGroup.Children.ToArray(); + for (int i = 0; i < documentToMove.Length; i++) + parentModel.InsertChildAt(insertToIndex + 1 + i, documentToMove[i]); } else + parentModel.InsertChildAt(insertToIndex + 1, floatingWindow.RootPanel); + } + else + { + var targetModelAsPositionableElement = targetModel as ILayoutPositionableElement; + var newOrientedPanel = new LayoutDocumentPaneGroup() { - var manager = parentModel.Root.Manager; - if (!manager.AllowMixedOrientation || parentModel.Orientation == System.Windows.Controls.Orientation.Horizontal) - { - parentModel.Orientation = System.Windows.Controls.Orientation.Horizontal; - int targetPaneIndex = parentModel.IndexOfChild(targetModel); - parentModel.Children.Insert(targetPaneIndex + 1, newLayoutDocumentPane); - } - else - { - LayoutDocumentPaneGroup newChildGroup = new LayoutDocumentPaneGroup(); - newChildGroup.Orientation = System.Windows.Controls.Orientation.Horizontal; - parentModel.ReplaceChild(targetModel, newChildGroup); - newChildGroup.Children.Add(targetModel); - newChildGroup.Children.Add(newLayoutDocumentPane); - } + Orientation = System.Windows.Controls.Orientation.Horizontal, + DockWidth = targetModelAsPositionableElement.DockWidth, + DockHeight = targetModelAsPositionableElement.DockHeight, + }; + + parentModel.InsertChildAt(insertToIndex, newOrientedPanel); + newOrientedPanel.Children.Add(targetModel); + newOrientedPanel.Children.Add(floatingWindow.RootPanel); - } } + } break; #endregion case DropTargetType.DocumentPaneDockInside: #region DropTargetType.DocumentPaneDockInside - { - var paneModel = targetModel as LayoutDocumentPane; - var sourceModel = floatingWindow.RootDocument; + { + var paneModel = targetModel as LayoutDocumentPane; + var layoutDocumentPaneGroup = floatingWindow.RootPanel as LayoutDocumentPaneGroup; - int i = 0; - if (_tabIndex != -1) - { - i = _tabIndex; - } - else - { - var previousIndex = 0; - var previousContainer = ((ILayoutPreviousContainer)sourceModel).PreviousContainer; - if (object.ReferenceEquals(previousContainer, targetModel) && (sourceModel.PreviousContainerIndex != -1)) - { - previousIndex = sourceModel.PreviousContainerIndex; - } - - i = previousIndex; - } - sourceModel.IsActive = false; - paneModel.Children.Insert(System.Math.Min(paneModel.Children.Count, i), sourceModel); - sourceModel.IsActive = true; + int i = _tabIndex == -1 ? 0 : _tabIndex; + foreach (var anchorableToImport in + layoutDocumentPaneGroup.Descendents().OfType().ToArray()) + { + paneModel.Children.Insert(i, anchorableToImport); + i++; } + } break; - #endregion - - + #endregion } + documentActive.IsActive = true; + base.Drop(floatingWindow); } diff --git a/source/Components/AvalonDock/Controls/DocumentPaneGroupDropTarget.cs b/source/Components/AvalonDock/Controls/DocumentPaneGroupDropTarget.cs index 5e249d76..7a41da17 100644 --- a/source/Components/AvalonDock/Controls/DocumentPaneGroupDropTarget.cs +++ b/source/Components/AvalonDock/Controls/DocumentPaneGroupDropTarget.cs @@ -44,8 +44,8 @@ protected override void Drop(LayoutDocumentFloatingWindow floatingWindow) #region DropTargetType.DocumentPaneGroupDockInside { var paneGroupModel = targetModel as LayoutDocumentPaneGroup; - var paneModel = paneGroupModel.Children[0] as LayoutDocumentPane; - var sourceModel = floatingWindow.RootDocument; + var paneModel = paneGroupModel as LayoutDocumentPaneGroup; + var sourceModel = floatingWindow.RootPanel as LayoutDocumentPaneGroup; paneModel.Children.Insert(0, sourceModel); } diff --git a/source/Components/AvalonDock/Controls/DragService.cs b/source/Components/AvalonDock/Controls/DragService.cs index cfc0f1f9..4ed0c635 100644 --- a/source/Components/AvalonDock/Controls/DragService.cs +++ b/source/Components/AvalonDock/Controls/DragService.cs @@ -186,6 +186,7 @@ internal void Abort() private void GetOverlayWindowHosts() { _overlayWindowHosts.AddRange(_manager.GetFloatingWindowsByZOrder().OfType().Where(fw => fw != _floatingWindow && fw.IsVisible)); + _overlayWindowHosts.AddRange(_manager.GetFloatingWindowsByZOrder().OfType().Where(fw => fw != _floatingWindow && fw.IsVisible)); _overlayWindowHosts.Add(_manager); } diff --git a/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs index f2c981c8..bbd210af 100644 --- a/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutDocumentFloatingWindowControl.cs @@ -8,14 +8,18 @@ This program is provided to you under the terms of the Microsoft Public ************************************************************************/ using System; +using System.Collections.Generic; +using System.Linq; using AvalonDock.Layout; using System.Windows; using System.Windows.Controls.Primitives; +using System.Windows.Input; +using AvalonDock.Commands; using Microsoft.Windows.Shell; namespace AvalonDock.Controls { - public class LayoutDocumentFloatingWindowControl : LayoutFloatingWindowControl + public class LayoutDocumentFloatingWindowControl : LayoutFloatingWindowControl, IOverlayWindowHost { #region Members @@ -34,6 +38,9 @@ internal LayoutDocumentFloatingWindowControl(LayoutDocumentFloatingWindow model, : base(model, isContentImmutable) { _model = model; + HideWindowCommand = new RelayCommand((p) => OnExecuteHideWindowCommand(p), (p) => CanExecuteHideWindowCommand(p)); + CloseWindowCommand = new RelayCommand((p) => OnExecuteCloseWindowCommand(p), (p) => CanExecuteCloseWindowCommand(p)); + Closed += (sender, args) => { Owner?.Focus(); }; UpdateThemeResources(); } @@ -44,43 +51,72 @@ internal LayoutDocumentFloatingWindowControl(LayoutDocumentFloatingWindow model) #endregion - #region Properties + #region Overrides - public LayoutItem RootDocumentLayoutItem + public override ILayoutElement Model { get { - return _model.Root.Manager.GetLayoutItemFromModel(_model.RootDocument); + return _model; } } - #endregion + #region SingleContentLayoutItem + + /// + /// SingleContentLayoutItem Dependency Property + /// + public static readonly DependencyProperty SingleContentLayoutItemProperty = + DependencyProperty.Register("SingleContentLayoutItem", typeof(LayoutItem), typeof(LayoutDocumentFloatingWindowControl), + new FrameworkPropertyMetadata((LayoutItem)null, + new PropertyChangedCallback(OnSingleContentLayoutItemChanged))); + + /// + /// Gets or sets the SingleContentLayoutItem property. This dependency property + /// indicates the layout item of the selected content when is shown a single document pane. + /// + public LayoutItem SingleContentLayoutItem + { + get { return (LayoutItem)GetValue(SingleContentLayoutItemProperty); } + set { SetValue(SingleContentLayoutItemProperty, value); } + } - #region Overrides + /// + /// Handles changes to the property. + /// + private static void OnSingleContentLayoutItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((LayoutDocumentFloatingWindowControl)d).OnSingleContentLayoutItemChanged(e); + } - public override ILayoutElement Model + /// + /// Provides derived classes an opportunity to handle changes to the property. + /// + protected virtual void OnSingleContentLayoutItemChanged(DependencyPropertyChangedEventArgs e) { - get - { - return _model; - } } + #endregion + protected override void OnInitialized(EventArgs e) { base.OnInitialized(e); - if (_model.RootDocument == null) - { - InternalClose(); - } - else - { - var manager = _model.Root.Manager; + var manager = _model.Root.Manager; - Content = manager.CreateUIElementForModel(_model.RootDocument); + Content = manager.CreateUIElementForModel(_model.RootPanel); + // TODO IsVisibleChanged + //SetBinding(SingleContentLayoutItemProperty, new Binding("Model.SinglePane.SelectedContent") { Source = this, Converter = new LayoutItemFromLayoutModelConverter() }); - _model.RootDocumentChanged += new EventHandler(_model_RootDocumentChanged); + _model.RootPanel.ChildrenCollectionChanged += RootPanelOnChildrenCollectionChanged; + } + + void _model_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + if (e.PropertyName == "RootPanel" && + _model.RootPanel == null) + { + InternalClose(); } } @@ -91,8 +127,8 @@ protected override IntPtr FilterMessage(IntPtr hwnd, int msg, IntPtr wParam, Int case Win32Helper.WM_NCLBUTTONDOWN: //Left button down on title -> start dragging over docking manager if (wParam.ToInt32() == Win32Helper.HT_CAPTION) { - if (_model.RootDocument != null) - _model.RootDocument.IsActive = true; + _model.Descendents().OfType().First(p => p.ChildrenCount > 0 && p.SelectedContent != null).SelectedContent.IsActive = true; + handled = true; } break; case Win32Helper.WM_NCRBUTTONUP: @@ -115,26 +151,36 @@ protected override IntPtr FilterMessage(IntPtr hwnd, int msg, IntPtr wParam, Int protected override void OnClosed(EventArgs e) { var root = Model.Root; - root.Manager.RemoveFloatingWindow(this); - root.CollectGarbage(); + // MK sometimes root is null, prevent crash, or should it always be set?? + if (root != null) + { + root.Manager.RemoveFloatingWindow(this); + root.CollectGarbage(); + } + + if (_overlayWindow != null) + { + _overlayWindow.Close(); + _overlayWindow = null; + } base.OnClosed(e); if (!CloseInitiatedByUser) { - root.FloatingWindows.Remove(_model); + root?.FloatingWindows.Remove(_model); } - _model.RootDocumentChanged -= new EventHandler(_model_RootDocumentChanged); + _model.PropertyChanged -= new System.ComponentModel.PropertyChangedEventHandler(_model_PropertyChanged); } #endregion #region Private Methods - private void _model_RootDocumentChanged(object sender, EventArgs e) + private void RootPanelOnChildrenCollectionChanged(object sender, EventArgs e) { - if (_model.RootDocument == null) + if( _model.RootPanel == null ||_model.RootPanel.Children.Count == 0) { InternalClose(); } @@ -143,11 +189,11 @@ private void _model_RootDocumentChanged(object sender, EventArgs e) private bool OpenContextMenu() { var ctxMenu = _model.Root.Manager.DocumentContextMenu; - if (ctxMenu != null && RootDocumentLayoutItem != null) + if (ctxMenu != null && SingleContentLayoutItem != null) { ctxMenu.PlacementTarget = null; ctxMenu.Placement = PlacementMode.MousePoint; - ctxMenu.DataContext = RootDocumentLayoutItem; + ctxMenu.DataContext = SingleContentLayoutItem; ctxMenu.IsOpen = true; return true; } @@ -155,6 +201,196 @@ private bool OpenContextMenu() return false; } + protected override void OnClosing(System.ComponentModel.CancelEventArgs e) + { + // TODO + if (CloseInitiatedByUser && !KeepContentVisibleOnClose) + { + e.Cancel = true; + //_model.Descendents().OfType().ToArray().ForEach((a) => a.Hide()); + } + + base.OnClosing(e); + } + + bool IOverlayWindowHost.HitTest(Point dragPoint) + { + Rect detectionRect = new Rect(this.PointToScreenDPIWithoutFlowDirection(new Point()), this.TransformActualSizeToAncestor()); + return detectionRect.Contains(dragPoint); + } + + DockingManager IOverlayWindowHost.Manager + { + get { return _model.Root.Manager; } + } + + OverlayWindow _overlayWindow = null; + + void CreateOverlayWindow() + { + if (_overlayWindow == null) + _overlayWindow = new OverlayWindow(this); + Rect rectWindow = new Rect(this.PointToScreenDPIWithoutFlowDirection(new Point()), this.TransformActualSizeToAncestor()); + _overlayWindow.Left = rectWindow.Left; + _overlayWindow.Top = rectWindow.Top; + _overlayWindow.Width = rectWindow.Width; + _overlayWindow.Height = rectWindow.Height; + } + + IOverlayWindow IOverlayWindowHost.ShowOverlayWindow(LayoutFloatingWindowControl draggingWindow) + { + CreateOverlayWindow(); + _overlayWindow.Owner = draggingWindow; + _overlayWindow.EnableDropTargets(); + _overlayWindow.Show(); + + return _overlayWindow; + } + + public void HideOverlayWindow() + { + _dropAreas = null; + _overlayWindow.Owner = null; + _overlayWindow.HideDropTargets(); + } + + List _dropAreas = null; + public IEnumerable GetDropAreas(LayoutFloatingWindowControl draggingWindow) + { + if (_dropAreas != null) + return _dropAreas; + + _dropAreas = new List(); + + var rootVisual = (Content as FloatingWindowContentHost).RootVisual; + + foreach (var areaHost in rootVisual.FindVisualChildren()) + { + _dropAreas.Add(new DropArea( + areaHost, + DropAreaType.AnchorablePane)); + } + foreach (var areaHost in rootVisual.FindVisualChildren()) + { + _dropAreas.Add(new DropArea( + areaHost, + DropAreaType.DocumentPane)); + } + + return _dropAreas; + } + + #region HideWindowCommand + public ICommand HideWindowCommand + { + get; + private set; + } + + private bool CanExecuteHideWindowCommand(object parameter) + { + if (Model == null) + return false; + + var root = Model.Root; + if (root == null) + return false; + + var manager = root.Manager; + if (manager == null) + return false; + + // TODO check CanHide of anchorables + bool canExecute = false; + foreach (var content in this.Model.Descendents().OfType().ToArray()) + { + if ((content is LayoutAnchorable anchorable && !anchorable.CanHide) || + !content.CanClose) + { + canExecute = false; + break; + } + + //if (!(manager.GetLayoutItemFromModel(content) is LayoutAnchorableItem layoutAnchorableItem) || + // layoutAnchorableItem.HideCommand == null || + // !layoutAnchorableItem.HideCommand.CanExecute(parameter)) + //{ + // canExecute = false; + // break; + //} + if (!(manager.GetLayoutItemFromModel(content) is LayoutItem layoutItem) || + layoutItem.CloseCommand == null || + !layoutItem.CloseCommand.CanExecute(parameter)) + { + canExecute = false; + break; + } + + canExecute = true; + } + + return canExecute; + } + + private void OnExecuteHideWindowCommand(object parameter) + { + var manager = Model.Root.Manager; + foreach (var anchorable in this.Model.Descendents().OfType().ToArray()) + { + //if (manager.GetLayoutItemFromModel(anchorable) is LayoutAnchorableItem layoutAnchorableItem) layoutAnchorableItem.HideCommand.Execute(parameter); + //else + if (manager.GetLayoutItemFromModel(anchorable) is LayoutItem layoutItem) layoutItem.CloseCommand.Execute(parameter); + } + } + #endregion + + #region CloseWindowCommand + public ICommand CloseWindowCommand + { + get; + private set; + } + + private bool CanExecuteCloseWindowCommand( object parameter ) + { + var manager = Model?.Root?.Manager; + if( manager == null ) + return false; + + var canExecute = false; + foreach( var document in this.Model.Descendents().OfType().ToArray() ) + { + if( !document.CanClose ) + { + canExecute = false; + break; + } + + if( !(manager.GetLayoutItemFromModel( document ) is LayoutDocumentItem documentLayoutItem) || + documentLayoutItem.CloseCommand == null || + !documentLayoutItem.CloseCommand.CanExecute( parameter ) ) + { + canExecute = false; + break; + } + canExecute = true; + } + + return canExecute; + } + + private void OnExecuteCloseWindowCommand( object parameter ) + { + var manager = Model.Root.Manager; + foreach( var document in this.Model.Descendents().OfType().ToArray() ) + { + var documentLayoutItem = manager.GetLayoutItemFromModel( document ) as LayoutDocumentItem; + documentLayoutItem?.CloseCommand.Execute(parameter); + } + } + #endregion - } + + #endregion + } } diff --git a/source/Components/AvalonDock/Controls/LayoutDocumentPaneControl.cs b/source/Components/AvalonDock/Controls/LayoutDocumentPaneControl.cs index 293d51aa..4e7563b4 100644 --- a/source/Components/AvalonDock/Controls/LayoutDocumentPaneControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutDocumentPaneControl.cs @@ -20,7 +20,6 @@ public class LayoutDocumentPaneControl : TabControl, ILayoutControl//, ILogicalC { #region Members - private List _logicalChildren = new List(); private LayoutDocumentPane _model; #endregion @@ -62,22 +61,6 @@ public ILayoutElement Model #region Overrides - protected override System.Collections.IEnumerator LogicalChildren - { - get - { - return _logicalChildren.GetEnumerator(); - } - } - - protected override void OnSelectionChanged(SelectionChangedEventArgs e) - { - base.OnSelectionChanged(e); - - if (_model.SelectedContent != null) - _model.SelectedContent.IsActive = true; - } - protected override void OnMouseLeftButtonDown(System.Windows.Input.MouseButtonEventArgs e) { base.OnMouseLeftButtonDown(e); diff --git a/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs b/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs index 5a5ea4dd..34c17b60 100644 --- a/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs +++ b/source/Components/AvalonDock/Controls/LayoutFloatingWindowControl.cs @@ -483,8 +483,6 @@ private void OnActivated(object sender, EventArgs e) { IntPtr windowHandle = new WindowInteropHelper(this).Handle; var mousePosition = this.PointToScreenDPI(Mouse.GetPosition(this)); - var clientArea = Win32Helper.GetClientRect(windowHandle); - var windowArea = Win32Helper.GetWindowRect(windowHandle); // BugFix Issue #6 // This code is initializes the drag when content (document or toolwindow) is dragged diff --git a/source/Components/AvalonDock/DockingManager.cs b/source/Components/AvalonDock/DockingManager.cs index cf6155e8..18ba44d2 100644 --- a/source/Components/AvalonDock/DockingManager.cs +++ b/source/Components/AvalonDock/DockingManager.cs @@ -2132,7 +2132,7 @@ internal UIElement CreateUIElementForModel(ILayoutElement model) }; newFW.SetParentToMainWindowOf(this); - var paneForExtensions = modelFW.RootDocument; + var paneForExtensions = modelFW.RootPanel; if (paneForExtensions != null) { //ensure that floating window position is inside current (or nearest) monitor @@ -2201,15 +2201,31 @@ internal void StartDraggingFloatingWindowForContent(LayoutContent contentModel, if (contentModel.CanFloat == false) return; - var fwc = this.CreateFloatingWindow(contentModel, false); + LayoutFloatingWindowControl fwc = null; + + // For last document re-use floating window + if (contentModel.Parent.ChildrenCount == 1) + { + foreach (var fw in _fwList) + foreach (var layoutElement in ((LayoutDocumentFloatingWindow)fw.Model).Children) + foreach (var pane in ((LayoutDocumentPaneGroup)layoutElement).Children) + foreach (var layoutDoc in ((LayoutDocumentPane)pane).Children) + if (layoutDoc == contentModel) + fwc = fw; + } + + var show = fwc == null; // Do not show already visible floating window + if (fwc == null) + fwc = this.CreateFloatingWindow(contentModel, false); + if (fwc != null) { Dispatcher.BeginInvoke(new Action(() => - { - if (startDrag) - fwc.AttachDrag(); - fwc.Show(); - }), DispatcherPriority.Send); + { + // Activate only inactive document + if (startDrag) fwc.AttachDrag(); + if (show) fwc.Show(); + }), DispatcherPriority.Send); } } @@ -3330,7 +3346,18 @@ private LayoutFloatingWindowControl CreateFloatingWindowCore(LayoutContent conte var anchorableDocument = contentModel as LayoutDocument; fw = new LayoutDocumentFloatingWindow() { - RootDocument = anchorableDocument + RootPanel = new LayoutDocumentPaneGroup( + new LayoutDocumentPane(anchorableDocument) + { + DockWidth = parentPaneAsPositionableElement.DockWidth, + DockHeight = parentPaneAsPositionableElement.DockHeight, + DockMinHeight = parentPaneAsPositionableElement.DockMinHeight, + DockMinWidth = parentPaneAsPositionableElement.DockMinWidth, + FloatingLeft = parentPaneAsPositionableElement.FloatingLeft, + FloatingTop = parentPaneAsPositionableElement.FloatingTop, + FloatingWidth = parentPaneAsPositionableElement.FloatingWidth, + FloatingHeight = parentPaneAsPositionableElement.FloatingHeight, + }) }; Layout.FloatingWindows.Add(fw); diff --git a/source/Components/AvalonDock/Layout/LayoutDocumentFloatingWindow.cs b/source/Components/AvalonDock/Layout/LayoutDocumentFloatingWindow.cs index a85ac2d6..a67e1ec8 100644 --- a/source/Components/AvalonDock/Layout/LayoutDocumentFloatingWindow.cs +++ b/source/Components/AvalonDock/Layout/LayoutDocumentFloatingWindow.cs @@ -11,14 +11,15 @@ This program is provided to you under the terms of the Microsoft Public using System.Collections.Generic; using System.Windows.Markup; using System.Diagnostics; +using System.Linq; using System.Xml.Serialization; using System.Xml; namespace AvalonDock.Layout { - [ContentProperty("RootDocument")] + [ContentProperty("RootPanel")] [Serializable] - public class LayoutDocumentFloatingWindow : LayoutFloatingWindow + public class LayoutDocumentFloatingWindow : LayoutFloatingWindow, ILayoutElementWithVisibility { #region Constructors @@ -30,31 +31,68 @@ public LayoutDocumentFloatingWindow() #region Properties - #region RootDocument + #region RootPanel - private LayoutDocument _rootDocument = null; - public LayoutDocument RootDocument + private LayoutDocumentPaneGroup _rootPanel = null; + public LayoutDocumentPaneGroup RootPanel { get { - return _rootDocument; + return _rootPanel; } set { - if (_rootDocument != value) + if( _rootPanel != value ) { - RaisePropertyChanging("RootDocument"); - _rootDocument = value; - if (_rootDocument != null) - _rootDocument.Parent = this; - RaisePropertyChanged("RootDocument"); - - if (RootDocumentChanged != null) - RootDocumentChanged(this, EventArgs.Empty); + if (_rootPanel != null) + _rootPanel.ChildrenTreeChanged -= new EventHandler(_rootPanel_ChildrenTreeChanged); + + _rootPanel = value; + if (_rootPanel != null) + _rootPanel.Parent = this; + + if (_rootPanel != null) + _rootPanel.ChildrenTreeChanged += new EventHandler(_rootPanel_ChildrenTreeChanged); + + RaisePropertyChanged("RootPanel"); + RaisePropertyChanged("IsSinglePane"); + RaisePropertyChanged("SinglePane"); + RaisePropertyChanged("Children"); + RaisePropertyChanged("ChildrenCount"); + ((ILayoutElementWithVisibility)this).ComputeVisibility(); } } } + void _rootPanel_ChildrenTreeChanged(object sender, ChildrenTreeChangedEventArgs e) + { + RaisePropertyChanged("IsSinglePane"); + RaisePropertyChanged("SinglePane"); + + } + + public bool IsSinglePane + { + get + { + return RootPanel != null && RootPanel.Descendents().OfType().Where(p => p.IsVisible).Count() == 1; + } + } + + public ILayoutDocumentPane SinglePane + { + get + { + if (!IsSinglePane) + return null; + + var singlePane = RootPanel.Descendents().OfType().Single(p => p.IsVisible); + //singlePane.UpdateIsDirectlyHostedInFloatingWindow(); + return singlePane; + } + } + + #endregion #endregion @@ -65,38 +103,67 @@ public override IEnumerable Children { get { - if (RootDocument == null) - yield break; - - yield return RootDocument; + if (ChildrenCount == 1) + yield return RootPanel; } } public override void RemoveChild(ILayoutElement element) { - Debug.Assert(element == RootDocument && element != null); - RootDocument = null; + Debug.Assert( element == RootPanel && element != null ); + RootPanel = null; } public override void ReplaceChild(ILayoutElement oldElement, ILayoutElement newElement) { - Debug.Assert(oldElement == RootDocument && oldElement != null); - RootDocument = newElement as LayoutDocument; + Debug.Assert( oldElement == RootPanel && oldElement != null ); + RootPanel = newElement as LayoutDocumentPaneGroup; } public override int ChildrenCount { get { - return RootDocument != null ? 1 : 0; + return RootPanel == null ? 0 : 1; } } + #region IsVisible + [NonSerialized] + private bool _isVisible = true; + + [XmlIgnore] + public bool IsVisible + { + get { return _isVisible; } + private set + { + if (_isVisible != value) + { + RaisePropertyChanging("IsVisible"); + _isVisible = value; + RaisePropertyChanged("IsVisible"); + if (IsVisibleChanged != null) + IsVisibleChanged(this, EventArgs.Empty); + } + } + } + + public event EventHandler IsVisibleChanged; + + #endregion + + void ILayoutElementWithVisibility.ComputeVisibility() + { + IsVisible = RootPanel != null && RootPanel.IsVisible; + } + + public override bool IsValid { get { - return RootDocument != null; + return RootPanel != null; } } @@ -140,7 +207,7 @@ public override void ReadXml(XmlReader reader) serializer = new XmlSerializer(type); } - RootDocument = (LayoutDocument)serializer.Deserialize(reader); + RootPanel = (LayoutDocumentPaneGroup)serializer.Deserialize(reader); } reader.ReadEndElement(); @@ -152,16 +219,10 @@ public override void ConsoleDump(int tab) System.Diagnostics.Trace.Write(new string(' ', tab * 4)); System.Diagnostics.Trace.WriteLine("FloatingDocumentWindow()"); - RootDocument.ConsoleDump(tab + 1); + RootPanel.ConsoleDump(tab + 1); } #endif #endregion - - #region Events - - public event EventHandler RootDocumentChanged; - - #endregion } } diff --git a/source/Components/AvalonDock/Layout/LayoutDocumentPane.cs b/source/Components/AvalonDock/Layout/LayoutDocumentPane.cs index 838597cd..1436101b 100644 --- a/source/Components/AvalonDock/Layout/LayoutDocumentPane.cs +++ b/source/Components/AvalonDock/Layout/LayoutDocumentPane.cs @@ -11,6 +11,7 @@ This program is provided to you under the terms of the Microsoft Public using System.Collections.Generic; using System.Linq; using System.Windows.Markup; +using System.Xml.Serialization; namespace AvalonDock.Layout { @@ -102,20 +103,6 @@ public LayoutContent SelectedContent #endregion - #region ChildrenSorted - - public IEnumerable ChildrenSorted - { - get - { - var listSorted = this.Children.ToList(); - listSorted.Sort(); - return listSorted; - } - } - - #endregion - #endregion #region Overrides @@ -143,41 +130,49 @@ protected override void ChildMoved(int oldIndex, int newIndex) protected override void OnChildrenCollectionChanged() { - if (this.SelectedContentIndex >= this.ChildrenCount) - this.SelectedContentIndex = this.Children.Count - 1; - if (this.SelectedContentIndex == -1) + AutoFixSelectedContent(); + for (int i = 0; i < Children.Count; i++) { - if (this.ChildrenCount > 0) + if (Children[i].IsSelected) { - if (this.Root == null) - { - this.SetNextSelectedIndex(); - } - else - { - var childrenToSelect = this.Children.OrderByDescending(c => c.LastActivationTimeStamp.GetValueOrDefault()).First(); - this.SelectedContentIndex = this.Children.IndexOf(childrenToSelect); - childrenToSelect.IsActive = true; - } - } - else - { - if (this.Root != null) - { - this.Root.ActiveContent = null; - } + SelectedContentIndex = i; + break; } } + RaisePropertyChanged("CanClose"); + RaisePropertyChanged("CanHide"); + RaisePropertyChanged("IsDirectlyHostedInFloatingWindow"); base.OnChildrenCollectionChanged(); - RaisePropertyChanged("ChildrenSorted"); + RaisePropertyChanged( "ChildrenSorted" ); + } + + [XmlIgnore] + bool _autoFixSelectedContent = true; + void AutoFixSelectedContent() + { + if (_autoFixSelectedContent) + { + if (SelectedContentIndex >= ChildrenCount) + SelectedContentIndex = Children.Count - 1; + + if (SelectedContentIndex == -1 && ChildrenCount > 0) + SetNextSelectedIndex(); + } } - protected override void OnIsVisibleChanged() + public bool IsDirectlyHostedInFloatingWindow { - this.UpdateParentVisibility(); - base.OnIsVisibleChanged(); + get + { + var parentFloatingWindow = this.FindParent(); + if (parentFloatingWindow != null) + return parentFloatingWindow.IsSinglePane; + + return false; + //return Parent != null && Parent.ChildrenCount == 1 && Parent.Parent is LayoutFloatingWindow; + } } public override void WriteXml(System.Xml.XmlWriter writer) @@ -219,7 +214,10 @@ public override void ConsoleDump(int tab) public int IndexOf(LayoutContent content) { - return Children.IndexOf(content); + var documentChild = content as LayoutDocument; + if (documentChild == null) + return -1; + return Children.IndexOf(documentChild); } #endregion @@ -243,13 +241,50 @@ internal void SetNextSelectedIndex() #region Private Methods - private void UpdateParentVisibility() + internal void UpdateIsDirectlyHostedInFloatingWindow() { - var parentPane = this.Parent as ILayoutElementWithVisibility; - if (parentPane != null) - parentPane.ComputeVisibility(); + RaisePropertyChanged("IsDirectlyHostedInFloatingWindow"); } + public bool IsHostedInFloatingWindow + { + get + { + return this.FindParent() != null; + } + } + + public IEnumerable ChildrenSorted + { + get + { + var listSorted = Children.ToList(); + listSorted.Sort(); + return listSorted; + } + } + + protected override void OnParentChanged(ILayoutContainer oldValue, ILayoutContainer newValue) + { + var oldGroup = oldValue as ILayoutGroup; + if (oldGroup != null) + oldGroup.ChildrenCollectionChanged -= new EventHandler(OnParentChildrenCollectionChanged); + + RaisePropertyChanged("IsDirectlyHostedInFloatingWindow"); + + var newGroup = newValue as ILayoutGroup; + if (newGroup != null) + newGroup.ChildrenCollectionChanged += new EventHandler(OnParentChildrenCollectionChanged); + + base.OnParentChanged(oldValue, newValue); + } + + void OnParentChildrenCollectionChanged(object sender, EventArgs e) + { + RaisePropertyChanged("IsDirectlyHostedInFloatingWindow"); + } + + #endregion #region ILayoutPaneSerializable Interface diff --git a/source/Components/AvalonDock/Layout/LayoutRoot.cs b/source/Components/AvalonDock/Layout/LayoutRoot.cs index baceeafa..4d7fdc30 100644 --- a/source/Components/AvalonDock/Layout/LayoutRoot.cs +++ b/source/Components/AvalonDock/Layout/LayoutRoot.cs @@ -482,6 +482,21 @@ public void CollectGarbage() exitFlag = false; break; } + foreach (var emptyPaneGroup in this.Descendents().OfType().Where(p => p.ChildrenCount == 0)) + { + var parentGroup = emptyPaneGroup.Parent as ILayoutContainer; + if (!(parentGroup.Parent is LayoutDocumentFloatingWindow)) continue; + var index = RootPanel.IndexOfChild(this.Descendents().OfType().First()); + parentGroup.RemoveChild(emptyPaneGroup); + if (!this.Descendents().OfType().Any()) + { + // Now the last Pane container is deleted, at least one is required for documents to be added. + // We did not want to keep an empty window floating, but add a new one to the main window + RootPanel.Children.Insert(index < 0 ? 0 : index, emptyPaneGroup); + } + exitFlag = false; + break; + } } if (!exitFlag) diff --git a/source/Components/AvalonDock/Themes/generic.xaml b/source/Components/AvalonDock/Themes/generic.xaml index 4c988d24..7e8bfe11 100644 --- a/source/Components/AvalonDock/Themes/generic.xaml +++ b/source/Components/AvalonDock/Themes/generic.xaml @@ -1021,12 +1021,17 @@ - - - - -