From 5667fdc46a8610c7dd723db2be746931ef2e4a08 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Mon, 6 Feb 2017 10:46:52 +0100 Subject: [PATCH] QmlDesigner: introduce SwitchSplitTabWidget as center widget Change-Id: I2ab91c3a6c68bb64c6875c37fa6fe2b4abc8b82d Reviewed-by: Thomas Hartmann --- .../components/resources/centerwidget.css | 39 ++-- .../designercore/model/viewmanager.cpp | 6 +- src/plugins/qmldesigner/designmodewidget.cpp | 44 ++-- src/plugins/qmldesigner/designmodewidget.h | 9 +- src/plugins/qmldesigner/qmldesignerplugin.pri | 2 + src/plugins/qmldesigner/qmldesignerplugin.qbs | 2 + .../qmldesigner/switchsplittabwidget.cpp | 205 ++++++++++++++++++ .../qmldesigner/switchsplittabwidget.h | 68 ++++++ 8 files changed, 325 insertions(+), 50 deletions(-) create mode 100644 src/plugins/qmldesigner/switchsplittabwidget.cpp create mode 100644 src/plugins/qmldesigner/switchsplittabwidget.h diff --git a/src/plugins/qmldesigner/components/resources/centerwidget.css b/src/plugins/qmldesigner/components/resources/centerwidget.css index 02b51b7d52f..1f91f8993b2 100644 --- a/src/plugins/qmldesigner/components/resources/centerwidget.css +++ b/src/plugins/qmldesigner/components/resources/centerwidget.css @@ -1,28 +1,20 @@ -QWidget#backgroundWidget { - background-color: creatorTheme.QmlDesignerTabDark +QWidget#centralTabBar { + background-color: creatorTheme.QmlDesignerBackgroundColorDarkAlternate; } - -QTabWidget#centralTabWidget::pane { - border: 0px; +QWidget#tabBarBackground { background-color: creatorTheme.QmlDesignerBackgroundColorDarkAlternate; } -QTabWidget#centralTabWidget::tab-bar { alignment: right; } - -QTabBar::tab:selected { - border: none; - background-color: creatorTheme.QmlDesignerTabLight; - color: creatorTheme.QmlDesignerTabDark; +QTabBar#centralTabBar::tab:first { + width: 0px; + height: 0px; } - QTabBar#centralTabBar::tab { - width: 16px; - height: 80px; - - border-image: none; background-color: creatorTheme.QmlDesignerTabDark; color: creatorTheme.QmlDesignerTabLight; + width: 11px; + height: 100px; margin-top: 0x; margin-bottom: 0px; margin-left: 0px; @@ -35,3 +27,18 @@ QTabBar#centralTabBar::tab:selected { background-color: creatorTheme.QmlDesignerTabLight; color: creatorTheme.QmlDesignerTabDark; } + +QToolButton { + background-color: creatorTheme.QmlDesignerBackgroundColorDarkAlternate; + width: 08px; + height: 16px; + border: none; +} + +QSplitter::handle:horizontal { + width: 0px; +} + +QSplitter::handle:vertical { + height: 0px; +} diff --git a/src/plugins/qmldesigner/designercore/model/viewmanager.cpp b/src/plugins/qmldesigner/designercore/model/viewmanager.cpp index 4b6ca2ef090..a1bdc14a474 100644 --- a/src/plugins/qmldesigner/designercore/model/viewmanager.cpp +++ b/src/plugins/qmldesigner/designercore/model/viewmanager.cpp @@ -79,10 +79,8 @@ ViewManager::ViewManager() { d->formEditorView.setGotoErrorCallback([this](int line, int column) { d->textEditorView.gotoCursorPosition(line, column); - if (Internal::DesignModeWidget *designModeWidget = QmlDesignerPlugin::instance()->mainWidget()) { - if (QTabWidget *centralTabWidget = designModeWidget->centralTabWidget()) - centralTabWidget->setCurrentIndex(1); - } + if (Internal::DesignModeWidget *designModeWidget = QmlDesignerPlugin::instance()->mainWidget()) + designModeWidget->showInternalTextEditor(); }); } diff --git a/src/plugins/qmldesigner/designmodewidget.cpp b/src/plugins/qmldesigner/designmodewidget.cpp index d78c45735f1..9da885fe03c 100644 --- a/src/plugins/qmldesigner/designmodewidget.cpp +++ b/src/plugins/qmldesigner/designmodewidget.cpp @@ -24,10 +24,10 @@ ****************************************************************************/ #include "designmodewidget.h" +#include "switchsplittabwidget.h" #include -#include #include "qmldesignerplugin.h" #include "crumblebar.h" #include "documentwarningwidget.h" @@ -37,6 +37,7 @@ #include #include +#include #include #include #include @@ -229,7 +230,10 @@ void DesignModeWidget::disableWidgets() void DesignModeWidget::switchTextOrForm() { - m_centralTabWidget->setCurrentIndex(m_centralTabWidget->currentIndex() == 0 ? 1 : 0); + if (m_centralTabWidget->currentWidget() == viewManager().widget("TextEditor")) + m_centralTabWidget->switchTo(viewManager().widget("FormEditor")); + else + m_centralTabWidget->switchTo(viewManager().widget("TextEditor")); } void DesignModeWidget::showWarningMessageBox(const QList &warnings) @@ -471,34 +475,24 @@ static QWidget *createbottomSideBarWidget(const QList &widgetInfos) static Core::MiniSplitter *createCentralSplitter(const QList &widgetInfos) { - QList centralWidgetInfos; - foreach (const WidgetInfo &widgetInfo, widgetInfos) { - if (widgetInfo.placementHint == widgetInfo.CentralPane) - centralWidgetInfos.append(widgetInfo); - } - // editor and output panes Core::MiniSplitter *outputPlaceholderSplitter = new Core::MiniSplitter; outputPlaceholderSplitter->setStretchFactor(0, 10); outputPlaceholderSplitter->setStretchFactor(1, 0); outputPlaceholderSplitter->setOrientation(Qt::Vertical); - QTabWidget* tabWidget = createWidgetsInTabWidget(centralWidgetInfos); - tabWidget->setObjectName("centralTabWidget"); - tabWidget->setTabPosition(QTabWidget::East); - tabWidget->tabBar()->setObjectName("centralTabBar"); - tabWidget->setTabBarAutoHide(true); + SwitchSplitTabWidget *switchSplitTabWidget = new SwitchSplitTabWidget(); - QWidget *backgroundWidget = new QWidget(); - backgroundWidget->setObjectName("backgroundWidget"); - backgroundWidget->setLayout(new QVBoxLayout()); - backgroundWidget->layout()->setMargin(0); - backgroundWidget->layout()->addWidget(tabWidget); + QString sheet = QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/centerwidget.css")); + switchSplitTabWidget->setStyleSheet(Theming::replaceCssColors(sheet)); - QByteArray sheet = Utils::FileReader::fetchQrc(":/qmldesigner/centerwidget.css"); - backgroundWidget->setStyleSheet(Theming::replaceCssColors(QString::fromUtf8(sheet))); - outputPlaceholderSplitter->addWidget(backgroundWidget); + foreach (const WidgetInfo &widgetInfo, widgetInfos) { + if (widgetInfo.placementHint == widgetInfo.CentralPane) + switchSplitTabWidget->addTab(widgetInfo.widget, widgetInfo.tabName); + } + + outputPlaceholderSplitter->addWidget(switchSplitTabWidget); QWidget *bottomSideBar = createbottomSideBarWidget(widgetInfos); bottomSideBar->setObjectName("bottomSideBar"); @@ -522,8 +516,10 @@ QWidget *DesignModeWidget::createCenterWidget() horizontalLayout->addWidget(createCrumbleBarFrame()); Core::MiniSplitter *centralSplitter = createCentralSplitter(viewManager().widgetInfos()); - m_centralTabWidget = centralSplitter->findChild("centralTabWidget"); + m_centralTabWidget = centralSplitter->findChild(); Q_ASSERT(m_centralTabWidget); + m_centralTabWidget->switchTo(viewManager().widget("FormEditor")); + m_bottomSideBar = centralSplitter->findChild("bottomSideBar"); Q_ASSERT(m_bottomSideBar); horizontalLayout->addWidget(centralSplitter); @@ -574,9 +570,9 @@ CrumbleBar *DesignModeWidget::crumbleBar() const return m_crumbleBar; } -QTabWidget *DesignModeWidget::centralTabWidget() const +void DesignModeWidget::showInternalTextEditor() { - return m_centralTabWidget; + m_centralTabWidget->switchTo(viewManager().widget("TextEditor")); } QString DesignModeWidget::contextHelpId() const diff --git a/src/plugins/qmldesigner/designmodewidget.h b/src/plugins/qmldesigner/designmodewidget.h index 6e832b15e86..e9828204908 100644 --- a/src/plugins/qmldesigner/designmodewidget.h +++ b/src/plugins/qmldesigner/designmodewidget.h @@ -34,10 +34,6 @@ #include #include -QT_BEGIN_NAMESPACE -class QTabWidget; -QT_END_NAMESPACE - namespace Core { class SideBar; class SideBarItem; @@ -50,6 +46,7 @@ namespace QmlDesigner { class ItemLibraryWidget; class CrumbleBar; class DocumentWarningWidget; +class SwitchSplitTabWidget; namespace Internal { @@ -85,7 +82,7 @@ public: bool gotoCodeWasClicked(); CrumbleBar* crumbleBar() const; - QTabWidget* centralTabWidget() const; + void showInternalTextEditor(); public slots: void restoreDefaultView(); @@ -112,7 +109,7 @@ private: // functions private: // variables QSplitter *m_mainSplitter = nullptr; QPointer m_warningWidget; - QTabWidget* m_centralTabWidget = nullptr; + SwitchSplitTabWidget* m_centralTabWidget = nullptr; QScopedPointer m_leftSideBar; QScopedPointer m_rightSideBar; diff --git a/src/plugins/qmldesigner/qmldesignerplugin.pri b/src/plugins/qmldesigner/qmldesignerplugin.pri index b900f8d883e..503b47a83b1 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.pri +++ b/src/plugins/qmldesigner/qmldesignerplugin.pri @@ -2,6 +2,7 @@ HEADERS += $$PWD/qmldesignerconstants.h \ $$PWD/shortcutmanager.h \ $$PWD/qmldesignerplugin.h \ $$PWD/designmodewidget.h \ + $$PWD/switchsplittabwidget.h \ $$PWD/designersettings.h \ $$PWD/settingspage.h \ $$PWD/designmodecontext.h \ @@ -13,6 +14,7 @@ HEADERS += $$PWD/qmldesignerconstants.h \ SOURCES += $$PWD/qmldesignerplugin.cpp \ $$PWD/shortcutmanager.cpp \ $$PWD/designmodewidget.cpp \ + $$PWD/switchsplittabwidget.cpp \ $$PWD/designersettings.cpp \ $$PWD/settingspage.cpp \ $$PWD/designmodecontext.cpp \ diff --git a/src/plugins/qmldesigner/qmldesignerplugin.qbs b/src/plugins/qmldesigner/qmldesignerplugin.qbs index 1934ce21edc..c73a52b0339 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.qbs +++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs @@ -641,6 +641,8 @@ Project { "designmodecontext.h", "designmodewidget.cpp", "designmodewidget.h", + "switchsplittabwidget.cpp", + "switchsplittabwidget.h", "documentmanager.cpp", "documentmanager.h", "documentwarningwidget.cpp", diff --git a/src/plugins/qmldesigner/switchsplittabwidget.cpp b/src/plugins/qmldesigner/switchsplittabwidget.cpp new file mode 100644 index 00000000000..4cf6dd9c10c --- /dev/null +++ b/src/plugins/qmldesigner/switchsplittabwidget.cpp @@ -0,0 +1,205 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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. +** +****************************************************************************/ + +#include "switchsplittabwidget.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace QmlDesigner { +SwitchSplitTabWidget::SwitchSplitTabWidget(QWidget *parent) + : QWidget(parent) + , m_splitter(new QSplitter) + , m_tabBar(new QTabBar) + , m_tabBarBackground(new QWidget) +{ + // setting object names for css + setObjectName("backgroundWidget"); + m_splitter->setObjectName("centralTabWidget"); + m_splitter->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + m_tabBar->setObjectName("centralTabBar"); + m_tabBar->setShape(QTabBar::RoundedEast); + m_tabBar->setDocumentMode(false); + // add a faketab to have the possibility to unselect all tabs + m_tabBar->addTab(QString()); + selectFakeTab(); + + m_tabBarBackground->setObjectName("tabBarBackground"); + + connect(m_tabBar, &QTabBar::tabBarClicked, [this] (int index) { + if (index != -1) + updateSplitterSizes(index - fakeTab); + }); + + setLayout(new QHBoxLayout); + layout()->setContentsMargins(0, 0, 0, 0); + layout()->setSpacing(0); + layout()->addWidget(m_splitter); + + m_tabBarBackground->setLayout(new QVBoxLayout); + m_tabBarBackground->layout()->setContentsMargins(0, 0, 0, 0); + m_tabBarBackground->layout()->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding)); + m_tabBarBackground->layout()->addWidget(m_tabBar); + + QToolButton *horizontalButton = new QToolButton; + horizontalButton->setIcon(Utils::Icons::SPLIT_HORIZONTAL.icon()); + connect(horizontalButton, &QToolButton::clicked, [this] () { + m_splitter->setOrientation(Qt::Vertical); + updateSplitterSizes(); + selectFakeTab(); + }); + QToolButton *verticalButton = new QToolButton; + verticalButton->setIcon(Utils::Icons::SPLIT_VERTICAL.icon()); + connect(verticalButton, &QToolButton::clicked, [this] () { + m_splitter->setOrientation(Qt::Horizontal); + updateSplitterSizes(); + selectFakeTab(); + }); + + m_tabBarBackground->layout()->addWidget(horizontalButton); + m_tabBarBackground->layout()->addWidget(verticalButton); + layout()->addWidget(m_tabBarBackground); + updateSplitButtons(); +} + +int SwitchSplitTabWidget::count() const +{ + return m_splitter->count(); +} + +QWidget *SwitchSplitTabWidget::currentWidget() const +{ + QList sizes = m_splitter->sizes(); + for (int i = 0; i < count(); ++i) { + if (sizes.at(i) > 0 && m_splitter->widget(i)->hasFocus()) + return m_splitter->widget(i); + } + return nullptr; +} + +void SwitchSplitTabWidget::updateSplitterSizes(int index) +{ + if (isHidden()) { + // we can not get the sizes if the splitter is hidden + m_splittSizesAreDirty = true; + return; + } + QVector splitterSizes = m_splitter->sizes().toVector(); + int splitterFullSize = 0; + for (int size : splitterSizes) + splitterFullSize += size; + if (index > -1) { + // collapse all but not the one at index + splitterSizes.fill(0); + splitterSizes.replace(index, splitterFullSize); + } else { + // distribute full size + splitterSizes.fill(splitterFullSize / splitterSizes.count()); + } + m_splitter->setSizes(splitterSizes.toList()); + m_splittSizesAreDirty = false; +} + +int SwitchSplitTabWidget::addTab(QWidget *w, const QString &label) +{ + m_splitter->addWidget(w); + const int newIndex = m_tabBar->addTab(label); + if (mode() == TabMode) { + m_tabBar->setCurrentIndex(newIndex); + updateSplitterSizes(newIndex - fakeTab); + } + if (mode() == SplitMode) + updateSplitterSizes(); + updateSplitButtons(); + return newIndex; +} + +QWidget *SwitchSplitTabWidget::takeTabWidget(const int index) +{ + if (index == -1 || index > count() - 1) + return nullptr; + QWidget *widget = m_splitter->widget(index); + widget->setParent(nullptr); + m_tabBar->removeTab(index + fakeTab); + // TODO: set which mode and tab is the current one + updateSplitButtons(); + return widget; +} + +void SwitchSplitTabWidget::switchTo(QWidget *widget) +{ + if (widget == nullptr || currentWidget() == widget) + return; + const int widgetIndex = m_splitter->indexOf(widget); + Q_ASSERT(widgetIndex != -1); + if (mode() == TabMode) { + updateSplitterSizes(widgetIndex); + m_tabBar->setCurrentIndex(widgetIndex + fakeTab); + } + widget->setFocus(); +} + +bool SwitchSplitTabWidget::event(QEvent *event) +{ + if (event->type() == QEvent::Show && m_splittSizesAreDirty) { + bool returnValue = QWidget::event(event); + updateSplitterSizes(m_tabBar->currentIndex() - fakeTab); + return returnValue; + } + + return QWidget::event(event); +} + +void SwitchSplitTabWidget::updateSplitButtons() +{ + const bool isTabBarNecessary = count() > 1; + m_tabBarBackground->setVisible(isTabBarNecessary); +} + +void SwitchSplitTabWidget::selectFakeTab() +{ + m_tabBar->setCurrentIndex(0); +} + +SwitchSplitTabWidget::Mode SwitchSplitTabWidget::mode() +{ + const bool isTabBarNecessary = count() > 1; + const int fakeTabPosition = 0; + const int hasSelectedTab = m_tabBar->currentIndex() > fakeTabPosition; + if (isTabBarNecessary && !hasSelectedTab) + return SplitMode; + return TabMode; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/switchsplittabwidget.h b/src/plugins/qmldesigner/switchsplittabwidget.h new file mode 100644 index 00000000000..10c048da5dd --- /dev/null +++ b/src/plugins/qmldesigner/switchsplittabwidget.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 once + +#include + +QT_BEGIN_NAMESPACE +class QTabBar; +class QSplitter; +class QPushButton; +QT_END_NAMESPACE + +namespace QmlDesigner { +class SwitchSplitTabWidget : public QWidget +{ + Q_OBJECT + enum Mode { + SplitMode, + TabMode + }; +public: + explicit SwitchSplitTabWidget(QWidget *parent = 0); + int count() const; + QWidget *currentWidget() const; + + int addTab(QWidget *widget, const QString &label); + QWidget *takeTabWidget(const int index); + void switchTo(QWidget *widget); + +protected: + bool event(QEvent* event) override; + +private: + void updateSplitterSizes(int index = -1); + void updateSplitButtons(); + void selectFakeTab(); + Mode mode(); + + QSplitter *m_splitter = nullptr; + QTabBar *m_tabBar = nullptr; + QWidget *m_tabBarBackground = nullptr; + const int fakeTab = 1; + bool m_splittSizesAreDirty = true; +}; +} // namespace QmlDesigner