/// <summary>
        /// Tries to move the dragged items from the <see cref="FloatWindow"/> into a
        /// <see cref="DockTabPanel"/>.
        /// </summary>
        private void DragFloatWindowIntoDockTabPanel()
        {
            Debug.Assert(_targetDockTabPane == null);
            Debug.Assert(_floatWindow != null);

            var dockTabPane = GetTargetPane() as DockTabPane;

            if (dockTabPane == null)
            {
                return; // No DockTabPane (view).
            }
            var dockTabPaneVM = dockTabPane.GetViewModel();

            if (dockTabPaneVM == null)
            {
                return; // No IDockTabPane (view-model).
            }
            if (GetDockTabItemAtMouse(dockTabPane) == null)
            {
                return; // No DockTabItem hit.
            }
            if (!CanDock(dockTabPaneVM, DockPosition.Inside))
            {
                return; // Docking not allowed.
            }
            // Remove currently dragged FloatWindow.
            var floatWindowVM = _floatWindow.GetViewModel();

            foreach (var item in _draggedItems)
            {
                DockHelper.Remove(floatWindowVM, item);
            }

            _floatWindow = null;
            Win32.ReleaseCapture(); // Exit Win32 move window loop.

            // Add items into target DockTabPane.
            _targetDockTabPane = dockTabPane;
            foreach (var item in _draggedItems)
            {
                item.DockState = dockTabPaneVM.DockState;
                dockTabPaneVM.Items.Add(item);
            }

            // Make sure the current item is selected in DockTabPane.
            _dockStrategy.Activate(_activeItem);

            // When the Win32 move window loop exits, the DockControl receives a LostMouseCapture
            // event. --> Defer dragging of the DockTabItems.
            _dockControl.Dispatcher.BeginInvoke(new Action(() =>
            {
                if (!_dockControl.IsMouseCaptured)
                {
                    if (!_dockControl.CaptureMouse())
                    {
                        // Failed to capture the mouse.
                        EndDrag(false);
                        return;
                    }
                    _dockControl.LostMouseCapture  += OnLostMouseCapture;
                    _dockControl.MouseMove         += OnMouseMove;
                    _dockControl.MouseLeftButtonUp += OnMouseLeftButtonUp;
                    _dockControl.PreviewKeyDown    += OnPreviewKeyDown;

                    _targetDockTabPane.PreviewKeyDown += OnPreviewKeyDown;

                    DragDockTabItems();
                }
            }));

            HideBorderDockIndicators();
            HidePaneIndicators();

            _dockControl.UpdateFloatWindows();

            _layoutChanged = true;
        }
        private void DragDockTabItemsIntoFloatWindow()
        {
            Debug.Assert(_floatWindow == null);

            if (_targetDockTabPane != null)
            {
                ClearTranslateTransform();

                _targetDockTabPane.PreviewKeyDown -= OnPreviewKeyDown;
                _targetDockTabPane = null;
            }

            _dockControl.LostMouseCapture  -= OnLostMouseCapture;
            _dockControl.MouseLeftButtonUp -= OnMouseLeftButtonUp;
            _dockControl.MouseMove         -= OnMouseMove;
            _dockControl.PreviewKeyDown    -= OnPreviewKeyDown;

            _dockControl.ReleaseMouseCapture();

            // Remove the dragged items from their current location.
            foreach (var item in _draggedItems)
            {
                DockHelper.Remove(_dockStrategy.DockControl.RootPane, item);

                foreach (var floatWindow in _dockStrategy.DockControl.FloatWindows)
                {
                    DockHelper.Remove(floatWindow, item);
                }

                DockHelper.Remove(_dockStrategy.DockControl.AutoHideLeft, item);
                DockHelper.Remove(_dockStrategy.DockControl.AutoHideRight, item);
                DockHelper.Remove(_dockStrategy.DockControl.AutoHideTop, item);
                DockHelper.Remove(_dockStrategy.DockControl.AutoHideBottom, item);
            }

            // Move items into a new FloatWindow.
            foreach (var item in _draggedItems)
            {
                item.DockState = DockState.Float;
            }

            var newPaneVM = _dockStrategy.CreateDockTabPane(_draggedItems[0], DockState.Float);

            for (int i = 1; i < _draggedItems.Count; i++)
            {
                newPaneVM.Items.Add(_draggedItems[i]);
            }

            var floatWindowVM = _dockStrategy.CreateFloatWindow();

            floatWindowVM.RootPane = newPaneVM;
            _dockStrategy.DockControl.FloatWindows.Add(floatWindowVM);
            _dockStrategy.Activate(_activeItem);
            _dockStrategy.Cleanup();

            // Get the newly created FloatWindow (view) from the DockControl.
            _floatWindow = GetFloatWindow(floatWindowVM);

            Debug.Assert(_floatWindow != null);

            LimitFloatWindowSize(_floatWindow, _initialSize);

            // Limit mouse offset to FloatWindow size.
            double actualWidth = _floatWindow.ActualWidth;

            if (actualWidth > 0)
            {
                _mouseOffset.X = Math.Min(_mouseOffset.X, actualWidth / 2);
            }

            Point position = GetFloatWindowPosition();

            _floatWindow.Left = position.X;
            _floatWindow.Top  = position.Y;

            // Wait until FloatWindow is loaded, initiate the Win32 move window loop.
            _floatWindow.Dispatcher.BeginInvoke(new Action(() =>
            {
                if (_floatWindow != null)
                {
                    LimitFloatWindowSize(_floatWindow, _initialSize);

                    // Limit mouse offset to FloatWindow size.
                    _mouseOffset.X = Math.Min(_mouseOffset.X, _floatWindow.ActualWidth / 2);
                    _mouseOffset.Y = Math.Max(_mouseOffset.Y, 8);

                    Point pos         = GetFloatWindowPosition();
                    _floatWindow.Left = pos.X;
                    _floatWindow.Top  = pos.Y;

                    if (Mouse.LeftButton == MouseButtonState.Pressed)
                    {
                        _floatWindow.DragMove();
                    }
                    else
                    {
                        EndDrag(true);
                    }
                }
            }), DispatcherPriority.Loaded); // Important: Action needs to be invoked before input.
        }
