diff --git a/demo/ImageViewer.cpp b/demo/ImageViewer.cpp new file mode 100644 index 000000000..43c7d151b --- /dev/null +++ b/demo/ImageViewer.cpp @@ -0,0 +1,281 @@ +//============================================================================ +/// \file ImageViewer.cpp +/// \author Uwe Kindler +/// \date 04.11.2022 +/// \brief Implementation of CImageViewer +//============================================================================ + +//============================================================================ +// INCLUDES +//============================================================================ +#include "ImageViewer.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "RenderWidget.h" + +/** + * Private image viewer data + */ +struct ImageViewerPrivate +{ + CImageViewer* _this; + CRenderWidget* RenderWidget;///< renders the image to screen + bool AutoFit;///< automatically fit image to window size on resize events + QSize ImageSize;///< stores the image size to detect image size changes + QPoint MouseMoveStartPos;///< for calculation of mouse move vector + QLabel* ScalingLabel;///< label displays scaling factor + QList OverlayTools;///< list of tool widget to overlay + + ImageViewerPrivate(CImageViewer* _public) : _this(_public) {} +}; + + + +//============================================================================ +CImageViewer::CImageViewer(QWidget *parent) + : Super(parent), + d(new ImageViewerPrivate(this)) +{ + d->AutoFit = true; + d->RenderWidget = new CRenderWidget(this); + + this->setBackgroundRole(QPalette::Light); + this->setAlignment(Qt::AlignCenter); + this->setWidget(d->RenderWidget); + this->createActions(); + this->setMouseTracking(false); // only produce mouse move events if mouse button pressed +} + + +//============================================================================ +CImageViewer::~CImageViewer() +{ + delete d; +} + + +//============================================================================ +bool CImageViewer::loadFile(const QString& fileName) +{ + QImageReader reader(fileName); + reader.setAutoTransform(true); + const QImage newImage = reader.read(); + if (newImage.isNull()) + { + QMessageBox::information(this, QGuiApplication::applicationDisplayName(), + tr("Cannot load %1: %2") + .arg(QDir::toNativeSeparators(fileName), reader.errorString())); + return false; + } + + setImage(newImage); + setWindowFilePath(fileName); + return true; +} + + +//=========================================================================== +void CImageViewer::setImage(const QImage &newImage) +{ + d->RenderWidget->showImage(newImage); + this->adjustDisplaySize(newImage); +} + + +//============================================================================ +void CImageViewer::adjustDisplaySize(const QImage& Image) +{ + if (d->ImageSize == Image.size()) + { + return; + } + d->ImageSize = Image.size(); + if (d->AutoFit) + { + this->fitToWindow(); + } +} + + +//=========================================================================== +static void initializeImageFileDialog(QFileDialog &dialog, QFileDialog::AcceptMode acceptMode) +{ + static bool firstDialog = true; + + if (firstDialog) { + firstDialog = false; + const QStringList picturesLocations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation); + dialog.setDirectory(picturesLocations.isEmpty() ? QDir::currentPath() : picturesLocations.last()); + } + + QStringList mimeTypeFilters; + const QByteArrayList supportedMimeTypes = acceptMode == QFileDialog::AcceptOpen + ? QImageReader::supportedMimeTypes() : QImageWriter::supportedMimeTypes(); + for (const QByteArray &mimeTypeName : supportedMimeTypes) + mimeTypeFilters.append(mimeTypeName); + mimeTypeFilters.sort(); + dialog.setMimeTypeFilters(mimeTypeFilters); + dialog.selectMimeTypeFilter("image/jpeg"); + if (acceptMode == QFileDialog::AcceptSave) + dialog.setDefaultSuffix("jpg"); +} + + +//=========================================================================== +void CImageViewer::open() +{ + QFileDialog dialog(this, tr("Open File")); + initializeImageFileDialog(dialog, QFileDialog::AcceptOpen); + + while (dialog.exec() == QDialog::Accepted && !loadFile(dialog.selectedFiles().first())) {} +} + + +//=========================================================================== +void CImageViewer::createActions() +{ + QAction* a; + a = new QAction(tr("&Open...")); + a->setIcon(QIcon(":/adsdemo/images/perm_media.svg")); + connect(a, &QAction::triggered, this, &CImageViewer::open); + a->setShortcut(QKeySequence::Open);; + this->addAction(a); + + a = new QAction(tr("Fit on Screen")); + a->setIcon(QIcon(":/adsdemo/images/zoom_out_map.svg")); + connect(a, &QAction::triggered, this, &CImageViewer::fitToWindow); + this->addAction(a); + + a = new QAction(tr("Actual Pixels")); + a->setIcon(QIcon(":/adsdemo/images/find_in_page.svg")); + connect(a, &QAction::triggered, this, &CImageViewer::normalSize); + this->addAction(a); + + a = new QAction(this); + a->setSeparator(true); + this->addAction(a); + + a = new QAction(tr("Zoom In (25%)")); + a->setIcon(QIcon(":/adsdemo/images/zoom_in.svg")); + connect(a, &QAction::triggered, this, &CImageViewer::zoomIn); + this->addAction(a); + + a = new QAction(tr("Zoom Out (25%)")); + a->setIcon(QIcon(":/adsdemo/images/zoom_out.svg")); + connect(a, &QAction::triggered, this, &CImageViewer::zoomOut); + this->addAction(a); + + this->setContextMenuPolicy(Qt::ActionsContextMenu); +} + + +//=========================================================================== +void CImageViewer::zoomIn() +{ + d->AutoFit = false; + d->RenderWidget->zoomIn(); +} + + +//=========================================================================== +void CImageViewer::zoomOut() +{ + d->AutoFit = false; + d->RenderWidget->zoomOut(); +} + + +//=========================================================================== +void CImageViewer::normalSize() +{ + d->AutoFit = false; + d->RenderWidget->normalSize(); +} + + +//=========================================================================== +void CImageViewer::fitToWindow() +{ + d->AutoFit = true; + d->RenderWidget->scaleToSize(this->maximumViewportSize()); +} + + +//============================================================================ +void CImageViewer::resizeEvent(QResizeEvent* ResizeEvent) +{ + Super::resizeEvent(ResizeEvent); + if (d->AutoFit) + { + this->fitToWindow(); + } +} + + +//============================================================================ +void CImageViewer::mousePressEvent(QMouseEvent* Event) +{ + d->RenderWidget->setCursor(Qt::ClosedHandCursor); + d->MouseMoveStartPos = Event->pos(); + Super::mousePressEvent(Event); +} + + +//============================================================================ +void CImageViewer::mouseReleaseEvent(QMouseEvent* Event) +{ + d->RenderWidget->setCursor(Qt::OpenHandCursor); + Super::mouseReleaseEvent(Event); +} + + +//============================================================================ +void CImageViewer::mouseMoveEvent(QMouseEvent* Event) +{ + QPoint MoveVector = Event->pos() - d->MouseMoveStartPos; + d->MouseMoveStartPos = Event->pos(); + horizontalScrollBar()->setValue(horizontalScrollBar()->value() + - MoveVector.x()); + verticalScrollBar()->setValue(verticalScrollBar()->value() - MoveVector.y()); +} + + +//============================================================================ +void CImageViewer::wheelEvent(QWheelEvent* Event) +{ + double numDegrees = Event->delta() / 8; + double numSteps = numDegrees / 15; + d->AutoFit = false; + double Zoom; + if (numSteps < 0) + { + Zoom = pow(0.9, 0 - numSteps); + } + else + { + Zoom = pow(1.10, numSteps); + } + d->RenderWidget->zoomByValue(Zoom); +} + +#include "moc_ImageViewer.cpp" +//--------------------------------------------------------------------------- +// EOF ImageViewer.cpp diff --git a/demo/ImageViewer.h b/demo/ImageViewer.h new file mode 100644 index 000000000..37bc8cba7 --- /dev/null +++ b/demo/ImageViewer.h @@ -0,0 +1,88 @@ +#ifndef ImageViewerH +#define ImageViewerH +//============================================================================ +/// \file ImageViewer.h +/// \author Uwe Kindler +/// \date 04.11.2022 +/// \brief Declaration of CImageViewer +//============================================================================ + +//============================================================================ +// INCLUDES +//============================================================================ +#include + +QT_BEGIN_NAMESPACE +class QLabel; +QT_END_NAMESPACE + + +struct ImageViewerPrivate; + +/** + * Tiny simple image viewer for showing images in demo + */ +class CImageViewer : public QScrollArea +{ + Q_OBJECT +public: + using Super = QScrollArea; + + explicit CImageViewer(QWidget *parent = nullptr); + virtual ~CImageViewer(); + + bool loadFile(const QString& Filename); + void setImage(const QImage &newImage); + +public Q_SLOTS: + void open(); + void zoomIn(); + void zoomOut(); + void normalSize(); + void fitToWindow(); + +protected: + /** + * @brief Reimplemented from QScrollArea to adjust image scaling if m_AutoFit is + * true. + */ + virtual void resizeEvent(QResizeEvent* ResizeEvent); + + /** + * @brief Handle mouse press events. + */ + virtual void mousePressEvent(QMouseEvent* Event); + + /** + * @brief Handles mouse release events. + */ + virtual void mouseReleaseEvent(QMouseEvent* Event); + + /** + * @brief Handle mouse move events. + */ + virtual void mouseMoveEvent(QMouseEvent* Event); + + /** + * @brief Use mouse wheel to change scaling of the image. + */ + virtual void wheelEvent(QWheelEvent* Event); + +private: + /** + * @brief Create the wiget actions. + */ + void createActions(); + + /** + * @brief Adjust size of render widget in case of image size change. + * @param[in] Image The new image that may have a different image size. + */ + void adjustDisplaySize(const QImage& Image); + + ImageViewerPrivate* d; + friend ImageViewerPrivate; +}; + +//--------------------------------------------------------------------------- +#endif // ImageViewerH diff --git a/demo/MainWindow.cpp b/demo/MainWindow.cpp index 6d8d0c950..07dea017f 100644 --- a/demo/MainWindow.cpp +++ b/demo/MainWindow.cpp @@ -60,6 +60,7 @@ #include #include #include +#include #ifdef Q_OS_WIN @@ -80,6 +81,7 @@ #include "DockComponentsFactory.h" #include "StatusDialog.h" #include "DockSplitter.h" +#include "ImageViewer.h" /** @@ -216,9 +218,11 @@ struct MainWindowPrivate QFileSystemModel* m = new QFileSystemModel(w); m->setRootPath(QDir::currentPath()); w->setModel(m); + w->setRootIndex(m->index(QDir::currentPath())); ads::CDockWidget* DockWidget = new ads::CDockWidget(QString("Filesystem %1") .arg(FileSystemCount++)); DockWidget->setWidget(w); + DockWidget->setIcon(svgIcon(":/adsdemo/images/folder_open.svg")); ui.menuView->addAction(DockWidget->toggleViewAction()); // We disable focus to test focus highlighting if the dock widget content // does not support focus @@ -279,6 +283,7 @@ struct MainWindowPrivate ads::CDockWidget* DockWidget = new ads::CDockWidget(QString("Label %1").arg(LabelCount++)); DockWidget->setWidget(l); + DockWidget->setIcon(svgIcon(":/adsdemo/images/font_download.svg")); ui.menuView->addAction(DockWidget->toggleViewAction()); return DockWidget; } @@ -316,6 +321,35 @@ struct MainWindowPrivate return DockWidget; } + /** + * Creates a simply image viewr + */ + ads::CDockWidget* createImageViewer() + { + static int ImageViewerCount = 0; + auto w = new CImageViewer(); + auto ImageIndex = QRandomGenerator::global()->bounded(4); + auto FileName = ":adsdemo/images/ads_logo.svg"; + + // Pick a random image from a number of images + switch (ImageIndex) + { + case 0: FileName = ":adsdemo/images/ads_tile_blue.svg"; break; + case 1: FileName = ":adsdemo/images/ads_tile_blue_light.svg"; break; + case 2: FileName = ":adsdemo/images/ads_tile_green.svg"; break; + case 3: FileName = ":adsdemo/images/ads_tile_orange.svg"; break; + } + + auto Result = w->loadFile(FileName); + qDebug() << "loadFile result: " << Result; + ads::CDockWidget* DockWidget = new ads::CDockWidget(QString("Image Viewer %1").arg(ImageViewerCount++)); + DockWidget->setIcon(svgIcon(":/adsdemo/images/photo.svg")); + DockWidget->setWidget(w,ads:: CDockWidget::ForceNoScrollArea); + auto ToolBar = DockWidget->createDefaultToolBar(); + ToolBar->addActions(w->actions()); + return DockWidget; + } + /** * Create a table widget */ @@ -494,7 +528,10 @@ void MainWindowPrivate::createContent() #ifdef Q_OS_WIN #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) - if (!ads::CDockManager::testConfigFlag(ads::CDockManager::OpaqueUndocking)) + // ActiveX widget only works without OpaqueUndocking and without + // auto hide feature + if (!ads::CDockManager::testConfigFlag(ads::CDockManager::OpaqueUndocking) + && !ads::CDockManager::testAutoHideConfigFlag(ads::CDockManager::AutoHideFeatureEnabled)) { DockManager->addDockWidget(ads::CenterDockWidgetArea, createActiveXWidget(), RighDockArea); } @@ -506,6 +543,10 @@ void MainWindowPrivate::createContent() _this->connect(DockWidget, SIGNAL(viewToggled(bool)), SLOT(onViewToggled(bool))); _this->connect(DockWidget, SIGNAL(visibilityChanged(bool)), SLOT(onViewVisibilityChanged(bool))); } + + // Create image viewer + DockWidget = createImageViewer(); + DockManager->addDockWidget(ads::LeftDockWidgetArea, DockWidget); } @@ -550,6 +591,22 @@ void MainWindowPrivate::createActions() _this->connect(a, SIGNAL(triggered()), SLOT(createTable())); ui.menuTests->addAction(a); + a = ui.toolBar->addAction("Create Image Viewer"); + auto ToolButton = qobject_cast(ui.toolBar->widgetForAction(a)); + ToolButton->setPopupMode(QToolButton::InstantPopup); + a->setToolTip("Creates floating, docked or pinned image viewer"); + a->setIcon(svgIcon(":/adsdemo/images/panorama.svg")); + ui.menuTests->addAction(a); + auto Menu = new QMenu(); + ToolButton->setMenu(Menu); + a = Menu->addAction("Floating Image Viewer"); + _this->connect(a, SIGNAL(triggered()), SLOT(createImageViewer())); + a = Menu->addAction("Docked Image Viewer"); + _this->connect(a, SIGNAL(triggered()), SLOT(createImageViewer())); + a = Menu->addAction("Pinned Image Viewer"); + _this->connect(a, SIGNAL(triggered()), SLOT(createImageViewer())); + + ui.menuTests->addSeparator(); a = ui.menuTests->addAction("Show Status Dialog"); _this->connect(a, SIGNAL(triggered()), SLOT(showStatusDialog())); @@ -557,6 +614,12 @@ void MainWindowPrivate::createActions() a = ui.menuTests->addAction("Toggle Label 0 Window Title"); _this->connect(a, SIGNAL(triggered()), SLOT(toggleDockWidgetWindowTitle())); ui.menuTests->addSeparator(); + + a = ui.toolBar->addAction("Apply VS Style"); + a->setToolTip("Applies a Visual Studio light style (visual_studio_light.css)." ); + a->setIcon(svgIcon(":/adsdemo/images/color_lens.svg")); + QObject::connect(a, &QAction::triggered, _this, &CMainWindow::applyVsStyle); + ui.menuTests->addAction(a); } @@ -646,11 +709,14 @@ CMainWindow::CMainWindow(QWidget *parent) : // uncomment the following line if you want a central widget in the main dock container (the dock manager) without a titlebar // If you enable this code, you can test it in the demo with the Calendar 0 // dock widget. - // CDockManager::setConfigFlag(CDockManager::HideSingleCentralWidgetTitleBar, true); + //CDockManager::setConfigFlag(CDockManager::HideSingleCentralWidgetTitleBar, true); // uncomment the following line to enable focus highlighting of the dock // widget that has the focus - CDockManager::setConfigFlag(CDockManager::FocusHighlighting, true); + CDockManager::setConfigFlag(CDockManager::FocusHighlighting, true); + + // uncomment if you would like to enable dock widget auto hiding + CDockManager::setAutoHideConfigFlags(CDockManager::DefaultAutoHideConfig); // uncomment if you would like to enable an equal distribution of the // available size of a splitter to all contained dock widgets @@ -739,13 +805,14 @@ void CMainWindow::savePerspective() //============================================================================ void CMainWindow::onViewToggled(bool Open) { + Q_UNUSED(Open); auto DockWidget = qobject_cast(sender()); if (!DockWidget) { return; } - qDebug() << DockWidget->objectName() << " viewToggled(" << Open << ")"; + //qDebug() << DockWidget->objectName() << " viewToggled(" << Open << ")"; } @@ -822,6 +889,20 @@ void CMainWindow::onEditorCloseRequested() } +//============================================================================ +void CMainWindow::onImageViewerCloseRequested() +{ + auto DockWidget = qobject_cast(sender()); + int Result = QMessageBox::question(this, "Close Image Viewer", QString("%1 " + "contains unsaved changes? Would you like to close it?") + .arg(DockWidget->windowTitle())); + if (QMessageBox::Yes == Result) + { + DockWidget->closeDockWidget(); + } +} + + //============================================================================ void CMainWindow::createTable() { @@ -856,3 +937,45 @@ void CMainWindow::toggleDockWidgetWindowTitle() d->WindowTitleTestDockWidget->setWindowTitle(Title); } + +//============================================================================ +void CMainWindow::applyVsStyle() +{ + QFile StyleSheetFile(":adsdemo/res/visual_studio_light.css"); + StyleSheetFile.open(QIODevice::ReadOnly); + QTextStream StyleSheetStream(&StyleSheetFile); + auto Stylesheet = StyleSheetStream.readAll(); + StyleSheetFile.close(); + d->DockManager->setStyleSheet(Stylesheet); +} + + +//============================================================================ +void CMainWindow::createImageViewer() +{ + QAction* a = qobject_cast(sender()); + qDebug() << "createImageViewer " << a->text(); + + auto DockWidget = d->createImageViewer(); + DockWidget->setFeature(ads::CDockWidget::DockWidgetDeleteOnClose, true); + DockWidget->setFeature(ads::CDockWidget::DockWidgetForceCloseWithArea, true); + DockWidget->setFeature(ads::CDockWidget::CustomCloseHandling, true); + DockWidget->resize(QSize(640, 480)); + connect(DockWidget, &ads::CDockWidget::closeRequested, this, + &CMainWindow::onImageViewerCloseRequested); + + if (a->text().startsWith("Floating")) + { + auto FloatingWidget = d->DockManager->addDockWidgetFloating(DockWidget); + FloatingWidget->move(QPoint(20, 20)); + } + else if (a->text().startsWith("Docked")) + { + d->DockManager->addDockWidget(ads::RightDockWidgetArea, DockWidget); + } + else if (a->text().startsWith("Pinned")) + { + d->DockManager->addAutoHideDockWidget(ads::SideBarLeft, DockWidget); + } +} + diff --git a/demo/MainWindow.h b/demo/MainWindow.h index cd0eca6b0..4afee1483 100644 --- a/demo/MainWindow.h +++ b/demo/MainWindow.h @@ -63,8 +63,11 @@ private slots: void createEditor(); void createTable(); void onEditorCloseRequested(); + void onImageViewerCloseRequested(); void showStatusDialog(); void toggleDockWidgetWindowTitle(); + void applyVsStyle(); + void createImageViewer(); }; #endif // MAINWINDOW_H diff --git a/demo/RenderWidget.cpp b/demo/RenderWidget.cpp new file mode 100644 index 000000000..c168e963a --- /dev/null +++ b/demo/RenderWidget.cpp @@ -0,0 +1,108 @@ +//============================================================================ +/// \file RenderWidget.cpp +/// \author Uwe Kindler +/// \date 04.11.2022 +/// \brief Implementation of CRenderWidget +//============================================================================ + + +//============================================================================ +// INCLUDES +//============================================================================ +#include "RenderWidget.h" + +#include +#include + + +//=========================================================================== +CRenderWidget::CRenderWidget(QWidget* Parent) : + QWidget(Parent), m_ScaleFactor(1) +{ + this->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); + this->setCursor(Qt::OpenHandCursor); +} + +//=========================================================================== +CRenderWidget::~CRenderWidget() +{ + +} + +//=========================================================================== +void CRenderWidget::showImage(const QImage& Image) +{ + m_Image = QPixmap::fromImage(Image); + this->adjustWidgetSize(); + this->repaint(); +} + +//=========================================================================== +void CRenderWidget::paintEvent(QPaintEvent* Event) +{ + Q_UNUSED(Event); + QPainter Painter(this); + Painter.setRenderHint(QPainter::SmoothPixmapTransform, true); + Painter.setRenderHint(QPainter::Antialiasing, true); + Painter.scale(m_ScaleFactor, m_ScaleFactor); + Painter.drawPixmap(QPoint(0, 0), m_Image); +} + +//============================================================================ +void CRenderWidget::zoomIn() +{ + scaleImage(1.25); +} + +//============================================================================ +void CRenderWidget::zoomOut() +{ + scaleImage(0.8); +} + +//============================================================================ +void CRenderWidget::zoomByValue(double ZoomValue) +{ + scaleImage(ZoomValue); +} + +//============================================================================ +void CRenderWidget::normalSize() +{ + m_ScaleFactor = 1; + this->adjustWidgetSize(); +} + +//============================================================================ +void CRenderWidget::scaleImage(double ScaleFactor) +{ + m_ScaleFactor *= ScaleFactor; + this->adjustWidgetSize(); +} + +//============================================================================ +void CRenderWidget::adjustWidgetSize() +{ + QSize ScaledImageSize = m_Image.size() * m_ScaleFactor; + if (ScaledImageSize != this->size()) + { + this->setFixedSize(ScaledImageSize); + } +} + +//============================================================================ +void CRenderWidget::scaleToSize(const QSize& TargetSize) +{ + if (m_Image.isNull()) + { + return; + } + double ScaleFactorH = (double) TargetSize.width() / m_Image.size().width(); + double ScaleFactorV = (double) TargetSize.height() + / m_Image.size().height(); + m_ScaleFactor = (ScaleFactorH < ScaleFactorV) ? ScaleFactorH : ScaleFactorV; + this->adjustWidgetSize(); +} + +//--------------------------------------------------------------------------- +// EOF RenderWidget.cpp diff --git a/demo/RenderWidget.h b/demo/RenderWidget.h new file mode 100644 index 000000000..81fedf145 --- /dev/null +++ b/demo/RenderWidget.h @@ -0,0 +1,111 @@ +#ifndef RenderWidgetH +#define RenderWidgetH +//============================================================================ +/// \file RenderWidget.h +/// \author Uwe Kindler +/// \date 04.11.2022 +/// \brief Declaration of CRenderWidget +//============================================================================ + +//============================================================================ +// INCLUDES +//============================================================================ +#include +#include + +//============================================================================ +// FORWARD DECLARATIONS +//============================================================================ +class QImage; + + +/** + * @brief Widget for fast display of images (i.e. for video capture devices) + */ +class CRenderWidget : public QWidget +{ + Q_OBJECT +private: + QPixmap m_Image; + double m_ScaleFactor; + +protected: + /** + * @brief Reimplemented paint event method showing actual image. + */ + void paintEvent(QPaintEvent* PaintEvent); + + /** + * @brief Change scale factor + */ + void scaleImage(double ScaleFactor); + + /** + * @brief Adjust widget size to size of image. + */ + void adjustWidgetSize(); + +public: + /** + * Constructor + * @param[in] Parent Parent widget. + */ + CRenderWidget(QWidget* Parent); + + /** + * Destructor + */ + virtual ~CRenderWidget(); + +signals: + /** + * @brief Signalize change of captured image size. + * @param ImageSize New image size. + */ + void imageSizeChanged(const QSize& ImageSize); + +public slots: + /** + * @brief Show new image in render widget. + */ + void showImage(const QImage& Image); + + /** + * @brief Zoom into the scene. + * This function decreases the scaling factor by setting it to the previous + * value in internal scaling list. + * @brief Steps The number of steps to zoom in. One step is 25%. + */ + void zoomIn(); + + /** + * @brief Zoom out of the scene. + * This function decreases the scaling factor by setting it to the next + * value in internal scaling list. + * @brief Steps The number of steps to zoom out. One step is 25%. + */ + void zoomOut(); + + /** + * @brief Change zoom by zoom value. + * @param[in] ZoomValue This is the zoom value to apply. A value of 1 + * means no change a value > 1 increases the image (i.e. 1.25 would increase + * the image by 25%) and a value of < 1 decreases the image size (i.e. + * a value of 0.8 would decrease the image size by 25%). + */ + void zoomByValue(double ZoomValue); + + /** + * @brief Resets the actual scaling to 1 and display the image with its + * actual pixel size. + */ + void normalSize(); + + /** + * @brief Scales the wiget and its content image to the given TargetSize + */ + void scaleToSize(const QSize& TargetSize); +}; // class CRenderWidget + +//--------------------------------------------------------------------------- +#endif // RenderWidgetH diff --git a/demo/app.ico b/demo/app.ico new file mode 100644 index 000000000..3a57c647d Binary files /dev/null and b/demo/app.ico differ diff --git a/demo/app.rc b/demo/app.rc new file mode 100644 index 000000000..b8bb54af7 --- /dev/null +++ b/demo/app.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "app.ico" diff --git a/demo/demo.pro b/demo/demo.pro index 80ad86672..a94b5d12a 100644 --- a/demo/demo.pro +++ b/demo/demo.pro @@ -2,7 +2,7 @@ ADS_OUT_ROOT = $${OUT_PWD}/.. TARGET = AdvancedDockingSystemDemo DESTDIR = $${ADS_OUT_ROOT}/lib -QT += core gui widgets +QT += core gui widgets svg include(../ads.pri) @@ -15,19 +15,25 @@ lessThan(QT_MAJOR_VERSION, 6) { CONFIG += c++14 CONFIG += debug_and_release DEFINES += QT_DEPRECATED_WARNINGS +RC_FILE += app.rc adsBuildStatic { DEFINES += ADS_STATIC } -SOURCES += \ - main.cpp \ - MainWindow.cpp \ - StatusDialog.cpp HEADERS += \ MainWindow.h \ - StatusDialog.h + StatusDialog.h \ + ImageViewer.h \ + RenderWidget.h + +SOURCES += \ + main.cpp \ + MainWindow.cpp \ + StatusDialog.cpp \ + ImageViewer.cpp \ + RenderWidget.cpp FORMS += \ mainwindow.ui \ diff --git a/demo/demo.qrc b/demo/demo.qrc index cabf10958..a182763a3 100644 --- a/demo/demo.qrc +++ b/demo/demo.qrc @@ -17,5 +17,23 @@ images/create_floating_editor.svg images/create_floating_table.svg images/docked_editor.svg + res/visual_studio_light.css + images/color_lens.svg + images/ads_icon.svg + images/ads_logo.svg + images/find_in_page.svg + images/perm_media.svg + images/zoom_in.svg + images/zoom_out.svg + images/zoom_out_map.svg + images/ads_tile_blue.svg + images/ads_tile_blue_light.svg + images/ads_tile_green.svg + images/ads_tile_orange.svg + images/photo.svg + images/crop_original.svg + images/panorama.svg + images/ads_icon2.svg + images/font_download.svg diff --git a/demo/images/ads_icon.svg b/demo/images/ads_icon.svg new file mode 100644 index 000000000..ee0ac56cc --- /dev/null +++ b/demo/images/ads_icon.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/demo/images/ads_icon2.svg b/demo/images/ads_icon2.svg new file mode 100644 index 000000000..8ed092a33 --- /dev/null +++ b/demo/images/ads_icon2.svg @@ -0,0 +1,11 @@ + + + electric_iron icon - Licensed under Iconfu Standard License v1.0 (https://www.iconfu.com/iconfu_standard_license) - Incors GmbH + + + + + + + + diff --git a/demo/images/ads_logo.svg b/demo/images/ads_logo.svg new file mode 100644 index 000000000..da1b3b4de --- /dev/null +++ b/demo/images/ads_logo.svg @@ -0,0 +1,12 @@ + + + + Qt Advanced Docking + + + + + + + + diff --git a/demo/images/ads_tile_blue.svg b/demo/images/ads_tile_blue.svg new file mode 100644 index 000000000..91ec6dc1b --- /dev/null +++ b/demo/images/ads_tile_blue.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demo/images/ads_tile_blue_light.svg b/demo/images/ads_tile_blue_light.svg new file mode 100644 index 000000000..aa9bb833b --- /dev/null +++ b/demo/images/ads_tile_blue_light.svg @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demo/images/ads_tile_green.svg b/demo/images/ads_tile_green.svg new file mode 100644 index 000000000..ce8075af4 --- /dev/null +++ b/demo/images/ads_tile_green.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demo/images/ads_tile_orange.svg b/demo/images/ads_tile_orange.svg new file mode 100644 index 000000000..bb343dddf --- /dev/null +++ b/demo/images/ads_tile_orange.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demo/images/color_lens.svg b/demo/images/color_lens.svg new file mode 100644 index 000000000..456049712 --- /dev/null +++ b/demo/images/color_lens.svg @@ -0,0 +1,6 @@ + + color_lens icon - Licensed under Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0) - Created with Iconfu.com - Derivative work of Material icons (Copyright Google Inc.) + + + + \ No newline at end of file diff --git a/demo/images/crop_original.svg b/demo/images/crop_original.svg new file mode 100644 index 000000000..a4a6513a5 --- /dev/null +++ b/demo/images/crop_original.svg @@ -0,0 +1,6 @@ + + crop_original icon - Licensed under Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0) - Created with Iconfu.com - Derivative work of Material icons (Copyright Google Inc.) + + + + \ No newline at end of file diff --git a/demo/images/find_in_page.svg b/demo/images/find_in_page.svg new file mode 100644 index 000000000..ff4e34667 --- /dev/null +++ b/demo/images/find_in_page.svg @@ -0,0 +1,6 @@ + + find_in_page icon - Licensed under Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0) - Created with Iconfu.com - Derivative work of Material icons (Copyright Google Inc.) + + + + \ No newline at end of file diff --git a/demo/images/font_download.svg b/demo/images/font_download.svg new file mode 100644 index 000000000..ecf5582e4 --- /dev/null +++ b/demo/images/font_download.svg @@ -0,0 +1,6 @@ + + font_download icon - Licensed under Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0) - Created with Iconfu.com - Derivative work of Material icons (Copyright Google Inc.) + + + + \ No newline at end of file diff --git a/demo/images/panorama.svg b/demo/images/panorama.svg new file mode 100644 index 000000000..5716c3dfe --- /dev/null +++ b/demo/images/panorama.svg @@ -0,0 +1,6 @@ + + panorama icon - Licensed under Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0) - Created with Iconfu.com - Derivative work of Material icons (Copyright Google Inc.) + + + + \ No newline at end of file diff --git a/demo/images/perm_media.svg b/demo/images/perm_media.svg new file mode 100644 index 000000000..2dac12f97 --- /dev/null +++ b/demo/images/perm_media.svg @@ -0,0 +1,6 @@ + + perm_media icon - Licensed under Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0) - Created with Iconfu.com - Derivative work of Material icons (Copyright Google Inc.) + + + + \ No newline at end of file diff --git a/demo/images/photo.svg b/demo/images/photo.svg new file mode 100644 index 000000000..6da023429 --- /dev/null +++ b/demo/images/photo.svg @@ -0,0 +1,6 @@ + + photo icon - Licensed under Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0) - Created with Iconfu.com - Derivative work of Material icons (Copyright Google Inc.) + + + + \ No newline at end of file diff --git a/demo/images/zoom_in.svg b/demo/images/zoom_in.svg new file mode 100644 index 000000000..b65ca3c4f --- /dev/null +++ b/demo/images/zoom_in.svg @@ -0,0 +1,6 @@ + + zoom_in icon - Licensed under Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0) - Created with Iconfu.com - Derivative work of Material icons (Copyright Google Inc.) + + + + \ No newline at end of file diff --git a/demo/images/zoom_out.svg b/demo/images/zoom_out.svg new file mode 100644 index 000000000..d2912384d --- /dev/null +++ b/demo/images/zoom_out.svg @@ -0,0 +1,6 @@ + + zoom_out icon - Licensed under Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0) - Created with Iconfu.com - Derivative work of Material icons (Copyright Google Inc.) + + + + \ No newline at end of file diff --git a/demo/images/zoom_out_map.svg b/demo/images/zoom_out_map.svg new file mode 100644 index 000000000..dedc2d5f7 --- /dev/null +++ b/demo/images/zoom_out_map.svg @@ -0,0 +1,6 @@ + + zoom_out_map icon - Licensed under Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0) - Created with Iconfu.com - Derivative work of Material icons (Copyright Google Inc.) + + + + \ No newline at end of file diff --git a/demo/main.cpp b/demo/main.cpp index a9487867e..750c983f0 100644 --- a/demo/main.cpp +++ b/demo/main.cpp @@ -45,17 +45,18 @@ int main(int argc, char *argv[]) QApplication a(argc, argv); a.setApplicationName("Advanced Docking System Demo"); a.setQuitOnLastWindowClosed(true); - - QFile StyleSheetFile(":/adsdemo/app.css"); - StyleSheetFile.open(QIODevice::ReadOnly); - QTextStream StyleSheetStream(&StyleSheetFile); - a.setStyleSheet(StyleSheetStream.readAll()); - StyleSheetFile.close(); + a.setWindowIcon(QIcon(":/adsdemo/images/ads_icon2.svg")); qInstallMessageHandler(myMessageOutput); qDebug() << "Message handler test"; CMainWindow mw; mw.show(); + + QFile StyleSheetFile(":/adsdemo/app.css"); + StyleSheetFile.open(QIODevice::ReadOnly); + QTextStream StyleSheetStream(&StyleSheetFile); + a.setStyleSheet(StyleSheetStream.readAll()); + StyleSheetFile.close(); return a.exec(); } diff --git a/demo/res/visual_studio_light.css b/demo/res/visual_studio_light.css new file mode 100644 index 000000000..056163add --- /dev/null +++ b/demo/res/visual_studio_light.css @@ -0,0 +1,379 @@ + +/* + * Visual Studio like light theme + */ + +/***************************************************************************** + * CDockManager + *****************************************************************************/ +ads--CDockManager +{ + background: palette(window); +} + + + +/***************************************************************************** + * CDockContainerWidget + *****************************************************************************/ +ads--CDockContainerWidget { + background: palette(window); + padding: 2px; +} + + +/***************************************************************************** + * CDockAreaWidget + *****************************************************************************/ +ads--CDockAreaWidget { + background: palette(window); + /*border: 1px solid palette(dark);*/ +} + + +ads--CDockAreaTitleBar { + background: transparent; + border-bottom: 2px solid rgb(204, 204, 204); + padding-bottom: 0px; +} + + +ads--CTitleBarButton { + padding: 0px 0px; + background: transparent; + border: none; +} + +ads--CTitleBarButton:hover { + background: rgba(0, 0, 0, 24); +} + +ads--CTitleBarButton:pressed { + background: rgba(0, 0, 0, 48); +} + +QScrollArea#dockWidgetScrollArea { + padding: 0px; + border: none; +} + +#tabsMenuButton::menu-indicator { + image: none; +} + + +#dockAreaCloseButton { + qproperty-icon: url(:/ads/images/close-button.svg), + url(:/ads/images/close-button-disabled.svg) disabled; + qproperty-iconSize: 16px; +} + +#detachGroupButton { + qproperty-icon: url(:/ads/images/detach-button.svg), + url(:/ads/images/detach-button-disabled.svg) disabled; + qproperty-iconSize: 16px; +} + + +ads--CDockAreaWidget[focused="true"] ads--CDockAreaTitleBar { + border-bottom: 2px solid palette(highlight); +} + + +/***************************************************************************** + * CDockWidgetTab + *****************************************************************************/ +ads--CDockWidgetTab { + background: palette(window); + border: none; + padding: 0 0px; + qproperty-iconSize: 16px 16px;/* this is optional in case you would like to change icon size*/ +} + +ads--CDockWidgetTab[activeTab="true"] { + background: rgb(204, 204, 204); +} + +ads--CDockWidgetTab QLabel { + color: palette(foreground); +} + +ads--CDockWidgetTab[activeTab="true"] QLabel { + color: palette(foreground); +} + + +#tabCloseButton { + margin-top: 2px; + background: none; + border: none; + padding: 0px -2px; + qproperty-icon: url(:/ads/images/close-button.svg), + url(:/ads/images/close-button-disabled.svg) disabled; + qproperty-iconSize: 16px; +} + +#tabCloseButton:hover { + /*border: 1px solid rgba(0, 0, 0, 32);*/ + background: rgba(0, 0, 0, 24); +} + +#tabCloseButton:pressed { + background: rgba(0, 0, 0, 48); +} + + +/* Focus related styling */ +ads--CDockWidgetTab[focused="true"] { + background: palette(highlight); + border-color: palette(highlight); +} + +ads--CDockWidgetTab[focused="true"] > #tabCloseButton { + qproperty-icon: url(:/ads/images/close-button-focused.svg) +} + +ads--CDockWidgetTab[focused="true"]>#tabCloseButton:hover { + background: rgba(255, 255, 255, 48); +} + +ads--CDockWidgetTab[focused="true"]>#tabCloseButton:pressed { + background: rgba(255, 255, 255, 92); +} + +ads--CDockWidgetTab[focused="true"] QLabel { + color: palette(light); +} + + +/***************************************************************************** + * CDockWidget + *****************************************************************************/ +ads--CDockWidget { + background: palette(light); + border: 1px solid rgb(204, 204, 204); + border-top: none; +} + + + +/***************************************************************************** + * + * Styling of auto hide functionality + * + *****************************************************************************/ + + +/***************************************************************************** + * CAutoHideTab + *****************************************************************************/ +ads--CAutoHideTab { + qproperty-iconSize: 16px 16px;/* this is optional in case you would like to change icon size*/ + background: none; + border: none; + padding-left: 2px; + padding-right: 0px; + text-align: center; + min-height: 20px; + padding-bottom: 2px; +} + + +ads--CAutoHideTab:hover +{ + color: palette(highlight); +} + + +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="0"], +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="2"] { + border-top: 6px solid rgba(0, 0, 0, 48); +} + + +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="1"], +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="3"] { + border-bottom: 6px solid rgba(0, 0, 0, 48); +} + + + +ads--CAutoHideTab:hover[iconOnly="false"][sideBarLocation="0"], +ads--CAutoHideTab:hover[iconOnly="false"][sideBarLocation="2"], +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="0"][activeTab="true"], +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="2"][activeTab="true"] { + border-top: 6px solid palette(highlight); +} + + +ads--CAutoHideTab:hover[iconOnly="false"][sideBarLocation="1"], +ads--CAutoHideTab:hover[iconOnly="false"][sideBarLocation="3"], +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="1"][activeTab="true"], +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="3"][activeTab="true"] { + border-bottom: 6px solid palette(highlight); +} + + +/** + * Auto hide tabs with icon only + */ +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="0"] { + border-top: 6px solid rgba(0, 0, 0, 48); +} + +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="1"] { + border-left: 6px solid rgba(0, 0, 0, 48); +} + +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="2"] { + border-right: 6px solid rgba(0, 0, 0, 48); +} + +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="3"] { + border-bottom: 6px solid rgba(0, 0, 0, 48); +} + + +/** + * Auto hide tabs with icon only hover + */ +ads--CAutoHideTab:hover[iconOnly="true"][sideBarLocation="0"], +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="0"][activeTab="true"] { + border-top: 6px solid palette(highlight); +} + +ads--CAutoHideTab:hover[iconOnly="true"][sideBarLocation="1"], +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="1"][activeTab="true"] { + border-left: 6px solid palette(highlight); +} + +ads--CAutoHideTab:hover[iconOnly="true"][sideBarLocation="2"], +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="2"][activeTab="true"] { + border-right: 6px solid palette(highlight); +} + +ads--CAutoHideTab:hover[iconOnly="true"][sideBarLocation="3"], +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="3"][activeTab="true"] { + border-bottom: 6px solid palette(highlight); +} + + + +/***************************************************************************** + * CAutoHideSideBar + *****************************************************************************/ +ads--CAutoHideSideBar{ + background: palette(window); + border: none; + qproperty-spacing: 12; +} + +#sideTabsContainerWidget { + background: transparent; +} + + +ads--CAutoHideSideBar[sideBarLocation="0"] { + border-bottom: 1px solid palette(dark); +} + +ads--CAutoHideSideBar[sideBarLocation="1"] { + border-right: 1px solid palette(dark); +} + +ads--CAutoHideSideBar[sideBarLocation="2"] { + border-left: 1px solid palette(dark); +} + +ads--CAutoHideSideBar[sideBarLocation="3"] { + border-top: 1px solid palette(dark); +} + + +/***************************************************************************** + * CAutoHideDockContainer + *****************************************************************************/ +ads--CAutoHideDockContainer { + background: palette(window); +} + + +ads--CAutoHideDockContainer ads--CDockAreaTitleBar { + background: palette(highlight); + padding: 0px; + border: none; +} + + +/* + * This is required because the ads--CDockAreaWidget[focused="true"] will + * overwrite the ads--CAutoHideDockContainer ads--CDockAreaTitleBar rule + */ +ads--CAutoHideDockContainer ads--CDockAreaWidget[focused="true"] ads--CDockAreaTitleBar { + background: palette(highlight); + padding: 0px; + border: none; +} + + +#autoHideTitleLabel { + padding-left: 4px; + color: palette(light); +} + + +/***************************************************************************** + * CAutoHideDockContainer titlebar buttons + *****************************************************************************/ +#dockAreaAutoHideButton { + qproperty-icon: url(:/ads/images/vs-pin-button.svg); + qproperty-iconSize: 16px; +} + +ads--CAutoHideDockContainer #dockAreaAutoHideButton { + qproperty-icon: url(:/ads/images/vs-pin-button-pinned-focused.svg); + qproperty-iconSize: 16px; +} + + +ads--CAutoHideDockContainer #dockAreaCloseButton{ + qproperty-icon: url(:/ads/images/close-button-focused.svg) +} + + +ads--CAutoHideDockContainer ads--CTitleBarButton:hover { + background: rgba(255, 255, 255, 48); +} + +ads--CAutoHideDockContainer ads--CTitleBarButton:pressed { + background: rgba(255, 255, 255, 96); +} + +/***************************************************************************** + * CAutoHideDockContainer Titlebar and Buttons + *****************************************************************************/ + + +/***************************************************************************** + * CResizeHandle + *****************************************************************************/ +ads--CResizeHandle { + background: palette(window); +} + + +ads--CAutoHideDockContainer[sideBarLocation="0"] ads--CResizeHandle { + border-top: 1px solid palette(dark); +} + +ads--CAutoHideDockContainer[sideBarLocation="1"] ads--CResizeHandle { + border-left: 1px solid palette(dark); +} + +ads--CAutoHideDockContainer[sideBarLocation="2"] ads--CResizeHandle { + border-right: 1px solid palette(dark); +} + +ads--CAutoHideDockContainer[sideBarLocation="3"] ads--CResizeHandle { + border-top: 1px solid palette(dark); +} diff --git a/doc/AutoHide_PinTo.png b/doc/AutoHide_PinTo.png new file mode 100644 index 000000000..5f653e564 Binary files /dev/null and b/doc/AutoHide_PinTo.png differ diff --git a/doc/ads_logo.svg b/doc/ads_logo.svg index 107254086..13edf85cd 100644 --- a/doc/ads_logo.svg +++ b/doc/ads_logo.svg @@ -1,88 +1,33 @@ - - - - - electric_iron icon - Licensed under Iconfu Standard License v1.0 (https://www.iconfu.com/iconfu_standard_license) - Incors GmbH - - Qt Advanced Docking - - - - - - + + + electric_iron icon - Licensed under Iconfu Standard License v1.0 (https://www.iconfu.com/iconfu_standard_license) - Incors GmbH + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/ads_logo_ukraine.jpg b/doc/ads_logo_ukraine.jpg new file mode 100644 index 000000000..bf36a0267 Binary files /dev/null and b/doc/ads_logo_ukraine.jpg differ diff --git a/doc/autohide-feature-demo.png b/doc/autohide-feature-demo.png new file mode 100644 index 000000000..9e7eff704 Binary files /dev/null and b/doc/autohide-feature-demo.png differ diff --git a/doc/cfg_flag_AutoHideSideBarsIconOnly_false.png b/doc/cfg_flag_AutoHideSideBarsIconOnly_false.png new file mode 100644 index 000000000..95293a66a Binary files /dev/null and b/doc/cfg_flag_AutoHideSideBarsIconOnly_false.png differ diff --git a/doc/cfg_flag_AutoHideSideBarsIconOnly_true.png b/doc/cfg_flag_AutoHideSideBarsIconOnly_true.png new file mode 100644 index 000000000..575fdea4a Binary files /dev/null and b/doc/cfg_flag_AutoHideSideBarsIconOnly_true.png differ diff --git a/doc/cfg_flag_DockAreaHasAutoHideButton.png b/doc/cfg_flag_DockAreaHasAutoHideButton.png new file mode 100644 index 000000000..0ca1013d3 Binary files /dev/null and b/doc/cfg_flag_DockAreaHasAutoHideButton.png differ diff --git a/doc/taiwan_ukraine.jpg b/doc/taiwan_ukraine.jpg new file mode 100644 index 000000000..f2f58229b Binary files /dev/null and b/doc/taiwan_ukraine.jpg differ diff --git a/doc/user-guide.md b/doc/user-guide.md index 39a51a159..45fd7f5dd 100644 --- a/doc/user-guide.md +++ b/doc/user-guide.md @@ -28,6 +28,18 @@ - [`FloatingContainerForceNativeTitleBar` (Linux only)](#floatingcontainerforcenativetitlebar-linux-only) - [`FloatingContainerForceQWidgetTitleBar` (Linux only)](#floatingcontainerforceqwidgettitlebar-linux-only) - [`MiddleMouseButtonClosesTab`](#middlemousebuttonclosestab) +- [Auto-Hide Configuration Flags](#auto-hide-configuration-flags) + - [Auto Hide Dock Widgets](#auto-hide-dock-widgets) + - [Pinning Auto-Hide Widgets to a certain border](#pinning-auto-hide-widgets-to-a-certain-border) + - [Show / Hide Auto-Hide Widgets via Mouse Over](#show--hide-auto-hide-widgets-via-mouse-over) + - [Adding Auto Hide Widgets](#adding-auto-hide-widgets) + - [Setting Auto-Hide Flags](#setting-auto-hide-flags) + - [`AutoHideFeatureEnabled`](#autohidefeatureenabled) + - [`DockAreaHasAutoHideButton`](#dockareahasautohidebutton) + - [`AutoHideButtonTogglesArea`](#autohidebuttontogglesarea) + - [`AutoHideButtonCheckable`](#autohidebuttoncheckable) + - [`AutoHideSideBarsIconOnly`](#autohidesidebarsicononly) + - [`AutoHideShowOnMouseOver`](#autohideshowonmouseover) - [DockWidget Feature Flags](#dockwidget-feature-flags) - [`DockWidgetClosable`](#dockwidgetclosable) - [`DockWidgetMovable`](#dockwidgetmovable) @@ -486,6 +498,129 @@ possible in various web browsers. ![MiddleMouseButtonClosesTab true](cfg_flag_MiddleMouseButtonClosesTab.gif) +## Auto-Hide Configuration Flags + +### Auto Hide Dock Widgets + +The Advanced Docking System supports "Auto-Hide" functionality for **all** +dock containers. The "Auto Hide" feature allows to display more information +using less screen space by hiding or showing windows pinned to one of the +four dock container borders. + +Enabling this feature adds a button with a pin icon to each dock area. + +![DockAreaHasAutoHideButton true](cfg_flag_DockAreaHasAutoHideButton.png) + +By clicking this button, the current dock widget (or the complete area - depending on the +configuration flags) will be pinned to a certain border. The border is choosen +depending on the location of the dock area. If you click the pin button while +holding down the **Ctrl** key, the whole dock area will be pinned to a certain +border. + +### Pinning Auto-Hide Widgets to a certain border + +If you would like to pin a dock widget or a dock area to a certain border, +then you can right-click into the dock widget tab or into the dock area title bar +to show the context menu. Then you can select the location via the **Pin to** menu: + +![Pin to](AutoHide_PinTo.png) + +### Show / Hide Auto-Hide Widgets via Mouse Over + +Normally Auto-Hide widgets are shown by clicking the Auto-Hide tab and hidden by +clicking the Auto-Hide tab again or by clicking into any other dock widget in +the same container. If the Auto-Hide config flag `AutoHideShowOnMouseOver` is set, +the Auto-Hide widget is shown, if the user hovers over the Auto-Hide tab and is +collapsed if the mouse cursor leaves the Auto-Hide widget. Showing and hiding +by mouse click still works if this feature is enabled. + +### Adding Auto Hide Widgets + +Adding an auto hide widget is similar to adding a dock widget, simply call +`dockManager->addAutoHideDockWidget()`. + +```c++ +CDockManager::setAutoHideConfigFlags(CDockManager::DefaultAutoHideConfig); +d->DockManager = new CDockManager(this); +CDockWidget* TableDockWidget = new CDockWidget("Table 1"); +DockManager->addAutoHideDockWidget(SideBarLeft, TableDockWidget); +``` + +See `autohide` example or the demo application to learn how it works. + +### Setting Auto-Hide Flags + +The Advanced Docking System has a number of global configuration flags to +configure the Auto-Hide functionality. You should set the Auto-Hide flags before +creating the dock manager instance. That means, you should set the Auto-Hide +flags after setting the configuration flags. + +```c++ +CDockManager::setConfigFlag(CDockManager::FocusHighlighting, true); +CDockManager::setAutoHideConfigFlags(CDockManager::DefaultAutoHideConfig); +CDockManager::setAutoHideConfigFlag(CDockManager::AutoHideShowOnMouseOver, true); +... +d->DockManager = new CDockManager(this); +``` + +If you set the Auto-Hide flags, you can set individual flags using the +function `CDockManager::setAutoHideConfigFlag` or you can set all flags using +the function `CDockManager::setAutoHideConfigFlags`. Instead of settings all +flags individually, it is better to pick a predefined set of configuration +flags and then modify individual flags. The following predefined +configurations are available + +- `DefaultAutoHideConfig` - default auto hide config + +Pick one of those predefined configurations and then modify the following +configurations flags to adjust the docking system to your needs. + +### `AutoHideFeatureEnabled` + +Enables / disables the Auto-Hide functionality. Only if this flag is enabled, +the other Auto-Hide flags will be evaluated. + +### `DockAreaHasAutoHideButton` + +If this flag is set (default), then each dock area has a pin button in the title +bar to toggle Auto-Hide state. + +![DockAreaHasAutoHideButton true](cfg_flag_DockAreaHasAutoHideButton.png) + +### `AutoHideButtonTogglesArea` + +If set, the the pin button in the dock area title bar toggles the complete area. +If not set (default), then the pin button toggles the current active tab / dock +widget and pressing the **Ctrl** key while clicking the pin button toggles the +complete ara. + +### `AutoHideButtonCheckable` + +Normally the pin button in the dock area title bar is not checkable. If this +flag is set, then the button is checkable. That means, if button is checked, +the dock widget is pinned. + +### `AutoHideSideBarsIconOnly` + +Normally the Auto-Hide tabs show the icon and title of the dock widget: + +![AutoHideSideBarsIconOnly false](cfg_flag_AutoHideSideBarsIconOnly_false.png) + +You can set this flag, if you would like to have only icons in the Auto-Hide +tabs instead of icon and dock widget title. If this is set, the Auto-Hide tab +needs less space. The tooltip of each tab still shows the dock widget title. + +![AutoHideSideBarsIconOnly true](cfg_flag_AutoHideSideBarsIconOnly_true.png) + +### `AutoHideShowOnMouseOver` + +Normally Auto-Hide widgets are shown by clicking the Auto-Hide tab and +hidden by clicking the Auto-Hide tab again or by clicking into any other +dock widget in the same container. If this flag is set, the Auto-Hide widget +is shown, if the user hovers over the Auto-Hide tab or if the users moves the +mouse outside of the Auto-Hide widget. Showing and hiding my mouse click still +works if this feature is enabled. + ## DockWidget Feature Flags ### `DockWidgetClosable` diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 5ae02d6e2..ba2a064e6 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -5,5 +5,6 @@ add_subdirectory(hideshow) add_subdirectory(sidebar) add_subdirectory(deleteonclose) add_subdirectory(centralwidget) +add_subdirectory(autohide) add_subdirectory(emptydockarea) add_subdirectory(dockindock) \ No newline at end of file diff --git a/examples/autohide/CMakeLists.txt b/examples/autohide/CMakeLists.txt new file mode 100644 index 000000000..cec3910e2 --- /dev/null +++ b/examples/autohide/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 3.5) +project(ads_example_autohide VERSION ${VERSION_SHORT}) +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} 5.5 COMPONENTS Core Gui Widgets REQUIRED) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +add_executable(AutoHideExample WIN32 + main.cpp + mainwindow.cpp + mainwindow.ui +) +target_include_directories(AutoHideExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src") +target_link_libraries(AutoHideExample PRIVATE qtadvanceddocking) +target_link_libraries(AutoHideExample PUBLIC Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Gui + Qt${QT_VERSION_MAJOR}::Widgets) +set_target_properties(AutoHideExample 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 Auto Hide 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/autohide/autohide.pro b/examples/autohide/autohide.pro new file mode 100644 index 000000000..8a00c6253 --- /dev/null +++ b/examples/autohide/autohide.pro @@ -0,0 +1,34 @@ +ADS_OUT_ROOT = $${OUT_PWD}/../.. + +QT += core gui widgets + +TARGET = AutoHideExample +DESTDIR = $${ADS_OUT_ROOT}/lib +TEMPLATE = app +CONFIG += c++14 +CONFIG += debug_and_release +adsBuildStatic { + DEFINES += ADS_STATIC +} + +# 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 + +SOURCES += \ + main.cpp \ + mainwindow.cpp + +HEADERS += \ + mainwindow.h + +FORMS += \ + mainwindow.ui + +LIBS += -L$${ADS_OUT_ROOT}/lib +include(../../ads.pri) +INCLUDEPATH += ../../src +DEPENDPATH += ../../src + diff --git a/examples/autohide/main.cpp b/examples/autohide/main.cpp new file mode 100644 index 000000000..fa4c4fdea --- /dev/null +++ b/examples/autohide/main.cpp @@ -0,0 +1,10 @@ +#include +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + CMainWindow w; + w.show(); + return a.exec(); +} diff --git a/examples/autohide/main.py b/examples/autohide/main.py new file mode 100644 index 000000000..5fe6a492d --- /dev/null +++ b/examples/autohide/main.py @@ -0,0 +1,106 @@ +import os +import sys + +from PyQt5 import uic +from PyQt5.QtCore import Qt, QTimer, QDir, QSignalBlocker +from PyQt5.QtGui import QCloseEvent, QIcon +from PyQt5.QtWidgets import (QApplication, QLabel, QCalendarWidget, QFrame, QTreeView, + QTableWidget, QFileSystemModel, QPlainTextEdit, QToolBar, + QWidgetAction, QComboBox, QAction, QSizePolicy, QInputDialog) + +from PyQtAds import QtAds + +UI_FILE = os.path.join(os.path.dirname(__file__), 'mainwindow.ui') +MainWindowUI, MainWindowBase = uic.loadUiType(UI_FILE) + +class MainWindow(MainWindowUI, MainWindowBase): + + def __init__(self, parent=None): + super().__init__(parent) + + self.setupUi(self) + + QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.OpaqueSplitterResize, True) + QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.XmlCompressionEnabled, False) + QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.FocusHighlighting, True) + self.dock_manager = QtAds.CDockManager(self) + + # Set central widget + text_edit = QPlainTextEdit() + text_edit.setPlaceholderText("This is the central editor. Enter your text here.") + central_dock_widget = QtAds.CDockWidget("CentralWidget") + central_dock_widget.setWidget(text_edit) + central_dock_area = self.dock_manager.setCentralWidget(central_dock_widget) + central_dock_area.setAllowedAreas(QtAds.DockWidgetArea.OuterDockAreas) + + # create other dock widgets + table = QTableWidget() + table.setColumnCount(3) + table.setRowCount(10) + table_dock_widget = QtAds.CDockWidget("Table 1") + table_dock_widget.setWidget(table) + table_dock_widget.setMinimumSizeHintMode(QtAds.CDockWidget.MinimumSizeHintFromDockWidget) + table_dock_widget.resize(250, 150) + table_dock_widget.setMinimumSize(200, 150) + table_area = self.dock_manager.addDockWidget(QtAds.DockWidgetArea.LeftDockWidgetArea, table_dock_widget) + self.menuView.addAction(table_dock_widget.toggleViewAction()) + + table = QTableWidget() + table.setColumnCount(5) + table.setRowCount(1020) + table_dock_widget = QtAds.CDockWidget("Table 2") + table_dock_widget.setWidget(table) + table_dock_widget.setMinimumSizeHintMode(QtAds.CDockWidget.MinimumSizeHintFromDockWidget) + table_dock_widget.resize(250, 150) + table_dock_widget.setMinimumSize(200, 150) + table_area = self.dock_manager.addDockWidget(QtAds.DockWidgetArea.BottomDockWidgetArea, table_dock_widget, table_area) + self.menuView.addAction(table_dock_widget.toggleViewAction()) + + properties_table = QTableWidget() + properties_table.setColumnCount(3) + properties_table.setRowCount(10) + properties_dock_widget = QtAds.CDockWidget("Properties") + properties_dock_widget.setWidget(properties_table) + properties_dock_widget.setMinimumSizeHintMode(QtAds.CDockWidget.MinimumSizeHintFromDockWidget) + properties_dock_widget.resize(250, 150) + properties_dock_widget.setMinimumSize(200, 150) + self.dock_manager.addDockWidget(QtAds.DockWidgetArea.RightDockWidgetArea, properties_dock_widget, central_dock_area) + self.menuView.addAction(properties_dock_widget.toggleViewAction()) + + self.create_perspective_ui() + + def create_perspective_ui(self): + save_perspective_action = QAction("Create Perspective", self) + save_perspective_action.triggered.connect(self.save_perspective) + perspective_list_action = QWidgetAction(self) + self.perspective_combobox = QComboBox(self) + self.perspective_combobox.setSizeAdjustPolicy(QComboBox.AdjustToContents) + self.perspective_combobox.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) + self.perspective_combobox.activated[str].connect(self.dock_manager.openPerspective) + perspective_list_action.setDefaultWidget(self.perspective_combobox) + self.toolBar.addSeparator() + self.toolBar.addAction(perspective_list_action) + self.toolBar.addAction(save_perspective_action) + + def save_perspective(self): + perspective_name, ok = QInputDialog.getText(self, "Save Perspective", "Enter Unique name:") + if not ok or not perspective_name: + return + + self.dock_manager.addPerspective(perspective_name) + blocker = QSignalBlocker(self.perspective_combobox) + self.perspective_combobox.clear() + self.perspective_combobox.addItems(self.dock_manager.perspectiveNames()) + self.perspective_combobox.setCurrentText(perspective_name) + + def closeEvent(self, event: QCloseEvent): + self.dock_manager.deleteLater() + super().closeEvent(event) + + +if __name__ == '__main__': + app = QApplication(sys.argv) + + w = MainWindow() + w.show() + app.exec_() diff --git a/examples/autohide/mainwindow.cpp b/examples/autohide/mainwindow.cpp new file mode 100644 index 000000000..d70b51ce7 --- /dev/null +++ b/examples/autohide/mainwindow.cpp @@ -0,0 +1,126 @@ +#include "mainwindow.h" + +#include "ui_mainwindow.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "AutoHideDockContainer.h" +#include "DockAreaWidget.h" +#include "DockAreaTitleBar.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); + CDockManager::setConfigFlag(CDockManager::FocusHighlighting, true); + CDockManager::setAutoHideConfigFlags(CDockManager::DefaultAutoHideConfig); + DockManager = new CDockManager(this); + + // Set central widget + QPlainTextEdit* w = new QPlainTextEdit(); + w->setPlaceholderText("This is the central editor. Enter your text here."); + CDockWidget* CentralDockWidget = new CDockWidget("CentralWidget"); + CentralDockWidget->setWidget(w); + auto* CentralDockArea = DockManager->setCentralWidget(CentralDockWidget); + CentralDockArea->setAllowedAreas(DockWidgetArea::OuterDockAreas); + + // create other dock widgets + QTableWidget* table = new QTableWidget(); + table->setColumnCount(3); + table->setRowCount(10); + CDockWidget* TableDockWidget = new CDockWidget("Table 1"); + TableDockWidget->setWidget(table); + TableDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget); + TableDockWidget->setMinimumSize(200,150); + const auto autoHideContainer = DockManager->addAutoHideDockWidget(SideBarLocation::SideBarLeft, TableDockWidget); + autoHideContainer->setSize(480); + ui->menuView->addAction(TableDockWidget->toggleViewAction()); + + table = new QTableWidget(); + table->setColumnCount(5); + table->setRowCount(1020); + TableDockWidget = new CDockWidget("Table 2"); + TableDockWidget->setWidget(table); + TableDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget); + TableDockWidget->resize(250, 150); + TableDockWidget->setMinimumSize(200,150); + DockManager->addAutoHideDockWidget(SideBarLocation::SideBarLeft, TableDockWidget); + ui->menuView->addAction(TableDockWidget->toggleViewAction()); + + QTableWidget* propertiesTable = new QTableWidget(); + propertiesTable->setColumnCount(3); + propertiesTable->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()); + + createPerspectiveUi(); +} + +CMainWindow::~CMainWindow() +{ + delete ui; +} + + +void CMainWindow::createPerspectiveUi() +{ + SavePerspectiveAction = new QAction("Create Perspective", this); + connect(SavePerspectiveAction, SIGNAL(triggered()), SLOT(savePerspective())); + PerspectiveListAction = new QWidgetAction(this); + PerspectiveComboBox = new QComboBox(this); + PerspectiveComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents); + PerspectiveComboBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + connect(PerspectiveComboBox, SIGNAL(currentTextChanged(const QString&)), + DockManager, SLOT(openPerspective(const QString&))); + PerspectiveListAction->setDefaultWidget(PerspectiveComboBox); + ui->toolBar->addSeparator(); + ui->toolBar->addAction(PerspectiveListAction); + ui->toolBar->addAction(SavePerspectiveAction); +} + + +void CMainWindow::savePerspective() +{ + QString PerspectiveName = QInputDialog::getText(this, "Save Perspective", "Enter unique name:"); + if (PerspectiveName.isEmpty()) + { + return; + } + + DockManager->addPerspective(PerspectiveName); + QSignalBlocker Blocker(PerspectiveComboBox); + PerspectiveComboBox->clear(); + PerspectiveComboBox->addItems(DockManager->perspectiveNames()); + PerspectiveComboBox->setCurrentText(PerspectiveName); +} + + +//============================================================================ +void CMainWindow::closeEvent(QCloseEvent* event) +{ + // Delete dock manager here to delete all floating widgets. This ensures + // that all top level windows of the dock manager are properly closed + DockManager->deleteLater(); + QMainWindow::closeEvent(event); +} + + diff --git a/examples/autohide/mainwindow.h b/examples/autohide/mainwindow.h new file mode 100644 index 000000000..75869da84 --- /dev/null +++ b/examples/autohide/mainwindow.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#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(); + +protected: + virtual void closeEvent(QCloseEvent* event) override; + +private: + QAction* SavePerspectiveAction = nullptr; + QWidgetAction* PerspectiveListAction = nullptr; + QComboBox* PerspectiveComboBox = nullptr; + + Ui::CMainWindow *ui; + + ads::CDockManager* DockManager; + ads::CDockAreaWidget* StatusDockArea; + ads::CDockWidget* TimelineDockWidget; + + void createPerspectiveUi(); + +private slots: + void savePerspective(); +}; +#endif // MAINWINDOW_H diff --git a/examples/autohide/mainwindow.ui b/examples/autohide/mainwindow.ui new file mode 100644 index 000000000..f7d3b09e8 --- /dev/null +++ b/examples/autohide/mainwindow.ui @@ -0,0 +1,47 @@ + + + CMainWindow + + + + 0 + 0 + 1284 + 757 + + + + MainWindow + + + + + + 0 + 0 + 1284 + 21 + + + + + View + + + + + + + toolBar + + + TopToolBarArea + + + false + + + + + + diff --git a/examples/centralwidget/mainwindow.cpp b/examples/centralwidget/mainwindow.cpp index fe1b34714..3ad0056df 100644 --- a/examples/centralwidget/mainwindow.cpp +++ b/examples/centralwidget/mainwindow.cpp @@ -96,7 +96,7 @@ void CMainWindow::createPerspectiveUi() PerspectiveComboBox = new QComboBox(this); PerspectiveComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents); PerspectiveComboBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - connect(PerspectiveComboBox, SIGNAL(activated(const QString&)), + connect(PerspectiveComboBox, SIGNAL(currentTextChanged(const QString&)), DockManager, SLOT(openPerspective(const QString&))); PerspectiveListAction->setDefaultWidget(PerspectiveComboBox); ui->toolBar->addSeparator(); diff --git a/examples/examples.pro b/examples/examples.pro index 3569f372e..4e4547a0f 100644 --- a/examples/examples.pro +++ b/examples/examples.pro @@ -1,6 +1,7 @@ TEMPLATE = subdirs SUBDIRS = \ + autohide \ centralwidget \ simple \ hideshow \ diff --git a/examples/hideshow/MainWindow.cpp b/examples/hideshow/MainWindow.cpp index e4d7449fb..44ce67219 100644 --- a/examples/hideshow/MainWindow.cpp +++ b/examples/hideshow/MainWindow.cpp @@ -5,6 +5,8 @@ #include #include +using namespace ads; + MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) diff --git a/persist/perspectives.ini b/persist/perspectives.ini new file mode 100644 index 000000000..e342ad72f --- /dev/null +++ b/persist/perspectives.ini @@ -0,0 +1,2 @@ +[Perspectives] +size=0 diff --git a/src/AutoHideDockContainer.cpp b/src/AutoHideDockContainer.cpp new file mode 100644 index 000000000..208ee075e --- /dev/null +++ b/src/AutoHideDockContainer.cpp @@ -0,0 +1,588 @@ +/******************************************************************************* +** Qt Advanced Docking System +** Copyright (C) 2017 Uwe Kindler +** +** This library is free software; you can redistribute it and/or +** modify it under the terms of the GNU Lesser General Public +** License as published by the Free Software Foundation; either +** version 2.1 of the License, or (at your option) any later version. +** +** This library is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public +** License along with this library; If not, see . +******************************************************************************/ + + +//============================================================================ +/// \file AutoHideDockContainer.cpp +/// \author Syarif Fakhri +/// \date 05.09.2022 +/// \brief Implementation of CAutoHideDockContainer class +//============================================================================ + +//============================================================================ +// INCLUDES +//============================================================================ +#include "AutoHideDockContainer.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "DockManager.h" +#include "DockWidgetTab.h" +#include "DockAreaWidget.h" +#include "DockingStateReader.h" +#include "ResizeHandle.h" +#include "DockComponentsFactory.h" +#include "AutoHideSideBar.h" +#include "AutoHideTab.h" + + +#include + +namespace ads +{ +static const int ResizeMargin = 30; + +//============================================================================ +bool static isHorizontalArea(SideBarLocation Area) +{ + switch (Area) + { + case SideBarLocation::SideBarTop: + case SideBarLocation::SideBarBottom: return true; + case SideBarLocation::SideBarLeft: + case SideBarLocation::SideBarRight: return false; + default: + return true; + } + + return true; +} + + +//============================================================================ +Qt::Edge static edgeFromSideTabBarArea(SideBarLocation Area) +{ + switch (Area) + { + case SideBarLocation::SideBarTop: return Qt::BottomEdge; + case SideBarLocation::SideBarBottom: return Qt::TopEdge; + case SideBarLocation::SideBarLeft: return Qt::RightEdge; + case SideBarLocation::SideBarRight: return Qt::LeftEdge; + default: + return Qt::LeftEdge; + } + + return Qt::LeftEdge; +} + + +//============================================================================ +int resizeHandleLayoutPosition(SideBarLocation Area) +{ + switch (Area) + { + case SideBarLocation::SideBarBottom: + case SideBarLocation::SideBarRight: return 0; + + case SideBarLocation::SideBarTop: + case SideBarLocation::SideBarLeft: return 1; + + default: + return 0; + } + + return 0; +} + + +/** + * Private data of CAutoHideDockContainer - pimpl + */ +struct AutoHideDockContainerPrivate +{ + CAutoHideDockContainer* _this; + CDockAreaWidget* DockArea{nullptr}; + CDockWidget* DockWidget{nullptr}; + SideBarLocation SideTabBarArea = SideBarNone; + QBoxLayout* Layout = nullptr; + CResizeHandle* ResizeHandle = nullptr; + QSize Size; // creates invalid size + QPointer SideTab; + + /** + * Private data constructor + */ + AutoHideDockContainerPrivate(CAutoHideDockContainer *_public); + + /** + * Convenience function to get a dock widget area + */ + DockWidgetArea getDockWidgetArea(SideBarLocation area) + { + switch (area) + { + case SideBarLocation::SideBarLeft: return LeftDockWidgetArea; + case SideBarLocation::SideBarRight: return RightDockWidgetArea; + case SideBarLocation::SideBarBottom: return BottomDockWidgetArea; + case SideBarLocation::SideBarTop: return TopDockWidgetArea; + default: + return LeftDockWidgetArea; + } + + return LeftDockWidgetArea; + } + + /** + * Update the resize limit of the resize handle + */ + void updateResizeHandleSizeLimitMax() + { + auto Rect = _this->dockContainer()->contentRect(); + const auto maxResizeHandleSize = ResizeHandle->orientation() == Qt::Horizontal + ? Rect.width() : Rect.height(); + ResizeHandle->setMaxResizeSize(maxResizeHandleSize - ResizeMargin); + } + + /** + * Convenience function to check, if this is an horizontal area + */ + bool isHorizontal() const + { + return isHorizontalArea(SideTabBarArea); + } + + /** + * Forward this event to the dock container + */ + void forwardEventToDockContainer(QEvent* event) + { + auto DockContainer = _this->dockContainer(); + if (DockContainer) + { + DockContainer->handleAutoHideWidgetEvent(event, _this); + } + } + +}; // struct AutoHideDockContainerPrivate + + +//============================================================================ +AutoHideDockContainerPrivate::AutoHideDockContainerPrivate( + CAutoHideDockContainer *_public) : + _this(_public) +{ + +} + + +//============================================================================ +CDockContainerWidget* CAutoHideDockContainer::dockContainer() const +{ + if (d->DockArea) + { + return d->DockArea->dockContainer(); + } + else + { + return internal::findParent(this); + } +} + + +//============================================================================ +CAutoHideDockContainer::CAutoHideDockContainer(CDockManager* DockManager, SideBarLocation area, CDockContainerWidget* parent) : + Super(parent), + d(new AutoHideDockContainerPrivate(this)) +{ + hide(); // auto hide dock container is initially always hidden + d->SideTabBarArea = area; + d->SideTab = componentsFactory()->createDockWidgetSideTab(nullptr); + connect(d->SideTab, &CAutoHideTab::pressed, this, &CAutoHideDockContainer::toggleCollapseState); + d->DockArea = new CDockAreaWidget(DockManager, parent); + d->DockArea->setObjectName("autoHideDockArea"); + d->DockArea->setAutoHideDockContainer(this); + + setObjectName("autoHideDockContainer"); + + d->Layout = new QBoxLayout(isHorizontalArea(area) ? QBoxLayout::TopToBottom : QBoxLayout::LeftToRight); + d->Layout->setContentsMargins(0, 0, 0, 0); + d->Layout->setSpacing(0); + setLayout(d->Layout); + d->Layout->addWidget(d->DockArea); + d->ResizeHandle = new CResizeHandle(edgeFromSideTabBarArea(area), this); + d->ResizeHandle->setMinResizeSize(64); + bool OpaqueResize = CDockManager::testConfigFlag(CDockManager::OpaqueSplitterResize); + d->ResizeHandle->setOpaqueResize(OpaqueResize); + d->Layout->insertWidget(resizeHandleLayoutPosition(area), d->ResizeHandle); + d->Size = d->DockArea->size(); + + updateSize(); + parent->registerAutoHideWidget(this); +} + + +//============================================================================ +CAutoHideDockContainer::CAutoHideDockContainer(CDockWidget* DockWidget, SideBarLocation area, CDockContainerWidget* parent) : + CAutoHideDockContainer(DockWidget->dockManager(), area, parent) +{ + addDockWidget(DockWidget); + hide(); +} + + +//============================================================================ +void CAutoHideDockContainer::updateSize() +{ + auto dockContainerParent = dockContainer(); + if (!dockContainerParent) + { + return; + } + + auto rect = dockContainerParent->contentRect(); + + switch (sideBarLocation()) + { + case SideBarLocation::SideBarTop: + resize(rect.width(), qMin(rect.height() - ResizeMargin, d->Size.height())); + move(rect.topLeft()); + break; + + case SideBarLocation::SideBarLeft: + resize(qMin(d->Size.width(), rect.width() - ResizeMargin), rect.height()); + move(rect.topLeft()); + break; + + case SideBarLocation::SideBarRight: + { + resize(qMin(d->Size.width(), rect.width() - ResizeMargin), rect.height()); + QPoint p = rect.topRight(); + p.rx() -= (width() - 1); + move(p); + } + break; + + case SideBarLocation::SideBarBottom: + { + resize(rect.width(), qMin(rect.height() - ResizeMargin, d->Size.height())); + QPoint p = rect.bottomLeft(); + p.ry() -= (height() - 1); + move(p); + } + break; + + default: + break; + } +} + +//============================================================================ +CAutoHideDockContainer::~CAutoHideDockContainer() +{ + ADS_PRINT("~CAutoHideDockContainer"); + + // Remove event filter in case there are any queued messages + qApp->removeEventFilter(this); + if (dockContainer()) + { + dockContainer()->removeAutoHideWidget(this); + } + + if (d->SideTab) + { + delete d->SideTab; + } + + delete d; +} + +//============================================================================ +CAutoHideSideBar* CAutoHideDockContainer::sideBar() const +{ + return dockContainer()->sideTabBar(d->SideTabBarArea); +} + + +//============================================================================ +CAutoHideTab* CAutoHideDockContainer::autoHideTab() const +{ + return d->SideTab; +} + + +//============================================================================ +CDockWidget* CAutoHideDockContainer::dockWidget() const +{ + return d->DockWidget; +} + +//============================================================================ +void CAutoHideDockContainer::addDockWidget(CDockWidget* DockWidget) +{ + if (d->DockWidget) + { + // Remove the old dock widget at this area + d->DockArea->removeDockWidget(d->DockWidget); + } + + d->DockWidget = DockWidget; + d->SideTab->setDockWidget(DockWidget); + CDockAreaWidget* OldDockArea = DockWidget->dockAreaWidget(); + auto IsRestoringState = DockWidget->dockManager()->isRestoringState(); + if (OldDockArea && !IsRestoringState) + { + // The initial size should be a little bit bigger than the original dock + // area size to prevent that the resize handle of this auto hid dock area + // is near of the splitter of the old dock area. + d->Size = OldDockArea->size() + QSize(16, 16); + OldDockArea->removeDockWidget(DockWidget); + } + d->DockArea->addDockWidget(DockWidget); + updateSize(); +} + + +//============================================================================ +SideBarLocation CAutoHideDockContainer::sideBarLocation() const +{ + return d->SideTabBarArea; +} + +//============================================================================ +CDockAreaWidget* CAutoHideDockContainer::dockAreaWidget() const +{ + return d->DockArea; +} + +//============================================================================ +void CAutoHideDockContainer::moveContentsToParent() +{ + cleanupAndDelete(); + // If we unpin the auto hide dock widget, then we insert it into the same + // location like it had as a auto hide widget. This brings the least surprise + // to the user and he does not have to search where the widget was inserted. + d->DockWidget->setDockArea(nullptr); + auto DockContainer = dockContainer(); + DockContainer->addDockWidget(d->getDockWidgetArea(d->SideTabBarArea), d->DockWidget); +} + + +//============================================================================ +void CAutoHideDockContainer::cleanupAndDelete() +{ + const auto dockWidget = d->DockWidget; + if (dockWidget) + { + + auto SideTab = d->SideTab; + SideTab->removeFromSideBar(); + SideTab->setParent(nullptr); + SideTab->hide(); + } + + hide(); + deleteLater(); +} + + +//============================================================================ +void CAutoHideDockContainer::saveState(QXmlStreamWriter& s) +{ + s.writeStartElement("Widget"); + s.writeAttribute("Name", d->DockWidget->objectName()); + s.writeAttribute("Closed", QString::number(d->DockWidget->isClosed() ? 1 : 0)); + s.writeAttribute("Size", QString::number(d->isHorizontal() ? d->Size.height() : d->Size.width())); + s.writeEndElement(); +} + + +//============================================================================ +void CAutoHideDockContainer::toggleView(bool Enable) +{ + if (Enable) + { + if (d->SideTab) + { + d->SideTab->show(); + } + } + else + { + if (d->SideTab) + { + d->SideTab->hide(); + } + hide(); + qApp->removeEventFilter(this); + } +} + + +//============================================================================ +void CAutoHideDockContainer::collapseView(bool Enable) +{ + if (Enable) + { + hide(); + qApp->removeEventFilter(this); + } + else + { + updateSize(); + d->updateResizeHandleSizeLimitMax(); + raise(); + show(); + d->DockWidget->dockManager()->setDockWidgetFocused(d->DockWidget); + qApp->installEventFilter(this); + } + + ADS_PRINT("CAutoHideDockContainer::collapseView " << Enable); + d->SideTab->updateStyle(); +} + + +//============================================================================ +void CAutoHideDockContainer::toggleCollapseState() +{ + collapseView(isVisible()); +} + + +//============================================================================ +void CAutoHideDockContainer::setSize(int Size) +{ + if (d->isHorizontal()) + { + d->Size.setHeight(Size); + } + else + { + d->Size.setWidth(Size); + } + + updateSize(); +} + + +//============================================================================ +bool CAutoHideDockContainer::eventFilter(QObject* watched, QEvent* event) +{ + if (event->type() == QEvent::Resize) + { + if (!d->ResizeHandle->isResizing()) + { + updateSize(); + } + } + else if (event->type() == QEvent::MouseButtonPress) + { + auto Container = dockContainer(); + // First we check, if the mouse button press is inside the container + // widget. If it is not, i.e. if someone resizes the main window or + // clicks into the application menu or toolbar, then we ignore the + // event + auto widget = qobject_cast(watched); + bool IsContainer = false; + while (widget) + { + if (widget == Container) + { + IsContainer = true; + } + widget = widget->parentWidget(); + } + + if (!IsContainer) + { + return Super::eventFilter(watched, event); + } + + // Now we check, if the user clicked inside of this auto hide container. + // If the click is inside of this auto hide container, then we can also + // ignore the event, because the auto hide overlay should not get collapsed if + // user works in it + QMouseEvent* me = static_cast(event); + auto pos = mapFromGlobal(me->globalPos()); + if (rect().contains(pos)) + { + return Super::eventFilter(watched, event); + } + + // Now check, if the user clicked into the side tab and ignore this event, + // because the side tab click handler will call collapseView(). If we + // do not ignore this here, then we will collapse the container and the side tab + // click handler will uncollapse it + auto SideTab = d->SideTab; + pos = SideTab->mapFromGlobal(me->globalPos()); + if (SideTab->rect().contains(pos)) + { + return Super::eventFilter(watched, event); + } + + // If the mouse button down event is in the dock manager but outside + // of the open auto hide container, then the auto hide dock widget + // should get collapsed + collapseView(true); + } + + return Super::eventFilter(watched, event); +} + + +//============================================================================ +void CAutoHideDockContainer::resizeEvent(QResizeEvent* event) +{ + Super::resizeEvent(event); + if (d->ResizeHandle->isResizing()) + { + d->Size = this->size(); + d->updateResizeHandleSizeLimitMax(); + } +} + + +//============================================================================ +void CAutoHideDockContainer::leaveEvent(QEvent *event) +{ + // Resizing of the dock container via the resize handle in non opaque mode + // mays cause a leave event that is not really a leave event. Therefore + // we check here, if we are really outside of our rect. + auto pos = mapFromGlobal(QCursor::pos()); + if (!rect().contains(pos)) + { + d->forwardEventToDockContainer(event); + } + Super::leaveEvent(event); +} + + +//============================================================================ +bool CAutoHideDockContainer::event(QEvent* event) +{ + switch (event->type()) + { + case QEvent::Enter: + case QEvent::Hide: + d->forwardEventToDockContainer(event); + break; + + default: + break; + } + + return Super::event(event); +} + +} + diff --git a/src/AutoHideDockContainer.h b/src/AutoHideDockContainer.h new file mode 100644 index 000000000..d91b97183 --- /dev/null +++ b/src/AutoHideDockContainer.h @@ -0,0 +1,170 @@ +#ifndef AutoHideDockContainerH +#define AutoHideDockContainerH +/******************************************************************************* +** Qt Advanced Docking System +** Copyright (C) 2017 Uwe Kindler +** +** This library is free software; you can redistribute it and/or +** modify it under the terms of the GNU Lesser General Public +** License as published by the Free Software Foundation; either +** version 2.1 of the License, or (at your option) any later version. +** +** This library is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public +** License along with this library; If not, see . +******************************************************************************/ + + +//============================================================================ +/// \file AutoHideDockContainer.h +/// \author Syarif Fakhri +/// \date 05.09.2022 +/// \brief Declaration of CAutoHideDockContainer class +//============================================================================ + +//============================================================================ +// INCLUDES +//============================================================================ +#include "ads_globals.h" + +#include +#include "AutoHideTab.h" + +class QXmlStreamWriter; + +namespace ads +{ +struct AutoHideDockContainerPrivate; +class CDockManager; +class CDockWidget; +class CDockContainerWidget; +class CAutoHideSideBar; +class CDockAreaWidget; +class CDockingStateReader; + +/** + * Auto hide container for hosting an auto hide dock widget + */ +class ADS_EXPORT CAutoHideDockContainer : public QFrame +{ + Q_OBJECT + Q_PROPERTY(int sideBarLocation READ sideBarLocation) +private: + AutoHideDockContainerPrivate* d; ///< private data (pimpl) + friend struct AutoHideDockContainerPrivate; + friend CAutoHideSideBar; + +protected: + virtual bool eventFilter(QObject* watched, QEvent* event) override; + virtual void resizeEvent(QResizeEvent* event) override; + virtual void leaveEvent(QEvent *event) override; + virtual bool event(QEvent* event) override; + + + /** + * Updates the size considering the size limits and the resize margins + */ + void updateSize(); + + /* + * Saves the state and size + */ + void saveState(QXmlStreamWriter& Stream); + +public: + using Super = QFrame; + /** + * Create Auto Hide widget with a dock manager + */ + CAutoHideDockContainer(CDockManager* DockManager, SideBarLocation area, + CDockContainerWidget* parent); + + /** + * Create Auto Hide widget with the given dock widget + */ + CAutoHideDockContainer(CDockWidget* DockWidget, SideBarLocation area, + CDockContainerWidget* parent); + + /** + * Virtual Destructor + */ + virtual ~CAutoHideDockContainer(); + + /** + * Get's the side tab bar + */ + CAutoHideSideBar* sideBar() const; + + /** + * Returns the side tab + */ + CAutoHideTab* autoHideTab() const; + + /** + * Get's the dock widget in this dock container + */ + CDockWidget* dockWidget() const; + + /** + * Adds a dock widget and removes the previous dock widget + */ + void addDockWidget(CDockWidget* DockWidget); + + /** + * Returns the side tab bar area of this Auto Hide dock container + */ + SideBarLocation sideBarLocation() const; + + /** + * Returns the dock area widget of this Auto Hide dock container + */ + CDockAreaWidget* dockAreaWidget() const; + + /** + * Returns the parent container that hosts this auto hide container + */ + CDockContainerWidget* dockContainer() const; + + /** + * Moves the contents to the parent container widget + * Used before removing this Auto Hide dock container + */ + void moveContentsToParent(); + + /** + * Cleanups up the side tab widget and then deletes itself + */ + void cleanupAndDelete(); + + /* + * Toggles the auto Hide dock container widget + * This will also hide the side tab widget + */ + void toggleView(bool Enable); + + /* + * Collapses the auto hide dock container widget + * Does not hide the side tab widget + */ + void collapseView(bool Enable); + + /** + * Toggles the current collapse state + */ + void toggleCollapseState(); + + /** + * Use this instead of resize. + * Depending on the sidebar location this will set the width or heigth + * of this auto hide container. + */ + void setSize(int Size); +}; +} // namespace ads + +//----------------------------------------------------------------------------- +#endif diff --git a/src/AutoHideSideBar.cpp b/src/AutoHideSideBar.cpp new file mode 100644 index 000000000..aa1ad1a9a --- /dev/null +++ b/src/AutoHideSideBar.cpp @@ -0,0 +1,356 @@ +/******************************************************************************* +** Qt Advanced Docking System +** Copyright (C) 2017 Uwe Kindler +** +** This library is free software; you can redistribute it and/or +** modify it under the terms of the GNU Lesser General Public +** License as published by the Free Software Foundation; either +** version 2.1 of the License, or (at your option) any later version. +** +** This library is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public +** License along with this library; If not, see . +******************************************************************************/ + + +//============================================================================ +/// \file AutoHideSideBar.cpp +/// \author Syarif Fakhri +/// \date 05.09.2022 +/// \brief Implementation of CAutoHideSideBar class +//============================================================================ + + +//============================================================================ +// INCLUDES +//============================================================================ +#include "AutoHideSideBar.h" + +#include +#include +#include +#include + +#include "DockContainerWidget.h" +#include "DockWidgetTab.h" +#include "DockFocusController.h" +#include "AutoHideDockContainer.h" +#include "DockAreaWidget.h" +#include "DockingStateReader.h" +#include "AutoHideTab.h" + +namespace ads +{ +class CTabsWidget; + +/** + * Private data class of CSideTabBar class (pimpl) + */ +struct AutoHideSideBarPrivate +{ + /** + * Private data constructor + */ + AutoHideSideBarPrivate(CAutoHideSideBar* _public); + + CAutoHideSideBar* _this; + CDockContainerWidget* ContainerWidget; + CTabsWidget* TabsContainerWidget; + QBoxLayout* TabsLayout; + Qt::Orientation Orientation; + SideBarLocation SideTabArea = SideBarLocation::SideBarLeft; + + /** + * Convenience function to check if this is a horizontal side bar + */ + bool isHorizontal() const + { + return Qt::Horizontal == Orientation; + } + + /** + * Called from viewport to forward event handling to this + */ + void handleViewportEvent(QEvent* e); +}; // struct AutoHideSideBarPrivate + + +/** + * This widget stores the tab buttons + */ +class CTabsWidget : public QWidget +{ +public: + using QWidget::QWidget; + using Super = QWidget; + AutoHideSideBarPrivate* EventHandler; + + /** + * Returns the size hint as minimum size hint + */ + virtual QSize minimumSizeHint() const override + { + return Super::sizeHint(); + } + + /** + * Forward event handling to EventHandler + */ + virtual bool event(QEvent* e) override + { + EventHandler->handleViewportEvent(e); + return Super::event(e); + } +}; + + +//============================================================================ +AutoHideSideBarPrivate::AutoHideSideBarPrivate(CAutoHideSideBar* _public) : + _this(_public) +{ +} + + +//============================================================================ +void AutoHideSideBarPrivate::handleViewportEvent(QEvent* e) +{ + switch (e->type()) + { + case QEvent::ChildRemoved: + if (TabsLayout->isEmpty()) + { + _this->hide(); + } + break; + + case QEvent::Resize: + if (_this->tabCount()) + { + auto ev = static_cast(e); + auto Tab = _this->tabAt(0); + int Size = isHorizontal() ? ev->size().height() : ev->size().width(); + int TabSize = isHorizontal() ? Tab->size().height() : Tab->size().width(); + // If the size of the side bar is less than the size of the first tab + // then there are no visible tabs in this side bar. This check will + // fail if someone will force a very big border via CSS!! + if (Size < TabSize) + { + _this->hide(); + } + } + else + { + _this->hide(); + } + break; + + default: + break; + } +} + + +//============================================================================ +CAutoHideSideBar::CAutoHideSideBar(CDockContainerWidget* parent, SideBarLocation area) : + Super(parent), + d(new AutoHideSideBarPrivate(this)) +{ + d->SideTabArea = area; + d->ContainerWidget = parent; + d->Orientation = (area == SideBarLocation::SideBarBottom || area == SideBarLocation::SideBarTop) + ? Qt::Horizontal : Qt::Vertical; + + setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + setFrameStyle(QFrame::NoFrame); + setWidgetResizable(true); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + d->TabsContainerWidget = new CTabsWidget(); + d->TabsContainerWidget->EventHandler = d; + d->TabsContainerWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + d->TabsContainerWidget->setObjectName("sideTabsContainerWidget"); + + + d->TabsLayout = new QBoxLayout(d->Orientation == Qt::Vertical ? QBoxLayout::TopToBottom : QBoxLayout::LeftToRight); + d->TabsLayout->setContentsMargins(0, 0, 0, 0); + d->TabsLayout->setSpacing(12); + d->TabsLayout->addStretch(1); + d->TabsContainerWidget->setLayout(d->TabsLayout); + setWidget(d->TabsContainerWidget); + + setFocusPolicy(Qt::NoFocus); + if (d->isHorizontal()) + { + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + } + else + { + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); + } + + hide(); +} + + +//============================================================================ +CAutoHideSideBar::~CAutoHideSideBar() +{ + ADS_PRINT("~CSideTabBar()"); + // The SideTabeBar is not the owner of the tabs and to prevent deletion + // we set the parent here to nullptr to remove it from the children + auto Tabs = findChildren(QString(), Qt::FindDirectChildrenOnly); + for (auto Tab : Tabs) + { + Tab->setParent(nullptr); + } + delete d; +} + + +//============================================================================ +void CAutoHideSideBar::insertTab(int Index, CAutoHideTab* SideTab) +{ + SideTab->setSideBar(this); + SideTab->installEventFilter(this); + if (Index < 0) + { + d->TabsLayout->insertWidget(d->TabsLayout->count() - 1, SideTab); + } + else + { + d->TabsLayout->insertWidget(Index, SideTab); + } + show(); +} + + +//============================================================================ +CAutoHideDockContainer* CAutoHideSideBar::insertDockWidget(int Index, CDockWidget* DockWidget) +{ + auto AutoHideContainer = new CAutoHideDockContainer(DockWidget, d->SideTabArea, d->ContainerWidget); + DockWidget->dockManager()->dockFocusController()->clearDockWidgetFocus(DockWidget); + auto Tab = AutoHideContainer->autoHideTab(); + DockWidget->setSideTabWidget(Tab); + insertTab(Index, Tab); + return AutoHideContainer; +} + + +//============================================================================ +void CAutoHideSideBar::removeTab(CAutoHideTab* SideTab) +{ + SideTab->removeEventFilter(this); + d->TabsLayout->removeWidget(SideTab); + if (d->TabsLayout->isEmpty()) + { + hide(); + } +} + + +//============================================================================ +bool CAutoHideSideBar::eventFilter(QObject *watched, QEvent *event) +{ + if (event->type() != QEvent::ShowToParent) + { + return false; + } + + // As soon as on tab is shown, we need to show the side tab bar + auto Tab = qobject_cast(watched); + if (Tab) + { + show(); + } + return false; +} + +//============================================================================ +Qt::Orientation CAutoHideSideBar::orientation() const +{ + return d->Orientation; +} + + +//============================================================================ +CAutoHideTab* CAutoHideSideBar::tabAt(int index) const +{ + return qobject_cast(d->TabsLayout->itemAt(index)->widget()); +} + + +//============================================================================ +int CAutoHideSideBar::tabCount() const +{ + return d->TabsLayout->count() - 1; +} + + +//============================================================================ +SideBarLocation CAutoHideSideBar::sideBarLocation() const +{ + return d->SideTabArea; +} + + +//============================================================================ +void CAutoHideSideBar::saveState(QXmlStreamWriter& s) const +{ + if (!tabCount()) + { + return; + } + + s.writeStartElement("SideBar"); + s.writeAttribute("Area", QString::number(sideBarLocation())); + s.writeAttribute("Tabs", QString::number(tabCount())); + + for (auto i = 0; i < tabCount(); ++i) + { + auto Tab = tabAt(i); + if (!Tab) + { + continue; + } + + Tab->dockWidget()->autoHideDockContainer()->saveState(s); + } + + s.writeEndElement(); +} + +//=========================================================================== +QSize CAutoHideSideBar::minimumSizeHint() const +{ + QSize Size = sizeHint(); + Size.setWidth(10); + return Size; +} + + +//=========================================================================== +QSize CAutoHideSideBar::sizeHint() const +{ + return d->TabsContainerWidget->sizeHint(); +} + + +//=========================================================================== +int CAutoHideSideBar::spacing() const +{ + return d->TabsLayout->spacing(); +} + +//=========================================================================== +void CAutoHideSideBar::setSpacing(int Spacing) +{ + d->TabsLayout->setSpacing(Spacing); +} + +} // namespace ads + diff --git a/src/AutoHideSideBar.h b/src/AutoHideSideBar.h new file mode 100644 index 000000000..b452ee023 --- /dev/null +++ b/src/AutoHideSideBar.h @@ -0,0 +1,156 @@ +#ifndef AutoHideSideBarH +#define AutoHideSideBarH +/******************************************************************************* +** Qt Advanced Docking System +** Copyright (C) 2017 Uwe Kindler +** +** This library is free software; you can redistribute it and/or +** modify it under the terms of the GNU Lesser General Public +** License as published by the Free Software Foundation; either +** version 2.1 of the License, or (at your option) any later version. +** +** This library is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public +** License along with this library; If not, see . +******************************************************************************/ + + +//============================================================================ +/// \file AutoHideSideBar.h +/// \author Syarif Fakhri +/// \date 05.09.2022 +/// \brief Declaration of CAutoHideSideBar class +//============================================================================ + +//============================================================================ +// INCLUDES +//============================================================================ +#include +#include "ads_globals.h" +#include "AutoHideTab.h" + +class QXmlStreamWriter; + +namespace ads +{ +struct AutoHideSideBarPrivate; +class DockContainerWidgetPrivate; +class CDockContainerWidget; +class CAutoHideTab; +class CAutoHideDockContainer; +class CDockingStateReader; + +/** + * Side tab bar widget that is shown at the edges of a dock container. + * The tab bar is only visible, if it contains visible content, that means if + * it contains visible tabs. If it is empty or all tabs are hidden, then the + * side bar is also hidden. As soon as one single tab becomes visible, this + * tab bar will be shown. + * The CAutoHideSideBar uses a QScrollArea here, to enable proper resizing. + * If the side bar contains many tabs, then the tabs are simply clipped - this + * is the same like in visual studio + */ +class ADS_EXPORT CAutoHideSideBar : public QScrollArea +{ + Q_OBJECT + Q_PROPERTY(int sideBarLocation READ sideBarLocation) + Q_PROPERTY(Qt::Orientation orientation READ orientation) + Q_PROPERTY(int spacing READ spacing WRITE setSpacing) + +private: + AutoHideSideBarPrivate* d; ///< private data (pimpl) + friend struct AutoHideSideBarPrivate; + friend class DockWidgetSideTab; + friend DockContainerWidgetPrivate; + +protected: + virtual bool eventFilter(QObject *watched, QEvent *event) override; + + /** + * Saves the state into the given stream + */ + void saveState(QXmlStreamWriter& Stream) const; + + /** + * Inserts the given dock widget tab at the given position. + * An Index value of -1 appends the side tab at the end. + */ + void insertTab(int Index, CAutoHideTab* SideTab); + +public: + using Super = QScrollArea; + + /** + * Default Constructor + */ + CAutoHideSideBar(CDockContainerWidget* parent, SideBarLocation area); + + /** + * Virtual Destructor + */ + virtual ~CAutoHideSideBar(); + + /** + * Removes the given DockWidgetSideTab from the tabbar + */ + void removeTab(CAutoHideTab* SideTab); + + /** + * Insert dock widget into the side bar. + * The function creates the auto hide dock container, inserts the + * auto hide tab + */ + CAutoHideDockContainer* insertDockWidget(int Index, CDockWidget* DockWidget); + + /** + * Returns orientation of side tab. + */ + Qt::Orientation orientation() const; + + /* + * get the side tab widget at position, returns nullptr if it's out of bounds + */ + CAutoHideTab* tabAt(int index) const; + + /* + * Gets the count of the tab widgets + */ + int tabCount() const; + + /** + * Getter for side tab bar area property + */ + SideBarLocation sideBarLocation() const; + + /** + * Overrides the minimumSizeHint() function of QScrollArea + * The minimumSizeHint() is bigger than the sizeHint () for the scroll + * area because even if the scrollbars are invisible, the required speace + * is reserved in the minimumSizeHint(). This override simply returns + * sizeHint(); + */ + virtual QSize minimumSizeHint() const override; + + /** + * The function provides a sizeHint that matches the height of the + * internal viewport. + */ + virtual QSize sizeHint() const override; + + /** + * Getter for spacing property - returns the spacing of the tabs + */ + int spacing() const; + + /** + * Setter for spacing property - sets the spacing + */ + void setSpacing(int Spacing); +}; +} // namespace ads +//----------------------------------------------------------------------------- +#endif diff --git a/src/AutoHideTab.cpp b/src/AutoHideTab.cpp new file mode 100644 index 000000000..72bbf0c22 --- /dev/null +++ b/src/AutoHideTab.cpp @@ -0,0 +1,274 @@ +/******************************************************************************* +** Qt Advanced Docking System +** Copyright (C) 2017 Uwe Kindler +** +** This library is free software; you can redistribute it and/or +** modify it under the terms of the GNU Lesser General Public +** License as published by the Free Software Foundation; either +** version 2.1 of the License, or (at your option) any later version. +** +** This library is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public +** License along with this library; If not, see . +******************************************************************************/ + + +//============================================================================ +/// \file AutoHideTab.cpp +/// \author Syarif Fakhri +/// \date 05.09.2022 +/// \brief Implementation of CAutoHideTab class +//============================================================================ + +//============================================================================ +// INCLUDES +//============================================================================ +#include "AutoHideTab.h" + +#include +#include +#include + +#include "AutoHideDockContainer.h" +#include "AutoHideSideBar.h" +#include "DockAreaWidget.h" +#include "DockManager.h" +#include "DockWidget.h" + +namespace ads +{ +/** + * Private data class of CDockWidgetTab class (pimpl) + */ +struct AutoHideTabPrivate +{ + CAutoHideTab* _this; + CDockWidget* DockWidget = nullptr; + CAutoHideSideBar* SideBar = nullptr; + Qt::Orientation Orientation{Qt::Vertical}; + QElapsedTimer TimeSinceHoverMousePress; + + /** + * Private data constructor + */ + AutoHideTabPrivate(CAutoHideTab* _public); + + /** + * Update the orientation, visibility and spacing based on the area of + * the side bar + */ + void updateOrientation(); + + /** + * Convenience function to ease dock container access + */ + CDockContainerWidget* dockContainer() const + { + return DockWidget ? DockWidget->dockContainer() : nullptr; + } + + /** + * Forward this event to the dock container + */ + void forwardEventToDockContainer(QEvent* event) + { + auto DockContainer = dockContainer(); + if (DockContainer) + { + DockContainer->handleAutoHideWidgetEvent(event, _this); + } + } +}; // struct DockWidgetTabPrivate + + +//============================================================================ +AutoHideTabPrivate::AutoHideTabPrivate(CAutoHideTab* _public) : + _this(_public) +{ + +} + + +//============================================================================ +void AutoHideTabPrivate::updateOrientation() +{ + bool IconOnly = CDockManager::testAutoHideConfigFlag(CDockManager::AutoHideSideBarsIconOnly); + if (IconOnly && !_this->icon().isNull()) + { + _this->setText(""); + _this->setOrientation(Qt::Horizontal); + } + else + { + auto area = SideBar->sideBarLocation(); + _this->setOrientation((area == SideBarBottom || area == SideBarTop) ? Qt::Horizontal : Qt::Vertical); + } +} + + +//============================================================================ +void CAutoHideTab::setSideBar(CAutoHideSideBar* SideTabBar) +{ + d->SideBar = SideTabBar; + if (d->SideBar) + { + d->updateOrientation(); + } +} + + +//============================================================================ +void CAutoHideTab::removeFromSideBar() +{ + if (d->SideBar == nullptr) + { + return; + } + d->SideBar->removeTab(this); + setSideBar(nullptr); +} + +//============================================================================ +CAutoHideTab::CAutoHideTab(QWidget* parent) : + CPushButton(parent), + d(new AutoHideTabPrivate(this)) +{ + setAttribute(Qt::WA_NoMousePropagation); + setFocusPolicy(Qt::NoFocus); +} + + +//============================================================================ +CAutoHideTab::~CAutoHideTab() +{ + ADS_PRINT("~CDockWidgetSideTab()"); + delete d; +} + + +//============================================================================ +void CAutoHideTab::updateStyle() +{ + internal::repolishStyle(this, internal::RepolishDirectChildren); + update(); +} + + +//============================================================================ +SideBarLocation CAutoHideTab::sideBarLocation() const +{ + if (d->SideBar) + { + return d->SideBar->sideBarLocation(); + } + + return SideBarLeft; +} + + +//============================================================================ +void CAutoHideTab::setOrientation(Qt::Orientation Orientation) +{ + d->Orientation = Orientation; + if (orientation() == Qt::Horizontal) + { + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + } + else + { + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum); + } + CPushButton::setButtonOrientation((Qt::Horizontal == Orientation) + ? CPushButton::Horizontal : CPushButton::VerticalTopToBottom); + updateStyle(); +} + + +//============================================================================ +Qt::Orientation CAutoHideTab::orientation() const +{ + return d->Orientation; +} + + +//============================================================================ +bool CAutoHideTab::isActiveTab() const +{ + if (d->DockWidget && d->DockWidget->autoHideDockContainer()) + { + return d->DockWidget->autoHideDockContainer()->isVisible(); + } + + return false; +} + + +//============================================================================ +CDockWidget* CAutoHideTab::dockWidget() const +{ + return d->DockWidget; +} + + +//============================================================================ +void CAutoHideTab::setDockWidget(CDockWidget* DockWidget) +{ + if (!DockWidget) + { + return; + } + d->DockWidget = DockWidget; + setText(DockWidget->windowTitle()); + setIcon(d->DockWidget->icon()); + setToolTip(DockWidget->windowTitle()); +} + + +//============================================================================ +bool CAutoHideTab::event(QEvent* event) +{ + if (!CDockManager::testAutoHideConfigFlag(CDockManager::AutoHideShowOnMouseOver)) + { + return Super::event(event); + } + + switch (event->type()) + { + case QEvent::Enter: + case QEvent::Leave: + d->forwardEventToDockContainer(event); + break; + + case QEvent::MouseButtonPress: + // If AutoHideShowOnMouseOver is active, then the showing is triggered + // by a MousePressEvent sent to this tab. To prevent accidental hiding + // of the tab by a mouse click, we wait at least 500 ms before we accept + // the mouse click + if (!event->spontaneous()) + { + d->TimeSinceHoverMousePress.restart(); + d->forwardEventToDockContainer(event); + } + else if (d->TimeSinceHoverMousePress.hasExpired(500)) + { + d->forwardEventToDockContainer(event); + } + break; + + default: + break; + } + return Super::event(event); +} + +//============================================================================ +bool CAutoHideTab::iconOnly() const +{ + return CDockManager::testAutoHideConfigFlag(CDockManager::AutoHideSideBarsIconOnly) && !icon().isNull(); +} + +} diff --git a/src/AutoHideTab.h b/src/AutoHideTab.h new file mode 100644 index 000000000..fec49cd00 --- /dev/null +++ b/src/AutoHideTab.h @@ -0,0 +1,134 @@ +#ifndef AutoHideTabH +#define AutoHideTabH +/******************************************************************************* +** Qt Advanced Docking System +** Copyright (C) 2017 Uwe Kindler +** +** This library is free software; you can redistribute it and/or +** modify it under the terms of the GNU Lesser General Public +** License as published by the Free Software Foundation; either +** version 2.1 of the License, or (at your option) any later version. +** +** This library is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public +** License along with this library; If not, see . +******************************************************************************/ + + +//============================================================================ +/// \file AutoHideTab.h +/// \author Syarif Fakhri +/// \date 05.09.2022 +/// \brief Declaration of CAutoHideTab class +//============================================================================ + +//============================================================================ +// INCLUDES +//============================================================================ +#include "PushButton.h" + +#include "ads_globals.h" + +namespace ads +{ +struct AutoHideTabPrivate; +class CDockWidget; +class CAutoHideSideBar; +class CDockWidgetTab; +struct SideTabIconLabelPrivate; + +/** + * A dock widget Side tab that shows a title or an icon. + * The dock widget tab is shown in the side tab bar to switch between + * pinned dock widgets + */ +class ADS_EXPORT CAutoHideTab : public CPushButton +{ + Q_OBJECT + + Q_PROPERTY(int sideBarLocation READ sideBarLocation) + Q_PROPERTY(Qt::Orientation orientation READ orientation) + Q_PROPERTY(bool activeTab READ isActiveTab) + Q_PROPERTY(bool iconOnly READ iconOnly) + +private: + AutoHideTabPrivate* d; ///< private data (pimpl) + friend struct AutoHideTabPrivate; + friend class CDockWidget; + friend class CAutoHideDockContainer; + +protected: + friend class CAutoHideSideBar; + friend class CDockAreaWidget; + friend class CDockContainerWidget; + + void setSideBar(CAutoHideSideBar *SideTabBar); + void removeFromSideBar(); + virtual bool event(QEvent* event) override; + +public: + using Super = CPushButton; + + /** + * Default Constructor + * param[in] DockWidget The dock widget this title bar belongs to + * param[in] parent The parent widget of this title bar + */ + CAutoHideTab(QWidget* parent = nullptr); + + /** + * Virtual Destructor + */ + virtual ~CAutoHideTab(); + + /** + * Update stylesheet style if a property changes + */ + void updateStyle(); + + /** + * Getter for side tab bar area property + */ + SideBarLocation sideBarLocation() const; + + /** + * Set orientation vertical or horizontal + */ + void setOrientation(Qt::Orientation Orientation); + + /** + * Returns the current orientation + */ + Qt::Orientation orientation() const; + + /** + * Returns true, if this is the active tab. The tab is active if the auto + * hide widget is visible + */ + bool isActiveTab() const; + + /** + * returns the dock widget this belongs to + */ + CDockWidget* dockWidget() const; + + /** + * Sets the dock widget that is controlled by this tab + */ + void setDockWidget(CDockWidget* DockWidget); + + /** + * Returns true if the auto hide config flag AutoHideSideBarsIconOnly + * is set and if the tab has an icon - that means the icon is not null + */ + bool iconOnly() const; +}; // class AutoHideTab +} + // namespace ads +//----------------------------------------------------------------------------- + +#endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0c4d671ab..ba62e48e3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -27,6 +27,11 @@ set(ads_SRCS FloatingDragPreview.cpp IconProvider.cpp DockComponentsFactory.cpp + AutoHideSideBar.cpp + AutoHideTab.cpp + AutoHideDockContainer.cpp + PushButton.cpp + ResizeHandle.cpp ads.qrc ) set(ads_HEADERS @@ -48,6 +53,11 @@ set(ads_HEADERS FloatingDragPreview.h IconProvider.h DockComponentsFactory.h + AutoHideSideBar.h + AutoHideTab.h + AutoHideDockContainer.h + PushButton.h + ResizeHandle.h ) add_compile_options("$<$:/utf-8>") if (UNIX AND NOT APPLE) diff --git a/src/DockAreaTitleBar.cpp b/src/DockAreaTitleBar.cpp index 8651d2994..02796afd8 100644 --- a/src/DockAreaTitleBar.cpp +++ b/src/DockAreaTitleBar.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include "DockAreaTitleBar_p.h" #include "ads_globals.h" @@ -49,14 +50,17 @@ #include "DockWidget.h" #include "DockWidgetTab.h" #include "DockAreaTabBar.h" -#include "IconProvider.h" #include "DockComponentsFactory.h" #include "DockFocusController.h" +#include "ElidingLabel.h" +#include "AutoHideDockContainer.h" +#include "IconProvider.h" #include namespace ads { +static const char* const LocationProperty = "Location"; /** * Private data class of CDockAreaTitleBar class (pimpl) @@ -65,11 +69,13 @@ struct DockAreaTitleBarPrivate { CDockAreaTitleBar* _this; QPointer TabsMenuButton; + QPointer AutoHideButton; QPointer UndockButton; QPointer CloseButton; QBoxLayout* Layout; CDockAreaWidget* DockArea; CDockAreaTabBar* TabBar; + CElidingLabel* AutoHideTitleLabel; bool MenuOutdated = true; QMenu* TabsMenu; QList DockWidgetActionsButtons; @@ -89,7 +95,13 @@ struct DockAreaTitleBarPrivate */ void createButtons(); + /** + * Creates the auto hide title label, only displayed when the dock area is overlayed + */ + void createAutoHideTitleLabel(); + + /** * Creates the internal TabBar */ void createTabBar(); @@ -111,6 +123,15 @@ struct DockAreaTitleBarPrivate return CDockManager::testConfigFlag(Flag); } + /** + * Returns true if the given config flag is set + * Convenience function to ease config flag testing + */ + static bool testAutoHideConfigFlag(CDockManager::eAutoHideFlag Flag) + { + return CDockManager::testAutoHideConfigFlag(Flag); + } + /** * Test function for current drag state */ @@ -119,7 +140,6 @@ struct DockAreaTitleBarPrivate return this->DragState == dragState; } - /** * Starts floating */ @@ -129,6 +149,19 @@ struct DockAreaTitleBarPrivate * Makes the dock area floating */ IFloatingWidget* makeAreaFloating(const QPoint& Offset, eDragState DragState); + + /** + * Helper function to create and initialize the menu entries for + * the "Auto Hide Group To..." menu + */ + QAction* createAutoHideToAction(const QString& Title, SideBarLocation Location, + QMenu* Menu) + { + auto Action = Menu->addAction(Title); + Action->setProperty("Location", Location); + QObject::connect(Action, &QAction::triggered, _this, &CDockAreaTitleBar::onAutoHideToActionClicked); + return Action; + } };// struct DockAreaTitleBarPrivate //============================================================================ @@ -172,19 +205,25 @@ void DockAreaTitleBarPrivate::createButtons() Layout->addWidget(UndockButton, 0); _this->connect(UndockButton, SIGNAL(clicked()), SLOT(onUndockButtonClicked())); + // AutoHide Button + const auto autoHideEnabled = testAutoHideConfigFlag(CDockManager::AutoHideFeatureEnabled); + AutoHideButton = new CTitleBarButton(testAutoHideConfigFlag(CDockManager::DockAreaHasAutoHideButton) && autoHideEnabled); + AutoHideButton->setObjectName("dockAreaAutoHideButton"); + AutoHideButton->setAutoRaise(true); + internal::setToolTip(AutoHideButton, _this->titleBarButtonToolTip(TitleBarButtonAutoHide)); + internal::setButtonIcon(AutoHideButton, QStyle::SP_DialogOkButton, ads::AutoHideIcon); + AutoHideButton->setSizePolicy(ButtonSizePolicy); + AutoHideButton->setCheckable(testAutoHideConfigFlag(CDockManager::AutoHideButtonCheckable)); + AutoHideButton->setChecked(false); + Layout->addWidget(AutoHideButton, 0); + _this->connect(AutoHideButton, SIGNAL(clicked()), SLOT(onAutoHideButtonClicked())); + // Close button CloseButton = new CTitleBarButton(testConfigFlag(CDockManager::DockAreaHasCloseButton)); CloseButton->setObjectName("dockAreaCloseButton"); CloseButton->setAutoRaise(true); internal::setButtonIcon(CloseButton, QStyle::SP_TitleBarCloseButton, ads::DockAreaCloseIcon); - if (testConfigFlag(CDockManager::DockAreaCloseButtonClosesTab)) - { - internal::setToolTip(CloseButton, QObject::tr("Close Active Tab")); - } - else - { - internal::setToolTip(CloseButton, QObject::tr("Close Group")); - } + internal::setToolTip(CloseButton, _this->titleBarButtonToolTip(TitleBarButtonClose)); CloseButton->setSizePolicy(ButtonSizePolicy); CloseButton->setIconSize(QSize(16, 16)); Layout->addWidget(CloseButton, 0); @@ -192,6 +231,15 @@ void DockAreaTitleBarPrivate::createButtons() } +//============================================================================ +void DockAreaTitleBarPrivate::createAutoHideTitleLabel() +{ + AutoHideTitleLabel = new CElidingLabel(""); + AutoHideTitleLabel->setObjectName("autoHideTitleLabel"); + Layout->addWidget(AutoHideTitleLabel); +} + + //============================================================================ void DockAreaTitleBarPrivate::createTabBar() { @@ -249,6 +297,10 @@ IFloatingWidget* DockAreaTitleBarPrivate::makeAreaFloating(const QPoint& Offset, //============================================================================ void DockAreaTitleBarPrivate::startFloating(const QPoint& Offset) { + if (DockArea->autoHideDockContainer() != nullptr) + { + DockArea->autoHideDockContainer()->hide(); + } FloatingWidget = makeAreaFloating(Offset, DraggingFloatingWidget); } @@ -268,6 +320,8 @@ CDockAreaTitleBar::CDockAreaTitleBar(CDockAreaWidget* parent) : setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); d->createTabBar(); + d->createAutoHideTitleLabel(); + d->AutoHideTitleLabel->setVisible(false); // Default hidden d->Layout->addWidget(new CSpacerWidget(this)); d->createButtons(); @@ -373,6 +427,11 @@ void CDockAreaTitleBar::onUndockButtonClicked() { if (d->DockArea->features().testFlag(CDockWidget::DockWidgetFloatable)) { + if (d->DockArea->autoHideDockContainer()) + { + d->DockArea->autoHideDockContainer()->cleanupAndDelete(); + } + d->makeAreaFloating(mapFromGlobal(QCursor::pos()), DraggingInactive); } } @@ -390,7 +449,14 @@ void CDockAreaTitleBar::onTabsMenuActionTriggered(QAction* Action) //============================================================================ void CDockAreaTitleBar::updateDockWidgetActionsButtons() { - CDockWidget* DockWidget = d->TabBar->currentTab()->dockWidget(); + auto* const currentTab = d->TabBar->currentTab(); + if (currentTab == nullptr) + { + // It's possible for this to be nullptr when restoring state + return; + } + + CDockWidget* DockWidget = currentTab->dockWidget(); if (!d->DockWidgetActionsButtons.isEmpty()) { for (auto Button : d->DockWidgetActionsButtons) @@ -439,6 +505,36 @@ void CDockAreaTitleBar::onCurrentTabChanged(int Index) } +//============================================================================ +void CDockAreaTitleBar::onAutoHideButtonClicked() +{ + if (CDockManager::testAutoHideConfigFlag(CDockManager::AutoHideButtonTogglesArea) + || qApp->keyboardModifiers().testFlag(Qt::ControlModifier)) + { + d->DockArea->toggleAutoHide(); + } + else + { + d->DockArea->currentDockWidget()->toggleAutoHide(); + } +} + + +//============================================================================ +void CDockAreaTitleBar::onAutoHideDockAreaActionClicked() +{ + d->DockArea->toggleAutoHide(); +} + + +//============================================================================ +void CDockAreaTitleBar::onAutoHideToActionClicked() +{ + int Location = sender()->property(LocationProperty).toInt(); + d->DockArea->toggleAutoHide((SideBarLocation)Location); +} + + //============================================================================ QAbstractButton* CDockAreaTitleBar::button(TitleBarButton which) const { @@ -447,12 +543,20 @@ QAbstractButton* CDockAreaTitleBar::button(TitleBarButton which) const case TitleBarButtonTabsMenu: return d->TabsMenuButton; case TitleBarButtonUndock: return d->UndockButton; case TitleBarButtonClose: return d->CloseButton; + case TitleBarButtonAutoHide: return d->AutoHideButton; default: return nullptr; } } +//============================================================================ +CElidingLabel* CDockAreaTitleBar::autoHideTitleLabel() const +{ + return d->AutoHideTitleLabel; +} + + //============================================================================ void CDockAreaTitleBar::setVisible(bool Visible) { @@ -472,7 +576,6 @@ void CDockAreaTitleBar::mousePressEvent(QMouseEvent* ev) if (CDockManager::testConfigFlag(CDockManager::FocusHighlighting)) { - //d->TabBar->currentTab()->setFocus(Qt::OtherFocusReason); d->dockManager()->dockFocusController()->setDockWidgetTabFocused(d->TabBar->currentTab()); } return; @@ -523,7 +626,8 @@ void CDockAreaTitleBar::mouseMoveEvent(QMouseEvent* ev) // sense to move it to a new floating widget and leave this one // empty if (d->DockArea->dockContainer()->isFloating() - && d->DockArea->dockContainer()->visibleDockAreaCount() == 1) + && d->DockArea->dockContainer()->visibleDockAreaCount() == 1 + && !d->DockArea->isAutoHide()) { return; } @@ -542,7 +646,7 @@ void CDockAreaTitleBar::mouseMoveEvent(QMouseEvent* ev) int DragDistance = (d->DragStartMousePos - ev->pos()).manhattanLength(); if (DragDistance >= CDockManager::startDragDistance()) { - ADS_PRINT("CDockAreaTitlBar::startFloating"); + ADS_PRINT("CDockAreaTitleBar::startFloating"); d->startFloating(d->DragStartMousePos); auto Overlay = d->DockArea->dockManager()->containerOverlay(); Overlay->setAllowedAreas(OuterDockAreas); @@ -567,6 +671,11 @@ void CDockAreaTitleBar::mouseDoubleClickEvent(QMouseEvent *event) { return; } + if (d->DockArea->autoHideDockContainer()) + { + d->DockArea->autoHideDockContainer()->cleanupAndDelete(); + } + d->makeAreaFloating(event->pos(), DraggingInactive); } @@ -580,13 +689,39 @@ void CDockAreaTitleBar::contextMenuEvent(QContextMenuEvent* ev) return; } + const bool isAutoHide = d->DockArea->isAutoHide(); + const bool isTopLevelArea = d->DockArea->isTopLevelArea(); + QAction* Action; QMenu Menu(this); - auto Action = Menu.addAction(tr("Detach Group"), this, SLOT(onUndockButtonClicked())); - Action->setEnabled(d->DockArea->features().testFlag(CDockWidget::DockWidgetFloatable)); - Menu.addSeparator(); - Action = Menu.addAction(tr("Close Group"), this, SLOT(onCloseButtonClicked())); + if (!isTopLevelArea) + { + Action = Menu.addAction(isAutoHide ? tr("Detach") : tr("Detach Group"), + this, SLOT(onUndockButtonClicked())); + Action->setEnabled(d->DockArea->features().testFlag(CDockWidget::DockWidgetFloatable)); + if (CDockManager::testAutoHideConfigFlag(CDockManager::AutoHideFeatureEnabled)) + { + Action = Menu.addAction(isAutoHide ? tr("Unpin (Dock)") : tr("Pin Group"), this, SLOT(onAutoHideDockAreaActionClicked())); + auto AreaIsPinnable = d->DockArea->features().testFlag(CDockWidget::DockWidgetPinnable); + Action->setEnabled(AreaIsPinnable); + + if (!isAutoHide) + { + auto menu = Menu.addMenu(tr("Pin Group To...")); + menu->setEnabled(AreaIsPinnable); + d->createAutoHideToAction(tr("Top"), SideBarTop, menu); + d->createAutoHideToAction(tr("Left"), SideBarLeft, menu); + d->createAutoHideToAction(tr("Right"), SideBarRight, menu); + d->createAutoHideToAction(tr("Bottom"), SideBarBottom, menu); + } + } + Menu.addSeparator(); + } + Action = Menu.addAction(isAutoHide ? tr("Close") : tr("Close Group"), this, SLOT(onCloseButtonClicked())); Action->setEnabled(d->DockArea->features().testFlag(CDockWidget::DockWidgetClosable)); - Menu.addAction(tr("Close Other Groups"), d->DockArea, SLOT(closeOtherAreas())); + if (!isAutoHide && !isTopLevelArea) + { + Action = Menu.addAction(tr("Close Other Groups"), d->DockArea, SLOT(closeOtherAreas())); + } Menu.exec(ev->globalPos()); } @@ -604,6 +739,50 @@ int CDockAreaTitleBar::indexOf(QWidget *widget) const return d->Layout->indexOf(widget); } +//============================================================================ +QString CDockAreaTitleBar::titleBarButtonToolTip(TitleBarButton Button) const +{ + switch (Button) + { + case TitleBarButtonAutoHide: + if (d->DockArea->isAutoHide()) + { + return tr("Unpin (Dock)"); + } + + if (CDockManager::testAutoHideConfigFlag(CDockManager::AutoHideButtonTogglesArea)) + { + return tr("Pin Group"); + } + else + { + return tr("Pin Active Tab (Press Ctrl to Pin Group)"); + } + break; + + case TitleBarButtonClose: + if (d->DockArea->isAutoHide()) + { + return tr("Close"); + } + + if (CDockManager::testConfigFlag(CDockManager::DockAreaCloseButtonClosesTab)) + { + return tr("Close Active Tab"); + } + else + { + return tr("Close Group"); + } + break; + + default: + break; + } + + return QString(); +} + //============================================================================ CTitleBarButton::CTitleBarButton(bool visible, QWidget* parent) : tTitleBarButton(parent), diff --git a/src/DockAreaTitleBar.h b/src/DockAreaTitleBar.h index 37f3a9182..a31464034 100644 --- a/src/DockAreaTitleBar.h +++ b/src/DockAreaTitleBar.h @@ -41,6 +41,7 @@ namespace ads class CDockAreaTabBar; class CDockAreaWidget; struct DockAreaTitleBarPrivate; +class CElidingLabel; /** * Title bar of a dock area. @@ -60,9 +61,12 @@ private Q_SLOTS: void onUndockButtonClicked(); void onTabsMenuActionTriggered(QAction* Action); void onCurrentTabChanged(int Index); + void onAutoHideButtonClicked(); + void onAutoHideDockAreaActionClicked(); + void onAutoHideToActionClicked(); protected: - /** + /** * Stores mouse position to detect dragging */ virtual void mousePressEvent(QMouseEvent* ev) override; @@ -119,6 +123,11 @@ public Q_SLOTS: */ QAbstractButton* button(TitleBarButton which) const; + /** + * Returns the auto hide title label, used when the dock area is expanded and auto hidden + */ + CElidingLabel* autoHideTitleLabel() const; + /** * Updates the visibility of the dock widget actions in the title bar */ @@ -148,6 +157,12 @@ public Q_SLOTS: */ int indexOf(QWidget *widget) const; + /** + * Close group tool tip based on the current state + * Auto hide widgets can only have one dock widget so it does not make sense for the tooltip to show close group + */ + QString titleBarButtonToolTip(TitleBarButton Button) const; + Q_SIGNALS: /** * This signal is emitted if a tab in the tab bar is clicked by the user diff --git a/src/DockAreaWidget.cpp b/src/DockAreaWidget.cpp index a8d4297e6..93c816b56 100644 --- a/src/DockAreaWidget.cpp +++ b/src/DockAreaWidget.cpp @@ -28,22 +28,21 @@ //============================================================================ // INCLUDES //============================================================================ +#include +#include #include "DockAreaWidget.h" #include #include -#include #include #include #include #include #include -#include #include -#include #include - +#include "ElidingLabel.h" #include "DockContainerWidget.h" #include "DockWidget.h" #include "FloatingDockContainer.h" @@ -54,6 +53,7 @@ #include "DockAreaTitleBar.h" #include "DockComponentsFactory.h" #include "DockWidgetTab.h" +#include "DockingStateReader.h" namespace ads @@ -61,6 +61,15 @@ namespace ads static const char* const INDEX_PROPERTY = "index"; static const char* const ACTION_PROPERTY = "action"; +/** + * Check, if auto hide is enabled + */ +static bool isAutoHideFeatureEnabled() +{ + return CDockManager::testAutoHideConfigFlag(CDockManager::AutoHideFeatureEnabled); +} + + /** * Internal dock area layout mimics stack layout but only inserts the current * widget into the internal QLayout object. @@ -249,6 +258,7 @@ struct DockAreaWidgetPrivate DockAreaLayout* ContentsLayout = nullptr; CDockAreaTitleBar* TitleBar = nullptr; CDockManager* DockManager = nullptr; + CAutoHideDockContainer* AutoHideDockContainer = nullptr; bool UpdateTitleBarButtons = false; DockWidgetAreas AllowedAreas = DefaultAllowedAreas; QSize MinSizeHint; @@ -310,6 +320,11 @@ struct DockAreaWidgetPrivate */ void updateTitleBarButtonStates(); + /** + * Udpates the enable state of the close and detach button + */ + void updateTitleBarButtonVisibility(bool isTopLevel); + /** * Scans all contained dock widgets for the max. minimum size hint */ @@ -358,11 +373,40 @@ void DockAreaWidgetPrivate::updateTitleBarButtonStates() _this->features().testFlag(CDockWidget::DockWidgetClosable)); TitleBar->button(TitleBarButtonUndock)->setEnabled( _this->features().testFlag(CDockWidget::DockWidgetFloatable)); + TitleBar->button(TitleBarButtonAutoHide)->setEnabled( + _this->features().testFlag(CDockWidget::DockWidgetPinnable)); TitleBar->updateDockWidgetActionsButtons(); UpdateTitleBarButtons = false; } +//============================================================================ +void DockAreaWidgetPrivate::updateTitleBarButtonVisibility(bool IsTopLevel) +{ + auto *const container = _this->dockContainer(); + if (!container) + { + return; + } + + if (IsTopLevel) + { + TitleBar->button(TitleBarButtonClose)->setVisible(!container->isFloating()); + TitleBar->button(TitleBarButtonAutoHide)->setVisible(!container->isFloating()); + // Undock and tabs should never show when auto hidden + TitleBar->button(TitleBarButtonUndock)->setVisible(!container->isFloating() && !_this->isAutoHide()); + TitleBar->button(TitleBarButtonTabsMenu)->setVisible(!_this->isAutoHide()); + } + else + { + TitleBar->button(TitleBarButtonClose)->setVisible(true); + TitleBar->button(TitleBarButtonAutoHide)->setVisible(true); + TitleBar->button(TitleBarButtonUndock)->setVisible(!_this->isAutoHide()); + TitleBar->button(TitleBarButtonTabsMenu)->setVisible(!_this->isAutoHide()); + } +} + + //============================================================================ CDockAreaWidget::CDockAreaWidget(CDockManager* DockManager, CDockContainerWidget* parent) : QFrame(parent), @@ -404,6 +448,26 @@ CDockContainerWidget* CDockAreaWidget::dockContainer() const return internal::findParent(this); } +//============================================================================ +CAutoHideDockContainer* CDockAreaWidget::autoHideDockContainer() const +{ + return d->AutoHideDockContainer; +} + +//============================================================================ +bool CDockAreaWidget::isAutoHide() const +{ + return d->AutoHideDockContainer != nullptr; +} + +//============================================================================ +void CDockAreaWidget::setAutoHideDockContainer(CAutoHideDockContainer* AutoHideDockContainer) +{ + d->AutoHideDockContainer = AutoHideDockContainer; + updateAutoHideButtonCheckState(); + updateTitleBarButtonsToolTips(); +} + //============================================================================ void CDockAreaWidget::addDockWidget(CDockWidget* DockWidget) @@ -426,12 +490,14 @@ void CDockAreaWidget::insertDockWidget(int index, CDockWidget* DockWidget, d->tabBar()->insertTab(index, TabWidget); d->tabBar()->blockSignals(false); TabWidget->setVisible(!DockWidget->isClosed()); + d->TitleBar->autoHideTitleLabel()->setText(DockWidget->windowTitle()); DockWidget->setProperty(INDEX_PROPERTY, index); d->MinSizeHint.setHeight(qMax(d->MinSizeHint.height(), DockWidget->minimumSizeHint().height())); d->MinSizeHint.setWidth(qMax(d->MinSizeHint.width(), DockWidget->minimumSizeHint().width())); if (Activate) { setCurrentIndex(index); + DockWidget->setClosedState(false); // Set current index can show the widget without changing the close state, added to keep the close state consistent } // If this dock area is hidden, then we need to make it visible again // by calling DockWidget->toggleViewInternal(true); @@ -448,6 +514,15 @@ void CDockAreaWidget::insertDockWidget(int index, CDockWidget* DockWidget, void CDockAreaWidget::removeDockWidget(CDockWidget* DockWidget) { ADS_PRINT("CDockAreaWidget::removeDockWidget"); + + // If this dock area is in a auto hide container, then we can delete + // the auto hide container now + if (isAutoHide()) + { + autoHideDockContainer()->cleanupAndDelete(); + return; + } + auto CurrentDockWidget = currentDockWidget(); auto NextOpenDockWidget = (DockWidget == CurrentDockWidget) ? nextOpenDockWidget(DockWidget) : nullptr; @@ -530,6 +605,10 @@ void CDockAreaWidget::hideAreaWithNoVisibleContent() { FloatingWidget->hide(); } + if (isAutoHide()) + { + autoHideDockContainer()->hide(); + } } @@ -584,6 +663,7 @@ void CDockAreaWidget::internalSetCurrentDockWidget(CDockWidget* DockWidget) } setCurrentIndex(Index); + DockWidget->setClosedState(false); // Set current index can show the widget without changing the close state, added to keep the close state consistent } @@ -747,17 +827,27 @@ void CDockAreaWidget::updateTitleBarVisibility() return; } - if (CDockManager::testConfigFlag(CDockManager::AlwaysShowTabs)) + if (!d->TitleBar) { - return; + return; } - if (d->TitleBar) - { + bool IsAutoHide = isAutoHide(); + if (!CDockManager::testConfigFlag(CDockManager::AlwaysShowTabs)) + { bool Hidden = Container->hasTopLevelDockWidget() && (Container->isFloating() || CDockManager::testConfigFlag(CDockManager::HideSingleCentralWidgetTitleBar)); Hidden |= (d->Flags.testFlag(HideSingleWidgetTitleBar) && openDockWidgetsCount() == 1); + Hidden &= !IsAutoHide; // Titlebar must always be visible when auto hidden so it can be dragged d->TitleBar->setVisible(!Hidden); + } + + if (isAutoHideFeatureEnabled()) + { + auto tabBar = d->TitleBar->tabBar(); + tabBar->setVisible(!IsAutoHide); // Never show tab bar when auto hidden + d->TitleBar->autoHideTitleLabel()->setVisible(IsAutoHide); // Always show when auto hidden, never otherwise + updateTitleBarButtonVisibility(Container->topLevelDockArea() == this); } } @@ -772,6 +862,32 @@ void CDockAreaWidget::markTitleBarMenuOutdated() } +//============================================================================ +void CDockAreaWidget::updateAutoHideButtonCheckState() +{ + auto autoHideButton = titleBarButton(TitleBarButtonAutoHide); + autoHideButton->blockSignals(true); + autoHideButton->setChecked(isAutoHide()); + autoHideButton->blockSignals(false); +} + + +//============================================================================ +void CDockAreaWidget::updateTitleBarButtonVisibility(bool IsTopLevel) const +{ + d->updateTitleBarButtonVisibility(IsTopLevel); +} + + +//============================================================================ +void CDockAreaWidget::updateTitleBarButtonsToolTips() +{ + internal::setToolTip(titleBarButton(TitleBarButtonClose), + titleBar()->titleBarButtonToolTip(TitleBarButtonClose)); + internal::setToolTip(titleBarButton(TitleBarButtonAutoHide), + titleBar()->titleBarButtonToolTip(TitleBarButtonAutoHide)); +} + //============================================================================ void CDockAreaWidget::saveState(QXmlStreamWriter& s) const @@ -781,8 +897,7 @@ void CDockAreaWidget::saveState(QXmlStreamWriter& s) const auto CurrentDockWidget = currentDockWidget(); QString Name = CurrentDockWidget ? CurrentDockWidget->objectName() : ""; s.writeAttribute("Current", Name); - // To keep the saved XML data small, we only save the allowed areas and the - // dock area flags if the values are different from the default values + if (d->AllowedAreas != DefaultAllowedAreas) { s.writeAttribute("AllowedAreas", QString::number(d->AllowedAreas, 16)); @@ -802,6 +917,103 @@ void CDockAreaWidget::saveState(QXmlStreamWriter& s) const } +//============================================================================ +bool CDockAreaWidget::restoreState(CDockingStateReader& s, CDockAreaWidget*& CreatedWidget, + bool Testing, CDockContainerWidget* Container) +{ + bool Ok; +#ifdef ADS_DEBUG_PRINT + int Tabs = s.attributes().value("Tabs").toInt(&Ok); + if (!Ok) + { + return false; + } +#endif + + QString CurrentDockWidget = s.attributes().value("Current").toString(); + ADS_PRINT("Restore NodeDockArea Tabs: " << Tabs << " Current: " + << CurrentDockWidget); + + auto DockManager = Container->dockManager(); + CDockAreaWidget* DockArea = nullptr; + if (!Testing) + { + DockArea = new CDockAreaWidget(DockManager, Container); + const auto AllowedAreasAttribute = s.attributes().value("AllowedAreas"); + if (!AllowedAreasAttribute.isEmpty()) + { + DockArea->setAllowedAreas((DockWidgetArea)AllowedAreasAttribute.toInt(nullptr, 16)); + } + + const auto FlagsAttribute = s.attributes().value("Flags"); + if (!FlagsAttribute.isEmpty()) + { + DockArea->setDockAreaFlags((CDockAreaWidget::DockAreaFlags)FlagsAttribute.toInt(nullptr, 16)); + } + } + + while (s.readNextStartElement()) + { + if (s.name() != QLatin1String("Widget")) + { + continue; + } + + auto ObjectName = s.attributes().value("Name"); + if (ObjectName.isEmpty()) + { + return false; + } + + bool Closed = s.attributes().value("Closed").toInt(&Ok); + if (!Ok) + { + return false; + } + + s.skipCurrentElement(); + CDockWidget* DockWidget = DockManager->findDockWidget(ObjectName.toString()); + if (!DockWidget || Testing) + { + continue; + } + + ADS_PRINT("Dock Widget found - parent " << DockWidget->parent()); + if (DockWidget->autoHideDockContainer()) + { + DockWidget->autoHideDockContainer()->cleanupAndDelete(); + } + + // We hide the DockArea here to prevent the short display (the flashing) + // of the dock areas during application startup + DockArea->hide(); + DockArea->addDockWidget(DockWidget); + DockWidget->setToggleViewActionChecked(!Closed); + DockWidget->setClosedState(Closed); + DockWidget->setProperty(internal::ClosedProperty, Closed); + DockWidget->setProperty(internal::DirtyProperty, false); + } + + if (Testing) + { + return true; + } + + if (!DockArea->dockWidgetsCount()) + { + delete DockArea; + DockArea = nullptr; + } + else + { + DockArea->setProperty("currentDockWidget", CurrentDockWidget); + } + + CreatedWidget = DockArea; + return true; +} + + //============================================================================ CDockWidget* CDockAreaWidget::nextOpenDockWidget(CDockWidget* DockWidget) const { @@ -961,7 +1173,8 @@ void CDockAreaWidget::closeArea() // closeRequested signal and not actually delete unless the signal is handled in a way that deletes it auto OpenDockWidgets = openedDockWidgets(); if (OpenDockWidgets.count() == 1 && - (OpenDockWidgets[0]->features().testFlag(CDockWidget::DockWidgetDeleteOnClose) || OpenDockWidgets[0]->features().testFlag(CDockWidget::CustomCloseHandling))) + (OpenDockWidgets[0]->features().testFlag(CDockWidget::DockWidgetDeleteOnClose) || OpenDockWidgets[0]->features().testFlag(CDockWidget::CustomCloseHandling)) + && !isAutoHide()) { OpenDockWidgets[0]->closeDockWidgetInternal(); } @@ -969,16 +1182,172 @@ void CDockAreaWidget::closeArea() { for (auto DockWidget : openedDockWidgets()) { - if ((DockWidget->features().testFlag(CDockWidget::DockWidgetDeleteOnClose) && DockWidget->features().testFlag(CDockWidget::DockWidgetForceCloseWithArea)) || - DockWidget->features().testFlag(CDockWidget::CustomCloseHandling)) + if ((DockWidget->features().testFlag(CDockWidget::DockWidgetDeleteOnClose) && DockWidget->features().testFlag(CDockWidget::DockWidgetForceCloseWithArea)) || + DockWidget->features().testFlag(CDockWidget::CustomCloseHandling)) + { + DockWidget->closeDockWidgetInternal(); + } + else if (DockWidget->features().testFlag(CDockWidget::DockWidgetDeleteOnClose) && isAutoHide()) + { DockWidget->closeDockWidgetInternal(); + } else + { DockWidget->toggleView(false); + } } } } +enum eBorderLocation +{ + BorderNone = 0, + BorderLeft = 0x01, + BorderRight = 0x02, + BorderTop = 0x04, + BorderBottom = 0x08, + BorderVertical = BorderLeft | BorderRight, + BorderHorizontal = BorderTop | BorderBottom, + BorderTopLeft = BorderTop | BorderLeft, + BorderTopRight = BorderTop | BorderRight, + BorderBottomLeft = BorderBottom | BorderLeft, + BorderBottomRight = BorderBottom | BorderRight, + BorderVerticalBottom = BorderVertical | BorderBottom, + BorderVerticalTop = BorderVertical | BorderTop, + BorderHorizontalLeft = BorderHorizontal | BorderLeft, + BorderHorizontalRight = BorderHorizontal | BorderRight, + BorderAll = BorderVertical | BorderHorizontal +}; + + +//============================================================================ +SideBarLocation CDockAreaWidget::calculateSideTabBarArea() const +{ + auto Container = dockContainer(); + auto ContentRect = Container->contentRect(); + + int borders = BorderNone; // contains all borders that are touched by the dock ware + auto DockAreaTopLeft = mapTo(Container, rect().topLeft()); + auto DockAreaRect = rect(); + DockAreaRect.moveTo(DockAreaTopLeft); + const qreal aspectRatio = DockAreaRect.width() / (qMax(1, DockAreaRect.height()) * 1.0); + const qreal sizeRatio = (qreal)ContentRect.width() / DockAreaRect.width(); + static const int MinBorderDistance = 16; + bool HorizontalOrientation = (aspectRatio > 1.0) && (sizeRatio < 3.0); + + // measure border distances - a distance less than 16 px means we touch the + // border + int BorderDistance[4]; + + int Distance = qAbs(ContentRect.topLeft().y() - DockAreaRect.topLeft().y()); + BorderDistance[SideBarLocation::SideBarTop] = (Distance < MinBorderDistance) ? 0 : Distance; + if (!BorderDistance[SideBarLocation::SideBarTop]) + { + borders |= BorderTop; + } + + Distance = qAbs(ContentRect.bottomRight().y() - DockAreaRect.bottomRight().y()); + BorderDistance[SideBarLocation::SideBarBottom] = (Distance < MinBorderDistance) ? 0 : Distance; + if (!BorderDistance[SideBarLocation::SideBarBottom]) + { + borders |= BorderBottom; + } + + Distance = qAbs(ContentRect.topLeft().x() - DockAreaRect.topLeft().x()); + BorderDistance[SideBarLocation::SideBarLeft] = (Distance < MinBorderDistance) ? 0 : Distance; + if (!BorderDistance[SideBarLocation::SideBarLeft]) + { + borders |= BorderLeft; + } + + Distance = qAbs(ContentRect.bottomRight().x() - DockAreaRect.bottomRight().x()); + BorderDistance[SideBarLocation::SideBarRight] = (Distance < MinBorderDistance) ? 0 : Distance; + if (!BorderDistance[SideBarLocation::SideBarRight]) + { + borders |= BorderRight; + } + + auto SideTab = SideBarLocation::SideBarRight; + switch (borders) + { + // 1. It's touching all borders + case BorderAll: SideTab = HorizontalOrientation ? SideBarLocation::SideBarBottom : SideBarLocation::SideBarRight; break; + + // 2. It's touching 3 borders + case BorderVerticalBottom : SideTab = SideBarLocation::SideBarBottom; break; + case BorderVerticalTop : SideTab = SideBarLocation::SideBarTop; break; + case BorderHorizontalLeft: SideTab = SideBarLocation::SideBarLeft; break; + case BorderHorizontalRight: SideTab = SideBarLocation::SideBarRight; break; + + // 3. Its touching horizontal or vertical borders + case BorderVertical : SideTab = SideBarLocation::SideBarBottom; break; + case BorderHorizontal: SideTab = SideBarLocation::SideBarRight; break; + + // 4. Its in a corner + case BorderTopLeft : SideTab = HorizontalOrientation ? SideBarLocation::SideBarTop : SideBarLocation::SideBarLeft; break; + case BorderTopRight : SideTab = HorizontalOrientation ? SideBarLocation::SideBarTop : SideBarLocation::SideBarRight; break; + case BorderBottomLeft : SideTab = HorizontalOrientation ? SideBarLocation::SideBarBottom : SideBarLocation::SideBarLeft; break; + case BorderBottomRight : SideTab = HorizontalOrientation ? SideBarLocation::SideBarBottom : SideBarLocation::SideBarRight; break; + + // 5 Ists touching only one border + case BorderLeft: SideTab = SideBarLocation::SideBarLeft; break; + case BorderRight: SideTab = SideBarLocation::SideBarRight; break; + case BorderTop: SideTab = SideBarLocation::SideBarTop; break; + case BorderBottom: SideTab = SideBarLocation::SideBarBottom; break; + } + + return SideTab; +} + + +//============================================================================ +void CDockAreaWidget::setAutoHide(bool Enable, SideBarLocation Location) +{ + if (!isAutoHideFeatureEnabled()) + { + return; + } + + if (!Enable) + { + if (isAutoHide()) + { + autoHideDockContainer()->moveContentsToParent(); + } + return; + } + + auto area = (SideBarNone == Location) ? calculateSideTabBarArea() : Location; + for (const auto DockWidget : openedDockWidgets()) + { + if (Enable == isAutoHide()) + { + continue; + } + + if (!DockWidget->features().testFlag(CDockWidget::DockWidgetPinnable)) + { + continue; + } + + dockContainer()->createAndSetupAutoHideContainer(area, DockWidget); + } +} + + +//============================================================================ +void CDockAreaWidget::toggleAutoHide(SideBarLocation Location) +{ + if (!isAutoHideFeatureEnabled()) + { + return; + } + + setAutoHide(!isAutoHide(), Location); +} + + //============================================================================ void CDockAreaWidget::closeOtherAreas() { @@ -1005,6 +1374,22 @@ bool CDockAreaWidget::isCentralWidgetArea() const } +//============================================================================ +bool CDockAreaWidget::containsCentralWidget() const +{ + auto centralWidget = dockManager()->centralWidget(); + for (const auto &dockWidget : dockWidgets()) + { + if (dockWidget == centralWidget) + { + return true; + } + } + + return false; +} + + //============================================================================ QSize CDockAreaWidget::minimumSizeHint() const { @@ -1034,6 +1419,19 @@ void CDockAreaWidget::onDockWidgetFeaturesChanged() } +//============================================================================ +bool CDockAreaWidget::isTopLevelArea() const +{ + auto Container = dockContainer(); + if (!Container) + { + return false; + } + + return (Container->topLevelDockArea() == this); +} + + #ifdef Q_OS_WIN //============================================================================ bool CDockAreaWidget::event(QEvent *e) diff --git a/src/DockAreaWidget.h b/src/DockAreaWidget.h index eaf290cce..a3ef76c29 100644 --- a/src/DockAreaWidget.h +++ b/src/DockAreaWidget.h @@ -33,6 +33,7 @@ #include #include "ads_globals.h" +#include "AutoHideTab.h" #include "DockWidget.h" QT_FORWARD_DECLARE_CLASS(QXmlStreamWriter) @@ -45,6 +46,7 @@ class CDockManager; class CDockContainerWidget; class DockContainerWidgetPrivate; class CDockAreaTitleBar; +class CDockingStateReader; /** @@ -65,6 +67,7 @@ class ADS_EXPORT CDockAreaWidget : public QFrame friend class CDockWidget; friend struct DockManagerPrivate; friend class CDockManager; + friend class CAutoHideDockContainer; void onDockWidgetFeaturesChanged(); private Q_SLOTS: @@ -76,6 +79,22 @@ private Q_SLOTS: */ void reorderDockWidget(int fromIndex, int toIndex); + /* + * Update the auto hide button checked state based on if it's contained in an auto hide container or not + */ + void updateAutoHideButtonCheckState(); + + /* + * Update the title bar button tooltips + */ + void updateTitleBarButtonsToolTips(); + + /** + * Calculate the auto hide side bar location depending on the dock area + * widget position in the container + */ + SideBarLocation calculateSideTabBarArea() const; + protected: #ifdef Q_OS_WIN @@ -149,6 +168,11 @@ private Q_SLOTS: */ void markTitleBarMenuOutdated(); + /* + * Update the title bar button visibility based on if it's top level or not + */ + void updateTitleBarButtonVisibility(bool IsTopLevel) const; + protected Q_SLOTS: void toggleView(bool Open); @@ -186,6 +210,23 @@ protected Q_SLOTS: */ CDockContainerWidget* dockContainer() const; + /** + * Returns the auto hide dock container widget this dock area widget belongs to or 0 + * if there is no + */ + CAutoHideDockContainer* autoHideDockContainer() const; + + /** + * Returns true if the dock area is in an auto hide container + */ + bool isAutoHide() const; + + + /** + * Sets the current auto hide dock container + */ + void setAutoHideDockContainer(CAutoHideDockContainer* AutoHideDockContainer); + /** * Returns the largest minimumSizeHint() of the dock widgets in this * area. @@ -262,6 +303,13 @@ protected Q_SLOTS: */ void saveState(QXmlStreamWriter& Stream) const; + /** + * Restores a dock area. + * \see restoreChildNodes() for details + */ + static bool restoreState(CDockingStateReader& Stream, CDockAreaWidget*& CreatedWidget, + bool Testing, CDockContainerWidget* ParentContainer); + /** * This functions returns the dock widget features of all dock widget in * this area. @@ -319,10 +367,22 @@ protected Q_SLOTS: void setDockAreaFlag(eDockAreaFlag Flag, bool On); /** - * Returns true if the area contains the central widget of it's manager. + * Returns true if the area has a single dock widget and contains the central widget of it's manager. */ bool isCentralWidgetArea() const; + /** + * Returns true if the area contains the central widget of it's manager. + */ + bool containsCentralWidget() const; + + /** + * If this dock area is the one and only visible area in a container, then + * this function returns true + */ + bool isTopLevelArea() const; + + public Q_SLOTS: /** * This activates the tab for the given tab index. @@ -337,6 +397,19 @@ public Q_SLOTS: void closeArea(); /** + * Sets the dock area into auto hide mode or into normal mode. + * If the dock area is switched to auto hide mode, then all dock widgets + * that are pinable will be added to the sidebar + */ + void setAutoHide(bool Enable, SideBarLocation Location = SideBarNone); + + /** + * Switches the dock area to auto hide mode or vice versa depending on its + * current state. + */ + void toggleAutoHide(SideBarLocation Location = SideBarNone); + + /** * This function closes all other areas except of this area */ void closeOtherAreas(); diff --git a/src/DockComponentsFactory.cpp b/src/DockComponentsFactory.cpp index 1f8be65d6..05259d7b4 100644 --- a/src/DockComponentsFactory.cpp +++ b/src/DockComponentsFactory.cpp @@ -8,6 +8,7 @@ //============================================================================ // INCLUDES //============================================================================ +#include #include "DockComponentsFactory.h" #include @@ -29,6 +30,12 @@ CDockWidgetTab* CDockComponentsFactory::createDockWidgetTab(CDockWidget* DockWid return new CDockWidgetTab(DockWidget); } +//============================================================================ +CAutoHideTab* CDockComponentsFactory::createDockWidgetSideTab(CDockWidget *DockWidget) const +{ + return new CAutoHideTab(DockWidget); +} + //============================================================================ CDockAreaTabBar* CDockComponentsFactory::createDockAreaTabBar(CDockAreaWidget* DockArea) const diff --git a/src/DockComponentsFactory.h b/src/DockComponentsFactory.h index f0598e989..0d2b7d4c8 100644 --- a/src/DockComponentsFactory.h +++ b/src/DockComponentsFactory.h @@ -19,6 +19,7 @@ class CDockAreaTitleBar; class CDockAreaTabBar; class CDockAreaWidget; class CDockWidget; +class CAutoHideTab; @@ -46,6 +47,12 @@ class ADS_EXPORT CDockComponentsFactory */ virtual CDockWidgetTab* createDockWidgetTab(CDockWidget* DockWidget) const; + /** + * This default implementation just creates a dock widget side tab with + * new CDockWidgetTab(DockWidget). + */ + virtual CAutoHideTab* createDockWidgetSideTab(CDockWidget* DockWidget) const; + /** * This default implementation just creates a dock area tab bar with * new CDockAreaTabBar(DockArea). diff --git a/src/DockContainerWidget.cpp b/src/DockContainerWidget.cpp index 5451eb32e..3876ff5ab 100644 --- a/src/DockContainerWidget.cpp +++ b/src/DockContainerWidget.cpp @@ -38,6 +38,11 @@ #include #include #include +#include +#include +#include +#include +#include #include "DockManager.h" #include "DockAreaWidget.h" @@ -47,6 +52,12 @@ #include "DockOverlay.h" #include "ads_globals.h" #include "DockSplitter.h" +#include "DockWidgetTab.h" +#include "DockAreaTitleBar.h" +#include "DockFocusController.h" +#include "AutoHideDockContainer.h" +#include "AutoHideSideBar.h" +#include "AutoHideTab.h" #include #include @@ -131,12 +142,17 @@ class DockContainerWidgetPrivate QPointer DockManager; unsigned int zOrderIndex = 0; QList DockAreas; + QList AutoHideWidgets; + QMap SideTabBarWidgets; QGridLayout* Layout = nullptr; QSplitter* RootSplitter = nullptr; bool isFloating = false; CDockAreaWidget* LastAddedAreaCache[5]; int VisibleDockAreaCount = -1; CDockAreaWidget* TopLevelDockArea = nullptr; + QTimer DelayedAutoHideTimer; + CAutoHideTab* DelayedAutoHideTab; + bool DelayedAutoHideShow = false; /** * Private data constructor @@ -212,6 +228,11 @@ class DockContainerWidgetPrivate void saveChildNodesState(QXmlStreamWriter& Stream, QWidget* Widget); /** + * Save state of auto hide widgets + */ + void saveAutoHideWidgetsState(QXmlStreamWriter& Stream); + + /** * Restore state of child nodes. * \param[in] Stream The data stream that contains the serialized state * \param[out] CreatedWidget The widget created from parsed data or 0 if @@ -236,6 +257,12 @@ class DockContainerWidgetPrivate bool restoreDockArea(CDockingStateReader& Stream, QWidget*& CreatedWidget, bool Testing); + /** + * Restores a auto hide side bar + */ + bool restoreSideBar(CDockingStateReader& Stream, QWidget*& CreatedWidget, + bool Testing); + /** * Helper function for recursive dumping of layout */ @@ -351,6 +378,13 @@ DockContainerWidgetPrivate::DockContainerWidgetPrivate(CDockContainerWidget* _pu _this(_public) { std::fill(std::begin(LastAddedAreaCache),std::end(LastAddedAreaCache), nullptr); + DelayedAutoHideTimer.setSingleShot(true); + DelayedAutoHideTimer.setInterval(500); + QObject::connect(&DelayedAutoHideTimer, &QTimer::timeout, [this](){ + auto GlobalPos = DelayedAutoHideTab->mapToGlobal(QPoint(0, 0)); + qApp->sendEvent(DelayedAutoHideTab, new QMouseEvent(QEvent::MouseButtonPress, + QPoint(0, 0), GlobalPos, Qt::LeftButton, {Qt::LeftButton}, Qt::NoModifier)); + }); } @@ -402,13 +436,11 @@ void DockContainerWidgetPrivate::onVisibleDockAreaCountChanged() if (TopLevelDockArea) { this->TopLevelDockArea = TopLevelDockArea; - TopLevelDockArea->titleBarButton(TitleBarButtonUndock)->setVisible(false || !_this->isFloating()); - TopLevelDockArea->titleBarButton(TitleBarButtonClose)->setVisible(false || !_this->isFloating()); + TopLevelDockArea->updateTitleBarButtonVisibility(true); } else if (this->TopLevelDockArea) { - this->TopLevelDockArea->titleBarButton(TitleBarButtonUndock)->setVisible(true); - this->TopLevelDockArea->titleBarButton(TitleBarButtonClose)->setVisible(true); + this->TopLevelDockArea->updateTitleBarButtonVisibility(false); this->TopLevelDockArea = nullptr; } } @@ -520,8 +552,9 @@ void DockContainerWidgetPrivate::dropIntoSection(CFloatingDockContainer* Floatin return; } + CDockContainerWidget* FloatingContainer = FloatingWidget->dockContainer(); auto InsertParam = internal::dockAreaInsertParameters(area); - auto NewDockAreas = FloatingWidget->dockContainer()->findChildren( + auto NewDockAreas = FloatingContainer->findChildren( QString(), Qt::FindChildrenRecursively); QSplitter* TargetAreaSplitter = internal::findParent(TargetArea); @@ -534,9 +567,7 @@ void DockContainerWidgetPrivate::dropIntoSection(CFloatingDockContainer* Floatin TargetAreaSplitter = Splitter; } int AreaIndex = TargetAreaSplitter->indexOf(TargetArea); - auto Widget = FloatingWidget->dockContainer()->findChild(QString(), Qt::FindDirectChildrenOnly); - auto FloatingSplitter = qobject_cast(Widget); - + auto FloatingSplitter = FloatingContainer->rootSplitter(); if (TargetAreaSplitter->orientation() == InsertParam.orientation()) { auto Sizes = TargetAreaSplitter->sizes(); @@ -544,7 +575,7 @@ void DockContainerWidgetPrivate::dropIntoSection(CFloatingDockContainer* Floatin bool AdjustSplitterSizes = true; if ((FloatingSplitter->orientation() != InsertParam.orientation()) && FloatingSplitter->count() > 1) { - TargetAreaSplitter->insertWidget(AreaIndex + InsertParam.insertOffset(), Widget); + TargetAreaSplitter->insertWidget(AreaIndex + InsertParam.insertOffset(), FloatingSplitter); updateSplitterHandles(TargetAreaSplitter); } else @@ -574,7 +605,7 @@ void DockContainerWidgetPrivate::dropIntoSection(CFloatingDockContainer* Floatin bool AdjustSplitterSizes = true; if ((FloatingSplitter->orientation() != InsertParam.orientation()) && FloatingSplitter->count() > 1) { - NewSplitter->addWidget(Widget); + NewSplitter->addWidget(FloatingSplitter); updateSplitterHandles(NewSplitter); } else @@ -805,8 +836,8 @@ void DockContainerWidgetPrivate::addDockAreasToList(const QListtitleBarButton(TitleBarButtonUndock)->setVisible(true); DockArea->titleBarButton(TitleBarButtonClose)->setVisible(true); + DockArea->titleBarButton(TitleBarButtonAutoHide)->setVisible(true); } // We need to ensure, that the dock area title bar is visible. The title bar @@ -874,6 +905,21 @@ void DockContainerWidgetPrivate::saveChildNodesState(QXmlStreamWriter& s, QWidge } +//============================================================================ +void DockContainerWidgetPrivate::saveAutoHideWidgetsState(QXmlStreamWriter& s) +{ + for (const auto sideTabBar : SideTabBarWidgets.values()) + { + if (!sideTabBar->tabCount()) + { + continue; + } + + sideTabBar->saveState(s); + } +} + + //============================================================================ bool DockContainerWidgetPrivate::restoreSplitter(CDockingStateReader& s, QWidget*& CreatedWidget, bool Testing) @@ -994,90 +1040,76 @@ bool DockContainerWidgetPrivate::restoreSplitter(CDockingStateReader& s, bool DockContainerWidgetPrivate::restoreDockArea(CDockingStateReader& s, QWidget*& CreatedWidget, bool Testing) { - bool Ok; -#ifdef ADS_DEBUG_PRINT - int Tabs = s.attributes().value("Tabs").toInt(&Ok); - if (!Ok) + CDockAreaWidget* DockArea = nullptr; + auto Result = CDockAreaWidget::restoreState(s, DockArea, Testing, _this); + if (Result && DockArea) { - return false; + appendDockAreas({DockArea}); } -#endif + CreatedWidget = DockArea; + return Result; +} - QString CurrentDockWidget = s.attributes().value("Current").toString(); - ADS_PRINT("Restore NodeDockArea Tabs: " << Tabs << " Current: " - << CurrentDockWidget); - CDockAreaWidget* DockArea = nullptr; - if (!Testing) +//============================================================================ +bool DockContainerWidgetPrivate::restoreSideBar(CDockingStateReader& s, + QWidget*& CreatedWidget, bool Testing) +{ + Q_UNUSED(CreatedWidget) + // Simply ignore side bar auto hide widgets from saved state if + // auto hide support is disabled + if (!CDockManager::testAutoHideConfigFlag(CDockManager::AutoHideFeatureEnabled)) { - DockArea = new CDockAreaWidget(DockManager, _this); - const auto AllowedAreasAttribute = s.attributes().value("AllowedAreas"); - if (!AllowedAreasAttribute.isEmpty()) - { - DockArea->setAllowedAreas((DockWidgetArea)AllowedAreasAttribute.toInt(nullptr, 16)); - } + return true; + } - const auto FlagsAttribute = s.attributes().value("Flags"); - if (!FlagsAttribute.isEmpty()) - { - DockArea->setDockAreaFlags((CDockAreaWidget::DockAreaFlags)FlagsAttribute.toInt(nullptr, 16)); - } + bool Ok; + auto Area = (ads::SideBarLocation)s.attributes().value("Area").toInt(&Ok); + if (!Ok) + { + return false; } while (s.readNextStartElement()) { - if (s.name() != QLatin1String("Widget")) + if (s.name() != QLatin1String("Widget")) { continue; } - auto ObjectName = s.attributes().value("Name"); - if (ObjectName.isEmpty()) + auto Name = s.attributes().value("Name"); + if (Name.isEmpty()) { return false; } + bool Ok; bool Closed = s.attributes().value("Closed").toInt(&Ok); if (!Ok) { return false; } + int Size = s.attributes().value("Size").toInt(&Ok); + if (!Ok) + { + return false; + } + s.skipCurrentElement(); - CDockWidget* DockWidget = DockManager->findDockWidget(ObjectName.toString()); + CDockWidget* DockWidget = DockManager->findDockWidget(Name.toString()); if (!DockWidget || Testing) { continue; } - ADS_PRINT("Dock Widget found - parent " << DockWidget->parent()); - // We hide the DockArea here to prevent the short display (the flashing) - // of the dock areas during application startup - DockArea->hide(); - DockArea->addDockWidget(DockWidget); - DockWidget->setToggleViewActionChecked(!Closed); - DockWidget->setClosedState(Closed); - DockWidget->setProperty(internal::ClosedProperty, Closed); + auto SideBar = _this->sideTabBar(Area); + auto AutoHideContainer = SideBar->insertDockWidget(-1, DockWidget); + AutoHideContainer->setSize(Size); + DockWidget->setProperty(internal::ClosedProperty, Closed); DockWidget->setProperty(internal::DirtyProperty, false); } - if (Testing) - { - return true; - } - - if (!DockArea->dockWidgetsCount()) - { - delete DockArea; - DockArea = nullptr; - } - else - { - DockArea->setProperty("currentDockWidget", CurrentDockWidget); - appendDockAreas({DockArea}); - } - - CreatedWidget = DockArea; return true; } @@ -1099,6 +1131,11 @@ bool DockContainerWidgetPrivate::restoreChildNodes(CDockingStateReader& s, Result = restoreDockArea(s, CreatedWidget, Testing); ADS_PRINT("DockAreaWidget"); } + else if (s.name() == QLatin1String("SideBar")) + { + Result = restoreSideBar(s, CreatedWidget, Testing); + ADS_PRINT("SideBar"); + } else { s.skipCurrentElement(); @@ -1288,6 +1325,8 @@ CDockContainerWidget::CDockContainerWidget(CDockManager* DockManager, QWidget *p d->Layout = new QGridLayout(); d->Layout->setContentsMargins(0, 0, 0, 0); d->Layout->setSpacing(0); + d->Layout->setColumnStretch(1, 1); + d->Layout->setRowStretch(1, 1); setLayout(d->Layout); // The function d->newSplitter() accesses the config flags from dock @@ -1299,9 +1338,11 @@ CDockContainerWidget::CDockContainerWidget(CDockManager* DockManager, QWidget *p { d->DockManager->registerDockContainer(this); createRootSplitter(); + createSideTabBarWidgets(); } } + //============================================================================ CDockContainerWidget::~CDockContainerWidget() { @@ -1309,6 +1350,7 @@ CDockContainerWidget::~CDockContainerWidget() { d->DockManager->removeDockContainer(this); } + delete d; } @@ -1317,6 +1359,7 @@ CDockContainerWidget::~CDockContainerWidget() CDockAreaWidget* CDockContainerWidget::addDockWidget(DockWidgetArea area, CDockWidget* Dockwidget, CDockAreaWidget* DockAreaWidget) { + auto TopLevelDockWidget = topLevelDockWidget(); CDockAreaWidget* OldDockArea = Dockwidget->dockAreaWidget(); if (OldDockArea) { @@ -1324,16 +1367,50 @@ CDockAreaWidget* CDockContainerWidget::addDockWidget(DockWidgetArea area, CDockW } Dockwidget->setDockManager(d->DockManager); + CDockAreaWidget* DockArea; if (DockAreaWidget) { - return d->addDockWidgetToDockArea(area, Dockwidget, DockAreaWidget); + DockArea = d->addDockWidgetToDockArea(area, Dockwidget, DockAreaWidget); } else { - return d->addDockWidgetToContainer(area, Dockwidget); + DockArea = d->addDockWidgetToContainer(area, Dockwidget); } + + if (TopLevelDockWidget) + { + auto NewTopLevelDockWidget = topLevelDockWidget(); + // If the container contained only one visible dock widget, the we need + // to emit a top level event for this widget because it is not the one and + // only visible docked widget anymore + if (!NewTopLevelDockWidget) + { + CDockWidget::emitTopLevelEventForWidget(TopLevelDockWidget, false); + } + } + return DockArea; } + +//============================================================================ +CAutoHideDockContainer* CDockContainerWidget::createAndSetupAutoHideContainer( + SideBarLocation area, CDockWidget* DockWidget) +{ + if (!CDockManager::testAutoHideConfigFlag(CDockManager::AutoHideFeatureEnabled)) + { + Q_ASSERT_X(false, "CDockContainerWidget::createAndInitializeDockWidgetOverlayContainer", + "Requested area does not exist in config"); + return nullptr; + } + if (d->DockManager != DockWidget->dockManager()) + { + DockWidget->setDockManager(d->DockManager); // Auto hide Dock Container needs a valid dock manager + } + + return sideTabBar(area)->insertDockWidget(-1, DockWidget); +} + + //============================================================================ void CDockContainerWidget::removeDockWidget(CDockWidget* Dockwidget) { @@ -1375,6 +1452,13 @@ bool CDockContainerWidget::event(QEvent *e) } +//============================================================================ +QList CDockContainerWidget::autoHideWidgets() const +{ + return d->AutoHideWidgets; +} + + //============================================================================ void CDockContainerWidget::addDockArea(CDockAreaWidget* DockAreaWidget, DockWidgetArea area) @@ -1393,6 +1477,13 @@ void CDockContainerWidget::addDockArea(CDockAreaWidget* DockAreaWidget, void CDockContainerWidget::removeDockArea(CDockAreaWidget* area) { ADS_PRINT("CDockContainerWidget::removeDockArea"); + // If it is an auto hide area, then there is nothing much to do + if (area->isAutoHide()) + { + area->setAutoHideDockContainer(nullptr); + return; + } + area->disconnect(this); d->DockAreas.removeAll(area); CDockSplitter* Splitter = internal::findParent(area); @@ -1533,6 +1624,12 @@ void CDockContainerWidget::dropFloatingWidget(CFloatingDockContainer* FloatingWi auto ContainerDropArea = d->DockManager->containerOverlay()->dropAreaUnderCursor(); bool Dropped = false; + auto autoHideWidgets = FloatingWidget->dockContainer()->autoHideWidgets(); + for (const auto autohideWidget : autoHideWidgets) + { + createAndSetupAutoHideContainer(autohideWidget->sideBarLocation(), autohideWidget->dockWidget()); + } + if (DockArea) { auto dropOverlay = d->DockManager->dockAreaOverlay(); @@ -1675,6 +1772,7 @@ void CDockContainerWidget::saveState(QXmlStreamWriter& s) const #endif } d->saveChildNodesState(s, d->RootSplitter); + d->saveAutoHideWidgetsState(s); s.writeEndElement(); } @@ -1685,7 +1783,7 @@ bool CDockContainerWidget::restoreState(CDockingStateReader& s, bool Testing) bool IsFloating = s.attributes().value("Floating").toInt(); ADS_PRINT("Restore CDockContainerWidget Floating" << IsFloating); - QWidget*NewRootSplitter {}; + QWidget* NewRootSplitter {}; if (!Testing) { d->VisibleDockAreaCount = -1;// invalidate the dock area count @@ -1759,7 +1857,41 @@ void CDockContainerWidget::createRootSplitter() return; } d->RootSplitter = d->newSplitter(Qt::Horizontal); - d->Layout->addWidget(d->RootSplitter); + d->Layout->addWidget(d->RootSplitter, 1, 1); // Add it to the center - the 0 and 2 indexes are used for the SideTabBar widgets +} + + +//============================================================================ +void CDockContainerWidget::createSideTabBarWidgets() +{ + if (!CDockManager::testAutoHideConfigFlag(CDockManager::AutoHideFeatureEnabled)) + { + return; + } + + { + auto Area = SideBarLocation::SideBarLeft; + d->SideTabBarWidgets[Area] = new CAutoHideSideBar(this, Area); + d->Layout->addWidget(d->SideTabBarWidgets[Area], 1, 0); + } + + { + auto Area = SideBarLocation::SideBarRight; + d->SideTabBarWidgets[Area] = new CAutoHideSideBar(this, Area); + d->Layout->addWidget(d->SideTabBarWidgets[Area], 1, 2); + } + + { + auto Area = SideBarLocation::SideBarBottom; + d->SideTabBarWidgets[Area] = new CAutoHideSideBar(this, Area); + d->Layout->addWidget(d->SideTabBarWidgets[Area], 2, 1); + } + + { + auto Area = SideBarLocation::SideBarTop; + d->SideTabBarWidgets[Area] = new CAutoHideSideBar(this, Area); + d->Layout->addWidget(d->SideTabBarWidgets[Area], 0, 1); + } } @@ -1841,13 +1973,25 @@ QList CDockContainerWidget::dockWidgets() const return Result; } - //============================================================================ void CDockContainerWidget::updateSplitterHandles(QSplitter* splitter) { d->updateSplitterHandles(splitter); } +//============================================================================ +void CDockContainerWidget::registerAutoHideWidget(CAutoHideDockContainer* AutohideWidget) +{ + d->AutoHideWidgets.append(AutohideWidget); + Q_EMIT autoHideWidgetCreated(AutohideWidget); + ADS_PRINT("d->AutoHideWidgets.count() " << d->AutoHideWidgets.count()); +} + +//============================================================================ +void CDockContainerWidget::removeAutoHideWidget(CAutoHideDockContainer* AutohideWidget) +{ + d->AutoHideWidgets.removeAll(AutohideWidget); +} //============================================================================ CDockWidget::DockWidgetFeatures CDockContainerWidget::features() const @@ -1894,7 +2038,123 @@ void CDockContainerWidget::closeOtherAreas(CDockAreaWidget* KeepOpenArea) } } +//============================================================================ +CAutoHideSideBar* CDockContainerWidget::sideTabBar(SideBarLocation area) const +{ + return d->SideTabBarWidgets[area]; +} + +//============================================================================ +QRect CDockContainerWidget::contentRect() const +{ + if (!d->RootSplitter) + { + return QRect(); + } + + return d->RootSplitter->geometry(); +} + + +//=========================================================================== +QRect CDockContainerWidget::contentRectGlobal() const +{ + if (!d->RootSplitter) + { + return QRect(); + } + return internal::globalGeometry(d->RootSplitter); +} + + +//=========================================================================== +CDockManager* CDockContainerWidget::dockManager() const +{ + return d->DockManager; +} + + +//=========================================================================== +void CDockContainerWidget::handleAutoHideWidgetEvent(QEvent* e, QWidget* w) +{ + if (!CDockManager::testAutoHideConfigFlag(CDockManager::AutoHideShowOnMouseOver)) + { + return; + } + + if (dockManager()->isRestoringState()) + { + return; + } + + auto AutoHideTab = qobject_cast(w); + if (AutoHideTab) + { + switch (e->type()) + { + case QEvent::Enter: + if (!AutoHideTab->dockWidget()->isVisible()) + { + d->DelayedAutoHideTab = AutoHideTab; + d->DelayedAutoHideShow = true; + d->DelayedAutoHideTimer.start(); + } + else + { + d->DelayedAutoHideTimer.stop(); + } + break; + + case QEvent::MouseButtonPress: + d->DelayedAutoHideTimer.stop(); + break; + + case QEvent::Leave: + if (AutoHideTab->dockWidget()->isVisible()) + { + d->DelayedAutoHideTab = AutoHideTab; + d->DelayedAutoHideShow = false; + d->DelayedAutoHideTimer.start(); + } + else + { + d->DelayedAutoHideTimer.stop(); + } + break; + + default: + break; + } + return; + } + + auto AutoHideContainer = qobject_cast(w); + if (AutoHideContainer) + { + switch (e->type()) + { + case QEvent::Enter: + case QEvent::Hide: + d->DelayedAutoHideTimer.stop(); + break; + + case QEvent::Leave: + if (AutoHideContainer->isVisible()) + { + d->DelayedAutoHideTab = AutoHideContainer->autoHideTab(); + d->DelayedAutoHideShow = false; + d->DelayedAutoHideTimer.start(); + } + break; + + default: + break; + } + return; + return; + } +} } // namespace ads //--------------------------------------------------------------------------- diff --git a/src/DockContainerWidget.h b/src/DockContainerWidget.h index a9ebea430..dd73e504b 100644 --- a/src/DockContainerWidget.h +++ b/src/DockContainerWidget.h @@ -33,6 +33,7 @@ #include #include "ads_globals.h" +#include "AutoHideTab.h" #include "DockWidget.h" QT_FORWARD_DECLARE_CLASS(QXmlStreamWriter) @@ -50,6 +51,10 @@ struct FloatingDockContainerPrivate; class CFloatingDragPreview; struct FloatingDragPreviewPrivate; class CDockingStateReader; +class CAutoHideSideBar; +class CAutoHideTab; +struct AutoHideTabPrivate; +struct AutoHideDockContainerPrivate; /** @@ -74,6 +79,10 @@ class ADS_EXPORT CDockContainerWidget : public QFrame friend class CDockWidget; friend class CFloatingDragPreview; friend struct FloatingDragPreviewPrivate; + friend CAutoHideDockContainer; + friend CAutoHideTab; + friend AutoHideTabPrivate; + friend AutoHideDockContainerPrivate; protected: /** @@ -86,11 +95,23 @@ class ADS_EXPORT CDockContainerWidget : public QFrame */ QSplitter* rootSplitter() const; + /** + * Creates and initializes a dockwidget auto hide container into the given area. + * Initializing inserts the tabs into the side tab widget and hides it + * Returns nullptr if you try and insert into an area where the configuration is not enabled + */ + CAutoHideDockContainer* createAndSetupAutoHideContainer(SideBarLocation area, CDockWidget* DockWidget); + /** * Helper function for creation of the root splitter */ void createRootSplitter(); + /** + * Helper function for creation of the side tab bar widgets + */ + void createSideTabBarWidgets(); + /** * Drop floating widget into the container */ @@ -142,7 +163,8 @@ class ADS_EXPORT CDockContainerWidget : public QFrame CDockWidget* topLevelDockWidget() const; /** - * Returns the top level dock area. + * If the container has only one single visible dock area, then this + * functions returns this top level dock area */ CDockAreaWidget* topLevelDockArea() const; @@ -162,6 +184,24 @@ class ADS_EXPORT CDockContainerWidget : public QFrame */ void updateSplitterHandles(QSplitter* splitter); + /** + * Registers the given floating widget in the internal list of + * auto hide widgets + */ + void registerAutoHideWidget(CAutoHideDockContainer* AutoHideWidget); + + /** + * Remove the given auto hide widget from the list of registered auto hide + * widgets + */ + void removeAutoHideWidget(CAutoHideDockContainer* AutoHideWidget); + + /** + * Handles widget events of auto hide widgets to trigger delayed show + * or hide actions for auto hide container on auto hide tab mouse over + */ + void handleAutoHideWidgetEvent(QEvent* e, QWidget* w); + public: /** * Default Constructor @@ -278,6 +318,36 @@ class ADS_EXPORT CDockContainerWidget : public QFrame */ void closeOtherAreas(CDockAreaWidget* KeepOpenArea); + /** + * Returns the side tab widget for the given area + */ + CAutoHideSideBar* sideTabBar(SideBarLocation area) const; + + + /** + * Access function for auto hide widgets + */ + QList autoHideWidgets() const; + + /** + * Returns the content rectangle without the autohide side bars + */ + QRect contentRect() const; + + /** + * Returns the content rectangle mapped to global space. + * The content rectangle is the rectangle of the dock manager widget minus + * the auto hide side bars. So that means, it is the geometry of the + * internal root splitter mapped to global space. + */ + QRect contentRectGlobal() const; + + /** + * Returns the dock manager that owns this container + */ + CDockManager* dockManager() const; + + Q_SIGNALS: /** * This signal is emitted if one or multiple dock areas has been added to @@ -286,6 +356,11 @@ class ADS_EXPORT CDockContainerWidget : public QFrame */ void dockAreasAdded(); + /** + * This signal is emitted, if a new auto hide widget has been created. + */ + void autoHideWidgetCreated(ads::CAutoHideDockContainer* AutoHideWidget); + /** * This signal is emitted if one or multiple dock areas has been removed */ diff --git a/src/DockFocusController.cpp b/src/DockFocusController.cpp index f1e9282db..322ff29a8 100644 --- a/src/DockFocusController.cpp +++ b/src/DockFocusController.cpp @@ -8,6 +8,7 @@ //============================================================================ // INCLUDES //============================================================================ +#include #include "DockFocusController.h" #include @@ -185,7 +186,7 @@ void DockFocusControllerPrivate::updateDockWidgetFocus(CDockWidget* DockWidget) } #endif - if (old == DockWidget && !ForceFocusChangedSignal) + if (old == DockWidget && !ForceFocusChangedSignal) { return; } @@ -311,6 +312,14 @@ void CDockFocusController::setDockWidgetTabFocused(CDockWidgetTab* Tab) } +//=========================================================================== +void CDockFocusController::clearDockWidgetFocus(CDockWidget* dockWidget) +{ + dockWidget->clearFocus(); + updateDockWidgetFocusStyle(dockWidget, false); +} + + //=========================================================================== void CDockFocusController::setDockWidgetFocused(CDockWidget* focusedNow) { diff --git a/src/DockFocusController.h b/src/DockFocusController.h index b080f3bad..06ec049fc 100644 --- a/src/DockFocusController.h +++ b/src/DockFocusController.h @@ -75,6 +75,11 @@ private Q_SLOTS: */ void setDockWidgetTabFocused(CDockWidgetTab* Tab); + /* + * Request clear focus for a dock widget + */ + void clearDockWidgetFocus(CDockWidget* dockWidget); + public Q_SLOTS: /** * Request a focus change to the given dock widget diff --git a/src/DockManager.cpp b/src/DockManager.cpp index 61210d0a3..febd4952a 100644 --- a/src/DockManager.cpp +++ b/src/DockManager.cpp @@ -28,6 +28,7 @@ //============================================================================ // INCLUDES //============================================================================ +#include #include "DockWidgetTab.h" #include "DockManager.h" @@ -91,6 +92,7 @@ enum eStateFileVersion }; static CDockManager::ConfigFlags StaticConfigFlags = CDockManager::DefaultNonOpaqueConfig; +static CDockManager::AutoHideFlags StaticAutoHideConfigFlags; // auto hide feature is disabled by default static QString FloatingContainersTitle; @@ -153,7 +155,7 @@ struct DockManagerPrivate { for (auto DockWidget : DockWidgetsMap) { - DockWidget->setProperty("dirty", true); + DockWidget->setProperty(internal::DirtyProperty, true); } } @@ -346,6 +348,12 @@ void DockManagerPrivate::restoreDockWidgetsOpenState() { if (DockWidget->property(internal::DirtyProperty).toBool()) { + // If the DockWidget is an auto hide widget that is not assigned yet, + // then we need to delete the auto hide container now + if (DockWidget->isAutoHide()) + { + DockWidget->autoHideDockContainer()->cleanupAndDelete(); + } DockWidget->flagAsUnassigned(); Q_EMIT DockWidget->viewToggled(false); } @@ -484,6 +492,7 @@ CDockManager::CDockManager(QWidget *parent) : d(new DockManagerPrivate(this)) { createRootSplitter(); + createSideTabBarWidgets(); QMainWindow* MainWindow = qobject_cast(parent); if (MainWindow) { @@ -538,6 +547,7 @@ CDockManager::~CDockManager() { delete FloatingWidget; } + delete d; } @@ -636,7 +646,6 @@ void CDockManager::removeFloatingWidget(CFloatingDockContainer* FloatingWidget) d->FloatingWidgets.removeAll(FloatingWidget); } - //============================================================================ void CDockManager::registerDockContainer(CDockContainerWidget* DockContainer) { @@ -845,7 +854,7 @@ CDockAreaWidget* CDockManager::addDockWidget(DockWidgetArea area, CDockWidget* Dockwidget, CDockAreaWidget* DockAreaWidget) { d->DockWidgetsMap.insert(Dockwidget->objectName(), Dockwidget); - auto Container = DockAreaWidget ? DockAreaWidget->dockContainer(): this; + auto Container = DockAreaWidget ? DockAreaWidget->dockContainer() : this; auto AreaOfAddedDockWidget = Container->addDockWidget(area, Dockwidget, DockAreaWidget); Q_EMIT dockWidgetAdded(Dockwidget); return AreaOfAddedDockWidget; @@ -861,6 +870,24 @@ CDockAreaWidget* CDockManager::addDockWidgetToContainer(DockWidgetArea area, return AreaOfAddedDockWidget; } +//============================================================================ +CAutoHideDockContainer* CDockManager::addAutoHideDockWidget(SideBarLocation area, CDockWidget* Dockwidget) +{ + return addAutoHideDockWidgetToContainer(area, Dockwidget, this); +} + +//============================================================================ +CAutoHideDockContainer* CDockManager::addAutoHideDockWidgetToContainer(SideBarLocation area, CDockWidget* Dockwidget, + CDockContainerWidget* DockContainerWidget) +{ + d->DockWidgetsMap.insert(Dockwidget->objectName(), Dockwidget); + auto container = DockContainerWidget->createAndSetupAutoHideContainer(area, Dockwidget); + container->collapseView(true); + + Q_EMIT dockWidgetAdded(Dockwidget); + return container; +} + //============================================================================ CDockAreaWidget* CDockManager::addDockWidgetTab(DockWidgetArea area, @@ -1046,6 +1073,7 @@ CDockAreaWidget* CDockManager::setCentralWidget(CDockWidget* widget) widget->setFeature(CDockWidget::DockWidgetClosable, false); widget->setFeature(CDockWidget::DockWidgetMovable, false); widget->setFeature(CDockWidget::DockWidgetFloatable, false); + widget->setFeature(CDockWidget::DockWidgetPinnable, false); d->CentralWidget = widget; CDockAreaWidget* CentralArea = addDockWidget(CenterDockWidgetArea, widget); CentralArea->setDockAreaFlag(CDockAreaWidget::eDockAreaFlag::HideSingleWidgetTitleBar, true); @@ -1117,6 +1145,11 @@ CDockManager::ConfigFlags CDockManager::configFlags() return StaticConfigFlags; } +CDockManager::AutoHideFlags CDockManager::autoHideConfigFlags() +{ + return StaticAutoHideConfigFlags; +} + //=========================================================================== void CDockManager::setConfigFlags(const ConfigFlags Flags) @@ -1125,12 +1158,26 @@ void CDockManager::setConfigFlags(const ConfigFlags Flags) } +//=========================================================================== +void CDockManager::setAutoHideConfigFlags(const AutoHideFlags Flags) +{ + StaticAutoHideConfigFlags = Flags; +} + + //=========================================================================== void CDockManager::setConfigFlag(eConfigFlag Flag, bool On) { internal::setFlag(StaticConfigFlags, Flag, On); } + +//=========================================================================== +void CDockManager::setAutoHideConfigFlag(eAutoHideFlag Flag, bool On) +{ + internal::setFlag(StaticAutoHideConfigFlags, Flag, On); +} + //=========================================================================== bool CDockManager::testConfigFlag(eConfigFlag Flag) { @@ -1138,6 +1185,13 @@ bool CDockManager::testConfigFlag(eConfigFlag Flag) } +//=========================================================================== +bool CDockManager::testAutoHideConfigFlag(eAutoHideFlag Flag) +{ + return autoHideConfigFlags().testFlag(Flag); +} + + //=========================================================================== CIconProvider& CDockManager::iconProvider() { @@ -1263,6 +1317,7 @@ void CDockManager::setFloatingContainersTitle(const QString& Title) FloatingContainersTitle = Title; } + //=========================================================================== QString CDockManager::floatingContainersTitle() { diff --git a/src/DockManager.h b/src/DockManager.h index 2072287ca..eec829dd7 100644 --- a/src/DockManager.h +++ b/src/DockManager.h @@ -54,6 +54,7 @@ struct DockAreaWidgetPrivate; class CIconProvider; class CDockComponentsFactory; class CDockFocusController; +class CAutoHideSideBar; /** * The central dock manager that maintains the complete docking system. @@ -84,6 +85,8 @@ class ADS_EXPORT CDockManager : public CDockContainerWidget friend class CFloatingDragPreview; friend struct FloatingDragPreviewPrivate; friend class CDockAreaTitleBar; + friend class CAutoHideDockContainer; + friend CAutoHideSideBar; protected: @@ -204,12 +207,12 @@ class ADS_EXPORT CDockManager : public CDockContainerWidget DefaultDockAreaButtons = DockAreaHasCloseButton | DockAreaHasUndockButton - | DockAreaHasTabsMenuButton,///< default configuration of dock area title bar buttons + | DockAreaHasTabsMenuButton, ///< default configuration of dock area title bar buttons DefaultBaseConfig = DefaultDockAreaButtons | ActiveTabHasCloseButton | XmlCompressionEnabled - | FloatingContainerHasWidgetTitle,///< default base configuration settings + | FloatingContainerHasWidgetTitle, ///< default base configuration settings DefaultOpaqueConfig = DefaultBaseConfig | OpaqueSplitterResize @@ -223,6 +226,27 @@ class ADS_EXPORT CDockManager : public CDockContainerWidget }; Q_DECLARE_FLAGS(ConfigFlags, eConfigFlag) + + /** + * These global configuration flags configure some dock manager auto hide + * settings + * Set the dock manager flags, before you create the dock manager instance. + */ + enum eAutoHideFlag + { + AutoHideFeatureEnabled = 0x01, //!< enables / disables auto hide feature + DockAreaHasAutoHideButton = 0x02, //!< If the flag is set each dock area has a auto hide menu button + AutoHideButtonTogglesArea = 0x04, //!< If the flag is set, the auto hide button enables auto hiding for all dock widgets in an area, if disabled, only the current dock widget will be toggled + AutoHideButtonCheckable = 0x08, //!< If the flag is set, the auto hide button will be checked and unchecked depending on the auto hide state. Mainly for styling purposes. + AutoHideSideBarsIconOnly = 0x10,///< show only icons in auto hide side tab - if a tab has no icon, then the text will be shown + AutoHideShowOnMouseOver = 0x20, ///< show the auto hide window on mouse over tab and hide it if mouse leaves auto hide container + + DefaultAutoHideConfig = AutoHideFeatureEnabled + | DockAreaHasAutoHideButton ///< the default configuration for the auto hide feature + }; + Q_DECLARE_FLAGS(AutoHideFlags, eAutoHideFlag) + + /** * Default Constructor. * If the given parent is a QMainWindow, the dock manager sets itself as the @@ -242,6 +266,11 @@ class ADS_EXPORT CDockManager : public CDockContainerWidget */ static ConfigFlags configFlags(); + /** + * This function returns the auto hide configuration flags + */ + static AutoHideFlags autoHideConfigFlags(); + /** * Sets the global configuration flags for the whole docking system. * Call this function before you create the dock manager and before @@ -249,17 +278,35 @@ class ADS_EXPORT CDockManager : public CDockContainerWidget */ static void setConfigFlags(const ConfigFlags Flags); + /** + * Sets the global configuration flags for the whole docking system. + * Call this function before you create the dock manager and before + * your create the first dock widget. + */ + static void setAutoHideConfigFlags(const AutoHideFlags Flags); + /** * Set a certain config flag. * \see setConfigFlags() */ static void setConfigFlag(eConfigFlag Flag, bool On = true); + /** + * Set a certain overlay config flag. + * \see setConfigFlags() + */ + static void setAutoHideConfigFlag(eAutoHideFlag Flag, bool On = true); + /** * Returns true if the given config flag is set */ static bool testConfigFlag(eConfigFlag Flag); + /** + * Returns true if the given overlay config flag is set + */ + static bool testAutoHideConfigFlag(eAutoHideFlag Flag); + /** * Returns the global icon provider. * The icon provider enables the use of custom icons in case using @@ -292,6 +339,21 @@ class ADS_EXPORT CDockManager : public CDockContainerWidget CDockAreaWidget* addDockWidgetToContainer(DockWidgetArea area, CDockWidget* Dockwidget, CDockContainerWidget* DockContainerWidget); + /** + * Adds an Auto-Hide widget to the dock manager container pinned to + * the given side bar location. + * \return Returns the CAutoHideDockContainer that contains the new DockWidget + */ + CAutoHideDockContainer* addAutoHideDockWidget(SideBarLocation Location, CDockWidget* Dockwidget); + + /** + * Adds an Auto-Hide widget to the given DockContainerWidget pinned to + * the given side bar location in this container. + * \return Returns the CAutoHideDockContainer that contains the new DockWidget + */ + CAutoHideDockContainer* addAutoHideDockWidgetToContainer(SideBarLocation Location, + CDockWidget* Dockwidget, CDockContainerWidget* DockContainerWidget); + /** * This function will add the given Dockwidget to the given dock area as * a new tab. diff --git a/src/DockWidget.cpp b/src/DockWidget.cpp index e4b94d8a0..e519880f0 100644 --- a/src/DockWidget.cpp +++ b/src/DockWidget.cpp @@ -28,6 +28,9 @@ //============================================================================ // INCLUDES //============================================================================ +#include +#include +#include #include "DockWidgetTab.h" #include "DockWidget.h" @@ -91,6 +94,7 @@ struct DockWidgetPrivate QList TitleBarActions; CDockWidget::eMinimumSizeHintMode MinimumSizeHintMode = CDockWidget::MinimumSizeHintFromDockWidget; WidgetFactory* Factory = nullptr; + QPointer SideTabWidget; /** * Private data constructor @@ -114,6 +118,12 @@ struct DockWidgetPrivate */ void updateParentDockArea(); + /** + * Closes all auto hide dock widgets if there are no more opened dock areas + * This prevents the auto hide dock widgets from being pinned to an empty dock area + */ + void closeAutoHideDockWidgetsIfNeeded(); + /** * Setup the top tool bar */ @@ -169,7 +179,7 @@ void DockWidgetPrivate::showDockWidget() DockArea->toggleView(true); TabWidget->show(); QSplitter* Splitter = internal::findParent(DockArea); - while (Splitter && !Splitter->isVisible()) + while (Splitter && !Splitter->isVisible() && !DockArea->isAutoHide()) { Splitter->show(); Splitter = internal::findParent(Splitter); @@ -182,6 +192,13 @@ void DockWidgetPrivate::showDockWidget() CFloatingDockContainer*>(Container); FloatingWidget->show(); } + + // If this widget is pinned and there are no opened dock widgets, unpin the auto hide widget by moving it's contents to parent container + // While restoring state, opened dock widgets are not valid + if (Container->openedDockWidgets().count() == 0 && DockArea->isAutoHide() && !DockManager->isRestoringState()) + { + DockArea->autoHideDockContainer()->moveContentsToParent(); + } } } @@ -191,7 +208,9 @@ void DockWidgetPrivate::hideDockWidget() { TabWidget->hide(); updateParentDockArea(); - + + closeAutoHideDockWidgetsIfNeeded(); + if (Features.testFlag(CDockWidget::DeleteContentOnClose)) { Widget->deleteLater(); @@ -226,6 +245,24 @@ void DockWidgetPrivate::updateParentDockArea() } +//============================================================================ +void DockWidgetPrivate::closeAutoHideDockWidgetsIfNeeded() +{ + if (_this->dockContainer() && _this->dockContainer()->openedDockWidgets().isEmpty() && !_this->dockManager()->isRestoringState()) + { + for (auto autoHideWidget : _this->dockContainer()->autoHideWidgets()) + { + if (autoHideWidget->dockWidget() == _this) + { + continue; + } + + autoHideWidget->dockWidget()->toggleView(false); + } + } +} + + //============================================================================ void DockWidgetPrivate::setupToolBar() { @@ -287,7 +324,8 @@ CDockWidget::CDockWidget(const QString &title, QWidget *parent) : setObjectName(title); d->TabWidget = componentsFactory()->createDockWidgetTab(this); - d->ToggleViewAction = new QAction(title, this); + + d->ToggleViewAction = new QAction(title, this); d->ToggleViewAction->setCheckable(true); connect(d->ToggleViewAction, SIGNAL(triggered(bool)), this, SLOT(toggleView(bool))); @@ -302,7 +340,7 @@ CDockWidget::CDockWidget(const QString &title, QWidget *parent) : //============================================================================ CDockWidget::~CDockWidget() { - ADS_PRINT("~CDockWidget()"); + ADS_PRINT("~CDockWidget()"); delete d; } @@ -379,7 +417,7 @@ QWidget* CDockWidget::takeWidget() { w->setParent(nullptr); } - return w; + return w; } @@ -397,6 +435,18 @@ CDockWidgetTab* CDockWidget::tabWidget() const } +//============================================================================ +CAutoHideDockContainer* CDockWidget::autoHideDockContainer() const +{ + if (!d->DockArea) + { + return nullptr; + } + + return d->DockArea->autoHideDockContainer(); +} + + //============================================================================ void CDockWidget::setFeatures(DockWidgetFeatures features) { @@ -416,8 +466,8 @@ void CDockWidget::setFeatures(DockWidgetFeatures features) void CDockWidget::setFeature(DockWidgetFeature flag, bool on) { auto Features = features(); - internal::setFlag(Features, flag, on); - setFeatures(Features); + internal::setFlag(Features, flag, on); + setFeatures(Features); } @@ -470,6 +520,26 @@ CDockAreaWidget* CDockWidget::dockAreaWidget() const return d->DockArea; } +//============================================================================ +CAutoHideTab* CDockWidget::sideTabWidget() const +{ + return d->SideTabWidget; +} + + +//============================================================================ +void CDockWidget::setSideTabWidget(CAutoHideTab* SideTab) const +{ + d->SideTabWidget = SideTab; +} + + +//============================================================================ +bool CDockWidget::isAutoHide() const +{ + return !d->SideTabWidget.isNull(); +} + //============================================================================ bool CDockWidget::isFloating() const @@ -541,7 +611,7 @@ void CDockWidget::setMinimumSizeHintMode(eMinimumSizeHintMode Mode) //============================================================================ bool CDockWidget::isCentralWidget() const { - return dockManager()->centralWidget() == this; + return dockManager()->centralWidget() == this; } @@ -577,6 +647,8 @@ void CDockWidget::toggleViewInternal(bool Open) CDockWidget* TopLevelDockWidgetBefore = DockContainer ? DockContainer->topLevelDockWidget() : nullptr; + d->Closed = !Open; + if (Open) { d->showDockWidget(); @@ -585,7 +657,7 @@ void CDockWidget::toggleViewInternal(bool Open) { d->hideDockWidget(); } - d->Closed = !Open; + d->ToggleViewAction->blockSignals(true); d->ToggleViewAction->setChecked(Open); d->ToggleViewAction->blockSignals(false); @@ -594,6 +666,11 @@ void CDockWidget::toggleViewInternal(bool Open) d->DockArea->toggleDockWidgetView(this, Open); } + if (d->DockArea->isAutoHide()) + { + d->DockArea->autoHideDockContainer()->toggleView(Open); + } + if (Open && TopLevelDockWidgetBefore) { CDockWidget::emitTopLevelEventForWidget(TopLevelDockWidgetBefore, false); @@ -662,7 +739,7 @@ bool CDockWidget::event(QEvent *e) case QEvent::Show: Q_EMIT visibilityChanged(geometry().right() >= 0 && geometry().bottom() >= 0); - break; + break; case QEvent::WindowTitleChange : { @@ -671,6 +748,10 @@ bool CDockWidget::event(QEvent *e) { d->TabWidget->setText(title); } + if (d->SideTabWidget) + { + d->SideTabWidget->setText(title); + } if (d->ToggleViewAction) { d->ToggleViewAction->setText(title); @@ -721,6 +802,12 @@ void CDockWidget::setTabToolTip(const QString &text) void CDockWidget::setIcon(const QIcon& Icon) { d->TabWidget->setIcon(Icon); + + if (d->SideTabWidget) + { + d->SideTabWidget->setIcon(Icon); + } + if (!d->ToggleViewAction->isCheckable()) { d->ToggleViewAction->setIcon(Icon); @@ -938,7 +1025,7 @@ bool CDockWidget::closeDockWidgetInternal(bool ForceClose) } if (features().testFlag(CDockWidget::DockWidgetDeleteOnClose)) - { + { // If the dock widget is floating, then we check if we also need to // delete the floating widget if (isFloating()) @@ -954,13 +1041,17 @@ bool CDockWidget::closeDockWidgetInternal(bool ForceClose) FloatingWidget->hide(); } } + if (d->DockArea && d->DockArea->isAutoHide()) + { + d->DockArea->autoHideDockContainer()->cleanupAndDelete(); + } deleteDockWidget(); Q_EMIT closed(); - } - else - { - toggleView(false); - } + } + else + { + toggleView(false); + } return true; } @@ -1065,6 +1156,45 @@ void CDockWidget::raise() } +//============================================================================ +void CDockWidget::setAutoHide(bool Enable, SideBarLocation Location) +{ + if (!CDockManager::testAutoHideConfigFlag(CDockManager::AutoHideFeatureEnabled)) + { + return; + } + + // Do nothing if nothing changes + if (Enable == isAutoHide()) + { + return; + } + + auto DockArea = dockAreaWidget(); + if (!Enable) + { + DockArea->setAutoHide(false); + } + else + { + auto area = (SideBarNone == Location) ? DockArea->calculateSideTabBarArea() : Location; + dockContainer()->createAndSetupAutoHideContainer(area, this); + } +} + + +//============================================================================ +void CDockWidget::toggleAutoHide(SideBarLocation Location) +{ + if (!CDockManager::testAutoHideConfigFlag(CDockManager::AutoHideFeatureEnabled)) + { + return; + } + + setAutoHide(!isAutoHide(), Location); +} + + } // namespace ads //--------------------------------------------------------------------------- diff --git a/src/DockWidget.h b/src/DockWidget.h index abf876618..c0d0d91df 100644 --- a/src/DockWidget.h +++ b/src/DockWidget.h @@ -46,6 +46,9 @@ class CDockContainerWidget; class CDockAreaWidget; class DockContainerWidgetPrivate; class CFloatingDockContainer; +class CAutoHideTab; +class CAutoHideDockContainer; +class CAutoHideSideBar; /** * The QDockWidget class provides a widget that can be docked inside a @@ -75,6 +78,8 @@ private Q_SLOTS: friend class CDockWidgetTab; friend struct DockWidgetTabPrivate; friend struct DockAreaTitleBarPrivate; + friend class CAutoHideDockContainer; + friend CAutoHideSideBar; /** * Assigns the dock manager that manages this dock widget @@ -156,7 +161,8 @@ private Q_SLOTS: DockWidgetForceCloseWithArea = 0x040, ///< dock widget will be closed when the dock area hosting it is closed NoTab = 0x080, ///< dock widget tab will never be shown if this flag is set DeleteContentOnClose = 0x100, ///< deletes only the contained widget on close, keeping the dock widget intact and in place. Attempts to rebuild the contents widget on show if there is a widget factory set. - DefaultDockWidgetFeatures = DockWidgetClosable | DockWidgetMovable | DockWidgetFloatable | DockWidgetFocusable, + DockWidgetPinnable = 0x200, ///< dock widget can be pinned and added to an auto hide dock container + DefaultDockWidgetFeatures = DockWidgetClosable | DockWidgetMovable | DockWidgetFloatable | DockWidgetFocusable | DockWidgetPinnable, AllDockWidgetFeatures = DefaultDockWidgetFeatures | DockWidgetDeleteOnClose | CustomCloseHandling, DockWidgetAlwaysCloseAndDelete = DockWidgetForceCloseWithArea | DockWidgetDeleteOnClose, NoDockWidgetFeatures = 0x000 @@ -343,6 +349,29 @@ private Q_SLOTS: */ CDockAreaWidget* dockAreaWidget() const; + /** + * Returns the side tab widget for this dock, if this dock widget is in + * a auto hide container. If it is not in a auto hide container, then this + * function returns a nullptr, + */ + CAutoHideTab* sideTabWidget() const; + + /** + * Assign a side tab widget if this dock widget is an auto hide container + */ + void setSideTabWidget(CAutoHideTab* SideTab) const; + + /** + * Returns true, if this dock widget is in an auto hide container + */ + bool isAutoHide() const; + + /** + * Returns the auto hide dock container of this dock widget + * or 0 if there is none + */ + CAutoHideDockContainer* autoHideDockContainer() const; + /** * This property holds whether the dock widget is floating. * A dock widget is only floating, if it is the one and only widget inside @@ -563,6 +592,18 @@ public Q_SLOTS: */ void showNormal(); + /** + * Sets the dock widget into auto hide mode if this feature is enabled + * via CDockManager::setAutoHideFlags(CDockManager::AutoHideFeatureEnabled) + */ + void setAutoHide(bool Enable, SideBarLocation Location = SideBarNone); + + /** + * Switches the dock widget to auto hide mode or vice versa depending on its + * current state. + */ + void toggleAutoHide(SideBarLocation Location = SideBarNone); + Q_SIGNALS: /** diff --git a/src/DockWidgetTab.cpp b/src/DockWidgetTab.cpp index 512e6b521..7a8194262 100644 --- a/src/DockWidgetTab.cpp +++ b/src/DockWidgetTab.cpp @@ -28,6 +28,7 @@ //============================================================================ // INCLUDES //============================================================================ +#include #include "FloatingDragPreview.h" #include "ElidingLabel.h" #include "DockWidgetTab.h" @@ -55,7 +56,7 @@ namespace ads { - +static const char* const LocationProperty = "Location"; using tTabLabel = CElidingLabel; /** @@ -216,6 +217,18 @@ struct DockWidgetTabPrivate return DockWidget->dockManager()->dockFocusController(); } + /** + * Helper function to create and initialize the menu entries for + * the "Auto Hide Group To..." menu + */ + QAction* createAutoHideToAction(const QString& Title, SideBarLocation Location, + QMenu* Menu) + { + auto Action = Menu->addAction(Title); + Action->setProperty("Location", Location); + QObject::connect(Action, &QAction::triggered, _this, &CDockWidgetTab::onAutoHideToActionClicked); + return Action; + } }; // struct DockWidgetTabPrivate @@ -342,10 +355,6 @@ CDockWidgetTab::CDockWidgetTab(CDockWidget* DockWidget, QWidget *parent) : d->DockWidget = DockWidget; d->createLayout(); setFocusPolicy(Qt::NoFocus); - /*if (CDockManager::testConfigFlag(CDockManager::FocusHighlighting)) - { - setFocusPolicy(Qt::ClickFocus); - }*/ } //============================================================================ @@ -509,19 +518,40 @@ void CDockWidgetTab::contextMenuEvent(QContextMenuEvent* ev) } d->saveDragStartMousePosition(ev->globalPos()); - QMenu Menu(this); const bool isFloatable = d->DockWidget->features().testFlag(CDockWidget::DockWidgetFloatable); const bool isNotOnlyTabInContainer = !d->DockArea->dockContainer()->hasTopLevelDockWidget(); - + const bool isTopLevelArea = d->DockArea->isTopLevelArea(); const bool isDetachable = isFloatable && isNotOnlyTabInContainer; + QAction* Action; + QMenu Menu(this); + + if (!isTopLevelArea) + { + Action = Menu.addAction(tr("Detach"), this, SLOT(detachDockWidget())); + Action->setEnabled(isDetachable); + if (CDockManager::testAutoHideConfigFlag(CDockManager::AutoHideFeatureEnabled)) + { + Action = Menu.addAction(tr("Pin"), this, SLOT(autoHideDockWidget())); + auto IsPinnable = d->DockWidget->features().testFlag(CDockWidget::DockWidgetPinnable); + Action->setEnabled(IsPinnable); + + auto menu = Menu.addMenu(tr("Pin To...")); + menu->setEnabled(IsPinnable); + d->createAutoHideToAction(tr("Top"), SideBarTop, menu); + d->createAutoHideToAction(tr("Left"), SideBarLeft, menu); + d->createAutoHideToAction(tr("Right"), SideBarRight, menu); + d->createAutoHideToAction(tr("Bottom"), SideBarBottom, menu); + } + } - auto Action = Menu.addAction(tr("Detach"), this, SLOT(detachDockWidget())); - Action->setEnabled(isDetachable); Menu.addSeparator(); Action = Menu.addAction(tr("Close"), this, SIGNAL(closeRequested())); Action->setEnabled(isClosable()); - Menu.addAction(tr("Close Others"), this, SIGNAL(closeOtherTabsRequested())); + if (d->DockArea->openDockWidgetsCount() > 1) + { + Action = Menu.addAction(tr("Close Others"), this, SIGNAL(closeOtherTabsRequested())); + } Menu.exec(ev->globalPos()); } @@ -703,6 +733,21 @@ void CDockWidgetTab::detachDockWidget() } +//=========================================================================== +void CDockWidgetTab::autoHideDockWidget() +{ + d->DockWidget->setAutoHide(true); +} + + +//=========================================================================== +void CDockWidgetTab::onAutoHideToActionClicked() +{ + int Location = sender()->property(LocationProperty).toInt(); + d->DockWidget->toggleAutoHide((SideBarLocation)Location); +} + + //============================================================================ bool CDockWidgetTab::event(QEvent *e) { @@ -752,17 +797,12 @@ QSize CDockWidgetTab::iconSize() const return d->IconSize; } - //============================================================================ void CDockWidgetTab::setIconSize(const QSize& Size) { d->IconSize = Size; d->updateIcon(); } - - - - } // namespace ads //--------------------------------------------------------------------------- // EOF DockWidgetTab.cpp diff --git a/src/DockWidgetTab.h b/src/DockWidgetTab.h index 7ee45e387..11d0ba512 100644 --- a/src/DockWidgetTab.h +++ b/src/DockWidgetTab.h @@ -58,10 +58,13 @@ class ADS_EXPORT CDockWidgetTab : public QFrame friend struct DockWidgetTabPrivate; friend class CDockWidget; friend class CDockManager; + friend class CAutoHideDockContainer; void onDockWidgetFeaturesChanged(); private Q_SLOTS: void detachDockWidget(); + void autoHideDockWidget(); + void onAutoHideToActionClicked(); protected: virtual void mousePressEvent(QMouseEvent* ev) override; diff --git a/src/FloatingDragPreview.cpp b/src/FloatingDragPreview.cpp index c6ceb1a95..a48611df6 100644 --- a/src/FloatingDragPreview.cpp +++ b/src/FloatingDragPreview.cpp @@ -8,6 +8,7 @@ //============================================================================ // INCLUDES //============================================================================ +#include #include "FloatingDragPreview.h" #include @@ -120,14 +121,21 @@ void FloatingDragPreviewPrivate::updateDropOverlays(const QPoint &GlobalPos) } int VisibleDockAreas = TopContainer->visibleDockAreaCount(); - ContainerOverlay->setAllowedAreas( - VisibleDockAreas > 1 ? OuterDockAreas : AllDockAreas); + + // Include the overlay widget we're dragging as a visible widget + auto dockAreaWidget = qobject_cast(Content); + if (dockAreaWidget && dockAreaWidget->isAutoHide()) + { + VisibleDockAreas++; + } + + ContainerOverlay->setAllowedAreas( VisibleDockAreas > 1 ? OuterDockAreas : AllDockAreas); auto DockArea = TopContainer->dockAreaAt(GlobalPos); if (DockArea && DockArea->isVisible() && VisibleDockAreas >= 0 && DockArea != ContentSourceArea) { DockAreaOverlay->enableDropPreview(true); - DockAreaOverlay->setAllowedAreas( - (VisibleDockAreas == 1) ? NoDockWidgetArea : DockArea->allowedAreas()); + DockAreaOverlay->setAllowedAreas( (VisibleDockAreas == 1) ? NoDockWidgetArea : DockArea->allowedAreas()); + DockWidgetArea Area = DockAreaOverlay->showOverlay(DockArea); // A CenterDockWidgetArea for the dockAreaOverlay() indicates that @@ -319,6 +327,9 @@ void CFloatingDragPreview::startFloating(const QPoint &DragStartMousePos, void CFloatingDragPreview::finishDragging() { ADS_PRINT("CFloatingDragPreview::finishDragging"); + + cleanupAutoHideContainerWidget(); + auto DockDropArea = d->DockManager->dockAreaOverlay()->visibleDropAreaUnderCursor(); auto ContainerDropArea = d->DockManager->containerOverlay()->visibleDropAreaUnderCursor(); if (!d->DropContainer) @@ -353,6 +364,22 @@ void CFloatingDragPreview::finishDragging() } +//============================================================================ +void CFloatingDragPreview::cleanupAutoHideContainerWidget() +{ + auto DroppedDockWidget = qobject_cast(d->Content); + auto DroppedArea = qobject_cast(d->Content); + if (DroppedDockWidget && DroppedDockWidget->autoHideDockContainer()) + { + DroppedDockWidget->autoHideDockContainer()->cleanupAndDelete(); + } + if (DroppedArea && DroppedArea->autoHideDockContainer()) + { + DroppedArea->autoHideDockContainer()->cleanupAndDelete(); + } +} + + //============================================================================ void CFloatingDragPreview::paintEvent(QPaintEvent* event) { diff --git a/src/FloatingDragPreview.h b/src/FloatingDragPreview.h index 38224808c..236f80eb5 100644 --- a/src/FloatingDragPreview.h +++ b/src/FloatingDragPreview.h @@ -92,6 +92,11 @@ private Q_SLOTS: */ virtual void finishDragging() override; + /** + * Cleanup auto hide container if the dragged widget has one + */ + void cleanupAutoHideContainerWidget(); + Q_SIGNALS: /** * This signal is emitted, if dragging has been canceled by escape key diff --git a/src/PushButton.cpp b/src/PushButton.cpp new file mode 100644 index 000000000..54883c88d --- /dev/null +++ b/src/PushButton.cpp @@ -0,0 +1,70 @@ +//============================================================================ +/// \file PushButton.cpp +/// \author Uwe Kindler +/// \date 18.10.2022 +/// \brief Implementation of CPushButton +//============================================================================ + +//============================================================================ +// INCLUDES +//============================================================================ +#include "PushButton.h" + +#include +#include +#include +#include + + +namespace ads +{ +QSize CPushButton::sizeHint() const +{ + QSize sh = QPushButton::sizeHint(); + + if (m_Orientation != CPushButton::Horizontal) + { + sh.transpose(); + } + + return sh; +} + +void CPushButton::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event); + + QStylePainter painter(this); + QStyleOptionButton option; + initStyleOption(&option); + + if (m_Orientation == CPushButton::VerticalTopToBottom) + { + painter.rotate(90); + painter.translate(0, -1 * width()); + option.rect = option.rect.transposed(); + } + else if (m_Orientation == CPushButton::VerticalBottomToTop) + { + painter.rotate(-90); + painter.translate(-1 * height(), 0); + option.rect = option.rect.transposed(); + } + + painter.drawControl(QStyle::CE_PushButton, option); +} + +CPushButton::Orientation CPushButton::buttonOrientation() const +{ + return m_Orientation; +} + +void CPushButton::setButtonOrientation(Orientation orientation) +{ + m_Orientation = orientation; + updateGeometry(); +} +} // namespace ads + +//--------------------------------------------------------------------------- +// EOF PushButton.cpp diff --git a/src/PushButton.h b/src/PushButton.h new file mode 100644 index 000000000..5c4ac7e44 --- /dev/null +++ b/src/PushButton.h @@ -0,0 +1,58 @@ +#ifndef PushButtonH +#define PushButtonH +//============================================================================ +/// \file PushButton.h +/// \author Uwe Kindler +/// \date 18.10.2022 +/// \brief Declaration of CPushButton +//============================================================================ + +//============================================================================ +// INCLUDES +//============================================================================ +#include "ads_globals.h" + +#include + +namespace ads +{ + +/** + * ADS specific push button class with orientation support + */ +class ADS_EXPORT CPushButton : public QPushButton +{ + Q_OBJECT +public: + enum Orientation { + Horizontal, + VerticalTopToBottom, + VerticalBottomToTop + }; + + using QPushButton::QPushButton; + + virtual QSize sizeHint() const override; + + /** + * Returns the current orientation + */ + Orientation buttonOrientation() const; + + /** + * Set the orientation of this button + */ + void setButtonOrientation(Orientation orientation); + +protected: + virtual void paintEvent(QPaintEvent *event) override; + +private: + Orientation m_Orientation = Horizontal; +}; + +} // namespace ads + +//--------------------------------------------------------------------------- +#endif // PushButtonH + diff --git a/src/ResizeHandle.cpp b/src/ResizeHandle.cpp new file mode 100644 index 000000000..84f02d2ad --- /dev/null +++ b/src/ResizeHandle.cpp @@ -0,0 +1,326 @@ +//============================================================================ +/// \file ResizeHandle.cpp +/// \author Uwe Kindler +/// \date 24.10.2022 +/// \brief Implementation of CResizeHandle class +//============================================================================ + +//============================================================================ +// INCLUDES +//============================================================================ +#include "ResizeHandle.h" + +#include +#include +#include +#include +#include +#include + +#include "ads_globals.h" + +namespace ads +{ +/** + * Private data class of CResizeHandle class (pimpl) + */ +struct ResizeHandlePrivate +{ + CResizeHandle *_this; + Qt::Edge HandlePosition = Qt::LeftEdge; + QWidget* Target = nullptr; + int MouseOffset = 0; + bool Pressed = false; + int MinSize = 0; + int MaxSize = 1; + QPointer RubberBand; + bool OpaqueResize = false; + int HandleWidth = 4; + + /** + * Private data constructor + */ + ResizeHandlePrivate(CResizeHandle *_public); + + /** + * Pick position component from pos depending on orientation + */ + int pick(const QPoint &pos) const + { + return _this->orientation() == Qt::Horizontal ? pos.x() : pos.y(); + } + + /** + * Returns true, if orientation is horizontal + */ + bool isHorizontal() const + { + return _this->orientation() == Qt::Horizontal; + } + + /** + * Set rubberband position + */ + void setRubberBand(int Pos); + + /** + * Calculates the resize position and geometry + */ + void doResizing(QMouseEvent* e, bool ForceResize = false); +}; +// struct ResizeHandlePrivate + +//============================================================================ +ResizeHandlePrivate::ResizeHandlePrivate(CResizeHandle *_public) : + _this(_public) +{ + +} + + +//============================================================================ +void ResizeHandlePrivate::setRubberBand(int Pos) +{ + if (!RubberBand) + { + RubberBand = new QRubberBand(QRubberBand::Line, Target->parentWidget()); + } + + auto Geometry = _this->geometry(); + auto TopLeft = Target->mapTo(Target->parentWidget(), Geometry.topLeft()); + switch (HandlePosition) + { + case Qt::LeftEdge: + case Qt::RightEdge: TopLeft.rx() += Pos; break; + case Qt::TopEdge: + case Qt::BottomEdge: TopLeft.ry() += Pos; break; + } + + Geometry.moveTopLeft(TopLeft); + RubberBand->setGeometry(Geometry); + RubberBand->show(); +} + + +//============================================================================ +void ResizeHandlePrivate::doResizing(QMouseEvent* e, bool ForceResize) +{ + int pos = pick(e->pos()) - MouseOffset; + auto OldGeometry = Target->geometry(); + auto NewGeometry = OldGeometry; + switch (HandlePosition) + { + case Qt::LeftEdge: + { + NewGeometry.adjust(pos, 0, 0, 0); + int Size = qBound(MinSize, NewGeometry.width(), MaxSize); + pos += (NewGeometry.width() - Size); + NewGeometry.setWidth(Size); + NewGeometry.moveTopRight(OldGeometry.topRight()); + } + break; + + + case Qt::RightEdge: + { + NewGeometry.adjust(0, 0, pos, 0); + int Size = qBound(MinSize, NewGeometry.width(), MaxSize); + pos -= (NewGeometry.width() - Size); + NewGeometry.setWidth(Size); + } + break; + + case Qt::TopEdge: + { + NewGeometry.adjust(0, pos, 0, 0); + int Size = qBound(MinSize, NewGeometry.height(), MaxSize); + pos += (NewGeometry.height() - Size); + NewGeometry.setHeight(Size); + NewGeometry.moveBottomLeft(OldGeometry.bottomLeft()); + } + break; + + case Qt::BottomEdge: + { + NewGeometry.adjust(0, 0, 0, pos); + int Size = qBound(MinSize, NewGeometry.height(), MaxSize); + pos -= (NewGeometry.height() - Size); + NewGeometry.setHeight(Size); + } + break; + } + + if (_this->opaqueResize() || ForceResize) + { + Target->setGeometry(NewGeometry); + } + else + { + setRubberBand(pos); + } +} + + +//============================================================================ +CResizeHandle::CResizeHandle(Qt::Edge HandlePosition, QWidget* parent) : + Super(parent), + d(new ResizeHandlePrivate(this)) +{ + d->Target = parent; + setHandlePosition(HandlePosition); + setMinResizeSize(48); + setMaxResizeSize(d->isHorizontal() ? parent->height() : parent->width()); + + if (!d->isHorizontal()) + { + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + } + else + { + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); + } +} + + +//============================================================================ +CResizeHandle::~CResizeHandle() +{ + delete d; +} + + +//============================================================================ +void CResizeHandle::mouseMoveEvent(QMouseEvent* e) +{ + if (!(e->buttons() & Qt::LeftButton)) + { + return; + } + + d->doResizing(e); +} + + +//============================================================================ +void CResizeHandle::mousePressEvent(QMouseEvent* e) +{ + if (e->button() == Qt::LeftButton) + { + d->MouseOffset = d->pick(e->pos()); + d->Pressed = true; + update(); + } +} + + +//============================================================================ +void CResizeHandle::mouseReleaseEvent(QMouseEvent* e) +{ + if (!opaqueResize() && e->button() == Qt::LeftButton) + { + if (d->RubberBand) + { + d->RubberBand->deleteLater(); + } + d->doResizing(e, true); + } + if (e->button() == Qt::LeftButton) + { + d->Pressed = false; + update(); + } +} + + +//============================================================================ +void CResizeHandle::setHandlePosition(Qt::Edge HandlePosition) +{ + d->HandlePosition = HandlePosition; + switch (d->HandlePosition) + { + case Qt::LeftEdge: // fall through + case Qt::RightEdge: setCursor(Qt::SizeHorCursor); break; + + case Qt::TopEdge: // fall through + case Qt::BottomEdge: setCursor(Qt::SizeVerCursor); break; + } +} + + +//============================================================================ +Qt::Edge CResizeHandle::handlePostion() const +{ + return d->HandlePosition; +} + + +//============================================================================ +Qt::Orientation CResizeHandle::orientation() const +{ + switch (d->HandlePosition) + { + case Qt::LeftEdge: // fall through + case Qt::RightEdge: return Qt::Horizontal; + + case Qt::TopEdge: // fall through + case Qt::BottomEdge: return Qt::Vertical; + } + + return Qt::Horizontal; +} + + +//============================================================================ +QSize CResizeHandle::sizeHint() const +{ + QSize Result; + switch (d->HandlePosition) + { + case Qt::LeftEdge: // fall through + case Qt::RightEdge: Result = QSize(d->HandleWidth, d->Target->height()); break; + + case Qt::TopEdge: // fall through + case Qt::BottomEdge: Result = QSize(d->Target->width(), d->HandleWidth); break; + } + + return Result; +} + + +//============================================================================ +bool CResizeHandle::isResizing() const +{ + return d->Pressed; +} + + +//============================================================================ +void CResizeHandle::setMinResizeSize(int MinSize) +{ + d->MinSize = MinSize; +} + + +//============================================================================ +void CResizeHandle::setMaxResizeSize(int MaxSize) +{ + d->MaxSize = MaxSize; +} + + +//============================================================================ +void CResizeHandle::setOpaqueResize(bool opaque) +{ + d->OpaqueResize = opaque; +} + + +//============================================================================ +bool CResizeHandle::opaqueResize() const +{ + return d->OpaqueResize; +} +} // namespace ads + +//--------------------------------------------------------------------------- +// EOF ResizeHandle.cpp diff --git a/src/ResizeHandle.h b/src/ResizeHandle.h new file mode 100644 index 000000000..1b5af3216 --- /dev/null +++ b/src/ResizeHandle.h @@ -0,0 +1,102 @@ +#ifndef ResizeHandleH +#define ResizeHandleH +//============================================================================ +/// \file ResizeHandle.h +/// \author Uwe Kindler +/// \date 24.10.2022 +/// \brief Declaration of CResizeHandle class +//============================================================================ + +//============================================================================ +// INCLUDES +//============================================================================ +#include "ads_globals.h" +#include + +namespace ads +{ +struct ResizeHandlePrivate; + +/** + * Resize handle for resizing its parent widget + */ +class ADS_EXPORT CResizeHandle : public QFrame +{ + Q_OBJECT + Q_DISABLE_COPY(CResizeHandle) + Q_PROPERTY(bool opaqueResize READ opaqueResize WRITE setOpaqueResize) +private: + ResizeHandlePrivate* d; ///< private data (pimpl) + friend struct ResizeHandlePrivate; + +protected: + void mouseMoveEvent(QMouseEvent *) override; + void mousePressEvent(QMouseEvent *) override; + void mouseReleaseEvent(QMouseEvent *) override; + +public: + using Super = QFrame; + + /** + * Default Constructor + */ + CResizeHandle(Qt::Edge HandlePosition, QWidget* parent); + + /** + * Virtual Destructor + */ + virtual ~CResizeHandle(); + + /** + * Sets the handle position + */ + void setHandlePosition(Qt::Edge HandlePosition); + + /** + * Returns the handle position + */ + Qt::Edge handlePostion() const; + + /** + * Returns the orientation of this resize handle + */ + Qt::Orientation orientation() const; + + /** + * Returns the size hint + */ + QSize sizeHint() const override; + + /** + * Returns true, if resizing is active + */ + bool isResizing() const; + + /** + * Sets the minimum size for the widget that is going to be resized. + * The resize handle will not resize the target widget to a size smaller + * than this value + */ + void setMinResizeSize(int MinSize); + + /** + * Sets the maximum size for the widget that is going to be resized + * The resize handle will not resize the target widget to a size bigger + * than this value + */ + void setMaxResizeSize(int MaxSize); + + /** + * Enable / disable opaque resizing + */ + void setOpaqueResize(bool opaque = true); + + /** + * Returns true if widgets are resized dynamically (opaquely) while + * interactively moving the resize handle. Otherwise returns false. + */ + bool opaqueResize() const; +}; // class name +} // namespace ads +//----------------------------------------------------------------------------- +#endif // ResizeHandleH diff --git a/src/ads.qrc b/src/ads.qrc index a29a54210..019f871f3 100644 --- a/src/ads.qrc +++ b/src/ads.qrc @@ -2,6 +2,7 @@ stylesheets/default.css images/close-button.svg + images/pin-button.svg images/close-button-disabled.svg stylesheets/default_linux.css images/close-button-focused.svg @@ -14,5 +15,8 @@ images/maximize-button-focused.svg images/restore-button.svg images/restore-button-focused.svg + images/vs-pin-button.svg + images/vs-pin-button-pinned.svg + images/vs-pin-button-pinned-focused.svg diff --git a/src/ads_globals.cpp b/src/ads_globals.cpp index 8eca15946..3971abf3c 100644 --- a/src/ads_globals.cpp +++ b/src/ads_globals.cpp @@ -406,6 +406,15 @@ void repolishStyle(QWidget* w, eRepolishChildOptions Options) } } + +//============================================================================ +QRect globalGeometry(QWidget* w) +{ + QRect g = w->geometry(); + g.moveTopLeft(w->mapToGlobal(QPoint(0, 0))); + return g; +} + } // namespace internal } // namespace ads diff --git a/src/ads_globals.h b/src/ads_globals.h index 777bd824c..1b86b012d 100644 --- a/src/ads_globals.h +++ b/src/ads_globals.h @@ -69,6 +69,7 @@ QT_FORWARD_DECLARE_CLASS(QSplitter) namespace ads { +Q_NAMESPACE class CDockSplitter; enum DockWidgetArea @@ -91,7 +92,8 @@ enum TitleBarButton { TitleBarButtonTabsMenu, TitleBarButtonUndock, - TitleBarButtonClose + TitleBarButtonClose, + TitleBarButtonAutoHide }; /** @@ -111,6 +113,7 @@ enum eDragState enum eIcon { TabCloseIcon, //!< TabCloseIcon + AutoHideIcon, //!< AutoHideIcon DockAreaMenuIcon, //!< DockAreaMenuIcon DockAreaUndockIcon,//!< DockAreaUndockIcon DockAreaCloseIcon, //!< DockAreaCloseIcon @@ -128,6 +131,20 @@ enum eBitwiseOperator }; +/** + * Each dock container supports 4 sidebars + */ +enum SideBarLocation +{ + SideBarTop, + SideBarLeft, + SideBarRight, + SideBarBottom, + SideBarNone +}; +Q_ENUMS(SideBarLocation); + + namespace internal { static const bool RestoreTesting = true; @@ -305,6 +322,11 @@ enum eRepolishChildOptions void repolishStyle(QWidget* w, eRepolishChildOptions Options = RepolishIgnoreChildren); +/** + * Returns the geometry of the given widget in global space + */ +QRect globalGeometry(QWidget* w); + } // namespace internal } // namespace ads diff --git a/src/images/pin-button.svg b/src/images/pin-button.svg new file mode 100644 index 000000000..12ff7cb7a --- /dev/null +++ b/src/images/pin-button.svg @@ -0,0 +1,53 @@ + +image/svg+xml diff --git a/src/images/vs-pin-button-pinned-focused.svg b/src/images/vs-pin-button-pinned-focused.svg new file mode 100644 index 000000000..6608545ec --- /dev/null +++ b/src/images/vs-pin-button-pinned-focused.svg @@ -0,0 +1,2 @@ + + diff --git a/src/images/vs-pin-button-pinned.svg b/src/images/vs-pin-button-pinned.svg new file mode 100644 index 000000000..dfb98a88d --- /dev/null +++ b/src/images/vs-pin-button-pinned.svg @@ -0,0 +1,2 @@ + + diff --git a/src/images/vs-pin-button.svg b/src/images/vs-pin-button.svg new file mode 100644 index 000000000..d93a70117 --- /dev/null +++ b/src/images/vs-pin-button.svg @@ -0,0 +1,2 @@ + + diff --git a/src/src.pro b/src/src.pro index 9f6d2654a..79344b124 100644 --- a/src/src.pro +++ b/src/src.pro @@ -47,7 +47,12 @@ HEADERS += \ ElidingLabel.h \ IconProvider.h \ DockComponentsFactory.h \ - DockFocusController.h + DockFocusController.h \ + AutoHideDockContainer.h \ + AutoHideSideBar.h \ + AutoHideTab.h \ + PushButton.h \ + ResizeHandle.h SOURCES += \ @@ -67,7 +72,12 @@ SOURCES += \ ElidingLabel.cpp \ IconProvider.cpp \ DockComponentsFactory.cpp \ - DockFocusController.cpp + DockFocusController.cpp \ + AutoHideDockContainer.cpp \ + AutoHideSideBar.cpp \ + AutoHideTab.cpp \ + PushButton.cpp \ + ResizeHandle.cpp unix:!macx { diff --git a/src/stylesheets/default.css b/src/stylesheets/default.css index 7952bd11a..83d1e32ac 100644 --- a/src/stylesheets/default.css +++ b/src/stylesheets/default.css @@ -1,22 +1,65 @@ /* * Default style sheet on Windows Platforms */ + +/***************************************************************************** + * CDockContainerWidget + *****************************************************************************/ ads--CDockContainerWidget { - background: palette(dark); + background: palette(window); } -ads--CDockContainerWidget > QSplitter{ - padding: 1 0 1 0; + + +/***************************************************************************** + * CDockSplitter + *****************************************************************************/ +ads--CDockContainerWidget > QSplitter { + padding: 1 0 1 0; } ads--CDockContainerWidget ads--CDockSplitter::handle { - background: palette(dark); + background: palette(dark); } + +/***************************************************************************** + * CDockAreaWidget + *****************************************************************************/ ads--CDockAreaWidget { - background: palette(window); - border: 1px solid white; + background: palette(window); +} + +ads--CTitleBarButton { + padding: 0px 0px; +} + + +#tabsMenuButton::menu-indicator { + image: none; +} + +#tabsMenuButton { + qproperty-icon: url(:/ads/images/tabs-menu-button.svg); + qproperty-iconSize: 16px; +} + +#dockAreaCloseButton { + qproperty-icon: url(:/ads/images/close-button.svg), + url(:/ads/images/close-button-disabled.svg) disabled; + qproperty-iconSize: 16px; } +#detachGroupButton { + qproperty-icon: url(:/ads/images/detach-button.svg), + url(:/ads/images/detach-button-disabled.svg) disabled; + qproperty-iconSize: 16px; +} + + + +/***************************************************************************** + * CDockWidgetTab and close button styling + *****************************************************************************/ ads--CDockWidgetTab { background: palette(window); border-color: palette(light); @@ -39,6 +82,30 @@ ads--CDockWidgetTab[activeTab="true"] QLabel { color: palette(foreground); } + +#tabCloseButton { + margin-top: 2px; + background: none; + border: none; + padding: 0px -2px; + qproperty-icon: url(:/ads/images/close-button.svg), + url(:/ads/images/close-button-disabled.svg) disabled; + qproperty-iconSize: 16px; +} + +#tabCloseButton:hover { + border: 1px solid rgba(0, 0, 0, 32); + background: rgba(0, 0, 0, 16); +} + +#tabCloseButton:pressed { + background: rgba(0, 0, 0, 32); +} + + +/***************************************************************************** + * CDockWidget + *****************************************************************************/ ads--CDockWidget { background: palette(light); border-color: palette(light); @@ -46,51 +113,231 @@ ads--CDockWidget { border-width: 1px 0 0 0; } -ads--CTitleBarButton { - padding: 0px 0px; -} QScrollArea#dockWidgetScrollArea { padding: 0px; border: none; } -#tabCloseButton { - margin-top: 2px; + +/***************************************************************************** + * + * Styling of auto hide functionality + * + *****************************************************************************/ + + +/***************************************************************************** + * CAutoHideTab + *****************************************************************************/ +ads--CAutoHideTab { + qproperty-iconSize: 16px 16px;/* this is optional in case you would like to change icon size*/ background: none; border: none; - padding: 0px -2px; - qproperty-icon: url(:/ads/images/close-button.svg), - url(:/ads/images/close-button-disabled.svg) disabled; - qproperty-iconSize: 16px; + padding-left: 2px; + padding-right: 0px; + text-align: center; + min-height: 20px; + padding-bottom: 2px; } -#tabCloseButton:hover { - border: 1px solid rgba(0, 0, 0, 32); - background: rgba(0, 0, 0, 16); + +ads--CAutoHideTab:hover +{ + color: palette(highlight); } -#tabCloseButton:pressed { - background: rgba(0, 0, 0, 32); + +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="0"], +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="2"] { + border-top: 6px solid rgba(0, 0, 0, 48); } -#tabsMenuButton::menu-indicator { - image: none; + +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="1"], +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="3"] { + border-bottom: 6px solid rgba(0, 0, 0, 48); } -#tabsMenuButton { - qproperty-icon: url(:/ads/images/tabs-menu-button.svg); + + +ads--CAutoHideTab:hover[iconOnly="false"][sideBarLocation="0"], +ads--CAutoHideTab:hover[iconOnly="false"][sideBarLocation="2"], +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="0"][activeTab="true"], +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="2"][activeTab="true"] { + border-top: 6px solid palette(highlight); +} + + +ads--CAutoHideTab:hover[iconOnly="false"][sideBarLocation="1"], +ads--CAutoHideTab:hover[iconOnly="false"][sideBarLocation="3"], +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="1"][activeTab="true"], +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="3"][activeTab="true"] { + border-bottom: 6px solid palette(highlight); +} + + +/** + * Auto hide tabs with icon only + */ +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="0"] { + border-top: 6px solid rgba(0, 0, 0, 48); +} + +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="1"] { + border-left: 6px solid rgba(0, 0, 0, 48); +} + +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="2"] { + border-right: 6px solid rgba(0, 0, 0, 48); +} + +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="3"] { + border-bottom: 6px solid rgba(0, 0, 0, 48); +} + + +/** + * Auto hide tabs with icon only hover + */ +ads--CAutoHideTab:hover[iconOnly="true"][sideBarLocation="0"], +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="0"][activeTab="true"] { + border-top: 6px solid palette(highlight); +} + +ads--CAutoHideTab:hover[iconOnly="true"][sideBarLocation="1"], +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="1"][activeTab="true"] { + border-left: 6px solid palette(highlight); +} + +ads--CAutoHideTab:hover[iconOnly="true"][sideBarLocation="2"], +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="2"][activeTab="true"] { + border-right: 6px solid palette(highlight); +} + +ads--CAutoHideTab:hover[iconOnly="true"][sideBarLocation="3"], +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="3"][activeTab="true"] { + border-bottom: 6px solid palette(highlight); +} + + +/***************************************************************************** + * CAutoHideSideBar + *****************************************************************************/ +ads--CAutoHideSideBar{ + background: palette(window); + border: none; + qproperty-spacing: 12; +} + +#sideTabsContainerWidget { + background: transparent; +} + + +ads--CAutoHideSideBar[sideBarLocation="0"] { + border-bottom: 1px solid palette(dark); +} + +ads--CAutoHideSideBar[sideBarLocation="1"] { + border-right: 1px solid palette(dark); +} + +ads--CAutoHideSideBar[sideBarLocation="2"] { + border-left: 1px solid palette(dark); +} + +ads--CAutoHideSideBar[sideBarLocation="3"] { + border-top: 1px solid palette(dark); +} + + +/***************************************************************************** + * CAutoHideDockContainer + *****************************************************************************/ +ads--CAutoHideDockContainer { + background: palette(window); +} + + +ads--CAutoHideDockContainer ads--CDockAreaTitleBar { + background: palette(highlight); + padding: 0px; + border: none; +} + + +/* + * This is required because the ads--CDockAreaWidget[focused="true"] will + * overwrite the ads--CAutoHideDockContainer ads--CDockAreaTitleBar rule + */ +ads--CAutoHideDockContainer ads--CDockAreaWidget[focused="true"] ads--CDockAreaTitleBar { + background: palette(highlight); + padding: 0px; + border: none; +} + + +#autoHideTitleLabel { + padding-left: 4px; + color: palette(light); +} + + +/***************************************************************************** + * CAutoHideDockContainer titlebar buttons + *****************************************************************************/ +#dockAreaAutoHideButton { + qproperty-icon: url(:/ads/images/vs-pin-button.svg); qproperty-iconSize: 16px; } -#dockAreaCloseButton { - qproperty-icon: url(:/ads/images/close-button.svg), - url(:/ads/images/close-button-disabled.svg) disabled; +ads--CAutoHideDockContainer #dockAreaAutoHideButton { + qproperty-icon: url(:/ads/images/vs-pin-button-pinned-focused.svg); qproperty-iconSize: 16px; } -#detachGroupButton { - qproperty-icon: url(:/ads/images/detach-button.svg), - url(:/ads/images/detach-button-disabled.svg) disabled; - qproperty-iconSize: 16px; + +ads--CAutoHideDockContainer #dockAreaCloseButton{ + qproperty-icon: url(:/ads/images/close-button-focused.svg) +} + + +ads--CAutoHideDockContainer ads--CTitleBarButton:hover { + background: rgba(255, 255, 255, 48); +} + +ads--CAutoHideDockContainer ads--CTitleBarButton:pressed { + background: rgba(255, 255, 255, 96); } + +/***************************************************************************** + * CAutoHideDockContainer Titlebar and Buttons + *****************************************************************************/ + + +/***************************************************************************** + * CResizeHandle + *****************************************************************************/ +ads--CResizeHandle { + background: palette(window); +} + + +ads--CAutoHideDockContainer[sideBarLocation="0"] ads--CResizeHandle { + border-top: 1px solid palette(dark); +} + +ads--CAutoHideDockContainer[sideBarLocation="1"] ads--CResizeHandle { + border-left: 1px solid palette(dark); +} + +ads--CAutoHideDockContainer[sideBarLocation="2"] ads--CResizeHandle { + border-right: 1px solid palette(dark); +} + +ads--CAutoHideDockContainer[sideBarLocation="3"] ads--CResizeHandle { + border-top: 1px solid palette(dark); +} + + diff --git a/src/stylesheets/default_linux.css b/src/stylesheets/default_linux.css index ac41491eb..573d7dfe6 100644 --- a/src/stylesheets/default_linux.css +++ b/src/stylesheets/default_linux.css @@ -1,26 +1,67 @@ /* * Default style sheet on Linux Platforms */ + +/***************************************************************************** + * CDockContainerWidget + *****************************************************************************/ ads--CDockContainerWidget { - background: palette(dark); + background: palette(window); } + + +/***************************************************************************** + * CDockSplitter + *****************************************************************************/ ads--CDockContainerWidget > QSplitter{ - padding: 1 0 1 0; + padding: 1 0 1 0; } ads--CDockContainerWidget ads--CDockSplitter::handle { - background: palette(dark); + background: palette(dark); } + +/***************************************************************************** + * CDockAreaWidget + *****************************************************************************/ ads--CDockAreaWidget { - background: palette(window); - border: 1px solid white; + background: palette(window); } ads--CDockAreaWidget #tabsMenuButton::menu-indicator { - image: none; + image: none; +} + + +ads--CTitleBarButton { + padding: 0px 0px; +} + + +#tabsMenuButton { + qproperty-icon: url(:/ads/images/tabs-menu-button.svg); + qproperty-iconSize: 16px; +} + + +#dockAreaCloseButton { + qproperty-icon: url(:/ads/images/close-button.svg), + url(:/ads/images/close-button-disabled.svg) disabled; + qproperty-iconSize: 16px; } + +#detachGroupButton { + qproperty-icon: url(:/ads/images/detach-button.svg), + url(:/ads/images/detach-button-disabled.svg) disabled; + qproperty-iconSize: 16px; +} + + +/***************************************************************************** + * CDockWidgetTab and close button styling + *****************************************************************************/ ads--CDockWidgetTab { background: palette(window); border-color: palette(light); @@ -29,73 +70,64 @@ ads--CDockWidgetTab { padding: 0 0px; } + ads--CDockWidgetTab[activeTab="true"] { background: qlineargradient(spread : pad, x1 : 0, y1 : 0, x2 : 0, y2 : 0.5, stop : 0 palette(window), stop:1 palette(light)); /*background: palette(highlight);*/ } + ads--CDockWidgetTab QLabel { color: palette(dark); } + ads--CDockWidgetTab[activeTab="true"] QLabel { color: palette(foreground); } -ads--CDockWidget { - background: palette(light); - border-color: palette(light); - border-style: solid; - border-width: 1px 0 0 0; -} - -ads--CTitleBarButton { - padding: 0px 0px; -} - -QScrollArea#dockWidgetScrollArea { - padding: 0px; - border: none; -} #tabCloseButton { - margin-top: 2px; - background: none; - border: none; - padding: 0px -2px; - qproperty-icon: url(:/ads/images/close-button.svg), - url(:/ads/images/close-button-disabled.svg) disabled; - qproperty-iconSize: 16px; + margin-top: 2px; + background: none; + border: none; + padding: 0px -2px; + qproperty-icon: url(:/ads/images/close-button.svg), + url(:/ads/images/close-button-disabled.svg) disabled; + qproperty-iconSize: 16px; } #tabCloseButton:hover { - border: 1px solid rgba(0, 0, 0, 32); - background: rgba(0, 0, 0, 16); + border: 1px solid rgba(0, 0, 0, 32); + background: rgba(0, 0, 0, 16); } #tabCloseButton:pressed { - background: rgba(0, 0, 0, 32); + background: rgba(0, 0, 0, 32); } -#tabsMenuButton { - qproperty-icon: url(:/ads/images/tabs-menu-button.svg); - qproperty-iconSize: 16px; -} -#dockAreaCloseButton { - qproperty-icon: url(:/ads/images/close-button.svg), - url(:/ads/images/close-button-disabled.svg) disabled; - qproperty-iconSize: 16px; +/***************************************************************************** + * CDockWidget + *****************************************************************************/ +ads--CDockWidget { + background: palette(light); + border-color: palette(light); + border-style: solid; + border-width: 1px 0 0 0; } -#detachGroupButton { - qproperty-icon: url(:/ads/images/detach-button.svg), - url(:/ads/images/detach-button-disabled.svg) disabled; - qproperty-iconSize: 16px; + +QScrollArea#dockWidgetScrollArea { + padding: 0px; + border: none; } +/***************************************************************************** + * Floating widget styling + *****************************************************************************/ ads--CFloatingWidgetTitleBar { background: palette(midlight); qproperty-maximizeIcon: url(:/ads/images/maximize-button.svg); @@ -122,3 +154,229 @@ ads--CFloatingWidgetTitleBar { #floatingTitleCloseButton:pressed { background: rgba(0, 0, 0, 48); } + + +/***************************************************************************** + * + * Styling of auto hide functionality + * + *****************************************************************************/ + + +/***************************************************************************** + * CAutoHideTab + *****************************************************************************/ +ads--CAutoHideTab { + qproperty-iconSize: 16px 16px;/* this is optional in case you would like to change icon size*/ + background: none; + border: none; + padding-left: 2px; + padding-right: 0px; + text-align: center; + min-height: 20px; + padding-bottom: 2px; +} + + +ads--CAutoHideTab:hover +{ + color: palette(highlight); +} + + +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="0"], +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="2"] { + border-top: 6px solid rgba(0, 0, 0, 48); +} + + +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="1"], +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="3"] { + border-bottom: 6px solid rgba(0, 0, 0, 48); +} + + + +ads--CAutoHideTab:hover[iconOnly="false"][sideBarLocation="0"], +ads--CAutoHideTab:hover[iconOnly="false"][sideBarLocation="2"], +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="0"][activeTab="true"], +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="2"][activeTab="true"] { + border-top: 6px solid palette(highlight); +} + + +ads--CAutoHideTab:hover[iconOnly="false"][sideBarLocation="1"], +ads--CAutoHideTab:hover[iconOnly="false"][sideBarLocation="3"], +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="1"][activeTab="true"], +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="3"][activeTab="true"] { + border-bottom: 6px solid palette(highlight); +} + + +/** + * Auto hide tabs with icon only + */ +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="0"] { + border-top: 6px solid rgba(0, 0, 0, 48); +} + +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="1"] { + border-left: 6px solid rgba(0, 0, 0, 48); +} + +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="2"] { + border-right: 6px solid rgba(0, 0, 0, 48); +} + +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="3"] { + border-bottom: 6px solid rgba(0, 0, 0, 48); +} + + +/** + * Auto hide tabs with icon only hover + */ +ads--CAutoHideTab:hover[iconOnly="true"][sideBarLocation="0"], +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="0"][activeTab="true"] { + border-top: 6px solid palette(highlight); +} + +ads--CAutoHideTab:hover[iconOnly="true"][sideBarLocation="1"], +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="1"][activeTab="true"] { + border-left: 6px solid palette(highlight); +} + +ads--CAutoHideTab:hover[iconOnly="true"][sideBarLocation="2"], +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="2"][activeTab="true"] { + border-right: 6px solid palette(highlight); +} + +ads--CAutoHideTab:hover[iconOnly="true"][sideBarLocation="3"], +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="3"][activeTab="true"] { + border-bottom: 6px solid palette(highlight); +} + + +/***************************************************************************** + * CAutoHideSideBar + *****************************************************************************/ +ads--CAutoHideSideBar{ + background: palette(window); + border: none; + qproperty-spacing: 12; +} + +#sideTabsContainerWidget { + background: transparent; +} + + +ads--CAutoHideSideBar[sideBarLocation="0"] { + border-bottom: 1px solid palette(dark); +} + +ads--CAutoHideSideBar[sideBarLocation="1"] { + border-right: 1px solid palette(dark); +} + +ads--CAutoHideSideBar[sideBarLocation="2"] { + border-left: 1px solid palette(dark); +} + +ads--CAutoHideSideBar[sideBarLocation="3"] { + border-top: 1px solid palette(dark); +} + + +/***************************************************************************** + * CAutoHideDockContainer + *****************************************************************************/ +ads--CAutoHideDockContainer { + background: palette(window); +} + + +ads--CAutoHideDockContainer ads--CDockAreaTitleBar { + background: palette(highlight); + padding: 0px; + border: none; +} + + +/* + * This is required because the ads--CDockAreaWidget[focused="true"] will + * overwrite the ads--CAutoHideDockContainer ads--CDockAreaTitleBar rule + */ +ads--CAutoHideDockContainer ads--CDockAreaWidget[focused="true"] ads--CDockAreaTitleBar { + background: palette(highlight); + padding: 0px; + border: none; +} + + +#autoHideTitleLabel { + padding-left: 4px; + color: palette(light); +} + + +/***************************************************************************** + * CAutoHideDockContainer titlebar buttons + *****************************************************************************/ +#dockAreaAutoHideButton { + qproperty-icon: url(:/ads/images/vs-pin-button.svg); + qproperty-iconSize: 16px; +} + +ads--CAutoHideDockContainer #dockAreaAutoHideButton { + qproperty-icon: url(:/ads/images/vs-pin-button-pinned-focused.svg); + qproperty-iconSize: 16px; +} + + +ads--CAutoHideDockContainer #dockAreaCloseButton{ + qproperty-icon: url(:/ads/images/close-button-focused.svg) +} + + +ads--CAutoHideDockContainer ads--CTitleBarButton:hover { + background: rgba(255, 255, 255, 48); + border: none; +} + +ads--CAutoHideDockContainer ads--CTitleBarButton:pressed { + background: rgba(255, 255, 255, 96); + border: none; +} + +/***************************************************************************** + * CAutoHideDockContainer Titlebar and Buttons + *****************************************************************************/ + + +/***************************************************************************** + * CResizeHandle + *****************************************************************************/ +ads--CResizeHandle { + background: palette(window); +} + + +ads--CAutoHideDockContainer[sideBarLocation="0"] ads--CResizeHandle { + border-top: 1px solid palette(dark); +} + +ads--CAutoHideDockContainer[sideBarLocation="1"] ads--CResizeHandle { + border-left: 1px solid palette(dark); +} + +ads--CAutoHideDockContainer[sideBarLocation="2"] ads--CResizeHandle { + border-right: 1px solid palette(dark); +} + +ads--CAutoHideDockContainer[sideBarLocation="3"] ads--CResizeHandle { + border-top: 1px solid palette(dark); +} + + + diff --git a/src/stylesheets/focus_highlighting.css b/src/stylesheets/focus_highlighting.css index 555172184..0fd9f183a 100644 --- a/src/stylesheets/focus_highlighting.css +++ b/src/stylesheets/focus_highlighting.css @@ -1,18 +1,77 @@ /* * Default style sheet on Windows Platforms with focus highlighting flag enabled */ + + +/***************************************************************************** + * CDockContainerWidget + *****************************************************************************/ ads--CDockContainerWidget { - background: palette(dark); + background: palette(window); +} + + +/***************************************************************************** + * CDockSplitter + *****************************************************************************/ +ads--CDockContainerWidget > QSplitter { + padding: 1 0 1 0; } -ads--CDockContainerWidget > QSplitter{ - padding: 1 0 1 0; + + +ads--CDockSplitter::handle { + background-color: palette(dark); + /* uncomment the following line if you would like to change the size of + the splitter handles */ + /* height: 1px; */ } + +/***************************************************************************** + * CDockAreaWidget + *****************************************************************************/ ads--CDockAreaWidget { background: palette(window); - border: 1px solid white; } + +ads--CDockAreaTitleBar { + background: transparent; + border-bottom: 2px solid palette(light); + padding-bottom: 0px; +} + +ads--CDockAreaWidget[focused="true"] ads--CDockAreaTitleBar { + border-bottom: 2px solid palette(highlight); +} + +ads--CTitleBarButton { + padding: 0px 0px; +} + + +#tabsMenuButton::menu-indicator { + image: none; +} + + +#dockAreaCloseButton { + qproperty-icon: url(:/ads/images/close-button.svg), + url(:/ads/images/close-button-disabled.svg) disabled; + qproperty-iconSize: 16px; +} + +#detachGroupButton { + qproperty-icon: url(:/ads/images/detach-button.svg), + url(:/ads/images/detach-button-disabled.svg) disabled; + qproperty-iconSize: 16px; +} + + + +/***************************************************************************** + * CDockWidgetTab + *****************************************************************************/ ads--CDockWidgetTab { background: palette(window); border-color: palette(light); @@ -36,6 +95,53 @@ ads--CDockWidgetTab[activeTab="true"] QLabel { color: palette(foreground); } + +#tabCloseButton { + margin-top: 2px; + background: none; + border: none; + padding: 0px -2px; + qproperty-icon: url(:/ads/images/close-button.svg), + url(:/ads/images/close-button-disabled.svg) disabled; + qproperty-iconSize: 16px; +} + +#tabCloseButton:hover { + /*border: 1px solid rgba(0, 0, 0, 32);*/ + background: rgba(0, 0, 0, 24); +} + +#tabCloseButton:pressed { + background: rgba(0, 0, 0, 48); +} + +/* Focus related styling */ +ads--CDockWidgetTab[focused="true"] { + background: palette(highlight); + border-color: palette(highlight); +} + +ads--CDockWidgetTab[focused="true"] > #tabCloseButton { + qproperty-icon: url(:/ads/images/close-button-focused.svg) +} + +ads--CDockWidgetTab[focused="true"] > #tabCloseButton:hover { + background: rgba(255, 255, 255, 48); +} + +ads--CDockWidgetTab[focused="true"] > #tabCloseButton:pressed { + background: rgba(255, 255, 255, 92); +} + +ads--CDockWidgetTab[focused="true"] QLabel { + color: palette(light); +} + + + +/***************************************************************************** + * CDockWidget + *****************************************************************************/ ads--CDockWidget { background: palette(light); border-color: palette(light); @@ -43,87 +149,231 @@ ads--CDockWidget { border-width: 1px 0 0 0; } -ads--CTitleBarButton { - padding: 0px 0px; -} QScrollArea#dockWidgetScrollArea { padding: 0px; border: none; } -#tabsMenuButton::menu-indicator { - image: none; -} -#tabCloseButton { - margin-top: 2px; + +/***************************************************************************** + * + * Styling of auto hide functionality + * + *****************************************************************************/ + + +/***************************************************************************** + * CAutoHideTab + *****************************************************************************/ +ads--CAutoHideTab { + qproperty-iconSize: 16px 16px;/* this is optional in case you would like to change icon size*/ background: none; border: none; - padding: 0px -2px; - qproperty-icon: url(:/ads/images/close-button.svg), - url(:/ads/images/close-button-disabled.svg) disabled; - qproperty-iconSize: 16px; + padding-left: 2px; + padding-right: 0px; + text-align: center; + min-height: 20px; + padding-bottom: 2px; } -#tabCloseButton:hover { - /*border: 1px solid rgba(0, 0, 0, 32);*/ - background: rgba(0, 0, 0, 24); + +ads--CAutoHideTab:hover +{ + color: palette(highlight); } -#tabCloseButton:pressed { - background: rgba(0, 0, 0, 48); + +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="0"], +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="2"] { + border-top: 6px solid rgba(0, 0, 0, 48); } -#dockAreaCloseButton { - qproperty-icon: url(:/ads/images/close-button.svg), - url(:/ads/images/close-button-disabled.svg) disabled; - qproperty-iconSize: 16px; + +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="1"], +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="3"] { + border-bottom: 6px solid rgba(0, 0, 0, 48); } -#detachGroupButton { - qproperty-icon: url(:/ads/images/detach-button.svg), - url(:/ads/images/detach-button-disabled.svg) disabled; - qproperty-iconSize: 16px; + + +ads--CAutoHideTab:hover[iconOnly="false"][sideBarLocation="0"], +ads--CAutoHideTab:hover[iconOnly="false"][sideBarLocation="2"], +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="0"][activeTab="true"], +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="2"][activeTab="true"] { + border-top: 6px solid palette(highlight); } -ads--CDockSplitter::handle { - background-color: palette(dark); - /* uncomment the following line if you would like to change the size of - the splitter handles */ - /* height: 1px; */ + +ads--CAutoHideTab:hover[iconOnly="false"][sideBarLocation="1"], +ads--CAutoHideTab:hover[iconOnly="false"][sideBarLocation="3"], +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="1"][activeTab="true"], +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="3"][activeTab="true"] { + border-bottom: 6px solid palette(highlight); } -/* Focus related styling */ -ads--CDockWidgetTab[focused="true"] { + +/** + * Auto hide tabs with icon only + */ +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="0"] { + border-top: 6px solid rgba(0, 0, 0, 48); +} + +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="1"] { + border-left: 6px solid rgba(0, 0, 0, 48); +} + +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="2"] { + border-right: 6px solid rgba(0, 0, 0, 48); +} + +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="3"] { + border-bottom: 6px solid rgba(0, 0, 0, 48); +} + + +/** + * Auto hide tabs with icon only hover + */ +ads--CAutoHideTab:hover[iconOnly="true"][sideBarLocation="0"], +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="0"][activeTab="true"] { + border-top: 6px solid palette(highlight); +} + +ads--CAutoHideTab:hover[iconOnly="true"][sideBarLocation="1"], +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="1"][activeTab="true"] { + border-left: 6px solid palette(highlight); +} + +ads--CAutoHideTab:hover[iconOnly="true"][sideBarLocation="2"], +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="2"][activeTab="true"] { + border-right: 6px solid palette(highlight); +} + +ads--CAutoHideTab:hover[iconOnly="true"][sideBarLocation="3"], +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="3"][activeTab="true"] { + border-bottom: 6px solid palette(highlight); +} + + + +/***************************************************************************** + * CAutoHideSideBar + *****************************************************************************/ +ads--CAutoHideSideBar{ + background: palette(window); + border: none; + qproperty-spacing: 12; +} + +#sideTabsContainerWidget { + background: transparent; +} + + +ads--CAutoHideSideBar[sideBarLocation="0"] { + border-bottom: 1px solid palette(dark); +} + +ads--CAutoHideSideBar[sideBarLocation="1"] { + border-right: 1px solid palette(dark); +} + +ads--CAutoHideSideBar[sideBarLocation="2"] { + border-left: 1px solid palette(dark); +} + +ads--CAutoHideSideBar[sideBarLocation="3"] { + border-top: 1px solid palette(dark); +} + + +/***************************************************************************** + * CAutoHideDockContainer + *****************************************************************************/ +ads--CAutoHideDockContainer { + background: palette(window); +} + + +ads--CAutoHideDockContainer ads--CDockAreaTitleBar { background: palette(highlight); - border-color: palette(highlight); + padding: 0px; + border: none; } -ads--CDockWidgetTab[focused="true"]>#tabCloseButton { + +/* + * This is required because the ads--CDockAreaWidget[focused="true"] will + * overwrite the ads--CAutoHideDockContainer ads--CDockAreaTitleBar rule + */ +ads--CAutoHideDockContainer ads--CDockAreaWidget[focused="true"] ads--CDockAreaTitleBar { + background: palette(highlight); + padding: 0px; + border: none; +} + + +#autoHideTitleLabel { + padding-left: 4px; + color: palette(light); +} + + +/***************************************************************************** + * CAutoHideDockContainer titlebar buttons + *****************************************************************************/ +#dockAreaAutoHideButton { + qproperty-icon: url(:/ads/images/vs-pin-button.svg); + qproperty-iconSize: 16px; +} + +ads--CAutoHideDockContainer #dockAreaAutoHideButton { + qproperty-icon: url(:/ads/images/vs-pin-button-pinned-focused.svg); + qproperty-iconSize: 16px; +} + + +ads--CAutoHideDockContainer #dockAreaCloseButton{ qproperty-icon: url(:/ads/images/close-button-focused.svg) } -ads--CDockWidgetTab[focused="true"]>#tabCloseButton:hover { + +ads--CAutoHideDockContainer ads--CTitleBarButton:hover { background: rgba(255, 255, 255, 48); } -ads--CDockWidgetTab[focused="true"]>#tabCloseButton:pressed { - background: rgba(255, 255, 255, 92); +ads--CAutoHideDockContainer ads--CTitleBarButton:pressed { + background: rgba(255, 255, 255, 96); } -ads--CDockWidgetTab[focused="true"] QLabel { - color: palette(light); +/***************************************************************************** + * CAutoHideDockContainer Titlebar and Buttons + *****************************************************************************/ + + +/***************************************************************************** + * CResizeHandle + *****************************************************************************/ +ads--CResizeHandle { + background: palette(window); } -ads--CDockAreaTitleBar { - background: transparent; - border-bottom: 2px solid palette(light); - padding-bottom: 0px; + +ads--CAutoHideDockContainer[sideBarLocation="0"] ads--CResizeHandle { + border-top: 1px solid palette(dark); } -ads--CDockAreaWidget[focused="true"] ads--CDockAreaTitleBar { - background: transparent; - border-bottom: 2px solid palette(highlight); - padding-bottom: 0px; +ads--CAutoHideDockContainer[sideBarLocation="1"] ads--CResizeHandle { + border-left: 1px solid palette(dark); +} + +ads--CAutoHideDockContainer[sideBarLocation="2"] ads--CResizeHandle { + border-right: 1px solid palette(dark); +} + +ads--CAutoHideDockContainer[sideBarLocation="3"] ads--CResizeHandle { + border-top: 1px solid palette(dark); } diff --git a/src/stylesheets/focus_highlighting_linux.css b/src/stylesheets/focus_highlighting_linux.css index f16ab5e82..f21b137a4 100644 --- a/src/stylesheets/focus_highlighting_linux.css +++ b/src/stylesheets/focus_highlighting_linux.css @@ -1,9 +1,18 @@ /* * Default style sheet on Linux Platforms with focus highlighting flag enabled */ + +/***************************************************************************** + * CDockContainerWidget + *****************************************************************************/ ads--CDockContainerWidget { - background: palette(dark); + background: palette(window); } + + +/***************************************************************************** + * CDockSplitter + *****************************************************************************/ ads--CDockContainerWidget > QSplitter{ padding: 1 0 1 0; } @@ -13,15 +22,64 @@ ads--CDockContainerWidget ads--CDockSplitter::handle { background: palette(dark); } + +/***************************************************************************** + * CDockAreaWidget + *****************************************************************************/ ads--CDockAreaWidget { background: palette(window); - border: 1px solid white; } ads--CDockAreaWidget #tabsMenuButton::menu-indicator { image: none; } + +ads--CTitleBarButton { + padding: 0px 0px; +} + + +#tabsMenuButton { + qproperty-icon: url(:/ads/images/tabs-menu-button.svg); + qproperty-iconSize: 16px; +} + + +#dockAreaCloseButton { + qproperty-icon: url(:/ads/images/close-button.svg), + url(:/ads/images/close-button-disabled.svg) disabled; + qproperty-iconSize: 16px; +} + + +#detachGroupButton { + qproperty-icon: url(:/ads/images/detach-button.svg), + url(:/ads/images/detach-button-disabled.svg) disabled; + qproperty-iconSize: 16px; +} + + + +/***************************************************************************** + * CDockAreaTitleBar + *****************************************************************************/ +ads--CDockAreaTitleBar { + background: transparent; + border-bottom: 2px solid palette(light); + padding-bottom: 0px; +} + +ads--CDockAreaWidget[focused="true"] ads--CDockAreaTitleBar { + background: transparent; + border-bottom: 2px solid palette(highlight); + padding-bottom: 0px; +} + + +/***************************************************************************** + * CDockWidgetTab and close button styling + *****************************************************************************/ ads--CDockWidgetTab { background: palette(window); border-color: palette(light); @@ -44,33 +102,6 @@ ads--CDockWidgetTab[activeTab="true"] QLabel { color: palette(foreground); } -ads--CDockWidget { - background: palette(light); - border-color: palette(light); - border-style: solid; - border-width: 1px 0 0 0; -} - -ads--CTitleBarButton { - padding: 0px 0px; -} - -QScrollArea#dockWidgetScrollArea { - padding: 0px; - border: none; -} - -#dockAreaCloseButton { - qproperty-icon: url(:/ads/images/close-button.svg), - url(:/ads/images/close-button-disabled.svg) disabled; - qproperty-iconSize: 16px; -} - -#detachGroupButton { - qproperty-icon: url(:/ads/images/detach-button.svg), - url(:/ads/images/detach-button-disabled.svg) disabled; - qproperty-iconSize: 16px; -} #tabCloseButton { margin-top: 2px; @@ -92,27 +123,21 @@ QScrollArea#dockWidgetScrollArea { } -#tabsMenuButton { - qproperty-icon: url(:/ads/images/tabs-menu-button.svg); - qproperty-iconSize: 16px; -} - - /* Focus related styling */ ads--CDockWidgetTab[focused="true"] { background: palette(highlight); border-color: palette(highlight); } -ads--CDockWidgetTab[focused="true"]>#tabCloseButton { +ads--CDockWidgetTab[focused="true"] > #tabCloseButton { qproperty-icon: url(:/ads/images/close-button-focused.svg) } -ads--CDockWidgetTab[focused="true"]>#tabCloseButton:hover { +ads--CDockWidgetTab[focused="true"] > #tabCloseButton:hover { background: rgba(255, 255, 255, 48); } -ads--CDockWidgetTab[focused="true"]>#tabCloseButton:pressed { +ads--CDockWidgetTab[focused="true"] > #tabCloseButton:pressed { background: rgba(255, 255, 255, 92); } @@ -120,19 +145,28 @@ ads--CDockWidgetTab[focused="true"] QLabel { color: palette(light); } -ads--CDockAreaTitleBar { - background: transparent; - border-bottom: 2px solid palette(light); - padding-bottom: 0px; + +/***************************************************************************** + * CDockWidget + *****************************************************************************/ +ads--CDockWidget { + background: palette(light); + border-color: palette(light); + border-style: solid; + border-width: 1px 0 0 0; } -ads--CDockAreaWidget[focused="true"] ads--CDockAreaTitleBar { - background: transparent; - border-bottom: 2px solid palette(highlight); - padding-bottom: 0px; + +QScrollArea#dockWidgetScrollArea { + padding: 0px; + border: none; } + +/***************************************************************************** + * Floating widget styling + *****************************************************************************/ ads--CFloatingWidgetTitleBar { qproperty-maximizeIcon: url(:/ads/images/maximize-button.svg); qproperty-normalIcon: url(:/ads/images/restore-button.svg); @@ -197,3 +231,227 @@ ads--CFloatingDockContainer[isActiveWindow="true"] #floatingTitleMaximizeButton: } */ + +/***************************************************************************** + * + * Styling of auto hide functionality + * + *****************************************************************************/ + + +/***************************************************************************** + * CAutoHideTab + *****************************************************************************/ +ads--CAutoHideTab { + qproperty-iconSize: 16px 16px;/* this is optional in case you would like to change icon size*/ + background: none; + border: none; + padding-left: 2px; + padding-right: 0px; + text-align: center; + min-height: 20px; + padding-bottom: 2px; +} + + +ads--CAutoHideTab:hover +{ + color: palette(highlight); +} + + +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="0"], +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="2"] { + border-top: 6px solid rgba(0, 0, 0, 48); +} + + +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="1"], +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="3"] { + border-bottom: 6px solid rgba(0, 0, 0, 48); +} + + + +ads--CAutoHideTab:hover[iconOnly="false"][sideBarLocation="0"], +ads--CAutoHideTab:hover[iconOnly="false"][sideBarLocation="2"], +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="0"][activeTab="true"], +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="2"][activeTab="true"] { + border-top: 6px solid palette(highlight); +} + + +ads--CAutoHideTab:hover[iconOnly="false"][sideBarLocation="1"], +ads--CAutoHideTab:hover[iconOnly="false"][sideBarLocation="3"], +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="1"][activeTab="true"], +ads--CAutoHideTab[iconOnly="false"][sideBarLocation="3"][activeTab="true"] { + border-bottom: 6px solid palette(highlight); +} + + +/** + * Auto hide tabs with icon only + */ +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="0"] { + border-top: 6px solid rgba(0, 0, 0, 48); +} + +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="1"] { + border-left: 6px solid rgba(0, 0, 0, 48); +} + +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="2"] { + border-right: 6px solid rgba(0, 0, 0, 48); +} + +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="3"] { + border-bottom: 6px solid rgba(0, 0, 0, 48); +} + + +/** + * Auto hide tabs with icon only hover + */ +ads--CAutoHideTab:hover[iconOnly="true"][sideBarLocation="0"], +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="0"][activeTab="true"] { + border-top: 6px solid palette(highlight); +} + +ads--CAutoHideTab:hover[iconOnly="true"][sideBarLocation="1"], +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="1"][activeTab="true"] { + border-left: 6px solid palette(highlight); +} + +ads--CAutoHideTab:hover[iconOnly="true"][sideBarLocation="2"], +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="2"][activeTab="true"] { + border-right: 6px solid palette(highlight); +} + +ads--CAutoHideTab:hover[iconOnly="true"][sideBarLocation="3"], +ads--CAutoHideTab[iconOnly="true"][sideBarLocation="3"][activeTab="true"] { + border-bottom: 6px solid palette(highlight); +} + + + +/***************************************************************************** + * CAutoHideSideBar + *****************************************************************************/ +ads--CAutoHideSideBar{ + background: palette(window); + border: none; + qproperty-spacing: 12; +} + +#sideTabsContainerWidget { + background: transparent; +} + + +ads--CAutoHideSideBar[sideBarLocation="0"] { + border-bottom: 1px solid palette(dark); +} + +ads--CAutoHideSideBar[sideBarLocation="1"] { + border-right: 1px solid palette(dark); +} + +ads--CAutoHideSideBar[sideBarLocation="2"] { + border-left: 1px solid palette(dark); +} + +ads--CAutoHideSideBar[sideBarLocation="3"] { + border-top: 1px solid palette(dark); +} + + +/***************************************************************************** + * CAutoHideDockContainer + *****************************************************************************/ +ads--CAutoHideDockContainer { + background: palette(window); +} + + +ads--CAutoHideDockContainer ads--CDockAreaTitleBar { + background: palette(highlight); + padding: 0px; + border: none; +} + + +/* + * This is required because the ads--CDockAreaWidget[focused="true"] will + * overwrite the ads--CAutoHideDockContainer ads--CDockAreaTitleBar rule + */ +ads--CAutoHideDockContainer ads--CDockAreaWidget[focused="true"] ads--CDockAreaTitleBar { + background: palette(highlight); + padding: 0px; + border: none; +} + + +#autoHideTitleLabel { + padding-left: 4px; + color: palette(light); +} + + +/***************************************************************************** + * CAutoHideDockContainer titlebar buttons + *****************************************************************************/ +#dockAreaAutoHideButton { + qproperty-icon: url(:/ads/images/vs-pin-button.svg); + qproperty-iconSize: 16px; +} + +ads--CAutoHideDockContainer #dockAreaAutoHideButton { + qproperty-icon: url(:/ads/images/vs-pin-button-pinned-focused.svg); + qproperty-iconSize: 16px; +} + + +ads--CAutoHideDockContainer #dockAreaCloseButton{ + qproperty-icon: url(:/ads/images/close-button-focused.svg) +} + + +ads--CAutoHideDockContainer ads--CTitleBarButton:hover { + background: rgba(255, 255, 255, 48); + border: none; +} + +ads--CAutoHideDockContainer ads--CTitleBarButton:pressed { + background: rgba(255, 255, 255, 96); + border: none; +} + +/***************************************************************************** + * CAutoHideDockContainer Titlebar and Buttons + *****************************************************************************/ + + +/***************************************************************************** + * CResizeHandle + *****************************************************************************/ +ads--CResizeHandle { + background: palette(window); +} + + +ads--CAutoHideDockContainer[sideBarLocation="0"] ads--CResizeHandle { + border-top: 1px solid palette(dark); +} + +ads--CAutoHideDockContainer[sideBarLocation="1"] ads--CResizeHandle { + border-left: 1px solid palette(dark); +} + +ads--CAutoHideDockContainer[sideBarLocation="2"] ads--CResizeHandle { + border-right: 1px solid palette(dark); +} + +ads--CAutoHideDockContainer[sideBarLocation="3"] ads--CResizeHandle { + border-top: 1px solid palette(dark); +} +