diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..f044fe5d7 --- /dev/null +++ b/.clang-format @@ -0,0 +1,146 @@ +--- +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveMacros: None +AlignConsecutiveAssignments: None +AlignConsecutiveDeclarations: None +AlignEscapedNewlines: Right +AlignOperands: Align +AlignTrailingComments: true + +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: true +AllowShortFunctionsOnASingleLine: InlineOnly +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: Inline +AllowShortLoopsOnASingleLine: false + +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes + +BinPackArguments: true +BinPackParameters: true + +BraceWrapping: + AfterCaseLabel: true + AfterClass: true + AfterControlStatement: Always + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterStruct: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: true + BeforeElse: true + BeforeLambdaBody: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false + +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBraces: Custom +BreakBeforeTernaryOperators: false +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: AfterColon +BreakStringLiterals: true + +ColumnLimit: 82 +CommentPragmas: '^(!.*|@c)' + +CompactNamespaces: false + +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true + +FixNamespaceComments: true + +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^' # windows headers + Priority: -1 + - Regex: '^' # Qt headers + Priority: 3 + - Regex: '^' # other headers + Priority: 5 + - Regex: '<[[:alnum:]._/]+>' # system headers + Priority: 6 + - Regex: '.*' + Priority: 7 + +IncludeIsMainRegex: '(_p)?$' + +IndentCaseBlocks: false +IndentCaseLabels: false +IndentPPDirectives: AfterHash +IndentWidth: 4 +IndentWrappedFunctionNames: false + +KeepEmptyLinesAtTheStartOfBlocks: false +MaxEmptyLinesToKeep: 1 + +NamespaceIndentation: None + +PenaltyBreakAssignment: 20 +PenaltyBreakBeforeFirstCallParameter: 15 +PenaltyBreakComment: 10 +PenaltyBreakFirstLessLess: 5 +# PenaltyBreakOpenParenthesis: 30 +PenaltyBreakString: 150 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 100 +PenaltyIndentedWhitespace: 0 +PenaltyReturnTypeOnItsOwnLine: 150 + +PointerAlignment: Left +ReflowComments: true + +SortIncludes: CaseSensitive +SortUsingDeclarations: true + +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false + +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false + +SpacesBeforeTrailingComments: 2 +SpacesInAngles: Never +SpacesInCStyleCastParentheses: false +SpacesInConditionalStatement: false +SpacesInParentheses: false +SpacesInSquareBrackets: false + +Standard: c++17 + +StatementMacros: [ 'Q_UNUSED', 'PIMPL_D', 'PIMPL_Q', 'OD_ENTRY', 'OD_ENTRY_PROCIMG' ] + +TabWidth: 4 +UseTab: Never diff --git a/.github/workflows/linux-builds.yml b/.github/workflows/linux-builds.yml index c102eb0de..eb7265356 100644 --- a/.github/workflows/linux-builds.yml +++ b/.github/workflows/linux-builds.yml @@ -1,34 +1,59 @@ name: linux-builds -on: [push] +on: + push: + workflow_dispatch: -jobs: - build: - strategy: - matrix: - os: [ubuntu-20.04] +jobs: + build_ubuntu_2204_cmake: + runs-on: ubuntu-22.04 + + env: + QT_VERSION: 6.4.2 + QT_DIR: ${{ github.workspace }}/Qt - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v1 - - name: install qt + - name: Checkout source + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Fetch full history and tags + + - name: Install dependencies run: | - sudo apt-get update --fix-missing - sudo apt-get install qt5-default - sudo apt-get install qtbase5-private-dev - - name: qmake - run: qmake - - name: make - run: make -j4 - + sudo apt-get update + sudo apt-get install -y build-essential cmake ninja-build libgl1-mesa-dev libxkbcommon-x11-0 libx11-dev + + - name: Install Qt + uses: jurplel/install-qt-action@v3 + with: + version: ${{ env.QT_VERSION }} + target: desktop + host: linux + arch: gcc_64 + dir: ${{ env.QT_DIR }} + setup-python: false + + - name: Configure with CMake + run: | + cmake -S . -B build \ + -DCMAKE_PREFIX_PATH="${{ env.QT_DIR }}/Qt/${{ env.QT_VERSION }}/gcc_64" \ + -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/install \ + -DCMAKE_BUILD_TYPE=Release \ + -G Ninja + + - name: Build + run: cmake --build build + + - name: Run CMake install (optional) + run: cmake --install build + build_ubuntu_2204: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v2 - name: Cache Qt id: cache-qt-6-4 - uses: actions/cache@v1 # not v2! + uses: actions/cache@v3 with: path: ../Qt/6.4.2 key: ${{ runner.os }}-QtCache-Qt6-4 diff --git a/.github/workflows/windows-cmake.yml b/.github/workflows/windows-cmake.yml new file mode 100644 index 000000000..620db953c --- /dev/null +++ b/.github/workflows/windows-cmake.yml @@ -0,0 +1,102 @@ +name: windows-builds + +on: + push: + workflow_dispatch: + +jobs: + build_windows_msvc: + name: Build with MSVC and Ninja + runs-on: windows-2022 + + env: + QT_VERSION: 6.4.2 + QT_DIR: ${{ github.workspace }}\Qt + + steps: + - name: 📦 Checkout source code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: ⚙️ Install Ninja build system + run: choco install ninja --no-progress + + - name: 📥 Install Qt for MSVC + uses: jurplel/install-qt-action@v3 + with: + version: ${{ env.QT_VERSION }} + target: desktop + host: windows + arch: win64_msvc2019_64 + dir: ${{ env.QT_DIR }} + setup-python: false + + - name: 🏗️ Setup MSVC Developer Environment + uses: TheMrMilchmann/setup-msvc-dev@v3 + with: + arch: x64 + + - name: 🛠️ Configure CMake with Ninja + MSVC + run: | + cmake -S . -B build -G Ninja ` + -DCMAKE_PREFIX_PATH="${{ env.QT_DIR }}\Qt\${{ env.QT_VERSION }}\msvc2019_64" ` + -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}\install" ` + -DCMAKE_BUILD_TYPE=Release + shell: powershell + + - name: 🔨 Build with Ninja + MSVC + run: cmake --build build + shell: powershell + + - name: 📦 Install built files + run: cmake --install build + shell: powershell + + build_windows_mingw: + name: Build with Qt's MinGW and CMake (no Ninja) + runs-on: windows-2022 + + env: + QT_VERSION: 6.8.3 + QT_DIR: ${{ github.workspace }}\Qt + + steps: + - name: 📦 Checkout source code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: 📥 Install Qt + MinGW + uses: jurplel/install-qt-action@v4 + with: + aqtversion: '==3.1.19' + version: ${{ env.QT_VERSION }} + target: desktop + host: windows + arch: win64_mingw + dir: ${{ env.QT_DIR }} + tools: 'tools_mingw1310' + setup-python: false + + - name: ➕ Add Qt-bundled MinGW to PATH + shell: powershell + run: | + echo "${{ env.QT_DIR }}\Tools\mingw1310_64\bin" >> $env:GITHUB_PATH + + - name: 🛠️ Configure CMake (MinGW) + shell: powershell + run: | + cmake -S . -B build-mingw ` + -DCMAKE_PREFIX_PATH="${{ env.QT_DIR }}\Qt\${{ env.QT_VERSION }}\mingw_64" ` + -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}\install ` + -DCMAKE_BUILD_TYPE=Release ` + -G "MinGW Makefiles" + + - name: 🔨 Build with CMake (MinGW) + shell: powershell + run: cmake --build build-mingw -- -j2 + + - name: 📦 Install built files (MinGW) + shell: powershell + run: cmake --install build-mingw diff --git a/.gitignore b/.gitignore index edb77ec89..2a7ed562d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -*.pro.user +*.pro.user* /build *.o *.dylib @@ -7,6 +7,9 @@ qrc_* moc_* ui_* Makefile +*.dll +*.a +build* # IDEs .idea diff --git a/CMakeLists.txt b/CMakeLists.txt index 72271a634..f9b1d7f4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,8 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.16) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +if (POLICY CMP0091) + cmake_policy(SET CMP0091 NEW) +endif (POLICY CMP0091) # By default, the version information is extracted from the git index. However, # we can override this behavior by explicitly setting ADS_VERSION and @@ -12,11 +16,13 @@ if(NOT ADS_VERSION) string(REGEX REPLACE "^[0-9]+\\.([0-9]+).*" "\\1" VERSION_MINOR "${GitTagVersion}") string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" VERSION_PATCH "${GitTagVersion}") set(VERSION_SHORT "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") + set(VERSION_SONAME "${VERSION_MAJOR}") else() string(REGEX MATCHALL "[\.]" VERSION_DOT_MATCHES ${ADS_VERSION}) list(LENGTH VERSION_DOT_MATCHES VERSION_DOT_COUNT) if(VERSION_DOT_COUNT EQUAL 2) set(VERSION_SHORT ${ADS_VERSION}) + string(REGEX REPLACE "^([0-9]+)\\..*" "\\1" VERSION_SONAME "${ADS_VERSION}") else() message(FATAL_ERROR "ADS_VERSION must be in major.minor.patch format, e.g. 3.8.1. Got ${ADS_VERSION}") endif() diff --git a/README.md b/README.md index dd2ccba0e..97ee54432 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,11 @@ ------------------ -[![GitHub release (latest by date)](https://img.shields.io/github/v/release/githubuser0xFFFF/Qt-Advanced-Docking-System?color=%23ff9833)](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/releases/latest) +[![GitHub release (latest by date)](https://img.shields.io/github/v/release/githubuser0xFFFF/Qt-Advanced-Docking-System)](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/releases/latest) [![License: LGPL v2.1](https://img.shields.io/badge/License-LGPL%20v2.1-blue.svg)](gnu-lgpl-v2.1.md) [![Build status](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/workflows/linux-builds/badge.svg)](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/actions?query=workflow%3Alinux-builds) [![Build status](https://ci.appveyor.com/api/projects/status/qcfb3cy932jw9mpy/branch/master?svg=true)](https://ci.appveyor.com/project/githubuser0xFFFF/qt-advanced-docking-system/branch/master) +[![windows-builds](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/actions/workflows/windows-cmake.yml/badge.svg?branch=master)](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/actions/workflows/windows-cmake.yml) [![GitHub contributors](https://img.shields.io/github/contributors/githubuser0xFFFF/Qt-Advanced-Docking-System?color=ffdf00)](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/graphs/contributors) Qt Advanced Docking System lets you create customizable layouts using a full @@ -15,13 +16,77 @@ featured window docking system similar to what is found in many popular integrated development environments (IDEs) such as Visual Studio. - [What's new...](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/releases/latest) -- [Documentation](doc/user-guide.md) -- Original Repository: https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System +- [Documentation](https://githubuser0xffff.github.io/Qt-Advanced-Docking-System/doc/user-guide.html) +- Original Repository: [https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System) [![Video Advanced Docking](doc/advanced-docking_video.png)](https://www.youtube.com/watch?v=7pdNfafg3Qc) ## New and Noteworthy +Release [4.1](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/releases/latest) significantly improves the Auto-Hide functionality and also brings improvements +for Drag and Drop of dock widgets into dock area tabs. These are the highlights of the new version: + +#### Drag & Drop to Auto-Hide + +Now you can easily drag any dock widget or any floating widget to the +borders of a window to pin it as a auto-hide tab in one of the 4 sidebars. +If you drag a dock widget close the one of the four window borders, special +drop overlays will be shown to indicate the drop area for auto-hide widgets: + +![Auo-Hide drag to Sidebar](doc/AutoHide_Drag_to_Sidebar.gif) + +Of course, this also works with dock areas: + +![Auo-Hide drag Dock Area](doc/AutoHide_Drag_DockArea.gif) + +If you drag a dock widget or dock area into a sidebar, then you even have +control over where tabs are inserted. Simply drag your mouse over a specific +auto-hide tab, and your dragged dock widget will be inserted before this tab. +Drag to the sidebar area behind the last tab, and the dragged widget will be +appended as last tab. In the following screen capture, the **Image Viewer 1** will +be inserted before the **Table 0** Auto-Hide tab and the **Image Viewer 2** +is appende behind the last tab: + +![Auo-Hide tab insert order](doc/AutoHide_Tab_Insert_Order.gif) + +#### Auto-Hide Tab Insertion Order + +It is also possible to drag Auto-Hide tabs to a new auto-hide position. +That means, you can drag them to a different border or sidebar: + +![Auto-Hide change sidebar](doc/AutoHide_Change_Sidebar.gif) + +#### Auto-Hide Tab Sorting + +You can drag Auto-Hide tabs to a new position in the current sidebar +to sort them: + +![Auo-Hide sort tabs](doc/AutoHide_Sort_Tabs.gif) + +#### Auto-Hide Drag to Float / Dock + +But that is not all. You can also simply move Auto-Hide tabs to another +floating widget or dock them via drag and drop: + +![Auo-Hide drag to float or dock](doc/AutoHide_Drag_to_Float_or_Dock.gif) + +#### Auto-Hide Context Menu + +All Auto-Hide tabs now have a context menu, that provides all the functionality +that you know from Dock widget tabs. With the **Pin To...** item from the +context menu it is very easy to move an Auto-Hide tab to a different Auto-Hide +sidebar: + +![Auo-Hide context menu](doc/AutoHide_Context_Menu.png) + +#### Dock Area Tab Insert Order + +And last but not least the new version also improves the docking of widgets +into the tabs of a Dock area. Just as with Auto-Hide tabs, you can now determine the position at which a tab is inserted by moving the mouse over an already existing tab (insertion before the tab) or behind the last tab +(appending): + +![Dock area tab insert order](doc/DockArea_Tab_Insertion_Order.gif) + The [release 4.0](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/releases/latest) adds the following features: @@ -77,6 +142,12 @@ know it from Visual Studio. ### Overview - [New and Noteworthy](#new-and-noteworthy) + - [Drag \& Drop to Auto-Hide](#drag--drop-to-auto-hide) + - [Auto-Hide Tab Insertion Order](#auto-hide-tab-insertion-order) + - [Auto-Hide Tab Sorting](#auto-hide-tab-sorting) + - [Auto-Hide Drag to Float / Dock](#auto-hide-drag-to-float--dock) + - [Auto-Hide Context Menu](#auto-hide-context-menu) + - [Dock Area Tab Insert Order](#dock-area-tab-insert-order) - [Features](#features) - [Overview](#overview) - [Docking everywhere - no central widget](#docking-everywhere---no-central-widget) @@ -91,6 +162,7 @@ know it from Visual Studio. - [Auto-Hide Functionality](#auto-hide-functionality) - [Python Bindings](#python-bindings) - [PySide6](#pyside6) + - [PyQt6](#pyqt6) - [PyQt5](#pyqt5) - [Tested Compatible Environments](#tested-compatible-environments) - [Supported Qt Versions](#supported-qt-versions) @@ -119,8 +191,10 @@ know it from Visual Studio. - [RDE – Robox Development Environment](#rde--robox-development-environment) - [ResInsight](#resinsight) - [ADTF 3](#adtf-3) - - [DREAM.3D NX](#dream3d-nx) + - [DREAM3D-NX](#dream3d-nx) - [LabPlot](#labplot) + - [Scrutiny Debugger](#scrutiny-debugger) + - [PySoWorks](#pysoworks) - [Alternative Docking System Implementations](#alternative-docking-system-implementations) - [KDDockWidgets](#kddockwidgets) - [QtitanDocking](#qtitandocking) @@ -147,7 +221,7 @@ into floating windows is supported. ### Grouped dragging -When dragging the titlebar of a dock, all the tabs that are tabbed with it are +When dragging the titlebar of a dock, all the tabs that are tabbed with it are going to be dragged. So you can move complete groups of tabbed widgets into a floating widget or from one dock area to another one. @@ -160,7 +234,7 @@ a floating widget or from one dock area to another one. A perspective defines the set and layout of dock windows in the main window. You can save the current layout of the dockmanager into a named perspective to make your own custom perspective. Later you can simply -select a perspective from the perspective list to quickly switch the complete +select a perspective from the perspective list to quickly switch the complete main window layout. ![Perspective](doc/perspectives.gif) @@ -182,6 +256,7 @@ If this flag is cleared, the widget resizing is deferred until the mouse button In contrast to the standard Qt docking system, docking with the ADS works more like a drag & drop operation. That means, the dragged dock widget or dock area is not undocked immediately. Instead, a drag preview widget is created and dragged around to indicate the future position of the dock widget or dock area. The actual dock operation is only executed when the mouse button is released. That makes it possible, to cancel an active drag operation with the escape key. The drag preview widget can be configured by a number of global dock manager flags: + - `DragPreviewIsDynamic`: if this flag is enabled, the preview will be adjusted dynamically to the drop area - `DragPreviewShowsContentPixmap`: the created drag preview window shows a static copy of the content of the dock widget / dock are that is dragged - `DragPreviewHasWindowFrame`: this flag configures if the drag preview is frameless like a QRubberBand or looks like a real window @@ -238,7 +313,7 @@ More about the auto hide configuration options in the [online documentation...]( ![Python Logo](doc/python_logo.png) Thanks to the contribution of several users, the Advanced Docking System comes -with a complete Python integration. Python bindings are available for **PyQt5** and +with a complete Python integration. Python bindings are available for **PyQt5**, **PyQt6**, and **PySide6**. ### PySide6 @@ -251,13 +326,30 @@ pip install PySide6-QtAds ``` Sample code is available [here](https://github.com/mborgerson/Qt-Advanced-Docking-System/tree/pyside6/examples). To run the samples, you'll also need to install latest qtpy -from source (pip install https://github.com/spyder-ide/qtpy/archive/refs/heads/master.zip). +from source (pip install ). The PySide6 bindings were contributed by: - [mborgerson](https://github.com/mborgerson) Please file PySide6-QtAds-specific issues on its [pyside6_qtads](https://github.com/mborgerson/pyside6_qtads) fork for tracking. For more information about the PySide6 bindings read [this](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/issues/298) issue. +### PyQt6 + +A PyQt6 ADS package is available via PyPi and can be installed on Windows, +macOS, and Linux with: + +```bash +pip install PyQt6Ads +``` + +Sample code is available [here](https://github.com/pyapp-kit/PyQt6Ads/tree/main/examples). + +The PyQt6 bindings were contributed by: + +- [tlambert03](https://github.com/tlambert03) + +Please file PyQt6Ads-specific issues at [pyapp-kit/PyQt6Ads](https://github.com/pyapp-kit/PyQt6Ads). + ### PyQt5 A package is available via [conda-forge](https://github.com/conda-forge/pyqtads-feedstock). @@ -281,24 +373,22 @@ The library supports **Qt5** and **Qt6**. ### Windows -Windows 10 [![Build status](https://ci.appveyor.com/api/projects/status/qcfb3cy932jw9mpy/branch/master?svg=true)](https://ci.appveyor.com/project/githubuser0xFFFF/qt-advanced-docking-system/branch/master) +Windows 10 / 11 [![Build status](https://ci.appveyor.com/api/projects/status/qcfb3cy932jw9mpy/branch/master?svg=true)](https://ci.appveyor.com/project/githubuser0xFFFF/qt-advanced-docking-system/branch/master) +[![windows-builds](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/actions/workflows/windows-cmake.yml/badge.svg?branch=master)](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/actions/workflows/windows-cmake.yml) The library was developed on and for Windows. It is used in a commercial Windows application and is therefore constantly tested. ### macOS -macOS [![Build Status](https://travis-ci.org/githubuser0xFFFF/Qt-Advanced-Docking-System.svg?branch=master)](https://travis-ci.org/githubuser0xFFFF/Qt-Advanced-Docking-System) - The application can be compiled for macOS. A user reported, that the library works on macOS. If have not tested it. ![Advanced Docking on macOS](doc/macos.png) ### Linux -[![Build Status](https://travis-ci.org/githubuser0xFFFF/Qt-Advanced-Docking-System.svg?branch=master)](https://travis-ci.org/githubuser0xFFFF/Qt-Advanced-Docking-System) [![Build status](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/workflows/linux-builds/badge.svg)](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/actions?query=workflow%3Alinux-builds) -Unfortunately, there is no such thing as a Linux operating system. Linux is a heterogeneous environment with a variety of different distributions. So it is not possible to support "Linux" like this is possible for Windows. It is only possible to support and test a small subset of Linux distributions. The library can be compiled for and has been developed and tested with the some Linux distributions. Depending on the used window manager or compositor, dock widgets +Unfortunately, there is no such thing as a Linux operating system. Linux is a heterogeneous environment with a variety of different distributions. So it is not possible to support "Linux" like it is possible for Windows. It is only possible to support and test a small subset of Linux distributions. The library can be compiled for and has been developed and tested with some Linux distributions. Depending on the used window manager or compositor, dock widgets with native title bars are supported or not. If native title bars are not supported, the library switches to `QWidget` based title bars. @@ -320,7 +410,9 @@ Screenshot Ubuntu: ## Build The Linux build requires private header files. Make sure that they are installed. -The library uses SVG icons, so ensure that Qt SVG support is installed. +The library uses SVG icons, so ensure that Qt SVG support is installed. The demo +application creates a `QQuickWidget` for testing, so ensure that the required +libraries are installed. ### Qt5 on Ubuntu 18.04 or 20.04 @@ -331,13 +423,13 @@ sudo apt install qt5-default qtbase5-private-dev ### Qt5 on Ubuntu 22.04 ```bash -sudo apt install qtbase5-dev qtbase5-private-dev qtbase5-dev-tools libqt5svg5 +sudo apt install qtbase5-dev qtbase5-private-dev qtbase5-dev-tools libqt5svg5 libqt5qml5 qtdeclarative5-dev ``` ### Qt6 on Ubuntu 22.04 ```bash -sudo apt install qt6-base-dev qt6-base-private-dev qt6-tools-dev libqt6svg6 +sudo apt install qt6-default qt6-base-dev qt6-base-private-dev qt6-tools-dev libqt6svg6 qt6-qtdeclarative ``` Open the `ads.pro` file with QtCreator and start the build, that's it. @@ -390,7 +482,7 @@ MainWindow::MainWindow(QWidget *parent) : // Create the dock manager after the ui is setup. Because the // parent parameter is a QMainWindow the dock manager registers // itself as the central widget as such the ui must be set up first. - m_DockManager = new ads::CDockManager(this); + DockManager = new ads::CDockManager(this); // Create example content label - this can be any application specific // widget @@ -401,7 +493,7 @@ MainWindow::MainWindow(QWidget *parent) : // Create a dock widget with the title Label 1 and set the created label // as the dock widget content - ads::CDockWidget* DockWidget = new ads::CDockWidget("Label 1"); + ads::CDockWidget* DockWidget = DockManager->createDockWidget("Label 1"); DockWidget->setWidget(l); // Add the toggleViewAction of the dock widget to the menu to give @@ -409,7 +501,7 @@ MainWindow::MainWindow(QWidget *parent) : ui->menuView->addAction(DockWidget->toggleViewAction()); // Add the dock widget to the top dock widget area - m_DockManager->addDockWidget(ads::TopDockWidgetArea, DockWidget); + DockManager->addDockWidget(ads::TopDockWidgetArea, DockWidget); } MainWindow::~MainWindow() @@ -510,7 +602,7 @@ highlights are: - Simple Drag & Drop user interface. - Load data from file. - Connect to live streaming of data. -- Save the visualization layout and configurations to re-use them later. +- Save the visualization layout and configurations to reuse them later. - Fast OpenGL visualization. - Can handle thousands of timeseries and millions of data points. - Transform your data using a simple editor: derivative, moving average, integral, etc… @@ -522,7 +614,7 @@ highlights are: ### [Notepad Next](https://github.com/dail8859/NotepadNext) -Notepad Next is a cross-platform reimplementation of Notepad++ that uses the +Notepad Next is a cross-platform reimplementation of Notepad++ that uses the Advanced Docking System to arrange the open source files on the screen. [read more...](https://github.com/dail8859/NotepadNext) @@ -585,7 +677,7 @@ The Automotive Data and Time-Triggered Framework was designed as a Rapid Prototy - Recording of vehicle data for visualisation - Simulation of complex scenarios in SIL/HIL test environments -The software features time-based processing of multiple data streams and graphical editing of dynamic filter graphs. It also includes an SDK for custom plug-ins and reusable components, as well as components for data visualization in both 2D and 3D. This is was the +The software features time-based processing of multiple data streams and graphical editing of dynamic filter graphs. It also includes an SDK for custom plug-ins and reusable components, as well as components for data visualization in both 2D and 3D. This is was the [manual](https://support.digitalwerk.net/adtf/v3/adtf_html/page_adtf_xsystem_plugin.html) says about the switch to Qt Advanced Docking: @@ -595,15 +687,17 @@ says about the switch to Qt Advanced Docking: ![ADTF](doc/showcase_adtf.png) -### [DREAM.3D NX](https://github.com/BlueQuartzSoftware/DREAM3D) +### [DREAM3D-NX](https://www.dream3d.io) -DREAM.3D *(Digital Representation Environment for Analysis of Materials in 3D)* is an open source, cross-platform and modular, software suite that allows users to prepare, reconstruct, quantify, instantiate, and mesh, multidimensional, multimodal microstructural data, as well as many other applications. +DREAM3D-NX *(Digital Representation Environment for Analysis of Materials in 3D)* is a cross-platform and modular, software suite that allows users to prepare, reconstruct, quantify, instantiate, and mesh, multidimensional, multimodal microstructural data, as well as many other applications. -[BlueQuartz Software](http://www.bluequartz.net/) is currently completely rewriting the DREAM.3D application. For the upcoming version **[DREAM3D NX](http://www.dream3d.io/)** they improved the UI by using the Advanced Docking System. An [early version](http://www.dream3d.io/) of **DREAM3D NX** with ADS is already available to any user who would like to take the brand new version out for a spin. +[BlueQuartz Software](http://www.bluequartz.net/) has completely rewritten the old DREAM.3D version 6.5 application +taking advantage of the Advanced Docking System to present a highly customizable user interface +for DREAM3D-NX Version 7. ![DREAM.3D NX](doc/showcase_dream3d_nx.png) -[read more...](http://dream3d.bluequartz.net/) +[read more...](http://www.dream3d.io/) ### [LabPlot](https://labplot.kde.org/) @@ -615,6 +709,36 @@ The LabPlot project recently switched to the Qt Advanced Docking System for thei [read more...](https://labplot.kde.org/) +### [Scrutiny Debugger](https://scrutinydebugger.com/) + +Scrutiny is an [open source](https://github.com/scrutinydebugger/) non-intrusive, real-time debugger for embedded systems, built for fast introspection, memory access, and signal visualization—without stopping your application. It uses the Qt Advanced Docking System (ADS) to provide fully customizable dashboards for monitoring and control. + +Some of the highlights are: + +- **Debug without interfering** — Inspect C++ memory live without disrupting execution flow. +- **HIL testing made easy** — Write to variables through the GUI or SDK to simulate conditions. +- **Configure during production** — Access and tweak variables anytime via GUI or Python. +- **Catch fast events** — Embedded graphs sample at task-level frequency, even with multithreading. +- **Build your dashboard** — Use ADS to create and save custom layouts for your debug sessions. +- **No JTAG needed** — Communicates over serial, CAN, or network interfaces. +- **Compact C++11 library** — Lightweight and easy to integrate into your firmware. +- **Live graphs and runtime values** — Visualize variables, RPVs, and aliases in real time. + +ADS powers Scrutiny’s flexible interface, letting users organize views and data panels to suit their workflow — ideal for real-time monitoring, testing, and debugging. + +[read more...](https://scrutinydebugger.com/) + +[![Scrutiny Debugger UI](doc/showcase_scrutiny-dark.png)](https://www.youtube.com/watch?v=Dd3osxW-Clo) + + +### [PySoWorks](https://pypi.org/project/pysoworks/) + +PySoWorks is an application for controlling the piezo amplifiers, such as the [NV200/D](https://www.piezosystem.com/product/nv-200-d-compact-amplifier/), from [piezosystem jena](https://www.piezosystem.com/) GmbH. + +![LabPlot](doc/showcase_pysoworks.png) + +[read more...](https://piezosystemjena.github.io/PySoWorks/) + ## Alternative Docking System Implementations If this Qt Advanced Docking System does not fit to your needs you may consider some of the alternative docking system solutions for Qt. diff --git a/demo/CMakeLists.txt b/demo/CMakeLists.txt index 6e30a9592..02706d4c3 100644 --- a/demo/CMakeLists.txt +++ b/demo/CMakeLists.txt @@ -1,8 +1,8 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.16) project(ads_demo 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) +find_package(Qt${QT_VERSION_MAJOR} 5.5 COMPONENTS Core Gui Widgets Quick QuickWidgets REQUIRED) if(WIN32 AND QT_VERSION_MAJOR LESS 6) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS AxContainer REQUIRED) endif() @@ -21,11 +21,13 @@ add_executable(AdvancedDockingSystemDemo WIN32 ${ads_demo_SRCS}) target_include_directories(AdvancedDockingSystemDemo PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../src") target_link_libraries(AdvancedDockingSystemDemo PUBLIC Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui - Qt${QT_VERSION_MAJOR}::Widgets) + Qt${QT_VERSION_MAJOR}::Widgets + Qt${QT_VERSION_MAJOR}::Quick + Qt${QT_VERSION_MAJOR}::QuickWidgets) if(WIN32 AND QT_VERSION_MAJOR LESS 6) target_link_libraries(AdvancedDockingSystemDemo PUBLIC Qt${QT_VERSION_MAJOR}::AxContainer) endif() -target_link_libraries(AdvancedDockingSystemDemo PRIVATE qt${QT_VERSION_MAJOR}advanceddocking) +target_link_libraries(AdvancedDockingSystemDemo PRIVATE qtadvanceddocking-qt${QT_VERSION_MAJOR}) set_target_properties(AdvancedDockingSystemDemo PROPERTIES AUTOMOC ON AUTORCC ON diff --git a/demo/MainWindow.cpp b/demo/MainWindow.cpp index fb0a51bf3..e5b910c14 100644 --- a/demo/MainWindow.cpp +++ b/demo/MainWindow.cpp @@ -62,6 +62,7 @@ #include #include #include +#include #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) @@ -74,18 +75,17 @@ #endif #endif -#include "DockManager.h" -#include "DockWidget.h" -#include "DockAreaWidget.h" -#include "DockAreaTitleBar.h" #include "DockAreaTabBar.h" -#include "FloatingDockContainer.h" +#include "DockAreaTitleBar.h" +#include "DockAreaWidget.h" #include "DockComponentsFactory.h" -#include "StatusDialog.h" +#include "DockManager.h" #include "DockSplitter.h" +#include "DockWidget.h" +#include "FloatingDockContainer.h" #include "ImageViewer.h" - - +#include "MyDockAreaTitleBar.h" +#include "StatusDialog.h" /** * Returns a random number from 0 to highest - 1 @@ -146,7 +146,7 @@ class CCustomComponentsFactory : public ads::CDockComponentsFactory using Super = ads::CDockComponentsFactory; ads::CDockAreaTitleBar* createDockAreaTitleBar(ads::CDockAreaWidget* DockArea) const override { - auto TitleBar = new ads::CDockAreaTitleBar(DockArea); + auto TitleBar = new MyDockAreaTitleBar(DockArea); auto CustomButton = new QToolButton(DockArea); CustomButton->setToolTip(QObject::tr("Help")); CustomButton->setIcon(svgIcon(":/adsdemo/images/help_outline.svg")); @@ -236,7 +236,7 @@ struct MainWindowPrivate m->setRootPath(QDir::currentPath()); w->setModel(m); w->setRootIndex(m->index(QDir::currentPath())); - ads::CDockWidget* DockWidget = new ads::CDockWidget(QString("Filesystem %1") + ads::CDockWidget* DockWidget = DockManager->createDockWidget(QString("Filesystem %1") .arg(FileSystemCount++)); DockWidget->setWidget(w); DockWidget->setIcon(svgIcon(":/adsdemo/images/folder_open.svg")); @@ -257,7 +257,7 @@ struct MainWindowPrivate { static int CalendarCount = 0; QCalendarWidget* w = new QCalendarWidget(); - ads::CDockWidget* DockWidget = new ads::CDockWidget(QString("Calendar %1").arg(CalendarCount++)); + ads::CDockWidget* DockWidget = DockManager->createDockWidget(QString("Calendar %1").arg(CalendarCount++)); // The following lines are for testing the setWidget() and takeWidget() // functionality DockWidget->setWidget(w); @@ -270,6 +270,10 @@ struct MainWindowPrivate auto ToolBar = DockWidget->createDefaultToolBar(); ToolBar->addAction(ui.actionSaveState); ToolBar->addAction(ui.actionRestoreState); + // For testing all calendar dock widgets have a the tool button style + // Qt::ToolButtonTextUnderIcon + DockWidget->setToolBarStyleSource(ads::CDockWidget::ToolBarStyleFromDockWidget); + DockWidget->setToolBarStyle(Qt::ToolButtonTextUnderIcon, ads::CDockWidget::StateFloating); return DockWidget; } @@ -298,7 +302,7 @@ struct MainWindowPrivate .arg(LabelCount) .arg(QTime::currentTime().toString("hh:mm:ss:zzz"))); - ads::CDockWidget* DockWidget = new ads::CDockWidget(QString("Label %1").arg(LabelCount++)); + ads::CDockWidget* DockWidget = DockManager->createDockWidget(QString("Label %1").arg(LabelCount++)); DockWidget->setWidget(l); DockWidget->setIcon(svgIcon(":/adsdemo/images/font_download.svg")); ui.menuView->addAction(DockWidget->toggleViewAction()); @@ -316,7 +320,7 @@ struct MainWindowPrivate w->setPlaceholderText("This is an editor. If you close the editor, it will be " "deleted. Enter your text here."); w->setStyleSheet("border: none"); - ads::CDockWidget* DockWidget = new ads::CDockWidget(QString("Editor %1").arg(EditorCount++)); + ads::CDockWidget* DockWidget = DockManager->createDockWidget(QString("Editor %1").arg(EditorCount++)); DockWidget->setWidget(w); DockWidget->setIcon(svgIcon(":/adsdemo/images/edit.svg")); DockWidget->setFeature(ads::CDockWidget::CustomCloseHandling, true); @@ -359,7 +363,7 @@ struct MainWindowPrivate auto Result = w->loadFile(FileName); qDebug() << "loadFile result: " << Result; - ads::CDockWidget* DockWidget = new ads::CDockWidget(QString("Image Viewer %1").arg(ImageViewerCount++)); + ads::CDockWidget* DockWidget = DockManager->createDockWidget(QString("Image Viewer %1").arg(ImageViewerCount++)); DockWidget->setIcon(svgIcon(":/adsdemo/images/photo.svg")); DockWidget->setWidget(w,ads:: CDockWidget::ForceNoScrollArea); auto ToolBar = DockWidget->createDefaultToolBar(); @@ -374,7 +378,7 @@ struct MainWindowPrivate { static int TableCount = 0; auto w = new CMinSizeTableWidget(); - ads::CDockWidget* DockWidget = new ads::CDockWidget(QString("Table %1").arg(TableCount++)); + ads::CDockWidget* DockWidget = DockManager->createDockWidget(QString("Table %1").arg(TableCount++)); static int colCount = 5; static int rowCount = 30; w->setColumnCount(colCount); @@ -392,7 +396,7 @@ struct MainWindowPrivate DockWidget->setMinimumSizeHintMode(ads::CDockWidget::MinimumSizeHintFromContent); auto ToolBar = DockWidget->createDefaultToolBar(); auto Action = ToolBar->addAction(svgIcon(":/adsdemo/images/fullscreen.svg"), "Toggle Fullscreen"); - QObject::connect(Action, &QAction::triggered, [=]() + QObject::connect(Action, &QAction::triggered, [DockWidget]() { if (DockWidget->isFullScreen()) { @@ -407,6 +411,17 @@ struct MainWindowPrivate return DockWidget; } + /** + * Create QQuickWidget for test for OpenGL and QQuick + */ + ads::CDockWidget *createQQuickWidget() + { + QQuickWidget *widget = new QQuickWidget(); + ads::CDockWidget *dockWidget = DockManager->createDockWidget("Quick"); + dockWidget->setWidget(widget); + return dockWidget; + } + #ifdef Q_OS_WIN #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) @@ -417,14 +432,13 @@ struct MainWindowPrivate { static int ActiveXCount = 0; QAxWidget* w = new QAxWidget("{6bf52a52-394a-11d3-b153-00c04f79faa6}", parent); - ads::CDockWidget* DockWidget = new ads::CDockWidget(QString("Active X %1").arg(ActiveXCount++)); + ads::CDockWidget* DockWidget = DockManager->createDockWidget(QString("Active X %1").arg(ActiveXCount++)); DockWidget->setWidget(w); ui.menuView->addAction(DockWidget->toggleViewAction()); return DockWidget; } #endif #endif - }; //============================================================================ @@ -437,8 +451,8 @@ void MainWindowPrivate::createContent() // For this Special Dock Area we want to avoid dropping on the center of it (i.e. we don't want this widget to be ever tabbified): { - SpecialDockArea->setAllowedAreas(ads::OuterDockAreas); - //SpecialDockArea->setAllowedAreas({ads::LeftDockWidgetArea, ads::RightDockWidgetArea}); // just for testing + //SpecialDockArea->setAllowedAreas(ads::OuterDockAreas); + SpecialDockArea->setAllowedAreas({ads::LeftDockWidgetArea, ads::RightDockWidgetArea, ads::TopDockWidgetArea}); // just for testing } DockWidget = createLongTextLabelDockWidget(); @@ -456,12 +470,12 @@ void MainWindowPrivate::createContent() appendFeaturStringToWindowTitle(FileSystemWidget); // Test custom factory - we inject a help button into the title bar - ads::CDockComponentsFactory::setFactory(new CCustomComponentsFactory()); + DockManager->setComponentsFactory(new CCustomComponentsFactory()); auto TopDockArea = DockManager->addDockWidget(ads::TopDockWidgetArea, FileSystemWidget); // Uncomment the next line if you would like to test the // HideSingleWidgetTitleBar functionality // TopDockArea->setDockAreaFlag(ads::CDockAreaWidget::HideSingleWidgetTitleBar, true); - ads::CDockComponentsFactory::resetDefaultFactory(); + DockManager->setComponentsFactory(ads::CDockComponentsFactory::factory()); // We create a calendar widget and clear all flags to prevent the dock area // from closing @@ -496,7 +510,7 @@ void MainWindowPrivate::createContent() auto TitleBar = DockArea->titleBar(); int Index = TitleBar->indexOf(TitleBar->tabBar()); TitleBar->insertWidget(Index + 1, CustomButton); - QObject::connect(CustomButton, &QToolButton::clicked, [=]() + QObject::connect(CustomButton, &QToolButton::clicked, [DockArea, this]() { auto DockWidget = createEditorWidget(); DockWidget->setFeature(ads::CDockWidget::DockWidgetDeleteOnClose, true); @@ -506,7 +520,9 @@ void MainWindowPrivate::createContent() // Test dock area docking auto RighDockArea = DockManager->addDockWidget(ads::RightDockWidgetArea, createLongTextLabelDockWidget(), TopDockArea); - DockManager->addDockWidget(ads::TopDockWidgetArea, createLongTextLabelDockWidget(), RighDockArea); + DockWidget = createLongTextLabelDockWidget(); + DockWidget->setFeature(ads::CDockWidget::DockWidgetPinnable, false); + DockManager->addDockWidget(ads::TopDockWidgetArea, DockWidget, RighDockArea); auto BottomDockArea = DockManager->addDockWidget(ads::BottomDockWidgetArea, createLongTextLabelDockWidget(), RighDockArea); DockManager->addDockWidget(ads::CenterDockWidgetArea, createLongTextLabelDockWidget(), RighDockArea); auto LabelDockWidget = createLongTextLabelDockWidget(); @@ -514,6 +530,7 @@ void MainWindowPrivate::createContent() // Tests CustomCloseHandling without DeleteOnClose LabelDockWidget->setFeature(ads::CDockWidget::CustomCloseHandling, true); + LabelDockWidget->setWindowTitle(LabelDockWidget->windowTitle() + " [Custom Close]"); QObject::connect(LabelDockWidget, &ads::CDockWidget::closeRequested, [LabelDockWidget, this]() { int Result = QMessageBox::question(_this, "Custom Close Request", @@ -557,6 +574,11 @@ void MainWindowPrivate::createContent() // Create image viewer DockWidget = createImageViewer(); DockManager->addDockWidget(ads::LeftDockWidgetArea, DockWidget); + + // Create quick widget + DockWidget = createQQuickWidget(); + DockWidget->setFeature(ads::CDockWidget::DockWidgetClosable, true); + DockManager->addDockWidget(ads::LeftDockWidgetArea, DockWidget); } @@ -569,19 +591,29 @@ void MainWindowPrivate::createActions() ui.toolBar->addAction(ui.actionRestoreState); ui.actionRestoreState->setIcon(svgIcon(":/adsdemo/images/restore.svg")); - SavePerspectiveAction = new QAction("Create Perspective", _this); - SavePerspectiveAction->setIcon(svgIcon(":/adsdemo/images/picture_in_picture.svg")); - _this->connect(SavePerspectiveAction, SIGNAL(triggered()), SLOT(savePerspective())); + ui.toolBar->addSeparator(); + + QAction* a = ui.toolBar->addAction("Lock Workspace"); + a->setIcon(svgIcon(":/adsdemo/images/lock_outline.svg")); + a->setCheckable(true); + a->setChecked(false); + QObject::connect(a, &QAction::triggered, _this, &CMainWindow::lockWorkspace); + PerspectiveListAction = new QWidgetAction(_this); PerspectiveComboBox = new QComboBox(_this); PerspectiveComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents); - PerspectiveComboBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + PerspectiveComboBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); PerspectiveListAction->setDefaultWidget(PerspectiveComboBox); - ui.toolBar->addSeparator(); ui.toolBar->addAction(PerspectiveListAction); + + a = SavePerspectiveAction = ui.toolBar->addAction("Create Perspective"); + a->setIcon(svgIcon(":/adsdemo/images/picture_in_picture.svg")); + QObject::connect(a, &QAction::triggered, _this, &CMainWindow::savePerspective); ui.toolBar->addAction(SavePerspectiveAction); - QAction* a = ui.toolBar->addAction("Create Floating Editor"); + ui.toolBar->addSeparator(); + + a = ui.toolBar->addAction("Create Floating Editor"); a->setProperty("Floating", true); a->setToolTip("Creates floating dynamic dockable editor windows that are deleted on close"); a->setIcon(svgIcon(":/adsdemo/images/note_add.svg")); @@ -603,6 +635,7 @@ void MainWindowPrivate::createActions() _this->connect(a, SIGNAL(triggered()), SLOT(createEditor())); ui.menuTests->addAction(a); + ui.toolBar->addSeparator(); a = ui.toolBar->addAction("Create Floating Table"); a->setToolTip("Creates floating dynamic dockable table with millions of entries"); a->setIcon(svgIcon(":/adsdemo/images/grid_on.svg")); @@ -736,6 +769,9 @@ CMainWindow::CMainWindow(QWidget *parent) : // uncomment if you would like to enable dock widget auto hiding CDockManager::setAutoHideConfigFlags({CDockManager::DefaultAutoHideConfig}); + // uncomment if you would like to disable closing auto hide widget with mouse click outside of auto hide container + //CDockManager::setAutoHideConfigFlag(CDockManager::AutoHideCloseOnOutsideMouseClick, false); + // uncomment if you would like to enable an equal distribution of the // available size of a splitter to all contained dock widgets // CDockManager::setConfigFlag(CDockManager::EqualSplitOnInsertion, true); @@ -745,6 +781,7 @@ CMainWindow::CMainWindow(QWidget *parent) : // Now create the dock manager and its content d->DockManager = new CDockManager(this); + d->DockManager->setDockWidgetToolBarStyle(Qt::ToolButtonIconOnly, ads::CDockWidget::StateFloating); #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) connect(d->PerspectiveComboBox, SIGNAL(activated(QString)), @@ -1006,3 +1043,17 @@ void CMainWindow::createImageViewer() } } + +//============================================================================ +void CMainWindow::lockWorkspace(bool Value) +{ + if (Value) + { + d->DockManager->lockDockWidgetFeaturesGlobally(); + } + else + { + d->DockManager->lockDockWidgetFeaturesGlobally(ads::CDockWidget::NoDockWidgetFeatures); + } +} + diff --git a/demo/MainWindow.h b/demo/MainWindow.h index 4afee1483..dd1236085 100644 --- a/demo/MainWindow.h +++ b/demo/MainWindow.h @@ -68,6 +68,7 @@ private slots: void toggleDockWidgetWindowTitle(); void applyVsStyle(); void createImageViewer(); + void lockWorkspace(bool Value); }; #endif // MAINWINDOW_H diff --git a/demo/MyDockAreaTitleBar.h b/demo/MyDockAreaTitleBar.h new file mode 100644 index 000000000..e5ed06d17 --- /dev/null +++ b/demo/MyDockAreaTitleBar.h @@ -0,0 +1,58 @@ +#ifndef QTADS_MYDOCKAREATITLEBAR_H +#define QTADS_MYDOCKAREATITLEBAR_H +/******************************************************************************* +** 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 . +******************************************************************************/ + + +//============================================================================ +// INCLUDES +//============================================================================ +#include + + +/** + * Custom DockAreaTitleBar that adds a custom context menu + */ +class MyDockAreaTitleBar : public ads::CDockAreaTitleBar +{ +public: + explicit MyDockAreaTitleBar(ads::CDockAreaWidget *parent) : + CDockAreaTitleBar(parent) + { + } + + QMenu* buildContextMenu(QMenu*) override + { + auto menu = ads::CDockAreaTitleBar::buildContextMenu(nullptr); + menu->addSeparator(); + auto action = menu->addAction(tr("Format HardDrive")); + + connect(action, &QAction::triggered, this, [this]() + { + QMessageBox msgBox; + msgBox.setText("No, just kidding"); + msgBox.setStandardButtons(QMessageBox::Abort); + msgBox.setDefaultButton(QMessageBox::Abort); + msgBox.exec(); + }); + + return menu; + } +}; + +#endif // QTADS_MYDOCKAREATITLEBAR_H diff --git a/demo/demo.pro b/demo/demo.pro index 3df699bc2..913b5be12 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 quick quickwidgets include(../ads.pri) diff --git a/demo/demo.qrc b/demo/demo.qrc index 6fadba218..8b3529374 100644 --- a/demo/demo.qrc +++ b/demo/demo.qrc @@ -36,5 +36,8 @@ images/panorama.svg images/ads_icon2.svg images/font_download.svg + images/lock_outline.svg + images/lock.svg + images/lock_open.svg diff --git a/demo/images/lock.svg b/demo/images/lock.svg new file mode 100644 index 000000000..ec11129c0 --- /dev/null +++ b/demo/images/lock.svg @@ -0,0 +1,6 @@ + + lock 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/lock_open.svg b/demo/images/lock_open.svg new file mode 100644 index 000000000..a30effc04 --- /dev/null +++ b/demo/images/lock_open.svg @@ -0,0 +1,6 @@ + + lock_open 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/lock_outline.svg b/demo/images/lock_outline.svg new file mode 100644 index 000000000..613e430dc --- /dev/null +++ b/demo/images/lock_outline.svg @@ -0,0 +1,6 @@ + + lock_outline 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 750c983f0..5ddbbc0b6 100644 --- a/demo/main.cpp +++ b/demo/main.cpp @@ -41,6 +41,7 @@ int main(int argc, char *argv[]) QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); #endif #endif + QApplication::setAttribute(Qt::AA_ShareOpenGLContexts); std::shared_ptr b; QApplication a(argc, argv); a.setApplicationName("Advanced Docking System Demo"); diff --git a/demo/status_dialog.py b/demo/status_dialog.py index 8c42f1588..cf58a385d 100644 --- a/demo/status_dialog.py +++ b/demo/status_dialog.py @@ -2,7 +2,7 @@ import sys from PyQt5 import uic -from PyQtAds import QtAds +import PyQtAds as QtAds UI_FILE = os.path.join(os.path.dirname(__file__), 'StatusDialog.ui') StatusDialogUI, StatusDialogBase = uic.loadUiType(UI_FILE) diff --git a/doc/AutoHide_Change_Sidebar.gif b/doc/AutoHide_Change_Sidebar.gif new file mode 100644 index 000000000..07312fb57 Binary files /dev/null and b/doc/AutoHide_Change_Sidebar.gif differ diff --git a/doc/AutoHide_Context_Menu.png b/doc/AutoHide_Context_Menu.png new file mode 100644 index 000000000..614e7062d Binary files /dev/null and b/doc/AutoHide_Context_Menu.png differ diff --git a/doc/AutoHide_Drag_DockArea.gif b/doc/AutoHide_Drag_DockArea.gif new file mode 100644 index 000000000..50e18723c Binary files /dev/null and b/doc/AutoHide_Drag_DockArea.gif differ diff --git a/doc/AutoHide_Drag_to_Float_or_Dock.gif b/doc/AutoHide_Drag_to_Float_or_Dock.gif new file mode 100644 index 000000000..fa7f0ba36 Binary files /dev/null and b/doc/AutoHide_Drag_to_Float_or_Dock.gif differ diff --git a/doc/AutoHide_Drag_to_Sidebar.gif b/doc/AutoHide_Drag_to_Sidebar.gif new file mode 100644 index 000000000..72e998f61 Binary files /dev/null and b/doc/AutoHide_Drag_to_Sidebar.gif differ diff --git a/doc/AutoHide_Sort_Tabs.gif b/doc/AutoHide_Sort_Tabs.gif new file mode 100644 index 000000000..6bac89cd5 Binary files /dev/null and b/doc/AutoHide_Sort_Tabs.gif differ diff --git a/doc/AutoHide_Tab_Insert_Order.gif b/doc/AutoHide_Tab_Insert_Order.gif new file mode 100644 index 000000000..391d80a3b Binary files /dev/null and b/doc/AutoHide_Tab_Insert_Order.gif differ diff --git a/doc/DockArea_Tab_Insertion_Order.gif b/doc/DockArea_Tab_Insertion_Order.gif new file mode 100644 index 000000000..2823d7666 Binary files /dev/null and b/doc/DockArea_Tab_Insertion_Order.gif differ diff --git a/doc/ads_qt_marketplace_description.md b/doc/ads_qt_marketplace_description.md index f273d7dab..1d8c987df 100644 --- a/doc/ads_qt_marketplace_description.md +++ b/doc/ads_qt_marketplace_description.md @@ -12,10 +12,10 @@ styles as much as possible. ## Features -### Docking everywhere - no central widget +### Docking everywhere - with or without a central widget -There is no central widget like in the Qt docking system. You can dock on every -border of the main window or you can dock into each dock area - so you are +The Advanced Docking System works with or without a central widget. +You can dock on every border of the main window or you can dock into each dock area - so you are free to dock almost everywhere. ![Dropping widgets](https://raw.githubusercontent.com/githubuser0xFFFF/Qt-Advanced-Docking-System/master/doc/preview-dragndrop.png) @@ -65,7 +65,7 @@ If this flag is cleared, the widget resizing is deferred until the mouse button ### Opaque and non-opaque undocking -By default, opaque undocking is active. That means, as soon as you drag a dock widget or a dock area with a number of dock widgets it will be undocked and moved into a floating widget and then the floating widget will be dragged around. That means undocking will take place immediatelly. You can compare this with opaque splitter resizing. If the flag `OpaqueUndocking` is cleared, then non-opaque undocking is active. In this mode, undocking is more like a standard drag and drop operation. That means, the dragged dock widget or dock area is not undocked immediatelly. Instead, a drag preview widget is created and dragged around to indicate the future position of the dock widget or dock area. The actual dock operation is only executed when the mouse button is released. That makes it possible, to cancel an active drag operation with the escape key. +By default, opaque undocking is active. That means, as soon as you drag a dock widget or a dock area with a number of dock widgets it will be undocked and moved into a floating widget and then the floating widget will be dragged around. That means undocking will take place immediately. You can compare this with opaque splitter resizing. If the flag `OpaqueUndocking` is cleared, then non-opaque undocking is active. In this mode, undocking is more like a standard drag and drop operation. That means, the dragged dock widget or dock area is not undocked immediately. Instead, a drag preview widget is created and dragged around to indicate the future position of the dock widget or dock area. The actual dock operation is only executed when the mouse button is released. That makes it possible, to cancel an active drag operation with the escape key. The drag preview widget can be configured by a number of global dock manager flags: diff --git a/doc/cfg_flag_AutoHideHasCloseButton.png b/doc/cfg_flag_AutoHideHasCloseButton.png new file mode 100644 index 000000000..4cd16a37e Binary files /dev/null and b/doc/cfg_flag_AutoHideHasCloseButton.png differ diff --git a/doc/cfg_flag_AutoHideHasMinimizeButton.png b/doc/cfg_flag_AutoHideHasMinimizeButton.png new file mode 100644 index 000000000..f99143af6 Binary files /dev/null and b/doc/cfg_flag_AutoHideHasMinimizeButton.png differ diff --git a/doc/cfg_flag_AutoHideOpenOnDragHover.gif b/doc/cfg_flag_AutoHideOpenOnDragHover.gif new file mode 100644 index 000000000..8b62e26ce Binary files /dev/null and b/doc/cfg_flag_AutoHideOpenOnDragHover.gif differ diff --git a/doc/cfg_flag_DisableTabTextEliding_false.png b/doc/cfg_flag_DisableTabTextEliding_false.png new file mode 100644 index 000000000..72acc4e3a Binary files /dev/null and b/doc/cfg_flag_DisableTabTextEliding_false.png differ diff --git a/doc/cfg_flag_DisableTabTextEliding_true.png b/doc/cfg_flag_DisableTabTextEliding_true.png new file mode 100644 index 000000000..256eba388 Binary files /dev/null and b/doc/cfg_flag_DisableTabTextEliding_true.png differ diff --git a/doc/cfg_flag_ShowTabTextOnlyForActiveTab_true.png b/doc/cfg_flag_ShowTabTextOnlyForActiveTab_true.png new file mode 100644 index 000000000..8678bc45b Binary files /dev/null and b/doc/cfg_flag_ShowTabTextOnlyForActiveTab_true.png differ diff --git a/doc/cfg_flag_TabsAtBottom.png b/doc/cfg_flag_TabsAtBottom.png new file mode 100644 index 000000000..724987a95 Binary files /dev/null and b/doc/cfg_flag_TabsAtBottom.png differ diff --git a/doc/showcase_dream3d_nx.png b/doc/showcase_dream3d_nx.png index b64434fd0..07af0ed16 100644 Binary files a/doc/showcase_dream3d_nx.png and b/doc/showcase_dream3d_nx.png differ diff --git a/doc/showcase_pysoworks.png b/doc/showcase_pysoworks.png new file mode 100644 index 000000000..9e06aa77c Binary files /dev/null and b/doc/showcase_pysoworks.png differ diff --git a/doc/showcase_scrutiny-dark.png b/doc/showcase_scrutiny-dark.png new file mode 100644 index 000000000..250d658c4 Binary files /dev/null and b/doc/showcase_scrutiny-dark.png differ diff --git a/doc/user-guide.md b/doc/user-guide.md index 176b321fe..e6634f977 100644 --- a/doc/user-guide.md +++ b/doc/user-guide.md @@ -1,60 +1,81 @@ # User Guide -- [Configuration Flags](#configuration-flags) - - [Setting Configuration Flags](#setting-configuration-flags) - - [`ActiveTabHasCloseButton`](#activetabhasclosebutton) - - [`DockAreaHasCloseButton`](#dockareahasclosebutton) - - [`DockAreaCloseButtonClosesTab`](#dockareaclosebuttonclosestab) - - [`OpaqueSplitterResize`](#opaquesplitterresize) - - [`XmlAutoFormattingEnabled`](#xmlautoformattingenabled) - - [`XmlCompressionEnabled`](#xmlcompressionenabled) - - [`TabCloseButtonIsToolButton`](#tabclosebuttonistoolbutton) - - [`AllTabsHaveCloseButton`](#alltabshaveclosebutton) - - [`RetainTabSizeWhenCloseButtonHidden`](#retaintabsizewhenclosebuttonhidden) - - [`DragPreviewIsDynamic`](#dragpreviewisdynamic) - - [`DragPreviewShowsContentPixmap`](#dragpreviewshowscontentpixmap) - - [`DragPreviewHasWindowFrame`](#dragpreviewhaswindowframe) - - [`AlwaysShowTabs`](#alwaysshowtabs) - - [`DockAreaHasUndockButton`](#dockareahasundockbutton) - - [`DockAreaHasTabsMenuButton`](#dockareahastabsmenubutton) - - [`DockAreaHideDisabledButtons`](#dockareahidedisabledbuttons) - - [`DockAreaDynamicTabsMenuButtonVisibility`](#dockareadynamictabsmenubuttonvisibility) - - [`FloatingContainerHasWidgetTitle`](#floatingcontainerhaswidgettitle) - - [`FloatingContainerHasWidgetIcon`](#floatingcontainerhaswidgeticon) - - [`HideSingleCentralWidgetTitleBar`](#hidesinglecentralwidgettitlebar) - - [`FocusHighlighting`](#focushighlighting) - - [`EqualSplitOnInsertion`](#equalsplitoninsertion) - - [`FloatingContainerForceNativeTitleBar` (Linux only)](#floatingcontainerforcenativetitlebar-linux-only) - - [`FloatingContainerForceQWidgetTitleBar` (Linux only)](#floatingcontainerforceqwidgettitlebar-linux-only) - - [`MiddleMouseButtonClosesTab`](#middlemousebuttonclosestab) -- [Auto-Hide Configuration Flags](#auto-hide-configuration-flags) +- [User Guide](#user-guide) + - [Configuration Flags](#configuration-flags) + - [Setting Configuration Flags](#setting-configuration-flags) + - [`ActiveTabHasCloseButton`](#activetabhasclosebutton) + - [`DockAreaHasCloseButton`](#dockareahasclosebutton) + - [`DockAreaCloseButtonClosesTab`](#dockareaclosebuttonclosestab) + - [`OpaqueSplitterResize`](#opaquesplitterresize) + - [`XmlAutoFormattingEnabled`](#xmlautoformattingenabled) + - [`XmlCompressionEnabled`](#xmlcompressionenabled) + - [`TabCloseButtonIsToolButton`](#tabclosebuttonistoolbutton) + - [`AllTabsHaveCloseButton`](#alltabshaveclosebutton) + - [`RetainTabSizeWhenCloseButtonHidden`](#retaintabsizewhenclosebuttonhidden) + - [`DragPreviewIsDynamic`](#dragpreviewisdynamic) + - [`DragPreviewShowsContentPixmap`](#dragpreviewshowscontentpixmap) + - [`DragPreviewHasWindowFrame`](#dragpreviewhaswindowframe) + - [`AlwaysShowTabs`](#alwaysshowtabs) + - [`DockAreaHasUndockButton`](#dockareahasundockbutton) + - [`DockAreaHasTabsMenuButton`](#dockareahastabsmenubutton) + - [`DockAreaHideDisabledButtons`](#dockareahidedisabledbuttons) + - [`DockAreaDynamicTabsMenuButtonVisibility`](#dockareadynamictabsmenubuttonvisibility) + - [`FloatingContainerHasWidgetTitle`](#floatingcontainerhaswidgettitle) + - [`FloatingContainerHasWidgetIcon`](#floatingcontainerhaswidgeticon) + - [`HideSingleCentralWidgetTitleBar`](#hidesinglecentralwidgettitlebar) + - [`FocusHighlighting`](#focushighlighting) + - [`EqualSplitOnInsertion`](#equalsplitoninsertion) + - [`FloatingContainerForceNativeTitleBar` (Linux only)](#floatingcontainerforcenativetitlebar-linux-only) + - [`FloatingContainerForceQWidgetTitleBar` (Linux only)](#floatingcontainerforceqwidgettitlebar-linux-only) + - [`MiddleMouseButtonClosesTab`](#middlemousebuttonclosestab) + - [`DisableTabTextEliding`](#disabletabtexteliding) + - [`ShowTabTextOnlyForActiveTab`](#showtabtextonlyforactivetab) + - [`DoubleClickUndocksWidget`](#doubleclickundockswidget) + - [`TabsAtBottom`](#tabsatbottom) - [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) - - [`AutoHideCloseButtonCollapsesDock`](#autohideclosebuttoncollapsesdock) -- [DockWidget Feature Flags](#dockwidget-feature-flags) - - [`DockWidgetClosable`](#dockwidgetclosable) - - [`DockWidgetMovable`](#dockwidgetmovable) - - [`DockWidgetFloatable`](#dockwidgetfloatable) - - [`DockWidgetDeleteOnClose`](#dockwidgetdeleteonclose) - - [`CustomCloseHandling`](#customclosehandling) - - [`DockWidgetFocusable`](#dockwidgetfocusable) - - [`DockWidgetForceCloseWithArea`](#dockwidgetforceclosewitharea) - - [`NoTab`](#notab) - - [`DeleteContentOnClose`](#deletecontentonclose) -- [Central Widget](#central-widget) -- [Empty Dock Area](#empty-dock-area) -- [Custom Close Handling](#custom-close-handling) -- [Styling](#styling) - - [Disabling the Internal Style Sheet](#disabling-the-internal-style-sheet) + - [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) + - [Drag \& Drop to Auto-Hide](#drag--drop-to-auto-hide) + - [Auto-Hide Tab Insertion Order](#auto-hide-tab-insertion-order) + - [Auto-Hide Tab Sorting](#auto-hide-tab-sorting) + - [Auto-Hide Drag to Float / Dock](#auto-hide-drag-to-float--dock) + - [Auto-Hide Context Menu](#auto-hide-context-menu) + - [Adding Auto Hide Widgets](#adding-auto-hide-widgets) + - [Auto-Hide Configuration Flags](#auto-hide-configuration-flags) + - [Setting Auto-Hide Flags](#setting-auto-hide-flags) + - [`AutoHideFeatureEnabled`](#autohidefeatureenabled) + - [`DockAreaHasAutoHideButton`](#dockareahasautohidebutton) + - [`AutoHideButtonTogglesArea`](#autohidebuttontogglesarea) + - [`AutoHideButtonCheckable`](#autohidebuttoncheckable) + - [`AutoHideSideBarsIconOnly`](#autohidesidebarsicononly) + - [`AutoHideShowOnMouseOver`](#autohideshowonmouseover) + - [`AutoHideCloseButtonCollapsesDock`](#autohideclosebuttoncollapsesdock) + - [`AutoHideHasCloseButton`](#autohidehasclosebutton) + - [`AutoHideHasMinimizeButton`](#autohidehasminimizebutton) + - [`AutoHideOpenOnDragHover`](#autohideopenondraghover) + - [`AutoHideCloseOnOutsideMouseClick`](#autohidecloseonoutsidemouseclick) + - [DockWidget Feature Flags](#dockwidget-feature-flags) + - [`DockWidgetClosable`](#dockwidgetclosable) + - [`DockWidgetMovable`](#dockwidgetmovable) + - [`DockWidgetFloatable`](#dockwidgetfloatable) + - [`DockWidgetDeleteOnClose`](#dockwidgetdeleteonclose) + - [`CustomCloseHandling`](#customclosehandling) + - [`DockWidgetFocusable`](#dockwidgetfocusable) + - [`DockWidgetForceCloseWithArea`](#dockwidgetforceclosewitharea) + - [`NoTab`](#notab) + - [`DeleteContentOnClose`](#deletecontentonclose) + - [Central Widget](#central-widget) + - [Empty Dock Area](#empty-dock-area) + - [Custom Close Handling](#custom-close-handling) + - [Globally Lock Docking Features](#globally-lock-docking-features) + - [Dock Widget Size / Minimum Size Handling](#dock-widget-size--minimum-size-handling) + - [Styling](#styling) + - [Disabling the Internal Style Sheet](#disabling-the-internal-style-sheet) + - [Using ADS on Linux](#using-ads-on-linux) + - [Supported Distributions](#supported-distributions) + - [Requirements](#requirements) + - [Manjaro xfce 25.0.1 and Xubuntu 24.04.2 issues](#manjaro-xfce-2501-and-xubuntu-24042-issues) + - [OpenGl + ADS](#opengl--ads) ## Configuration Flags @@ -141,7 +162,7 @@ This ie enabled by default to minimize the size of the saved data. ### `TabCloseButtonIsToolButton` -If enabled the tab close buttons will be `QToolButtons` instead of `QPushButtons` - +If enabled the tab close buttons will be `QToolButtons` instead of `QPushButtons` - disabled by default. Normally the default configuration should be ok but if your application requires `QToolButtons` instead of `QPushButtons` for styling reasons or for any other reasons, then you can enable this flag. @@ -172,7 +193,7 @@ constant, that means, if enabled, the tabs need more space. ### `DragPreviewIsDynamic` -If non-opaque undocking is enabled, this flag defines the behavior of the drag +If non-opaque undocking is enabled, this flag defines the behavior of the drag preview window. If this flag is enabled, then it will give the user the impression, that the floating drag preview is dynamically adjusted to the drop area. In order to give the perfect impression, you should disable the flags @@ -188,7 +209,7 @@ CDockManager::setConfigFlag(CDockManager::DragPreviewHasWindowFrame, false); ### `DragPreviewShowsContentPixmap` -If non-opaque undocking is enabled, the created drag preview window shows a +If non-opaque undocking is enabled, the created drag preview window shows a copy of the content of the dock widget / dock are that is dragged, if this flag is enabled (default). @@ -201,7 +222,7 @@ like window without any content. ### `DragPreviewHasWindowFrame` -If non-opaque undocking is enabled, then this flag configures if the drag +If non-opaque undocking is enabled, then this flag configures if the drag preview is frameless (default) or looks like a real window. If it is enabled, then the drag preview is a transparent window with a system window frame. @@ -294,8 +315,9 @@ or to close it via the close button. ![HideSingleCentralWidgetTitleBar true](cfg_flag_HideSingleCentralWidgetTitleBar_true.png) -The Advanced Docking System is meant for applications without a static central -widget and normally does not know anything about a central static widget. +Unless a central widget explicitly has been set with setCentralWidget, the +Advanced Docking System is without a static central widget and it wouldn't know +about a central static widget. Therefore this flag is disabled by default and a central single dock widget still has a titlebar to drag it out of the main window. @@ -368,7 +390,7 @@ ads--CDockAreaWidget[focused="true"] ads--CDockAreaTitleBar If you have a content widget that does not support focussing for some reason (like `QVTKOpenGLStereoWidget` from the [VTK library](https://github.com/Kitware/VTK)), -then you can manually switch the focus by reacting on mouse events. The +then you can manually switch the focus by reacting on mouse events. The following code shows, how to install en event filter on the `QVTKOpenGLStereoWidget` to properly switch the focus on `QEvent::MouseButtonPress`: @@ -412,7 +434,7 @@ bool CMainWindow::eventFilter(QObject *watched, QEvent *event) ### `EqualSplitOnInsertion` This flag configures how the space is distributed if a new dock widget is -inserted into an existing dock area. The flag is disabled by default. If 3 +inserted into an existing dock area. The flag is disabled by default. If 3 dock widgets are inserted with the following code ```c++ @@ -423,7 +445,7 @@ then this is the result, if the flag is disabled: ![EqualSplitOnInsertion false](cfg_flag_EqualSplitOnInsertion_false.png) -If the flag is enabled, then the space is equally distributed to all widgets +If the flag is enabled, then the space is equally distributed to all widgets in a splitter: ![EqualSplitOnInsertion true](cfg_flag_EqualSplitOnInsertion_true.png) @@ -467,13 +489,40 @@ possible in various web browsers. ![MiddleMouseButtonClosesTab true](cfg_flag_MiddleMouseButtonClosesTab.gif) -## Auto-Hide Configuration Flags +### `DisableTabTextEliding` + +Set this flag to disable eliding of tab texts in dock area tabs: + +![DisableTabTextEliding true](cfg_flag_DisableTabTextEliding_true.png) + +The flag is disabled by default and the text in all tabs is elided to show as +many tabs as possible even if there is not much space: + +![DisableTabTextEliding false](cfg_flag_DisableTabTextEliding_false.png) + +### `ShowTabTextOnlyForActiveTab` + +Set this flag (default = false) to show label texts in dock area tabs only +for active tabs. Inactive tabs only show their icon: + +![MShowTabTextOnlyForActiveTab true](cfg_flag_ShowTabTextOnlyForActiveTab_true.png) + +### `DoubleClickUndocksWidget` + +If the flag is set (default), a double click on a tab undocks the dock widget. +If you would like to disable undocking, just clear this flag. + +### `TabsAtBottom` + +If the flag is set, tabs will be shown at the bottom instead of in the title bar. -### Auto Hide Dock Widgets +![TabsAtBottom true](cfg_flag_TabsAtBottom.png) + +## 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 +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. @@ -481,7 +530,7 @@ 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 +configuration flags) will be pinned to a certain border. The border is chosen 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. @@ -503,6 +552,59 @@ 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. +### Drag & Drop to Auto-Hide + +You can easily drag any dock widget or any floating widget to the +borders of a window to pin it as a auto-hide tab in one of the 4 sidebars. +If you drag a dock widget close the one of the four window borders, special +drop overlays will be shown to indicate the drop area for auto-hide widgets: + +![Auo-Hide drag to Sidebar](AutoHide_Drag_to_Sidebar.gif) + +Of course, this also works with dock areas: + +![Auo-Hide drag Dock Area](AutoHide_Drag_DockArea.gif) + +If you drag a dock widget or dock area into a sidebar, then you even have +control over where tabs are inserted. Simply drag your mouse over a specific +auto-hide tab, and your dragged dock widget will be inserted before this tab. +Drag to the sidebar area behind the last tab, and the dragged widget will be +appended as last tab. In the following screen capture, the **Image Viewer 1** will +be inserted before the **Table 0** Auto-Hide tab and the **Image Viewer 2** +is appended behind the last tab: + +![Auo-Hide tab insert order](AutoHide_Tab_Insert_Order.gif) + +### Auto-Hide Tab Insertion Order + +It is also possible to drag Auto-Hide tabs to a new auto-hide position. +That means, you can drag them to a different border or sidebar: + +![Auto-Hide change sidebar](AutoHide_Change_Sidebar.gif) + +### Auto-Hide Tab Sorting + +You can drag Auto-Hide tabs to a new position in the current sidebar +to sort them: + +![Auo-Hide sort tabs](AutoHide_Sort_Tabs.gif) + +### Auto-Hide Drag to Float / Dock + +But that is not all. You can also simply move Auto-Hide tabs to another +floating widget or dock them via drag and drop: + +![Auo-Hide drag to float or dock](AutoHide_Drag_to_Float_or_Dock.gif) + +### Auto-Hide Context Menu + +All Auto-Hide tabs now have a context menu, that provides all the functionality +that you know from Dock widget tabs. With the **Pin To...** item from the +context menu it is very easy to move an Auto-Hide tab to a different Auto-Hide +sidebar: + +![Auo-Hide context menu](AutoHide_Context_Menu.png) + ### Adding Auto Hide Widgets Adding an auto hide widget is similar to adding a dock widget, simply call @@ -517,6 +619,8 @@ DockManager->addAutoHideDockWidget(SideBarLeft, TableDockWidget); See `autohide` example or the demo application to learn how it works. +## Auto-Hide Configuration Flags + ### Setting Auto-Hide Flags The Advanced Docking System has a number of global configuration flags to @@ -551,7 +655,7 @@ 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 +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) @@ -595,7 +699,7 @@ works if this feature is enabled. Some users don't understand the distinction between closing an auto hide dock and collapsing an auto hide dock. This may lead to situations where they press the close button (losing the side tab widget) instead of simply clicking outside -the auto hide dock (collapsing the dock). +the auto hide dock (collapsing the dock). ![AutoHideCloseButtonCollapsesDock false](cfg_flag_AutoHideCloseButtonCollapsesDock_false.gif) @@ -605,6 +709,38 @@ closing it. ![AutoHideCloseButtonCollapsesDock true](cfg_flag_AutoHideCloseButtonCollapsesDock_true.gif) +If you enable the `AutoHideHasMinimizeButton` flag, you should disable this +flag our you will have two buttons with minimize functionality. + +### `AutoHideHasCloseButton` + +If this flag is set (default), then each auto hide widget has a close button: + +![AutoHideHasCloseButton](cfg_flag_AutoHideHasCloseButton.png) + +The functionality of the close button (close or minimize) is configured by the +`AutoHideCloseButtonCollapsesDock` flag. + +### `AutoHideHasMinimizeButton` + +If this flag is set (disabled by default), then each auto hide widget has a minimize button. + +![AutoHideHasMinimizeButton](cfg_flag_AutoHideHasMinimizeButton.png) + +### `AutoHideOpenOnDragHover` + +If this flag is set (disabled by default), then holding a dragging cursor hover an auto-hide collapsed dock's tab will open said dock: + +![AutoHideOpenOnDragHover](cfg_flag_AutoHideOpenOnDragHover.gif) + +Said dock must be set to accept drops to hide when cursor leaves its scope. See `AutoHideDragNDropExample` for more details. + +### `AutoHideCloseOnOutsideMouseClick` + +If this flag is set (default), the auto hide dock container will collapse if the +user clicks outside of the container. If not set, the auto hide container can be +closed only via click on auto hide sidebar tab. + ## DockWidget Feature Flags ### `DockWidgetClosable` @@ -726,6 +862,93 @@ Normally clicking the close button of a dock widget will just hide the widget an When an entire area is closed, the default behavior is to hide the dock widgets it contains regardless of the `DockWidgetDeleteOnClose` flag except if there is only one dock widget. In this special case, the `DockWidgetDeleteOnClose` flag is followed. This behavior can be changed by setting the `DockWidgetForceCloseWithArea` flag to all the dock widgets that needs to be closed with their area. +## Globally Lock Docking Features + +It is possible to globally lock features of all dock widgets to "freeze" the +current workspace layout. That means, you can now lock your workspace +to avoid accidentally dragging a docked view. When locking wasn't possible, +users had to manually dock it back to the desired place after each accidental +undock. + +You can use a combination of the following feature flags to define which features +shall get locked: + +- `CDockWidget::DockWidgetClosable` +- `CDockWidget::DockWidgetMovable` +- `CDockWidget::DockWidgetFloatable` +- `CDockWidget::DockWidgetPinable` + +To clear the locked features, you can use `CDockWidget::NoDockWidgetFeatures` +The following code shows how to lock and unlock all dock widget features +globally. + +```c++ +DockManager->lockDockWidgetFeaturesGlobally(); +DockManager->lockDockWidgetFeaturesGlobally(CDockWidget::NoDockWidgetFeatures); +``` + +## Dock Widget Size / Minimum Size Handling + +There are several `CDockWidget` mode enums to control how a `CDockWidget` is +resized and how the docking system handles the minimum size of a dockwidget. + +The first one is the `eInsertMode` enum: + +```c++ +enum eInsertMode +{ + AutoScrollArea, + ForceScrollArea, + ForceNoScrollArea +}; +``` + +The InsertMode defines how the widget is inserted into the dock widget, when you +call the `CDockWidget::setWidget` method: + +```c++ +DockWidget->setWidget(widget, CDockWidget::AutoScrollArea); +``` + +The content of a dock widget should be resizable do a very small size to +prevent the dock widget from blocking the resizing. To ensure, that a +dock widget can be resized very well, it is better to insert the content +widget into a scroll area or to provide a widget that is already a scroll +area or that contains a scroll area (such as an `QAbstractItemView`) +If the InsertMode is `AutoScrollArea`, the DockWidget tries to automatically +detect how to insert the given widget. If the widget is derived from +`QScrollArea` (i.e. an `QAbstractItemView`), then the widget is inserted +directly. If the given widget is not a scroll area, the widget will be +inserted into a scroll area. + +To force insertion into a scroll area, you can also provide the InsertMode +`ForceScrollArea`. In this case a scroll area will also be created for content +widgets that are derived from `QScrollArea` To prevent insertion into a scroll +area, you can provide the InsertMode `ForceNoScrollArea`. In this case, the +content widget is always inserted directly. + +A second enum, the `eMinimumSizeHintMode` defines, which value will be returned +from the `CDockWidget::minimumSizeHint()` function: + +```c++ +enum eMinimumSizeHintMode +{ + MinimumSizeHintFromDockWidget, + MinimumSizeHintFromContent, + MinimumSizeHintFromDockWidgetMinimumSize, + MinimumSizeHintFromContentMinimumSize, +}; +``` + +To ensure, that a dock widget does not block resizing, the dock widget +reimplements `minimumSizeHint()` function to return a very small minimum +size hint. If you would like to adhere the `minimumSizeHint()` from the +content widget, then set the `minimumSizeHintMode()` to +`MinimumSizeHintFromContent`. If you would like to use the `minimumSize()` +value of the content widget or the dock widget, then you can use the +`MinimumSizeHintFromDockWidgetMinimumSize` or +`MinimumSizeHintFromContentMinimumSize` modes. + ## Styling The Advanced Docking System supports styling via [Qt Style Sheets](https://doc.qt.io/qt-5/stylesheet.html). All components like splitters, tabs, buttons, titlebar and @@ -743,3 +966,51 @@ just call the function for settings the stylesheet with an empty string. DockManager->setStyleSheet(""); ``` +## Using ADS on Linux + +### Supported Distributions + +Unfortunately, there is no such thing as a Linux operating system. Linux is a heterogeneous environment with a variety of different distributions. So it is not possible to support "Linux" like it is possible for Windows. It is only possible to support and test a small subset of Linux distributions. The library can be compiled for and has been developed and tested with some Linux distributions. Depending on the used window manager or compositor, dock widgets +with native title bars are supported or not. If native title bars are not supported, +the library switches to `QWidget` based title bars. + +- **Kubuntu 18.04 and 19.10** - uses KWin - no native title bars +- **Ubuntu 18.04, 19.10 and 20.04** - native title bars are supported +- **Ubuntu 22.04** - uses Wayland -> no native title bars + +### Requirements + +There are some requirements for the Linux distribution that have to be met: + +- an X server that supports ARGB visuals and a compositing window manager. This is required to display the translucent dock overlays ([https://doc.qt.io/qt-5/qwidget.html#creating-translucent-windows](https://doc.qt.io/qt-5/qwidget.html#creating-translucent-windows)). If your Linux distribution does not support this, or if you disable this feature, you will very likely see issue [#95](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/issues/95). +- Wayland is not properly supported by Qt yet. If you use Wayland, then you should set the session type to x11: `XDG_SESSION_TYPE=x11 ./AdvancedDockingSystemDemo`. You will find more details about this in issue [#288](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/issues/288). + +Screenshot Kubuntu: +![Advanced Docking on Kubuntu Linux](linux_kubuntu_1804.png) + +Screenshot Ubuntu: +![Advanced Docking on Ubuntu Linux](linux_ubuntu_1910.png) + +### Manjaro xfce 25.0.1 and Xubuntu 24.04.2 issues + +There is a known focus stealing issue with the xfce4 compositor reported in issue [#734]. This issue +can be solved by enabling the setting **Activate focus stealing prevention**. `Settings > Window Manager Tweaks` has a tab called `Focus`. + +Selecting `Activate focus stealing prevention` and `Do nothing` for `When a window raises itself`, seems to mitigate the issue. Deselecting `Enable display compositing` on the `Compositor` tab, also works. + +![Window Manager Tweaks](xfce4_focus_stealing_issue.png) + +## OpenGl + ADS + +If you would like to use OpenGL widgets with ADS such as `GLWidget`, QML or `QWebEngineView`, then you need +to set `QApplication::setAttribute(Qt::AA_ShareOpenGLContexts)` before creating your application and your +widgets (see issue [#732]) + +```c++ +int main(int argc, char *argv[]) +{ + QApplication::setAttribute(Qt::AA_ShareOpenGLContexts); + QApplication application(argc, argv); + ... +} +``` diff --git a/doc/xfce4_focus_stealing_issue.png b/doc/xfce4_focus_stealing_issue.png new file mode 100644 index 000000000..09f04079f Binary files /dev/null and b/doc/xfce4_focus_stealing_issue.png differ diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index ba2a064e6..431d48aff 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.16) project(QtADSExamples LANGUAGES CXX VERSION ${VERSION_SHORT}) add_subdirectory(simple) add_subdirectory(hideshow) @@ -6,5 +6,6 @@ add_subdirectory(sidebar) add_subdirectory(deleteonclose) add_subdirectory(centralwidget) add_subdirectory(autohide) +add_subdirectory(autohidedragndrop) add_subdirectory(emptydockarea) -add_subdirectory(dockindock) \ No newline at end of file +add_subdirectory(dockindock) diff --git a/examples/autohide/CMakeLists.txt b/examples/autohide/CMakeLists.txt index 337906770..c696bdbdb 100644 --- a/examples/autohide/CMakeLists.txt +++ b/examples/autohide/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.16) 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) @@ -9,7 +9,7 @@ add_executable(AutoHideExample WIN32 mainwindow.ui ) target_include_directories(AutoHideExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src") -target_link_libraries(AutoHideExample PRIVATE qt${QT_VERSION_MAJOR}advanceddocking) +target_link_libraries(AutoHideExample PRIVATE qtadvanceddocking-qt${QT_VERSION_MAJOR}) target_link_libraries(AutoHideExample PUBLIC Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Widgets) diff --git a/examples/autohide/main.py b/examples/autohide/main.py index 5fe6a492d..48c29278f 100644 --- a/examples/autohide/main.py +++ b/examples/autohide/main.py @@ -8,7 +8,7 @@ QTableWidget, QFileSystemModel, QPlainTextEdit, QToolBar, QWidgetAction, QComboBox, QAction, QSizePolicy, QInputDialog) -from PyQtAds import QtAds +import PyQtAds as QtAds UI_FILE = os.path.join(os.path.dirname(__file__), 'mainwindow.ui') MainWindowUI, MainWindowBase = uic.loadUiType(UI_FILE) diff --git a/examples/autohide/mainwindow.cpp b/examples/autohide/mainwindow.cpp index d70b51ce7..4e6199682 100644 --- a/examples/autohide/mainwindow.cpp +++ b/examples/autohide/mainwindow.cpp @@ -33,7 +33,7 @@ CMainWindow::CMainWindow(QWidget *parent) // Set central widget QPlainTextEdit* w = new QPlainTextEdit(); w->setPlaceholderText("This is the central editor. Enter your text here."); - CDockWidget* CentralDockWidget = new CDockWidget("CentralWidget"); + CDockWidget* CentralDockWidget = DockManager->createDockWidget("CentralWidget"); CentralDockWidget->setWidget(w); auto* CentralDockArea = DockManager->setCentralWidget(CentralDockWidget); CentralDockArea->setAllowedAreas(DockWidgetArea::OuterDockAreas); @@ -42,7 +42,7 @@ CMainWindow::CMainWindow(QWidget *parent) QTableWidget* table = new QTableWidget(); table->setColumnCount(3); table->setRowCount(10); - CDockWidget* TableDockWidget = new CDockWidget("Table 1"); + CDockWidget* TableDockWidget = DockManager->createDockWidget("Table 1"); TableDockWidget->setWidget(table); TableDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget); TableDockWidget->setMinimumSize(200,150); @@ -53,7 +53,7 @@ CMainWindow::CMainWindow(QWidget *parent) table = new QTableWidget(); table->setColumnCount(5); table->setRowCount(1020); - TableDockWidget = new CDockWidget("Table 2"); + TableDockWidget = DockManager->createDockWidget("Table 2"); TableDockWidget->setWidget(table); TableDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget); TableDockWidget->resize(250, 150); @@ -64,7 +64,7 @@ CMainWindow::CMainWindow(QWidget *parent) QTableWidget* propertiesTable = new QTableWidget(); propertiesTable->setColumnCount(3); propertiesTable->setRowCount(10); - CDockWidget* PropertiesDockWidget = new CDockWidget("Properties"); + CDockWidget* PropertiesDockWidget = DockManager->createDockWidget("Properties"); PropertiesDockWidget->setWidget(propertiesTable); PropertiesDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget); PropertiesDockWidget->resize(250, 150); diff --git a/examples/autohidedragndrop/CMakeLists.txt b/examples/autohidedragndrop/CMakeLists.txt new file mode 100644 index 000000000..cae013c91 --- /dev/null +++ b/examples/autohidedragndrop/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.16) +project(ads_example_autohide_dragndrop 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(AutoHideDragNDropExample WIN32 + main.cpp + mainwindow.cpp + mainwindow.ui + droppableitem.cpp +) +target_include_directories(AutoHideDragNDropExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src") +target_link_libraries(AutoHideDragNDropExample PRIVATE qtadvanceddocking-qt${QT_VERSION_MAJOR}) +target_link_libraries(AutoHideDragNDropExample PUBLIC Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Gui + Qt${QT_VERSION_MAJOR}::Widgets) +set_target_properties(AutoHideDragNDropExample 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 With Drag N Drop 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/autohidedragndrop/autohidedragndrop.pro b/examples/autohidedragndrop/autohidedragndrop.pro new file mode 100644 index 000000000..f812355fe --- /dev/null +++ b/examples/autohidedragndrop/autohidedragndrop.pro @@ -0,0 +1,36 @@ +ADS_OUT_ROOT = $${OUT_PWD}/../.. + +QT += core gui widgets + +TARGET = AutoHideDragNDropExample +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 \ + droppableitem.cpp + +HEADERS += \ + mainwindow.h \ + droppableitem.h + +FORMS += \ + mainwindow.ui + +LIBS += -L$${ADS_OUT_ROOT}/lib +include(../../ads.pri) +INCLUDEPATH += ../../src +DEPENDPATH += ../../src + diff --git a/examples/autohidedragndrop/droppableitem.cpp b/examples/autohidedragndrop/droppableitem.cpp new file mode 100644 index 000000000..d7d2addf5 --- /dev/null +++ b/examples/autohidedragndrop/droppableitem.cpp @@ -0,0 +1,38 @@ +#include "droppableitem.h" + +#include +#include +#include +#include +#include + +DroppableItem::DroppableItem(const QString& text, QWidget* parent) + : QPushButton(text, parent) +{ + setAcceptDrops(true); + setSizePolicy(QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding); +} + +void DroppableItem::dragEnterEvent(QDragEnterEvent* event) +{ + if (event->mimeData()->hasText()) + { + event->acceptProposedAction(); + setCursor(Qt::DragMoveCursor); + } +} + +void DroppableItem::dragLeaveEvent(QDragLeaveEvent* event) +{ + Q_UNUSED(event); + unsetCursor(); +} + +void DroppableItem::dropEvent(QDropEvent* event) +{ + if (event->mimeData()->hasText()) + { + event->acceptProposedAction(); + setText(event->mimeData()->text()); + } +} diff --git a/examples/autohidedragndrop/droppableitem.h b/examples/autohidedragndrop/droppableitem.h new file mode 100644 index 000000000..a8fcd913e --- /dev/null +++ b/examples/autohidedragndrop/droppableitem.h @@ -0,0 +1,19 @@ +#include +#include + +class QDragEnterEvent; +class QDragLeaveEvent; +class QDropEvent; + +class DroppableItem : public QPushButton +{ + Q_OBJECT; + +public: + DroppableItem(const QString& text = QString(), QWidget* parent = nullptr); + +protected: + void dragEnterEvent(QDragEnterEvent* event) override; + void dragLeaveEvent(QDragLeaveEvent* event) override; + void dropEvent(QDropEvent* event) override; +}; diff --git a/examples/autohidedragndrop/main.cpp b/examples/autohidedragndrop/main.cpp new file mode 100644 index 000000000..fa4c4fdea --- /dev/null +++ b/examples/autohidedragndrop/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/autohidedragndrop/main.py b/examples/autohidedragndrop/main.py new file mode 100644 index 000000000..4ec084b07 --- /dev/null +++ b/examples/autohidedragndrop/main.py @@ -0,0 +1,84 @@ +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) + +import PyQtAds as 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) + QtAds.CDockManager.setAutoHideConfigFlag(QtAds.CDockManager.AutoHideOpenOnDragHover, 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) + + + droppable_item = DroppableItem("Drop text here.") + drop_dock_widget = QtAds.CDockWidget("Tab") + drop_dock_widget.setWidget(droppable_item) + drop_dock_widget.setMinimumSizeHintMode(QtAds.CDockWidget.MinimumSizeHintFromDockWidget) + drop_dock_widget.setMinimumSize(200, 150) + drop_dock_widget.setAcceptDrops(True) + drop_area = self.dock_manager.addDockWidget(QtAds.DockWidgetArea.LeftDockWidgetArea, drop_dock_widget) + drop_area.setAcceptDrops(True) + self.menuView.addAction(drop_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/autohidedragndrop/mainwindow.cpp b/examples/autohidedragndrop/mainwindow.cpp new file mode 100644 index 000000000..774fca2ce --- /dev/null +++ b/examples/autohidedragndrop/mainwindow.cpp @@ -0,0 +1,132 @@ +#include "mainwindow.h" +#include "droppableitem.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::setConfigFlag(CDockManager::TabsAtBottom, true); + CDockManager::setAutoHideConfigFlags(CDockManager::DefaultAutoHideConfig); + CDockManager::setAutoHideConfigFlag(CDockManager::AutoHideOpenOnDragHover, true); + CDockManager::setConfigParam(CDockManager::AutoHideOpenOnDragHoverDelay_ms, 500); + DockManager = new CDockManager(this); + + // Set central widget + QPlainTextEdit* w = new QPlainTextEdit(); + w->setPlaceholderText("This is the central editor. Enter your text here."); + CDockWidget* CentralDockWidget = DockManager->createDockWidget("CentralWidget"); + CentralDockWidget->setWidget(w); + auto* CentralDockArea = DockManager->setCentralWidget(CentralDockWidget); + CentralDockArea->setAllowedAreas(DockWidgetArea::OuterDockAreas); + + { + DroppableItem* droppableItem = new DroppableItem("Drop text here."); + CDockWidget* dropDockWidget = DockManager->createDockWidget("Tab 1"); + dropDockWidget->setWidget(droppableItem); + dropDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget); + dropDockWidget->setMinimumSize(200,150); + dropDockWidget->setAcceptDrops(true); + const auto autoHideContainer = DockManager->addAutoHideDockWidget(SideBarLocation::SideBarLeft, dropDockWidget); + autoHideContainer->setSize(480); + autoHideContainer->setAcceptDrops(true); + ui->menuView->addAction(dropDockWidget->toggleViewAction()); + } + { + DroppableItem* droppableItem = new DroppableItem("Drop text here."); + CDockWidget* dropDockWidget = DockManager->createDockWidget("Tab 2"); + dropDockWidget->setWidget(droppableItem); + dropDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget); + dropDockWidget->setMinimumSize(200,150); + dropDockWidget->setAcceptDrops(true); + const auto autoHideContainer = DockManager->addAutoHideDockWidget(SideBarLocation::SideBarRight, dropDockWidget); + autoHideContainer->setSize(480); + autoHideContainer->setAcceptDrops(true); + ui->menuView->addAction(dropDockWidget->toggleViewAction()); + } + + QTableWidget* propertiesTable = new QTableWidget(); + propertiesTable->setColumnCount(3); + propertiesTable->setRowCount(10); + CDockWidget* PropertiesDockWidget = DockManager->createDockWidget("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/autohidedragndrop/mainwindow.h b/examples/autohidedragndrop/mainwindow.h new file mode 100644 index 000000000..75869da84 --- /dev/null +++ b/examples/autohidedragndrop/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/autohidedragndrop/mainwindow.ui b/examples/autohidedragndrop/mainwindow.ui new file mode 100644 index 000000000..f7d3b09e8 --- /dev/null +++ b/examples/autohidedragndrop/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/CMakeLists.txt b/examples/centralwidget/CMakeLists.txt index ae42cdfb9..8d7a10643 100644 --- a/examples/centralwidget/CMakeLists.txt +++ b/examples/centralwidget/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.16) project(ads_example_centralwidget 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) @@ -9,7 +9,7 @@ add_executable(CentralWidgetExample WIN32 mainwindow.ui ) target_include_directories(CentralWidgetExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src") -target_link_libraries(CentralWidgetExample PRIVATE qt${QT_VERSION_MAJOR}advanceddocking) +target_link_libraries(CentralWidgetExample PRIVATE qtadvanceddocking-qt${QT_VERSION_MAJOR}) target_link_libraries(CentralWidgetExample PUBLIC Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Widgets) diff --git a/examples/centralwidget/main.py b/examples/centralwidget/main.py index 5fe6a492d..48c29278f 100644 --- a/examples/centralwidget/main.py +++ b/examples/centralwidget/main.py @@ -8,7 +8,7 @@ QTableWidget, QFileSystemModel, QPlainTextEdit, QToolBar, QWidgetAction, QComboBox, QAction, QSizePolicy, QInputDialog) -from PyQtAds import QtAds +import PyQtAds as QtAds UI_FILE = os.path.join(os.path.dirname(__file__), 'mainwindow.ui') MainWindowUI, MainWindowBase = uic.loadUiType(UI_FILE) diff --git a/examples/centralwidget/mainwindow.cpp b/examples/centralwidget/mainwindow.cpp index 3ad0056df..bb218b886 100644 --- a/examples/centralwidget/mainwindow.cpp +++ b/examples/centralwidget/mainwindow.cpp @@ -40,7 +40,7 @@ CMainWindow::CMainWindow(QWidget *parent) // Set central widget QPlainTextEdit* w = new QPlainTextEdit(); w->setPlaceholderText("This is the central editor. Enter your text here."); - CDockWidget* CentralDockWidget = new CDockWidget("CentralWidget"); + CDockWidget* CentralDockWidget = DockManager->createDockWidget("CentralWidget"); CentralDockWidget->setWidget(w); auto* CentralDockArea = DockManager->setCentralWidget(CentralDockWidget); CentralDockArea->setAllowedAreas(DockWidgetArea::OuterDockAreas); @@ -49,7 +49,7 @@ CMainWindow::CMainWindow(QWidget *parent) QTableWidget* table = new QTableWidget(); table->setColumnCount(3); table->setRowCount(10); - CDockWidget* TableDockWidget = new CDockWidget("Table 1"); + CDockWidget* TableDockWidget = DockManager->createDockWidget("Table 1"); TableDockWidget->setWidget(table); TableDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget); TableDockWidget->resize(250, 150); @@ -60,7 +60,7 @@ CMainWindow::CMainWindow(QWidget *parent) table = new QTableWidget(); table->setColumnCount(5); table->setRowCount(1020); - TableDockWidget = new CDockWidget("Table 2"); + TableDockWidget = DockManager->createDockWidget("Table 2"); TableDockWidget->setWidget(table); TableDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget); TableDockWidget->resize(250, 150); @@ -71,7 +71,7 @@ CMainWindow::CMainWindow(QWidget *parent) QTableWidget* propertiesTable = new QTableWidget(); propertiesTable->setColumnCount(3); propertiesTable->setRowCount(10); - CDockWidget* PropertiesDockWidget = new CDockWidget("Properties"); + CDockWidget* PropertiesDockWidget = DockManager->createDockWidget("Properties"); PropertiesDockWidget->setWidget(propertiesTable); PropertiesDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget); PropertiesDockWidget->resize(250, 150); diff --git a/examples/configflags/CMakeLists.txt b/examples/configflags/CMakeLists.txt new file mode 100644 index 000000000..2f4bb2bd7 --- /dev/null +++ b/examples/configflags/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 3.16) +project(ads_example_centralwidget 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(configFlagsExample WIN32 + main.cpp + mainwindow.cpp + mainwindow.ui +) +target_include_directories(CentralWidgetExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src") +target_link_libraries(CentralWidgetExample PRIVATE qtadvanceddocking-qt${QT_VERSION_MAJOR}) +target_link_libraries(CentralWidgetExample PUBLIC Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Gui + Qt${QT_VERSION_MAJOR}::Widgets) +set_target_properties(CentralWidgetExample PROPERTIES + AUTOMOC ON + AUTORCC ON + AUTOUIC ON + CXX_STANDARD 14 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS OFF + VERSION ${VERSION_SHORT} + EXPORT_NAME "Qt Advanced Docking System Central Widget Example" + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${ads_PlatformDir}/lib" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${ads_PlatformDir}/lib" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${ads_PlatformDir}/bin" +) diff --git a/examples/configflags/configflags.pro b/examples/configflags/configflags.pro new file mode 100644 index 000000000..83271745d --- /dev/null +++ b/examples/configflags/configflags.pro @@ -0,0 +1,34 @@ +ADS_OUT_ROOT = $${OUT_PWD}/../.. + +QT += core gui widgets + +TARGET = ConfigFlagsExample +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/configflags/main.cpp b/examples/configflags/main.cpp new file mode 100644 index 000000000..fa4c4fdea --- /dev/null +++ b/examples/configflags/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/configflags/mainwindow.cpp b/examples/configflags/mainwindow.cpp new file mode 100644 index 000000000..a50052843 --- /dev/null +++ b/examples/configflags/mainwindow.cpp @@ -0,0 +1,64 @@ +#include "mainwindow.h" + +#include "ui_mainwindow.h" + +#include +#include + +#include "DockAreaWidget.h" + + +using namespace ads; + + +CMainWindow::CMainWindow(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::CMainWindow) +{ + ui->setupUi(this); + + // Add the toolbar + auto toolbar_ = addToolBar("Top Toolbar"); + + // Create the dock manager + ads::CDockManager::setConfigFlags(ads::CDockManager::DefaultOpaqueConfig); + ads::CDockManager::setConfigFlag(ads::CDockManager::DockAreaHasCloseButton, + false); + ads::CDockManager::setConfigFlag(ads::CDockManager::DockAreaHasUndockButton, + false); + ads::CDockManager::setConfigFlag( + ads::CDockManager::DockAreaHasTabsMenuButton, false); + auto DockManager = new ads::CDockManager(this); + + // Create a dockable widget + QLabel *l1 = new QLabel(); + l1->setWordWrap(true); + l1->setAlignment(Qt::AlignTop | Qt::AlignLeft); + l1->setText("Docking widget 1"); + ads::CDockWidget *dockWidget1 = DockManager->createDockWidget("Dock 1"); + dockWidget1->setWidget(l1); + DockManager->addDockWidget(ads::LeftDockWidgetArea, dockWidget1); + + QLabel *l2 = new QLabel(); + l2->setWordWrap(true); + l2->setAlignment(Qt::AlignTop | Qt::AlignLeft); + l2->setText("Docking widget 2"); + ads::CDockWidget *dockWidget2 = DockManager->createDockWidget("Dock 2"); + dockWidget2->setWidget(l2); + DockManager->addDockWidget(ads::RightDockWidgetArea, dockWidget2); + + // Add menu actions + ui->menuView->addAction(dockWidget1->toggleViewAction()); + ui->menuView->addAction(dockWidget2->toggleViewAction()); + toolbar_->addAction(dockWidget1->toggleViewAction()); + toolbar_->addAction(dockWidget2->toggleViewAction()); +} + + +CMainWindow::~CMainWindow() +{ + delete ui; +} + + + diff --git a/examples/configflags/mainwindow.h b/examples/configflags/mainwindow.h new file mode 100644 index 000000000..a2b45da77 --- /dev/null +++ b/examples/configflags/mainwindow.h @@ -0,0 +1,27 @@ +#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(); + +private: + Ui::CMainWindow *ui; +}; +#endif // MAINWINDOW_H diff --git a/examples/configflags/mainwindow.ui b/examples/configflags/mainwindow.ui new file mode 100644 index 000000000..f7d3b09e8 --- /dev/null +++ b/examples/configflags/mainwindow.ui @@ -0,0 +1,47 @@ + + + CMainWindow + + + + 0 + 0 + 1284 + 757 + + + + MainWindow + + + + + + 0 + 0 + 1284 + 21 + + + + + View + + + + + + + toolBar + + + TopToolBarArea + + + false + + + + + + diff --git a/examples/deleteonclose/CMakeLists.txt b/examples/deleteonclose/CMakeLists.txt index 98557a6fd..f4df970c7 100644 --- a/examples/deleteonclose/CMakeLists.txt +++ b/examples/deleteonclose/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.16) project(ads_example_deleteonclose 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) @@ -7,7 +7,7 @@ add_executable(DeleteOnCloseTest WIN32 main.cpp ) target_include_directories(DeleteOnCloseTest PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src") -target_link_libraries(DeleteOnCloseTest PRIVATE qt${QT_VERSION_MAJOR}advanceddocking) +target_link_libraries(DeleteOnCloseTest PRIVATE qtadvanceddocking-qt${QT_VERSION_MAJOR}) target_link_libraries(DeleteOnCloseTest PUBLIC Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Widgets) diff --git a/examples/deleteonclose/main.cpp b/examples/deleteonclose/main.cpp index 25f8ed14c..d5eca4a3e 100644 --- a/examples/deleteonclose/main.cpp +++ b/examples/deleteonclose/main.cpp @@ -46,7 +46,7 @@ int main(int argc, char *argv[]) int i = 0; QObject::connect(action, &QAction::triggered, [&]() { - auto dw = new ads::CDockWidget(QStringLiteral("test %1 [DockWidgetDeleteOnClose]").arg(i++), &w); + auto dw = dockManager->createDockWidget(QStringLiteral("test %1 [DockWidgetDeleteOnClose]").arg(i++), &w); auto editor = new QTextEdit(QStringLiteral("lorem ipsum..."), dw); dw->setWidget(editor); dw->setFeature(ads::CDockWidget::DockWidgetDeleteOnClose, true); @@ -54,7 +54,7 @@ int main(int argc, char *argv[]) qDebug() << "doc dock widget created!" << dw << area; }); - auto dw = new ads::CDockWidget(QStringLiteral("test %1 [DeleteContentOnClose]").arg(i++), &w); + auto dw = dockManager->createDockWidget(QStringLiteral("test %1 [DeleteContentOnClose]").arg(i++), &w); auto editor = new QTextEdit(QStringLiteral("recreated lorem ipsum......"), dw); dw->setWidget(editor); dw->setFeature(ads::CDockWidget::DeleteContentOnClose, true); @@ -77,7 +77,7 @@ int main(int argc, char *argv[]) action = new QAction("New", &w); w.menuBar()->addAction(action); QObject::connect(action, &QAction::triggered, [&]() { - auto dw = new ads::CDockWidget(QStringLiteral("test %1").arg(i++), &w); + auto dw = dockManager->createDockWidget(QStringLiteral("test %1").arg(i++), &w); auto editor = new QTextEdit(QStringLiteral("lorem ipsum..."), dw); dw->setWidget(editor); auto area = dockManager->addDockWidgetTab(ads::CenterDockWidgetArea, dw); diff --git a/examples/deleteonclose/main.py b/examples/deleteonclose/main.py index 514f1a07f..d1504bffc 100644 --- a/examples/deleteonclose/main.py +++ b/examples/deleteonclose/main.py @@ -1,6 +1,6 @@ import sys -from PyQtAds import QtAds +import PyQtAds as QtAds from PyQt5.QtGui import QCloseEvent from PyQt5.QtCore import (qDebug, pyqtSlot, QObject, pyqtSignal) from PyQt5.QtWidgets import (QMainWindow, QAction, QTextEdit, QApplication, @@ -70,4 +70,4 @@ def on_action2_triggered(): action.triggered.connect(on_action2_triggered) w.show() - app.exec_() \ No newline at end of file + app.exec_() diff --git a/examples/dockindock/CMakeLists.txt b/examples/dockindock/CMakeLists.txt index 39ba98cad..25c143826 100644 --- a/examples/dockindock/CMakeLists.txt +++ b/examples/dockindock/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.16) project(ads_example_dockindock 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) @@ -12,7 +12,7 @@ add_executable(DockInDockExample WIN32 mainframe.cpp ) target_include_directories(DockInDockExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src") -target_link_libraries(DockInDockExample PRIVATE qt${QT_VERSION_MAJOR}advanceddocking) +target_link_libraries(DockInDockExample PRIVATE qtadvanceddocking-qt${QT_VERSION_MAJOR}) target_link_libraries(DockInDockExample PUBLIC Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Widgets) diff --git a/examples/dockindock/dockindock.cpp b/examples/dockindock/dockindock.cpp index bacf42cbd..cbc983bc6 100644 --- a/examples/dockindock/dockindock.cpp +++ b/examples/dockindock/dockindock.cpp @@ -54,7 +54,7 @@ ads::CDockAreaWidget* DockInDockWidget::addTabWidget( QWidget* widget, const QSt } } - ads::CDockWidget* DockWidget = new ads::CDockWidget(name); + ads::CDockWidget* DockWidget = m_mgr->createDockWidget(name); DockWidget->setWidget(widget); DockWidget->setIcon( icon ); @@ -101,7 +101,7 @@ DockInDockWidget* DockInDockWidget::createGroup( const QString& groupName, QIcon DockInDockWidget* child = new DockInDockWidget( this, m_topLevelDockWidget, m_perspectivesManager ); child->setObjectName( groupName ); - ads::CDockWidget* DockWidget = new ads::CDockWidget(groupName); + ads::CDockWidget* DockWidget = m_mgr->createDockWidget(groupName); DockWidget->setWidget(child); DockWidget->setIcon(icon); diff --git a/examples/dockindock/dockindock.py b/examples/dockindock/dockindock.py index 4853b5747..5d64fe170 100644 --- a/examples/dockindock/dockindock.py +++ b/examples/dockindock/dockindock.py @@ -3,7 +3,7 @@ from PyQt5.QtWidgets import (QApplication, QWidget, QVBoxLayout, QMessageBox, QInputDialog, QMenu, QLineEdit) from PyQt5.QtGui import QIcon -from PyQtAds import QtAds +import PyQtAds as QtAds from dockindockmanager import DockInDockManager from perspectiveactions import LoadPerspectiveAction, RemovePerspectiveAction @@ -200,4 +200,4 @@ def dumpStatus(self, echo: callable = print, widget: QtAds.CDockWidget = None, t for closed in self.getManager().dockWidgetsMap().values(): if not closed in visible_widgets: - self.dumpStatus(widget=closed, tab=tab, suffix=" (closed)") \ No newline at end of file + self.dumpStatus(widget=closed, tab=tab, suffix=" (closed)") diff --git a/examples/dockindock/dockindockmanager.py b/examples/dockindock/dockindockmanager.py index c6f47cb74..224d7195a 100644 --- a/examples/dockindock/dockindockmanager.py +++ b/examples/dockindock/dockindockmanager.py @@ -1,7 +1,7 @@ from PyQt5.QtWidgets import QAction, QMenu, QInputDialog, QLineEdit from PyQt5.QtCore import QSettings -from PyQtAds import QtAds +import PyQtAds as QtAds CHILD_PREFIX = "Child-" @@ -211,4 +211,4 @@ def move(self, widget: QtAds.CDockWidget, move_to: QtAds.CDockManager) -> None: widget.dockManager().removeDockWidget(widget) move_to.addDockWidget(QtAds.CenterDockWidgetArea, widget, move_to.getInsertDefaultPos()) else: - assert False \ No newline at end of file + assert False diff --git a/examples/dockindock/main.cpp b/examples/dockindock/main.cpp index 83e8bb7e4..9ae353929 100644 --- a/examples/dockindock/main.cpp +++ b/examples/dockindock/main.cpp @@ -1,5 +1,5 @@ #include -#include "../../examples/simple/MainWindow.h" +#include "mainframe.h" int main(int argc, char *argv[]) { diff --git a/examples/dockindock/main.py b/examples/dockindock/main.py index 5b6e4788d..be32b082d 100644 --- a/examples/dockindock/main.py +++ b/examples/dockindock/main.py @@ -4,7 +4,7 @@ from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel from PyQt5.QtCore import Qt -from PyQtAds import QtAds +import PyQtAds as QtAds from perspectives import PerspectivesManager from dockindock import DockInDockWidget @@ -69,4 +69,4 @@ def cleanup(self): w = MainWindow() w.show() - app.exec_() \ No newline at end of file + app.exec_() diff --git a/examples/dockindock/perspectives.py b/examples/dockindock/perspectives.py index ab9dddb50..9b6c00df4 100644 --- a/examples/dockindock/perspectives.py +++ b/examples/dockindock/perspectives.py @@ -4,7 +4,7 @@ import atexit from PyQt5.QtCore import pyqtSignal, QSettings, QObject -from PyQtAds import QtAds +import PyQtAds as QtAds from dockindockmanager import DockInDockManager from dockindock import DockInDockWidget @@ -200,4 +200,4 @@ def savePerspectives(self) -> None: except FileNotFoundError: pass if not shutil.copy(settings.fileName(), to_save): - assert False \ No newline at end of file + assert False diff --git a/examples/emptydockarea/CMakeLists.txt b/examples/emptydockarea/CMakeLists.txt index c9b02f219..bdeb0c922 100644 --- a/examples/emptydockarea/CMakeLists.txt +++ b/examples/emptydockarea/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.16) project(ads_example_centralwidget 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) @@ -9,7 +9,7 @@ add_executable(EmptyDockAreaExample WIN32 mainwindow.ui ) target_include_directories(EmptyDockAreaExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src") -target_link_libraries(EmptyDockAreaExample PRIVATE qt${QT_VERSION_MAJOR}advanceddocking) +target_link_libraries(EmptyDockAreaExample PRIVATE qtadvanceddocking-qt${QT_VERSION_MAJOR}) target_link_libraries(EmptyDockAreaExample PUBLIC Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Widgets) diff --git a/examples/emptydockarea/main.py b/examples/emptydockarea/main.py index 046d45fe4..475cc2ed9 100644 --- a/examples/emptydockarea/main.py +++ b/examples/emptydockarea/main.py @@ -6,7 +6,7 @@ from PyQt5.QtWidgets import (QApplication, QMainWindow, QLabel, QComboBox, QTableWidget, QAction, QWidgetAction, QSizePolicy, QInputDialog) from PyQt5.QtGui import QCloseEvent -from PyQtAds import QtAds +import PyQtAds as QtAds UI_FILE = os.path.join(os.path.dirname(__file__), 'mainwindow.ui') @@ -105,4 +105,4 @@ def closeEvent(self, event: QCloseEvent): w = CMainWindow() w.show() - app.exec_() \ No newline at end of file + app.exec_() diff --git a/examples/emptydockarea/mainwindow.cpp b/examples/emptydockarea/mainwindow.cpp index 2dbe99ca5..2c00e9bd6 100644 --- a/examples/emptydockarea/mainwindow.cpp +++ b/examples/emptydockarea/mainwindow.cpp @@ -19,8 +19,6 @@ #include #include "DockAreaWidget.h" -#include "DockAreaTitleBar.h" -#include "DockAreaTabBar.h" #include "FloatingDockContainer.h" #include "DockComponentsFactory.h" @@ -32,16 +30,19 @@ CMainWindow::CMainWindow(QWidget *parent) , ui(new Ui::CMainWindow) { ui->setupUi(this); - CDockManager::setConfigFlag(CDockManager::OpaqueSplitterResize, true); - CDockManager::setConfigFlag(CDockManager::XmlCompressionEnabled, false); - CDockManager::setConfigFlag(CDockManager::FocusHighlighting, true); + ads::CDockManager::setConfigFlag( ads::CDockManager::DockAreaHasCloseButton, false ); + ads::CDockManager::setConfigFlag( ads::CDockManager::AllTabsHaveCloseButton, true ); + ads::CDockManager::setConfigFlag( ads::CDockManager::DockAreaHasUndockButton, false ); + ads::CDockManager::setConfigFlag( ads::CDockManager::DockAreaDynamicTabsMenuButtonVisibility, true ); + ads::CDockManager::setConfigFlag( ads::CDockManager::DisableTabTextEliding, true ); + ads::CDockManager::setConfigFlag( ads::CDockManager::DoubleClickUndocksWidget, false ); DockManager = new CDockManager(this); // Set central widget QLabel* label = new QLabel(); label->setText("This is a DockArea which is always visible, even if it does not contain any DockWidgets."); label->setAlignment(Qt::AlignCenter); - CDockWidget* CentralDockWidget = new CDockWidget("CentralWidget"); + CDockWidget* CentralDockWidget = DockManager->createDockWidget("CentralWidget"); CentralDockWidget->setWidget(label); CentralDockWidget->setFeature(ads::CDockWidget::NoTab, true); auto* CentralDockArea = DockManager->setCentralWidget(CentralDockWidget); @@ -50,7 +51,7 @@ CMainWindow::CMainWindow(QWidget *parent) QTableWidget* table = new QTableWidget(); table->setColumnCount(3); table->setRowCount(10); - CDockWidget* TableDockWidget = new CDockWidget("Table 1"); + CDockWidget* TableDockWidget = DockManager->createDockWidget("Table 1"); TableDockWidget->setWidget(table); TableDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget); TableDockWidget->resize(250, 150); @@ -62,7 +63,7 @@ CMainWindow::CMainWindow(QWidget *parent) table = new QTableWidget(); table->setColumnCount(5); table->setRowCount(1020); - TableDockWidget = new CDockWidget("Table 2"); + TableDockWidget = DockManager->createDockWidget("Table 2"); TableDockWidget->setWidget(table); TableDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget); TableDockWidget->resize(250, 150); @@ -73,7 +74,7 @@ CMainWindow::CMainWindow(QWidget *parent) QTableWidget* propertiesTable = new QTableWidget(); propertiesTable->setColumnCount(3); propertiesTable->setRowCount(10); - CDockWidget* PropertiesDockWidget = new CDockWidget("Properties"); + CDockWidget* PropertiesDockWidget = DockManager->createDockWidget("Properties"); PropertiesDockWidget->setWidget(propertiesTable); PropertiesDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget); PropertiesDockWidget->resize(250, 150); @@ -98,8 +99,13 @@ void CMainWindow::createPerspectiveUi() PerspectiveComboBox = new QComboBox(this); PerspectiveComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents); PerspectiveComboBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + connect(PerspectiveComboBox, &QComboBox::textActivated, + DockManager, &CDockManager::openPerspective); +#else connect(PerspectiveComboBox, SIGNAL(activated(const QString&)), DockManager, SLOT(openPerspective(const QString&))); +#endif PerspectiveListAction->setDefaultWidget(PerspectiveComboBox); ui->toolBar->addSeparator(); ui->toolBar->addAction(PerspectiveListAction); diff --git a/examples/examples.pro b/examples/examples.pro index 4e4547a0f..05e739a94 100644 --- a/examples/examples.pro +++ b/examples/examples.pro @@ -2,10 +2,12 @@ TEMPLATE = subdirs SUBDIRS = \ autohide \ + autohidedragndrop \ centralwidget \ simple \ hideshow \ sidebar \ deleteonclose \ emptydockarea \ - dockindock + dockindock \ + configflags diff --git a/examples/hideshow/CMakeLists.txt b/examples/hideshow/CMakeLists.txt index becab358f..f5a702bd0 100644 --- a/examples/hideshow/CMakeLists.txt +++ b/examples/hideshow/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.16) project(ads_example_hideshow 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) @@ -9,7 +9,7 @@ add_executable(HideShowExample WIN32 MainWindow.ui ) target_include_directories(HideShowExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src") -target_link_libraries(HideShowExample PRIVATE qt${QT_VERSION_MAJOR}advanceddocking) +target_link_libraries(HideShowExample PRIVATE qtadvanceddocking-qt${QT_VERSION_MAJOR}) target_link_libraries(HideShowExample PUBLIC Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Widgets) diff --git a/examples/hideshow/MainWindow.cpp b/examples/hideshow/MainWindow.cpp index 44ce67219..6b73a19fb 100644 --- a/examples/hideshow/MainWindow.cpp +++ b/examples/hideshow/MainWindow.cpp @@ -35,7 +35,7 @@ MainWindow::MainWindow(QWidget *parent) : // Create a dock widget with the title Label 1 and set the created label // as the dock widget content - ads::CDockWidget* DockWidget = new ads::CDockWidget("Label 1"); + ads::CDockWidget* DockWidget = m_DockManager->createDockWidget("Label 1"); DockWidget->setWidget(l); // Add the toggleViewAction of the dock widget to the menu to give diff --git a/examples/sidebar/CMakeLists.txt b/examples/sidebar/CMakeLists.txt index a2e05e6e5..ce6fd6d21 100644 --- a/examples/sidebar/CMakeLists.txt +++ b/examples/sidebar/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.16) project(ads_example_sidebar 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) @@ -9,7 +9,7 @@ add_executable(SidebarExample WIN32 MainWindow.ui ) target_include_directories(SidebarExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src") -target_link_libraries(SidebarExample PRIVATE qt${QT_VERSION_MAJOR}advanceddocking) +target_link_libraries(SidebarExample PRIVATE qtadvanceddocking-qt${QT_VERSION_MAJOR}) target_link_libraries(SidebarExample PUBLIC Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Widgets) diff --git a/examples/sidebar/MainWindow.cpp b/examples/sidebar/MainWindow.cpp index 9d9390897..07e339d60 100644 --- a/examples/sidebar/MainWindow.cpp +++ b/examples/sidebar/MainWindow.cpp @@ -28,7 +28,7 @@ MainWindow::MainWindow(QWidget *parent) : // Create a dock widget with the title Label 1 and set the created label // as the dock widget content - ads::CDockWidget* DockWidget = new ads::CDockWidget("Label 1"); + ads::CDockWidget* DockWidget = m_DockManager->createDockWidget("Label 1"); DockWidget->setWidget(l); // Add the toggleViewAction of the dock widget to the menu to give @@ -42,7 +42,7 @@ MainWindow::MainWindow(QWidget *parent) : // Create an example editor QPlainTextEdit* te = new QPlainTextEdit(); te->setPlaceholderText("Please enter your text here into this QPlainTextEdit..."); - DockWidget = new ads::CDockWidget("Editor 1"); + DockWidget = m_DockManager->createDockWidget("Editor 1"); DockWidget->setWidget(te); ui->menuView->addAction(DockWidget->toggleViewAction()); m_DockManager->addDockWidget(ads::BottomDockWidgetArea, DockWidget); diff --git a/examples/sidebar/main.py b/examples/sidebar/main.py index 27170ba99..8005908d8 100644 --- a/examples/sidebar/main.py +++ b/examples/sidebar/main.py @@ -5,7 +5,7 @@ from PyQt5.QtCore import Qt, QMargins from PyQt5.QtWidgets import QApplication, QLabel, QVBoxLayout, QPlainTextEdit -from PyQtAds import QtAds +import PyQtAds as QtAds UI_FILE = os.path.join(os.path.dirname(__file__), 'MainWindow.ui') MainWindowUI, MainWindowBase = uic.loadUiType(UI_FILE) diff --git a/examples/simple/CMakeLists.txt b/examples/simple/CMakeLists.txt index 99f42a398..573c413c7 100644 --- a/examples/simple/CMakeLists.txt +++ b/examples/simple/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.16) project(ads_example_simple 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) @@ -9,7 +9,7 @@ add_executable(SimpleExample WIN32 MainWindow.ui ) target_include_directories(SimpleExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src") -target_link_libraries(SimpleExample PRIVATE qt${QT_VERSION_MAJOR}advanceddocking) +target_link_libraries(SimpleExample PRIVATE qtadvanceddocking-qt${QT_VERSION_MAJOR}) target_link_libraries(SimpleExample PUBLIC Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Widgets) diff --git a/examples/simple/MainWindow.cpp b/examples/simple/MainWindow.cpp index 46e24b3ab..5eb08bec4 100644 --- a/examples/simple/MainWindow.cpp +++ b/examples/simple/MainWindow.cpp @@ -24,7 +24,7 @@ MainWindow::MainWindow(QWidget *parent) : // Create a dock widget with the title Label 1 and set the created label // as the dock widget content - ads::CDockWidget* DockWidget = new ads::CDockWidget("Label 1"); + ads::CDockWidget* DockWidget = m_DockManager->createDockWidget("Label 1"); DockWidget->setWidget(l); // Add the toggleViewAction of the dock widget to the menu to give diff --git a/examples/simple/main.py b/examples/simple/main.py index b21767ddc..dcacef893 100644 --- a/examples/simple/main.py +++ b/examples/simple/main.py @@ -6,7 +6,7 @@ from PyQt5.QtGui import QCloseEvent from PyQt5.QtWidgets import QApplication, QLabel -from PyQtAds import QtAds +import PyQtAds as QtAds UI_FILE = os.path.join(os.path.dirname(__file__), 'MainWindow.ui') MainWindowUI, MainWindowBase = uic.loadUiType(UI_FILE) diff --git a/pyproject.toml b/pyproject.toml index cbe95d1be..a9b93bb77 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,12 +1,12 @@ # Specify the build system. [build-system] -requires = ["sip >=6.0.2, <6.3", "PyQt-builder >=1.6, <2", "PyQt5==5.15.4", "PyQt5-sip<13,>=12.8"] +requires = ["sip >=6.0.2", "PyQt-builder >=1.6", "PyQt5>=5.15", "PyQt5-sip>=12.8"] build-backend = "sipbuild.api" # Specify the PEP 566 metadata for the project. [tool.sip.metadata] name = "PyQtAds" -version = "4.0.2" +version = "4.4.1" summary = "Python bindings for Qt Advanced Docking System" home-page = "https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/" license = "LGPL v2.1" @@ -16,8 +16,10 @@ description-content-type = "text/markdown" [tool.sip.project] tag-prefix = "QtAds" +dunder-init = true [tool.sip.bindings.ads] +pep484-pyi = true define-macros = ["ADS_SHARED_EXPORT"] sip-file = "ads.sip" include-dirs = ["src"] diff --git a/sip/AutoHideDockContainer.sip b/sip/AutoHideDockContainer.sip index 7d5a18b00..3b13d10e0 100644 --- a/sip/AutoHideDockContainer.sip +++ b/sip/AutoHideDockContainer.sip @@ -24,9 +24,10 @@ public: CAutoHideDockContainer(ads::CDockWidget* DockWidget /Transfer/, ads::SideBarLocation area, ads::CDockContainerWidget* parent /TransferThis/); virtual ~CAutoHideDockContainer(); - ads::CAutoHideSideBar* sideBar() const; + ads::CAutoHideSideBar* autoHideSideBar() const; ads::CAutoHideTab* autoHideTab() const; ads::CDockWidget* dockWidget() const; + int tabIndex() const; void addDockWidget(ads::CDockWidget* DockWidget /Transfer/); ads::SideBarLocation sideBarLocation() const; void setSideBarLocation(ads::SideBarLocation SideBarLocation); @@ -38,7 +39,9 @@ public: void collapseView(bool Enable); void toggleCollapseState(); void setSize(int Size); - + void resetToInitialDockWidgetSize(); + Qt::Orientation orientation() const; + void moveToNewSideBarLocation(ads::SideBarLocation); }; }; diff --git a/sip/AutoHideSideBar.sip b/sip/AutoHideSideBar.sip index 492464bbc..69047481d 100644 --- a/sip/AutoHideSideBar.sip +++ b/sip/AutoHideSideBar.sip @@ -24,10 +24,15 @@ public: void removeTab(ads::CAutoHideTab* SideTab) /TransferBack/; ads::CAutoHideDockContainer* insertDockWidget(int Index, ads::CDockWidget* DockWidget /Transfer/); void removeAutoHideWidget(ads::CAutoHideDockContainer* AutoHideWidget) /TransferBack/; - void addAutoHideWidget(ads::CAutoHideDockContainer* AutoHideWidget); + void addAutoHideWidget(ads::CAutoHideDockContainer* AutoHideWidget, int Index); Qt::Orientation orientation() const; - ads::CAutoHideTab* tabAt(int index) const; - int tabCount() const; + ads::CAutoHideTab* tab(int index) const; + int tabAt(const QPoint& Pos) const; + int tabInsertIndexAt(const QPoint& Pos) const; + int indexOfTab(const CAutoHideTab& Tab) const; + int count() const; + int visibleTabCount() const; + bool hasVisibleTabs() const; ads::SideBarLocation sideBarLocation() const; virtual QSize minimumSizeHint() const; virtual QSize sizeHint() const; diff --git a/sip/AutoHideTab.sip b/sip/AutoHideTab.sip index 42e607c06..4d72e7beb 100644 --- a/sip/AutoHideTab.sip +++ b/sip/AutoHideTab.sip @@ -16,6 +16,10 @@ protected: void setSideBar(ads::CAutoHideSideBar *SideTabBar); void removeFromSideBar(); virtual bool event(QEvent* event); + virtual void contextMenuEvent(QContextMenuEvent* ev); + virtual void mousePressEvent(QMouseEvent* ev); + virtual void mouseReleaseEvent(QMouseEvent* ev); + virtual void mouseMoveEvent(QMouseEvent* ev); public: CAutoHideTab(QWidget* parent /TransferThis/ = 0); @@ -29,6 +33,10 @@ public: void setDockWidget(ads::CDockWidget* DockWidget); bool iconOnly() const; ads::CAutoHideSideBar* sideBar() const; + int tabIndex() const; + void setDockWidgetFloating(); + void unpinDockWidget(); + void requestCloseDockWidget(); }; }; diff --git a/sip/DockAreaTabBar.sip b/sip/DockAreaTabBar.sip index ff867a43a..ab6e72ff0 100644 --- a/sip/DockAreaTabBar.sip +++ b/sip/DockAreaTabBar.sip @@ -23,6 +23,8 @@ public: int currentIndex() const; ads::CDockWidgetTab* currentTab() const; ads::CDockWidgetTab* tab(int Index) const; + int tabAt(const QPoint& Pos) const; + int tabInsertIndexAt(const QPoint& Pos) const; virtual bool eventFilter(QObject *watched, QEvent *event); bool isTabOpen(int Index) const; virtual QSize minimumSizeHint() const; diff --git a/sip/DockAreaTitleBar.sip b/sip/DockAreaTitleBar.sip index 8d3fad794..06c7b1971 100644 --- a/sip/DockAreaTitleBar.sip +++ b/sip/DockAreaTitleBar.sip @@ -5,6 +5,26 @@ namespace ads { +class CTitleBarButton : QToolButton +{ + %TypeHeaderCode + #include + %End + +public: + CTitleBarButton(bool ShowInTitleBar, bool HideWhenDisabled, TitleBarButton ButtonId, + QWidget* /TransferThis/ = Q_NULLPTR ); + virtual void setVisible(bool); + void setShowInTitleBar(bool); + + TitleBarButton buttonId() const; + ads::CDockAreaTitleBar* titleBar() const; + bool isInAutoHideArea() const; + +protected: + bool event(QEvent *ev); +}; + class CDockAreaTitleBar : QFrame { %TypeHeaderCode @@ -26,14 +46,17 @@ public: CDockAreaTitleBar(ads::CDockAreaWidget* parent /TransferThis/); virtual ~CDockAreaTitleBar(); ads::CDockAreaTabBar* tabBar() const; - QAbstractButton* button(ads::TitleBarButton which) const; + ads::CTitleBarButton* button(ads::TitleBarButton which) const; ads::CElidingLabel* autoHideTitleLabel() const; + ads::CDockAreaWidget* dockAreaWidget() const; void updateDockWidgetActionsButtons(); virtual void setVisible(bool Visible); void insertWidget(int index, QWidget *widget /Transfer/ ); int indexOf(QWidget *widget) const; QString titleBarButtonToolTip(ads::TitleBarButton Button) const; - + void setAreaFloating(); + void showAutoHideControls(bool Show); + bool isAutoHide() const; signals: void tabBarClicked(int index); diff --git a/sip/DockAreaTitleBar_p.sip b/sip/DockAreaTitleBar_p.sip index 0ae333944..243a97864 100644 --- a/sip/DockAreaTitleBar_p.sip +++ b/sip/DockAreaTitleBar_p.sip @@ -5,21 +5,6 @@ namespace ads { -class CTitleBarButton : QToolButton -{ - %TypeHeaderCode - #include - %End - -protected: - bool event(QEvent *ev); - -public: - CTitleBarButton(bool visible = true, QWidget* parent /TransferThis/ = Q_NULLPTR ); - virtual void setVisible(bool visible); -}; - - class CSpacerWidget : QWidget { %TypeHeaderCode diff --git a/sip/DockAreaWidget.sip b/sip/DockAreaWidget.sip index ae742630b..6e091f6ab 100644 --- a/sip/DockAreaWidget.sip +++ b/sip/DockAreaWidget.sip @@ -40,6 +40,10 @@ public: virtual ~CDockAreaWidget(); ads::CDockManager* dockManager() const; ads::CDockContainerWidget* dockContainer() const; + ads::CAutoHideDockContainer* autoHideDockContainer() const; + ads::CDockSplitter* parentSplitter() const; + bool isAutoHide() const; + void setAutoHideDockContainer(CAutoHideDockContainer*); virtual QSize minimumSizeHint() const; QRect titleBarGeometry() const; QRect contentAreaGeometry() const; @@ -74,7 +78,10 @@ public: public slots: void setCurrentIndex(int index); void closeArea(); + void setAutoHide(bool Enable, SideBarLocation Location = ads::SideBarNone, int TabIndex = -1); + void toggleAutoHide(SideBarLocation Location = ads::SideBarNone); void closeOtherAreas(); + void setFloating(); signals: void tabBarClicked(int index); diff --git a/sip/DockContainerWidget.sip b/sip/DockContainerWidget.sip index 922db7553..982dcf6c9 100644 --- a/sip/DockContainerWidget.sip +++ b/sip/DockContainerWidget.sip @@ -9,8 +9,8 @@ namespace ads * Container that manages a number of dock areas with single dock widgets * or tabyfied dock widgets in each area. * Each window that support docking has a DockContainerWidget. That means - * the main application window and all floating windows are ore contain - * an DockContainerWidget. + * the main application window and all floating windows contain a + * DockContainerWidget. */ class CDockContainerWidget : QFrame { @@ -20,13 +20,14 @@ class CDockContainerWidget : QFrame protected: virtual bool event(QEvent *e); - QSplitter* rootSplitter() const; - ads::CAutoHideDockContainer* createAndSetupAutoHideContainer(ads::SideBarLocation area, ads::CDockWidget* DockWidget /Transfer/); + ads::CDockSplitter* rootSplitter() const; + ads::CAutoHideDockContainer* createAndSetupAutoHideContainer(ads::SideBarLocation area, ads::CDockWidget* DockWidget /Transfer/, int TabIndex = -1); void createRootSplitter(); void dropFloatingWidget(ads::CFloatingDockContainer* FloatingWidget, const QPoint& TargetPos); - void dropWidget(QWidget* Widget, DockWidgetArea DropArea, CDockAreaWidget* TargetAreaWidget); + void dropWidget(QWidget* Widget, DockWidgetArea DropArea, CDockAreaWidget* TargetAreaWidget, int TabIndex = -1); void addDockArea(ads::CDockAreaWidget* DockAreaWidget /Transfer/, ads::DockWidgetArea area = ads::CenterDockWidgetArea); void removeDockArea(ads::CDockAreaWidget* area /TransferBack/); + /*QList> removeAllDockAreas();*/ void saveState(QXmlStreamWriter& Stream) const; bool restoreState(CDockingStateReader& Stream, bool Testing); ads::CDockAreaWidget* lastAddedDockAreaWidget(ads::DockWidgetArea area) const; @@ -59,7 +60,7 @@ public: ads::CDockWidget::DockWidgetFeatures features() const; ads::CFloatingDockContainer* floatingWidget() const; void closeOtherAreas(ads::CDockAreaWidget* KeepOpenArea); - ads::CAutoHideSideBar* sideTabBar(SideBarLocation area) const; + ads::CAutoHideSideBar* autoHideSideBar(SideBarLocation area) const; QList autoHideWidgets() const; QRect contentRect() const; QRect contentRectGlobal() const; diff --git a/sip/DockFocusController.sip b/sip/DockFocusController.sip index a5cf1d162..9a9f5494a 100644 --- a/sip/DockFocusController.sip +++ b/sip/DockFocusController.sip @@ -23,6 +23,7 @@ public: ads::CDockWidget* focusedDockWidget() const; void setDockWidgetTabFocused(ads::CDockWidgetTab* Tab); void clearDockWidgetFocus(ads::CDockWidget* dockWidget); + void setDockWidgetTabPressed(bool Value); public slots: void setDockWidgetFocused(ads::CDockWidget* focusedNow); diff --git a/sip/DockManager.sip b/sip/DockManager.sip index 5abc9b5f8..8ece9c6db 100644 --- a/sip/DockManager.sip +++ b/sip/DockManager.sip @@ -172,6 +172,8 @@ public: FloatingContainerForceNativeTitleBar, FloatingContainerForceQWidgetTitleBar, MiddleMouseButtonClosesTab, + DisableTabTextEliding, + ShowTabTextOnlyForActiveTab, DefaultDockAreaButtons, DefaultBaseConfig, DefaultOpaqueConfig, @@ -187,6 +189,9 @@ public: AutoHideButtonCheckable, AutoHideSideBarsIconOnly, AutoHideShowOnMouseOver, + AutoHideCloseButtonCollapsesDock, + AutoHideHasCloseButton, + AutoHideHasMinimizeButton, DefaultAutoHideConfig, }; typedef QFlags AutoHideFlags; @@ -237,14 +242,22 @@ public: QMenu* viewMenu() const; void setViewMenuInsertionOrder(ads::CDockManager::eViewMenuInsertionOrder Order); bool isRestoringState() const; + bool isLeavingMinimizedState() const; static int startDragDistance(); ads::CDockWidget* focusedDockWidget() const; QList splitterSizes(ads::CDockAreaWidget *ContainedArea) const; void setSplitterSizes(ads::CDockAreaWidget *ContainedArea, const QList& sizes); static void setFloatingContainersTitle(const QString& Title); static QString floatingContainersTitle(); + void setDockWidgetToolBarStyle(Qt::ToolButtonStyle Style, ads::CDockWidget::eState State); + Qt::ToolButtonStyle dockWidgetToolBarStyle(ads::CDockWidget::eState State) const; + void setDockWidgetToolBarIconSize(const QSize& IconSize, ads::CDockWidget::eState State); + QSize dockWidgetToolBarIconSize(ads::CDockWidget::eState State) const; + ads::CDockWidget::DockWidgetFeatures globallyLockedDockWidgetFeatures() const; + void lockDockWidgetFeaturesGlobally(ads::CDockWidget::DockWidgetFeatures Features = ads::CDockWidget::GloballyLockableFeatures); public slots: + void endLeavingMinimizedState(); void openPerspective(const QString& PerspectiveName); void setDockWidgetFocused(ads::CDockWidget* DockWidget); diff --git a/sip/DockOverlay.sip b/sip/DockOverlay.sip index 158880015..feb6d45b7 100644 --- a/sip/DockOverlay.sip +++ b/sip/DockOverlay.sip @@ -22,8 +22,10 @@ public: CDockOverlay(QWidget* parent /TransferThis/, eMode Mode = ads::CDockOverlay::ModeDockAreaOverlay); virtual ~CDockOverlay(); void setAllowedAreas(ads::DockWidgetAreas areas); + void setAllowedArea(ads::DockWidgetArea area, bool Enable); ads::DockWidgetAreas allowedAreas() const; ads::DockWidgetArea dropAreaUnderCursor() const; + int tabIndexUnderCursor() const; ads::DockWidgetArea visibleDropAreaUnderCursor() const; ads::DockWidgetArea showOverlay(QWidget* target); void hideOverlay(); diff --git a/sip/DockWidget.sip b/sip/DockWidget.sip index d4289128a..c19656114 100644 --- a/sip/DockWidget.sip +++ b/sip/DockWidget.sip @@ -39,6 +39,7 @@ public: DefaultDockWidgetFeatures, AllDockWidgetFeatures, DockWidgetAlwaysCloseAndDelete, + GloballyLockableFeatures, NoDockWidgetFeatures }; typedef QFlags DockWidgetFeatures; @@ -49,6 +50,12 @@ public: StateDocked, StateFloating }; + + enum eToolBarStyleSource + { + ToolBarStyleFromDockManager, + ToolBarStyleFromDockWidget + }; enum eInsertMode { @@ -60,7 +67,9 @@ public: enum eMinimumSizeHintMode { MinimumSizeHintFromDockWidget, - MinimumSizeHintFromContent + MinimumSizeHintFromContent, + MinimumSizeHintFromDockWidgetMinimumSize, + MinimumSizeHintFromContentMinimumSize, }; enum eToggleViewActionMode @@ -70,7 +79,7 @@ public: }; - CDockWidget(const QString &title, QWidget* parent /TransferThis/ = 0); + CDockWidget(const QString &title, QWidget* parent /TransferThis/ = Q_NULLPTR); virtual ~CDockWidget(); virtual QSize minimumSizeHint() const; void setWidget(QWidget* widget /Transfer/, ads::CDockWidget::eInsertMode InsertMode = AutoScrollArea); @@ -80,6 +89,7 @@ public: void setFeatures(ads::CDockWidget::DockWidgetFeatures features); void setFeature(ads::CDockWidget::DockWidgetFeature flag, bool on); ads::CDockWidget::DockWidgetFeatures features() const; + void notifyFeaturesChanged(); ads::CDockManager* dockManager() const; ads::CDockContainerWidget* dockContainer() const; ads::CFloatingDockContainer* floatingDockContainer() const; @@ -88,18 +98,23 @@ public: void setSideTabWidget(ads::CAutoHideTab* SideTab /Transfer/) const; bool isAutoHide() const; ads::CAutoHideDockContainer* autoHideDockContainer() const; + ads::SideBarLocation autoHideLocation() const; bool isFloating() const; bool isInFloatingContainer() const; bool isClosed() const; QAction* toggleViewAction() const; + void setToggleViewAction(QAction* action); void setToggleViewActionMode(ads::CDockWidget::eToggleViewActionMode Mode); void setMinimumSizeHintMode(ads::CDockWidget::eMinimumSizeHintMode Mode); + ads::CDockWidget::eMinimumSizeHintMode minimumSizeHintMode() const; bool isCentralWidget() const; void setIcon(const QIcon& Icon); QIcon icon() const; QToolBar* toolBar() const; QToolBar* createDefaultToolBar(); void setToolBar(QToolBar* ToolBar /Transfer/ ); + void setToolBarStyleSource(ads::CDockWidget::eToolBarStyleSource Source); + ads::CDockWidget::eToolBarStyleSource toolBarStyleSource() const; void setToolBarStyle(Qt::ToolButtonStyle Style, ads::CDockWidget::eState State); Qt::ToolButtonStyle toolBarStyle(ads::CDockWidget::eState State) const; void setToolBarIconSize(const QSize& IconSize, ads::CDockWidget::eState State); @@ -122,9 +137,10 @@ public slots: void setFloating(); void deleteDockWidget(); void closeDockWidget(); + void requestCloseDockWidget(); void showFullScreen(); void showNormal(); - void setAutoHide(bool Enable, ads::SideBarLocation Location = ads::SideBarNone); + void setAutoHide(bool Enable, ads::SideBarLocation Location = ads::SideBarNone, int TabIndex = -1); void toggleAutoHide(ads::SideBarLocation Location = ads::SideBarNone); signals: diff --git a/sip/ElidingLabel.sip b/sip/ElidingLabel.sip index 7cd47b189..57fef9c18 100644 --- a/sip/ElidingLabel.sip +++ b/sip/ElidingLabel.sip @@ -17,8 +17,8 @@ protected: virtual void mouseDoubleClickEvent( QMouseEvent *ev ); public: - CElidingLabel(QWidget* parent /TransferThis/ = 0, Qt::WindowFlags f = 0); - CElidingLabel(const QString& text, QWidget* parent /TransferThis/ = 0, Qt::WindowFlags f = 0); + CElidingLabel(QWidget* parent /TransferThis/ = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags ()); + CElidingLabel(const QString& text, QWidget* parent /TransferThis/ = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags ()); virtual ~CElidingLabel(); Qt::TextElideMode elideMode() const; void setElideMode(Qt::TextElideMode mode); diff --git a/sip/FloatingDockContainer.sip b/sip/FloatingDockContainer.sip index b36f4ce13..79ed06e38 100644 --- a/sip/FloatingDockContainer.sip +++ b/sip/FloatingDockContainer.sip @@ -44,6 +44,7 @@ protected: void startDragging(const QPoint& DragStartMousePos, const QSize& Size, QWidget* MouseEventHandler); virtual void finishDragging(); + void deleteContent(); void initFloatingGeometry(const QPoint& DragStartMousePos, const QSize& Size); void moveFloating(); bool restoreState(ads::CDockingStateReader& Stream, bool Testing); @@ -82,6 +83,7 @@ public: bool hasTopLevelDockWidget() const; ads::CDockWidget* topLevelDockWidget() const; QList dockWidgets() const; + void finishDropOperation(); %If (WS_X11) void onMaximizeRequest(); diff --git a/sip/FloatingDragPreview.sip b/sip/FloatingDragPreview.sip index 0dfc5023c..ad5be7133 100644 --- a/sip/FloatingDragPreview.sip +++ b/sip/FloatingDragPreview.sip @@ -29,7 +29,7 @@ public: // implements IFloatingWidget ads::eDragState DragState, QWidget* MouseEventHandler); virtual void moveFloating(); virtual void finishDragging(); - void cleanupAutoHideContainerWidget(); + void cleanupAutoHideContainerWidget(ads::DockWidgetArea ContainerDropArea); signals: void draggingCanceled(); diff --git a/sip/ads.sip b/sip/ads.sip index 70a252ae8..28c2695f3 100644 --- a/sip/ads.sip +++ b/sip/ads.sip @@ -1,4 +1,6 @@ %Module(name=PyQtAds, call_super_init=True, keyword_arguments="Optional", use_limited_api=True) +%HideNamespace(name=ads) + %Import QtCore/QtCoremod.sip %DefaultSupertype sip.simplewrapper diff --git a/sip/ads_globals.sip b/sip/ads_globals.sip index 62eb67421..719b9306e 100644 --- a/sip/ads_globals.sip +++ b/sip/ads_globals.sip @@ -50,19 +50,31 @@ namespace ads TopDockWidgetArea, BottomDockWidgetArea, CenterDockWidgetArea, + LeftAutoHideArea, + RightAutoHideArea, + TopAutoHideArea, + BottomAutoHideArea, InvalidDockWidgetArea, OuterDockAreas, + AutoHideDockAreas, AllDockAreas }; typedef QFlags DockWidgetAreas; + enum eTabIndex + { + TabDefaultInsertIndex, + TabInvalidIndex + }; + enum TitleBarButton { TitleBarButtonTabsMenu, TitleBarButtonUndock, TitleBarButtonClose, - TitleBarButtonAutoHide + TitleBarButtonAutoHide, + TitleBarButtonMinimize }; enum eDragState @@ -115,6 +127,9 @@ namespace ads int insertOffset() const; }; ads::internal::CDockInsertParam dockAreaInsertParameters(ads::DockWidgetArea Area); + ads::SideBarLocation toSideBarLocation(ads::DockWidgetArea Area); + bool isHorizontalSideBarLocation(ads::SideBarLocation Location); + bool isSideBarArea(ads::DockWidgetArea Area); SIP_PYOBJECT findParent(SIP_PYTYPE type, const QWidget *w) const /TypeHint="QObject"/; %MethodCode diff --git a/src/AutoHideDockContainer.cpp b/src/AutoHideDockContainer.cpp index 3762eec23..9f13ee0f2 100644 --- a/src/AutoHideDockContainer.cpp +++ b/src/AutoHideDockContainer.cpp @@ -117,12 +117,28 @@ struct AutoHideDockContainerPrivate CResizeHandle* ResizeHandle = nullptr; QSize Size; // creates invalid size QPointer SideTab; + QSize SizeCache; /** * Private data constructor */ AutoHideDockContainerPrivate(CAutoHideDockContainer *_public); + /** + * Convenience function to ease access to dock manager components factory + */ + QSharedPointer componentsFactory() const + { + if (!DockWidget || !DockWidget->dockManager()) + { + return CDockComponentsFactory::factory(); + } + else + { + return DockWidget->dockManager()->componentsFactory(); + } + } + /** * Convenience function to get a dock widget area */ @@ -198,7 +214,7 @@ CAutoHideDockContainer::CAutoHideDockContainer(CDockWidget* DockWidget, SideBarL { hide(); // auto hide dock container is initially always hidden d->SideTabBarArea = area; - d->SideTab = componentsFactory()->createDockWidgetSideTab(nullptr); + d->SideTab = d->componentsFactory()->createDockWidgetSideTab(nullptr); connect(d->SideTab, &CAutoHideTab::pressed, this, &CAutoHideDockContainer::toggleCollapseState); d->DockArea = new CDockAreaWidget(DockWidget->dockManager(), parent); d->DockArea->setObjectName("autoHideDockArea"); @@ -215,6 +231,7 @@ CAutoHideDockContainer::CAutoHideDockContainer(CDockWidget* DockWidget, SideBarL bool OpaqueResize = CDockManager::testConfigFlag(CDockManager::OpaqueSplitterResize); d->ResizeHandle->setOpaqueResize(OpaqueResize); d->Size = d->DockArea->size(); + d->SizeCache = DockWidget->size(); addDockWidget(DockWidget); parent->registerAutoHideWidget(this); @@ -237,7 +254,6 @@ void CAutoHideDockContainer::updateSize() } auto rect = dockContainerParent->contentRect(); - switch (sideBarLocation()) { case SideBarLocation::SideBarTop: @@ -271,6 +287,15 @@ void CAutoHideDockContainer::updateSize() default: break; } + + if (orientation() == Qt::Horizontal) + { + d->SizeCache.setHeight(this->height()); + } + else + { + d->SizeCache.setWidth(this->width()); + } } //============================================================================ @@ -294,7 +319,7 @@ CAutoHideDockContainer::~CAutoHideDockContainer() } //============================================================================ -CAutoHideSideBar* CAutoHideDockContainer::sideBar() const +CAutoHideSideBar* CAutoHideDockContainer::autoHideSideBar() const { if (d->SideTab) { @@ -303,7 +328,7 @@ CAutoHideSideBar* CAutoHideDockContainer::sideBar() const else { auto DockContainer = dockContainer(); - return DockContainer ? DockContainer->sideTabBar(d->SideTabBarArea) : nullptr; + return DockContainer ? DockContainer->autoHideSideBar(d->SideTabBarArea) : nullptr; } } @@ -344,6 +369,10 @@ void CAutoHideDockContainer::addDockWidget(CDockWidget* DockWidget) } d->DockArea->addDockWidget(DockWidget); updateSize(); + // The dock area is not visible and will not update the size when updateSize() + // is called for this auto hide container. Therefore we explicitly resize + // it here. As soon as it will become visible, it will get the right size + d->DockArea->resize(size()); } @@ -396,8 +425,8 @@ void CAutoHideDockContainer::cleanupAndDelete() const auto dockWidget = d->DockWidget; if (dockWidget) { - auto SideTab = d->SideTab; + dockWidget->setSideTabWidget(nullptr); SideTab->removeFromSideBar(); SideTab->setParent(nullptr); SideTab->hide(); @@ -573,8 +602,12 @@ bool CAutoHideDockContainer::eventFilter(QObject* watched, QEvent* event) return Super::eventFilter(watched, event); } - // user clicked into container - collapse the auto hide widget - collapseView(true); + // user clicked outside of autohide container - collapse the auto hide widget + if (CDockManager::testAutoHideConfigFlag( + CDockManager::AutoHideCloseOnOutsideMouseClick)) + { + collapseView(true); + } } else if (event->type() == internal::FloatingWidgetDragStartEvent) { @@ -643,5 +676,63 @@ bool CAutoHideDockContainer::event(QEvent* event) return Super::event(event); } +//============================================================================ +void CAutoHideDockContainer::dragLeaveEvent(QDragLeaveEvent*) +{ + if (CDockManager::testAutoHideConfigFlag(CDockManager::AutoHideOpenOnDragHover)) + { + collapseView(true); + } +} + +//============================================================================ +Qt::Orientation CAutoHideDockContainer::orientation() const +{ + return ads::internal::isHorizontalSideBarLocation(d->SideTabBarArea) + ? Qt::Horizontal : Qt::Vertical; +} + + +//============================================================================ +void CAutoHideDockContainer::resetToInitialDockWidgetSize() +{ + if (orientation() == Qt::Horizontal) + { + setSize(d->SizeCache.height()); + } + else + { + setSize(d->SizeCache.width()); + } } + +//============================================================================ +void CAutoHideDockContainer::moveToNewSideBarLocation(SideBarLocation NewSideBarLocation, + int TabIndex) +{ + if (NewSideBarLocation == sideBarLocation() && TabIndex == this->tabIndex()) + { + return; + } + + auto OldOrientation = orientation(); + auto SideBar = dockContainer()->autoHideSideBar(NewSideBarLocation); + SideBar->addAutoHideWidget(this, TabIndex); + // If we move a horizontal auto hide container to a vertical position + // then we resize it to the original dock widget size, to avoid + // an extremely stretched dock widget after insertion + if (SideBar->orientation() != OldOrientation) + { + resetToInitialDockWidgetSize(); + } +} + + +//============================================================================ +int CAutoHideDockContainer::tabIndex() const +{ + return d->SideTab->tabIndex(); +} + +} diff --git a/src/AutoHideDockContainer.h b/src/AutoHideDockContainer.h index d042b40b1..e12dbbaca 100644 --- a/src/AutoHideDockContainer.h +++ b/src/AutoHideDockContainer.h @@ -34,7 +34,7 @@ #include #include "AutoHideTab.h" -class QXmlStreamWriter; +QT_FORWARD_DECLARE_CLASS(QXmlStreamWriter) namespace ads { @@ -65,6 +65,7 @@ class ADS_EXPORT CAutoHideDockContainer : public QFrame virtual void resizeEvent(QResizeEvent* event) override; virtual void leaveEvent(QEvent *event) override; virtual bool event(QEvent* event) override; + virtual void dragLeaveEvent(QDragLeaveEvent* ev) override; /** * Updates the size considering the size limits and the resize margins @@ -91,9 +92,9 @@ class ADS_EXPORT CAutoHideDockContainer : public QFrame virtual ~CAutoHideDockContainer(); /** - * Get's the side tab bar + * Gets the side tab bar */ - CAutoHideSideBar* sideBar() const; + CAutoHideSideBar* autoHideSideBar() const; /** * Returns the side tab @@ -101,10 +102,15 @@ class ADS_EXPORT CAutoHideDockContainer : public QFrame CAutoHideTab* autoHideTab() const; /** - * Get's the dock widget in this dock container + * Gets the dock widget in this dock container */ CDockWidget* dockWidget() const; + /** + * Returns the index of this container in the sidebar + */ + int tabIndex() const; + /** * Adds a dock widget and removes the previous dock widget */ @@ -134,7 +140,7 @@ class ADS_EXPORT CAutoHideDockContainer : public QFrame /** * Moves the contents to the parent container widget - * Used before removing this Auto Hide dock container + * Used before removing this Auto Hide dock container */ void moveContentsToParent(); @@ -162,10 +168,34 @@ class ADS_EXPORT CAutoHideDockContainer : public QFrame /** * Use this instead of resize. - * Depending on the sidebar location this will set the width or heigth + * Depending on the sidebar location this will set the width or height * of this auto hide container. */ void setSize(int Size); + + /** + * Resets the width or height to the initial dock widget size dependinng on + * the orientation. + * If the orientation is Qt::Horizontal, then the height is reset to + * the initial size and if orientation is Qt::Vertical, then the width is + * reset to the initial size + */ + void resetToInitialDockWidgetSize(); + + /** + * Returns orientation of this container. + * Left and right containers have a Qt::Vertical orientation and top / bottom + * containers have a Qt::Horizontal orientation. + * The function returns the orientation of the corresponding auto hide + * side bar. + */ + Qt::Orientation orientation() const; + + /** + * Removes the AutoHide container from the current side bar and adds + * it to the new side bar given in SideBarLocation + */ + void moveToNewSideBarLocation(SideBarLocation SideBarLocation, int TabIndex = -1); }; } // namespace ads diff --git a/src/AutoHideSideBar.cpp b/src/AutoHideSideBar.cpp index f0aa03d46..7dea2fe3e 100644 --- a/src/AutoHideSideBar.cpp +++ b/src/AutoHideSideBar.cpp @@ -196,6 +196,7 @@ void CAutoHideSideBar::insertTab(int Index, CAutoHideTab* SideTab) { SideTab->setSideBar(this); SideTab->installEventFilter(this); + // Default insertion is append if (Index < 0) { d->TabsLayout->insertWidget(d->TabsLayout->count() - 1, SideTab); @@ -233,12 +234,25 @@ void CAutoHideSideBar::removeAutoHideWidget(CAutoHideDockContainer* AutoHideWidg } //============================================================================ -void CAutoHideSideBar::addAutoHideWidget(CAutoHideDockContainer* AutoHideWidget) +void CAutoHideSideBar::addAutoHideWidget(CAutoHideDockContainer* AutoHideWidget, + int TabIndex) { auto SideBar = AutoHideWidget->autoHideTab()->sideBar(); if (SideBar == this) { - return; + // If we move to the same tab index or if we insert before the next + // tab index, then we will end at the same tab position and can leave + if (AutoHideWidget->tabIndex() == TabIndex || (AutoHideWidget->tabIndex() + 1) == TabIndex) + { + return; + } + + // We remove this auto hide widget from the sidebar in the code below + // and therefore need to correct the TabIndex here + if (AutoHideWidget->tabIndex() < TabIndex) + { + --TabIndex; + } } if (SideBar) @@ -248,7 +262,7 @@ void CAutoHideSideBar::addAutoHideWidget(CAutoHideDockContainer* AutoHideWidget) AutoHideWidget->setParent(d->ContainerWidget); AutoHideWidget->setSideBarLocation(d->SideTabArea); d->ContainerWidget->registerAutoHideWidget(AutoHideWidget); - insertTab(-1, AutoHideWidget->autoHideTab()); + insertTab(TabIndex, AutoHideWidget->autoHideTab()); } @@ -302,14 +316,14 @@ Qt::Orientation CAutoHideSideBar::orientation() const //============================================================================ -CAutoHideTab* CAutoHideSideBar::tabAt(int index) const +CAutoHideTab* CAutoHideSideBar::tab(int index) const { return qobject_cast(d->TabsLayout->itemAt(index)->widget()); } //============================================================================ -int CAutoHideSideBar::tabCount() const +int CAutoHideSideBar::count() const { return d->TabsLayout->count() - 1; } @@ -318,17 +332,17 @@ int CAutoHideSideBar::tabCount() const //============================================================================ int CAutoHideSideBar::visibleTabCount() const { - int count = 0; + int VisibleTabCount = 0; auto ParentWidget = parentWidget(); - for (auto i = 0; i < tabCount(); i++) + for (auto i = 0; i < count(); i++) { - if (tabAt(i)->isVisibleTo(ParentWidget)) + if (tab(i)->isVisibleTo(ParentWidget)) { - count++; + VisibleTabCount++; } } - return count; + return VisibleTabCount; } @@ -336,9 +350,9 @@ int CAutoHideSideBar::visibleTabCount() const bool CAutoHideSideBar::hasVisibleTabs() const { auto ParentWidget = parentWidget(); - for (auto i = 0; i < tabCount(); i++) + for (auto i = 0; i < count(); i++) { - if (tabAt(i)->isVisibleTo(ParentWidget)) + if (tab(i)->isVisibleTo(ParentWidget)) { return true; } @@ -348,6 +362,21 @@ bool CAutoHideSideBar::hasVisibleTabs() const } +//============================================================================ +int CAutoHideSideBar::indexOfTab(const CAutoHideTab& Tab) const +{ + for (auto i = 0; i < count(); i++) + { + if (tab(i) == &Tab) + { + return i; + } + } + + return -1; +} + + //============================================================================ SideBarLocation CAutoHideSideBar::sideBarLocation() const { @@ -358,18 +387,18 @@ SideBarLocation CAutoHideSideBar::sideBarLocation() const //============================================================================ void CAutoHideSideBar::saveState(QXmlStreamWriter& s) const { - if (!tabCount()) + if (!count()) { return; } s.writeStartElement("SideBar"); s.writeAttribute("Area", QString::number(sideBarLocation())); - s.writeAttribute("Tabs", QString::number(tabCount())); + s.writeAttribute("Tabs", QString::number(count())); - for (auto i = 0; i < tabCount(); ++i) + for (auto i = 0; i < count(); ++i) { - auto Tab = tabAt(i); + auto Tab = tab(i); if (!Tab) { continue; @@ -385,7 +414,14 @@ void CAutoHideSideBar::saveState(QXmlStreamWriter& s) const QSize CAutoHideSideBar::minimumSizeHint() const { QSize Size = sizeHint(); - Size.setWidth(10); + if (d->isHorizontal()) + { + Size.setWidth(0); + } + else + { + Size.setHeight(0); + } return Size; } @@ -417,5 +453,56 @@ CDockContainerWidget* CAutoHideSideBar::dockContainer() const return d->ContainerWidget; } + +//=========================================================================== +int CAutoHideSideBar::tabAt(const QPoint& Pos) const +{ + if (!isVisible()) + { + return TabInvalidIndex; + } + + if (orientation() == Qt::Horizontal) + { + if (Pos.x() < tab(0)->geometry().x()) + { + return -1; + } + } + else + { + if (Pos.y() < tab(0)->geometry().y()) + { + return -1; + } + } + + + for (int i = 0; i < count(); ++i) + { + if (tab(i)->geometry().contains(Pos)) + { + return i; + } + } + + return count(); +} + + +//=========================================================================== +int CAutoHideSideBar::tabInsertIndexAt(const QPoint& Pos) const +{ + int Index = tabAt(Pos); + if (Index == TabInvalidIndex) + { + return TabDefaultInsertIndex; + } + else + { + return (Index < 0) ? 0 : Index; + } +} + } // namespace ads diff --git a/src/AutoHideSideBar.h b/src/AutoHideSideBar.h index 1b0cab5f8..350568253 100644 --- a/src/AutoHideSideBar.h +++ b/src/AutoHideSideBar.h @@ -33,7 +33,7 @@ #include "ads_globals.h" #include "AutoHideTab.h" -class QXmlStreamWriter; +QT_FORWARD_DECLARE_CLASS(QXmlStreamWriter) namespace ads { @@ -117,7 +117,7 @@ class ADS_EXPORT CAutoHideSideBar : public QScrollArea * If the AutoHideWidget is in another sidebar, then it will be removed * from this sidebar. */ - void addAutoHideWidget(CAutoHideDockContainer* AutoHideWidget); + void addAutoHideWidget(CAutoHideDockContainer* AutoHideWidget, int Index = TabDefaultInsertIndex); /** * Returns orientation of side tab. @@ -125,14 +125,32 @@ class ADS_EXPORT CAutoHideSideBar : public QScrollArea Qt::Orientation orientation() const; /* - * get the side tab widget at position, returns nullptr if it's out of bounds + * Get the side tab widget at position, returns nullptr if it's out of bounds */ - CAutoHideTab* tabAt(int index) const; + CAutoHideTab* tab(int index) const; + + /** + * Returns the tab at the given position. + * Returns -1 if the position is left of the first tab and count() if the + * position is right of the last tab. Returns InvalidTabIndex (-2) to + * indicate an invalid value. + */ + int tabAt(const QPoint& Pos) const; + + /** + * Returns the tab insertion index for the given mouse cursor position + */ + int tabInsertIndexAt(const QPoint& Pos) const; + + /** + * Returns the index of the given tab + */ + int indexOfTab(const CAutoHideTab& Tab) const; /* * Gets the count of the tab widgets */ - int tabCount() const; + int count() const; /** * Returns the number of visible tabs to its parent widget. @@ -155,7 +173,7 @@ class ADS_EXPORT CAutoHideSideBar : public QScrollArea /** * 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 + * area because even if the scrollbars are invisible, the required space * is reserved in the minimumSizeHint(). This override simply returns * sizeHint(); */ diff --git a/src/AutoHideTab.cpp b/src/AutoHideTab.cpp index a6f7cc4ad..f6f3ad93c 100644 --- a/src/AutoHideTab.cpp +++ b/src/AutoHideTab.cpp @@ -32,15 +32,22 @@ #include #include #include +#include +#include +#include #include "AutoHideDockContainer.h" #include "AutoHideSideBar.h" #include "DockAreaWidget.h" #include "DockManager.h" #include "DockWidget.h" +#include "FloatingDragPreview.h" +#include "DockOverlay.h" +#include "ads_globals.h" namespace ads { + /** * Private data class of CDockWidgetTab class (pimpl) */ @@ -51,6 +58,13 @@ struct AutoHideTabPrivate CAutoHideSideBar* SideBar = nullptr; Qt::Orientation Orientation{Qt::Vertical}; QElapsedTimer TimeSinceHoverMousePress; + QTimer DragOverTimer; + bool MousePressed = false; + eDragState DragState = DraggingInactive; + QPoint GlobalDragStartMousePosition; + QPoint DragStartMousePosition; + IFloatingWidget* FloatingWidget = nullptr; + Qt::Orientation DragStartOrientation; /** * Private data constructor @@ -82,6 +96,55 @@ struct AutoHideTabPrivate DockContainer->handleAutoHideWidgetEvent(event, _this); } } + + /** + * 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(internal::LocationProperty, Location); + QObject::connect(Action, &QAction::triggered, _this, &CAutoHideTab::onAutoHideToActionClicked); + Action->setEnabled(Location != _this->sideBarLocation()); + return Action; + } + + /** + * Test function for current drag state + */ + bool isDraggingState(eDragState dragState) const + { + return this->DragState == dragState; + } + + /** + * Saves the drag start position in global and local coordinates + */ + void saveDragStartMousePosition(const QPoint& GlobalPos) + { + GlobalDragStartMousePosition = GlobalPos; + DragStartMousePosition = _this->mapFromGlobal(GlobalPos); + } + + /** + * Starts floating of the dock widget that belongs to this title bar + * Returns true, if floating has been started and false if floating + * is not possible for any reason + */ + bool startFloating(eDragState DraggingState = DraggingFloatingWidget); + + template + IFloatingWidget* createFloatingWidget(T* Widget) + { + auto w = new CFloatingDragPreview(Widget); + _this->connect(w, &CFloatingDragPreview::draggingCanceled, [this]() + { + DragState = DraggingInactive; + }); + return w; + } }; // struct DockWidgetTabPrivate @@ -110,6 +173,53 @@ void AutoHideTabPrivate::updateOrientation() } +//============================================================================ +bool AutoHideTabPrivate::startFloating(eDragState DraggingState) +{ + auto DockArea = DockWidget->dockAreaWidget(); + ADS_PRINT("isFloating " << dockContainer()->isFloating()); + + ADS_PRINT("startFloating"); + DragState = DraggingState; + IFloatingWidget* FloatingWidget = nullptr; + FloatingWidget = createFloatingWidget(DockArea); + auto Size = DockArea->size(); + auto StartPos = DragStartMousePosition; + auto AutoHideContainer = DockWidget->autoHideDockContainer(); + DragStartOrientation = AutoHideContainer->orientation(); + switch (SideBar->sideBarLocation()) + { + case SideBarLeft: + StartPos.rx() = AutoHideContainer->rect().left() + 10; + break; + + case SideBarRight: + StartPos.rx() = AutoHideContainer->rect().right() - 10; + break; + + case SideBarTop: + StartPos.ry() = AutoHideContainer->rect().top() + 10; + break; + + case SideBarBottom: + StartPos.ry() = AutoHideContainer->rect().bottom() - 10; + break; + + case SideBarNone: + return false; + } + FloatingWidget->startFloating(StartPos, Size, DraggingFloatingWidget, _this); + auto DockManager = DockWidget->dockManager(); + auto Overlay = DockManager->containerOverlay(); + Overlay->setAllowedAreas(OuterDockAreas); + this->FloatingWidget = FloatingWidget; + qApp->postEvent(DockWidget, new QEvent((QEvent::Type)internal::DockedWidgetDragStartEvent)); + + return true; +} + + + //============================================================================ void CAutoHideTab::setSideBar(CAutoHideSideBar* SideTabBar) { @@ -146,6 +256,14 @@ CAutoHideTab::CAutoHideTab(QWidget* parent) : { setAttribute(Qt::WA_NoMousePropagation); setFocusPolicy(Qt::NoFocus); + if (CDockManager::testAutoHideConfigFlag(CDockManager::AutoHideOpenOnDragHover)) { + setAcceptDrops(true); + } + + d->DragOverTimer.setInterval(CDockManager::configParam( + CDockManager::AutoHideOpenOnDragHoverDelay_ms, 500).toInt()); + d->DragOverTimer.setSingleShot(true); + connect(&d->DragOverTimer, &QTimer::timeout, this, &CAutoHideTab::onDragHoverDelayExpired); } @@ -249,33 +367,254 @@ bool CAutoHideTab::event(QEvent* event) 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(); } + +//============================================================================ +void CAutoHideTab::contextMenuEvent(QContextMenuEvent* ev) +{ + ev->accept(); + d->saveDragStartMousePosition(ev->globalPos()); + + const bool isFloatable = d->DockWidget->features().testFlag(CDockWidget::DockWidgetFloatable); + QAction* Action; + QMenu Menu(this); + + Action = Menu.addAction(tr("Detach"), this, SLOT(setDockWidgetFloating())); + Action->setEnabled(isFloatable); + 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); + + Action = Menu.addAction(tr("Unpin (Dock)"), this, SLOT(unpinDockWidget())); + Menu.addSeparator(); + Action = Menu.addAction(tr("Close"), this, SLOT(requestCloseDockWidget())); + Action->setEnabled(d->DockWidget->features().testFlag(CDockWidget::DockWidgetClosable)); + + Menu.exec(ev->globalPos()); +} + + +//============================================================================ +void CAutoHideTab::setDockWidgetFloating() +{ + d->DockWidget->setFloating(); +} + + +//============================================================================ +void CAutoHideTab::unpinDockWidget() +{ + d->DockWidget->setAutoHide(false); +} + + +//=========================================================================== +void CAutoHideTab::onAutoHideToActionClicked() +{ + int Location = sender()->property(internal::LocationProperty).toInt(); + d->DockWidget->setAutoHide(true, (SideBarLocation)Location); +} + + +//============================================================================ +void CAutoHideTab::mousePressEvent(QMouseEvent* ev) +{ + // 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 (!ev->spontaneous()) + { + d->TimeSinceHoverMousePress.restart(); + d->forwardEventToDockContainer(ev); + } + else if (d->TimeSinceHoverMousePress.hasExpired(500)) + { + d->forwardEventToDockContainer(ev); + } + + if (ev->button() == Qt::LeftButton) + { + ev->accept(); + d->MousePressed = true; + d->saveDragStartMousePosition(internal::globalPositionOf(ev)); + d->DragState = DraggingMousePressed; + } + Super::mousePressEvent(ev); +} + + + +//============================================================================ +void CAutoHideTab::mouseReleaseEvent(QMouseEvent* ev) +{ + if (ev->button() == Qt::LeftButton) + { + d->MousePressed = false; + auto CurrentDragState = d->DragState; + d->GlobalDragStartMousePosition = QPoint(); + d->DragStartMousePosition = QPoint(); + d->DragState = DraggingInactive; + + switch (CurrentDragState) + { + case DraggingTab: + // End of tab moving, emit signal + /*if (d->DockArea) + { + ev->accept(); + Q_EMIT moved(internal::globalPositionOf(ev)); + }*/ + break; + + case DraggingFloatingWidget: + ev->accept(); + d->FloatingWidget->finishDragging(); + if (d->DockWidget->autoHideDockContainer() && d->DragStartOrientation != orientation()) + { + d->DockWidget->autoHideDockContainer()->resetToInitialDockWidgetSize(); + } + break; + + default: + break; // do nothing + } + } + + Super::mouseReleaseEvent(ev); +} + + +//============================================================================ +void CAutoHideTab::mouseMoveEvent(QMouseEvent *ev) +{ + if (!(ev->buttons() & Qt::LeftButton) + || d->isDraggingState(DraggingInactive)) + { + d->DragState = DraggingInactive; + Super::mouseMoveEvent(ev); + return; + } + + // move floating window + if (d->isDraggingState(DraggingFloatingWidget)) + { + d->FloatingWidget->moveFloating(); + Super::mouseMoveEvent(ev); + return; + } + + // move tab + if (d->isDraggingState(DraggingTab)) + { + // Moving the tab is always allowed because it does not mean moving the + // dock widget around + //d->moveTab(ev); + } + + auto MappedPos = mapToParent(ev->pos()); + bool MouseOutsideBar = (MappedPos.x() < 0) + || (MappedPos.x() > parentWidget()->rect().right()); + // Maybe a fixed drag distance is better here ? + int DragDistanceY = qAbs( + d->GlobalDragStartMousePosition.y() + - internal::globalPositionOf(ev).y()); + if (DragDistanceY >= CDockManager::startDragDistance() || MouseOutsideBar) + { + // Floating is only allowed for widgets that are floatable + // We can create the drag preview if the widget is movable. + auto Features = d->DockWidget->features(); + if (Features.testFlag(CDockWidget::DockWidgetFloatable) + || (Features.testFlag(CDockWidget::DockWidgetMovable))) + { + d->startFloating(); + } + return; + } + + Super::mouseMoveEvent(ev); +} + +//============================================================================ +void CAutoHideTab::dragEnterEvent(QDragEnterEvent *ev) +{ + Q_UNUSED(ev); + if (CDockManager::testAutoHideConfigFlag(CDockManager::AutoHideOpenOnDragHover)) + { + d->DragOverTimer.start(); + ev->accept(); + } +} + +//============================================================================ +void CAutoHideTab::dragLeaveEvent(QDragLeaveEvent *ev) +{ + Q_UNUSED(ev); + if (CDockManager::testAutoHideConfigFlag(CDockManager::AutoHideOpenOnDragHover)) + { + d->DragOverTimer.stop(); + } +} + + +//============================================================================ +void CAutoHideTab::requestCloseDockWidget() +{ + d->DockWidget->requestCloseDockWidget(); +} + +//============================================================================ +int CAutoHideTab::tabIndex() const +{ + if (!d->SideBar) + { + return -1; + } + + return d->SideBar->indexOfTab(*this); +} + + +//============================================================================ +void CAutoHideTab::onDragHoverDelayExpired() +{ + static const char* const PropertyId = "ActiveDragOverAutoHideContainer"; + + // First we check if there is an active auto hide container that is visible + // In this case, we collapse it before we open the new one + auto v = d->DockWidget->dockManager()->property(PropertyId); + if (v.isValid()) + { + auto ActiveAutoHideContainer = v.value>(); + if (ActiveAutoHideContainer) + { + ActiveAutoHideContainer->collapseView(true); + } + } + + auto AutoHideContainer = d->DockWidget->autoHideDockContainer(); + AutoHideContainer->collapseView(false); + d->DockWidget->dockManager()->setProperty(PropertyId, + QVariant::fromValue(QPointer(AutoHideContainer))); +} + + } diff --git a/src/AutoHideTab.h b/src/AutoHideTab.h index ca8bb560c..20ee70a91 100644 --- a/src/AutoHideTab.h +++ b/src/AutoHideTab.h @@ -55,7 +55,7 @@ class ADS_EXPORT CAutoHideTab : public CPushButton Q_PROPERTY(bool activeTab READ isActiveTab) Q_PROPERTY(bool iconOnly READ iconOnly) -private: +private: AutoHideTabPrivate* d; ///< private data (pimpl) friend struct AutoHideTabPrivate; friend class CDockWidget; @@ -65,11 +65,20 @@ class ADS_EXPORT CAutoHideTab : public CPushButton friend class CDockContainerWidget; friend DockContainerWidgetPrivate; +private Q_SLOTS: + void onAutoHideToActionClicked(); + void onDragHoverDelayExpired(); protected: void setSideBar(CAutoHideSideBar *SideTabBar); void removeFromSideBar(); virtual bool event(QEvent* event) override; + virtual void contextMenuEvent(QContextMenuEvent* ev) override; + virtual void mousePressEvent(QMouseEvent* ev) override; + virtual void mouseReleaseEvent(QMouseEvent* ev) override; + virtual void mouseMoveEvent(QMouseEvent* ev) override; + virtual void dragEnterEvent(QDragEnterEvent* ev) override; + virtual void dragLeaveEvent(QDragLeaveEvent* ev) override; public: using Super = CPushButton; @@ -133,6 +142,27 @@ class ADS_EXPORT CAutoHideTab : public CPushButton * not in a side bar */ CAutoHideSideBar* sideBar() const; + + /** + * Returns the index of this tab in the sideBar + */ + int tabIndex() const; + +public Q_SLOTS: + /** + * Set the dock widget floating, if it is floatable + */ + void setDockWidgetFloating(); + + /** + * Unpin and dock the auto hide widget + */ + void unpinDockWidget(); + + /** + * Calls the requestCloseDockWidget() function for the assigned dock widget + */ + void requestCloseDockWidget(); }; // class AutoHideTab } // namespace ads diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9c9c9e40e..38b1b6e13 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,15 +1,22 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.16) project(QtAdvancedDockingSystem LANGUAGES CXX VERSION ${VERSION_SHORT}) +include(GNUInstallDirs) if (${QT_VERSION_MAJOR}) message(STATUS "Forced to use Qt version ${QT_VERSION_MAJOR}") find_package(QT NAMES Qt${QT_VERSION_MAJOR} COMPONENTS Core REQUIRED) else() find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED) endif() -find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Widgets REQUIRED) -if (UNIX AND NOT APPLE) - include_directories(${Qt${QT_VERSION_MAJOR}Gui_PRIVATE_INCLUDE_DIRS}) + +set(ads_DEP_LIBS Core Gui Widgets) +if (UNIX AND NOT APPLE AND QT_VERSION_MAJOR STREQUAL "6" AND QT_VERSION_MINOR GREATER 8) + set(ads_DEP_LIBS Core Gui GuiPrivate Widgets) endif() +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS ${ads_DEP_LIBS} REQUIRED) +if (UNIX AND NOT APPLE AND ((QT_VERSION_MAJOR STREQUAL "6" AND QT_VERSION_MINOR GREATER 8) OR QT_VERSION_MAJOR STREQUAL "5")) + include_directories(${Qt${QT_VERSION_MAJOR}Gui_PRIVATE_INCLUDE_DIRS}) +endif() + set(CMAKE_INCLUDE_CURRENT_DIR ON) if(BUILD_STATIC) set(CMAKE_STATIC_LIBRARY_SUFFIX "_static${CMAKE_STATIC_LIBRARY_SUFFIX}") @@ -36,7 +43,7 @@ set(ads_SRCS AutoHideTab.cpp AutoHideDockContainer.cpp PushButton.cpp - ResizeHandle.cpp + ResizeHandle.cpp ads.qrc ) set(ads_HEADERS @@ -62,7 +69,7 @@ set(ads_HEADERS AutoHideTab.h AutoHideDockContainer.h PushButton.h - ResizeHandle.h + ResizeHandle.h ) add_compile_options("$<$:/utf-8>") if (UNIX AND NOT APPLE) @@ -70,7 +77,7 @@ if (UNIX AND NOT APPLE) set(ads_HEADERS linux/FloatingWidgetTitleBar.h ${ads_HEADERS}) endif() -set(library_name "qt${QT_VERSION_MAJOR}advanceddocking") +set(library_name "qtadvanceddocking-qt${QT_VERSION_MAJOR}") if(BUILD_STATIC) add_library(${library_name} STATIC ${ads_SRCS} ${ads_HEADERS}) target_compile_definitions( ${library_name} PUBLIC ADS_STATIC) @@ -84,15 +91,27 @@ add_library(ads::${library_name} ALIAS ${library_name}) target_link_libraries(${library_name} PUBLIC Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Widgets) + +if(UNIX AND NOT APPLE AND QT_VERSION_MAJOR STREQUAL "6") + target_link_libraries(${library_name} PRIVATE Qt6::GuiPrivate) #needed for +endif() + if (UNIX AND NOT APPLE) - target_link_libraries(${library_name} PUBLIC xcb) + if (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") + find_package(X11 REQUIRED) + target_link_libraries(${library_name} PUBLIC X11::xcb) + else() + target_link_libraries(${library_name} PUBLIC xcb) + endif() endif() set_target_properties(${library_name} PROPERTIES AUTOMOC ON AUTORCC ON CXX_EXTENSIONS OFF VERSION ${VERSION_SHORT} + SOVERSION ${VERSION_SONAME} EXPORT_NAME ${library_name} + DEBUG_POSTFIX "d" # 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" @@ -114,36 +133,36 @@ write_basic_package_version_file( COMPATIBILITY SameMajorVersion ) install(FILES ${ads_HEADERS} - DESTINATION include/${library_name} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${library_name} COMPONENT headers ) install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../LICENSE" "${CMAKE_CURRENT_SOURCE_DIR}/../gnu-lgpl-v2.1.md" - DESTINATION license/ads + DESTINATION share/ads/license COMPONENT license ) install(TARGETS ${library_name} EXPORT adsTargets - RUNTIME DESTINATION bin - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib - INCLUDES DESTINATION include/${library_name} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${library_name} ) install(EXPORT adsTargets FILE adsTargets.cmake NAMESPACE ads:: - DESTINATION lib/cmake/${library_name} + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${library_name} ) install(FILES qtadvanceddockingConfig.cmake RENAME ${library_name}Config.cmake - DESTINATION lib/cmake/${library_name} + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${library_name} ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${library_name}ConfigVersion.cmake" - DESTINATION lib/cmake/${library_name} + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${library_name} ) target_include_directories(${library_name} PUBLIC - $ + $ $ ) diff --git a/src/DockAreaTabBar.cpp b/src/DockAreaTabBar.cpp index 48e3c22e5..8de254c40 100644 --- a/src/DockAreaTabBar.cpp +++ b/src/DockAreaTabBar.cpp @@ -111,8 +111,9 @@ void DockAreaTabBarPrivate::updateTabs() // Sometimes the synchronous calculation of the rectangular area fails // Therefore we use QTimer::singleShot here to execute the call // within the event loop - see #520 - QTimer::singleShot(0, [&, TabWidget]{ - _this->ensureWidgetVisible(TabWidget); + QTimer::singleShot(0, _this, [&, TabWidget] + { + _this->ensureWidgetVisible(TabWidget); }); } else @@ -159,19 +160,9 @@ CDockAreaTabBar::~CDockAreaTabBar() //============================================================================ void CDockAreaTabBar::wheelEvent(QWheelEvent* Event) { - Event->accept(); - const int direction = Event->angleDelta().y(); - if (direction < 0) - { - horizontalScrollBar()->setValue(horizontalScrollBar()->value() + 20); - } - else - { - horizontalScrollBar()->setValue(horizontalScrollBar()->value() - 20); - } + QCoreApplication::sendEvent(horizontalScrollBar(), Event); } - //============================================================================ void CDockAreaTabBar::setCurrentIndex(int index) { @@ -300,7 +291,7 @@ int CDockAreaTabBar::currentIndex() const //=========================================================================== CDockWidgetTab* CDockAreaTabBar::currentTab() const { - if (d->CurrentIndex < 0) + if (d->CurrentIndex < 0 || d->CurrentIndex >= d->TabsLayout->count()) { return nullptr; } @@ -343,27 +334,13 @@ void CDockAreaTabBar::onTabCloseRequested() void CDockAreaTabBar::onCloseOtherTabsRequested() { auto Sender = qobject_cast(sender()); - for (int i = 0; i < count(); ++i) - { - auto Tab = tab(i); - if (Tab->isClosable() && !Tab->isHidden() && Tab != Sender) - { - // If the dock widget is deleted with the closeTab() call, its tab - // it will no longer be in the layout, and thus the index needs to - // be updated to not skip any tabs - int Offset = Tab->dockWidget()->features().testFlag( - CDockWidget::DockWidgetDeleteOnClose) ? 1 : 0; - closeTab(i); - - // If the the dock widget blocks closing, i.e. if the flag - // CustomCloseHandling is set, and the dock widget is still open, - // then we do not need to correct the index - if (Tab->dockWidget()->isClosed()) - { - i -= Offset; - } - } - } + + for (int i = count() - 1; i >= 0; --i) { + auto Tab = tab(i); + if (Tab->isClosable() && !Tab->isHidden() && Tab != Sender) { + closeTab(i); + } + } } @@ -389,15 +366,18 @@ void CDockAreaTabBar::onTabWidgetMoved(const QPoint& GlobalPos) int fromIndex = d->TabsLayout->indexOf(MovingTab); auto MousePos = mapFromGlobal(GlobalPos); - MousePos.rx() = qMax(d->firstTab()->geometry().left(), MousePos.x()); - MousePos.rx() = qMin(d->lastTab()->geometry().right(), MousePos.x()); + MousePos.rx() = qMax(0, MousePos.x()); + MousePos.rx() = qMin(width(), MousePos.x()); int toIndex = -1; // Find tab under mouse for (int i = 0; i < count(); ++i) { CDockWidgetTab* DropTab = tab(i); + auto TabGeometry = DropTab->geometry(); + TabGeometry.setTopLeft(d->TabsContainerWidget->mapToParent(TabGeometry.topLeft())); + TabGeometry.setBottomRight(d->TabsContainerWidget->mapToParent(TabGeometry.bottomRight())); if (DropTab == MovingTab || !DropTab->isVisibleTo(this) - || !DropTab->geometry().contains(MousePos)) + || !TabGeometry.contains(MousePos)) { continue; } @@ -469,6 +449,15 @@ bool CDockAreaTabBar::eventFilter(QObject *watched, QEvent *event) updateGeometry(); break; + // Manage wheel event + case QEvent::Wheel: + // Ignore wheel events if tab is currently dragged + if (Tab->dragState() == DraggingInactive) + { + wheelEvent((QWheelEvent* )event); + } + break; + default: break; } @@ -504,6 +493,53 @@ QSize CDockAreaTabBar::sizeHint() const return d->TabsContainerWidget->sizeHint(); } + +//=========================================================================== +int CDockAreaTabBar::tabAt(const QPoint& Pos) const +{ + if (!isVisible()) + { + return TabInvalidIndex; + } + + if (Pos.x() < tab(0)->geometry().x()) + { + return -1; + } + + for (int i = 0; i < count(); ++i) + { + if (tab(i)->geometry().contains(Pos)) + { + return i; + } + } + + return count(); +} + + +//=========================================================================== +int CDockAreaTabBar::tabInsertIndexAt(const QPoint& Pos) const +{ + int Index = tabAt(Pos); + if (Index == TabInvalidIndex) + { + return TabDefaultInsertIndex; + } + else + { + return (Index < 0) ? 0 : Index; + } +} + + +//=========================================================================== +bool CDockAreaTabBar::areTabsOverflowing() const +{ + return d->TabsContainerWidget->width() > width(); +} + } // namespace ads diff --git a/src/DockAreaTabBar.h b/src/DockAreaTabBar.h index 781f5e9da..60557fe61 100644 --- a/src/DockAreaTabBar.h +++ b/src/DockAreaTabBar.h @@ -65,8 +65,7 @@ private Q_SLOTS: void onTabWidgetMoved(const QPoint& GlobalPos); protected: - virtual void wheelEvent(QWheelEvent* Event) override; - + virtual void wheelEvent(QWheelEvent* Event) override; public: using Super = QScrollArea; @@ -113,6 +112,19 @@ private Q_SLOTS: */ CDockWidgetTab* tab(int Index) const; + /** + * Returns the tab at the given position. + * Returns -1 if the position is left of the first tab and count() if the + * position is right of the last tab. Returns -2 to indicate an invalid + * value. + */ + int tabAt(const QPoint& Pos) const; + + /** + * Returns the tab insertion index for the given mouse cursor position + */ + int tabInsertIndexAt(const QPoint& Pos) const; + /** * Filters the tab widget events */ @@ -128,7 +140,7 @@ private Q_SLOTS: /** * 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 + * area because even if the scrollbars are invisible, the required space * is reserved in the minimumSizeHint(). This override simply returns * sizeHint(); */ @@ -140,6 +152,12 @@ private Q_SLOTS: */ virtual QSize sizeHint() const override; + /** + * This function returns true, if the tabs need more space than the size + * of the tab bar. + */ + bool areTabsOverflowing() const; + public Q_SLOTS: /** * This property sets the index of the tab bar's visible tab diff --git a/src/DockAreaTitleBar.cpp b/src/DockAreaTitleBar.cpp index d5af9bf6c..7dd261f89 100644 --- a/src/DockAreaTitleBar.cpp +++ b/src/DockAreaTitleBar.cpp @@ -60,7 +60,6 @@ namespace ads { -static const char* const LocationProperty = "Location"; /** * Private data class of CDockAreaTitleBar class (pimpl) @@ -68,14 +67,15 @@ static const char* const LocationProperty = "Location"; struct DockAreaTitleBarPrivate { CDockAreaTitleBar* _this; - QPointer TabsMenuButton; - QPointer AutoHideButton; - QPointer UndockButton; - QPointer CloseButton; + QPointer TabsMenuButton; + QPointer AutoHideButton; + QPointer UndockButton; + QPointer CloseButton; + QPointer MinimizeButton; QBoxLayout* Layout; CDockAreaWidget* DockArea; CDockAreaTabBar* TabBar; - CElidingLabel* AutoHideTitleLabel; + CElidingLabel* AutoHideTitleLabel = nullptr; bool MenuOutdated = true; QMenu* TabsMenu; QList DockWidgetActionsButtons; @@ -114,6 +114,14 @@ struct DockAreaTitleBarPrivate return DockArea->dockManager(); } + /** + * Convenience function for access to dock manager components factory + */ + QSharedPointer componentsFactory() const + { + return dockManager()->componentsFactory(); + } + /** * Returns true if the given config flag is set * Convenience function to ease config flag testing @@ -158,7 +166,7 @@ struct DockAreaTitleBarPrivate QMenu* Menu) { auto Action = Menu->addAction(Title); - Action->setProperty("Location", Location); + Action->setProperty(internal::LocationProperty, Location); QObject::connect(Action, &QAction::triggered, _this, &CDockAreaTitleBar::onAutoHideToActionClicked); return Action; } @@ -178,7 +186,8 @@ void DockAreaTitleBarPrivate::createButtons() QSizePolicy ButtonSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); // Tabs menu button - TabsMenuButton = new CTitleBarButton(testConfigFlag(CDockManager::DockAreaHasTabsMenuButton)); + TabsMenuButton = new CTitleBarButton(testConfigFlag(CDockManager::DockAreaHasTabsMenuButton), + false, TitleBarButtonTabsMenu); TabsMenuButton->setObjectName("tabsMenuButton"); TabsMenuButton->setAutoRaise(true); TabsMenuButton->setPopupMode(QToolButton::InstantPopup); @@ -196,7 +205,8 @@ void DockAreaTitleBarPrivate::createButtons() SLOT(onTabsMenuActionTriggered(QAction*))); // Undock button - UndockButton = new CTitleBarButton(testConfigFlag(CDockManager::DockAreaHasUndockButton)); + UndockButton = new CTitleBarButton(testConfigFlag(CDockManager::DockAreaHasUndockButton), + true, TitleBarButtonUndock); UndockButton->setObjectName("detachGroupButton"); UndockButton->setAutoRaise(true); internal::setToolTip(UndockButton, QObject::tr("Detach Group")); @@ -207,7 +217,8 @@ void DockAreaTitleBarPrivate::createButtons() // AutoHide Button const auto autoHideEnabled = testAutoHideConfigFlag(CDockManager::AutoHideFeatureEnabled); - AutoHideButton = new CTitleBarButton(testAutoHideConfigFlag(CDockManager::DockAreaHasAutoHideButton) && autoHideEnabled); + AutoHideButton = new CTitleBarButton(testAutoHideConfigFlag(CDockManager::DockAreaHasAutoHideButton) && autoHideEnabled, + true, TitleBarButtonAutoHide); AutoHideButton->setObjectName("dockAreaAutoHideButton"); AutoHideButton->setAutoRaise(true); internal::setToolTip(AutoHideButton, _this->titleBarButtonToolTip(TitleBarButtonAutoHide)); @@ -218,8 +229,21 @@ void DockAreaTitleBarPrivate::createButtons() Layout->addWidget(AutoHideButton, 0); _this->connect(AutoHideButton, SIGNAL(clicked()), SLOT(onAutoHideButtonClicked())); + // Minimize button + MinimizeButton = new CTitleBarButton(testAutoHideConfigFlag(CDockManager::AutoHideHasMinimizeButton), + false, TitleBarButtonMinimize); + MinimizeButton->setObjectName("dockAreaMinimizeButton"); + MinimizeButton->setAutoRaise(true); + MinimizeButton->setVisible(false); + internal::setButtonIcon(MinimizeButton, QStyle::SP_TitleBarMinButton, ads::DockAreaMinimizeIcon); + internal::setToolTip(MinimizeButton, QObject::tr("Minimize")); + MinimizeButton->setSizePolicy(ButtonSizePolicy); + Layout->addWidget(MinimizeButton, 0); + _this->connect(MinimizeButton, SIGNAL(clicked()), SLOT(minimizeAutoHideContainer())); + // Close button - CloseButton = new CTitleBarButton(testConfigFlag(CDockManager::DockAreaHasCloseButton)); + CloseButton = new CTitleBarButton(testConfigFlag(CDockManager::DockAreaHasCloseButton), + true, TitleBarButtonClose); CloseButton->setObjectName("dockAreaCloseButton"); CloseButton->setAutoRaise(true); internal::setButtonIcon(CloseButton, QStyle::SP_TitleBarCloseButton, ads::DockAreaCloseIcon); @@ -236,7 +260,10 @@ void DockAreaTitleBarPrivate::createAutoHideTitleLabel() { AutoHideTitleLabel = new CElidingLabel(""); AutoHideTitleLabel->setObjectName("autoHideTitleLabel"); + // When the tabs are at the top, they will be at position 0, insert the label behind them, and hide it. Layout->addWidget(AutoHideTitleLabel); + AutoHideTitleLabel->setVisible(CDockManager::testConfigFlag(CDockManager::TabsAtBottom)); + Layout->addWidget(new CSpacerWidget(_this)); } @@ -245,7 +272,9 @@ void DockAreaTitleBarPrivate::createTabBar() { TabBar = componentsFactory()->createDockAreaTabBar(DockArea); TabBar->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred); - Layout->addWidget(TabBar); + if (!CDockManager::testConfigFlag(CDockManager::TabsAtBottom)) + Layout->addWidget(TabBar); + _this->connect(TabBar, SIGNAL(tabClosed(int)), SLOT(markTabsMenuOutdated())); _this->connect(TabBar, SIGNAL(tabOpened(int)), SLOT(markTabsMenuOutdated())); _this->connect(TabBar, SIGNAL(tabInserted(int)), SLOT(markTabsMenuOutdated())); @@ -276,7 +305,7 @@ IFloatingWidget* DockAreaTitleBarPrivate::makeAreaFloating(const QPoint& Offset, else { auto w = new CFloatingDragPreview(DockArea); - QObject::connect(w, &CFloatingDragPreview::draggingCanceled, [=]() + QObject::connect(w, &CFloatingDragPreview::draggingCanceled, [this]() { this->DragState = DraggingInactive; }); @@ -325,8 +354,6 @@ CDockAreaTitleBar::CDockAreaTitleBar(CDockAreaWidget* parent) : d->createTabBar(); d->createAutoHideTitleLabel(); - d->AutoHideTitleLabel->setVisible(false); // Default hidden - d->Layout->addWidget(new CSpacerWidget(this)); d->createButtons(); setFocusPolicy(Qt::NoFocus); @@ -360,27 +387,51 @@ CDockAreaTabBar* CDockAreaTitleBar::tabBar() const return d->TabBar; } + +//============================================================================ +void CDockAreaTitleBar::resizeEvent(QResizeEvent *event) +{ + Super::resizeEvent(event); + if (CDockManager::testConfigFlag(CDockManager::DockAreaDynamicTabsMenuButtonVisibility) + && CDockManager::testConfigFlag(CDockManager::DisableTabTextEliding)) + { + // Use queued connection to ensure that the resizing and relayouting has + // finished to ensure that the d->TabBar->areTabsOverflowing() function + // returns the correct value + QMetaObject::invokeMethod(this, "markTabsMenuOutdated", Qt::QueuedConnection); + } +} + + //============================================================================ void CDockAreaTitleBar::markTabsMenuOutdated() { - if(DockAreaTitleBarPrivate::testConfigFlag(CDockManager::DockAreaDynamicTabsMenuButtonVisibility)) + if (CDockManager::testConfigFlag(CDockManager::DockAreaDynamicTabsMenuButtonVisibility)) { - bool hasElidedTabTitle = false; - for (int i = 0; i < d->TabBar->count(); ++i) + bool TabsMenuButtonVisible = false; + if (CDockManager::testConfigFlag(CDockManager::DisableTabTextEliding)) { - if (!d->TabBar->isTabOpen(i)) - { - continue; - } - CDockWidgetTab* Tab = d->TabBar->tab(i); - if(Tab->isTitleElided()) + TabsMenuButtonVisible = d->TabBar->areTabsOverflowing(); + } + else + { + bool hasElidedTabTitle = false; + for (int i = 0; i < d->TabBar->count(); ++i) { - hasElidedTabTitle = true; - break; + if (!d->TabBar->isTabOpen(i)) + { + continue; + } + CDockWidgetTab* Tab = d->TabBar->tab(i); + if(Tab->isTitleElided()) + { + hasElidedTabTitle = true; + break; + } } + TabsMenuButtonVisible = (hasElidedTabTitle && (d->TabBar->count() > 1)); } - bool visible = (hasElidedTabTitle && (d->TabBar->count() > 1)); - QMetaObject::invokeMethod(d->TabsMenuButton, "setVisible", Qt::QueuedConnection, Q_ARG(bool, visible)); + QMetaObject::invokeMethod(d->TabsMenuButton, "setVisible", Qt::QueuedConnection, Q_ARG(bool, TabsMenuButtonVisible)); } d->MenuOutdated = true; } @@ -431,6 +482,24 @@ void CDockAreaTitleBar::onCloseButtonClicked() } +//============================================================================ +void CDockAreaTitleBar::onAutoHideCloseActionTriggered() +{ + d->DockArea->closeArea(); +} + + +//============================================================================ +void CDockAreaTitleBar::minimizeAutoHideContainer() +{ + auto AutoHideContainer = d->DockArea->autoHideDockContainer(); + if (AutoHideContainer) + { + AutoHideContainer->collapseView(true); + } +} + + //============================================================================ void CDockAreaTitleBar::onUndockButtonClicked() { @@ -479,7 +548,7 @@ void CDockAreaTitleBar::updateDockWidgetActionsButtons() int InsertIndex = indexOf(d->TabsMenuButton); for (auto Action : Actions) { - auto Button = new CTitleBarButton(true, this); + auto Button = new CTitleBarButton(true, false, TitleBarButtonTabsMenu, this); Button->setDefaultAction(Action); Button->setAutoRaise(true); Button->setPopupMode(QToolButton::InstantPopup); @@ -533,13 +602,13 @@ void CDockAreaTitleBar::onAutoHideDockAreaActionClicked() //============================================================================ void CDockAreaTitleBar::onAutoHideToActionClicked() { - int Location = sender()->property(LocationProperty).toInt(); + int Location = sender()->property(internal::LocationProperty).toInt(); d->DockArea->toggleAutoHide((SideBarLocation)Location); } //============================================================================ -QAbstractButton* CDockAreaTitleBar::button(TitleBarButton which) const +CTitleBarButton* CDockAreaTitleBar::button(TitleBarButton which) const { switch (which) { @@ -547,6 +616,7 @@ QAbstractButton* CDockAreaTitleBar::button(TitleBarButton which) const case TitleBarButtonUndock: return d->UndockButton; case TitleBarButtonClose: return d->CloseButton; case TitleBarButtonAutoHide: return d->AutoHideButton; + case TitleBarButtonMinimize: return d->MinimizeButton; default: return nullptr; } @@ -673,10 +743,37 @@ void CDockAreaTitleBar::mouseDoubleClickEvent(QMouseEvent *event) return; } + if (!CDockManager::testConfigFlag(CDockManager::DoubleClickUndocksWidget)) + { + return; + } + d->makeAreaFloating(event->pos(), DraggingInactive); } +//============================================================================ +void CDockAreaTitleBar::setAreaFloating() +{ + // If this is the last dock area in a dock container it does not make + // sense to move it to a new floating widget and leave this one + // empty. + auto DockContainer = d->DockArea->dockContainer(); + if (DockContainer->isFloating() && DockContainer->dockAreaCount() == 1 + && !d->DockArea->isAutoHide()) + { + return; + } + + if (!d->DockArea->features().testFlag(CDockWidget::DockWidgetFloatable)) + { + return; + } + + d->makeAreaFloating(mapFromGlobal(QCursor::pos()), DraggingInactive); +} + + //============================================================================ void CDockAreaTitleBar::contextMenuEvent(QContextMenuEvent* ev) { @@ -686,24 +783,35 @@ void CDockAreaTitleBar::contextMenuEvent(QContextMenuEvent* ev) return; } - const bool isAutoHide = d->DockArea->isAutoHide(); + auto Menu = buildContextMenu(nullptr); + Menu->exec(ev->globalPos()); + delete Menu; +} + +QMenu* CDockAreaTitleBar::buildContextMenu(QMenu *Menu) +{ + const bool isAutoHide = d->DockArea->isAutoHide(); const bool isTopLevelArea = d->DockArea->isTopLevelArea(); QAction* Action; - QMenu Menu(this); - if (!isTopLevelArea) + if (Menu == nullptr) + { + Menu = new QMenu(this); + } + + if (!isTopLevelArea) { - Action = Menu.addAction(isAutoHide ? tr("Detach") : tr("Detach Group"), + 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())); + 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...")); + auto menu = Menu->addMenu(tr("Pin Group To...")); menu->setEnabled(AreaIsPinnable); d->createAutoHideToAction(tr("Top"), SideBarTop, menu); d->createAutoHideToAction(tr("Left"), SideBarLeft, menu); @@ -711,18 +819,27 @@ void CDockAreaTitleBar::contextMenuEvent(QContextMenuEvent* ev) d->createAutoHideToAction(tr("Bottom"), SideBarBottom, menu); } } - Menu.addSeparator(); + Menu->addSeparator(); + } + + if (isAutoHide) + { + Action = Menu->addAction(tr("Minimize"), this, SLOT(minimizeAutoHideContainer())); + Action = Menu->addAction(tr("Close"), this, SLOT(onAutoHideCloseActionTriggered())); + } + else + { + Action = Menu->addAction(isAutoHide ? tr("Close") : tr("Close Group"), this, SLOT(onCloseButtonClicked())); } - Action = Menu.addAction(isAutoHide ? tr("Close") : tr("Close Group"), this, SLOT(onCloseButtonClicked())); + Action->setEnabled(d->DockArea->features().testFlag(CDockWidget::DockWidgetClosable)); if (!isAutoHide && !isTopLevelArea) { - Action = Menu.addAction(tr("Close Other Groups"), d->DockArea, SLOT(closeOtherAreas())); + Action = Menu->addAction(tr("Close Other Groups"), d->DockArea, SLOT(closeOtherAreas())); } - Menu.exec(ev->globalPos()); + return Menu; } - //============================================================================ void CDockAreaTitleBar::insertWidget(int index, QWidget *widget) { @@ -760,7 +877,8 @@ QString CDockAreaTitleBar::titleBarButtonToolTip(TitleBarButton Button) const case TitleBarButtonClose: if (d->DockArea->isAutoHide()) { - return tr("Close"); + bool Minimize = CDockManager::testAutoHideConfigFlag(CDockManager::AutoHideCloseButtonCollapsesDock); + return Minimize ? tr("Minimize") : tr("Close"); } if (CDockManager::testConfigFlag(CDockManager::DockAreaCloseButtonClosesTab)) @@ -780,11 +898,39 @@ QString CDockAreaTitleBar::titleBarButtonToolTip(TitleBarButton Button) const return QString(); } + +//============================================================================ +void CDockAreaTitleBar::showAutoHideControls(bool Show) +{ + if (Show) + d->TabBar->setVisible(false); // Auto hide toolbar never has tabs + + d->MinimizeButton->setVisible(Show); + if (!CDockManager::testConfigFlag(CDockManager::TabsAtBottom)) + d->AutoHideTitleLabel->setVisible(Show); +} + + +//============================================================================ +bool CDockAreaTitleBar::isAutoHide() const +{ + return d->DockArea && d->DockArea->isAutoHide(); +} + + +//============================================================================ +CDockAreaWidget* CDockAreaTitleBar::dockAreaWidget() const +{ + return d->DockArea; +} + + //============================================================================ -CTitleBarButton::CTitleBarButton(bool visible, QWidget* parent) +CTitleBarButton::CTitleBarButton(bool showInTitleBar, bool hideWhenDisabled, TitleBarButton ButtonId, QWidget* parent) : tTitleBarButton(parent), - Visible(visible), - HideWhenDisabled(CDockManager::testConfigFlag(CDockManager::DockAreaHideDisabledButtons)) + ShowInTitleBar(showInTitleBar), + HideWhenDisabled(CDockManager::testConfigFlag(CDockManager::DockAreaHideDisabledButtons) && hideWhenDisabled), + TitleBarButtonId(ButtonId) { setFocusPolicy(Qt::NoFocus); } @@ -792,8 +938,8 @@ CTitleBarButton::CTitleBarButton(bool visible, QWidget* parent) //============================================================================ void CTitleBarButton::setVisible(bool visible) { - // 'visible' can stay 'true' if and only if this button is configured to generaly visible: - visible = visible && this->Visible; + // 'visible' can stay 'true' if and only if this button is configured to generally visible: + visible = visible && this->ShowInTitleBar; // 'visible' can stay 'true' unless: this button is configured to be invisible when it is disabled and it is currently disabled: if (visible && HideWhenDisabled) @@ -804,19 +950,62 @@ void CTitleBarButton::setVisible(bool visible) Super::setVisible(visible); } + +//============================================================================ +void CTitleBarButton::setShowInTitleBar(bool Show) +{ + this->ShowInTitleBar = Show; + if (!Show) + { + setVisible(false); + } +} + + //============================================================================ bool CTitleBarButton::event(QEvent *ev) { - if (QEvent::EnabledChange == ev->type() && HideWhenDisabled) + if (QEvent::EnabledChange != ev->type() || !HideWhenDisabled || !ShowInTitleBar) { - // force setVisible() call - // Calling setVisible() directly here doesn't work well when button is expected to be shown first time - QMetaObject::invokeMethod(this, "setVisible", Qt::QueuedConnection, Q_ARG(bool, isEnabled())); + return Super::event(ev); + } + + bool Show = true; + if (isInAutoHideArea()) + { + switch (TitleBarButtonId) + { + case TitleBarButtonClose: Show = CDockManager::testAutoHideConfigFlag(CDockManager::AutoHideHasCloseButton); break; + case TitleBarButtonUndock: Show = false; break; + default: + break; + } } + // force setVisible() call - Calling setVisible() directly here doesn't + // work well when button is expected to be shown first time + QMetaObject::invokeMethod(this, "setVisible", Qt::QueuedConnection, + Q_ARG(bool, isEnabledTo(this->parentWidget()) & Show)); + return Super::event(ev); } + +//============================================================================ +CDockAreaTitleBar* CTitleBarButton::titleBar() const +{ + return qobject_cast(parentWidget()); +} + + +//============================================================================ +bool CTitleBarButton::isInAutoHideArea() const +{ + auto TitleBar = titleBar(); + return TitleBar && TitleBar->isAutoHide(); +} + + //============================================================================ CSpacerWidget::CSpacerWidget(QWidget* Parent /*= 0*/) : Super(Parent) { @@ -824,6 +1013,7 @@ CSpacerWidget::CSpacerWidget(QWidget* Parent /*= 0*/) : Super(Parent) setStyleSheet("border: none; background: none;"); } + } // namespace ads //--------------------------------------------------------------------------- diff --git a/src/DockAreaTitleBar.h b/src/DockAreaTitleBar.h index a31464034..56820c4b9 100644 --- a/src/DockAreaTitleBar.h +++ b/src/DockAreaTitleBar.h @@ -30,6 +30,7 @@ //============================================================================ // INCLUDES //============================================================================ +#include #include #include "ads_globals.h" @@ -42,6 +43,63 @@ class CDockAreaTabBar; class CDockAreaWidget; struct DockAreaTitleBarPrivate; class CElidingLabel; +class CDockAreaTitleBar; + +using tTitleBarButton = QToolButton; + +/** +* Title bar button of a dock area that customizes tTitleBarButton appearance/behaviour +* according to various config flags such as: +* CDockManager::DockAreaHas_xxx_Button - if set to 'false' keeps the button always invisible +* CDockManager::DockAreaHideDisabledButtons - if set to 'true' hides button when it is disabled +*/ +class CTitleBarButton : public tTitleBarButton +{ + Q_OBJECT + +private: + bool ShowInTitleBar = true; + bool HideWhenDisabled = false; + TitleBarButton TitleBarButtonId; + +public: + using Super = tTitleBarButton; + CTitleBarButton(bool ShowInTitleBar, bool HideWhenDisabled, TitleBarButton ButtonId, + QWidget* parent = nullptr); + + /** + * Adjust this visibility change request with our internal settings: + */ + virtual void setVisible(bool visible) override; + + /** + * Configures, if the title bar button should be shown in title bar + */ + void setShowInTitleBar(bool Show); + + /** + * Identifier for the title bar button + */ + TitleBarButton buttonId() const {return TitleBarButtonId;} + + /** + * Return the title bar that contains this button + */ + CDockAreaTitleBar* titleBar() const; + + /** + * Returns true, if the button is in a title bar in an auto hide area + */ + bool isInAutoHideArea() const; + + +protected: + /** + * Handle EnabledChanged signal to set button invisible if the configured + */ + bool event(QEvent *ev) override; +}; + /** * Title bar of a dock area. @@ -58,6 +116,8 @@ class ADS_EXPORT CDockAreaTitleBar : public QFrame private Q_SLOTS: void onTabsMenuAboutToShow(); void onCloseButtonClicked(); + void onAutoHideCloseActionTriggered(); + void minimizeAutoHideContainer(); void onUndockButtonClicked(); void onTabsMenuActionTriggered(QAction* Action); void onCurrentTabChanged(int Index); @@ -92,6 +152,11 @@ private Q_SLOTS: */ virtual void contextMenuEvent(QContextMenuEvent *event) override; + /** + * Handle resize events + */ + virtual void resizeEvent(QResizeEvent *event) override; + public Q_SLOTS: /** * Call this slot to tell the title bar that it should update the tabs menu @@ -121,13 +186,18 @@ public Q_SLOTS: /** * Returns the button corresponding to the given title bar button identifier */ - QAbstractButton* button(TitleBarButton which) const; + CTitleBarButton* button(TitleBarButton which) const; /** * Returns the auto hide title label, used when the dock area is expanded and auto hidden */ CElidingLabel* autoHideTitleLabel() const; + /** + * Returns the dock area widget that contains this title bar + */ + CDockAreaWidget* dockAreaWidget() const; + /** * Updates the visibility of the dock widget actions in the title bar */ @@ -163,6 +233,36 @@ public Q_SLOTS: */ QString titleBarButtonToolTip(TitleBarButton Button) const; + /** + * Moves the dock area into its own floating widget if the area + * DockWidgetFloatable flag is true + */ + void setAreaFloating(); + + /** + * Call this function, to create all the required auto hide controls + */ + void showAutoHideControls(bool Show); + + /** + * Returns true, if the auto hide controls are visible + */ + bool isAutoHide() const; + + /** + * Fills the provided menu with standard entries. If a nullptr is passed, a + * new menu is created and filled with standard entries. + * This function is called from the actual version of contextMenuEvent, but + * can be called from any code. Caller is responsible of deleting the created + * object. + * + * @param menu The QMenu to fill with standard entries. If nullptr, a new + * QMenu will be created. + * @return The filled QMenu, either the provided one or a newly created one if + * nullptr was passed. + */ + virtual QMenu *buildContextMenu(QMenu *); + Q_SIGNALS: /** * This signal is emitted if a tab in the tab bar is clicked by the user diff --git a/src/DockAreaTitleBar_p.h b/src/DockAreaTitleBar_p.h index b94a51008..f097919ab 100644 --- a/src/DockAreaTitleBar_p.h +++ b/src/DockAreaTitleBar_p.h @@ -31,43 +31,12 @@ // INCLUDES //============================================================================ #include -#include #include "ads_globals.h" namespace ads { -using tTitleBarButton = QToolButton; -/** -* Title bar button of a dock area that customizes tTitleBarButton appearance/behaviour -* according to various config flags such as: -* CDockManager::DockAreaHas_xxx_Button - if set to 'false' keeps the button always invisible -* CDockManager::DockAreaHideDisabledButtons - if set to 'true' hides button when it is disabled -*/ -class CTitleBarButton : public tTitleBarButton -{ - Q_OBJECT - -private: - bool Visible = true; - bool HideWhenDisabled = false; - -public: - using Super = tTitleBarButton; - CTitleBarButton(bool visible = true, QWidget* parent = nullptr); - - /** - * Adjust this visibility change request with our internal settings: - */ - virtual void setVisible(bool visible) override; - -protected: - /** - * Handle EnabledChanged signal to set button invisible if the configured - */ - bool event(QEvent *ev) override; -}; /** @@ -83,7 +52,7 @@ class CSpacerWidget : public QWidget Q_OBJECT public: using Super = QWidget; - CSpacerWidget(QWidget* Parent = 0); + CSpacerWidget(QWidget* Parent = nullptr); virtual QSize sizeHint() const override {return QSize(0, 0);} virtual QSize minimumSizeHint() const override {return QSize(0, 0);} }; diff --git a/src/DockAreaWidget.cpp b/src/DockAreaWidget.cpp index c1d8d5565..dfcb4ee08 100644 --- a/src/DockAreaWidget.cpp +++ b/src/DockAreaWidget.cpp @@ -34,7 +34,6 @@ #include #include -#include #include #include #include @@ -81,7 +80,7 @@ class CDockAreaLayout { private: QBoxLayout* m_ParentLayout; - QList m_Widgets; + QList> m_Widgets; int m_CurrentIndex = -1; QWidget* m_CurrentWidget = nullptr; @@ -180,14 +179,17 @@ class CDockAreaLayout parent->setUpdatesEnabled(false); } - auto LayoutItem = m_ParentLayout->takeAt(1); - if (LayoutItem) + if (m_CurrentWidget) { - LayoutItem->widget()->setParent(nullptr); + auto LayoutItem = m_ParentLayout->takeAt(1); + if (LayoutItem) + { + LayoutItem->widget()->setParent(nullptr); + } + delete LayoutItem; } - delete LayoutItem; - m_ParentLayout->addWidget(next); + m_ParentLayout->insertWidget(1, next); if (prev) { prev->hide(); @@ -270,6 +272,14 @@ struct DockAreaWidgetPrivate */ DockAreaWidgetPrivate(CDockAreaWidget* _public); + /** + * Convenience function to ease components factory access + */ + QSharedPointer componentsFactory() const + { + return DockManager->componentsFactory(); + } + /** * Creates the layout for top area with tabs and close button */ @@ -317,12 +327,12 @@ struct DockAreaWidgetPrivate } /** - * Udpates the enable state of the close and detach button + * Updates the enable state of the close and detach button */ void updateTitleBarButtonStates(); /** - * Udpates the enable state of the close and detach button + * Updates the enable state of the close and detach button */ void updateTitleBarButtonVisibility(bool isTopLevel); @@ -355,6 +365,14 @@ void DockAreaWidgetPrivate::createTitleBar() { TitleBar = componentsFactory()->createDockAreaTitleBar(_this); Layout->addWidget(TitleBar); + if (CDockManager::testConfigFlag(CDockManager::TabsAtBottom)) + { + // Title bar will be index 0, container widgets will be index 1, + // so tabs will always be at the end of the layout. + Layout->addWidget(tabBar()); + tabBar()->setVisible(CDockManager::testConfigFlag(CDockManager::AlwaysShowTabs)); + } + QObject::connect(tabBar(), &CDockAreaTabBar::tabCloseRequested, _this, &CDockAreaWidget::onTabCloseRequested); QObject::connect(TitleBar, &CDockAreaTitleBar::tabBarClicked, _this, &CDockAreaWidget::setCurrentIndex); QObject::connect(tabBar(), &CDockAreaTabBar::tabMoved, _this, &CDockAreaWidget::reorderDockWidget); @@ -370,10 +388,21 @@ void DockAreaWidgetPrivate::updateTitleBarButtonStates() return; } - TitleBar->button(TitleBarButtonClose)->setEnabled( - _this->features().testFlag(CDockWidget::DockWidgetClosable)); - TitleBar->button(TitleBarButtonUndock)->setEnabled( - _this->features().testFlag(CDockWidget::DockWidgetFloatable)); + if (_this->isAutoHide()) + { + if (CDockManager::testAutoHideConfigFlag(CDockManager::AutoHideHasCloseButton)) + { + TitleBar->button(TitleBarButtonClose)->setEnabled( + _this->features().testFlag(CDockWidget::DockWidgetClosable)); + } + } + else + { + TitleBar->button(TitleBarButtonUndock)->setEnabled( + _this->features().testFlag(CDockWidget::DockWidgetFloatable)); + TitleBar->button(TitleBarButtonClose)->setEnabled( + _this->features().testFlag(CDockWidget::DockWidgetClosable)); + } TitleBar->button(TitleBarButtonAutoHide)->setEnabled( _this->features().testFlag(CDockWidget::DockWidgetPinnable)); TitleBar->updateDockWidgetActionsButtons(); @@ -390,20 +419,30 @@ void DockAreaWidgetPrivate::updateTitleBarButtonVisibility(bool IsTopLevel) return; } - if (IsTopLevel) + bool IsAutoHide = _this->isAutoHide(); + if (IsAutoHide) + { + bool ShowCloseButton = CDockManager::testAutoHideConfigFlag(CDockManager::AutoHideHasCloseButton); + TitleBar->button(TitleBarButtonClose)->setVisible(ShowCloseButton); + TitleBar->button(TitleBarButtonAutoHide)->setVisible(true); + TitleBar->button(TitleBarButtonUndock)->setVisible(false); + TitleBar->button(TitleBarButtonTabsMenu)->setVisible(false); + } + else 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()); + TitleBar->button(TitleBarButtonUndock)->setVisible(!container->isFloating()); + TitleBar->button(TitleBarButtonTabsMenu)->setVisible(true); } else { TitleBar->button(TitleBarButtonClose)->setVisible(true); - TitleBar->button(TitleBarButtonAutoHide)->setVisible(true); - TitleBar->button(TitleBarButtonUndock)->setVisible(!_this->isAutoHide()); - TitleBar->button(TitleBarButtonTabsMenu)->setVisible(!_this->isAutoHide()); + bool ShowAutoHideButton = CDockManager::testAutoHideConfigFlag(CDockManager::DockAreaHasAutoHideButton); + TitleBar->button(TitleBarButtonAutoHide)->setVisible(ShowAutoHideButton); + TitleBar->button(TitleBarButtonUndock)->setVisible(true); + TitleBar->button(TitleBarButtonTabsMenu)->setVisible(true); } } @@ -455,6 +494,13 @@ CAutoHideDockContainer* CDockAreaWidget::autoHideDockContainer() const return d->AutoHideDockContainer; } + +//============================================================================ +CDockSplitter* CDockAreaWidget::parentSplitter() const +{ + return internal::findParent(this); +} + //============================================================================ bool CDockAreaWidget::isAutoHide() const { @@ -467,6 +513,7 @@ void CDockAreaWidget::setAutoHideDockContainer(CAutoHideDockContainer* AutoHideD d->AutoHideDockContainer = AutoHideDockContainer; updateAutoHideButtonCheckState(); updateTitleBarButtonsToolTips(); + d->TitleBar->button(TitleBarButtonAutoHide)->setShowInTitleBar(true); } @@ -590,7 +637,7 @@ void CDockAreaWidget::hideAreaWithNoVisibleContent() this->toggleView(false); // Hide empty parent splitters - auto Splitter = internal::findParent(this); + auto Splitter = parentSplitter(); internal::hideEmptyParentSplitters(Splitter); //Hide empty floating widget @@ -626,15 +673,7 @@ void CDockAreaWidget::hideAreaWithNoVisibleContent() void CDockAreaWidget::onTabCloseRequested(int Index) { ADS_PRINT("CDockAreaWidget::onTabCloseRequested " << Index); - auto* DockWidget = dockWidget(Index); - if (DockWidget->features().testFlag(CDockWidget::DockWidgetDeleteOnClose) || DockWidget->features().testFlag(CDockWidget::CustomCloseHandling)) - { - DockWidget->closeDockWidgetInternal(); - } - else - { - DockWidget->toggleView(false); - } + dockWidget(Index)->requestCloseDockWidget(); } @@ -698,6 +737,7 @@ void CDockAreaWidget::setCurrentIndex(int index) TabBar->setCurrentIndex(index); d->ContentsLayout->setCurrentIndex(index); d->ContentsLayout->currentWidget()->show(); + d->TitleBar->autoHideTitleLabel()->setText(d->ContentsLayout->currentWidget()->windowTitle()); Q_EMIT currentChanged(index); } @@ -747,7 +787,7 @@ int CDockAreaWidget::openDockWidgetsCount() const int Count = 0; for (int i = 0; i < d->ContentsLayout->count(); ++i) { - if (!dockWidget(i)->isClosed()) + if (dockWidget(i) && !dockWidget(i)->isClosed()) { ++Count; } @@ -763,7 +803,7 @@ QList CDockAreaWidget::openedDockWidgets() const for (int i = 0; i < d->ContentsLayout->count(); ++i) { CDockWidget* DockWidget = dockWidget(i); - if (!DockWidget->isClosed()) + if (DockWidget && !DockWidget->isClosed()) { DockWidgetList.append(dockWidget(i)); } @@ -777,7 +817,7 @@ int CDockAreaWidget::indexOfFirstOpenDockWidget() const { for (int i = 0; i < d->ContentsLayout->count(); ++i) { - if (!dockWidget(i)->isClosed()) + if (dockWidget(i) && !dockWidget(i)->isClosed()) { return i; } @@ -800,7 +840,6 @@ CDockWidget* CDockAreaWidget::dockWidget(int Index) const return qobject_cast(d->ContentsLayout->widget(Index)); } - //============================================================================ void CDockAreaWidget::reorderDockWidget(int fromIndex, int toIndex) { @@ -845,18 +884,45 @@ void CDockAreaWidget::updateTitleBarVisibility() 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 + bool Hidden = false; + if (!IsAutoHide) // Titlebar must always be visible when auto hidden so it can be dragged + { + if (Container->isFloating() || CDockManager::testConfigFlag(CDockManager::HideSingleCentralWidgetTitleBar)) + { + // Always show title bar if it contains title bar actions + if (CDockWidget* TopLevelWidget = Container->topLevelDockWidget()) + { + Hidden |= TopLevelWidget->titleBarActions().empty(); + } + else if (CDockManager::testConfigFlag(CDockManager::TabsAtBottom)) + { + Hidden = true; + + for (CDockWidget* DockWidget : Container->openedDockWidgets()) + { + if (!DockWidget->titleBarActions().empty()) + { + Hidden = false; + break; + } + } + } + } + if (!Hidden && d->Flags.testFlag(HideSingleWidgetTitleBar)) + { + // Always show title bar if it contains title bar actions + auto DockWidgets = openedDockWidgets(); + Hidden |= (DockWidgets.size() == 1) && DockWidgets.front()->titleBarActions().empty(); + } + } d->TitleBar->setVisible(!Hidden); + if (CDockManager::testConfigFlag(CDockManager::TabsAtBottom)) + d->TitleBar->tabBar()->setVisible(openDockWidgetsCount() > 1); } 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 + d->TitleBar->showAutoHideControls(IsAutoHide); updateTitleBarButtonVisibility(Container->topLevelDockArea() == this); } } @@ -1290,17 +1356,17 @@ SideBarLocation CDockAreaWidget::calculateSideTabBarArea() const case BorderHorizontalLeft: SideTab = SideBarLocation::SideBarLeft; break; case BorderHorizontalRight: SideTab = SideBarLocation::SideBarRight; break; - // 3. Its touching horizontal or vertical borders + // 3. It's touching horizontal or vertical borders case BorderVertical : SideTab = SideBarLocation::SideBarBottom; break; case BorderHorizontal: SideTab = SideBarLocation::SideBarRight; break; - // 4. Its in a corner + // 4. It's 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 + // 5. It's touching only one border case BorderLeft: SideTab = SideBarLocation::SideBarLeft; break; case BorderRight: SideTab = SideBarLocation::SideBarRight; break; case BorderTop: SideTab = SideBarLocation::SideBarTop; break; @@ -1312,7 +1378,7 @@ SideBarLocation CDockAreaWidget::calculateSideTabBarArea() const //============================================================================ -void CDockAreaWidget::setAutoHide(bool Enable, SideBarLocation Location) +void CDockAreaWidget::setAutoHide(bool Enable, SideBarLocation Location, int TabIndex) { if (!isAutoHideFeatureEnabled()) { @@ -1323,11 +1389,18 @@ void CDockAreaWidget::setAutoHide(bool Enable, SideBarLocation Location) { if (isAutoHide()) { - autoHideDockContainer()->moveContentsToParent(); + d->AutoHideDockContainer->moveContentsToParent(); } return; } + // If this is already an auto hide container, then move it to new location + if (isAutoHide()) + { + d->AutoHideDockContainer->moveToNewSideBarLocation(Location, TabIndex); + return; + } + auto area = (SideBarNone == Location) ? calculateSideTabBarArea() : Location; for (const auto DockWidget : openedDockWidgets()) { @@ -1341,7 +1414,7 @@ void CDockAreaWidget::setAutoHide(bool Enable, SideBarLocation Location) continue; } - dockContainer()->createAndSetupAutoHideContainer(area, DockWidget); + dockContainer()->createAndSetupAutoHideContainer(area, DockWidget, TabIndex++); } } @@ -1442,6 +1515,13 @@ bool CDockAreaWidget::isTopLevelArea() const } +//============================================================================ +void CDockAreaWidget::setFloating() +{ + d->TitleBar->setAreaFloating(); +} + + #ifdef Q_OS_WIN //============================================================================ bool CDockAreaWidget::event(QEvent *e) diff --git a/src/DockAreaWidget.h b/src/DockAreaWidget.h index a3ef76c29..e0c39c73c 100644 --- a/src/DockAreaWidget.h +++ b/src/DockAreaWidget.h @@ -38,6 +38,7 @@ QT_FORWARD_DECLARE_CLASS(QXmlStreamWriter) QT_FORWARD_DECLARE_CLASS(QAbstractButton) +QT_FORWARD_DECLARE_CLASS(QMenu) namespace ads { @@ -47,6 +48,7 @@ class CDockContainerWidget; class DockContainerWidgetPrivate; class CDockAreaTitleBar; class CDockingStateReader; +class CDockSplitter; /** @@ -216,12 +218,16 @@ protected Q_SLOTS: */ CAutoHideDockContainer* autoHideDockContainer() const; + /** + * Returns the parent splitter that contains this dock area + */ + CDockSplitter* parentSplitter() const; + /** * Returns true if the dock area is in an auto hide container */ bool isAutoHide() const; - /** * Sets the current auto hide dock container */ @@ -401,7 +407,7 @@ public Q_SLOTS: * 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); + void setAutoHide(bool Enable, SideBarLocation Location = SideBarNone, int TabIndex = -1); /** * Switches the dock area to auto hide mode or vice versa depending on its @@ -414,6 +420,12 @@ public Q_SLOTS: */ void closeOtherAreas(); + /** + * Moves the dock area into its own floating widget if the area + * DockWidgetFloatable flag is true + */ + void setFloating(); + Q_SIGNALS: /** * This signal is emitted when user clicks on a tab at an index. diff --git a/src/DockComponentsFactory.cpp b/src/DockComponentsFactory.cpp index 05259d7b4..874bf111f 100644 --- a/src/DockComponentsFactory.cpp +++ b/src/DockComponentsFactory.cpp @@ -21,7 +21,8 @@ namespace ads { -static std::unique_ptr DefaultFactory(new CDockComponentsFactory()); + +static QSharedPointer DefaultFactory; //============================================================================ @@ -52,9 +53,13 @@ CDockAreaTitleBar* CDockComponentsFactory::createDockAreaTitleBar(CDockAreaWidge //============================================================================ -const CDockComponentsFactory* CDockComponentsFactory::factory() +QSharedPointer CDockComponentsFactory::factory() { - return DefaultFactory.get(); + if (!DefaultFactory) + { + DefaultFactory.reset(new CDockComponentsFactory()); + } + return DefaultFactory; } @@ -70,6 +75,7 @@ void CDockComponentsFactory::resetDefaultFactory() { DefaultFactory.reset(new CDockComponentsFactory()); } + } // namespace ads //--------------------------------------------------------------------------- diff --git a/src/DockComponentsFactory.h b/src/DockComponentsFactory.h index 0d2b7d4c8..6ea881852 100644 --- a/src/DockComponentsFactory.h +++ b/src/DockComponentsFactory.h @@ -66,9 +66,11 @@ class ADS_EXPORT CDockComponentsFactory virtual CDockAreaTitleBar* createDockAreaTitleBar(CDockAreaWidget* DockArea) const; /** - * Returns the default components factory + * This returns the default dock components factory instance. + * If no components factory is assigned to a specific dock manager, this + * global factory instance will be used. */ - static const CDockComponentsFactory* factory(); + static QSharedPointer factory(); /** * Sets a new default factory for creation of GUI elements. @@ -82,15 +84,6 @@ class ADS_EXPORT CDockComponentsFactory static void resetDefaultFactory(); }; - -/** - * Convenience function to ease factory instance access - */ -inline const CDockComponentsFactory* componentsFactory() -{ - return CDockComponentsFactory::factory(); -} - } // namespace ads //--------------------------------------------------------------------------- diff --git a/src/DockContainerWidget.cpp b/src/DockContainerWidget.cpp index 73420ee42..0a4deafc2 100644 --- a/src/DockContainerWidget.cpp +++ b/src/DockContainerWidget.cpp @@ -141,11 +141,11 @@ class DockContainerWidgetPrivate CDockContainerWidget* _this; QPointer DockManager; unsigned int zOrderIndex = 0; - QList DockAreas; + QList> DockAreas; QList AutoHideWidgets; QMap SideTabBarWidgets; QGridLayout* Layout = nullptr; - QSplitter* RootSplitter = nullptr; + CDockSplitter* RootSplitter = nullptr; bool isFloating = false; CDockAreaWidget* LastAddedAreaCache[5]; int VisibleDockAreaCount = -1; @@ -181,17 +181,29 @@ class DockContainerWidgetPrivate */ void dropIntoContainer(CFloatingDockContainer* FloatingWidget, DockWidgetArea area); + /** + * Drop floating widget into auto hide side bar + */ + void dropIntoAutoHideSideBar(CFloatingDockContainer* FloatingWidget, DockWidgetArea area); + + /** + * Creates a new tab for a widget dropped into the center of a section + */ + void dropIntoCenterOfSection(CFloatingDockContainer* FloatingWidget, + CDockAreaWidget* TargetArea, int TabIndex = 0); + /** * Drop floating widget into dock area */ void dropIntoSection(CFloatingDockContainer* FloatingWidget, - CDockAreaWidget* TargetArea, DockWidgetArea area); + CDockAreaWidget* TargetArea, DockWidgetArea area, int TabIndex = 0); /** * Moves the dock widget or dock area given in Widget parameter to a * new dock widget area */ - void moveToNewSection(QWidget* Widget, CDockAreaWidget* TargetArea, DockWidgetArea area); + void moveToNewSection(QWidget* Widget, CDockAreaWidget* TargetArea, DockWidgetArea area, + int TabIndex = 0); /** * Moves the dock widget or dock area given in Widget parameter to a @@ -202,13 +214,13 @@ class DockContainerWidgetPrivate /** * Creates a new tab for a widget dropped into the center of a section */ - void dropIntoCenterOfSection(CFloatingDockContainer* FloatingWidget, - CDockAreaWidget* TargetArea); + void moveIntoCenterOfSection(QWidget* Widget, CDockAreaWidget* TargetArea, int TabIndex = 0); /** - * Creates a new tab for a widget dropped into the center of a section + * Moves the dock widget or dock area given in Widget parameter to + * a auto hide sidebar area */ - void moveIntoCenterOfSection(QWidget* Widget, CDockAreaWidget* TargetArea); + void moveToAutoHideSideBar(QWidget* Widget, DockWidgetArea area, int TabIndex = TabDefaultInsertIndex); /** @@ -238,7 +250,7 @@ class DockContainerWidgetPrivate * \param[out] CreatedWidget The widget created from parsed data or 0 if * the parsed widget was an empty splitter * \param[in] Testing If Testing is true, only the stream data is - * parsed without modifiying anything. + * parsed without modifying anything. */ bool restoreChildNodes(CDockingStateReader& Stream, QWidget*& CreatedWidget, bool Testing); @@ -287,7 +299,11 @@ class DockContainerWidgetPrivate VisibleDockAreaCount = 0; for (auto DockArea : DockAreas) { - VisibleDockAreaCount += DockArea->isHidden() ? 0 : 1; + if (!DockArea) + { + continue; + } + VisibleDockAreaCount += (DockArea->isHidden() ? 0 : 1); } } @@ -454,7 +470,7 @@ void DockContainerWidgetPrivate::dropIntoContainer(CFloatingDockContainer* Float CDockContainerWidget* FloatingDockContainer = FloatingWidget->dockContainer(); auto NewDockAreas = FloatingDockContainer->findChildren( QString(), Qt::FindChildrenRecursively); - QSplitter* Splitter = RootSplitter; + auto Splitter = RootSplitter; if (DockAreas.count() <= 1) { @@ -462,7 +478,7 @@ void DockContainerWidgetPrivate::dropIntoContainer(CFloatingDockContainer* Float } else if (Splitter->orientation() != InsertParam.orientation()) { - QSplitter* NewSplitter = newSplitter(InsertParam.orientation()); + auto NewSplitter = newSplitter(InsertParam.orientation()); QLayoutItem* li = Layout->replaceWidget(Splitter, NewSplitter); NewSplitter->addWidget(Splitter); updateSplitterHandles(NewSplitter); @@ -505,14 +521,33 @@ void DockContainerWidgetPrivate::dropIntoContainer(CFloatingDockContainer* Float } +//============================================================================ +void DockContainerWidgetPrivate::dropIntoAutoHideSideBar(CFloatingDockContainer* FloatingWidget, DockWidgetArea area) +{ + auto SideBarLocation = internal::toSideBarLocation(area); + auto NewDockAreas = FloatingWidget->findChildren( + QString(), Qt::FindChildrenRecursively); + int TabIndex = DockManager->containerOverlay()->tabIndexUnderCursor(); + for (auto DockArea : NewDockAreas) + { + auto DockWidgets = DockArea->dockWidgets(); + for (auto DockWidget : DockWidgets) + { + _this->createAndSetupAutoHideContainer(SideBarLocation, DockWidget, TabIndex++); + } + } +} + + //============================================================================ void DockContainerWidgetPrivate::dropIntoCenterOfSection( - CFloatingDockContainer* FloatingWidget, CDockAreaWidget* TargetArea) + CFloatingDockContainer* FloatingWidget, CDockAreaWidget* TargetArea, int TabIndex) { CDockContainerWidget* FloatingContainer = FloatingWidget->dockContainer(); auto NewDockWidgets = FloatingContainer->dockWidgets(); auto TopLevelDockArea = FloatingContainer->topLevelDockArea(); int NewCurrentIndex = -1; + TabIndex = qMax(0, TabIndex); // If the floating widget contains only one single dock are, then the // current dock widget of the dock area will also be the future current @@ -525,7 +560,7 @@ void DockContainerWidgetPrivate::dropIntoCenterOfSection( for (int i = 0; i < NewDockWidgets.count(); ++i) { CDockWidget* DockWidget = NewDockWidgets[i]; - TargetArea->insertDockWidget(i, DockWidget, false); + TargetArea->insertDockWidget(TabIndex + i, DockWidget, false); // If the floating widget contains multiple visible dock areas, then we // simply pick the first visible open dock widget and make it // the current one. @@ -534,7 +569,7 @@ void DockContainerWidgetPrivate::dropIntoCenterOfSection( NewCurrentIndex = i; } } - TargetArea->setCurrentIndex(NewCurrentIndex); + TargetArea->setCurrentIndex(NewCurrentIndex + TabIndex); TargetArea->updateTitleBarVisibility(); return; } @@ -542,13 +577,13 @@ void DockContainerWidgetPrivate::dropIntoCenterOfSection( //============================================================================ void DockContainerWidgetPrivate::dropIntoSection(CFloatingDockContainer* FloatingWidget, - CDockAreaWidget* TargetArea, DockWidgetArea area) + CDockAreaWidget* TargetArea, DockWidgetArea area, int TabIndex) { // Dropping into center means all dock widgets in the dropped floating // widget will become tabs of the drop area if (CenterDockWidgetArea == area) { - dropIntoCenterOfSection(FloatingWidget, TargetArea); + dropIntoCenterOfSection(FloatingWidget, TargetArea, TabIndex); return; } @@ -556,16 +591,7 @@ void DockContainerWidgetPrivate::dropIntoSection(CFloatingDockContainer* Floatin auto InsertParam = internal::dockAreaInsertParameters(area); auto NewDockAreas = FloatingContainer->findChildren( QString(), Qt::FindChildrenRecursively); - QSplitter* TargetAreaSplitter = internal::findParent(TargetArea); - - if (!TargetAreaSplitter) - { - QSplitter* Splitter = newSplitter(InsertParam.orientation()); - Layout->replaceWidget(TargetArea, Splitter); - Splitter->addWidget(TargetArea); - updateSplitterHandles(Splitter); - TargetAreaSplitter = Splitter; - } + auto TargetAreaSplitter = TargetArea->parentSplitter(); int AreaIndex = TargetAreaSplitter->indexOf(TargetArea); auto FloatingSplitter = FloatingContainer->rootSplitter(); if (TargetAreaSplitter->orientation() == InsertParam.orientation()) @@ -638,11 +664,13 @@ void DockContainerWidgetPrivate::dropIntoSection(CFloatingDockContainer* Floatin //============================================================================ -void DockContainerWidgetPrivate::moveIntoCenterOfSection(QWidget* Widget, CDockAreaWidget* TargetArea) +void DockContainerWidgetPrivate::moveIntoCenterOfSection(QWidget* Widget, CDockAreaWidget* TargetArea, + int TabIndex) { auto DroppedDockWidget = qobject_cast(Widget); auto DroppedArea = qobject_cast(Widget); + TabIndex = qMax(0, TabIndex); if (DroppedDockWidget) { CDockAreaWidget* OldDockArea = DroppedDockWidget->dockAreaWidget(); @@ -655,7 +683,7 @@ void DockContainerWidgetPrivate::moveIntoCenterOfSection(QWidget* Widget, CDockA { OldDockArea->removeDockWidget(DroppedDockWidget); } - TargetArea->insertDockWidget(0, DroppedDockWidget, true); + TargetArea->insertDockWidget(TabIndex, DroppedDockWidget, true); } else { @@ -664,9 +692,9 @@ void DockContainerWidgetPrivate::moveIntoCenterOfSection(QWidget* Widget, CDockA for (int i = 0; i < NewDockWidgets.count(); ++i) { CDockWidget* DockWidget = NewDockWidgets[i]; - TargetArea->insertDockWidget(i, DockWidget, false); + TargetArea->insertDockWidget(TabIndex + i, DockWidget, false); } - TargetArea->setCurrentIndex(NewCurrentIndex); + TargetArea->setCurrentIndex(TabIndex + NewCurrentIndex); DroppedArea->dockContainer()->removeDockArea(DroppedArea); DroppedArea->deleteLater(); } @@ -677,13 +705,14 @@ void DockContainerWidgetPrivate::moveIntoCenterOfSection(QWidget* Widget, CDockA //============================================================================ -void DockContainerWidgetPrivate::moveToNewSection(QWidget* Widget, CDockAreaWidget* TargetArea, DockWidgetArea area) +void DockContainerWidgetPrivate::moveToNewSection(QWidget* Widget, CDockAreaWidget* TargetArea, DockWidgetArea area, + int TabIndex) { // Dropping into center means all dock widgets in the dropped floating // widget will become tabs of the drop area if (CenterDockWidgetArea == area) { - moveIntoCenterOfSection(Widget, TargetArea); + moveIntoCenterOfSection(Widget, TargetArea, TabIndex); return; } @@ -708,7 +737,7 @@ void DockContainerWidgetPrivate::moveToNewSection(QWidget* Widget, CDockAreaWidg } auto InsertParam = internal::dockAreaInsertParameters(area); - QSplitter* TargetAreaSplitter = internal::findParent(TargetArea); + auto TargetAreaSplitter = TargetArea->parentSplitter(); int AreaIndex = TargetAreaSplitter->indexOf(TargetArea); auto Sizes = TargetAreaSplitter->sizes(); if (TargetAreaSplitter->orientation() == InsertParam.orientation()) @@ -738,6 +767,48 @@ void DockContainerWidgetPrivate::moveToNewSection(QWidget* Widget, CDockAreaWidg } +//============================================================================ +void DockContainerWidgetPrivate::moveToAutoHideSideBar(QWidget* Widget, DockWidgetArea area, int TabIndex) +{ + CDockWidget* DroppedDockWidget = qobject_cast(Widget); + CDockAreaWidget* DroppedDockArea = qobject_cast(Widget); + auto SideBarLocation = internal::toSideBarLocation(area); + + if (DroppedDockWidget) + { + if (_this == DroppedDockWidget->dockContainer()) + { + DroppedDockWidget->setAutoHide(true, SideBarLocation, TabIndex); + } + else + { + _this->createAndSetupAutoHideContainer(SideBarLocation, DroppedDockWidget, TabIndex); + } + } + else + { + if (_this == DroppedDockArea->dockContainer()) + { + DroppedDockArea->setAutoHide(true, SideBarLocation, TabIndex); + } + else + { + for (const auto DockWidget : DroppedDockArea->openedDockWidgets()) + { + if (!DockWidget->features().testFlag( + CDockWidget::DockWidgetPinnable)) + { + continue; + } + + _this->createAndSetupAutoHideContainer(SideBarLocation, + DockWidget, TabIndex++); + } + } + } +} + + //============================================================================ void DockContainerWidgetPrivate::updateSplitterHandles( QSplitter* splitter ) { @@ -801,7 +872,7 @@ void DockContainerWidgetPrivate::moveToContainer(QWidget* Widget, DockWidgetArea // it already has and do nothing, if it is the same place. It would // also work without this check, but it looks nicer with the check // because there will be no layout updates - auto Splitter = internal::findParent(DroppedDockArea); + auto Splitter = DroppedDockArea->parentSplitter(); auto InsertParam = internal::dockAreaInsertParameters(area); if (Splitter == RootSplitter && InsertParam.orientation() == Splitter->orientation()) { @@ -857,7 +928,10 @@ void DockContainerWidgetPrivate::addDockAreasToList(const QList NewDockAreas) { - DockAreas.append(NewDockAreas); + for (auto *newDockArea : NewDockAreas) + { + DockAreas.append(newDockArea); + } for (auto DockArea : NewDockAreas) { QObject::connect(DockArea, @@ -908,7 +982,7 @@ void DockContainerWidgetPrivate::saveAutoHideWidgetsState(QXmlStreamWriter& s) { for (const auto sideTabBar : SideTabBarWidgets.values()) { - if (!sideTabBar->tabCount()) + if (!sideTabBar->count()) { continue; } @@ -1101,12 +1175,12 @@ bool DockContainerWidgetPrivate::restoreSideBar(CDockingStateReader& s, continue; } - auto SideBar = _this->sideTabBar(Area); + auto SideBar = _this->autoHideSideBar(Area); CAutoHideDockContainer* AutoHideContainer; if (DockWidget->isAutoHide()) { AutoHideContainer = DockWidget->autoHideDockContainer(); - if (AutoHideContainer->sideBar() != SideBar) + if (AutoHideContainer->autoHideSideBar() != SideBar) { SideBar->addAutoHideWidget(AutoHideContainer); } @@ -1193,7 +1267,7 @@ void DockContainerWidgetPrivate::addDockArea(CDockAreaWidget* NewDockArea, DockW } else { - QSplitter* NewSplitter = newSplitter(InsertParam.orientation()); + auto NewSplitter = newSplitter(InsertParam.orientation()); if (InsertParam.append()) { QLayoutItem* li = Layout->replaceWidget(Splitter, NewSplitter); @@ -1288,7 +1362,7 @@ CDockAreaWidget* DockContainerWidgetPrivate::addDockWidgetToDockArea(DockWidgetA NewDockArea->addDockWidget(Dockwidget); auto InsertParam = internal::dockAreaInsertParameters(area); - QSplitter* TargetAreaSplitter = internal::findParent(TargetDockArea); + auto TargetAreaSplitter = TargetDockArea->parentSplitter(); int index = TargetAreaSplitter ->indexOf(TargetDockArea); if (TargetAreaSplitter->orientation() == InsertParam.orientation()) { @@ -1305,7 +1379,7 @@ CDockAreaWidget* DockContainerWidgetPrivate::addDockWidgetToDockArea(DockWidgetA { ADS_PRINT("TargetAreaSplitter->orientation() != InsertParam.orientation()"); auto TargetAreaSizes = TargetAreaSplitter->sizes(); - QSplitter* NewSplitter = newSplitter(InsertParam.orientation()); + auto NewSplitter = newSplitter(InsertParam.orientation()); NewSplitter->addWidget(TargetDockArea); insertWidgetIntoSplitter(NewSplitter, NewDockArea, InsertParam.append()); @@ -1404,7 +1478,7 @@ CDockAreaWidget* CDockContainerWidget::addDockWidget(DockWidgetArea area, CDockW //============================================================================ CAutoHideDockContainer* CDockContainerWidget::createAndSetupAutoHideContainer( - SideBarLocation area, CDockWidget* DockWidget) + SideBarLocation area, CDockWidget* DockWidget, int TabIndex) { if (!CDockManager::testAutoHideConfigFlag(CDockManager::AutoHideFeatureEnabled)) { @@ -1417,7 +1491,7 @@ CAutoHideDockContainer* CDockContainerWidget::createAndSetupAutoHideContainer( DockWidget->setDockManager(d->DockManager); // Auto hide Dock Container needs a valid dock manager } - return sideTabBar(area)->insertDockWidget(-1, DockWidget); + return autoHideSideBar(area)->insertDockWidget(TabIndex, DockWidget); } @@ -1496,7 +1570,7 @@ void CDockContainerWidget::removeDockArea(CDockAreaWidget* area) area->disconnect(this); d->DockAreas.removeAll(area); - CDockSplitter* Splitter = internal::findParent(area); + auto Splitter = area->parentSplitter(); // Remove are from parent splitter and recursively hide tree of parent // splitters if it has no visible content @@ -1528,7 +1602,7 @@ void CDockContainerWidget::removeDockArea(CDockAreaWidget* area) } QWidget* widget = Splitter->widget(0); - QSplitter* ChildSplitter = qobject_cast(widget); + auto ChildSplitter = qobject_cast(widget); // If the one and only content widget of the splitter is not a splitter // then we are finished if (!ChildSplitter) @@ -1569,12 +1643,21 @@ void CDockContainerWidget::removeDockArea(CDockAreaWidget* area) } +//============================================================================ +QList> CDockContainerWidget::removeAllDockAreas() +{ + auto Result = d->DockAreas; + d->DockAreas.clear(); + return Result; +} + + //============================================================================ CDockAreaWidget* CDockContainerWidget::dockAreaAt(const QPoint& GlobalPos) const { for (const auto& DockArea : d->DockAreas) { - if (DockArea->isVisible() && DockArea->rect().contains(DockArea->mapFromGlobal(GlobalPos))) + if (DockArea && DockArea->isVisible() && DockArea->rect().contains(DockArea->mapFromGlobal(GlobalPos))) { return DockArea; } @@ -1611,7 +1694,7 @@ int CDockContainerWidget::visibleDockAreaCount() const int Result = 0; for (auto DockArea : d->DockAreas) { - Result += DockArea->isHidden() ? 0 : 1; + Result += (!DockArea || DockArea->isHidden()) ? 0 : 1; } return Result; @@ -1629,11 +1712,12 @@ void CDockContainerWidget::dropFloatingWidget(CFloatingDockContainer* FloatingWi ADS_PRINT("CDockContainerWidget::dropFloatingWidget"); CDockWidget* SingleDroppedDockWidget = FloatingWidget->topLevelDockWidget(); CDockWidget* SingleDockWidget = topLevelDockWidget(); - CDockAreaWidget* DockArea = dockAreaAt(TargetPos); auto dropArea = InvalidDockWidgetArea; auto ContainerDropArea = d->DockManager->containerOverlay()->dropAreaUnderCursor(); bool Dropped = false; + CDockAreaWidget* DockArea = dockAreaAt(TargetPos); + // mouse is over dock area if (DockArea) { auto dropOverlay = d->DockManager->dockAreaOverlay(); @@ -1648,35 +1732,40 @@ void CDockContainerWidget::dropFloatingWidget(CFloatingDockContainer* FloatingWi if (dropArea != InvalidDockWidgetArea) { ADS_PRINT("Dock Area Drop Content: " << dropArea); - d->dropIntoSection(FloatingWidget, DockArea, dropArea); + int TabIndex = d->DockManager->dockAreaOverlay()->tabIndexUnderCursor(); + d->dropIntoSection(FloatingWidget, DockArea, dropArea, TabIndex); Dropped = true; } } - // mouse is over container - if (InvalidDockWidgetArea == dropArea) + // mouse is over container or auto hide side bar + if (InvalidDockWidgetArea == dropArea && InvalidDockWidgetArea != ContainerDropArea) { - dropArea = ContainerDropArea; - ADS_PRINT("Container Drop Content: " << dropArea); - if (dropArea != InvalidDockWidgetArea) - { - d->dropIntoContainer(FloatingWidget, dropArea); - Dropped = true; - } + if (internal::isSideBarArea(ContainerDropArea)) + { + ADS_PRINT("Container Drop Content: " << ContainerDropArea); + d->dropIntoAutoHideSideBar(FloatingWidget, ContainerDropArea); + } + else + { + ADS_PRINT("Container Drop Content: " << ContainerDropArea); + d->dropIntoContainer(FloatingWidget, ContainerDropArea); + } + Dropped = true; } // Remove the auto hide widgets from the FloatingWidget and insert // them into this widget for (auto AutohideWidget : FloatingWidget->dockContainer()->autoHideWidgets()) { - auto SideBar = sideTabBar(AutohideWidget->sideBarLocation()); + auto SideBar = autoHideSideBar(AutohideWidget->sideBarLocation()); SideBar->addAutoHideWidget(AutohideWidget); } if (Dropped) { // Fix https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/issues/351 - FloatingWidget->hideAndDeleteLater(); + FloatingWidget->finishDropOperation(); // If we dropped a floating widget with only one single dock widget, then we // drop a top level widget that changes from floating to docked now @@ -1697,12 +1786,17 @@ void CDockContainerWidget::dropFloatingWidget(CFloatingDockContainer* FloatingWi //============================================================================ -void CDockContainerWidget::dropWidget(QWidget* Widget, DockWidgetArea DropArea, CDockAreaWidget* TargetAreaWidget) +void CDockContainerWidget::dropWidget(QWidget* Widget, DockWidgetArea DropArea, CDockAreaWidget* TargetAreaWidget, + int TabIndex) { CDockWidget* SingleDockWidget = topLevelDockWidget(); if (TargetAreaWidget) { - d->moveToNewSection(Widget, TargetAreaWidget, DropArea); + d->moveToNewSection(Widget, TargetAreaWidget, DropArea, TabIndex); + } + else if (internal::isSideBarArea(DropArea)) + { + d->moveToAutoHideSideBar(Widget, DropArea, TabIndex); } else { @@ -1724,7 +1818,7 @@ QList CDockContainerWidget::openedDockAreas() const QList Result; for (auto DockArea : d->DockAreas) { - if (!DockArea->isHidden()) + if (DockArea && !DockArea->isHidden()) { Result.append(DockArea); } @@ -1740,7 +1834,7 @@ QList CDockContainerWidget::openedDockWidgets() const QList DockWidgetList; for (auto DockArea : d->DockAreas) { - if (!DockArea->isHidden()) + if (DockArea && !DockArea->isHidden()) { DockWidgetList.append(DockArea->openedDockWidgets()); } @@ -1755,7 +1849,7 @@ bool CDockContainerWidget::hasOpenDockAreas() const { for (auto DockArea : d->DockAreas) { - if (!DockArea->isHidden()) + if (DockArea && !DockArea->isHidden()) { return true; } @@ -1845,17 +1939,18 @@ bool CDockContainerWidget::restoreState(CDockingStateReader& s, bool Testing) NewRootSplitter = d->newSplitter(Qt::Horizontal); } - d->Layout->replaceWidget(d->RootSplitter, NewRootSplitter); - QSplitter* OldRoot = d->RootSplitter; - d->RootSplitter = qobject_cast(NewRootSplitter); + QLayoutItem* li = d->Layout->replaceWidget(d->RootSplitter, NewRootSplitter); + auto OldRoot = d->RootSplitter; + d->RootSplitter = qobject_cast(NewRootSplitter); OldRoot->deleteLater(); + delete li; return true; } //============================================================================ -QSplitter* CDockContainerWidget::rootSplitter() const +CDockSplitter* CDockContainerWidget::rootSplitter() const { return d->RootSplitter; } @@ -1977,8 +2072,12 @@ CDockAreaWidget* CDockContainerWidget::topLevelDockArea() const QList CDockContainerWidget::dockWidgets() const { QList Result; - for (const auto DockArea : d->DockAreas) + for (const auto& DockArea : d->DockAreas) { + if (!DockArea) + { + continue; + } Result.append(DockArea->dockWidgets()); } @@ -2009,8 +2108,12 @@ void CDockContainerWidget::removeAutoHideWidget(CAutoHideDockContainer* Autohide CDockWidget::DockWidgetFeatures CDockContainerWidget::features() const { CDockWidget::DockWidgetFeatures Features(CDockWidget::AllDockWidgetFeatures); - for (const auto DockArea : d->DockAreas) + for (const auto& DockArea : d->DockAreas) { + if (!DockArea) + { + continue; + } Features &= DockArea->features(); } @@ -2028,9 +2131,9 @@ CFloatingDockContainer* CDockContainerWidget::floatingWidget() const //============================================================================ void CDockContainerWidget::closeOtherAreas(CDockAreaWidget* KeepOpenArea) { - for (const auto DockArea : d->DockAreas) + for (const auto& DockArea : d->DockAreas) { - if (DockArea == KeepOpenArea) + if (!DockArea || DockArea == KeepOpenArea) { continue; } @@ -2051,7 +2154,7 @@ void CDockContainerWidget::closeOtherAreas(CDockAreaWidget* KeepOpenArea) } //============================================================================ -CAutoHideSideBar* CDockContainerWidget::sideTabBar(SideBarLocation area) const +CAutoHideSideBar* CDockContainerWidget::autoHideSideBar(SideBarLocation area) const { return d->SideTabBarWidgets[area]; } @@ -2065,7 +2168,21 @@ QRect CDockContainerWidget::contentRect() const return QRect(); } - return d->RootSplitter->geometry(); + if (d->RootSplitter->hasVisibleContent()) + { + return d->RootSplitter->geometry(); + } + else + { + auto ContentRect = this->rect(); + ContentRect.adjust( + autoHideSideBar(SideBarLeft)->sizeHint().width(), + autoHideSideBar(SideBarTop)->sizeHint().height(), + -autoHideSideBar(SideBarRight)->sizeHint().width(), + -autoHideSideBar(SideBarBottom)->sizeHint().height()); + + return ContentRect; + } } diff --git a/src/DockContainerWidget.h b/src/DockContainerWidget.h index 4a32e5a94..f55855a3d 100644 --- a/src/DockContainerWidget.h +++ b/src/DockContainerWidget.h @@ -53,13 +53,14 @@ struct FloatingDragPreviewPrivate; class CDockingStateReader; class CAutoHideSideBar; class CAutoHideTab; +class CDockSplitter; struct AutoHideTabPrivate; struct AutoHideDockContainerPrivate; /** * Container that manages a number of dock areas with single dock widgets - * or tabyfied dock widgets in each area. + * or tabified dock widgets in each area. * Each window that support docking has a DockContainerWidget. That means * the main application window and all floating windows contain a * DockContainerWidget instance. @@ -94,14 +95,14 @@ class ADS_EXPORT CDockContainerWidget : public QFrame /** * Access function for the internal root splitter */ - QSplitter* rootSplitter() const; + CDockSplitter* 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); + CAutoHideDockContainer* createAndSetupAutoHideContainer(SideBarLocation area, CDockWidget* DockWidget, int TabIndex = -1); /** * Helper function for creation of the root splitter @@ -125,7 +126,8 @@ class ADS_EXPORT CDockContainerWidget : public QFrame * a nullptr, then the DropArea indicates the drop area in the given * TargetAreaWidget */ - void dropWidget(QWidget* Widget, DockWidgetArea DropArea, CDockAreaWidget* TargetAreaWidget); + void dropWidget(QWidget* Widget, DockWidgetArea DropArea, CDockAreaWidget* TargetAreaWidget, + int TabIndex = -1); /** * Adds the given dock area to this container widget @@ -137,6 +139,11 @@ class ADS_EXPORT CDockContainerWidget : public QFrame */ void removeDockArea(CDockAreaWidget* area); + /** + * Remove all dock areas and returns the list of removed dock areas + */ + QList> removeAllDockAreas(); + /** * Saves the state into the given stream */ @@ -322,7 +329,7 @@ class ADS_EXPORT CDockContainerWidget : public QFrame /** * Returns the side tab widget for the given area */ - CAutoHideSideBar* sideTabBar(SideBarLocation area) const; + CAutoHideSideBar* autoHideSideBar(SideBarLocation area) const; /** diff --git a/src/DockFocusController.cpp b/src/DockFocusController.cpp index a85d01088..57112c785 100644 --- a/src/DockFocusController.cpp +++ b/src/DockFocusController.cpp @@ -49,6 +49,7 @@ struct DockFocusControllerPrivate #endif CDockManager* DockManager; bool ForceFocusChangedSignal = false; + bool TabPressed = false; /** * Private data constructor @@ -114,6 +115,7 @@ DockFocusControllerPrivate::DockFocusControllerPrivate( //============================================================================ void DockFocusControllerPrivate::updateDockWidgetFocus(CDockWidget* DockWidget) { + if (!DockWidget) return; if (!DockWidget->features().testFlag(CDockWidget::DockWidgetFocusable)) { return; @@ -267,7 +269,9 @@ void CDockFocusController::onApplicationFocusChanged(QWidget* focusedOld, QWidge { Q_UNUSED(focusedOld); - if (d->DockManager->isRestoringState()) + // Ignore focus changes if we are restoring state, or if user clicked + // a tab which in turn caused the focus change + if (d->DockManager->isRestoringState() || d->TabPressed) { return; } @@ -418,6 +422,19 @@ CDockWidget* CDockFocusController::focusedDockWidget() const return d->FocusedDockWidget.data(); } + +//========================================================================== +CDockAreaWidget* CDockFocusController::focusedDockArea() const +{ + return d->FocusedArea.data(); +} + + +//========================================================================== +void CDockFocusController::setDockWidgetTabPressed(bool Value) +{ + d->TabPressed = Value; +} } // namespace ads //--------------------------------------------------------------------------- diff --git a/src/DockFocusController.h b/src/DockFocusController.h index 06ec049fc..80f0c2af4 100644 --- a/src/DockFocusController.h +++ b/src/DockFocusController.h @@ -69,6 +69,12 @@ private Q_SLOTS: */ CDockWidget* focusedDockWidget() const; + /** + * Returns the dock area that contains the focusedDockWidget() or nullptr if + * the focused dock widget is not in this area. + */ + CDockAreaWidget* focusedDockArea() const; + /** * Request focus highlighting for the given dock widget assigned to the tab * given in Tab parameter @@ -80,6 +86,12 @@ private Q_SLOTS: */ void clearDockWidgetFocus(CDockWidget* dockWidget); + /** + * Notifies the dock focus controller, that a the mouse is pressed or + * released + */ + void setDockWidgetTabPressed(bool Value); + public Q_SLOTS: /** * Request a focus change to the given dock widget diff --git a/src/DockManager.cpp b/src/DockManager.cpp index 9ccaec3e7..9678cbfa4 100644 --- a/src/DockManager.cpp +++ b/src/DockManager.cpp @@ -48,6 +48,7 @@ #include #include #include +#include #include "FloatingDockContainer.h" #include "DockOverlay.h" @@ -59,6 +60,8 @@ #include "DockAreaTitleBar.h" #include "DockFocusController.h" #include "DockSplitter.h" +#include "DockComponentsFactory.h" + #if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) #include "linux/FloatingWidgetTitleBar.h" @@ -94,6 +97,7 @@ enum eStateFileVersion static CDockManager::ConfigFlags StaticConfigFlags = CDockManager::DefaultNonOpaqueConfig; static CDockManager::AutoHideFlags StaticAutoHideConfigFlags; // auto hide feature is disabled by default +static QVector StaticConfigParams(CDockManager::ConfigParamCount); static QString FloatingContainersTitle; @@ -103,8 +107,8 @@ static QString FloatingContainersTitle; struct DockManagerPrivate { CDockManager* _this; - QList FloatingWidgets; - QList HiddenFloatingWidgets; + QList> FloatingWidgets; + QList> HiddenFloatingWidgets; QList Containers; CDockOverlay* ContainerOverlay; CDockOverlay* DockAreaOverlay; @@ -118,6 +122,12 @@ struct DockManagerPrivate CDockFocusController* FocusController = nullptr; CDockWidget* CentralWidget = nullptr; bool IsLeavingMinimized = false; + Qt::ToolButtonStyle ToolBarStyleDocked = Qt::ToolButtonIconOnly; + Qt::ToolButtonStyle ToolBarStyleFloating = Qt::ToolButtonTextUnderIcon; + QSize ToolBarIconSizeDocked = QSize(16, 16); + QSize ToolBarIconSizeFloating = QSize(24, 24); + CDockWidget::DockWidgetFeatures LockedDockWidgetFeatures; + QSharedPointer ComponentFactory {ads::CDockComponentsFactory::factory()}; /** * Private data constructor @@ -149,7 +159,10 @@ struct DockManagerPrivate // Hide updates of floating widgets from user for (auto FloatingWidget : FloatingWidgets) { - FloatingWidget->hide(); + if (FloatingWidget) + { + FloatingWidget->hide(); + } } } @@ -329,7 +342,8 @@ bool DockManagerPrivate::restoreStateFromXml(const QByteArray &state, int versi int FloatingWidgetIndex = DockContainerCount - 1; for (int i = FloatingWidgetIndex; i < FloatingWidgets.count(); ++i) { - auto* floatingWidget = FloatingWidgets[i]; + CFloatingDockContainer* floatingWidget = FloatingWidgets[i]; + if (!floatingWidget) continue; _this->removeDockContainer(floatingWidget->dockContainer()); floatingWidget->deleteLater(); } @@ -516,14 +530,27 @@ CDockManager::CDockManager(QWidget *parent) : window()->installEventFilter(this); #if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) - connect(qApp, &QApplication::focusWindowChanged, [](QWindow* focusWindow) + connect(qApp, &QApplication::focusWindowChanged, this, [this](QWindow* focusWindow) { - // bring modal dialogs to foreground to ensure that they are in front of any - // floating dock widget - if (focusWindow && focusWindow->isModal()) + if (!focusWindow) { - focusWindow->raise(); + return; } + + // bring the main application window that hosts the dock manager and all floating + // widgets in front of any other application + this->raise(); + for (auto FloatingWidget : d->FloatingWidgets) + { + if (FloatingWidget) + { + FloatingWidget->raise(); + } + } + + // ensure that the dragged floating window is in front of the main application window + // and any other floating widget - this will also ensure that modal dialogs come to foreground + focusWindow->raise(); }); #endif } @@ -532,28 +559,73 @@ CDockManager::CDockManager(QWidget *parent) : CDockManager::~CDockManager() { // fix memory leaks, see https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/issues/307 - std::vector areas; - for ( int i = 0; i != dockAreaCount(); ++i ) - { - areas.push_back( dockArea(i) ); - } - for ( auto area : areas ) - { - for ( auto widget : area->dockWidgets() ) - delete widget; + std::vector> areas; + for (int i = 0; i != dockAreaCount(); ++i) + { + areas.push_back( dockArea(i) ); + } + for ( auto area : areas ) + { + if (!area || area->dockManager() != this) continue; - delete area; - } + // QPointer delete safety - just in case some dock widget in destruction + // deletes another related/twin or child dock widget. + std::vector> deleteWidgets; + for ( auto widget : area->dockWidgets() ) + { + deleteWidgets.push_back(widget); + } + for ( auto ptrWdg : deleteWidgets) + { + delete ptrWdg; + } + } auto FloatingWidgets = d->FloatingWidgets; for (auto FloatingWidget : FloatingWidgets) { + FloatingWidget->deleteContent(); delete FloatingWidget; } + // Delete Dock Widgets before Areas so widgets can access them late (like dtor) + for ( auto area : areas ) + { + delete area; + } + delete d; } + +//============================================================================ +CDockWidget* CDockManager::createDockWidget(const QString &title, QWidget* parent) +{ + return new CDockWidget(this, title, parent); +} + + +//============================================================================ +QSharedPointer CDockManager::componentsFactory() const +{ + return d->ComponentFactory; +} + + +//============================================================================ +void CDockManager::setComponentsFactory(ads::CDockComponentsFactory* factory) +{ + setComponentsFactory(QSharedPointer(factory)); +} + + +//============================================================================ +void CDockManager::setComponentsFactory(QSharedPointer factory) +{ + d->ComponentFactory = factory; +} + + //============================================================================ #if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) bool CDockManager::eventFilter(QObject *obj, QEvent *e) @@ -564,7 +636,7 @@ bool CDockManager::eventFilter(QObject *obj, QEvent *e) // Window always on top of the MainWindow. if (e->type() == QEvent::WindowActivate) { - for (auto _window : floatingWidgets()) + for (auto _window : d->FloatingWidgets) { if (!_window->isVisible() || window()->isMinimized()) { @@ -586,7 +658,7 @@ bool CDockManager::eventFilter(QObject *obj, QEvent *e) } else if (e->type() == QEvent::WindowDeactivate) { - for (auto _window : floatingWidgets()) + for (auto _window : d->FloatingWidgets) { if (!_window->isVisible() || window()->isMinimized()) { @@ -609,7 +681,7 @@ bool CDockManager::eventFilter(QObject *obj, QEvent *e) // Sync minimize with MainWindow if (e->type() == QEvent::WindowStateChange) { - for (auto _window : floatingWidgets()) + for (auto _window : d->FloatingWidgets) { if (! _window->isVisible()) { @@ -721,7 +793,12 @@ const QList CDockManager::dockContainers() const //============================================================================ const QList CDockManager::floatingWidgets() const { - return d->FloatingWidgets; + QList res; + for (auto &fl : d->FloatingWidgets) + { + if (fl) res.append(fl); + } + return res; } @@ -1121,7 +1198,7 @@ QAction* CDockManager::addToggleViewActionToMenu(QAction* ToggleViewAction, bool AlphabeticallySorted = (MenuAlphabeticallySorted == d->MenuInsertionOrder); if (!Group.isEmpty()) { - QMenu* GroupMenu = d->ViewMenuGroups.value(Group, 0); + QMenu* GroupMenu = d->ViewMenuGroups.value(Group, nullptr); if (!GroupMenu) { GroupMenu = new QMenu(Group, this); @@ -1285,7 +1362,7 @@ void CDockManager::hideManagerAndFloatingWidgets() d->HiddenFloatingWidgets.push_back( FloatingWidget ); FloatingWidget->hide(); - // hidding floating widget automatically marked contained CDockWidgets as hidden + // hiding floating widget automatically marked contained CDockWidgets as hidden // but they must remain marked as visible as we want them to be restored visible // when CDockManager will be shown back for ( auto dockWidget : VisibleWidgets ) @@ -1314,7 +1391,7 @@ QList CDockManager::splitterSizes(CDockAreaWidget *ContainedArea) const { if (ContainedArea) { - auto Splitter = internal::findParent(ContainedArea); + auto Splitter = ContainedArea->parentSplitter(); if (Splitter) { return Splitter->sizes(); @@ -1331,7 +1408,7 @@ void CDockManager::setSplitterSizes(CDockAreaWidget *ContainedArea, const QList< return; } - auto Splitter = internal::findParent(ContainedArea); + auto Splitter = ContainedArea->parentSplitter(); if (Splitter && Splitter->count() == sizes.count()) { Splitter->setSizes(sizes); @@ -1361,6 +1438,114 @@ QString CDockManager::floatingContainersTitle() return FloatingContainersTitle; } + +//=========================================================================== +void CDockManager::setDockWidgetToolBarStyle(Qt::ToolButtonStyle Style, CDockWidget::eState State) +{ + if (CDockWidget::StateFloating == State) + { + d->ToolBarStyleFloating = Style; + } + else + { + d->ToolBarStyleDocked = Style; + } +} + + +//=========================================================================== +Qt::ToolButtonStyle CDockManager::dockWidgetToolBarStyle(CDockWidget::eState State) const +{ + if (CDockWidget::StateFloating == State) + { + return d->ToolBarStyleFloating; + } + else + { + return d->ToolBarStyleDocked; + } +} + + +//=========================================================================== +void CDockManager::setDockWidgetToolBarIconSize(const QSize& IconSize, CDockWidget::eState State) +{ + if (CDockWidget::StateFloating == State) + { + d->ToolBarIconSizeFloating = IconSize; + } + else + { + d->ToolBarIconSizeDocked = IconSize; + } +} + + +//=========================================================================== +QSize CDockManager::dockWidgetToolBarIconSize(CDockWidget::eState State) const +{ + if (CDockWidget::StateFloating == State) + { + return d->ToolBarIconSizeFloating; + } + else + { + return d->ToolBarIconSizeDocked; + } +} + + +//=========================================================================== +void CDockManager::lockDockWidgetFeaturesGlobally(CDockWidget::DockWidgetFeatures Value) +{ + // Limit the features to CDockWidget::GloballyLockableFeatures + Value &= CDockWidget::GloballyLockableFeatures; + if (d->LockedDockWidgetFeatures == Value) + { + return; + } + + d->LockedDockWidgetFeatures = Value; + // Call the notifyFeaturesChanged() function for all dock widgets to update + // the state of the close and detach buttons + for (auto DockWidget : d->DockWidgetsMap) + { + DockWidget->notifyFeaturesChanged(); + } +} + + +//=========================================================================== +CDockWidget::DockWidgetFeatures CDockManager::globallyLockedDockWidgetFeatures() const +{ + return d->LockedDockWidgetFeatures; +} + + +//=========================================================================== +void CDockManager::setConfigParam(CDockManager::eConfigParam Param, QVariant Value) +{ + StaticConfigParams[Param] = Value; +} + + +//=========================================================================== +QVariant CDockManager::configParam(eConfigParam Param, QVariant Default) +{ + return StaticConfigParams[Param].isValid() ? StaticConfigParams[Param] : Default; +} + + +//=========================================================================== +void CDockManager::raise() +{ + if (parentWidget()) + { + parentWidget()->raise(); + } +} + + } // namespace ads //--------------------------------------------------------------------------- diff --git a/src/DockManager.h b/src/DockManager.h index 8085f1c1b..fcc5ce315 100644 --- a/src/DockManager.h +++ b/src/DockManager.h @@ -55,6 +55,8 @@ class CIconProvider; class CDockComponentsFactory; class CDockFocusController; class CAutoHideSideBar; +class CAutoHideTab; +struct AutoHideTabPrivate; /** * The central dock manager that maintains the complete docking system. @@ -87,6 +89,8 @@ class ADS_EXPORT CDockManager : public CDockContainerWidget friend class CDockAreaTitleBar; friend class CAutoHideDockContainer; friend CAutoHideSideBar; + friend CAutoHideTab; + friend AutoHideTabPrivate; public Q_SLOTS: /** @@ -150,7 +154,7 @@ public Q_SLOTS: virtual void showEvent(QShowEvent *event) override; /** - * Acces for the internal dock focus controller. + * Access for the internal dock focus controller. * This function only returns a valid object, if the FocusHighlighting * flag is set. */ @@ -209,6 +213,11 @@ public Q_SLOTS: //!< If neither this nor FloatingContainerForceNativeTitleBar is set (the default) native titlebars are used except on known bad systems. //! Users can overwrite this by setting the environment variable ADS_UseNativeTitle to "1" or "0". MiddleMouseButtonClosesTab = 0x2000000, //! If the flag is set, the user can use the mouse middle button to close the tab under the mouse + DisableTabTextEliding = 0x4000000, //! Set this flag to disable eliding of tab texts in dock area tabs + ShowTabTextOnlyForActiveTab =0x8000000, //! Set this flag to show label texts in dock area tabs only for active tabs + DoubleClickUndocksWidget = 0x10000000, //!< If the flag is set, a double click on a tab undocks the widget + TabsAtBottom = 0x20000000, //!< If the flag is set, tabs will be shown at the bottom instead of in the title bar. + DefaultDockAreaButtons = DockAreaHasCloseButton | DockAreaHasUndockButton @@ -217,7 +226,8 @@ public Q_SLOTS: DefaultBaseConfig = DefaultDockAreaButtons | ActiveTabHasCloseButton | XmlCompressionEnabled - | FloatingContainerHasWidgetTitle, ///< default base configuration settings + | FloatingContainerHasWidgetTitle + | DoubleClickUndocksWidget, ///< default base configuration settings DefaultOpaqueConfig = DefaultBaseConfig | OpaqueSplitterResize @@ -246,12 +256,28 @@ public Q_SLOTS: AutoHideSideBarsIconOnly = 0x10,///< show only icons in auto hide side tab - if a tab has no icon, then the text will be shown AutoHideShowOnMouseOver = 0x20, ///< show the auto hide window on mouse over tab and hide it if mouse leaves auto hide container AutoHideCloseButtonCollapsesDock = 0x40, ///< Close button of an auto hide container collapses the dock instead of hiding it completely + AutoHideHasCloseButton = 0x80, //< If the flag is set an auto hide title bar has a close button + AutoHideHasMinimizeButton = 0x100, ///< if this flag is set, the auto hide title bar has a minimize button to collapse the dock widget + AutoHideOpenOnDragHover = 0x200, ///< if this flag is set, dragging hover the tab bar will open the dock + AutoHideCloseOnOutsideMouseClick = 0x400, ///< if this flag is set, the auto hide dock container will collapse if the user clicks outside of the container, if not set, the auto hide container can be closed only via click on sidebar tab DefaultAutoHideConfig = AutoHideFeatureEnabled - | DockAreaHasAutoHideButton ///< the default configuration for left and right side bars + | DockAreaHasAutoHideButton + | AutoHideHasMinimizeButton + | AutoHideCloseOnOutsideMouseClick + }; Q_DECLARE_FLAGS(AutoHideFlags, eAutoHideFlag) + /** + * Global configuration parameters that you can set via setConfigParam() + */ + enum eConfigParam + { + AutoHideOpenOnDragHoverDelay_ms, ///< Delay in ms before the dock opens on drag hover if AutoHideOpenOnDragHover flag is set + ConfigParamCount // just a delimiter to count number of config params + }; + /** * Default Constructor. @@ -267,6 +293,39 @@ public Q_SLOTS: */ virtual ~CDockManager() override; + /** + * Creates a new dock widget with the specified title and optional parent + * widget. + * + * The new dock widget will be managed by the dock manager, and its lifetime + * will be tied to the dock manager. If a parent widget is provided, the dock + * widget will be created as a child of the parent widget. If no parent widget + * is provided, the dock widget will be created as a top-level widget. + * + * @param title The title of the dock widget. + * @param parent The parent widget, if any. Defaults to nullptr. + * @return Returns a pointer to the created CDockWidget. + */ + CDockWidget *createDockWidget(const QString &title, QWidget* parent = nullptr); + + /** + * Returns the dock manager specific factory for creating components of + * fock widgets + */ + QSharedPointer componentsFactory() const; + + /** + * Sets a custom factory for creating components of dock widgets. + * The pointer is stored internally into a shared pointer so you should not + * delete the given factory object as long as it is used by the dock manager. + */ + void setComponentsFactory(ads::CDockComponentsFactory* Factory); + + /** + * Sets a custom factory for creating components of dock widgets. + */ + void setComponentsFactory(QSharedPointer); + /** * This function returns the global configuration flags */ @@ -313,6 +372,17 @@ public Q_SLOTS: */ static bool testAutoHideConfigFlag(eAutoHideFlag Flag); + /** + * Sets the value for the given config parameter + */ + static void setConfigParam(eConfigParam Param, QVariant Value); + + /** + * Returns the value for the given config parameter or the default value + * if the parameter is not set. + */ + static QVariant configParam(eConfigParam Param, QVariant Default); + /** * Returns the global icon provider. * The icon provider enables the use of custom icons in case using @@ -535,7 +605,7 @@ public Q_SLOTS: * The order defines how the actions are added to the view menu. * The default insertion order is MenuAlphabeticallySorted to make it * easier for users to find the menu entry for a certain dock widget. - * You need to call this function befor you insert the first menu item + * You need to call this function before you insert the first menu item * into the view menu. */ void setViewMenuInsertionOrder(eViewMenuInsertionOrder Order); @@ -620,6 +690,68 @@ public Q_SLOTS: */ static QString floatingContainersTitle(); + /** + * This function sets the tool button style for the given dock widget state. + * It is possible to switch the tool button style depending on the state. + * If a dock widget is floating, then here are more space and it is + * possible to select a style that requires more space like + * Qt::ToolButtonTextUnderIcon. For the docked state Qt::ToolButtonIconOnly + * might be better. + */ + void setDockWidgetToolBarStyle(Qt::ToolButtonStyle Style, CDockWidget::eState State); + + /** + * Returns the tool button style for the given docking state. + * \see setToolBarStyle() + */ + Qt::ToolButtonStyle dockWidgetToolBarStyle(CDockWidget::eState State) const; + + /** + * This function sets the tool button icon size for the given state. + * If a dock widget is floating, there is more space and increasing the + * icon size is possible. For docked widgets, small icon sizes, eg. 16 x 16 + * might be better. + */ + void setDockWidgetToolBarIconSize(const QSize& IconSize, CDockWidget::eState State); + + /** + * Returns the icon size for a given docking state. + * \see setToolBarIconSize() + */ + QSize dockWidgetToolBarIconSize(CDockWidget::eState State) const; + + /** + * Returns all dock widget features that are globally locked by the dock + * manager. + * Globally locked features are removed from the features of all dock + * widgets. + */ + CDockWidget::DockWidgetFeatures globallyLockedDockWidgetFeatures() const; + + /** + * Globally Lock features of all dock widgets to "freeze" the current + * workspace layout. + * For example, it is now possible to lock the workspace to avoid + * accidentally dragging a docked view. Locking wasn’t possible before. + * So, users had to manually dock it back to the desired place after + * each accidental undock. + * You can use a combination of the following feature flags: + * - CDockWidget::DockWidgetClosable + * - CDockWidget::DockWidgetMovable + * - CDockWidget::DockWidgetFloatable + * - CDockWidget::DockWidgetPinable + * + * To clear the locked features, you can use CDockWidget::NoDockWidgetFeatures + * The following code shows how to lock and unlock dock widget features + * globally. + * + * \code + * DockManager->lockDockWidgetFeaturesGlobally(); + * DockManager->lockDockWidgetFeaturesGlobally(CDockWidget::NoDockWidgetFeatures); + * \code + */ + void lockDockWidgetFeaturesGlobally(CDockWidget::DockWidgetFeatures Features = CDockWidget::GloballyLockableFeatures); + public Q_SLOTS: /** * Opens the perspective with the given name. @@ -639,6 +771,12 @@ public Q_SLOTS: */ void hideManagerAndFloatingWidgets(); + /** + * Calls raise() for the widget that hosts this dock manager. + * This will bring the widget in front of any other application that is running + */ + void raise(); + Q_SIGNALS: /** * This signal is emitted if the list of perspectives changed. diff --git a/src/DockOverlay.cpp b/src/DockOverlay.cpp index afefa26ed..3ae43a2d7 100644 --- a/src/DockOverlay.cpp +++ b/src/DockOverlay.cpp @@ -38,11 +38,18 @@ #include "DockAreaWidget.h" #include "DockAreaTitleBar.h" +#include "DockContainerWidget.h" +#include "AutoHideSideBar.h" +#include "DockManager.h" +#include "DockAreaTabBar.h" #include namespace ads { +static const int AutoHideAreaWidth = 32; +static const int AutoHideAreaMouseZone = 8; +static const int InvalidTabIndex = -2; /** * Private data class of CDockOverlay @@ -57,11 +64,23 @@ struct DockOverlayPrivate bool DropPreviewEnabled = true; CDockOverlay::eMode Mode = CDockOverlay::ModeDockAreaOverlay; QRect DropAreaRect; + int TabIndex = InvalidTabIndex; /** * Private data constructor */ DockOverlayPrivate(CDockOverlay* _public) : _this(_public) {} + + /** + * Returns the overlay width / height depending on the visibility + * of the sidebar + */ + int sideBarOverlaySize(SideBarLocation sideBarLocation); + + /** + * The area where the mouse is considered in the sidebar + */ + int sideBarMouseZone(SideBarLocation sideBarLocation); }; /** @@ -155,8 +174,20 @@ struct DockOverlayCrossPrivate QLabel* l = new QLabel(); l->setObjectName("DockWidgetAreaLabel"); - const qreal metric = dropIndicatiorWidth(l); - const QSizeF size(metric, metric); + qreal metric = dropIndicatiorWidth(l); + QSizeF size(metric, metric); + if (internal::isSideBarArea(DockWidgetArea)) + { + auto SideBarLocation = internal::toSideBarLocation(DockWidgetArea); + if (internal::isHorizontalSideBarLocation(SideBarLocation)) + { + size.setHeight(size.height() / 2); + } + else + { + size.setWidth(size.width() / 2); + } + } l->setPixmap(createHighDpiDropIndicatorPixmap(size, DockWidgetArea, Mode)); l->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint); @@ -182,6 +213,11 @@ struct DockOverlayCrossPrivate { QColor borderColor = iconColor(CDockOverlayCross::FrameColor); QColor backgroundColor = iconColor(CDockOverlayCross::WindowBackgroundColor); + QColor overlayColor = iconColor(CDockOverlayCross::OverlayColor); + if (overlayColor.alpha() == 255) + { + overlayColor.setAlpha(64); + } #if QT_VERSION >= 0x050600 double DevicePixelRatio = _this->window()->devicePixelRatioF(); @@ -239,22 +275,22 @@ struct DockOverlayCrossPrivate } QSizeF baseSize = baseRect.size(); - if (CDockOverlay::ModeContainerOverlay == Mode && DockWidgetArea != CenterDockWidgetArea) + bool IsOuterContainerArea = (CDockOverlay::ModeContainerOverlay == Mode) + && (DockWidgetArea != CenterDockWidgetArea) + && !internal::isSideBarArea(DockWidgetArea); + + if (IsOuterContainerArea) { baseRect = areaRect; } p.fillRect(baseRect, backgroundColor); + if (areaRect.isValid()) { pen = p.pen(); pen.setColor(borderColor); - QColor Color = iconColor(CDockOverlayCross::OverlayColor); - if (Color.alpha() == 255) - { - Color.setAlpha(64); - } - p.setBrush(Color); + p.setBrush(overlayColor); p.setPen(Qt::NoPen); p.drawRect(areaRect); @@ -267,6 +303,7 @@ struct DockOverlayCrossPrivate } p.restore(); + p.save(); // Draw outer border pen = p.pen(); @@ -282,8 +319,9 @@ struct DockOverlayCrossPrivate p.drawRect(FrameRect); p.restore(); + // Draw arrow for outer container drop indicators - if (CDockOverlay::ModeContainerOverlay == Mode && DockWidgetArea != CenterDockWidgetArea) + if (IsOuterContainerArea) { QRectF ArrowRect; ArrowRect.setSize(baseSize); @@ -326,6 +364,38 @@ struct DockOverlayCrossPrivate }; +//============================================================================ +int DockOverlayPrivate::sideBarOverlaySize(SideBarLocation sideBarLocation) +{ + auto Container = qobject_cast(TargetWidget.data()); + auto SideBar = Container->autoHideSideBar(sideBarLocation); + if (!SideBar || !SideBar->isVisibleTo(Container)) + { + return AutoHideAreaWidth; + } + else + { + return (SideBar->orientation() == Qt::Horizontal) ? SideBar->height() : SideBar->width(); + } +} + + +//============================================================================ +int DockOverlayPrivate::sideBarMouseZone(SideBarLocation sideBarLocation) +{ + auto Container = qobject_cast(TargetWidget.data()); + auto SideBar = Container->autoHideSideBar(sideBarLocation); + if (!SideBar || !SideBar->isVisibleTo(Container)) + { + return AutoHideAreaMouseZone; + } + else + { + return (SideBar->orientation() == Qt::Horizontal) ? SideBar->height() : SideBar->width(); + } +} + + //============================================================================ CDockOverlay::CDockOverlay(QWidget* parent, eMode Mode) : QFrame(parent), @@ -359,12 +429,26 @@ CDockOverlay::~CDockOverlay() void CDockOverlay::setAllowedAreas(DockWidgetAreas areas) { if (areas == d->AllowedAreas) + { return; + } d->AllowedAreas = areas; d->Cross->reset(); } +//============================================================================ +void CDockOverlay::setAllowedArea(DockWidgetArea area, bool Enable) +{ + auto AreasOld = d->AllowedAreas; + d->AllowedAreas.setFlag(area, Enable); + if (AreasOld != d->AllowedAreas) + { + d->Cross->reset(); + } +} + + //============================================================================ DockWidgetAreas CDockOverlay::allowedAreas() const { @@ -375,22 +459,68 @@ DockWidgetAreas CDockOverlay::allowedAreas() const //============================================================================ DockWidgetArea CDockOverlay::dropAreaUnderCursor() const { + d->TabIndex = InvalidTabIndex; + if (!d->TargetWidget) + { + return InvalidDockWidgetArea; + } + DockWidgetArea Result = d->Cross->cursorLocation(); if (Result != InvalidDockWidgetArea) { return Result; } - CDockAreaWidget* DockArea = qobject_cast(d->TargetWidget.data()); - if (!DockArea) + auto CursorPos = QCursor::pos(); + auto DockArea = qobject_cast(d->TargetWidget.data()); + if (!DockArea && CDockManager::autoHideConfigFlags().testFlag(CDockManager::AutoHideFeatureEnabled)) + { + auto Rect = rect(); + const QPoint pos = mapFromGlobal(QCursor::pos()); + if ((pos.x() < d->sideBarMouseZone(SideBarLeft)) + && d->AllowedAreas.testFlag(LeftAutoHideArea)) + { + Result = LeftAutoHideArea; + } + else if (pos.x() > (Rect.width() - d->sideBarMouseZone(SideBarRight)) + && d->AllowedAreas.testFlag(RightAutoHideArea)) + { + Result = RightAutoHideArea; + } + else if (pos.y() < d->sideBarMouseZone(SideBarTop) + && d->AllowedAreas.testFlag(TopAutoHideArea)) + { + Result = TopAutoHideArea; + } + else if (pos.y() > (Rect.height() - d->sideBarMouseZone(SideBarBottom)) + && d->AllowedAreas.testFlag(BottomAutoHideArea)) + { + Result = BottomAutoHideArea; + } + + auto SideBarLocation = ads::internal::toSideBarLocation(Result); + if (SideBarLocation != SideBarNone) + { + auto Container = qobject_cast(d->TargetWidget.data()); + auto SideBar = Container->autoHideSideBar(SideBarLocation); + if (SideBar->isVisible()) + { + d->TabIndex = SideBar->tabInsertIndexAt(SideBar->mapFromGlobal(CursorPos)); + } + } + return Result; + } + else if (!DockArea) { return Result; } if (DockArea->allowedAreas().testFlag(CenterDockWidgetArea) && !DockArea->titleBar()->isHidden() - && DockArea->titleBarGeometry().contains(DockArea->mapFromGlobal(QCursor::pos()))) + && DockArea->titleBarGeometry().contains(DockArea->mapFromGlobal(CursorPos))) { + auto TabBar = DockArea->titleBar()->tabBar(); + d->TabIndex = TabBar->tabInsertIndexAt(TabBar->mapFromGlobal(CursorPos)); return CenterDockWidgetArea; } @@ -398,6 +528,13 @@ DockWidgetArea CDockOverlay::dropAreaUnderCursor() const } +//============================================================================ +int CDockOverlay::tabIndexUnderCursor() const +{ + return d->TabIndex; +} + + //============================================================================ DockWidgetArea CDockOverlay::visibleDropAreaUnderCursor() const { @@ -471,6 +608,7 @@ bool CDockOverlay::dropPreviewEnabled() const void CDockOverlay::paintEvent(QPaintEvent* event) { Q_UNUSED(event); + // Draw rect based on location if (!d->DropPreviewEnabled) { @@ -490,8 +628,13 @@ void CDockOverlay::paintEvent(QPaintEvent* event) case BottomDockWidgetArea: r.setY(r.height() * (1 - 1 / Factor)); break; case LeftDockWidgetArea: r.setWidth(r.width() / Factor); break; case CenterDockWidgetArea: r = rect();break; + case LeftAutoHideArea: r.setWidth(d->sideBarOverlaySize(SideBarLeft)); break; + case RightAutoHideArea: r.setX(r.width() - d->sideBarOverlaySize(SideBarRight)); break; + case TopAutoHideArea: r.setHeight(d->sideBarOverlaySize(SideBarTop)); break; + case BottomAutoHideArea: r.setY(r.height() - d->sideBarOverlaySize(SideBarBottom)); break; default: return; } + QPainter painter(this); QColor Color = palette().color(QPalette::Active, QPalette::Highlight); QPen Pen = painter.pen(); @@ -627,6 +770,7 @@ void CDockOverlayCross::setupOverlayCross(CDockOverlay::eMode Mode) areaWidgets.insert(BottomDockWidgetArea, d->createDropIndicatorWidget(BottomDockWidgetArea, Mode)); areaWidgets.insert(LeftDockWidgetArea, d->createDropIndicatorWidget(LeftDockWidgetArea, Mode)); areaWidgets.insert(CenterDockWidgetArea, d->createDropIndicatorWidget(CenterDockWidgetArea, Mode)); + #if QT_VERSION >= 0x050600 d->LastDevicePixelRatio = devicePixelRatioF(); #else diff --git a/src/DockOverlay.h b/src/DockOverlay.h index 88905e0bf..36376f591 100644 --- a/src/DockOverlay.h +++ b/src/DockOverlay.h @@ -72,6 +72,11 @@ class ADS_EXPORT CDockOverlay : public QFrame */ void setAllowedAreas(DockWidgetAreas areas); + /** + * Enable / disable a certain area + */ + void setAllowedArea(DockWidgetArea area, bool Enable); + /** * Returns flags with all allowed drop areas */ @@ -82,6 +87,17 @@ class ADS_EXPORT CDockOverlay : public QFrame */ DockWidgetArea dropAreaUnderCursor() const; + /** + * If the drop area is the CenterDockWidgetArea or a sidebar area, + * then this function returns the index of the tab under cursor. + * Call this function after call to dropAreaUnderCursor() because this + * function updates the tab index. + * A value of -1 indicates a position before the first tab and a value of + * tabCount() indicates a position behind the last tab. + * A value of -2 indicates an valid value + */ + int tabIndexUnderCursor() const; + /** * This function returns the same like dropAreaUnderCursor() if this * overlay is not hidden and if drop preview is enabled and returns diff --git a/src/DockSplitter.h b/src/DockSplitter.h index 1cc766cd4..498a507ad 100644 --- a/src/DockSplitter.h +++ b/src/DockSplitter.h @@ -39,7 +39,7 @@ struct DockSplitterPrivate; /** * Splitter used internally instead of QSplitter with some additional - * fuctionality. + * functionality. */ class ADS_EXPORT CDockSplitter : public QSplitter { diff --git a/src/DockWidget.cpp b/src/DockWidget.cpp index 9dde469c9..b4e0bb133 100644 --- a/src/DockWidget.cpp +++ b/src/DockWidget.cpp @@ -80,8 +80,8 @@ struct DockWidgetPrivate QWidget* Widget = nullptr; CDockWidgetTab* TabWidget = nullptr; CDockWidget::DockWidgetFeatures Features = CDockWidget::DefaultDockWidgetFeatures; - CDockManager* DockManager = nullptr; - CDockAreaWidget* DockArea = nullptr; + QPointer DockManager; + QPointer DockArea; QAction* ToggleViewAction = nullptr; bool Closed = false; QScrollArea* ScrollArea = nullptr; @@ -95,12 +95,21 @@ struct DockWidgetPrivate CDockWidget::eMinimumSizeHintMode MinimumSizeHintMode = CDockWidget::MinimumSizeHintFromDockWidget; WidgetFactory* Factory = nullptr; QPointer SideTabWidget; + CDockWidget::eToolBarStyleSource ToolBarStyleSource = CDockWidget::ToolBarStyleFromDockManager; /** * Private data constructor */ DockWidgetPrivate(CDockWidget* _public); + /** + * Convenience function to ease components factory access + */ + QSharedPointer componentsFactory() const + { + return DockManager ? DockManager->componentsFactory() : CDockComponentsFactory::factory(); + } + /** * Show dock widget */ @@ -139,6 +148,11 @@ struct DockWidgetPrivate * returns true on success. */ bool createWidgetFromFactory(); + + /** + * Use the dock manager toolbar style and icon size for the different states + */ + void setToolBarStyleFromDockManager(); }; // struct DockWidgetPrivate @@ -178,11 +192,11 @@ void DockWidgetPrivate::showDockWidget() DockArea->setCurrentDockWidget(_this); DockArea->toggleView(true); TabWidget->show(); - QSplitter* Splitter = internal::findParent(DockArea); + auto Splitter = DockArea->parentSplitter(); while (Splitter && !Splitter->isVisible() && !DockArea->isAutoHide()) { Splitter->show(); - Splitter = internal::findParent(Splitter); + Splitter = internal::findParent(Splitter); } CDockContainerWidget* Container = DockArea->dockContainer(); @@ -213,6 +227,12 @@ void DockWidgetPrivate::hideDockWidget() if (Features.testFlag(CDockWidget::DeleteContentOnClose)) { + if (ScrollArea) + { + ScrollArea->takeWidget(); + delete ScrollArea; + ScrollArea = nullptr; + } Widget->deleteLater(); Widget = nullptr; } @@ -259,7 +279,10 @@ void DockWidgetPrivate::closeAutoHideDockWidgetsIfNeeded() return; } - if (!DockContainer->openedDockWidgets().isEmpty()) + // If the dock container is the dock manager, or if it is not empty, then we + // don't need to do anything + if ((DockContainer == _this->dockManager()) + || !DockContainer->openedDockWidgets().isEmpty()) { return; } @@ -325,11 +348,35 @@ bool DockWidgetPrivate::createWidgetFromFactory() } +//============================================================================ +void DockWidgetPrivate::setToolBarStyleFromDockManager() +{ + if (!DockManager) + { + return; + } + auto State = CDockWidget::StateDocked; + _this->setToolBarIconSize(DockManager->dockWidgetToolBarIconSize(State), State); + _this->setToolBarStyle(DockManager->dockWidgetToolBarStyle(State), State); + State = CDockWidget::StateFloating; + _this->setToolBarIconSize(DockManager->dockWidgetToolBarIconSize(State), State); + _this->setToolBarStyle(DockManager->dockWidgetToolBarStyle(State), State); +} + + //============================================================================ CDockWidget::CDockWidget(const QString &title, QWidget *parent) : - QFrame(parent), - d(new DockWidgetPrivate(this)) + CDockWidget(nullptr, title, parent) +{ +} + + +//============================================================================ +CDockWidget::CDockWidget(CDockManager *manager, const QString &title, QWidget* parent) + : QFrame(parent), + d(new DockWidgetPrivate(this)) { + d->DockManager = manager; d->Layout = new QBoxLayout(QBoxLayout::TopToBottom); d->Layout->setContentsMargins(0, 0, 0, 0); d->Layout->setSpacing(0); @@ -337,7 +384,7 @@ CDockWidget::CDockWidget(const QString &title, QWidget *parent) : setWindowTitle(title); setObjectName(title); - d->TabWidget = componentsFactory()->createDockWidgetTab(this); + d->TabWidget = d->componentsFactory()->createDockWidgetTab(this); d->ToggleViewAction = new QAction(title, this); d->ToggleViewAction->setCheckable(true); @@ -351,13 +398,28 @@ CDockWidget::CDockWidget(const QString &title, QWidget *parent) : } } + //============================================================================ CDockWidget::~CDockWidget() { - ADS_PRINT("~CDockWidget()"); + ADS_PRINT("~CDockWidget(): " << this->windowTitle()); delete d; } +//============================================================================ +void CDockWidget::setToggleViewAction(QAction* action) +{ + if (!action) + { + return; + } + + d->ToggleViewAction->setParent(nullptr); + delete d->ToggleViewAction; + d->ToggleViewAction = action; + d->ToggleViewAction->setParent(this); + connect(d->ToggleViewAction, &QAction::triggered, this, &CDockWidget::toggleView); +} //============================================================================ void CDockWidget::setToggleViewActionChecked(bool Checked) @@ -469,10 +531,19 @@ void CDockWidget::setFeatures(DockWidgetFeatures features) return; } d->Features = features; + notifyFeaturesChanged(); +} + + +//============================================================================ +void CDockWidget::notifyFeaturesChanged() +{ Q_EMIT featuresChanged(d->Features); d->TabWidget->onDockWidgetFeaturesChanged(); if(CDockAreaWidget* DockArea = dockAreaWidget()) + { DockArea->onDockWidgetFeaturesChanged(); + } } @@ -488,7 +559,14 @@ void CDockWidget::setFeature(DockWidgetFeature flag, bool on) //============================================================================ CDockWidget::DockWidgetFeatures CDockWidget::features() const { - return d->Features; + if (d->DockManager) + { + return d->Features &~ d->DockManager->globallyLockedDockWidgetFeatures(); + } + else + { + return d->Features; + } } @@ -503,6 +581,15 @@ CDockManager* CDockWidget::dockManager() const void CDockWidget::setDockManager(CDockManager* DockManager) { d->DockManager = DockManager; + if (!DockManager) + { + return; + } + + if (ToolBarStyleFromDockManager == d->ToolBarStyleSource) + { + d->setToolBarStyleFromDockManager(); + } } @@ -515,7 +602,7 @@ CDockContainerWidget* CDockWidget::dockContainer() const } else { - return 0; + return nullptr; } } @@ -555,6 +642,13 @@ bool CDockWidget::isAutoHide() const } +//============================================================================ +SideBarLocation CDockWidget::autoHideLocation() const +{ + return isAutoHide() ? autoHideDockContainer()->sideBarLocation() : SideBarNone; +} + + //============================================================================ bool CDockWidget::isFloating() const { @@ -691,11 +785,10 @@ void CDockWidget::toggleViewInternal(bool Open) if (d->DockArea) { d->DockArea->toggleDockWidgetView(this, Open); - } - - if (d->DockArea->isAutoHide()) - { - d->DockArea->autoHideDockContainer()->toggleView(Open); + if (d->DockArea->isAutoHide()) + { + d->DockArea->autoHideDockContainer()->toggleView(Open); + } } if (Open && TopLevelDockWidgetBefore) @@ -748,9 +841,9 @@ void CDockWidget::saveState(QXmlStreamWriter& s) const void CDockWidget::flagAsUnassigned() { d->Closed = true; - setParent(d->DockManager); setVisible(false); setDockArea(nullptr); + setParent(d->DockManager); tabWidget()->setParent(this); } @@ -1021,7 +1114,15 @@ void CDockWidget::setFloating() { return; } - d->TabWidget->detachDockWidget(); + + if (this->isAutoHide()) + { + dockAreaWidget()->setFloating(); + } + else + { + d->TabWidget->detachDockWidget(); + } } @@ -1044,6 +1145,22 @@ void CDockWidget::closeDockWidget() } + +//============================================================================ +void CDockWidget::requestCloseDockWidget() +{ + if (features().testFlag(CDockWidget::DockWidgetDeleteOnClose) + || features().testFlag(CDockWidget::CustomCloseHandling)) + { + closeDockWidgetInternal(false); + } + else + { + toggleView(false); + } +} + + //============================================================================ bool CDockWidget::closeDockWidgetInternal(bool ForceClose) { @@ -1190,7 +1307,7 @@ void CDockWidget::raise() //============================================================================ -void CDockWidget::setAutoHide(bool Enable, SideBarLocation Location) +void CDockWidget::setAutoHide(bool Enable, SideBarLocation Location, int TabIndex) { if (!CDockManager::testAutoHideConfigFlag(CDockManager::AutoHideFeatureEnabled)) { @@ -1198,20 +1315,25 @@ void CDockWidget::setAutoHide(bool Enable, SideBarLocation Location) } // Do nothing if nothing changes - if (Enable == isAutoHide()) + if (Enable == isAutoHide() && Location == autoHideLocation()) { return; } auto DockArea = dockAreaWidget(); + if (!Enable) { DockArea->setAutoHide(false); } + else if (isAutoHide()) + { + autoHideDockContainer()->moveToNewSideBarLocation(Location); + } else { auto area = (SideBarNone == Location) ? DockArea->calculateSideTabBarArea() : Location; - dockContainer()->createAndSetupAutoHideContainer(area, this); + dockContainer()->createAndSetupAutoHideContainer(area, this, TabIndex); } } @@ -1228,6 +1350,24 @@ void CDockWidget::toggleAutoHide(SideBarLocation Location) } +//============================================================================ +void CDockWidget::setToolBarStyleSource(eToolBarStyleSource Source) +{ + d->ToolBarStyleSource = Source; + if (ToolBarStyleFromDockManager == d->ToolBarStyleSource) + { + d->setToolBarStyleFromDockManager(); + } +} + + +//============================================================================ +CDockWidget::eToolBarStyleSource CDockWidget::toolBarStyleSource() const +{ + return d->ToolBarStyleSource; +} + + } // namespace ads //--------------------------------------------------------------------------- diff --git a/src/DockWidget.h b/src/DockWidget.h index 43f8048a3..04d10f8b9 100644 --- a/src/DockWidget.h +++ b/src/DockWidget.h @@ -165,6 +165,7 @@ private Q_SLOTS: DefaultDockWidgetFeatures = DockWidgetClosable | DockWidgetMovable | DockWidgetFloatable | DockWidgetFocusable | DockWidgetPinnable, AllDockWidgetFeatures = DefaultDockWidgetFeatures | DockWidgetDeleteOnClose | CustomCloseHandling, DockWidgetAlwaysCloseAndDelete = DockWidgetForceCloseWithArea | DockWidgetDeleteOnClose, + GloballyLockableFeatures = DockWidgetClosable | DockWidgetMovable | DockWidgetFloatable | DockWidgetPinnable, NoDockWidgetFeatures = 0x000 }; Q_DECLARE_FLAGS(DockWidgetFeatures, DockWidgetFeature) @@ -176,6 +177,12 @@ private Q_SLOTS: StateFloating }; + enum eToolBarStyleSource + { + ToolBarStyleFromDockManager, + ToolBarStyleFromDockWidget + }; + /** * Sets the widget for the dock widget to widget. * The InsertMode defines how the widget is inserted into the dock widget. @@ -246,11 +253,38 @@ private Q_SLOTS: * object name is required by the dock manager to properly save and restore * the state of the dock widget. That means, the title needs to be unique. * If your title is not unique or if you would like to change the title - * during runtime, you need to set a unique object name explicitely + * during runtime, you need to set a unique object name explicitly * by calling setObjectName() after construction. * Use the layoutFlags to configure the layout of the dock widget. + * \note If you would like to use custom TabWidget implementations, you need + * to use the constructor with the CDockManager argument. + */ + Q_DECL_DEPRECATED explicit CDockWidget(const QString &title, QWidget* parent = nullptr); + + /** + * This constructor creates a dock widget for the given dock manager with the + * provided title. + * + * @param manager Pointer to the dock manager that owns the dock widget. + * @param title The title is the text that is shown in the window title when + * the dock widget is floating and it is the title that is shown in the + * titlebar or the tab of this dock widget if it is tabified. + * @param parent Pointer to the parent widget, defaults to nullptr. + * + * @note The object name of the dock widget is also set to the title. The + * object name is required by the dock manager to properly save and restore + * the state of the dock widget. That means, the title needs to be unique. If + * the title is not unique or if you would like to change the title during + * runtime, you need to set a unique object name explicitly by calling + * setObjectName() after construction. Use the layoutFlags to configure the + * layout of the dock widget. + * + * @note this constructor is preferred over the two argument version, especially + * when custom factories are in use. Indeed, it will use the Dock Manager factory, + * and not the default factory. For this reason, the original constructor should + * be deprecated in favour of this version. */ - CDockWidget(const QString &title, QWidget* parent = 0); + CDockWidget(CDockManager *manager, const QString &title, QWidget* parent = nullptr); /** * Virtual Destructor @@ -330,6 +364,11 @@ private Q_SLOTS: */ DockWidgetFeatures features() const; + /** + * Triggers notification of feature change signals and functions + */ + void notifyFeaturesChanged(); + /** * Returns the dock manager that manages the dock widget or 0 if the widget * has not been assigned to any dock manager yet @@ -377,6 +416,12 @@ private Q_SLOTS: */ CAutoHideDockContainer* autoHideDockContainer() const; + /** + * Returns the auto hide side bar location or SideBarNone if, this is not + * an autohide dock widget + */ + SideBarLocation autoHideLocation() const; + /** * This property holds whether the dock widget is floating. * A dock widget is only floating, if it is the one and only widget inside @@ -403,6 +448,12 @@ private Q_SLOTS: */ QAction* toggleViewAction() const; + /** + * Use provided action to be the default toggle view action for this dock widget. + * This dock widget now owns the action. + */ + void setToggleViewAction(QAction* action); + /** * Configures the behavior of the toggle view action. * \see eToggleViewActionMode for a detailed description @@ -439,7 +490,7 @@ private Q_SLOTS: /** * This function returns the dock widget top tool bar. - * If no toolbar is assigned, this function returns nullptr. To get a vaild + * If no toolbar is assigned, this function returns nullptr. To get a valid * toolbar you either need to create a default empty toolbar via * createDefaultToolBar() function or you need to assign your custom * toolbar via setToolBar(). @@ -461,6 +512,17 @@ private Q_SLOTS: */ void setToolBar(QToolBar* ToolBar); + /** + * Configures, if the dock widget uses the global tool bar styles from + * dock manager or if it uses its own tool bar style + */ + void setToolBarStyleSource(eToolBarStyleSource Source); + + /** + * Returns the configured tool bar style source + */ + eToolBarStyleSource toolBarStyleSource() const; + /** * This function sets the tool button style for the given dock widget state. * It is possible to switch the tool button style depending on the state. @@ -578,10 +640,19 @@ public Q_SLOTS: void deleteDockWidget(); /** - * Closes the dock widget + * Closes the dock widget. + * The function forces closing of the dock widget even for CustomCloseHandling. */ void closeDockWidget(); + /** + * Request closing of the dock widget. + * For DockWidget with default close handling, the function does the same + * like clodeDockWidget() but if the flas CustomCloseHandling is set, + * the function only emits the closeRequested() signal. + */ + void requestCloseDockWidget(); + /** * Shows the widget in full-screen mode. * Normally this function only affects windows. To make the interface @@ -606,7 +677,7 @@ public Q_SLOTS: * 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); + void setAutoHide(bool Enable, SideBarLocation Location = SideBarNone, int TabIndex = -1); /** * Switches the dock widget to auto hide mode or vice versa depending on its diff --git a/src/DockWidgetTab.cpp b/src/DockWidgetTab.cpp index 5829a26f7..96bf28159 100644 --- a/src/DockWidgetTab.cpp +++ b/src/DockWidgetTab.cpp @@ -50,13 +50,11 @@ #include "FloatingDockContainer.h" #include "DockOverlay.h" #include "DockManager.h" -#include "IconProvider.h" #include "DockFocusController.h" namespace ads { -static const char* const LocationProperty = "Location"; using tTabLabel = CElidingLabel; /** @@ -171,7 +169,7 @@ struct DockWidgetTabPrivate else { auto w = new CFloatingDragPreview(Widget); - _this->connect(w, &CFloatingDragPreview::draggingCanceled, [=]() + _this->connect(w, &CFloatingDragPreview::draggingCanceled, [this]() { DragState = DraggingInactive; }); @@ -225,7 +223,7 @@ struct DockWidgetTabPrivate QMenu* Menu) { auto Action = Menu->addAction(Title); - Action->setProperty("Location", Location); + Action->setProperty(internal::LocationProperty, Location); QObject::connect(Action, &QAction::triggered, _this, &CDockWidgetTab::onAutoHideToActionClicked); return Action; } @@ -245,7 +243,14 @@ DockWidgetTabPrivate::DockWidgetTabPrivate(CDockWidgetTab* _public) : void DockWidgetTabPrivate::createLayout() { TitleLabel = new tTabLabel(); - TitleLabel->setElideMode(Qt::ElideRight); + if (CDockManager::testConfigFlag(CDockManager::DisableTabTextEliding)) + { + TitleLabel->setElideMode(Qt::ElideNone); + } + else + { + TitleLabel->setElideMode(Qt::ElideRight); + } TitleLabel->setText(DockWidget->windowTitle()); TitleLabel->setObjectName("dockWidgetTabLabel"); TitleLabel->setAlignment(Qt::AlignCenter); @@ -376,6 +381,7 @@ void CDockWidgetTab::mousePressEvent(QMouseEvent* ev) d->DragState = DraggingMousePressed; if (CDockManager::testConfigFlag(CDockManager::FocusHighlighting)) { + d->focusController()->setDockWidgetTabPressed(true); d->focusController()->setDockWidgetTabFocused(this); } Q_EMIT clicked(); @@ -412,7 +418,13 @@ void CDockWidgetTab::mouseReleaseEvent(QMouseEvent* ev) d->FloatingWidget->finishDragging(); break; - default:; // do nothing + default: + break; + } + + if (CDockManager::testConfigFlag(CDockManager::FocusHighlighting)) + { + d->focusController()->setDockWidgetTabPressed(false); } } else if (ev->button() == Qt::MiddleButton) @@ -493,7 +505,7 @@ void CDockWidgetTab::mouseMoveEvent(QMouseEvent* ev) else if (d->DockArea->openDockWidgetsCount() > 1 && (internal::globalPositionOf(ev) - d->GlobalDragStartMousePosition).manhattanLength() >= QApplication::startDragDistance()) // Wait a few pixels before start moving { - // If we start dragging the tab, we save its inital position to + // If we start dragging the tab, we save its initial position to // restore it later if (DraggingTab != d->DragState) { @@ -516,26 +528,35 @@ void CDockWidgetTab::contextMenuEvent(QContextMenuEvent* ev) return; } + auto Menu = buildContextMenu(nullptr); d->saveDragStartMousePosition(ev->globalPos()); + Menu->exec(ev->globalPos()); +} +QMenu* CDockWidgetTab::buildContextMenu(QMenu *Menu) +{ + if (Menu == nullptr) { + Menu = new QMenu(this); + } + + qDebug() << "CDockWidgetTab::buildContextMenu"; 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; + const bool isFloating = d->DockWidget->isFloating(); + const bool isDetachable = isFloatable && !isFloating; QAction* Action; - QMenu Menu(this); - if (!isTopLevelArea) + if (!(isTopLevelArea && isFloating)) { - Action = Menu.addAction(tr("Detach"), this, SLOT(detachDockWidget())); + Action = Menu->addAction(tr("Detach"), this, SLOT(detachDockWidget())); Action->setEnabled(isDetachable); if (CDockManager::testAutoHideConfigFlag(CDockManager::AutoHideFeatureEnabled)) { - Action = Menu.addAction(tr("Pin"), this, SLOT(autoHideDockWidget())); + 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...")); + auto menu = Menu->addMenu(tr("Pin To...")); menu->setEnabled(IsPinnable); d->createAutoHideToAction(tr("Top"), SideBarTop, menu); d->createAutoHideToAction(tr("Left"), SideBarLeft, menu); @@ -544,17 +565,16 @@ void CDockWidgetTab::contextMenuEvent(QContextMenuEvent* ev) } } - Menu.addSeparator(); - Action = Menu.addAction(tr("Close"), this, SIGNAL(closeRequested())); + Menu->addSeparator(); + Action = Menu->addAction(tr("Close"), this, SIGNAL(closeRequested())); Action->setEnabled(isClosable()); if (d->DockArea->openDockWidgetsCount() > 1) { - Action = Menu.addAction(tr("Close Others"), this, SIGNAL(closeOtherTabsRequested())); + Menu->addAction(tr("Close Others"), this, SIGNAL(closeOtherTabsRequested())); } - Menu.exec(ev->globalPos()); -} - + return Menu; +} //============================================================================ bool CDockWidgetTab::isActiveTab() const { @@ -567,13 +587,21 @@ void CDockWidgetTab::setActiveTab(bool active) { d->updateCloseButtonVisibility(active); + if(CDockManager::testConfigFlag(CDockManager::ShowTabTextOnlyForActiveTab) && !d->Icon.isNull()) + { + if(active) + d->TitleLabel->setVisible(true); + else + d->TitleLabel->setVisible(false); + } + // Focus related stuff if (CDockManager::testConfigFlag(CDockManager::FocusHighlighting) && !d->DockWidget->dockManager()->isRestoringState()) { bool UpdateFocusStyle = false; - if (active && !hasFocus()) - { - //setFocus(Qt::OtherFocusReason); + // Update the focus only, if this the dock area of this tab is the focused dock area + if (active && !hasFocus() && (d->focusController()->focusedDockArea() == this->dockAreaWidget())) + { d->focusController()->setDockWidgetTabFocused(this); UpdateFocusStyle = true; } @@ -671,7 +699,7 @@ QString CDockWidgetTab::text() const //============================================================================ void CDockWidgetTab::mouseDoubleClickEvent(QMouseEvent *event) { - if (event->button() == Qt::LeftButton) + if (event->button() == Qt::LeftButton && CDockManager::testConfigFlag(CDockManager::DoubleClickUndocksWidget)) { // If this is the last dock area in a dock container it does not make // sense to move it to a new floating widget and leave this one @@ -742,7 +770,7 @@ void CDockWidgetTab::autoHideDockWidget() //=========================================================================== void CDockWidgetTab::onAutoHideToActionClicked() { - int Location = sender()->property(LocationProperty).toInt(); + int Location = sender()->property(internal::LocationProperty).toInt(); d->DockWidget->toggleAutoHide((SideBarLocation)Location); } @@ -768,6 +796,13 @@ bool CDockWidgetTab::event(QEvent *e) } +//============================================================================ +eDragState CDockWidgetTab::dragState() const +{ + return d->DragState; +} + + //============================================================================ void CDockWidgetTab::onDockWidgetFeaturesChanged() { @@ -802,6 +837,7 @@ 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 11d0ba512..02781affd 100644 --- a/src/DockWidgetTab.h +++ b/src/DockWidgetTab.h @@ -35,6 +35,8 @@ #include "ads_globals.h" +QT_FORWARD_DECLARE_CLASS(QMenu) + namespace ads { class CDockWidget; @@ -84,7 +86,7 @@ private Q_SLOTS: * param[in] DockWidget The dock widget this title bar belongs to * param[in] parent The parent widget of this title bar */ - CDockWidgetTab(CDockWidget* DockWidget, QWidget* parent = 0); + CDockWidgetTab(CDockWidget* DockWidget, QWidget* parent = nullptr); /** * Virtual Destructor @@ -173,11 +175,31 @@ private Q_SLOTS: /** * Set an explicit icon size. - * If no icon size has been set explicitely, than the tab sets the icon size + * If no icon size has been set explicitly, than the tab sets the icon size * depending on the style */ void setIconSize(const QSize& Size); + /** + * Returns the current drag state of this tab. + * Use this function to determine if the tab is currently being dragged + */ + eDragState dragState() const; + + /** + * Fills the provided menu with standard entries. If a nullptr is passed, a + * new menu is created and filled with standard entries. + * This function is called from the actual version of contextMenuEvent, but + * can be called from any code. Caller is responsible of deleting the created + * object. + * + * @param menu The QMenu to fill with standard entries. If nullptr, a new + * QMenu will be created. + * @return The filled QMenu, either the provided one or a newly created one if + * nullptr was passed. + */ + virtual QMenu *buildContextMenu(QMenu *); + public Q_SLOTS: virtual void setVisible(bool visible) override; diff --git a/src/ElidingLabel.h b/src/ElidingLabel.h index 894dcb069..e2330e68b 100644 --- a/src/ElidingLabel.h +++ b/src/ElidingLabel.h @@ -58,8 +58,8 @@ class ADS_EXPORT CElidingLabel : public QLabel public: using Super = QLabel; - CElidingLabel(QWidget* parent = 0, Qt::WindowFlags f = Qt::WindowFlags ()); - CElidingLabel(const QString& text, QWidget* parent = 0, Qt::WindowFlags f = Qt::WindowFlags ()); + CElidingLabel(QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags ()); + CElidingLabel(const QString& text, QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags ()); virtual ~CElidingLabel(); /** diff --git a/src/FloatingDockContainer.cpp b/src/FloatingDockContainer.cpp index 77ece4555..426c1ff1a 100644 --- a/src/FloatingDockContainer.cpp +++ b/src/FloatingDockContainer.cpp @@ -62,7 +62,7 @@ namespace ads #ifdef Q_OS_WIN #if 0 // set to 1 if you need this function for debugging /** - * Just for debuging to convert windows message identifiers to strings + * Just for debugging to convert windows message identifiers to strings */ static const char* windowsMessageString(int MessageId) { @@ -357,7 +357,7 @@ static const char* windowsMessageString(int MessageId) #endif -static unsigned int zOrderCounter = 0; +static unsigned int zOrderCounterFloating = 0; /** * Private data class of CFloatingDockContainer class (pimpl) */ @@ -365,7 +365,7 @@ struct FloatingDockContainerPrivate { CFloatingDockContainer *_this; CDockContainerWidget *DockContainer; - unsigned int zOrderIndex = ++zOrderCounter; + unsigned int zOrderIndex = ++zOrderCounterFloating; QPointer DockManager; eDragState DraggingState = DraggingInactive; QPoint DragStartMousePosition; @@ -378,6 +378,7 @@ struct FloatingDockContainerPrivate QWidget* MouseEventHandler = nullptr; CFloatingWidgetTitleBar* TitleBar = nullptr; bool IsResizing = false; + bool MousePressed = false; #endif /** @@ -498,10 +499,8 @@ void FloatingDockContainerPrivate::titleMouseReleaseEvent() return; } - if (DockManager->dockAreaOverlay()->dropAreaUnderCursor() - != InvalidDockWidgetArea - || DockManager->containerOverlay()->dropAreaUnderCursor() - != InvalidDockWidgetArea) + if (DockManager->dockAreaOverlay()->dropAreaUnderCursor() != InvalidDockWidgetArea + || DockManager->containerOverlay()->dropAreaUnderCursor() != InvalidDockWidgetArea) { CDockOverlay *Overlay = DockManager->containerOverlay(); if (!Overlay->dropOverlayRect().isValid()) @@ -509,21 +508,26 @@ void FloatingDockContainerPrivate::titleMouseReleaseEvent() Overlay = DockManager->dockAreaOverlay(); } - // Resize the floating widget to the size of the highlighted drop area - // rectangle - QRect Rect = Overlay->dropOverlayRect(); - int FrameWidth = (_this->frameSize().width() - _this->rect().width()) - / 2; - int TitleBarHeight = _this->frameSize().height() - - _this->rect().height() - FrameWidth; - if (Rect.isValid()) + // Do not resize if we drop into an autohide sidebar area to preserve + // the dock area size for the initial size of the auto hide area + if (!ads::internal::isSideBarArea(Overlay->dropAreaUnderCursor())) { - QPoint TopLeft = Overlay->mapToGlobal(Rect.topLeft()); - TopLeft.ry() += TitleBarHeight; - _this->setGeometry( - QRect(TopLeft, - QSize(Rect.width(), Rect.height() - TitleBarHeight))); - QApplication::processEvents(); + // Resize the floating widget to the size of the highlighted drop area + // rectangle + QRect Rect = Overlay->dropOverlayRect(); + int FrameWidth = (_this->frameSize().width() - _this->rect().width()) + / 2; + int TitleBarHeight = _this->frameSize().height() + - _this->rect().height() - FrameWidth; + if (Rect.isValid()) + { + QPoint TopLeft = Overlay->mapToGlobal(Rect.topLeft()); + TopLeft.ry() += TitleBarHeight; + _this->setGeometry( + QRect(TopLeft, + QSize(Rect.width(), Rect.height() - TitleBarHeight))); + QApplication::processEvents(); + } } DropContainer->dropFloatingWidget(_this, QCursor::pos()); } @@ -532,6 +536,7 @@ void FloatingDockContainerPrivate::titleMouseReleaseEvent() DockManager->dockAreaOverlay()->hideOverlay(); } + //============================================================================ void FloatingDockContainerPrivate::updateDropOverlays(const QPoint &GlobalPos) { @@ -585,11 +590,25 @@ void FloatingDockContainerPrivate::updateDropOverlays(const QPoint &GlobalPos) } int VisibleDockAreas = TopContainer->visibleDockAreaCount(); - ContainerOverlay->setAllowedAreas( - VisibleDockAreas > 1 ? OuterDockAreas : AllDockAreas); + DockWidgetAreas AllowedContainerAreas = (VisibleDockAreas > 1) ? OuterDockAreas : AllDockAreas; + auto DockArea = TopContainer->dockAreaAt(GlobalPos); + // If the dock container contains only one single DockArea, then we need + // to respect the allowed areas - only the center area is relevant here because + // all other allowed areas are from the container + if (VisibleDockAreas == 1 && DockArea) + { + AllowedContainerAreas.setFlag(CenterDockWidgetArea, DockArea->allowedAreas().testFlag(CenterDockWidgetArea)); + } + + if (DockContainer->features().testFlag(CDockWidget::DockWidgetPinnable)) + { + AllowedContainerAreas |= AutoHideDockAreas; + } + + ContainerOverlay->setAllowedAreas(AllowedContainerAreas); + DockWidgetArea ContainerArea = ContainerOverlay->showOverlay(TopContainer); ContainerOverlay->enableDropPreview(ContainerArea != InvalidDockWidgetArea); - auto DockArea = TopContainer->dockAreaAt(GlobalPos); if (DockArea && DockArea->isVisible() && VisibleDockAreas > 0) { DockAreaOverlay->enableDropPreview(true); @@ -643,7 +662,6 @@ CFloatingDockContainer::CFloatingDockContainer(CDockManager *DockManager) : #if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) QDockWidget::setWidget(d->DockContainer); - QDockWidget::setFloating(true); QDockWidget::setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); @@ -742,6 +760,7 @@ CFloatingDockContainer::CFloatingDockContainer(CDockWidget *DockWidget) : d->DockManager->notifyWidgetOrAreaRelocation(DockWidget); } + //============================================================================ CFloatingDockContainer::~CFloatingDockContainer() { @@ -753,6 +772,36 @@ CFloatingDockContainer::~CFloatingDockContainer() delete d; } + +//============================================================================ +void CFloatingDockContainer::deleteContent() +{ + std::vector> areas; + for (int i = 0; i != dockContainer()->dockAreaCount(); ++i) + { + areas.push_back( dockContainer()->dockArea(i) ); + } + for (auto area : areas) + { + if (!area) + { + continue; + } + + // QPointer delete safety - just in case some dock widget in destruction + // deletes another related/twin or child dock widget. + std::vector> deleteWidgets; + for (auto widget : area->dockWidgets()) + { + deleteWidgets.push_back(widget); + } + for (auto ptrWdg : deleteWidgets) + { + delete ptrWdg; + } + } +} + //============================================================================ CDockContainerWidget* CFloatingDockContainer::dockContainer() const { @@ -769,7 +818,7 @@ void CFloatingDockContainer::changeEvent(QEvent *event) if (isActiveWindow()) { ADS_PRINT("FloatingWidget::changeEvent QEvent::ActivationChange "); - d->zOrderIndex = ++zOrderCounter; + d->zOrderIndex = ++zOrderCounterFloating; #if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) if (d->DraggingState == DraggingFloatingWidget) @@ -1124,7 +1173,7 @@ QList CFloatingDockContainer::dockWidgets() const } //============================================================================ -void CFloatingDockContainer::hideAndDeleteLater() +void CFloatingDockContainer::finishDropOperation() { // Widget has been redocked, so it must be hidden right way (see // https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/issues/351) @@ -1132,6 +1181,11 @@ void CFloatingDockContainer::hideAndDeleteLater() // dock widgets that shall not be toggled hidden. d->AutoHideChildren = false; hide(); + // The floating widget will be deleted now. Ensure, that the destructor + // of the floating widget does not delete any dock areas that have been + // moved to a new container - simply remove all dock areas before deleting + // the floating widget + d->DockContainer->removeAllDockAreas(); deleteLater(); if (d->DockManager) { @@ -1287,7 +1341,8 @@ void CFloatingDockContainer::onMaximizeRequest() //============================================================================ void CFloatingDockContainer::showNormal(bool fixGeometry) { - if (windowState() == Qt::WindowMaximized) + if ( (windowState() & Qt::WindowMaximized) != 0 || + (windowState() & Qt::WindowFullScreen) != 0) { QRect oldNormal = normalGeometry(); Super::showNormal(); @@ -1338,12 +1393,12 @@ void CFloatingDockContainer::resizeEvent(QResizeEvent *event) Super::resizeEvent(event); } -static bool s_mousePressed = false; + //============================================================================ void CFloatingDockContainer::moveEvent(QMoveEvent *event) { Super::moveEvent(event); - if (!d->IsResizing && event->spontaneous() && s_mousePressed) + if (!d->IsResizing && event->spontaneous() && d->MousePressed) { d->setState(DraggingFloatingWidget); d->updateDropOverlays(QCursor::pos()); @@ -1359,10 +1414,10 @@ bool CFloatingDockContainer::event(QEvent *e) switch (e->type()) { case QEvent::WindowActivate: - s_mousePressed = false; + d->MousePressed = false; break; case QEvent::WindowDeactivate: - s_mousePressed = true; + d->MousePressed = true; break; default: break; diff --git a/src/FloatingDockContainer.h b/src/FloatingDockContainer.h index 333915f80..2dd9d499f 100644 --- a/src/FloatingDockContainer.h +++ b/src/FloatingDockContainer.h @@ -131,21 +131,19 @@ private Q_SLOTS: virtual void startFloating(const QPoint& DragStartMousePos, const QSize& Size, eDragState DragState, QWidget* MouseEventHandler) override; - /** - * Call this function to start dragging the floating widget - */ - void startDragging(const QPoint& DragStartMousePos, const QSize& Size, - QWidget* MouseEventHandler) - { - startFloating(DragStartMousePos, Size, DraggingFloatingWidget, MouseEventHandler); - } - /** * Call this function if you explicitly want to signal that dragging has * finished */ virtual void finishDragging() override; + /** + * This function deletes all dock widgets in it. + * This functions should be called only from dock manager in its + * destructor before deleting the floating widget + */ + void deleteContent(); + /** * Call this function if you just want to initialize the position * and size of the floating widget @@ -229,6 +227,15 @@ private Q_SLOTS: */ CDockContainerWidget* dockContainer() const; + /** + * Call this function to start dragging the floating widget + */ + void startDragging(const QPoint& DragStartMousePos, const QSize& Size, + QWidget* MouseEventHandler) + { + startFloating(DragStartMousePos, Size, DraggingFloatingWidget, MouseEventHandler); + } + /** * This function returns true, if it can be closed. * It can be closed, if all dock widgets in all dock areas can be closed @@ -258,9 +265,9 @@ private Q_SLOTS: QList dockWidgets() const; /** - * This function hides the floating bar instantely and delete it later. + * This function hides the floating widget instantly and delete it later. */ - void hideAndDeleteLater(); + void finishDropOperation(); #if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) /** diff --git a/src/FloatingDragPreview.cpp b/src/FloatingDragPreview.cpp index 8ee3b8e49..f7a45a2b2 100644 --- a/src/FloatingDragPreview.cpp +++ b/src/FloatingDragPreview.cpp @@ -22,6 +22,8 @@ #include "DockManager.h" #include "DockContainerWidget.h" #include "DockOverlay.h" +#include "AutoHideDockContainer.h" +#include "ads_globals.h" namespace ads { @@ -33,6 +35,7 @@ struct FloatingDragPreviewPrivate { CFloatingDragPreview *_this; QWidget* Content; + CDockWidget::DockWidgetFeatures ContentFeatures; CDockAreaWidget* ContentSourceArea = nullptr; QPoint DragStartMousePosition; CDockManager* DockManager; @@ -77,20 +80,36 @@ struct FloatingDragPreviewPrivate * Returns true, if the content is floatable */ bool isContentFloatable() const + { + return this->ContentFeatures.testFlag(CDockWidget::DockWidgetFloatable); + } + + /** + * Returns true, if the content is pinnable + */ + bool isContentPinnable() const + { + return this->ContentFeatures.testFlag(CDockWidget::DockWidgetPinnable); + } + + /** + * Returns the content features + */ + CDockWidget::DockWidgetFeatures contentFeatures() const { CDockWidget* DockWidget = qobject_cast(Content); - if (DockWidget && DockWidget->features().testFlag(CDockWidget::DockWidgetFloatable)) + if (DockWidget) { - return true; + return DockWidget->features(); } CDockAreaWidget* DockArea = qobject_cast(Content); - if (DockArea && DockArea->features().testFlag(CDockWidget::DockWidgetFloatable)) + if (DockArea) { - return true; + return DockArea->features(); } - return false; + return CDockWidget::DockWidgetFeatures(); } }; // struct LedArrayPanelPrivate @@ -126,8 +145,6 @@ void FloatingDragPreviewPrivate::updateDropOverlays(const QPoint &GlobalPos) DropContainer = TopContainer; auto ContainerOverlay = DockManager->containerOverlay(); auto DockAreaOverlay = DockManager->dockAreaOverlay(); - auto DockDropArea = DockAreaOverlay->dropAreaUnderCursor(); - auto ContainerDropArea = ContainerOverlay->dropAreaUnderCursor(); if (!TopContainer) { @@ -140,6 +157,9 @@ void FloatingDragPreviewPrivate::updateDropOverlays(const QPoint &GlobalPos) return; } + auto DockDropArea = DockAreaOverlay->dropAreaUnderCursor(); + auto ContainerDropArea = ContainerOverlay->dropAreaUnderCursor(); + int VisibleDockAreas = TopContainer->visibleDockAreaCount(); // Include the overlay widget we're dragging as a visible widget @@ -149,13 +169,27 @@ void FloatingDragPreviewPrivate::updateDropOverlays(const QPoint &GlobalPos) VisibleDockAreas++; } - ContainerOverlay->setAllowedAreas( VisibleDockAreas > 1 ? OuterDockAreas : AllDockAreas); + DockWidgetAreas AllowedContainerAreas = (VisibleDockAreas > 1) ? OuterDockAreas : AllDockAreas; + //ContainerOverlay->enableDropPreview(ContainerDropArea != InvalidDockWidgetArea); auto DockArea = TopContainer->dockAreaAt(GlobalPos); + // If the dock container contains only one single DockArea, then we need + // to respect the allowed areas - only the center area is relevant here because + // all other allowed areas are from the container + if (VisibleDockAreas == 1 && DockArea) + { + AllowedContainerAreas.setFlag(CenterDockWidgetArea, DockArea->allowedAreas().testFlag(CenterDockWidgetArea)); + } + + if (isContentPinnable()) + { + AllowedContainerAreas |= AutoHideDockAreas; + } + ContainerOverlay->setAllowedAreas(AllowedContainerAreas); + ContainerOverlay->enableDropPreview(ContainerDropArea != InvalidDockWidgetArea); if (DockArea && DockArea->isVisible() && VisibleDockAreas >= 0 && DockArea != ContentSourceArea) { DockAreaOverlay->enableDropPreview(true); DockAreaOverlay->setAllowedAreas( (VisibleDockAreas == 1) ? NoDockWidgetArea : DockArea->allowedAreas()); - DockWidgetArea Area = DockAreaOverlay->showOverlay(DockArea); // A CenterDockWidgetArea for the dockAreaOverlay() indicates that @@ -178,15 +212,13 @@ void FloatingDragPreviewPrivate::updateDropOverlays(const QPoint &GlobalPos) DockAreaOverlay->hideOverlay(); // If there is only one single visible dock area in a container, then // it does not make sense to show a dock overlay because the dock area - // would be removed and inserted at the same position + // would be removed and inserted at the same position. Only auto hide + // area is allowed if (VisibleDockAreas == 1) { - ContainerOverlay->hideOverlay(); - } - else - { - ContainerOverlay->showOverlay(TopContainer); + ContainerOverlay->setAllowedAreas(AutoHideDockAreas); } + ContainerOverlay->showOverlay(TopContainer); if (DockArea == ContentSourceArea && InvalidDockWidgetArea == ContainerDropArea) @@ -249,6 +281,7 @@ CFloatingDragPreview::CFloatingDragPreview(QWidget* Content, QWidget* parent) : d(new FloatingDragPreviewPrivate(this)) { d->Content = Content; + d->ContentFeatures = d->contentFeatures(); setAttribute(Qt::WA_DeleteOnClose); if (CDockManager::testConfigFlag(CDockManager::DragPreviewHasWindowFrame)) { @@ -268,8 +301,6 @@ CFloatingDragPreview::CFloatingDragPreview(QWidget* Content, QWidget* parent) : setWindowFlags(Flags); #endif - setWindowOpacity(0.6); - // Create a static image of the widget that should get undocked // This is like some kind preview image like it is uses in drag and drop // operations @@ -356,7 +387,7 @@ void CFloatingDragPreview::finishDragging() // state if they are dragged into a floating window if (ValidDropArea || d->isContentFloatable()) { - cleanupAutoHideContainerWidget(); + cleanupAutoHideContainerWidget(ContainerDropArea); } if (!d->DropContainer) @@ -365,20 +396,21 @@ void CFloatingDragPreview::finishDragging() } else if (DockDropArea != InvalidDockWidgetArea) { - d->DropContainer->dropWidget(d->Content, DockDropArea, d->DropContainer->dockAreaAt(QCursor::pos())); + d->DropContainer->dropWidget(d->Content, DockDropArea, d->DropContainer->dockAreaAt(QCursor::pos()), + d->DockManager->dockAreaOverlay()->tabIndexUnderCursor()); } else if (ContainerDropArea != InvalidDockWidgetArea) { + CDockAreaWidget* DockArea = nullptr; // If there is only one single dock area, and we drop into the center // then we tabify the dropped widget into the only visible dock area if (d->DropContainer->visibleDockAreaCount() <= 1 && CenterDockWidgetArea == ContainerDropArea) { - d->DropContainer->dropWidget(d->Content, ContainerDropArea, d->DropContainer->dockAreaAt(QCursor::pos())); - } - else - { - d->DropContainer->dropWidget(d->Content, ContainerDropArea, nullptr); + DockArea = d->DropContainer->dockAreaAt(QCursor::pos()); } + + d->DropContainer->dropWidget(d->Content, ContainerDropArea, DockArea, + d->DockManager->containerOverlay()->tabIndexUnderCursor()); } else { @@ -392,18 +424,29 @@ void CFloatingDragPreview::finishDragging() //============================================================================ -void CFloatingDragPreview::cleanupAutoHideContainerWidget() +void CFloatingDragPreview::cleanupAutoHideContainerWidget(DockWidgetArea ContainerDropArea) { auto DroppedDockWidget = qobject_cast(d->Content); auto DroppedArea = qobject_cast(d->Content); - if (DroppedDockWidget && DroppedDockWidget->autoHideDockContainer()) + auto AutoHideContainer = DroppedDockWidget + ? DroppedDockWidget->autoHideDockContainer() + : DroppedArea->autoHideDockContainer(); + + if (!AutoHideContainer) { - DroppedDockWidget->autoHideDockContainer()->cleanupAndDelete(); + return; } - if (DroppedArea && DroppedArea->autoHideDockContainer()) + + // If the dropped widget is already an auto hide widget and if it is moved + // to a new side bar location in the same container, then we do not need + // to cleanup + if (ads::internal::isSideBarArea(ContainerDropArea) + && (d->DropContainer == AutoHideContainer->dockContainer())) { - DroppedArea->autoHideDockContainer()->cleanupAndDelete(); + return; } + + AutoHideContainer->cleanupAndDelete(); } @@ -417,6 +460,7 @@ void CFloatingDragPreview::paintEvent(QPaintEvent* event) } QPainter painter(this); + painter.setOpacity(0.6); if (CDockManager::testConfigFlag(CDockManager::DragPreviewShowsContentPixmap)) { painter.drawPixmap(QPoint(0, 0), d->ContentPreviewPixmap); diff --git a/src/FloatingDragPreview.h b/src/FloatingDragPreview.h index 236f80eb5..68e3e5cf9 100644 --- a/src/FloatingDragPreview.h +++ b/src/FloatingDragPreview.h @@ -95,7 +95,7 @@ private Q_SLOTS: /** * Cleanup auto hide container if the dragged widget has one */ - void cleanupAutoHideContainerWidget(); + void cleanupAutoHideContainerWidget(DockWidgetArea ContainerDropArea); Q_SIGNALS: /** diff --git a/src/ads.qrc b/src/ads.qrc index 019f871f3..307e93b1f 100644 --- a/src/ads.qrc +++ b/src/ads.qrc @@ -18,5 +18,10 @@ images/vs-pin-button.svg images/vs-pin-button-pinned.svg images/vs-pin-button-pinned-focused.svg + images/vs-pin-button_45.svg + images/pin-button-big.svg + images/minimize-button.svg + images/minimize-button-focused.svg + images/vs-pin-button-disabled.svg diff --git a/src/ads_globals.cpp b/src/ads_globals.cpp index 2afb6756f..b019318e8 100644 --- a/src/ads_globals.cpp +++ b/src/ads_globals.cpp @@ -92,7 +92,7 @@ xcb_atom_t xcb_get_atom(const char *name) } xcb_connection_t *connection = x11_connection(); xcb_intern_atom_cookie_t request = xcb_intern_atom(connection, 1, strlen(name), name); - xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(connection, request, NULL); + xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(connection, request, nullptr); if (!reply) { return XCB_ATOM_NONE; @@ -283,14 +283,14 @@ QString detectWindowManagerX11() } if(sup_windows.length() == 0) { - ADS_PRINT("Failed to get the supporting window on non EWMH comform WM."); + ADS_PRINT("Failed to get the supporting window on non EWMH conform WM."); return "UNKNOWN"; } support_win = sup_windows[0]; QString ret = xcb_get_prop_string(support_win, "_NET_WM_NAME"); if(ret.length() == 0) { - ADS_PRINT("Empty WM name occured."); + ADS_PRINT("Empty WM name occurred."); return "UNKNOWN"; } return ret; @@ -333,6 +333,47 @@ CDockInsertParam dockAreaInsertParameters(DockWidgetArea Area) } +//============================================================================ +SideBarLocation toSideBarLocation(DockWidgetArea Area) +{ + switch (Area) + { + case LeftAutoHideArea: return SideBarLeft; + case RightAutoHideArea: return SideBarRight; + case TopAutoHideArea: return SideBarTop; + case BottomAutoHideArea: return SideBarBottom; + default: + return SideBarNone; + } + + return SideBarNone; +} + + +//============================================================================ +bool isHorizontalSideBarLocation(SideBarLocation Location) +{ + switch (Location) + { + case SideBarTop: + case SideBarBottom: return true; + case SideBarLeft: + case SideBarRight: return false; + default: + return false; + } + + return false; +} + + +//============================================================================ +bool isSideBarArea(DockWidgetArea Area) +{ + return toSideBarLocation(Area) != SideBarNone; +} + + //============================================================================ QPixmap createTransparentPixmap(const QPixmap& Source, qreal Opacity) { diff --git a/src/ads_globals.h b/src/ads_globals.h index aff8667a5..f7ccc8e31 100644 --- a/src/ads_globals.h +++ b/src/ads_globals.h @@ -82,20 +82,33 @@ enum DockWidgetArea TopDockWidgetArea = 0x04, BottomDockWidgetArea = 0x08, CenterDockWidgetArea = 0x10, + LeftAutoHideArea = 0x20, + RightAutoHideArea = 0x40, + TopAutoHideArea = 0x80, + BottomAutoHideArea = 0x100, InvalidDockWidgetArea = NoDockWidgetArea, OuterDockAreas = TopDockWidgetArea | LeftDockWidgetArea | RightDockWidgetArea | BottomDockWidgetArea, + AutoHideDockAreas = LeftAutoHideArea | RightAutoHideArea | TopAutoHideArea | BottomAutoHideArea, AllDockAreas = OuterDockAreas | CenterDockWidgetArea }; Q_DECLARE_FLAGS(DockWidgetAreas, DockWidgetArea) +enum eTabIndex +{ + TabDefaultInsertIndex = -1, + TabInvalidIndex = -2 +}; + + enum TitleBarButton { TitleBarButtonTabsMenu, TitleBarButtonUndock, TitleBarButtonClose, - TitleBarButtonAutoHide + TitleBarButtonAutoHide, + TitleBarButtonMinimize }; /** @@ -119,6 +132,7 @@ enum eIcon DockAreaMenuIcon, //!< DockAreaMenuIcon DockAreaUndockIcon,//!< DockAreaUndockIcon DockAreaCloseIcon, //!< DockAreaCloseIcon + DockAreaMinimizeIcon, IconCount, //!< just a delimiter for range checks }; @@ -144,7 +158,7 @@ enum SideBarLocation SideBarBottom, SideBarNone }; -Q_ENUMS(SideBarLocation); +Q_ENUMS(SideBarLocation) namespace internal @@ -153,6 +167,7 @@ static const bool RestoreTesting = true; static const bool Restore = false; static const char* const ClosedProperty = "close"; static const char* const DirtyProperty = "dirty"; +static const char* const LocationProperty = "Location"; extern const int FloatingWidgetDragStartEvent; extern const int DockedWidgetDragStartEvent; @@ -177,7 +192,7 @@ void xcb_update_prop(bool set, WId window, const char *type, const char *prop, c bool xcb_dump_props(WId window, const char *type); /** * Gets the active window manager from the X11 Server. - * Requires a EWMH conform window manager (Allmost all common used ones are). + * Requires a EWMH conform window manager (Almost all common used ones are). * Returns "UNKNOWN" otherwise. */ QString windowManager(); @@ -194,6 +209,7 @@ void replaceSplitterWidget(QSplitter* Splitter, QWidget* From, QWidget* To); */ void hideEmptyParentSplitters(CDockSplitter* FirstParentSplitter); + /** * Convenience class for QPair to provide better naming than first and * second @@ -212,6 +228,25 @@ class CDockInsertParam : public QPair */ CDockInsertParam dockAreaInsertParameters(DockWidgetArea Area); + +/** + * Returns the SieBarLocation for the AutoHide dock widget areas + */ +SideBarLocation toSideBarLocation(DockWidgetArea Area); + + +/** + * Returns true for the top or bottom side bar and false for the + * left and right side bar + */ +bool isHorizontalSideBarLocation(SideBarLocation Location); + + +/** + * Returns true, if the given dock area is a SideBar area + */ +bool isSideBarArea(DockWidgetArea Area); + /** * Searches for the parent widget of the given type. * Returns the parent widget of the given widget or 0 if the widget is not @@ -234,7 +269,7 @@ T findParent(const QWidget* w) } parentWidget = parentWidget->parentWidget(); } - return 0; + return nullptr; } /** diff --git a/src/images/minimize-button-focused.svg b/src/images/minimize-button-focused.svg new file mode 100644 index 000000000..c473a1333 --- /dev/null +++ b/src/images/minimize-button-focused.svg @@ -0,0 +1,2 @@ + + diff --git a/src/images/minimize-button.svg b/src/images/minimize-button.svg new file mode 100644 index 000000000..78df69e41 --- /dev/null +++ b/src/images/minimize-button.svg @@ -0,0 +1,2 @@ + + diff --git a/src/images/pin-button-big.svg b/src/images/pin-button-big.svg new file mode 100644 index 000000000..305d88029 --- /dev/null +++ b/src/images/pin-button-big.svg @@ -0,0 +1,2 @@ + + diff --git a/src/images/vs-pin-button-disabled.svg b/src/images/vs-pin-button-disabled.svg new file mode 100644 index 000000000..33b4b176e --- /dev/null +++ b/src/images/vs-pin-button-disabled.svg @@ -0,0 +1,2 @@ + + diff --git a/src/images/vs-pin-button_45.svg b/src/images/vs-pin-button_45.svg new file mode 100644 index 000000000..d8dc06116 --- /dev/null +++ b/src/images/vs-pin-button_45.svg @@ -0,0 +1,2 @@ + + diff --git a/src/linux/FloatingWidgetTitleBar.cpp b/src/linux/FloatingWidgetTitleBar.cpp index 854674864..00e6fcd4d 100644 --- a/src/linux/FloatingWidgetTitleBar.cpp +++ b/src/linux/FloatingWidgetTitleBar.cpp @@ -137,11 +137,11 @@ CFloatingWidgetTitleBar::CFloatingWidgetTitleBar(CFloatingDockContainer *parent) d->FloatingWidget = parent; d->createLayout(); - auto normalPixmap = this->style()->standardPixmap(QStyle::SP_TitleBarNormalButton, 0, d->MaximizeButton); + auto normalPixmap = this->style()->standardPixmap(QStyle::SP_TitleBarNormalButton, nullptr, d->MaximizeButton); d->NormalIcon.addPixmap(normalPixmap, QIcon::Normal); d->NormalIcon.addPixmap(internal::createTransparentPixmap(normalPixmap, 0.25), QIcon::Disabled); - auto maxPixmap = this->style()->standardPixmap(QStyle::SP_TitleBarMaxButton, 0, d->MaximizeButton); + auto maxPixmap = this->style()->standardPixmap(QStyle::SP_TitleBarMaxButton, nullptr, d->MaximizeButton); d->MaximizeIcon.addPixmap(maxPixmap, QIcon::Normal); d->MaximizeIcon.addPixmap(internal::createTransparentPixmap(maxPixmap, 0.25), QIcon::Disabled); setMaximizedIcon(d->Maximized); diff --git a/src/stylesheets/default.css b/src/stylesheets/default.css index 83d1e32ac..5a737012b 100644 --- a/src/stylesheets/default.css +++ b/src/stylesheets/default.css @@ -82,6 +82,10 @@ ads--CDockWidgetTab[activeTab="true"] QLabel { color: palette(foreground); } +#autoHideTitleLabel { + padding-left: 4px; + color: palette(foreground); +} #tabCloseButton { margin-top: 2px; @@ -278,7 +282,7 @@ ads--CAutoHideDockContainer ads--CDockAreaWidget[focused="true"] ads--CDockAreaT } -#autoHideTitleLabel { +ads--CAutoHideDockContainer #autoHideTitleLabel { padding-left: 4px; color: palette(light); } @@ -288,7 +292,8 @@ ads--CAutoHideDockContainer ads--CDockAreaWidget[focused="true"] ads--CDockAreaT * CAutoHideDockContainer titlebar buttons *****************************************************************************/ #dockAreaAutoHideButton { - qproperty-icon: url(:/ads/images/vs-pin-button.svg); + qproperty-icon: url(:/ads/images/vs-pin-button.svg), + url(:/ads/images/vs-pin-button-disabled.svg) disabled; qproperty-iconSize: 16px; } @@ -298,6 +303,12 @@ ads--CAutoHideDockContainer #dockAreaAutoHideButton { } +ads--CAutoHideDockContainer #dockAreaMinimizeButton { + qproperty-icon: url(:/ads/images/minimize-button-focused.svg); + qproperty-iconSize: 16px; +} + + ads--CAutoHideDockContainer #dockAreaCloseButton{ qproperty-icon: url(:/ads/images/close-button-focused.svg) } diff --git a/src/stylesheets/default_linux.css b/src/stylesheets/default_linux.css index 573d7dfe6..058352628 100644 --- a/src/stylesheets/default_linux.css +++ b/src/stylesheets/default_linux.css @@ -39,6 +39,12 @@ ads--CTitleBarButton { } +#autoHideTitleLabel { + padding-left: 4px; + color: palette(foreground); +} + + #tabsMenuButton { qproperty-icon: url(:/ads/images/tabs-menu-button.svg); qproperty-iconSize: 16px; @@ -88,6 +94,12 @@ ads--CDockWidgetTab[activeTab="true"] QLabel { } +#autoHideTitleLabel { + padding-left: 4px; + color: palette(foreground); +} + + #tabCloseButton { margin-top: 2px; background: none; @@ -314,7 +326,7 @@ ads--CAutoHideDockContainer ads--CDockAreaWidget[focused="true"] ads--CDockAreaT } -#autoHideTitleLabel { +ads--CAutoHideDockContainer #autoHideTitleLabel { padding-left: 4px; color: palette(light); } @@ -324,8 +336,9 @@ ads--CAutoHideDockContainer ads--CDockAreaWidget[focused="true"] ads--CDockAreaT * CAutoHideDockContainer titlebar buttons *****************************************************************************/ #dockAreaAutoHideButton { - qproperty-icon: url(:/ads/images/vs-pin-button.svg); - qproperty-iconSize: 16px; + qproperty-icon: url(:/ads/images/vs-pin-button.svg), + url(:/ads/images/vs-pin-button-disabled.svg) disabled; + qproperty-iconSize: 16px; } ads--CAutoHideDockContainer #dockAreaAutoHideButton { @@ -334,6 +347,12 @@ ads--CAutoHideDockContainer #dockAreaAutoHideButton { } +ads--CAutoHideDockContainer #dockAreaMinimizeButton { + qproperty-icon: url(:/ads/images/minimize-button-focused.svg); + qproperty-iconSize: 16px; +} + + ads--CAutoHideDockContainer #dockAreaCloseButton{ qproperty-icon: url(:/ads/images/close-button-focused.svg) } diff --git a/src/stylesheets/focus_highlighting.css b/src/stylesheets/focus_highlighting.css index 0fd9f183a..9c2f8ed85 100644 --- a/src/stylesheets/focus_highlighting.css +++ b/src/stylesheets/focus_highlighting.css @@ -95,6 +95,10 @@ ads--CDockWidgetTab[activeTab="true"] QLabel { color: palette(foreground); } +#autoHideTitleLabel { + padding-left: 4px; + color: palette(foreground); +} #tabCloseButton { margin-top: 2px; @@ -316,7 +320,7 @@ ads--CAutoHideDockContainer ads--CDockAreaWidget[focused="true"] ads--CDockAreaT } -#autoHideTitleLabel { +ads--CAutoHideDockContainer #autoHideTitleLabel { padding-left: 4px; color: palette(light); } @@ -326,16 +330,24 @@ ads--CAutoHideDockContainer ads--CDockAreaWidget[focused="true"] ads--CDockAreaT * CAutoHideDockContainer titlebar buttons *****************************************************************************/ #dockAreaAutoHideButton { - qproperty-icon: url(:/ads/images/vs-pin-button.svg); + qproperty-icon: url(:/ads/images/vs-pin-button.svg), + url(:/ads/images/vs-pin-button-disabled.svg) disabled; qproperty-iconSize: 16px; } + ads--CAutoHideDockContainer #dockAreaAutoHideButton { qproperty-icon: url(:/ads/images/vs-pin-button-pinned-focused.svg); qproperty-iconSize: 16px; } +ads--CAutoHideDockContainer #dockAreaMinimizeButton { + qproperty-icon: url(:/ads/images/minimize-button-focused.svg); + qproperty-iconSize: 16px; +} + + ads--CAutoHideDockContainer #dockAreaCloseButton{ qproperty-icon: url(:/ads/images/close-button-focused.svg) } diff --git a/src/stylesheets/focus_highlighting_linux.css b/src/stylesheets/focus_highlighting_linux.css index f21b137a4..48ec71a22 100644 --- a/src/stylesheets/focus_highlighting_linux.css +++ b/src/stylesheets/focus_highlighting_linux.css @@ -40,6 +40,12 @@ ads--CTitleBarButton { } +#autoHideTitleLabel { + padding-left: 4px; + color: palette(foreground); +} + + #tabsMenuButton { qproperty-icon: url(:/ads/images/tabs-menu-button.svg); qproperty-iconSize: 16px; @@ -391,7 +397,7 @@ ads--CAutoHideDockContainer ads--CDockAreaWidget[focused="true"] ads--CDockAreaT } -#autoHideTitleLabel { +ads--CAutoHideDockContainer #autoHideTitleLabel { padding-left: 4px; color: palette(light); } @@ -401,8 +407,9 @@ ads--CAutoHideDockContainer ads--CDockAreaWidget[focused="true"] ads--CDockAreaT * CAutoHideDockContainer titlebar buttons *****************************************************************************/ #dockAreaAutoHideButton { - qproperty-icon: url(:/ads/images/vs-pin-button.svg); - qproperty-iconSize: 16px; + qproperty-icon: url(:/ads/images/vs-pin-button.svg), + url(:/ads/images/vs-pin-button-disabled.svg) disabled; + qproperty-iconSize: 16px; } ads--CAutoHideDockContainer #dockAreaAutoHideButton { @@ -411,6 +418,12 @@ ads--CAutoHideDockContainer #dockAreaAutoHideButton { } +ads--CAutoHideDockContainer #dockAreaMinimizeButton { + qproperty-icon: url(:/ads/images/minimize-button-focused.svg); + qproperty-iconSize: 16px; +} + + ads--CAutoHideDockContainer #dockAreaCloseButton{ qproperty-icon: url(:/ads/images/close-button-focused.svg) }