From 1a11e5ddcdf82f62eab98b40fc7a2cf2a27ef3a8 Mon Sep 17 00:00:00 2001 From: hulswit Date: Thu, 20 Aug 2020 16:36:02 +0200 Subject: [PATCH 1/5] Central Widget concept added Adde option to set a dock widget as central widget. It influences resizing behavior of the splitters. The central widget will be stretched with the main window and remaing dock widgets and threir respective areas will be resized only vertically if docked left or right and horizontaly if docked top or bottom --- examples/CMakeLists.txt | 3 +- examples/centralWidget/CMakeLists.txt | 26 +++ examples/centralWidget/centralWidget.pro | 56 ++++++ examples/centralWidget/digitalclock.cpp | 27 +++ examples/centralWidget/digitalclock.h | 17 ++ examples/centralWidget/main.cpp | 11 ++ examples/centralWidget/mainwindow.cpp | 237 +++++++++++++++++++++++ examples/centralWidget/mainwindow.h | 32 +++ examples/centralWidget/mainwindow.ui | 30 +++ examples/examples.pro | 1 + src/DockAreaWidget.cpp | 11 ++ src/DockAreaWidget.h | 7 +- src/DockContainerWidget.cpp | 132 ++++++++++--- src/DockContainerWidget.h | 6 + src/DockManager.cpp | 18 ++ src/DockManager.h | 14 +- src/DockSplitter.cpp | 14 ++ src/DockSplitter.h | 5 + src/DockWidget.cpp | 6 + src/DockWidget.h | 5 + 20 files changed, 629 insertions(+), 29 deletions(-) create mode 100644 examples/centralWidget/CMakeLists.txt create mode 100644 examples/centralWidget/centralWidget.pro create mode 100644 examples/centralWidget/digitalclock.cpp create mode 100644 examples/centralWidget/digitalclock.h create mode 100644 examples/centralWidget/main.cpp create mode 100644 examples/centralWidget/mainwindow.cpp create mode 100644 examples/centralWidget/mainwindow.h create mode 100644 examples/centralWidget/mainwindow.ui diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index d1a4516..3b24252 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -2,4 +2,5 @@ cmake_minimum_required(VERSION 3.5) project(QtADSExamples LANGUAGES CXX VERSION ${VERSION_SHORT}) add_subdirectory(simple) add_subdirectory(sidebar) -add_subdirectory(deleteonclose) \ No newline at end of file +add_subdirectory(deleteonclose) +add_subdirectory(centralWidget) \ No newline at end of file diff --git a/examples/centralWidget/CMakeLists.txt b/examples/centralWidget/CMakeLists.txt new file mode 100644 index 0000000..4cc7de9 --- /dev/null +++ b/examples/centralWidget/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.5) +project(ads_example_centralwidget VERSION ${VERSION_SHORT}) +find_package(Qt5 5.5 COMPONENTS Core Gui Widgets REQUIRED) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +add_executable(CentralWidgetExample WIN32 + main.cpp + mainwindow.cpp + mainwindow.ui + digitalclock.cpp +) +target_include_directories(CentralWidgetExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src") +target_link_libraries(CentralWidgetExample PRIVATE qtadvanceddocking) +target_link_libraries(CentralWidgetExample PUBLIC Qt5::Core Qt5::Gui Qt5::Widgets) +set_target_properties(CentralWidgetExample PROPERTIES + AUTOMOC ON + AUTORCC ON + AUTOUIC ON + CXX_STANDARD 14 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS OFF + VERSION ${VERSION_SHORT} + EXPORT_NAME "Qt Advanced Docking System Central Widget Example" + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${ads_PlatformDir}/lib" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${ads_PlatformDir}/lib" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${ads_PlatformDir}/bin" +) diff --git a/examples/centralWidget/centralWidget.pro b/examples/centralWidget/centralWidget.pro new file mode 100644 index 0000000..f39896d --- /dev/null +++ b/examples/centralWidget/centralWidget.pro @@ -0,0 +1,56 @@ +ADS_OUT_ROOT = $${OUT_PWD}/../.. + +QT += core gui + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = CentralWidgetExample +DESTDIR = $${ADS_OUT_ROOT}/lib +TEMPLATE = app +CONFIG += c++14 +CONFIG += debug_and_release + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +SOURCES += \ + digitalclock.cpp \ + main.cpp \ + mainwindow.cpp + +HEADERS += \ + digitalclock.h \ + mainwindow.h + +FORMS += \ + mainwindow.ui + +LIBS += -L$${ADS_OUT_ROOT}/lib + +# Dependency: AdvancedDockingSystem (shared) +CONFIG(debug, debug|release){ + win32 { + LIBS += -lqtadvanceddockingd + } + else:mac { + LIBS += -lqtadvanceddocking_debug + } + else { + LIBS += -lqtadvanceddocking + } +} +else{ + LIBS += -lqtadvanceddocking +} + +INCLUDEPATH += ../../src +DEPENDPATH += ../../src + diff --git a/examples/centralWidget/digitalclock.cpp b/examples/centralWidget/digitalclock.cpp new file mode 100644 index 0000000..2752d6b --- /dev/null +++ b/examples/centralWidget/digitalclock.cpp @@ -0,0 +1,27 @@ +#include "digitalclock.h" + +#include +#include + +CDigitalClock::CDigitalClock(QWidget *parent) + : QLCDNumber(parent) +{ + setDigitCount(8); + setSegmentStyle(Filled); + + QTimer *timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, &CDigitalClock::showTime); + timer->start(1000); + + showTime(); + + setWindowTitle(tr("Digital Clock")); + resize(150, 60); +} + +void CDigitalClock::showTime() +{ + QTime time = QTime::currentTime(); + QString text = time.toString("hh:mm:ss"); + display(text); +} diff --git a/examples/centralWidget/digitalclock.h b/examples/centralWidget/digitalclock.h new file mode 100644 index 0000000..4c155d3 --- /dev/null +++ b/examples/centralWidget/digitalclock.h @@ -0,0 +1,17 @@ +#ifndef DIGITALCLOCK_H +#define DIGITALCLOCK_H + +#include + +class CDigitalClock : public QLCDNumber +{ + Q_OBJECT + +public: + CDigitalClock(QWidget *parent = nullptr); + +private slots: + void showTime(); +}; + +#endif // DIGITALCLOCK_H diff --git a/examples/centralWidget/main.cpp b/examples/centralWidget/main.cpp new file mode 100644 index 0000000..3f073f3 --- /dev/null +++ b/examples/centralWidget/main.cpp @@ -0,0 +1,11 @@ +#include "mainwindow.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + CMainWindow w; + w.show(); + return a.exec(); +} diff --git a/examples/centralWidget/mainwindow.cpp b/examples/centralWidget/mainwindow.cpp new file mode 100644 index 0000000..cc4a8d9 --- /dev/null +++ b/examples/centralWidget/mainwindow.cpp @@ -0,0 +1,237 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" +#include "digitalclock.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "DockAreaWidget.h" +#include "DockAreaTitleBar.h" +#include "DockAreaTabBar.h" +#include "FloatingDockContainer.h" +#include "DockComponentsFactory.h" + +using namespace ads; + +const QString CMainWindow::kTableTopLayout = "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "344 272 " + "" + "" + "" + "" + "" + "" + "" + "258 758 258 " + "" + "" + "" + "" + "52 621 52 " + "" + "" + ""; + +const QString CMainWindow::kTableBottomLayout = "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "258 758 258 " + "" + "" + "" + "" + "" + "" + "" + "621 52 52 " + "" + "" + ""; + +CMainWindow::CMainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::CMainWindow) +{ + ui->setupUi(this); + CDockManager::setConfigFlag(CDockManager::OpaqueSplitterResize, true); + CDockManager::setConfigFlag(CDockManager::XmlCompressionEnabled, false); + DockManager = new CDockManager(this); + QCalendarWidget* calendar = new QCalendarWidget(); + CDockWidget* CentralDockWidget = new CDockWidget("CentralWidget"); + CentralDockWidget->setWidget(calendar); + CentralDockWidget->setFeature(CDockWidget::DockWidgetClosable, false); + CentralDockWidget->setFeature(CDockWidget::DockWidgetMovable, false); + CentralDockWidget->setFeature(CDockWidget::DockWidgetFloatable, false); + auto* CentralDockArea = DockManager->setCentralWidget(CentralDockWidget); + CentralDockArea->setAllowedAreas(DockWidgetArea::OuterDockAreas); + CentralDockArea->setDockAreaFlag(CDockAreaWidget::eDockAreaFlag::HideSingleWidgetTitleBar, true); + + QTreeView* fileTree = new QTreeView(); + fileTree->setFrameShape(QFrame::NoFrame); + QFileSystemModel* fileModel = new QFileSystemModel(fileTree); + fileModel->setRootPath(QDir::currentPath()); + fileTree->setModel(fileModel); + CDockWidget* DataDockWidget = new CDockWidget("File system"); + DataDockWidget->setWidget(fileTree); + DataDockWidget->resize(150, 250); + DataDockWidget->setMinimumSize(100, 250); + auto* fileArea = DockManager->addDockWidget(DockWidgetArea::LeftDockWidgetArea, DataDockWidget, CentralDockArea); + + QTableWidget* table = new QTableWidget(); + table->setColumnCount(3); + table->setRowCount(10); + CDockWidget* TableDockWidget = new CDockWidget("Table"); + TableDockWidget->setWidget(table); + TableDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget); + TableDockWidget->resize(250, 150); + TableDockWidget->setMinimumSize(200,150); + DockManager->addDockWidget(DockWidgetArea::BottomDockWidgetArea, TableDockWidget, fileArea); + + QTableWidget* propertiesTable = new QTableWidget(); + table->setColumnCount(3); + table->setRowCount(10); + CDockWidget* PropertiesDockWidget = new CDockWidget("Properties"); + PropertiesDockWidget->setWidget(propertiesTable); + PropertiesDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget); + PropertiesDockWidget->resize(250, 150); + PropertiesDockWidget->setMinimumSize(200,150); + DockManager->addDockWidget(DockWidgetArea::RightDockWidgetArea, PropertiesDockWidget, CentralDockArea); + + QWidget* timeLineWidget = new QWidget(); + QHBoxLayout* timelineLayout = new QHBoxLayout(timeLineWidget); + QRadioButton* radioDockTop = new QRadioButton("Top", timeLineWidget); + QRadioButton* radioDockBottom = new QRadioButton("Bottom", timeLineWidget); + radioDockTop->setChecked(true); + timelineLayout->addWidget(new QLabel("Fixed height Dock widget.")); + timelineLayout->addStretch(1); + timelineLayout->addWidget(new QLabel("Apply predefined perspective: ", this)); + timelineLayout->addWidget(radioDockTop); + timelineLayout->addWidget(radioDockBottom); + TimelineDockWidget = new CDockWidget("Timeline"); + TimelineDockWidget->setWidget(timeLineWidget); +// TimelineDockWidget->setResizeMode(CDockWidget::eResizeMode::ResizeHorizontal); + TimelineDockWidget->setFeature(CDockWidget::DockWidgetClosable, false); + TimelineDockWidget->setFeature(CDockWidget::DockWidgetMovable, false); + TimelineDockWidget->setFeature(CDockWidget::DockWidgetFloatable, false); + TimelineDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget); + TimelineDockWidget->setMinimumSize(QSize(50, 50)); + TimelineDockWidget->setFixedHeight(50); + auto *TimelineDockArea = DockManager->addDockWidget(DockWidgetArea::TopDockWidgetArea, TimelineDockWidget); + TimelineDockArea->setDockAreaFlag(CDockAreaWidget::eDockAreaFlag::HideSingleWidgetTitleBar, true); + TimelineDockArea->setAllowedAreas(DockWidgetArea::OuterDockAreas); + connect(radioDockTop, &QRadioButton::toggled, [this](bool checked){ + bool ok = true; + if(!checked) + { + ok = DockManager->restoreState(kTableBottomLayout.toUtf8()); + } + else + { + ok = DockManager->restoreState(kTableTopLayout.toUtf8()); + } + if(!ok) + { + QMessageBox msgBox; + msgBox.setText("Failed to apply perspective!"); + msgBox.exec(); + } + }); + + QWidget* statusWidget = new QWidget(); + QHBoxLayout* statusLayout = new QHBoxLayout(statusWidget); + statusLayout->setSpacing(10); + CDigitalClock* clock = new CDigitalClock(statusWidget); + statusLayout->addWidget(new QLabel("Status Bar")); + QPushButton* OpenPerspectiveButton = new QPushButton("Open Perspective", statusWidget); + connect(OpenPerspectiveButton, &QPushButton::clicked, [this](){ + QString PerspectiveName = QFileDialog::getOpenFileName(this, "Open Perspective", "", "Perspective files (*.xml)"); + if (PerspectiveName.isEmpty()) + { + return; + } + + QFile stateFile(PerspectiveName); + stateFile.open(QIODevice::ReadOnly); + QByteArray state = stateFile.readAll(); + stateFile.close(); + if(!DockManager->restoreState(state)) + { + QMessageBox msgBox; + msgBox.setText("Failed to apply perspective " + stateFile.fileName()); + msgBox.exec(); + } + }); + QPushButton* SavePerspectiveButton = new QPushButton("Create Perspective", statusWidget); + connect(SavePerspectiveButton, &QPushButton::clicked, [this](){ + QString PerspectiveName = QInputDialog::getText(this, "Save Perspective", "Enter unique name:"); + if (PerspectiveName.isEmpty()) + { + return; + } + + QByteArray state = DockManager->saveState(); + QFile stateFile(PerspectiveName + ".xml"); + stateFile.open(QIODevice::WriteOnly); + stateFile.write(state); + stateFile.close(); + }); + statusLayout->addWidget(OpenPerspectiveButton); + statusLayout->addWidget(SavePerspectiveButton); + statusLayout->addStretch(1); + statusLayout->addWidget(clock); + CDockWidget* StatusDockWidget = new CDockWidget("Status"); + StatusDockWidget->setWidget(statusWidget); +// StatusDockWidget->setResizeMode(CDockWidget::eResizeMode::ResizeHorizontal); + StatusDockWidget->setFeature(CDockWidget::DockWidgetClosable, false); + StatusDockWidget->setFeature(CDockWidget::DockWidgetMovable, false); + StatusDockWidget->setFeature(CDockWidget::DockWidgetFloatable, false); + StatusDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget); + StatusDockWidget->setMinimumSize(QSize(50, 50)); + StatusDockWidget->setFixedHeight(50); + StatusDockArea = DockManager->addDockWidget(DockWidgetArea::BottomDockWidgetArea, StatusDockWidget); + StatusDockArea->setAllowedAreas(DockWidgetArea::OuterDockAreas); + StatusDockArea->setDockAreaFlag(CDockAreaWidget::eDockAreaFlag::HideSingleWidgetTitleBar, true); +} + +CMainWindow::~CMainWindow() +{ + delete ui; +} + diff --git a/examples/centralWidget/mainwindow.h b/examples/centralWidget/mainwindow.h new file mode 100644 index 0000000..cbd05b0 --- /dev/null +++ b/examples/centralWidget/mainwindow.h @@ -0,0 +1,32 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include + +#include "DockManager.h" +#include "DockAreaWidget.h" +#include "DockWidget.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class CMainWindow; } +QT_END_NAMESPACE + +class CMainWindow : public QMainWindow +{ + Q_OBJECT + +public: + CMainWindow(QWidget *parent = nullptr); + ~CMainWindow(); + +private: + static const QString kTableTopLayout; + static const QString kTableBottomLayout; + + Ui::CMainWindow *ui; + + ads::CDockManager* DockManager; + ads::CDockAreaWidget* StatusDockArea; + ads::CDockWidget* TimelineDockWidget; +}; +#endif // MAINWINDOW_H diff --git a/examples/centralWidget/mainwindow.ui b/examples/centralWidget/mainwindow.ui new file mode 100644 index 0000000..73ae7b1 --- /dev/null +++ b/examples/centralWidget/mainwindow.ui @@ -0,0 +1,30 @@ + + + CMainWindow + + + + 0 + 0 + 1284 + 757 + + + + MainWindow + + + + + + 0 + 0 + 1284 + 21 + + + + + + + diff --git a/examples/examples.pro b/examples/examples.pro index 68419c9..de8cf7f 100644 --- a/examples/examples.pro +++ b/examples/examples.pro @@ -1,6 +1,7 @@ TEMPLATE = subdirs SUBDIRS = \ + centralwidget \ simple \ sidebar \ deleteonclose diff --git a/src/DockAreaWidget.cpp b/src/DockAreaWidget.cpp index 092f997..6d3b8c9 100644 --- a/src/DockAreaWidget.cpp +++ b/src/DockAreaWidget.cpp @@ -440,6 +440,7 @@ void CDockAreaWidget::insertDockWidget(int index, CDockWidget* DockWidget, DockWidget->toggleViewInternal(true); } d->updateTitleBarButtonStates(); + updateTitleBarVisibility(); } @@ -946,6 +947,16 @@ CDockAreaTitleBar* CDockAreaWidget::titleBar() const } +//============================================================================ +bool CDockAreaWidget::isCentralWidgetArea() +{ + if(dockWidgetsCount()!=1) + return false; + + return dockManager()->centralWidget()==dockWidgets()[0]; +} + + //============================================================================ QSize CDockAreaWidget::minimumSizeHint() const { diff --git a/src/DockAreaWidget.h b/src/DockAreaWidget.h index 337eac2..0c4653a 100644 --- a/src/DockAreaWidget.h +++ b/src/DockAreaWidget.h @@ -153,7 +153,7 @@ public: }; Q_DECLARE_FLAGS(DockAreaFlags, eDockAreaFlag) - /** + /** * Default Constructor */ CDockAreaWidget(CDockManager* DockManager, CDockContainerWidget* parent); @@ -306,6 +306,11 @@ public: */ void setDockAreaFlag(eDockAreaFlag Flag, bool On); + /** + * Returns true if the area contains the central widget of it's manager. + */ + bool isCentralWidgetArea(); + public slots: /** * This activates the tab for the given tab index. diff --git a/src/DockContainerWidget.cpp b/src/DockContainerWidget.cpp index 4d716c6..56aef07 100644 --- a/src/DockContainerWidget.cpp +++ b/src/DockContainerWidget.cpp @@ -322,6 +322,17 @@ public: Splitter->setSizes(SplitterSizes); } + /** + * This finction forces the dock container widget to update handles of splitters + * based on resize modes of dock widgets aontained in the container. + */ + void updateSplitterHandles(QSplitter* splitter); + + /** + * This function returns true if the area is not allowed to resize in the direstion + * of the splitter. Otherwise returns true. + */ + bool widgetResizesWithContainer(QWidget* widget); // private slots: ------------------------------------------------------------ void onDockAreaViewToggled(bool Visible) @@ -421,7 +432,8 @@ void DockContainerWidgetPrivate::dropIntoContainer(CFloatingDockContainer* Float QSplitter* NewSplitter = newSplitter(InsertParam.orientation()); QLayoutItem* li = Layout->replaceWidget(Splitter, NewSplitter); NewSplitter->addWidget(Splitter); - Splitter = NewSplitter; + updateSplitterHandles(NewSplitter); + Splitter = NewSplitter; delete li; } @@ -430,19 +442,21 @@ void DockContainerWidgetPrivate::dropIntoContainer(CFloatingDockContainer* Float if (FloatingSplitter->count() == 1) { insertWidgetIntoSplitter(Splitter, FloatingSplitter->widget(0), InsertParam.append()); - } + updateSplitterHandles(Splitter); + } else if (FloatingSplitter->orientation() == InsertParam.orientation()) { int InsertIndex = InsertParam.append() ? Splitter->count() : 0; while (FloatingSplitter->count()) { Splitter->insertWidget(InsertIndex++, FloatingSplitter->widget(0)); - } - } + updateSplitterHandles(Splitter); + } + } else { insertWidgetIntoSplitter(Splitter, FloatingSplitter, InsertParam.append()); - } + } RootSplitter = Splitter; addDockAreasToList(NewDockAreas); @@ -453,7 +467,7 @@ void DockContainerWidgetPrivate::dropIntoContainer(CFloatingDockContainer* Float if (!Splitter->isVisible()) { Splitter->show(); - } + } _this->dumpLayout(); } @@ -515,7 +529,8 @@ void DockContainerWidgetPrivate::dropIntoSection(CFloatingDockContainer* Floatin QSplitter* Splitter = newSplitter(InsertParam.orientation()); Layout->replaceWidget(TargetArea, Splitter); Splitter->addWidget(TargetArea); - TargetAreaSplitter = Splitter; + updateSplitterHandles(Splitter); + TargetAreaSplitter = Splitter; } int AreaIndex = TargetAreaSplitter->indexOf(TargetArea); auto Widget = FloatingWidget->dockContainer()->findChild(QString(), Qt::FindDirectChildrenOnly); @@ -529,7 +544,8 @@ void DockContainerWidgetPrivate::dropIntoSection(CFloatingDockContainer* Floatin if ((FloatingSplitter->orientation() != InsertParam.orientation()) && FloatingSplitter->count() > 1) { TargetAreaSplitter->insertWidget(AreaIndex + InsertParam.insertOffset(), Widget); - } + updateSplitterHandles(TargetAreaSplitter); + } else { AdjustSplitterSizes = (FloatingSplitter->count() == 1); @@ -537,8 +553,9 @@ void DockContainerWidgetPrivate::dropIntoSection(CFloatingDockContainer* Floatin while (FloatingSplitter->count()) { TargetAreaSplitter->insertWidget(InsertIndex++, FloatingSplitter->widget(0)); - } - } + updateSplitterHandles(TargetAreaSplitter); + } + } if (AdjustSplitterSizes) { @@ -557,28 +574,32 @@ void DockContainerWidgetPrivate::dropIntoSection(CFloatingDockContainer* Floatin if ((FloatingSplitter->orientation() != InsertParam.orientation()) && FloatingSplitter->count() > 1) { NewSplitter->addWidget(Widget); - } + updateSplitterHandles(NewSplitter); + } else { AdjustSplitterSizes = (FloatingSplitter->count() == 1); while (FloatingSplitter->count()) { NewSplitter->addWidget(FloatingSplitter->widget(0)); - } - } + updateSplitterHandles(NewSplitter); + } + } // Save the sizes before insertion and restore it later to prevent // shrinking of existing area auto Sizes = TargetAreaSplitter->sizes(); insertWidgetIntoSplitter(NewSplitter, TargetArea, !InsertParam.append()); - if (AdjustSplitterSizes) + updateSplitterHandles(NewSplitter); + if (AdjustSplitterSizes) { int Size = TargetAreaSize / 2; NewSplitter->setSizes({Size, Size}); } TargetAreaSplitter->insertWidget(AreaIndex, NewSplitter); TargetAreaSplitter->setSizes(Sizes); - } + updateSplitterHandles(TargetAreaSplitter); + } addDockAreasToList(NewDockAreas); _this->dumpLayout(); @@ -663,7 +684,8 @@ void DockContainerWidgetPrivate::moveToNewSection(QWidget* Widget, CDockAreaWidg { int TargetAreaSize = (InsertParam.orientation() == Qt::Horizontal) ? TargetArea->width() : TargetArea->height(); TargetAreaSplitter->insertWidget(AreaIndex + InsertParam.insertOffset(), NewDockArea); - int Size = (TargetAreaSize - TargetAreaSplitter->handleWidth()) / 2; + updateSplitterHandles(TargetAreaSplitter); + int Size = (TargetAreaSize - TargetAreaSplitter->handleWidth()) / 2; Sizes[AreaIndex] = Size; Sizes.insert(AreaIndex, Size); } @@ -674,16 +696,54 @@ void DockContainerWidgetPrivate::moveToNewSection(QWidget* Widget, CDockAreaWidg QSplitter* NewSplitter = newSplitter(InsertParam.orientation()); NewSplitter->addWidget(TargetArea); insertWidgetIntoSplitter(NewSplitter, NewDockArea, InsertParam.append()); - int Size = TargetAreaSize / 2; + updateSplitterHandles(NewSplitter); + int Size = TargetAreaSize / 2; NewSplitter->setSizes({Size, Size}); TargetAreaSplitter->insertWidget(AreaIndex, NewSplitter); - } + updateSplitterHandles(TargetAreaSplitter); + } TargetAreaSplitter->setSizes(Sizes); addDockAreasToList({NewDockArea}); } +//============================================================================ +void DockContainerWidgetPrivate::updateSplitterHandles( QSplitter* splitter ) +{ + if( splitter ) + { + for( int index = 0; index < splitter->count(); index++ ) + { + splitter->setStretchFactor(index, widgetResizesWithContainer(splitter->widget(index)) ? 1 : 0); + } + } +} + + +//============================================================================ +bool DockContainerWidgetPrivate::widgetResizesWithContainer(QWidget* widget) +{ + if(!DockManager->centralWidget()) + return true; + + CDockAreaWidget* Area = dynamic_cast< CDockAreaWidget* >( widget ); + if(Area) + { + return Area->isCentralWidgetArea(); + } + + CDockSplitter* innerSplitter = dynamic_cast< CDockSplitter* >( widget ); + if(innerSplitter) + { + return innerSplitter->resizeWithContainer(); + } + + return false; +} + + + //============================================================================ void DockContainerWidgetPrivate::moveToContainer(QWidget* Widget, DockWidgetArea area) { @@ -892,6 +952,10 @@ bool DockContainerWidgetPrivate::restoreSplitter(CDockingStateReader& s, Splitter->addWidget(ChildNode); Visible |= ChildNode->isVisibleTo(Splitter); } + if(!Testing) + { + updateSplitterHandles(Splitter); + } if (Sizes.count() != WidgetCount) { @@ -1069,7 +1133,8 @@ void DockContainerWidgetPrivate::addDockArea(CDockAreaWidget* NewDockArea, DockW if (Splitter->orientation() == InsertParam.orientation()) { insertWidgetIntoSplitter(Splitter, NewDockArea, InsertParam.append()); - if (Splitter->isHidden()) + updateSplitterHandles(Splitter); + if (Splitter->isHidden()) { Splitter->show(); } @@ -1082,14 +1147,16 @@ void DockContainerWidgetPrivate::addDockArea(CDockAreaWidget* NewDockArea, DockW QLayoutItem* li = Layout->replaceWidget(Splitter, NewSplitter); NewSplitter->addWidget(Splitter); NewSplitter->addWidget(NewDockArea); - delete li; + updateSplitterHandles(NewSplitter); + delete li; } else { NewSplitter->addWidget(NewDockArea); QLayoutItem* li = Layout->replaceWidget(Splitter, NewSplitter); NewSplitter->addWidget(Splitter); - delete li; + updateSplitterHandles(NewSplitter); + delete li; } RootSplitter = NewSplitter; } @@ -1175,7 +1242,8 @@ CDockAreaWidget* DockContainerWidgetPrivate::addDockWidgetToDockArea(DockWidgetA { ADS_PRINT("TargetAreaSplitter->orientation() == InsertParam.orientation()"); TargetAreaSplitter->insertWidget(index + InsertParam.insertOffset(), NewDockArea); - // do nothing, if flag is not enabled + updateSplitterHandles(TargetAreaSplitter); + // do nothing, if flag is not enabled if (CDockManager::testConfigFlag(CDockManager::EqualSplitOnInsertion)) { adjustSplitterSizesOnInsertion(TargetAreaSplitter); @@ -1188,11 +1256,14 @@ CDockAreaWidget* DockContainerWidgetPrivate::addDockWidgetToDockArea(DockWidgetA auto TargetAreaSizes = TargetAreaSplitter->sizes(); QSplitter* NewSplitter = newSplitter(InsertParam.orientation()); NewSplitter->addWidget(TargetDockArea); +// updateSplitterHandles(NewSplitter); insertWidgetIntoSplitter(NewSplitter, NewDockArea, InsertParam.append()); - TargetAreaSplitter->insertWidget(index, NewSplitter); - if (CDockManager::testConfigFlag(CDockManager::EqualSplitOnInsertion)) - { + updateSplitterHandles(NewSplitter); + TargetAreaSplitter->insertWidget(index, NewSplitter); + updateSplitterHandles(TargetAreaSplitter); + if (CDockManager::testConfigFlag(CDockManager::EqualSplitOnInsertion)) + { TargetAreaSplitter->setSizes(TargetAreaSizes); adjustSplitterSizesOnInsertion(NewSplitter); } @@ -1381,9 +1452,11 @@ void CDockContainerWidget::removeDockArea(CDockAreaWidget* area) } delete Splitter; + Splitter = nullptr; emitAndExit: - CDockWidget* TopLevelWidget = topLevelDockWidget(); + updateSplitterHandles(Splitter); + CDockWidget* TopLevelWidget = topLevelDockWidget(); // Updated the title bar visibility of the dock widget if there is only // one single visible dock widget @@ -1732,6 +1805,13 @@ QList CDockContainerWidget::dockWidgets() const } +//============================================================================ +void CDockContainerWidget::updateSplitterHandles(QSplitter* splitter) +{ + d->updateSplitterHandles(splitter); +} + + //============================================================================ CDockWidget::DockWidgetFeatures CDockContainerWidget::features() const { diff --git a/src/DockContainerWidget.h b/src/DockContainerWidget.h index 1ce2a9e..5e8b8cb 100644 --- a/src/DockContainerWidget.h +++ b/src/DockContainerWidget.h @@ -155,6 +155,12 @@ protected: */ QList dockWidgets() const; + /** + * This finction forces the dock container widget to update handles of splitters + * based on resize modes of dock widgets aontained in the container. + */ + void updateSplitterHandles(QSplitter* splitter); + public: /** * Default Constructor diff --git a/src/DockManager.cpp b/src/DockManager.cpp index 95e4fac..717ffde 100644 --- a/src/DockManager.cpp +++ b/src/DockManager.cpp @@ -108,6 +108,7 @@ struct DockManagerPrivate bool RestoringState = false; QVector UninitializedFloatingWidgets; CDockFocusController* FocusController = nullptr; + CDockWidget* CentralWidget = nullptr; /** * Private data constructor @@ -816,6 +817,23 @@ void CDockManager::loadPerspectives(QSettings& Settings) Settings.endArray(); } +CDockWidget* CDockManager::centralWidget() +{ + return d->CentralWidget; +} + +//============================================================================ +CDockAreaWidget* CDockManager::setCentralWidget(CDockWidget* widget) +{ + if(d->CentralWidget) + { + addDockWidget(RightDockWidgetArea, d->CentralWidget); + } + + d->CentralWidget = widget; + return addDockWidget(CenterDockWidgetArea, widget); +} + //============================================================================ QAction* CDockManager::addToggleViewActionToMenu(QAction* ToggleViewAction, const QString& Group, const QIcon& GroupIcon) diff --git a/src/DockManager.h b/src/DockManager.h index b682c43..7808a43 100644 --- a/src/DockManager.h +++ b/src/DockManager.h @@ -378,7 +378,19 @@ public: */ void loadPerspectives(QSettings& Settings); - /** + /** + * This function returns managers central widget or nullptr if no central widget is set. + */ + CDockWidget* centralWidget(); + + /** + * Adds dockwidget 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. + */ + CDockAreaWidget* setCentralWidget(CDockWidget* widget); + + /** * Adds a toggle view action to the the internal view menu. * You can either manage the insertion of the toggle view actions in your * application or you can add the actions to the internal view menu and diff --git a/src/DockSplitter.cpp b/src/DockSplitter.cpp index 92f988d..2be1f07 100644 --- a/src/DockSplitter.cpp +++ b/src/DockSplitter.cpp @@ -102,6 +102,20 @@ QWidget* CDockSplitter::lastWidget() const return (count() > 0) ? widget(count() - 1) : nullptr; } +//============================================================================ +bool CDockSplitter::resizeWithContainer() +{ + QList areas = findChildren(); + + for(int i=0; iisCentralWidgetArea()) + return true; + } + return false; +} + } // namespace ads //--------------------------------------------------------------------------- diff --git a/src/DockSplitter.h b/src/DockSplitter.h index 24abc40..9dacdf8 100644 --- a/src/DockSplitter.h +++ b/src/DockSplitter.h @@ -71,6 +71,11 @@ public: * Returns last widget of nullptr is splitter is empty */ QWidget* lastWidget() const; + + /** + * Returns true if the splitter contains central widget of dock manager. + */ + bool resizeWithContainer(); }; // class CDockSplitter } // namespace ads diff --git a/src/DockWidget.cpp b/src/DockWidget.cpp index 56c697e..1c3d521 100644 --- a/src/DockWidget.cpp +++ b/src/DockWidget.cpp @@ -461,6 +461,12 @@ void CDockWidget::setMinimumSizeHintMode(eMinimumSizeHintMode Mode) } +bool CDockWidget::isCentralWidget() +{ + return dockManager()->centralWidget() == this; +} + + //============================================================================ void CDockWidget::toggleView(bool Open) { diff --git a/src/DockWidget.h b/src/DockWidget.h index 72f2341..ef8be51 100644 --- a/src/DockWidget.h +++ b/src/DockWidget.h @@ -360,6 +360,11 @@ public: */ void setMinimumSizeHintMode(eMinimumSizeHintMode Mode); + /** + * Returns true if the dock wisget is set as central widget of it's dock manager + */ + bool isCentralWidget(); + /** * Sets the dock widget icon that is shown in tabs and in toggle view * actions From 14c29f695c205868745bf2e0a107fa86e34a7847 Mon Sep 17 00:00:00 2001 From: hulswit Date: Fri, 21 Aug 2020 13:30:59 +0200 Subject: [PATCH 2/5] Central widget update Updated the setting of central widget with option to set where the possible old central widget will be placed. Fixed option of "unsetting" central widget by setting it to nullptr. --- .gitignore | 1 + examples/centralWidget/centralWidget.pro | 2 -- examples/centralWidget/digitalclock.cpp | 27 ------------------------ examples/centralWidget/digitalclock.h | 17 --------------- examples/centralWidget/mainwindow.cpp | 15 ++----------- src/DockContainerWidget.cpp | 10 +++++---- src/DockManager.cpp | 21 +++++++++++++----- src/DockManager.h | 4 +++- 8 files changed, 28 insertions(+), 69 deletions(-) delete mode 100644 examples/centralWidget/digitalclock.cpp delete mode 100644 examples/centralWidget/digitalclock.h diff --git a/.gitignore b/.gitignore index f9e79be..05aa783 100644 --- a/.gitignore +++ b/.gitignore @@ -382,3 +382,4 @@ MigrationBackup/ FodyWeavers.xsd / build /Settings.ini +.vscode/settings.json diff --git a/examples/centralWidget/centralWidget.pro b/examples/centralWidget/centralWidget.pro index f39896d..9c00fd6 100644 --- a/examples/centralWidget/centralWidget.pro +++ b/examples/centralWidget/centralWidget.pro @@ -22,12 +22,10 @@ DEFINES += QT_DEPRECATED_WARNINGS #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ - digitalclock.cpp \ main.cpp \ mainwindow.cpp HEADERS += \ - digitalclock.h \ mainwindow.h FORMS += \ diff --git a/examples/centralWidget/digitalclock.cpp b/examples/centralWidget/digitalclock.cpp deleted file mode 100644 index 2752d6b..0000000 --- a/examples/centralWidget/digitalclock.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "digitalclock.h" - -#include -#include - -CDigitalClock::CDigitalClock(QWidget *parent) - : QLCDNumber(parent) -{ - setDigitCount(8); - setSegmentStyle(Filled); - - QTimer *timer = new QTimer(this); - connect(timer, &QTimer::timeout, this, &CDigitalClock::showTime); - timer->start(1000); - - showTime(); - - setWindowTitle(tr("Digital Clock")); - resize(150, 60); -} - -void CDigitalClock::showTime() -{ - QTime time = QTime::currentTime(); - QString text = time.toString("hh:mm:ss"); - display(text); -} diff --git a/examples/centralWidget/digitalclock.h b/examples/centralWidget/digitalclock.h deleted file mode 100644 index 4c155d3..0000000 --- a/examples/centralWidget/digitalclock.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef DIGITALCLOCK_H -#define DIGITALCLOCK_H - -#include - -class CDigitalClock : public QLCDNumber -{ - Q_OBJECT - -public: - CDigitalClock(QWidget *parent = nullptr); - -private slots: - void showTime(); -}; - -#endif // DIGITALCLOCK_H diff --git a/examples/centralWidget/mainwindow.cpp b/examples/centralWidget/mainwindow.cpp index cc4a8d9..285de5b 100644 --- a/examples/centralWidget/mainwindow.cpp +++ b/examples/centralWidget/mainwindow.cpp @@ -1,6 +1,5 @@ #include "mainwindow.h" #include "ui_mainwindow.h" -#include "digitalclock.h" #include #include @@ -96,12 +95,8 @@ CMainWindow::CMainWindow(QWidget *parent) QCalendarWidget* calendar = new QCalendarWidget(); CDockWidget* CentralDockWidget = new CDockWidget("CentralWidget"); CentralDockWidget->setWidget(calendar); - CentralDockWidget->setFeature(CDockWidget::DockWidgetClosable, false); - CentralDockWidget->setFeature(CDockWidget::DockWidgetMovable, false); - CentralDockWidget->setFeature(CDockWidget::DockWidgetFloatable, false); auto* CentralDockArea = DockManager->setCentralWidget(CentralDockWidget); CentralDockArea->setAllowedAreas(DockWidgetArea::OuterDockAreas); - CentralDockArea->setDockAreaFlag(CDockAreaWidget::eDockAreaFlag::HideSingleWidgetTitleBar, true); QTreeView* fileTree = new QTreeView(); fileTree->setFrameShape(QFrame::NoFrame); @@ -139,20 +134,18 @@ CMainWindow::CMainWindow(QWidget *parent) QRadioButton* radioDockTop = new QRadioButton("Top", timeLineWidget); QRadioButton* radioDockBottom = new QRadioButton("Bottom", timeLineWidget); radioDockTop->setChecked(true); - timelineLayout->addWidget(new QLabel("Fixed height Dock widget.")); + timelineLayout->addWidget(new QLabel("Test Widget.")); timelineLayout->addStretch(1); timelineLayout->addWidget(new QLabel("Apply predefined perspective: ", this)); timelineLayout->addWidget(radioDockTop); timelineLayout->addWidget(radioDockBottom); TimelineDockWidget = new CDockWidget("Timeline"); TimelineDockWidget->setWidget(timeLineWidget); -// TimelineDockWidget->setResizeMode(CDockWidget::eResizeMode::ResizeHorizontal); TimelineDockWidget->setFeature(CDockWidget::DockWidgetClosable, false); TimelineDockWidget->setFeature(CDockWidget::DockWidgetMovable, false); TimelineDockWidget->setFeature(CDockWidget::DockWidgetFloatable, false); TimelineDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget); TimelineDockWidget->setMinimumSize(QSize(50, 50)); - TimelineDockWidget->setFixedHeight(50); auto *TimelineDockArea = DockManager->addDockWidget(DockWidgetArea::TopDockWidgetArea, TimelineDockWidget); TimelineDockArea->setDockAreaFlag(CDockAreaWidget::eDockAreaFlag::HideSingleWidgetTitleBar, true); TimelineDockArea->setAllowedAreas(DockWidgetArea::OuterDockAreas); @@ -177,7 +170,6 @@ CMainWindow::CMainWindow(QWidget *parent) QWidget* statusWidget = new QWidget(); QHBoxLayout* statusLayout = new QHBoxLayout(statusWidget); statusLayout->setSpacing(10); - CDigitalClock* clock = new CDigitalClock(statusWidget); statusLayout->addWidget(new QLabel("Status Bar")); QPushButton* OpenPerspectiveButton = new QPushButton("Open Perspective", statusWidget); connect(OpenPerspectiveButton, &QPushButton::clicked, [this](){ @@ -215,19 +207,16 @@ CMainWindow::CMainWindow(QWidget *parent) statusLayout->addWidget(OpenPerspectiveButton); statusLayout->addWidget(SavePerspectiveButton); statusLayout->addStretch(1); - statusLayout->addWidget(clock); CDockWidget* StatusDockWidget = new CDockWidget("Status"); StatusDockWidget->setWidget(statusWidget); -// StatusDockWidget->setResizeMode(CDockWidget::eResizeMode::ResizeHorizontal); StatusDockWidget->setFeature(CDockWidget::DockWidgetClosable, false); StatusDockWidget->setFeature(CDockWidget::DockWidgetMovable, false); StatusDockWidget->setFeature(CDockWidget::DockWidgetFloatable, false); StatusDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget); StatusDockWidget->setMinimumSize(QSize(50, 50)); - StatusDockWidget->setFixedHeight(50); StatusDockArea = DockManager->addDockWidget(DockWidgetArea::BottomDockWidgetArea, StatusDockWidget); StatusDockArea->setAllowedAreas(DockWidgetArea::OuterDockAreas); - StatusDockArea->setDockAreaFlag(CDockAreaWidget::eDockAreaFlag::HideSingleWidgetTitleBar, true); + StatusDockArea->setDockAreaFlag(ads::CDockAreaWidget::eDockAreaFlag::HideSingleWidgetTitleBar, true); } CMainWindow::~CMainWindow() diff --git a/src/DockContainerWidget.cpp b/src/DockContainerWidget.cpp index 56aef07..171a8e0 100644 --- a/src/DockContainerWidget.cpp +++ b/src/DockContainerWidget.cpp @@ -711,11 +711,14 @@ void DockContainerWidgetPrivate::moveToNewSection(QWidget* Widget, CDockAreaWidg //============================================================================ void DockContainerWidgetPrivate::updateSplitterHandles( QSplitter* splitter ) { - if( splitter ) + if(DockManager->centralWidget()) { - for( int index = 0; index < splitter->count(); index++ ) + if( splitter ) { - splitter->setStretchFactor(index, widgetResizesWithContainer(splitter->widget(index)) ? 1 : 0); + for( int index = 0; index < splitter->count(); index++ ) + { + splitter->setStretchFactor(index, widgetResizesWithContainer(splitter->widget(index)) ? 1 : 0); + } } } } @@ -1256,7 +1259,6 @@ CDockAreaWidget* DockContainerWidgetPrivate::addDockWidgetToDockArea(DockWidgetA auto TargetAreaSizes = TargetAreaSplitter->sizes(); QSplitter* NewSplitter = newSplitter(InsertParam.orientation()); NewSplitter->addWidget(TargetDockArea); -// updateSplitterHandles(NewSplitter); insertWidgetIntoSplitter(NewSplitter, NewDockArea, InsertParam.append()); updateSplitterHandles(NewSplitter); diff --git a/src/DockManager.cpp b/src/DockManager.cpp index d04cc50..145501f 100644 --- a/src/DockManager.cpp +++ b/src/DockManager.cpp @@ -407,6 +407,7 @@ bool DockManagerPrivate::restoreState(const QByteArray& State, int version) return false; } + CentralWidget = nullptr; // Hide updates of floating widgets from use hideFloatingWidgets(); markDockWidgetsDirty(); @@ -830,15 +831,25 @@ CDockWidget* CDockManager::centralWidget() } //============================================================================ -CDockAreaWidget* CDockManager::setCentralWidget(CDockWidget* widget) +CDockAreaWidget* CDockManager::setCentralWidget(CDockWidget* widget, CDockWidget* oldCentralWidget, DockWidgetArea oldCentralWidgetArea) { - if(d->CentralWidget) + oldCentralWidget = d->CentralWidget; + if(oldCentralWidget) { - addDockWidget(RightDockWidgetArea, d->CentralWidget); + addDockWidget(oldCentralWidgetArea, oldCentralWidget); } - d->CentralWidget = widget; - return addDockWidget(CenterDockWidgetArea, widget); + if(widget) + { + widget->setFeature(CDockWidget::DockWidgetClosable, false); + widget->setFeature(CDockWidget::DockWidgetMovable, false); + widget->setFeature(CDockWidget::DockWidgetFloatable, false); + d->CentralWidget = widget; + CDockAreaWidget* CentralArea = addDockWidget(CenterDockWidgetArea, widget); + CentralArea->setDockAreaFlag(CDockAreaWidget::eDockAreaFlag::HideSingleWidgetTitleBar, true); + return CentralArea; + } + return nullptr; } //============================================================================ diff --git a/src/DockManager.h b/src/DockManager.h index 7808a43..07f735c 100644 --- a/src/DockManager.h +++ b/src/DockManager.h @@ -387,8 +387,10 @@ public: * Adds dockwidget 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. + * If a central widget does exist, it will be docked to oldCentralWidgetArea + * and returned in oldCentralWidget. */ - CDockAreaWidget* setCentralWidget(CDockWidget* widget); + CDockAreaWidget* setCentralWidget(CDockWidget* widget, CDockWidget* oldCentralWidget = nullptr, DockWidgetArea oldCentralWidgetArea = DockWidgetArea::RightDockWidgetArea); /** * Adds a toggle view action to the the internal view menu. From ba69f3e6b9b15d60657dd5287ec881dabc2c75a9 Mon Sep 17 00:00:00 2001 From: hulswit Date: Fri, 21 Aug 2020 13:42:52 +0200 Subject: [PATCH 3/5] CMake Fix --- examples/centralWidget/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/centralWidget/CMakeLists.txt b/examples/centralWidget/CMakeLists.txt index 4cc7de9..aa69603 100644 --- a/examples/centralWidget/CMakeLists.txt +++ b/examples/centralWidget/CMakeLists.txt @@ -6,7 +6,6 @@ add_executable(CentralWidgetExample WIN32 main.cpp mainwindow.cpp mainwindow.ui - digitalclock.cpp ) target_include_directories(CentralWidgetExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src") target_link_libraries(CentralWidgetExample PRIVATE qtadvanceddocking) From 835a532e755a90177993e4f1a9fbedba1c2c4447 Mon Sep 17 00:00:00 2001 From: Uwe Kindler Date: Mon, 24 Aug 2020 10:22:12 +0200 Subject: [PATCH 4/5] Corrected constness of some functions, changed signatur of setCentralWidget function --- src/DockAreaWidget.cpp | 8 ++++--- src/DockAreaWidget.h | 2 +- src/DockContainerWidget.cpp | 36 ++++++++++++++-------------- src/DockContainerWidget.h | 4 ++-- src/DockManager.cpp | 47 +++++++++++++++++++++---------------- src/DockManager.h | 19 ++++++++++----- src/DockSplitter.cpp | 10 ++++---- src/DockSplitter.h | 2 +- src/DockWidget.cpp | 3 ++- src/DockWidget.h | 4 ++-- 10 files changed, 77 insertions(+), 58 deletions(-) diff --git a/src/DockAreaWidget.cpp b/src/DockAreaWidget.cpp index 2fb345c..fe9097f 100644 --- a/src/DockAreaWidget.cpp +++ b/src/DockAreaWidget.cpp @@ -968,12 +968,14 @@ CDockAreaTitleBar* CDockAreaWidget::titleBar() const //============================================================================ -bool CDockAreaWidget::isCentralWidgetArea() +bool CDockAreaWidget::isCentralWidgetArea() const { - if(dockWidgetsCount()!=1) + if (dockWidgetsCount()!= 1) + { return false; + } - return dockManager()->centralWidget()==dockWidgets()[0]; + return dockManager()->centralWidget() == dockWidgets()[0]; } diff --git a/src/DockAreaWidget.h b/src/DockAreaWidget.h index 231af91..ac7dcb5 100644 --- a/src/DockAreaWidget.h +++ b/src/DockAreaWidget.h @@ -310,7 +310,7 @@ public: /** * Returns true if the area contains the central widget of it's manager. */ - bool isCentralWidgetArea(); + bool isCentralWidgetArea() const; public slots: /** diff --git a/src/DockContainerWidget.cpp b/src/DockContainerWidget.cpp index 171a8e0..a10495e 100644 --- a/src/DockContainerWidget.cpp +++ b/src/DockContainerWidget.cpp @@ -323,14 +323,15 @@ public: } /** - * This finction forces the dock container widget to update handles of splitters - * based on resize modes of dock widgets aontained in the container. + * This function forces the dock container widget to update handles of splitters + * based if a central widget exists. */ void updateSplitterHandles(QSplitter* splitter); /** - * This function returns true if the area is not allowed to resize in the direstion - * of the splitter. Otherwise returns true. + * If no central widget exists, the widgets resize with the container. + * If a central widget exists, the widgets surrounding the central widget + * do not resize its height or width. */ bool widgetResizesWithContainer(QWidget* widget); @@ -711,15 +712,14 @@ void DockContainerWidgetPrivate::moveToNewSection(QWidget* Widget, CDockAreaWidg //============================================================================ void DockContainerWidgetPrivate::updateSplitterHandles( QSplitter* splitter ) { - if(DockManager->centralWidget()) + if (!DockManager->centralWidget() || !splitter) + { + return; + } + + for (int i = 0; i < splitter->count(); ++i) { - if( splitter ) - { - for( int index = 0; index < splitter->count(); index++ ) - { - splitter->setStretchFactor(index, widgetResizesWithContainer(splitter->widget(index)) ? 1 : 0); - } - } + splitter->setStretchFactor(i, widgetResizesWithContainer(splitter->widget(i)) ? 1 : 0); } } @@ -727,19 +727,21 @@ void DockContainerWidgetPrivate::updateSplitterHandles( QSplitter* splitter ) //============================================================================ bool DockContainerWidgetPrivate::widgetResizesWithContainer(QWidget* widget) { - if(!DockManager->centralWidget()) + if (!DockManager->centralWidget()) + { return true; + } - CDockAreaWidget* Area = dynamic_cast< CDockAreaWidget* >( widget ); + auto Area = qobject_cast(widget); if(Area) { return Area->isCentralWidgetArea(); } - CDockSplitter* innerSplitter = dynamic_cast< CDockSplitter* >( widget ); - if(innerSplitter) + auto innerSplitter = qobject_cast(widget); + if (innerSplitter) { - return innerSplitter->resizeWithContainer(); + return innerSplitter->isResizingWithContainer(); } return false; diff --git a/src/DockContainerWidget.h b/src/DockContainerWidget.h index 5e8b8cb..0bc674b 100644 --- a/src/DockContainerWidget.h +++ b/src/DockContainerWidget.h @@ -156,8 +156,8 @@ protected: QList dockWidgets() const; /** - * This finction forces the dock container widget to update handles of splitters - * based on resize modes of dock widgets aontained in the container. + * 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); diff --git a/src/DockManager.cpp b/src/DockManager.cpp index 503a122..55ef20f 100644 --- a/src/DockManager.cpp +++ b/src/DockManager.cpp @@ -831,31 +831,38 @@ void CDockManager::loadPerspectives(QSettings& Settings) Settings.endArray(); } -CDockWidget* CDockManager::centralWidget() + +//============================================================================ +CDockWidget* CDockManager::centralWidget() const { return d->CentralWidget; } -//============================================================================ -CDockAreaWidget* CDockManager::setCentralWidget(CDockWidget* widget, CDockWidget* oldCentralWidget, DockWidgetArea oldCentralWidgetArea) -{ - oldCentralWidget = d->CentralWidget; - if(oldCentralWidget) - { - addDockWidget(oldCentralWidgetArea, oldCentralWidget); - } - if(widget) - { - widget->setFeature(CDockWidget::DockWidgetClosable, false); - widget->setFeature(CDockWidget::DockWidgetMovable, false); - widget->setFeature(CDockWidget::DockWidgetFloatable, false); - d->CentralWidget = widget; - CDockAreaWidget* CentralArea = addDockWidget(CenterDockWidgetArea, widget); - CentralArea->setDockAreaFlag(CDockAreaWidget::eDockAreaFlag::HideSingleWidgetTitleBar, true); - return CentralArea; - } - return nullptr; +//============================================================================ +CDockAreaWidget* CDockManager::setCentralWidget(CDockWidget* widget) +{ + if (!widget) + { + d->CentralWidget = nullptr; + return nullptr; + } + + // Setting a new central widget is now allowed if there is alread a central + // widget + if (d->CentralWidget) + { + return nullptr; + } + + + widget->setFeature(CDockWidget::DockWidgetClosable, false); + widget->setFeature(CDockWidget::DockWidgetMovable, false); + widget->setFeature(CDockWidget::DockWidgetFloatable, false); + d->CentralWidget = widget; + CDockAreaWidget* CentralArea = addDockWidget(CenterDockWidgetArea, widget); + CentralArea->setDockAreaFlag(CDockAreaWidget::eDockAreaFlag::HideSingleWidgetTitleBar, true); + return CentralArea; } //============================================================================ diff --git a/src/DockManager.h b/src/DockManager.h index 07f735c..f56fc13 100644 --- a/src/DockManager.h +++ b/src/DockManager.h @@ -381,16 +381,23 @@ public: /** * This function returns managers central widget or nullptr if no central widget is set. */ - CDockWidget* centralWidget(); + CDockWidget* centralWidget() const; /** - * Adds dockwidget into the central area and marks it as central widget. + * 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. - * If a central widget does exist, it will be docked to oldCentralWidgetArea - * and returned in oldCentralWidget. + * 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 cok 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. + * \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. */ - CDockAreaWidget* setCentralWidget(CDockWidget* widget, CDockWidget* oldCentralWidget = nullptr, DockWidgetArea oldCentralWidgetArea = DockWidgetArea::RightDockWidgetArea); + CDockAreaWidget* setCentralWidget(CDockWidget* widget); /** * Adds a toggle view action to the the internal view menu. diff --git a/src/DockSplitter.cpp b/src/DockSplitter.cpp index 2be1f07..047f0f4 100644 --- a/src/DockSplitter.cpp +++ b/src/DockSplitter.cpp @@ -103,16 +103,16 @@ QWidget* CDockSplitter::lastWidget() const } //============================================================================ -bool CDockSplitter::resizeWithContainer() +bool CDockSplitter::isResizingWithContainer() const { - QList areas = findChildren(); - - for(int i=0; i()) { - CDockAreaWidget* area = areas.at(i); if(area->isCentralWidgetArea()) + { return true; + } } + return false; } diff --git a/src/DockSplitter.h b/src/DockSplitter.h index 9dacdf8..1cc766c 100644 --- a/src/DockSplitter.h +++ b/src/DockSplitter.h @@ -75,7 +75,7 @@ public: /** * Returns true if the splitter contains central widget of dock manager. */ - bool resizeWithContainer(); + bool isResizingWithContainer() const; }; // class CDockSplitter } // namespace ads diff --git a/src/DockWidget.cpp b/src/DockWidget.cpp index 22b282b..0893e2d 100644 --- a/src/DockWidget.cpp +++ b/src/DockWidget.cpp @@ -463,7 +463,8 @@ void CDockWidget::setMinimumSizeHintMode(eMinimumSizeHintMode Mode) } -bool CDockWidget::isCentralWidget() +//============================================================================ +bool CDockWidget::isCentralWidget() const { return dockManager()->centralWidget() == this; } diff --git a/src/DockWidget.h b/src/DockWidget.h index b574fa4..b5fb5a1 100644 --- a/src/DockWidget.h +++ b/src/DockWidget.h @@ -361,9 +361,9 @@ public: void setMinimumSizeHintMode(eMinimumSizeHintMode Mode); /** - * Returns true if the dock wisget is set as central widget of it's dock manager + * Returns true if the dock widget is set as central widget of it's dock manager */ - bool isCentralWidget(); + bool isCentralWidget() const; /** * Sets the dock widget icon that is shown in tabs and in toggle view From 08a8cee1c6f7770bfec9970061535167262cb31a Mon Sep 17 00:00:00 2001 From: Uwe Kindler Date: Mon, 24 Aug 2020 11:14:58 +0200 Subject: [PATCH 5/5] Updated centralwidget test --- examples/CMakeLists.txt | 2 +- examples/centralWidget/mainwindow.cpp | 226 ------------------ .../CMakeLists.txt | 0 .../centralwidget.pro} | 0 .../{centralWidget => centralwidget}/main.cpp | 3 +- examples/centralwidget/mainwindow.cpp | 83 +++++++ .../mainwindow.h | 0 .../mainwindow.ui | 6 + 8 files changed, 91 insertions(+), 229 deletions(-) delete mode 100644 examples/centralWidget/mainwindow.cpp rename examples/{centralWidget => centralwidget}/CMakeLists.txt (100%) rename examples/{centralWidget/centralWidget.pro => centralwidget/centralwidget.pro} (100%) rename examples/{centralWidget => centralwidget}/main.cpp (85%) create mode 100644 examples/centralwidget/mainwindow.cpp rename examples/{centralWidget => centralwidget}/mainwindow.h (100%) rename examples/{centralWidget => centralwidget}/mainwindow.ui (80%) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 3b24252..32ca8c4 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -3,4 +3,4 @@ project(QtADSExamples LANGUAGES CXX VERSION ${VERSION_SHORT}) add_subdirectory(simple) add_subdirectory(sidebar) add_subdirectory(deleteonclose) -add_subdirectory(centralWidget) \ No newline at end of file +add_subdirectory(centralwidget) \ No newline at end of file diff --git a/examples/centralWidget/mainwindow.cpp b/examples/centralWidget/mainwindow.cpp deleted file mode 100644 index 285de5b..0000000 --- a/examples/centralWidget/mainwindow.cpp +++ /dev/null @@ -1,226 +0,0 @@ -#include "mainwindow.h" -#include "ui_mainwindow.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "DockAreaWidget.h" -#include "DockAreaTitleBar.h" -#include "DockAreaTabBar.h" -#include "FloatingDockContainer.h" -#include "DockComponentsFactory.h" - -using namespace ads; - -const QString CMainWindow::kTableTopLayout = "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "344 272 " - "" - "" - "" - "" - "" - "" - "" - "258 758 258 " - "" - "" - "" - "" - "52 621 52 " - "" - "" - ""; - -const QString CMainWindow::kTableBottomLayout = "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "258 758 258 " - "" - "" - "" - "" - "" - "" - "" - "621 52 52 " - "" - "" - ""; - -CMainWindow::CMainWindow(QWidget *parent) - : QMainWindow(parent) - , ui(new Ui::CMainWindow) -{ - ui->setupUi(this); - CDockManager::setConfigFlag(CDockManager::OpaqueSplitterResize, true); - CDockManager::setConfigFlag(CDockManager::XmlCompressionEnabled, false); - DockManager = new CDockManager(this); - QCalendarWidget* calendar = new QCalendarWidget(); - CDockWidget* CentralDockWidget = new CDockWidget("CentralWidget"); - CentralDockWidget->setWidget(calendar); - auto* CentralDockArea = DockManager->setCentralWidget(CentralDockWidget); - CentralDockArea->setAllowedAreas(DockWidgetArea::OuterDockAreas); - - QTreeView* fileTree = new QTreeView(); - fileTree->setFrameShape(QFrame::NoFrame); - QFileSystemModel* fileModel = new QFileSystemModel(fileTree); - fileModel->setRootPath(QDir::currentPath()); - fileTree->setModel(fileModel); - CDockWidget* DataDockWidget = new CDockWidget("File system"); - DataDockWidget->setWidget(fileTree); - DataDockWidget->resize(150, 250); - DataDockWidget->setMinimumSize(100, 250); - auto* fileArea = DockManager->addDockWidget(DockWidgetArea::LeftDockWidgetArea, DataDockWidget, CentralDockArea); - - QTableWidget* table = new QTableWidget(); - table->setColumnCount(3); - table->setRowCount(10); - CDockWidget* TableDockWidget = new CDockWidget("Table"); - TableDockWidget->setWidget(table); - TableDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget); - TableDockWidget->resize(250, 150); - TableDockWidget->setMinimumSize(200,150); - DockManager->addDockWidget(DockWidgetArea::BottomDockWidgetArea, TableDockWidget, fileArea); - - QTableWidget* propertiesTable = new QTableWidget(); - table->setColumnCount(3); - table->setRowCount(10); - CDockWidget* PropertiesDockWidget = new CDockWidget("Properties"); - PropertiesDockWidget->setWidget(propertiesTable); - PropertiesDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget); - PropertiesDockWidget->resize(250, 150); - PropertiesDockWidget->setMinimumSize(200,150); - DockManager->addDockWidget(DockWidgetArea::RightDockWidgetArea, PropertiesDockWidget, CentralDockArea); - - QWidget* timeLineWidget = new QWidget(); - QHBoxLayout* timelineLayout = new QHBoxLayout(timeLineWidget); - QRadioButton* radioDockTop = new QRadioButton("Top", timeLineWidget); - QRadioButton* radioDockBottom = new QRadioButton("Bottom", timeLineWidget); - radioDockTop->setChecked(true); - timelineLayout->addWidget(new QLabel("Test Widget.")); - timelineLayout->addStretch(1); - timelineLayout->addWidget(new QLabel("Apply predefined perspective: ", this)); - timelineLayout->addWidget(radioDockTop); - timelineLayout->addWidget(radioDockBottom); - TimelineDockWidget = new CDockWidget("Timeline"); - TimelineDockWidget->setWidget(timeLineWidget); - TimelineDockWidget->setFeature(CDockWidget::DockWidgetClosable, false); - TimelineDockWidget->setFeature(CDockWidget::DockWidgetMovable, false); - TimelineDockWidget->setFeature(CDockWidget::DockWidgetFloatable, false); - TimelineDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget); - TimelineDockWidget->setMinimumSize(QSize(50, 50)); - auto *TimelineDockArea = DockManager->addDockWidget(DockWidgetArea::TopDockWidgetArea, TimelineDockWidget); - TimelineDockArea->setDockAreaFlag(CDockAreaWidget::eDockAreaFlag::HideSingleWidgetTitleBar, true); - TimelineDockArea->setAllowedAreas(DockWidgetArea::OuterDockAreas); - connect(radioDockTop, &QRadioButton::toggled, [this](bool checked){ - bool ok = true; - if(!checked) - { - ok = DockManager->restoreState(kTableBottomLayout.toUtf8()); - } - else - { - ok = DockManager->restoreState(kTableTopLayout.toUtf8()); - } - if(!ok) - { - QMessageBox msgBox; - msgBox.setText("Failed to apply perspective!"); - msgBox.exec(); - } - }); - - QWidget* statusWidget = new QWidget(); - QHBoxLayout* statusLayout = new QHBoxLayout(statusWidget); - statusLayout->setSpacing(10); - statusLayout->addWidget(new QLabel("Status Bar")); - QPushButton* OpenPerspectiveButton = new QPushButton("Open Perspective", statusWidget); - connect(OpenPerspectiveButton, &QPushButton::clicked, [this](){ - QString PerspectiveName = QFileDialog::getOpenFileName(this, "Open Perspective", "", "Perspective files (*.xml)"); - if (PerspectiveName.isEmpty()) - { - return; - } - - QFile stateFile(PerspectiveName); - stateFile.open(QIODevice::ReadOnly); - QByteArray state = stateFile.readAll(); - stateFile.close(); - if(!DockManager->restoreState(state)) - { - QMessageBox msgBox; - msgBox.setText("Failed to apply perspective " + stateFile.fileName()); - msgBox.exec(); - } - }); - QPushButton* SavePerspectiveButton = new QPushButton("Create Perspective", statusWidget); - connect(SavePerspectiveButton, &QPushButton::clicked, [this](){ - QString PerspectiveName = QInputDialog::getText(this, "Save Perspective", "Enter unique name:"); - if (PerspectiveName.isEmpty()) - { - return; - } - - QByteArray state = DockManager->saveState(); - QFile stateFile(PerspectiveName + ".xml"); - stateFile.open(QIODevice::WriteOnly); - stateFile.write(state); - stateFile.close(); - }); - statusLayout->addWidget(OpenPerspectiveButton); - statusLayout->addWidget(SavePerspectiveButton); - statusLayout->addStretch(1); - CDockWidget* StatusDockWidget = new CDockWidget("Status"); - StatusDockWidget->setWidget(statusWidget); - StatusDockWidget->setFeature(CDockWidget::DockWidgetClosable, false); - StatusDockWidget->setFeature(CDockWidget::DockWidgetMovable, false); - StatusDockWidget->setFeature(CDockWidget::DockWidgetFloatable, false); - StatusDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget); - StatusDockWidget->setMinimumSize(QSize(50, 50)); - StatusDockArea = DockManager->addDockWidget(DockWidgetArea::BottomDockWidgetArea, StatusDockWidget); - StatusDockArea->setAllowedAreas(DockWidgetArea::OuterDockAreas); - StatusDockArea->setDockAreaFlag(ads::CDockAreaWidget::eDockAreaFlag::HideSingleWidgetTitleBar, true); -} - -CMainWindow::~CMainWindow() -{ - delete ui; -} - diff --git a/examples/centralWidget/CMakeLists.txt b/examples/centralwidget/CMakeLists.txt similarity index 100% rename from examples/centralWidget/CMakeLists.txt rename to examples/centralwidget/CMakeLists.txt diff --git a/examples/centralWidget/centralWidget.pro b/examples/centralwidget/centralwidget.pro similarity index 100% rename from examples/centralWidget/centralWidget.pro rename to examples/centralwidget/centralwidget.pro diff --git a/examples/centralWidget/main.cpp b/examples/centralwidget/main.cpp similarity index 85% rename from examples/centralWidget/main.cpp rename to examples/centralwidget/main.cpp index 3f073f3..fa4c4fd 100644 --- a/examples/centralWidget/main.cpp +++ b/examples/centralwidget/main.cpp @@ -1,5 +1,4 @@ -#include "mainwindow.h" - +#include #include int main(int argc, char *argv[]) diff --git a/examples/centralwidget/mainwindow.cpp b/examples/centralwidget/mainwindow.cpp new file mode 100644 index 0000000..f4c7858 --- /dev/null +++ b/examples/centralwidget/mainwindow.cpp @@ -0,0 +1,83 @@ +#include "mainwindow.h" + +#include "ui_mainwindow.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "DockAreaWidget.h" +#include "DockAreaTitleBar.h" +#include "DockAreaTabBar.h" +#include "FloatingDockContainer.h" +#include "DockComponentsFactory.h" + +using namespace ads; + +CMainWindow::CMainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::CMainWindow) +{ + ui->setupUi(this); + CDockManager::setConfigFlag(CDockManager::OpaqueSplitterResize, true); + CDockManager::setConfigFlag(CDockManager::XmlCompressionEnabled, false); + DockManager = new CDockManager(this); + + // Set central widget + QCalendarWidget* calendar = new QCalendarWidget(); + CDockWidget* CentralDockWidget = new CDockWidget("CentralWidget"); + CentralDockWidget->setWidget(calendar); + auto* CentralDockArea = DockManager->setCentralWidget(CentralDockWidget); + CentralDockArea->setAllowedAreas(DockWidgetArea::OuterDockAreas); + + // create other dock widgets + QTreeView* fileTree = new QTreeView(); + fileTree->setFrameShape(QFrame::NoFrame); + QFileSystemModel* fileModel = new QFileSystemModel(fileTree); + fileModel->setRootPath(QDir::currentPath()); + fileTree->setModel(fileModel); + CDockWidget* DataDockWidget = new CDockWidget("File system"); + DataDockWidget->setWidget(fileTree); + DataDockWidget->resize(150, 250); + DataDockWidget->setMinimumSize(100, 250); + auto* fileArea = DockManager->addDockWidget(DockWidgetArea::LeftDockWidgetArea, DataDockWidget, CentralDockArea); + ui->menuView->addAction(DataDockWidget->toggleViewAction()); + + QTableWidget* table = new QTableWidget(); + table->setColumnCount(3); + table->setRowCount(10); + CDockWidget* TableDockWidget = new CDockWidget("Table"); + TableDockWidget->setWidget(table); + TableDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget); + TableDockWidget->resize(250, 150); + TableDockWidget->setMinimumSize(200,150); + DockManager->addDockWidget(DockWidgetArea::BottomDockWidgetArea, TableDockWidget, fileArea); + ui->menuView->addAction(TableDockWidget->toggleViewAction()); + + QTableWidget* propertiesTable = new QTableWidget(); + table->setColumnCount(3); + table->setRowCount(10); + CDockWidget* PropertiesDockWidget = new CDockWidget("Properties"); + PropertiesDockWidget->setWidget(propertiesTable); + PropertiesDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget); + PropertiesDockWidget->resize(250, 150); + PropertiesDockWidget->setMinimumSize(200,150); + DockManager->addDockWidget(DockWidgetArea::RightDockWidgetArea, PropertiesDockWidget, CentralDockArea); + ui->menuView->addAction(PropertiesDockWidget->toggleViewAction()); +} + +CMainWindow::~CMainWindow() +{ + delete ui; +} + diff --git a/examples/centralWidget/mainwindow.h b/examples/centralwidget/mainwindow.h similarity index 100% rename from examples/centralWidget/mainwindow.h rename to examples/centralwidget/mainwindow.h diff --git a/examples/centralWidget/mainwindow.ui b/examples/centralwidget/mainwindow.ui similarity index 80% rename from examples/centralWidget/mainwindow.ui rename to examples/centralwidget/mainwindow.ui index 73ae7b1..135fdd1 100644 --- a/examples/centralWidget/mainwindow.ui +++ b/examples/centralwidget/mainwindow.ui @@ -23,6 +23,12 @@ 21 + + + View + + +