QmlDesigner: Add top level toolbar

* Added Designer\TopToolBar=true to .ini to enable toolbar
* Added backend to CrumbleBar
* Added crumble bar
* Added QML toolbar
* Disabled original toolbar in DesignModeWidget
* Added callback to DesignerActionManager
* Added id to ZoomPreviewAction
* Dock Manager is exposed in DesignModeWidget
* Added new version of icon font

Change-Id: I8c8ad16137c84229854a1d0fa6dfdf498edf4253
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
Reviewed-by: Henning Gründl <henning.gruendl@qt.io>
This commit is contained in:
Thomas Hartmann
2023-01-23 11:59:10 +01:00
parent 1b0a3c71b7
commit 2e334303fe
27 changed files with 1698 additions and 88 deletions

View File

@@ -307,7 +307,7 @@ Rectangle {
}
}
onCurrentStateInternalIdChanged: layoutTimer.start()
//onCurrentStateInternalIdChanged: layoutTimer.start()
StudioControls.Dialog {
id: editDialog
@@ -573,6 +573,8 @@ Rectangle {
y: scrollView.height - height
width: scrollView.availableWidth
orientation: Qt.Horizontal
active: frame.contentHeight > scrollView.height
visible: frame.contentHeight > scrollView.height
}
ScrollBar.vertical: StateScrollBar {
@@ -582,6 +584,8 @@ Rectangle {
y: scrollView.topPadding
height: scrollView.availableHeight
orientation: Qt.Vertical
active: frame.contentWidth > scrollView.width
visible: frame.contentWidth > scrollView.width
}
Flickable {

View File

@@ -45,6 +45,7 @@ T.AbstractButton {
color: control.style.background.idle
border.color: control.style.border.idle
border.width: control.style.borderWidth
radius: control.style.radius
}
indicator: Item {
@@ -77,7 +78,7 @@ T.AbstractButton {
when: control.enabled && control.pressed
PropertyChanges {
target: buttonIcon
color: control.style.icon.idle
color: control.style.icon.interaction
}
},
State {

View File

@@ -93,8 +93,8 @@ T.ComboBox {
__parentPopup: control.popup
x: comboBoxInput.x + comboBoxInput.width
y: control.style.borderWidth
width: control.style.baseIconSize.width - control.style.borderWidth
height: control.style.baseIconSize.height - control.style.borderWidth * 2
width: control.style.squareControlSize.width - control.style.borderWidth
height: control.style.squareControlSize.height - control.style.borderWidth * 2
}
background: Rectangle {
@@ -162,7 +162,7 @@ T.ComboBox {
: control.style.text.idle
font.family: StudioTheme.Constants.iconFont.family
font.pixelSize: control.style.smallIconFontSize
visible: control.currentIndex === index ? true : false
visible: control.currentIndex === index
anchors.fill: parent
renderType: Text.NativeRendering
horizontalAlignment: Text.AlignHCenter
@@ -261,8 +261,7 @@ T.ComboBox {
// tab focus. It is therefor possible to use the mouse wheel to scroll through the items.
State {
name: "focus"
when: control.enabled && control.activeFocus && !control.editable
&& !control.open
when: control.enabled && control.activeFocus && !control.editable && !control.open
PropertyChanges {
target: control
wheelEnabled: true

View File

@@ -87,7 +87,8 @@ TextInput {
},
State {
name: "dragHover"
when: control.__parentControl.enabled && control.__parentControl.hasActiveHoverDrag
when: control.__parentControl.enabled
&& (control.__parentControl.hasActiveHoverDrag ?? false)
PropertyChanges {
target: background
color: control.style.background.interaction

View File

@@ -0,0 +1,230 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
import QtQuick
import QtQuick.Templates 2.12 as T
import StudioTheme 1.0 as StudioTheme
T.ComboBox {
id: control
property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle
property bool hover: (comboBoxInput.hover || window.visible) && control.enabled
property bool edit: false
property bool open: window.visible
editable: false
width: control.style.controlSize.width
height: control.style.controlSize.height
leftPadding: control.style.borderWidth
rightPadding: popupIndicator.width + control.style.borderWidth
font.pixelSize: control.style.baseFontSize
delegate: ItemDelegate {
required property int index
required property var modelData
width: control.width
highlighted: control.highlightedIndex === index
contentItem: Text {
text: modelData
color: "#21be2b"
font: control.font
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
}
indicator: CheckIndicator {
id: popupIndicator
style: control.style
__parentControl: control
__parentPopup: comboBoxPopup
x: comboBoxInput.x + comboBoxInput.width
y: control.style.borderWidth
width: control.style.squareControlSize.width - control.style.borderWidth
height: control.style.squareControlSize.height - control.style.borderWidth * 2
}
contentItem: ComboBoxInput {
id: comboBoxInput
style: control.style
__parentControl: control
text: control.editText
}
background: Rectangle {
id: comboBoxBackground
color: control.style.background.idle
border.color: control.style.border.idle
border.width: control.style.borderWidth
width: control.width
height: control.height
}
popup: T.Popup {
id: comboBoxPopup
width: 0
height: 0
closePolicy: T.Popup.CloseOnEscape | T.Popup.CloseOnPressOutsideParent
onAboutToShow: {
control.menuDelegate.parent = window.contentItem
control.menuDelegate.visible = true
window.show()
window.requestActivate()
window.x = control.mapToGlobal(0,0).x
window.y = control.mapToGlobal(0,0).y + control.height
control.menuDelegate.focus = true
}
onAboutToHide: window.hide()
}
// Close popup when application goes to background
Connections {
target: Qt.application
function onStateChanged() {
if (Qt.application.state === Qt.ApplicationInactive)
comboBoxPopup.close()
}
}
Window {
id: window
width: control.menuDelegate.implicitWidth
height: control.menuDelegate.implicitHeight
visible: false
flags: Qt.Popup | Qt.NoDropShadowWindowHint
modality: Qt.NonModal
transientParent: control.Window.window
color: "transparent"
}
property Menu menuDelegate: Menu {
id: textEditMenu
y: 0
width: control.width
overlap: 0
Repeater {
model: control.model
MenuItem {
id: menuItem
x: 0
text: modelData
onTriggered: {
control.currentIndex = index
comboBoxPopup.close()
}
background: Rectangle {
implicitWidth: textLabel.implicitWidth + menuItem.labelSpacing
+ menuItem.leftPadding + menuItem.rightPadding
implicitHeight: control.style.controlSize.height
x: control.style.borderWidth
y: control.style.borderWidth
width: menuItem.menu.width - (control.style.borderWidth * 2)
height: menuItem.height - (control.style.borderWidth * 2)
color: menuItem.highlighted ? control.style.interaction
: "transparent"
}
contentItem: Item {
Text {
id: textLabel
leftPadding: itemDelegateIconArea.width
text: menuItem.text
font: control.font
color: menuItem.highlighted ? control.style.text.selectedText
: control.style.text.idle
anchors.verticalCenter: parent.verticalCenter
}
Item {
id: itemDelegateIconArea
width: menuItem.height
height: menuItem.height
T.Label {
id: itemDelegateIcon
text: StudioTheme.Constants.tickIcon
color: textLabel.color
font.family: StudioTheme.Constants.iconFont.family
font.pixelSize: control.style.smallIconFontSize
visible: control.currentIndex === index
anchors.fill: parent
renderType: Text.NativeRendering
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
}
}
}
}
states: [
State {
name: "default"
when: control.enabled && !control.hover && !control.edit && !control.open
&& !control.activeFocus && !control.hasActiveDrag
PropertyChanges {
target: control
wheelEnabled: false
}
PropertyChanges {
target: comboBoxInput
selectByMouse: false
}
PropertyChanges {
target: comboBoxBackground
color: control.style.background.idle
}
},
// This state is intended for ComboBoxes which aren't editable, but have focus e.g. via
// tab focus. It is therefor possible to use the mouse wheel to scroll through the items.
State {
name: "focus"
when: control.enabled && control.activeFocus && !control.editable && !control.open
PropertyChanges {
target: control
wheelEnabled: true
}
PropertyChanges {
target: comboBoxInput
focus: true
}
},
State {
name: "popup"
when: control.enabled && control.open
PropertyChanges {
target: control
wheelEnabled: true
}
PropertyChanges {
target: comboBoxInput
selectByMouse: false
readOnly: true
}
PropertyChanges {
target: comboBoxBackground
color: control.style.background.interaction
border.color: control.style.border.interaction
}
},
State {
name: "disable"
when: !control.enabled
PropertyChanges {
target: comboBoxBackground
color: control.style.background.disabled
border.color: control.style.border.disabled
}
}
]
}

View File

@@ -49,3 +49,4 @@ TextField 1.0 TextField.qml
ToolTip 1.0 ToolTip.qml
TranslationIndicator 1.0 TranslationIndicator.qml
VerticalScrollBar 1.0 VerticalScrollBar.qml
TopLevelComboBox 1.0 TopLevelComboBox.qml

View File

@@ -17,7 +17,7 @@ QtObject {
property real bigIconFontSize: Values.bigIconFontSize
property real borderWidth: Values.border
property real radius: 4
property real radius: Values.radius
property size smallControlSize: Qt.size(Values.smallRectWidth,
Values.smallRectWidth)
@@ -28,7 +28,6 @@ QtObject {
property size smallIconSize: Qt.size(Values.spinControlIconSizeMulti,
Values.spinControlIconSizeMulti)
property size baseIconSize: Qt.size(Values.height, Values.height)
// TODO only used once
property size spinBoxIndicatorSize: Qt.size(Values.spinBoxIndicatorWidth,
@@ -92,6 +91,7 @@ QtObject {
component BorderColors: QtObject {
property color idle: Values.themeControlOutline
property color interaction: Values.themeControlOutlineInteraction
property color hover: "red"
property color disabled: Values.themeControlOutlineDisabled
}

View File

@@ -4,7 +4,7 @@
import QtQuick
ControlStyle {
background {
idle: "red"
}
controlSize: Qt.size(Values.topLevelComboWidth, Values.topLevelComboHeight)
baseIconFontSize: Values.topLevelComboIcon
smallIconFontSize: 10
}

View File

@@ -10,6 +10,10 @@ QtObject {
property real baseHeight: 29
property real topLevelComboWidth: 210
property real topLevelComboHeight: 36
property real topLevelComboIcon: 20
property real smallFont: 8
property real baseFont: 12
property real mediumFont: 14
@@ -77,6 +81,7 @@ QtObject {
property real marginTopBottom: 4
property real border: 1
property real borderHover: 3
property real radius: 0
property real maxComboBoxPopupHeight: Math.round(300 * values.scaleFactor)
property real maxTextAreaPopupHeight: Math.round(150 * values.scaleFactor)
@@ -350,4 +355,24 @@ QtObject {
property ControlStyle controlStyle: DefaultStyle {}
property ControlStyle toolbarStyle: ToolbarStyle {}
property ControlStyle primaryToolbarStyle: ToolbarStyle {
baseIconFontSize: values.baseFontSize
radius: 4
icon: ControlStyle.IconColors {
idle: values.themeTextSelectedTextColor
disabled: "#636363"
}
background: ControlStyle.BackgroundColors {
idle: values.themeInteraction
}
border: ControlStyle.BorderColors {
idle: values.themeInteraction
hover: "#000000"
interaction: "#DCDADA"
disabled: "#636363"
}
}
}

View File

@@ -0,0 +1,21 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
import QtQuick 2.15
import QtQuick.Controls 2.15
import StudioControls
import StudioTheme 1.0 as StudioTheme
Item {
id: toolbarContainer
height: 24
width: 2024
Rectangle {
color: "#2d2d2d"
anchors.fill: parent
}
}

View File

@@ -0,0 +1,93 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
import QtQuick
import QtQuick.Controls
ListView {
id: root
property real crumbleWidth: 166
property real crumbleHeight: 36
property real inset: 5
property real strokeWidth: 1
property real textLeftMargin: 18
property real textTopMargin: 6
property real textRightMargin: 6
property real textBottomMargin: 6
property alias font: fontMetrics.font
signal clicked(index: int)
boundsBehavior: Flickable.StopAtBounds
flickableDirection: Flickable.HorizontalFlick
interactive: true
orientation: ListView.Horizontal
clip: true
focus: true
function updateContentX() {
root.contentX = (root.contentWidth < root.width) ? 0 : root.contentWidth - root.width
}
onCountChanged: root.updateContentX()
onWidthChanged: root.updateContentX()
visible: root.count > 1 && root.width > (root.crumbleWidth) - 24
delegate: CrumbleBread {
text: fileName
tooltip: fileAddress
width: root.crumbleWidth
height: root.crumbleHeight
inset: root.inset
strokeWidth: root.strokeWidth
textLeftMargin: root.textLeftMargin
textTopMargin: root.textTopMargin
textRightMargin: root.textRightMargin
textBottomMargin: root.textBottomMargin
font: root.font
modelSize: root.count
onClicked: {
if (index + 1 < root.count)
root.clicked(index)
}
}
add: Transition {
NumberAnimation {
property: "opacity"
from: 0
to: 1.0
duration: 400
}
NumberAnimation {
property: "scale"
from: 0
to: 1.0
duration: 400
}
}
displaced: Transition {
NumberAnimation {
property: "x"
duration: 400
easing.type: Easing.OutBack
}
}
FontMetrics {
id: fontMetrics
font.pixelSize: 12
font.family: "SF Pro"
}
}

View File

@@ -0,0 +1,176 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
import QtQuick
import QtQuick.Controls
import QtQuick.Shapes
Item {
id: root
antialiasing: true
property int modelSize
/* Colors might come from Theme */
property color idleBackgroundColor: "#2e2f30"
property color idleStrokeColor: "#1f1f1f"
property color idleTextColor: "#ffffff"
property color hoverBackgroundColor: "#434343"
property color hoverStrokeColor: "#434343"
property color activeColor: "#57b9fc"
property color activeTextColor: "#1f1f1f"
property string tooltip
property alias text: label.text
property alias font: label.font
property alias inset: backgroundPath.inset
property alias strokeWidth: backgroundPath.strokeWidth
property real textLeftMargin: 18
property real textTopMargin: 6
property real textRightMargin: 6
property real textBottomMargin: 6
readonly property int itemIndex: index
readonly property bool isFirst: itemIndex === 0
readonly property bool isLast: (itemIndex + 1) === modelSize
signal clicked(int callIdx)
width: 166
height: 36
Shape {
id: backgroundShape
anchors.fill: root
antialiasing: root.antialiasing
layer.enabled: antialiasing
layer.smooth: antialiasing
layer.samples: antialiasing ? 4 : 0
ShapePath {
id: backgroundPath
joinStyle: ShapePath.MiterJoin
fillColor: root.idleBackgroundColor
strokeColor: root.idleStrokeColor
strokeWidth: 1
property real inset: 5
property real rightOut: root.isLast ? 0 : root.inset
property real leftIn: root.isFirst ? 0 : root.inset
property real strokeOffset: backgroundPath.strokeWidth / 2
property real topY: backgroundPath.strokeOffset
property real bottomY: backgroundShape.height - backgroundPath.strokeOffset
property real halfY: (backgroundPath.topY + backgroundPath.bottomY) / 2
startX: backgroundPath.strokeOffset
startY: backgroundPath.topY
PathLine {
x: backgroundShape.width - backgroundPath.strokeOffset - backgroundPath.rightOut
y: backgroundPath.topY
}
PathLine {
x: backgroundShape.width - backgroundPath.strokeOffset
y: backgroundPath.halfY
}
PathLine {
x: backgroundShape.width - backgroundPath.strokeOffset - backgroundPath.rightOut
y: backgroundPath.bottomY
}
PathLine {
x: backgroundPath.strokeOffset
y: backgroundPath.bottomY
}
PathLine {
x: backgroundPath.leftIn + backgroundPath.strokeOffset
y: backgroundPath.halfY
}
PathLine {
x: backgroundPath.strokeOffset
y: backgroundPath.topY
}
}
}
Text {
id: label
anchors.fill: parent
anchors.leftMargin: root.textLeftMargin
anchors.topMargin: root.textTopMargin
anchors.rightMargin: root.textRightMargin
anchors.bottomMargin: root.textBottomMargin
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
color: root.idleTextColor
wrapMode: Text.Wrap
elide: Text.ElideRight
maximumLineCount: 1
text: "Crumble File"
font.pixelSize: 12
font.family: "SF Pro"
ToolTip.text: root.tooltip
ToolTip.visible: mouseArea.containsMouse
ToolTip.delay: 1000
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: root.clicked(root.itemIndex)
}
states: [
State {
name: "idle"
when: !mouseArea.containsMouse && !mouseArea.pressed && !root.isLast
PropertyChanges {
target: backgroundPath
fillColor: root.idleBackgroundColor
strokeColor: root.idleStrokeColor
}
},
State {
name: "active"
when: root.isLast
extend: "pressed"
},
State {
name: "hover"
when: mouseArea.containsMouse && !mouseArea.pressed
PropertyChanges {
target: backgroundPath
fillColor: root.hoverBackgroundColor
strokeColor: root.hoverStrokeColor
}
},
State {
name: "pressed"
when: mouseArea.pressed
PropertyChanges {
target: backgroundPath
strokeColor: root.activeColor
fillColor: root.activeColor
}
PropertyChanges {
target: label
color: root.activeTextColor
}
}
]
}

View File

@@ -0,0 +1,223 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
import QtQuick 2.15
import QtQuick.Controls 2.15
import StudioControls
import StudioTheme 1.0 as StudioTheme
import ToolBar 1.0
Rectangle {
id: toolbarContainer
color: "#2d2d2d"
border.color: "#00000000"
height: 56
width: 2024
ToolBarBackend {
id: backend
}
Item {
id: topToolbarOtherMode
anchors.fill: parent
visible: !backend.isInDesignMode
ToolbarButton {
id: homeOther
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 10
enabled: backend.isDesignModeEnabled
tooltip: qsTr("Switch to Design Mode")
buttonIcon: StudioTheme.Constants.topToolbar_designMode
onClicked: backend.triggerModeChange()
}
}
Item {
id: topToolbar
anchors.fill: parent
visible: backend.isInDesignMode
ToolbarButton {
id: home
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 10
buttonIcon: StudioTheme.Constants.topToolbar_home
onClicked: backend.triggerModeChange()
}
ToolbarButton {
id: runProject
anchors.verticalCenter: parent.verticalCenter
anchors.left: home.right
anchors.leftMargin: 10
buttonIcon: StudioTheme.Constants.topToolbar_runProject
style: StudioTheme.ToolbarStyle {
icon: StudioTheme.ControlStyle.IconColors {
idle: "#649a5d"
}
}
onClicked: backend.runProject()
}
ToolbarButton {
id: livePreviewButton
style: StudioTheme.Values.primaryToolbarStyle
width: 96
anchors.verticalCenter: parent.verticalCenter
anchors.left: runProject.right
anchors.leftMargin: 10
iconFont: StudioTheme.Constants.font
buttonIcon: qsTr("Live Preview")
onClicked: livePreview.trigger()
ActionSubscriber {
id: livePreview
actionId: "LivePreview"
}
}
TopLevelComboBox {
id: currentFile
style: StudioTheme.Values.toolbarStyle
width: 320
anchors.verticalCenter: parent.verticalCenter
anchors.left: livePreviewButton.right
anchors.leftMargin: 10
model: backend.documentModel
currentIndex: backend.documentIndex
onActivated: backend.openFileByIndex(index)
}
ToolbarButton {
id: backButton
anchors.verticalCenter: parent.verticalCenter
anchors.left: currentFile.right
anchors.leftMargin: 10
enabled: backend.canGoBack
tooltip: qsTr("Go Back")
buttonIcon: StudioTheme.Constants.topToolbar_navFile
iconRotation: 0
onClicked: backend.goBackward()
}
ToolbarButton {
id: forwardButton
anchors.verticalCenter: parent.verticalCenter
anchors.left: backButton.right
anchors.leftMargin: 10
enabled: backend.canGoForward
tooltip: qsTr("Go Forward")
buttonIcon: StudioTheme.Constants.topToolbar_navFile
iconRotation: 180
onClicked: backend.goForward()
}
ToolbarButton {
id: closeButton
anchors.verticalCenter: parent.verticalCenter
anchors.left: forwardButton.right
anchors.leftMargin: 10
tooltip: qsTr("Close")
buttonIcon: StudioTheme.Constants.topToolbar_closeFile
onClicked: backend.closeCurrentDocument()
}
CrumbleBar {
id: flickable
height: 36
anchors.left: closeButton.right
anchors.leftMargin: 10
anchors.right: createComponent.left
anchors.rightMargin: 10
anchors.verticalCenter: parent.verticalCenter
model: CrumbleBarModel {
id: crumbleBarModel
}
onClicked: crumbleBarModel.onCrumblePathElementClicked(index)
}
ToolbarButton {
id: createComponent
anchors.verticalCenter: parent.verticalCenter
anchors.right: enterComponent.left
anchors.rightMargin: 10
enabled: moveToComponentBackend.available
tooltip: moveToComponentBackend.tooltip
buttonIcon: StudioTheme.Constants.topToolbar_makeComponent
onClicked: moveToComponentBackend.trigger()
ActionSubscriber {
id: moveToComponentBackend
actionId: "MakeComponent"
}
}
ToolbarButton {
id: enterComponent
anchors.verticalCenter: parent.verticalCenter
anchors.right: workspaces.left
anchors.rightMargin: 10
enabled: goIntoComponentBackend.available
tooltip: goIntoComponentBackend.tooltip
buttonIcon: StudioTheme.Constants.topToolbar_enterComponent
onClicked: goIntoComponentBackend.trigger()
ActionSubscriber {
id: goIntoComponentBackend
actionId: "GoIntoComponent"
}
}
TopLevelComboBox {
id: workspaces
style: StudioTheme.Values.toolbarStyle
width: 210
anchors.verticalCenter: parent.verticalCenter
anchors.right: annotations.left
anchors.rightMargin: 10
model: backend.workspaces
onCurrentTextChanged: backend.setCurrentWorkspace(workspaces.currentText)
}
ToolbarButton {
id: annotations
anchors.verticalCenter: parent.verticalCenter
anchors.right: shareButton.left
anchors.rightMargin: 10
tooltip: qsTr("Edit Annotations")
buttonIcon: StudioTheme.Constants.topToolbar_annotations
onClicked: backend.editGlobalAnnoation()
}
ToolbarButton {
id: shareButton
style: StudioTheme.Values.primaryToolbarStyle
width: 96
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: 8
iconFont: StudioTheme.Constants.font
buttonIcon: qsTr("Share")
onClicked: backend.shareApplicationOnline()
}
}
}

View File

@@ -0,0 +1,24 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
import QtQuick
import StudioControls 1.0 as StudioControls
import StudioTheme 1.0 as StudioTheme
import HelperWidgets 2.0
StudioControls.AbstractButton {
id: button
property alias tooltip: toolTipArea.tooltip
style: StudioTheme.Values.toolbarStyle
hover: toolTipArea.containsMouse
ToolTipArea {
id: toolTipArea
anchors.fill: parent
// Without setting the acceptedButtons property the clicked event won't
// reach the AbstractButton, it will be consumed by the ToolTipArea
acceptedButtons: Qt.NoButton
}
}

View File

@@ -1111,6 +1111,15 @@ extend_qtc_plugin(QmlDesigner
shortcutwidget.cpp shortcutwidget.h
)
extend_qtc_plugin(QmlDesigner
SOURCES_PREFIX components/toolbar
SOURCES
toolbar.cpp
toolbar.h
toolbarbackend.cpp
toolbarbackend.h
)
extend_qtc_plugin(QmlDesigner
CONDITION TARGET Nanotrace
DEPENDS Nanotrace

View File

@@ -6,6 +6,7 @@
#include "qmldesignerplugin.h"
#include <nodeabstractproperty.h>
#include <toolbar.h>
#include <coreplugin/documentmanager.h>
#include <coreplugin/idocument.h>
@@ -60,6 +61,7 @@ void CrumbleBar::pushFile(const Utils::FilePath &fileName)
{
if (!m_isInternalCalled) {
crumblePath()->clear();
m_pathes.clear();
} else {
// If the path already exists in crumblePath, pop up to first instance of that to avoid
// cyclical crumblePath
@@ -71,8 +73,10 @@ void CrumbleBar::pushFile(const Utils::FilePath &fileName)
}
if (match != -1) {
for (int i = crumblePath()->length() - 1 - match; i > 0; --i)
for (int i = crumblePath()->length() - 1 - match; i > 0; --i) {
crumblePath()->popElement();
m_pathes.removeLast();
}
}
}
@@ -81,10 +85,13 @@ void CrumbleBar::pushFile(const Utils::FilePath &fileName)
CrumbleBarInfo crumbleBarInfo;
crumbleBarInfo.fileName = fileName;
crumblePath()->pushElement(fileName.fileName(), QVariant::fromValue(crumbleBarInfo));
m_pathes.append({fileName, fileName.fileName(), {}});
}
m_isInternalCalled = false;
updateVisibility();
emit pathChanged();
}
void CrumbleBar::pushInFileComponent(const ModelNode &modelNode)
@@ -97,10 +104,13 @@ void CrumbleBar::pushInFileComponent(const ModelNode &modelNode)
crumblePath()->popElement();
crumblePath()->pushElement(crumbleBarInfo.displayName, QVariant::fromValue(crumbleBarInfo));
m_pathes.append({{}, crumbleBarInfo.displayName, modelNode});
m_isInternalCalled = false;
updateVisibility();
emit pathChanged();
}
void CrumbleBar::nextFileIsCalledInternally()
@@ -120,6 +130,21 @@ Utils::CrumblePath *CrumbleBar::crumblePath()
return m_crumblePath;
}
QStringList CrumbleBar::path() const
{
QStringList list;
for (auto &path : m_pathes) {
list.append(path.displayName);
}
return list;
}
QList<CrumbleBarInfo> CrumbleBar::infos() const
{
return m_pathes;
}
bool CrumbleBar::showSaveDialog()
{
bool canceled = false;
@@ -151,11 +176,15 @@ void CrumbleBar::onCrumblePathElementClicked(const QVariant &data)
if (!inlineComp && !showSaveDialog())
return;
while (clickedCrumbleBarInfo != crumblePath()->dataForLastIndex().value<CrumbleBarInfo>())
while (clickedCrumbleBarInfo != crumblePath()->dataForLastIndex().value<CrumbleBarInfo>()) {
crumblePath()->popElement();
m_pathes.removeLast();
}
if (crumblePath()->dataForLastIndex().value<CrumbleBarInfo>().modelNode.isValid())
if (crumblePath()->dataForLastIndex().value<CrumbleBarInfo>().modelNode.isValid()) {
crumblePath()->popElement();
m_pathes.removeLast();
}
m_isInternalCalled = true;
if (inlineComp) {
@@ -164,6 +193,7 @@ void CrumbleBar::onCrumblePathElementClicked(const QVariant &data)
QmlDesignerPlugin::instance()->viewManager().setComponentViewToMaster();
} else {
crumblePath()->popElement();
m_pathes.removeLast();
nextFileIsCalledInternally();
Core::EditorManager::openEditor(clickedCrumbleBarInfo.fileName,
Utils::Id(),
@@ -175,12 +205,14 @@ void CrumbleBar::onCrumblePathElementClicked(const QVariant &data)
QmlDesignerPlugin::instance()->viewManager().setComponentViewToMaster();
}
}
emit pathChanged();
updateVisibility();
}
void CrumbleBar::updateVisibility()
{
crumblePath()->setVisible(crumblePath()->length() > 1);
if (!ToolBar::isVisible())
crumblePath()->setVisible(crumblePath()->length() > 1);
}
bool operator ==(const CrumbleBarInfo &first, const CrumbleBarInfo &second)

View File

@@ -10,6 +10,24 @@
namespace QmlDesigner {
class CrumbleBarInfo {
public:
CrumbleBarInfo() = default;
CrumbleBarInfo(Utils::FilePath f,
QString d,
ModelNode m) :
fileName(f),
displayName(d),
modelNode(m)
{}
Utils::FilePath fileName;
QString displayName;
ModelNode modelNode;
};
class CrumbleBar : public QObject
{
Q_OBJECT
@@ -24,21 +42,23 @@ public:
Utils::CrumblePath *crumblePath();
private:
QStringList path() const;
QList<CrumbleBarInfo> infos() const;
void onCrumblePathElementClicked(const QVariant &data);
signals:
void pathChanged();
private:
void updateVisibility();
bool showSaveDialog();
private:
bool m_isInternalCalled = false;
Utils::CrumblePath *m_crumblePath = nullptr;
};
class CrumbleBarInfo {
public:
Utils::FilePath fileName;
QString displayName;
ModelNode modelNode;
QList<CrumbleBarInfo> m_pathes;
};
bool operator ==(const CrumbleBarInfo &first, const CrumbleBarInfo &second);

View File

@@ -293,6 +293,11 @@ QIcon DesignerActionManager::contextIcon(int contextId) const
return m_designerIcons->icon(DesignerIcons::IconId(contextId), DesignerIcons::ContextMenuArea);
}
void DesignerActionManager::addAddActionCallback(ActionAddedInterface callback)
{
m_callBacks.append(callback);
}
class VisiblityModelNodeAction : public ModelNodeContextMenuAction
{
public:
@@ -2051,6 +2056,10 @@ void DesignerActionManager::createDefaultModelNodePreviewImageHandlers()
void DesignerActionManager::addDesignerAction(ActionInterface *newAction)
{
m_designerActions.append(QSharedPointer<ActionInterface>(newAction));
for (auto callback : m_callBacks) {
callback(newAction);
}
}
void DesignerActionManager::addCreatorCommand(Core::Command *command, const QByteArray &category, int priority,

View File

@@ -14,6 +14,8 @@
#include <QToolBar>
#include <QImage>
#include <functional>
QT_BEGIN_NAMESPACE
class QGraphicsItem;
class QGraphicsWidget;
@@ -27,6 +29,7 @@ class DesignerIcons;
using AddResourceOperation = std::function<AddFilesResult(const QStringList &, const QString &, bool)>;
using ModelNodePreviewImageOperation = std::function<QVariant(const ModelNode &)>;
using ActionAddedInterface = std::function<void(ActionInterface*)>;
struct AddResourceHandler
{
@@ -121,6 +124,8 @@ public:
QHash<QString, QStringList> handleExternalAssetsDrop(const QMimeData *data) const;
QIcon contextIcon(int contextId) const;
void addAddActionCallback(ActionAddedInterface callback);
private:
void addTransitionEffectAction(const TypeName &typeName);
void addCustomTransitionEffectAction();
@@ -133,6 +138,7 @@ private:
QList<ModelNodePreviewImageHandler> m_modelNodePreviewImageHandlers;
ExternalDependenciesInterface &m_externalDependencies;
QScopedPointer<DesignerIcons> m_designerIcons;
QList<ActionAddedInterface> m_callBacks;
};
} //QmlDesigner

View File

@@ -0,0 +1,126 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#include "toolbar.h"
#include "toolbarbackend.h"
#include <theme.h>
#include <coreplugin/icore.h>
#include <utils/filepath.h>
#include <utils/qtcassert.h>
#include <QStatusBar>
#include <QToolBar>
#include <QMainWindow>
#include <QQmlEngine>
namespace QmlDesigner {
QmlDesigner::ToolBar::ToolBar()
{
}
static Utils::FilePath propertyEditorResourcesPath()
{
#ifdef SHARE_QML_PATH
if (qEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE"))
return Utils::FilePath::fromString(QLatin1String(SHARE_QML_PATH) + "/propertyEditorQmlSources");
#endif
return Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources");
}
Utils::FilePath qmlSourcesStatusBarPath()
{
#ifdef SHARE_QML_PATH
if (qEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE"))
return Utils::FilePath::fromString(QLatin1String(SHARE_QML_PATH) + "/statusbar");
#endif
return Core::ICore::resourcePath("qmldesigner/statusbar");
}
Utils::FilePath qmlSourcesPath()
{
#ifdef SHARE_QML_PATH
if (qEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE"))
return Utils::FilePath::fromString(QLatin1String(SHARE_QML_PATH) + "/toolbar");
#endif
return Core::ICore::resourcePath("qmldesigner/toolbar");
}
void ToolBar::create()
{
if (!isVisible())
return;
ToolBarBackend::registerDeclarativeType();
auto window = Core::ICore::mainWindow();
//Core::ICore::statusBar()->hide();
auto toolBar = new QToolBar;
toolBar->setFloatable(false);
toolBar->setMovable(false);
auto quickWidget = new QQuickWidget;
quickWidget->setFixedHeight(48);
quickWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
quickWidget->setMinimumWidth(200);
quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
quickWidget->engine()->addImportPath(propertyEditorResourcesPath().toString() + "/imports");
Utils::FilePath qmlFilePath = qmlSourcesPath() / "Main.qml";
QTC_ASSERT(qmlFilePath.exists(), return );
Theme::setupTheme(quickWidget->engine());
quickWidget->setSource(QUrl::fromLocalFile(qmlFilePath.toFSPathString()));
toolBar->addWidget(quickWidget);
window->addToolBar(toolBar);
}
void ToolBar::createStatusBar()
{
if (!isVisible())
return;
ToolBarBackend::registerDeclarativeType();
auto quickWidget = new QQuickWidget;
quickWidget->setFixedHeight(24);
quickWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
quickWidget->setMinimumWidth(200);
quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
quickWidget->engine()->addImportPath(propertyEditorResourcesPath().toString() + "/imports");
Utils::FilePath qmlFilePath = qmlSourcesStatusBarPath() + QStringLiteral("/Main.qml");
QTC_ASSERT(qmlFilePath.exists(), return );
Theme::setupTheme(quickWidget->engine());
quickWidget->setSource(QUrl::fromLocalFile(qmlFilePath.toFSPathString()));
for (QWidget *w : Core::ICore::statusBar()->findChildren<QWidget *>(Qt::FindDirectChildrenOnly)) {
w->hide();
}
Core::ICore::statusBar()->addWidget(quickWidget);
}
bool ToolBar::isVisible()
{
QSettings *settings = Core::ICore::settings();
const QString qdsToolbarEntry = "QML/Designer/TopToolBar";
return settings->value(qdsToolbarEntry, false).toBool();
}
} // namespace QmlDesigner

View File

@@ -0,0 +1,21 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#pragma once
#include <QQuickWidget>
namespace QmlDesigner {
class ToolBar : public QQuickWidget
{
Q_OBJECT
public:
ToolBar();
static void create();
static void createStatusBar();
static bool isVisible();
};
} // namespace QmlDesigner

View File

@@ -0,0 +1,409 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#include "toolbarbackend.h"
#include <crumblebar.h>
#include <designeractionmanager.h>
#include <designmodewidget.h>
#include <viewmanager.h>
#include <qmldesignerplugin.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/designmode.h>
#include <coreplugin/editormanager/documentmodel.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
#include <coreplugin/modemanager.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
#include <qmlprojectmanager/qmlproject.h>
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
#include <QQmlEngine>
namespace QmlDesigner {
static Internal::DesignModeWidget *designModeWidget()
{
return QmlDesignerPlugin::instance()->mainWidget();
}
static DesignDocument *currentDesignDocument()
{
return QmlDesignerPlugin::instance()->documentManager().currentDesignDocument();
}
static CrumbleBar *crumbleBar()
{
return designModeWidget()->crumbleBar();
}
static Utils::FilePath getMainUiFile()
{
auto project = ProjectExplorer::SessionManager::startupProject();
if (!project)
return {};
if (!project->activeTarget())
return {};
auto qmlBuildSystem = qobject_cast<QmlProjectManager::QmlBuildSystem *>(
project->activeTarget()->buildSystem());
return qmlBuildSystem->mainUiFilePath();
}
static void openUiFile()
{
const Utils::FilePath mainUiFile = getMainUiFile();
if (mainUiFile.completeSuffix() == "ui.qml" && mainUiFile.exists())
Core::EditorManager::openEditor(mainUiFile, Utils::Id());
}
void ToolBarBackend::triggerModeChange()
{
QTimer::singleShot(0, []() { //Do not trigger mode change directly from QML
bool qmlFileOpen = false;
auto document = Core::EditorManager::currentDocument();
if (document)
qmlFileOpen = document->filePath().fileName().endsWith(".qml");
if (Core::ModeManager::currentModeId() == Core::Constants::MODE_DESIGN)
Core::ModeManager::activateMode(Core::Constants::MODE_WELCOME);
else if (qmlFileOpen)
Core::ModeManager::activateMode(Core::Constants::MODE_DESIGN);
else if (Core::ModeManager::currentModeId() == Core::Constants::MODE_WELCOME)
openUiFile();
else
Core::ModeManager::activateMode(Core::Constants::MODE_WELCOME);
});
}
void ToolBarBackend::runProject()
{
ProjectExplorer::ProjectExplorerPlugin::runStartupProject(
ProjectExplorer::Constants::NORMAL_RUN_MODE);
}
void ToolBarBackend::goForward()
{
QTC_ASSERT(designModeWidget(), return );
designModeWidget()->toolBarOnGoForwardClicked();
}
void ToolBarBackend::goBackward()
{
QTC_ASSERT(designModeWidget(), return );
designModeWidget()->toolBarOnGoBackClicked();
}
void ToolBarBackend::openFileByIndex(int i)
{
auto fileName = Core::DocumentModel::entries().at(i)->fileName();
Core::EditorManager::openEditor(fileName, Utils::Id(), Core::EditorManager::DoNotMakeVisible);
}
void ToolBarBackend::closeCurrentDocument()
{
Core::EditorManager::slotCloseCurrentEditorOrDocument();
}
void ToolBarBackend::shareApplicationOnline()
{
auto command = Core::ActionManager::command("QmlProject.ShareDesign");
if (command)
command->action()->trigger();
}
void ToolBarBackend::setCurrentWorkspace(const QString &workspace)
{
designModeWidget()->dockManager()->openWorkspace(workspace);
}
void ToolBarBackend::editGlobalAnnoation()
{
ModelNode node = currentDesignDocument()->rewriterView()->rootModelNode();
if (node.isValid()) {
designModeWidget()->globalAnnotationEditor().setModelNode(node);
designModeWidget()->globalAnnotationEditor().showWidget();
}
}
bool ToolBarBackend::canGoBack() const
{
QTC_ASSERT(designModeWidget(), return false);
return designModeWidget()->canGoBack();
}
bool ToolBarBackend::canGoForward() const
{
QTC_ASSERT(designModeWidget(), return false);
return designModeWidget()->canGoForward();
}
ToolBarBackend::ToolBarBackend(QObject *parent)
: QObject(parent)
{
ActionAddedInterface callback = [this](ActionInterface *interface) {
if (interface->menuId() == "PreviewZoom")
m_zoomAction = interface;
};
QmlDesignerPlugin::instance()->viewManager().designerActionManager().addAddActionCallback(callback);
connect(designModeWidget(),
&Internal::DesignModeWidget::navigationHistoryChanged,
this,
&ToolBarBackend::navigationHistoryChanged);
connect(Core::DocumentModel::model(),
&QAbstractItemModel::rowsInserted,
this,
&ToolBarBackend::updateDocumentModel);
connect(Core::DocumentModel::model(),
&QAbstractItemModel::rowsRemoved,
this,
&ToolBarBackend::updateDocumentModel);
connect(Core::DocumentModel::model(),
&QAbstractItemModel::dataChanged,
this,
&ToolBarBackend::updateDocumentModel);
connect(Core::DocumentModel::model(),
&QAbstractItemModel::modelReset,
this,
&ToolBarBackend::updateDocumentModel);
connect(Core::EditorManager::instance(),
&Core::EditorManager::currentEditorChanged,
this,
&ToolBarBackend::documentIndexChanged);
connect(designModeWidget(), &Internal::DesignModeWidget::initialized, this, [this]() {
const auto dockManager = designModeWidget()->dockManager();
connect(dockManager,
&ADS::DockManager::workspaceListChanged,
this,
&ToolBarBackend::setupWorkspaces);
connect(dockManager, &ADS::DockManager::workspaceLoaded, this, [this](const QString &) {
emit currentWorkspaceChanged();
});
setupWorkspaces();
});
auto editorManager = Core::EditorManager::instance();
connect(editorManager, &Core::EditorManager::documentClosed, this, [this]() {
if (isInDesignMode() && Core::DocumentModel::entryCount() == 0) {
QTimer::singleShot(0, []() { /* The mode change has to happen from event loop.
Otherwise we and up in an invalid state */
Core::ModeManager::activateMode(Core::Constants::MODE_WELCOME);
});
}
});
connect(Core::ICore::instance(), &Core::ICore::coreAboutToOpen, this, [this] {
connect(Core::DesignMode::instance(), &Core::DesignMode::enabledStateChanged, this, [this] {
emit isDesignModeEnabledChanged();
});
});
connect(Core::ModeManager::instance(), &Core::ModeManager::currentModeChanged, this, [this]() {
emit isInDesignModeChanged();
});
}
void ToolBarBackend::registerDeclarativeType()
{
qmlRegisterType<ToolBarBackend>("ToolBar", 1, 0, "ToolBarBackend");
qmlRegisterType<ActionSubscriber>("ToolBar", 1, 0, "ActionSubscriber");
qmlRegisterType<CrumbleBarModel>("ToolBar", 1, 0, "CrumbleBarModel");
}
QStringList ToolBarBackend::documentModel() const
{
return m_openDocuments;
}
void ToolBarBackend::updateDocumentModel()
{
m_openDocuments.clear();
for (auto &entry : Core::DocumentModel::entries())
m_openDocuments.append(entry->displayName());
emit openDocumentsChanged();
}
int ToolBarBackend::documentIndex() const
{
if (Core::EditorManager::currentDocument())
return Core::DocumentModel::indexOfDocument(Core::EditorManager::currentDocument()).value();
return -1;
}
QString ToolBarBackend::currentWorkspace() const
{
return designModeWidget()->dockManager()->activeWorkspace();
}
QStringList ToolBarBackend::workspaces() const
{
return m_workspaces;
}
bool ToolBarBackend::isInDesignMode() const
{
if (!Core::ModeManager::instance())
return false;
return Core::ModeManager::currentModeId() == Core::Constants::MODE_DESIGN;
}
bool ToolBarBackend::isDesignModeEnabled() const
{
if (Core::DesignMode::instance())
return Core::DesignMode::instance()->isEnabled() || getMainUiFile().exists();
return false;
}
void ToolBarBackend::setupWorkspaces()
{
m_workspaces.clear();
m_workspaces = designModeWidget()->dockManager()->workspaces();
Utils::sort(m_workspaces);
emit workspacesChanged();
}
ActionSubscriber::ActionSubscriber(QObject *parent)
: QObject(parent)
{
ActionAddedInterface callback = [this](ActionInterface *interface) {
if (interface->menuId() == m_actionId.toLatin1()) {
m_interface = interface;
setupNotifier();
}
};
QmlDesignerPlugin::instance()->viewManager().designerActionManager().addAddActionCallback(callback);
}
void ActionSubscriber::trigger()
{
if (m_interface)
m_interface->action()->trigger();
}
bool ActionSubscriber::available() const
{
if (m_interface)
return m_interface->action()->isEnabled();
return false;
}
bool ActionSubscriber::checked() const
{
if (m_interface)
return m_interface->action()->isChecked();
return false;
}
QString ActionSubscriber::actionId() const
{
return m_actionId;
}
void ActionSubscriber::setActionId(const QString &id)
{
if (id == m_actionId)
return;
m_actionId = id;
emit actionIdChanged();
emit tooltipChanged();
}
QString ActionSubscriber::tooltip() const
{
if (m_interface)
return m_interface->action()->text();
return {};
}
void ActionSubscriber::setupNotifier()
{
if (!m_interface)
return;
connect(m_interface->action(), &QAction::enabledChanged, this, &ActionSubscriber::availableChanged);
emit tooltipChanged();
}
CrumbleBarModel::CrumbleBarModel(QObject *)
{
connect(crumbleBar(), &CrumbleBar::pathChanged, this, &CrumbleBarModel::handleCrumblePathChanged);
}
int CrumbleBarModel::rowCount(const QModelIndex &) const
{
return crumbleBar()->path().count();
}
QHash<int, QByteArray> CrumbleBarModel::roleNames() const
{
static QHash<int, QByteArray> roleNames{{Qt::UserRole + 1, "fileName"},
{Qt::UserRole + 2, "fileAddress"}};
return roleNames;
}
QVariant CrumbleBarModel::data(const QModelIndex &index, int role) const
{
if (index.isValid() && index.row() < rowCount()) {
auto info = crumbleBar()->infos().at(index.row());
if (role == Qt::UserRole + 1) {
return info.displayName;
} else if (role == Qt::UserRole + 2) {
return info.fileName.displayName();
} else {
qWarning() << Q_FUNC_INFO << "invalid role";
}
} else {
qWarning() << Q_FUNC_INFO << "invalid index";
}
return QVariant();
}
void CrumbleBarModel::handleCrumblePathChanged()
{
beginResetModel();
endResetModel();
}
void CrumbleBarModel::onCrumblePathElementClicked(int i)
{
if (i < rowCount()) {
auto info = crumbleBar()->infos().at(i);
crumbleBar()->onCrumblePathElementClicked(QVariant::fromValue(info));
}
}
} // namespace QmlDesigner

View File

@@ -0,0 +1,129 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#pragma once
#include <QAbstractListModel>
#include <QObject>
namespace QmlDesigner {
class ActionInterface;
class CrumbleBarModel : public QAbstractListModel
{
Q_OBJECT
public:
explicit CrumbleBarModel(QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QHash<int, QByteArray> roleNames() const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
void handleCrumblePathChanged();
Q_INVOKABLE void onCrumblePathElementClicked(int i);
private:
};
class ActionSubscriber : public QObject
{
Q_OBJECT
public:
ActionSubscriber(QObject *parent = nullptr);
Q_PROPERTY(QString actionId READ actionId WRITE setActionId NOTIFY actionIdChanged)
Q_PROPERTY(bool available READ available NOTIFY availableChanged)
Q_PROPERTY(bool checked READ checked NOTIFY checkedChanged)
Q_PROPERTY(QString tooltip READ tooltip NOTIFY tooltipChanged)
Q_INVOKABLE void trigger();
bool available() const;
bool checked() const;
QString actionId() const;
void setActionId(const QString &id);
QString tooltip() const;
signals:
void actionIdChanged();
void availableChanged();
void checkedChanged();
void tooltipChanged();
private:
void setupNotifier();
ActionInterface *m_interface = nullptr;
QString m_actionId;
};
class ToolBarBackend : public QObject
{
Q_OBJECT
Q_PROPERTY(bool canGoBack READ canGoBack NOTIFY navigationHistoryChanged)
Q_PROPERTY(bool canGoForward READ canGoForward NOTIFY navigationHistoryChanged)
Q_PROPERTY(QStringList documentModel READ documentModel NOTIFY openDocumentsChanged)
Q_PROPERTY(int documentIndex READ documentIndex NOTIFY documentIndexChanged)
Q_PROPERTY(QString currentWorkspace READ currentWorkspace NOTIFY currentWorkspaceChanged)
Q_PROPERTY(QStringList workspaces READ workspaces NOTIFY workspacesChanged)
Q_PROPERTY(bool isInDesignMode READ isInDesignMode NOTIFY isInDesignModeChanged)
Q_PROPERTY(bool isDesignModeEnabled READ isDesignModeEnabled NOTIFY isDesignModeEnabledChanged)
public:
ToolBarBackend(QObject *parent = nullptr);
static void registerDeclarativeType();
Q_INVOKABLE void triggerModeChange();
Q_INVOKABLE void runProject();
Q_INVOKABLE void goForward();
Q_INVOKABLE void goBackward();
Q_INVOKABLE void openFileByIndex(int i);
Q_INVOKABLE void closeCurrentDocument();
Q_INVOKABLE void shareApplicationOnline();
Q_INVOKABLE void setCurrentWorkspace(const QString &workspace);
Q_INVOKABLE void editGlobalAnnoation();
bool canGoBack() const;
bool canGoForward() const;
QStringList documentModel() const;
void updateDocumentModel();
int documentIndex() const;
QString currentWorkspace() const;
QStringList workspaces() const;
bool isInDesignMode() const;
bool isDesignModeEnabled() const;
signals:
void navigationHistoryChanged();
void openDocumentsChanged();
void documentIndexChanged();
void currentWorkspaceChanged();
void workspacesChanged();
void isInDesignModeChanged();
void isDesignModeEnabledChanged();
private:
void setupWorkspaces();
ActionInterface *m_zoomAction;
ActionInterface *editAnnotation;
ActionInterface *goIntoComponent;
ActionInterface *moveToComponent;
QStringList m_openDocuments;
QStringList m_workspaces;
};
} // namespace QmlDesigner

View File

@@ -14,6 +14,7 @@
#include <nodeinstanceview.h>
#include <itemlibrarywidget.h>
#include <theme.h>
#include <toolbar.h>
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
@@ -341,27 +342,69 @@ void DesignModeWidget::setup()
mviews->addAction(command);
// Create toolbars
auto toolBar = new QToolBar();
if (!ToolBar::isVisible()) {
auto toolBar = new QToolBar();
toolBar->addAction(viewManager().componentViewAction());
toolBar->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
DesignerActionToolBar *designerToolBar = QmlDesignerPlugin::instance()->viewManager().designerActionManager().createToolBar(m_toolBar);
toolBar->addAction(viewManager().componentViewAction());
toolBar->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
DesignerActionToolBar *designerToolBar = QmlDesignerPlugin::instance()->viewManager().designerActionManager().createToolBar(m_toolBar);
designerToolBar->layout()->addWidget(toolBar);
designerToolBar->layout()->addWidget(toolBar);
m_toolBar->addCenterToolBar(designerToolBar);
m_toolBar->setMinimumWidth(320);
m_toolBar->setToolbarCreationFlags(Core::EditorToolBar::FlagsStandalone);
m_toolBar->setNavigationVisible(true);
m_toolBar->addCenterToolBar(designerToolBar);
m_toolBar->setMinimumWidth(320);
m_toolBar->setToolbarCreationFlags(Core::EditorToolBar::FlagsStandalone);
m_toolBar->setNavigationVisible(true);
connect(m_toolBar, &Core::EditorToolBar::goForwardClicked, this, &DesignModeWidget::toolBarOnGoForwardClicked);
connect(m_toolBar, &Core::EditorToolBar::goBackClicked, this, &DesignModeWidget::toolBarOnGoBackClicked);
connect(m_toolBar, &Core::EditorToolBar::goForwardClicked, this, &DesignModeWidget::toolBarOnGoForwardClicked);
connect(m_toolBar, &Core::EditorToolBar::goBackClicked, this, &DesignModeWidget::toolBarOnGoBackClicked);
QToolBar* toolBarWrapper = new QToolBar();
toolBarWrapper->addWidget(m_toolBar);
toolBarWrapper->addWidget(createCrumbleBarFrame());
toolBarWrapper->setMovable(false);
addToolBar(Qt::TopToolBarArea, toolBarWrapper);
QToolBar* toolBarWrapper = new QToolBar();
toolBarWrapper->addWidget(m_toolBar);
toolBarWrapper->addWidget(createCrumbleBarFrame());
toolBarWrapper->setMovable(false);
addToolBar(Qt::TopToolBarArea, toolBarWrapper);
addSpacerToToolBar(toolBar);
auto workspaceComboBox = new QComboBox();
workspaceComboBox->setMinimumWidth(120);
workspaceComboBox->setToolTip(tr("Switch the active workspace."));
auto sortedWorkspaces = m_dockManager->workspaces();
Utils::sort(sortedWorkspaces);
workspaceComboBox->addItems(sortedWorkspaces);
workspaceComboBox->setCurrentText(m_dockManager->activeWorkspace());
toolBar->addWidget(workspaceComboBox);
connect(m_dockManager, &ADS::DockManager::workspaceListChanged,
workspaceComboBox, [this, workspaceComboBox]() {
workspaceComboBox->clear();
auto sortedWorkspaces = m_dockManager->workspaces();
Utils::sort(sortedWorkspaces);
workspaceComboBox->addItems(sortedWorkspaces);
workspaceComboBox->setCurrentText(m_dockManager->activeWorkspace());
});
connect(m_dockManager, &ADS::DockManager::workspaceLoaded, workspaceComboBox, &QComboBox::setCurrentText);
connect(workspaceComboBox, &QComboBox::activated,
m_dockManager, [this, workspaceComboBox]([[maybe_unused]] int index) {
m_dockManager->openWorkspace(workspaceComboBox->currentText());
});
const QIcon gaIcon = Utils::StyleHelper::getIconFromIconFont(
fontName, Theme::getIconUnicode(Theme::Icon::annotationBubble),
36, 36, Theme::getColor(Theme::IconsBaseColor));
toolBar->addAction(gaIcon, tr("Edit global annotation for current file."), [&](){
ModelNode node = currentDesignDocument()->rewriterView()->rootModelNode();
if (node.isValid()) {
m_globalAnnotationEditor.setModelNode(node);
m_globalAnnotationEditor.showWidget();
}
});
}
if (currentDesignDocument())
setupNavigatorHistory(currentDesignDocument()->textEditor());
@@ -392,43 +435,6 @@ void DesignModeWidget::setup()
}
});
addSpacerToToolBar(toolBar);
auto workspaceComboBox = new QComboBox();
workspaceComboBox->setMinimumWidth(120);
workspaceComboBox->setToolTip(tr("Switch the active workspace."));
auto sortedWorkspaces = m_dockManager->workspaces();
Utils::sort(sortedWorkspaces);
workspaceComboBox->addItems(sortedWorkspaces);
workspaceComboBox->setCurrentText(m_dockManager->activeWorkspace());
toolBar->addWidget(workspaceComboBox);
connect(m_dockManager, &ADS::DockManager::workspaceListChanged,
workspaceComboBox, [this, workspaceComboBox]() {
workspaceComboBox->clear();
auto sortedWorkspaces = m_dockManager->workspaces();
Utils::sort(sortedWorkspaces);
workspaceComboBox->addItems(sortedWorkspaces);
workspaceComboBox->setCurrentText(m_dockManager->activeWorkspace());
});
connect(m_dockManager, &ADS::DockManager::workspaceLoaded, workspaceComboBox, &QComboBox::setCurrentText);
connect(workspaceComboBox, &QComboBox::activated,
m_dockManager, [this, workspaceComboBox]([[maybe_unused]] int index) {
m_dockManager->openWorkspace(workspaceComboBox->currentText());
});
const QIcon gaIcon = Utils::StyleHelper::getIconFromIconFont(
fontName, Theme::getIconUnicode(Theme::Icon::annotationBubble),
36, 36, Theme::getColor(Theme::IconsBaseColor));
toolBar->addAction(gaIcon, tr("Edit global annotation for current file."), [&](){
ModelNode node = currentDesignDocument()->rewriterView()->rootModelNode();
if (node.isValid()) {
m_globalAnnotationEditor.setModelNode(node);
m_globalAnnotationEditor.showWidget();
}
});
viewManager().enableWidgets();
@@ -500,6 +506,26 @@ void DesignModeWidget::toolBarOnGoForwardClicked()
}
}
bool DesignModeWidget::canGoForward()
{
return m_canGoForward;
}
bool DesignModeWidget::canGoBack()
{
return m_canGoBack;
}
ADS::DockManager *DesignModeWidget::dockManager() const
{
return m_dockManager;
}
GlobalAnnotationEditor &DesignModeWidget::globalAnnotationEditor()
{
return m_globalAnnotationEditor;
}
DesignDocument *DesignModeWidget::currentDesignDocument() const
{
return QmlDesignerPlugin::instance()->documentManager().currentDesignDocument();
@@ -515,11 +541,14 @@ void DesignModeWidget::setupNavigatorHistory(Core::IEditor *editor)
if (!m_keepNavigatorHistory)
addNavigatorHistoryEntry(editor->document()->filePath());
const bool canGoBack = m_navigatorHistoryCounter > 0;
const bool canGoForward = m_navigatorHistoryCounter < (m_navigatorHistory.size() - 1);
m_toolBar->setCanGoBack(canGoBack);
m_toolBar->setCanGoForward(canGoForward);
m_toolBar->setCurrentEditor(editor);
m_canGoBack = m_navigatorHistoryCounter > 0;
m_canGoForward = m_navigatorHistoryCounter < (m_navigatorHistory.size() - 1);
m_toolBar->setCanGoBack(m_canGoBack);
m_toolBar->setCanGoForward(m_canGoForward);
if (!ToolBar::isVisible())
m_toolBar->setCurrentEditor(editor);
emit navigationHistoryChanged();
}
void DesignModeWidget::addNavigatorHistoryEntry(const Utils::FilePath &fileName)
@@ -576,6 +605,8 @@ void DesignModeWidget::initialize()
}
m_initStatus = Initialized;
emit initialized();
}
} // namespace Internal

View File

@@ -64,12 +64,23 @@ public:
static QWidget *createProjectExplorerWidget(QWidget *parent);
private:
enum InitializeStatus { NotInitialized, Initializing, Initialized };
void toolBarOnGoBackClicked();
void toolBarOnGoForwardClicked();
bool canGoForward();
bool canGoBack();
ADS::DockManager *dockManager() const;
GlobalAnnotationEditor &globalAnnotationEditor();
signals:
void navigationHistoryChanged();
void initialized();
private:
enum InitializeStatus { NotInitialized, Initializing, Initialized };
void setup();
bool isInNodeDefinition(int nodeOffset, int nodeLength, int cursorPos) const;
QmlDesigner::ModelNode nodeForPosition(int cursorPos) const;
@@ -96,6 +107,9 @@ private:
ADS::DockManager *m_dockManager = nullptr;
ADS::DockWidget *m_outputPaneDockWidget = nullptr;
GlobalAnnotationEditor m_globalAnnotationEditor;
bool m_canGoForward = false;
bool m_canGoBack = false;
};
} // namespace Internal

View File

@@ -15,6 +15,7 @@
#include "qmldesignerprojectmanager.h"
#include "quick2propertyeditorview.h"
#include "settingspage.h"
#include <toolbar.h>
#include <colortool/colortool.h>
#include <connectionview.h>
@@ -92,7 +93,7 @@ public:
return {"QmlDesigner.Wizards.Enterprise"};
}
QSet<Utils::Id> availablePlatforms() const override { return {}; }
QString displayNameForPlatform(Utils::Id id) const override { return {}; }
QString displayNameForPlatform(Utils::Id) const override { return {}; }
};
QString normalizeIdentifier(const QString &string)
@@ -270,6 +271,11 @@ bool QmlDesignerPlugin::initialize(const QStringList & /*arguments*/, QString *e
Core::AsynchronousMessageBox::warning(composedTitle, description.toString());
});
if (QmlProjectManager::QmlProject::isQtDesignStudio()) {
ToolBar::create();
ToolBar::createStatusBar();
}
return true;
}

View File

@@ -113,7 +113,7 @@ QByteArray ZoomPreviewAction::category() const
QByteArray ZoomPreviewAction::menuId() const
{
return QByteArray();
return "PreviewZoom";
}
int ZoomPreviewAction::priority() const