docking window functionality in QML not working

270 Views Asked by At

I got inspired by this code https://github.com/wbt729/quickdock and extending it to make it work like native windows docking functionality like in Visual studio , where the user can grab the title bar and drag the window to mouse cursor.

I can drag and undock the window fine but it does not drag it to mouse cursor when initially moved

here is the example of the code :

import QtQuick
import QtQuick.Controls 
import QtQuick.Window 
import QtQuick.Controls.Material

Item {
    id: root
    default property alias contents: placeholder.data
    property alias title: titleLabel.text
    implicitHeight: mainWindow.height - statusBar.height - 15
    
    Rectangle {
        
        id: content
        anchors.fill: parent       
        height: implicitHeight
        state: "docked"        
        border.color: "slategrey"
        border.width: 1
        ToolBar {         
            id: toolBar
            implicitHeight: 30
            Material.foreground: "white"
            Material.background: "#dcdcdc"            
            anchors { top: parent.top; left: parent.left; right: parent.right }            
            Label {
                id: titleLabel
                color: "black"
                anchors { left: parent.left; leftMargin: 8; verticalCenter: parent.verticalCenter }
            }
            
            MouseArea {
                id: dragMouseArea
                anchors.fill: parent
                acceptedButtons: Qt.LeftButton | Qt.RightButton
                property variant clickPos: "0,0"
                onPressed: (mouse) => {
                               clickPos = Qt.point(mouse.x,mouse.y)
                               if (content.state === "docked") {
                                   // Set the initial position of the window when undocked
                                   window.x = mouse.x
                                   window.y = mouse.y                        
                               }
                           }    
                
                onPositionChanged:  (mouse) => {
                                        var delta = Qt.point(mouse.x-clickPos.x, mouse.y-clickPos.y)
                                        var new_x = window.x + delta.x
                                        var new_y = window.y + delta.y
                                        
                                        
                                        
                                        if (content.state === "docked") {
                                            // Check if the mouse is outside the bounds of the docked area
                                            if (mouse.y < toolBar.height || mouse.x < 0 || mouse.x > parent.width) {
                                                content.state = "undocked"
                                                // Move the window to the mouse position
                                                window.x = new_x
                                                window.y = new_y
                                                
                                                
                                                
                                                
                                            }
                                        } else {
                                            // Get the maximum x and y positions for the undocked window
                                            var max_x = Screen.desktopAvailableWidth - window.width
                                            var max_y = Screen.desktopAvailableHeight - window.height
                                            
                                            // Update the window position, making sure it stays within the screen bounds
                                            if (new_x < 0) new_x = 0
                                            if (new_x > max_x) new_x = max_x
                                            if (new_y < 0) new_y = 0
                                            if (new_y > max_y) new_y = max_y
                                            
                                            window.x = new_x
                                            window.y = new_y
                                            
                                            
                                        }
                                        
                                    }
                
                onClicked: {
                    if (mouse.button === Qt.RightButton)
                        contextMenu.popup()
                }
                
                Menu {
                    id: contextMenu
                    MenuItem { id: dockMenu; text: "Dock"; onClicked: content.state = "docked"; implicitHeight: 30}
                    MenuItem { id: unDockMenu; text: "Float"; onClicked: content.state = "undocked" ; implicitHeight: 30}  
                }
            }           
            
        }
        Item {
            id: placeholder
            anchors { top: toolBar.bottom; left: parent.left; right: parent.right; bottom: parent.bottom }
        }
        states: [
            State {
                name: "undocked"
                PropertyChanges { target: root; height: 0 }
                PropertyChanges { target: window; visible: true }
                ParentChange { target: content; parent: undockedContainer }
                //PropertyChanges { target: window; visible: true; x: 250; y: 250 }
                //this is to make sure the width becomes zero and rest of the control takes the realstate
                PropertyChanges {target: navigationWindow; width: 0 }
                PropertyChanges {target: toolBar; Material.background: "#f0e68c" }
                PropertyChanges {target: unDockMenu; enabled: false }
                PropertyChanges {target: dockMenu; enabled: true } 
            },
            State {
                name: "docked"
                PropertyChanges { target: root; height: implicitHeight }
                PropertyChanges { target: window; visible: false }
                ParentChange { target: content; parent: root }
                PropertyChanges {target: unDockMenu; enabled: true }
                PropertyChanges {target: dockMenu; enabled: false } 
            }
        ]
    }
    Window {
        id: window
        width: 250;
        height: 700;
        
        flags: Qt.Window | Qt.CustomizeWindowHint
        
        Item {         
            id: undockedContainer
            anchors.fill: parent
        }
        
        onClosing: {
            content.state = "docked"
        }
        
    }
}
1

There are 1 best solutions below

4
Jürgen Lutz On

Here one aproach to solve your positioning problem. Remove the onPress function and change the onPositionChanged function to this:

property point relativePos;

onPositionChanged:  (mouse) => {
     // get the mouse pos in screen coordinates
     let globalMousePos = mapToGlobal(Qt.point(mouse.x, mouse.y));
     var new_x = globalMousePos.x;
     var new_y = globalMousePos.y;

     if (content.state === "docked") {
        // Check if the mouse is outside the bounds of the docked area
       if (mouse.y < toolBar.height || mouse.x < 0 || mouse.x > parent.width) {
           console.log("Initial window position: " + new_x + " " + new_y);
           content.state = "undocked"
           //  store the click point where the drag started
           relativePos = mapToItem(root, Qt.point(mouse.x, mouse.y));
           window.x = new_x - relativePos.x;
           window.y = new_y - relativePos.y;
        }
    } else {
         console.log("Move the window to the mouse position: " + new_x + " " + new_y);
         window.x = Math.max(0,
                    Math.min(new_x - relativePos.x, Screen.desktopAvailableWidth - window.width));
         window.y = Math.max(0, Math.min(new_y - relativePos.y, Screen.desktopAvailableHeight - window.height));
     }

 }

See the comments for more details.