forked from qt-creator/qt-creator
Merge "Merge remote-tracking branch 'origin/qds/dev'"
This commit is contained in:
Binary file not shown.
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 26 KiB |
Binary file not shown.
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 28 KiB |
@@ -113,14 +113,35 @@
|
||||
\c {QtQuick.Controls 2.3} and to use Qt Quick Studio Components 1.0,
|
||||
you need the import statement \c {QtQuick.Studio.Components 1.0}.
|
||||
You can also import a module as an alias.
|
||||
\li In the \uicontrol {Properties} field, specify properties for the
|
||||
component. You can add and modify properties in \QDS.
|
||||
\li In the \uicontrol {Properties} field, specify new properties or assign
|
||||
value to the existing properties of the component. You can also add and modify
|
||||
properties in \QDS.
|
||||
Following are few examples of properties:
|
||||
\code
|
||||
property int counter: 5
|
||||
property string label: "ok"
|
||||
antialiasing : true
|
||||
width: parent.width / 2
|
||||
\endcode
|
||||
\li In the \uicontrol {Snippet} field, specify component to be added as child under this
|
||||
component.
|
||||
Following example adds a Connection component:
|
||||
\code
|
||||
Connections {
|
||||
target: myItem
|
||||
onVisibleChanged: console.log(original_Text.visible)
|
||||
}
|
||||
\endcode
|
||||
\note The code must have a scope of a component(e.g. Item, MouseArea, Connections etc.)
|
||||
with a valid syntax for \l {UI Files}.
|
||||
\li Select the \uicontrol Clip check box to enable clipping in the
|
||||
component generated from the layer. The generated component will clip
|
||||
its own painting, as well as the painting of its children, to its
|
||||
bounding rectangle.
|
||||
\li Select the \uicontrol Visible check box to determine the visibility
|
||||
of the layer in the generated UI in \QDS.
|
||||
\li Select the \uicontrol {Start Screen} check box to set the selected component as the
|
||||
start component.
|
||||
\li Select \uicontrol Export to launch the export dialog to export the document
|
||||
into a .qtbridge archive.
|
||||
\endlist
|
||||
|
@@ -226,6 +226,8 @@
|
||||
To align a camera to the \uicontrol{3D} view:
|
||||
\list 1
|
||||
\li Select a camera in the \uicontrol{3D} or \uicontrol {Navigator} view.
|
||||
\note If you don't have a camera selected, the most recently selected camera
|
||||
is aligned to the view.
|
||||
\li In the \uicontrol{3D} view,
|
||||
select \inlineimage icons/align-camera-on.png
|
||||
.
|
||||
@@ -234,9 +236,11 @@
|
||||
This moves and rotates the camera so that the camera shows the same view
|
||||
as the current view in the \uicontrol{3D} view.
|
||||
|
||||
To align the the \uicontrol{3D} view to a camera:
|
||||
To align the \uicontrol{3D} view to a camera:
|
||||
\list 1
|
||||
\li Select a camera in the \uicontrol{3D} view or \uicontrol {Navigator}.
|
||||
\note If you don't have a camera selected, the view is aligned to the most recently
|
||||
selected camera.
|
||||
\li In the \uicontrol{3D} view,
|
||||
select \inlineimage icons/align-view-on.png
|
||||
.
|
||||
|
@@ -11,7 +11,7 @@
|
||||
\note The \uicontrol {Content Library} view is included in the
|
||||
\l{https://www.qt.io/pricing}{Qt Design Studio Enterprise license}.
|
||||
|
||||
The \uicontrol{Content Library} view contains material, texture, and
|
||||
The \uicontrol{Content Library} view contains material, texture, effect, and
|
||||
environment bundles with assets that you can use in your project. When you
|
||||
have added an asset from \uicontrol {Content Library}, you can use it in
|
||||
your project.
|
||||
@@ -63,4 +63,15 @@
|
||||
\endlist
|
||||
\endlist
|
||||
|
||||
\section1 Adding an Effect to Your Project
|
||||
|
||||
To add an effect to your project, do one of the following:
|
||||
|
||||
\list
|
||||
\li Right-click the effect in \uicontrol {Content Library} and select
|
||||
\uicontrol {Add an Instance}.
|
||||
\li From \uicontrol {Content Library}, drag the effect to the \uicontrol 3D or
|
||||
\uicontrol Navigator view.
|
||||
\endlist
|
||||
|
||||
*/
|
||||
|
@@ -0,0 +1,13 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
|
||||
PopupDialog {
|
||||
property alias backend: form.backend
|
||||
|
||||
BindingsDialogForm {
|
||||
id: form
|
||||
y: 32
|
||||
}
|
||||
}
|
@@ -0,0 +1,101 @@
|
||||
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import StudioControls
|
||||
|
||||
Rectangle {
|
||||
width: 400
|
||||
height: 800
|
||||
color: "#1b1b1b"
|
||||
|
||||
property var backend
|
||||
|
||||
Text {
|
||||
id: text1
|
||||
x: 10
|
||||
y: 25
|
||||
color: "#ffffff"
|
||||
text: qsTr("Target")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
Text {
|
||||
id: text111
|
||||
x: 80
|
||||
y: 25
|
||||
color: "red"
|
||||
text: backend.targetNode
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
TopLevelComboBox {
|
||||
id: target
|
||||
x: 101
|
||||
width: 210
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: -335
|
||||
model: backend.property.model ?? []
|
||||
enabled: false
|
||||
//I see no use case to actually change the property name
|
||||
//onActivated: backend.targetNode.activateIndex(target.currentIndex)
|
||||
property int currentTypeIndex: backend.property.currentIndex ?? 0
|
||||
onCurrentTypeIndexChanged: target.currentIndex = target.currentTypeIndex
|
||||
}
|
||||
|
||||
Text {
|
||||
id: text2
|
||||
x: 13
|
||||
y: 111
|
||||
color: "#ffffff"
|
||||
text: qsTr("Source Propety")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
TopLevelComboBox {
|
||||
id: sourceNode
|
||||
x: 135
|
||||
y: 98
|
||||
width: 156
|
||||
|
||||
model: backend.sourceNode.model ?? []
|
||||
|
||||
onModelChanged: sourceNode.currentIndex = sourceNode.currentTypeIndex
|
||||
|
||||
onActivated: backend.sourceNode.activateIndex(sourceNode.currentIndex)
|
||||
property int currentTypeIndex: backend.sourceNode.currentIndex ?? 0
|
||||
onCurrentTypeIndexChanged: sourceNode.currentIndex = sourceNode.currentTypeIndex
|
||||
}
|
||||
|
||||
Text {
|
||||
x: 13
|
||||
y: 88
|
||||
color: "#ffffff"
|
||||
text: qsTr("Source Node")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
TopLevelComboBox {
|
||||
id: sourceProperty
|
||||
x: 140
|
||||
y: 121
|
||||
width: 156
|
||||
|
||||
model: backend.sourceProperty.model ?? []
|
||||
onModelChanged: sourceProperty.currentIndex = sourceProperty.currentTypeIndex
|
||||
onActivated: backend.sourceProperty.activateIndex(
|
||||
sourceProperty.currentIndex)
|
||||
property int currentTypeIndex: backend.sourceProperty.currentIndex ?? 0
|
||||
onCurrentTypeIndexChanged: sourceProperty.currentIndex = sourceProperty.currentTypeIndex
|
||||
}
|
||||
|
||||
Text {
|
||||
id: text3
|
||||
x: 10
|
||||
y: 55
|
||||
color: "#ffffff"
|
||||
text: qsTr("Property")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
}
|
@@ -0,0 +1,123 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import ConnectionsEditor
|
||||
import HelperWidgets 2.0 as HelperWidgets
|
||||
import StudioControls 1.0 as StudioControls
|
||||
import StudioTheme as StudioTheme
|
||||
import ConnectionsEditorEditorBackend
|
||||
|
||||
ListView {
|
||||
id: listView
|
||||
width: 606
|
||||
height: 160
|
||||
interactive: false
|
||||
highlightMoveDuration: 0
|
||||
|
||||
onVisibleChanged: {
|
||||
dialog.hide()
|
||||
}
|
||||
|
||||
property int modelCurrentIndex: listView.model.currentIndex ?? 0
|
||||
|
||||
/* Something weird with currentIndex happens when items are removed added.
|
||||
listView.model.currentIndex contains the persistent index.
|
||||
*/
|
||||
onModelCurrentIndexChanged: {
|
||||
listView.currentIndex = listView.model.currentIndex
|
||||
}
|
||||
|
||||
onCurrentIndexChanged: {
|
||||
listView.currentIndex = listView.model.currentIndex
|
||||
dialog.backend.currentRow = listView.currentIndex
|
||||
}
|
||||
|
||||
data: [
|
||||
BindingsDialog {
|
||||
id: dialog
|
||||
visible: false
|
||||
backend: listView.model.delegate
|
||||
}
|
||||
]
|
||||
delegate: Item {
|
||||
|
||||
width: 600
|
||||
height: 18
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
listView.model.currentIndex = index
|
||||
listView.currentIndex = index
|
||||
dialog.backend.currentRow = index
|
||||
dialog.popup(mouseArea)
|
||||
}
|
||||
|
||||
property int currentIndex: listView.currentIndex
|
||||
}
|
||||
|
||||
Row {
|
||||
id: row1
|
||||
x: 0
|
||||
y: 0
|
||||
width: 600
|
||||
height: 16
|
||||
spacing: 10
|
||||
|
||||
Text {
|
||||
width: 120
|
||||
color: "#ffffff"
|
||||
text: target ?? ""
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.bold: false
|
||||
}
|
||||
|
||||
Text {
|
||||
width: 120
|
||||
text: targetProperty ?? ""
|
||||
color: "#ffffff"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.bold: false
|
||||
}
|
||||
|
||||
Text {
|
||||
width: 120
|
||||
text: source ?? ""
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: "#ffffff"
|
||||
font.bold: false
|
||||
}
|
||||
|
||||
Text {
|
||||
width: 120
|
||||
text: sourceProperty ?? ""
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: "#ffffff"
|
||||
font.bold: false
|
||||
}
|
||||
|
||||
Text {
|
||||
width: 120
|
||||
|
||||
text: "-"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignRight
|
||||
font.pointSize: 14
|
||||
color: "#ffffff"
|
||||
font.bold: true
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: listView.model.remove(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
highlight: Rectangle {
|
||||
color: "#2a5593"
|
||||
width: 600
|
||||
}
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
|
||||
PopupDialog {
|
||||
ConnectionsDialogForm {
|
||||
y: 32
|
||||
}
|
||||
}
|
@@ -0,0 +1,242 @@
|
||||
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import StudioControls
|
||||
|
||||
Rectangle {
|
||||
width: 400
|
||||
height: 800
|
||||
color: "#1b1b1b"
|
||||
|
||||
Text {
|
||||
id: text1
|
||||
x: 10
|
||||
y: 25
|
||||
color: "#ffffff"
|
||||
text: qsTr("Target:")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
TopLevelComboBox {
|
||||
id: target
|
||||
x: 95
|
||||
width: 210
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: -367
|
||||
model: ["mySpinbox", "foo", "backendObject"]
|
||||
}
|
||||
|
||||
Text {
|
||||
id: text2
|
||||
x: 10
|
||||
y: 131
|
||||
color: "#ffffff"
|
||||
text: qsTr("Signal")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
TopLevelComboBox {
|
||||
id: signal
|
||||
x: 10
|
||||
y: 7
|
||||
width: 156
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: -207
|
||||
model: ["onClicked", "onPressed", "onReleased"]
|
||||
}
|
||||
|
||||
TopLevelComboBox {
|
||||
id: action
|
||||
x: 207
|
||||
y: 7
|
||||
width: 156
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: -207
|
||||
model: ["Call Function", "Assign", "ChnageState"]
|
||||
}
|
||||
|
||||
Text {
|
||||
id: text3
|
||||
x: 207
|
||||
y: 131
|
||||
color: "#ffffff"
|
||||
text: qsTr("Action")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
Item {
|
||||
id: functionGroup
|
||||
x: 0
|
||||
y: 276
|
||||
width: 400
|
||||
height: 176
|
||||
|
||||
Text {
|
||||
id: text4
|
||||
x: 17
|
||||
y: -11
|
||||
color: "#ffffff"
|
||||
text: qsTr("Target")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
TopLevelComboBox {
|
||||
id: functionTarget
|
||||
x: 10
|
||||
y: 7
|
||||
width: 156
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: -48
|
||||
model: ["mySpinBox", "backendObject", "someButton"]
|
||||
}
|
||||
|
||||
TopLevelComboBox {
|
||||
id: functionName
|
||||
x: 203
|
||||
y: 7
|
||||
width: 156
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: -48
|
||||
model: ["start", "trigger", "stop"]
|
||||
}
|
||||
|
||||
Text {
|
||||
id: text5
|
||||
x: 203
|
||||
y: -11
|
||||
color: "#ffffff"
|
||||
text: qsTr("Function")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: statesGroup
|
||||
x: 0
|
||||
y: 383
|
||||
width: 400
|
||||
height: 106
|
||||
Text {
|
||||
id: text6
|
||||
x: 17
|
||||
y: -11
|
||||
color: "#ffffff"
|
||||
text: qsTr("State Group")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
TopLevelComboBox {
|
||||
id: stateGroup
|
||||
x: 10
|
||||
y: 7
|
||||
width: 156
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: -12
|
||||
model: ["default", "State Group 01", "State Group 02"]
|
||||
}
|
||||
|
||||
TopLevelComboBox {
|
||||
id: stateName
|
||||
x: 209
|
||||
y: 7
|
||||
width: 156
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: -12
|
||||
model: ["State 01", "State 02", "State 03"]
|
||||
}
|
||||
|
||||
Text {
|
||||
id: text7
|
||||
x: 209
|
||||
y: -11
|
||||
color: "#ffffff"
|
||||
text: qsTr("State")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: assignment
|
||||
x: 10
|
||||
y: 505
|
||||
width: 400
|
||||
height: 106
|
||||
Text {
|
||||
id: text8
|
||||
x: 17
|
||||
y: -11
|
||||
color: "#ffffff"
|
||||
text: qsTr("target")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
TopLevelComboBox {
|
||||
id: valueTarget
|
||||
x: 10
|
||||
y: 7
|
||||
width: 156
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: -12
|
||||
model: ["mySpinBox", "myButton", "backendObject"]
|
||||
}
|
||||
|
||||
TopLevelComboBox {
|
||||
id: valueSource
|
||||
x: 209
|
||||
y: 7
|
||||
width: 156
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: -12
|
||||
model: ["mySpinBox", "myButton", "backendObject"]
|
||||
}
|
||||
|
||||
Text {
|
||||
id: text9
|
||||
x: 209
|
||||
y: -11
|
||||
color: "#ffffff"
|
||||
text: qsTr("source")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
Text {
|
||||
id: text10
|
||||
x: 17
|
||||
y: 76
|
||||
color: "#ffffff"
|
||||
text: qsTr("value")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
TopLevelComboBox {
|
||||
id: valueOut
|
||||
x: 10
|
||||
y: -2
|
||||
width: 156
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: 84
|
||||
model: ["width", "height", "opacity"]
|
||||
}
|
||||
|
||||
TopLevelComboBox {
|
||||
id: valueIn
|
||||
x: 209
|
||||
y: -2
|
||||
width: 156
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: 84
|
||||
model: ["width", "height", "x", "y"]
|
||||
}
|
||||
|
||||
Text {
|
||||
id: text11
|
||||
x: 209
|
||||
y: 76
|
||||
color: "#ffffff"
|
||||
text: qsTr("value")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,115 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import ConnectionsEditor
|
||||
import HelperWidgets 2.0 as HelperWidgets
|
||||
import StudioControls 1.0 as StudioControls
|
||||
import StudioTheme as StudioTheme
|
||||
import ConnectionsEditorEditorBackend
|
||||
|
||||
ListView {
|
||||
id: listView
|
||||
width: 606
|
||||
height: 160
|
||||
interactive: false
|
||||
highlightMoveDuration: 0
|
||||
|
||||
onVisibleChanged: {
|
||||
dialog.hide()
|
||||
}
|
||||
|
||||
property int modelCurrentIndex: listView.model.currentIndex ?? 0
|
||||
|
||||
/*
|
||||
Something weird with currentIndex happens when items are removed added.
|
||||
listView.model.currentIndex contains the persistent index.
|
||||
*/
|
||||
onModelCurrentIndexChanged: {
|
||||
listView.currentIndex = listView.model.currentIndex
|
||||
}
|
||||
|
||||
onCurrentIndexChanged: {
|
||||
listView.currentIndex = listView.model.currentIndex
|
||||
}
|
||||
|
||||
data: [
|
||||
ConnectionsDialog {
|
||||
id: dialog
|
||||
visible: false
|
||||
}
|
||||
]
|
||||
|
||||
delegate: Item {
|
||||
|
||||
width: 600
|
||||
height: 18
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
listView.model.currentIndex = index
|
||||
listView.currentIndex = index
|
||||
dialog.popup(mouseArea)
|
||||
}
|
||||
|
||||
property int currentIndex: listView.currentIndex
|
||||
}
|
||||
|
||||
Row {
|
||||
id: row1
|
||||
x: 0
|
||||
y: 0
|
||||
width: 600
|
||||
height: 16
|
||||
spacing: 10
|
||||
|
||||
Text {
|
||||
width: 120
|
||||
color: "#ffffff"
|
||||
text: target
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.bold: false
|
||||
}
|
||||
|
||||
Text {
|
||||
width: 120
|
||||
text: signal
|
||||
color: "#ffffff"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.bold: false
|
||||
}
|
||||
|
||||
Text {
|
||||
width: 120
|
||||
|
||||
text: action
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: "#ffffff"
|
||||
font.bold: false
|
||||
}
|
||||
|
||||
Text {
|
||||
width: 120
|
||||
|
||||
text: "-"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignRight
|
||||
font.pointSize: 14
|
||||
color: "#ffffff"
|
||||
font.bold: true
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: listView.model.remove(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
highlight: Rectangle {
|
||||
color: "#2a5593"
|
||||
width: 600
|
||||
}
|
||||
}
|
117
share/qtcreator/qmldesigner/connectionseditor/Main.qml
Normal file
117
share/qtcreator/qmldesigner/connectionseditor/Main.qml
Normal file
@@ -0,0 +1,117 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import ConnectionsEditor
|
||||
import HelperWidgets 2.0 as HelperWidgets
|
||||
import StudioControls 1.0 as StudioControls
|
||||
import StudioTheme as StudioTheme
|
||||
import ConnectionsEditorEditorBackend
|
||||
|
||||
Rectangle {
|
||||
width: 640
|
||||
height: 1080
|
||||
color: "#232222"
|
||||
|
||||
Rectangle {
|
||||
id: rectangle
|
||||
x: 10
|
||||
y: 10
|
||||
width: 620
|
||||
height: 97
|
||||
color: "#333333"
|
||||
|
||||
Rectangle {
|
||||
id: rectangle1
|
||||
x: 10
|
||||
y: 10
|
||||
width: 600
|
||||
height: 34
|
||||
color: "#00ffffff"
|
||||
border.width: 1
|
||||
|
||||
Text {
|
||||
id: text1
|
||||
x: 10
|
||||
y: 10
|
||||
color: "#b5b2b2"
|
||||
text: qsTr("Search")
|
||||
font.pixelSize: 12
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
x: 10
|
||||
y: 50
|
||||
TabCheckButton {
|
||||
id: connections
|
||||
text: "Connections"
|
||||
checked: true
|
||||
autoExclusive: true
|
||||
checkable: true
|
||||
}
|
||||
|
||||
TabCheckButton {
|
||||
id: bindings
|
||||
text: "Bindings"
|
||||
autoExclusive: true
|
||||
checkable: true
|
||||
}
|
||||
|
||||
TabCheckButton {
|
||||
id: properties
|
||||
text: "Properties"
|
||||
autoExclusive: true
|
||||
checkable: true
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: text2
|
||||
x: 577
|
||||
y: 58
|
||||
color: "#ffffff"
|
||||
text: qsTr("+")
|
||||
font.pixelSize: 18
|
||||
font.bold: true
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
print(ConnectionsEditorEditorBackend.dynamicPropertiesModel.delegate)
|
||||
print(ConnectionsEditorEditorBackend.dynamicPropertiesModel.delegate.type)
|
||||
print(ConnectionsEditorEditorBackend.dynamicPropertiesModel.delegate.type.model)
|
||||
|
||||
if (connections.checked)
|
||||
ConnectionsEditorEditorBackend.connectionModel.add()
|
||||
else if (bindings.checked)
|
||||
ConnectionsEditorEditorBackend.bindingModel.add()
|
||||
else if (properties.checked)
|
||||
ConnectionsEditorEditorBackend.dynamicPropertiesModel.add()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ConnectionsListView {
|
||||
visible: connections.checked
|
||||
x: 17
|
||||
y: 124
|
||||
model: ConnectionsEditorEditorBackend.connectionModel
|
||||
}
|
||||
|
||||
BindingsListView {
|
||||
visible: bindings.checked
|
||||
x: 17
|
||||
y: 124
|
||||
model: ConnectionsEditorEditorBackend.bindingModel
|
||||
}
|
||||
|
||||
PropertiesListView {
|
||||
visible: properties.checked
|
||||
x: 17
|
||||
y: 124
|
||||
model: ConnectionsEditorEditorBackend.dynamicPropertiesModel
|
||||
}
|
||||
}
|
@@ -0,0 +1,72 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
Window {
|
||||
id: window
|
||||
width: 400
|
||||
height: 800
|
||||
visible: true
|
||||
flags: Qt.FramelessWindowHint || Qt.Dialog
|
||||
|
||||
color: Qt.transparent
|
||||
|
||||
property int bw: 5
|
||||
|
||||
function popup(item) {
|
||||
print("popup " + item)
|
||||
var padding = 12
|
||||
var p = item.mapToGlobal(0, 0)
|
||||
dialog.x = p.x - dialog.width - padding
|
||||
if (dialog.x < 0)
|
||||
dialog.x = p.x + item.width + padding
|
||||
dialog.y = p.y
|
||||
dialog.show()
|
||||
dialog.raise()
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: rectangle1
|
||||
color: "#d7d7d7"
|
||||
border.color: "#232323"
|
||||
anchors.fill: parent
|
||||
|
||||
|
||||
|
||||
Rectangle {
|
||||
id: rectangle
|
||||
height: 32
|
||||
color: "#797979"
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 0
|
||||
anchors.leftMargin: 0
|
||||
anchors.rightMargin: 0
|
||||
DragHandler {
|
||||
grabPermissions: TapHandler.CanTakeOverFromAnything
|
||||
onActiveChanged: if (active) { window.startSystemMove(); }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: rectangle2
|
||||
x: 329
|
||||
width: 16
|
||||
height: 16
|
||||
color: "#ffffff"
|
||||
radius: 4
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 6
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
onClicked: window.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
|
||||
PopupDialog {
|
||||
property alias backend: form.backend
|
||||
|
||||
PropertiesDialogForm {
|
||||
id: form
|
||||
y: 32
|
||||
}
|
||||
}
|
@@ -0,0 +1,76 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
|
||||
import StudioControls
|
||||
|
||||
Rectangle {
|
||||
width: 400
|
||||
height: 800
|
||||
color: "#1b1b1b"
|
||||
property var backend
|
||||
|
||||
Text {
|
||||
id: text1
|
||||
x: 10
|
||||
y: 25
|
||||
color: "#ffffff"
|
||||
text: qsTr("Type:")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
TopLevelComboBox {
|
||||
id: target
|
||||
x: 95
|
||||
width: 210
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: -367
|
||||
model: backend.type.model ?? []
|
||||
onActivated: backend.type.activateIndex(target.currentIndex)
|
||||
property int currentTypeIndex: backend.type.currentIndex ?? 0
|
||||
onCurrentTypeIndexChanged: target.currentIndex = target.currentTypeIndex
|
||||
}
|
||||
|
||||
Text {
|
||||
id: text2
|
||||
x: 10
|
||||
y: 131
|
||||
color: "#ffffff"
|
||||
text: qsTr("Name")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
TextInput {
|
||||
id: name
|
||||
x: 70
|
||||
y: 131
|
||||
color: "white"
|
||||
width: 156
|
||||
text: backend.name.text ?? ""
|
||||
onEditingFinished: {
|
||||
backend.name.activateText(name.text)
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
x: 10
|
||||
y: 81
|
||||
color: "#ffffff"
|
||||
text: qsTr("Value")
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
TextInput {
|
||||
id: value
|
||||
color: "red"
|
||||
x: 70
|
||||
y: 81
|
||||
width: 156
|
||||
text: backend.value.text ?? ""
|
||||
onEditingFinished: {
|
||||
backend.value.activateText(value.text)
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,128 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import ConnectionsEditor
|
||||
import HelperWidgets 2.0 as HelperWidgets
|
||||
import StudioControls 1.0 as StudioControls
|
||||
import StudioTheme as StudioTheme
|
||||
import ConnectionsEditorEditorBackend
|
||||
|
||||
ListView {
|
||||
id: listView
|
||||
width: 606
|
||||
height: 160
|
||||
interactive: false
|
||||
highlightMoveDuration: 0
|
||||
|
||||
onVisibleChanged: {
|
||||
dialog.hide()
|
||||
}
|
||||
|
||||
property int modelCurrentIndex: listView.model.currentIndex ?? 0
|
||||
|
||||
/* Something weird with currentIndex happens when items are removed added.
|
||||
listView.model.currentIndex contains the persistent index.
|
||||
*/
|
||||
onModelCurrentIndexChanged: {
|
||||
listView.currentIndex = listView.model.currentIndex
|
||||
}
|
||||
|
||||
onCurrentIndexChanged: {
|
||||
listView.currentIndex = listView.model.currentIndex
|
||||
dialog.backend.currentRow = listView.currentIndex
|
||||
}
|
||||
|
||||
data: [
|
||||
PropertiesDialog {
|
||||
id: dialog
|
||||
visible: false
|
||||
backend: listView.model.delegate
|
||||
}
|
||||
]
|
||||
|
||||
delegate: Item {
|
||||
|
||||
width: 600
|
||||
height: 18
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
|
||||
property int currentIndex: listView.currentIndex
|
||||
|
||||
Connections {
|
||||
target: mouseArea
|
||||
function onClicked() {
|
||||
listView.model.currentIndex = index
|
||||
listView.currentIndex = index
|
||||
dialog.backend.currentRow = index
|
||||
dialog.popup(mouseArea)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: row1
|
||||
x: 0
|
||||
y: 0
|
||||
width: 600
|
||||
height: 16
|
||||
spacing: 10
|
||||
|
||||
Text {
|
||||
width: 120
|
||||
color: "#ffffff"
|
||||
text: target ?? ""
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.bold: false
|
||||
}
|
||||
|
||||
Text {
|
||||
width: 120
|
||||
text: name ?? ""
|
||||
color: "#ffffff"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.bold: false
|
||||
}
|
||||
|
||||
Text {
|
||||
width: 120
|
||||
text: type ?? ""
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: "#ffffff"
|
||||
font.bold: false
|
||||
}
|
||||
|
||||
Text {
|
||||
width: 120
|
||||
text: value ?? ""
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: "#ffffff"
|
||||
font.bold: false
|
||||
}
|
||||
|
||||
Text {
|
||||
width: 120
|
||||
|
||||
text: "-"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
horizontalAlignment: Text.AlignRight
|
||||
font.pointSize: 14
|
||||
color: "#ffffff"
|
||||
font.bold: true
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: listView.model.remove(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
highlight: Rectangle {
|
||||
color: "#2a5593"
|
||||
width: 600
|
||||
}
|
||||
}
|
@@ -0,0 +1,80 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Templates
|
||||
|
||||
Button {
|
||||
id: control
|
||||
|
||||
implicitWidth: Math.max(
|
||||
buttonBackground ? buttonBackground.implicitWidth : 0,
|
||||
textItem.implicitWidth + leftPadding + rightPadding)
|
||||
implicitHeight: Math.max(
|
||||
buttonBackground ? buttonBackground.implicitHeight : 0,
|
||||
textItem.implicitHeight + topPadding + bottomPadding)
|
||||
leftPadding: 4
|
||||
rightPadding: 4
|
||||
|
||||
text: "My Button"
|
||||
|
||||
background: buttonBackground
|
||||
Rectangle {
|
||||
id: buttonBackground
|
||||
color: "#047eff"
|
||||
implicitWidth: 100
|
||||
implicitHeight: 40
|
||||
opacity: enabled ? 1 : 0.3
|
||||
radius: 12
|
||||
border.color: "#047eff"
|
||||
}
|
||||
|
||||
contentItem: textItem
|
||||
Text {
|
||||
id: textItem
|
||||
text: control.text
|
||||
|
||||
opacity: enabled ? 1.0 : 0.3
|
||||
color: "#ffffff"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "normal"
|
||||
when: !control.down && !control.checked
|
||||
|
||||
PropertyChanges {
|
||||
target: buttonBackground
|
||||
visible: false
|
||||
color: "#00000000"
|
||||
border.color: "#047eff"
|
||||
}
|
||||
|
||||
PropertyChanges {
|
||||
target: textItem
|
||||
color: "#ffffff"
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "down"
|
||||
when: control.down && !control.checked
|
||||
PropertyChanges {
|
||||
target: textItem
|
||||
color: "#ffffff"
|
||||
}
|
||||
|
||||
PropertyChanges {
|
||||
target: buttonBackground
|
||||
color: "#047eff"
|
||||
border.color: "#00000000"
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "down1"
|
||||
when: control.checked
|
||||
extend: "down"
|
||||
}
|
||||
]
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2022 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
pragma Singleton
|
||||
import QtQuick
|
||||
|
||||
QtObject {
|
||||
readonly property int width: 1920
|
||||
readonly property int height: 1080
|
||||
|
||||
property int thumbnailSize: 250
|
||||
// Breakpoint to control when the state thumbnail view toggles from normal to tiny
|
||||
readonly property int thumbnailBreak: 150
|
||||
|
||||
readonly property int minThumbSize: 100
|
||||
readonly property int maxThumbSize: 350
|
||||
}
|
@@ -0,0 +1 @@
|
||||
singleton Constants 1.0 Constants.qml
|
@@ -40,6 +40,13 @@ T.TextField {
|
||||
hoverEnabled: true
|
||||
clip: true
|
||||
|
||||
Keys.onPressed: (event) => {
|
||||
if (event.key === Qt.Key_Escape && event.modifiers === Qt.NoModifier) {
|
||||
control.clear()
|
||||
event.accepted = true
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: placeholder
|
||||
x: control.leftPadding
|
||||
|
@@ -179,166 +179,168 @@ QtObject {
|
||||
readonly property string materialPreviewEnvironment: "\u00C6"
|
||||
readonly property string materialPreviewModel: "\u00C7"
|
||||
readonly property string material_medium: "\u00C8"
|
||||
readonly property string mergeCells: "\u00C9"
|
||||
readonly property string merge_small: "\u00CA"
|
||||
readonly property string minus: "\u00CB"
|
||||
readonly property string mirror: "\u00CC"
|
||||
readonly property string more_medium: "\u00CD"
|
||||
readonly property string mouseArea_small: "\u00CE"
|
||||
readonly property string moveDown_medium: "\u00CF"
|
||||
readonly property string moveInwards_medium: "\u00D0"
|
||||
readonly property string moveUp_medium: "\u00D1"
|
||||
readonly property string moveUpwards_medium: "\u00D2"
|
||||
readonly property string move_medium: "\u00D3"
|
||||
readonly property string newMaterial: "\u00D4"
|
||||
readonly property string nextFile_large: "\u00D5"
|
||||
readonly property string openLink: "\u00D6"
|
||||
readonly property string openMaterialBrowser: "\u00D7"
|
||||
readonly property string orientation: "\u00D8"
|
||||
readonly property string orthCam_medium: "\u00D9"
|
||||
readonly property string orthCam_small: "\u00DA"
|
||||
readonly property string paddingEdge: "\u00DB"
|
||||
readonly property string paddingFrame: "\u00DC"
|
||||
readonly property string particleAnimation_medium: "\u00DD"
|
||||
readonly property string pasteStyle: "\u00DE"
|
||||
readonly property string paste_small: "\u00DF"
|
||||
readonly property string pause: "\u00E0"
|
||||
readonly property string perspectiveCam_medium: "\u00E1"
|
||||
readonly property string perspectiveCam_small: "\u00E2"
|
||||
readonly property string pin: "\u00E3"
|
||||
readonly property string plane_medium: "\u00E4"
|
||||
readonly property string plane_small: "\u00E5"
|
||||
readonly property string play: "\u00E6"
|
||||
readonly property string playFill_medium: "\u00E7"
|
||||
readonly property string playOutline_medium: "\u00E8"
|
||||
readonly property string plus: "\u00E9"
|
||||
readonly property string pointLight_small: "\u00EA"
|
||||
readonly property string positioners_small: "\u00EB"
|
||||
readonly property string previewEnv_medium: "\u00EC"
|
||||
readonly property string previousFile_large: "\u00ED"
|
||||
readonly property string promote: "\u00EE"
|
||||
readonly property string properties_medium: "\u00EF"
|
||||
readonly property string readOnly: "\u00F0"
|
||||
readonly property string recordFill_medium: "\u00F1"
|
||||
readonly property string recordOutline_medium: "\u00F2"
|
||||
readonly property string redo: "\u00F3"
|
||||
readonly property string reload_medium: "\u00F4"
|
||||
readonly property string remove_medium: "\u00F5"
|
||||
readonly property string remove_small: "\u00F6"
|
||||
readonly property string rename_small: "\u00F7"
|
||||
readonly property string replace_small: "\u00F8"
|
||||
readonly property string resetView_small: "\u00F9"
|
||||
readonly property string restartParticles_medium: "\u00FA"
|
||||
readonly property string reverseOrder_medium: "\u00FB"
|
||||
readonly property string roatate_medium: "\u00FC"
|
||||
readonly property string rotationFill: "\u00FD"
|
||||
readonly property string rotationOutline: "\u00FE"
|
||||
readonly property string runProjFill_large: "\u00FF"
|
||||
readonly property string runProjOutline_large: "\u0100"
|
||||
readonly property string s_anchors: "\u0101"
|
||||
readonly property string s_annotations: "\u0102"
|
||||
readonly property string s_arrange: "\u0103"
|
||||
readonly property string s_boundingBox: "\u0104"
|
||||
readonly property string s_component: "\u0105"
|
||||
readonly property string s_connections: "\u0106"
|
||||
readonly property string s_edit: "\u0107"
|
||||
readonly property string s_enterComponent: "\u0108"
|
||||
readonly property string s_eventList: "\u0109"
|
||||
readonly property string s_group: "\u010A"
|
||||
readonly property string s_layouts: "\u010B"
|
||||
readonly property string s_merging: "\u010C"
|
||||
readonly property string s_mouseArea: "\u010D"
|
||||
readonly property string s_positioners: "\u010E"
|
||||
readonly property string s_selection: "\u010F"
|
||||
readonly property string s_snapping: "\u0110"
|
||||
readonly property string s_timeline: "\u0111"
|
||||
readonly property string s_visibility: "\u0112"
|
||||
readonly property string saveLogs_medium: "\u0113"
|
||||
readonly property string scale_medium: "\u0114"
|
||||
readonly property string search: "\u0115"
|
||||
readonly property string search_small: "\u0116"
|
||||
readonly property string sectionToggle: "\u0117"
|
||||
readonly property string selectFill_medium: "\u0118"
|
||||
readonly property string selectOutline_medium: "\u0119"
|
||||
readonly property string selectParent_small: "\u011A"
|
||||
readonly property string selection_small: "\u011B"
|
||||
readonly property string settings_medium: "\u011C"
|
||||
readonly property string signal_small: "\u011D"
|
||||
readonly property string snapping_small: "\u011E"
|
||||
readonly property string sphere_medium: "\u011F"
|
||||
readonly property string sphere_small: "\u0120"
|
||||
readonly property string splitColumns: "\u0121"
|
||||
readonly property string splitRows: "\u0122"
|
||||
readonly property string spotLight_small: "\u0123"
|
||||
readonly property string stackedContainer_small: "\u0124"
|
||||
readonly property string startNode: "\u0125"
|
||||
readonly property string step_medium: "\u0126"
|
||||
readonly property string stop_medium: "\u0127"
|
||||
readonly property string testIcon: "\u0128"
|
||||
readonly property string textAlignBottom: "\u0129"
|
||||
readonly property string textAlignCenter: "\u012A"
|
||||
readonly property string textAlignJustified: "\u012B"
|
||||
readonly property string textAlignLeft: "\u012C"
|
||||
readonly property string textAlignMiddle: "\u012D"
|
||||
readonly property string textAlignRight: "\u012E"
|
||||
readonly property string textAlignTop: "\u012F"
|
||||
readonly property string textBulletList: "\u0130"
|
||||
readonly property string textFullJustification: "\u0131"
|
||||
readonly property string textNumberedList: "\u0132"
|
||||
readonly property string textures_medium: "\u0133"
|
||||
readonly property string tickIcon: "\u0134"
|
||||
readonly property string tickMark_small: "\u0135"
|
||||
readonly property string timeline_small: "\u0136"
|
||||
readonly property string toEndFrame_medium: "\u0137"
|
||||
readonly property string toNextFrame_medium: "\u0138"
|
||||
readonly property string toPrevFrame_medium: "\u0139"
|
||||
readonly property string toStartFrame_medium: "\u013A"
|
||||
readonly property string topToolbar_annotations: "\u013B"
|
||||
readonly property string topToolbar_closeFile: "\u013C"
|
||||
readonly property string topToolbar_designMode: "\u013D"
|
||||
readonly property string topToolbar_enterComponent: "\u013E"
|
||||
readonly property string topToolbar_home: "\u013F"
|
||||
readonly property string topToolbar_makeComponent: "\u0140"
|
||||
readonly property string topToolbar_navFile: "\u0141"
|
||||
readonly property string topToolbar_runProject: "\u0142"
|
||||
readonly property string translationCreateFiles: "\u0143"
|
||||
readonly property string translationCreateReport: "\u0144"
|
||||
readonly property string translationExport: "\u0145"
|
||||
readonly property string translationImport: "\u0146"
|
||||
readonly property string translationSelectLanguages: "\u0147"
|
||||
readonly property string translationTest: "\u0148"
|
||||
readonly property string transparent: "\u0149"
|
||||
readonly property string triState: "\u014A"
|
||||
readonly property string triangleArcA: "\u014B"
|
||||
readonly property string triangleArcB: "\u014C"
|
||||
readonly property string triangleCornerA: "\u014D"
|
||||
readonly property string triangleCornerB: "\u014E"
|
||||
readonly property string unLinked: "\u014F"
|
||||
readonly property string undo: "\u0150"
|
||||
readonly property string unify_medium: "\u0151"
|
||||
readonly property string unpin: "\u0152"
|
||||
readonly property string upDownIcon: "\u0153"
|
||||
readonly property string upDownSquare2: "\u0154"
|
||||
readonly property string updateAvailable_medium: "\u0155"
|
||||
readonly property string updateContent_medium: "\u0156"
|
||||
readonly property string visibilityOff: "\u0157"
|
||||
readonly property string visibilityOn: "\u0158"
|
||||
readonly property string visible_medium: "\u0159"
|
||||
readonly property string visible_small: "\u015A"
|
||||
readonly property string wildcard: "\u015B"
|
||||
readonly property string wizardsAutomotive: "\u015C"
|
||||
readonly property string wizardsDesktop: "\u015D"
|
||||
readonly property string wizardsGeneric: "\u015E"
|
||||
readonly property string wizardsMcuEmpty: "\u015F"
|
||||
readonly property string wizardsMcuGraph: "\u0160"
|
||||
readonly property string wizardsMobile: "\u0161"
|
||||
readonly property string wizardsUnknown: "\u0162"
|
||||
readonly property string zoomAll: "\u0163"
|
||||
readonly property string zoomIn: "\u0164"
|
||||
readonly property string zoomIn_medium: "\u0165"
|
||||
readonly property string zoomOut: "\u0166"
|
||||
readonly property string zoomOut_medium: "\u0167"
|
||||
readonly property string zoomSelection: "\u0168"
|
||||
readonly property string maxBar_small: "\u00C9"
|
||||
readonly property string mergeCells: "\u00CA"
|
||||
readonly property string merge_small: "\u00CB"
|
||||
readonly property string minus: "\u00CC"
|
||||
readonly property string mirror: "\u00CD"
|
||||
readonly property string more_medium: "\u00CE"
|
||||
readonly property string mouseArea_small: "\u00CF"
|
||||
readonly property string moveDown_medium: "\u00D0"
|
||||
readonly property string moveInwards_medium: "\u00D1"
|
||||
readonly property string moveUp_medium: "\u00D2"
|
||||
readonly property string moveUpwards_medium: "\u00D3"
|
||||
readonly property string move_medium: "\u00D4"
|
||||
readonly property string newMaterial: "\u00D5"
|
||||
readonly property string nextFile_large: "\u00D6"
|
||||
readonly property string normalBar_small: "\u00D7"
|
||||
readonly property string openLink: "\u00D8"
|
||||
readonly property string openMaterialBrowser: "\u00D9"
|
||||
readonly property string orientation: "\u00DA"
|
||||
readonly property string orthCam_medium: "\u00DB"
|
||||
readonly property string orthCam_small: "\u00DC"
|
||||
readonly property string paddingEdge: "\u00DD"
|
||||
readonly property string paddingFrame: "\u00DE"
|
||||
readonly property string particleAnimation_medium: "\u00DF"
|
||||
readonly property string pasteStyle: "\u00E0"
|
||||
readonly property string paste_small: "\u00E1"
|
||||
readonly property string pause: "\u00E2"
|
||||
readonly property string perspectiveCam_medium: "\u00E3"
|
||||
readonly property string perspectiveCam_small: "\u00E4"
|
||||
readonly property string pin: "\u00E5"
|
||||
readonly property string plane_medium: "\u00E6"
|
||||
readonly property string plane_small: "\u00E7"
|
||||
readonly property string play: "\u00E8"
|
||||
readonly property string playFill_medium: "\u00E9"
|
||||
readonly property string playOutline_medium: "\u00EA"
|
||||
readonly property string plus: "\u00EB"
|
||||
readonly property string pointLight_small: "\u00EC"
|
||||
readonly property string positioners_small: "\u00ED"
|
||||
readonly property string previewEnv_medium: "\u00EE"
|
||||
readonly property string previousFile_large: "\u00EF"
|
||||
readonly property string promote: "\u00F0"
|
||||
readonly property string properties_medium: "\u00F1"
|
||||
readonly property string readOnly: "\u00F2"
|
||||
readonly property string recordFill_medium: "\u00F3"
|
||||
readonly property string recordOutline_medium: "\u00F4"
|
||||
readonly property string redo: "\u00F5"
|
||||
readonly property string reload_medium: "\u00F6"
|
||||
readonly property string remove_medium: "\u00F7"
|
||||
readonly property string remove_small: "\u00F8"
|
||||
readonly property string rename_small: "\u00F9"
|
||||
readonly property string replace_small: "\u00FA"
|
||||
readonly property string resetView_small: "\u00FB"
|
||||
readonly property string restartParticles_medium: "\u00FC"
|
||||
readonly property string reverseOrder_medium: "\u00FD"
|
||||
readonly property string roatate_medium: "\u00FE"
|
||||
readonly property string rotationFill: "\u00FF"
|
||||
readonly property string rotationOutline: "\u0100"
|
||||
readonly property string runProjFill_large: "\u0101"
|
||||
readonly property string runProjOutline_large: "\u0102"
|
||||
readonly property string s_anchors: "\u0103"
|
||||
readonly property string s_annotations: "\u0104"
|
||||
readonly property string s_arrange: "\u0105"
|
||||
readonly property string s_boundingBox: "\u0106"
|
||||
readonly property string s_component: "\u0107"
|
||||
readonly property string s_connections: "\u0108"
|
||||
readonly property string s_edit: "\u0109"
|
||||
readonly property string s_enterComponent: "\u010A"
|
||||
readonly property string s_eventList: "\u010B"
|
||||
readonly property string s_group: "\u010C"
|
||||
readonly property string s_layouts: "\u010D"
|
||||
readonly property string s_merging: "\u010E"
|
||||
readonly property string s_mouseArea: "\u010F"
|
||||
readonly property string s_positioners: "\u0110"
|
||||
readonly property string s_selection: "\u0111"
|
||||
readonly property string s_snapping: "\u0112"
|
||||
readonly property string s_timeline: "\u0113"
|
||||
readonly property string s_visibility: "\u0114"
|
||||
readonly property string saveLogs_medium: "\u0115"
|
||||
readonly property string scale_medium: "\u0116"
|
||||
readonly property string search: "\u0117"
|
||||
readonly property string search_small: "\u0118"
|
||||
readonly property string sectionToggle: "\u0119"
|
||||
readonly property string selectFill_medium: "\u011A"
|
||||
readonly property string selectOutline_medium: "\u011B"
|
||||
readonly property string selectParent_small: "\u011C"
|
||||
readonly property string selection_small: "\u011D"
|
||||
readonly property string settings_medium: "\u011E"
|
||||
readonly property string signal_small: "\u011F"
|
||||
readonly property string snapping_small: "\u0120"
|
||||
readonly property string sphere_medium: "\u0121"
|
||||
readonly property string sphere_small: "\u0122"
|
||||
readonly property string splitColumns: "\u0123"
|
||||
readonly property string splitRows: "\u0124"
|
||||
readonly property string spotLight_small: "\u0125"
|
||||
readonly property string stackedContainer_small: "\u0126"
|
||||
readonly property string startNode: "\u0127"
|
||||
readonly property string step_medium: "\u0128"
|
||||
readonly property string stop_medium: "\u0129"
|
||||
readonly property string testIcon: "\u012A"
|
||||
readonly property string textAlignBottom: "\u012B"
|
||||
readonly property string textAlignCenter: "\u012C"
|
||||
readonly property string textAlignJustified: "\u012D"
|
||||
readonly property string textAlignLeft: "\u012E"
|
||||
readonly property string textAlignMiddle: "\u012F"
|
||||
readonly property string textAlignRight: "\u0130"
|
||||
readonly property string textAlignTop: "\u0131"
|
||||
readonly property string textBulletList: "\u0132"
|
||||
readonly property string textFullJustification: "\u0133"
|
||||
readonly property string textNumberedList: "\u0134"
|
||||
readonly property string textures_medium: "\u0135"
|
||||
readonly property string tickIcon: "\u0136"
|
||||
readonly property string tickMark_small: "\u0137"
|
||||
readonly property string timeline_small: "\u0138"
|
||||
readonly property string toEndFrame_medium: "\u0139"
|
||||
readonly property string toNextFrame_medium: "\u013A"
|
||||
readonly property string toPrevFrame_medium: "\u013B"
|
||||
readonly property string toStartFrame_medium: "\u013C"
|
||||
readonly property string topToolbar_annotations: "\u013D"
|
||||
readonly property string topToolbar_closeFile: "\u013E"
|
||||
readonly property string topToolbar_designMode: "\u013F"
|
||||
readonly property string topToolbar_enterComponent: "\u0140"
|
||||
readonly property string topToolbar_home: "\u0141"
|
||||
readonly property string topToolbar_makeComponent: "\u0142"
|
||||
readonly property string topToolbar_navFile: "\u0143"
|
||||
readonly property string topToolbar_runProject: "\u0144"
|
||||
readonly property string translationCreateFiles: "\u0145"
|
||||
readonly property string translationCreateReport: "\u0146"
|
||||
readonly property string translationExport: "\u0147"
|
||||
readonly property string translationImport: "\u0148"
|
||||
readonly property string translationSelectLanguages: "\u0149"
|
||||
readonly property string translationTest: "\u014A"
|
||||
readonly property string transparent: "\u014B"
|
||||
readonly property string triState: "\u014C"
|
||||
readonly property string triangleArcA: "\u014D"
|
||||
readonly property string triangleArcB: "\u014E"
|
||||
readonly property string triangleCornerA: "\u014F"
|
||||
readonly property string triangleCornerB: "\u0150"
|
||||
readonly property string unLinked: "\u0151"
|
||||
readonly property string undo: "\u0152"
|
||||
readonly property string unify_medium: "\u0153"
|
||||
readonly property string unpin: "\u0154"
|
||||
readonly property string upDownIcon: "\u0155"
|
||||
readonly property string upDownSquare2: "\u0156"
|
||||
readonly property string updateAvailable_medium: "\u0157"
|
||||
readonly property string updateContent_medium: "\u0158"
|
||||
readonly property string visibilityOff: "\u0159"
|
||||
readonly property string visibilityOn: "\u015A"
|
||||
readonly property string visible_medium: "\u015B"
|
||||
readonly property string visible_small: "\u015C"
|
||||
readonly property string wildcard: "\u015D"
|
||||
readonly property string wizardsAutomotive: "\u015E"
|
||||
readonly property string wizardsDesktop: "\u015F"
|
||||
readonly property string wizardsGeneric: "\u0160"
|
||||
readonly property string wizardsMcuEmpty: "\u0161"
|
||||
readonly property string wizardsMcuGraph: "\u0162"
|
||||
readonly property string wizardsMobile: "\u0163"
|
||||
readonly property string wizardsUnknown: "\u0164"
|
||||
readonly property string zoomAll: "\u0165"
|
||||
readonly property string zoomIn: "\u0166"
|
||||
readonly property string zoomIn_medium: "\u0167"
|
||||
readonly property string zoomOut: "\u0168"
|
||||
readonly property string zoomOut_medium: "\u0169"
|
||||
readonly property string zoomSelection: "\u016A"
|
||||
|
||||
readonly property font iconFont: Qt.font({
|
||||
"family": controlIcons.name,
|
||||
|
Binary file not shown.
@@ -3,6 +3,9 @@ add_qtc_library(AdvancedDockingSystem
|
||||
SOURCES
|
||||
ads_globals.cpp ads_globals.h
|
||||
advanceddockingsystemtr.h
|
||||
autohidedockcontainer.cpp autohidedockcontainer.h
|
||||
autohidesidebar.cpp autohidesidebar.h
|
||||
autohidetab.cpp autohidetab.h
|
||||
dockareatabbar.cpp dockareatabbar.h
|
||||
dockareatitlebar.cpp dockareatitlebar.h
|
||||
dockareawidget.cpp dockareawidget.h
|
||||
@@ -19,6 +22,8 @@ add_qtc_library(AdvancedDockingSystem
|
||||
floatingdockcontainer.cpp floatingdockcontainer.h
|
||||
floatingdragpreview.cpp floatingdragpreview.h
|
||||
iconprovider.cpp iconprovider.h
|
||||
pushbutton.cpp pushbutton.h
|
||||
resizehandle.cpp resizehandle.h
|
||||
workspace.cpp workspace.h
|
||||
workspacedialog.cpp workspacedialog.h
|
||||
workspaceinputdialog.cpp workspaceinputdialog.h
|
||||
@@ -28,6 +33,7 @@ add_qtc_library(AdvancedDockingSystem
|
||||
|
||||
extend_qtc_library(AdvancedDockingSystem
|
||||
INCLUDES linux
|
||||
CONDITION UNIX AND NOT APPLE
|
||||
SOURCES
|
||||
linux/floatingwidgettitlebar.cpp linux/floatingwidgettitlebar.h
|
||||
)
|
||||
|
@@ -14,10 +14,19 @@
|
||||
#include <QStyle>
|
||||
#include <QVariant>
|
||||
|
||||
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
|
||||
#include <QApplication>
|
||||
#include <QFile>
|
||||
#include <QSettings>
|
||||
#endif
|
||||
|
||||
namespace ADS {
|
||||
|
||||
namespace internal {
|
||||
|
||||
const int g_floatingWidgetDragStartEvent = QEvent::registerEventType();
|
||||
const int g_dockedWidgetDragStartEvent = QEvent::registerEventType();
|
||||
|
||||
void replaceSplitterWidget(QSplitter *splitter, QWidget *from, QWidget *to)
|
||||
{
|
||||
int index = splitter->indexOf(from);
|
||||
@@ -25,6 +34,16 @@ void replaceSplitterWidget(QSplitter *splitter, QWidget *from, QWidget *to)
|
||||
splitter->insertWidget(index, to);
|
||||
}
|
||||
|
||||
void hideEmptyParentSplitters(DockSplitter *splitter)
|
||||
{
|
||||
while (splitter && splitter->isVisible()) {
|
||||
if (!splitter->hasVisibleContent())
|
||||
splitter->hide();
|
||||
|
||||
splitter = internal::findParent<DockSplitter *>(splitter);
|
||||
}
|
||||
}
|
||||
|
||||
DockInsertParam dockAreaInsertParameters(DockWidgetArea area)
|
||||
{
|
||||
switch (area) {
|
||||
@@ -44,6 +63,45 @@ DockInsertParam dockAreaInsertParameters(DockWidgetArea area)
|
||||
return DockInsertParam(Qt::Vertical, false);
|
||||
}
|
||||
|
||||
SideBarLocation toSideBarLocation(DockWidgetArea area)
|
||||
{
|
||||
switch (area) {
|
||||
case LeftAutoHideArea:
|
||||
return SideBarLeft;
|
||||
case RightAutoHideArea:
|
||||
return SideBarRight;
|
||||
case TopAutoHideArea:
|
||||
return SideBarTop;
|
||||
case BottomAutoHideArea:
|
||||
return SideBarBottom;
|
||||
default:
|
||||
return SideBarNone;
|
||||
}
|
||||
|
||||
return SideBarNone;
|
||||
}
|
||||
|
||||
bool isHorizontalSideBarLocation(SideBarLocation location)
|
||||
{
|
||||
switch (location) {
|
||||
case SideBarTop:
|
||||
case SideBarBottom:
|
||||
return true;
|
||||
case SideBarLeft:
|
||||
case SideBarRight:
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isSideBarArea(DockWidgetArea area)
|
||||
{
|
||||
return toSideBarLocation(area) != SideBarNone;
|
||||
}
|
||||
|
||||
QPixmap createTransparentPixmap(const QPixmap &source, qreal opacity)
|
||||
{
|
||||
QPixmap transparentPixmap(source.size());
|
||||
@@ -54,18 +112,8 @@ QPixmap createTransparentPixmap(const QPixmap &source, qreal opacity)
|
||||
return transparentPixmap;
|
||||
}
|
||||
|
||||
void hideEmptyParentSplitters(DockSplitter *splitter)
|
||||
{
|
||||
while (splitter && splitter->isVisible()) {
|
||||
if (!splitter->hasVisibleContent()) {
|
||||
splitter->hide();
|
||||
}
|
||||
splitter = internal::findParent<DockSplitter *>(splitter);
|
||||
}
|
||||
}
|
||||
|
||||
void setButtonIcon(QAbstractButton *button,
|
||||
QStyle::StandardPixmap standarPixmap,
|
||||
QStyle::StandardPixmap standardPixmap,
|
||||
ADS::eIcon customIconId)
|
||||
{
|
||||
// First we try to use custom icons if available
|
||||
@@ -75,12 +123,12 @@ void setButtonIcon(QAbstractButton *button,
|
||||
return;
|
||||
}
|
||||
|
||||
if (Utils::HostOsInfo::isLinuxHost()) {
|
||||
button->setIcon(button->style()->standardIcon(standarPixmap));
|
||||
if (Utils::HostOsInfo::isAnyUnixHost() && !Utils::HostOsInfo::isMacHost()) {
|
||||
button->setIcon(button->style()->standardIcon(standardPixmap));
|
||||
} else {
|
||||
// The standard icons does not look good on high DPI screens so we create
|
||||
// our own "standard" icon here.
|
||||
QPixmap normalPixmap = button->style()->standardPixmap(standarPixmap, nullptr, button);
|
||||
QPixmap normalPixmap = button->style()->standardPixmap(standardPixmap, nullptr, button);
|
||||
icon.addPixmap(internal::createTransparentPixmap(normalPixmap, 0.25), QIcon::Disabled);
|
||||
icon.addPixmap(normalPixmap, QIcon::Normal);
|
||||
button->setIcon(icon);
|
||||
@@ -98,14 +146,22 @@ void repolishStyle(QWidget *widget, eRepolishChildOptions options)
|
||||
if (RepolishIgnoreChildren == options)
|
||||
return;
|
||||
|
||||
QList<QWidget*> children = widget->findChildren<QWidget *>(QString(),
|
||||
(RepolishDirectChildren == options) ? Qt::FindDirectChildrenOnly : Qt::FindChildrenRecursively);
|
||||
for (auto w : children)
|
||||
{
|
||||
QList<QWidget *> children = widget->findChildren<QWidget *>(QString(),
|
||||
(RepolishDirectChildren == options)
|
||||
? Qt::FindDirectChildrenOnly
|
||||
: Qt::FindChildrenRecursively);
|
||||
for (auto w : children) {
|
||||
w->style()->unpolish(w);
|
||||
w->style()->polish(w);
|
||||
}
|
||||
}
|
||||
|
||||
QRect globalGeometry(QWidget *w)
|
||||
{
|
||||
QRect g = w->geometry();
|
||||
g.moveTopLeft(w->mapToGlobal(QPoint(0, 0)));
|
||||
return g;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace ADS
|
||||
|
@@ -16,11 +16,11 @@ class QSplitter;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#if defined(ADVANCEDDOCKINGSYSTEM_LIBRARY)
|
||||
# define ADS_EXPORT Q_DECL_EXPORT
|
||||
#define ADS_EXPORT Q_DECL_EXPORT
|
||||
#elif defined(ADVANCEDDOCKINGSYSTEM_STATIC_LIBRARY)
|
||||
# define ADS_EXPORT
|
||||
#define ADS_EXPORT
|
||||
#else
|
||||
# define ADS_EXPORT Q_DECL_IMPORT
|
||||
#define ADS_EXPORT Q_DECL_IMPORT
|
||||
#endif
|
||||
|
||||
//#define ADS_DEBUG_PRINT
|
||||
@@ -47,15 +47,27 @@ enum DockWidgetArea {
|
||||
TopDockWidgetArea = 0x04,
|
||||
BottomDockWidgetArea = 0x08,
|
||||
CenterDockWidgetArea = 0x10,
|
||||
LeftAutoHideArea = 0x20,
|
||||
RightAutoHideArea = 0x40,
|
||||
TopAutoHideArea = 0x80,
|
||||
BottomAutoHideArea = 0x100,
|
||||
|
||||
InvalidDockWidgetArea = NoDockWidgetArea,
|
||||
OuterDockAreas = TopDockWidgetArea | LeftDockWidgetArea | RightDockWidgetArea
|
||||
| BottomDockWidgetArea,
|
||||
AutoHideDockAreas = LeftAutoHideArea | RightAutoHideArea | TopAutoHideArea | BottomAutoHideArea,
|
||||
AllDockAreas = OuterDockAreas | CenterDockWidgetArea
|
||||
};
|
||||
Q_DECLARE_FLAGS(DockWidgetAreas, DockWidgetArea)
|
||||
|
||||
enum eTitleBarButton { TitleBarButtonTabsMenu, TitleBarButtonUndock, TitleBarButtonClose };
|
||||
enum eTabIndex { TabDefaultInsertIndex = -1, TabInvalidIndex = -2 };
|
||||
|
||||
enum eTitleBarButton {
|
||||
TitleBarButtonTabsMenu,
|
||||
TitleBarButtonUndock,
|
||||
TitleBarButtonClose,
|
||||
TitleBarButtonAutoHide
|
||||
};
|
||||
|
||||
/**
|
||||
* The different dragging states
|
||||
@@ -71,42 +83,49 @@ enum eDragState {
|
||||
* The different icons used in the UI
|
||||
*/
|
||||
enum eIcon {
|
||||
TabCloseIcon, //!< TabCloseIcon
|
||||
DockAreaMenuIcon, //!< DockAreaMenuIcon
|
||||
DockAreaUndockIcon, //!< DockAreaUndockIcon
|
||||
DockAreaCloseIcon, //!< DockAreaCloseIcon
|
||||
FloatingWidgetCloseIcon, //!< FloatingWidgetCloseIcon
|
||||
TabCloseIcon, //!< TabCloseIcon
|
||||
AutoHideIcon, //!< AutoHideIcon
|
||||
DockAreaMenuIcon, //!< DockAreaMenuIcon
|
||||
DockAreaUndockIcon, //!< DockAreaUndockIcon
|
||||
DockAreaCloseIcon, //!< DockAreaCloseIcon
|
||||
FloatingWidgetCloseIcon, //!< FloatingWidgetCloseIcon
|
||||
FloatingWidgetNormalIcon, //!< FloatingWidgetNormalIcon
|
||||
FloatingWidgetMaximizeIcon, //!< FloatingWidgetMaximizeIcon
|
||||
|
||||
IconCount, //!< just a delimiter for range checks
|
||||
IconCount, //!< just a delimiter for range checks
|
||||
};
|
||||
|
||||
/**
|
||||
* For bitwise combination of dock wdget features
|
||||
* For bitwise combination of dock widget features
|
||||
*/
|
||||
enum eBitwiseOperator
|
||||
{
|
||||
BitwiseAnd,
|
||||
BitwiseOr
|
||||
};
|
||||
enum eBitwiseOperator { BitwiseAnd, BitwiseOr };
|
||||
|
||||
/**
|
||||
* Each dock container supports 4 sidebars
|
||||
*/
|
||||
enum SideBarLocation { SideBarTop, SideBarLeft, SideBarRight, SideBarBottom, SideBarNone };
|
||||
|
||||
namespace internal {
|
||||
const char *const closedProperty = "close";
|
||||
const char *const dirtyProperty = "dirty";
|
||||
|
||||
const char *const g_closedProperty = "close";
|
||||
const char *const g_dirtyProperty = "dirty";
|
||||
|
||||
extern const int g_floatingWidgetDragStartEvent;
|
||||
extern const int g_dockedWidgetDragStartEvent;
|
||||
|
||||
/**
|
||||
* Replace the from widget in the given splitter with the To widget
|
||||
* Replace the \p from widget in the given splitter with the \p to widget.
|
||||
*/
|
||||
void replaceSplitterWidget(QSplitter *splitter, QWidget *from, QWidget *to);
|
||||
|
||||
/**
|
||||
* This function walks the splitter tree upwards to hides all splitters
|
||||
* that do not have visible content
|
||||
* This function walks the splitter tree upwards to hide all splitters that do not
|
||||
* have visible content.
|
||||
*/
|
||||
void hideEmptyParentSplitters(DockSplitter *firstParentSplitter);
|
||||
|
||||
/**
|
||||
* Convenience class for QPair to provide better naming than first and
|
||||
* second
|
||||
* Convenience class for QPair to provide better naming than first and second.
|
||||
*/
|
||||
class DockInsertParam : public QPair<Qt::Orientation, bool>
|
||||
{
|
||||
@@ -121,18 +140,30 @@ public:
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the insertion parameters for the given dock area
|
||||
* Returns the insertion parameters for the given dock area.
|
||||
*/
|
||||
DockInsertParam dockAreaInsertParameters(DockWidgetArea area);
|
||||
|
||||
/**
|
||||
* Searches for the parent widget of the given type.
|
||||
* Returns the parent widget of the given widget or 0 if the widget is not
|
||||
* child of any widget of type T
|
||||
*
|
||||
* It is not safe to use this function in in DockWidget because only
|
||||
* the current dock widget has a parent. All dock widgets that are not the
|
||||
* current dock widget in a dock area have no parent.
|
||||
* Returns the SieBarLocation for the AutoHide dock widget areas.
|
||||
*/
|
||||
SideBarLocation toSideBarLocation(DockWidgetArea area);
|
||||
|
||||
/**
|
||||
* Returns true for the top or bottom side bar ansd false for the left and right side bar.
|
||||
*/
|
||||
bool isHorizontalSideBarLocation(SideBarLocation location);
|
||||
|
||||
/**
|
||||
* Returns true, if the given dock area is a SideBar area.
|
||||
*/
|
||||
bool isSideBarArea(DockWidgetArea area);
|
||||
|
||||
/**
|
||||
* Searches for the parent widget of the given type. Returns the parent widget of the given
|
||||
* widget or 0 if the widget is not child of any widget of type T.
|
||||
* It is not safe to use this function in in DockWidget because only the current dock widget has a
|
||||
* parent. All dock widgets that are not the current dock widget in a dock area have no parent.
|
||||
*/
|
||||
template<class T>
|
||||
T findParent(const QWidget *widget)
|
||||
@@ -149,14 +180,13 @@ T findParent(const QWidget *widget)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a semi transparent pixmap from the given pixmap Source.
|
||||
* The Opacity parameter defines the opacity from completely transparent (0.0)
|
||||
* to completely opaque (1.0)
|
||||
* Creates a semi transparent pixmap from the given pixmap source. The opacity parameter defines
|
||||
* the opacity from completely transparent (0.0) to completely opaque (1.0).
|
||||
*/
|
||||
QPixmap createTransparentPixmap(const QPixmap &source, qreal opacity);
|
||||
|
||||
/**
|
||||
* Helper function for settings flags in a QFlags instance.
|
||||
* Helper function for setting flags in a QFlags instance.
|
||||
*/
|
||||
template<class T>
|
||||
void setFlag(T &flags, typename T::enum_type flag, bool on = true)
|
||||
@@ -165,8 +195,7 @@ void setFlag(T &flags, typename T::enum_type flag, bool on = true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for settings tooltips without cluttering the code with
|
||||
* tests for preprocessor macros
|
||||
* Helper function for setting tooltips without cluttering the code with tests for preprocessor macros.
|
||||
*/
|
||||
template <class QObjectPtr>
|
||||
void setToolTip(QObjectPtr obj, const QString &tip)
|
||||
@@ -180,20 +209,20 @@ void setToolTip(QObjectPtr obj, const QString &tip)
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to set the icon of a certain button.
|
||||
* Use this function to set the icons for the dock area and dock widget buttons.
|
||||
* The function first uses the CustomIconId to get an icon from the
|
||||
* IconProvider. You can register your custom icons with the icon provider, if
|
||||
* you do not want to use the default buttons and if you do not want to use
|
||||
* stylesheets.
|
||||
* If the IconProvider does not return a valid icon (icon is null), the function
|
||||
* fetches the given standard pixmap from the QStyle.
|
||||
* param[in] Button The button whose icons are to be set
|
||||
* param[in] StandardPixmap The standard pixmap to be used for the button
|
||||
* param[in] CustomIconId The identifier for the custom icon.
|
||||
* Helper function to set the icon of a certain button. Use this function to set the icons for
|
||||
* the dock area and dock widget buttons.
|
||||
* The function first uses the \p customIconId to get an icon from the IconProvider. You can
|
||||
* register your custom icons with the icon provider, if you do not want to use the default buttons
|
||||
* and if you do not want to use stylesheets.
|
||||
* If the IconProvider does not return a valid icon (icon is null), the function fetches the given
|
||||
* standard pixmap from the QStyle.
|
||||
* param[in] button The button whose icons are to be set
|
||||
* param[in] standardPixmap The standard pixmap to be used for the button
|
||||
* param[in] customIconId The identifier for the custom icon.
|
||||
*/
|
||||
void setButtonIcon(QAbstractButton *button, QStyle::StandardPixmap standarPixmap,
|
||||
ADS::eIcon CustomIconId);
|
||||
void setButtonIcon(QAbstractButton *button,
|
||||
QStyle::StandardPixmap standardPixmap,
|
||||
ADS::eIcon customIconId);
|
||||
|
||||
enum eRepolishChildOptions
|
||||
{
|
||||
@@ -203,10 +232,14 @@ enum eRepolishChildOptions
|
||||
};
|
||||
|
||||
/**
|
||||
* Calls unpolish() / polish for the style of the given widget to update
|
||||
* stylesheet if a property changes
|
||||
* Calls unpolish() / polish for the style of the given widget to update stylesheet if a property changes.
|
||||
*/
|
||||
void repolishStyle(QWidget *widget, eRepolishChildOptions options = RepolishIgnoreChildren);
|
||||
|
||||
/**
|
||||
* Returns the geometry of the given widget in global space.
|
||||
*/
|
||||
QRect globalGeometry(QWidget *widget);
|
||||
|
||||
} // namespace internal
|
||||
} // namespace ADS
|
||||
|
@@ -15,6 +15,9 @@ QtcLibrary {
|
||||
files: [
|
||||
"ads_globals.cpp", "ads_globals.h",
|
||||
"advanceddockingsystemtr.h",
|
||||
"autohidedockcontainer.cpp", "autohidedockcontainer.h",
|
||||
"autohidesidebar.cpp", "autohidesidebar.h",
|
||||
"autohidetab.cpp", "autohidetab.h",
|
||||
"dockareatabbar.cpp", "dockareatabbar.h",
|
||||
"dockareatitlebar.cpp", "dockareatitlebar.h",
|
||||
"dockareawidget.cpp", "dockareawidget.h",
|
||||
@@ -31,6 +34,8 @@ QtcLibrary {
|
||||
"floatingdockcontainer.cpp", "floatingdockcontainer.h",
|
||||
"floatingdragpreview.cpp", "floatingdragpreview.h",
|
||||
"iconprovider.cpp", "iconprovider.h",
|
||||
"pushbutton.cpp", "pushbutton.h",
|
||||
"resizehandle.cpp", "resizehandle.h",
|
||||
"workspace.cpp", "workspace.h",
|
||||
"workspacedialog.cpp", "workspacedialog.h",
|
||||
"workspaceinputdialog.cpp", "workspaceinputdialog.h",
|
||||
|
556
src/libs/advanceddockingsystem/autohidedockcontainer.cpp
Normal file
556
src/libs/advanceddockingsystem/autohidedockcontainer.cpp
Normal file
@@ -0,0 +1,556 @@
|
||||
// Copyright (C) 2020 Uwe Kindler
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-2.1-or-later OR GPL-3.0-or-later
|
||||
|
||||
#include "autohidedockcontainer.h"
|
||||
|
||||
#include "ads_globals_p.h"
|
||||
#include "autohidesidebar.h"
|
||||
#include "autohidetab.h"
|
||||
#include "dockareawidget.h"
|
||||
#include "dockcomponentsfactory.h"
|
||||
#include "dockmanager.h"
|
||||
#include "resizehandle.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QBoxLayout>
|
||||
#include <QCursor>
|
||||
#include <QPainter>
|
||||
#include <QPointer>
|
||||
#include <QSplitter>
|
||||
#include <QXmlStreamWriter>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace ADS {
|
||||
|
||||
static const int resizeMargin = 30;
|
||||
|
||||
bool static isHorizontalArea(SideBarLocation area)
|
||||
{
|
||||
switch (area) {
|
||||
case SideBarLocation::SideBarTop:
|
||||
case SideBarLocation::SideBarBottom:
|
||||
return true;
|
||||
case SideBarLocation::SideBarLeft:
|
||||
case SideBarLocation::SideBarRight:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Qt::Edge static edgeFromSideTabBarArea(SideBarLocation area)
|
||||
{
|
||||
switch (area) {
|
||||
case SideBarLocation::SideBarTop:
|
||||
return Qt::BottomEdge;
|
||||
case SideBarLocation::SideBarBottom:
|
||||
return Qt::TopEdge;
|
||||
case SideBarLocation::SideBarLeft:
|
||||
return Qt::RightEdge;
|
||||
case SideBarLocation::SideBarRight:
|
||||
return Qt::LeftEdge;
|
||||
default:
|
||||
return Qt::LeftEdge;
|
||||
}
|
||||
|
||||
return Qt::LeftEdge;
|
||||
}
|
||||
|
||||
int resizeHandleLayoutPosition(SideBarLocation area)
|
||||
{
|
||||
switch (area) {
|
||||
case SideBarLocation::SideBarBottom:
|
||||
case SideBarLocation::SideBarRight:
|
||||
return 0;
|
||||
|
||||
case SideBarLocation::SideBarTop:
|
||||
case SideBarLocation::SideBarLeft:
|
||||
return 1;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Private data of CAutoHideDockContainer - pimpl
|
||||
*/
|
||||
struct AutoHideDockContainerPrivate
|
||||
{
|
||||
AutoHideDockContainer *q;
|
||||
DockAreaWidget *m_dockArea{nullptr};
|
||||
DockWidget *m_dockWidget{nullptr};
|
||||
SideBarLocation m_sideTabBarArea = SideBarNone;
|
||||
QBoxLayout *m_layout = nullptr;
|
||||
ResizeHandle *m_resizeHandle = nullptr;
|
||||
QSize m_size; // creates invalid size
|
||||
QPointer<AutoHideTab> m_sideTab;
|
||||
QSize m_sizeCache;
|
||||
|
||||
/**
|
||||
* Private data constructor
|
||||
*/
|
||||
AutoHideDockContainerPrivate(AutoHideDockContainer *parent);
|
||||
|
||||
/**
|
||||
* Convenience function to get a dock widget area
|
||||
*/
|
||||
DockWidgetArea getDockWidgetArea(SideBarLocation area)
|
||||
{
|
||||
switch (area) {
|
||||
case SideBarLocation::SideBarLeft:
|
||||
return LeftDockWidgetArea;
|
||||
case SideBarLocation::SideBarRight:
|
||||
return RightDockWidgetArea;
|
||||
case SideBarLocation::SideBarBottom:
|
||||
return BottomDockWidgetArea;
|
||||
case SideBarLocation::SideBarTop:
|
||||
return TopDockWidgetArea;
|
||||
default:
|
||||
return LeftDockWidgetArea;
|
||||
}
|
||||
|
||||
return LeftDockWidgetArea;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the resize limit of the resize handle
|
||||
*/
|
||||
void updateResizeHandleSizeLimitMax()
|
||||
{
|
||||
auto rect = q->dockContainer()->contentRect();
|
||||
const auto maxResizeHandleSize = m_resizeHandle->orientation() == Qt::Horizontal
|
||||
? rect.width()
|
||||
: rect.height();
|
||||
m_resizeHandle->setMaxResizeSize(maxResizeHandleSize - resizeMargin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function to check, if this is an horizontal area
|
||||
*/
|
||||
bool isHorizontal() const { return isHorizontalArea(m_sideTabBarArea); }
|
||||
|
||||
/**
|
||||
* Forward this event to the dock container
|
||||
*/
|
||||
void forwardEventToDockContainer(QEvent *event)
|
||||
{
|
||||
auto dockContainer = q->dockContainer();
|
||||
if (dockContainer)
|
||||
dockContainer->handleAutoHideWidgetEvent(event, q);
|
||||
}
|
||||
|
||||
}; // struct AutoHideDockContainerPrivate
|
||||
|
||||
AutoHideDockContainerPrivate::AutoHideDockContainerPrivate(AutoHideDockContainer *parent)
|
||||
: q(parent)
|
||||
{}
|
||||
|
||||
DockContainerWidget *AutoHideDockContainer::dockContainer() const
|
||||
{
|
||||
return internal::findParent<DockContainerWidget *>(this);
|
||||
}
|
||||
|
||||
AutoHideDockContainer::AutoHideDockContainer(DockWidget *dockWidget,
|
||||
SideBarLocation area,
|
||||
DockContainerWidget *parent)
|
||||
: QFrame(parent)
|
||||
, d(new AutoHideDockContainerPrivate(this))
|
||||
{
|
||||
hide(); // auto hide dock container is initially always hidden
|
||||
d->m_sideTabBarArea = area;
|
||||
d->m_sideTab = componentsFactory()->createDockWidgetSideTab(nullptr);
|
||||
connect(d->m_sideTab, &AutoHideTab::pressed, this, &AutoHideDockContainer::toggleCollapseState);
|
||||
d->m_dockArea = new DockAreaWidget(dockWidget->dockManager(), parent);
|
||||
d->m_dockArea->setObjectName("autoHideDockArea");
|
||||
d->m_dockArea->setAutoHideDockContainer(this);
|
||||
|
||||
setObjectName("autoHideDockContainer");
|
||||
|
||||
d->m_layout = new QBoxLayout(isHorizontalArea(area) ? QBoxLayout::TopToBottom
|
||||
: QBoxLayout::LeftToRight);
|
||||
d->m_layout->setContentsMargins(0, 0, 0, 0);
|
||||
d->m_layout->setSpacing(0);
|
||||
setLayout(d->m_layout);
|
||||
d->m_resizeHandle = new ResizeHandle(edgeFromSideTabBarArea(area), this);
|
||||
d->m_resizeHandle->setMinResizeSize(64);
|
||||
bool opaqueResize = DockManager::testConfigFlag(DockManager::OpaqueSplitterResize);
|
||||
d->m_resizeHandle->setOpaqueResize(opaqueResize);
|
||||
d->m_size = d->m_dockArea->size();
|
||||
d->m_sizeCache = dockWidget->size();
|
||||
|
||||
addDockWidget(dockWidget);
|
||||
parent->registerAutoHideWidget(this);
|
||||
// The dock area should not be added to the layout before it contains the dock widget. If you
|
||||
// add it to the layout before it contains the dock widget then you will likely see this
|
||||
// warning for OpenGL widgets or QAxWidgets:
|
||||
// setGeometry: Unable to set geometry XxY+Width+Height on QWidgetWindow/'WidgetClassWindow
|
||||
d->m_layout->addWidget(d->m_dockArea);
|
||||
d->m_layout->insertWidget(resizeHandleLayoutPosition(area), d->m_resizeHandle);
|
||||
}
|
||||
|
||||
void AutoHideDockContainer::updateSize()
|
||||
{
|
||||
auto dockContainerParent = dockContainer();
|
||||
if (!dockContainerParent)
|
||||
return;
|
||||
|
||||
auto rect = dockContainerParent->contentRect();
|
||||
|
||||
switch (sideBarLocation()) {
|
||||
case SideBarLocation::SideBarTop:
|
||||
resize(rect.width(), qMin(rect.height() - resizeMargin, d->m_size.height()));
|
||||
move(rect.topLeft());
|
||||
break;
|
||||
|
||||
case SideBarLocation::SideBarLeft:
|
||||
resize(qMin(d->m_size.width(), rect.width() - resizeMargin), rect.height());
|
||||
move(rect.topLeft());
|
||||
break;
|
||||
|
||||
case SideBarLocation::SideBarRight: {
|
||||
resize(qMin(d->m_size.width(), rect.width() - resizeMargin), rect.height());
|
||||
QPoint p = rect.topRight();
|
||||
p.rx() -= (width() - 1);
|
||||
move(p);
|
||||
} break;
|
||||
|
||||
case SideBarLocation::SideBarBottom: {
|
||||
resize(rect.width(), qMin(rect.height() - resizeMargin, d->m_size.height()));
|
||||
QPoint p = rect.bottomLeft();
|
||||
p.ry() -= (height() - 1);
|
||||
move(p);
|
||||
} break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (orientation() == Qt::Horizontal)
|
||||
d->m_sizeCache.setHeight(this->height());
|
||||
else
|
||||
d->m_sizeCache.setWidth(this->width());
|
||||
}
|
||||
|
||||
AutoHideDockContainer::~AutoHideDockContainer()
|
||||
{
|
||||
qCInfo(adsLog) << Q_FUNC_INFO;
|
||||
|
||||
// Remove event filter in case there are any queued messages
|
||||
qApp->removeEventFilter(this);
|
||||
if (dockContainer())
|
||||
dockContainer()->removeAutoHideWidget(this);
|
||||
|
||||
if (d->m_sideTab)
|
||||
delete d->m_sideTab;
|
||||
|
||||
delete d;
|
||||
}
|
||||
|
||||
AutoHideSideBar *AutoHideDockContainer::autoHideSideBar() const
|
||||
{
|
||||
if (d->m_sideTab) {
|
||||
return d->m_sideTab->sideBar();
|
||||
} else {
|
||||
auto container = dockContainer();
|
||||
return container ? container->autoHideSideBar(d->m_sideTabBarArea) : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
AutoHideTab *AutoHideDockContainer::autoHideTab() const
|
||||
{
|
||||
return d->m_sideTab;
|
||||
}
|
||||
|
||||
DockWidget *AutoHideDockContainer::dockWidget() const
|
||||
{
|
||||
return d->m_dockWidget;
|
||||
}
|
||||
|
||||
void AutoHideDockContainer::addDockWidget(DockWidget *dockWidget)
|
||||
{
|
||||
if (d->m_dockWidget) {
|
||||
// Remove the old dock widget at this area
|
||||
d->m_dockArea->removeDockWidget(d->m_dockWidget);
|
||||
}
|
||||
|
||||
d->m_dockWidget = dockWidget;
|
||||
d->m_sideTab->setDockWidget(dockWidget);
|
||||
DockAreaWidget *oldDockArea = dockWidget->dockAreaWidget();
|
||||
auto isRestoringState = dockWidget->dockManager()->isRestoringState();
|
||||
if (oldDockArea && !isRestoringState) {
|
||||
// The initial size should be a little bit bigger than the original dock area size to
|
||||
// prevent that the resize handle of this auto hid dock area is near of the splitter of
|
||||
// the old dock area.
|
||||
d->m_size = oldDockArea->size() + QSize(16, 16);
|
||||
oldDockArea->removeDockWidget(dockWidget);
|
||||
}
|
||||
d->m_dockArea->addDockWidget(dockWidget);
|
||||
updateSize();
|
||||
// The dock area is not visible and will not update the size when updateSize() is called for
|
||||
// this auto hide container. Therefore we explicitly resize it here. As soon as it will
|
||||
// become visible, it will get the right size
|
||||
d->m_dockArea->resize(size());
|
||||
}
|
||||
|
||||
SideBarLocation AutoHideDockContainer::sideBarLocation() const
|
||||
{
|
||||
return d->m_sideTabBarArea;
|
||||
}
|
||||
|
||||
void AutoHideDockContainer::setSideBarLocation(SideBarLocation sideBarLocation)
|
||||
{
|
||||
if (d->m_sideTabBarArea == sideBarLocation)
|
||||
return;
|
||||
|
||||
d->m_sideTabBarArea = sideBarLocation;
|
||||
d->m_layout->removeWidget(d->m_resizeHandle);
|
||||
d->m_layout->setDirection(isHorizontalArea(sideBarLocation) ? QBoxLayout::TopToBottom
|
||||
: QBoxLayout::LeftToRight);
|
||||
d->m_layout->insertWidget(resizeHandleLayoutPosition(sideBarLocation), d->m_resizeHandle);
|
||||
d->m_resizeHandle->setHandlePosition(edgeFromSideTabBarArea(sideBarLocation));
|
||||
internal::repolishStyle(this, internal::RepolishDirectChildren);
|
||||
}
|
||||
|
||||
DockAreaWidget *AutoHideDockContainer::dockAreaWidget() const
|
||||
{
|
||||
return d->m_dockArea;
|
||||
}
|
||||
|
||||
void AutoHideDockContainer::moveContentsToParent()
|
||||
{
|
||||
cleanupAndDelete();
|
||||
// If we unpin the auto hide dock widget, then we insert it into the same
|
||||
// location like it had as a auto hide widget. This brings the least surprise
|
||||
// to the user and he does not have to search where the widget was inserted.
|
||||
d->m_dockWidget->setDockArea(nullptr);
|
||||
auto DockContainer = dockContainer();
|
||||
DockContainer->addDockWidget(d->getDockWidgetArea(d->m_sideTabBarArea), d->m_dockWidget);
|
||||
}
|
||||
|
||||
void AutoHideDockContainer::cleanupAndDelete()
|
||||
{
|
||||
const auto dockWidget = d->m_dockWidget;
|
||||
if (dockWidget) {
|
||||
auto sideTab = d->m_sideTab;
|
||||
sideTab->removeFromSideBar();
|
||||
sideTab->setParent(nullptr);
|
||||
sideTab->hide();
|
||||
}
|
||||
|
||||
hide();
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
void AutoHideDockContainer::saveState(QXmlStreamWriter &s)
|
||||
{
|
||||
s.writeStartElement("widget");
|
||||
s.writeAttribute("name", d->m_dockWidget->objectName());
|
||||
s.writeAttribute("closed", QString::number(d->m_dockWidget->isClosed() ? 1 : 0));
|
||||
s.writeAttribute("size",
|
||||
QString::number(d->isHorizontal() ? d->m_size.height() : d->m_size.width()));
|
||||
s.writeEndElement();
|
||||
}
|
||||
|
||||
void AutoHideDockContainer::toggleView(bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
if (d->m_sideTab)
|
||||
d->m_sideTab->show();
|
||||
} else {
|
||||
if (d->m_sideTab)
|
||||
d->m_sideTab->hide();
|
||||
|
||||
hide();
|
||||
qApp->removeEventFilter(this);
|
||||
}
|
||||
}
|
||||
|
||||
void AutoHideDockContainer::collapseView(bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
hide();
|
||||
qApp->removeEventFilter(this);
|
||||
} else {
|
||||
updateSize();
|
||||
d->updateResizeHandleSizeLimitMax();
|
||||
raise();
|
||||
show();
|
||||
d->m_dockWidget->dockManager()->setDockWidgetFocused(d->m_dockWidget);
|
||||
qApp->installEventFilter(this);
|
||||
}
|
||||
|
||||
qCInfo(adsLog) << Q_FUNC_INFO << enable;
|
||||
d->m_sideTab->updateStyle();
|
||||
}
|
||||
|
||||
void AutoHideDockContainer::toggleCollapseState()
|
||||
{
|
||||
collapseView(isVisible());
|
||||
}
|
||||
|
||||
void AutoHideDockContainer::setSize(int size)
|
||||
{
|
||||
if (d->isHorizontal())
|
||||
d->m_size.setHeight(size);
|
||||
else
|
||||
d->m_size.setWidth(size);
|
||||
|
||||
updateSize();
|
||||
}
|
||||
|
||||
Qt::Orientation AutoHideDockContainer::orientation() const
|
||||
{
|
||||
return internal::isHorizontalSideBarLocation(d->m_sideTabBarArea) ? Qt::Horizontal
|
||||
: Qt::Vertical;
|
||||
}
|
||||
|
||||
void AutoHideDockContainer::resetToInitialDockWidgetSize()
|
||||
{
|
||||
if (orientation() == Qt::Horizontal)
|
||||
setSize(d->m_sizeCache.height());
|
||||
else
|
||||
setSize(d->m_sizeCache.width());
|
||||
}
|
||||
|
||||
void AutoHideDockContainer::moveToNewSideBarLocation(SideBarLocation newSideBarLocation, int index)
|
||||
{
|
||||
if (newSideBarLocation == sideBarLocation() && index == tabIndex())
|
||||
return;
|
||||
|
||||
auto oldOrientation = orientation();
|
||||
auto sideBar = dockContainer()->autoHideSideBar(newSideBarLocation);
|
||||
sideBar->addAutoHideWidget(this, index);
|
||||
// If we move a horizontal auto hide container to a vertical position then we resize it to the
|
||||
// orginal dock widget size, to avoid an extremely stretched dock widget after insertion.
|
||||
if (sideBar->orientation() != oldOrientation)
|
||||
resetToInitialDockWidgetSize();
|
||||
}
|
||||
|
||||
int AutoHideDockContainer::tabIndex() const
|
||||
{
|
||||
return d->m_sideTab->tabIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the object given in ancestor is an ancestor of the object given in descendant
|
||||
*/
|
||||
static bool objectIsAncestorOf(const QObject* descendant, const QObject* ancestor)
|
||||
{
|
||||
if (!ancestor)
|
||||
return false;
|
||||
|
||||
while (descendant) {
|
||||
if (descendant == ancestor)
|
||||
return true;
|
||||
|
||||
descendant = descendant->parent();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the object given in ancestor is the object given in descendant
|
||||
* or if it is an ancestor of the object given in descendant
|
||||
*/
|
||||
static bool isObjectOrAncestor(const QObject *descendant, const QObject *ancestor)
|
||||
{
|
||||
if (ancestor && (descendant == ancestor))
|
||||
return true;
|
||||
else
|
||||
return objectIsAncestorOf(descendant, ancestor);
|
||||
}
|
||||
|
||||
bool AutoHideDockContainer::eventFilter(QObject *watched, QEvent *event)
|
||||
{
|
||||
// A switch case statement would be nicer here, but we cannot use
|
||||
// internal::FloatingWidgetDragStartEvent in a switch case
|
||||
if (event->type() == QEvent::Resize) {
|
||||
if (!d->m_resizeHandle->isResizing())
|
||||
updateSize();
|
||||
} else if (event->type() == QEvent::MouseButtonPress) {
|
||||
auto widget = qobject_cast<QWidget *>(watched);
|
||||
// Ignore non widget events
|
||||
if (!widget)
|
||||
return QFrame::eventFilter(watched, event);
|
||||
|
||||
// Now check, if the user clicked into the side tab and ignore this event,
|
||||
// because the side tab click handler will call collapseView(). If we
|
||||
// do not ignore this here, then we will collapse the container and the side tab
|
||||
// click handler will uncollapse it
|
||||
if (widget == d->m_sideTab.data())
|
||||
return QFrame::eventFilter(watched, event);
|
||||
|
||||
// Now we check, if the user clicked inside of this auto hide container.
|
||||
// If the click is inside of this auto hide container, then we can
|
||||
// ignore the event, because the auto hide overlay should not get collapsed if
|
||||
// user works in it
|
||||
if (isObjectOrAncestor(widget, this))
|
||||
return QFrame::eventFilter(watched, event);
|
||||
|
||||
// Ignore the mouse click if it is not inside of this container
|
||||
if (!isObjectOrAncestor(widget, dockContainer()))
|
||||
return QFrame::eventFilter(watched, event);
|
||||
|
||||
// User clicked into container - collapse the auto hide widget
|
||||
collapseView(true);
|
||||
} else if (event->type() == internal::g_floatingWidgetDragStartEvent) {
|
||||
// If we are dragging our own floating widget, the we do not need to collapse the view
|
||||
auto widget = dockContainer()->floatingWidget();
|
||||
if (widget != watched)
|
||||
collapseView(true);
|
||||
} else if (event->type() == internal::g_dockedWidgetDragStartEvent) {
|
||||
collapseView(true);
|
||||
}
|
||||
|
||||
return QFrame::eventFilter(watched, event);
|
||||
}
|
||||
|
||||
void AutoHideDockContainer::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
QFrame::resizeEvent(event);
|
||||
|
||||
if (d->m_resizeHandle->isResizing()) {
|
||||
d->m_size = this->size();
|
||||
d->updateResizeHandleSizeLimitMax();
|
||||
}
|
||||
}
|
||||
|
||||
void AutoHideDockContainer::leaveEvent(QEvent *event)
|
||||
{
|
||||
// Resizing of the dock container via the resize handle in non opaque mode
|
||||
// mays cause a leave event that is not really a leave event. Therefore
|
||||
// we check here, if we are really outside of our rect.
|
||||
auto pos = mapFromGlobal(QCursor::pos());
|
||||
if (!rect().contains(pos))
|
||||
d->forwardEventToDockContainer(event);
|
||||
|
||||
QFrame::leaveEvent(event);
|
||||
}
|
||||
|
||||
bool AutoHideDockContainer::event(QEvent *event)
|
||||
{
|
||||
switch (event->type()) {
|
||||
case QEvent::Enter:
|
||||
case QEvent::Hide:
|
||||
d->forwardEventToDockContainer(event);
|
||||
break;
|
||||
|
||||
case QEvent::MouseButtonPress:
|
||||
return true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return QFrame::event(event);
|
||||
}
|
||||
|
||||
} // namespace ADS
|
168
src/libs/advanceddockingsystem/autohidedockcontainer.h
Normal file
168
src/libs/advanceddockingsystem/autohidedockcontainer.h
Normal file
@@ -0,0 +1,168 @@
|
||||
// Copyright (C) 2020 Uwe Kindler
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-2.1-or-later OR GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ads_globals.h"
|
||||
#include "autohidetab.h"
|
||||
|
||||
#include <QSplitter>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QXmlStreamWriter;
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace ADS {
|
||||
|
||||
struct AutoHideDockContainerPrivate;
|
||||
class DockManager;
|
||||
class DockWidget;
|
||||
class DockContainerWidget;
|
||||
class AutoHideSideBar;
|
||||
class DockAreaWidget;
|
||||
class DockingStateReader;
|
||||
struct SideTabBarPrivate;
|
||||
|
||||
/**
|
||||
* Auto hide container for hosting an auto hide dock widget
|
||||
*/
|
||||
class ADS_EXPORT AutoHideDockContainer : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int sideBarLocation READ sideBarLocation CONSTANT) // TODO
|
||||
private:
|
||||
AutoHideDockContainerPrivate *d; ///< private data (pimpl)
|
||||
friend struct AutoHideDockContainerPrivate;
|
||||
friend AutoHideSideBar;
|
||||
friend SideTabBarPrivate;
|
||||
|
||||
protected:
|
||||
virtual bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
virtual void resizeEvent(QResizeEvent *event) override;
|
||||
virtual void leaveEvent(QEvent *event) override;
|
||||
virtual bool event(QEvent *event) override;
|
||||
|
||||
/**
|
||||
* Updates the size considering the size limits and the resize margins
|
||||
*/
|
||||
void updateSize();
|
||||
|
||||
/*
|
||||
* Saves the state and size
|
||||
*/
|
||||
void saveState(QXmlStreamWriter &Stream);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create Auto Hide widget with the given dock widget
|
||||
*/
|
||||
AutoHideDockContainer(DockWidget *dockWidget, SideBarLocation area, DockContainerWidget *parent);
|
||||
|
||||
/**
|
||||
* Virtual Destructor
|
||||
*/
|
||||
virtual ~AutoHideDockContainer();
|
||||
|
||||
/**
|
||||
* Get's the side tab bar
|
||||
*/
|
||||
AutoHideSideBar *autoHideSideBar() const;
|
||||
|
||||
/**
|
||||
* Returns the side tab
|
||||
*/
|
||||
AutoHideTab *autoHideTab() const;
|
||||
|
||||
/**
|
||||
* Get's the dock widget in this dock container
|
||||
*/
|
||||
DockWidget *dockWidget() const;
|
||||
|
||||
/**
|
||||
* Adds a dock widget and removes the previous dock widget
|
||||
*/
|
||||
void addDockWidget(DockWidget *dockWidget);
|
||||
|
||||
/**
|
||||
* Returns the side tab bar area of this Auto Hide dock container
|
||||
*/
|
||||
SideBarLocation sideBarLocation() const;
|
||||
|
||||
/**
|
||||
* Sets a new SideBarLocation.
|
||||
* If a new side bar location is set, the auto hide dock container needs
|
||||
* to update its resize handle position
|
||||
*/
|
||||
void setSideBarLocation(SideBarLocation sideBarLocation);
|
||||
|
||||
/**
|
||||
* Returns the dock area widget of this Auto Hide dock container
|
||||
*/
|
||||
DockAreaWidget *dockAreaWidget() const;
|
||||
|
||||
/**
|
||||
* Returns the parent container that hosts this auto hide container
|
||||
*/
|
||||
DockContainerWidget *dockContainer() const;
|
||||
|
||||
/**
|
||||
* Moves the contents to the parent container widget
|
||||
* Used before removing this Auto Hide dock container
|
||||
*/
|
||||
void moveContentsToParent();
|
||||
|
||||
/**
|
||||
* Cleanups up the side tab widget and then deletes itself
|
||||
*/
|
||||
void cleanupAndDelete();
|
||||
|
||||
/**
|
||||
* Toggles the auto Hide dock container widget. This will also hide the side tab widget.
|
||||
*/
|
||||
void toggleView(bool enable);
|
||||
|
||||
/**
|
||||
* Collapses the auto hide dock container widget
|
||||
* Does not hide the side tab widget
|
||||
*/
|
||||
void collapseView(bool enable);
|
||||
|
||||
/**
|
||||
* Toggles the current collapse state
|
||||
*/
|
||||
void toggleCollapseState();
|
||||
|
||||
/**
|
||||
* Use this instead of resize.
|
||||
* Depending on the sidebar location this will set the width or height of this auto hide container.
|
||||
*/
|
||||
void setSize(int size);
|
||||
|
||||
/**
|
||||
* Returns orientation of this container.
|
||||
* Left and right containers have a Qt::Vertical orientation and top / bottom containers have
|
||||
* a Qt::Horizontal orientation. The function returns the orientation of the corresponding
|
||||
* auto hide side bar.
|
||||
*/
|
||||
Qt::Orientation orientation() const;
|
||||
|
||||
/**
|
||||
* Resets the with or hight to the initial dock widget size dependinng on the orientation.
|
||||
* If the orientation is Qt::Horizontal, then the height is reset to the initial size and if
|
||||
* orientation is Qt::Vertical, then the width is reset to the initial size.
|
||||
*/
|
||||
void resetToInitialDockWidgetSize();
|
||||
|
||||
/**
|
||||
* Removes the AutoHide container from the current side bar and adds it to the new side bar
|
||||
* given in SideBarLocation.
|
||||
*/
|
||||
void moveToNewSideBarLocation(SideBarLocation sideBarLocation, int index = -1);
|
||||
|
||||
/**
|
||||
* Returns the index of this container in the sidebar.
|
||||
*/
|
||||
int tabIndex() const;
|
||||
};
|
||||
|
||||
} // namespace ADS
|
366
src/libs/advanceddockingsystem/autohidesidebar.cpp
Normal file
366
src/libs/advanceddockingsystem/autohidesidebar.cpp
Normal file
@@ -0,0 +1,366 @@
|
||||
// Copyright (C) 2020 Uwe Kindler
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-2.1-or-later OR GPL-3.0-or-later
|
||||
|
||||
#include "autohidesidebar.h"
|
||||
|
||||
#include "ads_globals_p.h"
|
||||
#include "autohidedockcontainer.h"
|
||||
#include "autohidetab.h"
|
||||
#include "dockareawidget.h"
|
||||
#include "dockcontainerwidget.h"
|
||||
#include "dockfocuscontroller.h"
|
||||
#include "dockingstatereader.h"
|
||||
#include "dockwidgettab.h"
|
||||
|
||||
#include <QBoxLayout>
|
||||
#include <QPainter>
|
||||
#include <QResizeEvent>
|
||||
#include <QStyleOption>
|
||||
#include <QXmlStreamWriter>
|
||||
|
||||
namespace ADS {
|
||||
|
||||
class TabsWidget;
|
||||
|
||||
/**
|
||||
* Private data class of CSideTabBar class (pimpl)
|
||||
*/
|
||||
struct AutoHideSideBarPrivate
|
||||
{
|
||||
/**
|
||||
* Private data constructor
|
||||
*/
|
||||
AutoHideSideBarPrivate(AutoHideSideBar *parent);
|
||||
|
||||
AutoHideSideBar *q;
|
||||
DockContainerWidget *m_containerWidget;
|
||||
TabsWidget *m_tabsContainerWidget;
|
||||
QBoxLayout *m_tabsLayout;
|
||||
Qt::Orientation m_orientation;
|
||||
SideBarLocation m_sideTabArea = SideBarLocation::SideBarLeft;
|
||||
|
||||
/**
|
||||
* Convenience function to check if this is a horizontal side bar
|
||||
*/
|
||||
bool isHorizontal() const { return Qt::Horizontal == m_orientation; }
|
||||
|
||||
/**
|
||||
* Called from viewport to forward event handling to this
|
||||
*/
|
||||
void handleViewportEvent(QEvent* e);
|
||||
}; // struct AutoHideSideBarPrivate
|
||||
|
||||
/**
|
||||
* This widget stores the tab buttons
|
||||
*/
|
||||
class TabsWidget : public QWidget
|
||||
{
|
||||
public:
|
||||
using QWidget::QWidget;
|
||||
using Super = QWidget;
|
||||
AutoHideSideBarPrivate *eventHandler;
|
||||
|
||||
/**
|
||||
* Returns the size hint as minimum size hint
|
||||
*/
|
||||
virtual QSize minimumSizeHint() const override { return Super::sizeHint(); }
|
||||
|
||||
/**
|
||||
* Forward event handling to EventHandler
|
||||
*/
|
||||
virtual bool event(QEvent *e) override
|
||||
{
|
||||
eventHandler->handleViewportEvent(e);
|
||||
return Super::event(e);
|
||||
}
|
||||
};
|
||||
|
||||
AutoHideSideBarPrivate::AutoHideSideBarPrivate(AutoHideSideBar *parent)
|
||||
: q(parent)
|
||||
{}
|
||||
|
||||
void AutoHideSideBarPrivate::handleViewportEvent(QEvent* e)
|
||||
{
|
||||
switch (e->type()) {
|
||||
case QEvent::ChildRemoved:
|
||||
if (m_tabsLayout->isEmpty())
|
||||
q->hide();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
AutoHideSideBar::AutoHideSideBar(DockContainerWidget *parent, SideBarLocation area)
|
||||
: Super(parent)
|
||||
, d(new AutoHideSideBarPrivate(this))
|
||||
{
|
||||
d->m_sideTabArea = area;
|
||||
d->m_containerWidget = parent;
|
||||
d->m_orientation = (area == SideBarLocation::SideBarBottom
|
||||
|| area == SideBarLocation::SideBarTop)
|
||||
? Qt::Horizontal
|
||||
: Qt::Vertical;
|
||||
|
||||
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
||||
setFrameStyle(QFrame::NoFrame);
|
||||
setWidgetResizable(true);
|
||||
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
|
||||
d->m_tabsContainerWidget = new TabsWidget();
|
||||
d->m_tabsContainerWidget->eventHandler = d;
|
||||
d->m_tabsContainerWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
||||
d->m_tabsContainerWidget->setObjectName("sideTabsContainerWidget");
|
||||
|
||||
d->m_tabsLayout = new QBoxLayout(d->m_orientation == Qt::Vertical ? QBoxLayout::TopToBottom
|
||||
: QBoxLayout::LeftToRight);
|
||||
d->m_tabsLayout->setContentsMargins(0, 0, 0, 0);
|
||||
d->m_tabsLayout->setSpacing(12);
|
||||
d->m_tabsLayout->addStretch(1);
|
||||
d->m_tabsContainerWidget->setLayout(d->m_tabsLayout);
|
||||
setWidget(d->m_tabsContainerWidget);
|
||||
|
||||
setFocusPolicy(Qt::NoFocus);
|
||||
if (d->isHorizontal())
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
else
|
||||
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
|
||||
|
||||
hide();
|
||||
}
|
||||
|
||||
AutoHideSideBar::~AutoHideSideBar()
|
||||
{
|
||||
qCInfo(adsLog) << Q_FUNC_INFO;
|
||||
// The SideTabeBar is not the owner of the tabs and to prevent deletion
|
||||
// we set the parent here to nullptr to remove it from the children
|
||||
auto tabs = findChildren<AutoHideTab *>(QString(), Qt::FindDirectChildrenOnly);
|
||||
for (auto tab : tabs)
|
||||
tab->setParent(nullptr);
|
||||
|
||||
delete d;
|
||||
}
|
||||
|
||||
void AutoHideSideBar::insertTab(int index, AutoHideTab *sideTab)
|
||||
{
|
||||
sideTab->setSideBar(this);
|
||||
sideTab->installEventFilter(this);
|
||||
// Default insertion is append
|
||||
if (index < 0)
|
||||
d->m_tabsLayout->insertWidget(d->m_tabsLayout->count() - 1, sideTab);
|
||||
else
|
||||
d->m_tabsLayout->insertWidget(index, sideTab);
|
||||
|
||||
show();
|
||||
}
|
||||
|
||||
AutoHideDockContainer *AutoHideSideBar::insertDockWidget(int index, DockWidget *dockWidget)
|
||||
{
|
||||
auto autoHideContainer = new AutoHideDockContainer(dockWidget,
|
||||
d->m_sideTabArea,
|
||||
d->m_containerWidget);
|
||||
dockWidget->dockManager()->dockFocusController()->clearDockWidgetFocus(dockWidget);
|
||||
auto tab = autoHideContainer->autoHideTab();
|
||||
dockWidget->setSideTabWidget(tab);
|
||||
insertTab(index, tab);
|
||||
return autoHideContainer;
|
||||
}
|
||||
|
||||
void AutoHideSideBar::removeAutoHideWidget(AutoHideDockContainer *autoHideWidget)
|
||||
{
|
||||
autoHideWidget->autoHideTab()->removeFromSideBar();
|
||||
auto dockContainer = autoHideWidget->dockContainer();
|
||||
if (dockContainer)
|
||||
dockContainer->removeAutoHideWidget(autoHideWidget);
|
||||
|
||||
autoHideWidget->setParent(nullptr);
|
||||
}
|
||||
|
||||
void AutoHideSideBar::addAutoHideWidget(AutoHideDockContainer *autoHideWidget, int index)
|
||||
{
|
||||
auto sideBar = autoHideWidget->autoHideTab()->sideBar();
|
||||
if (sideBar == this) {
|
||||
// If we move to the same tab index or if we insert before the next tab index, then we will
|
||||
// end at the same tab position and can leave.
|
||||
if (autoHideWidget->tabIndex() == index || (autoHideWidget->tabIndex() + 1) == index)
|
||||
return;
|
||||
|
||||
// We remove this auto hide widget from the sidebar in the code below and therefore need
|
||||
// to correct the TabIndex here.
|
||||
if (autoHideWidget->tabIndex() < index)
|
||||
--index;
|
||||
}
|
||||
|
||||
if (sideBar)
|
||||
sideBar->removeAutoHideWidget(autoHideWidget);
|
||||
|
||||
autoHideWidget->setParent(d->m_containerWidget);
|
||||
autoHideWidget->setSideBarLocation(d->m_sideTabArea);
|
||||
d->m_containerWidget->registerAutoHideWidget(autoHideWidget);
|
||||
insertTab(index, autoHideWidget->autoHideTab());
|
||||
}
|
||||
|
||||
void AutoHideSideBar::removeTab(AutoHideTab *sideTab)
|
||||
{
|
||||
sideTab->removeEventFilter(this);
|
||||
d->m_tabsLayout->removeWidget(sideTab);
|
||||
if (d->m_tabsLayout->isEmpty())
|
||||
hide();
|
||||
}
|
||||
|
||||
bool AutoHideSideBar::eventFilter(QObject *watched, QEvent *event)
|
||||
{
|
||||
auto tab = qobject_cast<AutoHideTab *>(watched);
|
||||
if (!tab)
|
||||
return false;
|
||||
|
||||
switch (event->type()) {
|
||||
case QEvent::ShowToParent:
|
||||
show();
|
||||
break;
|
||||
|
||||
case QEvent::HideToParent:
|
||||
if (!hasVisibleTabs())
|
||||
hide();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Qt::Orientation AutoHideSideBar::orientation() const
|
||||
{
|
||||
return d->m_orientation;
|
||||
}
|
||||
|
||||
AutoHideTab *AutoHideSideBar::tab(int index) const
|
||||
{
|
||||
return qobject_cast<AutoHideTab *>(d->m_tabsLayout->itemAt(index)->widget());
|
||||
}
|
||||
|
||||
int AutoHideSideBar::tabAt(const QPoint &pos) const
|
||||
{
|
||||
if (!isVisible())
|
||||
return TabInvalidIndex;
|
||||
|
||||
if (orientation() == Qt::Horizontal) {
|
||||
if (pos.x() < tab(0)->geometry().x())
|
||||
return -1;
|
||||
} else {
|
||||
if (pos.y() < tab(0)->geometry().y())
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < count(); ++i) {
|
||||
if (tab(i)->geometry().contains(pos))
|
||||
return i;
|
||||
}
|
||||
|
||||
return count();
|
||||
}
|
||||
|
||||
int AutoHideSideBar::tabInsertIndexAt(const QPoint &pos) const
|
||||
{
|
||||
int index = tabAt(pos);
|
||||
if (index == TabInvalidIndex)
|
||||
return TabDefaultInsertIndex;
|
||||
else
|
||||
return (index < 0) ? 0 : index;
|
||||
}
|
||||
|
||||
int AutoHideSideBar::indexOfTab(const AutoHideTab &autoHideTab) const
|
||||
{
|
||||
for (auto i = 0; i < count(); i++) {
|
||||
if (tab(i) == &autoHideTab)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int AutoHideSideBar::count() const
|
||||
{
|
||||
return d->m_tabsLayout->count() - 1;
|
||||
}
|
||||
|
||||
int AutoHideSideBar::visibleTabCount() const
|
||||
{
|
||||
int c = 0;
|
||||
auto parent = parentWidget();
|
||||
for (auto i = 0; i < count(); i++) {
|
||||
if (tab(i)->isVisibleTo(parent))
|
||||
c++;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
bool AutoHideSideBar::hasVisibleTabs() const
|
||||
{
|
||||
auto parent = parentWidget();
|
||||
for (auto i = 0; i < count(); i++) {
|
||||
if (tab(i)->isVisibleTo(parent))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
SideBarLocation AutoHideSideBar::sideBarLocation() const
|
||||
{
|
||||
return d->m_sideTabArea;
|
||||
}
|
||||
|
||||
void AutoHideSideBar::saveState(QXmlStreamWriter &s) const
|
||||
{
|
||||
if (!count())
|
||||
return;
|
||||
|
||||
s.writeStartElement("sideBar");
|
||||
s.writeAttribute("area", QString::number(sideBarLocation()));
|
||||
s.writeAttribute("tabs", QString::number(count()));
|
||||
|
||||
for (auto i = 0; i < count(); ++i) {
|
||||
auto currentTab = tab(i);
|
||||
if (!currentTab)
|
||||
continue;
|
||||
|
||||
currentTab->dockWidget()->autoHideDockContainer()->saveState(s);
|
||||
}
|
||||
|
||||
s.writeEndElement();
|
||||
}
|
||||
|
||||
QSize AutoHideSideBar::minimumSizeHint() const
|
||||
{
|
||||
QSize size = sizeHint();
|
||||
size.setWidth(10);
|
||||
return size;
|
||||
}
|
||||
|
||||
QSize AutoHideSideBar::sizeHint() const
|
||||
{
|
||||
return d->m_tabsContainerWidget->sizeHint();
|
||||
}
|
||||
|
||||
int AutoHideSideBar::spacing() const
|
||||
{
|
||||
return d->m_tabsLayout->spacing();
|
||||
}
|
||||
|
||||
void AutoHideSideBar::setSpacing(int spacing)
|
||||
{
|
||||
d->m_tabsLayout->setSpacing(spacing);
|
||||
}
|
||||
|
||||
DockContainerWidget *AutoHideSideBar::dockContainer() const
|
||||
{
|
||||
return d->m_containerWidget;
|
||||
}
|
||||
|
||||
} // namespace ADS
|
174
src/libs/advanceddockingsystem/autohidesidebar.h
Normal file
174
src/libs/advanceddockingsystem/autohidesidebar.h
Normal file
@@ -0,0 +1,174 @@
|
||||
// Copyright (C) 2020 Uwe Kindler
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-2.1-or-later OR GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ads_globals.h"
|
||||
|
||||
#include "autohidetab.h"
|
||||
|
||||
#include <QScrollArea>
|
||||
|
||||
class QXmlStreamWriter;
|
||||
|
||||
namespace ADS {
|
||||
|
||||
struct AutoHideSideBarPrivate;
|
||||
class DockContainerWidgetPrivate;
|
||||
class DockContainerWidget;
|
||||
class AutoHideTab;
|
||||
class AutoHideDockContainer;
|
||||
class DockingStateReader;
|
||||
|
||||
/**
|
||||
* Side tab bar widget that is shown at the edges of a dock container.
|
||||
* The tab bar is only visible, if it contains visible content, that means if
|
||||
* it contains visible tabs. If it is empty or all tabs are hidden, then the
|
||||
* side bar is also hidden. As soon as one single tab becomes visible, this
|
||||
* tab bar will be shown.
|
||||
* The CAutoHideSideBar uses a QScrollArea here, to enable proper resizing.
|
||||
* If the side bar contains many tabs, then the tabs are simply clipped - this
|
||||
* is the same like in visual studio
|
||||
*/
|
||||
class ADS_EXPORT AutoHideSideBar : public QScrollArea
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int sideBarLocation READ sideBarLocation)
|
||||
Q_PROPERTY(Qt::Orientation orientation READ orientation)
|
||||
Q_PROPERTY(int spacing READ spacing WRITE setSpacing)
|
||||
|
||||
private:
|
||||
AutoHideSideBarPrivate* d; ///< private data (pimpl)
|
||||
friend struct AutoHideSideBarPrivate;
|
||||
friend class DockWidgetSideTab;
|
||||
friend DockContainerWidgetPrivate;
|
||||
friend DockContainerWidget;
|
||||
|
||||
protected:
|
||||
virtual bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
|
||||
/**
|
||||
* Saves the state into the given stream
|
||||
*/
|
||||
void saveState(QXmlStreamWriter &stream) const;
|
||||
|
||||
/**
|
||||
* Inserts the given dock widget tab at the given position.
|
||||
* An Index value of -1 appends the side tab at the end.
|
||||
*/
|
||||
void insertTab(int index, AutoHideTab *sideTab);
|
||||
|
||||
public:
|
||||
using Super = QScrollArea;
|
||||
|
||||
/**
|
||||
* Default Constructor
|
||||
*/
|
||||
AutoHideSideBar(DockContainerWidget *parent, SideBarLocation area);
|
||||
|
||||
/**
|
||||
* Virtual Destructor
|
||||
*/
|
||||
virtual ~AutoHideSideBar();
|
||||
|
||||
/**
|
||||
* Removes the given DockWidgetSideTab from the tabbar
|
||||
*/
|
||||
void removeTab(AutoHideTab *sideTab);
|
||||
|
||||
/**
|
||||
* Insert dock widget into the side bar. The function creates the auto hide dock container,
|
||||
* inserts the auto hide tab
|
||||
*/
|
||||
AutoHideDockContainer *insertDockWidget(int index, DockWidget *dockWidget);
|
||||
|
||||
/**
|
||||
* Removes the auto hide widget from this side bar
|
||||
*/
|
||||
void removeAutoHideWidget(AutoHideDockContainer *autoHideWidget);
|
||||
|
||||
/**
|
||||
* Adds the given AutoHideWidget to this sidebar. If the AutoHideWidget is in another sidebar,
|
||||
* then it will be removed from this sidebar.
|
||||
*/
|
||||
void addAutoHideWidget(AutoHideDockContainer *autoHideWidget, int index = TabDefaultInsertIndex);
|
||||
|
||||
/**
|
||||
* Returns orientation of side tab.
|
||||
*/
|
||||
Qt::Orientation orientation() const;
|
||||
|
||||
/**
|
||||
* Get the side tab widget at position, returns nullptr if it's out of bounds.
|
||||
*/
|
||||
AutoHideTab *tab(int index) const;
|
||||
|
||||
/**
|
||||
* Returns the tab at the given position.
|
||||
* Returns -1 if the position is left of the first tab and count() if the position is right
|
||||
* of the last tab. Returns InvalidTabIndex (-2) to indicate an invalid value.
|
||||
*/
|
||||
int tabAt(const QPoint &pos) const;
|
||||
|
||||
/**
|
||||
* Returns the tab insertion index for the given mouse cursor position.
|
||||
*/
|
||||
int tabInsertIndexAt(const QPoint &pos) const;
|
||||
|
||||
/**
|
||||
* Returns the index of the given tab.
|
||||
*/
|
||||
int indexOfTab(const AutoHideTab &tab) const;
|
||||
|
||||
/**
|
||||
* Gets the count of the tab widgets.
|
||||
*/
|
||||
int count() const;
|
||||
|
||||
/**
|
||||
* Returns the number of visible tabs to its parent widget.
|
||||
*/
|
||||
int visibleTabCount() const;
|
||||
|
||||
/**
|
||||
* Returns true, if the sidebar contains visible tabs to its parent widget.
|
||||
* The function returns as soon as it finds the first visible tab. That means, if you just want
|
||||
* to find out if there are visible tabs then this function is quicker than visibleTabCount().
|
||||
*/
|
||||
bool hasVisibleTabs() const;
|
||||
|
||||
/**
|
||||
* Getter for side tab bar area property
|
||||
*/
|
||||
SideBarLocation sideBarLocation() const;
|
||||
|
||||
/**
|
||||
* Overrides the minimumSizeHint() function of QScrollArea
|
||||
* The minimumSizeHint() is bigger than the sizeHint () for the scroll
|
||||
* area because even if the scrollbars are invisible, the required speace
|
||||
* is reserved in the minimumSizeHint(). This override simply returns sizeHint();
|
||||
*/
|
||||
virtual QSize minimumSizeHint() const override;
|
||||
|
||||
/**
|
||||
* The function provides a sizeHint that matches the height of the internal viewport.
|
||||
*/
|
||||
virtual QSize sizeHint() const override;
|
||||
|
||||
/**
|
||||
* Getter for spacing property - returns the spacing of the tabs
|
||||
*/
|
||||
int spacing() const;
|
||||
|
||||
/**
|
||||
* Setter for spacing property - sets the spacing
|
||||
*/
|
||||
void setSpacing(int spacing);
|
||||
|
||||
/**
|
||||
* Returns the dock container that hosts this sideBar()
|
||||
*/
|
||||
DockContainerWidget *dockContainer() const;
|
||||
};
|
||||
|
||||
} // namespace ADS
|
464
src/libs/advanceddockingsystem/autohidetab.cpp
Normal file
464
src/libs/advanceddockingsystem/autohidetab.cpp
Normal file
@@ -0,0 +1,464 @@
|
||||
// Copyright (C) 2020 Uwe Kindler
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-2.1-or-later OR GPL-3.0-or-later
|
||||
|
||||
#include "autohidetab.h"
|
||||
|
||||
#include "ads_globals_p.h"
|
||||
#include "autohidedockcontainer.h"
|
||||
#include "autohidesidebar.h"
|
||||
#include "dockareawidget.h"
|
||||
#include "dockmanager.h"
|
||||
#include "dockoverlay.h"
|
||||
#include "dockwidget.h"
|
||||
#include "floatingdragpreview.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QBoxLayout>
|
||||
#include <QContextMenuEvent>
|
||||
#include <QElapsedTimer>
|
||||
#include <QMenu>
|
||||
|
||||
namespace ADS {
|
||||
|
||||
static const char *const g_locationProperty = "Location";
|
||||
|
||||
/**
|
||||
* Private data class of CDockWidgetTab class (pimpl)
|
||||
*/
|
||||
struct AutoHideTabPrivate
|
||||
{
|
||||
AutoHideTab *q;
|
||||
DockWidget *m_dockWidget = nullptr;
|
||||
AutoHideSideBar *m_sideBar = nullptr;
|
||||
Qt::Orientation m_orientation{Qt::Vertical};
|
||||
QElapsedTimer m_timeSinceHoverMousePress;
|
||||
bool m_mousePressed = false;
|
||||
eDragState m_dragState = DraggingInactive;
|
||||
QPoint m_globalDragStartMousePosition;
|
||||
QPoint m_dragStartMousePosition;
|
||||
AbstractFloatingWidget *m_floatingWidget = nullptr;
|
||||
Qt::Orientation m_dragStartOrientation;
|
||||
|
||||
/**
|
||||
* Private data constructor
|
||||
*/
|
||||
AutoHideTabPrivate(AutoHideTab *parent);
|
||||
|
||||
/**
|
||||
* Update the orientation, visibility and spacing based on the area of the side bar
|
||||
*/
|
||||
void updateOrientation();
|
||||
|
||||
/**
|
||||
* Convenience function to ease dock container access
|
||||
*/
|
||||
DockContainerWidget *dockContainer() const
|
||||
{
|
||||
return m_dockWidget ? m_dockWidget->dockContainer() : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forward this event to the dock container
|
||||
*/
|
||||
void forwardEventToDockContainer(QEvent *event)
|
||||
{
|
||||
auto container = dockContainer();
|
||||
if (container)
|
||||
container->handleAutoHideWidgetEvent(event, q);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create and initialize the menu entries for the "Auto Hide Group To..." menu.
|
||||
*/
|
||||
QAction *createAutoHideToAction(const QString &title, SideBarLocation location, QMenu *menu)
|
||||
{
|
||||
auto action = menu->addAction(title);
|
||||
action->setProperty("Location", location);
|
||||
QObject::connect(action, &QAction::triggered, q, &AutoHideTab::onAutoHideToActionClicked);
|
||||
action->setEnabled(location != q->sideBarLocation());
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test function for current drag state.
|
||||
*/
|
||||
bool isDraggingState(eDragState dragState) const { return m_dragState == dragState; }
|
||||
|
||||
/**
|
||||
* Saves the drag start position in global and local coordinates.
|
||||
*/
|
||||
void saveDragStartMousePosition(const QPoint &globalPos)
|
||||
{
|
||||
m_globalDragStartMousePosition = globalPos;
|
||||
m_dragStartMousePosition = q->mapFromGlobal(globalPos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts floating of the dock widget that belongs to this title bar
|
||||
* Returns true, if floating has been started and false if floating is not possible for any reason.
|
||||
*/
|
||||
bool startFloating(eDragState draggingState = DraggingFloatingWidget);
|
||||
|
||||
template<typename T>
|
||||
AbstractFloatingWidget *createFloatingWidget(T *widget)
|
||||
{
|
||||
auto w = new FloatingDragPreview(widget);
|
||||
q->connect(w, &FloatingDragPreview::draggingCanceled, [=]() {
|
||||
m_dragState = DraggingInactive;
|
||||
});
|
||||
return w;
|
||||
}
|
||||
}; // struct DockWidgetTabPrivate
|
||||
|
||||
AutoHideTabPrivate::AutoHideTabPrivate(AutoHideTab *parent)
|
||||
: q(parent)
|
||||
{}
|
||||
|
||||
void AutoHideTabPrivate::updateOrientation()
|
||||
{
|
||||
bool iconOnly = DockManager::testAutoHideConfigFlag(DockManager::AutoHideSideBarsIconOnly);
|
||||
if (iconOnly && !q->icon().isNull()) {
|
||||
q->setText("");
|
||||
q->setOrientation(Qt::Horizontal);
|
||||
} else {
|
||||
auto area = m_sideBar->sideBarLocation();
|
||||
q->setOrientation((area == SideBarBottom || area == SideBarTop) ? Qt::Horizontal
|
||||
: Qt::Vertical);
|
||||
}
|
||||
}
|
||||
|
||||
bool AutoHideTabPrivate::startFloating(eDragState draggingState)
|
||||
{
|
||||
auto dockArea = m_dockWidget->dockAreaWidget();
|
||||
qCInfo(adsLog) << Q_FUNC_INFO << "isFloating " << dockContainer()->isFloating();
|
||||
|
||||
m_dragState = draggingState;
|
||||
AbstractFloatingWidget *floatingWidget = nullptr;
|
||||
floatingWidget = createFloatingWidget(dockArea);
|
||||
auto size = dockArea->size();
|
||||
auto startPos = m_dragStartMousePosition;
|
||||
auto autoHideContainer = m_dockWidget->autoHideDockContainer();
|
||||
m_dragStartOrientation = autoHideContainer->orientation();
|
||||
|
||||
switch (m_sideBar->sideBarLocation()) {
|
||||
case SideBarLeft:
|
||||
startPos.rx() = autoHideContainer->rect().left() + 10;
|
||||
break;
|
||||
|
||||
case SideBarRight:
|
||||
startPos.rx() = autoHideContainer->rect().right() - 10;
|
||||
break;
|
||||
|
||||
case SideBarTop:
|
||||
startPos.ry() = autoHideContainer->rect().top() + 10;
|
||||
break;
|
||||
|
||||
case SideBarBottom:
|
||||
startPos.ry() = autoHideContainer->rect().bottom() - 10;
|
||||
break;
|
||||
|
||||
case SideBarNone:
|
||||
return false;
|
||||
}
|
||||
floatingWidget->startFloating(startPos, size, DraggingFloatingWidget, q);
|
||||
auto dockManager = m_dockWidget->dockManager();
|
||||
auto overlay = dockManager->containerOverlay();
|
||||
overlay->setAllowedAreas(OuterDockAreas);
|
||||
m_floatingWidget = floatingWidget;
|
||||
qApp->postEvent(m_dockWidget, new QEvent((QEvent::Type) internal::g_dockedWidgetDragStartEvent));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
AutoHideTab::AutoHideTab(QWidget *parent)
|
||||
: PushButton(parent)
|
||||
, d(new AutoHideTabPrivate(this))
|
||||
{
|
||||
setAttribute(Qt::WA_NoMousePropagation);
|
||||
setFocusPolicy(Qt::NoFocus);
|
||||
}
|
||||
|
||||
AutoHideTab::~AutoHideTab()
|
||||
{
|
||||
qCInfo(adsLog) << Q_FUNC_INFO;
|
||||
delete d;
|
||||
}
|
||||
|
||||
void AutoHideTab::updateStyle()
|
||||
{
|
||||
internal::repolishStyle(this, internal::RepolishDirectChildren);
|
||||
update();
|
||||
}
|
||||
|
||||
SideBarLocation AutoHideTab::sideBarLocation() const
|
||||
{
|
||||
if (d->m_sideBar)
|
||||
return d->m_sideBar->sideBarLocation();
|
||||
|
||||
return SideBarLeft;
|
||||
}
|
||||
|
||||
void AutoHideTab::setOrientation(Qt::Orientation value)
|
||||
{
|
||||
d->m_orientation = value;
|
||||
if (orientation() == Qt::Horizontal) {
|
||||
setMinimumWidth(100);
|
||||
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
|
||||
} else {
|
||||
setMinimumHeight(100);
|
||||
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
|
||||
}
|
||||
|
||||
PushButton::setButtonOrientation((Qt::Horizontal == value) ? PushButton::Horizontal
|
||||
: PushButton::VerticalTopToBottom);
|
||||
updateStyle();
|
||||
}
|
||||
|
||||
Qt::Orientation AutoHideTab::orientation() const
|
||||
{
|
||||
return d->m_orientation;
|
||||
}
|
||||
|
||||
bool AutoHideTab::isActiveTab() const
|
||||
{
|
||||
if (d->m_dockWidget && d->m_dockWidget->autoHideDockContainer())
|
||||
return d->m_dockWidget->autoHideDockContainer()->isVisible();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
DockWidget *AutoHideTab::dockWidget() const
|
||||
{
|
||||
return d->m_dockWidget;
|
||||
}
|
||||
|
||||
void AutoHideTab::setDockWidget(DockWidget *dockWidget)
|
||||
{
|
||||
if (!dockWidget)
|
||||
return;
|
||||
|
||||
d->m_dockWidget = dockWidget;
|
||||
setText(dockWidget->windowTitle());
|
||||
setIcon(d->m_dockWidget->icon());
|
||||
setToolTip(dockWidget->windowTitle());
|
||||
}
|
||||
bool AutoHideTab::iconOnly() const
|
||||
{
|
||||
return DockManager::testAutoHideConfigFlag(DockManager::AutoHideSideBarsIconOnly)
|
||||
&& !icon().isNull();
|
||||
}
|
||||
|
||||
AutoHideSideBar *AutoHideTab::sideBar() const
|
||||
{
|
||||
return d->m_sideBar;
|
||||
}
|
||||
|
||||
int AutoHideTab::tabIndex() const
|
||||
{
|
||||
if (!d->m_sideBar)
|
||||
return -1;
|
||||
|
||||
return d->m_sideBar->indexOfTab(*this);
|
||||
}
|
||||
|
||||
void AutoHideTab::setDockWidgetFloating()
|
||||
{
|
||||
d->m_dockWidget->setFloating();
|
||||
}
|
||||
|
||||
void AutoHideTab::unpinDockWidget()
|
||||
{
|
||||
d->m_dockWidget->setAutoHide(false);
|
||||
}
|
||||
|
||||
void AutoHideTab::requestCloseDockWidget()
|
||||
{
|
||||
d->m_dockWidget->requestCloseDockWidget();
|
||||
}
|
||||
|
||||
void AutoHideTab::onAutoHideToActionClicked()
|
||||
{
|
||||
int location = sender()->property(g_locationProperty).toInt();
|
||||
d->m_dockWidget->setAutoHide(true, (SideBarLocation) location);
|
||||
}
|
||||
|
||||
void AutoHideTab::setSideBar(AutoHideSideBar *sideTabBar)
|
||||
{
|
||||
d->m_sideBar = sideTabBar;
|
||||
if (d->m_sideBar)
|
||||
d->updateOrientation();
|
||||
}
|
||||
|
||||
void AutoHideTab::removeFromSideBar()
|
||||
{
|
||||
if (d->m_sideBar == nullptr)
|
||||
return;
|
||||
|
||||
d->m_sideBar->removeTab(this);
|
||||
setSideBar(nullptr);
|
||||
}
|
||||
|
||||
bool AutoHideTab::event(QEvent *event)
|
||||
{
|
||||
if (!DockManager::testAutoHideConfigFlag(DockManager::AutoHideShowOnMouseOver))
|
||||
return Super::event(event);
|
||||
|
||||
switch (event->type()) {
|
||||
case QEvent::Enter:
|
||||
case QEvent::Leave:
|
||||
d->forwardEventToDockContainer(event);
|
||||
break;
|
||||
|
||||
case QEvent::MouseButtonPress:
|
||||
// If AutoHideShowOnMouseOver is active, then the showing is triggered by a MousePressEvent
|
||||
// sent to this tab. To prevent accidental hiding of the tab by a mouse click, we wait at
|
||||
// least 500 ms before we accept the mouse click.
|
||||
if (!event->spontaneous()) {
|
||||
d->m_timeSinceHoverMousePress.restart();
|
||||
d->forwardEventToDockContainer(event);
|
||||
} else if (d->m_timeSinceHoverMousePress.hasExpired(500)) {
|
||||
d->forwardEventToDockContainer(event);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return Super::event(event);
|
||||
}
|
||||
|
||||
void AutoHideTab::contextMenuEvent(QContextMenuEvent *event)
|
||||
{
|
||||
event->accept();
|
||||
d->saveDragStartMousePosition(event->globalPos());
|
||||
|
||||
const bool isFloatable = d->m_dockWidget->features().testFlag(DockWidget::DockWidgetFloatable);
|
||||
QMenu menu(this);
|
||||
|
||||
QAction *detachAction = menu.addAction(tr("Detach"));
|
||||
detachAction->connect(detachAction,
|
||||
&QAction::triggered,
|
||||
this,
|
||||
&AutoHideTab::setDockWidgetFloating);
|
||||
detachAction->setEnabled(isFloatable);
|
||||
|
||||
auto isPinnable = d->m_dockWidget->features().testFlag(DockWidget::DockWidgetPinnable);
|
||||
detachAction->setEnabled(isPinnable);
|
||||
|
||||
auto pinToMenu = menu.addMenu(tr("Pin To..."));
|
||||
pinToMenu->setEnabled(isPinnable);
|
||||
d->createAutoHideToAction(tr("Top"), SideBarTop, pinToMenu);
|
||||
d->createAutoHideToAction(tr("Left"), SideBarLeft, pinToMenu);
|
||||
d->createAutoHideToAction(tr("Right"), SideBarRight, pinToMenu);
|
||||
d->createAutoHideToAction(tr("Bottom"), SideBarBottom, pinToMenu);
|
||||
|
||||
QAction *unpinAction = menu.addAction(tr("Unpin (Dock)"));
|
||||
unpinAction->connect(unpinAction, &QAction::triggered, this, &AutoHideTab::unpinDockWidget);
|
||||
menu.addSeparator();
|
||||
QAction *closeAction = menu.addAction(tr("Close"));
|
||||
closeAction->connect(closeAction,
|
||||
&QAction::triggered,
|
||||
this,
|
||||
&AutoHideTab::requestCloseDockWidget);
|
||||
closeAction->setEnabled(d->m_dockWidget->features().testFlag(DockWidget::DockWidgetClosable));
|
||||
|
||||
menu.exec(event->globalPos());
|
||||
}
|
||||
|
||||
void AutoHideTab::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
// If AutoHideShowOnMouseOver is active, then the showing is triggered by a MousePressEvent
|
||||
// sent to this tab. To prevent accidental hiding of the tab by a mouse click, we wait at
|
||||
// least 500 ms before we accept the mouse click.
|
||||
if (!event->spontaneous()) {
|
||||
d->m_timeSinceHoverMousePress.restart();
|
||||
d->forwardEventToDockContainer(event);
|
||||
} else if (d->m_timeSinceHoverMousePress.hasExpired(500)) {
|
||||
d->forwardEventToDockContainer(event);
|
||||
}
|
||||
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
event->accept();
|
||||
d->m_mousePressed = true;
|
||||
d->saveDragStartMousePosition(event->globalPosition().toPoint());
|
||||
d->m_dragState = DraggingMousePressed;
|
||||
}
|
||||
|
||||
Super::mousePressEvent(event);
|
||||
}
|
||||
|
||||
void AutoHideTab::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
d->m_mousePressed = false;
|
||||
auto currentDragState = d->m_dragState;
|
||||
d->m_globalDragStartMousePosition = QPoint();
|
||||
d->m_dragStartMousePosition = QPoint();
|
||||
d->m_dragState = DraggingInactive;
|
||||
|
||||
switch (currentDragState) {
|
||||
case DraggingTab:
|
||||
// End of tab moving, emit signal
|
||||
//if (d->DockArea) {
|
||||
// event->accept();
|
||||
// Q_EMIT moved(internal::globalPositionOf(event));
|
||||
//}
|
||||
break;
|
||||
|
||||
case DraggingFloatingWidget:
|
||||
event->accept();
|
||||
d->m_floatingWidget->finishDragging();
|
||||
if (d->m_dockWidget->isAutoHide() && d->m_dragStartOrientation != orientation())
|
||||
d->m_dockWidget->autoHideDockContainer()->resetToInitialDockWidgetSize();
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break; // do nothing
|
||||
}
|
||||
}
|
||||
|
||||
Super::mouseReleaseEvent(event);
|
||||
}
|
||||
|
||||
void AutoHideTab::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
if (!(event->buttons() & Qt::LeftButton) || d->isDraggingState(DraggingInactive)) {
|
||||
d->m_dragState = DraggingInactive;
|
||||
Super::mouseMoveEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
// Move floating window
|
||||
if (d->isDraggingState(DraggingFloatingWidget)) {
|
||||
d->m_floatingWidget->moveFloating();
|
||||
Super::mouseMoveEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
// move tab
|
||||
if (d->isDraggingState(DraggingTab)) {
|
||||
// Moving the tab is always allowed because it does not mean moving the dock widget around.
|
||||
//d->moveTab(ev);
|
||||
}
|
||||
|
||||
auto mappedPos = mapToParent(event->pos());
|
||||
bool mouseOutsideBar = (mappedPos.x() < 0) || (mappedPos.x() > parentWidget()->rect().right());
|
||||
// Maybe a fixed drag distance is better here ?
|
||||
int dragDistanceY = qAbs(d->m_globalDragStartMousePosition.y()
|
||||
- event->globalPosition().toPoint().y());
|
||||
if (dragDistanceY >= DockManager::startDragDistance() || mouseOutsideBar) {
|
||||
// Floating is only allowed for widgets that are floatable
|
||||
// We can create the drag preview if the widget is movable.
|
||||
auto Features = d->m_dockWidget->features();
|
||||
if (Features.testFlag(DockWidget::DockWidgetFloatable)
|
||||
|| (Features.testFlag(DockWidget::DockWidgetMovable))) {
|
||||
d->startFloating();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Super::mouseMoveEvent(event);
|
||||
}
|
||||
|
||||
} // namespace ADS
|
133
src/libs/advanceddockingsystem/autohidetab.h
Normal file
133
src/libs/advanceddockingsystem/autohidetab.h
Normal file
@@ -0,0 +1,133 @@
|
||||
// Copyright (C) 2020 Uwe Kindler
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-2.1-or-later OR GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "pushbutton.h"
|
||||
|
||||
#include "ads_globals.h"
|
||||
|
||||
namespace ADS {
|
||||
|
||||
struct AutoHideTabPrivate;
|
||||
class DockWidget;
|
||||
class AutoHideSideBar;
|
||||
class DockWidgetTab;
|
||||
class DockContainerWidgetPrivate;
|
||||
|
||||
/**
|
||||
* A dock widget Side tab that shows a title or an icon.
|
||||
* The dock widget tab is shown in the side tab bar to switch between pinned dock widgets.
|
||||
*/
|
||||
class ADS_EXPORT AutoHideTab : public PushButton
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(int sideBarLocation READ sideBarLocation CONSTANT)
|
||||
Q_PROPERTY(Qt::Orientation orientation READ orientation CONSTANT)
|
||||
Q_PROPERTY(bool activeTab READ isActiveTab CONSTANT)
|
||||
Q_PROPERTY(bool iconOnly READ iconOnly CONSTANT)
|
||||
|
||||
private:
|
||||
AutoHideTabPrivate *d; ///< private data (pimpl)
|
||||
friend struct AutoHideTabPrivate;
|
||||
friend class DockWidget;
|
||||
friend class AutoHideDockContainer;
|
||||
friend class AutoHideSideBar;
|
||||
friend class DockAreaWidget;
|
||||
friend class DockContainerWidget;
|
||||
friend DockContainerWidgetPrivate;
|
||||
|
||||
void onAutoHideToActionClicked();
|
||||
|
||||
protected:
|
||||
void setSideBar(AutoHideSideBar *sideTabBar);
|
||||
void removeFromSideBar();
|
||||
virtual bool event(QEvent *event) override;
|
||||
virtual void contextMenuEvent(QContextMenuEvent *event) override;
|
||||
virtual void mousePressEvent(QMouseEvent *event) override;
|
||||
virtual void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
virtual void mouseMoveEvent(QMouseEvent *event) override;
|
||||
|
||||
public:
|
||||
using Super = PushButton;
|
||||
|
||||
/**
|
||||
* Default Constructor
|
||||
* param[in] parent The parent widget of this title bar
|
||||
*/
|
||||
AutoHideTab(QWidget *parent = nullptr);
|
||||
|
||||
/**
|
||||
* Virtual Destructor
|
||||
*/
|
||||
virtual ~AutoHideTab();
|
||||
|
||||
/**
|
||||
* Update stylesheet style if a property changes.
|
||||
*/
|
||||
void updateStyle();
|
||||
|
||||
/**
|
||||
* Getter for side tab bar area property.
|
||||
*/
|
||||
SideBarLocation sideBarLocation() const;
|
||||
|
||||
/**
|
||||
* Set orientation vertical or horizontal.
|
||||
*/
|
||||
void setOrientation(Qt::Orientation value);
|
||||
|
||||
/**
|
||||
* Returns the current orientation.
|
||||
*/
|
||||
Qt::Orientation orientation() const;
|
||||
|
||||
/**
|
||||
* Returns true, if this is the active tab. The tab is active if the auto hide widget is visible.
|
||||
*/
|
||||
bool isActiveTab() const;
|
||||
|
||||
/**
|
||||
* Returns the dock widget this belongs to.
|
||||
*/
|
||||
DockWidget *dockWidget() const;
|
||||
|
||||
/**
|
||||
* Sets the dock widget that is controlled by this tab.
|
||||
*/
|
||||
void setDockWidget(DockWidget *dockWidget);
|
||||
|
||||
/**
|
||||
* Returns true if the auto hide config flag AutoHideSideBarsIconOnly is set and if
|
||||
* the tab has an icon - that means the icon is not null.
|
||||
*/
|
||||
bool iconOnly() const;
|
||||
|
||||
/**
|
||||
* Returns the side bar that contains this tab or a nullptr if the tab is not in a side bar.
|
||||
*/
|
||||
AutoHideSideBar *sideBar() const;
|
||||
|
||||
/**
|
||||
* Returns the index of this tab in the sideBar.
|
||||
*/
|
||||
int tabIndex() const;
|
||||
|
||||
/**
|
||||
* Set the dock widget floating, if it is floatable
|
||||
*/
|
||||
void setDockWidgetFloating();
|
||||
|
||||
/**
|
||||
* Unpin and dock the auto hide widget.
|
||||
*/
|
||||
void unpinDockWidget();
|
||||
|
||||
/**
|
||||
* Calls the requestCloseDockWidget() function for the assigned dock widget.
|
||||
*/
|
||||
void requestCloseDockWidget();
|
||||
}; // class AutoHideTab
|
||||
|
||||
} // namespace ADS
|
@@ -13,340 +13,378 @@
|
||||
#include <QLoggingCategory>
|
||||
#include <QMouseEvent>
|
||||
#include <QScrollBar>
|
||||
#include <QTimer>
|
||||
#include <QtGlobal>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace ADS
|
||||
namespace ADS {
|
||||
/**
|
||||
* Private data class of DockAreaTabBar class (pimpl)
|
||||
*/
|
||||
class DockAreaTabBarPrivate
|
||||
{
|
||||
public:
|
||||
DockAreaTabBar *q;
|
||||
DockAreaWidget *m_dockArea = nullptr;
|
||||
QWidget *m_tabsContainerWidget = nullptr;
|
||||
QBoxLayout *m_tabsLayout = nullptr;
|
||||
int m_currentIndex = -1;
|
||||
|
||||
/**
|
||||
* Private data class of DockAreaTabBar class (pimpl)
|
||||
* Private data constructor
|
||||
*/
|
||||
class DockAreaTabBarPrivate
|
||||
{
|
||||
public:
|
||||
DockAreaTabBar *q;
|
||||
DockAreaWidget *m_dockArea = nullptr;
|
||||
QWidget *m_tabsContainerWidget = nullptr;
|
||||
QBoxLayout *m_tabsLayout = nullptr;
|
||||
int m_currentIndex = -1;
|
||||
DockAreaTabBarPrivate(DockAreaTabBar *parent);
|
||||
|
||||
/**
|
||||
* Private data constructor
|
||||
*/
|
||||
DockAreaTabBarPrivate(DockAreaTabBar *parent);
|
||||
/**
|
||||
* Update tabs after current index changed or when tabs are removed.
|
||||
* The function reassigns the stylesheet to update the tabs.
|
||||
*/
|
||||
void updateTabs();
|
||||
|
||||
/**
|
||||
* Update tabs after current index changed or when tabs are removed.
|
||||
* The function reassigns the stylesheet to update the tabs
|
||||
*/
|
||||
void updateTabs();
|
||||
/**
|
||||
* Convenience function to access first tab.
|
||||
*/
|
||||
DockWidgetTab *firstTab() const { return q->tab(0); }
|
||||
|
||||
/**
|
||||
* Convenience function to access first tab
|
||||
*/
|
||||
DockWidgetTab *firstTab() const { return q->tab(0); }
|
||||
/**
|
||||
* Convenience function to access last tab.
|
||||
*/
|
||||
DockWidgetTab *lastTab() const { return q->tab(q->count() - 1); }
|
||||
}; // class DockAreaTabBarPrivate
|
||||
|
||||
/**
|
||||
* Convenience function to access last tab
|
||||
*/
|
||||
DockWidgetTab *lastTab() const { return q->tab(q->count() - 1); }
|
||||
}; // class DockAreaTabBarPrivate
|
||||
DockAreaTabBarPrivate::DockAreaTabBarPrivate(DockAreaTabBar *parent)
|
||||
: q(parent)
|
||||
{}
|
||||
|
||||
DockAreaTabBarPrivate::DockAreaTabBarPrivate(DockAreaTabBar *parent)
|
||||
: q(parent)
|
||||
{}
|
||||
void DockAreaTabBarPrivate::updateTabs()
|
||||
{
|
||||
// Set active TAB and update all other tabs to be inactive
|
||||
for (int i = 0; i < q->count(); ++i) {
|
||||
auto tabWidget = q->tab(i);
|
||||
if (!tabWidget)
|
||||
continue;
|
||||
|
||||
void DockAreaTabBarPrivate::updateTabs()
|
||||
{
|
||||
// Set active TAB and update all other tabs to be inactive
|
||||
for (int i = 0; i < q->count(); ++i) {
|
||||
auto tabWidget = q->tab(i);
|
||||
if (!tabWidget)
|
||||
continue;
|
||||
if (i == m_currentIndex) {
|
||||
tabWidget->show();
|
||||
tabWidget->setActiveTab(true);
|
||||
// Sometimes the synchronous calculation of the rectangular area fails. Therefore we
|
||||
// use QTimer::singleShot here to execute the call within the event loop - see #520.
|
||||
QTimer::singleShot(0, q, [&, tabWidget] { q->ensureWidgetVisible(tabWidget); });
|
||||
} else {
|
||||
tabWidget->setActiveTab(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i == m_currentIndex) {
|
||||
tabWidget->show();
|
||||
tabWidget->setActiveTab(true);
|
||||
q->ensureWidgetVisible(tabWidget);
|
||||
} else {
|
||||
tabWidget->setActiveTab(false);
|
||||
DockAreaTabBar::DockAreaTabBar(DockAreaWidget *parent)
|
||||
: QScrollArea(parent)
|
||||
, d(new DockAreaTabBarPrivate(this))
|
||||
{
|
||||
d->m_dockArea = parent;
|
||||
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
||||
setFrameStyle(QFrame::NoFrame);
|
||||
setWidgetResizable(true);
|
||||
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
|
||||
d->m_tabsContainerWidget = new QWidget();
|
||||
d->m_tabsContainerWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
||||
d->m_tabsContainerWidget->setObjectName("tabsContainerWidget");
|
||||
d->m_tabsLayout = new QBoxLayout(QBoxLayout::LeftToRight);
|
||||
d->m_tabsLayout->setContentsMargins(0, 0, 0, 0);
|
||||
d->m_tabsLayout->setSpacing(0);
|
||||
d->m_tabsLayout->addStretch(1);
|
||||
d->m_tabsContainerWidget->setLayout(d->m_tabsLayout);
|
||||
setWidget(d->m_tabsContainerWidget);
|
||||
}
|
||||
|
||||
DockAreaTabBar::~DockAreaTabBar()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
void DockAreaTabBar::onTabClicked(DockWidgetTab *sourceTab)
|
||||
{
|
||||
const int index = d->m_tabsLayout->indexOf(sourceTab);
|
||||
if (index < 0)
|
||||
return;
|
||||
|
||||
setCurrentIndex(index);
|
||||
emit tabBarClicked(index);
|
||||
}
|
||||
|
||||
void DockAreaTabBar::onTabCloseRequested(DockWidgetTab *sourceTab)
|
||||
{
|
||||
const int index = d->m_tabsLayout->indexOf(sourceTab);
|
||||
closeTab(index);
|
||||
}
|
||||
|
||||
void DockAreaTabBar::onCloseOtherTabsRequested(DockWidgetTab *sourceTab)
|
||||
{
|
||||
for (int i = 0; i < count(); ++i) {
|
||||
auto currentTab = tab(i);
|
||||
if (currentTab->isClosable() && !currentTab->isHidden() && currentTab != sourceTab) {
|
||||
// If the dock widget is deleted with the closeTab() call, its tab it will no longer
|
||||
// be in the layout, and thus the index needs to be updated to not skip any tabs.
|
||||
int offset = currentTab->dockWidget()->features().testFlag(
|
||||
DockWidget::DockWidgetDeleteOnClose)
|
||||
? 1
|
||||
: 0;
|
||||
closeTab(i);
|
||||
// If the the dock widget blocks closing, i.e. if the flag CustomCloseHandling is set,
|
||||
// and the dock widget is still open, then we do not need to correct the index.
|
||||
if (currentTab->dockWidget()->isClosed())
|
||||
i -= offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DockAreaTabBar::onTabWidgetMoved(DockWidgetTab *sourceTab, const QPoint &globalPosition)
|
||||
{
|
||||
const int fromIndex = d->m_tabsLayout->indexOf(sourceTab);
|
||||
auto mousePos = mapFromGlobal(globalPosition);
|
||||
mousePos.rx() = qMax(d->firstTab()->geometry().left(), mousePos.x());
|
||||
mousePos.rx() = qMin(d->lastTab()->geometry().right(), mousePos.x());
|
||||
int toIndex = -1;
|
||||
// Find tab under mouse
|
||||
for (int i = 0; i < count(); ++i) {
|
||||
DockWidgetTab *dropTab = tab(i);
|
||||
if (dropTab == sourceTab || !dropTab->isVisibleTo(this)
|
||||
|| !dropTab->geometry().contains(mousePos))
|
||||
continue;
|
||||
|
||||
toIndex = d->m_tabsLayout->indexOf(dropTab);
|
||||
if (toIndex == fromIndex)
|
||||
toIndex = -1;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (toIndex > -1) {
|
||||
d->m_tabsLayout->removeWidget(sourceTab);
|
||||
d->m_tabsLayout->insertWidget(toIndex, sourceTab);
|
||||
qCInfo(adsLog) << "tabMoved from" << fromIndex << "to" << toIndex;
|
||||
emit tabMoved(fromIndex, toIndex);
|
||||
setCurrentIndex(toIndex);
|
||||
} else {
|
||||
// Ensure that the moved tab is reset to its start position
|
||||
d->m_tabsLayout->update();
|
||||
}
|
||||
}
|
||||
|
||||
void DockAreaTabBar::wheelEvent(QWheelEvent *event)
|
||||
{
|
||||
event->accept();
|
||||
const int direction = event->angleDelta().y();
|
||||
if (direction < 0)
|
||||
horizontalScrollBar()->setValue(horizontalScrollBar()->value() + 20);
|
||||
else
|
||||
horizontalScrollBar()->setValue(horizontalScrollBar()->value() - 20);
|
||||
}
|
||||
|
||||
int DockAreaTabBar::count() const
|
||||
{
|
||||
// The tab bar contains a stretch item as last item
|
||||
return d->m_tabsLayout->count() - 1;
|
||||
}
|
||||
|
||||
void DockAreaTabBar::insertTab(int index, DockWidgetTab *dockWidgetTab)
|
||||
{
|
||||
d->m_tabsLayout->insertWidget(index, dockWidgetTab);
|
||||
connect(dockWidgetTab, &DockWidgetTab::clicked, this, [this, dockWidgetTab] {
|
||||
onTabClicked(dockWidgetTab);
|
||||
});
|
||||
connect(dockWidgetTab, &DockWidgetTab::closeRequested, this, [this, dockWidgetTab] {
|
||||
onTabCloseRequested(dockWidgetTab);
|
||||
});
|
||||
connect(dockWidgetTab, &DockWidgetTab::closeOtherTabsRequested, this, [this, dockWidgetTab] {
|
||||
onCloseOtherTabsRequested(dockWidgetTab);
|
||||
});
|
||||
connect(dockWidgetTab,
|
||||
&DockWidgetTab::moved,
|
||||
this,
|
||||
[this, dockWidgetTab](const QPoint &globalPosition) {
|
||||
onTabWidgetMoved(dockWidgetTab, globalPosition);
|
||||
});
|
||||
connect(dockWidgetTab, &DockWidgetTab::elidedChanged, this, &DockAreaTabBar::elidedChanged);
|
||||
dockWidgetTab->installEventFilter(this);
|
||||
emit tabInserted(index);
|
||||
if (index <= d->m_currentIndex)
|
||||
setCurrentIndex(d->m_currentIndex + 1);
|
||||
else if (d->m_currentIndex == -1)
|
||||
setCurrentIndex(index);
|
||||
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void DockAreaTabBar::removeTab(DockWidgetTab *dockWidgetTab)
|
||||
{
|
||||
if (!count())
|
||||
return;
|
||||
|
||||
qCInfo(adsLog) << Q_FUNC_INFO;
|
||||
int newCurrentIndex = currentIndex();
|
||||
int removeIndex = d->m_tabsLayout->indexOf(dockWidgetTab);
|
||||
if (count() == 1)
|
||||
newCurrentIndex = -1;
|
||||
|
||||
if (newCurrentIndex > removeIndex) {
|
||||
newCurrentIndex--;
|
||||
} else if (newCurrentIndex == removeIndex) {
|
||||
newCurrentIndex = -1;
|
||||
// First we walk to the right to search for the next visible tab
|
||||
for (int i = (removeIndex + 1); i < count(); ++i) {
|
||||
if (tab(i)->isVisibleTo(this)) {
|
||||
newCurrentIndex = i - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DockAreaTabBar::DockAreaTabBar(DockAreaWidget *parent)
|
||||
: QScrollArea(parent)
|
||||
, d(new DockAreaTabBarPrivate(this))
|
||||
{
|
||||
d->m_dockArea = parent;
|
||||
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
||||
setFrameStyle(QFrame::NoFrame);
|
||||
setWidgetResizable(true);
|
||||
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
|
||||
d->m_tabsContainerWidget = new QWidget();
|
||||
d->m_tabsContainerWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
||||
d->m_tabsContainerWidget->setObjectName("tabsContainerWidget");
|
||||
d->m_tabsLayout = new QBoxLayout(QBoxLayout::LeftToRight);
|
||||
d->m_tabsLayout->setContentsMargins(0, 0, 0, 0);
|
||||
d->m_tabsLayout->setSpacing(0);
|
||||
d->m_tabsLayout->addStretch(1);
|
||||
d->m_tabsContainerWidget->setLayout(d->m_tabsLayout);
|
||||
setWidget(d->m_tabsContainerWidget);
|
||||
}
|
||||
|
||||
DockAreaTabBar::~DockAreaTabBar() { delete d; }
|
||||
|
||||
void DockAreaTabBar::wheelEvent(QWheelEvent *event)
|
||||
{
|
||||
event->accept();
|
||||
const int direction = event->angleDelta().y();
|
||||
if (direction < 0)
|
||||
horizontalScrollBar()->setValue(horizontalScrollBar()->value() + 20);
|
||||
else
|
||||
horizontalScrollBar()->setValue(horizontalScrollBar()->value() - 20);
|
||||
}
|
||||
|
||||
void DockAreaTabBar::setCurrentIndex(int index)
|
||||
{
|
||||
if (index == d->m_currentIndex)
|
||||
return;
|
||||
|
||||
if (index < -1 || index > (count() - 1)) {
|
||||
qWarning() << Q_FUNC_INFO << "Invalid index" << index;
|
||||
return;
|
||||
}
|
||||
|
||||
emit currentChanging(index);
|
||||
d->m_currentIndex = index;
|
||||
d->updateTabs();
|
||||
updateGeometry();
|
||||
emit currentChanged(index);
|
||||
}
|
||||
|
||||
int DockAreaTabBar::count() const
|
||||
{
|
||||
// The tab bar contains a stretch item as last item
|
||||
return d->m_tabsLayout->count() - 1;
|
||||
}
|
||||
|
||||
void DockAreaTabBar::insertTab(int index, DockWidgetTab *dockWidgetTab)
|
||||
{
|
||||
d->m_tabsLayout->insertWidget(index, dockWidgetTab);
|
||||
connect(dockWidgetTab, &DockWidgetTab::clicked,
|
||||
this, [this, dockWidgetTab] { onTabClicked(dockWidgetTab); });
|
||||
connect(dockWidgetTab, &DockWidgetTab::closeRequested,
|
||||
this, [this, dockWidgetTab] { onTabCloseRequested(dockWidgetTab); });
|
||||
connect(dockWidgetTab, &DockWidgetTab::closeOtherTabsRequested,
|
||||
this, [this, dockWidgetTab] { onCloseOtherTabsRequested(dockWidgetTab); });
|
||||
connect(dockWidgetTab, &DockWidgetTab::moved,
|
||||
this, [this, dockWidgetTab](const QPoint &globalPosition) {
|
||||
onTabWidgetMoved(dockWidgetTab, globalPosition);
|
||||
});
|
||||
connect(dockWidgetTab, &DockWidgetTab::elidedChanged,
|
||||
this, &DockAreaTabBar::elidedChanged);
|
||||
dockWidgetTab->installEventFilter(this);
|
||||
emit tabInserted(index);
|
||||
if (index <= d->m_currentIndex)
|
||||
setCurrentIndex(d->m_currentIndex + 1);
|
||||
else if (d->m_currentIndex == -1)
|
||||
setCurrentIndex(index);
|
||||
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void DockAreaTabBar::removeTab(DockWidgetTab *dockWidgetTab)
|
||||
{
|
||||
if (!count())
|
||||
return;
|
||||
|
||||
qCInfo(adsLog) << Q_FUNC_INFO;
|
||||
int newCurrentIndex = currentIndex();
|
||||
int removeIndex = d->m_tabsLayout->indexOf(dockWidgetTab);
|
||||
if (count() == 1)
|
||||
newCurrentIndex = -1;
|
||||
|
||||
if (newCurrentIndex > removeIndex) {
|
||||
newCurrentIndex--;
|
||||
} else if (newCurrentIndex == removeIndex) {
|
||||
newCurrentIndex = -1;
|
||||
// First we walk to the right to search for the next visible tab
|
||||
for (int i = (removeIndex + 1); i < count(); ++i) {
|
||||
// If there is no visible tab right to this tab then we walk to the left to find a visible tab.
|
||||
if (newCurrentIndex < 0) {
|
||||
for (int i = (removeIndex - 1); i >= 0; --i) {
|
||||
if (tab(i)->isVisibleTo(this)) {
|
||||
newCurrentIndex = i - 1;
|
||||
newCurrentIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If there is no visible tab right to this tab then we walk to
|
||||
// the left to find a visible tab
|
||||
if (newCurrentIndex < 0) {
|
||||
for (int i = (removeIndex - 1); i >= 0; --i) {
|
||||
if (tab(i)->isVisibleTo(this)) {
|
||||
newCurrentIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
emit removingTab(removeIndex);
|
||||
d->m_tabsLayout->removeWidget(dockWidgetTab);
|
||||
dockWidgetTab->disconnect(this);
|
||||
dockWidgetTab->removeEventFilter(this);
|
||||
qCInfo(adsLog) << "NewCurrentIndex " << newCurrentIndex;
|
||||
if (newCurrentIndex != d->m_currentIndex)
|
||||
setCurrentIndex(newCurrentIndex);
|
||||
else
|
||||
d->updateTabs();
|
||||
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
int DockAreaTabBar::currentIndex() const { return d->m_currentIndex; }
|
||||
|
||||
DockWidgetTab *DockAreaTabBar::currentTab() const
|
||||
{
|
||||
if (d->m_currentIndex < 0)
|
||||
return nullptr;
|
||||
else
|
||||
return qobject_cast<DockWidgetTab *>(
|
||||
d->m_tabsLayout->itemAt(d->m_currentIndex)->widget());
|
||||
}
|
||||
|
||||
void DockAreaTabBar::onTabClicked(DockWidgetTab *sourceTab)
|
||||
{
|
||||
const int index = d->m_tabsLayout->indexOf(sourceTab);
|
||||
if (index < 0)
|
||||
return;
|
||||
|
||||
setCurrentIndex(index);
|
||||
emit tabBarClicked(index);
|
||||
}
|
||||
|
||||
void DockAreaTabBar::onTabCloseRequested(DockWidgetTab *sourceTab)
|
||||
{
|
||||
const int index = d->m_tabsLayout->indexOf(sourceTab);
|
||||
closeTab(index);
|
||||
}
|
||||
|
||||
void DockAreaTabBar::onCloseOtherTabsRequested(DockWidgetTab *sourceTab)
|
||||
{
|
||||
for (int i = 0; i < count(); ++i) {
|
||||
auto currentTab = tab(i);
|
||||
if (currentTab->isClosable() && !currentTab->isHidden() && currentTab != sourceTab) {
|
||||
// If the dock widget is deleted with the closeTab() call, its tab it will no longer
|
||||
// be in the layout, and thus the index needs to be updated to not skip any tabs
|
||||
int offset = currentTab->dockWidget()->features().testFlag(
|
||||
DockWidget::DockWidgetDeleteOnClose)
|
||||
? 1
|
||||
: 0;
|
||||
closeTab(i);
|
||||
// If the the dock widget blocks closing, i.e. if the flag
|
||||
// CustomCloseHandling is set, and the dock widget is still open,
|
||||
// then we do not need to correct the index
|
||||
if (currentTab->dockWidget()->isClosed())
|
||||
i -= offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DockWidgetTab *DockAreaTabBar::tab(int index) const
|
||||
{
|
||||
if (index >= count() || index < 0)
|
||||
return nullptr;
|
||||
emit removingTab(removeIndex);
|
||||
d->m_tabsLayout->removeWidget(dockWidgetTab);
|
||||
dockWidgetTab->disconnect(this);
|
||||
dockWidgetTab->removeEventFilter(this);
|
||||
qCInfo(adsLog) << "NewCurrentIndex" << newCurrentIndex;
|
||||
if (newCurrentIndex != d->m_currentIndex)
|
||||
setCurrentIndex(newCurrentIndex);
|
||||
else
|
||||
d->updateTabs();
|
||||
|
||||
return qobject_cast<DockWidgetTab *>(d->m_tabsLayout->itemAt(index)->widget());
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
int DockAreaTabBar::currentIndex() const
|
||||
{
|
||||
return d->m_currentIndex;
|
||||
}
|
||||
|
||||
DockWidgetTab *DockAreaTabBar::currentTab() const
|
||||
{
|
||||
if (d->m_currentIndex < 0)
|
||||
return nullptr;
|
||||
else
|
||||
return qobject_cast<DockWidgetTab *>(d->m_tabsLayout->itemAt(d->m_currentIndex)->widget());
|
||||
}
|
||||
|
||||
DockWidgetTab *DockAreaTabBar::tab(int index) const
|
||||
{
|
||||
if (index >= count() || index < 0)
|
||||
return nullptr;
|
||||
|
||||
return qobject_cast<DockWidgetTab *>(d->m_tabsLayout->itemAt(index)->widget());
|
||||
}
|
||||
|
||||
int DockAreaTabBar::tabAt(const QPoint &pos) const
|
||||
{
|
||||
if (!isVisible())
|
||||
return TabInvalidIndex;
|
||||
|
||||
if (pos.x() < tab(0)->geometry().x())
|
||||
return -1;
|
||||
|
||||
for (int i = 0; i < count(); ++i) {
|
||||
if (tab(i)->geometry().contains(pos))
|
||||
return i;
|
||||
}
|
||||
|
||||
void DockAreaTabBar::onTabWidgetMoved(DockWidgetTab *sourceTab, const QPoint &globalPosition)
|
||||
{
|
||||
const int fromIndex = d->m_tabsLayout->indexOf(sourceTab);
|
||||
auto mousePos = mapFromGlobal(globalPosition);
|
||||
mousePos.rx() = qMax(d->firstTab()->geometry().left(), mousePos.x());
|
||||
mousePos.rx() = qMin(d->lastTab()->geometry().right(), mousePos.x());
|
||||
int toIndex = -1;
|
||||
// Find tab under mouse
|
||||
for (int i = 0; i < count(); ++i) {
|
||||
DockWidgetTab *dropTab = tab(i);
|
||||
if (dropTab == sourceTab || !dropTab->isVisibleTo(this)
|
||||
|| !dropTab->geometry().contains(mousePos))
|
||||
continue;
|
||||
return count();
|
||||
}
|
||||
|
||||
toIndex = d->m_tabsLayout->indexOf(dropTab);
|
||||
if (toIndex == fromIndex)
|
||||
toIndex = -1;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (toIndex > -1) {
|
||||
d->m_tabsLayout->removeWidget(sourceTab);
|
||||
d->m_tabsLayout->insertWidget(toIndex, sourceTab);
|
||||
qCInfo(adsLog) << "tabMoved from" << fromIndex << "to" << toIndex;
|
||||
emit tabMoved(fromIndex, toIndex);
|
||||
setCurrentIndex(toIndex);
|
||||
} else {
|
||||
// Ensure that the moved tab is reset to its start position
|
||||
d->m_tabsLayout->update();
|
||||
}
|
||||
}
|
||||
|
||||
void DockAreaTabBar::closeTab(int index)
|
||||
{
|
||||
if (index < 0 || index >= count())
|
||||
return;
|
||||
|
||||
auto dockWidgetTab = tab(index);
|
||||
if (dockWidgetTab->isHidden())
|
||||
return;
|
||||
|
||||
emit tabCloseRequested(index);
|
||||
}
|
||||
|
||||
bool DockAreaTabBar::eventFilter(QObject *watched, QEvent *event)
|
||||
{
|
||||
bool result = Super::eventFilter(watched, event);
|
||||
DockWidgetTab *dockWidgetTab = qobject_cast<DockWidgetTab *>(watched);
|
||||
if (!dockWidgetTab)
|
||||
return result;
|
||||
|
||||
switch (event->type()) {
|
||||
case QEvent::Hide:
|
||||
emit tabClosed(d->m_tabsLayout->indexOf(dockWidgetTab));
|
||||
updateGeometry();
|
||||
break;
|
||||
case QEvent::Show:
|
||||
emit tabOpened(d->m_tabsLayout->indexOf(dockWidgetTab));
|
||||
updateGeometry();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
int DockAreaTabBar::tabInsertIndexAt(const QPoint &pos) const
|
||||
{
|
||||
int index = tabAt(pos);
|
||||
if (index == TabInvalidIndex)
|
||||
return TabDefaultInsertIndex;
|
||||
else
|
||||
return (index < 0) ? 0 : index;
|
||||
}
|
||||
|
||||
bool DockAreaTabBar::eventFilter(QObject *watched, QEvent *event)
|
||||
{
|
||||
bool result = Super::eventFilter(watched, event);
|
||||
DockWidgetTab *dockWidgetTab = qobject_cast<DockWidgetTab *>(watched);
|
||||
if (!dockWidgetTab)
|
||||
return result;
|
||||
|
||||
switch (event->type()) {
|
||||
case QEvent::Hide:
|
||||
emit tabClosed(d->m_tabsLayout->indexOf(dockWidgetTab));
|
||||
updateGeometry();
|
||||
break;
|
||||
case QEvent::Show:
|
||||
emit tabOpened(d->m_tabsLayout->indexOf(dockWidgetTab));
|
||||
updateGeometry();
|
||||
break;
|
||||
// Setting the text of a tab will cause a LayoutRequest event
|
||||
case QEvent::LayoutRequest:
|
||||
updateGeometry();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
bool DockAreaTabBar::isTabOpen(int index) const
|
||||
{
|
||||
if (index < 0 || index >= count())
|
||||
return false;
|
||||
return result;
|
||||
}
|
||||
|
||||
return !tab(index)->isHidden();
|
||||
bool DockAreaTabBar::isTabOpen(int index) const
|
||||
{
|
||||
if (index < 0 || index >= count())
|
||||
return false;
|
||||
|
||||
return !tab(index)->isHidden();
|
||||
}
|
||||
|
||||
QSize DockAreaTabBar::minimumSizeHint() const
|
||||
{
|
||||
QSize size = sizeHint();
|
||||
size.setWidth(10);
|
||||
return size;
|
||||
}
|
||||
|
||||
QSize DockAreaTabBar::sizeHint() const
|
||||
{
|
||||
return d->m_tabsContainerWidget->sizeHint();
|
||||
}
|
||||
|
||||
void DockAreaTabBar::setCurrentIndex(int index)
|
||||
{
|
||||
if (index == d->m_currentIndex)
|
||||
return;
|
||||
|
||||
if (index < -1 || index > (count() - 1)) {
|
||||
qWarning() << Q_FUNC_INFO << "Invalid index" << index;
|
||||
return;
|
||||
}
|
||||
|
||||
QSize DockAreaTabBar::minimumSizeHint() const
|
||||
{
|
||||
QSize size = sizeHint();
|
||||
size.setWidth(10);
|
||||
return size;
|
||||
}
|
||||
emit currentChanging(index);
|
||||
d->m_currentIndex = index;
|
||||
d->updateTabs();
|
||||
updateGeometry();
|
||||
emit currentChanged(index);
|
||||
}
|
||||
|
||||
QSize DockAreaTabBar::sizeHint() const
|
||||
{
|
||||
return d->m_tabsContainerWidget->sizeHint();
|
||||
}
|
||||
void DockAreaTabBar::closeTab(int index)
|
||||
{
|
||||
if (index < 0 || index >= count())
|
||||
return;
|
||||
|
||||
auto dockWidgetTab = tab(index);
|
||||
if (dockWidgetTab->isHidden())
|
||||
return;
|
||||
|
||||
emit tabCloseRequested(index);
|
||||
}
|
||||
|
||||
} // namespace ADS
|
||||
|
@@ -86,6 +86,18 @@ public:
|
||||
*/
|
||||
DockWidgetTab *tab(int index) const;
|
||||
|
||||
/**
|
||||
* Returns the tab at the given position.
|
||||
* Returns -1 if the position is left of the first tab and count() if the position is right
|
||||
* of the last tab. Returns -2 to indicate an invalid value.
|
||||
*/
|
||||
int tabAt(const QPoint &pos) const;
|
||||
|
||||
/**
|
||||
* Returns the tab insertion index for the given mouse cursor position.
|
||||
*/
|
||||
int tabInsertIndexAt(const QPoint &pos) const;
|
||||
|
||||
/**
|
||||
* Filters the tab widget events
|
||||
*/
|
||||
@@ -101,7 +113,7 @@ public:
|
||||
/**
|
||||
* Overrides the minimumSizeHint() function of QScrollArea
|
||||
* The minimumSizeHint() is bigger than the sizeHint () for the scroll
|
||||
* area because even if the scrollbars are invisible, the required speace
|
||||
* area because even if the scrollbars are invisible, the required space
|
||||
* is reserved in the minimumSizeHint(). This override simply returns
|
||||
* sizeHint();
|
||||
*/
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -17,6 +17,7 @@ namespace ADS {
|
||||
class DockAreaTabBar;
|
||||
class DockAreaWidget;
|
||||
class DockAreaTitleBarPrivate;
|
||||
class ElidingLabel;
|
||||
|
||||
using TitleBarButtonType = QToolButton;
|
||||
|
||||
@@ -29,17 +30,22 @@ using TitleBarButtonType = QToolButton;
|
||||
class TitleBarButton : public TitleBarButtonType
|
||||
{
|
||||
Q_OBJECT
|
||||
bool m_visible = true;
|
||||
bool m_showInTitleBar = true;
|
||||
bool m_hideWhenDisabled = false;
|
||||
public:
|
||||
using Super = TitleBarButtonType;
|
||||
TitleBarButton(bool visible = true, QWidget *parent = nullptr);
|
||||
TitleBarButton(bool showInTitleBar = true, QWidget *parent = nullptr);
|
||||
|
||||
/**
|
||||
* Adjust this visibility change request with our internal settings:
|
||||
*/
|
||||
void setVisible(bool visible) override;
|
||||
|
||||
/**
|
||||
* Configures, if the title bar button should be shown in title bar
|
||||
*/
|
||||
void setShowInTitleBar(bool show);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Handle EnabledChanged signal to set button invisible if the configured
|
||||
@@ -81,6 +87,9 @@ private:
|
||||
void onUndockButtonClicked();
|
||||
void onTabsMenuActionTriggered(QAction *action);
|
||||
void onCurrentTabChanged(int index);
|
||||
void onAutoHideButtonClicked();
|
||||
void onAutoHideDockAreaActionClicked();
|
||||
void onAutoHideToActionClicked();
|
||||
|
||||
protected:
|
||||
/**
|
||||
@@ -135,7 +144,12 @@ public:
|
||||
/**
|
||||
* Returns the button corresponding to the given title bar button identifier
|
||||
*/
|
||||
QAbstractButton *button(eTitleBarButton which) const;
|
||||
TitleBarButton *button(eTitleBarButton which) const;
|
||||
|
||||
/**
|
||||
* Returns the auto hide title label, used when the dock area is expanded and auto hidden
|
||||
*/
|
||||
ElidingLabel* autoHideTitleLabel() const;
|
||||
|
||||
/**
|
||||
* Updates the visibility of the dock widget actions in the title bar
|
||||
@@ -166,6 +180,17 @@ public:
|
||||
*/
|
||||
int indexOf(QWidget *widget) const;
|
||||
|
||||
/**
|
||||
* Close group tool tip based on the current state
|
||||
* Auto hide widgets can only have one dock widget so it does not make sense for the tooltip to show close group
|
||||
*/
|
||||
QString titleBarButtonToolTip(eTitleBarButton button) const;
|
||||
|
||||
/**
|
||||
* Moves the dock area into its own floating widget if the area DockWidgetFloatable flag is true.
|
||||
*/
|
||||
void setAreaFloating();
|
||||
|
||||
signals:
|
||||
/**
|
||||
* This signal is emitted if a tab in the tab bar is clicked by the user
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "ads_globals.h"
|
||||
#include "autohidetab.h"
|
||||
#include "dockwidget.h"
|
||||
|
||||
#include <QFrame>
|
||||
@@ -20,6 +21,8 @@ class DockManager;
|
||||
class DockContainerWidget;
|
||||
class DockContainerWidgetPrivate;
|
||||
class DockAreaTitleBar;
|
||||
class DockingStateReader;
|
||||
class AutoHideDockContainer;
|
||||
|
||||
/**
|
||||
* DockAreaWidget manages multiple instances of DockWidgets.
|
||||
@@ -39,6 +42,9 @@ private:
|
||||
friend class DockWidget;
|
||||
friend class DockManagerPrivate;
|
||||
friend class DockManager;
|
||||
friend class AutoHideDockContainer;
|
||||
|
||||
void onDockWidgetFeaturesChanged();
|
||||
|
||||
void onTabCloseRequested(int index);
|
||||
|
||||
@@ -48,7 +54,34 @@ private:
|
||||
*/
|
||||
void reorderDockWidget(int fromIndex, int toIndex);
|
||||
|
||||
/*
|
||||
* Update the auto hide button checked state based on if it's contained in an auto hide container or not
|
||||
*/
|
||||
void updateAutoHideButtonCheckState();
|
||||
|
||||
/*
|
||||
* Update the title bar button tooltips
|
||||
*/
|
||||
void updateTitleBarButtonsToolTips();
|
||||
|
||||
/**
|
||||
* Calculate the auto hide side bar location depending on the dock area
|
||||
* widget position in the container
|
||||
*/
|
||||
SideBarLocation calculateSideTabBarArea() const;
|
||||
|
||||
protected:
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
/**
|
||||
* Reimplements QWidget::event to handle QEvent::PlatformSurface
|
||||
* This is here to fix issue #294 Tab refresh problem with a QGLWidget
|
||||
* that exists since Qt version 5.12.7. So this function is here to
|
||||
* work around a Qt issue.
|
||||
*/
|
||||
virtual bool event(QEvent *event) override;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Inserts a dock widget into dock area.
|
||||
* All dockwidgets in the dock area tabified in a stacked layout with tabs.
|
||||
@@ -110,11 +143,26 @@ protected:
|
||||
*/
|
||||
void markTitleBarMenuOutdated();
|
||||
|
||||
/*
|
||||
* Update the title bar button visibility based on if it's top level or not
|
||||
*/
|
||||
void updateTitleBarButtonVisibility(bool isTopLevel) const;
|
||||
|
||||
void toggleView(bool open);
|
||||
|
||||
public:
|
||||
using Super = QFrame;
|
||||
|
||||
/**
|
||||
* Dock area related flags
|
||||
*/
|
||||
enum eDockAreaFlag
|
||||
{
|
||||
HideSingleWidgetTitleBar = 0x0001,
|
||||
DefaultFlags = 0x0000
|
||||
};
|
||||
Q_DECLARE_FLAGS(DockAreaFlags, eDockAreaFlag)
|
||||
|
||||
/**
|
||||
* Default Constructor
|
||||
*/
|
||||
@@ -135,6 +183,27 @@ public:
|
||||
* if there is no
|
||||
*/
|
||||
DockContainerWidget *dockContainer() const;
|
||||
/**
|
||||
* Returns the auto hide dock container widget this dock area widget belongs to or 0
|
||||
* if there is no
|
||||
*/
|
||||
AutoHideDockContainer *autoHideDockContainer() const;
|
||||
|
||||
/**
|
||||
* Returns true if the dock area is in an auto hide container
|
||||
*/
|
||||
bool isAutoHide() const;
|
||||
|
||||
/**
|
||||
* Sets the current auto hide dock container
|
||||
*/
|
||||
void setAutoHideDockContainer(AutoHideDockContainer *autoHideDockContainer);
|
||||
|
||||
/**
|
||||
* Returns the largest minimumSizeHint() of the dock widgets in this area.
|
||||
* The minimum size hint is updated if a dock widget is removed or added.
|
||||
*/
|
||||
virtual QSize minimumSizeHint() const override;
|
||||
|
||||
/**
|
||||
* Returns the rectangle of the title area
|
||||
@@ -205,6 +274,12 @@ public:
|
||||
*/
|
||||
void saveState(QXmlStreamWriter &stream) const;
|
||||
|
||||
/**
|
||||
* Restores a dock area.
|
||||
* \see restoreChildNodes() for details
|
||||
*/
|
||||
static bool restoreState(DockingStateReader& stream, DockAreaWidget*& createdWidget, bool testing, DockContainerWidget* parentContainer);
|
||||
|
||||
/**
|
||||
* This functions returns the dock widget features of all dock widget in
|
||||
* this area.
|
||||
@@ -242,6 +317,41 @@ public:
|
||||
*/
|
||||
DockAreaTitleBar *titleBar() const;
|
||||
|
||||
/**
|
||||
* Returns the dock area flags - a combination of flags that configure the
|
||||
* appearance and features of the dock area.
|
||||
* \see setDockAreaFlasg()
|
||||
*/
|
||||
DockAreaFlags dockAreaFlags() const;
|
||||
|
||||
/**
|
||||
* Sets the dock area flags - a combination of flags that configure the
|
||||
* appearance and features of the dock area
|
||||
*/
|
||||
void setDockAreaFlags(DockAreaFlags flags);
|
||||
|
||||
/**
|
||||
* Sets the dock area flag Flag on this widget if on is true; otherwise
|
||||
* clears the flag.
|
||||
*/
|
||||
void setDockAreaFlag(eDockAreaFlag flag, bool on);
|
||||
|
||||
/**
|
||||
* Returns true if the area has a single dock widget and contains the central widget of it's manager.
|
||||
*/
|
||||
bool isCentralWidgetArea() const;
|
||||
|
||||
/**
|
||||
* Returns true if the area contains the central widget of it's manager.
|
||||
*/
|
||||
bool containsCentralWidget() const;
|
||||
|
||||
/**
|
||||
* If this dock area is the one and only visible area in a container, then
|
||||
* this function returns true
|
||||
*/
|
||||
bool isTopLevelArea() const;
|
||||
|
||||
/**
|
||||
* This activates the tab for the given tab index.
|
||||
* If the dock widget for the given tab is not visible, the this function
|
||||
@@ -254,17 +364,28 @@ public:
|
||||
*/
|
||||
void closeArea();
|
||||
|
||||
/**
|
||||
* Sets the dock area into auto hide mode or into normal mode.
|
||||
* If the dock area is switched to auto hide mode, then all dock widgets
|
||||
* that are pinable will be added to the sidebar
|
||||
*/
|
||||
void setAutoHide(bool enable, SideBarLocation location = SideBarNone, int tabIndex = -1);
|
||||
|
||||
/**
|
||||
* Switches the dock area to auto hide mode or vice versa depending on its
|
||||
* current state.
|
||||
*/
|
||||
void toggleAutoHide(SideBarLocation location = SideBarNone);
|
||||
|
||||
/**
|
||||
* This function closes all other areas except of this area
|
||||
*/
|
||||
void closeOtherAreas();
|
||||
|
||||
/**
|
||||
* Returns the largest minimumSizeHint() of the dock widgets in this
|
||||
* area.
|
||||
* The minimum size hint is updated if a dock widget is removed or added.
|
||||
* Moves the dock area into its own floating widget if the area DockWidgetFloatable flag is true.
|
||||
*/
|
||||
QSize minimumSizeHint() const override;
|
||||
void setFloating();
|
||||
|
||||
signals:
|
||||
/**
|
||||
|
@@ -3,46 +3,52 @@
|
||||
|
||||
#include "dockcomponentsfactory.h"
|
||||
|
||||
#include "dockwidgettab.h"
|
||||
#include "autohidetab.h"
|
||||
#include "dockareatabbar.h"
|
||||
#include "dockareatitlebar.h"
|
||||
#include "dockwidget.h"
|
||||
#include "dockareawidget.h"
|
||||
#include "dockwidget.h"
|
||||
#include "dockwidgettab.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace ADS
|
||||
namespace ADS {
|
||||
|
||||
static std::unique_ptr<DockComponentsFactory> g_defaultFactory(new DockComponentsFactory());
|
||||
|
||||
DockWidgetTab *DockComponentsFactory::createDockWidgetTab(DockWidget *dockWidget) const
|
||||
{
|
||||
static std::unique_ptr<DockComponentsFactory> g_defaultFactory(new DockComponentsFactory());
|
||||
return new DockWidgetTab(dockWidget);
|
||||
}
|
||||
|
||||
DockWidgetTab *DockComponentsFactory::createDockWidgetTab(DockWidget *dockWidget) const
|
||||
{
|
||||
return new DockWidgetTab(dockWidget);
|
||||
}
|
||||
AutoHideTab *DockComponentsFactory::createDockWidgetSideTab(DockWidget *dockWidget) const
|
||||
{
|
||||
return new AutoHideTab(dockWidget);
|
||||
}
|
||||
|
||||
DockAreaTabBar *DockComponentsFactory::createDockAreaTabBar(DockAreaWidget *dockArea) const
|
||||
{
|
||||
return new DockAreaTabBar(dockArea);
|
||||
}
|
||||
DockAreaTabBar *DockComponentsFactory::createDockAreaTabBar(DockAreaWidget *dockArea) const
|
||||
{
|
||||
return new DockAreaTabBar(dockArea);
|
||||
}
|
||||
|
||||
DockAreaTitleBar *DockComponentsFactory::createDockAreaTitleBar(DockAreaWidget *dockArea) const
|
||||
{
|
||||
return new DockAreaTitleBar(dockArea);
|
||||
}
|
||||
DockAreaTitleBar *DockComponentsFactory::createDockAreaTitleBar(DockAreaWidget *dockArea) const
|
||||
{
|
||||
return new DockAreaTitleBar(dockArea);
|
||||
}
|
||||
|
||||
const DockComponentsFactory *DockComponentsFactory::factory()
|
||||
{
|
||||
return g_defaultFactory.get();
|
||||
}
|
||||
const DockComponentsFactory *DockComponentsFactory::factory()
|
||||
{
|
||||
return g_defaultFactory.get();
|
||||
}
|
||||
|
||||
void DockComponentsFactory::setFactory(DockComponentsFactory *factory)
|
||||
{
|
||||
g_defaultFactory.reset(factory);
|
||||
}
|
||||
void DockComponentsFactory::setFactory(DockComponentsFactory *factory)
|
||||
{
|
||||
g_defaultFactory.reset(factory);
|
||||
}
|
||||
|
||||
void DockComponentsFactory::resetDefaultFactory()
|
||||
{
|
||||
g_defaultFactory.reset(new DockComponentsFactory());
|
||||
}
|
||||
void DockComponentsFactory::resetDefaultFactory()
|
||||
{
|
||||
g_defaultFactory.reset(new DockComponentsFactory());
|
||||
}
|
||||
|
||||
} // namespace ADS
|
||||
|
@@ -12,6 +12,7 @@ class DockAreaTitleBar;
|
||||
class DockAreaTabBar;
|
||||
class DockAreaWidget;
|
||||
class DockWidget;
|
||||
class AutoHideTab;
|
||||
|
||||
/**
|
||||
* Factory for creation of certain GUI elements for the docking framework.
|
||||
@@ -37,6 +38,12 @@ public:
|
||||
*/
|
||||
virtual DockWidgetTab *createDockWidgetTab(DockWidget *dockWidget) const;
|
||||
|
||||
/**
|
||||
* This default implementation just creates a dock widget side tab with
|
||||
* new CDockWidgetTab(DockWidget).
|
||||
*/
|
||||
virtual AutoHideTab *createDockWidgetSideTab(DockWidget *dockWidget) const;
|
||||
|
||||
/**
|
||||
* This default implementation just creates a dock area tab bar with
|
||||
* new DockAreaTabBar(dockArea).
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "ads_globals.h"
|
||||
#include "autohidetab.h"
|
||||
#include "dockwidget.h"
|
||||
|
||||
#include <QFrame>
|
||||
@@ -24,13 +25,16 @@ class FloatingDockContainerPrivate;
|
||||
class FloatingDragPreview;
|
||||
class DockingStateReader;
|
||||
class FloatingDragPreviewPrivate;
|
||||
class AutoHideSideBar;
|
||||
class AutoHideTab;
|
||||
struct AutoHideTabPrivate;
|
||||
struct AutoHideDockContainerPrivate;
|
||||
class AutoHideDockContainer;
|
||||
|
||||
/**
|
||||
* Container that manages a number of dock areas with single dock widgets
|
||||
* or tabified dock widgets in each area.
|
||||
* Each window that support docking has a DockContainerWidget. That means
|
||||
* the main application window and all floating windows contain a
|
||||
* DockContainerWidget instance.
|
||||
* Container that manages a number of dock areas with single dock widgets or tabified dock widgets
|
||||
* in each area. Each window that support docking has a DockContainerWidget. That means the main
|
||||
* application window and all floating windows contain a DockContainerWidget instance.
|
||||
*/
|
||||
class ADS_EXPORT DockContainerWidget : public QFrame
|
||||
{
|
||||
@@ -47,6 +51,11 @@ private:
|
||||
friend class DockWidget;
|
||||
friend class FloatingDragPreview;
|
||||
friend class FloatingDragPreviewPrivate;
|
||||
friend AutoHideDockContainer;
|
||||
friend AutoHideTab;
|
||||
friend AutoHideTabPrivate;
|
||||
friend AutoHideDockContainerPrivate;
|
||||
friend AutoHideSideBar;
|
||||
|
||||
protected:
|
||||
/**
|
||||
@@ -59,11 +68,25 @@ protected:
|
||||
*/
|
||||
QSplitter *rootSplitter() const;
|
||||
|
||||
/**
|
||||
* Creates and initializes a dockwidget auto hide container into the given area. Initializing
|
||||
* inserts the tabs into the side tab widget and hides it
|
||||
* Returns nullptr if you try and insert into an area where the configuration is not enabled
|
||||
*/
|
||||
AutoHideDockContainer *createAndSetupAutoHideContainer(SideBarLocation area,
|
||||
DockWidget *dockWidget,
|
||||
int tabIndex = -1);
|
||||
|
||||
/**
|
||||
* Helper function for creation of the root splitter
|
||||
*/
|
||||
void createRootSplitter();
|
||||
|
||||
/**
|
||||
* Helper function for creation of the side tab bar widgets
|
||||
*/
|
||||
void createSideTabBarWidgets();
|
||||
|
||||
/**
|
||||
* Drop floating widget into the container
|
||||
*/
|
||||
@@ -76,7 +99,10 @@ protected:
|
||||
* a nullptr, then the DropArea indicates the drop area in the given
|
||||
* TargetAreaWidget
|
||||
*/
|
||||
void dropWidget(QWidget *widget, DockWidgetArea dropArea, DockAreaWidget *targetAreaWidget);
|
||||
void dropWidget(QWidget *widget,
|
||||
DockWidgetArea dropArea,
|
||||
DockAreaWidget *targetAreaWidget,
|
||||
int tabIndex = -1);
|
||||
|
||||
/**
|
||||
* Adds the given dock area to this container widget
|
||||
@@ -91,7 +117,7 @@ protected:
|
||||
/**
|
||||
* This function replaces the goto construct. Still need to write a good description.
|
||||
*/
|
||||
void emitAndExit() const; // TODO rename
|
||||
void emitAndExit() const;
|
||||
|
||||
/**
|
||||
* Saves the state into the given stream
|
||||
@@ -134,6 +160,28 @@ protected:
|
||||
*/
|
||||
QList<DockWidget *> dockWidgets() const;
|
||||
|
||||
/**
|
||||
* This function forces the dock container widget to update handles of splitters
|
||||
* based on resize modes of dock widgets contained in the container.
|
||||
*/
|
||||
void updateSplitterHandles(QSplitter *splitter);
|
||||
|
||||
/**
|
||||
* Registers the given floating widget in the internal list of auto hide widgets
|
||||
*/
|
||||
void registerAutoHideWidget(AutoHideDockContainer *autoHideWidget);
|
||||
|
||||
/**
|
||||
* Remove the given auto hide widget from the list of registered auto hide widgets
|
||||
*/
|
||||
void removeAutoHideWidget(AutoHideDockContainer *autoHideWidget);
|
||||
|
||||
/**
|
||||
* Handles widget events of auto hide widgets to trigger delayed show
|
||||
* or hide actions for auto hide container on auto hide tab mouse over
|
||||
*/
|
||||
void handleAutoHideWidgetEvent(QEvent *e, QWidget *w);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Default Constructor
|
||||
@@ -154,7 +202,8 @@ public:
|
||||
*/
|
||||
DockAreaWidget *addDockWidget(DockWidgetArea area,
|
||||
DockWidget *dockWidget,
|
||||
DockAreaWidget *dockAreaWidget = nullptr);
|
||||
DockAreaWidget *dockAreaWidget = nullptr,
|
||||
int index = -1);
|
||||
|
||||
/**
|
||||
* Removes dockwidget
|
||||
@@ -190,6 +239,18 @@ public:
|
||||
*/
|
||||
QList<DockAreaWidget *> openedDockAreas() const;
|
||||
|
||||
/**
|
||||
* Returns a list for all open dock widgets in all open dock areas
|
||||
*/
|
||||
QList<DockWidget *> openedDockWidgets() const;
|
||||
|
||||
/**
|
||||
* This function returns true, if the container has open dock areas.
|
||||
* This functions is a little bit faster than calling openedDockAreas().isEmpty()
|
||||
* because it returns as soon as it finds an open dock area
|
||||
*/
|
||||
bool hasOpenDockAreas() const;
|
||||
|
||||
/**
|
||||
* This function returns true if this dock area has only one single
|
||||
* visible dock widget.
|
||||
@@ -239,6 +300,34 @@ public:
|
||||
*/
|
||||
void closeOtherAreas(DockAreaWidget *keepOpenArea);
|
||||
|
||||
/**
|
||||
* Returns the side tab widget for the given area
|
||||
*/
|
||||
AutoHideSideBar *autoHideSideBar(SideBarLocation area) const;
|
||||
|
||||
/**
|
||||
* Access function for auto hide widgets
|
||||
*/
|
||||
QList<AutoHideDockContainer *> autoHideWidgets() const;
|
||||
|
||||
/**
|
||||
* Returns the content rectangle without the autohide side bars
|
||||
*/
|
||||
QRect contentRect() const;
|
||||
|
||||
/**
|
||||
* Returns the content rectangle mapped to global space.
|
||||
* The content rectangle is the rectangle of the dock manager widget minus
|
||||
* the auto hide side bars. So that means, it is the geometry of the
|
||||
* internal root splitter mapped to global space.
|
||||
*/
|
||||
QRect contentRectGlobal() const;
|
||||
|
||||
/**
|
||||
* Returns the dock manager that owns this container
|
||||
*/
|
||||
DockManager *dockManager() const;
|
||||
|
||||
signals:
|
||||
/**
|
||||
* This signal is emitted if one or multiple dock areas has been added to
|
||||
@@ -247,6 +336,11 @@ signals:
|
||||
*/
|
||||
void dockAreasAdded();
|
||||
|
||||
/**
|
||||
* This signal is emitted, if a new auto hide widget has been created.
|
||||
*/
|
||||
void autoHideWidgetCreated(AutoHideDockContainer *autoHideWidget);
|
||||
|
||||
/**
|
||||
* This signal is emitted if one or multiple dock areas has been removed
|
||||
*/
|
||||
|
@@ -3,250 +3,332 @@
|
||||
|
||||
#include "dockfocuscontroller.h"
|
||||
|
||||
#include "dockareawidget.h"
|
||||
#include "ads_globals_p.h"
|
||||
#include "dockareatitlebar.h"
|
||||
#include "dockareawidget.h"
|
||||
#include "dockcontainerwidget.h"
|
||||
#include "dockmanager.h"
|
||||
#include "dockwidget.h"
|
||||
#include "dockwidgettab.h"
|
||||
#include "floatingdockcontainer.h"
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
|
||||
#include "linux/floatingwidgettitlebar.h"
|
||||
#endif
|
||||
|
||||
#include <QApplication>
|
||||
#include <QPointer>
|
||||
#include <QWindow>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
namespace ADS
|
||||
namespace ADS {
|
||||
|
||||
static const char *const g_focusedDockWidgetProperty = "FocusedDockWidget";
|
||||
|
||||
class DockFocusControllerPrivate
|
||||
{
|
||||
public:
|
||||
DockFocusController *q;
|
||||
QPointer<DockWidget> m_focusedDockWidget = nullptr;
|
||||
QPointer<DockAreaWidget> m_focusedArea = nullptr;
|
||||
QPointer<DockWidget> m_oldFocusedDockWidget = nullptr;
|
||||
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
|
||||
QPointer<FloatingDockContainer> m_floatingWidget = nullptr;
|
||||
#endif
|
||||
DockManager *m_dockManager = nullptr;
|
||||
bool m_forceFocusChangedSignal = false;
|
||||
bool m_tabPressed = false;
|
||||
|
||||
/**
|
||||
* Private data class of CDockFocusController class (pimpl)
|
||||
* Private data constructor
|
||||
*/
|
||||
class DockFocusControllerPrivate
|
||||
{
|
||||
public:
|
||||
DockFocusController *q;
|
||||
QPointer<DockWidget> m_focusedDockWidget = nullptr;
|
||||
QPointer<DockAreaWidget> m_focusedArea = nullptr;
|
||||
#ifdef Q_OS_LINUX
|
||||
QPointer<FloatingDockContainer> m_floatingWidget = nullptr;
|
||||
#endif
|
||||
DockManager *m_dockManager = nullptr;
|
||||
DockFocusControllerPrivate(DockFocusController *parent);
|
||||
|
||||
/**
|
||||
* Private data constructor
|
||||
*/
|
||||
DockFocusControllerPrivate(DockFocusController *parent);
|
||||
/**
|
||||
* This function updates the focus style of the given dock widget and
|
||||
* the dock area that it belongs to
|
||||
*/
|
||||
void updateDockWidgetFocus(DockWidget *dockWidget);
|
||||
}; // class DockFocusControllerPrivate
|
||||
|
||||
/**
|
||||
* This function updates the focus style of the given dock widget and
|
||||
* the dock area that it belongs to
|
||||
*/
|
||||
void updateDockWidgetFocus(DockWidget *dockWidget);
|
||||
}; // class DockFocusControllerPrivate
|
||||
static void updateDockWidgetFocusStyle(DockWidget *dockWidget, bool focused)
|
||||
{
|
||||
dockWidget->setProperty("focused", focused);
|
||||
dockWidget->tabWidget()->setProperty("focused", focused);
|
||||
dockWidget->tabWidget()->updateStyle();
|
||||
internal::repolishStyle(dockWidget);
|
||||
}
|
||||
|
||||
static void updateDockWidgetFocusStyle(DockWidget *dockWidget, bool focused)
|
||||
{
|
||||
dockWidget->setProperty("focused", focused);
|
||||
dockWidget->tabWidget()->setProperty("focused", focused);
|
||||
dockWidget->tabWidget()->updateStyle();
|
||||
internal::repolishStyle(dockWidget);
|
||||
}
|
||||
static void updateDockAreaFocusStyle(DockAreaWidget *dockArea, bool focused)
|
||||
{
|
||||
dockArea->setProperty("focused", focused);
|
||||
internal::repolishStyle(dockArea);
|
||||
internal::repolishStyle(dockArea->titleBar());
|
||||
}
|
||||
|
||||
static void updateDockAreaFocusStyle(DockAreaWidget *dockArea, bool focused)
|
||||
{
|
||||
dockArea->setProperty("focused", focused);
|
||||
internal::repolishStyle(dockArea);
|
||||
internal::repolishStyle(dockArea->titleBar());
|
||||
}
|
||||
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
|
||||
static void updateFloatingWidgetFocusStyle(FloatingDockContainer *floatingWidget, bool focused)
|
||||
{
|
||||
if (floatingWidget->hasNativeTitleBar())
|
||||
return;
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
static void updateFloatingWidgetFocusStyle(FloatingDockContainer *floatingWidget, bool focused)
|
||||
{
|
||||
auto titleBar = qobject_cast<FloatingWidgetTitleBar *>(floatingWidget->titleBarWidget());
|
||||
if (!titleBar)
|
||||
return;
|
||||
auto titleBar = qobject_cast<FloatingWidgetTitleBar *>(floatingWidget->titleBarWidget());
|
||||
if (!titleBar)
|
||||
return;
|
||||
|
||||
titleBar->setProperty("focused", focused);
|
||||
titleBar->updateStyle();
|
||||
}
|
||||
titleBar->setProperty("focused", focused);
|
||||
titleBar->updateStyle();
|
||||
}
|
||||
#endif
|
||||
|
||||
DockFocusControllerPrivate::DockFocusControllerPrivate(DockFocusController *parent)
|
||||
: q(parent)
|
||||
{}
|
||||
DockFocusControllerPrivate::DockFocusControllerPrivate(DockFocusController *parent)
|
||||
: q(parent)
|
||||
{}
|
||||
|
||||
void DockFocusControllerPrivate::updateDockWidgetFocus(DockWidget *dockWidget)
|
||||
{
|
||||
DockAreaWidget *newFocusedDockArea = nullptr;
|
||||
if (m_focusedDockWidget)
|
||||
updateDockWidgetFocusStyle(m_focusedDockWidget, false);
|
||||
void DockFocusControllerPrivate::updateDockWidgetFocus(DockWidget *dockWidget)
|
||||
{
|
||||
if (!dockWidget->features().testFlag(DockWidget::DockWidgetFocusable))
|
||||
return;
|
||||
|
||||
DockWidget *old = m_focusedDockWidget;
|
||||
m_focusedDockWidget = dockWidget;
|
||||
updateDockWidgetFocusStyle(m_focusedDockWidget, true);
|
||||
newFocusedDockArea = m_focusedDockWidget->dockAreaWidget();
|
||||
if (newFocusedDockArea && (m_focusedArea != newFocusedDockArea))
|
||||
{
|
||||
if (m_focusedArea)
|
||||
{
|
||||
QObject::disconnect(m_focusedArea, &DockAreaWidget::viewToggled,
|
||||
q, &DockFocusController::onFocusedDockAreaViewToggled);
|
||||
updateDockAreaFocusStyle(m_focusedArea, false);
|
||||
}
|
||||
QWindow *window = nullptr;
|
||||
auto dockContainer = dockWidget->dockContainer();
|
||||
if (dockContainer)
|
||||
window = dockContainer->window()->windowHandle();
|
||||
|
||||
m_focusedArea = newFocusedDockArea;
|
||||
updateDockAreaFocusStyle(m_focusedArea, true);
|
||||
QObject::connect(m_focusedArea, &DockAreaWidget::viewToggled,
|
||||
q, &DockFocusController::onFocusedDockAreaViewToggled);
|
||||
if (window)
|
||||
window->setProperty(g_focusedDockWidgetProperty,
|
||||
QVariant::fromValue(QPointer<DockWidget>(dockWidget)));
|
||||
|
||||
DockAreaWidget *newFocusedDockArea = nullptr;
|
||||
if (m_focusedDockWidget)
|
||||
updateDockWidgetFocusStyle(m_focusedDockWidget, false);
|
||||
|
||||
DockWidget *old = m_focusedDockWidget;
|
||||
m_focusedDockWidget = dockWidget;
|
||||
updateDockWidgetFocusStyle(m_focusedDockWidget, true);
|
||||
newFocusedDockArea = m_focusedDockWidget->dockAreaWidget();
|
||||
if (newFocusedDockArea && (m_focusedArea != newFocusedDockArea)) {
|
||||
if (m_focusedArea) {
|
||||
QObject::disconnect(m_focusedArea,
|
||||
&DockAreaWidget::viewToggled,
|
||||
q,
|
||||
&DockFocusController::onFocusedDockAreaViewToggled);
|
||||
updateDockAreaFocusStyle(m_focusedArea, false);
|
||||
}
|
||||
|
||||
auto dockContainer = m_focusedDockWidget->dockContainer();
|
||||
FloatingDockContainer *newFloatingWidget = nullptr;
|
||||
m_focusedArea = newFocusedDockArea;
|
||||
updateDockAreaFocusStyle(m_focusedArea, true);
|
||||
QObject::connect(m_focusedArea,
|
||||
&DockAreaWidget::viewToggled,
|
||||
q,
|
||||
&DockFocusController::onFocusedDockAreaViewToggled);
|
||||
}
|
||||
|
||||
if (dockContainer)
|
||||
newFloatingWidget = dockContainer->floatingWidget();
|
||||
FloatingDockContainer *newFloatingWidget = nullptr;
|
||||
dockContainer = m_focusedDockWidget->dockContainer();
|
||||
if (dockContainer)
|
||||
newFloatingWidget = dockContainer->floatingWidget();
|
||||
|
||||
if (newFloatingWidget)
|
||||
newFloatingWidget->setProperty("FocusedDockWidget", QVariant::fromValue(dockWidget));
|
||||
if (newFloatingWidget)
|
||||
newFloatingWidget->setProperty(g_focusedDockWidgetProperty,
|
||||
QVariant::fromValue(QPointer<DockWidget>(dockWidget)));
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
// This code is required for styling the floating widget titlebar for linux
|
||||
// depending on the current focus state
|
||||
if (m_floatingWidget == newFloatingWidget)
|
||||
return;
|
||||
|
||||
if (m_floatingWidget)
|
||||
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
|
||||
// This code is required for styling the floating widget titlebar for linux
|
||||
// depending on the current focus state
|
||||
if (m_floatingWidget != newFloatingWidget) {
|
||||
if (m_floatingWidget) {
|
||||
updateFloatingWidgetFocusStyle(m_floatingWidget, false);
|
||||
|
||||
}
|
||||
m_floatingWidget = newFloatingWidget;
|
||||
|
||||
if (m_floatingWidget)
|
||||
if (m_floatingWidget) {
|
||||
updateFloatingWidgetFocusStyle(m_floatingWidget, true);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (old != dockWidget)
|
||||
emit m_dockManager->focusedDockWidgetChanged(old, dockWidget);
|
||||
if (old == dockWidget && !m_forceFocusChangedSignal)
|
||||
return;
|
||||
|
||||
m_forceFocusChangedSignal = false;
|
||||
if (dockWidget->isVisible()) {
|
||||
emit m_dockManager->focusedDockWidgetChanged(old, dockWidget);
|
||||
} else {
|
||||
m_oldFocusedDockWidget = old;
|
||||
QObject::connect(dockWidget,
|
||||
&DockWidget::visibilityChanged,
|
||||
q,
|
||||
&DockFocusController::onDockWidgetVisibilityChanged);
|
||||
}
|
||||
}
|
||||
|
||||
DockFocusController::DockFocusController(DockManager *dockManager)
|
||||
: QObject(dockManager)
|
||||
, d(new DockFocusControllerPrivate(this))
|
||||
{
|
||||
d->m_dockManager = dockManager;
|
||||
connect(qApp, &QApplication::focusChanged,
|
||||
this, &DockFocusController::onApplicationFocusChanged);
|
||||
connect(d->m_dockManager, &DockManager::stateRestored,
|
||||
this, &DockFocusController::onStateRestored);
|
||||
}
|
||||
void DockFocusController::onDockWidgetVisibilityChanged(bool visible)
|
||||
{
|
||||
auto dockWidget = qobject_cast<DockWidget *>(sender());
|
||||
QObject::disconnect(dockWidget,
|
||||
&DockWidget::visibilityChanged,
|
||||
this,
|
||||
&DockFocusController::onDockWidgetVisibilityChanged);
|
||||
if (dockWidget && visible)
|
||||
emit d->m_dockManager->focusedDockWidgetChanged(d->m_oldFocusedDockWidget, dockWidget);
|
||||
}
|
||||
|
||||
DockFocusController::~DockFocusController()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
DockFocusController::DockFocusController(DockManager *dockManager)
|
||||
: QObject(dockManager)
|
||||
, d(new DockFocusControllerPrivate(this))
|
||||
{
|
||||
d->m_dockManager = dockManager;
|
||||
connect(qApp,
|
||||
&QApplication::focusChanged,
|
||||
this,
|
||||
&DockFocusController::onApplicationFocusChanged);
|
||||
connect(qApp,
|
||||
&QApplication::focusWindowChanged,
|
||||
this,
|
||||
&DockFocusController::onFocusWindowChanged);
|
||||
connect(d->m_dockManager,
|
||||
&DockManager::stateRestored,
|
||||
this,
|
||||
&DockFocusController::onStateRestored);
|
||||
}
|
||||
|
||||
void DockFocusController::onApplicationFocusChanged(QWidget *focusedOld, QWidget *focusedNow)
|
||||
{
|
||||
Q_UNUSED(focusedOld);
|
||||
DockFocusController::~DockFocusController()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
if (d->m_dockManager->isRestoringState())
|
||||
return;
|
||||
void DockFocusController::onFocusWindowChanged(QWindow *focusWindow)
|
||||
{
|
||||
if (!focusWindow)
|
||||
return;
|
||||
|
||||
if (!focusedNow)
|
||||
return;
|
||||
auto dockWidgetVar = focusWindow->property(g_focusedDockWidgetProperty);
|
||||
if (!dockWidgetVar.isValid())
|
||||
return;
|
||||
|
||||
DockWidget *dockWidget = nullptr;
|
||||
auto dockWidgetTab = qobject_cast<DockWidgetTab *>(focusedNow);
|
||||
if (dockWidgetTab)
|
||||
dockWidget = dockWidgetTab->dockWidget();
|
||||
auto dockWidget = dockWidgetVar.value<QPointer<DockWidget>>();
|
||||
if (!dockWidget)
|
||||
return;
|
||||
|
||||
if (!dockWidget)
|
||||
dockWidget = qobject_cast<DockWidget *>(focusedNow);
|
||||
d->updateDockWidgetFocus(dockWidget);
|
||||
}
|
||||
|
||||
bool focusActual = dockWidget && dockWidget->widget();
|
||||
void DockFocusController::onApplicationFocusChanged(QWidget *focusedOld, QWidget *focusedNow)
|
||||
{
|
||||
Q_UNUSED(focusedOld);
|
||||
|
||||
if (!dockWidget)
|
||||
dockWidget = internal::findParent<DockWidget *>(focusedNow);
|
||||
// Ignore focus changes if we are restoring state, or if user clicked a tab which in turn
|
||||
// caused the focus change.
|
||||
if (d->m_dockManager->isRestoringState() || d->m_tabPressed)
|
||||
return;
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
if (!dockWidget)
|
||||
return;
|
||||
#else
|
||||
if (!dockWidget || dockWidget->tabWidget()->isHidden())
|
||||
return;
|
||||
#endif
|
||||
qCInfo(adsLog) << Q_FUNC_INFO << "old: " << focusedOld << "new:" << focusedNow;
|
||||
|
||||
if (!focusedNow)
|
||||
return;
|
||||
|
||||
DockWidget *dockWidget = qobject_cast<DockWidget *>(focusedNow);
|
||||
if (!dockWidget)
|
||||
dockWidget = internal::findParent<DockWidget *>(focusedNow);
|
||||
|
||||
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
|
||||
if (!dockWidget)
|
||||
return;
|
||||
#else
|
||||
if (!dockWidget || dockWidget->tabWidget()->isHidden())
|
||||
return;
|
||||
#endif
|
||||
|
||||
d->updateDockWidgetFocus(dockWidget);
|
||||
}
|
||||
|
||||
void DockFocusController::setDockWidgetTabFocused(DockWidgetTab *tab)
|
||||
{
|
||||
auto dockWidget = tab->dockWidget();
|
||||
if (dockWidget)
|
||||
d->updateDockWidgetFocus(dockWidget);
|
||||
}
|
||||
|
||||
if (focusActual) {
|
||||
// Do this async to avoid issues when changing focus in middle of another focus handling
|
||||
QMetaObject::invokeMethod(dockWidget->widget(), QOverload<>::of(&QWidget::setFocus),
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
void DockFocusController::clearDockWidgetFocus(DockWidget *dockWidget)
|
||||
{
|
||||
dockWidget->clearFocus();
|
||||
updateDockWidgetFocusStyle(dockWidget, false);
|
||||
}
|
||||
|
||||
void DockFocusController::setDockWidgetFocused(DockWidget *focusedNow)
|
||||
{
|
||||
d->updateDockWidgetFocus(focusedNow);
|
||||
}
|
||||
|
||||
void DockFocusController::onFocusedDockAreaViewToggled(bool open)
|
||||
{
|
||||
if (d->m_dockManager->isRestoringState() || !d->m_focusedArea || open)
|
||||
return;
|
||||
|
||||
DockAreaWidget *dockArea = qobject_cast<DockAreaWidget *>(sender());
|
||||
if (!dockArea || open)
|
||||
return;
|
||||
|
||||
auto container = dockArea->dockContainer();
|
||||
auto openedDockAreas = container->openedDockAreas();
|
||||
if (openedDockAreas.isEmpty())
|
||||
return;
|
||||
|
||||
d->updateDockWidgetFocus(openedDockAreas[0]->currentDockWidget());
|
||||
}
|
||||
|
||||
void DockFocusController::notifyWidgetOrAreaRelocation(QWidget *droppedWidget)
|
||||
{
|
||||
if (d->m_dockManager->isRestoringState())
|
||||
return;
|
||||
|
||||
DockWidget *dockWidget = qobject_cast<DockWidget *>(droppedWidget);
|
||||
if (!dockWidget) {
|
||||
DockAreaWidget *dockArea = qobject_cast<DockAreaWidget *>(droppedWidget);
|
||||
if (dockArea)
|
||||
dockWidget = dockArea->currentDockWidget();
|
||||
}
|
||||
|
||||
void DockFocusController::setDockWidgetFocused(DockWidget *focusedNow)
|
||||
{
|
||||
d->updateDockWidgetFocus(focusedNow);
|
||||
}
|
||||
if (!dockWidget)
|
||||
return;
|
||||
|
||||
void DockFocusController::onFocusedDockAreaViewToggled(bool open)
|
||||
{
|
||||
if (d->m_dockManager->isRestoringState() || !d->m_focusedArea || open)
|
||||
return;
|
||||
d->m_forceFocusChangedSignal = true;
|
||||
DockManager::setWidgetFocus(dockWidget);
|
||||
}
|
||||
|
||||
auto container = d->m_focusedArea->dockContainer();
|
||||
auto openedDockAreas = container->openedDockAreas();
|
||||
if (openedDockAreas.isEmpty())
|
||||
return;
|
||||
void DockFocusController::notifyFloatingWidgetDrop(FloatingDockContainer *floatingWidget)
|
||||
{
|
||||
if (!floatingWidget || d->m_dockManager->isRestoringState())
|
||||
return;
|
||||
|
||||
DockManager::setWidgetFocus(openedDockAreas[0]->currentDockWidget()->tabWidget());
|
||||
}
|
||||
auto dockWidgetVar = floatingWidget->property(g_focusedDockWidgetProperty);
|
||||
if (!dockWidgetVar.isValid())
|
||||
return;
|
||||
|
||||
void DockFocusController::notifyWidgetOrAreaRelocation(QWidget *droppedWidget)
|
||||
{
|
||||
if (d->m_dockManager->isRestoringState())
|
||||
return;
|
||||
|
||||
DockWidget *dockWidget = qobject_cast<DockWidget *>(droppedWidget);
|
||||
if (dockWidget)
|
||||
{
|
||||
DockManager::setWidgetFocus(dockWidget->tabWidget());
|
||||
return;
|
||||
}
|
||||
|
||||
DockAreaWidget* dockArea = qobject_cast<DockAreaWidget*>(droppedWidget);
|
||||
if (!dockArea)
|
||||
return;
|
||||
|
||||
dockWidget = dockArea->currentDockWidget();
|
||||
auto dockWidget = dockWidgetVar.value<DockWidget *>();
|
||||
if (dockWidget) {
|
||||
dockWidget->dockAreaWidget()->setCurrentDockWidget(dockWidget);
|
||||
DockManager::setWidgetFocus(dockWidget->tabWidget());
|
||||
}
|
||||
}
|
||||
|
||||
void DockFocusController::notifyFloatingWidgetDrop(FloatingDockContainer *floatingWidget)
|
||||
{
|
||||
if (!floatingWidget || d->m_dockManager->isRestoringState())
|
||||
return;
|
||||
void DockFocusController::onStateRestored()
|
||||
{
|
||||
if (d->m_focusedDockWidget)
|
||||
updateDockWidgetFocusStyle(d->m_focusedDockWidget, false);
|
||||
}
|
||||
|
||||
auto vDockWidget = floatingWidget->property("FocusedDockWidget");
|
||||
if (!vDockWidget.isValid())
|
||||
return;
|
||||
DockWidget *DockFocusController::focusedDockWidget() const
|
||||
{
|
||||
return d->m_focusedDockWidget.data();
|
||||
}
|
||||
|
||||
auto dockWidget = vDockWidget.value<DockWidget *>();
|
||||
if (dockWidget) {
|
||||
dockWidget->dockAreaWidget()->setCurrentDockWidget(dockWidget);
|
||||
DockManager::setWidgetFocus(dockWidget->tabWidget());
|
||||
}
|
||||
}
|
||||
|
||||
void DockFocusController::onStateRestored()
|
||||
{
|
||||
if (d->m_focusedDockWidget)
|
||||
updateDockWidgetFocusStyle(d->m_focusedDockWidget, false);
|
||||
}
|
||||
void DockFocusController::setDockWidgetTabPressed(bool value)
|
||||
{
|
||||
d->m_tabPressed = value;
|
||||
}
|
||||
|
||||
} // namespace ADS
|
||||
|
@@ -27,8 +27,10 @@ private:
|
||||
|
||||
private:
|
||||
void onApplicationFocusChanged(QWidget *old, QWidget *now);
|
||||
void onFocusWindowChanged(QWindow *focusWindow);
|
||||
void onFocusedDockAreaViewToggled(bool open);
|
||||
void onStateRestored();
|
||||
void onDockWidgetVisibilityChanged(bool visible);
|
||||
|
||||
public:
|
||||
/**
|
||||
@@ -41,19 +43,6 @@ public:
|
||||
*/
|
||||
~DockFocusController() override;
|
||||
|
||||
/**
|
||||
* Helper function to set focus depending on the configuration of the
|
||||
* FocusStyling flag
|
||||
*/
|
||||
template <class QWidgetPtr>
|
||||
static void setWidgetFocus(QWidgetPtr widget)
|
||||
{
|
||||
if (!DockManager::testConfigFlag(DockManager::FocusHighlighting))
|
||||
return;
|
||||
|
||||
widget->setFocus(Qt::OtherFocusReason);
|
||||
}
|
||||
|
||||
/**
|
||||
* A container needs to call this function if a widget has been dropped
|
||||
* into it
|
||||
@@ -68,6 +57,28 @@ public:
|
||||
*/
|
||||
void notifyFloatingWidgetDrop(FloatingDockContainer *floatingWidget);
|
||||
|
||||
/**
|
||||
* Returns the dock widget that has focus style in the ui or a nullptr if
|
||||
* not dock widget is painted focused.
|
||||
*/
|
||||
DockWidget *focusedDockWidget() const;
|
||||
|
||||
/**
|
||||
* Notifies the dock focus controller, that a the mouse is pressed or released.
|
||||
*/
|
||||
void setDockWidgetTabPressed(bool value);
|
||||
|
||||
/**
|
||||
* Request focus highlighting for the given dock widget assigned to the tab
|
||||
* given in Tab parameter
|
||||
*/
|
||||
void setDockWidgetTabFocused(DockWidgetTab *tab);
|
||||
|
||||
/*
|
||||
* Request clear focus for a dock widget
|
||||
*/
|
||||
void clearDockWidgetFocus(DockWidget *dockWidget);
|
||||
|
||||
/**
|
||||
* Request a focus change to the given dock widget
|
||||
*/
|
||||
|
@@ -4,12 +4,14 @@
|
||||
#include "dockmanager.h"
|
||||
|
||||
#include "ads_globals.h"
|
||||
#include "advanceddockingsystemtr.h"
|
||||
#include "ads_globals_p.h"
|
||||
#include "advanceddockingsystemtr.h"
|
||||
#include "autohidedockcontainer.h"
|
||||
#include "dockareawidget.h"
|
||||
#include "dockfocuscontroller.h"
|
||||
#include "dockingstatereader.h"
|
||||
#include "dockoverlay.h"
|
||||
#include "docksplitter.h"
|
||||
#include "dockwidget.h"
|
||||
#include "floatingdockcontainer.h"
|
||||
#include "iconprovider.h"
|
||||
@@ -40,8 +42,13 @@
|
||||
#include <QMessageBox>
|
||||
#include <QSettings>
|
||||
#include <QVariant>
|
||||
#include <QWindow>
|
||||
#include <QXmlStreamWriter>
|
||||
|
||||
#if !(defined(Q_OS_UNIX) && !defined(Q_OS_MACOS))
|
||||
#include <QWindowStateChangeEvent>
|
||||
#endif
|
||||
|
||||
Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtWarningMsg);
|
||||
|
||||
using namespace Utils;
|
||||
@@ -57,6 +64,9 @@ enum eStateFileVersion {
|
||||
};
|
||||
|
||||
static DockManager::ConfigFlags g_staticConfigFlags = DockManager::DefaultNonOpaqueConfig;
|
||||
static DockManager::AutoHideFlags g_staticAutoHideConfigFlags; // auto hide is disabled by default
|
||||
|
||||
static QString g_floatingContainersTitle;
|
||||
|
||||
/**
|
||||
* Private data class of DockManager class (pimpl)
|
||||
@@ -66,6 +76,7 @@ class DockManagerPrivate
|
||||
public:
|
||||
DockManager *q;
|
||||
QList<QPointer<FloatingDockContainer>> m_floatingWidgets;
|
||||
QList<QPointer<FloatingDockContainer>> m_hiddenFloatingWidgets;
|
||||
QList<DockContainerWidget *> m_containers;
|
||||
DockOverlay *m_containerOverlay = nullptr;
|
||||
DockOverlay *m_dockAreaOverlay = nullptr;
|
||||
@@ -73,6 +84,8 @@ public:
|
||||
bool m_restoringState = false;
|
||||
QVector<FloatingDockContainer *> m_uninitializedFloatingWidgets;
|
||||
DockFocusController *m_focusController = nullptr;
|
||||
DockWidget *m_centralWidget = nullptr;
|
||||
bool m_isLeavingMinimized = false;
|
||||
|
||||
QString m_workspacePresetsPath;
|
||||
QList<Workspace> m_workspaces;
|
||||
@@ -114,7 +127,7 @@ public:
|
||||
void markDockWidgetsDirty()
|
||||
{
|
||||
for (const auto &dockWidget : std::as_const(m_dockWidgetsMap))
|
||||
dockWidget->setProperty("dirty", true);
|
||||
dockWidget->setProperty(internal::g_dirtyProperty, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -180,11 +193,27 @@ bool DockManagerPrivate::restoreStateFromXml(const QByteArray &state, int versio
|
||||
return false;
|
||||
}
|
||||
|
||||
qCInfo(adsLog) << stateReader.attributes().value("containers").toInt();
|
||||
|
||||
if (m_centralWidget) {
|
||||
const auto centralWidgetAttribute = stateReader.attributes().value("centralWidget");
|
||||
// If we have a central widget, but a state without central widget, then something is wrong.
|
||||
if (centralWidgetAttribute.isEmpty()) {
|
||||
qWarning()
|
||||
<< "DockManager has central widget, but saved state does not have central widget.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the object name of the central widget does not match the name of the saved central
|
||||
// widget, something is wrong.
|
||||
if (m_centralWidget->objectName() != centralWidgetAttribute.toString()) {
|
||||
qWarning() << "Object name of central widget does not match name of central widget in "
|
||||
"saved state.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool result = true;
|
||||
#ifdef ADS_DEBUG_PRINT
|
||||
int dockContainers = stateReader.attributes().value("containers").toInt();
|
||||
qCInfo(adsLog) << dockContainers;
|
||||
#endif
|
||||
int dockContainerCount = 0;
|
||||
while (stateReader.readNextStartElement()) {
|
||||
if (stateReader.name() == QLatin1String("container")) {
|
||||
@@ -199,10 +228,10 @@ bool DockManagerPrivate::restoreStateFromXml(const QByteArray &state, int versio
|
||||
if (!testing) {
|
||||
// Delete remaining empty floating widgets
|
||||
int floatingWidgetIndex = dockContainerCount - 1;
|
||||
int deleteCount = m_floatingWidgets.count() - floatingWidgetIndex;
|
||||
for (int i = 0; i < deleteCount; ++i) {
|
||||
m_floatingWidgets[floatingWidgetIndex + i]->deleteLater();
|
||||
q->removeDockContainer(m_floatingWidgets[floatingWidgetIndex + i]->dockContainer());
|
||||
for (int i = floatingWidgetIndex; i < m_floatingWidgets.count(); ++i) {
|
||||
QPointer<FloatingDockContainer> floatingWidget = m_floatingWidgets[i];
|
||||
q->removeDockContainer(floatingWidget->dockContainer());
|
||||
floatingWidget->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,11 +244,17 @@ void DockManagerPrivate::restoreDockWidgetsOpenState()
|
||||
// invisible to the user now and have no assigned dock area. They do not belong to any dock
|
||||
// container, until the user toggles the toggle view action the next time.
|
||||
for (auto dockWidget : std::as_const(m_dockWidgetsMap)) {
|
||||
if (dockWidget->property(internal::dirtyProperty).toBool()) {
|
||||
if (dockWidget->property(internal::g_dirtyProperty).toBool()) {
|
||||
// If the DockWidget is an auto hide widget that is not assigned yet,
|
||||
// then we need to delete the auto hide container now
|
||||
if (dockWidget->isAutoHide())
|
||||
dockWidget->autoHideDockContainer()->cleanupAndDelete();
|
||||
|
||||
dockWidget->flagAsUnassigned();
|
||||
emit dockWidget->viewToggled(false);
|
||||
} else {
|
||||
dockWidget->toggleViewInternal(!dockWidget->property(internal::closedProperty).toBool());
|
||||
dockWidget->toggleViewInternal(
|
||||
!dockWidget->property(internal::g_closedProperty).toBool());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -251,7 +286,7 @@ void DockManagerPrivate::restoreDockAreasIndices()
|
||||
|
||||
void DockManagerPrivate::emitTopLevelEvents()
|
||||
{
|
||||
// Finally we need to send the topLevelChanged() signals for all dock widgets if top
|
||||
// Finally we need to send the topLevelChanged() signal for all dock widgets if top
|
||||
// level changed.
|
||||
for (auto dockContainer : std::as_const(m_containers)) {
|
||||
DockWidget *topLevelDockWidget = dockContainer->topLevelDockWidget();
|
||||
@@ -288,6 +323,7 @@ bool DockManagerPrivate::restoreState(const QByteArray &state, int version)
|
||||
restoreDockWidgetsOpenState();
|
||||
restoreDockAreasIndices();
|
||||
emitTopLevelEvents();
|
||||
q->dumpLayout();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -301,6 +337,7 @@ DockManager::DockManager(QWidget *parent)
|
||||
});
|
||||
|
||||
createRootSplitter();
|
||||
createSideTabBarWidgets();
|
||||
QMainWindow *mainWindow = qobject_cast<QMainWindow *>(parent);
|
||||
if (mainWindow)
|
||||
mainWindow->setCentralWidget(this);
|
||||
@@ -311,6 +348,17 @@ DockManager::DockManager(QWidget *parent)
|
||||
|
||||
if (DockManager::configFlags().testFlag(DockManager::FocusHighlighting))
|
||||
d->m_focusController = new DockFocusController(this);
|
||||
|
||||
window()->installEventFilter(this);
|
||||
|
||||
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
|
||||
connect(qApp, &QApplication::focusWindowChanged, this, [](QWindow *focusWindow) {
|
||||
// Bring modal dialogs to foreground to ensure that they are in front of any
|
||||
// floating dock widget.
|
||||
if (focusWindow && focusWindow->isModal())
|
||||
focusWindow->raise();
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
DockManager::~DockManager()
|
||||
@@ -319,6 +367,18 @@ DockManager::~DockManager()
|
||||
save();
|
||||
saveStartupWorkspace();
|
||||
|
||||
// Fix memory leaks, see https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/issues/307
|
||||
std::vector<ADS::DockAreaWidget *> areas;
|
||||
for (int i = 0; i != dockAreaCount(); ++i)
|
||||
areas.push_back(dockArea(i));
|
||||
|
||||
for (auto area : areas) {
|
||||
for (auto widget : area->dockWidgets())
|
||||
delete widget;
|
||||
|
||||
delete area;
|
||||
}
|
||||
|
||||
// Using a temporary vector since the destructor of FloatingDockWidgetContainer
|
||||
// alters d->m_floatingWidgets.
|
||||
const auto copy = d->m_floatingWidgets;
|
||||
@@ -334,21 +394,41 @@ DockManager::ConfigFlags DockManager::configFlags()
|
||||
return g_staticConfigFlags;
|
||||
}
|
||||
|
||||
DockManager::AutoHideFlags DockManager::autoHideConfigFlags()
|
||||
{
|
||||
return g_staticAutoHideConfigFlags;
|
||||
}
|
||||
|
||||
void DockManager::setConfigFlags(const ConfigFlags flags)
|
||||
{
|
||||
g_staticConfigFlags = flags;
|
||||
}
|
||||
|
||||
void DockManager::setAutoHideConfigFlags(const AutoHideFlags flags)
|
||||
{
|
||||
g_staticAutoHideConfigFlags = flags;
|
||||
}
|
||||
|
||||
void DockManager::setConfigFlag(eConfigFlag flag, bool on)
|
||||
{
|
||||
internal::setFlag(g_staticConfigFlags, flag, on);
|
||||
}
|
||||
|
||||
void DockManager::setAutoHideConfigFlag(eAutoHideFlag flag, bool on)
|
||||
{
|
||||
internal::setFlag(g_staticAutoHideConfigFlags, flag, on);
|
||||
}
|
||||
|
||||
bool DockManager::testConfigFlag(eConfigFlag flag)
|
||||
{
|
||||
return configFlags().testFlag(flag);
|
||||
}
|
||||
|
||||
bool DockManager::testAutoHideConfigFlag(eAutoHideFlag flag)
|
||||
{
|
||||
return autoHideConfigFlags().testFlag(flag);
|
||||
}
|
||||
|
||||
IconProvider &DockManager::iconProvider()
|
||||
{
|
||||
static IconProvider instance;
|
||||
@@ -372,10 +452,9 @@ void DockManager::setWorkspacePresetsPath(const QString &path)
|
||||
|
||||
void DockManager::initialize()
|
||||
{
|
||||
qCInfo(adsLog) << "Initialize DockManager";
|
||||
qCInfo(adsLog) << Q_FUNC_INFO;
|
||||
|
||||
// Can't continue if settings are not set yet
|
||||
QTC_ASSERT(d->m_settings, return);
|
||||
QTC_ASSERT(d->m_settings, return); // Can't continue if settings are not set yet
|
||||
|
||||
syncWorkspacePresets();
|
||||
prepareWorkspaces();
|
||||
@@ -414,10 +493,40 @@ void DockManager::initialize()
|
||||
|
||||
DockAreaWidget *DockManager::addDockWidget(DockWidgetArea area,
|
||||
DockWidget *dockWidget,
|
||||
DockAreaWidget *dockAreaWidget)
|
||||
DockAreaWidget *dockAreaWidget,
|
||||
int index)
|
||||
{
|
||||
d->m_dockWidgetsMap.insert(dockWidget->objectName(), dockWidget);
|
||||
return DockContainerWidget::addDockWidget(area, dockWidget, dockAreaWidget);
|
||||
auto container = dockAreaWidget ? dockAreaWidget->dockContainer() : this;
|
||||
auto dockArea = container->addDockWidget(area, dockWidget, dockAreaWidget, index);
|
||||
emit dockWidgetAdded(dockWidget);
|
||||
return dockArea;
|
||||
}
|
||||
|
||||
DockAreaWidget *DockManager::addDockWidgetToContainer(DockWidgetArea area,
|
||||
DockWidget *dockWidget,
|
||||
DockContainerWidget *dockContainerWidget)
|
||||
{
|
||||
d->m_dockWidgetsMap.insert(dockWidget->objectName(), dockWidget);
|
||||
auto dockArea = dockContainerWidget->addDockWidget(area, dockWidget);
|
||||
emit dockWidgetAdded(dockWidget);
|
||||
return dockArea;
|
||||
}
|
||||
|
||||
AutoHideDockContainer *DockManager::addAutoHideDockWidget(SideBarLocation location,
|
||||
DockWidget *dockWidget)
|
||||
{
|
||||
return addAutoHideDockWidgetToContainer(location, dockWidget, this);
|
||||
}
|
||||
|
||||
AutoHideDockContainer *DockManager::addAutoHideDockWidgetToContainer(
|
||||
SideBarLocation location, DockWidget *dockWidget, DockContainerWidget *dockContainerWidget)
|
||||
{
|
||||
d->m_dockWidgetsMap.insert(dockWidget->objectName(), dockWidget);
|
||||
auto container = dockContainerWidget->createAndSetupAutoHideContainer(location, dockWidget);
|
||||
container->collapseView(true);
|
||||
emit dockWidgetAdded(dockWidget);
|
||||
return container;
|
||||
}
|
||||
|
||||
DockAreaWidget *DockManager::addDockWidgetTab(DockWidgetArea area, DockWidget *dockWidget)
|
||||
@@ -426,15 +535,16 @@ DockAreaWidget *DockManager::addDockWidgetTab(DockWidgetArea area, DockWidget *d
|
||||
if (areaWidget)
|
||||
return addDockWidget(ADS::CenterDockWidgetArea, dockWidget, areaWidget);
|
||||
else if (!openedDockAreas().isEmpty())
|
||||
return addDockWidget(area, dockWidget, openedDockAreas().constLast());
|
||||
return addDockWidget(area, dockWidget, openedDockAreas().constLast()); // TODO
|
||||
else
|
||||
return addDockWidget(area, dockWidget, nullptr);
|
||||
}
|
||||
|
||||
DockAreaWidget *DockManager::addDockWidgetTabToArea(DockWidget *dockWidget,
|
||||
DockAreaWidget *dockAreaWidget)
|
||||
DockAreaWidget *dockAreaWidget,
|
||||
int index)
|
||||
{
|
||||
return addDockWidget(ADS::CenterDockWidgetArea, dockWidget, dockAreaWidget);
|
||||
return addDockWidget(ADS::CenterDockWidgetArea, dockWidget, dockAreaWidget, index);
|
||||
}
|
||||
|
||||
FloatingDockContainer *DockManager::addDockWidgetFloating(DockWidget *dockWidget)
|
||||
@@ -452,6 +562,7 @@ FloatingDockContainer *DockManager::addDockWidgetFloating(DockWidget *dockWidget
|
||||
else
|
||||
d->m_uninitializedFloatingWidgets.append(floatingWidget);
|
||||
|
||||
emit dockWidgetAdded(dockWidget);
|
||||
return floatingWidget;
|
||||
}
|
||||
|
||||
@@ -465,6 +576,7 @@ void DockManager::removeDockWidget(DockWidget *dockWidget)
|
||||
emit dockWidgetAboutToBeRemoved(dockWidget);
|
||||
d->m_dockWidgetsMap.remove(dockWidget->objectName());
|
||||
DockContainerWidget::removeDockWidget(dockWidget);
|
||||
dockWidget->setDockManager(nullptr);
|
||||
emit dockWidgetRemoved(dockWidget);
|
||||
}
|
||||
|
||||
@@ -503,6 +615,10 @@ QByteArray DockManager::saveState(const QString &displayName, int version) const
|
||||
stream.writeAttribute("userVersion", QString::number(version));
|
||||
stream.writeAttribute("containers", QString::number(d->m_containers.count()));
|
||||
stream.writeAttribute(workspaceDisplayNameAttribute.toString(), displayName);
|
||||
|
||||
if (d->m_centralWidget)
|
||||
stream.writeAttribute("centralWidget", d->m_centralWidget->objectName());
|
||||
|
||||
for (auto container : std::as_const(d->m_containers))
|
||||
container->saveState(stream);
|
||||
|
||||
@@ -544,12 +660,185 @@ bool DockManager::isRestoringState() const
|
||||
return d->m_restoringState;
|
||||
}
|
||||
|
||||
bool DockManager::isLeavingMinimizedState() const
|
||||
{
|
||||
return d->m_isLeavingMinimized;
|
||||
}
|
||||
|
||||
bool DockManager::eventFilter(QObject *obj, QEvent *event)
|
||||
{
|
||||
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
|
||||
// Emulate Qt:Tool behavior.
|
||||
/*
|
||||
// Window always on top of the MainWindow.
|
||||
if (event->type() == QEvent::WindowActivate) {
|
||||
for (auto &floatingWidget : floatingWidgets()) {
|
||||
if (!floatingWidget->isVisible() || window()->isMinimized())
|
||||
continue;
|
||||
|
||||
// setWindowFlags(Qt::WindowStaysOnTopHint) will hide the window and thus requires
|
||||
// a show call. This then leads to flickering and a nasty endless loop (also buggy
|
||||
// behavior on Ubuntu). So we just do it ourself.
|
||||
floatingWidget->setWindowFlag(Qt::WindowStaysOnTopHint, true);
|
||||
}
|
||||
} else if (event->type() == QEvent::WindowDeactivate) {
|
||||
for (auto &floatingWidget : floatingWidgets()) {
|
||||
if (!floatingWidget->isVisible() || window()->isMinimized())
|
||||
continue;
|
||||
|
||||
floatingWidget->setWindowFlag(Qt::WindowStaysOnTopHint, false);
|
||||
|
||||
floatingWidget->raise();
|
||||
}
|
||||
}
|
||||
*/
|
||||
// Sync minimize with MainWindow
|
||||
if (event->type() == QEvent::WindowStateChange) {
|
||||
for (auto &floatingWidget : floatingWidgets()) {
|
||||
if (!floatingWidget->isVisible())
|
||||
continue;
|
||||
|
||||
if (window()->isMinimized())
|
||||
floatingWidget->showMinimized();
|
||||
else
|
||||
floatingWidget->setWindowState(floatingWidget->windowState()
|
||||
& (~Qt::WindowMinimized));
|
||||
}
|
||||
if (!window()->isMinimized())
|
||||
QApplication::setActiveWindow(window());
|
||||
}
|
||||
return Super::eventFilter(obj, event);
|
||||
|
||||
#else
|
||||
if (event->type() == QEvent::WindowStateChange) {
|
||||
QWindowStateChangeEvent *ev = static_cast<QWindowStateChangeEvent *>(event);
|
||||
if (ev->oldState().testFlag(Qt::WindowMinimized)) {
|
||||
d->m_isLeavingMinimized = true;
|
||||
QMetaObject::invokeMethod(this, "endLeavingMinimizedState", Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
return Super::eventFilter(obj, event);
|
||||
#endif
|
||||
}
|
||||
|
||||
DockWidget *DockManager::focusedDockWidget() const
|
||||
{
|
||||
if (!d->m_focusController)
|
||||
return nullptr;
|
||||
else
|
||||
return d->m_focusController->focusedDockWidget();
|
||||
}
|
||||
|
||||
QList<int> DockManager::splitterSizes(DockAreaWidget *containedArea) const
|
||||
{
|
||||
if (containedArea) {
|
||||
auto splitter = internal::findParent<DockSplitter *>(containedArea);
|
||||
if (splitter)
|
||||
return splitter->sizes();
|
||||
}
|
||||
|
||||
return QList<int>();
|
||||
}
|
||||
|
||||
void DockManager::setSplitterSizes(DockAreaWidget *containedArea, const QList<int> &sizes)
|
||||
{
|
||||
if (!containedArea)
|
||||
return;
|
||||
|
||||
auto splitter = internal::findParent<DockSplitter *>(containedArea);
|
||||
if (splitter && splitter->count() == sizes.count())
|
||||
splitter->setSizes(sizes);
|
||||
}
|
||||
|
||||
void DockManager::setFloatingContainersTitle(const QString &title)
|
||||
{
|
||||
g_floatingContainersTitle = title;
|
||||
}
|
||||
|
||||
QString DockManager::floatingContainersTitle()
|
||||
{
|
||||
if (g_floatingContainersTitle.isEmpty())
|
||||
return qApp->applicationDisplayName();
|
||||
|
||||
return g_floatingContainersTitle;
|
||||
}
|
||||
|
||||
DockWidget *DockManager::centralWidget() const
|
||||
{
|
||||
return d->m_centralWidget;
|
||||
}
|
||||
|
||||
DockAreaWidget *DockManager::setCentralWidget(DockWidget *widget)
|
||||
{
|
||||
if (!widget) {
|
||||
d->m_centralWidget = nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Setting a new central widget is now allowed if there is already a central widget or
|
||||
// if there are already other dock widgets.
|
||||
if (d->m_centralWidget) {
|
||||
qWarning(
|
||||
"Setting a central widget not possible because there is already a central widget.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Setting a central widget is now allowed if there are already other dock widgets.
|
||||
if (!d->m_dockWidgetsMap.isEmpty()) {
|
||||
qWarning("Setting a central widget not possible - the central widget need to be the first "
|
||||
"dock widget that is added to the dock manager.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
widget->setFeature(DockWidget::DockWidgetClosable, false);
|
||||
widget->setFeature(DockWidget::DockWidgetMovable, false);
|
||||
widget->setFeature(DockWidget::DockWidgetFloatable, false);
|
||||
widget->setFeature(DockWidget::DockWidgetPinnable, false);
|
||||
d->m_centralWidget = widget;
|
||||
DockAreaWidget *centralArea = addDockWidget(CenterDockWidgetArea, widget);
|
||||
centralArea->setDockAreaFlag(DockAreaWidget::eDockAreaFlag::HideSingleWidgetTitleBar, true);
|
||||
return centralArea;
|
||||
}
|
||||
|
||||
void DockManager::setDockWidgetFocused(DockWidget *dockWidget)
|
||||
{
|
||||
if (d->m_focusController)
|
||||
d->m_focusController->setDockWidgetFocused(dockWidget);
|
||||
}
|
||||
|
||||
void DockManager::hideManagerAndFloatingWidgets()
|
||||
{
|
||||
hide();
|
||||
|
||||
d->m_hiddenFloatingWidgets.clear();
|
||||
// Hide updates of floating widgets from user.
|
||||
for (auto &floatingWidget : d->m_floatingWidgets) {
|
||||
if (floatingWidget->isVisible()) {
|
||||
QList<DockWidget *> visibleWidgets;
|
||||
for (auto dockWidget : floatingWidget->dockWidgets()) {
|
||||
if (dockWidget->toggleViewAction()->isChecked())
|
||||
visibleWidgets.push_back(dockWidget);
|
||||
}
|
||||
|
||||
// Save as floating widget to be shown when DockManager will be shown back.
|
||||
d->m_hiddenFloatingWidgets.push_back(floatingWidget);
|
||||
floatingWidget->hide();
|
||||
|
||||
// Hidding floating widget automatically marked contained DockWidgets as hidden
|
||||
// but they must remain marked as visible as we want them to be restored visible
|
||||
// when DockManager will be shown back.
|
||||
for (auto dockWidget : visibleWidgets)
|
||||
dockWidget->toggleViewAction()->setChecked(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DockManager::endLeavingMinimizedState()
|
||||
{
|
||||
d->m_isLeavingMinimized = false;
|
||||
this->activateWindow();
|
||||
}
|
||||
|
||||
void DockManager::registerFloatingWidget(FloatingDockContainer *floatingWidget)
|
||||
{
|
||||
d->m_floatingWidgets.append(floatingWidget);
|
||||
@@ -602,15 +891,53 @@ void DockManager::notifyDockAreaRelocation(DockAreaWidget *, DockContainerWidget
|
||||
void DockManager::showEvent(QShowEvent *event)
|
||||
{
|
||||
Super::showEvent(event);
|
||||
// Fix issue #380
|
||||
restoreHiddenFloatingWidgets();
|
||||
|
||||
if (d->m_uninitializedFloatingWidgets.empty())
|
||||
return;
|
||||
|
||||
for (auto floatingWidget : std::as_const(d->m_uninitializedFloatingWidgets))
|
||||
floatingWidget->show();
|
||||
for (auto floatingWidget : std::as_const(d->m_uninitializedFloatingWidgets)) {
|
||||
// Check, if someone closed a floating dock widget before the dock manager is shown.
|
||||
if (floatingWidget->dockContainer()->hasOpenDockAreas())
|
||||
floatingWidget->show();
|
||||
}
|
||||
|
||||
d->m_uninitializedFloatingWidgets.clear();
|
||||
}
|
||||
|
||||
DockFocusController *DockManager::dockFocusController() const
|
||||
{
|
||||
return d->m_focusController;
|
||||
}
|
||||
|
||||
void DockManager::restoreHiddenFloatingWidgets()
|
||||
{
|
||||
if (d->m_hiddenFloatingWidgets.isEmpty())
|
||||
return;
|
||||
|
||||
// Restore floating widgets that were hidden upon hideManagerAndFloatingWidgets
|
||||
for (auto &floatingWidget : d->m_hiddenFloatingWidgets) {
|
||||
bool hasDockWidgetVisible = false;
|
||||
|
||||
// Needed to prevent FloatingDockContainer being shown empty
|
||||
// Could make sense to move this to FloatingDockContainer::showEvent(QShowEvent *event)
|
||||
// if experiencing FloatingDockContainer being shown empty in other situations, but let's keep
|
||||
// it here for now to make sure changes to fix Issue #380 does not impact existing behaviours
|
||||
for (auto dockWidget : floatingWidget->dockWidgets()) {
|
||||
if (dockWidget->toggleViewAction()->isChecked()) {
|
||||
dockWidget->toggleView(true);
|
||||
hasDockWidgetVisible = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasDockWidgetVisible)
|
||||
floatingWidget->show();
|
||||
}
|
||||
|
||||
d->m_hiddenFloatingWidgets.clear();
|
||||
}
|
||||
|
||||
Workspace *DockManager::activeWorkspace() const
|
||||
{
|
||||
return &d->m_workspace;
|
||||
|
@@ -46,6 +46,10 @@ class DockWidgetTab;
|
||||
class DockWidgetTabPrivate;
|
||||
struct DockAreaWidgetPrivate;
|
||||
class IconProvider;
|
||||
class DockFocusController;
|
||||
class AutoHideSideBar;
|
||||
class AutoHideTab;
|
||||
struct AutoHideTabPrivate;
|
||||
|
||||
inline constexpr QStringView workspaceFolderName{u"workspaces"};
|
||||
inline constexpr QStringView workspaceFileExtension{u"wrk"};
|
||||
@@ -80,6 +84,10 @@ private:
|
||||
friend class FloatingDragPreview;
|
||||
friend class FloatingDragPreviewPrivate;
|
||||
friend class DockAreaTitleBar;
|
||||
friend class AutoHideDockContainer;
|
||||
friend AutoHideSideBar;
|
||||
friend AutoHideTab;
|
||||
friend AutoHideTabPrivate;
|
||||
|
||||
public:
|
||||
using Super = DockContainerWidget;
|
||||
@@ -105,8 +113,6 @@ public:
|
||||
= 0x0080, //!< if this flag is set, then all tabs that are closable show a close button
|
||||
RetainTabSizeWhenCloseButtonHidden
|
||||
= 0x0100, //!< if this flag is set, the space for the close button is reserved even if the close button is not visible
|
||||
OpaqueUndocking
|
||||
= 0x0200, ///< If enabled, the widgets are immediately undocked into floating widgets, if disabled, only a draw preview is undocked and the real undocking is deferred until the mouse is released
|
||||
DragPreviewIsDynamic
|
||||
= 0x0400, ///< If opaque undocking is disabled, this flag defines the behavior of the drag preview window, if this flag is enabled, the preview will be adjusted dynamically to the drop area
|
||||
DragPreviewShowsContentPixmap
|
||||
@@ -117,34 +123,38 @@ public:
|
||||
= 0x2000, ///< If this option is enabled, the tab of a dock widget is always displayed - even if it is the only visible dock widget in a floating widget.
|
||||
DockAreaHasUndockButton = 0x4000, //!< If the flag is set each dock area has an undock button
|
||||
DockAreaHasTabsMenuButton
|
||||
= 0x8000, //!< If the flag is set each dock area has a tabs menu button
|
||||
= 0x8000, //!< If the flag is set each dock area has a tabs menu button
|
||||
DockAreaHideDisabledButtons
|
||||
= 0x10000, //!< If the flag is set disabled dock area buttons will not appear on the tollbar at all (enabling them will bring them back)
|
||||
= 0x10000, //!< If the flag is set disabled dock area buttons will not appear on the toolbar at all (enabling them will bring them back)
|
||||
DockAreaDynamicTabsMenuButtonVisibility
|
||||
= 0x20000, //!< If the flag is set, the tabs menu button will be shown only when it is required - that means, if the tabs are elided. If the tabs are not elided, it is hidden
|
||||
FloatingContainerHasWidgetTitle
|
||||
= 0x40000, //!< If set, the Floating Widget window title reflects the title of the current dock widget otherwise it displays application name as window title
|
||||
= 0x40000, //!< If set, the Floating Widget window title reflects the title of the current dock widget otherwise it displays the title set with `CDockManager::setFloatingContainersTitle` or application name as window title
|
||||
FloatingContainerHasWidgetIcon
|
||||
= 0x80000, //!< If set, the Floating Widget icon reflects the icon of the current dock widget otherwise it displays application icon
|
||||
HideSingleCentralWidgetTitleBar
|
||||
= 0x100000, //!< If there is only one single visible dock widget in the main dock container (the dock manager) and if this flag is set, then the titlebar of this dock widget will be hidden
|
||||
//!< this only makes sense for non draggable and non floatable widgets and enables the creation of some kind of "central" widget
|
||||
|
||||
FocusHighlighting
|
||||
= 0x200000, //!< enables styling of focused dock widget tabs or floating widget titlebar
|
||||
EqualSplitOnInsertion
|
||||
= 0x400000, ///!< if enabled, the space is equally distributed to all widgets in a splitter
|
||||
= 0x400000, ///!< if enabled, the space is equally distributed to all widgets in a splitter
|
||||
|
||||
MiddleMouseButtonClosesTab
|
||||
= 0x2000000, //! If the flag is set, the user can use the mouse middle button to close the tab under the mouse
|
||||
|
||||
DefaultDockAreaButtons
|
||||
= DockAreaHasCloseButton | DockAreaHasUndockButton
|
||||
| DockAreaHasTabsMenuButton, ///< default configuration of dock area title bar buttons
|
||||
|
||||
DefaultBaseConfig = DefaultDockAreaButtons | ActiveTabHasCloseButton
|
||||
| XmlAutoFormattingEnabled
|
||||
| FloatingContainerHasWidgetTitle, ///< default base configuration settings
|
||||
DefaultBaseConfig
|
||||
= DefaultDockAreaButtons | ActiveTabHasCloseButton | XmlAutoFormattingEnabled
|
||||
| FloatingContainerHasWidgetTitle, ///< default base configuration settings
|
||||
|
||||
DefaultOpaqueConfig
|
||||
= DefaultBaseConfig | OpaqueSplitterResize
|
||||
| OpaqueUndocking, ///< the default configuration with opaque operations - this may cause issues if ActiveX or Qt 3D windows are involved
|
||||
| DragPreviewShowsContentPixmap, ///< the default configuration for non opaque operations
|
||||
|
||||
DefaultNonOpaqueConfig
|
||||
= DefaultBaseConfig
|
||||
@@ -156,6 +166,31 @@ public:
|
||||
};
|
||||
Q_DECLARE_FLAGS(ConfigFlags, eConfigFlag)
|
||||
|
||||
/**
|
||||
* These global configuration flags configure some dock manager auto hide settings.
|
||||
* Set the dock manager flags, before you create the dock manager instance.
|
||||
*/
|
||||
enum eAutoHideFlag {
|
||||
AutoHideFeatureEnabled = 0x01, //!< enables / disables auto hide feature
|
||||
DockAreaHasAutoHideButton
|
||||
= 0x02, //!< If the flag is set each dock area has a auto hide menu button
|
||||
AutoHideButtonTogglesArea
|
||||
= 0x04, //!< If the flag is set, the auto hide button enables auto hiding for all dock widgets in an area, if disabled, only the current dock widget will be toggled
|
||||
AutoHideButtonCheckable
|
||||
= 0x08, //!< If the flag is set, the auto hide button will be checked and unchecked depending on the auto hide state. Mainly for styling purposes.
|
||||
AutoHideSideBarsIconOnly
|
||||
= 0x10, ///< show only icons in auto hide side tab - if a tab has no icon, then the text will be shown
|
||||
AutoHideShowOnMouseOver
|
||||
= 0x20, ///< show the auto hide window on mouse over tab and hide it if mouse leaves auto hide container
|
||||
AutoHideCloseButtonCollapsesDock
|
||||
= 0x40, ///< Close button of an auto hide container collapses the dock instead of hiding it completely
|
||||
|
||||
DefaultAutoHideConfig
|
||||
= AutoHideFeatureEnabled | DockAreaHasAutoHideButton
|
||||
| AutoHideCloseButtonCollapsesDock ///< the default configuration for left and right side bars
|
||||
};
|
||||
Q_DECLARE_FLAGS(AutoHideFlags, eAutoHideFlag)
|
||||
|
||||
/**
|
||||
* Default Constructor.
|
||||
* If the given parent is a QMainWindow, the dock manager sets itself as the central widget.
|
||||
@@ -174,23 +209,45 @@ public:
|
||||
*/
|
||||
static ConfigFlags configFlags();
|
||||
|
||||
/**
|
||||
* This function returns the auto hide configuration flags.
|
||||
*/
|
||||
static AutoHideFlags autoHideConfigFlags();
|
||||
|
||||
/**
|
||||
* Sets the global configuration flags for the whole docking system. Call this function before
|
||||
* you create the dock manager and before your create the first dock widget.
|
||||
*/
|
||||
static void setConfigFlags(const ConfigFlags flags);
|
||||
|
||||
/**
|
||||
* Sets the global configuration flags for the whole docking system. Call this function before
|
||||
* you create the dock manager and before your create the first dock widget.
|
||||
*/
|
||||
static void setAutoHideConfigFlags(const AutoHideFlags flags);
|
||||
|
||||
/**
|
||||
* Set a certain config flag.
|
||||
* \see setConfigFlags()
|
||||
*/
|
||||
static void setConfigFlag(eConfigFlag flag, bool on = true);
|
||||
|
||||
/**
|
||||
* Set a certain overlay config flag.
|
||||
* \see setConfigFlags()
|
||||
*/
|
||||
static void setAutoHideConfigFlag(eAutoHideFlag flag, bool on = true);
|
||||
|
||||
/**
|
||||
* Returns true if the given config flag is set.
|
||||
*/
|
||||
static bool testConfigFlag(eConfigFlag flag);
|
||||
|
||||
/**
|
||||
* Returns true if the given overlay config flag is set
|
||||
*/
|
||||
static bool testAutoHideConfigFlag(eAutoHideFlag flag);
|
||||
|
||||
/**
|
||||
* Returns the global icon provider.
|
||||
* The icon provider enables the use of custom icons in case using styleheets for icons is not
|
||||
@@ -242,7 +299,33 @@ public:
|
||||
*/
|
||||
DockAreaWidget *addDockWidget(DockWidgetArea area,
|
||||
DockWidget *dockWidget,
|
||||
DockAreaWidget *dockAreaWidget = nullptr);
|
||||
DockAreaWidget *dockAreaWidget = nullptr,
|
||||
int index = -1);
|
||||
|
||||
/**
|
||||
* Adds dockwidget into the given container.
|
||||
* This allows you to place the dock widget into a container, even if that
|
||||
* container does not yet contain a DockAreaWidget.
|
||||
* \return Returns the dock area widget that contains the new DockWidget
|
||||
*/
|
||||
DockAreaWidget *addDockWidgetToContainer(DockWidgetArea area,
|
||||
DockWidget *dockWidget,
|
||||
DockContainerWidget *dockContainerWidget);
|
||||
|
||||
/**
|
||||
* Adds an Auto-Hide widget to the dock manager container pinned to
|
||||
* the given side bar location.
|
||||
* \return Returns the CAutoHideDockContainer that contains the new DockWidget
|
||||
*/
|
||||
AutoHideDockContainer *addAutoHideDockWidget(SideBarLocation location, DockWidget *dockWidget);
|
||||
|
||||
/**
|
||||
* Adds an Auto-Hide widget to the given DockContainerWidget pinned to
|
||||
* the given side bar location in this container.
|
||||
* \return Returns the CAutoHideDockContainer that contains the new DockWidget
|
||||
*/
|
||||
AutoHideDockContainer *addAutoHideDockWidgetToContainer(
|
||||
SideBarLocation location, DockWidget *dockWidget, DockContainerWidget *dockContainerWidget);
|
||||
|
||||
/**
|
||||
* This function will add the given Dockwidget to the given dock area as a new tab. If no dock
|
||||
@@ -253,7 +336,9 @@ public:
|
||||
/**
|
||||
* This function will add the given Dockwidget to the given DockAreaWidget as a new tab.
|
||||
*/
|
||||
DockAreaWidget *addDockWidgetTabToArea(DockWidget *dockWidget, DockAreaWidget *dockAreaWidget);
|
||||
DockAreaWidget *addDockWidgetTabToArea(DockWidget *dockWidget,
|
||||
DockAreaWidget *dockAreaWidget,
|
||||
int index = -1);
|
||||
|
||||
/**
|
||||
* Adds the given DockWidget floating and returns the created FloatingDockContainer instance.
|
||||
@@ -322,11 +407,90 @@ public:
|
||||
bool isRestoringState() const;
|
||||
|
||||
/**
|
||||
* Request a focus change to the given dock widget.
|
||||
* This function only has an effect, if the flag CDockManager::FocusStyling is enabled.
|
||||
* This function returns true, if the DockManager window is restoring from minimized state.
|
||||
* The DockManager is in this state starting from the QWindowStateChangeEvent that signals the
|
||||
* state change from minimized to normal until endLeavingMinimizedState() function is called.
|
||||
*/
|
||||
bool isLeavingMinimizedState() const;
|
||||
|
||||
bool eventFilter(QObject *obj, QEvent *e) override;
|
||||
|
||||
/**
|
||||
* Returns the dock widget that has focus style in the ui or a nullptr if not dock widget is
|
||||
* painted focused. If the flag FocusHighlighting is disabled, this function always returns
|
||||
* nullptr.
|
||||
*/
|
||||
DockWidget *focusedDockWidget() const;
|
||||
|
||||
/**
|
||||
* Returns the sizes of the splitter that contains the dock area. If there is no splitter that
|
||||
* contains the area, an empty list will be returned.
|
||||
*/
|
||||
QList<int> splitterSizes(DockAreaWidget *containedArea) const;
|
||||
|
||||
/**
|
||||
* Update the sizes of a splitter
|
||||
* Programmatically updates the sizes of a given splitter by calling QSplitter::setSizes(). The
|
||||
* splitter will be the splitter that contains the supplied dock area widget. If there is not
|
||||
* splitter that contains the dock area, or the sizes supplied does not match the number of
|
||||
* children of the splitter, this method will have no effect.
|
||||
*/
|
||||
void setSplitterSizes(DockAreaWidget *containedArea, const QList<int> &sizes);
|
||||
|
||||
/**
|
||||
* Set a custom title for all FloatingContainer that does not reflect the title of the current
|
||||
* dock widget.
|
||||
*/
|
||||
static void setFloatingContainersTitle(const QString &title);
|
||||
|
||||
/**
|
||||
* Returns the title used by all FloatingContainer that does not reflect the title of the
|
||||
* current dock widget. If not title was set with setFloatingContainersTitle(), it returns
|
||||
* QGuiApplication::applicationDisplayName().
|
||||
*/
|
||||
static QString floatingContainersTitle();
|
||||
|
||||
/**
|
||||
* This function returns managers central widget or nullptr if no central widget is set.
|
||||
*/
|
||||
DockWidget *centralWidget() const;
|
||||
|
||||
/**
|
||||
* Adds dockwidget widget into the central area and marks it as central widget.
|
||||
* If central widget is set, it will be the only dock widget
|
||||
* that will resize with the dock container. A central widget if not
|
||||
* movable, floatable or closable and the titlebar of the central
|
||||
* dock area is not visible.
|
||||
* If the given widget could be set as central widget, the function returns
|
||||
* the created dock area. If the widget could not be set, because there
|
||||
* is already a central widget, this function returns a nullptr.
|
||||
* To clear the central widget, pass a nullptr to the function.
|
||||
* \note Setting a central widget is only possible if no other dock widgets
|
||||
* have been registered before. That means, this function should be the
|
||||
* first function that you call before you add other dock widgets.
|
||||
* \retval != 0 The dock area that contains the central widget
|
||||
* \retval nullptr Indicates that the given widget can not be set as central
|
||||
* widget because there is already a central widget.
|
||||
*/
|
||||
DockAreaWidget *setCentralWidget(DockWidget *widget);
|
||||
|
||||
/**
|
||||
* Request a focus change to the given dock widget. This function only has an effect, if the
|
||||
* flag DockManager::FocusStyling is enabled.
|
||||
*/
|
||||
void setDockWidgetFocused(DockWidget *dockWidget);
|
||||
|
||||
/**
|
||||
* Hide CDockManager and all floating widgets (See Issue #380). Calling regular QWidget::hide()
|
||||
* hides the DockManager but not the floating widgets.
|
||||
*/
|
||||
void hideManagerAndFloatingWidgets();
|
||||
|
||||
/**
|
||||
* Ends the isRestoringFromMinimizedState
|
||||
*/
|
||||
void endLeavingMinimizedState();
|
||||
|
||||
signals:
|
||||
/**
|
||||
* This signal is emitted if the list of workspaces changed.
|
||||
@@ -376,6 +540,12 @@ signals:
|
||||
*/
|
||||
void dockAreaCreated(ADS::DockAreaWidget *dockArea);
|
||||
|
||||
/**
|
||||
* This signal is emitted if a dock widget has been added to this
|
||||
* dock manager instance.
|
||||
*/
|
||||
void dockWidgetAdded(ADS::DockWidget *dockWidget);
|
||||
|
||||
/**
|
||||
* This signal is emitted just before removal of the given DockWidget.
|
||||
*/
|
||||
@@ -454,6 +624,17 @@ protected:
|
||||
*/
|
||||
void showEvent(QShowEvent *event) override;
|
||||
|
||||
/**
|
||||
* Access for the internal dock focus controller.
|
||||
* This function only returns a valid object, if the FocusHighlighting flag is set.
|
||||
*/
|
||||
DockFocusController *dockFocusController() const;
|
||||
|
||||
/**
|
||||
* Restore floating widgets hidden by an earlier call to hideManagerAndFloatingWidgets.
|
||||
*/
|
||||
void restoreHiddenFloatingWidgets();
|
||||
|
||||
public:
|
||||
// Workspace state
|
||||
Workspace *activeWorkspace() const;
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -52,11 +52,26 @@ public:
|
||||
*/
|
||||
DockWidgetAreas allowedAreas() const;
|
||||
|
||||
/**
|
||||
* Enable / disable a certain area
|
||||
*/
|
||||
void setAllowedArea(DockWidgetArea area, bool enable);
|
||||
|
||||
/**
|
||||
* Returns the drop area under the current cursor location
|
||||
*/
|
||||
DockWidgetArea dropAreaUnderCursor() const;
|
||||
|
||||
/**
|
||||
* If the drop area is the CenterDockWidgetArea or a sidebar area, then this function returns
|
||||
* the index of the tab under cursor. Call this function after call to dropAreaUnderCursor()
|
||||
* because this function updates the tab index.
|
||||
* A value of -1 indicates a position before the first tab and a value of tabCount() indicates
|
||||
* a position behind the last tab.
|
||||
* A value of -2 indicates an valid value
|
||||
*/
|
||||
int tabIndexUnderCursor() const;
|
||||
|
||||
/**
|
||||
* This function returns the same like dropAreaUnderCursor() if this
|
||||
* overlay is not hidden and if drop preview is enabled and returns
|
||||
|
@@ -12,61 +12,70 @@
|
||||
#include <QLoggingCategory>
|
||||
#include <QVariant>
|
||||
|
||||
namespace ADS
|
||||
namespace ADS {
|
||||
/**
|
||||
* Private dock splitter data
|
||||
*/
|
||||
struct DockSplitterPrivate
|
||||
{
|
||||
/**
|
||||
* Private dock splitter data
|
||||
*/
|
||||
struct DockSplitterPrivate
|
||||
{
|
||||
DockSplitter *q;
|
||||
int m_visibleContentCount = 0;
|
||||
DockSplitter *q;
|
||||
int m_visibleContentCount = 0;
|
||||
|
||||
DockSplitterPrivate(DockSplitter *parent)
|
||||
: q(parent)
|
||||
{}
|
||||
};
|
||||
|
||||
DockSplitter::DockSplitter(QWidget *parent)
|
||||
: QSplitter(parent)
|
||||
, d(new DockSplitterPrivate(this))
|
||||
{
|
||||
//setProperty("ads-splitter", true); // TODO
|
||||
setProperty(Utils::StyleHelper::C_MINI_SPLITTER, true);
|
||||
setChildrenCollapsible(false);
|
||||
}
|
||||
|
||||
DockSplitter::DockSplitter(Qt::Orientation orientation, QWidget *parent)
|
||||
: QSplitter(orientation, parent)
|
||||
, d(new DockSplitterPrivate(this))
|
||||
DockSplitterPrivate(DockSplitter *parent)
|
||||
: q(parent)
|
||||
{}
|
||||
};
|
||||
|
||||
DockSplitter::~DockSplitter()
|
||||
{
|
||||
qCInfo(adsLog) << Q_FUNC_INFO;
|
||||
delete d;
|
||||
}
|
||||
DockSplitter::DockSplitter(QWidget *parent)
|
||||
: QSplitter(parent)
|
||||
, d(new DockSplitterPrivate(this))
|
||||
{
|
||||
//setProperty("ads-splitter", true); // TODO
|
||||
setProperty(Utils::StyleHelper::C_MINI_SPLITTER, true);
|
||||
setChildrenCollapsible(false);
|
||||
}
|
||||
|
||||
bool DockSplitter::hasVisibleContent() const
|
||||
{
|
||||
// TODO Cache or precalculate this to speed up
|
||||
for (int i = 0; i < count(); ++i) {
|
||||
if (!widget(i)->isHidden()) {
|
||||
return true;
|
||||
}
|
||||
DockSplitter::DockSplitter(Qt::Orientation orientation, QWidget *parent)
|
||||
: QSplitter(orientation, parent)
|
||||
, d(new DockSplitterPrivate(this))
|
||||
{}
|
||||
|
||||
DockSplitter::~DockSplitter()
|
||||
{
|
||||
qCInfo(adsLog) << Q_FUNC_INFO;
|
||||
delete d;
|
||||
}
|
||||
|
||||
bool DockSplitter::hasVisibleContent() const
|
||||
{
|
||||
// TODO Cache or precalculate this to speed up
|
||||
for (int i = 0; i < count(); ++i) {
|
||||
if (!widget(i)->isHidden()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QWidget *DockSplitter::firstWidget() const
|
||||
{
|
||||
return (count() > 0) ? widget(0) : nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
QWidget *DockSplitter::firstWidget() const
|
||||
{
|
||||
return (count() > 0) ? widget(0) : nullptr;
|
||||
}
|
||||
|
||||
QWidget *DockSplitter::lastWidget() const
|
||||
{
|
||||
return (count() > 0) ? widget(count() - 1) : nullptr;
|
||||
}
|
||||
|
||||
bool DockSplitter::isResizingWithContainer() const
|
||||
{
|
||||
for (auto area : findChildren<DockAreaWidget *>()) {
|
||||
if (area->isCentralWidgetArea())
|
||||
return true;
|
||||
}
|
||||
|
||||
QWidget *DockSplitter::lastWidget() const
|
||||
{
|
||||
return (count() > 0) ? widget(count() - 1) : nullptr;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace ADS
|
||||
|
@@ -45,6 +45,11 @@ public:
|
||||
* Returns last widget of nullptr is splitter is empty
|
||||
*/
|
||||
QWidget *lastWidget() const;
|
||||
|
||||
/**
|
||||
* Returns true if the splitter contains central widget of dock manager.
|
||||
*/
|
||||
bool isResizingWithContainer() const;
|
||||
}; // class DockSplitter
|
||||
|
||||
} // namespace ADS
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -21,6 +21,9 @@ class DockContainerWidget;
|
||||
class DockAreaWidget;
|
||||
class DockContainerWidgetPrivate;
|
||||
class FloatingDockContainer;
|
||||
class AutoHideTab;
|
||||
class AutoHideDockContainer;
|
||||
class AutoHideSideBar;
|
||||
|
||||
/**
|
||||
* The QDockWidget class provides a widget that can be docked inside a
|
||||
@@ -49,6 +52,8 @@ protected:
|
||||
friend class DockWidgetTab;
|
||||
friend class DockWidgetTabPrivate;
|
||||
friend class DockAreaTitleBarPrivate;
|
||||
friend class AutoHideDockContainer;
|
||||
friend AutoHideSideBar;
|
||||
|
||||
/**
|
||||
* Assigns the dock manager that manages this dock widget
|
||||
@@ -120,14 +125,28 @@ public:
|
||||
using Super = QFrame;
|
||||
|
||||
enum DockWidgetFeature {
|
||||
DockWidgetClosable = 0x01,///< dock widget has a close button
|
||||
DockWidgetMovable = 0x02,///< dock widget is movable and can be moved to a new position in the current dock container
|
||||
DockWidgetFloatable = 0x04,
|
||||
DockWidgetDeleteOnClose = 0x08, ///< deletes the dock widget when it is closed
|
||||
CustomCloseHandling = 0x10,
|
||||
DefaultDockWidgetFeatures = DockWidgetClosable | DockWidgetMovable | DockWidgetFloatable,
|
||||
AllDockWidgetFeatures = DefaultDockWidgetFeatures | DockWidgetDeleteOnClose | CustomCloseHandling,
|
||||
NoDockWidgetFeatures = 0x00
|
||||
DockWidgetClosable = 0x001, ///< dock widget has a close button
|
||||
DockWidgetMovable
|
||||
= 0x002, ///< dock widget is movable and can be moved to a new position in the current dock container
|
||||
DockWidgetFloatable = 0x004, ///< dock widget can be dragged into a floating window
|
||||
DockWidgetDeleteOnClose = 0x008, ///< deletes the dock widget when it is closed
|
||||
CustomCloseHandling
|
||||
= 0x010, ///< clicking the close button will not close the dock widget but emits the closeRequested() signal instead
|
||||
DockWidgetFocusable = 0x020, ///< if this is enabled, a dock widget can get focus highlighting
|
||||
DockWidgetForceCloseWithArea
|
||||
= 0x040, ///< dock widget will be closed when the dock area hosting it is closed
|
||||
NoTab = 0x080, ///< dock widget tab will never be shown if this flag is set
|
||||
DeleteContentOnClose
|
||||
= 0x100, ///< deletes only the contained widget on close, keeping the dock widget intact
|
||||
///< and in place. Attempts to rebuild the contents widget on show if there is a widget factory set.
|
||||
DockWidgetPinnable
|
||||
= 0x200, ///< dock widget can be pinned and added to an auto hide dock container
|
||||
DefaultDockWidgetFeatures = DockWidgetClosable | DockWidgetMovable | DockWidgetFloatable
|
||||
| DockWidgetFocusable | DockWidgetPinnable,
|
||||
AllDockWidgetFeatures = DefaultDockWidgetFeatures | DockWidgetDeleteOnClose
|
||||
| CustomCloseHandling,
|
||||
DockWidgetAlwaysCloseAndDelete = DockWidgetForceCloseWithArea | DockWidgetDeleteOnClose,
|
||||
NoDockWidgetFeatures = 0x000
|
||||
};
|
||||
Q_DECLARE_FLAGS(DockWidgetFeatures, DockWidgetFeature)
|
||||
|
||||
@@ -158,10 +177,18 @@ public:
|
||||
* To ensure, that a dock widget does not block resizing, the dock widget
|
||||
* reimplements minimumSizeHint() function to return a very small minimum
|
||||
* size hint. If you would like to adhere the minimumSizeHint() from the
|
||||
* content widget, the set the minimumSizeHintMode() to
|
||||
* MinimumSizeHintFromContent.
|
||||
* content widget, then set the minimumSizeHintMode() to
|
||||
* MinimumSizeHintFromContent. If you would like to use the minimumSize()
|
||||
* value of the content widget or the dock widget, then you can use the
|
||||
* MinimumSizeHintFromDockWidgetMinimumSize or
|
||||
* MinimumSizeHintFromContentMinimumSize modes.
|
||||
*/
|
||||
enum eMinimumSizeHintMode { MinimumSizeHintFromDockWidget, MinimumSizeHintFromContent };
|
||||
enum eMinimumSizeHintMode {
|
||||
MinimumSizeHintFromDockWidget,
|
||||
MinimumSizeHintFromContent,
|
||||
MinimumSizeHintFromDockWidgetMinimumSize,
|
||||
MinimumSizeHintFromContentMinimumSize,
|
||||
};
|
||||
|
||||
/**
|
||||
* This mode configures the behavior of the toggle view action.
|
||||
@@ -205,7 +232,7 @@ public:
|
||||
/**
|
||||
* Sets the widget for the dock widget to widget.
|
||||
* The InsertMode defines how the widget is inserted into the dock widget.
|
||||
* The content of a dock widget should be resizable do a very small size to
|
||||
* The content of a dock widget should be resizable to a very small size to
|
||||
* prevent the dock widget from blocking the resizing. To ensure, that a
|
||||
* dock widget can be resized very well, it is better to insert the content+
|
||||
* widget into a scroll area or to provide a widget that is already a scroll
|
||||
@@ -221,6 +248,18 @@ public:
|
||||
*/
|
||||
void setWidget(QWidget *widget, eInsertMode insertMode = AutoScrollArea);
|
||||
|
||||
/**
|
||||
* Only used when the feature flag DeleteContentOnClose is set.
|
||||
* Using the flag and setting a widget factory allows to free the resources
|
||||
* of the widget of your application while retaining the position the next
|
||||
* time you want to show your widget, unlike the flag DockWidgetDeleteOnClose
|
||||
* which deletes the dock widget itself. Since we keep the dock widget, all
|
||||
* regular features of ADS should work as normal, including saving and
|
||||
* restoring the state of the docking system and using perspectives.
|
||||
*/
|
||||
using FactoryFunc = std::function<QWidget *(QWidget *)>;
|
||||
void setWidgetFactory(FactoryFunc createWidget, eInsertMode insertMode = AutoScrollArea);
|
||||
|
||||
/**
|
||||
* Remove the widget from the dock and give ownership back to the caller
|
||||
*/
|
||||
@@ -269,17 +308,50 @@ public:
|
||||
*/
|
||||
DockContainerWidget *dockContainer() const;
|
||||
|
||||
/**
|
||||
* This function return the floating DockContainer if is isFloating() is true
|
||||
* and a nullptr if this dock widget is not floating.
|
||||
*/
|
||||
FloatingDockContainer *floatingDockContainer() const;
|
||||
|
||||
/**
|
||||
* Returns the dock area widget this dock widget belongs to or 0
|
||||
* if this dock widget has not been docked yet
|
||||
*/
|
||||
DockAreaWidget *dockAreaWidget() const;
|
||||
|
||||
/**
|
||||
* Returns the side tab widget for this dock, if this dock widget is in
|
||||
* a auto hide container. If it is not in a auto hide container, then this
|
||||
* function returns a nullptr,
|
||||
*/
|
||||
AutoHideTab *sideTabWidget() const;
|
||||
|
||||
/**
|
||||
* Assign a side tab widget if this dock widget is an auto hide container
|
||||
*/
|
||||
void setSideTabWidget(AutoHideTab *sideTab) const;
|
||||
|
||||
/**
|
||||
* Returns true, if this dock widget is in an auto hide container
|
||||
*/
|
||||
bool isAutoHide() const;
|
||||
|
||||
/**
|
||||
* Returns the auto hide dock container of this dock widget or 0 if there is none.
|
||||
*/
|
||||
AutoHideDockContainer *autoHideDockContainer() const;
|
||||
|
||||
/**
|
||||
* Returns the auto hide side bar location or SideBarNone if, this is not an autohide dock widget.
|
||||
*/
|
||||
SideBarLocation autoHideLocation() const;
|
||||
|
||||
/**
|
||||
* This property holds whether the dock widget is floating.
|
||||
* A dock widget is only floating, if it is the one and only widget inside
|
||||
* of a floating container. If there are more than one dock widget in a
|
||||
* floating container, the all dock widgets are docked and not floating.
|
||||
* A dock widget is only floating, if it is the one and only widget inside of a floating
|
||||
* container. If there are more than one dock widget in a floating container, the all dock
|
||||
* widgets are docked and not floating.
|
||||
*/
|
||||
bool isFloating() const;
|
||||
|
||||
@@ -314,6 +386,16 @@ public:
|
||||
*/
|
||||
void setMinimumSizeHintMode(eMinimumSizeHintMode mode);
|
||||
|
||||
/**
|
||||
* Get the minimum size hint mode configured by setMinimumSizeHintMode
|
||||
*/
|
||||
eMinimumSizeHintMode minimumSizeHintMode() const;
|
||||
|
||||
/**
|
||||
* Returns true if the dock widget is set as central widget of it's dock manager
|
||||
*/
|
||||
bool isCentralWidget() const;
|
||||
|
||||
/**
|
||||
* Sets the dock widget icon that is shown in tabs and in toggle view
|
||||
* actions
|
||||
@@ -431,7 +513,7 @@ public: // reimplements QFrame
|
||||
|
||||
/**
|
||||
* This property controls whether the dock widget is open or closed.
|
||||
* The toogleViewAction triggers this slot
|
||||
* The toggleViewAction triggers this slot
|
||||
*/
|
||||
void toggleView(bool open = true);
|
||||
|
||||
@@ -468,26 +550,42 @@ public: // reimplements QFrame
|
||||
*/
|
||||
void closeDockWidget();
|
||||
|
||||
/**
|
||||
* Request closing of the dock widget.
|
||||
* For DockWidget with default close handling, the function does the same like clodeDockWidget()
|
||||
* but if the flag CustomCloseHandling is set, the function only emits the closeRequested() signal.
|
||||
*/
|
||||
void requestCloseDockWidget();
|
||||
|
||||
/**
|
||||
* Shows the widget in full-screen mode.
|
||||
* Normally this function only affects windows. To make the interface
|
||||
* compatible to QDockWidget, this function also maximizes a floating
|
||||
* dock widget.
|
||||
* Normally this function only affects windows. To make the interface compatible to QDockWidget,
|
||||
* this function also maximizes a floating dock widget.
|
||||
*
|
||||
* \note Full-screen mode works fine under Windows, but has certain
|
||||
* problems (doe not work) under X (Linux). These problems are due to
|
||||
* limitations of the ICCCM protocol that specifies the communication
|
||||
* between X11 clients and the window manager. ICCCM simply does not
|
||||
* \note Full-screen mode works fine under Windows, but has certain problems (doe not work)
|
||||
* under X (Linux). These problems are due to limitations of the ICCCM protocol that specifies
|
||||
* the communication between X11 clients and the window manager. ICCCM simply does not
|
||||
* understand the concept of non-decorated full-screen windows.
|
||||
*/
|
||||
void showFullScreen();
|
||||
|
||||
/**
|
||||
* This function complements showFullScreen() to restore the widget
|
||||
* after it has been in full screen mode.
|
||||
* This function complements showFullScreen() to restore the widget after it has been in full
|
||||
* screen mode.
|
||||
*/
|
||||
void showNormal();
|
||||
|
||||
/**
|
||||
* Sets the dock widget into auto hide mode if this feature is enabled
|
||||
* via CDockManager::setAutoHideFlags(CDockManager::AutoHideFeatureEnabled)
|
||||
*/
|
||||
void setAutoHide(bool enable, SideBarLocation location = SideBarNone, int tabIndex = -1);
|
||||
|
||||
/**
|
||||
* Switches the dock widget to auto hide mode or vice versa depending on its current state.
|
||||
*/
|
||||
void toggleAutoHide(SideBarLocation location = SideBarNone);
|
||||
|
||||
signals:
|
||||
/**
|
||||
* This signal is emitted if the dock widget is opened or closed
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -35,24 +35,27 @@ private:
|
||||
bool m_focus;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A dock widget tab that shows a title and an icon.
|
||||
* The dock widget tab is shown in the dock area title bar to switch between
|
||||
* tabbed dock widgets
|
||||
* The dock widget tab is shown in the dock area title bar to switch between tabbed dock widgets.
|
||||
*/
|
||||
class ADS_EXPORT DockWidgetTab : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool activeTab READ isActiveTab WRITE setActiveTab NOTIFY activeTabChanged)
|
||||
Q_PROPERTY(QSize iconSize READ iconSize WRITE setIconSize NOTIFY iconSizeChanged)
|
||||
|
||||
private:
|
||||
DockWidgetTabPrivate *d; ///< private data (pimpl)
|
||||
friend class DockWidgetTabPrivate;
|
||||
friend class DockWidget;
|
||||
friend class DockManager;
|
||||
friend class AutoHideDockContainer;
|
||||
|
||||
void onDockWidgetFeaturesChanged();
|
||||
void detachDockWidget();
|
||||
void autoHideDockWidget();
|
||||
void onAutoHideToActionClicked();
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
@@ -95,8 +98,7 @@ public:
|
||||
DockWidget *dockWidget() const;
|
||||
|
||||
/**
|
||||
* Sets the dock area widget the dockWidget returned by dockWidget()
|
||||
* function belongs to.
|
||||
* Sets the dock area widget the dockWidget returned by dockWidget() function belongs to.
|
||||
*/
|
||||
void setDockAreaWidget(DockAreaWidget *dockArea);
|
||||
|
||||
@@ -152,6 +154,19 @@ public:
|
||||
*/
|
||||
void updateStyle();
|
||||
|
||||
/**
|
||||
* Returns the icon size.
|
||||
* If no explicit icon size has been set, the function returns an invalid QSize.
|
||||
*/
|
||||
QSize iconSize() const;
|
||||
|
||||
/**
|
||||
* Set an explicit icon size.
|
||||
* If no icon size has been set explicitly, than the tab sets the icon size depending
|
||||
* on the style.
|
||||
*/
|
||||
void setIconSize(const QSize &size);
|
||||
|
||||
void setVisible(bool visible) override;
|
||||
|
||||
signals:
|
||||
@@ -161,6 +176,7 @@ signals:
|
||||
void closeOtherTabsRequested();
|
||||
void moved(const QPoint &globalPosition);
|
||||
void elidedChanged(bool elided);
|
||||
void iconSizeChanged();
|
||||
}; // class DockWidgetTab
|
||||
|
||||
} // namespace ADS
|
||||
|
@@ -6,143 +6,145 @@
|
||||
#include <QMouseEvent>
|
||||
|
||||
namespace ADS {
|
||||
/**
|
||||
* Private data of public ElidingLabel
|
||||
*/
|
||||
struct ElidingLabelPrivate
|
||||
{
|
||||
ElidingLabel *q;
|
||||
Qt::TextElideMode m_elideMode = Qt::ElideNone;
|
||||
QString m_text;
|
||||
bool m_isElided = false;
|
||||
|
||||
ElidingLabelPrivate(ElidingLabel *parent)
|
||||
: q(parent)
|
||||
{}
|
||||
/**
|
||||
* Private data of public ElidingLabel
|
||||
*/
|
||||
class ElidingLabelPrivate
|
||||
{
|
||||
public:
|
||||
ElidingLabel *q;
|
||||
Qt::TextElideMode m_elideMode = Qt::ElideNone;
|
||||
QString m_text;
|
||||
bool m_isElided = false;
|
||||
|
||||
void elideText(int width);
|
||||
|
||||
/**
|
||||
* Convenience function to check if the
|
||||
*/
|
||||
bool isModeElideNone() const { return Qt::ElideNone == m_elideMode; }
|
||||
};
|
||||
|
||||
void ElidingLabelPrivate::elideText(int width)
|
||||
{
|
||||
if (isModeElideNone())
|
||||
return;
|
||||
|
||||
QFontMetrics fm = q->fontMetrics();
|
||||
QString str = fm.elidedText(m_text, m_elideMode, width - q->margin() * 2 - q->indent());
|
||||
if (str == u'\u2026')
|
||||
str = m_text.at(0);
|
||||
|
||||
bool wasElided = m_isElided;
|
||||
m_isElided = str != m_text;
|
||||
if (m_isElided != wasElided)
|
||||
emit q->elidedChanged(m_isElided);
|
||||
|
||||
q->QLabel::setText(str);
|
||||
}
|
||||
|
||||
ElidingLabel::ElidingLabel(QWidget *parent, Qt::WindowFlags flags)
|
||||
: QLabel(parent, flags)
|
||||
, d(new ElidingLabelPrivate(this))
|
||||
ElidingLabelPrivate(ElidingLabel *parent)
|
||||
: q(parent)
|
||||
{}
|
||||
|
||||
ElidingLabel::ElidingLabel(const QString &text, QWidget *parent, Qt::WindowFlags flags)
|
||||
: QLabel(text, parent, flags)
|
||||
, d(new ElidingLabelPrivate(this))
|
||||
{
|
||||
d->m_text = text;
|
||||
void elideText(int width);
|
||||
|
||||
/**
|
||||
* Convenience function to check if the
|
||||
*/
|
||||
bool isModeElideNone() const { return Qt::ElideNone == m_elideMode; }
|
||||
};
|
||||
|
||||
void ElidingLabelPrivate::elideText(int width)
|
||||
{
|
||||
if (isModeElideNone())
|
||||
return;
|
||||
|
||||
QFontMetrics fm = q->fontMetrics();
|
||||
QString str = fm.elidedText(m_text, m_elideMode, width - q->margin() * 2 - q->indent());
|
||||
if (str == u'\u2026')
|
||||
str = m_text.at(0);
|
||||
|
||||
bool wasElided = m_isElided;
|
||||
m_isElided = (str != m_text);
|
||||
if (m_isElided != wasElided)
|
||||
emit q->elidedChanged(m_isElided);
|
||||
|
||||
q->QLabel::setText(str);
|
||||
}
|
||||
|
||||
ElidingLabel::ElidingLabel(QWidget *parent, Qt::WindowFlags flags)
|
||||
: QLabel(parent, flags)
|
||||
, d(new ElidingLabelPrivate(this))
|
||||
{}
|
||||
|
||||
ElidingLabel::ElidingLabel(const QString &text, QWidget *parent, Qt::WindowFlags flags)
|
||||
: QLabel(text, parent, flags)
|
||||
, d(new ElidingLabelPrivate(this))
|
||||
{
|
||||
d->m_text = text;
|
||||
internal::setToolTip(this, text);
|
||||
}
|
||||
|
||||
ElidingLabel::~ElidingLabel()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
Qt::TextElideMode ElidingLabel::elideMode() const
|
||||
{
|
||||
return d->m_elideMode;
|
||||
}
|
||||
|
||||
void ElidingLabel::setElideMode(Qt::TextElideMode mode)
|
||||
{
|
||||
d->m_elideMode = mode;
|
||||
d->elideText(size().width());
|
||||
}
|
||||
|
||||
bool ElidingLabel::isElided() const
|
||||
{
|
||||
return d->m_isElided;
|
||||
}
|
||||
|
||||
void ElidingLabel::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
Super::mouseReleaseEvent(event);
|
||||
if (event->button() != Qt::LeftButton)
|
||||
return;
|
||||
|
||||
emit clicked();
|
||||
}
|
||||
|
||||
void ElidingLabel::mouseDoubleClickEvent(QMouseEvent *event)
|
||||
{
|
||||
Q_UNUSED(event)
|
||||
emit doubleClicked();
|
||||
Super::mouseDoubleClickEvent(event);
|
||||
}
|
||||
|
||||
void ElidingLabel::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
if (!d->isModeElideNone())
|
||||
d->elideText(event->size().width());
|
||||
|
||||
Super::resizeEvent(event);
|
||||
}
|
||||
|
||||
QSize ElidingLabel::minimumSizeHint() const
|
||||
{
|
||||
if (hasPixmap() || d->isModeElideNone())
|
||||
return QLabel::minimumSizeHint();
|
||||
|
||||
const QFontMetrics &fm = fontMetrics();
|
||||
QSize size(fm.horizontalAdvance(d->m_text.left(2) + "…"), fm.height());
|
||||
return size;
|
||||
}
|
||||
|
||||
QSize ElidingLabel::sizeHint() const
|
||||
{
|
||||
if (hasPixmap() || d->isModeElideNone())
|
||||
return QLabel::sizeHint();
|
||||
|
||||
const QFontMetrics &fm = fontMetrics();
|
||||
QSize size(fm.horizontalAdvance(d->m_text), QLabel::sizeHint().height());
|
||||
return size;
|
||||
}
|
||||
|
||||
void ElidingLabel::setText(const QString &text)
|
||||
{
|
||||
d->m_text = text;
|
||||
if (d->isModeElideNone()) {
|
||||
Super::setText(text);
|
||||
} else {
|
||||
internal::setToolTip(this, text);
|
||||
}
|
||||
|
||||
ElidingLabel::~ElidingLabel()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
Qt::TextElideMode ElidingLabel::elideMode() const
|
||||
{
|
||||
return d->m_elideMode;
|
||||
}
|
||||
|
||||
void ElidingLabel::setElideMode(Qt::TextElideMode mode)
|
||||
{
|
||||
d->m_elideMode = mode;
|
||||
d->elideText(size().width());
|
||||
}
|
||||
}
|
||||
|
||||
bool ElidingLabel::isElided() const
|
||||
{
|
||||
return d->m_isElided;
|
||||
}
|
||||
QString ElidingLabel::text() const
|
||||
{
|
||||
return d->m_text;
|
||||
}
|
||||
|
||||
void ElidingLabel::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
Super::mouseReleaseEvent(event);
|
||||
if (event->button() != Qt::LeftButton)
|
||||
return;
|
||||
|
||||
emit clicked();
|
||||
}
|
||||
|
||||
void ElidingLabel::mouseDoubleClickEvent(QMouseEvent *event)
|
||||
{
|
||||
Q_UNUSED(event)
|
||||
emit doubleClicked();
|
||||
Super::mouseDoubleClickEvent(event);
|
||||
}
|
||||
|
||||
void ElidingLabel::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
if (!d->isModeElideNone())
|
||||
d->elideText(event->size().width());
|
||||
|
||||
Super::resizeEvent(event);
|
||||
}
|
||||
|
||||
bool ElidingLabel::hasPixmap() const
|
||||
{
|
||||
return !pixmap().isNull();
|
||||
}
|
||||
|
||||
QSize ElidingLabel::minimumSizeHint() const
|
||||
{
|
||||
if (hasPixmap() || d->isModeElideNone())
|
||||
return QLabel::minimumSizeHint();
|
||||
|
||||
const QFontMetrics &fm = fontMetrics();
|
||||
QSize size(fm.horizontalAdvance(d->m_text.left(2) + "…"), fm.height());
|
||||
return size;
|
||||
}
|
||||
|
||||
QSize ElidingLabel::sizeHint() const
|
||||
{
|
||||
if (hasPixmap() || d->isModeElideNone())
|
||||
return QLabel::sizeHint();
|
||||
|
||||
const QFontMetrics &fm = fontMetrics();
|
||||
QSize size(fm.horizontalAdvance(d->m_text), QLabel::sizeHint().height());
|
||||
return size;
|
||||
}
|
||||
|
||||
void ElidingLabel::setText(const QString &text)
|
||||
{
|
||||
d->m_text = text;
|
||||
if (d->isModeElideNone()) {
|
||||
Super::setText(text);
|
||||
} else {
|
||||
internal::setToolTip(this, text);
|
||||
d->elideText(this->size().width());
|
||||
}
|
||||
}
|
||||
|
||||
QString ElidingLabel::text() const
|
||||
{
|
||||
return d->m_text;
|
||||
}
|
||||
bool ElidingLabel::hasPixmap() const
|
||||
{
|
||||
return !pixmap().isNull();
|
||||
}
|
||||
|
||||
} // namespace ADS
|
||||
|
@@ -9,20 +9,19 @@
|
||||
|
||||
namespace ADS {
|
||||
|
||||
struct ElidingLabelPrivate;
|
||||
class ElidingLabelPrivate;
|
||||
|
||||
/**
|
||||
* A QLabel that supports eliding text.
|
||||
* Because the functions setText() and text() are no virtual functions setting
|
||||
* and reading the text via a pointer to the base class QLabel does not work
|
||||
* properly
|
||||
* Because the functions setText() and text() are no virtual functions setting and reading the
|
||||
* text via a pointer to the base class QLabel does not work properly.
|
||||
*/
|
||||
class ADS_EXPORT ElidingLabel : public QLabel
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
ElidingLabelPrivate *d;
|
||||
friend struct ElidingLabelPrivate;
|
||||
friend class ElidingLabelPrivate;
|
||||
|
||||
protected:
|
||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
@@ -32,8 +31,10 @@ protected:
|
||||
public:
|
||||
using Super = QLabel;
|
||||
|
||||
ElidingLabel(QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::Widget);
|
||||
ElidingLabel(const QString &text, QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::Widget);
|
||||
ElidingLabel(QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags());
|
||||
ElidingLabel(const QString &text,
|
||||
QWidget *parent = nullptr,
|
||||
Qt::WindowFlags flags = Qt::WindowFlags());
|
||||
~ElidingLabel() override;
|
||||
|
||||
/**
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@
|
||||
#include <QDockWidget>
|
||||
#include <QRubberBand>
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
|
||||
using FloatingWidgetBaseType = QDockWidget;
|
||||
#else
|
||||
using FloatingWidgetBaseType = QWidget;
|
||||
@@ -33,8 +33,8 @@ class DockingStateReader;
|
||||
|
||||
/**
|
||||
* Pure virtual interface for floating widgets.
|
||||
* This interface is used for opaque and non-opaque undocking. If opaque
|
||||
* undocking is used, the a real FloatingDockContainer widget will be created
|
||||
* This interface is used for opaque and non-opaque undocking. If opaque undocking is used,
|
||||
* the a real FloatingDockContainer widget will be created.
|
||||
*/
|
||||
class AbstractFloatingWidget
|
||||
{
|
||||
@@ -42,8 +42,7 @@ public:
|
||||
virtual ~AbstractFloatingWidget() = 0;
|
||||
/**
|
||||
* Starts floating.
|
||||
* This function should get called typically from a mouse press event
|
||||
* handler
|
||||
* This function should get called typically from a mouse press event handler.
|
||||
*/
|
||||
virtual void startFloating(const QPoint &dragStartMousePos,
|
||||
const QSize &size,
|
||||
@@ -52,26 +51,23 @@ public:
|
||||
= 0;
|
||||
|
||||
/**
|
||||
* Moves the widget to a new position relative to the position given when
|
||||
* startFloating() was called.
|
||||
* This function should be called from a mouse mouve event handler to
|
||||
* move the floating widget on mouse move events.
|
||||
* Moves the widget to a new position relative to the position given when startFloating()
|
||||
* was called. This function should be called from a mouse mouve event handler to move the
|
||||
* floating widget on mouse move events.
|
||||
*/
|
||||
virtual void moveFloating() = 0;
|
||||
|
||||
/**
|
||||
* Tells the widget that to finish dragging if the mouse is released.
|
||||
* This function should be called from a mouse release event handler
|
||||
* to finish the dragging
|
||||
* Tells the widget that to finish dragging if the mouse is released. This function should be
|
||||
* called from a mouse release event handler to finish the dragging.
|
||||
*/
|
||||
virtual void finishDragging() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* This implements a floating widget that is a dock container that accepts
|
||||
* docking of dock widgets like the main window and that can be docked into
|
||||
* another dock container.
|
||||
* Every floating window of the docking system is a FloatingDockContainer.
|
||||
* This implements a floating widget that is a dock container that accepts docking of dock widgets
|
||||
* like the main window and that can be docked into another dock container. Every floating window
|
||||
* of the docking system is a FloatingDockContainer.
|
||||
*/
|
||||
class ADS_EXPORT FloatingDockContainer : public FloatingWidgetBaseType,
|
||||
public AbstractFloatingWidget
|
||||
@@ -96,9 +92,8 @@ private:
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Starts floating at the given global position.
|
||||
* Use moveToGlobalPos() to move the widget to a new position
|
||||
* depending on the start position given in Pos parameter
|
||||
* Starts floating at the given global position. Use moveToGlobalPos() to move the widget
|
||||
* to a new position depending on the start position given in Pos parameter.
|
||||
*/
|
||||
void startFloating(const QPoint &dragStartMousePos,
|
||||
const QSize &size,
|
||||
@@ -106,7 +101,7 @@ protected:
|
||||
QWidget *mouseEventHandler) override;
|
||||
|
||||
/**
|
||||
* Call this function to start dragging the floating widget
|
||||
* Call this function to start dragging the floating widget.
|
||||
*/
|
||||
void startDragging(const QPoint &dragStartMousePos,
|
||||
const QSize &size,
|
||||
@@ -116,14 +111,13 @@ protected:
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this function if you explicitly want to signal that dragging has
|
||||
* finished
|
||||
* Call this function if you explicitly want to signal that dragging has finished.
|
||||
*/
|
||||
void finishDragging() override;
|
||||
|
||||
/**
|
||||
* Call this function if you just want to initialize the position
|
||||
* and size of the floating widget
|
||||
* Call this function if you just want to initialize the position and size of the
|
||||
* floating widget.
|
||||
*/
|
||||
void initFloatingGeometry(const QPoint &dragStartMousePos, const QSize &size)
|
||||
{
|
||||
@@ -131,21 +125,20 @@ protected:
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the widget to a new position relative to the position given when
|
||||
* startFloating() was called
|
||||
* Moves the widget to a new position relative to the position given when startFloating()
|
||||
* was called.
|
||||
*/
|
||||
void moveFloating() override;
|
||||
|
||||
/**
|
||||
* Restores the state from given stream.
|
||||
* If Testing is true, the function only parses the data from the given
|
||||
* stream but does not restore anything. You can use this check for
|
||||
* faulty files before you start restoring the state
|
||||
* Restores the state from given stream. If Testing is true, the function only parses the
|
||||
* data from the given stream but does not restore anything. You can use this check for
|
||||
* faulty files before you start restoring the state.
|
||||
*/
|
||||
bool restoreState(DockingStateReader &stream, bool testing);
|
||||
|
||||
/**
|
||||
* Call this function to update the window title
|
||||
* Call this function to update the window title.
|
||||
*/
|
||||
void updateWindowTitle();
|
||||
|
||||
@@ -158,6 +151,10 @@ protected: // reimplements QWidget
|
||||
#ifdef Q_OS_MACOS
|
||||
virtual bool event(QEvent *event) override;
|
||||
virtual void moveEvent(QMoveEvent *event) override;
|
||||
#elif defined(Q_OS_UNIX)
|
||||
virtual bool event(QEvent *e) override;
|
||||
virtual void moveEvent(QMoveEvent *event) override;
|
||||
virtual void resizeEvent(QResizeEvent *event) override;
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
@@ -202,26 +199,61 @@ public:
|
||||
bool isClosable() const;
|
||||
|
||||
/**
|
||||
* This function returns true, if this floating widget has only one single
|
||||
* visible dock widget in a single visible dock area.
|
||||
* The single dock widget is a real top level floating widget because no
|
||||
* other widgets are docked.
|
||||
* This function returns true, if this floating widget has only one single visible dock widget
|
||||
* in a single visible dock area. The single dock widget is a real top level floating widget
|
||||
* because no other widgets are docked.
|
||||
*/
|
||||
bool hasTopLevelDockWidget() const;
|
||||
|
||||
/**
|
||||
* This function returns the first dock widget in the first dock area.
|
||||
* If the function hasSingleDockWidget() returns true, then this function
|
||||
* returns this single dock widget.
|
||||
* This function returns the first dock widget in the first dock area. If the function
|
||||
* hasSingleDockWidget() returns true, then this function returns this single dock widget.
|
||||
*/
|
||||
DockWidget *topLevelDockWidget() const;
|
||||
|
||||
/**
|
||||
* This function returns a list of all dock widget in this floating widget.
|
||||
* This is a simple convenience function that simply calls the dockWidgets()
|
||||
* function of the internal container widget.
|
||||
* This function returns a list of all dock widget in this floating widget. This is a simple
|
||||
* convenience function that simply calls the dockWidgets() function of the internal
|
||||
* container widget.
|
||||
*/
|
||||
QList<DockWidget *> dockWidgets() const;
|
||||
|
||||
/**
|
||||
* This function hides the floating bar instantely and delete it later.
|
||||
*/
|
||||
void hideAndDeleteLater();
|
||||
|
||||
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
|
||||
/**
|
||||
* This is a function that responds to FloatingWidgetTitleBar::maximizeRequest()
|
||||
* Maximize or normalize the container size.
|
||||
*/
|
||||
void onMaximizeRequest();
|
||||
|
||||
/**
|
||||
* Normalize (Unmaximize) the window.
|
||||
* fixGeometry parameter fixes a "bug" in QT where immediately after calling showNormal
|
||||
* geometry is not set properly.
|
||||
* Set this true when moving the window immediately after normalizing.
|
||||
*/
|
||||
void showNormal(bool fixGeometry = false);
|
||||
|
||||
/**
|
||||
* Maximizes the window.
|
||||
*/
|
||||
void showMaximized();
|
||||
|
||||
/**
|
||||
* Returns if the window is currently maximized or not.
|
||||
*/
|
||||
bool isMaximized() const;
|
||||
|
||||
/**
|
||||
* Returns true if the floating widget has a native titlebar or false if
|
||||
* the floating widget has a QWidget based title bar
|
||||
*/
|
||||
bool hasNativeTitleBar();
|
||||
#endif
|
||||
}; // class FloatingDockContainer
|
||||
|
||||
} // namespace ADS
|
||||
|
@@ -4,6 +4,8 @@
|
||||
#include "floatingdragpreview.h"
|
||||
#include "ads_globals_p.h"
|
||||
|
||||
#include "ads_globals.h"
|
||||
#include "autohidedockcontainer.h"
|
||||
#include "dockareawidget.h"
|
||||
#include "dockcontainerwidget.h"
|
||||
#include "dockmanager.h"
|
||||
@@ -20,311 +22,405 @@
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace ADS
|
||||
namespace ADS {
|
||||
|
||||
/**
|
||||
* Private data class (pimpl)
|
||||
*/
|
||||
class FloatingDragPreviewPrivate
|
||||
{
|
||||
public:
|
||||
FloatingDragPreview *q;
|
||||
QWidget *m_content = nullptr;
|
||||
DockWidget::DockWidgetFeatures m_contentFeatures;
|
||||
DockAreaWidget *m_contentSourceArea = nullptr;
|
||||
QPoint m_dragStartMousePosition;
|
||||
DockManager *m_dockManager = nullptr;
|
||||
DockContainerWidget *m_dropContainer = nullptr;
|
||||
bool m_hidden = false;
|
||||
QPixmap m_contentPreviewPixmap;
|
||||
bool m_canceled = false;
|
||||
|
||||
/**
|
||||
* Private data class (pimpl)
|
||||
* Private data constructor
|
||||
*/
|
||||
class FloatingDragPreviewPrivate
|
||||
FloatingDragPreviewPrivate(FloatingDragPreview *parent);
|
||||
void updateDropOverlays(const QPoint &globalPosition);
|
||||
|
||||
void setHidden(bool value)
|
||||
{
|
||||
public:
|
||||
FloatingDragPreview *q;
|
||||
QWidget *m_content = nullptr;
|
||||
DockAreaWidget *m_contentSourceArea = nullptr;
|
||||
QPoint m_dragStartMousePosition;
|
||||
DockManager *m_dockManager = nullptr;
|
||||
DockContainerWidget *m_dropContainer = nullptr;
|
||||
bool m_hidden = false;
|
||||
QPixmap m_contentPreviewPixmap;
|
||||
bool m_canceled = false;
|
||||
|
||||
/**
|
||||
* Private data constructor
|
||||
*/
|
||||
FloatingDragPreviewPrivate(FloatingDragPreview *parent);
|
||||
void updateDropOverlays(const QPoint &globalPosition);
|
||||
|
||||
void setHidden(bool value)
|
||||
{
|
||||
m_hidden = value;
|
||||
q->update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel dragging and emit the draggingCanceled event
|
||||
*/
|
||||
void cancelDragging()
|
||||
{
|
||||
m_canceled = true;
|
||||
emit q->draggingCanceled();
|
||||
m_dockManager->containerOverlay()->hideOverlay();
|
||||
m_dockManager->dockAreaOverlay()->hideOverlay();
|
||||
q->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the real floating widget in case the mouse is released outside
|
||||
* outside of any drop area
|
||||
*/
|
||||
void createFloatingWidget();
|
||||
}; // class FloatingDragPreviewPrivate
|
||||
|
||||
void FloatingDragPreviewPrivate::updateDropOverlays(const QPoint &globalPosition)
|
||||
{
|
||||
if (!q->isVisible() || !m_dockManager)
|
||||
return;
|
||||
|
||||
auto containers = m_dockManager->dockContainers();
|
||||
DockContainerWidget *topContainer = nullptr;
|
||||
for (auto containerWidget : containers) {
|
||||
if (!containerWidget->isVisible())
|
||||
continue;
|
||||
|
||||
const QPoint mappedPosition = containerWidget->mapFromGlobal(globalPosition);
|
||||
if (containerWidget->rect().contains(mappedPosition)) {
|
||||
if (!topContainer || containerWidget->isInFrontOf(topContainer))
|
||||
topContainer = containerWidget;
|
||||
}
|
||||
}
|
||||
|
||||
m_dropContainer = topContainer;
|
||||
auto containerOverlay = m_dockManager->containerOverlay();
|
||||
auto dockAreaOverlay = m_dockManager->dockAreaOverlay();
|
||||
auto dockDropArea = dockAreaOverlay->dropAreaUnderCursor();
|
||||
auto containerDropArea = containerOverlay->dropAreaUnderCursor();
|
||||
|
||||
if (!topContainer) {
|
||||
containerOverlay->hideOverlay();
|
||||
dockAreaOverlay->hideOverlay();
|
||||
if (DockManager::testConfigFlag(DockManager::DragPreviewIsDynamic))
|
||||
setHidden(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const int visibleDockAreas = topContainer->visibleDockAreaCount();
|
||||
containerOverlay->setAllowedAreas(visibleDockAreas > 1 ? OuterDockAreas : AllDockAreas);
|
||||
auto dockArea = topContainer->dockAreaAt(globalPosition);
|
||||
if (dockArea && dockArea->isVisible() && visibleDockAreas >= 0
|
||||
&& dockArea != m_contentSourceArea) {
|
||||
dockAreaOverlay->enableDropPreview(true);
|
||||
dockAreaOverlay->setAllowedAreas((visibleDockAreas == 1) ? NoDockWidgetArea
|
||||
: dockArea->allowedAreas());
|
||||
DockWidgetArea area = dockAreaOverlay->showOverlay(dockArea);
|
||||
|
||||
// A CenterDockWidgetArea for the dockAreaOverlay() indicates that the mouse is in the
|
||||
// title bar. If the ContainerArea is valid then we ignore the dock area of the
|
||||
// dockAreaOverlay() and disable the drop preview
|
||||
if ((area == CenterDockWidgetArea) && (containerDropArea != InvalidDockWidgetArea)) {
|
||||
dockAreaOverlay->enableDropPreview(false);
|
||||
containerOverlay->enableDropPreview(true);
|
||||
} else {
|
||||
containerOverlay->enableDropPreview(InvalidDockWidgetArea == area);
|
||||
}
|
||||
containerOverlay->showOverlay(topContainer);
|
||||
} else {
|
||||
dockAreaOverlay->hideOverlay();
|
||||
// If there is only one single visible dock area in a container, then
|
||||
// it does not make sense to show a dock overlay because the dock area
|
||||
// would be removed and inserted at the same position
|
||||
if (visibleDockAreas <= 1)
|
||||
containerOverlay->hideOverlay();
|
||||
else
|
||||
containerOverlay->showOverlay(topContainer);
|
||||
|
||||
if (dockArea == m_contentSourceArea && InvalidDockWidgetArea == containerDropArea)
|
||||
m_dropContainer = nullptr;
|
||||
}
|
||||
|
||||
if (DockManager::testConfigFlag(DockManager::DragPreviewIsDynamic)) {
|
||||
setHidden(dockDropArea != InvalidDockWidgetArea
|
||||
|| containerDropArea != InvalidDockWidgetArea);
|
||||
}
|
||||
m_hidden = value;
|
||||
q->update();
|
||||
}
|
||||
|
||||
FloatingDragPreviewPrivate::FloatingDragPreviewPrivate(FloatingDragPreview *parent)
|
||||
: q(parent)
|
||||
{}
|
||||
/**
|
||||
* Cancel dragging and emit the draggingCanceled event.
|
||||
*/
|
||||
void cancelDragging()
|
||||
{
|
||||
m_canceled = true;
|
||||
emit q->draggingCanceled();
|
||||
m_dockManager->containerOverlay()->hideOverlay();
|
||||
m_dockManager->dockAreaOverlay()->hideOverlay();
|
||||
q->close();
|
||||
}
|
||||
|
||||
void FloatingDragPreviewPrivate::createFloatingWidget()
|
||||
/**
|
||||
* Creates the real floating widget in case the mouse is released outside outside of any
|
||||
* drop area.
|
||||
*/
|
||||
void createFloatingWidget();
|
||||
|
||||
/**
|
||||
* Returns true, if the content is floatable
|
||||
*/
|
||||
bool isContentFloatable() const
|
||||
{
|
||||
return m_contentFeatures.testFlag(DockWidget::DockWidgetFloatable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true, if the content is pinnable
|
||||
*/
|
||||
bool isContentPinnable() const
|
||||
{
|
||||
return m_contentFeatures.testFlag(DockWidget::DockWidgetPinnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content features
|
||||
*/
|
||||
DockWidget::DockWidgetFeatures contentFeatures() const
|
||||
{
|
||||
DockWidget *dockWidget = qobject_cast<DockWidget *>(m_content);
|
||||
if (dockWidget)
|
||||
return dockWidget->features();
|
||||
|
||||
DockAreaWidget *dockArea = qobject_cast<DockAreaWidget *>(m_content);
|
||||
if (dockArea)
|
||||
return dockArea->features();
|
||||
|
||||
FloatingDockContainer *floatingWidget = nullptr;
|
||||
return DockWidget::DockWidgetFeatures();
|
||||
}
|
||||
}; // class FloatingDragPreviewPrivate
|
||||
|
||||
if (dockWidget && dockWidget->features().testFlag(DockWidget::DockWidgetFloatable))
|
||||
floatingWidget = new FloatingDockContainer(dockWidget);
|
||||
else if (dockArea && dockArea->features().testFlag(DockWidget::DockWidgetFloatable))
|
||||
floatingWidget = new FloatingDockContainer(dockArea);
|
||||
void FloatingDragPreviewPrivate::updateDropOverlays(const QPoint &globalPosition)
|
||||
{
|
||||
if (!q->isVisible() || !m_dockManager)
|
||||
return;
|
||||
|
||||
if (floatingWidget) {
|
||||
floatingWidget->setGeometry(q->geometry());
|
||||
floatingWidget->show();
|
||||
if (!DockManager::testConfigFlag(DockManager::DragPreviewHasWindowFrame)) {
|
||||
QApplication::processEvents();
|
||||
int frameHeight = floatingWidget->frameGeometry().height() - floatingWidget->geometry().height();
|
||||
QRect fixedGeometry = q->geometry();
|
||||
fixedGeometry.adjust(0, frameHeight, 0, 0);
|
||||
floatingWidget->setGeometry(fixedGeometry);
|
||||
}
|
||||
auto containers = m_dockManager->dockContainers();
|
||||
DockContainerWidget *topContainer = nullptr;
|
||||
for (auto containerWidget : containers) {
|
||||
if (!containerWidget->isVisible())
|
||||
continue;
|
||||
|
||||
const QPoint mappedPosition = containerWidget->mapFromGlobal(globalPosition);
|
||||
if (containerWidget->rect().contains(mappedPosition)) {
|
||||
if (!topContainer || containerWidget->isInFrontOf(topContainer))
|
||||
topContainer = containerWidget;
|
||||
}
|
||||
}
|
||||
|
||||
FloatingDragPreview::FloatingDragPreview(QWidget *content, QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, d(new FloatingDragPreviewPrivate(this))
|
||||
{
|
||||
d->m_content = content;
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
if (DockManager::testConfigFlag(DockManager::DragPreviewHasWindowFrame)) {
|
||||
setWindowFlags(Qt::Window | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint);
|
||||
m_dropContainer = topContainer;
|
||||
auto containerOverlay = m_dockManager->containerOverlay();
|
||||
auto dockAreaOverlay = m_dockManager->dockAreaOverlay();
|
||||
|
||||
if (!topContainer) {
|
||||
containerOverlay->hideOverlay();
|
||||
dockAreaOverlay->hideOverlay();
|
||||
if (DockManager::testConfigFlag(DockManager::DragPreviewIsDynamic))
|
||||
setHidden(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto dockDropArea = dockAreaOverlay->dropAreaUnderCursor();
|
||||
auto containerDropArea = containerOverlay->dropAreaUnderCursor();
|
||||
|
||||
int visibleDockAreas = topContainer->visibleDockAreaCount();
|
||||
|
||||
// Include the overlay widget we're dragging as a visible widget
|
||||
auto dockAreaWidget = qobject_cast<DockAreaWidget *>(m_content);
|
||||
if (dockAreaWidget && dockAreaWidget->isAutoHide())
|
||||
visibleDockAreas++;
|
||||
|
||||
DockWidgetAreas allowedContainerAreas = (visibleDockAreas > 1) ? OuterDockAreas : AllDockAreas;
|
||||
|
||||
auto dockArea = topContainer->dockAreaAt(globalPosition);
|
||||
// If the dock container contains only one single DockArea, then we need
|
||||
// to respect the allowed areas - only the center area is relevant here because
|
||||
// all other allowed areas are from the container
|
||||
if (visibleDockAreas == 1 && dockArea)
|
||||
allowedContainerAreas.setFlag(CenterDockWidgetArea,
|
||||
dockArea->allowedAreas().testFlag(CenterDockWidgetArea));
|
||||
|
||||
if (isContentPinnable())
|
||||
allowedContainerAreas |= AutoHideDockAreas;
|
||||
|
||||
containerOverlay->setAllowedAreas(allowedContainerAreas);
|
||||
containerOverlay->enableDropPreview(containerDropArea != InvalidDockWidgetArea);
|
||||
|
||||
if (dockArea && dockArea->isVisible() && visibleDockAreas >= 0
|
||||
&& dockArea != m_contentSourceArea) {
|
||||
dockAreaOverlay->enableDropPreview(true);
|
||||
dockAreaOverlay->setAllowedAreas((visibleDockAreas == 1) ? NoDockWidgetArea
|
||||
: dockArea->allowedAreas());
|
||||
DockWidgetArea area = dockAreaOverlay->showOverlay(dockArea);
|
||||
|
||||
// A CenterDockWidgetArea for the dockAreaOverlay() indicates that the mouse is in the
|
||||
// title bar. If the ContainerArea is valid then we ignore the dock area of the
|
||||
// dockAreaOverlay() and disable the drop preview.
|
||||
if ((area == CenterDockWidgetArea) && (containerDropArea != InvalidDockWidgetArea)) {
|
||||
dockAreaOverlay->enableDropPreview(false);
|
||||
containerOverlay->enableDropPreview(true);
|
||||
} else {
|
||||
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
|
||||
setAttribute(Qt::WA_NoSystemBackground);
|
||||
setAttribute(Qt::WA_TranslucentBackground);
|
||||
containerOverlay->enableDropPreview(InvalidDockWidgetArea == area);
|
||||
}
|
||||
containerOverlay->showOverlay(topContainer);
|
||||
} else {
|
||||
dockAreaOverlay->hideOverlay();
|
||||
// If there is only one single visible dock area in a container, then it does not make
|
||||
// sense to show a dock overlay because the dock area would be removed and inserted at
|
||||
// the same position. Only auto hide area is allowed.
|
||||
if (visibleDockAreas == 1)
|
||||
containerOverlay->setAllowedAreas(AutoHideDockAreas);
|
||||
|
||||
if (Utils::HostOsInfo::isLinuxHost()) {
|
||||
auto flags = windowFlags();
|
||||
flags |= Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint;
|
||||
setWindowFlags(flags);
|
||||
}
|
||||
containerOverlay->showOverlay(topContainer);
|
||||
|
||||
setWindowOpacity(0.6);
|
||||
|
||||
// Create a static image of the widget that should get undocked
|
||||
// This is like some kind preview image like it is uses in drag and drop operations
|
||||
if (DockManager::testConfigFlag(DockManager::DragPreviewShowsContentPixmap)) {
|
||||
d->m_contentPreviewPixmap = QPixmap(content->size());
|
||||
content->render(&d->m_contentPreviewPixmap);
|
||||
}
|
||||
connect(qApp,
|
||||
&QApplication::applicationStateChanged,
|
||||
this,
|
||||
&FloatingDragPreview::onApplicationStateChanged);
|
||||
// The only safe way to receive escape key presses is to install an event
|
||||
// filter for the application object
|
||||
QApplication::instance()->installEventFilter(this);
|
||||
if (dockArea == m_contentSourceArea && InvalidDockWidgetArea == containerDropArea)
|
||||
m_dropContainer = nullptr;
|
||||
}
|
||||
|
||||
FloatingDragPreview::FloatingDragPreview(DockWidget *content)
|
||||
: FloatingDragPreview(static_cast<QWidget *>(content),
|
||||
content->dockManager()) // TODO static_cast?
|
||||
{
|
||||
d->m_dockManager = content->dockManager();
|
||||
if (content->dockAreaWidget()->openDockWidgetsCount() == 1)
|
||||
d->m_contentSourceArea = content->dockAreaWidget();
|
||||
if (DockManager::testConfigFlag(DockManager::DragPreviewIsDynamic))
|
||||
setHidden(dockDropArea != InvalidDockWidgetArea
|
||||
|| containerDropArea != InvalidDockWidgetArea);
|
||||
}
|
||||
|
||||
setWindowTitle(content->windowTitle());
|
||||
}
|
||||
FloatingDragPreviewPrivate::FloatingDragPreviewPrivate(FloatingDragPreview *parent)
|
||||
: q(parent)
|
||||
{}
|
||||
|
||||
FloatingDragPreview::FloatingDragPreview(DockAreaWidget *content)
|
||||
: FloatingDragPreview(static_cast<QWidget *>(content),
|
||||
content->dockManager()) // TODO static_cast?
|
||||
{
|
||||
d->m_dockManager = content->dockManager();
|
||||
d->m_contentSourceArea = content;
|
||||
setWindowTitle(content->currentDockWidget()->windowTitle());
|
||||
}
|
||||
void FloatingDragPreviewPrivate::createFloatingWidget()
|
||||
{
|
||||
DockWidget *dockWidget = qobject_cast<DockWidget *>(m_content);
|
||||
DockAreaWidget *dockArea = qobject_cast<DockAreaWidget *>(m_content);
|
||||
|
||||
FloatingDragPreview::~FloatingDragPreview() { delete d; }
|
||||
FloatingDockContainer *floatingWidget = nullptr;
|
||||
|
||||
void FloatingDragPreview::moveFloating()
|
||||
{
|
||||
const int borderSize = (frameSize().width() - size().width()) / 2;
|
||||
const QPoint moveToPos = QCursor::pos() - d->m_dragStartMousePosition
|
||||
- QPoint(borderSize, 0);
|
||||
move(moveToPos);
|
||||
d->updateDropOverlays(QCursor::pos());
|
||||
}
|
||||
if (dockWidget && dockWidget->features().testFlag(DockWidget::DockWidgetFloatable))
|
||||
floatingWidget = new FloatingDockContainer(dockWidget);
|
||||
else if (dockArea && dockArea->features().testFlag(DockWidget::DockWidgetFloatable))
|
||||
floatingWidget = new FloatingDockContainer(dockArea);
|
||||
|
||||
void FloatingDragPreview::startFloating(const QPoint &dragStartMousePos,
|
||||
const QSize &size,
|
||||
eDragState dragState,
|
||||
QWidget *mouseEventHandler)
|
||||
{
|
||||
Q_UNUSED(mouseEventHandler)
|
||||
Q_UNUSED(dragState)
|
||||
resize(size);
|
||||
d->m_dragStartMousePosition = dragStartMousePos;
|
||||
moveFloating();
|
||||
show();
|
||||
}
|
||||
|
||||
void FloatingDragPreview::finishDragging()
|
||||
{
|
||||
qCInfo(adsLog) << Q_FUNC_INFO;
|
||||
auto dockDropArea = d->m_dockManager->dockAreaOverlay()->visibleDropAreaUnderCursor();
|
||||
auto containerDropArea = d->m_dockManager->containerOverlay()->visibleDropAreaUnderCursor();
|
||||
if (!d->m_dropContainer) {
|
||||
d->createFloatingWidget();
|
||||
} else if (dockDropArea != InvalidDockWidgetArea) {
|
||||
d->m_dropContainer->dropWidget(d->m_content, dockDropArea, d->m_dropContainer->dockAreaAt(QCursor::pos()));
|
||||
} else if (containerDropArea != InvalidDockWidgetArea) {
|
||||
// If there is only one single dock area, and we drop into the center
|
||||
// then we tabify the dropped widget into the only visible dock area
|
||||
if (d->m_dropContainer->visibleDockAreaCount() <= 1 && CenterDockWidgetArea == containerDropArea)
|
||||
d->m_dropContainer->dropWidget(d->m_content, containerDropArea, d->m_dropContainer->dockAreaAt(QCursor::pos()));
|
||||
else
|
||||
d->m_dropContainer->dropWidget(d->m_content, containerDropArea, nullptr);
|
||||
} else {
|
||||
d->createFloatingWidget();
|
||||
}
|
||||
|
||||
this->close();
|
||||
d->m_dockManager->containerOverlay()->hideOverlay();
|
||||
d->m_dockManager->dockAreaOverlay()->hideOverlay();
|
||||
}
|
||||
|
||||
void FloatingDragPreview::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
Q_UNUSED(event)
|
||||
if (d->m_hidden)
|
||||
return;
|
||||
|
||||
QPainter painter(this);
|
||||
if (DockManager::testConfigFlag(DockManager::DragPreviewShowsContentPixmap))
|
||||
painter.drawPixmap(QPoint(0, 0), d->m_contentPreviewPixmap);
|
||||
|
||||
// If we do not have a window frame then we paint a QRubberBand like frameless window
|
||||
if (floatingWidget) {
|
||||
floatingWidget->setGeometry(q->geometry());
|
||||
floatingWidget->show();
|
||||
if (!DockManager::testConfigFlag(DockManager::DragPreviewHasWindowFrame)) {
|
||||
QColor color = palette().color(QPalette::Active, QPalette::Highlight);
|
||||
QPen pen = painter.pen();
|
||||
pen.setColor(color.darker(120));
|
||||
pen.setStyle(Qt::SolidLine);
|
||||
pen.setWidth(1);
|
||||
pen.setCosmetic(true);
|
||||
painter.setPen(pen);
|
||||
color = color.lighter(130);
|
||||
color.setAlpha(64);
|
||||
painter.setBrush(color);
|
||||
painter.drawRect(rect().adjusted(0, 0, -1, -1));
|
||||
QApplication::processEvents();
|
||||
int frameHeight = floatingWidget->frameGeometry().height()
|
||||
- floatingWidget->geometry().height();
|
||||
QRect fixedGeometry = q->geometry();
|
||||
fixedGeometry.adjust(0, frameHeight, 0, 0);
|
||||
floatingWidget->setGeometry(fixedGeometry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FloatingDragPreview::onApplicationStateChanged(Qt::ApplicationState state)
|
||||
{
|
||||
if (state != Qt::ApplicationActive) {
|
||||
disconnect(qApp,
|
||||
&QApplication::applicationStateChanged,
|
||||
this,
|
||||
&FloatingDragPreview::onApplicationStateChanged);
|
||||
FloatingDragPreview::FloatingDragPreview(QWidget *content, QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, d(new FloatingDragPreviewPrivate(this))
|
||||
{
|
||||
d->m_content = content;
|
||||
d->m_contentFeatures = d->contentFeatures();
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
if (DockManager::testConfigFlag(DockManager::DragPreviewHasWindowFrame)) {
|
||||
setWindowFlags(Qt::Window | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint);
|
||||
} else {
|
||||
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
|
||||
setAttribute(Qt::WA_NoSystemBackground);
|
||||
setAttribute(Qt::WA_TranslucentBackground);
|
||||
}
|
||||
|
||||
if (Utils::HostOsInfo::isAnyUnixHost() && !Utils::HostOsInfo::isMacHost()) {
|
||||
auto flags = windowFlags();
|
||||
flags |= Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint;
|
||||
setWindowFlags(flags);
|
||||
}
|
||||
|
||||
setWindowOpacity(0.6);
|
||||
|
||||
// Create a static image of the widget that should get undocked. This is like some kind preview
|
||||
// image like it is uses in drag and drop operations.
|
||||
if (DockManager::testConfigFlag(DockManager::DragPreviewShowsContentPixmap)) {
|
||||
d->m_contentPreviewPixmap = QPixmap(content->size());
|
||||
content->render(&d->m_contentPreviewPixmap);
|
||||
}
|
||||
connect(qApp,
|
||||
&QApplication::applicationStateChanged,
|
||||
this,
|
||||
&FloatingDragPreview::onApplicationStateChanged);
|
||||
// The only safe way to receive escape key presses is to install an event filter for the
|
||||
// application object.
|
||||
QApplication::instance()->installEventFilter(this);
|
||||
}
|
||||
|
||||
FloatingDragPreview::FloatingDragPreview(DockWidget *content)
|
||||
: FloatingDragPreview(static_cast<QWidget *>(content),
|
||||
content->dockManager()) // TODO static_cast?
|
||||
{
|
||||
d->m_dockManager = content->dockManager();
|
||||
if (content->dockAreaWidget()->openDockWidgetsCount() == 1)
|
||||
d->m_contentSourceArea = content->dockAreaWidget();
|
||||
|
||||
setWindowTitle(content->windowTitle());
|
||||
}
|
||||
|
||||
FloatingDragPreview::FloatingDragPreview(DockAreaWidget *content)
|
||||
: FloatingDragPreview(static_cast<QWidget *>(content),
|
||||
content->dockManager()) // TODO static_cast?
|
||||
{
|
||||
d->m_dockManager = content->dockManager();
|
||||
d->m_contentSourceArea = content;
|
||||
setWindowTitle(content->currentDockWidget()->windowTitle());
|
||||
}
|
||||
|
||||
FloatingDragPreview::~FloatingDragPreview()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
void FloatingDragPreview::moveFloating()
|
||||
{
|
||||
const int borderSize = (frameSize().width() - size().width()) / 2;
|
||||
const QPoint moveToPos = QCursor::pos() - d->m_dragStartMousePosition - QPoint(borderSize, 0);
|
||||
move(moveToPos);
|
||||
d->updateDropOverlays(QCursor::pos());
|
||||
}
|
||||
|
||||
void FloatingDragPreview::startFloating(const QPoint &dragStartMousePos,
|
||||
const QSize &size,
|
||||
eDragState dragState,
|
||||
QWidget *mouseEventHandler)
|
||||
{
|
||||
Q_UNUSED(mouseEventHandler)
|
||||
Q_UNUSED(dragState)
|
||||
resize(size);
|
||||
d->m_dragStartMousePosition = dragStartMousePos;
|
||||
moveFloating();
|
||||
show();
|
||||
}
|
||||
|
||||
void FloatingDragPreview::finishDragging()
|
||||
{
|
||||
qCInfo(adsLog) << Q_FUNC_INFO;
|
||||
auto dockDropArea = d->m_dockManager->dockAreaOverlay()->visibleDropAreaUnderCursor();
|
||||
auto containerDropArea = d->m_dockManager->containerOverlay()->visibleDropAreaUnderCursor();
|
||||
bool validDropArea = (dockDropArea != InvalidDockWidgetArea)
|
||||
|| (containerDropArea != InvalidDockWidgetArea);
|
||||
|
||||
// Non floatable auto hide widgets should stay in its current auto hide state if they are
|
||||
// dragged into a floating window.
|
||||
if (validDropArea || d->isContentFloatable())
|
||||
cleanupAutoHideContainerWidget(containerDropArea);
|
||||
|
||||
if (!d->m_dropContainer) {
|
||||
d->createFloatingWidget();
|
||||
} else if (dockDropArea != InvalidDockWidgetArea) {
|
||||
d->m_dropContainer->dropWidget(d->m_content,
|
||||
dockDropArea,
|
||||
d->m_dropContainer->dockAreaAt(QCursor::pos()),
|
||||
d->m_dockManager->dockAreaOverlay()->tabIndexUnderCursor());
|
||||
} else if (containerDropArea != InvalidDockWidgetArea) {
|
||||
// If there is only one single dock area, and we drop into the center then we tabify the
|
||||
// dropped widget into the only visible dock area.
|
||||
if (d->m_dropContainer->visibleDockAreaCount() <= 1
|
||||
&& CenterDockWidgetArea == containerDropArea)
|
||||
d->m_dropContainer
|
||||
->dropWidget(d->m_content,
|
||||
containerDropArea,
|
||||
d->m_dropContainer->dockAreaAt(QCursor::pos()),
|
||||
d->m_dockManager->containerOverlay()->tabIndexUnderCursor());
|
||||
else
|
||||
d->m_dropContainer->dropWidget(d->m_content, containerDropArea, nullptr);
|
||||
} else {
|
||||
d->createFloatingWidget();
|
||||
}
|
||||
|
||||
close();
|
||||
d->m_dockManager->containerOverlay()->hideOverlay();
|
||||
d->m_dockManager->dockAreaOverlay()->hideOverlay();
|
||||
}
|
||||
|
||||
void FloatingDragPreview::cleanupAutoHideContainerWidget(DockWidgetArea containerDropArea)
|
||||
{
|
||||
auto droppedDockWidget = qobject_cast<DockWidget *>(d->m_content);
|
||||
auto droppedArea = qobject_cast<DockAreaWidget *>(d->m_content);
|
||||
|
||||
auto autoHideContainer = droppedDockWidget ? droppedDockWidget->autoHideDockContainer()
|
||||
: droppedArea->autoHideDockContainer();
|
||||
|
||||
if (!autoHideContainer)
|
||||
return;
|
||||
|
||||
// If the dropped widget is already an auto hide widget and if it is moved to a new side bar
|
||||
// location in the same container, then we do not need to cleanup.
|
||||
if (internal::isSideBarArea(containerDropArea)
|
||||
&& (d->m_dropContainer == autoHideContainer->dockContainer()))
|
||||
return;
|
||||
|
||||
autoHideContainer->cleanupAndDelete();
|
||||
}
|
||||
|
||||
void FloatingDragPreview::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
Q_UNUSED(event)
|
||||
if (d->m_hidden)
|
||||
return;
|
||||
|
||||
QPainter painter(this);
|
||||
painter.setOpacity(0.6);
|
||||
if (DockManager::testConfigFlag(DockManager::DragPreviewShowsContentPixmap))
|
||||
painter.drawPixmap(QPoint(0, 0), d->m_contentPreviewPixmap);
|
||||
|
||||
// If we do not have a window frame then we paint a QRubberBand like frameless window
|
||||
if (!DockManager::testConfigFlag(DockManager::DragPreviewHasWindowFrame)) {
|
||||
QColor color = palette().color(QPalette::Active, QPalette::Highlight);
|
||||
QPen pen = painter.pen();
|
||||
pen.setColor(color.darker(120));
|
||||
pen.setStyle(Qt::SolidLine);
|
||||
pen.setWidth(1);
|
||||
pen.setCosmetic(true);
|
||||
painter.setPen(pen);
|
||||
color = color.lighter(130);
|
||||
color.setAlpha(64);
|
||||
painter.setBrush(color);
|
||||
painter.drawRect(rect().adjusted(0, 0, -1, -1));
|
||||
}
|
||||
}
|
||||
|
||||
void FloatingDragPreview::onApplicationStateChanged(Qt::ApplicationState state)
|
||||
{
|
||||
if (state != Qt::ApplicationActive) {
|
||||
disconnect(qApp,
|
||||
&QApplication::applicationStateChanged,
|
||||
this,
|
||||
&FloatingDragPreview::onApplicationStateChanged);
|
||||
d->cancelDragging();
|
||||
}
|
||||
}
|
||||
|
||||
bool FloatingDragPreview::eventFilter(QObject *watched, QEvent *event)
|
||||
{
|
||||
if (!d->m_canceled && event->type() == QEvent::KeyPress) {
|
||||
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
|
||||
if (keyEvent->key() == Qt::Key_Escape) {
|
||||
watched->removeEventFilter(this);
|
||||
d->cancelDragging();
|
||||
}
|
||||
}
|
||||
|
||||
bool FloatingDragPreview::eventFilter(QObject *watched, QEvent *event)
|
||||
{
|
||||
if (!d->m_canceled && event->type() == QEvent::KeyPress) {
|
||||
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
|
||||
if (keyEvent->key() == Qt::Key_Escape) {
|
||||
watched->removeEventFilter(this);
|
||||
d->cancelDragging();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace ADS
|
||||
|
@@ -86,6 +86,11 @@ public: // implements AbstractFloatingWidget
|
||||
*/
|
||||
void finishDragging() override;
|
||||
|
||||
/**
|
||||
* Cleanup auto hide container if the dragged widget has one.
|
||||
*/
|
||||
void cleanupAutoHideContainerWidget(DockWidgetArea containerDropArea);
|
||||
|
||||
signals:
|
||||
/**
|
||||
* This signal is emitted, if dragging has been canceled by escape key
|
||||
|
@@ -6,44 +6,45 @@
|
||||
#include <QVector>
|
||||
|
||||
namespace ADS {
|
||||
|
||||
/**
|
||||
* Private data class (pimpl)
|
||||
*/
|
||||
struct IconProviderPrivate
|
||||
{
|
||||
IconProvider *q;
|
||||
QVector<QIcon> m_userIcons{IconCount, QIcon()};
|
||||
|
||||
/**
|
||||
* Private data class (pimpl)
|
||||
* Private data constructor
|
||||
*/
|
||||
struct IconProviderPrivate
|
||||
{
|
||||
IconProvider *q;
|
||||
QVector<QIcon> m_userIcons{IconCount, QIcon()};
|
||||
IconProviderPrivate(IconProvider *parent);
|
||||
};
|
||||
// struct IconProviderPrivate
|
||||
|
||||
/**
|
||||
* Private data constructor
|
||||
*/
|
||||
IconProviderPrivate(IconProvider *parent);
|
||||
};
|
||||
// struct IconProviderPrivate
|
||||
IconProviderPrivate::IconProviderPrivate(IconProvider *parent)
|
||||
: q(parent)
|
||||
{}
|
||||
|
||||
IconProviderPrivate::IconProviderPrivate(IconProvider *parent)
|
||||
: q(parent)
|
||||
{}
|
||||
IconProvider::IconProvider()
|
||||
: d(new IconProviderPrivate(this))
|
||||
{}
|
||||
|
||||
IconProvider::IconProvider()
|
||||
: d(new IconProviderPrivate(this))
|
||||
{}
|
||||
IconProvider::~IconProvider()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
IconProvider::~IconProvider()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
QIcon IconProvider::customIcon(eIcon iconId) const
|
||||
{
|
||||
Q_ASSERT(iconId < d->m_userIcons.size());
|
||||
return d->m_userIcons[iconId];
|
||||
}
|
||||
|
||||
QIcon IconProvider::customIcon(eIcon iconId) const
|
||||
{
|
||||
Q_ASSERT(iconId < d->m_userIcons.size());
|
||||
return d->m_userIcons[iconId];
|
||||
}
|
||||
|
||||
void IconProvider::registerCustomIcon(eIcon iconId, const QIcon &icon)
|
||||
{
|
||||
Q_ASSERT(iconId < d->m_userIcons.size());
|
||||
d->m_userIcons[iconId] = icon;
|
||||
}
|
||||
void IconProvider::registerCustomIcon(eIcon iconId, const QIcon &icon)
|
||||
{
|
||||
Q_ASSERT(iconId < d->m_userIcons.size());
|
||||
d->m_userIcons[iconId] = icon;
|
||||
}
|
||||
|
||||
} // namespace ADS
|
||||
|
@@ -12,8 +12,7 @@ namespace ADS {
|
||||
struct IconProviderPrivate;
|
||||
|
||||
/**
|
||||
* This object provides all icons that are required by the advanced docking
|
||||
* system.
|
||||
* This object provides all icons that are required by the advanced docking system.
|
||||
* The IconProvider enables the user to register custom icons in case using
|
||||
* stylesheets is not an option.
|
||||
*/
|
||||
|
@@ -4,8 +4,10 @@
|
||||
#include "floatingwidgettitlebar.h"
|
||||
|
||||
#include "ads_globals.h"
|
||||
#include "dockmanager.h"
|
||||
#include "elidinglabel.h"
|
||||
#include "floatingdockcontainer.h"
|
||||
#include "iconprovider.h"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QMouseEvent>
|
||||
@@ -20,6 +22,7 @@ namespace ADS {
|
||||
|
||||
using TabLabelType = ElidingLabel;
|
||||
using CloseButtonType = QToolButton;
|
||||
using MaximizeButtonType = QToolButton;
|
||||
|
||||
/**
|
||||
* @brief Private data class of public interface CFloatingWidgetTitleBar
|
||||
@@ -31,8 +34,12 @@ public:
|
||||
QLabel *m_iconLabel = nullptr;
|
||||
TabLabelType *m_titleLabel = nullptr;
|
||||
CloseButtonType *m_closeButton = nullptr;
|
||||
MaximizeButtonType *m_maximizeButton = nullptr;
|
||||
FloatingDockContainer *m_floatingWidget = nullptr;
|
||||
eDragState m_dragState = DraggingInactive;
|
||||
QIcon m_maximizeIcon;
|
||||
QIcon m_normalIcon;
|
||||
bool m_maximized = false;
|
||||
|
||||
FloatingWidgetTitleBarPrivate(FloatingWidgetTitleBar *parent)
|
||||
: q(parent)
|
||||
@@ -46,15 +53,18 @@ public:
|
||||
|
||||
void FloatingWidgetTitleBarPrivate::createLayout()
|
||||
{
|
||||
// Title label
|
||||
m_titleLabel = new TabLabelType();
|
||||
m_titleLabel->setElideMode(Qt::ElideRight);
|
||||
m_titleLabel->setText("DockWidget->windowTitle()");
|
||||
m_titleLabel->setObjectName("floatingTitleLabel");
|
||||
m_titleLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||
|
||||
// Close button
|
||||
m_closeButton = new CloseButtonType();
|
||||
m_closeButton->setObjectName("floatingTitleCloseButton");
|
||||
m_closeButton->setAutoRaise(true);
|
||||
|
||||
internal::setButtonIcon(m_closeButton,
|
||||
QStyle::SP_TitleBarCloseButton,
|
||||
ADS::FloatingWidgetCloseIcon);
|
||||
@@ -68,6 +78,21 @@ void FloatingWidgetTitleBarPrivate::createLayout()
|
||||
q,
|
||||
&FloatingWidgetTitleBar::closeRequested);
|
||||
|
||||
// Maximize button
|
||||
m_maximizeButton = new MaximizeButtonType();
|
||||
m_maximizeButton->setObjectName("floatingTitleMaxButton");
|
||||
m_maximizeButton->setAutoRaise(true);
|
||||
|
||||
m_maximizeButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
m_maximizeButton->setIconSize(QSize(11, 11));
|
||||
m_maximizeButton->setFixedSize(QSize(17, 17));
|
||||
m_maximizeButton->setVisible(true);
|
||||
m_maximizeButton->setFocusPolicy(Qt::NoFocus);
|
||||
QObject::connect(m_maximizeButton,
|
||||
&QPushButton::clicked,
|
||||
q,
|
||||
&FloatingWidgetTitleBar::maximizeRequested);
|
||||
|
||||
QFontMetrics fontMetrics(m_titleLabel->font());
|
||||
int spacing = qRound(fontMetrics.height() / 4.0);
|
||||
|
||||
@@ -78,6 +103,7 @@ void FloatingWidgetTitleBarPrivate::createLayout()
|
||||
q->setLayout(layout);
|
||||
layout->addWidget(m_titleLabel, 1);
|
||||
layout->addSpacing(spacing);
|
||||
layout->addWidget(m_maximizeButton);
|
||||
layout->addWidget(m_closeButton);
|
||||
layout->addSpacing(1);
|
||||
layout->setAlignment(Qt::AlignCenter);
|
||||
@@ -91,6 +117,26 @@ FloatingWidgetTitleBar::FloatingWidgetTitleBar(FloatingDockContainer *parent)
|
||||
{
|
||||
d->m_floatingWidget = parent;
|
||||
d->createLayout();
|
||||
|
||||
d->m_normalIcon = DockManager::iconProvider().customIcon(ADS::FloatingWidgetNormalIcon);
|
||||
if (d->m_normalIcon.isNull()) {
|
||||
auto normalPixmap = style()->standardPixmap(QStyle::SP_TitleBarNormalButton,
|
||||
0,
|
||||
d->m_maximizeButton);
|
||||
d->m_normalIcon.addPixmap(normalPixmap, QIcon::Normal);
|
||||
d->m_normalIcon.addPixmap(internal::createTransparentPixmap(normalPixmap, 0.25),
|
||||
QIcon::Disabled);
|
||||
}
|
||||
|
||||
d->m_maximizeIcon = DockManager::iconProvider().customIcon(ADS::FloatingWidgetMaximizeIcon);
|
||||
if (d->m_maximizeIcon.isNull()) {
|
||||
auto maxPixmap = this->style()->standardPixmap(QStyle::SP_TitleBarMaxButton,
|
||||
0,
|
||||
d->m_maximizeButton);
|
||||
d->m_maximizeIcon.addPixmap(maxPixmap, QIcon::Normal);
|
||||
d->m_maximizeIcon.addPixmap(internal::createTransparentPixmap(maxPixmap, 0.25),
|
||||
QIcon::Disabled);
|
||||
}
|
||||
}
|
||||
|
||||
FloatingWidgetTitleBar::~FloatingWidgetTitleBar()
|
||||
@@ -125,8 +171,11 @@ void FloatingWidgetTitleBar::mouseMoveEvent(QMouseEvent *event)
|
||||
return;
|
||||
}
|
||||
|
||||
// move floating window
|
||||
// Move floating window
|
||||
if (DraggingFloatingWidget == d->m_dragState) {
|
||||
if (d->m_floatingWidget->isMaximized())
|
||||
d->m_floatingWidget->showNormal(true);
|
||||
|
||||
d->m_floatingWidget->moveFloating();
|
||||
Super::mouseMoveEvent(event);
|
||||
return;
|
||||
@@ -134,6 +183,16 @@ void FloatingWidgetTitleBar::mouseMoveEvent(QMouseEvent *event)
|
||||
Super::mouseMoveEvent(event);
|
||||
}
|
||||
|
||||
void FloatingWidgetTitleBar::mouseDoubleClickEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->buttons() & Qt::LeftButton) {
|
||||
emit maximizeRequested();
|
||||
event->accept();
|
||||
} else {
|
||||
QWidget::mouseDoubleClickEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void FloatingWidgetTitleBar::enableCloseButton(bool enable)
|
||||
{
|
||||
d->m_closeButton->setEnabled(enable);
|
||||
@@ -149,4 +208,13 @@ void FloatingWidgetTitleBar::updateStyle()
|
||||
internal::repolishStyle(this, internal::RepolishDirectChildren);
|
||||
}
|
||||
|
||||
void FloatingWidgetTitleBar::setMaximizedIcon(bool maximized)
|
||||
{
|
||||
d->m_maximized = maximized;
|
||||
if (maximized)
|
||||
d->m_maximizeButton->setIcon(d->m_normalIcon);
|
||||
else
|
||||
d->m_maximizeButton->setIcon(d->m_maximizeIcon);
|
||||
}
|
||||
|
||||
} // namespace ADS
|
||||
|
@@ -4,6 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <QFrame>
|
||||
#include <QIcon>
|
||||
|
||||
namespace ADS {
|
||||
|
||||
@@ -11,11 +12,10 @@ class FloatingDockContainer;
|
||||
class FloatingWidgetTitleBarPrivate;
|
||||
|
||||
/**
|
||||
* Titlebar for floating widgets to capture non client are mouse events.
|
||||
* Title bar for floating widgets to capture non client area mouse events.
|
||||
* Linux does not support NonClientArea mouse events like
|
||||
* QEvent::NonClientAreaMouseButtonPress. Because these events are required
|
||||
* for the docking system to work properly, we use our own titlebar here to
|
||||
* capture the required mouse events.
|
||||
* QEvent::NonClientAreaMouseButtonPress. Because these events are required for the docking system
|
||||
* to work properly, we use our own titlebar here to capture the required mouse events.
|
||||
*/
|
||||
class FloatingWidgetTitleBar : public QFrame
|
||||
{
|
||||
@@ -27,6 +27,7 @@ protected:
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
void mouseMoveEvent(QMouseEvent *event) override;
|
||||
void mouseDoubleClickEvent(QMouseEvent *event) override;
|
||||
|
||||
public:
|
||||
using Super = QWidget;
|
||||
@@ -52,11 +53,21 @@ public:
|
||||
*/
|
||||
void updateStyle();
|
||||
|
||||
/**
|
||||
* Change the maximize button icon according to current windows state
|
||||
*/
|
||||
void setMaximizedIcon(bool maximized);
|
||||
|
||||
signals:
|
||||
/**
|
||||
* This signal is emitted, if the close button is clicked.
|
||||
*/
|
||||
void closeRequested();
|
||||
|
||||
/**
|
||||
* This signal is emitted, if the maximize button is clicked.
|
||||
*/
|
||||
void maximizeRequested();
|
||||
};
|
||||
|
||||
} // namespace ADS
|
||||
|
55
src/libs/advanceddockingsystem/pushbutton.cpp
Normal file
55
src/libs/advanceddockingsystem/pushbutton.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright (C) 2020 Uwe Kindler
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-2.1-or-later OR GPL-3.0-or-later
|
||||
|
||||
#include "pushbutton.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QStyleOptionButton>
|
||||
#include <QDebug>
|
||||
#include <QStylePainter>
|
||||
|
||||
namespace ADS {
|
||||
|
||||
QSize PushButton::sizeHint() const
|
||||
{
|
||||
QSize sh = QPushButton::sizeHint();
|
||||
|
||||
if (m_orientation != PushButton::Horizontal)
|
||||
sh.transpose();
|
||||
|
||||
return sh;
|
||||
}
|
||||
|
||||
PushButton::Orientation PushButton::buttonOrientation() const
|
||||
{
|
||||
return m_orientation;
|
||||
}
|
||||
|
||||
void PushButton::setButtonOrientation(Orientation orientation)
|
||||
{
|
||||
m_orientation = orientation;
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void PushButton::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
|
||||
QStylePainter painter(this);
|
||||
QStyleOptionButton option;
|
||||
initStyleOption(&option);
|
||||
|
||||
if (m_orientation == PushButton::VerticalTopToBottom) {
|
||||
painter.rotate(90);
|
||||
painter.translate(0, -1 * width());
|
||||
option.rect = option.rect.transposed();
|
||||
} else if (m_orientation == PushButton::VerticalBottomToTop) {
|
||||
painter.rotate(-90);
|
||||
painter.translate(-1 * height(), 0);
|
||||
option.rect = option.rect.transposed();
|
||||
}
|
||||
|
||||
painter.drawControl(QStyle::CE_PushButton, option);
|
||||
}
|
||||
|
||||
} // namespace ADS
|
47
src/libs/advanceddockingsystem/pushbutton.h
Normal file
47
src/libs/advanceddockingsystem/pushbutton.h
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright (C) 2020 Uwe Kindler
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-2.1-or-later OR GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ads_globals.h"
|
||||
|
||||
#include <QPushButton>
|
||||
|
||||
namespace ADS {
|
||||
|
||||
/**
|
||||
* ADS specific push button class with orientation support
|
||||
*/
|
||||
class ADS_EXPORT PushButton : public QPushButton
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum Orientation {
|
||||
Horizontal,
|
||||
VerticalTopToBottom,
|
||||
VerticalBottomToTop
|
||||
};
|
||||
|
||||
using QPushButton::QPushButton;
|
||||
|
||||
virtual QSize sizeHint() const override;
|
||||
|
||||
/**
|
||||
* Returns the current orientation
|
||||
*/
|
||||
Orientation buttonOrientation() const;
|
||||
|
||||
/**
|
||||
* Set the orientation of this button
|
||||
*/
|
||||
void setButtonOrientation(Orientation orientation);
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
private:
|
||||
Orientation m_orientation = Horizontal;
|
||||
}; // class PushButton
|
||||
|
||||
} // namespace ADS
|
269
src/libs/advanceddockingsystem/resizehandle.cpp
Normal file
269
src/libs/advanceddockingsystem/resizehandle.cpp
Normal file
@@ -0,0 +1,269 @@
|
||||
// Copyright (C) 2020 Uwe Kindler
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-2.1-or-later OR GPL-3.0-or-later
|
||||
|
||||
#include "resizehandle.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QStyle>
|
||||
#include <QStyleOption>
|
||||
#include <QMouseEvent>
|
||||
#include <QRubberBand>
|
||||
#include <QPointer>
|
||||
|
||||
namespace ADS {
|
||||
|
||||
/**
|
||||
* Private data class of CResizeHandle class (pimpl)
|
||||
*/
|
||||
class ResizeHandlePrivate
|
||||
{
|
||||
public:
|
||||
ResizeHandle *q = nullptr;
|
||||
Qt::Edge m_handlePosition = Qt::LeftEdge;
|
||||
QWidget *m_target = nullptr;
|
||||
int m_mouseOffset = 0;
|
||||
bool m_pressed = false;
|
||||
int m_minSize = 0;
|
||||
int m_maxSize = 1;
|
||||
QPointer<QRubberBand> m_rubberBand;
|
||||
bool m_opaqueResize = false;
|
||||
int m_handleWidth = 4;
|
||||
|
||||
/**
|
||||
* Private data constructor
|
||||
*/
|
||||
ResizeHandlePrivate(ResizeHandle *parent);
|
||||
|
||||
/**
|
||||
* Pick position component from pos depending on orientation
|
||||
*/
|
||||
int pick(const QPoint &pos) const
|
||||
{
|
||||
return q->orientation() == Qt::Horizontal ? pos.x() : pos.y();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true, if orientation is horizontal
|
||||
*/
|
||||
bool isHorizontal() const { return q->orientation() == Qt::Horizontal; }
|
||||
|
||||
/**
|
||||
* Set rubberband position
|
||||
*/
|
||||
void setRubberBand(int pos);
|
||||
|
||||
/**
|
||||
* Calculates the resize position and geometry
|
||||
*/
|
||||
void doResizing(QMouseEvent *event, bool forceResize = false);
|
||||
};
|
||||
// class ResizeHandlePrivate
|
||||
|
||||
ResizeHandlePrivate::ResizeHandlePrivate(ResizeHandle *parent)
|
||||
: q(parent)
|
||||
{}
|
||||
|
||||
void ResizeHandlePrivate::setRubberBand(int pos)
|
||||
{
|
||||
if (!m_rubberBand)
|
||||
m_rubberBand = new QRubberBand(QRubberBand::Line, m_target->parentWidget());
|
||||
|
||||
auto geometry = q->geometry();
|
||||
auto topLeft = m_target->mapTo(m_target->parentWidget(), geometry.topLeft());
|
||||
switch (m_handlePosition) {
|
||||
case Qt::LeftEdge:
|
||||
case Qt::RightEdge:
|
||||
topLeft.rx() += pos;
|
||||
break;
|
||||
case Qt::TopEdge:
|
||||
case Qt::BottomEdge:
|
||||
topLeft.ry() += pos;
|
||||
break;
|
||||
}
|
||||
|
||||
geometry.moveTopLeft(topLeft);
|
||||
m_rubberBand->setGeometry(geometry);
|
||||
m_rubberBand->show();
|
||||
}
|
||||
|
||||
void ResizeHandlePrivate::doResizing(QMouseEvent *event, bool forceResize)
|
||||
{
|
||||
int pos = pick(event->pos()) - m_mouseOffset;
|
||||
auto oldGeometry = m_target->geometry();
|
||||
auto newGeometry = oldGeometry;
|
||||
switch (m_handlePosition) {
|
||||
case Qt::LeftEdge: {
|
||||
newGeometry.adjust(pos, 0, 0, 0);
|
||||
int size = qBound(m_minSize, newGeometry.width(), m_maxSize);
|
||||
pos += (newGeometry.width() - size);
|
||||
newGeometry.setWidth(size);
|
||||
newGeometry.moveTopRight(oldGeometry.topRight());
|
||||
} break;
|
||||
|
||||
case Qt::RightEdge: {
|
||||
newGeometry.adjust(0, 0, pos, 0);
|
||||
int size = qBound(m_minSize, newGeometry.width(), m_maxSize);
|
||||
pos -= (newGeometry.width() - size);
|
||||
newGeometry.setWidth(size);
|
||||
} break;
|
||||
|
||||
case Qt::TopEdge: {
|
||||
newGeometry.adjust(0, pos, 0, 0);
|
||||
int size = qBound(m_minSize, newGeometry.height(), m_maxSize);
|
||||
pos += (newGeometry.height() - size);
|
||||
newGeometry.setHeight(size);
|
||||
newGeometry.moveBottomLeft(oldGeometry.bottomLeft());
|
||||
} break;
|
||||
|
||||
case Qt::BottomEdge: {
|
||||
newGeometry.adjust(0, 0, 0, pos);
|
||||
int size = qBound(m_minSize, newGeometry.height(), m_maxSize);
|
||||
pos -= (newGeometry.height() - size);
|
||||
newGeometry.setHeight(size);
|
||||
} break;
|
||||
}
|
||||
|
||||
if (q->opaqueResize() || forceResize) {
|
||||
m_target->setGeometry(newGeometry);
|
||||
} else {
|
||||
setRubberBand(pos);
|
||||
}
|
||||
}
|
||||
|
||||
ResizeHandle::ResizeHandle(Qt::Edge handlePosition, QWidget *parent)
|
||||
: Super(parent)
|
||||
, d(new ResizeHandlePrivate(this))
|
||||
{
|
||||
d->m_target = parent;
|
||||
setMinResizeSize(48);
|
||||
setHandlePosition(handlePosition);
|
||||
}
|
||||
|
||||
ResizeHandle::~ResizeHandle()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
void ResizeHandle::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
if (!(event->buttons() & Qt::LeftButton))
|
||||
return;
|
||||
|
||||
d->doResizing(event);
|
||||
}
|
||||
|
||||
void ResizeHandle::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
d->m_mouseOffset = d->pick(event->pos());
|
||||
d->m_pressed = true;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void ResizeHandle::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
if (!opaqueResize() && event->button() == Qt::LeftButton) {
|
||||
if (d->m_rubberBand)
|
||||
d->m_rubberBand->deleteLater();
|
||||
|
||||
d->doResizing(event, true);
|
||||
}
|
||||
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
d->m_pressed = false;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void ResizeHandle::setHandlePosition(Qt::Edge handlePosition)
|
||||
{
|
||||
d->m_handlePosition = handlePosition;
|
||||
switch (d->m_handlePosition) {
|
||||
case Qt::LeftEdge: // fall through
|
||||
case Qt::RightEdge:
|
||||
setCursor(Qt::SizeHorCursor);
|
||||
break;
|
||||
|
||||
case Qt::TopEdge: // fall through
|
||||
case Qt::BottomEdge:
|
||||
setCursor(Qt::SizeVerCursor);
|
||||
break;
|
||||
}
|
||||
|
||||
setMaxResizeSize(d->isHorizontal() ? parentWidget()->height() : parentWidget()->width());
|
||||
if (!d->isHorizontal()) {
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
} else {
|
||||
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
|
||||
}
|
||||
}
|
||||
|
||||
Qt::Edge ResizeHandle::handlePostion() const
|
||||
{
|
||||
return d->m_handlePosition;
|
||||
}
|
||||
|
||||
Qt::Orientation ResizeHandle::orientation() const
|
||||
{
|
||||
switch (d->m_handlePosition) {
|
||||
case Qt::LeftEdge: // fall through
|
||||
case Qt::RightEdge:
|
||||
return Qt::Horizontal;
|
||||
|
||||
case Qt::TopEdge: // fall through
|
||||
case Qt::BottomEdge:
|
||||
return Qt::Vertical;
|
||||
}
|
||||
|
||||
return Qt::Horizontal;
|
||||
}
|
||||
|
||||
QSize ResizeHandle::sizeHint() const
|
||||
{
|
||||
QSize result;
|
||||
switch (d->m_handlePosition) {
|
||||
case Qt::LeftEdge: // fall through
|
||||
case Qt::RightEdge:
|
||||
result = QSize(d->m_handleWidth, d->m_target->height());
|
||||
break;
|
||||
|
||||
case Qt::TopEdge: // fall through
|
||||
case Qt::BottomEdge:
|
||||
result = QSize(d->m_target->width(), d->m_handleWidth);
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ResizeHandle::isResizing() const
|
||||
{
|
||||
return d->m_pressed;
|
||||
}
|
||||
|
||||
void ResizeHandle::setMinResizeSize(int minSize)
|
||||
{
|
||||
d->m_minSize = minSize;
|
||||
}
|
||||
|
||||
void ResizeHandle::setMaxResizeSize(int maxSize)
|
||||
{
|
||||
d->m_maxSize = maxSize;
|
||||
}
|
||||
|
||||
void ResizeHandle::setOpaqueResize(bool opaque)
|
||||
{
|
||||
if (d->m_opaqueResize == opaque)
|
||||
return;
|
||||
|
||||
d->m_opaqueResize = opaque;
|
||||
emit opaqueResizeChanged();
|
||||
}
|
||||
|
||||
bool ResizeHandle::opaqueResize() const
|
||||
{
|
||||
return d->m_opaqueResize;
|
||||
}
|
||||
|
||||
} // namespace ADS
|
99
src/libs/advanceddockingsystem/resizehandle.h
Normal file
99
src/libs/advanceddockingsystem/resizehandle.h
Normal file
@@ -0,0 +1,99 @@
|
||||
// Copyright (C) 2020 Uwe Kindler
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-2.1-or-later OR GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ads_globals.h"
|
||||
|
||||
#include <QFrame>
|
||||
|
||||
namespace ADS {
|
||||
|
||||
class ResizeHandlePrivate;
|
||||
|
||||
/**
|
||||
* Resize handle for resizing its parent widget
|
||||
*/
|
||||
class ADS_EXPORT ResizeHandle : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(ResizeHandle)
|
||||
Q_PROPERTY(bool opaqueResize READ opaqueResize WRITE setOpaqueResize NOTIFY opaqueResizeChanged)
|
||||
|
||||
private:
|
||||
ResizeHandlePrivate *d; ///< private data (pimpl)
|
||||
friend class ResizeHandlePrivate;
|
||||
|
||||
protected:
|
||||
void mouseMoveEvent(QMouseEvent *) override;
|
||||
void mousePressEvent(QMouseEvent *) override;
|
||||
void mouseReleaseEvent(QMouseEvent *) override;
|
||||
|
||||
public:
|
||||
using Super = QFrame;
|
||||
|
||||
/**
|
||||
* Default Constructor
|
||||
*/
|
||||
ResizeHandle(Qt::Edge handlePosition, QWidget *parent);
|
||||
|
||||
/**
|
||||
* Virtual Destructor
|
||||
*/
|
||||
virtual ~ResizeHandle();
|
||||
|
||||
/**
|
||||
* Sets the handle position
|
||||
*/
|
||||
void setHandlePosition(Qt::Edge handlePosition);
|
||||
|
||||
/**
|
||||
* Returns the handle position
|
||||
*/
|
||||
Qt::Edge handlePostion() const;
|
||||
|
||||
/**
|
||||
* Returns the orientation of this resize handle
|
||||
*/
|
||||
Qt::Orientation orientation() const;
|
||||
|
||||
/**
|
||||
* Returns the size hint
|
||||
*/
|
||||
QSize sizeHint() const override;
|
||||
|
||||
/**
|
||||
* Returns true, if resizing is active
|
||||
*/
|
||||
bool isResizing() const;
|
||||
|
||||
/**
|
||||
* Sets the minimum size for the widget that is going to be resized.
|
||||
* The resize handle will not resize the target widget to a size smaller
|
||||
* than this value
|
||||
*/
|
||||
void setMinResizeSize(int minSize);
|
||||
|
||||
/**
|
||||
* Sets the maximum size for the widget that is going to be resized
|
||||
* The resize handle will not resize the target widget to a size bigger
|
||||
* than this value
|
||||
*/
|
||||
void setMaxResizeSize(int maxSize);
|
||||
|
||||
/**
|
||||
* Enable / disable opaque resizing
|
||||
*/
|
||||
void setOpaqueResize(bool opaque = true);
|
||||
|
||||
/**
|
||||
* Returns true if widgets are resized dynamically (opaquely) while
|
||||
* interactively moving the resize handle. Otherwise returns false.
|
||||
*/
|
||||
bool opaqueResize() const;
|
||||
|
||||
signals:
|
||||
void opaqueResizeChanged();
|
||||
}; // class ResizeHandle
|
||||
|
||||
} // namespace ADS
|
@@ -15,76 +15,80 @@
|
||||
namespace QmlDesigner {
|
||||
|
||||
using EnumerationName = QByteArray;
|
||||
using EnumerationNameView = QByteArrayView;
|
||||
|
||||
class Enumeration
|
||||
{
|
||||
friend bool operator ==(const Enumeration &first, const Enumeration &second);
|
||||
friend bool operator <(const Enumeration &first, const Enumeration &second);
|
||||
friend QDataStream &operator>>(QDataStream &in, Enumeration &enumeration);
|
||||
|
||||
public:
|
||||
Enumeration() = default;
|
||||
Enumeration(const EnumerationName &enumerationName)
|
||||
: m_enumerationName(enumerationName)
|
||||
{
|
||||
}
|
||||
Enumeration(EnumerationName enumerationName)
|
||||
: m_enumerationName{std::move(enumerationName)}
|
||||
{}
|
||||
|
||||
Enumeration(const char *text)
|
||||
: m_enumerationName{text, static_cast<qsizetype>(std::strlen(text))}
|
||||
{}
|
||||
|
||||
Enumeration(const QString &enumerationName)
|
||||
: m_enumerationName(enumerationName.toUtf8())
|
||||
{}
|
||||
|
||||
Enumeration(const EnumerationName &scope, const EnumerationName &name)
|
||||
{
|
||||
}
|
||||
Enumeration(const QString &scope, const QString &name)
|
||||
{
|
||||
QString enumerationString = scope + QLatin1Char('.') + name;
|
||||
m_enumerationName = enumerationString.toUtf8();
|
||||
m_enumerationName.reserve(scope.size() + 1 + name.size());
|
||||
m_enumerationName.append(scope);
|
||||
m_enumerationName.append(1);
|
||||
m_enumerationName.append(name);
|
||||
}
|
||||
|
||||
EnumerationName scope() const
|
||||
EnumerationNameView scope() const
|
||||
{
|
||||
return m_enumerationName.split('.').constFirst();
|
||||
auto found = std::find(m_enumerationName.begin(), m_enumerationName.end(), '.');
|
||||
return {m_enumerationName.begin(), found};
|
||||
}
|
||||
EnumerationName name() const
|
||||
|
||||
EnumerationNameView toScope() const { return scope().toByteArray(); }
|
||||
|
||||
EnumerationNameView name() const
|
||||
{
|
||||
return m_enumerationName.split('.').last();
|
||||
auto found = std::find(m_enumerationName.begin(), m_enumerationName.end(), '.');
|
||||
if (found != m_enumerationName.end())
|
||||
return {std::next(found), m_enumerationName.end()};
|
||||
|
||||
return {m_enumerationName.end(), m_enumerationName.end()};
|
||||
}
|
||||
EnumerationName toEnumerationName() const
|
||||
|
||||
EnumerationName toName() const { return name().toByteArray(); }
|
||||
|
||||
EnumerationName toEnumerationName() const { return m_enumerationName; }
|
||||
|
||||
QString toString() const { return QString::fromUtf8(m_enumerationName); }
|
||||
QString nameToString() const { return QString::fromUtf8(name()); }
|
||||
|
||||
friend bool operator==(const Enumeration &first, const Enumeration &second)
|
||||
{
|
||||
return m_enumerationName;
|
||||
return first.m_enumerationName == second.m_enumerationName;
|
||||
}
|
||||
QString toString() const
|
||||
|
||||
friend bool operator<(const Enumeration &first, const Enumeration &second)
|
||||
{
|
||||
return QString::fromUtf8(m_enumerationName);
|
||||
return first.m_enumerationName < second.m_enumerationName;
|
||||
}
|
||||
QString nameToString() const
|
||||
|
||||
friend QDataStream &operator<<(QDataStream &out, const Enumeration &enumeration)
|
||||
{
|
||||
return QString::fromUtf8(name());
|
||||
return out << enumeration.m_enumerationName;
|
||||
}
|
||||
|
||||
friend QDataStream &operator>>(QDataStream &in, Enumeration &enumeration)
|
||||
{
|
||||
return in >> enumeration.m_enumerationName;
|
||||
}
|
||||
|
||||
private:
|
||||
EnumerationName m_enumerationName;
|
||||
};
|
||||
|
||||
inline QDataStream &operator<<(QDataStream &out, const Enumeration &enumeration){
|
||||
out << enumeration.toEnumerationName();
|
||||
return out;
|
||||
}
|
||||
|
||||
inline QDataStream &operator>>(QDataStream &in, Enumeration &enumeration)
|
||||
{
|
||||
in >> enumeration.m_enumerationName;
|
||||
return in;
|
||||
}
|
||||
|
||||
|
||||
inline bool operator==(const Enumeration &first, const Enumeration &second)
|
||||
{
|
||||
return first.m_enumerationName == second.m_enumerationName;
|
||||
}
|
||||
|
||||
inline bool operator<(const Enumeration &first, const Enumeration &second)
|
||||
{
|
||||
return first.m_enumerationName < second.m_enumerationName;
|
||||
}
|
||||
|
||||
inline QDebug operator <<(QDebug debug, const Enumeration &enumeration)
|
||||
{
|
||||
debug.nospace() << "Enumeration("
|
||||
|
@@ -19,8 +19,6 @@ public:
|
||||
|
||||
constexpr explicit BasicId() = default;
|
||||
|
||||
constexpr BasicId(const char *) = delete;
|
||||
|
||||
static constexpr BasicId create(InternalIntegerType idNumber)
|
||||
{
|
||||
BasicId id;
|
||||
|
@@ -108,13 +108,9 @@ public:
|
||||
static_assert(!std::is_array<Type>::value, "Input type is array and not char pointer!");
|
||||
}
|
||||
|
||||
BasicSmallString(const QString &qString)
|
||||
: BasicSmallString(BasicSmallString::fromQString(qString))
|
||||
{}
|
||||
BasicSmallString(const QString &qString) { append(qString); }
|
||||
|
||||
BasicSmallString(const QStringView qStringView)
|
||||
: BasicSmallString(BasicSmallString::fromQStringView(qStringView))
|
||||
{}
|
||||
BasicSmallString(const QStringView qStringView) { append(qStringView); }
|
||||
|
||||
BasicSmallString(const QByteArray &qByteArray)
|
||||
: BasicSmallString(qByteArray.constData(), qByteArray.size())
|
||||
@@ -127,9 +123,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
BasicSmallString(const std::wstring &wstring)
|
||||
: BasicSmallString(BasicSmallString::fromQStringView(wstring))
|
||||
{}
|
||||
BasicSmallString(const std::wstring &wstring) { append(wstring); }
|
||||
|
||||
template<typename BeginIterator,
|
||||
typename EndIterator,
|
||||
@@ -349,12 +343,10 @@ public:
|
||||
return BasicSmallString(utf8ByteArray.constData(), uint(utf8ByteArray.size()));
|
||||
}
|
||||
|
||||
// precondition: has to be null terminated
|
||||
bool contains(SmallStringView subStringToSearch) const
|
||||
{
|
||||
const char *found = std::strstr(data(), subStringToSearch.data());
|
||||
|
||||
return found != nullptr;
|
||||
return SmallStringView{*this}.find(subStringToSearch)
|
||||
!= SmallStringView::npos;
|
||||
}
|
||||
|
||||
bool contains(char characterToSearch) const
|
||||
@@ -455,7 +447,7 @@ public:
|
||||
constexpr size_type temporaryArraySize = Size * 6;
|
||||
|
||||
size_type oldSize = size();
|
||||
size_type maximumRequiredSize = static_cast<size_type>(encoder.requiredSpace(oldSize));
|
||||
size_type maximumRequiredSize = static_cast<size_type>(encoder.requiredSpace(string.size()));
|
||||
char *newEnd = nullptr;
|
||||
|
||||
if (maximumRequiredSize > temporaryArraySize) {
|
||||
|
@@ -82,7 +82,6 @@ if(TARGET QmlDesignerCore)
|
||||
add_feature_info("ProjectStorage" ${USE_PROJECTSTORAGE} "")
|
||||
endif()
|
||||
|
||||
|
||||
extend_qtc_library(QmlDesignerCore
|
||||
CONDITION ENABLE_COMPILE_WARNING_AS_ERROR
|
||||
PROPERTIES COMPILE_WARNING_AS_ERROR ON
|
||||
@@ -186,23 +185,40 @@ extend_qtc_library(QmlDesignerCore
|
||||
extend_qtc_library(QmlDesignerCore
|
||||
SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/designercore/include
|
||||
SOURCES
|
||||
abstractproperty.h
|
||||
abstractview.h
|
||||
basetexteditmodifier.h
|
||||
bytearraymodifier.h
|
||||
componenttextmodifier.h
|
||||
forwardview.h
|
||||
itemlibraryinfo.h
|
||||
metainforeader.h
|
||||
model.h
|
||||
nodehints.h
|
||||
plaintexteditmodifier.h
|
||||
nodeinstanceview.h
|
||||
propertyparser.h
|
||||
rewriterview.h
|
||||
subcomponentmanager.h
|
||||
textmodifier.h
|
||||
)
|
||||
|
||||
extend_qtc_library(QmlDesignerCore
|
||||
SOURCES_PROPERTIES SKIP_AUTOGEN ON
|
||||
SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/designercore/include
|
||||
SOURCES
|
||||
abstractproperty.h
|
||||
anchorline.h
|
||||
annotation.h
|
||||
asynchronousexplicitimagecache.h
|
||||
asynchronousimagecache.h
|
||||
auxiliarydata.h
|
||||
auxiliarydataproperties.h
|
||||
basetexteditmodifier.h
|
||||
bindingproperty.h
|
||||
componenttextmodifier.h
|
||||
customnotifications.h
|
||||
documentmessage.h
|
||||
enumerationmetainfo.h
|
||||
exception.h
|
||||
externaldependenciesinterface.h
|
||||
forwardview.h
|
||||
imagecacheauxiliarydata.h
|
||||
import.h
|
||||
invalidargumentexception.h
|
||||
@@ -214,33 +230,26 @@ extend_qtc_library(QmlDesignerCore
|
||||
invalidqmlsourceexception.h
|
||||
invalidreparentingexception.h
|
||||
invalidslideindexexception.h
|
||||
itemlibraryinfo.h
|
||||
mathutils.h
|
||||
metainfo.h
|
||||
metainforeader.h
|
||||
model.h
|
||||
modelfwd.h
|
||||
modelmerger.h
|
||||
modelnode.h
|
||||
modelnodepositionstorage.h
|
||||
nodeabstractproperty.h
|
||||
nodehints.h
|
||||
nodeinstance.h
|
||||
nodeinstanceview.h
|
||||
nodelistproperty.h
|
||||
nodemetainfo.h
|
||||
nodeproperty.h
|
||||
notimplementedexception.h
|
||||
plaintexteditmodifier.h
|
||||
propertycontainer.h
|
||||
propertymetainfo.h
|
||||
propertynode.h
|
||||
propertyparser.h
|
||||
qmlanchors.h
|
||||
qmlchangeset.h
|
||||
qmlconnections.h
|
||||
qmldesignercorelib_global.h
|
||||
qmldesignercorelib_exports.h
|
||||
qmldesignercorelib_global.h
|
||||
qmlitemnode.h
|
||||
qmlmodelnodefacade.h
|
||||
qmlobjectnode.h
|
||||
@@ -248,14 +257,11 @@ extend_qtc_library(QmlDesignerCore
|
||||
qmltimeline.h
|
||||
qmltimelinekeyframegroup.h
|
||||
removebasestateexception.h
|
||||
rewriterview.h
|
||||
rewritingexception.h
|
||||
signalhandlerproperty.h
|
||||
stringutils.h
|
||||
stylesheetmerger.h
|
||||
subcomponentmanager.h
|
||||
synchronousimagecache.h
|
||||
textmodifier.h
|
||||
variantproperty.h
|
||||
)
|
||||
|
||||
@@ -382,7 +388,7 @@ extend_qtc_library(QmlDesignerCore
|
||||
extend_qtc_library(QmlDesignerCore
|
||||
SOURCES_PREFIX designercore/projectstorage
|
||||
PUBLIC_INCLUDES designercore/projectstorage
|
||||
SOURCES_PROPERTIES SKIP_AUTOMOC ON
|
||||
SOURCES_PROPERTIES SKIP_AUTOGEN ON
|
||||
SOURCES
|
||||
commontypecache.h
|
||||
directorypathcompressor.h
|
||||
@@ -407,7 +413,7 @@ extend_qtc_library(QmlDesignerCore
|
||||
projectstorage.cpp projectstorage.h
|
||||
sourcepath.h
|
||||
sourcepathcache.h
|
||||
sourcepathcache.h
|
||||
sourcepathcacheinterface.h
|
||||
sourcepathcachetypes.h
|
||||
sourcepathview.h
|
||||
storagecache.h
|
||||
|
@@ -4,12 +4,13 @@
|
||||
#include "componentexporter.h"
|
||||
#include "exportnotification.h"
|
||||
|
||||
#include "designdocument.h"
|
||||
#include "nodemetainfo.h"
|
||||
#include "qmldesignerplugin.h"
|
||||
#include "rewriterview.h"
|
||||
#include "qmlitemnode.h"
|
||||
#include "qmlobjectnode.h"
|
||||
#include <designdocument.h>
|
||||
#include <model/modelutils.h>
|
||||
#include <nodemetainfo.h>
|
||||
#include <qmldesignerplugin.h>
|
||||
#include <qmlitemnode.h>
|
||||
#include <qmlobjectnode.h>
|
||||
#include <rewriterview.h>
|
||||
|
||||
#include <coreplugin/editormanager/editormanager.h>
|
||||
#include <projectexplorer/project.h>
|
||||
@@ -340,12 +341,11 @@ QString AssetExporter::componentUuid(const ModelNode &instance) const
|
||||
// Returns the UUID of the component's root node
|
||||
// Empty string is returned if the node is not an instance of a component within
|
||||
// the project.
|
||||
NodeMetaInfo metaInfo = instance.metaInfo();
|
||||
if (!metaInfo.isValid())
|
||||
return {};
|
||||
const QString path = metaInfo.componentFileName();
|
||||
if (m_componentUuidCache.contains(path))
|
||||
return m_componentUuidCache[path];
|
||||
if (instance) {
|
||||
const QString path = ModelUtils::componentFilePath(instance);
|
||||
return m_componentUuidCache.value(path);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@@ -28,7 +28,7 @@ static QByteArrayList populateLineage(const QmlDesigner::ModelNode &node)
|
||||
if (!node.isValid() || node.type().isEmpty())
|
||||
return {};
|
||||
|
||||
for (auto &info : node.metaInfo().superClasses())
|
||||
for (auto &info : node.metaInfo().prototypes())
|
||||
lineage.append(info.typeName());
|
||||
|
||||
return lineage;
|
||||
|
@@ -91,10 +91,7 @@ void BindingEditor::setBackendValue(const QVariant &backendValue)
|
||||
const ModelNode node = propertyEditorValue->modelNode();
|
||||
|
||||
if (node.isValid()) {
|
||||
m_backendValueTypeName = node.metaInfo()
|
||||
.property(propertyEditorValue->name())
|
||||
.propertyType()
|
||||
.simplifiedTypeName();
|
||||
m_backendValueType = node.metaInfo().property(propertyEditorValue->name()).propertyType();
|
||||
|
||||
QString nodeId = node.id();
|
||||
if (nodeId.isEmpty())
|
||||
@@ -102,9 +99,11 @@ void BindingEditor::setBackendValue(const QVariant &backendValue)
|
||||
|
||||
m_targetName = nodeId + "." + propertyEditorValue->name();
|
||||
|
||||
if (m_backendValueTypeName == "alias" || m_backendValueTypeName == "unknown")
|
||||
if (!m_backendValueType || m_backendValueType.isAlias()) {
|
||||
if (QmlObjectNode::isValidQmlObjectNode(node))
|
||||
m_backendValueTypeName = QmlObjectNode(node).instanceType(propertyEditorValue->name());
|
||||
m_backendValueType = node.model()->metaInfo(
|
||||
QmlObjectNode(node).instanceType(propertyEditorValue->name()));
|
||||
}
|
||||
}
|
||||
|
||||
emit backendValueChanged();
|
||||
@@ -135,7 +134,7 @@ void BindingEditor::setStateModelNode(const QVariant &stateModelNode)
|
||||
m_modelNode = m_stateModelNode.value<QmlDesigner::ModelNode>();
|
||||
|
||||
if (m_modelNode.isValid())
|
||||
m_backendValueTypeName = "bool";
|
||||
m_backendValueType = m_modelNode.model()->boolMetaInfo();
|
||||
|
||||
emit stateModelNodeChanged();
|
||||
}
|
||||
@@ -153,9 +152,9 @@ void BindingEditor::setModelNode(const ModelNode &modelNode)
|
||||
m_modelNode = modelNode;
|
||||
}
|
||||
|
||||
void BindingEditor::setBackendValueTypeName(const TypeName &backendValueTypeName)
|
||||
void BindingEditor::setBackendValueType(const NodeMetaInfo &backendValueType)
|
||||
{
|
||||
m_backendValueTypeName = backendValueTypeName;
|
||||
m_backendValueType = backendValueType;
|
||||
|
||||
emit backendValueChanged();
|
||||
}
|
||||
@@ -165,65 +164,80 @@ void BindingEditor::setTargetName(const QString &target)
|
||||
m_targetName = target;
|
||||
}
|
||||
|
||||
namespace {
|
||||
template<typename Tuple>
|
||||
bool isType(const Tuple &types, const TypeName &compareType)
|
||||
{
|
||||
return std::apply([&](const auto &...type) { return ((type == compareType) || ...); }, types);
|
||||
}
|
||||
|
||||
template<typename... Tuple>
|
||||
bool isType(const TypeName &first, const TypeName &second, const Tuple &...types)
|
||||
{
|
||||
return ((types == first) || ...) && ((types == second) || ...);
|
||||
}
|
||||
|
||||
bool compareTypes(const NodeMetaInfo &sourceType, const NodeMetaInfo &targetType)
|
||||
{
|
||||
if constexpr (useProjectStorage()) {
|
||||
return targetType.isVariant() || sourceType.isVariant() || targetType == sourceType
|
||||
|| (targetType.isNumber() && sourceType.isNumber())
|
||||
|| (targetType.isColor() && sourceType.isColor())
|
||||
|| (targetType.isString() && sourceType.isString());
|
||||
} else {
|
||||
const TypeName source = sourceType.simplifiedTypeName();
|
||||
const TypeName target = targetType.simplifiedTypeName();
|
||||
|
||||
static constexpr auto variantTypes = std::make_tuple("alias", "unknown", "variant", "var");
|
||||
|
||||
return isType(variantTypes, target) || isType(variantTypes, source)
|
||||
|| targetType == sourceType || isType(target, source, "double", "real", "int")
|
||||
|| isType(target, source, "QColor", "color")
|
||||
|| isType(target, source, "QString", "string");
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void BindingEditor::prepareBindings()
|
||||
{
|
||||
if (!m_modelNode.isValid() || m_backendValueTypeName.isEmpty())
|
||||
if (!m_modelNode.isValid() || !m_backendValueType) {
|
||||
return;
|
||||
}
|
||||
|
||||
const QList<QmlDesigner::ModelNode> allNodes = m_modelNode.view()->allModelNodes();
|
||||
|
||||
QList<BindingEditorDialog::BindingOption> bindings;
|
||||
|
||||
const QVarLengthArray<TypeName> variantTypes = {"alias", "unknown", "variant", "var"};
|
||||
const QVarLengthArray<TypeName> numericTypes = {"double", "real", "int"};
|
||||
const QVarLengthArray<TypeName> colorTypes = {"QColor", "color"};
|
||||
const QVarLengthArray<TypeName> stringTypes = {"QString", "string"};
|
||||
|
||||
auto isVariant = [&variantTypes](const TypeName &compareType) {
|
||||
return variantTypes.contains(compareType);
|
||||
};
|
||||
auto isNumeric = [&numericTypes](const TypeName &compareType) {
|
||||
return numericTypes.contains(compareType);
|
||||
};
|
||||
auto isColor = [&colorTypes](const TypeName &compareType) {
|
||||
return colorTypes.contains(compareType);
|
||||
};
|
||||
auto isString = [&stringTypes](const TypeName &compareType) {
|
||||
return stringTypes.contains(compareType);
|
||||
};
|
||||
|
||||
auto compareTypes = [&](const TypeName &targetType, const TypeName &sourceType) {
|
||||
return isVariant(targetType) || isVariant(sourceType) || (targetType == sourceType)
|
||||
|| (isNumeric(targetType) && isNumeric(sourceType))
|
||||
|| (isColor(targetType) && isColor(sourceType))
|
||||
|| (isString(targetType) && isString(sourceType));
|
||||
};
|
||||
|
||||
for (const auto &objnode : allNodes) {
|
||||
BindingEditorDialog::BindingOption binding;
|
||||
for (const auto &property : objnode.metaInfo().properties()) {
|
||||
const TypeName &propertyTypeName = property.propertyType().simplifiedTypeName();
|
||||
const auto &propertyType = property.propertyType();
|
||||
|
||||
if (compareTypes(m_backendValueTypeName, propertyTypeName))
|
||||
if (compareTypes(m_backendValueType, propertyType)) {
|
||||
binding.properties.append(QString::fromUtf8(property.name()));
|
||||
}
|
||||
}
|
||||
|
||||
//dynamic properties:
|
||||
for (const BindingProperty &bindingProperty : objnode.bindingProperties()) {
|
||||
if (bindingProperty.isValid()) {
|
||||
if (bindingProperty.isDynamic()) {
|
||||
const TypeName dynamicTypeName = bindingProperty.dynamicTypeName();
|
||||
if (compareTypes(m_backendValueTypeName, dynamicTypeName))
|
||||
auto model = bindingProperty.model();
|
||||
const auto dynamicType = model->metaInfo(bindingProperty.dynamicTypeName());
|
||||
if (compareTypes(m_backendValueType, dynamicType)) {
|
||||
binding.properties.append(QString::fromUtf8(bindingProperty.name()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const VariantProperty &variantProperty : objnode.variantProperties()) {
|
||||
if (variantProperty.isValid()) {
|
||||
if (variantProperty.isDynamic()) {
|
||||
const TypeName dynamicTypeName = variantProperty.dynamicTypeName();
|
||||
if (compareTypes(m_backendValueTypeName, dynamicTypeName))
|
||||
auto model = variantProperty.model();
|
||||
const auto dynamicType = model->metaInfo(variantProperty.dynamicTypeName());
|
||||
if (compareTypes(m_backendValueType, dynamicType)) {
|
||||
binding.properties.append(QString::fromUtf8(variantProperty.name()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -244,10 +258,11 @@ void BindingEditor::prepareBindings()
|
||||
BindingEditorDialog::BindingOption binding;
|
||||
|
||||
for (const auto &property : metaInfo.properties()) {
|
||||
const TypeName propertyTypeName = property.propertyType().typeName();
|
||||
const auto propertyType = property.propertyType();
|
||||
|
||||
if (compareTypes(m_backendValueTypeName, propertyTypeName))
|
||||
if (compareTypes(m_backendValueType, propertyType)) {
|
||||
binding.properties.append(QString::fromUtf8(property.name()));
|
||||
}
|
||||
}
|
||||
|
||||
if (!binding.properties.isEmpty()) {
|
||||
@@ -260,15 +275,24 @@ void BindingEditor::prepareBindings()
|
||||
}
|
||||
|
||||
if (!bindings.isEmpty() && !m_dialog.isNull())
|
||||
m_dialog->setAllBindings(bindings, m_backendValueTypeName);
|
||||
m_dialog->setAllBindings(bindings, m_backendValueType);
|
||||
}
|
||||
|
||||
void BindingEditor::updateWindowName()
|
||||
{
|
||||
if (!m_dialog.isNull() && !m_backendValueTypeName.isEmpty()) {
|
||||
const QString targetString = " ["
|
||||
+ (m_targetName.isEmpty() ? QString() : (m_targetName + ": "))
|
||||
+ QString::fromUtf8(m_backendValueTypeName) + "]";
|
||||
if (!m_dialog.isNull() && m_backendValueType) {
|
||||
QString targetString;
|
||||
if constexpr (useProjectStorage()) {
|
||||
auto exportedTypeNames = m_backendValueType.exportedTypeNamesForSourceId(
|
||||
m_modelNode.model()->fileUrlSourceId());
|
||||
if (exportedTypeNames.size()) {
|
||||
targetString = " [" + (m_targetName.isEmpty() ? QString() : (m_targetName + ": "))
|
||||
+ exportedTypeNames.front().name.toQString() + "]";
|
||||
}
|
||||
} else {
|
||||
targetString = " [" + (m_targetName.isEmpty() ? QString() : (m_targetName + ": "))
|
||||
+ QString::fromUtf8(m_backendValueType.simplifiedTypeName()) + "]";
|
||||
}
|
||||
|
||||
m_dialog->setWindowTitle(m_dialog->defaultTitle() + targetString);
|
||||
}
|
||||
|
@@ -49,7 +49,7 @@ public:
|
||||
|
||||
//3. modelnode + backend value type name + optional target name
|
||||
void setModelNode(const ModelNode &modelNode);
|
||||
void setBackendValueTypeName(const TypeName &backendValueTypeName);
|
||||
void setBackendValueType(const NodeMetaInfo &backendValueType);
|
||||
void setTargetName(const QString &target);
|
||||
|
||||
Q_INVOKABLE void prepareBindings();
|
||||
@@ -77,7 +77,7 @@ private:
|
||||
QVariant m_modelNodeBackend;
|
||||
QVariant m_stateModelNode;
|
||||
QmlDesigner::ModelNode m_modelNode;
|
||||
TypeName m_backendValueTypeName;
|
||||
NodeMetaInfo m_backendValueType;
|
||||
QString m_targetName;
|
||||
};
|
||||
|
||||
|
@@ -79,7 +79,7 @@ void BindingEditorDialog::adjustProperties()
|
||||
m_comboBoxProperty->setCurrentText(property);
|
||||
}
|
||||
|
||||
void BindingEditorDialog::setAllBindings(const QList<BindingOption> &bindings, const TypeName &type)
|
||||
void BindingEditorDialog::setAllBindings(const QList<BindingOption> &bindings, const NodeMetaInfo &type)
|
||||
{
|
||||
m_lock = true;
|
||||
|
||||
@@ -118,7 +118,7 @@ void BindingEditorDialog::setupComboBoxes()
|
||||
|
||||
void BindingEditorDialog::setupCheckBox()
|
||||
{
|
||||
const bool visible = (m_type == "bool");
|
||||
const bool visible = m_type.isBool();
|
||||
m_checkBoxNot->setVisible(visible);
|
||||
}
|
||||
|
||||
|
@@ -6,6 +6,8 @@
|
||||
|
||||
#include <bindingeditor/abstracteditordialog.h>
|
||||
|
||||
#include <nodemetainfo.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QComboBox;
|
||||
class QCheckBox;
|
||||
@@ -35,7 +37,7 @@ public:
|
||||
|
||||
void adjustProperties() override;
|
||||
|
||||
void setAllBindings(const QList<BindingOption> &bindings, const TypeName &type);
|
||||
void setAllBindings(const QList<BindingOption> &bindings, const NodeMetaInfo &type);
|
||||
|
||||
private:
|
||||
void setupUIComponents();
|
||||
@@ -53,7 +55,7 @@ private:
|
||||
QCheckBox *m_checkBoxNot = nullptr;
|
||||
|
||||
QList<BindingOption> m_bindings;
|
||||
TypeName m_type;
|
||||
NodeMetaInfo m_type;
|
||||
};
|
||||
|
||||
}
|
||||
|
@@ -3,13 +3,14 @@
|
||||
|
||||
#include "modelnodecontextmenu_helper.h"
|
||||
|
||||
#include <nodemetainfo.h>
|
||||
#include <modelnode.h>
|
||||
#include <qmlitemnode.h>
|
||||
#include <bindingproperty.h>
|
||||
#include <model/modelutils.h>
|
||||
#include <modelnode.h>
|
||||
#include <nodemetainfo.h>
|
||||
#include <nodeproperty.h>
|
||||
#include <qmldesignerplugin.h>
|
||||
#include <qmldesignerconstants.h>
|
||||
#include <qmldesignerplugin.h>
|
||||
#include <qmlitemnode.h>
|
||||
|
||||
#include <QFile>
|
||||
|
||||
@@ -76,10 +77,11 @@ bool selectionHasSameParent(const SelectionContext &selectionState)
|
||||
|
||||
bool fileComponentExists(const ModelNode &modelNode)
|
||||
{
|
||||
if (!modelNode.metaInfo().isFileComponent())
|
||||
if (!modelNode.metaInfo().isFileComponent()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const QString fileName = modelNode.metaInfo().componentFileName();
|
||||
const QString fileName = ModelUtils::componentFilePath(modelNode);
|
||||
|
||||
if (fileName.contains("qml/QtQuick"))
|
||||
return false;
|
||||
@@ -97,7 +99,8 @@ bool selectionIsImported3DAsset(const SelectionContext &selectionState)
|
||||
{
|
||||
ModelNode node = selectionState.currentSingleSelectedNode();
|
||||
if (selectionState.view() && node.hasMetaInfo()) {
|
||||
QString fileName = node.metaInfo().componentFileName(); // absolute path
|
||||
QString fileName = ModelUtils::componentFilePath(node);
|
||||
|
||||
if (fileName.isEmpty()) {
|
||||
// Node is not a file component, so we have to check if the current doc itself is
|
||||
fileName = node.model()->fileUrl().toLocalFile();
|
||||
|
@@ -187,6 +187,7 @@ public:
|
||||
materialPreviewEnvironment,
|
||||
materialPreviewModel,
|
||||
material_medium,
|
||||
maxBar_small,
|
||||
mergeCells,
|
||||
merge_small,
|
||||
minus,
|
||||
@@ -200,6 +201,7 @@ public:
|
||||
move_medium,
|
||||
newMaterial,
|
||||
nextFile_large,
|
||||
normalBar_small,
|
||||
openLink,
|
||||
openMaterialBrowser,
|
||||
orientation,
|
||||
|
@@ -480,6 +480,11 @@ void ViewManager::exportAsImage()
|
||||
d->formEditorView.exportAsImage();
|
||||
}
|
||||
|
||||
QImage ViewManager::takeFormEditorScreenshot()
|
||||
{
|
||||
return d->formEditorView.takeFormEditorScreenshot();
|
||||
}
|
||||
|
||||
void ViewManager::reformatFileUsingTextEditorView()
|
||||
{
|
||||
d->textEditorView.reformatFile();
|
||||
|
@@ -74,6 +74,7 @@ public:
|
||||
const AbstractView *view() const;
|
||||
|
||||
void exportAsImage();
|
||||
QImage takeFormEditorScreenshot();
|
||||
void reformatFileUsingTextEditorView();
|
||||
|
||||
QWidgetAction *componentViewAction() const;
|
||||
|
@@ -13,14 +13,16 @@
|
||||
#include <rewritertransaction.h>
|
||||
#include <rewriterview.h>
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QTimer>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
BindingModel::BindingModel(ConnectionView *parent)
|
||||
: QStandardItemModel(parent)
|
||||
, m_connectionView(parent)
|
||||
: QStandardItemModel(parent), m_connectionView(parent),
|
||||
m_delegate(new BindingModelBackendDelegate(this))
|
||||
{
|
||||
connect(this, &QStandardItemModel::dataChanged, this, &BindingModel::handleDataChanged);
|
||||
}
|
||||
@@ -40,6 +42,31 @@ void BindingModel::resetModel()
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void BindingModel::add()
|
||||
{
|
||||
addBindingForCurrentNode();
|
||||
}
|
||||
|
||||
void BindingModel::remove(int row)
|
||||
{
|
||||
deleteBindindByRow(row);
|
||||
}
|
||||
|
||||
int BindingModel::currentIndex() const
|
||||
{
|
||||
return m_currentIndex;
|
||||
}
|
||||
|
||||
void BindingModel::setCurrentIndex(int i)
|
||||
{
|
||||
if (m_currentIndex == i)
|
||||
return;
|
||||
|
||||
m_currentIndex = i;
|
||||
|
||||
emit currentIndexChanged();
|
||||
}
|
||||
|
||||
void BindingModel::bindingChanged(const BindingProperty &bindingProperty)
|
||||
{
|
||||
m_handleDataChanged = false;
|
||||
@@ -232,6 +259,19 @@ void BindingModel::addBindingForCurrentNode()
|
||||
}
|
||||
}
|
||||
|
||||
static void updateDisplayRoles(QStandardItem *item, const BindingProperty &property)
|
||||
{
|
||||
item->setData(property.parentModelNode().id(), BindingModel::TargetNameRole);
|
||||
item->setData(property.name(), BindingModel::TargetPropertyNameRole);
|
||||
|
||||
const AbstractProperty source = property.resolveToProperty();
|
||||
|
||||
if (source.isValid()) {
|
||||
item->setData(source.parentModelNode().id(), BindingModel::SourceNameRole);
|
||||
item->setData(source.name(), BindingModel::SourcePropertyNameRole);
|
||||
}
|
||||
}
|
||||
|
||||
void BindingModel::addBindingProperty(const BindingProperty &property)
|
||||
{
|
||||
QStandardItem *idItem;
|
||||
@@ -248,6 +288,7 @@ void BindingModel::addBindingProperty(const BindingProperty &property)
|
||||
QList<QStandardItem*> items;
|
||||
|
||||
items.append(idItem);
|
||||
updateDisplayRoles(idItem, property);
|
||||
items.append(targetPropertyNameItem);
|
||||
|
||||
QString sourceNodeName;
|
||||
@@ -267,6 +308,10 @@ void BindingModel::updateBindingProperty(int rowNumber)
|
||||
BindingProperty bindingProperty = bindingPropertyForRow(rowNumber);
|
||||
|
||||
if (bindingProperty.isValid()) {
|
||||
QStandardItem *idItem = item(rowNumber, 0);
|
||||
if (idItem)
|
||||
updateDisplayRoles(idItem, bindingProperty);
|
||||
|
||||
QString targetPropertyName = QString::fromUtf8(bindingProperty.name());
|
||||
updateDisplayRole(rowNumber, TargetPropertyNameRow, targetPropertyName);
|
||||
QString sourceNodeName;
|
||||
@@ -355,6 +400,7 @@ void BindingModel::updateCustomData(QStandardItem *item, const BindingProperty &
|
||||
{
|
||||
item->setData(bindingProperty.parentModelNode().internalId(), Qt::UserRole + 1);
|
||||
item->setData(bindingProperty.name(), Qt::UserRole + 2);
|
||||
updateDisplayRoles(item, bindingProperty);
|
||||
}
|
||||
|
||||
int BindingModel::findRowForBinding(const BindingProperty &bindingProperty)
|
||||
@@ -369,6 +415,8 @@ int BindingModel::findRowForBinding(const BindingProperty &bindingProperty)
|
||||
|
||||
bool BindingModel::getExpressionStrings(const BindingProperty &bindingProperty, QString *sourceNode, QString *sourceProperty)
|
||||
{
|
||||
//TODO reimplement using existing helper functions
|
||||
|
||||
//### todo we assume no expressions yet
|
||||
|
||||
const QString expression = bindingProperty.expression();
|
||||
@@ -438,4 +486,159 @@ void BindingModel::handleException()
|
||||
resetModel();
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> BindingModel::roleNames() const
|
||||
{
|
||||
static QHash<int, QByteArray> roleNames{{TargetNameRole, "target"},
|
||||
{TargetPropertyNameRole, "targetProperty"},
|
||||
{SourceNameRole, "source"},
|
||||
{SourcePropertyNameRole, "sourceProperty"}};
|
||||
|
||||
return roleNames;
|
||||
}
|
||||
|
||||
BindingModelBackendDelegate *BindingModel::delegate() const
|
||||
{
|
||||
return m_delegate;
|
||||
}
|
||||
|
||||
BindingModelBackendDelegate::BindingModelBackendDelegate(BindingModel *parent) : QObject(parent)
|
||||
{
|
||||
connect(&m_sourceNode, &StudioQmlComboBoxBackend::activated, this, [this]() {
|
||||
handleSourceNodeChanged();
|
||||
});
|
||||
|
||||
connect(&m_sourceNodeProperty, &StudioQmlComboBoxBackend::activated, this, [this]() {
|
||||
handleSourcePropertyChanged();
|
||||
});
|
||||
}
|
||||
|
||||
int BindingModelBackendDelegate::currentRow() const
|
||||
{
|
||||
return m_currentRow;
|
||||
}
|
||||
|
||||
void BindingModelBackendDelegate::setCurrentRow(int i)
|
||||
{
|
||||
// See BindingDelegate::createEditor
|
||||
|
||||
if (m_currentRow == i)
|
||||
return;
|
||||
|
||||
m_currentRow = i;
|
||||
|
||||
//setup
|
||||
|
||||
BindingModel *model = qobject_cast<BindingModel *>(parent());
|
||||
|
||||
QTC_ASSERT(model, return );
|
||||
|
||||
BindingProperty bindingProperty = model->bindingPropertyForRow(currentRow());
|
||||
|
||||
QString idLabel = bindingProperty.parentModelNode().id();
|
||||
if (idLabel.isEmpty())
|
||||
idLabel = bindingProperty.parentModelNode().simplifiedTypeName();
|
||||
|
||||
m_targetNode = idLabel;
|
||||
|
||||
emit targetNodeChanged();
|
||||
|
||||
m_property.setModel(model->possibleTargetProperties(bindingProperty));
|
||||
m_property.setCurrentText(QString::fromUtf8(bindingProperty.name()));
|
||||
|
||||
QStringList sourceNodes;
|
||||
|
||||
for (const ModelNode &modelNode : model->connectionView()->allModelNodes()) {
|
||||
if (!modelNode.id().isEmpty())
|
||||
sourceNodes.append(modelNode.id());
|
||||
}
|
||||
|
||||
std::sort(sourceNodes.begin(), sourceNodes.end());
|
||||
m_sourceNode.setModel(sourceNodes);
|
||||
|
||||
QString sourceNodeName;
|
||||
QString sourcePropertyName;
|
||||
model->getExpressionStrings(bindingProperty, &sourceNodeName, &sourcePropertyName);
|
||||
|
||||
m_sourceNode.setCurrentText(sourceNodeName);
|
||||
|
||||
m_sourceNodeProperty.setModel(model->possibleSourceProperties(bindingProperty));
|
||||
m_sourceNodeProperty.setCurrentText(sourcePropertyName);
|
||||
}
|
||||
|
||||
void BindingModelBackendDelegate::handleException()
|
||||
{
|
||||
QMessageBox::warning(nullptr, tr("Error"), m_exceptionError);
|
||||
//reset
|
||||
}
|
||||
|
||||
QString BindingModelBackendDelegate::targetNode() const
|
||||
{
|
||||
return m_targetNode;
|
||||
}
|
||||
|
||||
StudioQmlComboBoxBackend *BindingModelBackendDelegate::property()
|
||||
{
|
||||
return &m_property;
|
||||
}
|
||||
|
||||
StudioQmlComboBoxBackend *BindingModelBackendDelegate::sourceNode()
|
||||
{
|
||||
return &m_sourceNode;
|
||||
}
|
||||
|
||||
StudioQmlComboBoxBackend *BindingModelBackendDelegate::sourceProperty()
|
||||
{
|
||||
return &m_sourceNodeProperty;
|
||||
}
|
||||
|
||||
void BindingModelBackendDelegate::handleSourceNodeChanged()
|
||||
{
|
||||
BindingModel *model = qobject_cast<BindingModel *>(parent());
|
||||
|
||||
QTC_ASSERT(model, return );
|
||||
QTC_ASSERT(model->connectionView(), return );
|
||||
|
||||
const QString sourceNode = m_sourceNode.currentText();
|
||||
const QString sourceProperty = m_sourceNodeProperty.currentText();
|
||||
|
||||
QString expression;
|
||||
if (sourceProperty.isEmpty()) {
|
||||
expression = sourceNode;
|
||||
} else {
|
||||
expression = sourceNode + QLatin1String(".") + sourceProperty;
|
||||
}
|
||||
|
||||
BindingProperty bindingProperty = model->bindingPropertyForRow(currentRow());
|
||||
model->connectionView()->executeInTransaction("BindingModel::updateExpression",
|
||||
[&bindingProperty, expression]() {
|
||||
bindingProperty.setExpression(
|
||||
expression.trimmed());
|
||||
});
|
||||
}
|
||||
|
||||
void BindingModelBackendDelegate::handleSourcePropertyChanged()
|
||||
{
|
||||
BindingModel *model = qobject_cast<BindingModel *>(parent());
|
||||
|
||||
QTC_ASSERT(model, return );
|
||||
QTC_ASSERT(model->connectionView(), return );
|
||||
|
||||
const QString sourceNode = m_sourceNode.currentText();
|
||||
const QString sourceProperty = m_sourceNodeProperty.currentText();
|
||||
|
||||
QString expression;
|
||||
if (sourceProperty.isEmpty()) {
|
||||
expression = sourceNode;
|
||||
} else {
|
||||
expression = sourceNode + QLatin1String(".") + sourceProperty;
|
||||
}
|
||||
|
||||
BindingProperty bindingProperty = model->bindingPropertyForRow(currentRow());
|
||||
model->connectionView()->executeInTransaction("BindingModel::updateExpression",
|
||||
[&bindingProperty, expression]() {
|
||||
bindingProperty.setExpression(
|
||||
expression.trimmed());
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -7,16 +7,22 @@
|
||||
#include <bindingproperty.h>
|
||||
#include <variantproperty.h>
|
||||
|
||||
#include <studioquickwidget.h>
|
||||
|
||||
#include <QStandardItemModel>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class ConnectionView;
|
||||
class BindingModelBackendDelegate;
|
||||
|
||||
class BindingModel : public QStandardItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
|
||||
Q_PROPERTY(BindingModelBackendDelegate *delegate READ delegate CONSTANT)
|
||||
|
||||
public:
|
||||
enum ColumnRoles {
|
||||
TargetModelNodeRow = 0,
|
||||
@@ -24,6 +30,15 @@ public:
|
||||
SourceModelNodeRow = 2,
|
||||
SourcePropertyNameRow = 3
|
||||
};
|
||||
|
||||
enum UserRoles {
|
||||
InternalIdRole = Qt::UserRole + 2,
|
||||
TargetNameRole,
|
||||
TargetPropertyNameRole,
|
||||
SourceNameRole,
|
||||
SourcePropertyNameRole
|
||||
};
|
||||
|
||||
BindingModel(ConnectionView *parent = nullptr);
|
||||
void bindingChanged(const BindingProperty &bindingProperty);
|
||||
void bindingRemoved(const BindingProperty &bindingProperty);
|
||||
@@ -37,6 +52,18 @@ public:
|
||||
void addBindingForCurrentNode();
|
||||
void resetModel();
|
||||
|
||||
Q_INVOKABLE void add();
|
||||
Q_INVOKABLE void remove(int row);
|
||||
|
||||
int currentIndex() const;
|
||||
void setCurrentIndex(int i);
|
||||
bool getExpressionStrings(const BindingProperty &bindingProperty,
|
||||
QString *sourceNode,
|
||||
QString *sourceProperty);
|
||||
|
||||
signals:
|
||||
void currentIndexChanged();
|
||||
|
||||
protected:
|
||||
void addBindingProperty(const BindingProperty &property);
|
||||
void updateBindingProperty(int rowNumber);
|
||||
@@ -46,11 +73,11 @@ protected:
|
||||
ModelNode getNodeByIdOrParent(const QString &id, const ModelNode &targetNode) const;
|
||||
void updateCustomData(QStandardItem *item, const BindingProperty &bindingProperty);
|
||||
int findRowForBinding(const BindingProperty &bindingProperty);
|
||||
|
||||
bool getExpressionStrings(const BindingProperty &bindingProperty, QString *sourceNode, QString *sourceProperty);
|
||||
|
||||
void updateDisplayRole(int row, int columns, const QString &string);
|
||||
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
BindingModelBackendDelegate *delegate() const;
|
||||
|
||||
private:
|
||||
void handleDataChanged(const QModelIndex &topLeft, const QModelIndex& bottomRight);
|
||||
void handleException();
|
||||
@@ -60,7 +87,48 @@ private:
|
||||
bool m_lock = false;
|
||||
bool m_handleDataChanged = false;
|
||||
QString m_exceptionError;
|
||||
int m_currentIndex = 0;
|
||||
BindingModelBackendDelegate *m_delegate = nullptr;
|
||||
};
|
||||
|
||||
class BindingModelBackendDelegate : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(int currentRow READ currentRow WRITE setCurrentRow NOTIFY currentRowChanged)
|
||||
|
||||
Q_PROPERTY(QString targetNode READ targetNode NOTIFY targetNodeChanged)
|
||||
Q_PROPERTY(StudioQmlComboBoxBackend *property READ property CONSTANT)
|
||||
Q_PROPERTY(StudioQmlComboBoxBackend *sourceNode READ sourceNode CONSTANT)
|
||||
Q_PROPERTY(StudioQmlComboBoxBackend *sourceProperty READ sourceProperty CONSTANT)
|
||||
|
||||
public:
|
||||
BindingModelBackendDelegate(BindingModel *parent = nullptr);
|
||||
|
||||
signals:
|
||||
void currentRowChanged();
|
||||
//void nameChanged();
|
||||
void targetNodeChanged();
|
||||
|
||||
private:
|
||||
int currentRow() const;
|
||||
void setCurrentRow(int i);
|
||||
void handleException();
|
||||
QString targetNode() const;
|
||||
|
||||
StudioQmlComboBoxBackend *property();
|
||||
StudioQmlComboBoxBackend *sourceNode();
|
||||
StudioQmlComboBoxBackend *sourceProperty();
|
||||
|
||||
void handleSourceNodeChanged();
|
||||
void handleSourcePropertyChanged();
|
||||
|
||||
StudioQmlComboBoxBackend m_property;
|
||||
StudioQmlComboBoxBackend m_sourceNode;
|
||||
StudioQmlComboBoxBackend m_sourceNodeProperty;
|
||||
QString m_exceptionError;
|
||||
int m_currentRow = -1;
|
||||
QString m_targetNode;
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -252,6 +252,19 @@ void ConnectionModel::updateCustomData(QStandardItem *item, const SignalHandlerP
|
||||
{
|
||||
item->setData(signalHandlerProperty.parentModelNode().internalId(), UserRoles::InternalIdRole);
|
||||
item->setData(signalHandlerProperty.name(), UserRoles::TargetPropertyNameRole);
|
||||
item->setData(signalHandlerProperty.parentModelNode()
|
||||
.bindingProperty("target")
|
||||
.resolveToModelNode()
|
||||
.id(),
|
||||
UserRoles::TargetNameRole);
|
||||
|
||||
// TODO signalHandlerProperty.source() contains a statement that defines the type.
|
||||
// foo.bar() <- function call
|
||||
// foo.state = "literal" //state change
|
||||
//anything else is assignment
|
||||
// e.g. foo.bal = foo2.bula ; foo.bal = "literal" ; goo.gal = true
|
||||
|
||||
item->setData("Assignment", UserRoles::ActionTypeRole);
|
||||
}
|
||||
|
||||
ModelNode ConnectionModel::getTargetNodeForConnection(const ModelNode &connection) const
|
||||
@@ -370,6 +383,16 @@ void ConnectionModel::removeRowFromTable(const SignalHandlerProperty &property)
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionModel::add()
|
||||
{
|
||||
addConnection();
|
||||
}
|
||||
|
||||
void ConnectionModel::remove(int row)
|
||||
{
|
||||
deleteConnectionByRow(row);
|
||||
}
|
||||
|
||||
void ConnectionModel::handleException()
|
||||
{
|
||||
QMessageBox::warning(nullptr, tr("Error"), m_exceptionError);
|
||||
@@ -522,4 +545,12 @@ QStringList ConnectionModel::getPossibleSignalsForConnection(const ModelNode &co
|
||||
return stringList;
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> ConnectionModel::roleNames() const
|
||||
{
|
||||
static QHash<int, QByteArray> roleNames{{TargetPropertyNameRole, "signal"},
|
||||
{TargetNameRole, "target"},
|
||||
{ActionTypeRole, "action"}};
|
||||
return roleNames;
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -26,7 +26,9 @@ public:
|
||||
};
|
||||
enum UserRoles {
|
||||
InternalIdRole = Qt::UserRole + 1,
|
||||
TargetPropertyNameRole
|
||||
TargetPropertyNameRole,
|
||||
TargetNameRole,
|
||||
ActionTypeRole
|
||||
};
|
||||
ConnectionModel(ConnectionView *parent = nullptr);
|
||||
|
||||
@@ -49,6 +51,9 @@ public:
|
||||
void deleteConnectionByRow(int currentRow);
|
||||
void removeRowFromTable(const SignalHandlerProperty &property);
|
||||
|
||||
Q_INVOKABLE void add();
|
||||
Q_INVOKABLE void remove(int row);
|
||||
|
||||
protected:
|
||||
void addModelNode(const ModelNode &modelNode);
|
||||
void addConnection(const ModelNode &modelNode);
|
||||
@@ -61,6 +66,8 @@ protected:
|
||||
void updateCustomData(QStandardItem *item, const SignalHandlerProperty &signalHandlerProperty);
|
||||
QStringList getPossibleSignalsForConnection(const ModelNode &connection) const;
|
||||
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
private:
|
||||
void handleDataChanged(const QModelIndex &topLeft, const QModelIndex& bottomRight);
|
||||
void handleException();
|
||||
|
@@ -8,6 +8,7 @@
|
||||
#include "bindingmodel.h"
|
||||
#include "connectionmodel.h"
|
||||
#include "dynamicpropertiesmodel.h"
|
||||
#include "theme.h"
|
||||
|
||||
#include <bindingproperty.h>
|
||||
#include <nodeabstractproperty.h>
|
||||
@@ -16,19 +17,116 @@
|
||||
#include <qmldesignerplugin.h>
|
||||
#include <viewmanager.h>
|
||||
|
||||
#include <studioquickwidget.h>
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/messagebox.h>
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QQmlEngine>
|
||||
#include <QShortcut>
|
||||
#include <QTableView>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
static QString propertyEditorResourcesPath()
|
||||
{
|
||||
#ifdef SHARE_QML_PATH
|
||||
if (qEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE"))
|
||||
return QLatin1String(SHARE_QML_PATH) + "/propertyEditorQmlSources";
|
||||
#endif
|
||||
return Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources").toString();
|
||||
}
|
||||
|
||||
class ConnectionViewQuickWidget : public StudioQuickWidget
|
||||
{
|
||||
// Q_OBJECT carefull
|
||||
|
||||
public:
|
||||
ConnectionViewQuickWidget(ConnectionView *connectionEditorView)
|
||||
: m_connectionEditorView(connectionEditorView)
|
||||
|
||||
{
|
||||
engine()->addImportPath(qmlSourcesPath());
|
||||
engine()->addImportPath(propertyEditorResourcesPath() + "/imports");
|
||||
engine()->addImportPath(qmlSourcesPath() + "/imports");
|
||||
|
||||
m_qmlSourceUpdateShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_F12), this);
|
||||
connect(m_qmlSourceUpdateShortcut,
|
||||
&QShortcut::activated,
|
||||
this,
|
||||
&ConnectionViewQuickWidget::reloadQmlSource);
|
||||
|
||||
//setObjectName(Constants::OBJECT_NAME_STATES_EDITOR);
|
||||
setResizeMode(QQuickWidget::SizeRootObjectToView);
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
|
||||
auto map = registerPropertyMap("ConnectionsEditorEditorBackend");
|
||||
qmlRegisterAnonymousType<DynamicPropertiesModel>("ConnectionsEditorEditorBackend", 1);
|
||||
qmlRegisterAnonymousType<DynamicPropertiesModelBackendDelegate>(
|
||||
"ConnectionsEditorEditorBackend", 1);
|
||||
|
||||
map->setProperties(
|
||||
{{"connectionModel", QVariant::fromValue(m_connectionEditorView->connectionModel())}});
|
||||
|
||||
map->setProperties(
|
||||
{{"bindingModel", QVariant::fromValue(m_connectionEditorView->bindingModel())}});
|
||||
|
||||
map->setProperties(
|
||||
{{"dynamicPropertiesModel",
|
||||
QVariant::fromValue(m_connectionEditorView->dynamicPropertiesModel())}});
|
||||
|
||||
Theme::setupTheme(engine());
|
||||
|
||||
setMinimumWidth(195);
|
||||
setMinimumHeight(195);
|
||||
|
||||
// init the first load of the QML UI elements
|
||||
reloadQmlSource();
|
||||
}
|
||||
~ConnectionViewQuickWidget() = default;
|
||||
|
||||
static QString qmlSourcesPath()
|
||||
{
|
||||
#ifdef SHARE_QML_PATH
|
||||
if (qEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE"))
|
||||
return QLatin1String(SHARE_QML_PATH) + "/connectionseditor";
|
||||
#endif
|
||||
return Core::ICore::resourcePath("qmldesigner/connectionseditor").toString();
|
||||
}
|
||||
|
||||
private:
|
||||
void reloadQmlSource()
|
||||
{
|
||||
QString connectionEditorQmlFilePath = qmlSourcesPath() + QStringLiteral("/Main.qml");
|
||||
QTC_ASSERT(QFileInfo::exists(connectionEditorQmlFilePath), return );
|
||||
setSource(QUrl::fromLocalFile(connectionEditorQmlFilePath));
|
||||
|
||||
if (!rootObject()) {
|
||||
QString errorString;
|
||||
for (const QQmlError &error : errors())
|
||||
errorString += "\n" + error.toString();
|
||||
|
||||
Core::AsynchronousMessageBox::warning(
|
||||
tr("Cannot Create QtQuick View"),
|
||||
tr("ConnectionsEditorWidget: %1 cannot be created.%2")
|
||||
.arg(qmlSourcesPath(), errorString));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
QPointer<ConnectionView> m_connectionEditorView;
|
||||
QShortcut *m_qmlSourceUpdateShortcut;
|
||||
};
|
||||
|
||||
ConnectionView::ConnectionView(ExternalDependenciesInterface &externalDependencies)
|
||||
: AbstractView{externalDependencies}
|
||||
, m_connectionViewWidget(new ConnectionViewWidget())
|
||||
, m_connectionModel(new ConnectionModel(this))
|
||||
, m_bindingModel(new BindingModel(this))
|
||||
, m_dynamicPropertiesModel(new DynamicPropertiesModel(false, this))
|
||||
, m_backendModel(new BackendModel(this))
|
||||
: AbstractView{externalDependencies}, m_connectionViewWidget(new ConnectionViewWidget()),
|
||||
m_connectionModel(new ConnectionModel(this)), m_bindingModel(new BindingModel(this)),
|
||||
m_dynamicPropertiesModel(new DynamicPropertiesModel(false, this)),
|
||||
m_backendModel(new BackendModel(this)),
|
||||
m_connectionViewQuickWidget(new ConnectionViewQuickWidget(this))
|
||||
{
|
||||
connectionViewWidget()->setBindingModel(m_bindingModel);
|
||||
connectionViewWidget()->setConnectionModel(m_connectionModel);
|
||||
@@ -36,8 +134,11 @@ ConnectionView::ConnectionView(ExternalDependenciesInterface &externalDependenci
|
||||
connectionViewWidget()->setBackendModel(m_backendModel);
|
||||
}
|
||||
|
||||
ConnectionView::~ConnectionView() = default;
|
||||
|
||||
ConnectionView::~ConnectionView()
|
||||
{
|
||||
// Ensure that QML is deleted first to avoid calling back to C++.
|
||||
delete m_connectionViewQuickWidget.data();
|
||||
}
|
||||
void ConnectionView::modelAttached(Model *model)
|
||||
{
|
||||
AbstractView::modelAttached(model);
|
||||
@@ -195,7 +296,14 @@ void ConnectionView::currentStateChanged(const ModelNode &)
|
||||
|
||||
WidgetInfo ConnectionView::widgetInfo()
|
||||
{
|
||||
return createWidgetInfo(m_connectionViewWidget.data(),
|
||||
/* Enable new connection editor here */
|
||||
const bool newEditor = false;
|
||||
|
||||
QWidget *widget = m_connectionViewWidget.data();
|
||||
if (newEditor)
|
||||
widget = m_connectionViewQuickWidget.data();
|
||||
|
||||
return createWidgetInfo(widget,
|
||||
QLatin1String("ConnectionView"),
|
||||
WidgetInfo::LeftPane,
|
||||
0,
|
||||
@@ -257,6 +365,20 @@ BackendModel *ConnectionView::backendModel() const
|
||||
return m_backendModel;
|
||||
}
|
||||
|
||||
int ConnectionView::currentIndex() const
|
||||
{
|
||||
return m_currentIndex;
|
||||
}
|
||||
|
||||
void ConnectionView::setCurrentIndex(int i)
|
||||
{
|
||||
if (m_currentIndex == i)
|
||||
return;
|
||||
|
||||
m_currentIndex = i;
|
||||
emit currentIndexChanged();
|
||||
}
|
||||
|
||||
ConnectionView *ConnectionView::instance()
|
||||
{
|
||||
|
||||
|
@@ -20,11 +20,14 @@ class BindingModel;
|
||||
class ConnectionModel;
|
||||
class DynamicPropertiesModel;
|
||||
class BackendModel;
|
||||
class ConnectionViewQuickWidget;
|
||||
|
||||
class ConnectionView : public AbstractView
|
||||
class ConnectionView : public AbstractView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
|
||||
|
||||
public:
|
||||
ConnectionView(ExternalDependenciesInterface &externalDependencies);
|
||||
~ConnectionView() override;
|
||||
@@ -70,14 +73,24 @@ public:
|
||||
BindingModel *bindingModel() const;
|
||||
BackendModel *backendModel() const;
|
||||
|
||||
int currentIndex() const;
|
||||
void setCurrentIndex(int i);
|
||||
|
||||
static ConnectionView *instance();
|
||||
|
||||
signals:
|
||||
void currentIndexChanged();
|
||||
|
||||
private: //variables
|
||||
QPointer<ConnectionViewWidget> m_connectionViewWidget;
|
||||
|
||||
ConnectionModel *m_connectionModel;
|
||||
BindingModel *m_bindingModel;
|
||||
DynamicPropertiesModel *m_dynamicPropertiesModel;
|
||||
BackendModel *m_backendModel;
|
||||
int m_currentIndex = 0;
|
||||
|
||||
QPointer<ConnectionViewQuickWidget> m_connectionViewQuickWidget;
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -90,6 +90,8 @@ ConnectionViewWidget::ConnectionViewWidget(QWidget *parent) :
|
||||
this, &ConnectionViewWidget::handleTabChanged);
|
||||
|
||||
ui->stackedWidget->setCurrentIndex(0);
|
||||
|
||||
ui->stackedWidget->parentWidget()->hide();
|
||||
}
|
||||
|
||||
ConnectionViewWidget::~ConnectionViewWidget()
|
||||
@@ -192,18 +194,17 @@ void ConnectionViewWidget::contextMenuEvent(QContextMenuEvent *event)
|
||||
return;
|
||||
|
||||
const ModelNode node = property.parentModelNode();
|
||||
const TypeName typeName = property.isDynamic() ? property.dynamicTypeName()
|
||||
: node.metaInfo()
|
||||
.property(property.name())
|
||||
.propertyType()
|
||||
.typeName();
|
||||
auto model = node.model();
|
||||
const auto type = property.isDynamic()
|
||||
? model->metaInfo(property.dynamicTypeName())
|
||||
: node.metaInfo().property(property.name()).propertyType();
|
||||
|
||||
const QString targetName = node.displayName() + "." + property.name();
|
||||
|
||||
m_bindingEditor->showWidget();
|
||||
m_bindingEditor->setBindingValue(property.expression());
|
||||
m_bindingEditor->setModelNode(node);
|
||||
m_bindingEditor->setBackendValueTypeName(typeName);
|
||||
m_bindingEditor->setBackendValueType(type);
|
||||
m_bindingEditor->setTargetName(targetName);
|
||||
m_bindingEditor->prepareBindings();
|
||||
m_bindingEditor->updateWindowName();
|
||||
@@ -240,11 +241,12 @@ void ConnectionViewWidget::contextMenuEvent(QContextMenuEvent *event)
|
||||
return;
|
||||
|
||||
const QString targetName = node.displayName() + "." + abstractProperty.name();
|
||||
|
||||
auto model = node.model();
|
||||
m_dynamicEditor->showWidget();
|
||||
m_dynamicEditor->setBindingValue(newExpression);
|
||||
m_dynamicEditor->setModelNode(node);
|
||||
m_dynamicEditor->setBackendValueTypeName(abstractProperty.dynamicTypeName());
|
||||
m_dynamicEditor->setBackendValueType(
|
||||
model->metaInfo(abstractProperty.dynamicTypeName()));
|
||||
m_dynamicEditor->setTargetName(targetName);
|
||||
m_dynamicEditor->prepareBindings();
|
||||
m_dynamicEditor->updateWindowName();
|
||||
|
@@ -152,10 +152,34 @@ QString DynamicPropertiesModel::defaultExpressionForType(const TypeName &type)
|
||||
return expression;
|
||||
}
|
||||
|
||||
void DynamicPropertiesModel::add()
|
||||
{
|
||||
addDynamicPropertyForCurrentNode();
|
||||
}
|
||||
|
||||
void DynamicPropertiesModel::remove(int row)
|
||||
{
|
||||
deleteDynamicPropertyByRow(row);
|
||||
}
|
||||
|
||||
int DynamicPropertiesModel::currentIndex() const
|
||||
{
|
||||
return m_currentIndex;
|
||||
}
|
||||
|
||||
void DynamicPropertiesModel::setCurrentIndex(int i)
|
||||
{
|
||||
if (m_currentIndex == i)
|
||||
return;
|
||||
|
||||
m_currentIndex = i;
|
||||
|
||||
emit currentIndexChanged();
|
||||
}
|
||||
|
||||
DynamicPropertiesModel::DynamicPropertiesModel(bool explicitSelection, AbstractView *parent)
|
||||
: QStandardItemModel(parent)
|
||||
, m_view(parent)
|
||||
, m_explicitSelection(explicitSelection)
|
||||
: QStandardItemModel(parent), m_view(parent), m_explicitSelection(explicitSelection),
|
||||
m_delegate(new DynamicPropertiesModelBackendDelegate(this))
|
||||
{
|
||||
connect(this, &QStandardItemModel::dataChanged, this, &DynamicPropertiesModel::handleDataChanged);
|
||||
}
|
||||
@@ -163,6 +187,7 @@ DynamicPropertiesModel::DynamicPropertiesModel(bool explicitSelection, AbstractV
|
||||
void DynamicPropertiesModel::resetModel()
|
||||
{
|
||||
beginResetModel();
|
||||
const int backIndex = m_currentIndex;
|
||||
clear();
|
||||
setHorizontalHeaderLabels({tr("Item"), tr("Property"), tr("Property Type"), tr("Property Value")});
|
||||
|
||||
@@ -172,7 +197,9 @@ void DynamicPropertiesModel::resetModel()
|
||||
addModelNode(modelNode);
|
||||
}
|
||||
|
||||
emit currentIndexChanged();
|
||||
endResetModel();
|
||||
m_currentIndex = backIndex;
|
||||
}
|
||||
|
||||
|
||||
@@ -344,6 +371,8 @@ void DynamicPropertiesModel::bindingRemoved(const BindingProperty &bindingProper
|
||||
removeRow(rowNumber);
|
||||
}
|
||||
|
||||
emit currentIndexChanged();
|
||||
|
||||
m_handleDataChanged = true;
|
||||
}
|
||||
|
||||
@@ -360,6 +389,8 @@ void DynamicPropertiesModel::variantRemoved(const VariantProperty &variantProper
|
||||
removeRow(rowNumber);
|
||||
}
|
||||
|
||||
emit currentIndexChanged();
|
||||
|
||||
m_handleDataChanged = true;
|
||||
}
|
||||
|
||||
@@ -368,6 +399,7 @@ void DynamicPropertiesModel::reset()
|
||||
m_handleDataChanged = false;
|
||||
resetModel();
|
||||
m_handleDataChanged = true;
|
||||
emit currentIndexChanged();
|
||||
}
|
||||
|
||||
void DynamicPropertiesModel::setSelectedNode(const ModelNode &node)
|
||||
@@ -597,6 +629,8 @@ void DynamicPropertiesModel::updateBindingProperty(int rowNumber)
|
||||
BindingProperty bindingProperty = bindingPropertyForRow(rowNumber);
|
||||
|
||||
if (bindingProperty.isValid()) {
|
||||
updateCustomData(rowNumber, bindingProperty);
|
||||
|
||||
QString propertyName = QString::fromUtf8(bindingProperty.name());
|
||||
updateDisplayRole(rowNumber, PropertyNameRow, propertyName);
|
||||
QString value = bindingProperty.expression();
|
||||
@@ -617,6 +651,7 @@ void DynamicPropertiesModel::updateVariantProperty(int rowNumber)
|
||||
VariantProperty variantProperty = variantPropertyForRow(rowNumber);
|
||||
|
||||
if (variantProperty.isValid()) {
|
||||
updateCustomData(rowNumber, variantProperty);
|
||||
QString propertyName = QString::fromUtf8(variantProperty.name());
|
||||
updateDisplayRole(rowNumber, PropertyNameRow, propertyName);
|
||||
QVariant value = variantProperty.value();
|
||||
@@ -787,6 +822,16 @@ void DynamicPropertiesModel::updateCustomData(QStandardItem *item, const Abstrac
|
||||
{
|
||||
item->setData(property.parentModelNode().internalId(), Qt::UserRole + 1);
|
||||
item->setData(property.name(), Qt::UserRole + 2);
|
||||
|
||||
item->setData(property.parentModelNode().id(), TargetNameRole);
|
||||
item->setData(property.name(), PropertyNameRole);
|
||||
item->setData(property.parentModelNode().id(), TargetNameRole);
|
||||
item->setData(property.dynamicTypeName(), PropertyTypeRole);
|
||||
|
||||
if (property.isVariantProperty())
|
||||
item->setData(property.toVariantProperty().value(), PropertyValueRole);
|
||||
if (property.isBindingProperty())
|
||||
item->setData(property.toBindingProperty().expression(), PropertyValueRole);
|
||||
}
|
||||
|
||||
void DynamicPropertiesModel::updateCustomData(int row, const AbstractProperty &property)
|
||||
@@ -924,4 +969,217 @@ const ModelNode DynamicPropertiesModel::singleSelectedNode() const
|
||||
return m_view->singleSelectedModelNode();
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> DynamicPropertiesModel::roleNames() const
|
||||
{
|
||||
static QHash<int, QByteArray> roleNames{{TargetNameRole, "target"},
|
||||
{PropertyNameRole, "name"},
|
||||
{PropertyTypeRole, "type"},
|
||||
{PropertyValueRole, "value"}};
|
||||
|
||||
return roleNames;
|
||||
}
|
||||
|
||||
DynamicPropertiesModelBackendDelegate *DynamicPropertiesModel::delegate() const
|
||||
{
|
||||
return m_delegate;
|
||||
}
|
||||
|
||||
DynamicPropertiesModelBackendDelegate::DynamicPropertiesModelBackendDelegate(
|
||||
DynamicPropertiesModel *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
m_type.setModel({"int", "bool", "var", "real", "string", "url", "color"});
|
||||
|
||||
connect(&m_type, &StudioQmlComboBoxBackend::activated, this, [this]() { handleTypeChanged(); });
|
||||
connect(&m_name, &StudioQmlTextBackend::activated, this, [this]() { handleNameChanged(); });
|
||||
connect(&m_value, &StudioQmlTextBackend::activated, this, [this]() { handleValueChanged(); });
|
||||
}
|
||||
|
||||
int DynamicPropertiesModelBackendDelegate::currentRow() const
|
||||
{
|
||||
return m_currentRow;
|
||||
}
|
||||
|
||||
void DynamicPropertiesModelBackendDelegate::setCurrentRow(int i)
|
||||
{
|
||||
if (m_currentRow == i)
|
||||
return;
|
||||
|
||||
m_currentRow = i;
|
||||
|
||||
//setup
|
||||
|
||||
DynamicPropertiesModel *model = qobject_cast<DynamicPropertiesModel *>(parent());
|
||||
|
||||
QTC_ASSERT(model, return );
|
||||
|
||||
AbstractProperty property = model->abstractPropertyForRow(i);
|
||||
|
||||
m_type.setCurrentText(QString::fromUtf8(property.dynamicTypeName()));
|
||||
m_name.setText(QString::fromUtf8(property.name()));
|
||||
|
||||
if (property.isVariantProperty())
|
||||
m_value.setText(property.toVariantProperty().value().toString());
|
||||
else if (property.isBindingProperty())
|
||||
m_value.setText(property.toBindingProperty().expression());
|
||||
}
|
||||
|
||||
void DynamicPropertiesModelBackendDelegate::handleTypeChanged()
|
||||
{
|
||||
//void DynamicPropertiesModel::updatePropertyType(int rowNumber)
|
||||
const TypeName type = m_type.currentText().toUtf8();
|
||||
|
||||
DynamicPropertiesModel *model = qobject_cast<DynamicPropertiesModel *>(parent());
|
||||
|
||||
QTC_ASSERT(model, return );
|
||||
QTC_ASSERT(model->view(), return );
|
||||
|
||||
BindingProperty bindingProperty = model->bindingPropertyForRow(currentRow());
|
||||
|
||||
VariantProperty variantProperty = model->variantPropertyForRow(currentRow());
|
||||
|
||||
RewriterTransaction transaction = model->view()->beginRewriterTransaction(__FUNCTION__);
|
||||
|
||||
try {
|
||||
if (bindingProperty.isBindingProperty() || type == "var") { //var is always a binding
|
||||
const QString expression = bindingProperty.expression();
|
||||
variantProperty.parentModelNode().removeProperty(variantProperty.name());
|
||||
bindingProperty.setDynamicTypeNameAndExpression(type, expression);
|
||||
} else if (variantProperty.isVariantProperty()) {
|
||||
variantProperty.parentModelNode().removeProperty(variantProperty.name());
|
||||
variantProperty.setDynamicTypeNameAndValue(type, variantValue());
|
||||
}
|
||||
transaction.commit(); // committing in the try block
|
||||
} catch (Exception &e) {
|
||||
m_exceptionError = e.description();
|
||||
QTimer::singleShot(200, this, &DynamicPropertiesModelBackendDelegate::handleException);
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicPropertiesModelBackendDelegate::handleNameChanged()
|
||||
{
|
||||
//see DynamicPropertiesModel::updatePropertyName
|
||||
|
||||
const PropertyName newName = m_name.text().toUtf8();
|
||||
QTC_ASSERT(!newName.isEmpty(), return );
|
||||
|
||||
DynamicPropertiesModel *model = qobject_cast<DynamicPropertiesModel *>(parent());
|
||||
|
||||
QTC_ASSERT(model, return );
|
||||
QTC_ASSERT(model->view(), return );
|
||||
|
||||
BindingProperty bindingProperty = model->bindingPropertyForRow(currentRow());
|
||||
|
||||
ModelNode targetNode = bindingProperty.parentModelNode();
|
||||
|
||||
if (bindingProperty.isBindingProperty()) {
|
||||
model->view()->executeInTransaction(__FUNCTION__, [bindingProperty, newName, &targetNode]() {
|
||||
const QString expression = bindingProperty.expression();
|
||||
const PropertyName dynamicPropertyType = bindingProperty.dynamicTypeName();
|
||||
|
||||
targetNode.bindingProperty(newName).setDynamicTypeNameAndExpression(dynamicPropertyType,
|
||||
expression);
|
||||
targetNode.removeProperty(bindingProperty.name());
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
VariantProperty variantProperty = model->variantPropertyForRow(currentRow());
|
||||
|
||||
if (variantProperty.isVariantProperty()) {
|
||||
const QVariant value = variantProperty.value();
|
||||
const PropertyName dynamicPropertyType = variantProperty.dynamicTypeName();
|
||||
ModelNode targetNode = variantProperty.parentModelNode();
|
||||
|
||||
model->view()->executeInTransaction(__FUNCTION__, [=]() {
|
||||
targetNode.variantProperty(newName).setDynamicTypeNameAndValue(dynamicPropertyType,
|
||||
value);
|
||||
targetNode.removeProperty(variantProperty.name());
|
||||
});
|
||||
}
|
||||
|
||||
AbstractProperty property = targetNode.property(newName);
|
||||
|
||||
//order might have changed because of name change we have to select the correct row
|
||||
int newRow = model->findRowForProperty(property);
|
||||
model->setCurrentIndex(newRow);
|
||||
setCurrentRow(newRow);
|
||||
}
|
||||
|
||||
void DynamicPropertiesModelBackendDelegate::handleValueChanged()
|
||||
{
|
||||
//see void DynamicPropertiesModel::updateValue(int row)
|
||||
|
||||
DynamicPropertiesModel *model = qobject_cast<DynamicPropertiesModel *>(parent());
|
||||
|
||||
QTC_ASSERT(model, return );
|
||||
QTC_ASSERT(model->view(), return );
|
||||
|
||||
BindingProperty bindingProperty = model->bindingPropertyForRow(currentRow());
|
||||
|
||||
if (bindingProperty.isBindingProperty()) {
|
||||
const QString expression = m_value.text();
|
||||
|
||||
RewriterTransaction transaction = model->view()->beginRewriterTransaction(__FUNCTION__);
|
||||
try {
|
||||
bindingProperty.setDynamicTypeNameAndExpression(bindingProperty.dynamicTypeName(),
|
||||
expression);
|
||||
transaction.commit(); // committing in the try block
|
||||
} catch (Exception &e) {
|
||||
m_exceptionError = e.description();
|
||||
QTimer::singleShot(200, this, &DynamicPropertiesModelBackendDelegate::handleException);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
VariantProperty variantProperty = model->variantPropertyForRow(currentRow());
|
||||
|
||||
if (variantProperty.isVariantProperty()) {
|
||||
RewriterTransaction transaction = model->view()->beginRewriterTransaction(__FUNCTION__);
|
||||
try {
|
||||
variantProperty.setDynamicTypeNameAndValue(variantProperty.dynamicTypeName(),
|
||||
variantValue());
|
||||
transaction.commit(); // committing in the try block
|
||||
} catch (Exception &e) {
|
||||
m_exceptionError = e.description();
|
||||
QTimer::singleShot(200, this, &DynamicPropertiesModelBackendDelegate::handleException);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicPropertiesModelBackendDelegate::handleException()
|
||||
{
|
||||
QMessageBox::warning(nullptr, tr("Error"), m_exceptionError);
|
||||
//reset
|
||||
}
|
||||
|
||||
QVariant DynamicPropertiesModelBackendDelegate::variantValue() const
|
||||
{
|
||||
//improve
|
||||
const QString type = m_type.currentText();
|
||||
if (type == "real" || type == "int")
|
||||
return m_value.text().toFloat();
|
||||
|
||||
if (type == "bool")
|
||||
return m_value.text() == "true";
|
||||
|
||||
return m_value.text();
|
||||
}
|
||||
|
||||
StudioQmlComboBoxBackend *DynamicPropertiesModelBackendDelegate::type()
|
||||
{
|
||||
return &m_type;
|
||||
}
|
||||
|
||||
StudioQmlTextBackend *DynamicPropertiesModelBackendDelegate::name()
|
||||
{
|
||||
return &m_name;
|
||||
}
|
||||
|
||||
StudioQmlTextBackend *DynamicPropertiesModelBackendDelegate::value()
|
||||
{
|
||||
return &m_value;
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -5,6 +5,8 @@
|
||||
|
||||
#include <nodeinstanceglobal.h>
|
||||
|
||||
#include <studioquickwidget.h>
|
||||
|
||||
#include <QStandardItemModel>
|
||||
|
||||
namespace QmlDesigner {
|
||||
@@ -15,6 +17,8 @@ class BindingProperty;
|
||||
class ModelNode;
|
||||
class VariantProperty;
|
||||
|
||||
class DynamicPropertiesModelBackendDelegate;
|
||||
|
||||
class DynamicPropertiesModel : public QStandardItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -22,11 +26,22 @@ class DynamicPropertiesModel : public QStandardItemModel
|
||||
public:
|
||||
enum ColumnRoles {
|
||||
TargetModelNodeRow = 0,
|
||||
PropertyNameRow = 1,
|
||||
PropertyTypeRow = 2,
|
||||
PropertyValueRow = 3
|
||||
PropertyNameRow = 1,
|
||||
PropertyTypeRow = 2,
|
||||
PropertyValueRow = 3
|
||||
};
|
||||
|
||||
enum UserRoles {
|
||||
InternalIdRole = Qt::UserRole + 2,
|
||||
TargetNameRole,
|
||||
PropertyNameRole,
|
||||
PropertyTypeRole,
|
||||
PropertyValueRole
|
||||
};
|
||||
|
||||
Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
|
||||
Q_PROPERTY(DynamicPropertiesModelBackendDelegate *delegate READ delegate CONSTANT)
|
||||
|
||||
DynamicPropertiesModel(bool explicitSelection, AbstractView *parent);
|
||||
|
||||
void bindingPropertyChanged(const BindingProperty &bindingProperty);
|
||||
@@ -62,6 +77,17 @@ public:
|
||||
static QVariant defaultValueForType(const TypeName &type);
|
||||
static QString defaultExpressionForType(const TypeName &type);
|
||||
|
||||
Q_INVOKABLE void add();
|
||||
Q_INVOKABLE void remove(int row);
|
||||
|
||||
int currentIndex() const;
|
||||
void setCurrentIndex(int i);
|
||||
|
||||
int findRowForProperty(const AbstractProperty &abstractProperty) const;
|
||||
|
||||
signals:
|
||||
void currentIndexChanged();
|
||||
|
||||
protected:
|
||||
void addProperty(const QVariant &propertyValue,
|
||||
const QString &propertyType,
|
||||
@@ -79,12 +105,17 @@ protected:
|
||||
void updateCustomData(int row, const AbstractProperty &property);
|
||||
int findRowForBindingProperty(const BindingProperty &bindingProperty) const;
|
||||
int findRowForVariantProperty(const VariantProperty &variantProperty) const;
|
||||
int findRowForProperty(const AbstractProperty &abstractProperty) const;
|
||||
|
||||
bool getExpressionStrings(const BindingProperty &bindingProperty, QString *sourceNode, QString *sourceProperty);
|
||||
bool getExpressionStrings(const BindingProperty &bindingProperty,
|
||||
QString *sourceNode,
|
||||
QString *sourceProperty);
|
||||
|
||||
void updateDisplayRole(int row, int columns, const QString &string);
|
||||
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
DynamicPropertiesModelBackendDelegate *delegate() const;
|
||||
|
||||
private:
|
||||
void handleDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
|
||||
void handleException();
|
||||
@@ -95,6 +126,48 @@ private:
|
||||
QString m_exceptionError;
|
||||
QList<ModelNode> m_selectedNodes;
|
||||
bool m_explicitSelection = false;
|
||||
int m_currentIndex = 0;
|
||||
|
||||
DynamicPropertiesModelBackendDelegate *m_delegate = nullptr;
|
||||
};
|
||||
|
||||
class DynamicPropertiesModelBackendDelegate : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(StudioQmlComboBoxBackend *type READ type CONSTANT)
|
||||
Q_PROPERTY(int currentRow READ currentRow WRITE setCurrentRow NOTIFY currentRowChanged)
|
||||
Q_PROPERTY(StudioQmlTextBackend *name READ name CONSTANT)
|
||||
Q_PROPERTY(StudioQmlTextBackend *value READ value CONSTANT)
|
||||
//Q_PROPERTY(QString value READ value WRITE setValue NOTIFY valueChanged)
|
||||
|
||||
public:
|
||||
DynamicPropertiesModelBackendDelegate(DynamicPropertiesModel *parent = nullptr);
|
||||
|
||||
signals:
|
||||
void currentRowChanged();
|
||||
void nameChanged();
|
||||
void valueChanged();
|
||||
|
||||
private:
|
||||
int currentRow() const;
|
||||
void setCurrentRow(int i);
|
||||
void handleTypeChanged();
|
||||
void handleNameChanged();
|
||||
void handleValueChanged();
|
||||
void handleException();
|
||||
QVariant variantValue() const;
|
||||
|
||||
StudioQmlComboBoxBackend *type();
|
||||
|
||||
StudioQmlTextBackend *name();
|
||||
StudioQmlTextBackend *value();
|
||||
|
||||
StudioQmlComboBoxBackend m_type;
|
||||
StudioQmlTextBackend m_name;
|
||||
StudioQmlTextBackend m_value;
|
||||
int m_currentRow = -1;
|
||||
QString m_exceptionError;
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -205,7 +205,7 @@ void ContentLibraryBundleImporter::handleImportTimer()
|
||||
for (const QString &pendingType : pendingTypes) {
|
||||
NodeMetaInfo metaInfo = model->metaInfo(pendingType.toUtf8());
|
||||
const bool isImport = m_pendingTypes[pendingType];
|
||||
const bool typeComplete = metaInfo.isValid() && !metaInfo.superClasses().empty();
|
||||
const bool typeComplete = metaInfo.isValid() && !metaInfo.prototypes().empty();
|
||||
if (isImport == typeComplete) {
|
||||
m_pendingTypes.remove(pendingType);
|
||||
if (isImport)
|
||||
|
@@ -68,8 +68,9 @@ void CurveEditorView::modelAboutToBeDetached(Model *model)
|
||||
|
||||
bool dirtyfiesView(const ModelNode &node)
|
||||
{
|
||||
return QmlTimeline::isValidQmlTimeline(node)
|
||||
|| QmlTimelineKeyframeGroup::isValidQmlTimelineKeyframeGroup(node);
|
||||
return (node.type() == "QtQuick.Timeline.Keyframe" && node.hasParentProperty())
|
||||
|| QmlTimeline::isValidQmlTimeline(node)
|
||||
|| QmlTimelineKeyframeGroup::isValidQmlTimelineKeyframeGroup(node);
|
||||
}
|
||||
|
||||
void CurveEditorView::nodeRemoved([[maybe_unused]] const ModelNode &removedNode,
|
||||
@@ -143,13 +144,8 @@ void CurveEditorView::variantPropertiesChanged([[maybe_unused]] const QList<Vari
|
||||
[[maybe_unused]] PropertyChangeFlags propertyChange)
|
||||
{
|
||||
for (const auto &property : propertyList) {
|
||||
if ((property.name() == "frame" || property.name() == "value")
|
||||
&& property.parentModelNode().type() == "QtQuick.Timeline.Keyframe"
|
||||
&& property.parentModelNode().hasParentProperty()) {
|
||||
const ModelNode framesNode = property.parentModelNode().parentProperty().parentModelNode();
|
||||
if (QmlTimelineKeyframeGroup::isValidQmlTimelineKeyframeGroup(framesNode))
|
||||
updateKeyframes();
|
||||
}
|
||||
if (dirtyfiesView(property.parentModelNode()))
|
||||
updateKeyframes();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,20 +153,16 @@ void CurveEditorView::bindingPropertiesChanged([[maybe_unused]] const QList<Bind
|
||||
[[maybe_unused]] PropertyChangeFlags propertyChange)
|
||||
{
|
||||
for (const auto &property : propertyList) {
|
||||
if (property.name() == "easing.bezierCurve") {
|
||||
if (dirtyfiesView(property.parentModelNode()))
|
||||
updateKeyframes();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CurveEditorView::propertiesRemoved([[maybe_unused]] const QList<AbstractProperty> &propertyList)
|
||||
{
|
||||
for (const auto &property : propertyList) {
|
||||
if (property.name() == "keyframes") {
|
||||
ModelNode parent = property.parentModelNode();
|
||||
if (dirtyfiesView(parent))
|
||||
updateKeyframes();
|
||||
}
|
||||
if (dirtyfiesView(property.parentModelNode()))
|
||||
updateKeyframes();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -7,6 +7,7 @@
|
||||
#include <qmldesignerplugin.h>
|
||||
|
||||
#include <bindingproperty.h>
|
||||
#include <model/modelutils.h>
|
||||
#include <nodeabstractproperty.h>
|
||||
#include <nodelistproperty.h>
|
||||
#include <nodemetainfo.h>
|
||||
@@ -93,9 +94,9 @@ void DebugView::nodeCreated(const ModelNode &createdNode)
|
||||
message << createdNode.majorVersion() << "." << createdNode.minorVersion();
|
||||
message << createdNode.nodeSource();
|
||||
message << "MetaInfo " << createdNode.metaInfo().isValid() << " ";
|
||||
if (createdNode.metaInfo().isValid()) {
|
||||
message << createdNode.metaInfo().majorVersion() << "." << createdNode.metaInfo().minorVersion();
|
||||
message << createdNode.metaInfo().componentFileName();
|
||||
if (auto metaInfo = createdNode.metaInfo()) {
|
||||
message << metaInfo.majorVersion() << "." << metaInfo.minorVersion();
|
||||
message << ModelUtils::componentFilePath(createdNode);
|
||||
}
|
||||
log("::nodeCreated:", message.readAll());
|
||||
}
|
||||
@@ -282,9 +283,10 @@ void DebugView::selectedNodesChanged(const QList<ModelNode> &selectedNodes /*sel
|
||||
message << lineBreak;
|
||||
|
||||
if (selectedNode.metaInfo().isValid()) {
|
||||
for (const NodeMetaInfo &metaInfo : selectedNode.metaInfo().classHierarchy())
|
||||
for (const NodeMetaInfo &metaInfo : selectedNode.metaInfo().selfAndPrototypes()) {
|
||||
message << metaInfo.typeName() << " " << metaInfo.majorVersion() << "."
|
||||
<< metaInfo.minorVersion() << lineBreak;
|
||||
}
|
||||
|
||||
message << lineBreak;
|
||||
message << selectedNode.metaInfo().typeName();
|
||||
|
@@ -3,19 +3,20 @@
|
||||
|
||||
#include "bakelights.h"
|
||||
|
||||
#include "abstractview.h"
|
||||
#include "auxiliarydataproperties.h"
|
||||
#include "bakelightsdatamodel.h"
|
||||
#include "bakelightsconnectionmanager.h"
|
||||
#include "bindingproperty.h"
|
||||
#include "documentmanager.h"
|
||||
#include "modelnode.h"
|
||||
#include "nodeabstractproperty.h"
|
||||
#include "nodeinstanceview.h"
|
||||
#include "nodemetainfo.h"
|
||||
#include "plaintexteditmodifier.h"
|
||||
#include "rewriterview.h"
|
||||
#include "variantproperty.h"
|
||||
#include <abstractview.h>
|
||||
#include <auxiliarydataproperties.h>
|
||||
#include <bakelightsconnectionmanager.h>
|
||||
#include <bakelightsdatamodel.h>
|
||||
#include <bindingproperty.h>
|
||||
#include <documentmanager.h>
|
||||
#include <model/modelutils.h>
|
||||
#include <modelnode.h>
|
||||
#include <nodeabstractproperty.h>
|
||||
#include <nodeinstanceview.h>
|
||||
#include <nodemetainfo.h>
|
||||
#include <plaintexteditmodifier.h>
|
||||
#include <rewriterview.h>
|
||||
#include <variantproperty.h>
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
|
||||
@@ -240,18 +241,21 @@ void BakeLights::rebake()
|
||||
void BakeLights::exposeModelsAndLights(const QString &nodeId)
|
||||
{
|
||||
ModelNode compNode = m_view->modelNodeForId(nodeId);
|
||||
if (!compNode.isValid() || !compNode.isComponent()
|
||||
|| compNode.metaInfo().componentFileName().isEmpty()) {
|
||||
if (!compNode.isValid() || !compNode.isComponent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto componentFilePath = ModelUtils::componentFilePath(compNode);
|
||||
if (componentFilePath.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
RewriterView rewriter{m_view->externalDependencies(), RewriterView::Amend};
|
||||
ModelPointer compModel = QmlDesigner::Model::create("QtQuick/Item", 2, 1);
|
||||
const QString compFile = compNode.metaInfo().componentFileName();
|
||||
const Utils::FilePath compFilePath = Utils::FilePath::fromString(compFile);
|
||||
const Utils::FilePath compFilePath = Utils::FilePath::fromString(componentFilePath);
|
||||
QByteArray src = compFilePath.fileContents().value();
|
||||
|
||||
compModel->setFileUrl(QUrl::fromLocalFile(compFile));
|
||||
compModel->setFileUrl(QUrl::fromLocalFile(componentFilePath));
|
||||
|
||||
auto textDocument = std::make_unique<QTextDocument>(QString::fromUtf8(src));
|
||||
auto modifier = std::make_unique<IndentingTextEditModifier>(
|
||||
@@ -295,12 +299,12 @@ void BakeLights::exposeModelsAndLights(const QString &nodeId)
|
||||
|
||||
QString newText = modifier->text();
|
||||
if (newText != originalText) {
|
||||
QSaveFile saveFile(compFile);
|
||||
QSaveFile saveFile(componentFilePath);
|
||||
if (saveFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
||||
saveFile.write(newText.toUtf8());
|
||||
saveFile.commit();
|
||||
} else {
|
||||
qWarning() << __FUNCTION__ << "Failed to save changes to:" << compFile;
|
||||
qWarning() << __FUNCTION__ << "Failed to save changes to:" << componentFilePath;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -3,6 +3,8 @@
|
||||
|
||||
#include "backgroundaction.h"
|
||||
|
||||
#include <theme.h>
|
||||
|
||||
#include <utils/stylehelper.h>
|
||||
|
||||
#include <QComboBox>
|
||||
@@ -28,6 +30,14 @@ QIcon iconForColor(const QColor &color) {
|
||||
image.fill(0);
|
||||
QPainter p(&image);
|
||||
|
||||
if (color == BackgroundAction::ContextImage) {
|
||||
const QString unicode = Theme::getIconUnicode(Theme::Icon::textures_medium);
|
||||
const QString fontName = "qtds_propertyIconFont.ttf";
|
||||
QIcon icon = Utils::StyleHelper::getIconFromIconFont(fontName, unicode, 10, 10, Qt::white);
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
p.fillRect(2, 2, size - 4, size - 4, Qt::black);
|
||||
|
||||
if (color.alpha() == 0) {
|
||||
@@ -70,13 +80,13 @@ QList<QColor> BackgroundAction::colors()
|
||||
{
|
||||
static QColor alphaZero(Qt::transparent);
|
||||
static QList<QColor> colorList = {alphaZero,
|
||||
QColor(BackgroundAction::ContextImage),
|
||||
QColor(Qt::black),
|
||||
QColor(0x4c4e50),
|
||||
QColor(Qt::darkGray),
|
||||
QColor(Qt::lightGray),
|
||||
QColor(Qt::white)};
|
||||
|
||||
|
||||
return colorList;
|
||||
}
|
||||
|
||||
|
@@ -11,14 +11,12 @@ namespace QmlDesigner {
|
||||
|
||||
class BackgroundAction : public QWidgetAction
|
||||
{
|
||||
enum BackgroundType {
|
||||
CheckboardBackground,
|
||||
WhiteBackground,
|
||||
BlackBackground
|
||||
};
|
||||
enum BackgroundType { CheckboardBackground, WhiteBackground, BlackBackground };
|
||||
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum SpecialColor { ContextImage = Qt::yellow };
|
||||
|
||||
explicit BackgroundAction(QObject *parent);
|
||||
void setColor(const QColor &color);
|
||||
|
||||
|
@@ -2,9 +2,13 @@
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "formeditorgraphicsview.h"
|
||||
#include "backgroundaction.h"
|
||||
#include "formeditoritem.h"
|
||||
#include "formeditorwidget.h"
|
||||
#include "navigation2d.h"
|
||||
|
||||
#include <theme.h>
|
||||
|
||||
#include <utils/hostosinfo.h>
|
||||
|
||||
#include <QAction>
|
||||
@@ -198,10 +202,28 @@ void FormEditorGraphicsView::drawBackground(QPainter *painter, const QRectF &rec
|
||||
painter->save();
|
||||
painter->setBrushOrigin(0, 0);
|
||||
|
||||
painter->fillRect(rectangle.intersected(rootItemRect()), backgroundBrush());
|
||||
// paint rect around editable area
|
||||
painter->setPen(Qt::black);
|
||||
painter->drawRect(rootItemRect());
|
||||
|
||||
if (backgroundBrush().color() == BackgroundAction::ContextImage) {
|
||||
painter->fillRect(rectangle.intersected(rootItemRect()), Qt::gray);
|
||||
painter->setOpacity(0.5);
|
||||
if (!m_backgroundImage.isNull())
|
||||
painter->drawImage(rootItemRect().topLeft() + m_backgroundImage.offset(),
|
||||
m_backgroundImage);
|
||||
painter->setOpacity(1.0);
|
||||
} else {
|
||||
painter->fillRect(rectangle.intersected(rootItemRect()), backgroundBrush());
|
||||
}
|
||||
|
||||
QPen pen(Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_FormEditorSelectionColor));
|
||||
|
||||
pen.setStyle(Qt::DotLine);
|
||||
pen.setWidth(1);
|
||||
|
||||
painter->setPen(pen);
|
||||
|
||||
painter->drawRect(rootItemRect().adjusted(-1, -1, 0, 0));
|
||||
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
@@ -210,6 +232,17 @@ void FormEditorGraphicsView::frame(const QRectF &boundingRect)
|
||||
fitInView(boundingRect, Qt::KeepAspectRatio);
|
||||
}
|
||||
|
||||
void FormEditorGraphicsView::setBackgoundImage(const QImage &image)
|
||||
{
|
||||
m_backgroundImage = image;
|
||||
update();
|
||||
}
|
||||
|
||||
QImage FormEditorGraphicsView::backgroundImage() const
|
||||
{
|
||||
return m_backgroundImage;
|
||||
}
|
||||
|
||||
void FormEditorGraphicsView::setZoomFactor(double zoom)
|
||||
{
|
||||
resetTransform();
|
||||
|
@@ -28,6 +28,9 @@ public:
|
||||
void setZoomFactor(double zoom);
|
||||
void frame(const QRectF &bbox);
|
||||
|
||||
void setBackgoundImage(const QImage &image);
|
||||
QImage backgroundImage() const;
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
void wheelEvent(QWheelEvent *event) override;
|
||||
@@ -45,6 +48,7 @@ private:
|
||||
Panning m_isPanning = Panning::NotStarted;
|
||||
QPoint m_panningStartPosition;
|
||||
QRectF m_rootItemRect;
|
||||
QImage m_backgroundImage;
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -63,6 +63,8 @@ void FormEditorView::modelAttached(Model *model)
|
||||
if (!isEnabled())
|
||||
return;
|
||||
|
||||
m_formEditorWidget->setBackgoundImage({});
|
||||
|
||||
temporaryBlockView();
|
||||
setupFormEditorWidget();
|
||||
|
||||
@@ -649,6 +651,10 @@ void FormEditorView::auxiliaryDataChanged(const ModelNode &node,
|
||||
if (FormEditorItem *editorItem = scene()->itemForQmlItemNode(item))
|
||||
editorItem->setFrameColor(data.value<QColor>());
|
||||
}
|
||||
|
||||
if (key == contextImageProperty) {
|
||||
m_formEditorWidget->setBackgoundImage(data.value<QImage>());
|
||||
}
|
||||
}
|
||||
|
||||
static void updateTransitions(FormEditorScene *scene, const QmlItemNode &qmlItemNode)
|
||||
@@ -784,6 +790,11 @@ void FormEditorView::exportAsImage()
|
||||
m_formEditorWidget->exportAsImage(m_scene->rootFormEditorItem()->boundingRect());
|
||||
}
|
||||
|
||||
QImage FormEditorView::takeFormEditorScreenshot()
|
||||
{
|
||||
return m_formEditorWidget->takeFormEditorScreenshot();
|
||||
}
|
||||
|
||||
QPicture FormEditorView::renderToPicture() const
|
||||
{
|
||||
return m_formEditorWidget->renderToPicture();
|
||||
@@ -954,6 +965,11 @@ void FormEditorView::setupRootItemSize()
|
||||
|
||||
formEditorWidget()->setRootItemRect(rootQmlNode.instanceBoundingRect());
|
||||
formEditorWidget()->centerScene();
|
||||
|
||||
auto contextImage = rootModelNode().auxiliaryData(contextImageProperty);
|
||||
|
||||
if (contextImage)
|
||||
m_formEditorWidget->setBackgoundImage(contextImage.value().value<QImage>());
|
||||
}
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user