Merge remote-tracking branch 'origin/qds/dev'

Conflicts: tests/unit/tests/printers/gtest-creator-printing.cpp

Change-Id: I5f791161ca1a2966e98a3ca55bc60e5bcbb8f58f
This commit is contained in:
Tim Jenssen
2023-07-20 22:41:50 +02:00
203 changed files with 18635 additions and 7226 deletions

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

View File

@@ -113,14 +113,35 @@
\c {QtQuick.Controls 2.3} and to use Qt Quick Studio Components 1.0, \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 need the import statement \c {QtQuick.Studio.Components 1.0}.
You can also import a module as an alias. You can also import a module as an alias.
\li In the \uicontrol {Properties} field, specify properties for the \li In the \uicontrol {Properties} field, specify new properties or assign
component. You can add and modify properties in \QDS. 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 \li Select the \uicontrol Clip check box to enable clipping in the
component generated from the layer. The generated component will clip component generated from the layer. The generated component will clip
its own painting, as well as the painting of its children, to its its own painting, as well as the painting of its children, to its
bounding rectangle. bounding rectangle.
\li Select the \uicontrol Visible check box to determine the visibility \li Select the \uicontrol Visible check box to determine the visibility
of the layer in the generated UI in \QDS. 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 \li Select \uicontrol Export to launch the export dialog to export the document
into a .qtbridge archive. into a .qtbridge archive.
\endlist \endlist

View File

@@ -226,6 +226,8 @@
To align a camera to the \uicontrol{3D} view: To align a camera to the \uicontrol{3D} view:
\list 1 \list 1
\li Select a camera in the \uicontrol{3D} or \uicontrol {Navigator} view. \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, \li In the \uicontrol{3D} view,
select \inlineimage icons/align-camera-on.png 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 This moves and rotates the camera so that the camera shows the same view
as the current view in the \uicontrol{3D} 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 \list 1
\li Select a camera in the \uicontrol{3D} view or \uicontrol {Navigator}. \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, \li In the \uicontrol{3D} view,
select \inlineimage icons/align-view-on.png select \inlineimage icons/align-view-on.png
. .

View File

@@ -11,7 +11,7 @@
\note The \uicontrol {Content Library} view is included in the \note The \uicontrol {Content Library} view is included in the
\l{https://www.qt.io/pricing}{Qt Design Studio Enterprise license}. \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 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 have added an asset from \uicontrol {Content Library}, you can use it in
your project. your project.
@@ -63,4 +63,15 @@
\endlist \endlist
\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
*/ */

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}
}

View File

@@ -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
}
}

View 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
}
}

View File

@@ -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()
}
}
}
}
}

View File

@@ -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
}
}

View File

@@ -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)
}
}
}

View File

@@ -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
}
}

View File

@@ -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"
}
]
}

View File

@@ -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
}

View File

@@ -0,0 +1 @@
singleton Constants 1.0 Constants.qml

View File

@@ -40,6 +40,13 @@ T.TextField {
hoverEnabled: true hoverEnabled: true
clip: true clip: true
Keys.onPressed: (event) => {
if (event.key === Qt.Key_Escape && event.modifiers === Qt.NoModifier) {
control.clear()
event.accepted = true
}
}
Text { Text {
id: placeholder id: placeholder
x: control.leftPadding x: control.leftPadding

View File

@@ -179,166 +179,168 @@ QtObject {
readonly property string materialPreviewEnvironment: "\u00C6" readonly property string materialPreviewEnvironment: "\u00C6"
readonly property string materialPreviewModel: "\u00C7" readonly property string materialPreviewModel: "\u00C7"
readonly property string material_medium: "\u00C8" readonly property string material_medium: "\u00C8"
readonly property string mergeCells: "\u00C9" readonly property string maxBar_small: "\u00C9"
readonly property string merge_small: "\u00CA" readonly property string mergeCells: "\u00CA"
readonly property string minus: "\u00CB" readonly property string merge_small: "\u00CB"
readonly property string mirror: "\u00CC" readonly property string minus: "\u00CC"
readonly property string more_medium: "\u00CD" readonly property string mirror: "\u00CD"
readonly property string mouseArea_small: "\u00CE" readonly property string more_medium: "\u00CE"
readonly property string moveDown_medium: "\u00CF" readonly property string mouseArea_small: "\u00CF"
readonly property string moveInwards_medium: "\u00D0" readonly property string moveDown_medium: "\u00D0"
readonly property string moveUp_medium: "\u00D1" readonly property string moveInwards_medium: "\u00D1"
readonly property string moveUpwards_medium: "\u00D2" readonly property string moveUp_medium: "\u00D2"
readonly property string move_medium: "\u00D3" readonly property string moveUpwards_medium: "\u00D3"
readonly property string newMaterial: "\u00D4" readonly property string move_medium: "\u00D4"
readonly property string nextFile_large: "\u00D5" readonly property string newMaterial: "\u00D5"
readonly property string openLink: "\u00D6" readonly property string nextFile_large: "\u00D6"
readonly property string openMaterialBrowser: "\u00D7" readonly property string normalBar_small: "\u00D7"
readonly property string orientation: "\u00D8" readonly property string openLink: "\u00D8"
readonly property string orthCam_medium: "\u00D9" readonly property string openMaterialBrowser: "\u00D9"
readonly property string orthCam_small: "\u00DA" readonly property string orientation: "\u00DA"
readonly property string paddingEdge: "\u00DB" readonly property string orthCam_medium: "\u00DB"
readonly property string paddingFrame: "\u00DC" readonly property string orthCam_small: "\u00DC"
readonly property string particleAnimation_medium: "\u00DD" readonly property string paddingEdge: "\u00DD"
readonly property string pasteStyle: "\u00DE" readonly property string paddingFrame: "\u00DE"
readonly property string paste_small: "\u00DF" readonly property string particleAnimation_medium: "\u00DF"
readonly property string pause: "\u00E0" readonly property string pasteStyle: "\u00E0"
readonly property string perspectiveCam_medium: "\u00E1" readonly property string paste_small: "\u00E1"
readonly property string perspectiveCam_small: "\u00E2" readonly property string pause: "\u00E2"
readonly property string pin: "\u00E3" readonly property string perspectiveCam_medium: "\u00E3"
readonly property string plane_medium: "\u00E4" readonly property string perspectiveCam_small: "\u00E4"
readonly property string plane_small: "\u00E5" readonly property string pin: "\u00E5"
readonly property string play: "\u00E6" readonly property string plane_medium: "\u00E6"
readonly property string playFill_medium: "\u00E7" readonly property string plane_small: "\u00E7"
readonly property string playOutline_medium: "\u00E8" readonly property string play: "\u00E8"
readonly property string plus: "\u00E9" readonly property string playFill_medium: "\u00E9"
readonly property string pointLight_small: "\u00EA" readonly property string playOutline_medium: "\u00EA"
readonly property string positioners_small: "\u00EB" readonly property string plus: "\u00EB"
readonly property string previewEnv_medium: "\u00EC" readonly property string pointLight_small: "\u00EC"
readonly property string previousFile_large: "\u00ED" readonly property string positioners_small: "\u00ED"
readonly property string promote: "\u00EE" readonly property string previewEnv_medium: "\u00EE"
readonly property string properties_medium: "\u00EF" readonly property string previousFile_large: "\u00EF"
readonly property string readOnly: "\u00F0" readonly property string promote: "\u00F0"
readonly property string recordFill_medium: "\u00F1" readonly property string properties_medium: "\u00F1"
readonly property string recordOutline_medium: "\u00F2" readonly property string readOnly: "\u00F2"
readonly property string redo: "\u00F3" readonly property string recordFill_medium: "\u00F3"
readonly property string reload_medium: "\u00F4" readonly property string recordOutline_medium: "\u00F4"
readonly property string remove_medium: "\u00F5" readonly property string redo: "\u00F5"
readonly property string remove_small: "\u00F6" readonly property string reload_medium: "\u00F6"
readonly property string rename_small: "\u00F7" readonly property string remove_medium: "\u00F7"
readonly property string replace_small: "\u00F8" readonly property string remove_small: "\u00F8"
readonly property string resetView_small: "\u00F9" readonly property string rename_small: "\u00F9"
readonly property string restartParticles_medium: "\u00FA" readonly property string replace_small: "\u00FA"
readonly property string reverseOrder_medium: "\u00FB" readonly property string resetView_small: "\u00FB"
readonly property string roatate_medium: "\u00FC" readonly property string restartParticles_medium: "\u00FC"
readonly property string rotationFill: "\u00FD" readonly property string reverseOrder_medium: "\u00FD"
readonly property string rotationOutline: "\u00FE" readonly property string roatate_medium: "\u00FE"
readonly property string runProjFill_large: "\u00FF" readonly property string rotationFill: "\u00FF"
readonly property string runProjOutline_large: "\u0100" readonly property string rotationOutline: "\u0100"
readonly property string s_anchors: "\u0101" readonly property string runProjFill_large: "\u0101"
readonly property string s_annotations: "\u0102" readonly property string runProjOutline_large: "\u0102"
readonly property string s_arrange: "\u0103" readonly property string s_anchors: "\u0103"
readonly property string s_boundingBox: "\u0104" readonly property string s_annotations: "\u0104"
readonly property string s_component: "\u0105" readonly property string s_arrange: "\u0105"
readonly property string s_connections: "\u0106" readonly property string s_boundingBox: "\u0106"
readonly property string s_edit: "\u0107" readonly property string s_component: "\u0107"
readonly property string s_enterComponent: "\u0108" readonly property string s_connections: "\u0108"
readonly property string s_eventList: "\u0109" readonly property string s_edit: "\u0109"
readonly property string s_group: "\u010A" readonly property string s_enterComponent: "\u010A"
readonly property string s_layouts: "\u010B" readonly property string s_eventList: "\u010B"
readonly property string s_merging: "\u010C" readonly property string s_group: "\u010C"
readonly property string s_mouseArea: "\u010D" readonly property string s_layouts: "\u010D"
readonly property string s_positioners: "\u010E" readonly property string s_merging: "\u010E"
readonly property string s_selection: "\u010F" readonly property string s_mouseArea: "\u010F"
readonly property string s_snapping: "\u0110" readonly property string s_positioners: "\u0110"
readonly property string s_timeline: "\u0111" readonly property string s_selection: "\u0111"
readonly property string s_visibility: "\u0112" readonly property string s_snapping: "\u0112"
readonly property string saveLogs_medium: "\u0113" readonly property string s_timeline: "\u0113"
readonly property string scale_medium: "\u0114" readonly property string s_visibility: "\u0114"
readonly property string search: "\u0115" readonly property string saveLogs_medium: "\u0115"
readonly property string search_small: "\u0116" readonly property string scale_medium: "\u0116"
readonly property string sectionToggle: "\u0117" readonly property string search: "\u0117"
readonly property string selectFill_medium: "\u0118" readonly property string search_small: "\u0118"
readonly property string selectOutline_medium: "\u0119" readonly property string sectionToggle: "\u0119"
readonly property string selectParent_small: "\u011A" readonly property string selectFill_medium: "\u011A"
readonly property string selection_small: "\u011B" readonly property string selectOutline_medium: "\u011B"
readonly property string settings_medium: "\u011C" readonly property string selectParent_small: "\u011C"
readonly property string signal_small: "\u011D" readonly property string selection_small: "\u011D"
readonly property string snapping_small: "\u011E" readonly property string settings_medium: "\u011E"
readonly property string sphere_medium: "\u011F" readonly property string signal_small: "\u011F"
readonly property string sphere_small: "\u0120" readonly property string snapping_small: "\u0120"
readonly property string splitColumns: "\u0121" readonly property string sphere_medium: "\u0121"
readonly property string splitRows: "\u0122" readonly property string sphere_small: "\u0122"
readonly property string spotLight_small: "\u0123" readonly property string splitColumns: "\u0123"
readonly property string stackedContainer_small: "\u0124" readonly property string splitRows: "\u0124"
readonly property string startNode: "\u0125" readonly property string spotLight_small: "\u0125"
readonly property string step_medium: "\u0126" readonly property string stackedContainer_small: "\u0126"
readonly property string stop_medium: "\u0127" readonly property string startNode: "\u0127"
readonly property string testIcon: "\u0128" readonly property string step_medium: "\u0128"
readonly property string textAlignBottom: "\u0129" readonly property string stop_medium: "\u0129"
readonly property string textAlignCenter: "\u012A" readonly property string testIcon: "\u012A"
readonly property string textAlignJustified: "\u012B" readonly property string textAlignBottom: "\u012B"
readonly property string textAlignLeft: "\u012C" readonly property string textAlignCenter: "\u012C"
readonly property string textAlignMiddle: "\u012D" readonly property string textAlignJustified: "\u012D"
readonly property string textAlignRight: "\u012E" readonly property string textAlignLeft: "\u012E"
readonly property string textAlignTop: "\u012F" readonly property string textAlignMiddle: "\u012F"
readonly property string textBulletList: "\u0130" readonly property string textAlignRight: "\u0130"
readonly property string textFullJustification: "\u0131" readonly property string textAlignTop: "\u0131"
readonly property string textNumberedList: "\u0132" readonly property string textBulletList: "\u0132"
readonly property string textures_medium: "\u0133" readonly property string textFullJustification: "\u0133"
readonly property string tickIcon: "\u0134" readonly property string textNumberedList: "\u0134"
readonly property string tickMark_small: "\u0135" readonly property string textures_medium: "\u0135"
readonly property string timeline_small: "\u0136" readonly property string tickIcon: "\u0136"
readonly property string toEndFrame_medium: "\u0137" readonly property string tickMark_small: "\u0137"
readonly property string toNextFrame_medium: "\u0138" readonly property string timeline_small: "\u0138"
readonly property string toPrevFrame_medium: "\u0139" readonly property string toEndFrame_medium: "\u0139"
readonly property string toStartFrame_medium: "\u013A" readonly property string toNextFrame_medium: "\u013A"
readonly property string topToolbar_annotations: "\u013B" readonly property string toPrevFrame_medium: "\u013B"
readonly property string topToolbar_closeFile: "\u013C" readonly property string toStartFrame_medium: "\u013C"
readonly property string topToolbar_designMode: "\u013D" readonly property string topToolbar_annotations: "\u013D"
readonly property string topToolbar_enterComponent: "\u013E" readonly property string topToolbar_closeFile: "\u013E"
readonly property string topToolbar_home: "\u013F" readonly property string topToolbar_designMode: "\u013F"
readonly property string topToolbar_makeComponent: "\u0140" readonly property string topToolbar_enterComponent: "\u0140"
readonly property string topToolbar_navFile: "\u0141" readonly property string topToolbar_home: "\u0141"
readonly property string topToolbar_runProject: "\u0142" readonly property string topToolbar_makeComponent: "\u0142"
readonly property string translationCreateFiles: "\u0143" readonly property string topToolbar_navFile: "\u0143"
readonly property string translationCreateReport: "\u0144" readonly property string topToolbar_runProject: "\u0144"
readonly property string translationExport: "\u0145" readonly property string translationCreateFiles: "\u0145"
readonly property string translationImport: "\u0146" readonly property string translationCreateReport: "\u0146"
readonly property string translationSelectLanguages: "\u0147" readonly property string translationExport: "\u0147"
readonly property string translationTest: "\u0148" readonly property string translationImport: "\u0148"
readonly property string transparent: "\u0149" readonly property string translationSelectLanguages: "\u0149"
readonly property string triState: "\u014A" readonly property string translationTest: "\u014A"
readonly property string triangleArcA: "\u014B" readonly property string transparent: "\u014B"
readonly property string triangleArcB: "\u014C" readonly property string triState: "\u014C"
readonly property string triangleCornerA: "\u014D" readonly property string triangleArcA: "\u014D"
readonly property string triangleCornerB: "\u014E" readonly property string triangleArcB: "\u014E"
readonly property string unLinked: "\u014F" readonly property string triangleCornerA: "\u014F"
readonly property string undo: "\u0150" readonly property string triangleCornerB: "\u0150"
readonly property string unify_medium: "\u0151" readonly property string unLinked: "\u0151"
readonly property string unpin: "\u0152" readonly property string undo: "\u0152"
readonly property string upDownIcon: "\u0153" readonly property string unify_medium: "\u0153"
readonly property string upDownSquare2: "\u0154" readonly property string unpin: "\u0154"
readonly property string updateAvailable_medium: "\u0155" readonly property string upDownIcon: "\u0155"
readonly property string updateContent_medium: "\u0156" readonly property string upDownSquare2: "\u0156"
readonly property string visibilityOff: "\u0157" readonly property string updateAvailable_medium: "\u0157"
readonly property string visibilityOn: "\u0158" readonly property string updateContent_medium: "\u0158"
readonly property string visible_medium: "\u0159" readonly property string visibilityOff: "\u0159"
readonly property string visible_small: "\u015A" readonly property string visibilityOn: "\u015A"
readonly property string wildcard: "\u015B" readonly property string visible_medium: "\u015B"
readonly property string wizardsAutomotive: "\u015C" readonly property string visible_small: "\u015C"
readonly property string wizardsDesktop: "\u015D" readonly property string wildcard: "\u015D"
readonly property string wizardsGeneric: "\u015E" readonly property string wizardsAutomotive: "\u015E"
readonly property string wizardsMcuEmpty: "\u015F" readonly property string wizardsDesktop: "\u015F"
readonly property string wizardsMcuGraph: "\u0160" readonly property string wizardsGeneric: "\u0160"
readonly property string wizardsMobile: "\u0161" readonly property string wizardsMcuEmpty: "\u0161"
readonly property string wizardsUnknown: "\u0162" readonly property string wizardsMcuGraph: "\u0162"
readonly property string zoomAll: "\u0163" readonly property string wizardsMobile: "\u0163"
readonly property string zoomIn: "\u0164" readonly property string wizardsUnknown: "\u0164"
readonly property string zoomIn_medium: "\u0165" readonly property string zoomAll: "\u0165"
readonly property string zoomOut: "\u0166" readonly property string zoomIn: "\u0166"
readonly property string zoomOut_medium: "\u0167" readonly property string zoomIn_medium: "\u0167"
readonly property string zoomSelection: "\u0168" readonly property string zoomOut: "\u0168"
readonly property string zoomOut_medium: "\u0169"
readonly property string zoomSelection: "\u016A"
readonly property font iconFont: Qt.font({ readonly property font iconFont: Qt.font({
"family": controlIcons.name, "family": controlIcons.name,

View File

@@ -3,6 +3,9 @@ add_qtc_library(AdvancedDockingSystem
SOURCES SOURCES
ads_globals.cpp ads_globals.h ads_globals.cpp ads_globals.h
advanceddockingsystemtr.h advanceddockingsystemtr.h
autohidedockcontainer.cpp autohidedockcontainer.h
autohidesidebar.cpp autohidesidebar.h
autohidetab.cpp autohidetab.h
dockareatabbar.cpp dockareatabbar.h dockareatabbar.cpp dockareatabbar.h
dockareatitlebar.cpp dockareatitlebar.h dockareatitlebar.cpp dockareatitlebar.h
dockareawidget.cpp dockareawidget.h dockareawidget.cpp dockareawidget.h
@@ -19,6 +22,8 @@ add_qtc_library(AdvancedDockingSystem
floatingdockcontainer.cpp floatingdockcontainer.h floatingdockcontainer.cpp floatingdockcontainer.h
floatingdragpreview.cpp floatingdragpreview.h floatingdragpreview.cpp floatingdragpreview.h
iconprovider.cpp iconprovider.h iconprovider.cpp iconprovider.h
pushbutton.cpp pushbutton.h
resizehandle.cpp resizehandle.h
workspace.cpp workspace.h workspace.cpp workspace.h
workspacedialog.cpp workspacedialog.h workspacedialog.cpp workspacedialog.h
workspaceinputdialog.cpp workspaceinputdialog.h workspaceinputdialog.cpp workspaceinputdialog.h
@@ -28,6 +33,7 @@ add_qtc_library(AdvancedDockingSystem
extend_qtc_library(AdvancedDockingSystem extend_qtc_library(AdvancedDockingSystem
INCLUDES linux INCLUDES linux
CONDITION UNIX AND NOT APPLE
SOURCES SOURCES
linux/floatingwidgettitlebar.cpp linux/floatingwidgettitlebar.h linux/floatingwidgettitlebar.cpp linux/floatingwidgettitlebar.h
) )

View File

@@ -14,10 +14,19 @@
#include <QStyle> #include <QStyle>
#include <QVariant> #include <QVariant>
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
#include <QApplication>
#include <QFile>
#include <QSettings>
#endif
namespace ADS { namespace ADS {
namespace internal { namespace internal {
const int g_floatingWidgetDragStartEvent = QEvent::registerEventType();
const int g_dockedWidgetDragStartEvent = QEvent::registerEventType();
void replaceSplitterWidget(QSplitter *splitter, QWidget *from, QWidget *to) void replaceSplitterWidget(QSplitter *splitter, QWidget *from, QWidget *to)
{ {
int index = splitter->indexOf(from); int index = splitter->indexOf(from);
@@ -25,6 +34,16 @@ void replaceSplitterWidget(QSplitter *splitter, QWidget *from, QWidget *to)
splitter->insertWidget(index, 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) DockInsertParam dockAreaInsertParameters(DockWidgetArea area)
{ {
switch (area) { switch (area) {
@@ -44,6 +63,45 @@ DockInsertParam dockAreaInsertParameters(DockWidgetArea area)
return DockInsertParam(Qt::Vertical, false); 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 createTransparentPixmap(const QPixmap &source, qreal opacity)
{ {
QPixmap transparentPixmap(source.size()); QPixmap transparentPixmap(source.size());
@@ -54,18 +112,8 @@ QPixmap createTransparentPixmap(const QPixmap &source, qreal opacity)
return transparentPixmap; return transparentPixmap;
} }
void hideEmptyParentSplitters(DockSplitter *splitter)
{
while (splitter && splitter->isVisible()) {
if (!splitter->hasVisibleContent()) {
splitter->hide();
}
splitter = internal::findParent<DockSplitter *>(splitter);
}
}
void setButtonIcon(QAbstractButton *button, void setButtonIcon(QAbstractButton *button,
QStyle::StandardPixmap standarPixmap, QStyle::StandardPixmap standardPixmap,
ADS::eIcon customIconId) ADS::eIcon customIconId)
{ {
// First we try to use custom icons if available // First we try to use custom icons if available
@@ -75,12 +123,12 @@ void setButtonIcon(QAbstractButton *button,
return; return;
} }
if (Utils::HostOsInfo::isLinuxHost()) { if (Utils::HostOsInfo::isAnyUnixHost() && !Utils::HostOsInfo::isMacHost()) {
button->setIcon(button->style()->standardIcon(standarPixmap)); button->setIcon(button->style()->standardIcon(standardPixmap));
} else { } else {
// The standard icons does not look good on high DPI screens so we create // The standard icons does not look good on high DPI screens so we create
// our own "standard" icon here. // 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(internal::createTransparentPixmap(normalPixmap, 0.25), QIcon::Disabled);
icon.addPixmap(normalPixmap, QIcon::Normal); icon.addPixmap(normalPixmap, QIcon::Normal);
button->setIcon(icon); button->setIcon(icon);
@@ -98,14 +146,22 @@ void repolishStyle(QWidget *widget, eRepolishChildOptions options)
if (RepolishIgnoreChildren == options) if (RepolishIgnoreChildren == options)
return; return;
QList<QWidget*> children = widget->findChildren<QWidget *>(QString(), QList<QWidget *> children = widget->findChildren<QWidget *>(QString(),
(RepolishDirectChildren == options) ? Qt::FindDirectChildrenOnly : Qt::FindChildrenRecursively); (RepolishDirectChildren == options)
for (auto w : children) ? Qt::FindDirectChildrenOnly
{ : Qt::FindChildrenRecursively);
for (auto w : children) {
w->style()->unpolish(w); w->style()->unpolish(w);
w->style()->polish(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 internal
} // namespace ADS } // namespace ADS

View File

@@ -16,11 +16,11 @@ class QSplitter;
QT_END_NAMESPACE QT_END_NAMESPACE
#if defined(ADVANCEDDOCKINGSYSTEM_LIBRARY) #if defined(ADVANCEDDOCKINGSYSTEM_LIBRARY)
# define ADS_EXPORT Q_DECL_EXPORT #define ADS_EXPORT Q_DECL_EXPORT
#elif defined(ADVANCEDDOCKINGSYSTEM_STATIC_LIBRARY) #elif defined(ADVANCEDDOCKINGSYSTEM_STATIC_LIBRARY)
# define ADS_EXPORT #define ADS_EXPORT
#else #else
# define ADS_EXPORT Q_DECL_IMPORT #define ADS_EXPORT Q_DECL_IMPORT
#endif #endif
//#define ADS_DEBUG_PRINT //#define ADS_DEBUG_PRINT
@@ -47,15 +47,27 @@ enum DockWidgetArea {
TopDockWidgetArea = 0x04, TopDockWidgetArea = 0x04,
BottomDockWidgetArea = 0x08, BottomDockWidgetArea = 0x08,
CenterDockWidgetArea = 0x10, CenterDockWidgetArea = 0x10,
LeftAutoHideArea = 0x20,
RightAutoHideArea = 0x40,
TopAutoHideArea = 0x80,
BottomAutoHideArea = 0x100,
InvalidDockWidgetArea = NoDockWidgetArea, InvalidDockWidgetArea = NoDockWidgetArea,
OuterDockAreas = TopDockWidgetArea | LeftDockWidgetArea | RightDockWidgetArea OuterDockAreas = TopDockWidgetArea | LeftDockWidgetArea | RightDockWidgetArea
| BottomDockWidgetArea, | BottomDockWidgetArea,
AutoHideDockAreas = LeftAutoHideArea | RightAutoHideArea | TopAutoHideArea | BottomAutoHideArea,
AllDockAreas = OuterDockAreas | CenterDockWidgetArea AllDockAreas = OuterDockAreas | CenterDockWidgetArea
}; };
Q_DECLARE_FLAGS(DockWidgetAreas, DockWidgetArea) 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 * The different dragging states
@@ -72,41 +84,48 @@ enum eDragState {
*/ */
enum eIcon { enum eIcon {
TabCloseIcon, //!< TabCloseIcon TabCloseIcon, //!< TabCloseIcon
AutoHideIcon, //!< AutoHideIcon
DockAreaMenuIcon, //!< DockAreaMenuIcon DockAreaMenuIcon, //!< DockAreaMenuIcon
DockAreaUndockIcon, //!< DockAreaUndockIcon DockAreaUndockIcon, //!< DockAreaUndockIcon
DockAreaCloseIcon, //!< DockAreaCloseIcon DockAreaCloseIcon, //!< DockAreaCloseIcon
FloatingWidgetCloseIcon, //!< FloatingWidgetCloseIcon 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 enum eBitwiseOperator { BitwiseAnd, BitwiseOr };
{
BitwiseAnd,
BitwiseOr
};
namespace internal {
const char *const closedProperty = "close";
const char *const dirtyProperty = "dirty";
/** /**
* Replace the from widget in the given splitter with the To widget * Each dock container supports 4 sidebars
*/
enum SideBarLocation { SideBarTop, SideBarLeft, SideBarRight, SideBarBottom, SideBarNone };
namespace internal {
const char *const g_closedProperty = "close";
const char *const g_dirtyProperty = "dirty";
extern const int g_floatingWidgetDragStartEvent;
extern const int g_dockedWidgetDragStartEvent;
/**
* Replace the \p from widget in the given splitter with the \p to widget.
*/ */
void replaceSplitterWidget(QSplitter *splitter, QWidget *from, QWidget *to); void replaceSplitterWidget(QSplitter *splitter, QWidget *from, QWidget *to);
/** /**
* This function walks the splitter tree upwards to hides all splitters * This function walks the splitter tree upwards to hide all splitters that do not
* that do not have visible content * have visible content.
*/ */
void hideEmptyParentSplitters(DockSplitter *firstParentSplitter); void hideEmptyParentSplitters(DockSplitter *firstParentSplitter);
/** /**
* Convenience class for QPair to provide better naming than first and * Convenience class for QPair to provide better naming than first and second.
* second
*/ */
class DockInsertParam : public QPair<Qt::Orientation, bool> 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); DockInsertParam dockAreaInsertParameters(DockWidgetArea area);
/** /**
* Searches for the parent widget of the given type. * Returns the SieBarLocation for the AutoHide dock widget areas.
* Returns the parent widget of the given widget or 0 if the widget is not */
* child of any widget of type T SideBarLocation toSideBarLocation(DockWidgetArea area);
*
* 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 * Returns true for the top or bottom side bar ansd false for the left and right side bar.
* current dock widget in a dock area have no parent. */
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> template<class T>
T findParent(const QWidget *widget) T findParent(const QWidget *widget)
@@ -149,14 +180,13 @@ T findParent(const QWidget *widget)
} }
/** /**
* Creates a semi transparent pixmap from the given pixmap Source. * Creates a semi transparent pixmap from the given pixmap source. The opacity parameter defines
* The Opacity parameter defines the opacity from completely transparent (0.0) * the opacity from completely transparent (0.0) to completely opaque (1.0).
* to completely opaque (1.0)
*/ */
QPixmap createTransparentPixmap(const QPixmap &source, qreal opacity); 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> template<class T>
void setFlag(T &flags, typename T::enum_type flag, bool on = true) 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 * Helper function for setting tooltips without cluttering the code with tests for preprocessor macros.
* tests for preprocessor macros
*/ */
template <class QObjectPtr> template <class QObjectPtr>
void setToolTip(QObjectPtr obj, const QString &tip) 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. * Helper function to set the icon of a certain button. Use this function to set the icons for
* Use this function to set the icons for the dock area and dock widget buttons. * the dock area and dock widget buttons.
* The function first uses the CustomIconId to get an icon from the * The function first uses the \p customIconId to get an icon from the IconProvider. You can
* IconProvider. You can register your custom icons with the icon provider, if * register your custom icons with the icon provider, if you do not want to use the default buttons
* you do not want to use the default buttons and if you do not want to use * and if you do not want to use stylesheets.
* stylesheets. * If the IconProvider does not return a valid icon (icon is null), the function fetches the given
* If the IconProvider does not return a valid icon (icon is null), the function * standard pixmap from the QStyle.
* fetches the given standard pixmap from the QStyle. * param[in] button The button whose icons are to be set
* 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] StandardPixmap The standard pixmap to be used for the button * param[in] customIconId The identifier for the custom icon.
* param[in] CustomIconId The identifier for the custom icon.
*/ */
void setButtonIcon(QAbstractButton *button, QStyle::StandardPixmap standarPixmap, void setButtonIcon(QAbstractButton *button,
ADS::eIcon CustomIconId); QStyle::StandardPixmap standardPixmap,
ADS::eIcon customIconId);
enum eRepolishChildOptions enum eRepolishChildOptions
{ {
@@ -203,10 +232,14 @@ enum eRepolishChildOptions
}; };
/** /**
* Calls unpolish() / polish for the style of the given widget to update * Calls unpolish() / polish for the style of the given widget to update stylesheet if a property changes.
* stylesheet if a property changes
*/ */
void repolishStyle(QWidget *widget, eRepolishChildOptions options = RepolishIgnoreChildren); void repolishStyle(QWidget *widget, eRepolishChildOptions options = RepolishIgnoreChildren);
/**
* Returns the geometry of the given widget in global space.
*/
QRect globalGeometry(QWidget *widget);
} // namespace internal } // namespace internal
} // namespace ADS } // namespace ADS

View File

@@ -15,6 +15,9 @@ QtcLibrary {
files: [ files: [
"ads_globals.cpp", "ads_globals.h", "ads_globals.cpp", "ads_globals.h",
"advanceddockingsystemtr.h", "advanceddockingsystemtr.h",
"autohidedockcontainer.cpp", "autohidedockcontainer.h",
"autohidesidebar.cpp", "autohidesidebar.h",
"autohidetab.cpp", "autohidetab.h",
"dockareatabbar.cpp", "dockareatabbar.h", "dockareatabbar.cpp", "dockareatabbar.h",
"dockareatitlebar.cpp", "dockareatitlebar.h", "dockareatitlebar.cpp", "dockareatitlebar.h",
"dockareawidget.cpp", "dockareawidget.h", "dockareawidget.cpp", "dockareawidget.h",
@@ -31,6 +34,8 @@ QtcLibrary {
"floatingdockcontainer.cpp", "floatingdockcontainer.h", "floatingdockcontainer.cpp", "floatingdockcontainer.h",
"floatingdragpreview.cpp", "floatingdragpreview.h", "floatingdragpreview.cpp", "floatingdragpreview.h",
"iconprovider.cpp", "iconprovider.h", "iconprovider.cpp", "iconprovider.h",
"pushbutton.cpp", "pushbutton.h",
"resizehandle.cpp", "resizehandle.h",
"workspace.cpp", "workspace.h", "workspace.cpp", "workspace.h",
"workspacedialog.cpp", "workspacedialog.h", "workspacedialog.cpp", "workspacedialog.h",
"workspaceinputdialog.cpp", "workspaceinputdialog.h", "workspaceinputdialog.cpp", "workspaceinputdialog.h",

View 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

View 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

View 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

View 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

View 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

View 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

View File

@@ -13,18 +13,18 @@
#include <QLoggingCategory> #include <QLoggingCategory>
#include <QMouseEvent> #include <QMouseEvent>
#include <QScrollBar> #include <QScrollBar>
#include <QTimer>
#include <QtGlobal> #include <QtGlobal>
#include <iostream> #include <iostream>
namespace ADS namespace ADS {
{ /**
/**
* Private data class of DockAreaTabBar class (pimpl) * Private data class of DockAreaTabBar class (pimpl)
*/ */
class DockAreaTabBarPrivate class DockAreaTabBarPrivate
{ {
public: public:
DockAreaTabBar *q; DockAreaTabBar *q;
DockAreaWidget *m_dockArea = nullptr; DockAreaWidget *m_dockArea = nullptr;
QWidget *m_tabsContainerWidget = nullptr; QWidget *m_tabsContainerWidget = nullptr;
@@ -38,27 +38,27 @@ namespace ADS
/** /**
* Update tabs after current index changed or when tabs are removed. * Update tabs after current index changed or when tabs are removed.
* The function reassigns the stylesheet to update the tabs * The function reassigns the stylesheet to update the tabs.
*/ */
void updateTabs(); void updateTabs();
/** /**
* Convenience function to access first tab * Convenience function to access first tab.
*/ */
DockWidgetTab *firstTab() const { return q->tab(0); } DockWidgetTab *firstTab() const { return q->tab(0); }
/** /**
* Convenience function to access last tab * Convenience function to access last tab.
*/ */
DockWidgetTab *lastTab() const { return q->tab(q->count() - 1); } DockWidgetTab *lastTab() const { return q->tab(q->count() - 1); }
}; // class DockAreaTabBarPrivate }; // class DockAreaTabBarPrivate
DockAreaTabBarPrivate::DockAreaTabBarPrivate(DockAreaTabBar *parent) DockAreaTabBarPrivate::DockAreaTabBarPrivate(DockAreaTabBar *parent)
: q(parent) : q(parent)
{} {}
void DockAreaTabBarPrivate::updateTabs() void DockAreaTabBarPrivate::updateTabs()
{ {
// Set active TAB and update all other tabs to be inactive // Set active TAB and update all other tabs to be inactive
for (int i = 0; i < q->count(); ++i) { for (int i = 0; i < q->count(); ++i) {
auto tabWidget = q->tab(i); auto tabWidget = q->tab(i);
@@ -68,17 +68,19 @@ namespace ADS
if (i == m_currentIndex) { if (i == m_currentIndex) {
tabWidget->show(); tabWidget->show();
tabWidget->setActiveTab(true); tabWidget->setActiveTab(true);
q->ensureWidgetVisible(tabWidget); // 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 { } else {
tabWidget->setActiveTab(false); tabWidget->setActiveTab(false);
} }
} }
} }
DockAreaTabBar::DockAreaTabBar(DockAreaWidget *parent) DockAreaTabBar::DockAreaTabBar(DockAreaWidget *parent)
: QScrollArea(parent) : QScrollArea(parent)
, d(new DockAreaTabBarPrivate(this)) , d(new DockAreaTabBarPrivate(this))
{ {
d->m_dockArea = parent; d->m_dockArea = parent;
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
setFrameStyle(QFrame::NoFrame); setFrameStyle(QFrame::NoFrame);
@@ -95,174 +97,51 @@ namespace ADS
d->m_tabsLayout->addStretch(1); d->m_tabsLayout->addStretch(1);
d->m_tabsContainerWidget->setLayout(d->m_tabsLayout); d->m_tabsContainerWidget->setLayout(d->m_tabsLayout);
setWidget(d->m_tabsContainerWidget); setWidget(d->m_tabsContainerWidget);
} }
DockAreaTabBar::~DockAreaTabBar() { delete d; } DockAreaTabBar::~DockAreaTabBar()
{
delete d;
}
void DockAreaTabBar::wheelEvent(QWheelEvent *event) void DockAreaTabBar::onTabClicked(DockWidgetTab *sourceTab)
{ {
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 (tab(i)->isVisibleTo(this)) {
newCurrentIndex = i - 1;
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); const int index = d->m_tabsLayout->indexOf(sourceTab);
if (index < 0) if (index < 0)
return; return;
setCurrentIndex(index); setCurrentIndex(index);
emit tabBarClicked(index); emit tabBarClicked(index);
} }
void DockAreaTabBar::onTabCloseRequested(DockWidgetTab *sourceTab) void DockAreaTabBar::onTabCloseRequested(DockWidgetTab *sourceTab)
{ {
const int index = d->m_tabsLayout->indexOf(sourceTab); const int index = d->m_tabsLayout->indexOf(sourceTab);
closeTab(index); closeTab(index);
} }
void DockAreaTabBar::onCloseOtherTabsRequested(DockWidgetTab *sourceTab) void DockAreaTabBar::onCloseOtherTabsRequested(DockWidgetTab *sourceTab)
{ {
for (int i = 0; i < count(); ++i) { for (int i = 0; i < count(); ++i) {
auto currentTab = tab(i); auto currentTab = tab(i);
if (currentTab->isClosable() && !currentTab->isHidden() && currentTab != sourceTab) { if (currentTab->isClosable() && !currentTab->isHidden() && currentTab != sourceTab) {
// If the dock widget is deleted with the closeTab() call, its tab it will no longer // 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 // be in the layout, and thus the index needs to be updated to not skip any tabs.
int offset = currentTab->dockWidget()->features().testFlag( int offset = currentTab->dockWidget()->features().testFlag(
DockWidget::DockWidgetDeleteOnClose) DockWidget::DockWidgetDeleteOnClose)
? 1 ? 1
: 0; : 0;
closeTab(i); closeTab(i);
// If the the dock widget blocks closing, i.e. if the flag // If the the dock widget blocks closing, i.e. if the flag CustomCloseHandling is set,
// CustomCloseHandling is set, and the dock widget is still open, // and the dock widget is still open, then we do not need to correct the index.
// then we do not need to correct the index
if (currentTab->dockWidget()->isClosed()) if (currentTab->dockWidget()->isClosed())
i -= offset; i -= offset;
} }
} }
} }
DockWidgetTab *DockAreaTabBar::tab(int index) const void DockAreaTabBar::onTabWidgetMoved(DockWidgetTab *sourceTab, const QPoint &globalPosition)
{ {
if (index >= count() || index < 0)
return nullptr;
return qobject_cast<DockWidgetTab *>(d->m_tabsLayout->itemAt(index)->widget());
}
void DockAreaTabBar::onTabWidgetMoved(DockWidgetTab *sourceTab, const QPoint &globalPosition)
{
const int fromIndex = d->m_tabsLayout->indexOf(sourceTab); const int fromIndex = d->m_tabsLayout->indexOf(sourceTab);
auto mousePos = mapFromGlobal(globalPosition); auto mousePos = mapFromGlobal(globalPosition);
mousePos.rx() = qMax(d->firstTab()->geometry().left(), mousePos.x()); mousePos.rx() = qMax(d->firstTab()->geometry().left(), mousePos.x());
@@ -292,22 +171,148 @@ namespace ADS
// Ensure that the moved tab is reset to its start position // Ensure that the moved tab is reset to its start position
d->m_tabsLayout->update(); d->m_tabsLayout->update();
} }
} }
void DockAreaTabBar::closeTab(int index) void DockAreaTabBar::wheelEvent(QWheelEvent *event)
{ {
if (index < 0 || index >= count()) 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; return;
auto dockWidgetTab = tab(index); qCInfo(adsLog) << Q_FUNC_INFO;
if (dockWidgetTab->isHidden()) int newCurrentIndex = currentIndex();
return; int removeIndex = d->m_tabsLayout->indexOf(dockWidgetTab);
if (count() == 1)
newCurrentIndex = -1;
emit tabCloseRequested(index); 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;
}
} }
bool DockAreaTabBar::eventFilter(QObject *watched, QEvent *event) // 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());
}
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;
}
return count();
}
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); bool result = Super::eventFilter(watched, event);
DockWidgetTab *dockWidgetTab = qobject_cast<DockWidgetTab *>(watched); DockWidgetTab *dockWidgetTab = qobject_cast<DockWidgetTab *>(watched);
if (!dockWidgetTab) if (!dockWidgetTab)
@@ -322,31 +327,64 @@ namespace ADS
emit tabOpened(d->m_tabsLayout->indexOf(dockWidgetTab)); emit tabOpened(d->m_tabsLayout->indexOf(dockWidgetTab));
updateGeometry(); updateGeometry();
break; break;
// Setting the text of a tab will cause a LayoutRequest event
case QEvent::LayoutRequest:
updateGeometry();
break;
default: default:
break; break;
} }
return result; return result;
} }
bool DockAreaTabBar::isTabOpen(int index) const bool DockAreaTabBar::isTabOpen(int index) const
{ {
if (index < 0 || index >= count()) if (index < 0 || index >= count())
return false; return false;
return !tab(index)->isHidden(); return !tab(index)->isHidden();
} }
QSize DockAreaTabBar::minimumSizeHint() const QSize DockAreaTabBar::minimumSizeHint() const
{ {
QSize size = sizeHint(); QSize size = sizeHint();
size.setWidth(10); size.setWidth(10);
return size; 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::sizeHint() const emit currentChanging(index);
{ d->m_currentIndex = index;
return d->m_tabsContainerWidget->sizeHint(); d->updateTabs();
} updateGeometry();
emit currentChanged(index);
}
void DockAreaTabBar::closeTab(int index)
{
if (index < 0 || index >= count())
return;
auto dockWidgetTab = tab(index);
if (dockWidgetTab->isHidden())
return;
emit tabCloseRequested(index);
}
} // namespace ADS } // namespace ADS

View File

@@ -86,6 +86,18 @@ public:
*/ */
DockWidgetTab *tab(int index) const; 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 * Filters the tab widget events
*/ */
@@ -101,7 +113,7 @@ public:
/** /**
* Overrides the minimumSizeHint() function of QScrollArea * Overrides the minimumSizeHint() function of QScrollArea
* The minimumSizeHint() is bigger than the sizeHint () for the scroll * 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 * is reserved in the minimumSizeHint(). This override simply returns
* sizeHint(); * sizeHint();
*/ */

View File

@@ -15,10 +15,14 @@
#include "dockwidgettab.h" #include "dockwidgettab.h"
#include "floatingdockcontainer.h" #include "floatingdockcontainer.h"
#include "floatingdragpreview.h" #include "floatingdragpreview.h"
#include "iconprovider.h"
#include "autohidedockcontainer.h"
#include "dockfocuscontroller.h"
#include "elidinglabel.h"
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <QApplication>
#include <QBoxLayout> #include <QBoxLayout>
#include <QLoggingCategory> #include <QLoggingCategory>
#include <QMenu> #include <QMenu>
@@ -29,22 +33,27 @@
#include <iostream> #include <iostream>
namespace ADS namespace ADS {
{
/** static const char *const g_locationProperty = "Location";
/**
* Private data class of DockAreaTitleBar class (pimpl) * Private data class of DockAreaTitleBar class (pimpl)
*/ */
class DockAreaTitleBarPrivate class DockAreaTitleBarPrivate
{ {
public: public:
DockAreaTitleBar *q; DockAreaTitleBar *q;
QPointer<TitleBarButtonType> m_tabsMenuButton; QPointer<TitleBarButton> m_tabsMenuButton;
QPointer<TitleBarButtonType> m_undockButton; QPointer<TitleBarButton> m_autoHideButton;
QPointer<TitleBarButtonType> m_closeButton; QPointer<TitleBarButton> m_undockButton;
QPointer<TitleBarButton> m_closeButton;
QBoxLayout *m_layout = nullptr; QBoxLayout *m_layout = nullptr;
DockAreaWidget *m_dockArea = nullptr; DockAreaWidget *m_dockArea = nullptr;
DockAreaTabBar *m_tabBar = nullptr; DockAreaTabBar *m_tabBar = nullptr;
ElidingLabel *m_autoHideTitleLabel;
bool m_menuOutdated = true; bool m_menuOutdated = true;
QMenu *m_tabsMenu;
QList<TitleBarButtonType *> m_dockWidgetActionsButtons; QList<TitleBarButtonType *> m_dockWidgetActionsButtons;
QPoint m_dragStartMousePos; QPoint m_dragStartMousePos;
@@ -61,6 +70,11 @@ namespace ADS
*/ */
void createButtons(); void createButtons();
/**
* Creates the auto hide title label, only displayed when the dock area is overlayed
*/
void createAutoHideTitleLabel();
/** /**
* Creates the internal TabBar * Creates the internal TabBar
*/ */
@@ -80,12 +94,20 @@ namespace ADS
return DockManager::testConfigFlag(flag); return DockManager::testConfigFlag(flag);
} }
/**
* Returns true if the given config flag is set
* Convenience function to ease config flag testing
*/
static bool testAutoHideConfigFlag(DockManager::eAutoHideFlag flag)
{
return DockManager::testAutoHideConfigFlag(flag);
}
/** /**
* Test function for current drag state * Test function for current drag state
*/ */
bool isDraggingState(eDragState dragState) const { return this->m_dragState == dragState; } bool isDraggingState(eDragState dragState) const { return this->m_dragState == dragState; }
/** /**
* Starts floating * Starts floating
*/ */
@@ -95,14 +117,29 @@ namespace ADS
* Makes the dock area floating * Makes the dock area floating
*/ */
AbstractFloatingWidget *makeAreaFloating(const QPoint &offset, eDragState dragState); AbstractFloatingWidget *makeAreaFloating(const QPoint &offset, eDragState dragState);
}; // class DockAreaTitleBarPrivate
DockAreaTitleBarPrivate::DockAreaTitleBarPrivate(DockAreaTitleBar *parent) /**
: q(parent) * Helper function to create and initialize the menu entries for
{} * the "Auto Hide Group To..." menu
*/
void DockAreaTitleBarPrivate::createButtons() QAction *createAutoHideToAction(const QString &title, SideBarLocation location, QMenu *menu)
{ {
auto action = menu->addAction(title);
action->setProperty("Location", location);
QObject::connect(action,
&QAction::triggered,
q,
&DockAreaTitleBar::onAutoHideToActionClicked);
return action;
}
}; // class DockAreaTitleBarPrivate
DockAreaTitleBarPrivate::DockAreaTitleBarPrivate(DockAreaTitleBar *parent)
: q(parent)
{}
void DockAreaTitleBarPrivate::createButtons()
{
const QSize iconSize(11, 11); const QSize iconSize(11, 11);
const QSize buttonSize(17, 17); const QSize buttonSize(17, 17);
QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
@@ -148,13 +185,28 @@ namespace ADS
q, q,
&DockAreaTitleBar::onUndockButtonClicked); &DockAreaTitleBar::onUndockButtonClicked);
// AutoHide Button
const auto autoHideEnabled = testAutoHideConfigFlag(DockManager::AutoHideFeatureEnabled);
m_autoHideButton = new TitleBarButton(
testAutoHideConfigFlag(DockManager::DockAreaHasAutoHideButton) && autoHideEnabled);
m_autoHideButton->setObjectName("dockAreaAutoHideButton");
//m_autoHideButton->setAutoRaise(true);
internal::setToolTip(m_autoHideButton, q->titleBarButtonToolTip(TitleBarButtonAutoHide));
internal::setButtonIcon(m_autoHideButton, QStyle::SP_DialogOkButton, ADS::AutoHideIcon);
m_autoHideButton->setSizePolicy(sizePolicy);
m_autoHideButton->setCheckable(testAutoHideConfigFlag(DockManager::AutoHideButtonCheckable));
m_autoHideButton->setChecked(false);
m_layout->addWidget(m_autoHideButton, 0);
QObject::connect(m_autoHideButton,
&QToolButton::clicked,
q,
&DockAreaTitleBar::onAutoHideButtonClicked);
// Close button // Close button
m_closeButton = new TitleBarButton(testConfigFlag(DockManager::DockAreaHasCloseButton)); m_closeButton = new TitleBarButton(testConfigFlag(DockManager::DockAreaHasCloseButton));
m_closeButton->setObjectName("dockAreaCloseButton"); m_closeButton->setObjectName("dockAreaCloseButton");
//m_closeButton->setAutoRaise(true); //m_closeButton->setAutoRaise(true);
internal::setButtonIcon(m_closeButton, internal::setButtonIcon(m_closeButton, QStyle::SP_TitleBarCloseButton, ADS::DockAreaCloseIcon);
QStyle::SP_TitleBarCloseButton,
ADS::DockAreaCloseIcon);
if (testConfigFlag(DockManager::DockAreaCloseButtonClosesTab)) if (testConfigFlag(DockManager::DockAreaCloseButtonClosesTab))
internal::setToolTip(m_closeButton, Tr::tr("Close Active Tab")); internal::setToolTip(m_closeButton, Tr::tr("Close Active Tab"));
else else
@@ -170,10 +222,16 @@ namespace ADS
&DockAreaTitleBar::onCloseButtonClicked); &DockAreaTitleBar::onCloseButtonClicked);
m_layout->addSpacing(1); m_layout->addSpacing(1);
} }
void DockAreaTitleBarPrivate::createTabBar() void DockAreaTitleBarPrivate::createAutoHideTitleLabel()
{ {
m_autoHideTitleLabel = new ElidingLabel("");
m_autoHideTitleLabel->setObjectName("autoHideTitleLabel");
m_layout->addWidget(m_autoHideTitleLabel);
}
void DockAreaTitleBarPrivate::createTabBar()
{
m_tabBar = componentsFactory()->createDockAreaTabBar(m_dockArea); m_tabBar = componentsFactory()->createDockAreaTabBar(m_dockArea);
m_tabBar->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred); m_tabBar->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
m_layout->addWidget(m_tabBar); m_layout->addWidget(m_tabBar);
@@ -201,26 +259,24 @@ namespace ADS
&DockAreaTabBar::currentChanged, &DockAreaTabBar::currentChanged,
q, q,
&DockAreaTitleBar::onCurrentTabChanged); &DockAreaTitleBar::onCurrentTabChanged);
QObject::connect(m_tabBar, QObject::connect(m_tabBar, &DockAreaTabBar::tabBarClicked, q, &DockAreaTitleBar::tabBarClicked);
&DockAreaTabBar::tabBarClicked,
q,
&DockAreaTitleBar::tabBarClicked);
QObject::connect(m_tabBar, QObject::connect(m_tabBar,
&DockAreaTabBar::elidedChanged, &DockAreaTabBar::elidedChanged,
q, q,
&DockAreaTitleBar::markTabsMenuOutdated); &DockAreaTitleBar::markTabsMenuOutdated);
} }
AbstractFloatingWidget *DockAreaTitleBarPrivate::makeAreaFloating(const QPoint &offset, AbstractFloatingWidget *DockAreaTitleBarPrivate::makeAreaFloating(const QPoint &offset,
eDragState dragState) eDragState dragState)
{ {
QSize size = m_dockArea->size(); QSize size = m_dockArea->size();
m_dragState = dragState; m_dragState = dragState;
bool opaqueUndocking = DockManager::testConfigFlag(DockManager::OpaqueUndocking) bool createFloatingDockContainer = (DraggingFloatingWidget != dragState);
|| (DraggingFloatingWidget != dragState);
FloatingDockContainer *floatingDockContainer = nullptr; FloatingDockContainer *floatingDockContainer = nullptr;
AbstractFloatingWidget *floatingWidget; AbstractFloatingWidget *floatingWidget;
if (opaqueUndocking) { if (createFloatingDockContainer) {
if (m_dockArea->autoHideDockContainer())
m_dockArea->autoHideDockContainer()->cleanupAndDelete();
floatingWidget = floatingDockContainer = new FloatingDockContainer(m_dockArea); floatingWidget = floatingDockContainer = new FloatingDockContainer(m_dockArea);
} else { } else {
auto w = new FloatingDragPreview(m_dockArea); auto w = new FloatingDragPreview(m_dockArea);
@@ -239,23 +295,30 @@ namespace ADS
} }
return floatingWidget; return floatingWidget;
} }
void DockAreaTitleBarPrivate::startFloating(const QPoint &offset)
{
if (m_dockArea->autoHideDockContainer())
m_dockArea->autoHideDockContainer()->hide();
void DockAreaTitleBarPrivate::startFloating(const QPoint &offset)
{
m_floatingWidget = makeAreaFloating(offset, DraggingFloatingWidget); m_floatingWidget = makeAreaFloating(offset, DraggingFloatingWidget);
} qApp->postEvent(m_dockArea, new QEvent((QEvent::Type) internal::g_dockedWidgetDragStartEvent));
}
TitleBarButton::TitleBarButton(bool visible, QWidget *parent) TitleBarButton::TitleBarButton(bool showInTitleBar, QWidget *parent)
: TitleBarButtonType(parent) : TitleBarButtonType(parent)
, m_visible(visible) , m_showInTitleBar(showInTitleBar)
, m_hideWhenDisabled(DockAreaTitleBarPrivate::testConfigFlag(DockManager::DockAreaHideDisabledButtons)) , m_hideWhenDisabled(
{} DockAreaTitleBarPrivate::testConfigFlag(DockManager::DockAreaHideDisabledButtons))
{
setFocusPolicy(Qt::NoFocus);
}
void TitleBarButton::setVisible(bool visible) void TitleBarButton::setVisible(bool visible)
{ {
// 'visible' can stay 'true' if and only if this button is configured to generaly visible: // 'visible' can stay 'true' if and only if this button is configured to generally visible:
visible = visible && m_visible; visible = visible && m_showInTitleBar;
// 'visible' can stay 'true' unless: this button is configured to be invisible when it // 'visible' can stay 'true' unless: this button is configured to be invisible when it
// is disabled and it is currently disabled: // is disabled and it is currently disabled:
@@ -263,31 +326,40 @@ namespace ADS
visible = isEnabled(); visible = isEnabled();
Super::setVisible(visible); Super::setVisible(visible);
} }
bool TitleBarButton::event(QEvent *event) void TitleBarButton::setShowInTitleBar(bool show)
{ {
m_showInTitleBar = show;
if (!show)
setVisible(false);
}
bool TitleBarButton::event(QEvent *event)
{
if (QEvent::EnabledChange == event->type() && m_hideWhenDisabled) { if (QEvent::EnabledChange == event->type() && m_hideWhenDisabled) {
// force setVisible() call // force setVisible() call
// Calling setVisible() directly here doesn't work well when button is expected to be shown first time // Calling setVisible() directly here doesn't work well when button is expected to be shown first time
const bool visible = isEnabled(); const bool visible = isEnabled();
QMetaObject::invokeMethod(this, [this, visible] { setVisible(visible); }, Qt::QueuedConnection); QMetaObject::invokeMethod(
this, [this, visible] { setVisible(visible); }, Qt::QueuedConnection);
} }
return Super::event(event); return Super::event(event);
} }
SpacerWidget::SpacerWidget(QWidget *parent) SpacerWidget::SpacerWidget(QWidget *parent)
: QWidget(parent) : QWidget(parent)
{ {
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
setStyleSheet("border: none; background: none;"); setStyleSheet("border: none; background: none;");
} }
DockAreaTitleBar::DockAreaTitleBar(DockAreaWidget *parent) DockAreaTitleBar::DockAreaTitleBar(DockAreaWidget *parent)
: QFrame(parent) : QFrame(parent)
, d(new DockAreaTitleBarPrivate(this)) , d(new DockAreaTitleBarPrivate(this))
{ {
d->m_dockArea = parent; d->m_dockArea = parent;
setObjectName("dockAreaTitleBar"); setObjectName("dockAreaTitleBar");
@@ -298,11 +370,15 @@ namespace ADS
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
d->createTabBar(); d->createTabBar();
d->createAutoHideTitleLabel();
d->m_autoHideTitleLabel->setVisible(false); // Default hidden
d->m_layout->addWidget(new SpacerWidget(this)); d->m_layout->addWidget(new SpacerWidget(this));
d->createButtons(); d->createButtons();
} setFocusPolicy(Qt::NoFocus);
}
DockAreaTitleBar::~DockAreaTitleBar() { DockAreaTitleBar::~DockAreaTitleBar()
{
if (!d->m_closeButton.isNull()) if (!d->m_closeButton.isNull())
delete d->m_closeButton; delete d->m_closeButton;
@@ -313,32 +389,39 @@ namespace ADS
delete d->m_undockButton; delete d->m_undockButton;
delete d; delete d;
} }
DockAreaTabBar *DockAreaTitleBar::tabBar() const { return d->m_tabBar; } DockAreaTabBar *DockAreaTitleBar::tabBar() const
{
return d->m_tabBar;
}
void DockAreaTitleBar::markTabsMenuOutdated() { void DockAreaTitleBar::markTabsMenuOutdated()
if (DockAreaTitleBarPrivate::testConfigFlag(DockManager::DockAreaDynamicTabsMenuButtonVisibility)) { {
if (DockAreaTitleBarPrivate::testConfigFlag(
DockManager::DockAreaDynamicTabsMenuButtonVisibility)) {
bool hasElidedTabTitle = false; bool hasElidedTabTitle = false;
for (int i = 0; i < d->m_tabBar->count(); ++i) { for (int i = 0; i < d->m_tabBar->count(); ++i) {
if (!d->m_tabBar->isTabOpen(i)) if (!d->m_tabBar->isTabOpen(i))
continue; continue;
DockWidgetTab* tab = d->m_tabBar->tab(i); DockWidgetTab *tab = d->m_tabBar->tab(i);
if (tab->isTitleElided()) { if (tab->isTitleElided()) {
hasElidedTabTitle = true; hasElidedTabTitle = true;
break; break;
} }
} }
const bool visible = (hasElidedTabTitle && (d->m_tabBar->count() > 1)); const bool visible = (hasElidedTabTitle && (d->m_tabBar->count() > 1));
QMetaObject::invokeMethod(this, [this, visible] { QMetaObject::invokeMethod(
d->m_tabsMenuButton->setVisible(visible); }, Qt::QueuedConnection); this,
[this, visible] { d->m_tabsMenuButton->setVisible(visible); },
Qt::QueuedConnection);
} }
d->m_menuOutdated = true; d->m_menuOutdated = true;
} }
void DockAreaTitleBar::onTabsMenuAboutToShow() void DockAreaTitleBar::onTabsMenuAboutToShow()
{ {
if (!d->m_menuOutdated) if (!d->m_menuOutdated)
return; return;
@@ -355,35 +438,42 @@ namespace ADS
} }
d->m_menuOutdated = false; d->m_menuOutdated = false;
} }
void DockAreaTitleBar::onCloseButtonClicked() void DockAreaTitleBar::onCloseButtonClicked()
{ {
qCInfo(adsLog) << Q_FUNC_INFO; qCInfo(adsLog) << Q_FUNC_INFO;
if (DockAreaTitleBarPrivate::testConfigFlag(DockManager::DockAreaCloseButtonClosesTab)) if (DockManager::testAutoHideConfigFlag(DockManager::AutoHideCloseButtonCollapsesDock)
&& d->m_dockArea->autoHideDockContainer())
d->m_dockArea->autoHideDockContainer()->collapseView(true);
else if (DockAreaTitleBarPrivate::testConfigFlag(DockManager::DockAreaCloseButtonClosesTab))
d->m_tabBar->closeTab(d->m_tabBar->currentIndex()); d->m_tabBar->closeTab(d->m_tabBar->currentIndex());
else else
d->m_dockArea->closeArea(); d->m_dockArea->closeArea();
} }
void DockAreaTitleBar::onUndockButtonClicked() void DockAreaTitleBar::onUndockButtonClicked()
{ {
if (d->m_dockArea->features().testFlag(DockWidget::DockWidgetFloatable)) if (d->m_dockArea->features().testFlag(DockWidget::DockWidgetFloatable))
d->makeAreaFloating(mapFromGlobal(QCursor::pos()), DraggingInactive); d->makeAreaFloating(mapFromGlobal(QCursor::pos()), DraggingInactive);
} }
void DockAreaTitleBar::onTabsMenuActionTriggered(QAction *action) void DockAreaTitleBar::onTabsMenuActionTriggered(QAction *action)
{ {
int index = action->data().toInt(); int index = action->data().toInt();
d->m_tabBar->setCurrentIndex(index); d->m_tabBar->setCurrentIndex(index);
emit tabBarClicked(index); emit tabBarClicked(index);
} }
void DockAreaTitleBar::updateDockWidgetActionsButtons() void DockAreaTitleBar::updateDockWidgetActionsButtons()
{ {
QTC_ASSERT(d->m_tabBar->currentTab(), return ); auto tab = d->m_tabBar->currentTab();
QTC_ASSERT(d->m_tabBar->currentTab()->dockWidget(), return ); if (!tab)
DockWidget* dockWidget = d->m_tabBar->currentTab()->dockWidget(); return;
QTC_ASSERT(tab, return);
QTC_ASSERT(tab->dockWidget(), return);
DockWidget *dockWidget = tab->dockWidget();
if (!d->m_dockWidgetActionsButtons.isEmpty()) { if (!d->m_dockWidgetActionsButtons.isEmpty()) {
for (auto button : std::as_const(d->m_dockWidgetActionsButtons)) { for (auto button : std::as_const(d->m_dockWidgetActionsButtons)) {
d->m_layout->removeWidget(button); d->m_layout->removeWidget(button);
@@ -406,10 +496,10 @@ namespace ADS
d->m_layout->insertWidget(insertIndex++, button, 0); d->m_layout->insertWidget(insertIndex++, button, 0);
d->m_dockWidgetActionsButtons.append(button); d->m_dockWidgetActionsButtons.append(button);
} }
} }
void DockAreaTitleBar::onCurrentTabChanged(int index) void DockAreaTitleBar::onCurrentTabChanged(int index)
{ {
if (index < 0) if (index < 0)
return; return;
@@ -420,10 +510,30 @@ namespace ADS
} }
updateDockWidgetActionsButtons(); updateDockWidgetActionsButtons();
} }
QAbstractButton *DockAreaTitleBar::button(eTitleBarButton which) const void DockAreaTitleBar::onAutoHideButtonClicked()
{ {
if (DockManager::testAutoHideConfigFlag(DockManager::AutoHideButtonTogglesArea)
|| qApp->keyboardModifiers().testFlag(Qt::ControlModifier))
d->m_dockArea->toggleAutoHide();
else
d->m_dockArea->currentDockWidget()->toggleAutoHide();
}
void DockAreaTitleBar::onAutoHideDockAreaActionClicked()
{
d->m_dockArea->toggleAutoHide();
}
void DockAreaTitleBar::onAutoHideToActionClicked()
{
int location = sender()->property(g_locationProperty).toInt();
d->m_dockArea->toggleAutoHide((SideBarLocation) location);
}
TitleBarButton *DockAreaTitleBar::button(eTitleBarButton which) const
{
switch (which) { switch (which) {
case TitleBarButtonTabsMenu: case TitleBarButtonTabsMenu:
return d->m_tabsMenuButton; return d->m_tabsMenuButton;
@@ -431,49 +541,56 @@ namespace ADS
return d->m_undockButton; return d->m_undockButton;
case TitleBarButtonClose: case TitleBarButtonClose:
return d->m_closeButton; return d->m_closeButton;
case TitleBarButtonAutoHide:
return d->m_autoHideButton;
} }
return nullptr; return nullptr;
} }
void DockAreaTitleBar::setVisible(bool visible) ElidingLabel *DockAreaTitleBar::autoHideTitleLabel() const
{ {
return d->m_autoHideTitleLabel;
}
void DockAreaTitleBar::setVisible(bool visible)
{
Super::setVisible(visible); Super::setVisible(visible);
markTabsMenuOutdated(); markTabsMenuOutdated();
} }
void DockAreaTitleBar::mousePressEvent(QMouseEvent *event)
void DockAreaTitleBar::mousePressEvent(QMouseEvent *event) {
{
if (event->button() == Qt::LeftButton) { if (event->button() == Qt::LeftButton) {
event->accept(); event->accept();
d->m_dragStartMousePos = event->pos(); d->m_dragStartMousePos = event->pos();
d->m_dragState = DraggingMousePressed; d->m_dragState = DraggingMousePressed;
if (DockManager::testConfigFlag(DockManager::FocusHighlighting)) if (DockManager::testConfigFlag(DockManager::FocusHighlighting))
d->m_tabBar->currentTab()->setFocus(Qt::OtherFocusReason); d->dockManager()->dockFocusController()->setDockWidgetTabFocused(
d->m_tabBar->currentTab());
return; return;
} }
Super::mousePressEvent(event); Super::mousePressEvent(event);
} }
void DockAreaTitleBar::mouseReleaseEvent(QMouseEvent *event) void DockAreaTitleBar::mouseReleaseEvent(QMouseEvent *event)
{ {
if (event->button() == Qt::LeftButton) { if (event->button() == Qt::LeftButton) {
qCInfo(adsLog) << Q_FUNC_INFO; qCInfo(adsLog) << Q_FUNC_INFO;
event->accept(); event->accept();
auto CurrentDragState = d->m_dragState; auto currentDragState = d->m_dragState;
d->m_dragStartMousePos = QPoint(); d->m_dragStartMousePos = QPoint();
d->m_dragState = DraggingInactive; d->m_dragState = DraggingInactive;
if (DraggingFloatingWidget == CurrentDragState) if (DraggingFloatingWidget == currentDragState)
d->m_floatingWidget->finishDragging(); d->m_floatingWidget->finishDragging();
return; return;
} }
Super::mouseReleaseEvent(event); Super::mouseReleaseEvent(event);
} }
void DockAreaTitleBar::mouseMoveEvent(QMouseEvent *event) void DockAreaTitleBar::mouseMoveEvent(QMouseEvent *event)
{ {
Super::mouseMoveEvent(event); Super::mouseMoveEvent(event);
if (!(event->buttons() & Qt::LeftButton) || d->isDraggingState(DraggingInactive)) { if (!(event->buttons() & Qt::LeftButton) || d->isDraggingState(DraggingInactive)) {
d->m_dragState = DraggingInactive; d->m_dragState = DraggingInactive;
@@ -489,18 +606,17 @@ namespace ADS
// If this is the last dock area in a floating dock container it does not make // If this is the last dock area in a floating dock container it does not make
// sense to move it to a new floating widget and leave this one empty // sense to move it to a new floating widget and leave this one empty
if (d->m_dockArea->dockContainer()->isFloating() if (d->m_dockArea->dockContainer()->isFloating()
&& d->m_dockArea->dockContainer()->visibleDockAreaCount() == 1) { && d->m_dockArea->dockContainer()->visibleDockAreaCount() == 1
&& !d->m_dockArea->isAutoHide()) {
return; return;
} }
// If one single dock widget in this area is not floatable then the whole // If one single dock widget in this area is not floatable then the whole
// area is not floatable // area is not floatable
// If we do non opaque undocking, then we can create the floating drag // We can create the floating drag preview if the dock widget is movable
// preview if the dock widget is movable
auto features = d->m_dockArea->features(); auto features = d->m_dockArea->features();
if (!features.testFlag(DockWidget::DockWidgetFloatable) if (!features.testFlag(DockWidget::DockWidgetFloatable)
&& !(features.testFlag(DockWidget::DockWidgetMovable) && !(features.testFlag(DockWidget::DockWidgetMovable))) {
&& !DockManager::testConfigFlag(DockManager::OpaqueUndocking))) {
return; return;
} }
@@ -513,10 +629,10 @@ namespace ADS
} }
return; return;
} }
void DockAreaTitleBar::mouseDoubleClickEvent(QMouseEvent *event) void DockAreaTitleBar::mouseDoubleClickEvent(QMouseEvent *event)
{ {
// If this is the last dock area in a dock container it does not make // If this is the last dock area in a dock container it does not make
// sense to move it to a new floating widget and leave this one empty // sense to move it to a new floating widget and leave this one empty
if (d->m_dockArea->dockContainer()->isFloating() if (d->m_dockArea->dockContainer()->isFloating()
@@ -527,34 +643,117 @@ namespace ADS
return; return;
d->makeAreaFloating(event->pos(), DraggingInactive); d->makeAreaFloating(event->pos(), DraggingInactive);
} }
void DockAreaTitleBar::contextMenuEvent(QContextMenuEvent *event) void DockAreaTitleBar::contextMenuEvent(QContextMenuEvent *event)
{ {
event->accept(); event->accept();
if (d->isDraggingState(DraggingFloatingWidget)) if (d->isDraggingState(DraggingFloatingWidget))
return; return;
const bool isAutoHide = d->m_dockArea->isAutoHide();
const bool isTopLevelArea = d->m_dockArea->isTopLevelArea();
QMenu menu(this); QMenu menu(this);
auto action = menu.addAction(Tr::tr("Detach Area"), if (!isTopLevelArea) {
QAction *detachAction = menu.addAction(isAutoHide ? tr("Detach") : tr("Detach Group"));
detachAction->connect(detachAction,
&QAction::triggered,
this, this,
&DockAreaTitleBar::onUndockButtonClicked); &DockAreaTitleBar::onUndockButtonClicked);
action->setEnabled(d->m_dockArea->features().testFlag(DockWidget::DockWidgetFloatable)); detachAction->setEnabled(
d->m_dockArea->features().testFlag(DockWidget::DockWidgetFloatable));
if (DockManager::testAutoHideConfigFlag(DockManager::AutoHideFeatureEnabled)) {
QAction *pinAction = menu.addAction(isAutoHide ? tr("Unpin (Dock)") : tr("Pin Group"));
pinAction->connect(pinAction,
&QAction::triggered,
this,
&DockAreaTitleBar::onAutoHideDockAreaActionClicked);
auto areaIsPinnable = d->m_dockArea->features().testFlag(DockWidget::DockWidgetPinnable);
pinAction->setEnabled(areaIsPinnable);
if (!isAutoHide) {
auto tmp = menu.addMenu(tr("Pin Group To..."));
tmp->setEnabled(areaIsPinnable);
d->createAutoHideToAction(tr("Top"), SideBarTop, tmp);
d->createAutoHideToAction(tr("Left"), SideBarLeft, tmp);
d->createAutoHideToAction(tr("Right"), SideBarRight, tmp);
d->createAutoHideToAction(tr("Bottom"), SideBarBottom, tmp);
}
}
menu.addSeparator(); menu.addSeparator();
action = menu.addAction(Tr::tr("Close Area"), this, &DockAreaTitleBar::onCloseButtonClicked); }
action->setEnabled(d->m_dockArea->features().testFlag(DockWidget::DockWidgetClosable)); QAction *closeAction = menu.addAction(isAutoHide ? tr("Close") : tr("Close Group"));
menu.addAction(Tr::tr("Close Other Areas"), d->m_dockArea, &DockAreaWidget::closeOtherAreas); closeAction->connect(closeAction,
&QAction::triggered,
this,
&DockAreaTitleBar::onCloseButtonClicked);
closeAction->setEnabled(d->m_dockArea->features().testFlag(DockWidget::DockWidgetClosable));
if (!isAutoHide && !isTopLevelArea) {
QAction *closeOthersAction = menu.addAction(tr("Close Other Groups"));
closeOthersAction->connect(closeOthersAction,
&QAction::triggered,
d->m_dockArea,
&DockAreaWidget::closeOtherAreas);
}
menu.exec(event->globalPos()); menu.exec(event->globalPos());
} }
void DockAreaTitleBar::insertWidget(int index, QWidget *widget) void DockAreaTitleBar::insertWidget(int index, QWidget *widget)
{ {
d->m_layout->insertWidget(index, widget); d->m_layout->insertWidget(index, widget);
} }
int DockAreaTitleBar::indexOf(QWidget *widget) const int DockAreaTitleBar::indexOf(QWidget *widget) const
{ {
return d->m_layout->indexOf(widget); return d->m_layout->indexOf(widget);
}
QString DockAreaTitleBar::titleBarButtonToolTip(eTitleBarButton button) const
{
switch (button) {
case TitleBarButtonAutoHide:
if (d->m_dockArea->isAutoHide())
return tr("Unpin (Dock)");
if (DockManager::testAutoHideConfigFlag(DockManager::AutoHideButtonTogglesArea))
return tr("Pin Group");
else
return tr("Pin Active Tab (Press Ctrl to Pin Group)");
break;
case TitleBarButtonClose:
if (d->m_dockArea->isAutoHide())
return tr("Close");
if (DockManager::testConfigFlag(DockManager::DockAreaCloseButtonClosesTab))
return tr("Close Active Tab");
else
return tr("Close Group");
break;
default:
break;
} }
return QString();
}
void DockAreaTitleBar::setAreaFloating()
{
// If this is the last dock area in a dock container it does not make sense to move it to
// a new floating widget and leave this one empty.
auto dockContainer = d->m_dockArea->dockContainer();
if (dockContainer->isFloating() && dockContainer->dockAreaCount() == 1
&& !d->m_dockArea->isAutoHide())
return;
if (!d->m_dockArea->features().testFlag(DockWidget::DockWidgetFloatable))
return;
d->makeAreaFloating(mapFromGlobal(QCursor::pos()), DraggingInactive);
}
} // namespace ADS } // namespace ADS

View File

@@ -17,6 +17,7 @@ namespace ADS {
class DockAreaTabBar; class DockAreaTabBar;
class DockAreaWidget; class DockAreaWidget;
class DockAreaTitleBarPrivate; class DockAreaTitleBarPrivate;
class ElidingLabel;
using TitleBarButtonType = QToolButton; using TitleBarButtonType = QToolButton;
@@ -29,17 +30,22 @@ using TitleBarButtonType = QToolButton;
class TitleBarButton : public TitleBarButtonType class TitleBarButton : public TitleBarButtonType
{ {
Q_OBJECT Q_OBJECT
bool m_visible = true; bool m_showInTitleBar = true;
bool m_hideWhenDisabled = false; bool m_hideWhenDisabled = false;
public: public:
using Super = TitleBarButtonType; 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: * Adjust this visibility change request with our internal settings:
*/ */
void setVisible(bool visible) override; void setVisible(bool visible) override;
/**
* Configures, if the title bar button should be shown in title bar
*/
void setShowInTitleBar(bool show);
protected: protected:
/** /**
* Handle EnabledChanged signal to set button invisible if the configured * Handle EnabledChanged signal to set button invisible if the configured
@@ -81,6 +87,9 @@ private:
void onUndockButtonClicked(); void onUndockButtonClicked();
void onTabsMenuActionTriggered(QAction *action); void onTabsMenuActionTriggered(QAction *action);
void onCurrentTabChanged(int index); void onCurrentTabChanged(int index);
void onAutoHideButtonClicked();
void onAutoHideDockAreaActionClicked();
void onAutoHideToActionClicked();
protected: protected:
/** /**
@@ -135,7 +144,12 @@ public:
/** /**
* Returns the button corresponding to the given title bar button identifier * 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 * Updates the visibility of the dock widget actions in the title bar
@@ -166,6 +180,17 @@ public:
*/ */
int indexOf(QWidget *widget) const; 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: signals:
/** /**
* This signal is emitted if a tab in the tab bar is clicked by the user * 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

View File

@@ -4,6 +4,7 @@
#pragma once #pragma once
#include "ads_globals.h" #include "ads_globals.h"
#include "autohidetab.h"
#include "dockwidget.h" #include "dockwidget.h"
#include <QFrame> #include <QFrame>
@@ -20,6 +21,8 @@ class DockManager;
class DockContainerWidget; class DockContainerWidget;
class DockContainerWidgetPrivate; class DockContainerWidgetPrivate;
class DockAreaTitleBar; class DockAreaTitleBar;
class DockingStateReader;
class AutoHideDockContainer;
/** /**
* DockAreaWidget manages multiple instances of DockWidgets. * DockAreaWidget manages multiple instances of DockWidgets.
@@ -39,6 +42,9 @@ private:
friend class DockWidget; friend class DockWidget;
friend class DockManagerPrivate; friend class DockManagerPrivate;
friend class DockManager; friend class DockManager;
friend class AutoHideDockContainer;
void onDockWidgetFeaturesChanged();
void onTabCloseRequested(int index); void onTabCloseRequested(int index);
@@ -48,7 +54,34 @@ private:
*/ */
void reorderDockWidget(int fromIndex, int toIndex); 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: 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. * Inserts a dock widget into dock area.
* All dockwidgets in the dock area tabified in a stacked layout with tabs. * All dockwidgets in the dock area tabified in a stacked layout with tabs.
@@ -110,11 +143,26 @@ protected:
*/ */
void markTitleBarMenuOutdated(); 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); void toggleView(bool open);
public: public:
using Super = QFrame; using Super = QFrame;
/**
* Dock area related flags
*/
enum eDockAreaFlag
{
HideSingleWidgetTitleBar = 0x0001,
DefaultFlags = 0x0000
};
Q_DECLARE_FLAGS(DockAreaFlags, eDockAreaFlag)
/** /**
* Default Constructor * Default Constructor
*/ */
@@ -135,6 +183,27 @@ public:
* if there is no * if there is no
*/ */
DockContainerWidget *dockContainer() const; 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 * Returns the rectangle of the title area
@@ -205,6 +274,12 @@ public:
*/ */
void saveState(QXmlStreamWriter &stream) const; 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 functions returns the dock widget features of all dock widget in
* this area. * this area.
@@ -242,6 +317,41 @@ public:
*/ */
DockAreaTitleBar *titleBar() const; 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. * This activates the tab for the given tab index.
* If the dock widget for the given tab is not visible, the this function * If the dock widget for the given tab is not visible, the this function
@@ -254,17 +364,28 @@ public:
*/ */
void closeArea(); 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 * This function closes all other areas except of this area
*/ */
void closeOtherAreas(); void closeOtherAreas();
/** /**
* Returns the largest minimumSizeHint() of the dock widgets in this * Moves the dock area into its own floating widget if the area DockWidgetFloatable flag is true.
* area.
* The minimum size hint is updated if a dock widget is removed or added.
*/ */
QSize minimumSizeHint() const override; void setFloating();
signals: signals:
/** /**

View File

@@ -3,46 +3,52 @@
#include "dockcomponentsfactory.h" #include "dockcomponentsfactory.h"
#include "dockwidgettab.h" #include "autohidetab.h"
#include "dockareatabbar.h" #include "dockareatabbar.h"
#include "dockareatitlebar.h" #include "dockareatitlebar.h"
#include "dockwidget.h"
#include "dockareawidget.h" #include "dockareawidget.h"
#include "dockwidget.h"
#include "dockwidgettab.h"
#include <memory> #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());
DockWidgetTab *DockComponentsFactory::createDockWidgetTab(DockWidget *dockWidget) const
{
return new DockWidgetTab(dockWidget); return new DockWidgetTab(dockWidget);
} }
DockAreaTabBar *DockComponentsFactory::createDockAreaTabBar(DockAreaWidget *dockArea) const AutoHideTab *DockComponentsFactory::createDockWidgetSideTab(DockWidget *dockWidget) const
{ {
return new AutoHideTab(dockWidget);
}
DockAreaTabBar *DockComponentsFactory::createDockAreaTabBar(DockAreaWidget *dockArea) const
{
return new DockAreaTabBar(dockArea); return new DockAreaTabBar(dockArea);
} }
DockAreaTitleBar *DockComponentsFactory::createDockAreaTitleBar(DockAreaWidget *dockArea) const DockAreaTitleBar *DockComponentsFactory::createDockAreaTitleBar(DockAreaWidget *dockArea) const
{ {
return new DockAreaTitleBar(dockArea); return new DockAreaTitleBar(dockArea);
} }
const DockComponentsFactory *DockComponentsFactory::factory() const DockComponentsFactory *DockComponentsFactory::factory()
{ {
return g_defaultFactory.get(); return g_defaultFactory.get();
} }
void DockComponentsFactory::setFactory(DockComponentsFactory *factory) void DockComponentsFactory::setFactory(DockComponentsFactory *factory)
{ {
g_defaultFactory.reset(factory); g_defaultFactory.reset(factory);
} }
void DockComponentsFactory::resetDefaultFactory() void DockComponentsFactory::resetDefaultFactory()
{ {
g_defaultFactory.reset(new DockComponentsFactory()); g_defaultFactory.reset(new DockComponentsFactory());
} }
} // namespace ADS } // namespace ADS

View File

@@ -12,6 +12,7 @@ class DockAreaTitleBar;
class DockAreaTabBar; class DockAreaTabBar;
class DockAreaWidget; class DockAreaWidget;
class DockWidget; class DockWidget;
class AutoHideTab;
/** /**
* Factory for creation of certain GUI elements for the docking framework. * Factory for creation of certain GUI elements for the docking framework.
@@ -37,6 +38,12 @@ public:
*/ */
virtual DockWidgetTab *createDockWidgetTab(DockWidget *dockWidget) const; 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 * This default implementation just creates a dock area tab bar with
* new DockAreaTabBar(dockArea). * new DockAreaTabBar(dockArea).

File diff suppressed because it is too large Load Diff

View File

@@ -4,6 +4,7 @@
#pragma once #pragma once
#include "ads_globals.h" #include "ads_globals.h"
#include "autohidetab.h"
#include "dockwidget.h" #include "dockwidget.h"
#include <QFrame> #include <QFrame>
@@ -24,13 +25,16 @@ class FloatingDockContainerPrivate;
class FloatingDragPreview; class FloatingDragPreview;
class DockingStateReader; class DockingStateReader;
class FloatingDragPreviewPrivate; class FloatingDragPreviewPrivate;
class AutoHideSideBar;
class AutoHideTab;
struct AutoHideTabPrivate;
struct AutoHideDockContainerPrivate;
class AutoHideDockContainer;
/** /**
* Container that manages a number of dock areas with single dock widgets * Container that manages a number of dock areas with single dock widgets or tabified dock widgets
* or tabified dock widgets in each area. * in each area. Each window that support docking has a DockContainerWidget. That means the main
* Each window that support docking has a DockContainerWidget. That means * application window and all floating windows contain a DockContainerWidget instance.
* the main application window and all floating windows contain a
* DockContainerWidget instance.
*/ */
class ADS_EXPORT DockContainerWidget : public QFrame class ADS_EXPORT DockContainerWidget : public QFrame
{ {
@@ -47,6 +51,11 @@ private:
friend class DockWidget; friend class DockWidget;
friend class FloatingDragPreview; friend class FloatingDragPreview;
friend class FloatingDragPreviewPrivate; friend class FloatingDragPreviewPrivate;
friend AutoHideDockContainer;
friend AutoHideTab;
friend AutoHideTabPrivate;
friend AutoHideDockContainerPrivate;
friend AutoHideSideBar;
protected: protected:
/** /**
@@ -59,11 +68,25 @@ protected:
*/ */
QSplitter *rootSplitter() const; 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 * Helper function for creation of the root splitter
*/ */
void createRootSplitter(); void createRootSplitter();
/**
* Helper function for creation of the side tab bar widgets
*/
void createSideTabBarWidgets();
/** /**
* Drop floating widget into the container * Drop floating widget into the container
*/ */
@@ -76,7 +99,10 @@ protected:
* a nullptr, then the DropArea indicates the drop area in the given * a nullptr, then the DropArea indicates the drop area in the given
* TargetAreaWidget * 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 * 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. * 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 * Saves the state into the given stream
@@ -134,6 +160,28 @@ protected:
*/ */
QList<DockWidget *> dockWidgets() const; 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: public:
/** /**
* Default Constructor * Default Constructor
@@ -154,7 +202,8 @@ public:
*/ */
DockAreaWidget *addDockWidget(DockWidgetArea area, DockAreaWidget *addDockWidget(DockWidgetArea area,
DockWidget *dockWidget, DockWidget *dockWidget,
DockAreaWidget *dockAreaWidget = nullptr); DockAreaWidget *dockAreaWidget = nullptr,
int index = -1);
/** /**
* Removes dockwidget * Removes dockwidget
@@ -190,6 +239,18 @@ public:
*/ */
QList<DockAreaWidget *> openedDockAreas() const; 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 * This function returns true if this dock area has only one single
* visible dock widget. * visible dock widget.
@@ -239,6 +300,34 @@ public:
*/ */
void closeOtherAreas(DockAreaWidget *keepOpenArea); 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: signals:
/** /**
* This signal is emitted if one or multiple dock areas has been added to * This signal is emitted if one or multiple dock areas has been added to
@@ -247,6 +336,11 @@ signals:
*/ */
void dockAreasAdded(); 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 * This signal is emitted if one or multiple dock areas has been removed
*/ */

View File

@@ -3,39 +3,43 @@
#include "dockfocuscontroller.h" #include "dockfocuscontroller.h"
#include "dockareawidget.h" #include "ads_globals_p.h"
#include "dockareatitlebar.h" #include "dockareatitlebar.h"
#include "dockareawidget.h"
#include "dockcontainerwidget.h" #include "dockcontainerwidget.h"
#include "dockmanager.h" #include "dockmanager.h"
#include "dockwidget.h" #include "dockwidget.h"
#include "dockwidgettab.h" #include "dockwidgettab.h"
#include "floatingdockcontainer.h" #include "floatingdockcontainer.h"
#ifdef Q_OS_LINUX #if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
#include "linux/floatingwidgettitlebar.h" #include "linux/floatingwidgettitlebar.h"
#endif #endif
#include <QApplication> #include <QApplication>
#include <QPointer> #include <QPointer>
#include <QWindow>
#include <algorithm> #include <algorithm>
#include <iostream> #include <iostream>
namespace ADS namespace ADS {
static const char *const g_focusedDockWidgetProperty = "FocusedDockWidget";
class DockFocusControllerPrivate
{ {
/** public:
* Private data class of CDockFocusController class (pimpl)
*/
class DockFocusControllerPrivate
{
public:
DockFocusController *q; DockFocusController *q;
QPointer<DockWidget> m_focusedDockWidget = nullptr; QPointer<DockWidget> m_focusedDockWidget = nullptr;
QPointer<DockAreaWidget> m_focusedArea = nullptr; QPointer<DockAreaWidget> m_focusedArea = nullptr;
#ifdef Q_OS_LINUX QPointer<DockWidget> m_oldFocusedDockWidget = nullptr;
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
QPointer<FloatingDockContainer> m_floatingWidget = nullptr; QPointer<FloatingDockContainer> m_floatingWidget = nullptr;
#endif #endif
DockManager *m_dockManager = nullptr; DockManager *m_dockManager = nullptr;
bool m_forceFocusChangedSignal = false;
bool m_tabPressed = false;
/** /**
* Private data constructor * Private data constructor
@@ -47,41 +51,56 @@ namespace ADS
* the dock area that it belongs to * the dock area that it belongs to
*/ */
void updateDockWidgetFocus(DockWidget *dockWidget); void updateDockWidgetFocus(DockWidget *dockWidget);
}; // class DockFocusControllerPrivate }; // class DockFocusControllerPrivate
static void updateDockWidgetFocusStyle(DockWidget *dockWidget, bool focused) static void updateDockWidgetFocusStyle(DockWidget *dockWidget, bool focused)
{ {
dockWidget->setProperty("focused", focused); dockWidget->setProperty("focused", focused);
dockWidget->tabWidget()->setProperty("focused", focused); dockWidget->tabWidget()->setProperty("focused", focused);
dockWidget->tabWidget()->updateStyle(); dockWidget->tabWidget()->updateStyle();
internal::repolishStyle(dockWidget); internal::repolishStyle(dockWidget);
} }
static void updateDockAreaFocusStyle(DockAreaWidget *dockArea, bool focused) static void updateDockAreaFocusStyle(DockAreaWidget *dockArea, bool focused)
{ {
dockArea->setProperty("focused", focused); dockArea->setProperty("focused", focused);
internal::repolishStyle(dockArea); internal::repolishStyle(dockArea);
internal::repolishStyle(dockArea->titleBar()); 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()); auto titleBar = qobject_cast<FloatingWidgetTitleBar *>(floatingWidget->titleBarWidget());
if (!titleBar) if (!titleBar)
return; return;
titleBar->setProperty("focused", focused); titleBar->setProperty("focused", focused);
titleBar->updateStyle(); titleBar->updateStyle();
} }
#endif #endif
DockFocusControllerPrivate::DockFocusControllerPrivate(DockFocusController *parent) DockFocusControllerPrivate::DockFocusControllerPrivate(DockFocusController *parent)
: q(parent) : q(parent)
{} {}
void DockFocusControllerPrivate::updateDockWidgetFocus(DockWidget *dockWidget)
{
if (!dockWidget->features().testFlag(DockWidget::DockWidgetFocusable))
return;
QWindow *window = nullptr;
auto dockContainer = dockWidget->dockContainer();
if (dockContainer)
window = dockContainer->window()->windowHandle();
if (window)
window->setProperty(g_focusedDockWidgetProperty,
QVariant::fromValue(QPointer<DockWidget>(dockWidget)));
void DockFocusControllerPrivate::updateDockWidgetFocus(DockWidget *dockWidget)
{
DockAreaWidget *newFocusedDockArea = nullptr; DockAreaWidget *newFocusedDockArea = nullptr;
if (m_focusedDockWidget) if (m_focusedDockWidget)
updateDockWidgetFocusStyle(m_focusedDockWidget, false); updateDockWidgetFocusStyle(m_focusedDockWidget, false);
@@ -90,163 +109,226 @@ namespace ADS
m_focusedDockWidget = dockWidget; m_focusedDockWidget = dockWidget;
updateDockWidgetFocusStyle(m_focusedDockWidget, true); updateDockWidgetFocusStyle(m_focusedDockWidget, true);
newFocusedDockArea = m_focusedDockWidget->dockAreaWidget(); newFocusedDockArea = m_focusedDockWidget->dockAreaWidget();
if (newFocusedDockArea && (m_focusedArea != newFocusedDockArea)) if (newFocusedDockArea && (m_focusedArea != newFocusedDockArea)) {
{ if (m_focusedArea) {
if (m_focusedArea) QObject::disconnect(m_focusedArea,
{ &DockAreaWidget::viewToggled,
QObject::disconnect(m_focusedArea, &DockAreaWidget::viewToggled, q,
q, &DockFocusController::onFocusedDockAreaViewToggled); &DockFocusController::onFocusedDockAreaViewToggled);
updateDockAreaFocusStyle(m_focusedArea, false); updateDockAreaFocusStyle(m_focusedArea, false);
} }
m_focusedArea = newFocusedDockArea; m_focusedArea = newFocusedDockArea;
updateDockAreaFocusStyle(m_focusedArea, true); updateDockAreaFocusStyle(m_focusedArea, true);
QObject::connect(m_focusedArea, &DockAreaWidget::viewToggled, QObject::connect(m_focusedArea,
q, &DockFocusController::onFocusedDockAreaViewToggled); &DockAreaWidget::viewToggled,
q,
&DockFocusController::onFocusedDockAreaViewToggled);
} }
auto dockContainer = m_focusedDockWidget->dockContainer();
FloatingDockContainer *newFloatingWidget = nullptr; FloatingDockContainer *newFloatingWidget = nullptr;
dockContainer = m_focusedDockWidget->dockContainer();
if (dockContainer) if (dockContainer)
newFloatingWidget = dockContainer->floatingWidget(); newFloatingWidget = dockContainer->floatingWidget();
if (newFloatingWidget) if (newFloatingWidget)
newFloatingWidget->setProperty("FocusedDockWidget", QVariant::fromValue(dockWidget)); newFloatingWidget->setProperty(g_focusedDockWidgetProperty,
QVariant::fromValue(QPointer<DockWidget>(dockWidget)));
#ifdef Q_OS_LINUX #if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
// This code is required for styling the floating widget titlebar for linux // This code is required for styling the floating widget titlebar for linux
// depending on the current focus state // depending on the current focus state
if (m_floatingWidget == newFloatingWidget) if (m_floatingWidget != newFloatingWidget) {
return; if (m_floatingWidget) {
if (m_floatingWidget)
updateFloatingWidgetFocusStyle(m_floatingWidget, false); updateFloatingWidgetFocusStyle(m_floatingWidget, false);
}
m_floatingWidget = newFloatingWidget; m_floatingWidget = newFloatingWidget;
if (m_floatingWidget) if (m_floatingWidget) {
updateFloatingWidgetFocusStyle(m_floatingWidget, true); updateFloatingWidgetFocusStyle(m_floatingWidget, true);
}
}
#endif #endif
if (old != dockWidget) if (old == dockWidget && !m_forceFocusChangedSignal)
emit m_dockManager->focusedDockWidgetChanged(old, dockWidget); return;
}
DockFocusController::DockFocusController(DockManager *dockManager) m_forceFocusChangedSignal = false;
if (dockWidget->isVisible()) {
emit m_dockManager->focusedDockWidgetChanged(old, dockWidget);
} else {
m_oldFocusedDockWidget = old;
QObject::connect(dockWidget,
&DockWidget::visibilityChanged,
q,
&DockFocusController::onDockWidgetVisibilityChanged);
}
}
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(DockManager *dockManager)
: QObject(dockManager) : QObject(dockManager)
, d(new DockFocusControllerPrivate(this)) , d(new DockFocusControllerPrivate(this))
{ {
d->m_dockManager = dockManager; d->m_dockManager = dockManager;
connect(qApp, &QApplication::focusChanged, connect(qApp,
this, &DockFocusController::onApplicationFocusChanged); &QApplication::focusChanged,
connect(d->m_dockManager, &DockManager::stateRestored, this,
this, &DockFocusController::onStateRestored); &DockFocusController::onApplicationFocusChanged);
} connect(qApp,
&QApplication::focusWindowChanged,
this,
&DockFocusController::onFocusWindowChanged);
connect(d->m_dockManager,
&DockManager::stateRestored,
this,
&DockFocusController::onStateRestored);
}
DockFocusController::~DockFocusController() DockFocusController::~DockFocusController()
{ {
delete d; delete d;
} }
void DockFocusController::onApplicationFocusChanged(QWidget *focusedOld, QWidget *focusedNow) void DockFocusController::onFocusWindowChanged(QWindow *focusWindow)
{ {
if (!focusWindow)
return;
auto dockWidgetVar = focusWindow->property(g_focusedDockWidgetProperty);
if (!dockWidgetVar.isValid())
return;
auto dockWidget = dockWidgetVar.value<QPointer<DockWidget>>();
if (!dockWidget)
return;
d->updateDockWidgetFocus(dockWidget);
}
void DockFocusController::onApplicationFocusChanged(QWidget *focusedOld, QWidget *focusedNow)
{
Q_UNUSED(focusedOld); Q_UNUSED(focusedOld);
if (d->m_dockManager->isRestoringState()) // 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; return;
qCInfo(adsLog) << Q_FUNC_INFO << "old: " << focusedOld << "new:" << focusedNow;
if (!focusedNow) if (!focusedNow)
return; return;
DockWidget *dockWidget = nullptr; DockWidget *dockWidget = qobject_cast<DockWidget *>(focusedNow);
auto dockWidgetTab = qobject_cast<DockWidgetTab *>(focusedNow);
if (dockWidgetTab)
dockWidget = dockWidgetTab->dockWidget();
if (!dockWidget)
dockWidget = qobject_cast<DockWidget *>(focusedNow);
bool focusActual = dockWidget && dockWidget->widget();
if (!dockWidget) if (!dockWidget)
dockWidget = internal::findParent<DockWidget *>(focusedNow); dockWidget = internal::findParent<DockWidget *>(focusedNow);
#ifdef Q_OS_LINUX #if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
if (!dockWidget) if (!dockWidget)
return; return;
#else #else
if (!dockWidget || dockWidget->tabWidget()->isHidden()) if (!dockWidget || dockWidget->tabWidget()->isHidden())
return; return;
#endif #endif
d->updateDockWidgetFocus(dockWidget); d->updateDockWidgetFocus(dockWidget);
}
if (focusActual) { void DockFocusController::setDockWidgetTabFocused(DockWidgetTab *tab)
// Do this async to avoid issues when changing focus in middle of another focus handling {
QMetaObject::invokeMethod(dockWidget->widget(), QOverload<>::of(&QWidget::setFocus), auto dockWidget = tab->dockWidget();
Qt::QueuedConnection); if (dockWidget)
} d->updateDockWidgetFocus(dockWidget);
} }
void DockFocusController::setDockWidgetFocused(DockWidget *focusedNow) void DockFocusController::clearDockWidgetFocus(DockWidget *dockWidget)
{ {
dockWidget->clearFocus();
updateDockWidgetFocusStyle(dockWidget, false);
}
void DockFocusController::setDockWidgetFocused(DockWidget *focusedNow)
{
d->updateDockWidgetFocus(focusedNow); d->updateDockWidgetFocus(focusedNow);
} }
void DockFocusController::onFocusedDockAreaViewToggled(bool open) void DockFocusController::onFocusedDockAreaViewToggled(bool open)
{ {
if (d->m_dockManager->isRestoringState() || !d->m_focusedArea || open) if (d->m_dockManager->isRestoringState() || !d->m_focusedArea || open)
return; return;
auto container = d->m_focusedArea->dockContainer(); DockAreaWidget *dockArea = qobject_cast<DockAreaWidget *>(sender());
if (!dockArea || open)
return;
auto container = dockArea->dockContainer();
auto openedDockAreas = container->openedDockAreas(); auto openedDockAreas = container->openedDockAreas();
if (openedDockAreas.isEmpty()) if (openedDockAreas.isEmpty())
return; return;
DockManager::setWidgetFocus(openedDockAreas[0]->currentDockWidget()->tabWidget()); d->updateDockWidgetFocus(openedDockAreas[0]->currentDockWidget());
} }
void DockFocusController::notifyWidgetOrAreaRelocation(QWidget *droppedWidget) void DockFocusController::notifyWidgetOrAreaRelocation(QWidget *droppedWidget)
{ {
if (d->m_dockManager->isRestoringState()) if (d->m_dockManager->isRestoringState())
return; return;
DockWidget *dockWidget = qobject_cast<DockWidget *>(droppedWidget); DockWidget *dockWidget = qobject_cast<DockWidget *>(droppedWidget);
if (dockWidget) if (!dockWidget) {
{ DockAreaWidget *dockArea = qobject_cast<DockAreaWidget *>(droppedWidget);
DockManager::setWidgetFocus(dockWidget->tabWidget()); if (dockArea)
return;
}
DockAreaWidget* dockArea = qobject_cast<DockAreaWidget*>(droppedWidget);
if (!dockArea)
return;
dockWidget = dockArea->currentDockWidget(); dockWidget = dockArea->currentDockWidget();
DockManager::setWidgetFocus(dockWidget->tabWidget());
} }
void DockFocusController::notifyFloatingWidgetDrop(FloatingDockContainer *floatingWidget) if (!dockWidget)
{ return;
d->m_forceFocusChangedSignal = true;
DockManager::setWidgetFocus(dockWidget);
}
void DockFocusController::notifyFloatingWidgetDrop(FloatingDockContainer *floatingWidget)
{
if (!floatingWidget || d->m_dockManager->isRestoringState()) if (!floatingWidget || d->m_dockManager->isRestoringState())
return; return;
auto vDockWidget = floatingWidget->property("FocusedDockWidget"); auto dockWidgetVar = floatingWidget->property(g_focusedDockWidgetProperty);
if (!vDockWidget.isValid()) if (!dockWidgetVar.isValid())
return; return;
auto dockWidget = vDockWidget.value<DockWidget *>(); auto dockWidget = dockWidgetVar.value<DockWidget *>();
if (dockWidget) { if (dockWidget) {
dockWidget->dockAreaWidget()->setCurrentDockWidget(dockWidget); dockWidget->dockAreaWidget()->setCurrentDockWidget(dockWidget);
DockManager::setWidgetFocus(dockWidget->tabWidget()); DockManager::setWidgetFocus(dockWidget->tabWidget());
} }
} }
void DockFocusController::onStateRestored() void DockFocusController::onStateRestored()
{ {
if (d->m_focusedDockWidget) if (d->m_focusedDockWidget)
updateDockWidgetFocusStyle(d->m_focusedDockWidget, false); updateDockWidgetFocusStyle(d->m_focusedDockWidget, false);
} }
DockWidget *DockFocusController::focusedDockWidget() const
{
return d->m_focusedDockWidget.data();
}
void DockFocusController::setDockWidgetTabPressed(bool value)
{
d->m_tabPressed = value;
}
} // namespace ADS } // namespace ADS

View File

@@ -27,8 +27,10 @@ private:
private: private:
void onApplicationFocusChanged(QWidget *old, QWidget *now); void onApplicationFocusChanged(QWidget *old, QWidget *now);
void onFocusWindowChanged(QWindow *focusWindow);
void onFocusedDockAreaViewToggled(bool open); void onFocusedDockAreaViewToggled(bool open);
void onStateRestored(); void onStateRestored();
void onDockWidgetVisibilityChanged(bool visible);
public: public:
/** /**
@@ -41,19 +43,6 @@ public:
*/ */
~DockFocusController() override; ~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 * A container needs to call this function if a widget has been dropped
* into it * into it
@@ -68,6 +57,28 @@ public:
*/ */
void notifyFloatingWidgetDrop(FloatingDockContainer *floatingWidget); 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 * Request a focus change to the given dock widget
*/ */

View File

@@ -4,12 +4,14 @@
#include "dockmanager.h" #include "dockmanager.h"
#include "ads_globals.h" #include "ads_globals.h"
#include "advanceddockingsystemtr.h"
#include "ads_globals_p.h" #include "ads_globals_p.h"
#include "advanceddockingsystemtr.h"
#include "autohidedockcontainer.h"
#include "dockareawidget.h" #include "dockareawidget.h"
#include "dockfocuscontroller.h" #include "dockfocuscontroller.h"
#include "dockingstatereader.h" #include "dockingstatereader.h"
#include "dockoverlay.h" #include "dockoverlay.h"
#include "docksplitter.h"
#include "dockwidget.h" #include "dockwidget.h"
#include "floatingdockcontainer.h" #include "floatingdockcontainer.h"
#include "iconprovider.h" #include "iconprovider.h"
@@ -40,8 +42,13 @@
#include <QMessageBox> #include <QMessageBox>
#include <QSettings> #include <QSettings>
#include <QVariant> #include <QVariant>
#include <QWindow>
#include <QXmlStreamWriter> #include <QXmlStreamWriter>
#if !(defined(Q_OS_UNIX) && !defined(Q_OS_MACOS))
#include <QWindowStateChangeEvent>
#endif
Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtWarningMsg); Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtWarningMsg);
using namespace Utils; using namespace Utils;
@@ -57,6 +64,9 @@ enum eStateFileVersion {
}; };
static DockManager::ConfigFlags g_staticConfigFlags = DockManager::DefaultNonOpaqueConfig; 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) * Private data class of DockManager class (pimpl)
@@ -66,6 +76,7 @@ class DockManagerPrivate
public: public:
DockManager *q; DockManager *q;
QList<QPointer<FloatingDockContainer>> m_floatingWidgets; QList<QPointer<FloatingDockContainer>> m_floatingWidgets;
QList<QPointer<FloatingDockContainer>> m_hiddenFloatingWidgets;
QList<DockContainerWidget *> m_containers; QList<DockContainerWidget *> m_containers;
DockOverlay *m_containerOverlay = nullptr; DockOverlay *m_containerOverlay = nullptr;
DockOverlay *m_dockAreaOverlay = nullptr; DockOverlay *m_dockAreaOverlay = nullptr;
@@ -73,6 +84,8 @@ public:
bool m_restoringState = false; bool m_restoringState = false;
QVector<FloatingDockContainer *> m_uninitializedFloatingWidgets; QVector<FloatingDockContainer *> m_uninitializedFloatingWidgets;
DockFocusController *m_focusController = nullptr; DockFocusController *m_focusController = nullptr;
DockWidget *m_centralWidget = nullptr;
bool m_isLeavingMinimized = false;
QString m_workspacePresetsPath; QString m_workspacePresetsPath;
QList<Workspace> m_workspaces; QList<Workspace> m_workspaces;
@@ -114,7 +127,7 @@ public:
void markDockWidgetsDirty() void markDockWidgetsDirty()
{ {
for (const auto &dockWidget : std::as_const(m_dockWidgetsMap)) 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; 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; bool result = true;
#ifdef ADS_DEBUG_PRINT
int dockContainers = stateReader.attributes().value("containers").toInt();
qCInfo(adsLog) << dockContainers;
#endif
int dockContainerCount = 0; int dockContainerCount = 0;
while (stateReader.readNextStartElement()) { while (stateReader.readNextStartElement()) {
if (stateReader.name() == QLatin1String("container")) { if (stateReader.name() == QLatin1String("container")) {
@@ -199,10 +228,10 @@ bool DockManagerPrivate::restoreStateFromXml(const QByteArray &state, int versio
if (!testing) { if (!testing) {
// Delete remaining empty floating widgets // Delete remaining empty floating widgets
int floatingWidgetIndex = dockContainerCount - 1; int floatingWidgetIndex = dockContainerCount - 1;
int deleteCount = m_floatingWidgets.count() - floatingWidgetIndex; for (int i = floatingWidgetIndex; i < m_floatingWidgets.count(); ++i) {
for (int i = 0; i < deleteCount; ++i) { QPointer<FloatingDockContainer> floatingWidget = m_floatingWidgets[i];
m_floatingWidgets[floatingWidgetIndex + i]->deleteLater(); q->removeDockContainer(floatingWidget->dockContainer());
q->removeDockContainer(m_floatingWidgets[floatingWidgetIndex + i]->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 // 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. // container, until the user toggles the toggle view action the next time.
for (auto dockWidget : std::as_const(m_dockWidgetsMap)) { 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(); dockWidget->flagAsUnassigned();
emit dockWidget->viewToggled(false); emit dockWidget->viewToggled(false);
} else { } 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() 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. // level changed.
for (auto dockContainer : std::as_const(m_containers)) { for (auto dockContainer : std::as_const(m_containers)) {
DockWidget *topLevelDockWidget = dockContainer->topLevelDockWidget(); DockWidget *topLevelDockWidget = dockContainer->topLevelDockWidget();
@@ -288,6 +323,7 @@ bool DockManagerPrivate::restoreState(const QByteArray &state, int version)
restoreDockWidgetsOpenState(); restoreDockWidgetsOpenState();
restoreDockAreasIndices(); restoreDockAreasIndices();
emitTopLevelEvents(); emitTopLevelEvents();
q->dumpLayout();
return true; return true;
} }
@@ -301,6 +337,7 @@ DockManager::DockManager(QWidget *parent)
}); });
createRootSplitter(); createRootSplitter();
createSideTabBarWidgets();
QMainWindow *mainWindow = qobject_cast<QMainWindow *>(parent); QMainWindow *mainWindow = qobject_cast<QMainWindow *>(parent);
if (mainWindow) if (mainWindow)
mainWindow->setCentralWidget(this); mainWindow->setCentralWidget(this);
@@ -311,6 +348,17 @@ DockManager::DockManager(QWidget *parent)
if (DockManager::configFlags().testFlag(DockManager::FocusHighlighting)) if (DockManager::configFlags().testFlag(DockManager::FocusHighlighting))
d->m_focusController = new DockFocusController(this); 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() DockManager::~DockManager()
@@ -319,6 +367,18 @@ DockManager::~DockManager()
save(); save();
saveStartupWorkspace(); 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 // Using a temporary vector since the destructor of FloatingDockWidgetContainer
// alters d->m_floatingWidgets. // alters d->m_floatingWidgets.
const auto copy = d->m_floatingWidgets; const auto copy = d->m_floatingWidgets;
@@ -334,21 +394,41 @@ DockManager::ConfigFlags DockManager::configFlags()
return g_staticConfigFlags; return g_staticConfigFlags;
} }
DockManager::AutoHideFlags DockManager::autoHideConfigFlags()
{
return g_staticAutoHideConfigFlags;
}
void DockManager::setConfigFlags(const ConfigFlags flags) void DockManager::setConfigFlags(const ConfigFlags flags)
{ {
g_staticConfigFlags = flags; g_staticConfigFlags = flags;
} }
void DockManager::setAutoHideConfigFlags(const AutoHideFlags flags)
{
g_staticAutoHideConfigFlags = flags;
}
void DockManager::setConfigFlag(eConfigFlag flag, bool on) void DockManager::setConfigFlag(eConfigFlag flag, bool on)
{ {
internal::setFlag(g_staticConfigFlags, flag, 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) bool DockManager::testConfigFlag(eConfigFlag flag)
{ {
return configFlags().testFlag(flag); return configFlags().testFlag(flag);
} }
bool DockManager::testAutoHideConfigFlag(eAutoHideFlag flag)
{
return autoHideConfigFlags().testFlag(flag);
}
IconProvider &DockManager::iconProvider() IconProvider &DockManager::iconProvider()
{ {
static IconProvider instance; static IconProvider instance;
@@ -372,10 +452,9 @@ void DockManager::setWorkspacePresetsPath(const QString &path)
void DockManager::initialize() 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); // Can't continue if settings are not set yet
QTC_ASSERT(d->m_settings, return);
syncWorkspacePresets(); syncWorkspacePresets();
prepareWorkspaces(); prepareWorkspaces();
@@ -414,10 +493,40 @@ void DockManager::initialize()
DockAreaWidget *DockManager::addDockWidget(DockWidgetArea area, DockAreaWidget *DockManager::addDockWidget(DockWidgetArea area,
DockWidget *dockWidget, DockWidget *dockWidget,
DockAreaWidget *dockAreaWidget) DockAreaWidget *dockAreaWidget,
int index)
{ {
d->m_dockWidgetsMap.insert(dockWidget->objectName(), dockWidget); 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) DockAreaWidget *DockManager::addDockWidgetTab(DockWidgetArea area, DockWidget *dockWidget)
@@ -426,15 +535,16 @@ DockAreaWidget *DockManager::addDockWidgetTab(DockWidgetArea area, DockWidget *d
if (areaWidget) if (areaWidget)
return addDockWidget(ADS::CenterDockWidgetArea, dockWidget, areaWidget); return addDockWidget(ADS::CenterDockWidgetArea, dockWidget, areaWidget);
else if (!openedDockAreas().isEmpty()) else if (!openedDockAreas().isEmpty())
return addDockWidget(area, dockWidget, openedDockAreas().constLast()); return addDockWidget(area, dockWidget, openedDockAreas().constLast()); // TODO
else else
return addDockWidget(area, dockWidget, nullptr); return addDockWidget(area, dockWidget, nullptr);
} }
DockAreaWidget *DockManager::addDockWidgetTabToArea(DockWidget *dockWidget, 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) FloatingDockContainer *DockManager::addDockWidgetFloating(DockWidget *dockWidget)
@@ -452,6 +562,7 @@ FloatingDockContainer *DockManager::addDockWidgetFloating(DockWidget *dockWidget
else else
d->m_uninitializedFloatingWidgets.append(floatingWidget); d->m_uninitializedFloatingWidgets.append(floatingWidget);
emit dockWidgetAdded(dockWidget);
return floatingWidget; return floatingWidget;
} }
@@ -465,6 +576,7 @@ void DockManager::removeDockWidget(DockWidget *dockWidget)
emit dockWidgetAboutToBeRemoved(dockWidget); emit dockWidgetAboutToBeRemoved(dockWidget);
d->m_dockWidgetsMap.remove(dockWidget->objectName()); d->m_dockWidgetsMap.remove(dockWidget->objectName());
DockContainerWidget::removeDockWidget(dockWidget); DockContainerWidget::removeDockWidget(dockWidget);
dockWidget->setDockManager(nullptr);
emit dockWidgetRemoved(dockWidget); emit dockWidgetRemoved(dockWidget);
} }
@@ -503,6 +615,10 @@ QByteArray DockManager::saveState(const QString &displayName, int version) const
stream.writeAttribute("userVersion", QString::number(version)); stream.writeAttribute("userVersion", QString::number(version));
stream.writeAttribute("containers", QString::number(d->m_containers.count())); stream.writeAttribute("containers", QString::number(d->m_containers.count()));
stream.writeAttribute(workspaceDisplayNameAttribute.toString(), displayName); 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)) for (auto container : std::as_const(d->m_containers))
container->saveState(stream); container->saveState(stream);
@@ -544,12 +660,185 @@ bool DockManager::isRestoringState() const
return d->m_restoringState; 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) void DockManager::setDockWidgetFocused(DockWidget *dockWidget)
{ {
if (d->m_focusController) if (d->m_focusController)
d->m_focusController->setDockWidgetFocused(dockWidget); 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) void DockManager::registerFloatingWidget(FloatingDockContainer *floatingWidget)
{ {
d->m_floatingWidgets.append(floatingWidget); d->m_floatingWidgets.append(floatingWidget);
@@ -602,15 +891,53 @@ void DockManager::notifyDockAreaRelocation(DockAreaWidget *, DockContainerWidget
void DockManager::showEvent(QShowEvent *event) void DockManager::showEvent(QShowEvent *event)
{ {
Super::showEvent(event); Super::showEvent(event);
// Fix issue #380
restoreHiddenFloatingWidgets();
if (d->m_uninitializedFloatingWidgets.empty()) if (d->m_uninitializedFloatingWidgets.empty())
return; return;
for (auto floatingWidget : std::as_const(d->m_uninitializedFloatingWidgets)) 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(); floatingWidget->show();
}
d->m_uninitializedFloatingWidgets.clear(); 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 Workspace *DockManager::activeWorkspace() const
{ {
return &d->m_workspace; return &d->m_workspace;

View File

@@ -46,6 +46,10 @@ class DockWidgetTab;
class DockWidgetTabPrivate; class DockWidgetTabPrivate;
struct DockAreaWidgetPrivate; struct DockAreaWidgetPrivate;
class IconProvider; class IconProvider;
class DockFocusController;
class AutoHideSideBar;
class AutoHideTab;
struct AutoHideTabPrivate;
inline constexpr QStringView workspaceFolderName{u"workspaces"}; inline constexpr QStringView workspaceFolderName{u"workspaces"};
inline constexpr QStringView workspaceFileExtension{u"wrk"}; inline constexpr QStringView workspaceFileExtension{u"wrk"};
@@ -80,6 +84,10 @@ private:
friend class FloatingDragPreview; friend class FloatingDragPreview;
friend class FloatingDragPreviewPrivate; friend class FloatingDragPreviewPrivate;
friend class DockAreaTitleBar; friend class DockAreaTitleBar;
friend class AutoHideDockContainer;
friend AutoHideSideBar;
friend AutoHideTab;
friend AutoHideTabPrivate;
public: public:
using Super = DockContainerWidget; using Super = DockContainerWidget;
@@ -105,8 +113,6 @@ public:
= 0x0080, //!< if this flag is set, then all tabs that are closable show a close button = 0x0080, //!< if this flag is set, then all tabs that are closable show a close button
RetainTabSizeWhenCloseButtonHidden RetainTabSizeWhenCloseButtonHidden
= 0x0100, //!< if this flag is set, the space for the close button is reserved even if the close button is not visible = 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 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 = 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 DragPreviewShowsContentPixmap
@@ -119,32 +125,36 @@ public:
DockAreaHasTabsMenuButton 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 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 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 = 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 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 FloatingContainerHasWidgetIcon
= 0x80000, //!< If set, the Floating Widget icon reflects the icon of the current dock widget otherwise it displays application icon = 0x80000, //!< If set, the Floating Widget icon reflects the icon of the current dock widget otherwise it displays application icon
HideSingleCentralWidgetTitleBar 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 = 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 //!< this only makes sense for non draggable and non floatable widgets and enables the creation of some kind of "central" widget
FocusHighlighting FocusHighlighting
= 0x200000, //!< enables styling of focused dock widget tabs or floating widget titlebar = 0x200000, //!< enables styling of focused dock widget tabs or floating widget titlebar
EqualSplitOnInsertion 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 DefaultDockAreaButtons
= DockAreaHasCloseButton | DockAreaHasUndockButton = DockAreaHasCloseButton | DockAreaHasUndockButton
| DockAreaHasTabsMenuButton, ///< default configuration of dock area title bar buttons | DockAreaHasTabsMenuButton, ///< default configuration of dock area title bar buttons
DefaultBaseConfig = DefaultDockAreaButtons | ActiveTabHasCloseButton DefaultBaseConfig
| XmlAutoFormattingEnabled = DefaultDockAreaButtons | ActiveTabHasCloseButton | XmlAutoFormattingEnabled
| FloatingContainerHasWidgetTitle, ///< default base configuration settings | FloatingContainerHasWidgetTitle, ///< default base configuration settings
DefaultOpaqueConfig DefaultOpaqueConfig
= DefaultBaseConfig | OpaqueSplitterResize = 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 DefaultNonOpaqueConfig
= DefaultBaseConfig = DefaultBaseConfig
@@ -156,6 +166,31 @@ public:
}; };
Q_DECLARE_FLAGS(ConfigFlags, eConfigFlag) 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. * Default Constructor.
* If the given parent is a QMainWindow, the dock manager sets itself as the central widget. * If the given parent is a QMainWindow, the dock manager sets itself as the central widget.
@@ -174,23 +209,45 @@ public:
*/ */
static ConfigFlags configFlags(); 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 * 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. * you create the dock manager and before your create the first dock widget.
*/ */
static void setConfigFlags(const ConfigFlags flags); 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. * Set a certain config flag.
* \see setConfigFlags() * \see setConfigFlags()
*/ */
static void setConfigFlag(eConfigFlag flag, bool on = true); 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. * Returns true if the given config flag is set.
*/ */
static bool testConfigFlag(eConfigFlag flag); 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. * Returns the global icon provider.
* The icon provider enables the use of custom icons in case using styleheets for icons is not * 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, DockAreaWidget *addDockWidget(DockWidgetArea area,
DockWidget *dockWidget, 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 * 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. * 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. * Adds the given DockWidget floating and returns the created FloatingDockContainer instance.
@@ -322,11 +407,90 @@ public:
bool isRestoringState() const; bool isRestoringState() const;
/** /**
* Request a focus change to the given dock widget. * This function returns true, if the DockManager window is restoring from minimized state.
* This function only has an effect, if the flag CDockManager::FocusStyling is enabled. * 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); 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: signals:
/** /**
* This signal is emitted if the list of workspaces changed. * This signal is emitted if the list of workspaces changed.
@@ -376,6 +540,12 @@ signals:
*/ */
void dockAreaCreated(ADS::DockAreaWidget *dockArea); 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. * This signal is emitted just before removal of the given DockWidget.
*/ */
@@ -454,6 +624,17 @@ protected:
*/ */
void showEvent(QShowEvent *event) override; 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: public:
// Workspace state // Workspace state
Workspace *activeWorkspace() const; Workspace *activeWorkspace() const;

View File

@@ -3,8 +3,12 @@
#include "dockoverlay.h" #include "dockoverlay.h"
#include "dockareawidget.h" #include "autohidesidebar.h"
#include "dockareatabbar.h"
#include "dockareatitlebar.h" #include "dockareatitlebar.h"
#include "dockareawidget.h"
#include "dockcontainerwidget.h"
#include "dockmanager.h"
#include <utils/hostosinfo.h> #include <utils/hostosinfo.h>
@@ -25,12 +29,16 @@
namespace ADS { namespace ADS {
/** static const int g_autoHideAreaWidth = 32;
static const int g_autoHideAreaMouseZone = 8;
static const int g_invalidTabIndex = -2;
/**
* Private data class of DockOverlay * Private data class of DockOverlay
*/ */
class DockOverlayPrivate class DockOverlayPrivate
{ {
public: public:
DockOverlay *q; DockOverlay *q;
DockWidgetAreas m_allowedAreas = InvalidDockWidgetArea; DockWidgetAreas m_allowedAreas = InvalidDockWidgetArea;
DockOverlayCross *m_cross = nullptr; DockOverlayCross *m_cross = nullptr;
@@ -39,6 +47,7 @@ namespace ADS {
bool m_dropPreviewEnabled = true; bool m_dropPreviewEnabled = true;
DockOverlay::eMode m_mode = DockOverlay::ModeDockAreaOverlay; DockOverlay::eMode m_mode = DockOverlay::ModeDockAreaOverlay;
QRect m_dropAreaRect; QRect m_dropAreaRect;
int m_tabIndex = g_invalidTabIndex;
/** /**
* Private data constructor * Private data constructor
@@ -46,14 +55,24 @@ namespace ADS {
DockOverlayPrivate(DockOverlay *parent) DockOverlayPrivate(DockOverlay *parent)
: q(parent) : q(parent)
{} {}
};
/** /**
* Returns the overlay width / height depending on the visibility of the sidebar.
*/
int sideBarOverlaySize(SideBarLocation sideBarLocation);
/**
* The area where the mouse is considered in the sidebar.
*/
int sideBarMouseZone(SideBarLocation sideBarLocation);
};
/**
* Private data of DockOverlayCross class * Private data of DockOverlayCross class
*/ */
class DockOverlayCrossPrivate class DockOverlayCrossPrivate
{ {
public: public:
DockOverlayCross *q; DockOverlayCross *q;
DockOverlay::eMode m_mode = DockOverlay::ModeDockAreaOverlay; DockOverlay::eMode m_mode = DockOverlay::ModeDockAreaOverlay;
DockOverlay *m_dockOverlay = nullptr; DockOverlay *m_dockOverlay = nullptr;
@@ -118,9 +137,9 @@ namespace ADS {
* Helper function that returns the drop indicator width depending on the * Helper function that returns the drop indicator width depending on the
* operating system * operating system
*/ */
qreal dropIndicatiorWidth(QLabel *label) const qreal dropIndicatorWidth(QLabel *label) const
{ {
if (Utils::HostOsInfo::isLinuxHost()) if (Utils::HostOsInfo::isAnyUnixHost() && !Utils::HostOsInfo::isMacHost())
return 40; return 40;
else else
return static_cast<qreal>(label->fontMetrics().height()) * 3.f; return static_cast<qreal>(label->fontMetrics().height()) * 3.f;
@@ -131,8 +150,16 @@ namespace ADS {
QLabel *label = new QLabel(); QLabel *label = new QLabel();
label->setObjectName("DockWidgetAreaLabel"); label->setObjectName("DockWidgetAreaLabel");
const qreal metric = dropIndicatiorWidth(label); const qreal metric = dropIndicatorWidth(label);
const QSizeF size(metric, metric); QSizeF size(metric, metric);
if (internal::isSideBarArea(dockWidgetArea)) {
auto sideBarLocation = internal::toSideBarLocation(dockWidgetArea);
if (internal::isHorizontalSideBarLocation(sideBarLocation))
size.setHeight(size.height() / 2);
else
size.setWidth(size.width() / 2);
}
label->setPixmap(createHighDpiDropIndicatorPixmap(size, dockWidgetArea, mode)); label->setPixmap(createHighDpiDropIndicatorPixmap(size, dockWidgetArea, mode));
label->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint); label->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
@@ -144,7 +171,7 @@ namespace ADS {
void updateDropIndicatorIcon(QWidget *dropIndicatorWidget) void updateDropIndicatorIcon(QWidget *dropIndicatorWidget)
{ {
QLabel *label = qobject_cast<QLabel *>(dropIndicatorWidget); QLabel *label = qobject_cast<QLabel *>(dropIndicatorWidget);
const qreal metric = dropIndicatiorWidth(label); const qreal metric = dropIndicatorWidth(label);
const QSizeF size(metric, metric); const QSizeF size(metric, metric);
int area = label->property("dockWidgetArea").toInt(); int area = label->property("dockWidgetArea").toInt();
@@ -159,6 +186,11 @@ namespace ADS {
{ {
const QColor borderColor = iconColor(DockOverlayCross::FrameColor); const QColor borderColor = iconColor(DockOverlayCross::FrameColor);
const QColor backgroundColor = iconColor(DockOverlayCross::WindowBackgroundColor); const QColor backgroundColor = iconColor(DockOverlayCross::WindowBackgroundColor);
QColor overlayColor = iconColor(DockOverlayCross::OverlayColor);
if (overlayColor.alpha() == 255)
overlayColor.setAlpha(64);
const double devicePixelRatio = q->window()->devicePixelRatioF(); const double devicePixelRatio = q->window()->devicePixelRatioF();
const QSizeF pixmapSize = size * devicePixelRatio; const QSizeF pixmapSize = size * devicePixelRatio;
QPixmap pixmap(pixmapSize.toSize()); QPixmap pixmap(pixmapSize.toSize());
@@ -197,10 +229,8 @@ namespace ADS {
baseRect.y(), baseRect.y(),
baseRect.width() * 0.5, baseRect.width() * 0.5,
baseRect.height()); baseRect.height());
nonAreaRect = QRectF(baseRect.x(), nonAreaRect
baseRect.y(), = QRectF(baseRect.x(), baseRect.y(), baseRect.width() * 0.5, baseRect.height());
baseRect.width() * 0.5,
baseRect.height());
areaLine = QLineF(areaRect.topLeft(), areaRect.bottomLeft()); areaLine = QLineF(areaRect.topLeft(), areaRect.bottomLeft());
break; break;
case BottomDockWidgetArea: case BottomDockWidgetArea:
@@ -208,10 +238,8 @@ namespace ADS {
shadowRect.height() * 0.5, shadowRect.height() * 0.5,
baseRect.width(), baseRect.width(),
baseRect.height() * 0.5); baseRect.height() * 0.5);
nonAreaRect = QRectF(baseRect.x(), nonAreaRect
baseRect.y(), = QRectF(baseRect.x(), baseRect.y(), baseRect.width(), baseRect.height() * 0.5);
baseRect.width(),
baseRect.height() * 0.5);
areaLine = QLineF(areaRect.topLeft(), areaRect.topRight()); areaLine = QLineF(areaRect.topLeft(), areaRect.topRight());
break; break;
case LeftDockWidgetArea: case LeftDockWidgetArea:
@@ -227,19 +255,18 @@ namespace ADS {
} }
const QSizeF baseSize = baseRect.size(); const QSizeF baseSize = baseRect.size();
if (DockOverlay::ModeContainerOverlay == mode && dockWidgetArea != CenterDockWidgetArea) { bool isOuterContainerArea = (DockOverlay::ModeContainerOverlay == mode)
&& (dockWidgetArea != CenterDockWidgetArea)
&& !internal::isSideBarArea(dockWidgetArea);
if (isOuterContainerArea)
baseRect = areaRect; baseRect = areaRect;
}
painter.fillRect(baseRect, backgroundColor); painter.fillRect(baseRect, backgroundColor);
if (areaRect.isValid()) { if (areaRect.isValid()) {
pen = painter.pen(); pen = painter.pen();
pen.setColor(borderColor); pen.setColor(borderColor);
QColor color = iconColor(DockOverlayCross::OverlayColor); painter.setBrush(overlayColor);
if (color.alpha() == 255) {
color.setAlpha(64);
}
painter.setBrush(color);
painter.setPen(Qt::NoPen); painter.setPen(Qt::NoPen);
painter.drawRect(areaRect); painter.drawRect(areaRect);
@@ -268,7 +295,7 @@ namespace ADS {
painter.restore(); painter.restore();
// Draw arrow for outer container drop indicators // Draw arrow for outer container drop indicators
if (DockOverlay::ModeContainerOverlay == mode && dockWidgetArea != CenterDockWidgetArea) { if (isOuterContainerArea) {
QRectF arrowRect; QRectF arrowRect;
arrowRect.setSize(baseSize); arrowRect.setSize(baseSize);
arrowRect.setWidth(arrowRect.width() / 4.6); arrowRect.setWidth(arrowRect.width() / 4.6);
@@ -304,16 +331,36 @@ namespace ADS {
pixmap.setDevicePixelRatio(devicePixelRatio); pixmap.setDevicePixelRatio(devicePixelRatio);
return pixmap; return pixmap;
} }
}; // class DockOverlayCrossPrivate }; // class DockOverlayCrossPrivate
DockOverlay::DockOverlay(QWidget *parent, eMode mode) int DockOverlayPrivate::sideBarOverlaySize(SideBarLocation sideBarLocation)
{
auto container = qobject_cast<DockContainerWidget *>(m_targetWidget.data());
auto sideBar = container->autoHideSideBar(sideBarLocation);
if (!sideBar || !sideBar->isVisibleTo(container))
return g_autoHideAreaWidth;
else
return (sideBar->orientation() == Qt::Horizontal) ? sideBar->height() : sideBar->width();
}
int DockOverlayPrivate::sideBarMouseZone(SideBarLocation sideBarLocation)
{
auto container = qobject_cast<DockContainerWidget *>(m_targetWidget.data());
auto sideBar = container->autoHideSideBar(sideBarLocation);
if (!sideBar || !sideBar->isVisibleTo(container))
return g_autoHideAreaMouseZone;
else
return (sideBar->orientation() == Qt::Horizontal) ? sideBar->height() : sideBar->width();
}
DockOverlay::DockOverlay(QWidget *parent, eMode mode)
: QFrame(parent) : QFrame(parent)
, d(new DockOverlayPrivate(this)) , d(new DockOverlayPrivate(this))
{ {
d->m_mode = mode; d->m_mode = mode;
d->m_cross = new DockOverlayCross(this); d->m_cross = new DockOverlayCross(this);
if (Utils::HostOsInfo::isLinuxHost()) if (Utils::HostOsInfo::isAnyUnixHost() && !Utils::HostOsInfo::isMacHost())
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint
| Qt::X11BypassWindowManagerHint); | Qt::X11BypassWindowManagerHint);
else else
@@ -326,55 +373,103 @@ namespace ADS {
d->m_cross->setVisible(false); d->m_cross->setVisible(false);
setVisible(false); setVisible(false);
} }
DockOverlay::~DockOverlay() DockOverlay::~DockOverlay()
{ {
delete d; delete d;
} }
void DockOverlay::setAllowedAreas(DockWidgetAreas areas) void DockOverlay::setAllowedAreas(DockWidgetAreas areas)
{ {
if (areas == d->m_allowedAreas) if (areas == d->m_allowedAreas)
return; return;
d->m_allowedAreas = areas; d->m_allowedAreas = areas;
d->m_cross->reset(); d->m_cross->reset();
} }
DockWidgetAreas DockOverlay::allowedAreas() const void DockOverlay::setAllowedArea(DockWidgetArea area, bool enable)
{ {
auto areasOld = d->m_allowedAreas;
d->m_allowedAreas.setFlag(area, enable);
if (areasOld != d->m_allowedAreas)
d->m_cross->reset();
}
DockWidgetAreas DockOverlay::allowedAreas() const
{
return d->m_allowedAreas; return d->m_allowedAreas;
} }
DockWidgetArea DockOverlay::dropAreaUnderCursor() const
{
d->m_tabIndex = g_invalidTabIndex;
if (!d->m_targetWidget)
return InvalidDockWidgetArea;
DockWidgetArea DockOverlay::dropAreaUnderCursor() const
{
DockWidgetArea result = d->m_cross->cursorLocation(); DockWidgetArea result = d->m_cross->cursorLocation();
if (result != InvalidDockWidgetArea) if (result != InvalidDockWidgetArea)
return result; return result;
DockAreaWidget *dockArea = qobject_cast<DockAreaWidget *>(d->m_targetWidget.data()); auto cursorPos = QCursor::pos();
if (!dockArea) auto dockArea = qobject_cast<DockAreaWidget *>(d->m_targetWidget.data());
if (!dockArea
&& DockManager::autoHideConfigFlags().testFlag(DockManager::AutoHideFeatureEnabled)) {
auto rectangle = rect();
const QPoint pos = mapFromGlobal(QCursor::pos());
if ((pos.x() < d->sideBarMouseZone(SideBarLeft))
&& d->m_allowedAreas.testFlag(LeftAutoHideArea)) {
result = LeftAutoHideArea;
} else if (pos.x() > (rectangle.width() - d->sideBarMouseZone(SideBarRight))
&& d->m_allowedAreas.testFlag(RightAutoHideArea)) {
result = RightAutoHideArea;
} else if (pos.y() < d->sideBarMouseZone(SideBarTop)
&& d->m_allowedAreas.testFlag(TopAutoHideArea)) {
result = TopAutoHideArea;
} else if (pos.y() > (rectangle.height() - d->sideBarMouseZone(SideBarBottom))
&& d->m_allowedAreas.testFlag(BottomAutoHideArea)) {
result = BottomAutoHideArea;
}
auto sideBarLocation = internal::toSideBarLocation(result);
if (sideBarLocation != SideBarNone) {
auto Container = qobject_cast<DockContainerWidget *>(d->m_targetWidget.data());
auto SideBar = Container->autoHideSideBar(sideBarLocation);
if (SideBar->isVisible()) {
d->m_tabIndex = SideBar->tabInsertIndexAt(SideBar->mapFromGlobal(cursorPos));
}
}
return result; return result;
} else if (!dockArea) {
if (dockArea->allowedAreas().testFlag(CenterDockWidgetArea)
&& !dockArea->titleBar()->isHidden()
&& dockArea->titleBarGeometry().contains(dockArea->mapFromGlobal(QCursor::pos())))
return CenterDockWidgetArea;
return result; return result;
} }
DockWidgetArea DockOverlay::visibleDropAreaUnderCursor() const if (dockArea->allowedAreas().testFlag(CenterDockWidgetArea) && !dockArea->titleBar()->isHidden()
{ && dockArea->titleBarGeometry().contains(dockArea->mapFromGlobal(cursorPos))) {
auto tabBar = dockArea->titleBar()->tabBar();
d->m_tabIndex = tabBar->tabInsertIndexAt(tabBar->mapFromGlobal(cursorPos));
return CenterDockWidgetArea;
}
return result;
}
int DockOverlay::tabIndexUnderCursor() const
{
return d->m_tabIndex;
}
DockWidgetArea DockOverlay::visibleDropAreaUnderCursor() const
{
if (isHidden() || !d->m_dropPreviewEnabled) if (isHidden() || !d->m_dropPreviewEnabled)
return InvalidDockWidgetArea; return InvalidDockWidgetArea;
else else
return dropAreaUnderCursor(); return dropAreaUnderCursor();
} }
DockWidgetArea DockOverlay::showOverlay(QWidget *target) DockWidgetArea DockOverlay::showOverlay(QWidget *target)
{ {
if (d->m_targetWidget == target) { if (d->m_targetWidget == target) {
// Hint: We could update geometry of overlay here. // Hint: We could update geometry of overlay here.
DockWidgetArea dockWidgetArea = dropAreaUnderCursor(); DockWidgetArea dockWidgetArea = dropAreaUnderCursor();
@@ -397,30 +492,31 @@ namespace ADS {
d->m_cross->updatePosition(); d->m_cross->updatePosition();
d->m_cross->updateOverlayIcons(); d->m_cross->updateOverlayIcons();
return dropAreaUnderCursor(); return dropAreaUnderCursor();
} }
void DockOverlay::hideOverlay() void DockOverlay::hideOverlay()
{ {
hide(); hide();
d->m_targetWidget.clear(); d->m_targetWidget.clear();
d->m_lastLocation = InvalidDockWidgetArea; d->m_lastLocation = InvalidDockWidgetArea;
d->m_dropAreaRect = QRect(); d->m_dropAreaRect = QRect();
} }
void DockOverlay::enableDropPreview(bool enable) void DockOverlay::enableDropPreview(bool enable)
{ {
d->m_dropPreviewEnabled = enable; d->m_dropPreviewEnabled = enable;
update(); update();
} }
bool DockOverlay::dropPreviewEnabled() const bool DockOverlay::dropPreviewEnabled() const
{ {
return d->m_dropPreviewEnabled; return d->m_dropPreviewEnabled;
} }
void DockOverlay::paintEvent(QPaintEvent *event) void DockOverlay::paintEvent(QPaintEvent *event)
{ {
Q_UNUSED(event) Q_UNUSED(event)
// Draw rect based on location // Draw rect based on location
if (!d->m_dropPreviewEnabled) { if (!d->m_dropPreviewEnabled) {
d->m_dropAreaRect = QRect(); d->m_dropAreaRect = QRect();
@@ -447,9 +543,22 @@ namespace ADS {
case CenterDockWidgetArea: case CenterDockWidgetArea:
rectangle = rect(); rectangle = rect();
break; break;
case LeftAutoHideArea:
rectangle.setWidth(d->sideBarOverlaySize(SideBarLeft));
break;
case RightAutoHideArea:
rectangle.setX(rectangle.width() - d->sideBarOverlaySize(SideBarRight));
break;
case TopAutoHideArea:
rectangle.setHeight(d->sideBarOverlaySize(SideBarTop));
break;
case BottomAutoHideArea:
rectangle.setY(rectangle.height() - d->sideBarOverlaySize(SideBarBottom));
break;
default: default:
return; return;
} }
QPainter painter(this); QPainter painter(this);
QColor color = palette().color(QPalette::Active, QPalette::Highlight); QColor color = palette().color(QPalette::Active, QPalette::Highlight);
QPen pen = painter.pen(); QPen pen = painter.pen();
@@ -463,36 +572,36 @@ namespace ADS {
painter.setBrush(color); painter.setBrush(color);
painter.drawRect(rectangle.adjusted(0, 0, -1, -1)); painter.drawRect(rectangle.adjusted(0, 0, -1, -1));
d->m_dropAreaRect = rectangle; d->m_dropAreaRect = rectangle;
} }
QRect DockOverlay::dropOverlayRect() const QRect DockOverlay::dropOverlayRect() const
{ {
return d->m_dropAreaRect; return d->m_dropAreaRect;
} }
void DockOverlay::showEvent(QShowEvent *event) void DockOverlay::showEvent(QShowEvent *event)
{ {
d->m_cross->show(); d->m_cross->show();
QFrame::showEvent(event); QFrame::showEvent(event);
} }
void DockOverlay::hideEvent(QHideEvent *event) void DockOverlay::hideEvent(QHideEvent *event)
{ {
d->m_cross->hide(); d->m_cross->hide();
QFrame::hideEvent(event); QFrame::hideEvent(event);
} }
bool DockOverlay::event(QEvent *event) bool DockOverlay::event(QEvent *event)
{ {
bool result = Super::event(event); bool result = Super::event(event);
if (event->type() == QEvent::Polish) if (event->type() == QEvent::Polish)
d->m_cross->setupOverlayCross(d->m_mode); d->m_cross->setupOverlayCross(d->m_mode);
return result; return result;
} }
static int areaAlignment(const DockWidgetArea area) static int areaAlignment(const DockWidgetArea area)
{ {
switch (area) { switch (area) {
case TopDockWidgetArea: case TopDockWidgetArea:
return Qt::AlignHCenter | Qt::AlignBottom; return Qt::AlignHCenter | Qt::AlignBottom;
@@ -507,11 +616,11 @@ namespace ADS {
default: default:
return Qt::AlignCenter; return Qt::AlignCenter;
} }
} }
// DockOverlayCrossPrivate // DockOverlayCrossPrivate
QPoint DockOverlayCrossPrivate::areaGridPosition(const DockWidgetArea area) QPoint DockOverlayCrossPrivate::areaGridPosition(const DockWidgetArea area)
{ {
if (DockOverlay::ModeDockAreaOverlay == m_mode) { if (DockOverlay::ModeDockAreaOverlay == m_mode) {
switch (area) { switch (area) {
case TopDockWidgetArea: case TopDockWidgetArea:
@@ -543,15 +652,15 @@ namespace ADS {
return QPoint(); return QPoint();
} }
} }
} }
DockOverlayCross::DockOverlayCross(DockOverlay *overlay) DockOverlayCross::DockOverlayCross(DockOverlay *overlay)
: QWidget(overlay->parentWidget()) : QWidget(overlay->parentWidget())
, d(new DockOverlayCrossPrivate(this)) , d(new DockOverlayCrossPrivate(this))
{ {
d->m_dockOverlay = overlay; d->m_dockOverlay = overlay;
if (Utils::HostOsInfo::isLinuxHost()) if (Utils::HostOsInfo::isAnyUnixHost() && !Utils::HostOsInfo::isMacHost())
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint
| Qt::X11BypassWindowManagerHint); | Qt::X11BypassWindowManagerHint);
else else
@@ -563,15 +672,15 @@ namespace ADS {
d->m_gridLayout = new QGridLayout(); d->m_gridLayout = new QGridLayout();
d->m_gridLayout->setSpacing(0); d->m_gridLayout->setSpacing(0);
setLayout(d->m_gridLayout); setLayout(d->m_gridLayout);
} }
DockOverlayCross::~DockOverlayCross() DockOverlayCross::~DockOverlayCross()
{ {
delete d; delete d;
} }
void DockOverlayCross::setupOverlayCross(DockOverlay::eMode mode) void DockOverlayCross::setupOverlayCross(DockOverlay::eMode mode)
{ {
d->m_mode = mode; d->m_mode = mode;
QHash<DockWidgetArea, QWidget *> areaWidgets; QHash<DockWidgetArea, QWidget *> areaWidgets;
@@ -585,10 +694,10 @@ namespace ADS {
d->m_lastDevicePixelRatio = devicePixelRatioF(); d->m_lastDevicePixelRatio = devicePixelRatioF();
setAreaWidgets(areaWidgets); setAreaWidgets(areaWidgets);
d->m_updateRequired = false; d->m_updateRequired = false;
} }
void DockOverlayCross::updateOverlayIcons() void DockOverlayCross::updateOverlayIcons()
{ {
if (windowHandle()->devicePixelRatio() == d->m_lastDevicePixelRatio) // TODO if (windowHandle()->devicePixelRatio() == d->m_lastDevicePixelRatio) // TODO
return; return;
@@ -596,21 +705,21 @@ namespace ADS {
d->updateDropIndicatorIcon(widget); d->updateDropIndicatorIcon(widget);
d->m_lastDevicePixelRatio = devicePixelRatioF(); d->m_lastDevicePixelRatio = devicePixelRatioF();
} }
void DockOverlayCross::setIconColor(eIconColor colorIndex, const QColor &color) void DockOverlayCross::setIconColor(eIconColor colorIndex, const QColor &color)
{ {
d->m_iconColors[colorIndex] = color; d->m_iconColors[colorIndex] = color;
d->m_updateRequired = true; d->m_updateRequired = true;
} }
QColor DockOverlayCross::iconColor(eIconColor colorIndex) const QColor DockOverlayCross::iconColor(eIconColor colorIndex) const
{ {
return d->m_iconColors[colorIndex]; return d->m_iconColors[colorIndex];
} }
void DockOverlayCross::setAreaWidgets(const QHash<DockWidgetArea, QWidget *> &widgets) void DockOverlayCross::setAreaWidgets(const QHash<DockWidgetArea, QWidget *> &widgets)
{ {
// Delete old widgets. // Delete old widgets.
const auto values = d->m_dropIndicatorWidgets.values(); const auto values = d->m_dropIndicatorWidgets.values();
for (auto widget : values) { for (auto widget : values) {
@@ -662,16 +771,15 @@ namespace ADS {
d->m_gridLayout->setColumnStretch(4, 0); d->m_gridLayout->setColumnStretch(4, 0);
} }
reset(); reset();
} }
DockWidgetArea DockOverlayCross::cursorLocation() const DockWidgetArea DockOverlayCross::cursorLocation() const
{ {
const QPoint position = mapFromGlobal(QCursor::pos()); const QPoint position = mapFromGlobal(QCursor::pos());
const QHash<DockWidgetArea, QWidget *> areas = d->m_dropIndicatorWidgets; const QHash<DockWidgetArea, QWidget *> areas = d->m_dropIndicatorWidgets;
QHash<DockWidgetArea, QWidget *>::const_iterator constIt; QHash<DockWidgetArea, QWidget *>::const_iterator constIt;
for (constIt = areas.begin(); constIt != areas.end(); ++constIt) for (constIt = areas.begin(); constIt != areas.end(); ++constIt) {
{
if (d->m_dockOverlay->allowedAreas().testFlag(constIt.key()) && constIt.value() if (d->m_dockOverlay->allowedAreas().testFlag(constIt.key()) && constIt.value()
&& constIt.value()->isVisible() && constIt.value()->geometry().contains(position)) { && constIt.value()->isVisible() && constIt.value()->geometry().contains(position)) {
return constIt.key(); return constIt.key();
@@ -679,28 +787,28 @@ namespace ADS {
} }
return InvalidDockWidgetArea; return InvalidDockWidgetArea;
} }
void DockOverlayCross::showEvent(QShowEvent *) void DockOverlayCross::showEvent(QShowEvent *)
{ {
if (d->m_updateRequired) if (d->m_updateRequired)
setupOverlayCross(d->m_mode); setupOverlayCross(d->m_mode);
this->updatePosition(); this->updatePosition();
} }
void DockOverlayCross::updatePosition() void DockOverlayCross::updatePosition()
{ {
resize(d->m_dockOverlay->size()); resize(d->m_dockOverlay->size());
QPoint topLeft = d->m_dockOverlay->pos(); QPoint topLeft = d->m_dockOverlay->pos();
QPoint offest((this->width() - d->m_dockOverlay->width()) / 2, QPoint offest((this->width() - d->m_dockOverlay->width()) / 2,
(this->height() - d->m_dockOverlay->height()) / 2); (this->height() - d->m_dockOverlay->height()) / 2);
QPoint crossTopLeft = topLeft - offest; QPoint crossTopLeft = topLeft - offest;
move(crossTopLeft); move(crossTopLeft);
} }
void DockOverlayCross::reset() void DockOverlayCross::reset()
{ {
const QList<DockWidgetArea> allAreas{TopDockWidgetArea, const QList<DockWidgetArea> allAreas{TopDockWidgetArea,
RightDockWidgetArea, RightDockWidgetArea,
BottomDockWidgetArea, BottomDockWidgetArea,
@@ -716,10 +824,10 @@ namespace ADS {
if (item && (widget = item->widget()) != nullptr) if (item && (widget = item->widget()) != nullptr)
widget->setVisible(allowedAreas.testFlag(area)); widget->setVisible(allowedAreas.testFlag(area));
} }
} }
void DockOverlayCross::setIconColors(const QString &colors) void DockOverlayCross::setIconColors(const QString &colors)
{ {
static const QMap<QString, int> static const QMap<QString, int>
colorCompenentStringMap{{"Frame", DockOverlayCross::FrameColor}, colorCompenentStringMap{{"Frame", DockOverlayCross::FrameColor},
{"Background", DockOverlayCross::WindowBackgroundColor}, {"Background", DockOverlayCross::WindowBackgroundColor},
@@ -738,11 +846,11 @@ namespace ADS {
} }
d->m_updateRequired = true; d->m_updateRequired = true;
} }
QString DockOverlayCross::iconColors() const QString DockOverlayCross::iconColors() const
{ {
return QString(); return QString();
} }
} // namespace ADS } // namespace ADS

View File

@@ -52,11 +52,26 @@ public:
*/ */
DockWidgetAreas allowedAreas() const; DockWidgetAreas allowedAreas() const;
/**
* Enable / disable a certain area
*/
void setAllowedArea(DockWidgetArea area, bool enable);
/** /**
* Returns the drop area under the current cursor location * Returns the drop area under the current cursor location
*/ */
DockWidgetArea dropAreaUnderCursor() const; 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 * This function returns the same like dropAreaUnderCursor() if this
* overlay is not hidden and if drop preview is enabled and returns * overlay is not hidden and if drop preview is enabled and returns

View File

@@ -12,43 +12,42 @@
#include <QLoggingCategory> #include <QLoggingCategory>
#include <QVariant> #include <QVariant>
namespace ADS namespace ADS {
{ /**
/**
* Private dock splitter data * Private dock splitter data
*/ */
struct DockSplitterPrivate struct DockSplitterPrivate
{ {
DockSplitter *q; DockSplitter *q;
int m_visibleContentCount = 0; int m_visibleContentCount = 0;
DockSplitterPrivate(DockSplitter *parent) DockSplitterPrivate(DockSplitter *parent)
: q(parent) : q(parent)
{} {}
}; };
DockSplitter::DockSplitter(QWidget *parent) DockSplitter::DockSplitter(QWidget *parent)
: QSplitter(parent) : QSplitter(parent)
, d(new DockSplitterPrivate(this)) , d(new DockSplitterPrivate(this))
{ {
//setProperty("ads-splitter", true); // TODO //setProperty("ads-splitter", true); // TODO
setProperty(Utils::StyleHelper::C_MINI_SPLITTER, true); setProperty(Utils::StyleHelper::C_MINI_SPLITTER, true);
setChildrenCollapsible(false); setChildrenCollapsible(false);
} }
DockSplitter::DockSplitter(Qt::Orientation orientation, QWidget *parent) DockSplitter::DockSplitter(Qt::Orientation orientation, QWidget *parent)
: QSplitter(orientation, parent) : QSplitter(orientation, parent)
, d(new DockSplitterPrivate(this)) , d(new DockSplitterPrivate(this))
{} {}
DockSplitter::~DockSplitter() DockSplitter::~DockSplitter()
{ {
qCInfo(adsLog) << Q_FUNC_INFO; qCInfo(adsLog) << Q_FUNC_INFO;
delete d; delete d;
} }
bool DockSplitter::hasVisibleContent() const bool DockSplitter::hasVisibleContent() const
{ {
// TODO Cache or precalculate this to speed up // TODO Cache or precalculate this to speed up
for (int i = 0; i < count(); ++i) { for (int i = 0; i < count(); ++i) {
if (!widget(i)->isHidden()) { if (!widget(i)->isHidden()) {
@@ -57,16 +56,26 @@ namespace ADS
} }
return false; return false;
} }
QWidget *DockSplitter::firstWidget() const QWidget *DockSplitter::firstWidget() const
{ {
return (count() > 0) ? widget(0) : nullptr; 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 false;
{ }
return (count() > 0) ? widget(count() - 1) : nullptr;
}
} // namespace ADS } // namespace ADS

View File

@@ -45,6 +45,11 @@ public:
* Returns last widget of nullptr is splitter is empty * Returns last widget of nullptr is splitter is empty
*/ */
QWidget *lastWidget() const; QWidget *lastWidget() const;
/**
* Returns true if the splitter contains central widget of dock manager.
*/
bool isResizingWithContainer() const;
}; // class DockSplitter }; // class DockSplitter
} // namespace ADS } // namespace ADS

View File

@@ -5,11 +5,13 @@
#include "ads_globals.h" #include "ads_globals.h"
#include "ads_globals_p.h" #include "ads_globals_p.h"
#include "autohidedockcontainer.h"
#include "autohidesidebar.h"
#include "autohidetab.h"
#include "dockareawidget.h" #include "dockareawidget.h"
#include "dockcomponentsfactory.h" #include "dockcomponentsfactory.h"
#include "dockcontainerwidget.h" #include "dockcontainerwidget.h"
#include "dockmanager.h" #include "dockmanager.h"
#include "docksplitter.h"
#include "dockwidgettab.h" #include "dockwidgettab.h"
#include "floatingdockcontainer.h" #include "floatingdockcontainer.h"
@@ -23,17 +25,22 @@
#include <QStack> #include <QStack>
#include <QTextStream> #include <QTextStream>
#include <QToolBar> #include <QToolBar>
#include <QXmlStreamWriter>
#include <QWindow> #include <QWindow>
#include <QXmlStreamWriter>
namespace ADS namespace ADS {
{ /**
/**
* Private data class of DockWidget class (pimpl) * Private data class of DockWidget class (pimpl)
*/ */
class DockWidgetPrivate class DockWidgetPrivate
{
public:
struct WidgetFactory
{ {
public: DockWidget::FactoryFunc createWidget;
DockWidget::eInsertMode insertMode;
};
DockWidget *q = nullptr; DockWidget *q = nullptr;
QBoxLayout *m_layout = nullptr; QBoxLayout *m_layout = nullptr;
QWidget *m_widget = nullptr; QWidget *m_widget = nullptr;
@@ -51,7 +58,10 @@ namespace ADS
QSize m_toolBarIconSizeFloating = QSize(24, 24); QSize m_toolBarIconSizeFloating = QSize(24, 24);
bool m_isFloatingTopLevel = false; bool m_isFloatingTopLevel = false;
QList<QAction *> m_titleBarActions; QList<QAction *> m_titleBarActions;
DockWidget::eMinimumSizeHintMode m_minimumSizeHintMode = DockWidget::MinimumSizeHintFromDockWidget; DockWidget::eMinimumSizeHintMode m_minimumSizeHintMode
= DockWidget::MinimumSizeHintFromDockWidget;
WidgetFactory *m_factory = nullptr;
QPointer<AutoHideTab> m_sideTabWidget;
/** /**
* Private data constructor * Private data constructor
@@ -69,12 +79,17 @@ namespace ADS
void hideDockWidget(); void hideDockWidget();
/** /**
* Hides a dock area if all dock widgets in the area are closed. * Hides a dock area if all dock widgets in the area are closed. This function updates the
* This function updates the current selected tab and hides the parent * current selected tab and hides the parent dock area if it is empty.
* dock area if it is empty
*/ */
void updateParentDockArea(); void updateParentDockArea();
/**
* Closes all auto hide dock widgets if there are no more opened dock areas. This prevents the
* auto hide dock widgets from being pinned to an empty dock area.
*/
void closeAutoHideDockWidgetsIfNeeded();
/** /**
* Setup the top tool bar * Setup the top tool bar
*/ */
@@ -84,17 +99,32 @@ namespace ADS
* Setup the main scroll area * Setup the main scroll area
*/ */
void setupScrollArea(); void setupScrollArea();
}; // class DockWidgetPrivate
DockWidgetPrivate::DockWidgetPrivate(DockWidget *parent) /**
* Creates the content widget with the registered widget factory and returns true on success.
*/
bool createWidgetFromFactory();
}; // class DockWidgetPrivate
DockWidgetPrivate::DockWidgetPrivate(DockWidget *parent)
: q(parent) : q(parent)
{} {}
void DockWidgetPrivate::showDockWidget()
{
if (!m_widget) {
if (!createWidgetFromFactory()) {
Q_ASSERT(!m_features.testFlag(DockWidget::DeleteContentOnClose)
&& "DeleteContentOnClose flag was set, but the widget "
"factory is missing or it doesn't return a valid QWidget.");
return;
}
}
void DockWidgetPrivate::showDockWidget()
{
if (!m_dockArea) { if (!m_dockArea) {
FloatingDockContainer *floatingWidget = new FloatingDockContainer(q); FloatingDockContainer *floatingWidget = new FloatingDockContainer(q);
floatingWidget->resize(q->size()); // We use the size hint of the content widget to provide a good initial size
floatingWidget->resize(m_widget ? m_widget->sizeHint() : q->size());
m_tabWidget->show(); m_tabWidget->show();
floatingWidget->show(); floatingWidget->show();
} else { } else {
@@ -102,32 +132,46 @@ namespace ADS
m_dockArea->toggleView(true); m_dockArea->toggleView(true);
m_tabWidget->show(); m_tabWidget->show();
QSplitter *splitter = internal::findParent<QSplitter *>(m_dockArea); QSplitter *splitter = internal::findParent<QSplitter *>(m_dockArea);
while (splitter && !splitter->isVisible()) { while (splitter && !splitter->isVisible() && !m_dockArea->isAutoHide()) {
splitter->show(); splitter->show();
splitter = internal::findParent<QSplitter *>(splitter); splitter = internal::findParent<QSplitter *>(splitter);
} }
DockContainerWidget *container = m_dockArea->dockContainer(); DockContainerWidget *container = m_dockArea->dockContainer();
if (container->isFloating()) { if (container->isFloating()) {
FloatingDockContainer *floatingWidget FloatingDockContainer *floatingWidget = internal::findParent<FloatingDockContainer *>(
= internal::findParent<FloatingDockContainer *>(container); container);
floatingWidget->show(); floatingWidget->show();
} }
}
}
void DockWidgetPrivate::hideDockWidget() // If this widget is pinned and there are no opened dock widgets, unpin the auto hide widget
{ // by moving it's contents to parent container While restoring state, opened dock widgets
// are not valid
if (container->openedDockWidgets().count() == 0 && m_dockArea->isAutoHide()
&& !m_dockManager->isRestoringState())
m_dockArea->autoHideDockContainer()->moveContentsToParent();
}
}
void DockWidgetPrivate::hideDockWidget()
{
m_tabWidget->hide(); m_tabWidget->hide();
updateParentDockArea(); updateParentDockArea();
}
void DockWidgetPrivate::updateParentDockArea() closeAutoHideDockWidgetsIfNeeded();
{
if (m_features.testFlag(DockWidget::DeleteContentOnClose)) {
m_widget->deleteLater();
m_widget = nullptr;
}
}
void DockWidgetPrivate::updateParentDockArea()
{
if (!m_dockArea) if (!m_dockArea)
return; return;
// we don't need to change the current tab if the current DockWidget is not the one being closed // We don't need to change the current tab if the current DockWidget is not the one being closed
if (m_dockArea->currentDockWidget() != q) if (m_dockArea->currentDockWidget() != q)
return; return;
@@ -136,10 +180,31 @@ namespace ADS
m_dockArea->setCurrentDockWidget(nextDockWidget); m_dockArea->setCurrentDockWidget(nextDockWidget);
else else
m_dockArea->hideAreaWithNoVisibleContent(); m_dockArea->hideAreaWithNoVisibleContent();
} }
void DockWidgetPrivate::setupToolBar() void DockWidgetPrivate::closeAutoHideDockWidgetsIfNeeded()
{ {
auto dockContainer = q->dockContainer();
if (!dockContainer)
return;
if (q->dockManager()->isRestoringState())
return;
if (!dockContainer->openedDockWidgets().isEmpty())
return;
for (auto autoHideWidget : dockContainer->autoHideWidgets()) {
auto dockWidget = autoHideWidget->dockWidget();
if (dockWidget == q)
continue;
dockWidget->toggleView(false);
}
}
void DockWidgetPrivate::setupToolBar()
{
m_toolBar = new QToolBar(q); m_toolBar = new QToolBar(q);
m_toolBar->setObjectName("dockWidgetToolBar"); m_toolBar->setObjectName("dockWidgetToolBar");
m_layout->insertWidget(0, m_toolBar); m_layout->insertWidget(0, m_toolBar);
@@ -147,20 +212,36 @@ namespace ADS
m_toolBar->toggleViewAction()->setEnabled(false); m_toolBar->toggleViewAction()->setEnabled(false);
m_toolBar->toggleViewAction()->setVisible(false); m_toolBar->toggleViewAction()->setVisible(false);
QObject::connect(q, &DockWidget::topLevelChanged, q, &DockWidget::setToolbarFloatingStyle); QObject::connect(q, &DockWidget::topLevelChanged, q, &DockWidget::setToolbarFloatingStyle);
} }
void DockWidgetPrivate::setupScrollArea() void DockWidgetPrivate::setupScrollArea()
{ {
m_scrollArea = new QScrollArea(q); m_scrollArea = new QScrollArea(q);
m_scrollArea->setObjectName("dockWidgetScrollArea"); m_scrollArea->setObjectName("dockWidgetScrollArea");
m_scrollArea->setWidgetResizable(true); m_scrollArea->setWidgetResizable(true);
m_layout->addWidget(m_scrollArea); m_layout->addWidget(m_scrollArea);
} }
DockWidget::DockWidget(const QString &uniqueId, QWidget *parent) bool DockWidgetPrivate::createWidgetFromFactory()
{
if (!m_features.testFlag(DockWidget::DeleteContentOnClose))
return false;
if (!m_factory)
return false;
QWidget *w = m_factory->createWidget(q);
if (!w)
return false;
q->setWidget(w, m_factory->insertMode);
return true;
}
DockWidget::DockWidget(const QString &uniqueId, QWidget *parent)
: QFrame(parent) : QFrame(parent)
, d(new DockWidgetPrivate(this)) , d(new DockWidgetPrivate(this))
{ {
d->m_layout = new QBoxLayout(QBoxLayout::TopToBottom); d->m_layout = new QBoxLayout(QBoxLayout::TopToBottom);
d->m_layout->setContentsMargins(0, 0, 0, 0); d->m_layout->setContentsMargins(0, 0, 0, 0);
d->m_layout->setSpacing(0); d->m_layout->setSpacing(0);
@@ -180,21 +261,21 @@ namespace ADS
if (DockManager::testConfigFlag(DockManager::FocusHighlighting)) if (DockManager::testConfigFlag(DockManager::FocusHighlighting))
setFocusPolicy(Qt::ClickFocus); setFocusPolicy(Qt::ClickFocus);
} }
DockWidget::~DockWidget() DockWidget::~DockWidget()
{ {
qCInfo(adsLog) << Q_FUNC_INFO; qCInfo(adsLog) << Q_FUNC_INFO;
delete d; delete d;
} }
void DockWidget::setToggleViewActionChecked(bool checked) void DockWidget::setToggleViewActionChecked(bool checked)
{ {
d->m_toggleViewAction->setChecked(checked); d->m_toggleViewAction->setChecked(checked);
} }
void DockWidget::setWidget(QWidget *widget, eInsertMode insertMode) void DockWidget::setWidget(QWidget *widget, eInsertMode insertMode)
{ {
if (d->m_widget) if (d->m_widget)
takeWidget(); takeWidget();
@@ -210,10 +291,18 @@ namespace ADS
d->m_widget = widget; d->m_widget = widget;
d->m_widget->setProperty("dockWidgetContent", true); d->m_widget->setProperty("dockWidgetContent", true);
} }
QWidget *DockWidget::takeWidget() void DockWidget::setWidgetFactory(FactoryFunc createWidget, eInsertMode insertMode)
{ {
if (d->m_factory)
delete d->m_factory;
d->m_factory = new DockWidgetPrivate::WidgetFactory{createWidget, insertMode};
}
QWidget *DockWidget::takeWidget()
{
QWidget *w = nullptr; QWidget *w = nullptr;
if (d->m_scrollArea) { if (d->m_scrollArea) {
d->m_layout->removeWidget(d->m_scrollArea); d->m_layout->removeWidget(d->m_scrollArea);
@@ -231,55 +320,110 @@ namespace ADS
w->setParent(nullptr); w->setParent(nullptr);
return w; return w;
} }
QWidget *DockWidget::widget() const { return d->m_widget; } QWidget *DockWidget::widget() const
{
return d->m_widget;
}
DockWidgetTab *DockWidget::tabWidget() const { return d->m_tabWidget; } DockWidgetTab *DockWidget::tabWidget() const
{
return d->m_tabWidget;
}
void DockWidget::setFeatures(DockWidgetFeatures features) AutoHideDockContainer *DockWidget::autoHideDockContainer() const
{ {
if (!d->m_dockArea)
return nullptr;
return d->m_dockArea->autoHideDockContainer();
}
void DockWidget::setFeatures(DockWidgetFeatures features)
{
if (d->m_features == features) if (d->m_features == features)
return; return;
d->m_features = features; d->m_features = features;
emit featuresChanged(d->m_features); emit featuresChanged(d->m_features);
d->m_tabWidget->onDockWidgetFeaturesChanged(); d->m_tabWidget->onDockWidgetFeaturesChanged();
}
void DockWidget::setFeature(DockWidgetFeature flag, bool on) if (DockAreaWidget *dockArea = dockAreaWidget())
{ dockArea->onDockWidgetFeaturesChanged();
}
void DockWidget::setFeature(DockWidgetFeature flag, bool on)
{
auto currentFeatures = features(); auto currentFeatures = features();
internal::setFlag(currentFeatures, flag, on); internal::setFlag(currentFeatures, flag, on);
setFeatures(currentFeatures); setFeatures(currentFeatures);
} }
DockWidget::DockWidgetFeatures DockWidget::features() const { return d->m_features; } DockWidget::DockWidgetFeatures DockWidget::features() const
{
return d->m_features;
}
DockManager *DockWidget::dockManager() const { return d->m_dockManager; } DockManager *DockWidget::dockManager() const
{
return d->m_dockManager;
}
void DockWidget::setDockManager(DockManager *dockManager) { d->m_dockManager = dockManager; } void DockWidget::setDockManager(DockManager *dockManager)
{
d->m_dockManager = dockManager;
}
DockContainerWidget *DockWidget::dockContainer() const DockContainerWidget *DockWidget::dockContainer() const
{ {
if (d->m_dockArea) if (d->m_dockArea)
return d->m_dockArea->dockContainer(); return d->m_dockArea->dockContainer();
else else
return nullptr; return nullptr;
} }
DockAreaWidget *DockWidget::dockAreaWidget() const { return d->m_dockArea; } FloatingDockContainer *DockWidget::floatingDockContainer() const
{
auto container = dockContainer();
return container ? container->floatingWidget() : nullptr;
}
bool DockWidget::isFloating() const DockAreaWidget *DockWidget::dockAreaWidget() const
{ {
return d->m_dockArea;
}
AutoHideTab *DockWidget::sideTabWidget() const
{
return d->m_sideTabWidget;
}
void DockWidget::setSideTabWidget(AutoHideTab *sideTab) const
{
d->m_sideTabWidget = sideTab;
}
bool DockWidget::isAutoHide() const
{
return !d->m_sideTabWidget.isNull();
}
SideBarLocation DockWidget::autoHideLocation() const
{
return isAutoHide() ? autoHideDockContainer()->sideBarLocation() : SideBarNone;
}
bool DockWidget::isFloating() const
{
if (!isInFloatingContainer()) if (!isInFloatingContainer())
return false; return false;
return dockContainer()->topLevelDockWidget() == this; return dockContainer()->topLevelDockWidget() == this;
} }
bool DockWidget::isInFloatingContainer() const bool DockWidget::isInFloatingContainer() const
{ {
auto container = dockContainer(); auto container = dockContainer();
if (!container) if (!container)
return false; return false;
@@ -288,14 +432,20 @@ namespace ADS
return false; return false;
return true; return true;
} }
bool DockWidget::isClosed() const { return d->m_closed; } bool DockWidget::isClosed() const
{
return d->m_closed;
}
QAction *DockWidget::toggleViewAction() const { return d->m_toggleViewAction; } QAction *DockWidget::toggleViewAction() const
{
return d->m_toggleViewAction;
}
void DockWidget::setToggleViewActionMode(eToggleViewActionMode mode) void DockWidget::setToggleViewActionMode(eToggleViewActionMode mode)
{ {
if (ActionModeToggle == mode) { if (ActionModeToggle == mode) {
d->m_toggleViewAction->setCheckable(true); d->m_toggleViewAction->setCheckable(true);
d->m_toggleViewAction->setIcon(QIcon()); d->m_toggleViewAction->setIcon(QIcon());
@@ -303,43 +453,60 @@ namespace ADS
d->m_toggleViewAction->setCheckable(false); d->m_toggleViewAction->setCheckable(false);
d->m_toggleViewAction->setIcon(d->m_tabWidget->icon()); d->m_toggleViewAction->setIcon(d->m_tabWidget->icon());
} }
} }
void DockWidget::setMinimumSizeHintMode(eMinimumSizeHintMode mode) void DockWidget::setMinimumSizeHintMode(eMinimumSizeHintMode mode)
{ {
d->m_minimumSizeHintMode = mode; d->m_minimumSizeHintMode = mode;
} }
void DockWidget::toggleView(bool open) DockWidget::eMinimumSizeHintMode DockWidget::minimumSizeHintMode() const
{ {
// If the dock widget state is different, then we really need to toggle return d->m_minimumSizeHintMode;
// the state. If we are in the right state, then we simply make this }
// dock widget the current dock widget
bool DockWidget::isCentralWidget() const
{
return dockManager()->centralWidget() == this;
}
void DockWidget::toggleView(bool open)
{
// If the dock widget state is different, then we really need to toggle the state. If we are
// in the right state, then we simply make this dock widget the current dock widget.
auto autoHideContainer = autoHideDockContainer();
if (d->m_closed != !open) if (d->m_closed != !open)
toggleViewInternal(open); toggleViewInternal(open);
else if (open && d->m_dockArea) else if (open && d->m_dockArea && !autoHideContainer)
d->m_dockArea->setCurrentDockWidget(this); raise();
}
void DockWidget::toggleViewInternal(bool open) if (open && autoHideContainer)
{ autoHideContainer->collapseView(false);
}
void DockWidget::toggleViewInternal(bool open)
{
const DockContainerWidget *const beforeDockContainerWidget = dockContainer(); const DockContainerWidget *const beforeDockContainerWidget = dockContainer();
DockWidget *topLevelDockWidgetBefore = beforeDockContainerWidget DockWidget *topLevelDockWidgetBefore = beforeDockContainerWidget
? beforeDockContainerWidget->topLevelDockWidget() ? beforeDockContainerWidget->topLevelDockWidget()
: nullptr; : nullptr;
d->m_closed = !open;
if (open) if (open)
d->showDockWidget(); d->showDockWidget();
else else
d->hideDockWidget(); d->hideDockWidget();
d->m_closed = !open;
//d->m_toggleViewAction->blockSignals(true); //d->m_toggleViewAction->blockSignals(true);
d->m_toggleViewAction->setChecked(open); d->m_toggleViewAction->setChecked(open);
//d->m_toggleViewAction->blockSignals(false); //d->m_toggleViewAction->blockSignals(false);
if (d->m_dockArea) if (d->m_dockArea)
d->m_dockArea->toggleDockWidgetView(this, open); d->m_dockArea->toggleDockWidgetView(this, open);
if (d->m_dockArea->isAutoHide())
d->m_dockArea->autoHideDockContainer()->toggleView(open);
if (open && topLevelDockWidgetBefore) if (open && topLevelDockWidgetBefore)
DockWidget::emitTopLevelEventForWidget(topLevelDockWidgetBefore, false); DockWidget::emitTopLevelEventForWidget(topLevelDockWidgetBefore, false);
@@ -361,33 +528,34 @@ namespace ADS
emit closed(); emit closed();
emit viewToggled(open); emit viewToggled(open);
} }
void DockWidget::setDockArea(DockAreaWidget *dockArea) void DockWidget::setDockArea(DockAreaWidget *dockArea)
{ {
d->m_dockArea = dockArea; d->m_dockArea = dockArea;
d->m_toggleViewAction->setChecked(dockArea != nullptr && !this->isClosed()); d->m_toggleViewAction->setChecked(dockArea != nullptr && !isClosed());
} setParent(dockArea);
}
void DockWidget::saveState(QXmlStreamWriter &stream) const void DockWidget::saveState(QXmlStreamWriter &stream) const
{ {
stream.writeStartElement("widget"); stream.writeStartElement("widget");
stream.writeAttribute("name", objectName()); stream.writeAttribute("name", objectName());
stream.writeAttribute("closed", QVariant::fromValue(d->m_closed).toString()); stream.writeAttribute("closed", QVariant::fromValue(d->m_closed).toString());
stream.writeEndElement(); stream.writeEndElement();
} }
void DockWidget::flagAsUnassigned() void DockWidget::flagAsUnassigned()
{ {
d->m_closed = true; d->m_closed = true;
setParent(d->m_dockManager); setParent(d->m_dockManager);
setVisible(false); setVisible(false);
setDockArea(nullptr); setDockArea(nullptr);
tabWidget()->setParent(this); tabWidget()->setParent(this);
} }
bool DockWidget::event(QEvent *event) bool DockWidget::event(QEvent *event)
{ {
switch (event->type()) { switch (event->type()) {
case QEvent::Hide: case QEvent::Hide:
emit visibilityChanged(false); emit visibilityChanged(false);
@@ -397,32 +565,38 @@ namespace ADS
emit visibilityChanged(geometry().right() >= 0 && geometry().bottom() >= 0); emit visibilityChanged(geometry().right() >= 0 && geometry().bottom() >= 0);
break; break;
case QEvent::WindowTitleChange : case QEvent::WindowTitleChange: {
{
const auto title = windowTitle(); const auto title = windowTitle();
if (d->m_tabWidget) { if (d->m_tabWidget) {
d->m_tabWidget->setText(title); d->m_tabWidget->setText(title);
} }
if (d->m_sideTabWidget)
d->m_sideTabWidget->setText(title);
if (d->m_toggleViewAction) { if (d->m_toggleViewAction) {
d->m_toggleViewAction->setText(title); d->m_toggleViewAction->setText(title);
} }
if (d->m_dockArea) { if (d->m_dockArea) {
d->m_dockArea->markTitleBarMenuOutdated(); // update tabs menu d->m_dockArea->markTitleBarMenuOutdated(); // update tabs menu
} }
auto floatingWidget = floatingDockContainer();
if (floatingWidget)
floatingWidget->updateWindowTitle();
emit titleChanged(title); emit titleChanged(title);
} } break;
break;
default: default:
break; break;
} }
return Super::event(event); return Super::event(event);
} }
#ifndef QT_NO_TOOLTIP #ifndef QT_NO_TOOLTIP
void DockWidget::setTabToolTip(const QString &text) void DockWidget::setTabToolTip(const QString &text)
{ {
if (d->m_tabWidget) if (d->m_tabWidget)
d->m_tabWidget->setToolTip(text); d->m_tabWidget->setToolTip(text);
@@ -431,30 +605,40 @@ namespace ADS
if (d->m_dockArea) if (d->m_dockArea)
d->m_dockArea->markTitleBarMenuOutdated(); // update tabs menu d->m_dockArea->markTitleBarMenuOutdated(); // update tabs menu
} }
#endif #endif
void DockWidget::setIcon(const QIcon &icon) void DockWidget::setIcon(const QIcon &icon)
{ {
d->m_tabWidget->setIcon(icon); d->m_tabWidget->setIcon(icon);
if (d->m_sideTabWidget)
d->m_sideTabWidget->setIcon(icon);
if (!d->m_toggleViewAction->isCheckable()) if (!d->m_toggleViewAction->isCheckable())
d->m_toggleViewAction->setIcon(icon); d->m_toggleViewAction->setIcon(icon);
} }
QIcon DockWidget::icon() const { return d->m_tabWidget->icon(); } QIcon DockWidget::icon() const
{
return d->m_tabWidget->icon();
}
QToolBar *DockWidget::toolBar() const { return d->m_toolBar; } QToolBar *DockWidget::toolBar() const
{
return d->m_toolBar;
}
QToolBar *DockWidget::createDefaultToolBar() QToolBar *DockWidget::createDefaultToolBar()
{ {
if (!d->m_toolBar) if (!d->m_toolBar)
d->setupToolBar(); d->setupToolBar();
return d->m_toolBar; return d->m_toolBar;
} }
void DockWidget::setToolBar(QToolBar *toolBar) void DockWidget::setToolBar(QToolBar *toolBar)
{ {
if (d->m_toolBar) if (d->m_toolBar)
delete d->m_toolBar; delete d->m_toolBar;
@@ -462,46 +646,46 @@ namespace ADS
d->m_layout->insertWidget(0, d->m_toolBar); d->m_layout->insertWidget(0, d->m_toolBar);
connect(this, &DockWidget::topLevelChanged, this, &DockWidget::setToolbarFloatingStyle); connect(this, &DockWidget::topLevelChanged, this, &DockWidget::setToolbarFloatingStyle);
setToolbarFloatingStyle(isFloating()); setToolbarFloatingStyle(isFloating());
} }
void DockWidget::setToolBarStyle(Qt::ToolButtonStyle style, eState state) void DockWidget::setToolBarStyle(Qt::ToolButtonStyle style, eState state)
{ {
if (StateFloating == state) if (StateFloating == state)
d->m_toolBarStyleFloating = style; d->m_toolBarStyleFloating = style;
else else
d->m_toolBarStyleDocked = style; d->m_toolBarStyleDocked = style;
setToolbarFloatingStyle(isFloating()); setToolbarFloatingStyle(isFloating());
} }
Qt::ToolButtonStyle DockWidget::toolBarStyle(eState state) const Qt::ToolButtonStyle DockWidget::toolBarStyle(eState state) const
{ {
if (StateFloating == state) if (StateFloating == state)
return d->m_toolBarStyleFloating; return d->m_toolBarStyleFloating;
else else
return d->m_toolBarStyleDocked; return d->m_toolBarStyleDocked;
} }
void DockWidget::setToolBarIconSize(const QSize &iconSize, eState state) void DockWidget::setToolBarIconSize(const QSize &iconSize, eState state)
{ {
if (StateFloating == state) if (StateFloating == state)
d->m_toolBarIconSizeFloating = iconSize; d->m_toolBarIconSizeFloating = iconSize;
else else
d->m_toolBarIconSizeDocked = iconSize; d->m_toolBarIconSizeDocked = iconSize;
setToolbarFloatingStyle(isFloating()); setToolbarFloatingStyle(isFloating());
} }
QSize DockWidget::toolBarIconSize(eState state) const QSize DockWidget::toolBarIconSize(eState state) const
{ {
if (StateFloating == state) if (StateFloating == state)
return d->m_toolBarIconSizeFloating; return d->m_toolBarIconSizeFloating;
else else
return d->m_toolBarIconSizeDocked; return d->m_toolBarIconSizeDocked;
} }
void DockWidget::setToolbarFloatingStyle(bool floating) void DockWidget::setToolbarFloatingStyle(bool floating)
{ {
if (!d->m_toolBar) if (!d->m_toolBar)
return; return;
@@ -512,31 +696,35 @@ namespace ADS
auto buttonStyle = floating ? d->m_toolBarStyleFloating : d->m_toolBarStyleDocked; auto buttonStyle = floating ? d->m_toolBarStyleFloating : d->m_toolBarStyleDocked;
if (buttonStyle != d->m_toolBar->toolButtonStyle()) if (buttonStyle != d->m_toolBar->toolButtonStyle())
d->m_toolBar->setToolButtonStyle(buttonStyle); d->m_toolBar->setToolButtonStyle(buttonStyle);
} }
void DockWidget::emitTopLevelEventForWidget(DockWidget *topLevelDockWidget, bool floating) void DockWidget::emitTopLevelEventForWidget(DockWidget *topLevelDockWidget, bool floating)
{ {
if (topLevelDockWidget) { if (topLevelDockWidget) {
topLevelDockWidget->dockAreaWidget()->updateTitleBarVisibility(); topLevelDockWidget->dockAreaWidget()->updateTitleBarVisibility();
topLevelDockWidget->emitTopLevelChanged(floating); topLevelDockWidget->emitTopLevelChanged(floating);
} }
} }
void DockWidget::emitTopLevelChanged(bool floating) void DockWidget::emitTopLevelChanged(bool floating)
{ {
if (floating != d->m_isFloatingTopLevel) { if (floating != d->m_isFloatingTopLevel) {
d->m_isFloatingTopLevel = floating; d->m_isFloatingTopLevel = floating;
emit topLevelChanged(d->m_isFloatingTopLevel); emit topLevelChanged(d->m_isFloatingTopLevel);
} }
} }
void DockWidget::setClosedState(bool closed) { d->m_closed = closed; } void DockWidget::setClosedState(bool closed)
{
d->m_closed = closed;
}
QSize DockWidget::minimumSizeHint() const QSize DockWidget::minimumSizeHint() const
{ {
if (!d->m_widget) if (!d->m_widget)
return QSize(60, 40); return QSize(60, 40);
// TODO
DockContainerWidget *container = this->dockContainer(); DockContainerWidget *container = this->dockContainer();
if (!container || container->isFloating()) { if (!container || container->isFloating()) {
const QSize sh = d->m_widget->minimumSizeHint(); const QSize sh = d->m_widget->minimumSizeHint();
@@ -544,34 +732,57 @@ namespace ADS
return {std::max(s.width(), sh.width()), std::max(s.height(), sh.height())}; return {std::max(s.width(), sh.width()), std::max(s.height(), sh.height())};
} }
if (d->m_minimumSizeHintMode == DockWidget::MinimumSizeHintFromDockWidget) switch (d->m_minimumSizeHintMode) {
case MinimumSizeHintFromDockWidget:
return QSize(60, 40); return QSize(60, 40);
else case MinimumSizeHintFromContent:
return d->m_widget->minimumSizeHint(); return d->m_widget->minimumSizeHint();
case MinimumSizeHintFromDockWidgetMinimumSize:
return minimumSize();
case MinimumSizeHintFromContentMinimumSize:
return d->m_widget->minimumSize();
} }
void DockWidget::setFloating() return d->m_widget->minimumSizeHint();
{ }
void DockWidget::setFloating()
{
if (isClosed()) if (isClosed())
return; return;
if (isAutoHide())
dockAreaWidget()->setFloating();
else
d->m_tabWidget->detachDockWidget(); d->m_tabWidget->detachDockWidget();
} }
void DockWidget::deleteDockWidget()
{
auto manager = dockManager();
if (manager)
manager->removeDockWidget(this);
void DockWidget::deleteDockWidget()
{
dockManager()->removeDockWidget(this);
deleteLater(); deleteLater();
d->m_closed = true; d->m_closed = true;
} }
void DockWidget::closeDockWidget() void DockWidget::closeDockWidget()
{ {
closeDockWidgetInternal(true); closeDockWidgetInternal(true);
} }
bool DockWidget::closeDockWidgetInternal(bool forceClose) void DockWidget::requestCloseDockWidget()
{ {
if (features().testFlag(DockWidget::DockWidgetDeleteOnClose)
|| features().testFlag(DockWidget::CustomCloseHandling))
closeDockWidgetInternal(false);
else
toggleView(false);
}
bool DockWidget::closeDockWidgetInternal(bool forceClose)
{
if (!forceClose) if (!forceClose)
emit closeRequested(); emit closeRequested();
@@ -582,13 +793,17 @@ namespace ADS
// If the dock widget is floating, then we check if we also need to // If the dock widget is floating, then we check if we also need to
// delete the floating widget // delete the floating widget
if (isFloating()) { if (isFloating()) {
FloatingDockContainer* floatingWidget = internal::findParent< FloatingDockContainer *floatingWidget = internal::findParent<FloatingDockContainer *>(
FloatingDockContainer *>(this); this);
if (floatingWidget->dockWidgets().count() == 1) if (floatingWidget->dockWidgets().count() == 1)
floatingWidget->deleteLater(); floatingWidget->deleteLater();
else else
floatingWidget->hide(); floatingWidget->hide();
} }
if (d->m_dockArea && d->m_dockArea->isAutoHide())
d->m_dockArea->autoHideDockContainer()->cleanupAndDelete();
deleteDockWidget(); deleteDockWidget();
emit closed(); emit closed();
} else { } else {
@@ -596,70 +811,98 @@ namespace ADS
} }
return true; return true;
} }
void DockWidget::setTitleBarActions(QList<QAction *> actions) void DockWidget::setTitleBarActions(QList<QAction *> actions)
{ {
d->m_titleBarActions = actions; d->m_titleBarActions = actions;
} }
QList<QAction *> DockWidget::titleBarActions() const QList<QAction *> DockWidget::titleBarActions() const
{ {
return d->m_titleBarActions; return d->m_titleBarActions;
} }
void DockWidget::showFullScreen() void DockWidget::showFullScreen()
{ {
if (isFloating()) if (isFloating())
dockContainer()->floatingWidget()->showFullScreen(); dockContainer()->floatingWidget()->showFullScreen();
else else
Super::showFullScreen(); Super::showFullScreen();
} }
void DockWidget::showNormal() void DockWidget::showNormal()
{ {
if (isFloating()) if (isFloating())
dockContainer()->floatingWidget()->showNormal(); dockContainer()->floatingWidget()->showNormal();
else else
Super::showNormal(); Super::showNormal();
} }
bool DockWidget::isFullScreen() const bool DockWidget::isFullScreen() const
{ {
if (isFloating()) if (isFloating())
return dockContainer()->floatingWidget()->isFullScreen(); return dockContainer()->floatingWidget()->isFullScreen();
else else
return Super::isFullScreen(); return Super::isFullScreen();
} }
void DockWidget::setAsCurrentTab() void DockWidget::setAsCurrentTab()
{ {
if (d->m_dockArea && !isClosed()) if (d->m_dockArea && !isClosed())
d->m_dockArea->setCurrentDockWidget(this); d->m_dockArea->setCurrentDockWidget(this);
} }
bool DockWidget::isTabbed() const bool DockWidget::isTabbed() const
{ {
return d->m_dockArea && (d->m_dockArea->openDockWidgetsCount() > 1); return d->m_dockArea && (d->m_dockArea->openDockWidgetsCount() > 1);
} }
bool DockWidget::isCurrentTab() const bool DockWidget::isCurrentTab() const
{ {
return d->m_dockArea && (d->m_dockArea->currentDockWidget() == this); return d->m_dockArea && (d->m_dockArea->currentDockWidget() == this);
} }
void DockWidget::raise() void DockWidget::raise()
{ {
if (isClosed()) if (isClosed())
return; return;
setAsCurrentTab(); setAsCurrentTab();
if (isInFloatingContainer()) if (isInFloatingContainer()) {
{
auto floatingWindow = window(); auto floatingWindow = window();
floatingWindow->raise(); floatingWindow->raise();
floatingWindow->activateWindow(); floatingWindow->activateWindow();
} }
}
void DockWidget::setAutoHide(bool enable, SideBarLocation location, int tabIndex)
{
if (!DockManager::testAutoHideConfigFlag(DockManager::AutoHideFeatureEnabled))
return;
// Do nothing if nothing changes
if (enable == isAutoHide() && location == autoHideLocation())
return;
auto dockArea = dockAreaWidget();
if (!enable) {
dockArea->setAutoHide(false);
} else if (isAutoHide()) {
autoHideDockContainer()->moveToNewSideBarLocation(location);
} else {
auto area = (SideBarNone == location) ? dockArea->calculateSideTabBarArea() : location;
dockContainer()->createAndSetupAutoHideContainer(area, this, tabIndex);
} }
}
void DockWidget::toggleAutoHide(SideBarLocation location)
{
if (!DockManager::testAutoHideConfigFlag(DockManager::AutoHideFeatureEnabled))
return;
setAutoHide(!isAutoHide(), location);
}
} // namespace ADS } // namespace ADS

View File

@@ -21,6 +21,9 @@ class DockContainerWidget;
class DockAreaWidget; class DockAreaWidget;
class DockContainerWidgetPrivate; class DockContainerWidgetPrivate;
class FloatingDockContainer; class FloatingDockContainer;
class AutoHideTab;
class AutoHideDockContainer;
class AutoHideSideBar;
/** /**
* The QDockWidget class provides a widget that can be docked inside a * The QDockWidget class provides a widget that can be docked inside a
@@ -49,6 +52,8 @@ protected:
friend class DockWidgetTab; friend class DockWidgetTab;
friend class DockWidgetTabPrivate; friend class DockWidgetTabPrivate;
friend class DockAreaTitleBarPrivate; friend class DockAreaTitleBarPrivate;
friend class AutoHideDockContainer;
friend AutoHideSideBar;
/** /**
* Assigns the dock manager that manages this dock widget * Assigns the dock manager that manages this dock widget
@@ -120,14 +125,28 @@ public:
using Super = QFrame; using Super = QFrame;
enum DockWidgetFeature { enum DockWidgetFeature {
DockWidgetClosable = 0x01,///< dock widget has a close button DockWidgetClosable = 0x001, ///< 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 DockWidgetMovable
DockWidgetFloatable = 0x04, = 0x002, ///< dock widget is movable and can be moved to a new position in the current dock container
DockWidgetDeleteOnClose = 0x08, ///< deletes the dock widget when it is closed DockWidgetFloatable = 0x004, ///< dock widget can be dragged into a floating window
CustomCloseHandling = 0x10, DockWidgetDeleteOnClose = 0x008, ///< deletes the dock widget when it is closed
DefaultDockWidgetFeatures = DockWidgetClosable | DockWidgetMovable | DockWidgetFloatable, CustomCloseHandling
AllDockWidgetFeatures = DefaultDockWidgetFeatures | DockWidgetDeleteOnClose | CustomCloseHandling, = 0x010, ///< clicking the close button will not close the dock widget but emits the closeRequested() signal instead
NoDockWidgetFeatures = 0x00 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) Q_DECLARE_FLAGS(DockWidgetFeatures, DockWidgetFeature)
@@ -158,10 +177,18 @@ public:
* To ensure, that a dock widget does not block resizing, the dock widget * To ensure, that a dock widget does not block resizing, the dock widget
* reimplements minimumSizeHint() function to return a very small minimum * reimplements minimumSizeHint() function to return a very small minimum
* size hint. If you would like to adhere the minimumSizeHint() from the * size hint. If you would like to adhere the minimumSizeHint() from the
* content widget, the set the minimumSizeHintMode() to * content widget, then set the minimumSizeHintMode() to
* MinimumSizeHintFromContent. * 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. * This mode configures the behavior of the toggle view action.
@@ -205,7 +232,7 @@ public:
/** /**
* Sets the widget for the dock widget to widget. * Sets the widget for the dock widget to widget.
* The InsertMode defines how the widget is inserted into the dock 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 * 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+ * 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 * 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); 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 * Remove the widget from the dock and give ownership back to the caller
*/ */
@@ -269,17 +308,50 @@ public:
*/ */
DockContainerWidget *dockContainer() const; 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 * Returns the dock area widget this dock widget belongs to or 0
* if this dock widget has not been docked yet * if this dock widget has not been docked yet
*/ */
DockAreaWidget *dockAreaWidget() const; 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. * This property holds whether the dock widget is floating.
* A dock widget is only floating, if it is the one and only widget inside * A dock widget is only floating, if it is the one and only widget inside of a floating
* of a floating container. If there are more than one dock widget in a * container. If there are more than one dock widget in a floating container, the all dock
* floating container, the all dock widgets are docked and not floating. * widgets are docked and not floating.
*/ */
bool isFloating() const; bool isFloating() const;
@@ -314,6 +386,16 @@ public:
*/ */
void setMinimumSizeHintMode(eMinimumSizeHintMode mode); 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 * Sets the dock widget icon that is shown in tabs and in toggle view
* actions * actions
@@ -431,7 +513,7 @@ public: // reimplements QFrame
/** /**
* This property controls whether the dock widget is open or closed. * 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); void toggleView(bool open = true);
@@ -468,26 +550,42 @@ public: // reimplements QFrame
*/ */
void closeDockWidget(); 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. * Shows the widget in full-screen mode.
* Normally this function only affects windows. To make the interface * Normally this function only affects windows. To make the interface compatible to QDockWidget,
* compatible to QDockWidget, this function also maximizes a floating * this function also maximizes a floating dock widget.
* dock widget.
* *
* \note Full-screen mode works fine under Windows, but has certain * \note Full-screen mode works fine under Windows, but has certain problems (doe not work)
* problems (doe not work) under X (Linux). These problems are due to * under X (Linux). These problems are due to limitations of the ICCCM protocol that specifies
* limitations of the ICCCM protocol that specifies the communication * the communication between X11 clients and the window manager. ICCCM simply does not
* between X11 clients and the window manager. ICCCM simply does not
* understand the concept of non-decorated full-screen windows. * understand the concept of non-decorated full-screen windows.
*/ */
void showFullScreen(); void showFullScreen();
/** /**
* This function complements showFullScreen() to restore the widget * This function complements showFullScreen() to restore the widget after it has been in full
* after it has been in full screen mode. * screen mode.
*/ */
void showNormal(); 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: signals:
/** /**
* This signal is emitted if the dock widget is opened or closed * This signal is emitted if the dock widget is opened or closed

View File

@@ -7,13 +7,13 @@
#include "ads_globals_p.h" #include "ads_globals_p.h"
#include "advanceddockingsystemtr.h" #include "advanceddockingsystemtr.h"
#include "dockareawidget.h" #include "dockareawidget.h"
#include "dockfocuscontroller.h"
#include "dockmanager.h" #include "dockmanager.h"
#include "dockoverlay.h" #include "dockoverlay.h"
#include "dockwidget.h" #include "dockwidget.h"
#include "elidinglabel.h" #include "elidinglabel.h"
#include "floatingdockcontainer.h" #include "floatingdockcontainer.h"
#include "floatingdragpreview.h" #include "floatingdragpreview.h"
#include "iconprovider.h"
#include <utils/theme/theme.h> #include <utils/theme/theme.h>
@@ -23,26 +23,27 @@
#include <QLoggingCategory> #include <QLoggingCategory>
#include <QMenu> #include <QMenu>
#include <QMouseEvent> #include <QMouseEvent>
#include <QPainter>
#include <QPushButton> #include <QPushButton>
#include <QSplitter> #include <QSplitter>
#include <QStyle> #include <QStyle>
#include <QStyleOption> #include <QStyleOption>
#include <QToolButton>
#include <QPainter>
#include <QStylePainter> #include <QStylePainter>
#include <QToolButton>
#include <iostream> #include <iostream>
namespace ADS namespace ADS {
{
using TabLabelType = ElidingLabel;
/** static const char *const g_locationProperty = "Location";
using TabLabelType = ElidingLabel;
/**
* Private data class of DockWidgetTab class (pimpl) * Private data class of DockWidgetTab class (pimpl)
*/ */
class DockWidgetTabPrivate class DockWidgetTabPrivate
{ {
public: public:
DockWidgetTab *q; DockWidgetTab *q;
DockWidget *m_dockWidget = nullptr; DockWidget *m_dockWidget = nullptr;
QLabel *m_iconLabel = nullptr; QLabel *m_iconLabel = nullptr;
@@ -56,6 +57,7 @@ namespace ADS
QIcon m_icon; QIcon m_icon;
TabButton *m_closeButton = nullptr; TabButton *m_closeButton = nullptr;
QPoint m_tabDragStartPosition; QPoint m_tabDragStartPosition;
QSize m_iconSize;
/** /**
* Private data constructor * Private data constructor
@@ -95,19 +97,7 @@ namespace ADS
/** /**
* Creates the close button as QPushButton or as QToolButton * Creates the close button as QPushButton or as QToolButton
*/ */
TabButton *createCloseButton() const TabButton *createCloseButton() const { return new TabButton(); }
{
/*
if (testConfigFlag(DockManager::TabCloseButtonIsToolButton)) {
auto button = new QToolButton();
button->setAutoRaise(true);
return button;
} else {
return new QPushButton();
}
*/
return new TabButton();
}
template<typename T> template<typename T>
AbstractFloatingWidget *createFloatingWidget(T *widget, bool opaqueUndocking) AbstractFloatingWidget *createFloatingWidget(T *widget, bool opaqueUndocking)
@@ -123,6 +113,33 @@ namespace ADS
} }
} }
/**
* Update the close button visibility from current feature/config
*/
void updateCloseButtonVisibility(bool active)
{
bool dockWidgetClosable = m_dockWidget->features().testFlag(DockWidget::DockWidgetClosable);
bool activeTabHasCloseButton = testConfigFlag(DockManager::ActiveTabHasCloseButton);
bool allTabsHaveCloseButton = testConfigFlag(DockManager::AllTabsHaveCloseButton);
bool tabHasCloseButton = (activeTabHasCloseButton && active) | allTabsHaveCloseButton;
m_closeButton->setVisible(dockWidgetClosable && tabHasCloseButton);
}
/**
* Update the size policy of the close button depending on the
* RetainTabSizeWhenCloseButtonHidden feature
*/
void updateCloseButtonSizePolicy()
{
auto features = m_dockWidget->features();
auto sizePolicy = m_closeButton->sizePolicy();
sizePolicy.setRetainSizeWhenHidden(
features.testFlag(DockWidget::DockWidgetClosable)
&& testConfigFlag(DockManager::RetainTabSizeWhenCloseButtonHidden));
m_closeButton->setSizePolicy(sizePolicy);
}
/** /**
* Saves the drag start position in global and local coordinates * Saves the drag start position in global and local coordinates
*/ */
@@ -131,38 +148,69 @@ namespace ADS
m_globalDragStartMousePosition = globalPos; m_globalDragStartMousePosition = globalPos;
m_dragStartMousePosition = q->mapFromGlobal(globalPos); m_dragStartMousePosition = q->mapFromGlobal(globalPos);
} }
}; // class DockWidgetTabPrivate
DockWidgetTabPrivate::DockWidgetTabPrivate(DockWidgetTab *parent) /**
: q(parent) * Update the icon in case the icon size changed
{} */
void updateIcon()
void DockWidgetTabPrivate::createLayout()
{ {
if (!m_iconLabel || m_icon.isNull())
return;
if (m_iconSize.isValid())
m_iconLabel->setPixmap(m_icon.pixmap(m_iconSize));
else
m_iconLabel->setPixmap(
m_icon.pixmap(q->style()->pixelMetric(QStyle::PM_SmallIconSize, nullptr, q)));
m_iconLabel->setVisible(true);
}
/**
* Convenience function for access to the dock manager dock focus controller
*/
DockFocusController *focusController() const
{
return m_dockWidget->dockManager()->dockFocusController();
}
/**
* 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, &DockWidgetTab::onAutoHideToActionClicked);
return action;
}
}; // class DockWidgetTabPrivate
DockWidgetTabPrivate::DockWidgetTabPrivate(DockWidgetTab *parent)
: q(parent)
{}
void DockWidgetTabPrivate::createLayout()
{
m_titleLabel = new TabLabelType(); m_titleLabel = new TabLabelType();
m_titleLabel->setElideMode(Qt::ElideRight); m_titleLabel->setElideMode(Qt::ElideRight);
m_titleLabel->setText(m_dockWidget->windowTitle()); m_titleLabel->setText(m_dockWidget->windowTitle());
m_titleLabel->setObjectName("dockWidgetTabLabel"); m_titleLabel->setObjectName("dockWidgetTabLabel");
m_titleLabel->setAlignment(Qt::AlignCenter); m_titleLabel->setAlignment(Qt::AlignCenter);
QObject::connect(m_titleLabel, QObject::connect(m_titleLabel, &ElidingLabel::elidedChanged, q, &DockWidgetTab::elidedChanged);
&ElidingLabel::elidedChanged,
q,
&DockWidgetTab::elidedChanged);
m_closeButton = createCloseButton(); m_closeButton = createCloseButton();
m_closeButton->setObjectName("tabCloseButton"); m_closeButton->setObjectName("tabCloseButton");
internal::setButtonIcon(m_closeButton, internal::setButtonIcon(m_closeButton, QStyle::SP_TitleBarCloseButton, TabCloseIcon);
QStyle::SP_TitleBarCloseButton,
TabCloseIcon);
m_closeButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); m_closeButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
m_closeButton->setIconSize(QSize(11, 11)); m_closeButton->setIconSize(QSize(11, 11));
m_closeButton->setFixedSize(QSize(17, 17)); m_closeButton->setFixedSize(QSize(17, 17));
q->onDockWidgetFeaturesChanged(); m_closeButton->setFocusPolicy(Qt::NoFocus);
updateCloseButtonSizePolicy();
internal::setToolTip(m_closeButton, Tr::tr("Close Tab")); internal::setToolTip(m_closeButton, Tr::tr("Close Tab"));
QObject::connect(m_closeButton, QObject::connect(m_closeButton, &QAbstractButton::clicked, q, &DockWidgetTab::closeRequested);
&QAbstractButton::clicked,
q,
&DockWidgetTab::closeRequested);
QFontMetrics fontMetrics(m_titleLabel->font()); QFontMetrics fontMetrics(m_titleLabel->font());
int spacing = qRound(fontMetrics.height() / 4.0); int spacing = qRound(fontMetrics.height() / 4.0);
@@ -182,10 +230,10 @@ namespace ADS
m_closeButton->setCheckable(true); m_closeButton->setCheckable(true);
m_titleLabel->setVisible(true); m_titleLabel->setVisible(true);
} }
void DockWidgetTabPrivate::moveTab(QMouseEvent *event) void DockWidgetTabPrivate::moveTab(QMouseEvent *event)
{ {
event->accept(); event->accept();
QPoint distance = event->globalPosition().toPoint() - m_globalDragStartMousePosition; QPoint distance = event->globalPosition().toPoint() - m_globalDragStartMousePosition;
distance.setY(0); distance.setY(0);
@@ -194,64 +242,63 @@ namespace ADS
targetPos.rx() = qMin(q->parentWidget()->rect().right() - q->width() + 1, targetPos.rx()); targetPos.rx() = qMin(q->parentWidget()->rect().right() - q->width() + 1, targetPos.rx());
q->move(targetPos); q->move(targetPos);
q->raise(); q->raise();
} }
bool DockWidgetTabPrivate::startFloating(eDragState draggingState) bool DockWidgetTabPrivate::startFloating(eDragState draggingState)
{ {
auto dockContainer = m_dockWidget->dockContainer(); auto dockContainer = m_dockWidget->dockContainer();
qCInfo(adsLog) << "isFloating " << dockContainer->isFloating(); // If this is the last dock widget inside of this floating widget, then it does not make any
qCInfo(adsLog) << "areaCount " << dockContainer->dockAreaCount(); // sense, to make it floating because it is already floating.
qCInfo(adsLog) << "widgetCount " << m_dockWidget->dockAreaWidget()->dockWidgetsCount();
// if this is the last dock widget inside of this floating widget,
// then it does not make any sense, to make it floating because
// it is already floating
if (dockContainer->isFloating() && (dockContainer->visibleDockAreaCount() == 1) if (dockContainer->isFloating() && (dockContainer->visibleDockAreaCount() == 1)
&& (m_dockWidget->dockAreaWidget()->dockWidgetsCount() == 1)) { && (m_dockWidget->dockAreaWidget()->dockWidgetsCount() == 1))
return false; return false;
}
qCInfo(adsLog) << "startFloating";
m_dragState = draggingState; m_dragState = draggingState;
AbstractFloatingWidget *floatingWidget = nullptr; AbstractFloatingWidget *floatingWidget = nullptr;
bool opaqueUndocking = DockManager::testConfigFlag(DockManager::OpaqueUndocking) bool createContainer = (DraggingFloatingWidget != draggingState);
|| (DraggingFloatingWidget != draggingState);
// If section widget has multiple tabs, we take only one tab // If section widget has multiple tabs, we take only one tab. If it has only one single tab,
// If it has only one single tab, we can move the complete // we can move the complete dock area into floating widget.
// dock area into floating widget
QSize size; QSize size;
if (m_dockArea->dockWidgetsCount() > 1) { if (m_dockArea->dockWidgetsCount() > 1) {
floatingWidget = createFloatingWidget(m_dockWidget, opaqueUndocking); floatingWidget = createFloatingWidget(m_dockWidget, createContainer);
size = m_dockWidget->size(); size = m_dockWidget->size();
} else { } else {
floatingWidget = createFloatingWidget(m_dockArea, opaqueUndocking); floatingWidget = createFloatingWidget(m_dockArea, createContainer);
size = m_dockArea->size(); size = m_dockArea->size();
} }
if (DraggingFloatingWidget == draggingState) { if (DraggingFloatingWidget == draggingState) {
floatingWidget->startFloating(m_dragStartMousePosition, size, DraggingFloatingWidget, q); floatingWidget->startFloating(m_dragStartMousePosition, size, DraggingFloatingWidget, q);
auto Overlay = m_dockWidget->dockManager()->containerOverlay(); auto overlay = m_dockWidget->dockManager()->containerOverlay();
Overlay->setAllowedAreas(OuterDockAreas); overlay->setAllowedAreas(OuterDockAreas);
this->m_floatingWidget = floatingWidget; m_floatingWidget = floatingWidget;
qApp->postEvent(m_dockWidget,
new QEvent((QEvent::Type) internal::g_dockedWidgetDragStartEvent));
} else { } else {
floatingWidget->startFloating(m_dragStartMousePosition, size, DraggingInactive, nullptr); floatingWidget->startFloating(m_dragStartMousePosition, size, DraggingInactive, nullptr);
} }
return true; return true;
} }
TabButton::TabButton(QWidget *parent)
TabButton::TabButton(QWidget *parent)
: TabButtonType(parent) : TabButtonType(parent)
, m_active(false) , m_active(false)
, m_focus(false) , m_focus(false)
{} {}
void TabButton::setActive(bool value) { m_active = value; } void TabButton::setActive(bool value)
void TabButton::setFocus(bool value) { m_focus = value; } {
m_active = value;
}
void TabButton::setFocus(bool value)
{
m_focus = value;
}
void TabButton::paintEvent(QPaintEvent *event) void TabButton::paintEvent(QPaintEvent *event)
{ {
Q_UNUSED(event) Q_UNUSED(event)
QStylePainter p(this); QStylePainter p(this);
@@ -266,44 +313,46 @@ namespace ADS
if (m_focus) if (m_focus)
mode = QIcon::Mode::Selected; mode = QIcon::Mode::Selected;
const QPoint iconPosition = rect().center() - QPoint(iconSize().width() * 0.5, const QPoint iconPosition = rect().center()
iconSize().height() * 0.5); - QPoint(iconSize().width() * 0.5, iconSize().height() * 0.5);
p.drawPixmap(iconPosition, icon().pixmap(iconSize(), mode)); p.drawPixmap(iconPosition, icon().pixmap(iconSize(), mode));
} }
DockWidgetTab::DockWidgetTab(DockWidget *dockWidget, QWidget *parent)
DockWidgetTab::DockWidgetTab(DockWidget *dockWidget, QWidget *parent)
: QFrame(parent) : QFrame(parent)
, d(new DockWidgetTabPrivate(this)) , d(new DockWidgetTabPrivate(this))
{ {
setAttribute(Qt::WA_NoMousePropagation, true); setAttribute(Qt::WA_NoMousePropagation, true);
d->m_dockWidget = dockWidget; d->m_dockWidget = dockWidget;
d->createLayout(); d->createLayout();
if (DockManager::testConfigFlag(DockManager::FocusHighlighting)) setFocusPolicy(Qt::NoFocus);
setFocusPolicy(Qt::ClickFocus); }
}
DockWidgetTab::~DockWidgetTab() DockWidgetTab::~DockWidgetTab()
{ {
qCInfo(adsLog) << Q_FUNC_INFO; qCInfo(adsLog) << Q_FUNC_INFO;
delete d; delete d;
} }
void DockWidgetTab::mousePressEvent(QMouseEvent *event) void DockWidgetTab::mousePressEvent(QMouseEvent *event)
{ {
if (event->button() == Qt::LeftButton) { if (event->button() == Qt::LeftButton) {
event->accept(); event->accept();
d->saveDragStartMousePosition(event->globalPosition().toPoint()); d->saveDragStartMousePosition(event->globalPosition().toPoint());
d->m_dragState = DraggingMousePressed; d->m_dragState = DraggingMousePressed;
if (DockManager::testConfigFlag(DockManager::FocusHighlighting)) {
d->focusController()->setDockWidgetTabPressed(true);
d->focusController()->setDockWidgetTabFocused(this);
}
emit clicked(); emit clicked();
return; return;
} }
Super::mousePressEvent(event); Super::mousePressEvent(event);
} }
void DockWidgetTab::mouseReleaseEvent(QMouseEvent *event) void DockWidgetTab::mouseReleaseEvent(QMouseEvent *event)
{ {
if (event->button() == Qt::LeftButton) { if (event->button() == Qt::LeftButton) {
auto currentDragState = d->m_dragState; auto currentDragState = d->m_dragState;
d->m_globalDragStartMousePosition = QPoint(); d->m_globalDragStartMousePosition = QPoint();
@@ -314,47 +363,62 @@ namespace ADS
case DraggingTab: case DraggingTab:
// End of tab moving, emit signal // End of tab moving, emit signal
if (d->m_dockArea) { if (d->m_dockArea) {
event->accept();
emit moved(event->globalPosition().toPoint()); emit moved(event->globalPosition().toPoint());
} }
break; break;
case DraggingFloatingWidget: case DraggingFloatingWidget:
event->accept();
d->m_floatingWidget->finishDragging(); d->m_floatingWidget->finishDragging();
break; break;
default:; // do nothing default:
if (DockManager::testConfigFlag(DockManager::FocusHighlighting))
d->focusController()->setDockWidgetTabPressed(false);
break;
}
} else if (event->button() == Qt::MiddleButton) {
if (DockManager::testConfigFlag(DockManager::MiddleMouseButtonClosesTab)
&& d->m_dockWidget->features().testFlag(DockWidget::DockWidgetClosable)) {
// Only attempt to close if the mouse is still
// on top of the widget, to allow the user to cancel.
if (rect().contains(mapFromGlobal(QCursor::pos()))) {
event->accept();
emit closeRequested();
}
} }
} }
Super::mouseReleaseEvent(event); Super::mouseReleaseEvent(event);
} }
void DockWidgetTab::mouseMoveEvent(QMouseEvent *event) void DockWidgetTab::mouseMoveEvent(QMouseEvent *event)
{ {
if (!(event->buttons() & Qt::LeftButton) || d->isDraggingState(DraggingInactive)) { if (!(event->buttons() & Qt::LeftButton) || d->isDraggingState(DraggingInactive)) {
d->m_dragState = DraggingInactive; d->m_dragState = DraggingInactive;
Super::mouseMoveEvent(event); Super::mouseMoveEvent(event);
return; return;
} }
// move floating window // Move floating window
if (d->isDraggingState(DraggingFloatingWidget)) { if (d->isDraggingState(DraggingFloatingWidget)) {
d->m_floatingWidget->moveFloating(); d->m_floatingWidget->moveFloating();
Super::mouseMoveEvent(event); Super::mouseMoveEvent(event);
return; return;
} }
// move tab // Move tab
if (d->isDraggingState(DraggingTab)) { if (d->isDraggingState(DraggingTab)) {
// Moving the tab is always allowed because it does not mean moving the // Moving the tab is always allowed because it does not mean moving the dock widget around
// dock widget around
d->moveTab(event); d->moveTab(event);
} }
auto mappedPos = mapToParent(event->pos()); auto mappedPos = mapToParent(event->pos());
bool mouseOutsideBar = (mappedPos.x() < 0) || (mappedPos.x() > parentWidget()->rect().right()); bool mouseOutsideBar = (mappedPos.x() < 0) || (mappedPos.x() > parentWidget()->rect().right());
// Maybe a fixed drag distance is better here ? // Maybe a fixed drag distance is better here ?
int dragDistanceY = qAbs(d->m_globalDragStartMousePosition.y() - event->globalPosition().toPoint().y()); int dragDistanceY = qAbs(d->m_globalDragStartMousePosition.y()
- event->globalPosition().toPoint().y());
if (dragDistanceY >= DockManager::startDragDistance() || mouseOutsideBar) { if (dragDistanceY >= DockManager::startDragDistance() || mouseOutsideBar) {
// If this is the last dock area in a dock container with only // If this is the last dock area in a dock container with only
// one single dock widget it does not make sense to move it to a new // one single dock widget it does not make sense to move it to a new
@@ -366,27 +430,24 @@ namespace ADS
} }
// Floating is only allowed for widgets that are floatable // Floating is only allowed for widgets that are floatable
// If we do non opaque undocking, then can create the drag preview // We can create the drag preview if the widget is movable.
// if the widget is movable.
auto features = d->m_dockWidget->features(); auto features = d->m_dockWidget->features();
if (features.testFlag(DockWidget::DockWidgetFloatable) if (features.testFlag(DockWidget::DockWidgetFloatable)
|| (features.testFlag(DockWidget::DockWidgetMovable) || (features.testFlag(DockWidget::DockWidgetMovable))) {
&& !DockManager::testConfigFlag(DockManager::OpaqueUndocking))) {
// If we undock, we need to restore the initial position of this // If we undock, we need to restore the initial position of this
// tab because it looks strange if it remains on its dragged position // tab because it looks strange if it remains on its dragged position
if (d->isDraggingState(DraggingTab) if (d->isDraggingState(DraggingTab))
&& !DockManager::testConfigFlag(DockManager::OpaqueUndocking))
parentWidget()->layout()->update(); parentWidget()->layout()->update();
d->startFloating(); d->startFloating();
} }
return; return;
} else if (d->m_dockArea->openDockWidgetsCount() > 1 } else if (d->m_dockArea->openDockWidgetsCount() > 1
&& (event->globalPosition().toPoint() - d->m_globalDragStartMousePosition).manhattanLength() && (event->globalPosition().toPoint() - d->m_globalDragStartMousePosition)
.manhattanLength()
>= QApplication::startDragDistance()) // Wait a few pixels before start moving >= QApplication::startDragDistance()) // Wait a few pixels before start moving
{ {
// If we start dragging the tab, we save its initial position to // If we start dragging the tab, we save its initial position to restore it later
// restore it later
if (DraggingTab != d->m_dragState) if (DraggingTab != d->m_dragState)
d->m_tabDragStartPosition = this->pos(); d->m_tabDragStartPosition = this->pos();
@@ -395,49 +456,83 @@ namespace ADS
} }
Super::mouseMoveEvent(event); Super::mouseMoveEvent(event);
} }
void DockWidgetTab::contextMenuEvent(QContextMenuEvent *event) void DockWidgetTab::contextMenuEvent(QContextMenuEvent *event)
{ {
event->accept(); event->accept();
if (d->isDraggingState(DraggingFloatingWidget)) if (d->isDraggingState(DraggingFloatingWidget))
return; return;
d->saveDragStartMousePosition(event->globalPos()); d->saveDragStartMousePosition(event->globalPos());
QMenu menu(this);
const bool isFloatable = d->m_dockWidget->features().testFlag(DockWidget::DockWidgetFloatable); const bool isFloatable = d->m_dockWidget->features().testFlag(DockWidget::DockWidgetFloatable);
const bool isNotOnlyTabInContainer = !d->m_dockArea->dockContainer()->hasTopLevelDockWidget(); const bool isNotOnlyTabInContainer = !d->m_dockArea->dockContainer()->hasTopLevelDockWidget();
const bool isTopLevelArea = d->m_dockArea->isTopLevelArea();
const bool isDetachable = isFloatable && isNotOnlyTabInContainer; const bool isDetachable = isFloatable && isNotOnlyTabInContainer;
auto action = menu.addAction(Tr::tr("Detach"), this, &DockWidgetTab::detachDockWidget); QMenu menu(this);
action->setEnabled(isDetachable);
menu.addSeparator(); if (!isTopLevelArea) {
action = menu.addAction(Tr::tr("Close"), this, &DockWidgetTab::closeRequested); QAction *detachAction = menu.addAction(tr("Detach"));
action->setEnabled(isClosable()); detachAction->connect(detachAction,
menu.addAction(Tr::tr("Close Others"), this, &DockWidgetTab::closeOtherTabsRequested); &QAction::triggered,
menu.exec(event->globalPos()); this,
&DockWidgetTab::detachDockWidget);
detachAction->setEnabled(isDetachable);
if (DockManager::testAutoHideConfigFlag(DockManager::AutoHideFeatureEnabled)) {
QAction *pinAction = menu.addAction(tr("Pin"));
pinAction->connect(pinAction,
&QAction::triggered,
this,
&DockWidgetTab::autoHideDockWidget);
auto isPinnable = d->m_dockWidget->features().testFlag(DockWidget::DockWidgetPinnable);
pinAction->setEnabled(isPinnable);
auto subMenu = menu.addMenu(tr("Pin To..."));
subMenu->setEnabled(isPinnable);
d->createAutoHideToAction(tr("Top"), SideBarTop, subMenu);
d->createAutoHideToAction(tr("Left"), SideBarLeft, subMenu);
d->createAutoHideToAction(tr("Right"), SideBarRight, subMenu);
d->createAutoHideToAction(tr("Bottom"), SideBarBottom, subMenu);
}
} }
bool DockWidgetTab::isActiveTab() const { return d->m_isActiveTab; } menu.addSeparator();
void DockWidgetTab::setActiveTab(bool active) QAction *closeAction = menu.addAction(tr("Close"));
{ closeAction->connect(closeAction, &QAction::triggered, this, &DockWidgetTab::closeRequested);
bool dockWidgetClosable = d->m_dockWidget->features().testFlag( closeAction->setEnabled(isClosable());
DockWidget::DockWidgetClosable);
bool activeTabHasCloseButton = d->testConfigFlag(DockManager::ActiveTabHasCloseButton);
bool allTabsHaveCloseButton = d->testConfigFlag(DockManager::AllTabsHaveCloseButton);
bool tabHasCloseButton = (activeTabHasCloseButton && active) | allTabsHaveCloseButton;
d->m_closeButton->setVisible(dockWidgetClosable && tabHasCloseButton);
d->m_closeButton->setActive(active); if (d->m_dockArea->openDockWidgetsCount() > 1) {
QAction *closeOthersAction = menu.addAction(tr("Close Others"));
closeOthersAction->connect(closeOthersAction,
&QAction::triggered,
this,
&DockWidgetTab::closeOtherTabsRequested);
}
menu.exec(event->globalPos());
}
bool DockWidgetTab::isActiveTab() const
{
return d->m_isActiveTab;
}
void DockWidgetTab::setActiveTab(bool active)
{
d->updateCloseButtonVisibility(active);
d->m_closeButton->setActive(active); // TODO
// Focus related stuff // Focus related stuff
if (DockManager::testConfigFlag(DockManager::FocusHighlighting) if (DockManager::testConfigFlag(DockManager::FocusHighlighting)
&& !d->m_dockWidget->dockManager()->isRestoringState()) { && !d->m_dockWidget->dockManager()->isRestoringState()) {
bool updateFocusStyle = false; bool updateFocusStyle = false;
if (active && !hasFocus()) { if (active && !hasFocus()) {
setFocus(Qt::OtherFocusReason); d->focusController()->setDockWidgetTabFocused(this);
updateFocusStyle = true; updateFocusStyle = true;
} }
if (d->m_isActiveTab == active) { if (d->m_isActiveTab == active) {
@@ -455,16 +550,25 @@ namespace ADS
updateGeometry(); updateGeometry();
emit activeTabChanged(); emit activeTabChanged();
} }
DockWidget *DockWidgetTab::dockWidget() const { return d->m_dockWidget; } DockWidget *DockWidgetTab::dockWidget() const
{
return d->m_dockWidget;
}
void DockWidgetTab::setDockAreaWidget(DockAreaWidget *dockArea) { d->m_dockArea = dockArea; } void DockWidgetTab::setDockAreaWidget(DockAreaWidget *dockArea)
{
d->m_dockArea = dockArea;
}
DockAreaWidget *DockWidgetTab::dockAreaWidget() const { return d->m_dockArea; } DockAreaWidget *DockWidgetTab::dockAreaWidget() const
{
return d->m_dockArea;
}
void DockWidgetTab::setIcon(const QIcon &icon) void DockWidgetTab::setIcon(const QIcon &icon)
{ {
QBoxLayout *boxLayout = qobject_cast<QBoxLayout *>(layout()); QBoxLayout *boxLayout = qobject_cast<QBoxLayout *>(layout());
if (!d->m_iconLabel && icon.isNull()) if (!d->m_iconLabel && icon.isNull())
return; return;
@@ -490,82 +594,123 @@ namespace ADS
icon.pixmap(style()->pixelMetric(QStyle::PM_SmallIconSize, nullptr, this))); icon.pixmap(style()->pixelMetric(QStyle::PM_SmallIconSize, nullptr, this)));
d->m_iconLabel->setVisible(true); d->m_iconLabel->setVisible(true);
} }
} }
const QIcon &DockWidgetTab::icon() const { return d->m_icon; } const QIcon &DockWidgetTab::icon() const
{
return d->m_icon;
}
QString DockWidgetTab::text() const { return d->m_titleLabel->text(); } QString DockWidgetTab::text() const
{
return d->m_titleLabel->text();
}
void DockWidgetTab::mouseDoubleClickEvent(QMouseEvent *event) void DockWidgetTab::mouseDoubleClickEvent(QMouseEvent *event)
{ {
if (event->button() == Qt::LeftButton) {
// If this is the last dock area in a dock container it does not make // If this is the last dock area in a dock container it does not make
// sense to move it to a new floating widget and leave this one empty // sense to move it to a new floating widget and leave this one empty
if ((!d->m_dockArea->dockContainer()->isFloating() || d->m_dockArea->dockWidgetsCount() > 1) if ((!d->m_dockArea->dockContainer()->isFloating() || d->m_dockArea->dockWidgetsCount() > 1)
&& d->m_dockWidget->features().testFlag(DockWidget::DockWidgetFloatable)) { && d->m_dockWidget->features().testFlag(DockWidget::DockWidgetFloatable)) {
event->accept();
d->saveDragStartMousePosition(event->globalPosition().toPoint()); d->saveDragStartMousePosition(event->globalPosition().toPoint());
d->startFloating(DraggingInactive); d->startFloating(DraggingInactive);
} }
}
Super::mouseDoubleClickEvent(event); Super::mouseDoubleClickEvent(event);
} }
void DockWidgetTab::setVisible(bool visible) void DockWidgetTab::setVisible(bool visible)
{ {
// Just here for debugging to insert debug output visible &= !d->m_dockWidget->features().testFlag(DockWidget::NoTab);
Super::setVisible(visible); Super::setVisible(visible);
} }
void DockWidgetTab::setText(const QString &title) { d->m_titleLabel->setText(title); } void DockWidgetTab::setText(const QString &title)
bool DockWidgetTab::isTitleElided() const { return d->m_titleLabel->isElided(); } {
d->m_titleLabel->setText(title);
}
bool DockWidgetTab::isTitleElided() const
{
return d->m_titleLabel->isElided();
}
bool DockWidgetTab::isClosable() const bool DockWidgetTab::isClosable() const
{ {
return d->m_dockWidget return d->m_dockWidget && d->m_dockWidget->features().testFlag(DockWidget::DockWidgetClosable);
&& d->m_dockWidget->features().testFlag(DockWidget::DockWidgetClosable); }
}
void DockWidgetTab::detachDockWidget() void DockWidgetTab::detachDockWidget()
{ {
if (!d->m_dockWidget->features().testFlag(DockWidget::DockWidgetFloatable)) if (!d->m_dockWidget->features().testFlag(DockWidget::DockWidgetFloatable))
return; return;
d->saveDragStartMousePosition(QCursor::pos()); d->saveDragStartMousePosition(QCursor::pos());
d->startFloating(DraggingInactive); d->startFloating(DraggingInactive);
} }
bool DockWidgetTab::event(QEvent *event) void DockWidgetTab::autoHideDockWidget()
{ {
d->m_dockWidget->setAutoHide(true);
}
void DockWidgetTab::onAutoHideToActionClicked()
{
int location = sender()->property(g_locationProperty).toInt();
d->m_dockWidget->toggleAutoHide((SideBarLocation) location);
}
bool DockWidgetTab::event(QEvent *event)
{
#ifndef QT_NO_TOOLTIP #ifndef QT_NO_TOOLTIP
if (event->type() == QEvent::ToolTipChange) { if (event->type() == QEvent::ToolTipChange) {
const auto text = toolTip(); const auto text = toolTip();
d->m_titleLabel->setToolTip(text); d->m_titleLabel->setToolTip(text);
if (d->m_iconLabel)
d->m_iconLabel->setToolTip(text);
} }
#endif #endif
if (event->type() == QEvent::StyleChange)
d->updateIcon();
return Super::event(event); return Super::event(event);
} }
void DockWidgetTab::onDockWidgetFeaturesChanged() void DockWidgetTab::onDockWidgetFeaturesChanged()
{ {
auto features = d->m_dockWidget->features(); d->updateCloseButtonSizePolicy();
auto sizePolicy = d->m_closeButton->sizePolicy(); d->updateCloseButtonVisibility(isActiveTab());
sizePolicy.setRetainSizeWhenHidden( }
features.testFlag(DockWidget::DockWidgetClosable)
&& d->testConfigFlag(DockManager::RetainTabSizeWhenCloseButtonHidden));
d->m_closeButton->setSizePolicy(sizePolicy);
}
void DockWidgetTab::setElideMode(Qt::TextElideMode mode) void DockWidgetTab::setElideMode(Qt::TextElideMode mode)
{ {
d->m_titleLabel->setElideMode(mode); d->m_titleLabel->setElideMode(mode);
} }
void DockWidgetTab::updateStyle() void DockWidgetTab::updateStyle()
{ {
if (DockManager::testConfigFlag(DockManager::FocusHighlighting)) if (DockManager::testConfigFlag(DockManager::FocusHighlighting))
d->m_closeButton->setFocus(property("focused").toBool()); d->m_closeButton->setFocus(property("focused").toBool());
internal::repolishStyle(this, internal::RepolishDirectChildren); internal::repolishStyle(this, internal::RepolishDirectChildren);
} }
QSize DockWidgetTab::iconSize() const
{
return d->m_iconSize;
}
void DockWidgetTab::setIconSize(const QSize &size)
{
if (size == d->m_iconSize)
return;
d->m_iconSize = size;
d->updateIcon();
emit iconSizeChanged();
}
} // namespace ADS } // namespace ADS

View File

@@ -35,24 +35,27 @@ private:
bool m_focus; bool m_focus;
}; };
/** /**
* A dock widget tab that shows a title and an icon. * 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 * The dock widget tab is shown in the dock area title bar to switch between tabbed dock widgets.
* tabbed dock widgets
*/ */
class ADS_EXPORT DockWidgetTab : public QFrame class ADS_EXPORT DockWidgetTab : public QFrame
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(bool activeTab READ isActiveTab WRITE setActiveTab NOTIFY activeTabChanged) Q_PROPERTY(bool activeTab READ isActiveTab WRITE setActiveTab NOTIFY activeTabChanged)
Q_PROPERTY(QSize iconSize READ iconSize WRITE setIconSize NOTIFY iconSizeChanged)
private: private:
DockWidgetTabPrivate *d; ///< private data (pimpl) DockWidgetTabPrivate *d; ///< private data (pimpl)
friend class DockWidgetTabPrivate; friend class DockWidgetTabPrivate;
friend class DockWidget; friend class DockWidget;
friend class DockManager; friend class DockManager;
friend class AutoHideDockContainer;
void onDockWidgetFeaturesChanged(); void onDockWidgetFeaturesChanged();
void detachDockWidget(); void detachDockWidget();
void autoHideDockWidget();
void onAutoHideToActionClicked();
protected: protected:
void mousePressEvent(QMouseEvent *event) override; void mousePressEvent(QMouseEvent *event) override;
@@ -95,8 +98,7 @@ public:
DockWidget *dockWidget() const; DockWidget *dockWidget() const;
/** /**
* Sets the dock area widget the dockWidget returned by dockWidget() * Sets the dock area widget the dockWidget returned by dockWidget() function belongs to.
* function belongs to.
*/ */
void setDockAreaWidget(DockAreaWidget *dockArea); void setDockAreaWidget(DockAreaWidget *dockArea);
@@ -152,6 +154,19 @@ public:
*/ */
void updateStyle(); 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; void setVisible(bool visible) override;
signals: signals:
@@ -161,6 +176,7 @@ signals:
void closeOtherTabsRequested(); void closeOtherTabsRequested();
void moved(const QPoint &globalPosition); void moved(const QPoint &globalPosition);
void elidedChanged(bool elided); void elidedChanged(bool elided);
void iconSizeChanged();
}; // class DockWidgetTab }; // class DockWidgetTab
} // namespace ADS } // namespace ADS

View File

@@ -6,11 +6,13 @@
#include <QMouseEvent> #include <QMouseEvent>
namespace ADS { namespace ADS {
/**
/**
* Private data of public ElidingLabel * Private data of public ElidingLabel
*/ */
struct ElidingLabelPrivate class ElidingLabelPrivate
{ {
public:
ElidingLabel *q; ElidingLabel *q;
Qt::TextElideMode m_elideMode = Qt::ElideNone; Qt::TextElideMode m_elideMode = Qt::ElideNone;
QString m_text; QString m_text;
@@ -26,10 +28,10 @@ namespace ADS {
* Convenience function to check if the * Convenience function to check if the
*/ */
bool isModeElideNone() const { return Qt::ElideNone == m_elideMode; } bool isModeElideNone() const { return Qt::ElideNone == m_elideMode; }
}; };
void ElidingLabelPrivate::elideText(int width) void ElidingLabelPrivate::elideText(int width)
{ {
if (isModeElideNone()) if (isModeElideNone())
return; return;
@@ -39,110 +41,110 @@ namespace ADS {
str = m_text.at(0); str = m_text.at(0);
bool wasElided = m_isElided; bool wasElided = m_isElided;
m_isElided = str != m_text; m_isElided = (str != m_text);
if (m_isElided != wasElided) if (m_isElided != wasElided)
emit q->elidedChanged(m_isElided); emit q->elidedChanged(m_isElided);
q->QLabel::setText(str); q->QLabel::setText(str);
} }
ElidingLabel::ElidingLabel(QWidget *parent, Qt::WindowFlags flags) ElidingLabel::ElidingLabel(QWidget *parent, Qt::WindowFlags flags)
: QLabel(parent, flags) : QLabel(parent, flags)
, d(new ElidingLabelPrivate(this)) , d(new ElidingLabelPrivate(this))
{} {}
ElidingLabel::ElidingLabel(const QString &text, QWidget *parent, Qt::WindowFlags flags) ElidingLabel::ElidingLabel(const QString &text, QWidget *parent, Qt::WindowFlags flags)
: QLabel(text, parent, flags) : QLabel(text, parent, flags)
, d(new ElidingLabelPrivate(this)) , d(new ElidingLabelPrivate(this))
{ {
d->m_text = text; d->m_text = text;
internal::setToolTip(this, text); internal::setToolTip(this, text);
} }
ElidingLabel::~ElidingLabel() ElidingLabel::~ElidingLabel()
{ {
delete d; delete d;
} }
Qt::TextElideMode ElidingLabel::elideMode() const Qt::TextElideMode ElidingLabel::elideMode() const
{ {
return d->m_elideMode; return d->m_elideMode;
} }
void ElidingLabel::setElideMode(Qt::TextElideMode mode) void ElidingLabel::setElideMode(Qt::TextElideMode mode)
{ {
d->m_elideMode = mode; d->m_elideMode = mode;
d->elideText(size().width()); d->elideText(size().width());
} }
bool ElidingLabel::isElided() const bool ElidingLabel::isElided() const
{ {
return d->m_isElided; return d->m_isElided;
} }
void ElidingLabel::mouseReleaseEvent(QMouseEvent *event) void ElidingLabel::mouseReleaseEvent(QMouseEvent *event)
{ {
Super::mouseReleaseEvent(event); Super::mouseReleaseEvent(event);
if (event->button() != Qt::LeftButton) if (event->button() != Qt::LeftButton)
return; return;
emit clicked(); emit clicked();
} }
void ElidingLabel::mouseDoubleClickEvent(QMouseEvent *event) void ElidingLabel::mouseDoubleClickEvent(QMouseEvent *event)
{ {
Q_UNUSED(event) Q_UNUSED(event)
emit doubleClicked(); emit doubleClicked();
Super::mouseDoubleClickEvent(event); Super::mouseDoubleClickEvent(event);
} }
void ElidingLabel::resizeEvent(QResizeEvent *event) void ElidingLabel::resizeEvent(QResizeEvent *event)
{ {
if (!d->isModeElideNone()) if (!d->isModeElideNone())
d->elideText(event->size().width()); d->elideText(event->size().width());
Super::resizeEvent(event); Super::resizeEvent(event);
} }
bool ElidingLabel::hasPixmap() const QSize ElidingLabel::minimumSizeHint() const
{ {
return !pixmap().isNull();
}
QSize ElidingLabel::minimumSizeHint() const
{
if (hasPixmap() || d->isModeElideNone()) if (hasPixmap() || d->isModeElideNone())
return QLabel::minimumSizeHint(); return QLabel::minimumSizeHint();
const QFontMetrics &fm = fontMetrics(); const QFontMetrics &fm = fontMetrics();
QSize size(fm.horizontalAdvance(d->m_text.left(2) + ""), fm.height()); QSize size(fm.horizontalAdvance(d->m_text.left(2) + ""), fm.height());
return size; return size;
} }
QSize ElidingLabel::sizeHint() const QSize ElidingLabel::sizeHint() const
{ {
if (hasPixmap() || d->isModeElideNone()) if (hasPixmap() || d->isModeElideNone())
return QLabel::sizeHint(); return QLabel::sizeHint();
const QFontMetrics &fm = fontMetrics(); const QFontMetrics &fm = fontMetrics();
QSize size(fm.horizontalAdvance(d->m_text), QLabel::sizeHint().height()); QSize size(fm.horizontalAdvance(d->m_text), QLabel::sizeHint().height());
return size; return size;
} }
void ElidingLabel::setText(const QString &text) void ElidingLabel::setText(const QString &text)
{ {
d->m_text = text; d->m_text = text;
if (d->isModeElideNone()) { if (d->isModeElideNone()) {
Super::setText(text); Super::setText(text);
} else { } else {
internal::setToolTip(this, text); internal::setToolTip(this, text);
d->elideText(this->size().width()); d->elideText(size().width());
}
} }
}
QString ElidingLabel::text() const QString ElidingLabel::text() const
{ {
return d->m_text; return d->m_text;
} }
bool ElidingLabel::hasPixmap() const
{
return !pixmap().isNull();
}
} // namespace ADS } // namespace ADS

View File

@@ -9,20 +9,19 @@
namespace ADS { namespace ADS {
struct ElidingLabelPrivate; class ElidingLabelPrivate;
/** /**
* A QLabel that supports eliding text. * A QLabel that supports eliding text.
* Because the functions setText() and text() are no virtual functions setting * Because the functions setText() and text() are no virtual functions setting and reading the
* and reading the text via a pointer to the base class QLabel does not work * text via a pointer to the base class QLabel does not work properly.
* properly
*/ */
class ADS_EXPORT ElidingLabel : public QLabel class ADS_EXPORT ElidingLabel : public QLabel
{ {
Q_OBJECT Q_OBJECT
private: private:
ElidingLabelPrivate *d; ElidingLabelPrivate *d;
friend struct ElidingLabelPrivate; friend class ElidingLabelPrivate;
protected: protected:
void mouseReleaseEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override;
@@ -32,8 +31,10 @@ protected:
public: public:
using Super = QLabel; using Super = QLabel;
ElidingLabel(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::Widget); ElidingLabel(const QString &text,
QWidget *parent = nullptr,
Qt::WindowFlags flags = Qt::WindowFlags());
~ElidingLabel() override; ~ElidingLabel() override;
/** /**

View File

@@ -9,7 +9,6 @@
#include "dockmanager.h" #include "dockmanager.h"
#include "dockoverlay.h" #include "dockoverlay.h"
#include "dockwidget.h" #include "dockwidget.h"
#include "linux/floatingwidgettitlebar.h"
#include <utils/hostosinfo.h> #include <utils/hostosinfo.h>
@@ -19,6 +18,9 @@
#pragma comment(lib, "User32.lib") #pragma comment(lib, "User32.lib")
#endif #endif
#endif #endif
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
#include "linux/floatingwidgettitlebar.h"
#endif
#include <QAbstractButton> #include <QAbstractButton>
#include <QAction> #include <QAction>
@@ -30,8 +32,8 @@
#include <QMouseEvent> #include <QMouseEvent>
#include <QPointer> #include <QPointer>
namespace ADS namespace ADS {
{
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#if 0 // set to 1 if you need this function for debugging #if 0 // set to 1 if you need this function for debugging
/** /**
@@ -329,15 +331,15 @@ static const char* windowsMessageString(int messageId)
#endif #endif
#endif #endif
AbstractFloatingWidget::~AbstractFloatingWidget() = default; AbstractFloatingWidget::~AbstractFloatingWidget() = default;
static unsigned int zOrderCounter = 0; static unsigned int zOrderCounter = 0;
/** /**
* Private data class of FloatingDockContainer class (pimpl) * Private data class of FloatingDockContainer class (pimpl)
*/ */
class FloatingDockContainerPrivate class FloatingDockContainerPrivate
{ {
public: public:
FloatingDockContainer *q; FloatingDockContainer *q;
DockContainerWidget *m_dockContainer = nullptr; DockContainerWidget *m_dockContainer = nullptr;
unsigned int m_zOrderIndex = ++zOrderCounter; unsigned int m_zOrderIndex = ++zOrderCounter;
@@ -348,8 +350,13 @@ static const char* windowsMessageString(int messageId)
DockAreaWidget *m_singleDockArea = nullptr; DockAreaWidget *m_singleDockArea = nullptr;
QPoint m_dragStartPos; QPoint m_dragStartPos;
bool m_hiding = false; bool m_hiding = false;
QWidget *m_mouseEventHandler = nullptr; // linux only bool m_autoHideChildren = true;
FloatingWidgetTitleBar *m_titleBar = nullptr; // linux only #if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
QWidget *m_mouseEventHandler = nullptr;
FloatingWidgetTitleBar *m_titleBar = nullptr;
bool m_isResizing = false;
bool m_mousePressed = false;
#endif
/** /**
* Private data constructor * Private data constructor
@@ -357,6 +364,7 @@ static const char* windowsMessageString(int messageId)
FloatingDockContainerPrivate(FloatingDockContainer *parent); FloatingDockContainerPrivate(FloatingDockContainer *parent);
void titleMouseReleaseEvent(); void titleMouseReleaseEvent();
void updateDropOverlays(const QPoint &globalPosition); void updateDropOverlays(const QPoint &globalPosition);
/** /**
@@ -372,48 +380,71 @@ static const char* windowsMessageString(int messageId)
*/ */
bool isState(eDragState stateId) const { return stateId == m_draggingState; } bool isState(eDragState stateId) const { return stateId == m_draggingState; }
void setState(eDragState stateId) { m_draggingState = stateId; } /**
* Sets the dragging state and posts a FloatingWidgetDragStartEvent if dragging starts.
*/
void setState(eDragState stateId)
{
if (m_draggingState == stateId)
return;
m_draggingState = stateId;
if (m_draggingState == DraggingFloatingWidget)
qApp->postEvent(q, new QEvent((QEvent::Type) internal::g_floatingWidgetDragStartEvent));
}
void setWindowTitle(const QString &text) void setWindowTitle(const QString &text)
{ {
if (Utils::HostOsInfo::isLinuxHost()) #if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
if (m_titleBar)
m_titleBar->setTitle(text); m_titleBar->setTitle(text);
else #else
q->setWindowTitle(text); q->setWindowTitle(text);
#endif
} }
/** /**
* Reflect the current dock widget title in the floating widget windowTitle() * Reflect the current dock widget title in the floating widget windowTitle()
* depending on the DockManager::FloatingContainerHasWidgetTitle flag * depending on the DockManager::FloatingContainerHasWidgetTitle flag.
*/ */
void reflectCurrentWidget(DockWidget *currentWidget) void reflectCurrentWidget(DockWidget *currentWidget)
{ {
// reflect CurrentWidget's title if configured to do so, otherwise display application name as window title // Reflect currentWidget's title if configured to do so, otherwise display application name as window title
if (testConfigFlag(DockManager::FloatingContainerHasWidgetTitle)) if (testConfigFlag(DockManager::FloatingContainerHasWidgetTitle))
setWindowTitle(currentWidget->windowTitle()); setWindowTitle(currentWidget->windowTitle());
else else
setWindowTitle(QApplication::applicationDisplayName()); setWindowTitle(floatingContainersTitle());
// reflect currentWidget's icon if configured to do so, otherwise display application icon as window icon // Reflect currentWidget's icon if configured to do so, otherwise display application icon as window icon
QIcon currentWidgetIcon = currentWidget->icon(); QIcon currentWidgetIcon = currentWidget->icon();
if (testConfigFlag(DockManager::FloatingContainerHasWidgetIcon) && !currentWidgetIcon.isNull()) if (testConfigFlag(DockManager::FloatingContainerHasWidgetIcon)
&& !currentWidgetIcon.isNull())
q->setWindowIcon(currentWidget->icon()); q->setWindowIcon(currentWidget->icon());
else else
q->setWindowIcon(QApplication::windowIcon()); q->setWindowIcon(QApplication::windowIcon());
} }
/** /**
* Handles escape key press when dragging around the floating widget * Handles escape key press when dragging around the floating widget.
*/ */
void handleEscapeKey(); void handleEscapeKey();
}; // class FloatingDockContainerPrivate
FloatingDockContainerPrivate::FloatingDockContainerPrivate(FloatingDockContainer *parent) /**
* Returns the title used by all FloatingContainer that do not reflect the title of the
* current dock widget.
*
* If no title was set with DockManager::setFloatingContainersTitle(),
* it returns QGuiApplication::applicationDisplayName().
*/
static QString floatingContainersTitle() { return DockManager::floatingContainersTitle(); }
}; // class FloatingDockContainerPrivate
FloatingDockContainerPrivate::FloatingDockContainerPrivate(FloatingDockContainer *parent)
: q(parent) : q(parent)
{} {}
void FloatingDockContainerPrivate::titleMouseReleaseEvent() void FloatingDockContainerPrivate::titleMouseReleaseEvent()
{ {
setState(DraggingInactive); setState(DraggingInactive);
if (!m_dropContainer) if (!m_dropContainer)
return; return;
@@ -424,6 +455,9 @@ static const char* windowsMessageString(int messageId)
if (!overlay->dropOverlayRect().isValid()) if (!overlay->dropOverlayRect().isValid())
overlay = m_dockManager->dockAreaOverlay(); overlay = m_dockManager->dockAreaOverlay();
// Do not resize if we drop into an autohide sidebar area to preserve the dock area size
// for the initial size of the auto hide area.
if (!internal::isSideBarArea(overlay->dropAreaUnderCursor())) {
// Resize the floating widget to the size of the highlighted drop area rectangle // Resize the floating widget to the size of the highlighted drop area rectangle
QRect rect = overlay->dropOverlayRect(); QRect rect = overlay->dropOverlayRect();
int frameWidth = (q->frameSize().width() - q->rect().width()) / 2; int frameWidth = (q->frameSize().width() - q->rect().width()) / 2;
@@ -434,18 +468,25 @@ static const char* windowsMessageString(int messageId)
q->setGeometry(QRect(topLeft, QSize(rect.width(), rect.height() - titleBarHeight))); q->setGeometry(QRect(topLeft, QSize(rect.width(), rect.height() - titleBarHeight)));
QApplication::processEvents(); QApplication::processEvents();
} }
}
m_dropContainer->dropFloatingWidget(q, QCursor::pos()); m_dropContainer->dropFloatingWidget(q, QCursor::pos());
} }
m_dockManager->containerOverlay()->hideOverlay(); m_dockManager->containerOverlay()->hideOverlay();
m_dockManager->dockAreaOverlay()->hideOverlay(); m_dockManager->dockAreaOverlay()->hideOverlay();
} }
void FloatingDockContainerPrivate::updateDropOverlays(const QPoint &globalPosition) void FloatingDockContainerPrivate::updateDropOverlays(const QPoint &globalPosition)
{ {
if (!q->isVisible() || !m_dockManager) if (!q->isVisible() || !m_dockManager)
return; return;
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
// Prevent display of drop overlays and docking as long as a modal dialog is active
if (qApp->activeModalWidget())
return;
#endif
auto containers = m_dockManager->dockContainers(); auto containers = m_dockManager->dockContainers();
DockContainerWidget *topContainer = nullptr; DockContainerWidget *topContainer = nullptr;
for (auto containerWidget : containers) { for (auto containerWidget : containers) {
@@ -473,10 +514,23 @@ static const char* windowsMessageString(int messageId)
} }
int visibleDockAreas = topContainer->visibleDockAreaCount(); int visibleDockAreas = topContainer->visibleDockAreaCount();
containerOverlay->setAllowedAreas(visibleDockAreas > 1 ? OuterDockAreas : AllDockAreas);
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 (m_dockContainer->features().testFlag(DockWidget::DockWidgetPinnable))
allowedContainerAreas |= AutoHideDockAreas;
containerOverlay->setAllowedAreas(allowedContainerAreas);
DockWidgetArea containerArea = containerOverlay->showOverlay(topContainer); DockWidgetArea containerArea = containerOverlay->showOverlay(topContainer);
containerOverlay->enableDropPreview(containerArea != InvalidDockWidgetArea); containerOverlay->enableDropPreview(containerArea != InvalidDockWidgetArea);
auto dockArea = topContainer->dockAreaAt(globalPosition);
if (dockArea && dockArea->isVisible() && visibleDockAreas > 0) { if (dockArea && dockArea->isVisible() && visibleDockAreas > 0) {
dockAreaOverlay->enableDropPreview(true); dockAreaOverlay->enableDropPreview(true);
dockAreaOverlay->setAllowedAreas((visibleDockAreas == 1) ? NoDockWidgetArea dockAreaOverlay->setAllowedAreas((visibleDockAreas == 1) ? NoDockWidgetArea
@@ -485,7 +539,7 @@ static const char* windowsMessageString(int messageId)
// A CenterDockWidgetArea for the dockAreaOverlay() indicates that the mouse is in // 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 // the title bar. If the ContainerArea is valid then we ignore the dock area of the
// dockAreaOverlay() and disable the drop preview // dockAreaOverlay() and disable the drop preview.
if ((area == CenterDockWidgetArea) && (containerArea != InvalidDockWidgetArea)) { if ((area == CenterDockWidgetArea) && (containerArea != InvalidDockWidgetArea)) {
dockAreaOverlay->enableDropPreview(false); dockAreaOverlay->enableDropPreview(false);
containerOverlay->enableDropPreview(true); containerOverlay->enableDropPreview(true);
@@ -495,22 +549,23 @@ static const char* windowsMessageString(int messageId)
} else { } else {
dockAreaOverlay->hideOverlay(); dockAreaOverlay->hideOverlay();
} }
} }
void FloatingDockContainerPrivate::handleEscapeKey() void FloatingDockContainerPrivate::handleEscapeKey()
{ {
qCInfo(adsLog) << Q_FUNC_INFO; qCInfo(adsLog) << Q_FUNC_INFO;
setState(DraggingInactive); setState(DraggingInactive);
m_dockManager->containerOverlay()->hideOverlay(); m_dockManager->containerOverlay()->hideOverlay();
m_dockManager->dockAreaOverlay()->hideOverlay(); m_dockManager->dockAreaOverlay()->hideOverlay();
} }
FloatingDockContainer::FloatingDockContainer(DockManager *dockManager) FloatingDockContainer::FloatingDockContainer(DockManager *dockManager)
: FloatingWidgetBaseType(dockManager) : FloatingWidgetBaseType(dockManager)
, d(new FloatingDockContainerPrivate(this)) , d(new FloatingDockContainerPrivate(this))
{ {
d->m_dockManager = dockManager; d->m_dockManager = dockManager;
d->m_dockContainer = new DockContainerWidget(dockManager, this); d->m_dockContainer = new DockContainerWidget(dockManager, this);
connect(d->m_dockContainer, connect(d->m_dockContainer,
&DockContainerWidget::dockAreasAdded, &DockContainerWidget::dockAreasAdded,
this, this,
@@ -520,92 +575,124 @@ static const char* windowsMessageString(int messageId)
this, this,
&FloatingDockContainer::onDockAreasAddedOrRemoved); &FloatingDockContainer::onDockAreasAddedOrRemoved);
#ifdef Q_OS_LINUX #if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
d->m_titleBar = new FloatingWidgetTitleBar(this); // Order here is really important. setWindowFlags() must come first otherwise the resize handles
setWindowFlags(windowFlags() | Qt::Tool); // on linux are missing from floating dock widgets.
setWindowFlags(Qt::Window | Qt::WindowMinMaxButtonsHint | Qt::FramelessWindowHint | Qt::Tool);
QDockWidget::setWidget(d->m_dockContainer); QDockWidget::setWidget(d->m_dockContainer);
QDockWidget::setFloating(true); //QDockWidget::setFloating(true);
QDockWidget::setFeatures(DockWidgetClosable | DockWidgetMovable | DockWidgetFloatable); QDockWidget::setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable
| QDockWidget::DockWidgetFloatable);
d->m_titleBar = new FloatingWidgetTitleBar(this);
setTitleBarWidget(d->m_titleBar); setTitleBarWidget(d->m_titleBar);
d->m_titleBar->enableCloseButton(isClosable());
d->m_titleBar->setMaximizedIcon(windowState() == Qt::WindowMaximized);
connect(d->m_titleBar, connect(d->m_titleBar,
&FloatingWidgetTitleBar::closeRequested, &FloatingWidgetTitleBar::closeRequested,
this, this,
&FloatingDockContainer::close); &FloatingDockContainer::close);
#else connect(d->m_titleBar,
&FloatingWidgetTitleBar::maximizeRequested,
this,
&FloatingDockContainer::onMaximizeRequest);
#else
setWindowFlags(Qt::Window | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint | Qt::Tool); setWindowFlags(Qt::Window | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint | Qt::Tool);
QBoxLayout *boxLayout = new QBoxLayout(QBoxLayout::TopToBottom); QBoxLayout *boxLayout = new QBoxLayout(QBoxLayout::TopToBottom);
boxLayout->setContentsMargins(0, 0, 0, 0); boxLayout->setContentsMargins(0, 0, 0, 0);
boxLayout->setSpacing(0); boxLayout->setSpacing(0);
setLayout(boxLayout); setLayout(boxLayout);
boxLayout->addWidget(d->m_dockContainer); boxLayout->addWidget(d->m_dockContainer);
#endif #endif
dockManager->registerFloatingWidget(this); dockManager->registerFloatingWidget(this);
} }
FloatingDockContainer::FloatingDockContainer(DockAreaWidget *dockArea) FloatingDockContainer::FloatingDockContainer(DockAreaWidget *dockArea)
: FloatingDockContainer(dockArea->dockManager()) : FloatingDockContainer(dockArea->dockManager())
{ {
d->m_dockContainer->addDockArea(dockArea); d->m_dockContainer->addDockArea(dockArea);
#ifdef Q_OS_LINUX
d->m_titleBar->enableCloseButton(isClosable());
#endif
if (auto dw = topLevelDockWidget()) if (auto dw = topLevelDockWidget())
dw->emitTopLevelChanged(true); dw->emitTopLevelChanged(true);
d->m_dockManager->notifyWidgetOrAreaRelocation(dockArea); d->m_dockManager->notifyWidgetOrAreaRelocation(dockArea);
} }
FloatingDockContainer::FloatingDockContainer(DockWidget *dockWidget) FloatingDockContainer::FloatingDockContainer(DockWidget *dockWidget)
: FloatingDockContainer(dockWidget->dockManager()) : FloatingDockContainer(dockWidget->dockManager())
{ {
d->m_dockContainer->addDockWidget(CenterDockWidgetArea, dockWidget); d->m_dockContainer->addDockWidget(CenterDockWidgetArea, dockWidget);
#ifdef Q_OS_LINUX
d->m_titleBar->enableCloseButton(isClosable());
#endif
if (auto dw = topLevelDockWidget()) if (auto dw = topLevelDockWidget())
dw->emitTopLevelChanged(true); dw->emitTopLevelChanged(true);
d->m_dockManager->notifyWidgetOrAreaRelocation(dockWidget); d->m_dockManager->notifyWidgetOrAreaRelocation(dockWidget);
} }
FloatingDockContainer::~FloatingDockContainer() FloatingDockContainer::~FloatingDockContainer()
{ {
qCInfo(adsLog) << Q_FUNC_INFO; qCInfo(adsLog) << Q_FUNC_INFO;
if (d->m_dockManager) if (d->m_dockManager)
d->m_dockManager->removeFloatingWidget(this); d->m_dockManager->removeFloatingWidget(this);
delete d; delete d;
} }
DockContainerWidget *FloatingDockContainer::dockContainer() const { return d->m_dockContainer; } DockContainerWidget *FloatingDockContainer::dockContainer() const
{
return d->m_dockContainer;
}
void FloatingDockContainer::changeEvent(QEvent *event) void FloatingDockContainer::changeEvent(QEvent *event)
{ {
QWidget::changeEvent(event); Super::changeEvent(event);
if ((event->type() == QEvent::ActivationChange) && isActiveWindow()) { switch (event->type()) {
qCInfo(adsLog) << Q_FUNC_INFO << "QEvent::ActivationChange"; case QEvent::ActivationChange:
if (isActiveWindow()) {
qCInfo(adsLog) << Q_FUNC_INFO << "Event::ActivationChange";
d->m_zOrderIndex = ++zOrderCounter; d->m_zOrderIndex = ++zOrderCounter;
return;
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
if (d->m_draggingState == DraggingFloatingWidget) {
d->titleMouseReleaseEvent();
d->m_draggingState = DraggingInactive;
} }
#endif
} }
break;
case QEvent::WindowStateChange:
// If the DockManager window is restored from minimized on Windows then the FloatingWidgets
// are not properly restored to maximized but to normal state.
// We simply check here, if the FloatingWidget was maximized before and if the DockManager
// is just leaving the minimized state. In this case, we restore the maximized state of
// this floating widget.
if (d->m_dockManager->isLeavingMinimizedState()) {
QWindowStateChangeEvent *ev = static_cast<QWindowStateChangeEvent *>(event);
if (ev->oldState().testFlag(Qt::WindowMaximized))
showMaximized();
}
break;
default:
break; // do nothing
}
}
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
bool FloatingDockContainer::nativeEvent(const QByteArray &eventType, void *message, qintptr *result) bool FloatingDockContainer::nativeEvent(const QByteArray &eventType, void *message, qintptr *result)
{ {
QWidget::nativeEvent(eventType, message, result); QWidget::nativeEvent(eventType, message, result);
MSG *msg = static_cast<MSG *>(message); MSG *msg = static_cast<MSG *>(message);
switch (msg->message) switch (msg->message) {
{ case WM_MOVING: {
case WM_MOVING:
{
if (d->isState(DraggingFloatingWidget)) if (d->isState(DraggingFloatingWidget))
d->updateDropOverlays(QCursor::pos()); d->updateDropOverlays(QCursor::pos());
} } break;
break;
case WM_NCLBUTTONDOWN: case WM_NCLBUTTONDOWN:
if (msg->wParam == HTCAPTION && d->isState(DraggingInactive)) if (msg->wParam == HTCAPTION && d->isState(DraggingInactive)) {
{
qCInfo(adsLog) << Q_FUNC_INFO << "WM_NCLBUTTONDOWN" << eventType; qCInfo(adsLog) << Q_FUNC_INFO << "WM_NCLBUTTONDOWN" << eventType;
d->m_dragStartPos = pos(); d->m_dragStartPos = pos();
d->setState(DraggingMousePressed); d->setState(DraggingMousePressed);
@@ -617,8 +704,7 @@ bool FloatingDockContainer::nativeEvent(const QByteArray &eventType, void *messa
break; break;
case WM_ENTERSIZEMOVE: case WM_ENTERSIZEMOVE:
if (d->isState(DraggingMousePressed)) if (d->isState(DraggingMousePressed)) {
{
qCInfo(adsLog) << Q_FUNC_INFO << "WM_ENTERSIZEMOVE" << eventType; qCInfo(adsLog) << Q_FUNC_INFO << "WM_ENTERSIZEMOVE" << eventType;
d->setState(DraggingFloatingWidget); d->setState(DraggingFloatingWidget);
d->updateDropOverlays(QCursor::pos()); d->updateDropOverlays(QCursor::pos());
@@ -626,8 +712,7 @@ bool FloatingDockContainer::nativeEvent(const QByteArray &eventType, void *messa
break; break;
case WM_EXITSIZEMOVE: case WM_EXITSIZEMOVE:
if (d->isState(DraggingFloatingWidget)) if (d->isState(DraggingFloatingWidget)) {
{
qCInfo(adsLog) << Q_FUNC_INFO << "WM_EXITSIZEMOVE" << eventType; qCInfo(adsLog) << Q_FUNC_INFO << "WM_EXITSIZEMOVE" << eventType;
if (GetAsyncKeyState(VK_ESCAPE) & 0x8000) if (GetAsyncKeyState(VK_ESCAPE) & 0x8000)
d->handleEscapeKey(); d->handleEscapeKey();
@@ -640,25 +725,43 @@ bool FloatingDockContainer::nativeEvent(const QByteArray &eventType, void *messa
} }
#endif #endif
void FloatingDockContainer::closeEvent(QCloseEvent *event) void FloatingDockContainer::closeEvent(QCloseEvent *event)
{ {
qCInfo(adsLog) << Q_FUNC_INFO; qCInfo(adsLog) << Q_FUNC_INFO << "closable" << isClosable();
d->setState(DraggingInactive); d->setState(DraggingInactive);
event->ignore(); event->ignore();
if (isClosable()) { if (!isClosable())
auto dw = topLevelDockWidget();
if (dw && dw->features().testFlag(DockWidget::DockWidgetDeleteOnClose)) {
if (!dw->closeDockWidgetInternal())
return; return;
}
this->hide(); bool hasOpenDockWidgets = false;
for (auto dockWidget : d->m_dockContainer->openedDockWidgets()) {
if (dockWidget->features().testFlag(DockWidget::DockWidgetDeleteOnClose)
|| dockWidget->features().testFlag(DockWidget::CustomCloseHandling)) {
bool closed = dockWidget->closeDockWidgetInternal();
if (!closed)
hasOpenDockWidgets = true;
} else {
dockWidget->toggleView(false);
} }
} }
void FloatingDockContainer::hideEvent(QHideEvent *event) if (hasOpenDockWidgets)
{ return;
// In Qt version after 5.9.2 there seems to be a bug that causes the QWidget::event() function
// to not receive any NonClientArea mouse events anymore after a close/show cycle. The bug is
// reported here: https://bugreports.qt.io/browse/QTBUG-73295
// The following code is a workaround for Qt versions > 5.9.2 that seems to work. Starting from
// Qt version 5.12.2 this seems to work again. But now the QEvent::NonClientAreaMouseButtonPress
// function returns always Qt::RightButton even if the left button was pressed.
hide();
}
void FloatingDockContainer::hideEvent(QHideEvent *event)
{
qCInfo(adsLog) << Q_FUNC_INFO;
Super::hideEvent(event); Super::hideEvent(event);
if (event->spontaneous()) if (event->spontaneous())
return; return;
@@ -667,6 +770,7 @@ bool FloatingDockContainer::nativeEvent(const QByteArray &eventType, void *messa
if (d->m_dockManager->isRestoringState()) if (d->m_dockManager->isRestoringState())
return; return;
if (d->m_autoHideChildren) {
d->m_hiding = true; d->m_hiding = true;
for (auto dockArea : d->m_dockContainer->openedDockAreas()) { for (auto dockArea : d->m_dockContainer->openedDockAreas()) {
for (auto dockWidget : dockArea->openedDockWidgets()) for (auto dockWidget : dockArea->openedDockWidgets())
@@ -674,49 +778,57 @@ bool FloatingDockContainer::nativeEvent(const QByteArray &eventType, void *messa
} }
d->m_hiding = false; d->m_hiding = false;
} }
}
void FloatingDockContainer::showEvent(QShowEvent *event)
{
qCInfo(adsLog) << Q_FUNC_INFO;
void FloatingDockContainer::showEvent(QShowEvent *event)
{
Super::showEvent(event); Super::showEvent(event);
#ifdef Q_OS_LINUX #if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
if (DockManager::testConfigFlag(DockManager::FocusHighlighting)) if (DockManager::testConfigFlag(DockManager::FocusHighlighting))
window()->activateWindow(); window()->activateWindow();
#endif #endif
} }
void FloatingDockContainer::startFloating(const QPoint &dragStartMousePos, void FloatingDockContainer::startFloating(const QPoint &dragStartMousePos,
const QSize &size, const QSize &size,
eDragState dragState, eDragState dragState,
QWidget *mouseEventHandler) QWidget *mouseEventHandler)
{ {
#ifndef Q_OS_LINUX #if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
Q_UNUSED(mouseEventHandler) if (!isMaximized()) {
#endif
resize(size); resize(size);
d->setState(dragState);
d->m_dragStartMousePosition = dragStartMousePos; d->m_dragStartMousePosition = dragStartMousePos;
}
#ifdef Q_OS_LINUX d->setState(dragState);
if (DraggingFloatingWidget == dragState) { if (dragState == DraggingFloatingWidget) {
setAttribute(Qt::WA_X11NetWmWindowTypeDock, true);
d->m_mouseEventHandler = mouseEventHandler; d->m_mouseEventHandler = mouseEventHandler;
if (d->m_mouseEventHandler) if (d->m_mouseEventHandler)
d->m_mouseEventHandler->grabMouse(); d->m_mouseEventHandler->grabMouse();
} }
#endif
if (!isMaximized())
moveFloating();
show();
#else
Q_UNUSED(mouseEventHandler)
resize(size);
d->m_dragStartMousePosition = dragStartMousePos;
d->setState(dragState);
moveFloating(); moveFloating();
show(); show();
} #endif
}
void FloatingDockContainer::moveFloating() void FloatingDockContainer::moveFloating()
{ {
const int borderSize = (frameSize().width() - size().width()) / 2; const int borderSize = (frameSize().width() - size().width()) / 2;
const QPoint moveToPos = QCursor::pos() - d->m_dragStartMousePosition const QPoint moveToPos = QCursor::pos() - d->m_dragStartMousePosition - QPoint(borderSize, 0);
- QPoint(borderSize, 0);
move(moveToPos); move(moveToPos);
switch (d->m_draggingState) switch (d->m_draggingState) {
{
case DraggingMousePressed: case DraggingMousePressed:
d->setState(DraggingFloatingWidget); d->setState(DraggingFloatingWidget);
d->updateDropOverlays(QCursor::pos()); d->updateDropOverlays(QCursor::pos());
@@ -724,26 +836,25 @@ bool FloatingDockContainer::nativeEvent(const QByteArray &eventType, void *messa
case DraggingFloatingWidget: case DraggingFloatingWidget:
d->updateDropOverlays(QCursor::pos()); d->updateDropOverlays(QCursor::pos());
// On macOS when hiding the DockAreaOverlay the application would set // On macOS when hiding the DockAreaOverlay the application would set the main window as
// the main window as the active window for some reason. This fixes // the active window for some reason. This fixes that by resetting the active window to
// that by resetting the active window to the floating widget after // the floating widget after updating the overlays.
// updating the overlays.
if (Utils::HostOsInfo::isMacHost()) if (Utils::HostOsInfo::isMacHost())
QApplication::setActiveWindow(this); activateWindow();
break; break;
default: default:
break; break;
} }
} }
bool FloatingDockContainer::isClosable() const bool FloatingDockContainer::isClosable() const
{ {
return d->m_dockContainer->features().testFlag(DockWidget::DockWidgetClosable); return d->m_dockContainer->features().testFlag(DockWidget::DockWidgetClosable);
} }
void FloatingDockContainer::onDockAreasAddedOrRemoved() void FloatingDockContainer::onDockAreasAddedOrRemoved()
{ {
qCInfo(adsLog) << Q_FUNC_INFO; qCInfo(adsLog) << Q_FUNC_INFO;
auto topLevelDockArea = d->m_dockContainer->topLevelDockArea(); auto topLevelDockArea = d->m_dockContainer->topLevelDockArea();
if (topLevelDockArea) { if (topLevelDockArea) {
@@ -762,15 +873,14 @@ bool FloatingDockContainer::nativeEvent(const QByteArray &eventType, void *messa
&FloatingDockContainer::onDockAreaCurrentChanged); &FloatingDockContainer::onDockAreaCurrentChanged);
d->m_singleDockArea = nullptr; d->m_singleDockArea = nullptr;
} }
d->setWindowTitle(QApplication::applicationDisplayName()); d->setWindowTitle(d->floatingContainersTitle());
setWindowIcon(QApplication::windowIcon()); setWindowIcon(QApplication::windowIcon());
} }
} }
void FloatingDockContainer::updateWindowTitle() void FloatingDockContainer::updateWindowTitle()
{ {
// If this floating container will be hidden, then updating the window // If this floating container will be hidden, updating the window title is not required anymore.
// title is not required anymore
if (d->m_hiding) if (d->m_hiding)
return; return;
@@ -781,97 +891,105 @@ bool FloatingDockContainer::nativeEvent(const QByteArray &eventType, void *messa
d->setWindowTitle(QApplication::applicationDisplayName()); d->setWindowTitle(QApplication::applicationDisplayName());
setWindowIcon(QApplication::windowIcon()); setWindowIcon(QApplication::windowIcon());
} }
} }
void FloatingDockContainer::onDockAreaCurrentChanged(int index) void FloatingDockContainer::onDockAreaCurrentChanged(int index)
{ {
Q_UNUSED(index) Q_UNUSED(index)
DockWidget *currentWidget = d->m_singleDockArea->currentDockWidget(); DockWidget *currentWidget = d->m_singleDockArea->currentDockWidget();
d->reflectCurrentWidget(currentWidget); d->reflectCurrentWidget(currentWidget);
} }
bool FloatingDockContainer::restoreState(DockingStateReader &stream, bool testing) bool FloatingDockContainer::restoreState(DockingStateReader &stream, bool testing)
{ {
if (!d->m_dockContainer->restoreState(stream, testing)) if (!d->m_dockContainer->restoreState(stream, testing))
return false; return false;
onDockAreasAddedOrRemoved(); onDockAreasAddedOrRemoved();
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
if (d->m_titleBar)
d->m_titleBar->setMaximizedIcon(windowState() == Qt::WindowMaximized);
#endif
return true; return true;
} }
bool FloatingDockContainer::hasTopLevelDockWidget() const bool FloatingDockContainer::hasTopLevelDockWidget() const
{ {
return d->m_dockContainer->hasTopLevelDockWidget(); return d->m_dockContainer->hasTopLevelDockWidget();
} }
DockWidget *FloatingDockContainer::topLevelDockWidget() const DockWidget *FloatingDockContainer::topLevelDockWidget() const
{ {
return d->m_dockContainer->topLevelDockWidget(); return d->m_dockContainer->topLevelDockWidget();
} }
QList<DockWidget *> FloatingDockContainer::dockWidgets() const QList<DockWidget *> FloatingDockContainer::dockWidgets() const
{ {
return d->m_dockContainer->dockWidgets(); return d->m_dockContainer->dockWidgets();
} }
void FloatingDockContainer::finishDragging() void FloatingDockContainer::hideAndDeleteLater()
{ {
// Widget has been redocked, so it must be hidden right way (see
// https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/issues/351)
// but AutoHideChildren must be set to false because "this" still contains
// dock widgets that shall not be toggled hidden.
d->m_autoHideChildren = false;
hide();
deleteLater();
if (d->m_dockManager) {
d->m_dockManager->removeFloatingWidget(this);
d->m_dockManager->removeDockContainer(dockContainer());
}
}
void FloatingDockContainer::finishDragging()
{
qCInfo(adsLog) << Q_FUNC_INFO; qCInfo(adsLog) << Q_FUNC_INFO;
#ifdef Q_OS_LINUX #if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
setAttribute(Qt::WA_X11NetWmWindowTypeDock, false);
setWindowOpacity(1); setWindowOpacity(1);
activateWindow(); activateWindow();
if (d->m_mouseEventHandler) { if (d->m_mouseEventHandler) {
d->m_mouseEventHandler->releaseMouse(); d->m_mouseEventHandler->releaseMouse();
d->m_mouseEventHandler = nullptr; d->m_mouseEventHandler = nullptr;
} }
#endif #endif
d->titleMouseReleaseEvent(); d->titleMouseReleaseEvent();
} }
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
bool FloatingDockContainer::event(QEvent *event) bool FloatingDockContainer::event(QEvent *event)
{ {
switch (d->m_draggingState) switch (d->m_draggingState) {
{ case DraggingInactive: {
case DraggingInactive: // Normally we would check here, if the left mouse button is pressed. But from Qt version
{ // 5.12.2 on the mouse events from QEvent::NonClientAreaMouseButtonPress return the wrong
// Normally we would check here, if the left mouse button is pressed. // mouse button. The event always returns Qt::RightButton even if the left button is
// But from QT version 5.12.2 on the mouse events from // clicked. It is really great to work around the whole NonClientMouseArea bugs.
// QEvent::NonClientAreaMouseButtonPress return the wrong mouse button
// The event always returns Qt::RightButton even if the left button
// is clicked.
// It is really great to work around the whole NonClientMouseArea
// bugs
if (event->type() == QEvent::NonClientAreaMouseButtonPress if (event->type() == QEvent::NonClientAreaMouseButtonPress
/*&& QGuiApplication::mouseButtons().testFlag(Qt::LeftButton)*/) /*&& QGuiApplication::mouseButtons().testFlag(Qt::LeftButton)*/) {
{
qCInfo(adsLog) << Q_FUNC_INFO << "QEvent::NonClientAreaMouseButtonPress" qCInfo(adsLog) << Q_FUNC_INFO << "QEvent::NonClientAreaMouseButtonPress"
<< event->type(); << event->type();
d->m_dragStartPos = pos(); d->m_dragStartPos = pos();
d->setState(DraggingMousePressed); d->setState(DraggingMousePressed);
} }
} } break;
break;
case DraggingMousePressed: case DraggingMousePressed:
switch (event->type()) switch (event->type()) {
{
case QEvent::NonClientAreaMouseButtonDblClick: case QEvent::NonClientAreaMouseButtonDblClick:
qCInfo(adsLog) << Q_FUNC_INFO << "QEvent::NonClientAreaMouseButtonDblClick"; qCInfo(adsLog) << Q_FUNC_INFO << "QEvent::NonClientAreaMouseButtonDblClick";
d->setState(DraggingInactive); d->setState(DraggingInactive);
break; break;
case QEvent::Resize: case QEvent::Resize:
// If the first event after the mouse press is a resize event, then // If the first event after the mouse press is a resize event, then the user resizes
// the user resizes the window instead of dragging it around. // the window instead of dragging it around. But there is one exception. If the window
// But there is one exception. If the window is maximized, // is maximized, then dragging the window via title bar will cause the widget to
// then dragging the window via title bar will cause the widget to // leave the maximized state. This in turn will trigger a resize event. To know, if the
// leave the maximized state. This in turn will trigger a resize event. // resize event was triggered by user via moving a corner of the window frame or if it
// To know, if the resize event was triggered by user via moving a // was caused by a windows state change, we check, if we are not in maximized state.
// corner of the window frame or if it was caused by a windows state
// change, we check, if we are not in maximized state.
if (!isMaximized()) if (!isMaximized())
d->setState(DraggingInactive); d->setState(DraggingInactive);
break; break;
@@ -882,8 +1000,7 @@ bool FloatingDockContainer::event(QEvent *event)
break; break;
case DraggingFloatingWidget: case DraggingFloatingWidget:
if (event->type() == QEvent::NonClientAreaMouseButtonRelease) if (event->type() == QEvent::NonClientAreaMouseButtonRelease) {
{
qCInfo(adsLog) << Q_FUNC_INFO << "QEvent::NonClientAreaMouseButtonRelease"; qCInfo(adsLog) << Q_FUNC_INFO << "QEvent::NonClientAreaMouseButtonRelease";
d->titleMouseReleaseEvent(); d->titleMouseReleaseEvent();
} }
@@ -893,17 +1010,14 @@ bool FloatingDockContainer::event(QEvent *event)
break; break;
} }
#if (ADS_DEBUG_LEVEL > 0) qCInfo(adsLog) << Q_FUNC_INFO << event->type();
qDebug() << Q_FUNC_INFO << event->type();
#endif
return QWidget::event(event); return QWidget::event(event);
} }
void FloatingDockContainer::moveEvent(QMoveEvent *event) void FloatingDockContainer::moveEvent(QMoveEvent *event)
{ {
QWidget::moveEvent(event); QWidget::moveEvent(event);
switch (d->m_draggingState) switch (d->m_draggingState) {
{
case DraggingMousePressed: case DraggingMousePressed:
d->setState(DraggingFloatingWidget); d->setState(DraggingFloatingWidget);
d->updateDropOverlays(QCursor::pos()); d->updateDropOverlays(QCursor::pos());
@@ -911,15 +1025,85 @@ void FloatingDockContainer::moveEvent(QMoveEvent *event)
case DraggingFloatingWidget: case DraggingFloatingWidget:
d->updateDropOverlays(QCursor::pos()); d->updateDropOverlays(QCursor::pos());
// On macOS when hiding the DockAreaOverlay the application would set // On macOS when hiding the DockAreaOverlay the application would set the main window as
// the main window as the active window for some reason. This fixes // the active window for some reason. This fixes that by resetting the active window to
// that by resetting the active window to the floating widget after // the floating widget after updating the overlays.
// updating the overlays. activateWindow();
QApplication::setActiveWindow(this);
break; break;
default: default:
break; break;
} }
} }
#endif #endif
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
void FloatingDockContainer::onMaximizeRequest()
{
if (windowState() == Qt::WindowMaximized)
showNormal();
else
showMaximized();
}
void FloatingDockContainer::showNormal(bool fixGeometry)
{
if (windowState() == Qt::WindowMaximized) {
QRect oldNormal = normalGeometry();
Super::showNormal();
if (fixGeometry)
setGeometry(oldNormal);
}
if (d->m_titleBar)
d->m_titleBar->setMaximizedIcon(false);
}
void FloatingDockContainer::showMaximized()
{
Super::showMaximized();
if (d->m_titleBar)
d->m_titleBar->setMaximizedIcon(true);
}
bool FloatingDockContainer::isMaximized() const
{
return windowState() == Qt::WindowMaximized;
}
void FloatingDockContainer::resizeEvent(QResizeEvent *event)
{
d->m_isResizing = true;
Super::resizeEvent(event);
}
void FloatingDockContainer::moveEvent(QMoveEvent *event)
{
Super::moveEvent(event);
if (!d->m_isResizing && event->spontaneous() && d->m_mousePressed) {
d->setState(DraggingFloatingWidget);
d->updateDropOverlays(QCursor::pos());
}
d->m_isResizing = false;
}
bool FloatingDockContainer::event(QEvent *e)
{
bool result = Super::event(e);
switch (e->type()) {
case QEvent::WindowActivate:
d->m_mousePressed = false;
break;
case QEvent::WindowDeactivate:
d->m_mousePressed = true;
break;
default:
break;
}
return result;
}
bool FloatingDockContainer::hasNativeTitleBar()
{
return d->m_titleBar == nullptr;
}
#endif
} // namespace ADS } // namespace ADS

View File

@@ -8,7 +8,7 @@
#include <QDockWidget> #include <QDockWidget>
#include <QRubberBand> #include <QRubberBand>
#ifdef Q_OS_LINUX #if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
using FloatingWidgetBaseType = QDockWidget; using FloatingWidgetBaseType = QDockWidget;
#else #else
using FloatingWidgetBaseType = QWidget; using FloatingWidgetBaseType = QWidget;
@@ -33,8 +33,8 @@ class DockingStateReader;
/** /**
* Pure virtual interface for floating widgets. * Pure virtual interface for floating widgets.
* This interface is used for opaque and non-opaque undocking. If opaque * This interface is used for opaque and non-opaque undocking. If opaque undocking is used,
* undocking is used, the a real FloatingDockContainer widget will be created * the a real FloatingDockContainer widget will be created.
*/ */
class AbstractFloatingWidget class AbstractFloatingWidget
{ {
@@ -42,8 +42,7 @@ public:
virtual ~AbstractFloatingWidget() = 0; virtual ~AbstractFloatingWidget() = 0;
/** /**
* Starts floating. * Starts floating.
* This function should get called typically from a mouse press event * This function should get called typically from a mouse press event handler.
* handler
*/ */
virtual void startFloating(const QPoint &dragStartMousePos, virtual void startFloating(const QPoint &dragStartMousePos,
const QSize &size, const QSize &size,
@@ -52,26 +51,23 @@ public:
= 0; = 0;
/** /**
* Moves the widget to a new position relative to the position given when * Moves the widget to a new position relative to the position given when startFloating()
* startFloating() was called. * was called. This function should be called from a mouse mouve event handler to move the
* This function should be called from a mouse mouve event handler to * floating widget on mouse move events.
* move the floating widget on mouse move events.
*/ */
virtual void moveFloating() = 0; virtual void moveFloating() = 0;
/** /**
* Tells the widget that to finish dragging if the mouse is released. * Tells the widget that to finish dragging if the mouse is released. This function should be
* This function should be called from a mouse release event handler * called from a mouse release event handler to finish the dragging.
* to finish the dragging
*/ */
virtual void finishDragging() = 0; virtual void finishDragging() = 0;
}; };
/** /**
* This implements a floating widget that is a dock container that accepts * This implements a floating widget that is a dock container that accepts docking of dock widgets
* docking of dock widgets like the main window and that can be docked into * like the main window and that can be docked into another dock container. Every floating window
* another dock container. * of the docking system is a FloatingDockContainer.
* Every floating window of the docking system is a FloatingDockContainer.
*/ */
class ADS_EXPORT FloatingDockContainer : public FloatingWidgetBaseType, class ADS_EXPORT FloatingDockContainer : public FloatingWidgetBaseType,
public AbstractFloatingWidget public AbstractFloatingWidget
@@ -96,9 +92,8 @@ private:
protected: protected:
/** /**
* Starts floating at the given global position. * Starts floating at the given global position. Use moveToGlobalPos() to move the widget
* Use moveToGlobalPos() to move the widget to a new position * to a new position depending on the start position given in Pos parameter.
* depending on the start position given in Pos parameter
*/ */
void startFloating(const QPoint &dragStartMousePos, void startFloating(const QPoint &dragStartMousePos,
const QSize &size, const QSize &size,
@@ -106,7 +101,7 @@ protected:
QWidget *mouseEventHandler) override; 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, void startDragging(const QPoint &dragStartMousePos,
const QSize &size, const QSize &size,
@@ -116,14 +111,13 @@ protected:
} }
/** /**
* Call this function if you explicitly want to signal that dragging has * Call this function if you explicitly want to signal that dragging has finished.
* finished
*/ */
void finishDragging() override; void finishDragging() override;
/** /**
* Call this function if you just want to initialize the position * Call this function if you just want to initialize the position and size of the
* and size of the floating widget * floating widget.
*/ */
void initFloatingGeometry(const QPoint &dragStartMousePos, const QSize &size) 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 * Moves the widget to a new position relative to the position given when startFloating()
* startFloating() was called * was called.
*/ */
void moveFloating() override; void moveFloating() override;
/** /**
* Restores the state from given stream. * Restores the state from given stream. If Testing is true, the function only parses the
* If Testing is true, the function only parses the data from the given * data from the given stream but does not restore anything. You can use this check for
* stream but does not restore anything. You can use this check for * faulty files before you start restoring the state.
* faulty files before you start restoring the state
*/ */
bool restoreState(DockingStateReader &stream, bool testing); bool restoreState(DockingStateReader &stream, bool testing);
/** /**
* Call this function to update the window title * Call this function to update the window title.
*/ */
void updateWindowTitle(); void updateWindowTitle();
@@ -158,6 +151,10 @@ protected: // reimplements QWidget
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
virtual bool event(QEvent *event) override; virtual bool event(QEvent *event) override;
virtual void moveEvent(QMoveEvent *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 #endif
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
@@ -202,26 +199,61 @@ public:
bool isClosable() const; bool isClosable() const;
/** /**
* This function returns true, if this floating widget has only one single * This function returns true, if this floating widget has only one single visible dock widget
* visible dock widget in a single visible dock area. * in a single visible dock area. The single dock widget is a real top level floating widget
* The single dock widget is a real top level floating widget because no * because no other widgets are docked.
* other widgets are docked.
*/ */
bool hasTopLevelDockWidget() const; bool hasTopLevelDockWidget() const;
/** /**
* This function returns the first dock widget in the first dock area. * This function returns the first dock widget in the first dock area. If the function
* If the function hasSingleDockWidget() returns true, then this function * hasSingleDockWidget() returns true, then this function returns this single dock widget.
* returns this single dock widget.
*/ */
DockWidget *topLevelDockWidget() const; DockWidget *topLevelDockWidget() const;
/** /**
* This function returns a list of all dock widget in this floating widget. * This function returns a list of all dock widget in this floating widget. This is a simple
* This is a simple convenience function that simply calls the dockWidgets() * convenience function that simply calls the dockWidgets() function of the internal
* function of the internal container widget. * container widget.
*/ */
QList<DockWidget *> dockWidgets() const; 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 }; // class FloatingDockContainer
} // namespace ADS } // namespace ADS

View File

@@ -4,6 +4,8 @@
#include "floatingdragpreview.h" #include "floatingdragpreview.h"
#include "ads_globals_p.h" #include "ads_globals_p.h"
#include "ads_globals.h"
#include "autohidedockcontainer.h"
#include "dockareawidget.h" #include "dockareawidget.h"
#include "dockcontainerwidget.h" #include "dockcontainerwidget.h"
#include "dockmanager.h" #include "dockmanager.h"
@@ -20,16 +22,17 @@
#include <iostream> #include <iostream>
namespace ADS namespace ADS {
{
/** /**
* Private data class (pimpl) * Private data class (pimpl)
*/ */
class FloatingDragPreviewPrivate class FloatingDragPreviewPrivate
{ {
public: public:
FloatingDragPreview *q; FloatingDragPreview *q;
QWidget *m_content = nullptr; QWidget *m_content = nullptr;
DockWidget::DockWidgetFeatures m_contentFeatures;
DockAreaWidget *m_contentSourceArea = nullptr; DockAreaWidget *m_contentSourceArea = nullptr;
QPoint m_dragStartMousePosition; QPoint m_dragStartMousePosition;
DockManager *m_dockManager = nullptr; DockManager *m_dockManager = nullptr;
@@ -51,7 +54,7 @@ namespace ADS
} }
/** /**
* Cancel dragging and emit the draggingCanceled event * Cancel dragging and emit the draggingCanceled event.
*/ */
void cancelDragging() void cancelDragging()
{ {
@@ -63,14 +66,46 @@ namespace ADS
} }
/** /**
* Creates the real floating widget in case the mouse is released outside * Creates the real floating widget in case the mouse is released outside outside of any
* outside of any drop area * drop area.
*/ */
void createFloatingWidget(); void createFloatingWidget();
}; // class FloatingDragPreviewPrivate
void FloatingDragPreviewPrivate::updateDropOverlays(const QPoint &globalPosition) /**
* 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();
return DockWidget::DockWidgetFeatures();
}
}; // class FloatingDragPreviewPrivate
void FloatingDragPreviewPrivate::updateDropOverlays(const QPoint &globalPosition)
{
if (!q->isVisible() || !m_dockManager) if (!q->isVisible() || !m_dockManager)
return; return;
@@ -90,8 +125,6 @@ namespace ADS
m_dropContainer = topContainer; m_dropContainer = topContainer;
auto containerOverlay = m_dockManager->containerOverlay(); auto containerOverlay = m_dockManager->containerOverlay();
auto dockAreaOverlay = m_dockManager->dockAreaOverlay(); auto dockAreaOverlay = m_dockManager->dockAreaOverlay();
auto dockDropArea = dockAreaOverlay->dropAreaUnderCursor();
auto containerDropArea = containerOverlay->dropAreaUnderCursor();
if (!topContainer) { if (!topContainer) {
containerOverlay->hideOverlay(); containerOverlay->hideOverlay();
@@ -102,9 +135,32 @@ namespace ADS
return; return;
} }
const int visibleDockAreas = topContainer->visibleDockAreaCount(); auto dockDropArea = dockAreaOverlay->dropAreaUnderCursor();
containerOverlay->setAllowedAreas(visibleDockAreas > 1 ? OuterDockAreas : AllDockAreas); 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); 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 if (dockArea && dockArea->isVisible() && visibleDockAreas >= 0
&& dockArea != m_contentSourceArea) { && dockArea != m_contentSourceArea) {
dockAreaOverlay->enableDropPreview(true); dockAreaOverlay->enableDropPreview(true);
@@ -114,7 +170,7 @@ namespace ADS
// A CenterDockWidgetArea for the dockAreaOverlay() indicates that the mouse is in the // 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 // title bar. If the ContainerArea is valid then we ignore the dock area of the
// dockAreaOverlay() and disable the drop preview // dockAreaOverlay() and disable the drop preview.
if ((area == CenterDockWidgetArea) && (containerDropArea != InvalidDockWidgetArea)) { if ((area == CenterDockWidgetArea) && (containerDropArea != InvalidDockWidgetArea)) {
dockAreaOverlay->enableDropPreview(false); dockAreaOverlay->enableDropPreview(false);
containerOverlay->enableDropPreview(true); containerOverlay->enableDropPreview(true);
@@ -124,30 +180,29 @@ namespace ADS
containerOverlay->showOverlay(topContainer); containerOverlay->showOverlay(topContainer);
} else { } else {
dockAreaOverlay->hideOverlay(); dockAreaOverlay->hideOverlay();
// If there is only one single visible dock area in a container, then // If there is only one single visible dock area in a container, then it does not make
// it does not make sense to show a dock overlay because the dock area // sense to show a dock overlay because the dock area would be removed and inserted at
// would be removed and inserted at the same position // the same position. Only auto hide area is allowed.
if (visibleDockAreas <= 1) if (visibleDockAreas == 1)
containerOverlay->hideOverlay(); containerOverlay->setAllowedAreas(AutoHideDockAreas);
else
containerOverlay->showOverlay(topContainer); containerOverlay->showOverlay(topContainer);
if (dockArea == m_contentSourceArea && InvalidDockWidgetArea == containerDropArea) if (dockArea == m_contentSourceArea && InvalidDockWidgetArea == containerDropArea)
m_dropContainer = nullptr; m_dropContainer = nullptr;
} }
if (DockManager::testConfigFlag(DockManager::DragPreviewIsDynamic)) { if (DockManager::testConfigFlag(DockManager::DragPreviewIsDynamic))
setHidden(dockDropArea != InvalidDockWidgetArea setHidden(dockDropArea != InvalidDockWidgetArea
|| containerDropArea != InvalidDockWidgetArea); || containerDropArea != InvalidDockWidgetArea);
} }
}
FloatingDragPreviewPrivate::FloatingDragPreviewPrivate(FloatingDragPreview *parent) FloatingDragPreviewPrivate::FloatingDragPreviewPrivate(FloatingDragPreview *parent)
: q(parent) : q(parent)
{} {}
void FloatingDragPreviewPrivate::createFloatingWidget() void FloatingDragPreviewPrivate::createFloatingWidget()
{ {
DockWidget *dockWidget = qobject_cast<DockWidget *>(m_content); DockWidget *dockWidget = qobject_cast<DockWidget *>(m_content);
DockAreaWidget *dockArea = qobject_cast<DockAreaWidget *>(m_content); DockAreaWidget *dockArea = qobject_cast<DockAreaWidget *>(m_content);
@@ -163,19 +218,21 @@ namespace ADS
floatingWidget->show(); floatingWidget->show();
if (!DockManager::testConfigFlag(DockManager::DragPreviewHasWindowFrame)) { if (!DockManager::testConfigFlag(DockManager::DragPreviewHasWindowFrame)) {
QApplication::processEvents(); QApplication::processEvents();
int frameHeight = floatingWidget->frameGeometry().height() - floatingWidget->geometry().height(); int frameHeight = floatingWidget->frameGeometry().height()
- floatingWidget->geometry().height();
QRect fixedGeometry = q->geometry(); QRect fixedGeometry = q->geometry();
fixedGeometry.adjust(0, frameHeight, 0, 0); fixedGeometry.adjust(0, frameHeight, 0, 0);
floatingWidget->setGeometry(fixedGeometry); floatingWidget->setGeometry(fixedGeometry);
} }
} }
} }
FloatingDragPreview::FloatingDragPreview(QWidget *content, QWidget *parent) FloatingDragPreview::FloatingDragPreview(QWidget *content, QWidget *parent)
: QWidget(parent) : QWidget(parent)
, d(new FloatingDragPreviewPrivate(this)) , d(new FloatingDragPreviewPrivate(this))
{ {
d->m_content = content; d->m_content = content;
d->m_contentFeatures = d->contentFeatures();
setAttribute(Qt::WA_DeleteOnClose); setAttribute(Qt::WA_DeleteOnClose);
if (DockManager::testConfigFlag(DockManager::DragPreviewHasWindowFrame)) { if (DockManager::testConfigFlag(DockManager::DragPreviewHasWindowFrame)) {
setWindowFlags(Qt::Window | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint); setWindowFlags(Qt::Window | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint);
@@ -185,7 +242,7 @@ namespace ADS
setAttribute(Qt::WA_TranslucentBackground); setAttribute(Qt::WA_TranslucentBackground);
} }
if (Utils::HostOsInfo::isLinuxHost()) { if (Utils::HostOsInfo::isAnyUnixHost() && !Utils::HostOsInfo::isMacHost()) {
auto flags = windowFlags(); auto flags = windowFlags();
flags |= Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint; flags |= Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint;
setWindowFlags(flags); setWindowFlags(flags);
@@ -193,8 +250,8 @@ namespace ADS
setWindowOpacity(0.6); setWindowOpacity(0.6);
// Create a static image of the widget that should get undocked // Create a static image of the widget that should get undocked. This is like some kind preview
// This is like some kind preview image like it is uses in drag and drop operations // image like it is uses in drag and drop operations.
if (DockManager::testConfigFlag(DockManager::DragPreviewShowsContentPixmap)) { if (DockManager::testConfigFlag(DockManager::DragPreviewShowsContentPixmap)) {
d->m_contentPreviewPixmap = QPixmap(content->size()); d->m_contentPreviewPixmap = QPixmap(content->size());
content->render(&d->m_contentPreviewPixmap); content->render(&d->m_contentPreviewPixmap);
@@ -203,87 +260,126 @@ namespace ADS
&QApplication::applicationStateChanged, &QApplication::applicationStateChanged,
this, this,
&FloatingDragPreview::onApplicationStateChanged); &FloatingDragPreview::onApplicationStateChanged);
// The only safe way to receive escape key presses is to install an event // The only safe way to receive escape key presses is to install an event filter for the
// filter for the application object // application object.
QApplication::instance()->installEventFilter(this); QApplication::instance()->installEventFilter(this);
} }
FloatingDragPreview::FloatingDragPreview(DockWidget *content) FloatingDragPreview::FloatingDragPreview(DockWidget *content)
: FloatingDragPreview(static_cast<QWidget *>(content), : FloatingDragPreview(static_cast<QWidget *>(content),
content->dockManager()) // TODO static_cast? content->dockManager()) // TODO static_cast?
{ {
d->m_dockManager = content->dockManager(); d->m_dockManager = content->dockManager();
if (content->dockAreaWidget()->openDockWidgetsCount() == 1) if (content->dockAreaWidget()->openDockWidgetsCount() == 1)
d->m_contentSourceArea = content->dockAreaWidget(); d->m_contentSourceArea = content->dockAreaWidget();
setWindowTitle(content->windowTitle()); setWindowTitle(content->windowTitle());
} }
FloatingDragPreview::FloatingDragPreview(DockAreaWidget *content) FloatingDragPreview::FloatingDragPreview(DockAreaWidget *content)
: FloatingDragPreview(static_cast<QWidget *>(content), : FloatingDragPreview(static_cast<QWidget *>(content),
content->dockManager()) // TODO static_cast? content->dockManager()) // TODO static_cast?
{ {
d->m_dockManager = content->dockManager(); d->m_dockManager = content->dockManager();
d->m_contentSourceArea = content; d->m_contentSourceArea = content;
setWindowTitle(content->currentDockWidget()->windowTitle()); setWindowTitle(content->currentDockWidget()->windowTitle());
} }
FloatingDragPreview::~FloatingDragPreview() { delete d; } FloatingDragPreview::~FloatingDragPreview()
{
delete d;
}
void FloatingDragPreview::moveFloating() void FloatingDragPreview::moveFloating()
{ {
const int borderSize = (frameSize().width() - size().width()) / 2; const int borderSize = (frameSize().width() - size().width()) / 2;
const QPoint moveToPos = QCursor::pos() - d->m_dragStartMousePosition const QPoint moveToPos = QCursor::pos() - d->m_dragStartMousePosition - QPoint(borderSize, 0);
- QPoint(borderSize, 0);
move(moveToPos); move(moveToPos);
d->updateDropOverlays(QCursor::pos()); d->updateDropOverlays(QCursor::pos());
} }
void FloatingDragPreview::startFloating(const QPoint &dragStartMousePos, void FloatingDragPreview::startFloating(const QPoint &dragStartMousePos,
const QSize &size, const QSize &size,
eDragState dragState, eDragState dragState,
QWidget *mouseEventHandler) QWidget *mouseEventHandler)
{ {
Q_UNUSED(mouseEventHandler) Q_UNUSED(mouseEventHandler)
Q_UNUSED(dragState) Q_UNUSED(dragState)
resize(size); resize(size);
d->m_dragStartMousePosition = dragStartMousePos; d->m_dragStartMousePosition = dragStartMousePos;
moveFloating(); moveFloating();
show(); show();
} }
void FloatingDragPreview::finishDragging() void FloatingDragPreview::finishDragging()
{ {
qCInfo(adsLog) << Q_FUNC_INFO; qCInfo(adsLog) << Q_FUNC_INFO;
auto dockDropArea = d->m_dockManager->dockAreaOverlay()->visibleDropAreaUnderCursor(); auto dockDropArea = d->m_dockManager->dockAreaOverlay()->visibleDropAreaUnderCursor();
auto containerDropArea = d->m_dockManager->containerOverlay()->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) { if (!d->m_dropContainer) {
d->createFloatingWidget(); d->createFloatingWidget();
} else if (dockDropArea != InvalidDockWidgetArea) { } else if (dockDropArea != InvalidDockWidgetArea) {
d->m_dropContainer->dropWidget(d->m_content, dockDropArea, d->m_dropContainer->dockAreaAt(QCursor::pos())); d->m_dropContainer->dropWidget(d->m_content,
dockDropArea,
d->m_dropContainer->dockAreaAt(QCursor::pos()),
d->m_dockManager->dockAreaOverlay()->tabIndexUnderCursor());
} else if (containerDropArea != InvalidDockWidgetArea) { } else if (containerDropArea != InvalidDockWidgetArea) {
// If there is only one single dock area, and we drop into the center // If there is only one single dock area, and we drop into the center then we tabify the
// then we tabify the dropped widget into the only visible dock area // dropped widget into the only visible dock area.
if (d->m_dropContainer->visibleDockAreaCount() <= 1 && CenterDockWidgetArea == containerDropArea) if (d->m_dropContainer->visibleDockAreaCount() <= 1
d->m_dropContainer->dropWidget(d->m_content, containerDropArea, d->m_dropContainer->dockAreaAt(QCursor::pos())); && CenterDockWidgetArea == containerDropArea)
d->m_dropContainer
->dropWidget(d->m_content,
containerDropArea,
d->m_dropContainer->dockAreaAt(QCursor::pos()),
d->m_dockManager->containerOverlay()->tabIndexUnderCursor());
else else
d->m_dropContainer->dropWidget(d->m_content, containerDropArea, nullptr); d->m_dropContainer->dropWidget(d->m_content, containerDropArea, nullptr);
} else { } else {
d->createFloatingWidget(); d->createFloatingWidget();
} }
this->close(); close();
d->m_dockManager->containerOverlay()->hideOverlay(); d->m_dockManager->containerOverlay()->hideOverlay();
d->m_dockManager->dockAreaOverlay()->hideOverlay(); d->m_dockManager->dockAreaOverlay()->hideOverlay();
} }
void FloatingDragPreview::paintEvent(QPaintEvent *event) 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) Q_UNUSED(event)
if (d->m_hidden) if (d->m_hidden)
return; return;
QPainter painter(this); QPainter painter(this);
painter.setOpacity(0.6);
if (DockManager::testConfigFlag(DockManager::DragPreviewShowsContentPixmap)) if (DockManager::testConfigFlag(DockManager::DragPreviewShowsContentPixmap))
painter.drawPixmap(QPoint(0, 0), d->m_contentPreviewPixmap); painter.drawPixmap(QPoint(0, 0), d->m_contentPreviewPixmap);
@@ -301,10 +397,10 @@ namespace ADS
painter.setBrush(color); painter.setBrush(color);
painter.drawRect(rect().adjusted(0, 0, -1, -1)); painter.drawRect(rect().adjusted(0, 0, -1, -1));
} }
} }
void FloatingDragPreview::onApplicationStateChanged(Qt::ApplicationState state) void FloatingDragPreview::onApplicationStateChanged(Qt::ApplicationState state)
{ {
if (state != Qt::ApplicationActive) { if (state != Qt::ApplicationActive) {
disconnect(qApp, disconnect(qApp,
&QApplication::applicationStateChanged, &QApplication::applicationStateChanged,
@@ -312,10 +408,10 @@ namespace ADS
&FloatingDragPreview::onApplicationStateChanged); &FloatingDragPreview::onApplicationStateChanged);
d->cancelDragging(); d->cancelDragging();
} }
} }
bool FloatingDragPreview::eventFilter(QObject *watched, QEvent *event) bool FloatingDragPreview::eventFilter(QObject *watched, QEvent *event)
{ {
if (!d->m_canceled && event->type() == QEvent::KeyPress) { if (!d->m_canceled && event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if (keyEvent->key() == Qt::Key_Escape) { if (keyEvent->key() == Qt::Key_Escape) {
@@ -325,6 +421,6 @@ namespace ADS
} }
return false; return false;
} }
} // namespace ADS } // namespace ADS

View File

@@ -86,6 +86,11 @@ public: // implements AbstractFloatingWidget
*/ */
void finishDragging() override; void finishDragging() override;
/**
* Cleanup auto hide container if the dragged widget has one.
*/
void cleanupAutoHideContainerWidget(DockWidgetArea containerDropArea);
signals: signals:
/** /**
* This signal is emitted, if dragging has been canceled by escape key * This signal is emitted, if dragging has been canceled by escape key

View File

@@ -6,11 +6,12 @@
#include <QVector> #include <QVector>
namespace ADS { namespace ADS {
/**
/**
* Private data class (pimpl) * Private data class (pimpl)
*/ */
struct IconProviderPrivate struct IconProviderPrivate
{ {
IconProvider *q; IconProvider *q;
QVector<QIcon> m_userIcons{IconCount, QIcon()}; QVector<QIcon> m_userIcons{IconCount, QIcon()};
@@ -18,32 +19,32 @@ namespace ADS {
* Private data constructor * Private data constructor
*/ */
IconProviderPrivate(IconProvider *parent); IconProviderPrivate(IconProvider *parent);
}; };
// struct IconProviderPrivate // struct IconProviderPrivate
IconProviderPrivate::IconProviderPrivate(IconProvider *parent) IconProviderPrivate::IconProviderPrivate(IconProvider *parent)
: q(parent) : q(parent)
{} {}
IconProvider::IconProvider() IconProvider::IconProvider()
: d(new IconProviderPrivate(this)) : d(new IconProviderPrivate(this))
{} {}
IconProvider::~IconProvider() IconProvider::~IconProvider()
{ {
delete d; delete d;
} }
QIcon IconProvider::customIcon(eIcon iconId) const QIcon IconProvider::customIcon(eIcon iconId) const
{ {
Q_ASSERT(iconId < d->m_userIcons.size()); Q_ASSERT(iconId < d->m_userIcons.size());
return d->m_userIcons[iconId]; return d->m_userIcons[iconId];
} }
void IconProvider::registerCustomIcon(eIcon iconId, const QIcon &icon) void IconProvider::registerCustomIcon(eIcon iconId, const QIcon &icon)
{ {
Q_ASSERT(iconId < d->m_userIcons.size()); Q_ASSERT(iconId < d->m_userIcons.size());
d->m_userIcons[iconId] = icon; d->m_userIcons[iconId] = icon;
} }
} // namespace ADS } // namespace ADS

View File

@@ -12,8 +12,7 @@ namespace ADS {
struct IconProviderPrivate; struct IconProviderPrivate;
/** /**
* This object provides all icons that are required by the advanced docking * This object provides all icons that are required by the advanced docking system.
* system.
* The IconProvider enables the user to register custom icons in case using * The IconProvider enables the user to register custom icons in case using
* stylesheets is not an option. * stylesheets is not an option.
*/ */

View File

@@ -4,8 +4,10 @@
#include "floatingwidgettitlebar.h" #include "floatingwidgettitlebar.h"
#include "ads_globals.h" #include "ads_globals.h"
#include "dockmanager.h"
#include "elidinglabel.h" #include "elidinglabel.h"
#include "floatingdockcontainer.h" #include "floatingdockcontainer.h"
#include "iconprovider.h"
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QMouseEvent> #include <QMouseEvent>
@@ -20,6 +22,7 @@ namespace ADS {
using TabLabelType = ElidingLabel; using TabLabelType = ElidingLabel;
using CloseButtonType = QToolButton; using CloseButtonType = QToolButton;
using MaximizeButtonType = QToolButton;
/** /**
* @brief Private data class of public interface CFloatingWidgetTitleBar * @brief Private data class of public interface CFloatingWidgetTitleBar
@@ -31,8 +34,12 @@ public:
QLabel *m_iconLabel = nullptr; QLabel *m_iconLabel = nullptr;
TabLabelType *m_titleLabel = nullptr; TabLabelType *m_titleLabel = nullptr;
CloseButtonType *m_closeButton = nullptr; CloseButtonType *m_closeButton = nullptr;
MaximizeButtonType *m_maximizeButton = nullptr;
FloatingDockContainer *m_floatingWidget = nullptr; FloatingDockContainer *m_floatingWidget = nullptr;
eDragState m_dragState = DraggingInactive; eDragState m_dragState = DraggingInactive;
QIcon m_maximizeIcon;
QIcon m_normalIcon;
bool m_maximized = false;
FloatingWidgetTitleBarPrivate(FloatingWidgetTitleBar *parent) FloatingWidgetTitleBarPrivate(FloatingWidgetTitleBar *parent)
: q(parent) : q(parent)
@@ -46,15 +53,18 @@ public:
void FloatingWidgetTitleBarPrivate::createLayout() void FloatingWidgetTitleBarPrivate::createLayout()
{ {
// Title label
m_titleLabel = new TabLabelType(); m_titleLabel = new TabLabelType();
m_titleLabel->setElideMode(Qt::ElideRight); m_titleLabel->setElideMode(Qt::ElideRight);
m_titleLabel->setText("DockWidget->windowTitle()"); m_titleLabel->setText("DockWidget->windowTitle()");
m_titleLabel->setObjectName("floatingTitleLabel"); m_titleLabel->setObjectName("floatingTitleLabel");
m_titleLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); m_titleLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
// Close button
m_closeButton = new CloseButtonType(); m_closeButton = new CloseButtonType();
m_closeButton->setObjectName("floatingTitleCloseButton"); m_closeButton->setObjectName("floatingTitleCloseButton");
m_closeButton->setAutoRaise(true); m_closeButton->setAutoRaise(true);
internal::setButtonIcon(m_closeButton, internal::setButtonIcon(m_closeButton,
QStyle::SP_TitleBarCloseButton, QStyle::SP_TitleBarCloseButton,
ADS::FloatingWidgetCloseIcon); ADS::FloatingWidgetCloseIcon);
@@ -68,6 +78,21 @@ void FloatingWidgetTitleBarPrivate::createLayout()
q, q,
&FloatingWidgetTitleBar::closeRequested); &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()); QFontMetrics fontMetrics(m_titleLabel->font());
int spacing = qRound(fontMetrics.height() / 4.0); int spacing = qRound(fontMetrics.height() / 4.0);
@@ -78,6 +103,7 @@ void FloatingWidgetTitleBarPrivate::createLayout()
q->setLayout(layout); q->setLayout(layout);
layout->addWidget(m_titleLabel, 1); layout->addWidget(m_titleLabel, 1);
layout->addSpacing(spacing); layout->addSpacing(spacing);
layout->addWidget(m_maximizeButton);
layout->addWidget(m_closeButton); layout->addWidget(m_closeButton);
layout->addSpacing(1); layout->addSpacing(1);
layout->setAlignment(Qt::AlignCenter); layout->setAlignment(Qt::AlignCenter);
@@ -91,6 +117,26 @@ FloatingWidgetTitleBar::FloatingWidgetTitleBar(FloatingDockContainer *parent)
{ {
d->m_floatingWidget = parent; d->m_floatingWidget = parent;
d->createLayout(); 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() FloatingWidgetTitleBar::~FloatingWidgetTitleBar()
@@ -125,8 +171,11 @@ void FloatingWidgetTitleBar::mouseMoveEvent(QMouseEvent *event)
return; return;
} }
// move floating window // Move floating window
if (DraggingFloatingWidget == d->m_dragState) { if (DraggingFloatingWidget == d->m_dragState) {
if (d->m_floatingWidget->isMaximized())
d->m_floatingWidget->showNormal(true);
d->m_floatingWidget->moveFloating(); d->m_floatingWidget->moveFloating();
Super::mouseMoveEvent(event); Super::mouseMoveEvent(event);
return; return;
@@ -134,6 +183,16 @@ void FloatingWidgetTitleBar::mouseMoveEvent(QMouseEvent *event)
Super::mouseMoveEvent(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) void FloatingWidgetTitleBar::enableCloseButton(bool enable)
{ {
d->m_closeButton->setEnabled(enable); d->m_closeButton->setEnabled(enable);
@@ -149,4 +208,13 @@ void FloatingWidgetTitleBar::updateStyle()
internal::repolishStyle(this, internal::RepolishDirectChildren); 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 } // namespace ADS

View File

@@ -4,6 +4,7 @@
#pragma once #pragma once
#include <QFrame> #include <QFrame>
#include <QIcon>
namespace ADS { namespace ADS {
@@ -11,11 +12,10 @@ class FloatingDockContainer;
class FloatingWidgetTitleBarPrivate; 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 * Linux does not support NonClientArea mouse events like
* QEvent::NonClientAreaMouseButtonPress. Because these events are required * QEvent::NonClientAreaMouseButtonPress. Because these events are required for the docking system
* for the docking system to work properly, we use our own titlebar here to * to work properly, we use our own titlebar here to capture the required mouse events.
* capture the required mouse events.
*/ */
class FloatingWidgetTitleBar : public QFrame class FloatingWidgetTitleBar : public QFrame
{ {
@@ -27,6 +27,7 @@ protected:
void mousePressEvent(QMouseEvent *event) override; void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override;
void mouseDoubleClickEvent(QMouseEvent *event) override;
public: public:
using Super = QWidget; using Super = QWidget;
@@ -52,11 +53,21 @@ public:
*/ */
void updateStyle(); void updateStyle();
/**
* Change the maximize button icon according to current windows state
*/
void setMaximizedIcon(bool maximized);
signals: signals:
/** /**
* This signal is emitted, if the close button is clicked. * This signal is emitted, if the close button is clicked.
*/ */
void closeRequested(); void closeRequested();
/**
* This signal is emitted, if the maximize button is clicked.
*/
void maximizeRequested();
}; };
} // namespace ADS } // namespace ADS

View 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

View 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

View 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

View 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

View File

@@ -15,76 +15,80 @@
namespace QmlDesigner { namespace QmlDesigner {
using EnumerationName = QByteArray; using EnumerationName = QByteArray;
using EnumerationNameView = QByteArrayView;
class Enumeration 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: public:
Enumeration() = default; Enumeration() = default;
Enumeration(const EnumerationName &enumerationName) Enumeration(EnumerationName enumerationName)
: m_enumerationName(enumerationName) : m_enumerationName{std::move(enumerationName)}
{ {}
}
Enumeration(const char *text)
: m_enumerationName{text, static_cast<qsizetype>(std::strlen(text))}
{}
Enumeration(const QString &enumerationName) Enumeration(const QString &enumerationName)
: m_enumerationName(enumerationName.toUtf8()) : m_enumerationName(enumerationName.toUtf8())
{}
Enumeration(const EnumerationName &scope, const EnumerationName &name)
{ {
} m_enumerationName.reserve(scope.size() + 1 + name.size());
Enumeration(const QString &scope, const QString &name) m_enumerationName.append(scope);
{ m_enumerationName.append(1);
QString enumerationString = scope + QLatin1Char('.') + name; m_enumerationName.append(name);
m_enumerationName = enumerationString.toUtf8();
} }
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: private:
EnumerationName m_enumerationName; 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) inline QDebug operator <<(QDebug debug, const Enumeration &enumeration)
{ {
debug.nospace() << "Enumeration(" debug.nospace() << "Enumeration("

View File

@@ -19,8 +19,6 @@ public:
constexpr explicit BasicId() = default; constexpr explicit BasicId() = default;
constexpr BasicId(const char *) = delete;
static constexpr BasicId create(InternalIntegerType idNumber) static constexpr BasicId create(InternalIntegerType idNumber)
{ {
BasicId id; BasicId id;

View File

@@ -108,13 +108,9 @@ public:
static_assert(!std::is_array<Type>::value, "Input type is array and not char pointer!"); static_assert(!std::is_array<Type>::value, "Input type is array and not char pointer!");
} }
BasicSmallString(const QString &qString) BasicSmallString(const QString &qString) { append(qString); }
: BasicSmallString(BasicSmallString::fromQString(qString))
{}
BasicSmallString(const QStringView qStringView) BasicSmallString(const QStringView qStringView) { append(qStringView); }
: BasicSmallString(BasicSmallString::fromQStringView(qStringView))
{}
BasicSmallString(const QByteArray &qByteArray) BasicSmallString(const QByteArray &qByteArray)
: BasicSmallString(qByteArray.constData(), qByteArray.size()) : BasicSmallString(qByteArray.constData(), qByteArray.size())
@@ -127,9 +123,7 @@ public:
{ {
} }
BasicSmallString(const std::wstring &wstring) BasicSmallString(const std::wstring &wstring) { append(wstring); }
: BasicSmallString(BasicSmallString::fromQStringView(wstring))
{}
template<typename BeginIterator, template<typename BeginIterator,
typename EndIterator, typename EndIterator,
@@ -349,12 +343,10 @@ public:
return BasicSmallString(utf8ByteArray.constData(), uint(utf8ByteArray.size())); return BasicSmallString(utf8ByteArray.constData(), uint(utf8ByteArray.size()));
} }
// precondition: has to be null terminated
bool contains(SmallStringView subStringToSearch) const bool contains(SmallStringView subStringToSearch) const
{ {
const char *found = std::strstr(data(), subStringToSearch.data()); return SmallStringView{*this}.find(subStringToSearch)
!= SmallStringView::npos;
return found != nullptr;
} }
bool contains(char characterToSearch) const bool contains(char characterToSearch) const
@@ -455,7 +447,7 @@ public:
constexpr size_type temporaryArraySize = Size * 6; constexpr size_type temporaryArraySize = Size * 6;
size_type oldSize = size(); 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; char *newEnd = nullptr;
if (maximumRequiredSize > temporaryArraySize) { if (maximumRequiredSize > temporaryArraySize) {

View File

@@ -82,7 +82,6 @@ if(TARGET QmlDesignerCore)
add_feature_info("ProjectStorage" ${USE_PROJECTSTORAGE} "") add_feature_info("ProjectStorage" ${USE_PROJECTSTORAGE} "")
endif() endif()
extend_qtc_library(QmlDesignerCore extend_qtc_library(QmlDesignerCore
CONDITION ENABLE_COMPILE_WARNING_AS_ERROR CONDITION ENABLE_COMPILE_WARNING_AS_ERROR
PROPERTIES COMPILE_WARNING_AS_ERROR ON PROPERTIES COMPILE_WARNING_AS_ERROR ON
@@ -186,23 +185,40 @@ extend_qtc_library(QmlDesignerCore
extend_qtc_library(QmlDesignerCore extend_qtc_library(QmlDesignerCore
SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/designercore/include SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/designercore/include
SOURCES SOURCES
abstractproperty.h
abstractview.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 anchorline.h
annotation.h annotation.h
asynchronousexplicitimagecache.h asynchronousexplicitimagecache.h
asynchronousimagecache.h asynchronousimagecache.h
auxiliarydata.h auxiliarydata.h
auxiliarydataproperties.h auxiliarydataproperties.h
basetexteditmodifier.h
bindingproperty.h bindingproperty.h
componenttextmodifier.h
customnotifications.h customnotifications.h
documentmessage.h documentmessage.h
enumerationmetainfo.h enumerationmetainfo.h
exception.h exception.h
externaldependenciesinterface.h externaldependenciesinterface.h
forwardview.h
imagecacheauxiliarydata.h imagecacheauxiliarydata.h
import.h import.h
invalidargumentexception.h invalidargumentexception.h
@@ -214,33 +230,26 @@ extend_qtc_library(QmlDesignerCore
invalidqmlsourceexception.h invalidqmlsourceexception.h
invalidreparentingexception.h invalidreparentingexception.h
invalidslideindexexception.h invalidslideindexexception.h
itemlibraryinfo.h
mathutils.h mathutils.h
metainfo.h metainfo.h
metainforeader.h
model.h
modelfwd.h modelfwd.h
modelmerger.h modelmerger.h
modelnode.h modelnode.h
modelnodepositionstorage.h modelnodepositionstorage.h
nodeabstractproperty.h nodeabstractproperty.h
nodehints.h
nodeinstance.h nodeinstance.h
nodeinstanceview.h
nodelistproperty.h nodelistproperty.h
nodemetainfo.h nodemetainfo.h
nodeproperty.h nodeproperty.h
notimplementedexception.h notimplementedexception.h
plaintexteditmodifier.h
propertycontainer.h propertycontainer.h
propertymetainfo.h propertymetainfo.h
propertynode.h propertynode.h
propertyparser.h
qmlanchors.h qmlanchors.h
qmlchangeset.h qmlchangeset.h
qmlconnections.h qmlconnections.h
qmldesignercorelib_global.h
qmldesignercorelib_exports.h qmldesignercorelib_exports.h
qmldesignercorelib_global.h
qmlitemnode.h qmlitemnode.h
qmlmodelnodefacade.h qmlmodelnodefacade.h
qmlobjectnode.h qmlobjectnode.h
@@ -248,14 +257,11 @@ extend_qtc_library(QmlDesignerCore
qmltimeline.h qmltimeline.h
qmltimelinekeyframegroup.h qmltimelinekeyframegroup.h
removebasestateexception.h removebasestateexception.h
rewriterview.h
rewritingexception.h rewritingexception.h
signalhandlerproperty.h signalhandlerproperty.h
stringutils.h stringutils.h
stylesheetmerger.h stylesheetmerger.h
subcomponentmanager.h
synchronousimagecache.h synchronousimagecache.h
textmodifier.h
variantproperty.h variantproperty.h
) )
@@ -382,7 +388,7 @@ extend_qtc_library(QmlDesignerCore
extend_qtc_library(QmlDesignerCore extend_qtc_library(QmlDesignerCore
SOURCES_PREFIX designercore/projectstorage SOURCES_PREFIX designercore/projectstorage
PUBLIC_INCLUDES designercore/projectstorage PUBLIC_INCLUDES designercore/projectstorage
SOURCES_PROPERTIES SKIP_AUTOMOC ON SOURCES_PROPERTIES SKIP_AUTOGEN ON
SOURCES SOURCES
commontypecache.h commontypecache.h
directorypathcompressor.h directorypathcompressor.h
@@ -407,7 +413,7 @@ extend_qtc_library(QmlDesignerCore
projectstorage.cpp projectstorage.h projectstorage.cpp projectstorage.h
sourcepath.h sourcepath.h
sourcepathcache.h sourcepathcache.h
sourcepathcache.h sourcepathcacheinterface.h
sourcepathcachetypes.h sourcepathcachetypes.h
sourcepathview.h sourcepathview.h
storagecache.h storagecache.h

View File

@@ -4,12 +4,13 @@
#include "componentexporter.h" #include "componentexporter.h"
#include "exportnotification.h" #include "exportnotification.h"
#include "designdocument.h" #include <designdocument.h>
#include "nodemetainfo.h" #include <model/modelutils.h>
#include "qmldesignerplugin.h" #include <nodemetainfo.h>
#include "rewriterview.h" #include <qmldesignerplugin.h>
#include "qmlitemnode.h" #include <qmlitemnode.h>
#include "qmlobjectnode.h" #include <qmlobjectnode.h>
#include <rewriterview.h>
#include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/editormanager.h>
#include <projectexplorer/project.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 // Returns the UUID of the component's root node
// Empty string is returned if the node is not an instance of a component within // Empty string is returned if the node is not an instance of a component within
// the project. // the project.
NodeMetaInfo metaInfo = instance.metaInfo(); if (instance) {
if (!metaInfo.isValid()) const QString path = ModelUtils::componentFilePath(instance);
return {}; return m_componentUuidCache.value(path);
const QString path = metaInfo.componentFileName(); }
if (m_componentUuidCache.contains(path))
return m_componentUuidCache[path];
return {}; return {};
} }

View File

@@ -28,7 +28,7 @@ static QByteArrayList populateLineage(const QmlDesigner::ModelNode &node)
if (!node.isValid() || node.type().isEmpty()) if (!node.isValid() || node.type().isEmpty())
return {}; return {};
for (auto &info : node.metaInfo().superClasses()) for (auto &info : node.metaInfo().prototypes())
lineage.append(info.typeName()); lineage.append(info.typeName());
return lineage; return lineage;

View File

@@ -91,10 +91,7 @@ void BindingEditor::setBackendValue(const QVariant &backendValue)
const ModelNode node = propertyEditorValue->modelNode(); const ModelNode node = propertyEditorValue->modelNode();
if (node.isValid()) { if (node.isValid()) {
m_backendValueTypeName = node.metaInfo() m_backendValueType = node.metaInfo().property(propertyEditorValue->name()).propertyType();
.property(propertyEditorValue->name())
.propertyType()
.simplifiedTypeName();
QString nodeId = node.id(); QString nodeId = node.id();
if (nodeId.isEmpty()) if (nodeId.isEmpty())
@@ -102,9 +99,11 @@ void BindingEditor::setBackendValue(const QVariant &backendValue)
m_targetName = nodeId + "." + propertyEditorValue->name(); m_targetName = nodeId + "." + propertyEditorValue->name();
if (m_backendValueTypeName == "alias" || m_backendValueTypeName == "unknown") if (!m_backendValueType || m_backendValueType.isAlias()) {
if (QmlObjectNode::isValidQmlObjectNode(node)) if (QmlObjectNode::isValidQmlObjectNode(node))
m_backendValueTypeName = QmlObjectNode(node).instanceType(propertyEditorValue->name()); m_backendValueType = node.model()->metaInfo(
QmlObjectNode(node).instanceType(propertyEditorValue->name()));
}
} }
emit backendValueChanged(); emit backendValueChanged();
@@ -135,7 +134,7 @@ void BindingEditor::setStateModelNode(const QVariant &stateModelNode)
m_modelNode = m_stateModelNode.value<QmlDesigner::ModelNode>(); m_modelNode = m_stateModelNode.value<QmlDesigner::ModelNode>();
if (m_modelNode.isValid()) if (m_modelNode.isValid())
m_backendValueTypeName = "bool"; m_backendValueType = m_modelNode.model()->boolMetaInfo();
emit stateModelNodeChanged(); emit stateModelNodeChanged();
} }
@@ -153,9 +152,9 @@ void BindingEditor::setModelNode(const ModelNode &modelNode)
m_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(); emit backendValueChanged();
} }
@@ -165,68 +164,83 @@ void BindingEditor::setTargetName(const QString &target)
m_targetName = 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() void BindingEditor::prepareBindings()
{ {
if (!m_modelNode.isValid() || m_backendValueTypeName.isEmpty()) if (!m_modelNode.isValid() || !m_backendValueType) {
return; return;
}
const QList<QmlDesigner::ModelNode> allNodes = m_modelNode.view()->allModelNodes(); const QList<QmlDesigner::ModelNode> allNodes = m_modelNode.view()->allModelNodes();
QList<BindingEditorDialog::BindingOption> bindings; 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) { for (const auto &objnode : allNodes) {
BindingEditorDialog::BindingOption binding; BindingEditorDialog::BindingOption binding;
for (const auto &property : objnode.metaInfo().properties()) { 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())); binding.properties.append(QString::fromUtf8(property.name()));
} }
}
//dynamic properties: //dynamic properties:
for (const BindingProperty &bindingProperty : objnode.bindingProperties()) { for (const BindingProperty &bindingProperty : objnode.bindingProperties()) {
if (bindingProperty.isValid()) { if (bindingProperty.isValid()) {
if (bindingProperty.isDynamic()) { if (bindingProperty.isDynamic()) {
const TypeName dynamicTypeName = bindingProperty.dynamicTypeName(); auto model = bindingProperty.model();
if (compareTypes(m_backendValueTypeName, dynamicTypeName)) const auto dynamicType = model->metaInfo(bindingProperty.dynamicTypeName());
if (compareTypes(m_backendValueType, dynamicType)) {
binding.properties.append(QString::fromUtf8(bindingProperty.name())); binding.properties.append(QString::fromUtf8(bindingProperty.name()));
} }
} }
} }
}
for (const VariantProperty &variantProperty : objnode.variantProperties()) { for (const VariantProperty &variantProperty : objnode.variantProperties()) {
if (variantProperty.isValid()) { if (variantProperty.isValid()) {
if (variantProperty.isDynamic()) { if (variantProperty.isDynamic()) {
const TypeName dynamicTypeName = variantProperty.dynamicTypeName(); auto model = variantProperty.model();
if (compareTypes(m_backendValueTypeName, dynamicTypeName)) const auto dynamicType = model->metaInfo(variantProperty.dynamicTypeName());
if (compareTypes(m_backendValueType, dynamicType)) {
binding.properties.append(QString::fromUtf8(variantProperty.name())); binding.properties.append(QString::fromUtf8(variantProperty.name()));
} }
} }
} }
}
if (!binding.properties.isEmpty() && objnode.hasId()) { if (!binding.properties.isEmpty() && objnode.hasId()) {
binding.item = objnode.displayName(); binding.item = objnode.displayName();
@@ -244,11 +258,12 @@ void BindingEditor::prepareBindings()
BindingEditorDialog::BindingOption binding; BindingEditorDialog::BindingOption binding;
for (const auto &property : metaInfo.properties()) { 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())); binding.properties.append(QString::fromUtf8(property.name()));
} }
}
if (!binding.properties.isEmpty()) { if (!binding.properties.isEmpty()) {
binding.item = data.typeName; binding.item = data.typeName;
@@ -260,15 +275,24 @@ void BindingEditor::prepareBindings()
} }
if (!bindings.isEmpty() && !m_dialog.isNull()) if (!bindings.isEmpty() && !m_dialog.isNull())
m_dialog->setAllBindings(bindings, m_backendValueTypeName); m_dialog->setAllBindings(bindings, m_backendValueType);
} }
void BindingEditor::updateWindowName() void BindingEditor::updateWindowName()
{ {
if (!m_dialog.isNull() && !m_backendValueTypeName.isEmpty()) { if (!m_dialog.isNull() && m_backendValueType) {
const QString targetString = " [" QString targetString;
+ (m_targetName.isEmpty() ? QString() : (m_targetName + ": ")) if constexpr (useProjectStorage()) {
+ QString::fromUtf8(m_backendValueTypeName) + "]"; 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); m_dialog->setWindowTitle(m_dialog->defaultTitle() + targetString);
} }

View File

@@ -49,7 +49,7 @@ public:
//3. modelnode + backend value type name + optional target name //3. modelnode + backend value type name + optional target name
void setModelNode(const ModelNode &modelNode); void setModelNode(const ModelNode &modelNode);
void setBackendValueTypeName(const TypeName &backendValueTypeName); void setBackendValueType(const NodeMetaInfo &backendValueType);
void setTargetName(const QString &target); void setTargetName(const QString &target);
Q_INVOKABLE void prepareBindings(); Q_INVOKABLE void prepareBindings();
@@ -77,7 +77,7 @@ private:
QVariant m_modelNodeBackend; QVariant m_modelNodeBackend;
QVariant m_stateModelNode; QVariant m_stateModelNode;
QmlDesigner::ModelNode m_modelNode; QmlDesigner::ModelNode m_modelNode;
TypeName m_backendValueTypeName; NodeMetaInfo m_backendValueType;
QString m_targetName; QString m_targetName;
}; };

View File

@@ -79,7 +79,7 @@ void BindingEditorDialog::adjustProperties()
m_comboBoxProperty->setCurrentText(property); 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; m_lock = true;
@@ -118,7 +118,7 @@ void BindingEditorDialog::setupComboBoxes()
void BindingEditorDialog::setupCheckBox() void BindingEditorDialog::setupCheckBox()
{ {
const bool visible = (m_type == "bool"); const bool visible = m_type.isBool();
m_checkBoxNot->setVisible(visible); m_checkBoxNot->setVisible(visible);
} }

View File

@@ -6,6 +6,8 @@
#include <bindingeditor/abstracteditordialog.h> #include <bindingeditor/abstracteditordialog.h>
#include <nodemetainfo.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QComboBox; class QComboBox;
class QCheckBox; class QCheckBox;
@@ -35,7 +37,7 @@ public:
void adjustProperties() override; void adjustProperties() override;
void setAllBindings(const QList<BindingOption> &bindings, const TypeName &type); void setAllBindings(const QList<BindingOption> &bindings, const NodeMetaInfo &type);
private: private:
void setupUIComponents(); void setupUIComponents();
@@ -53,7 +55,7 @@ private:
QCheckBox *m_checkBoxNot = nullptr; QCheckBox *m_checkBoxNot = nullptr;
QList<BindingOption> m_bindings; QList<BindingOption> m_bindings;
TypeName m_type; NodeMetaInfo m_type;
}; };
} }

View File

@@ -3,13 +3,14 @@
#include "modelnodecontextmenu_helper.h" #include "modelnodecontextmenu_helper.h"
#include <nodemetainfo.h>
#include <modelnode.h>
#include <qmlitemnode.h>
#include <bindingproperty.h> #include <bindingproperty.h>
#include <model/modelutils.h>
#include <modelnode.h>
#include <nodemetainfo.h>
#include <nodeproperty.h> #include <nodeproperty.h>
#include <qmldesignerplugin.h>
#include <qmldesignerconstants.h> #include <qmldesignerconstants.h>
#include <qmldesignerplugin.h>
#include <qmlitemnode.h>
#include <QFile> #include <QFile>
@@ -76,10 +77,11 @@ bool selectionHasSameParent(const SelectionContext &selectionState)
bool fileComponentExists(const ModelNode &modelNode) bool fileComponentExists(const ModelNode &modelNode)
{ {
if (!modelNode.metaInfo().isFileComponent()) if (!modelNode.metaInfo().isFileComponent()) {
return true; return true;
}
const QString fileName = modelNode.metaInfo().componentFileName(); const QString fileName = ModelUtils::componentFilePath(modelNode);
if (fileName.contains("qml/QtQuick")) if (fileName.contains("qml/QtQuick"))
return false; return false;
@@ -97,7 +99,8 @@ bool selectionIsImported3DAsset(const SelectionContext &selectionState)
{ {
ModelNode node = selectionState.currentSingleSelectedNode(); ModelNode node = selectionState.currentSingleSelectedNode();
if (selectionState.view() && node.hasMetaInfo()) { if (selectionState.view() && node.hasMetaInfo()) {
QString fileName = node.metaInfo().componentFileName(); // absolute path QString fileName = ModelUtils::componentFilePath(node);
if (fileName.isEmpty()) { if (fileName.isEmpty()) {
// Node is not a file component, so we have to check if the current doc itself is // Node is not a file component, so we have to check if the current doc itself is
fileName = node.model()->fileUrl().toLocalFile(); fileName = node.model()->fileUrl().toLocalFile();

View File

@@ -187,6 +187,7 @@ public:
materialPreviewEnvironment, materialPreviewEnvironment,
materialPreviewModel, materialPreviewModel,
material_medium, material_medium,
maxBar_small,
mergeCells, mergeCells,
merge_small, merge_small,
minus, minus,
@@ -200,6 +201,7 @@ public:
move_medium, move_medium,
newMaterial, newMaterial,
nextFile_large, nextFile_large,
normalBar_small,
openLink, openLink,
openMaterialBrowser, openMaterialBrowser,
orientation, orientation,

View File

@@ -480,6 +480,11 @@ void ViewManager::exportAsImage()
d->formEditorView.exportAsImage(); d->formEditorView.exportAsImage();
} }
QImage ViewManager::takeFormEditorScreenshot()
{
return d->formEditorView.takeFormEditorScreenshot();
}
void ViewManager::reformatFileUsingTextEditorView() void ViewManager::reformatFileUsingTextEditorView()
{ {
d->textEditorView.reformatFile(); d->textEditorView.reformatFile();

View File

@@ -74,6 +74,7 @@ public:
const AbstractView *view() const; const AbstractView *view() const;
void exportAsImage(); void exportAsImage();
QImage takeFormEditorScreenshot();
void reformatFileUsingTextEditorView(); void reformatFileUsingTextEditorView();
QWidgetAction *componentViewAction() const; QWidgetAction *componentViewAction() const;

View File

@@ -13,14 +13,16 @@
#include <rewritertransaction.h> #include <rewritertransaction.h>
#include <rewriterview.h> #include <rewriterview.h>
#include <utils/qtcassert.h>
#include <QMessageBox> #include <QMessageBox>
#include <QTimer> #include <QTimer>
namespace QmlDesigner { namespace QmlDesigner {
BindingModel::BindingModel(ConnectionView *parent) BindingModel::BindingModel(ConnectionView *parent)
: QStandardItemModel(parent) : QStandardItemModel(parent), m_connectionView(parent),
, m_connectionView(parent) m_delegate(new BindingModelBackendDelegate(this))
{ {
connect(this, &QStandardItemModel::dataChanged, this, &BindingModel::handleDataChanged); connect(this, &QStandardItemModel::dataChanged, this, &BindingModel::handleDataChanged);
} }
@@ -40,6 +42,31 @@ void BindingModel::resetModel()
endResetModel(); 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) void BindingModel::bindingChanged(const BindingProperty &bindingProperty)
{ {
m_handleDataChanged = false; 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) void BindingModel::addBindingProperty(const BindingProperty &property)
{ {
QStandardItem *idItem; QStandardItem *idItem;
@@ -248,6 +288,7 @@ void BindingModel::addBindingProperty(const BindingProperty &property)
QList<QStandardItem*> items; QList<QStandardItem*> items;
items.append(idItem); items.append(idItem);
updateDisplayRoles(idItem, property);
items.append(targetPropertyNameItem); items.append(targetPropertyNameItem);
QString sourceNodeName; QString sourceNodeName;
@@ -267,6 +308,10 @@ void BindingModel::updateBindingProperty(int rowNumber)
BindingProperty bindingProperty = bindingPropertyForRow(rowNumber); BindingProperty bindingProperty = bindingPropertyForRow(rowNumber);
if (bindingProperty.isValid()) { if (bindingProperty.isValid()) {
QStandardItem *idItem = item(rowNumber, 0);
if (idItem)
updateDisplayRoles(idItem, bindingProperty);
QString targetPropertyName = QString::fromUtf8(bindingProperty.name()); QString targetPropertyName = QString::fromUtf8(bindingProperty.name());
updateDisplayRole(rowNumber, TargetPropertyNameRow, targetPropertyName); updateDisplayRole(rowNumber, TargetPropertyNameRow, targetPropertyName);
QString sourceNodeName; QString sourceNodeName;
@@ -355,6 +400,7 @@ void BindingModel::updateCustomData(QStandardItem *item, const BindingProperty &
{ {
item->setData(bindingProperty.parentModelNode().internalId(), Qt::UserRole + 1); item->setData(bindingProperty.parentModelNode().internalId(), Qt::UserRole + 1);
item->setData(bindingProperty.name(), Qt::UserRole + 2); item->setData(bindingProperty.name(), Qt::UserRole + 2);
updateDisplayRoles(item, bindingProperty);
} }
int BindingModel::findRowForBinding(const BindingProperty &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) bool BindingModel::getExpressionStrings(const BindingProperty &bindingProperty, QString *sourceNode, QString *sourceProperty)
{ {
//TODO reimplement using existing helper functions
//### todo we assume no expressions yet //### todo we assume no expressions yet
const QString expression = bindingProperty.expression(); const QString expression = bindingProperty.expression();
@@ -438,4 +486,159 @@ void BindingModel::handleException()
resetModel(); 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 } // namespace QmlDesigner

View File

@@ -7,16 +7,22 @@
#include <bindingproperty.h> #include <bindingproperty.h>
#include <variantproperty.h> #include <variantproperty.h>
#include <studioquickwidget.h>
#include <QStandardItemModel> #include <QStandardItemModel>
namespace QmlDesigner { namespace QmlDesigner {
class ConnectionView; class ConnectionView;
class BindingModelBackendDelegate;
class BindingModel : public QStandardItemModel class BindingModel : public QStandardItemModel
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
Q_PROPERTY(BindingModelBackendDelegate *delegate READ delegate CONSTANT)
public: public:
enum ColumnRoles { enum ColumnRoles {
TargetModelNodeRow = 0, TargetModelNodeRow = 0,
@@ -24,6 +30,15 @@ public:
SourceModelNodeRow = 2, SourceModelNodeRow = 2,
SourcePropertyNameRow = 3 SourcePropertyNameRow = 3
}; };
enum UserRoles {
InternalIdRole = Qt::UserRole + 2,
TargetNameRole,
TargetPropertyNameRole,
SourceNameRole,
SourcePropertyNameRole
};
BindingModel(ConnectionView *parent = nullptr); BindingModel(ConnectionView *parent = nullptr);
void bindingChanged(const BindingProperty &bindingProperty); void bindingChanged(const BindingProperty &bindingProperty);
void bindingRemoved(const BindingProperty &bindingProperty); void bindingRemoved(const BindingProperty &bindingProperty);
@@ -37,6 +52,18 @@ public:
void addBindingForCurrentNode(); void addBindingForCurrentNode();
void resetModel(); 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: protected:
void addBindingProperty(const BindingProperty &property); void addBindingProperty(const BindingProperty &property);
void updateBindingProperty(int rowNumber); void updateBindingProperty(int rowNumber);
@@ -46,11 +73,11 @@ protected:
ModelNode getNodeByIdOrParent(const QString &id, const ModelNode &targetNode) const; ModelNode getNodeByIdOrParent(const QString &id, const ModelNode &targetNode) const;
void updateCustomData(QStandardItem *item, const BindingProperty &bindingProperty); void updateCustomData(QStandardItem *item, const BindingProperty &bindingProperty);
int findRowForBinding(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); void updateDisplayRole(int row, int columns, const QString &string);
QHash<int, QByteArray> roleNames() const override;
BindingModelBackendDelegate *delegate() const;
private: private:
void handleDataChanged(const QModelIndex &topLeft, const QModelIndex& bottomRight); void handleDataChanged(const QModelIndex &topLeft, const QModelIndex& bottomRight);
void handleException(); void handleException();
@@ -60,7 +87,48 @@ private:
bool m_lock = false; bool m_lock = false;
bool m_handleDataChanged = false; bool m_handleDataChanged = false;
QString m_exceptionError; 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 } // namespace QmlDesigner

View File

@@ -252,6 +252,19 @@ void ConnectionModel::updateCustomData(QStandardItem *item, const SignalHandlerP
{ {
item->setData(signalHandlerProperty.parentModelNode().internalId(), UserRoles::InternalIdRole); item->setData(signalHandlerProperty.parentModelNode().internalId(), UserRoles::InternalIdRole);
item->setData(signalHandlerProperty.name(), UserRoles::TargetPropertyNameRole); 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 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() void ConnectionModel::handleException()
{ {
QMessageBox::warning(nullptr, tr("Error"), m_exceptionError); QMessageBox::warning(nullptr, tr("Error"), m_exceptionError);
@@ -522,4 +545,12 @@ QStringList ConnectionModel::getPossibleSignalsForConnection(const ModelNode &co
return stringList; return stringList;
} }
QHash<int, QByteArray> ConnectionModel::roleNames() const
{
static QHash<int, QByteArray> roleNames{{TargetPropertyNameRole, "signal"},
{TargetNameRole, "target"},
{ActionTypeRole, "action"}};
return roleNames;
}
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -26,7 +26,9 @@ public:
}; };
enum UserRoles { enum UserRoles {
InternalIdRole = Qt::UserRole + 1, InternalIdRole = Qt::UserRole + 1,
TargetPropertyNameRole TargetPropertyNameRole,
TargetNameRole,
ActionTypeRole
}; };
ConnectionModel(ConnectionView *parent = nullptr); ConnectionModel(ConnectionView *parent = nullptr);
@@ -49,6 +51,9 @@ public:
void deleteConnectionByRow(int currentRow); void deleteConnectionByRow(int currentRow);
void removeRowFromTable(const SignalHandlerProperty &property); void removeRowFromTable(const SignalHandlerProperty &property);
Q_INVOKABLE void add();
Q_INVOKABLE void remove(int row);
protected: protected:
void addModelNode(const ModelNode &modelNode); void addModelNode(const ModelNode &modelNode);
void addConnection(const ModelNode &modelNode); void addConnection(const ModelNode &modelNode);
@@ -61,6 +66,8 @@ protected:
void updateCustomData(QStandardItem *item, const SignalHandlerProperty &signalHandlerProperty); void updateCustomData(QStandardItem *item, const SignalHandlerProperty &signalHandlerProperty);
QStringList getPossibleSignalsForConnection(const ModelNode &connection) const; QStringList getPossibleSignalsForConnection(const ModelNode &connection) const;
QHash<int, QByteArray> roleNames() const override;
private: private:
void handleDataChanged(const QModelIndex &topLeft, const QModelIndex& bottomRight); void handleDataChanged(const QModelIndex &topLeft, const QModelIndex& bottomRight);
void handleException(); void handleException();

View File

@@ -8,6 +8,7 @@
#include "bindingmodel.h" #include "bindingmodel.h"
#include "connectionmodel.h" #include "connectionmodel.h"
#include "dynamicpropertiesmodel.h" #include "dynamicpropertiesmodel.h"
#include "theme.h"
#include <bindingproperty.h> #include <bindingproperty.h>
#include <nodeabstractproperty.h> #include <nodeabstractproperty.h>
@@ -16,19 +17,116 @@
#include <qmldesignerplugin.h> #include <qmldesignerplugin.h>
#include <viewmanager.h> #include <viewmanager.h>
#include <studioquickwidget.h>
#include <coreplugin/icore.h>
#include <coreplugin/messagebox.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <QQmlEngine>
#include <QShortcut>
#include <QTableView> #include <QTableView>
namespace QmlDesigner { 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) ConnectionView::ConnectionView(ExternalDependenciesInterface &externalDependencies)
: AbstractView{externalDependencies} : AbstractView{externalDependencies}, m_connectionViewWidget(new ConnectionViewWidget()),
, m_connectionViewWidget(new ConnectionViewWidget()) m_connectionModel(new ConnectionModel(this)), m_bindingModel(new BindingModel(this)),
, m_connectionModel(new ConnectionModel(this)) m_dynamicPropertiesModel(new DynamicPropertiesModel(false, this)),
, m_bindingModel(new BindingModel(this)) m_backendModel(new BackendModel(this)),
, m_dynamicPropertiesModel(new DynamicPropertiesModel(false, this)) m_connectionViewQuickWidget(new ConnectionViewQuickWidget(this))
, m_backendModel(new BackendModel(this))
{ {
connectionViewWidget()->setBindingModel(m_bindingModel); connectionViewWidget()->setBindingModel(m_bindingModel);
connectionViewWidget()->setConnectionModel(m_connectionModel); connectionViewWidget()->setConnectionModel(m_connectionModel);
@@ -36,8 +134,11 @@ ConnectionView::ConnectionView(ExternalDependenciesInterface &externalDependenci
connectionViewWidget()->setBackendModel(m_backendModel); 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) void ConnectionView::modelAttached(Model *model)
{ {
AbstractView::modelAttached(model); AbstractView::modelAttached(model);
@@ -195,7 +296,14 @@ void ConnectionView::currentStateChanged(const ModelNode &)
WidgetInfo ConnectionView::widgetInfo() 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"), QLatin1String("ConnectionView"),
WidgetInfo::LeftPane, WidgetInfo::LeftPane,
0, 0,
@@ -257,6 +365,20 @@ BackendModel *ConnectionView::backendModel() const
return m_backendModel; 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() ConnectionView *ConnectionView::instance()
{ {

View File

@@ -20,11 +20,14 @@ class BindingModel;
class ConnectionModel; class ConnectionModel;
class DynamicPropertiesModel; class DynamicPropertiesModel;
class BackendModel; class BackendModel;
class ConnectionViewQuickWidget;
class ConnectionView : public AbstractView class ConnectionView : public AbstractView
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
public: public:
ConnectionView(ExternalDependenciesInterface &externalDependencies); ConnectionView(ExternalDependenciesInterface &externalDependencies);
~ConnectionView() override; ~ConnectionView() override;
@@ -70,14 +73,24 @@ public:
BindingModel *bindingModel() const; BindingModel *bindingModel() const;
BackendModel *backendModel() const; BackendModel *backendModel() const;
int currentIndex() const;
void setCurrentIndex(int i);
static ConnectionView *instance(); static ConnectionView *instance();
signals:
void currentIndexChanged();
private: //variables private: //variables
QPointer<ConnectionViewWidget> m_connectionViewWidget; QPointer<ConnectionViewWidget> m_connectionViewWidget;
ConnectionModel *m_connectionModel; ConnectionModel *m_connectionModel;
BindingModel *m_bindingModel; BindingModel *m_bindingModel;
DynamicPropertiesModel *m_dynamicPropertiesModel; DynamicPropertiesModel *m_dynamicPropertiesModel;
BackendModel *m_backendModel; BackendModel *m_backendModel;
int m_currentIndex = 0;
QPointer<ConnectionViewQuickWidget> m_connectionViewQuickWidget;
}; };
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -90,6 +90,8 @@ ConnectionViewWidget::ConnectionViewWidget(QWidget *parent) :
this, &ConnectionViewWidget::handleTabChanged); this, &ConnectionViewWidget::handleTabChanged);
ui->stackedWidget->setCurrentIndex(0); ui->stackedWidget->setCurrentIndex(0);
ui->stackedWidget->parentWidget()->hide();
} }
ConnectionViewWidget::~ConnectionViewWidget() ConnectionViewWidget::~ConnectionViewWidget()
@@ -192,18 +194,17 @@ void ConnectionViewWidget::contextMenuEvent(QContextMenuEvent *event)
return; return;
const ModelNode node = property.parentModelNode(); const ModelNode node = property.parentModelNode();
const TypeName typeName = property.isDynamic() ? property.dynamicTypeName() auto model = node.model();
: node.metaInfo() const auto type = property.isDynamic()
.property(property.name()) ? model->metaInfo(property.dynamicTypeName())
.propertyType() : node.metaInfo().property(property.name()).propertyType();
.typeName();
const QString targetName = node.displayName() + "." + property.name(); const QString targetName = node.displayName() + "." + property.name();
m_bindingEditor->showWidget(); m_bindingEditor->showWidget();
m_bindingEditor->setBindingValue(property.expression()); m_bindingEditor->setBindingValue(property.expression());
m_bindingEditor->setModelNode(node); m_bindingEditor->setModelNode(node);
m_bindingEditor->setBackendValueTypeName(typeName); m_bindingEditor->setBackendValueType(type);
m_bindingEditor->setTargetName(targetName); m_bindingEditor->setTargetName(targetName);
m_bindingEditor->prepareBindings(); m_bindingEditor->prepareBindings();
m_bindingEditor->updateWindowName(); m_bindingEditor->updateWindowName();
@@ -240,11 +241,12 @@ void ConnectionViewWidget::contextMenuEvent(QContextMenuEvent *event)
return; return;
const QString targetName = node.displayName() + "." + abstractProperty.name(); const QString targetName = node.displayName() + "." + abstractProperty.name();
auto model = node.model();
m_dynamicEditor->showWidget(); m_dynamicEditor->showWidget();
m_dynamicEditor->setBindingValue(newExpression); m_dynamicEditor->setBindingValue(newExpression);
m_dynamicEditor->setModelNode(node); m_dynamicEditor->setModelNode(node);
m_dynamicEditor->setBackendValueTypeName(abstractProperty.dynamicTypeName()); m_dynamicEditor->setBackendValueType(
model->metaInfo(abstractProperty.dynamicTypeName()));
m_dynamicEditor->setTargetName(targetName); m_dynamicEditor->setTargetName(targetName);
m_dynamicEditor->prepareBindings(); m_dynamicEditor->prepareBindings();
m_dynamicEditor->updateWindowName(); m_dynamicEditor->updateWindowName();

View File

@@ -152,10 +152,34 @@ QString DynamicPropertiesModel::defaultExpressionForType(const TypeName &type)
return expression; 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) DynamicPropertiesModel::DynamicPropertiesModel(bool explicitSelection, AbstractView *parent)
: QStandardItemModel(parent) : QStandardItemModel(parent), m_view(parent), m_explicitSelection(explicitSelection),
, m_view(parent) m_delegate(new DynamicPropertiesModelBackendDelegate(this))
, m_explicitSelection(explicitSelection)
{ {
connect(this, &QStandardItemModel::dataChanged, this, &DynamicPropertiesModel::handleDataChanged); connect(this, &QStandardItemModel::dataChanged, this, &DynamicPropertiesModel::handleDataChanged);
} }
@@ -163,6 +187,7 @@ DynamicPropertiesModel::DynamicPropertiesModel(bool explicitSelection, AbstractV
void DynamicPropertiesModel::resetModel() void DynamicPropertiesModel::resetModel()
{ {
beginResetModel(); beginResetModel();
const int backIndex = m_currentIndex;
clear(); clear();
setHorizontalHeaderLabels({tr("Item"), tr("Property"), tr("Property Type"), tr("Property Value")}); setHorizontalHeaderLabels({tr("Item"), tr("Property"), tr("Property Type"), tr("Property Value")});
@@ -172,7 +197,9 @@ void DynamicPropertiesModel::resetModel()
addModelNode(modelNode); addModelNode(modelNode);
} }
emit currentIndexChanged();
endResetModel(); endResetModel();
m_currentIndex = backIndex;
} }
@@ -344,6 +371,8 @@ void DynamicPropertiesModel::bindingRemoved(const BindingProperty &bindingProper
removeRow(rowNumber); removeRow(rowNumber);
} }
emit currentIndexChanged();
m_handleDataChanged = true; m_handleDataChanged = true;
} }
@@ -360,6 +389,8 @@ void DynamicPropertiesModel::variantRemoved(const VariantProperty &variantProper
removeRow(rowNumber); removeRow(rowNumber);
} }
emit currentIndexChanged();
m_handleDataChanged = true; m_handleDataChanged = true;
} }
@@ -368,6 +399,7 @@ void DynamicPropertiesModel::reset()
m_handleDataChanged = false; m_handleDataChanged = false;
resetModel(); resetModel();
m_handleDataChanged = true; m_handleDataChanged = true;
emit currentIndexChanged();
} }
void DynamicPropertiesModel::setSelectedNode(const ModelNode &node) void DynamicPropertiesModel::setSelectedNode(const ModelNode &node)
@@ -597,6 +629,8 @@ void DynamicPropertiesModel::updateBindingProperty(int rowNumber)
BindingProperty bindingProperty = bindingPropertyForRow(rowNumber); BindingProperty bindingProperty = bindingPropertyForRow(rowNumber);
if (bindingProperty.isValid()) { if (bindingProperty.isValid()) {
updateCustomData(rowNumber, bindingProperty);
QString propertyName = QString::fromUtf8(bindingProperty.name()); QString propertyName = QString::fromUtf8(bindingProperty.name());
updateDisplayRole(rowNumber, PropertyNameRow, propertyName); updateDisplayRole(rowNumber, PropertyNameRow, propertyName);
QString value = bindingProperty.expression(); QString value = bindingProperty.expression();
@@ -617,6 +651,7 @@ void DynamicPropertiesModel::updateVariantProperty(int rowNumber)
VariantProperty variantProperty = variantPropertyForRow(rowNumber); VariantProperty variantProperty = variantPropertyForRow(rowNumber);
if (variantProperty.isValid()) { if (variantProperty.isValid()) {
updateCustomData(rowNumber, variantProperty);
QString propertyName = QString::fromUtf8(variantProperty.name()); QString propertyName = QString::fromUtf8(variantProperty.name());
updateDisplayRole(rowNumber, PropertyNameRow, propertyName); updateDisplayRole(rowNumber, PropertyNameRow, propertyName);
QVariant value = variantProperty.value(); 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.parentModelNode().internalId(), Qt::UserRole + 1);
item->setData(property.name(), Qt::UserRole + 2); 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) void DynamicPropertiesModel::updateCustomData(int row, const AbstractProperty &property)
@@ -924,4 +969,217 @@ const ModelNode DynamicPropertiesModel::singleSelectedNode() const
return m_view->singleSelectedModelNode(); 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 } // namespace QmlDesigner

View File

@@ -5,6 +5,8 @@
#include <nodeinstanceglobal.h> #include <nodeinstanceglobal.h>
#include <studioquickwidget.h>
#include <QStandardItemModel> #include <QStandardItemModel>
namespace QmlDesigner { namespace QmlDesigner {
@@ -15,6 +17,8 @@ class BindingProperty;
class ModelNode; class ModelNode;
class VariantProperty; class VariantProperty;
class DynamicPropertiesModelBackendDelegate;
class DynamicPropertiesModel : public QStandardItemModel class DynamicPropertiesModel : public QStandardItemModel
{ {
Q_OBJECT Q_OBJECT
@@ -27,6 +31,17 @@ public:
PropertyValueRow = 3 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); DynamicPropertiesModel(bool explicitSelection, AbstractView *parent);
void bindingPropertyChanged(const BindingProperty &bindingProperty); void bindingPropertyChanged(const BindingProperty &bindingProperty);
@@ -62,6 +77,17 @@ public:
static QVariant defaultValueForType(const TypeName &type); static QVariant defaultValueForType(const TypeName &type);
static QString defaultExpressionForType(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: protected:
void addProperty(const QVariant &propertyValue, void addProperty(const QVariant &propertyValue,
const QString &propertyType, const QString &propertyType,
@@ -79,12 +105,17 @@ protected:
void updateCustomData(int row, const AbstractProperty &property); void updateCustomData(int row, const AbstractProperty &property);
int findRowForBindingProperty(const BindingProperty &bindingProperty) const; int findRowForBindingProperty(const BindingProperty &bindingProperty) const;
int findRowForVariantProperty(const VariantProperty &variantProperty) 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); void updateDisplayRole(int row, int columns, const QString &string);
QHash<int, QByteArray> roleNames() const override;
DynamicPropertiesModelBackendDelegate *delegate() const;
private: private:
void handleDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); void handleDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
void handleException(); void handleException();
@@ -95,6 +126,48 @@ private:
QString m_exceptionError; QString m_exceptionError;
QList<ModelNode> m_selectedNodes; QList<ModelNode> m_selectedNodes;
bool m_explicitSelection = false; 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 } // namespace QmlDesigner

View File

@@ -205,7 +205,7 @@ void ContentLibraryBundleImporter::handleImportTimer()
for (const QString &pendingType : pendingTypes) { for (const QString &pendingType : pendingTypes) {
NodeMetaInfo metaInfo = model->metaInfo(pendingType.toUtf8()); NodeMetaInfo metaInfo = model->metaInfo(pendingType.toUtf8());
const bool isImport = m_pendingTypes[pendingType]; 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) { if (isImport == typeComplete) {
m_pendingTypes.remove(pendingType); m_pendingTypes.remove(pendingType);
if (isImport) if (isImport)

View File

@@ -68,7 +68,8 @@ void CurveEditorView::modelAboutToBeDetached(Model *model)
bool dirtyfiesView(const ModelNode &node) bool dirtyfiesView(const ModelNode &node)
{ {
return QmlTimeline::isValidQmlTimeline(node) return (node.type() == "QtQuick.Timeline.Keyframe" && node.hasParentProperty())
|| QmlTimeline::isValidQmlTimeline(node)
|| QmlTimelineKeyframeGroup::isValidQmlTimelineKeyframeGroup(node); || QmlTimelineKeyframeGroup::isValidQmlTimelineKeyframeGroup(node);
} }
@@ -143,35 +144,26 @@ void CurveEditorView::variantPropertiesChanged([[maybe_unused]] const QList<Vari
[[maybe_unused]] PropertyChangeFlags propertyChange) [[maybe_unused]] PropertyChangeFlags propertyChange)
{ {
for (const auto &property : propertyList) { for (const auto &property : propertyList) {
if ((property.name() == "frame" || property.name() == "value") if (dirtyfiesView(property.parentModelNode()))
&& property.parentModelNode().type() == "QtQuick.Timeline.Keyframe"
&& property.parentModelNode().hasParentProperty()) {
const ModelNode framesNode = property.parentModelNode().parentProperty().parentModelNode();
if (QmlTimelineKeyframeGroup::isValidQmlTimelineKeyframeGroup(framesNode))
updateKeyframes(); updateKeyframes();
} }
}
} }
void CurveEditorView::bindingPropertiesChanged([[maybe_unused]] const QList<BindingProperty> &propertyList, void CurveEditorView::bindingPropertiesChanged([[maybe_unused]] const QList<BindingProperty> &propertyList,
[[maybe_unused]] PropertyChangeFlags propertyChange) [[maybe_unused]] PropertyChangeFlags propertyChange)
{ {
for (const auto &property : propertyList) { for (const auto &property : propertyList) {
if (property.name() == "easing.bezierCurve") { if (dirtyfiesView(property.parentModelNode()))
updateKeyframes(); updateKeyframes();
} }
}
} }
void CurveEditorView::propertiesRemoved([[maybe_unused]] const QList<AbstractProperty> &propertyList) void CurveEditorView::propertiesRemoved([[maybe_unused]] const QList<AbstractProperty> &propertyList)
{ {
for (const auto &property : propertyList) { for (const auto &property : propertyList) {
if (property.name() == "keyframes") { if (dirtyfiesView(property.parentModelNode()))
ModelNode parent = property.parentModelNode();
if (dirtyfiesView(parent))
updateKeyframes(); updateKeyframes();
} }
}
} }
QmlTimeline CurveEditorView::activeTimeline() const QmlTimeline CurveEditorView::activeTimeline() const

View File

@@ -7,6 +7,7 @@
#include <qmldesignerplugin.h> #include <qmldesignerplugin.h>
#include <bindingproperty.h> #include <bindingproperty.h>
#include <model/modelutils.h>
#include <nodeabstractproperty.h> #include <nodeabstractproperty.h>
#include <nodelistproperty.h> #include <nodelistproperty.h>
#include <nodemetainfo.h> #include <nodemetainfo.h>
@@ -93,9 +94,9 @@ void DebugView::nodeCreated(const ModelNode &createdNode)
message << createdNode.majorVersion() << "." << createdNode.minorVersion(); message << createdNode.majorVersion() << "." << createdNode.minorVersion();
message << createdNode.nodeSource(); message << createdNode.nodeSource();
message << "MetaInfo " << createdNode.metaInfo().isValid() << " "; message << "MetaInfo " << createdNode.metaInfo().isValid() << " ";
if (createdNode.metaInfo().isValid()) { if (auto metaInfo = createdNode.metaInfo()) {
message << createdNode.metaInfo().majorVersion() << "." << createdNode.metaInfo().minorVersion(); message << metaInfo.majorVersion() << "." << metaInfo.minorVersion();
message << createdNode.metaInfo().componentFileName(); message << ModelUtils::componentFilePath(createdNode);
} }
log("::nodeCreated:", message.readAll()); log("::nodeCreated:", message.readAll());
} }
@@ -282,9 +283,10 @@ void DebugView::selectedNodesChanged(const QList<ModelNode> &selectedNodes /*sel
message << lineBreak; message << lineBreak;
if (selectedNode.metaInfo().isValid()) { if (selectedNode.metaInfo().isValid()) {
for (const NodeMetaInfo &metaInfo : selectedNode.metaInfo().classHierarchy()) for (const NodeMetaInfo &metaInfo : selectedNode.metaInfo().selfAndPrototypes()) {
message << metaInfo.typeName() << " " << metaInfo.majorVersion() << "." message << metaInfo.typeName() << " " << metaInfo.majorVersion() << "."
<< metaInfo.minorVersion() << lineBreak; << metaInfo.minorVersion() << lineBreak;
}
message << lineBreak; message << lineBreak;
message << selectedNode.metaInfo().typeName(); message << selectedNode.metaInfo().typeName();

View File

@@ -3,19 +3,20 @@
#include "bakelights.h" #include "bakelights.h"
#include "abstractview.h" #include <abstractview.h>
#include "auxiliarydataproperties.h" #include <auxiliarydataproperties.h>
#include "bakelightsdatamodel.h" #include <bakelightsconnectionmanager.h>
#include "bakelightsconnectionmanager.h" #include <bakelightsdatamodel.h>
#include "bindingproperty.h" #include <bindingproperty.h>
#include "documentmanager.h" #include <documentmanager.h>
#include "modelnode.h" #include <model/modelutils.h>
#include "nodeabstractproperty.h" #include <modelnode.h>
#include "nodeinstanceview.h" #include <nodeabstractproperty.h>
#include "nodemetainfo.h" #include <nodeinstanceview.h>
#include "plaintexteditmodifier.h" #include <nodemetainfo.h>
#include "rewriterview.h" #include <plaintexteditmodifier.h>
#include "variantproperty.h" #include <rewriterview.h>
#include <variantproperty.h>
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
@@ -240,18 +241,21 @@ void BakeLights::rebake()
void BakeLights::exposeModelsAndLights(const QString &nodeId) void BakeLights::exposeModelsAndLights(const QString &nodeId)
{ {
ModelNode compNode = m_view->modelNodeForId(nodeId); ModelNode compNode = m_view->modelNodeForId(nodeId);
if (!compNode.isValid() || !compNode.isComponent() if (!compNode.isValid() || !compNode.isComponent()) {
|| compNode.metaInfo().componentFileName().isEmpty()) { return;
}
auto componentFilePath = ModelUtils::componentFilePath(compNode);
if (componentFilePath.isEmpty()) {
return; return;
} }
RewriterView rewriter{m_view->externalDependencies(), RewriterView::Amend}; RewriterView rewriter{m_view->externalDependencies(), RewriterView::Amend};
ModelPointer compModel = QmlDesigner::Model::create("QtQuick/Item", 2, 1); ModelPointer compModel = QmlDesigner::Model::create("QtQuick/Item", 2, 1);
const QString compFile = compNode.metaInfo().componentFileName(); const Utils::FilePath compFilePath = Utils::FilePath::fromString(componentFilePath);
const Utils::FilePath compFilePath = Utils::FilePath::fromString(compFile);
QByteArray src = compFilePath.fileContents().value(); 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 textDocument = std::make_unique<QTextDocument>(QString::fromUtf8(src));
auto modifier = std::make_unique<IndentingTextEditModifier>( auto modifier = std::make_unique<IndentingTextEditModifier>(
@@ -295,12 +299,12 @@ void BakeLights::exposeModelsAndLights(const QString &nodeId)
QString newText = modifier->text(); QString newText = modifier->text();
if (newText != originalText) { if (newText != originalText) {
QSaveFile saveFile(compFile); QSaveFile saveFile(componentFilePath);
if (saveFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { if (saveFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
saveFile.write(newText.toUtf8()); saveFile.write(newText.toUtf8());
saveFile.commit(); saveFile.commit();
} else { } else {
qWarning() << __FUNCTION__ << "Failed to save changes to:" << compFile; qWarning() << __FUNCTION__ << "Failed to save changes to:" << componentFilePath;
} }
} }

View File

@@ -3,6 +3,8 @@
#include "backgroundaction.h" #include "backgroundaction.h"
#include <theme.h>
#include <utils/stylehelper.h> #include <utils/stylehelper.h>
#include <QComboBox> #include <QComboBox>
@@ -28,6 +30,14 @@ QIcon iconForColor(const QColor &color) {
image.fill(0); image.fill(0);
QPainter p(&image); 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); p.fillRect(2, 2, size - 4, size - 4, Qt::black);
if (color.alpha() == 0) { if (color.alpha() == 0) {
@@ -70,13 +80,13 @@ QList<QColor> BackgroundAction::colors()
{ {
static QColor alphaZero(Qt::transparent); static QColor alphaZero(Qt::transparent);
static QList<QColor> colorList = {alphaZero, static QList<QColor> colorList = {alphaZero,
QColor(BackgroundAction::ContextImage),
QColor(Qt::black), QColor(Qt::black),
QColor(0x4c4e50), QColor(0x4c4e50),
QColor(Qt::darkGray), QColor(Qt::darkGray),
QColor(Qt::lightGray), QColor(Qt::lightGray),
QColor(Qt::white)}; QColor(Qt::white)};
return colorList; return colorList;
} }

View File

@@ -11,14 +11,12 @@ namespace QmlDesigner {
class BackgroundAction : public QWidgetAction class BackgroundAction : public QWidgetAction
{ {
enum BackgroundType { enum BackgroundType { CheckboardBackground, WhiteBackground, BlackBackground };
CheckboardBackground,
WhiteBackground,
BlackBackground
};
Q_OBJECT Q_OBJECT
public: public:
enum SpecialColor { ContextImage = Qt::yellow };
explicit BackgroundAction(QObject *parent); explicit BackgroundAction(QObject *parent);
void setColor(const QColor &color); void setColor(const QColor &color);

View File

@@ -2,9 +2,13 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "formeditorgraphicsview.h" #include "formeditorgraphicsview.h"
#include "backgroundaction.h"
#include "formeditoritem.h" #include "formeditoritem.h"
#include "formeditorwidget.h" #include "formeditorwidget.h"
#include "navigation2d.h" #include "navigation2d.h"
#include <theme.h>
#include <utils/hostosinfo.h> #include <utils/hostosinfo.h>
#include <QAction> #include <QAction>
@@ -198,10 +202,28 @@ void FormEditorGraphicsView::drawBackground(QPainter *painter, const QRectF &rec
painter->save(); painter->save();
painter->setBrushOrigin(0, 0); painter->setBrushOrigin(0, 0);
painter->fillRect(rectangle.intersected(rootItemRect()), backgroundBrush());
// paint rect around editable area // 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(); painter->restore();
} }
@@ -210,6 +232,17 @@ void FormEditorGraphicsView::frame(const QRectF &boundingRect)
fitInView(boundingRect, Qt::KeepAspectRatio); 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) void FormEditorGraphicsView::setZoomFactor(double zoom)
{ {
resetTransform(); resetTransform();

View File

@@ -28,6 +28,9 @@ public:
void setZoomFactor(double zoom); void setZoomFactor(double zoom);
void frame(const QRectF &bbox); void frame(const QRectF &bbox);
void setBackgoundImage(const QImage &image);
QImage backgroundImage() const;
protected: protected:
bool eventFilter(QObject *watched, QEvent *event) override; bool eventFilter(QObject *watched, QEvent *event) override;
void wheelEvent(QWheelEvent *event) override; void wheelEvent(QWheelEvent *event) override;
@@ -45,6 +48,7 @@ private:
Panning m_isPanning = Panning::NotStarted; Panning m_isPanning = Panning::NotStarted;
QPoint m_panningStartPosition; QPoint m_panningStartPosition;
QRectF m_rootItemRect; QRectF m_rootItemRect;
QImage m_backgroundImage;
}; };
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -63,6 +63,8 @@ void FormEditorView::modelAttached(Model *model)
if (!isEnabled()) if (!isEnabled())
return; return;
m_formEditorWidget->setBackgoundImage({});
temporaryBlockView(); temporaryBlockView();
setupFormEditorWidget(); setupFormEditorWidget();
@@ -649,6 +651,10 @@ void FormEditorView::auxiliaryDataChanged(const ModelNode &node,
if (FormEditorItem *editorItem = scene()->itemForQmlItemNode(item)) if (FormEditorItem *editorItem = scene()->itemForQmlItemNode(item))
editorItem->setFrameColor(data.value<QColor>()); editorItem->setFrameColor(data.value<QColor>());
} }
if (key == contextImageProperty) {
m_formEditorWidget->setBackgoundImage(data.value<QImage>());
}
} }
static void updateTransitions(FormEditorScene *scene, const QmlItemNode &qmlItemNode) static void updateTransitions(FormEditorScene *scene, const QmlItemNode &qmlItemNode)
@@ -784,6 +790,11 @@ void FormEditorView::exportAsImage()
m_formEditorWidget->exportAsImage(m_scene->rootFormEditorItem()->boundingRect()); m_formEditorWidget->exportAsImage(m_scene->rootFormEditorItem()->boundingRect());
} }
QImage FormEditorView::takeFormEditorScreenshot()
{
return m_formEditorWidget->takeFormEditorScreenshot();
}
QPicture FormEditorView::renderToPicture() const QPicture FormEditorView::renderToPicture() const
{ {
return m_formEditorWidget->renderToPicture(); return m_formEditorWidget->renderToPicture();
@@ -954,6 +965,11 @@ void FormEditorView::setupRootItemSize()
formEditorWidget()->setRootItemRect(rootQmlNode.instanceBoundingRect()); formEditorWidget()->setRootItemRect(rootQmlNode.instanceBoundingRect());
formEditorWidget()->centerScene(); 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