玩转Qml(11)-更强的拖动组件
            
        
        
        
        
        
            本文于
                1038
            天之前发表,文中内容可能已经过时。
        
        
     
    
        简介
本文是《玩转Qml》系列文章的第十一篇,之前的<玩转Qml(2)-可以拖动的组件>分享过基本的
拖动组件,这次涛哥将教大家,实现更多功能的可拖动组件。
源码
《玩转Qml》系列文章,配套了一个优秀的开源项目:TaoQuick
github https://github.com/jaredtao/TaoQuick
访问不了或者速度太慢,可以用国内的镜像网站gitee
https://gitee.com/jaredtao/TaoQuick
效果图

使用
封装的组件名称是TemplateBorder。
使用很简单,在要支持拖拽的目标组件上,创建一个TemplateBorder实例即可,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
   | Rectangle {     x: 100     y: 200     width: 300     height: 200     color: "red"     smooth: true     antialiasing: true     MouseArea {         anchors.fill: parent         onClicked: {             parent.focus = true         }     }          TemplateBorder {                  width: parent.width + borderMargin * 2         height: parent.height + borderMargin * 2         anchors.centerIn: parent                  visible: parent.focus     } }
  | 
 
原理
拖拽的前提
目标组件不要用锚布局,不要用Layout布局。
拖拽需要修改目标组件的坐标和宽高,而锚布局、Layout会限定坐标或宽高。
拖拽原理
拖拽本身可以使用MouseArea的 drag.target,但这个target限制为item及其子类。
有时候还需要处理无边框窗口的拖动,窗口不是item,就不能用drag.target。
所以需要一个通用的拖拽算法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
   | ... MouseArea {     id: mouseArea     anchors.fill: parent     property int lastX: 0     property int lastY: 0     onPressedChanged: {         if (containsPress) {                        lastX = mouseX;             lastY = mouseY;         }     }     onPositionChanged: {         if (pressed) {                        parent.x += mouseX - lastX             parent.y += mouseY - lastY         }     } } ...
   | 
 
锚点
锚点就是在组件的左上角、右上角等八个点,分别放一个小圆圈,圆圈里面是可拖拽组件,分别控制组件的坐标、宽高。
注意每个点的计算规则都不太一样。
例如左上角,要同时计算x、y和宽高,而右上角则只计算y、和宽高:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
   |  Item {     id: root               property var control: parent          TDragItem {         id: leftTopHandle         posType: posLeftTop         onPosChange: {             if (control.x + xOffset < control.x + control.width)                 control.x += xOffset;             if (control.y + yOffset < control.y + control.height)                 control.y += yOffset;             if (control.width - xOffset > 0)                 control.width-= xOffset;             if (control.height -yOffset > 0)                 control.height -= yOffset;         }     }          TDragItem {         id: rightTopHandle         posType: posRightTop         x: parent.width - width         onPosChange: {                          if (control.width + xOffset > 0)                 control.width += xOffset;             if (control.height - yOffset > 0)                 control.height -= yOffset;             if (control.y + yOffset < control.y + control.height)                 control.y += yOffset;         }     }     ...     ... }
 
  | 
 
旋转
旋转算法和拖拽类似
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
   | MouseArea {     id: rotateArea     anchors.centerIn: parent     property int lastX: 0
      onPressedChanged: {       if (containsPress) {         lastX = mouseX;       }     }     onPositionChanged: {       if (pressed) {         let t = controller.rotation +(mouseX - lastX) / 5         //这里的除以5,用来消除抖动。         t = t % 360         controller.rotation = t       }     } }
  |