Exemple #3
0
 internal static IFloatWindow GetViewModel(this FloatWindow view)
 {
     return(GetViewModel <IFloatWindow>(view));
 }
        /// <summary>
        /// Starts a drag operation.
        /// </summary>
        /// <param name="floatWindow">The <see cref="FloatWindow"/> to be dragged.</param>
        /// <param name="dockTabPane">The <see cref="DockTabPane"/> to be dragged.</param>
        /// <param name="dockTabItem">The <see cref="DockTabItem"/> to be dragged.</param>
        /// <returns>
        /// <see langword="true" /> if the drag operation has been started; otherwise,
        /// <see langword="false" /> if the drag operation could not be started (e.g. because the
        /// mouse could not be captured).
        /// </returns>
        private bool BeginDrag(FloatWindow floatWindow, DockTabPane dockTabPane, DockTabItem dockTabItem)
        {
            _dockStrategy = _dockControl.GetViewModel()?.DockStrategy;
            if (_dockStrategy == null || _dockStrategy.DockControl.IsLocked)
            {
                Reset();
                return(false);
            }

            FrameworkElement element     = null;
            IDockTabPane     draggedPane = null;
            IDockTabItem     draggedItem = null;

            if (floatWindow != null)
            {
                // User is dragging a FloatWindow.
                // (Note: Dragging of FloatWindows with nested layouts is not supported.)
                draggedPane  = floatWindow.GetViewModel()?.RootPane as IDockTabPane;
                element      = floatWindow;
                _floatWindow = floatWindow;
                _initialSize = floatWindow.RenderSize;

                // Start dragging immediately.
                _dragDeltaExceeded = true;
            }
            else if (dockTabItem != null)
            {
                // User is dragging a DockTabItem in a DockTabPanel.
                draggedItem        = dockTabItem.GetViewModel();
                element            = dockTabItem;
                _targetDockTabPane = dockTabPane;
                _initialSize       = dockTabPane.RenderSize;

                // Start dragging when threshold is exceeded.
                _initialMousePosition = WindowsHelper.GetMousePosition(_dockControl);
                _dragDeltaExceeded    = false;
            }
            else if (dockTabPane != null)
            {
                // User is dragging a DockTabPane.
                draggedPane           = dockTabPane.GetViewModel();
                element               = dockTabPane;
                _initialSize          = dockTabPane.RenderSize;
                _initialMousePosition = WindowsHelper.GetMousePosition(_dockControl);

                // Start dragging when threshold is exceeded.
                _initialMousePosition = WindowsHelper.GetMousePosition(_dockControl);
                _dragDeltaExceeded    = false;
            }

            if (draggedPane == null && draggedItem == null)
            {
                Reset();
                return(false);
            }

            // When the user is dragging the FloatWindow, the mouse is captured by Win32 move window
            // loop. When dragging a DockTabPane or DockTabItem, the mouse needs to be
            // captured to receive mouse events.
            if (_floatWindow == null)
            {
                if (!_dockControl.CaptureMouse())
                {
                    // Failed to capture the mouse.
                    Reset();
                    return(false);
                }

                _dockControl.LostMouseCapture  += OnLostMouseCapture;
                _dockControl.MouseLeftButtonUp += OnMouseLeftButtonUp;
                _dockControl.MouseMove         += OnMouseMove;
                _dockControl.PreviewKeyDown    += OnPreviewKeyDown;
                if (_targetDockTabPane != null)
                {
                    _targetDockTabPane.PreviewKeyDown += OnPreviewKeyDown;
                }
            }

            _dockStrategy.Begin();

            if (draggedPane != null)
            {
                _dockStrategy.Activate(draggedPane);
                _activeItem = draggedPane.SelectedItem;
                foreach (var item in draggedPane.Items)
                {
                    if (item.DockState == draggedPane.DockState)
                    {
                        _draggedItems.Add(item);
                    }
                }
            }
            else
            {
                Debug.Assert(draggedItem != null);

                _dockStrategy.Activate(draggedItem);
                _activeItem = draggedItem;
                _draggedItems.Add(draggedItem);
            }

            Debug.Assert(_draggedItems.Count > 0);

            // Determine whether dragged items may end in a FloatWindow.
            _canFloat = CanFloat();

            // Store the mouse offset relative to the dragged element.
            _mouseOffset = (Vector)WindowsHelper.GetMousePosition(element);

            // Remember information needed for a rollback.
            ReplaceItemsWithProxies(draggedPane ?? _targetDockTabPane.GetViewModel());
            _originalDockState = _draggedItems[0].DockState;
            BackupFloatWindowPosition();

            // Override mouse cursors. (Mouse cursor should not change to caret over text editor.)
            Mouse.OverrideCursor = Cursors.Arrow;

            return(true);
        }
        internal void UpdateFloatWindows()
        {
            if (DockStrategy == null)
            {
                return;
            }

            var owner = Window.GetWindow(this);

            // Close obsolete FloatWindows.
            for (int i = _floatWindows.Count - 1; i >= 0; i--)
            {
                var floatWindow   = _floatWindows[i];
                var floatWindowVM = floatWindow.GetViewModel();
                if (floatWindowVM == null ||
                    !DockStrategy.DockControl.FloatWindows.Contains(floatWindowVM) ||
                    !DockStrategy.IsVisible(floatWindowVM))
                {
                    // ----- Close FloatWindow.
                    floatWindow.Close();
                    _floatWindows.RemoveAt(i);
                }
            }

            // Open new FloatWindows.
            for (int i = 0; i < DockStrategy.DockControl.FloatWindows.Count; i++)
            {
                var floatWindowVM = DockStrategy.DockControl.FloatWindows[i];
                if (DockStrategy.IsVisible(floatWindowVM) && GetView(floatWindowVM) == null)
                {
                    // ----- Open FloatWindow.

                    // Make sure that the floating window stays on the screen.
                    // At least 30 pixels must be visible.
                    const double safety       = 30;
                    double       screenLeft   = SystemParameters.VirtualScreenLeft;
                    double       screenTop    = SystemParameters.VirtualScreenTop;
                    double       screenRight  = screenLeft + SystemParameters.VirtualScreenWidth;
                    double       screenBottom = screenTop + SystemParameters.VirtualScreenHeight;
                    floatWindowVM.Left = Math.Min(Math.Max(floatWindowVM.Left, screenLeft), screenRight - safety);
                    floatWindowVM.Top  = Math.Min(Math.Max(floatWindowVM.Top, screenTop), screenBottom - safety);

                    var floatWindow = new FloatWindow(this)
                    {
                        DataContext = floatWindowVM,
                        Owner       = owner,
                    };

                    bool autoWidth  = Numeric.IsNaN(floatWindowVM.Width);
                    bool autoHeight = Numeric.IsNaN(floatWindowVM.Height);
                    if (autoWidth && autoHeight)
                    {
                        floatWindow.SizeToContent = SizeToContent.WidthAndHeight;
                    }
                    else if (autoWidth)
                    {
                        floatWindow.SizeToContent = SizeToContent.Width;
                    }
                    else if (autoHeight)
                    {
                        floatWindow.SizeToContent = SizeToContent.Height;
                    }
                    else
                    {
                        floatWindow.SizeToContent = SizeToContent.Manual;
                    }

                    floatWindow.SetBinding(ContentProperty, new Binding(nameof(IFloatWindow.RootPane)));
                    floatWindow.SetBinding(WidthProperty, new Binding(nameof(IFloatWindow.Width))
                    {
                        Mode = BindingMode.TwoWay
                    });
                    floatWindow.SetBinding(HeightProperty, new Binding(nameof(IFloatWindow.Height))
                    {
                        Mode = BindingMode.TwoWay
                    });
                    floatWindow.SetBinding(Window.LeftProperty, new Binding(nameof(IFloatWindow.Left))
                    {
                        Mode = BindingMode.TwoWay
                    });
                    floatWindow.SetBinding(Window.TopProperty, new Binding(nameof(IFloatWindow.Top))
                    {
                        Mode = BindingMode.TwoWay
                    });

                    floatWindow.Show();

                    // Bind WindowState after showing the window. Otherwise, it could be maximized
                    // on the wrong screen.
                    floatWindow.SetBinding(Window.WindowStateProperty, new Binding(nameof(IFloatWindow.WindowState))
                    {
                        Mode = BindingMode.TwoWay
                    });

                    _floatWindows.Add(floatWindow);
                }
            }
        }