Compare commits

...

20 Commits

Author SHA1 Message Date
Gary Wang
985ff74ccf feat: config flag to avoid QtADS using Qt StyleSheet (#835)
Resolve: githubuser0xFFFF/Qt-Advanced-Docking-System#833
2026-04-23 22:44:23 +02:00
Michael Jackson
a9aa10f4c2 BUG: Fixes overrides a destructor but is not marked 'override' (#836)
- If compiling with flag [-Werror,-Winconsistent-missing-destructor-override]
2026-04-23 22:43:02 +02:00
Lee Jae-Jin
7b30229355 feat: add preferred auto-hide sidebar location per dock widget (#829)
Add preferredAutoHideSideBarLocation property to CDockWidget that allows
applications to specify where a widget should be pinned, overriding
geometry-based detection. When unpinning, widgets with the same preferred
location merge as tabs instead of creating new splits.

Default is SideBarNone (existing geometry-based behavior preserved).
2026-04-05 09:03:15 +02:00
Luca
4a0b636148 Make all tab text readable in dark mode (753) (#828) 2026-04-05 08:59:35 +02:00
Andreas Ringlstetter
544c624f07 Unbind containers from DockManager to prevent accidental reuse before deletion (#823) 2026-04-05 08:58:29 +02:00
ma-robox
bbde511603 Toggling visibility of floating docked windows protected against external hide/show cycle by QWebEngineView. (#821)
Co-authored-by: AlbertoM <ma-robox@users.noreply.github.com>
2026-03-10 18:34:45 +01:00
Ruben de Bruin
ee3a526adb Fixes handling of native / alien widgets when (un)docking (#820)
* Initial plan

* Fix drawing artifacts when content widgets call winId()

When a content widget (e.g. VTK/OpenGL) calls winId() to get a native
window handle, Qt propagates native-window creation up the ancestor chain.
This makes CDockWidget, CDockAreaWidget, and CDockContainerWidget all
become native OS windows unexpectedly. Reparenting these native dock
widgets during floating or resizing then causes drawing artifacts.

Fix: set Qt::WA_DontCreateNativeAncestors on the content widget in
CDockWidget::setWidget() to stop native-window creation from propagating
up into the dock hierarchy.

Co-authored-by: RubendeBruin <34062862+RubendeBruin@users.noreply.github.com>

* Fix docking system to handle mix of native and alien Qt widgets

Three targeted fixes for the native/alien widget mixing problem:

1. Revert WA_DontCreateNativeAncestors from CDockWidget::setWidget().
   Setting it on the content widget created a broken native-inside-alien
   hierarchy: the QScrollArea (alien) could not clip its native child, so
   content escaped the scrollbox bounds; and alien CAutoHideDockContainer
   could not appear above the native content widget.
   Allowing natural propagation means QScrollArea also goes native, and OS
   parent/child window clipping works correctly.

2. Fix CDockContainerWidget::removeDockArea to use internalWinId() instead
   of testAttribute(WA_NativeWindow). WA_NativeWindow is only set when a
   widget is explicitly made native; widgets that became native through
   upward propagation from a descendant calling winId() have a real native
   window handle (internalWinId() != 0) but WA_NativeWindow may be unset.
   Setting their parent to nullptr would create a free-floating invisible
   OS window and cause drawing artifacts.

3. Fix CAutoHideDockContainer::collapseView: when the parent dock container
   is native but the auto-hide panel is still alien, promote the panel to a
   native window (winId()) before raise(). The OS always renders native
   child windows above a parent window's painted alien content, so an alien
   auto-hide panel can never appear above native sibling dock areas. Once
   the panel has its own OS-level window, raise() operates on OS Z-order
   and correctly places it above native content siblings.

Co-authored-by: RubendeBruin <34062862+RubendeBruin@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-03-10 18:34:08 +01:00
Alessandro de Jesus
e2c960238d Fixed Undock Button getting hidden after undocking and docking again a floating widget that contains only one single visble dock area (#816) 2026-03-08 08:11:09 +01:00
ZFail
62bc6fced7 Fixes that allows use project as submodule (#815)
* Fix ADS_VERSION support for builds without git dependency

- Move CMAKE_MODULE_PATH outside if(NOT ADS_VERSION) block so it's always available
- Parse ADS_VERSION to set PROJECT_VERSION_* variables for Versioning.cmake
- Update Versioning.cmake to use predefined version when available, fallback to git

This allows building with -DADS_VERSION=X.Y.Z without requiring git repository.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* Add ADS_PLATFORM_DIR option to customize output directory

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

---------

Co-authored-by: ArtemNabok <nabok.artem@yandex.ru>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-03-08 08:10:51 +01:00
Andreas Ringlstetter
dd83812b29 Toogle visibility of custom title bar based on fullscreen state (#814) 2026-02-16 21:51:40 +01:00
André Meyer
88db64451e FloatingDockContainer uses windowIcon from parent Widgets before falling back to QApplication (#812) 2026-02-11 06:02:56 +01:00
André Vautour
1a96a06cb6 Account for the tabs height when they are at the bottom. (#811)
Thank you
2026-02-11 06:01:01 +01:00
zhulongcc
00f88d9666 fix: Fix copy-paste errors in configflags CMakeLists. (#810) 2026-02-06 05:43:37 +01:00
mborgerson
664d02edd4 Raise dialogs only if they are modal (#808) 2026-02-06 05:41:12 +01:00
vhutter
72e5c566b7 Fix label color in CDockWidgetTab in dark mode (#804) 2026-01-27 06:37:17 +01:00
André Vautour
e78133b7b2 Update title bar label when the widget's title changes. (#798) 2026-01-15 21:42:18 +01:00
André Vautour
f9ad1d7801 Use Native Windows (#797)
* Avoid changing to a top-level window when moving a native window.

* Support using native windows for the dock/area widgets.
2026-01-15 21:39:51 +01:00
Boris Barbulovski
34b68d6eab Change git command working directory to this project (#796)
If this project is used as subproject of another CMake project, then git commands will fail because git will be executed from wrong location.
2026-01-13 07:00:14 +01:00
Boris Barbulovski
f8143d5419 Conditionally add Windows version resources (#795) 2026-01-11 21:20:35 +01:00
Uwe Kindler
d01ffe3bee Removed superfluous appveyour badge 2026-01-11 18:21:54 +01:00
25 changed files with 295 additions and 74 deletions

View File

@@ -4,12 +4,14 @@ if (POLICY CMP0091)
cmake_policy(SET CMP0091 NEW) cmake_policy(SET CMP0091 NEW)
endif (POLICY CMP0091) endif (POLICY CMP0091)
# Add cmake modules path (required for Versioning.cmake and git revision description)
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH})
# By default, the version information is extracted from the git index. However, # By default, the version information is extracted from the git index. However,
# we can override this behavior by explicitly setting ADS_VERSION and # we can override this behavior by explicitly setting ADS_VERSION and
# skipping the git checks. This is useful for cases where this project is being # skipping the git checks. This is useful for cases where this project is being
# used independently of its original git repo (e.g. vendored in another project) # used independently of its original git repo (e.g. vendored in another project)
if(NOT ADS_VERSION) if(NOT ADS_VERSION)
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH})
include(GetGitRevisionDescription) include(GetGitRevisionDescription)
git_describe(GitTagVersion --tags) git_describe(GitTagVersion --tags)
string(REGEX REPLACE "^([0-9]+)\\..*" "\\1" VERSION_MAJOR "${GitTagVersion}") string(REGEX REPLACE "^([0-9]+)\\..*" "\\1" VERSION_MAJOR "${GitTagVersion}")
@@ -23,6 +25,11 @@ else()
if(VERSION_DOT_COUNT EQUAL 2) if(VERSION_DOT_COUNT EQUAL 2)
set(VERSION_SHORT ${ADS_VERSION}) set(VERSION_SHORT ${ADS_VERSION})
string(REGEX REPLACE "^([0-9]+)\\..*" "\\1" VERSION_SONAME "${ADS_VERSION}") string(REGEX REPLACE "^([0-9]+)\\..*" "\\1" VERSION_SONAME "${ADS_VERSION}")
# Set PROJECT_VERSION_* variables for Versioning.cmake
string(REGEX MATCHALL "[0-9]+" VERSION_PARTS "${ADS_VERSION}")
list(GET VERSION_PARTS 0 PROJECT_VERSION_MAJOR)
list(GET VERSION_PARTS 1 PROJECT_VERSION_MINOR)
list(GET VERSION_PARTS 2 PROJECT_VERSION_PATCH)
else() else()
message(FATAL_ERROR "ADS_VERSION must be in major.minor.patch format, e.g. 3.8.1. Got ${ADS_VERSION}") message(FATAL_ERROR "ADS_VERSION must be in major.minor.patch format, e.g. 3.8.1. Got ${ADS_VERSION}")
endif() endif()
@@ -34,10 +41,15 @@ project(QtADS LANGUAGES CXX VERSION ${VERSION_SHORT})
option(BUILD_STATIC "Build the static library" OFF) option(BUILD_STATIC "Build the static library" OFF)
option(BUILD_EXAMPLES "Build the examples" ON) option(BUILD_EXAMPLES "Build the examples" ON)
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4") # Platform directory: auto-detected based on pointer size, or manually set
set(ads_PlatformDir "x86") if(NOT ADS_PLATFORM_DIR)
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4")
set(ads_PlatformDir "x86")
else()
set(ads_PlatformDir "x64")
endif()
else() else()
set(ads_PlatformDir "x64") set(ads_PlatformDir "${ADS_PLATFORM_DIR}")
endif() endif()
add_subdirectory(src) add_subdirectory(src)

View File

@@ -387,7 +387,7 @@ The library supports **Qt5** and **Qt6**.
### Windows ### Windows
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 10 / 11
[![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) [![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. The library was developed on and for Windows. It is used in a commercial Windows application and is therefore constantly tested.

View File

@@ -8,43 +8,48 @@
set(_VERSIONING_MODULE_DIR "${CMAKE_CURRENT_LIST_DIR}" CACHE INTERNAL "Versioning module directory") set(_VERSIONING_MODULE_DIR "${CMAKE_CURRENT_LIST_DIR}" CACHE INTERNAL "Versioning module directory")
# ------------------------------------------------------------ # ------------------------------------------------------------
# Extract version information from Git # Extract version information from Git or use predefined version
# ------------------------------------------------------------ # ------------------------------------------------------------
if(NOT DEFINED PROJECT_VERSION_MAJOR OR NOT DEFINED PROJECT_VERSION_MINOR OR NOT DEFINED PROJECT_VERSION_PATCH)
# Get tag (expected: v1.2.3 or 1.2.3 or 1.2.3-12-gHASH)
execute_process(
COMMAND git describe --tags --dirty
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
OUTPUT_VARIABLE GIT_DESC_RAW
OUTPUT_STRIP_TRAILING_WHITESPACE
)
# Get tag (expected: v1.2.3 or 1.2.3 or 1.2.3-12-gHASH) # Remove leading "v" if present
execute_process( string(REGEX REPLACE "^v" "" GIT_DESC "${GIT_DESC_RAW}")
COMMAND git describe --tags --dirty
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_DESC_RAW
OUTPUT_STRIP_TRAILING_WHITESPACE
)
# Remove leading "v" if present # Extract major.minor.patch
string(REGEX REPLACE "^v" "" GIT_DESC "${GIT_DESC_RAW}") string(REGEX MATCH "^([0-9]+)\\.([0-9]+)\\.([0-9]+)" _ "${GIT_DESC}")
set(PROJECT_VERSION_MAJOR "${CMAKE_MATCH_1}")
set(PROJECT_VERSION_MINOR "${CMAKE_MATCH_2}")
set(PROJECT_VERSION_PATCH "${CMAKE_MATCH_3}")
# Extract major.minor.patch
string(REGEX MATCH "^([0-9]+)\\.([0-9]+)\\.([0-9]+)" _ "${GIT_DESC}")
set(PROJECT_VERSION_MAJOR "${CMAKE_MATCH_1}") # Commit hash (full + short)
set(PROJECT_VERSION_MINOR "${CMAKE_MATCH_2}") execute_process(
set(PROJECT_VERSION_PATCH "${CMAKE_MATCH_3}") COMMAND git rev-parse HEAD
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
OUTPUT_VARIABLE PROJECT_GIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
execute_process(
COMMAND git rev-parse --short HEAD
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
OUTPUT_VARIABLE PROJECT_GIT_HASH_SHORT
OUTPUT_STRIP_TRAILING_WHITESPACE
)
endif()
set(PROJECT_VERSION_STRING set(PROJECT_VERSION_STRING
"${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}" "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}"
) )
# Commit hash (full + short)
execute_process(
COMMAND git rev-parse HEAD
OUTPUT_VARIABLE PROJECT_GIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
execute_process(
COMMAND git rev-parse --short HEAD
OUTPUT_VARIABLE PROJECT_GIT_HASH_SHORT
OUTPUT_STRIP_TRAILING_WHITESPACE
)
# Export variables to parent scope # Export variables to parent scope
set(PROJECT_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}" PARENT_SCOPE) set(PROJECT_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}" PARENT_SCOPE)
set(PROJECT_VERSION_MINOR "${PROJECT_VERSION_MINOR}" PARENT_SCOPE) set(PROJECT_VERSION_MINOR "${PROJECT_VERSION_MINOR}" PARENT_SCOPE)

View File

@@ -779,6 +779,9 @@ CMainWindow::CMainWindow(QWidget *parent) :
// uncomment if you would like to close tabs with the middle mouse button, web browser style // uncomment if you would like to close tabs with the middle mouse button, web browser style
// CDockManager::setConfigFlag(CDockManager::MiddleMouseButtonClosesTab, true); // CDockManager::setConfigFlag(CDockManager::MiddleMouseButtonClosesTab, true);
// uncomment if you would like to avoid using the built-in QSS stylesheet
// CDockManager::setConfigFlag(CDockManager::DisableStylesheet, true);
// Now create the dock manager and its content // Now create the dock manager and its content
d->DockManager = new CDockManager(this); d->DockManager = new CDockManager(this);
d->DockManager->setDockWidgetToolBarStyle(Qt::ToolButtonIconOnly, ads::CDockWidget::StateFloating); d->DockManager->setDockWidgetToolBarStyle(Qt::ToolButtonIconOnly, ads::CDockWidget::StateFloating);

View File

@@ -9,3 +9,4 @@ add_subdirectory(autohide)
add_subdirectory(autohidedragndrop) add_subdirectory(autohidedragndrop)
add_subdirectory(emptydockarea) add_subdirectory(emptydockarea)
add_subdirectory(dockindock) add_subdirectory(dockindock)
add_subdirectory(configflags)

View File

@@ -1,19 +1,19 @@
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
project(ads_example_centralwidget VERSION ${VERSION_SHORT}) project(ads_example_configflags VERSION ${VERSION_SHORT})
find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED) 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 REQUIRED)
set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_INCLUDE_CURRENT_DIR ON)
add_executable(configFlagsExample WIN32 add_executable(ConfigFlagsExample WIN32
main.cpp main.cpp
mainwindow.cpp mainwindow.cpp
mainwindow.ui mainwindow.ui
) )
target_include_directories(CentralWidgetExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src") target_include_directories(ConfigFlagsExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src")
target_link_libraries(CentralWidgetExample PRIVATE qtadvanceddocking-qt${QT_VERSION_MAJOR}) target_link_libraries(ConfigFlagsExample PRIVATE qtadvanceddocking-qt${QT_VERSION_MAJOR})
target_link_libraries(CentralWidgetExample PUBLIC Qt${QT_VERSION_MAJOR}::Core target_link_libraries(ConfigFlagsExample PUBLIC Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Gui
Qt${QT_VERSION_MAJOR}::Widgets) Qt${QT_VERSION_MAJOR}::Widgets)
set_target_properties(CentralWidgetExample PROPERTIES set_target_properties(ConfigFlagsExample PROPERTIES
AUTOMOC ON AUTOMOC ON
AUTORCC ON AUTORCC ON
AUTOUIC ON AUTOUIC ON
@@ -21,7 +21,7 @@ set_target_properties(CentralWidgetExample PROPERTIES
CXX_STANDARD_REQUIRED ON CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS OFF CXX_EXTENSIONS OFF
VERSION ${VERSION_SHORT} VERSION ${VERSION_SHORT}
EXPORT_NAME "Qt Advanced Docking System Central Widget Example" EXPORT_NAME "Qt Advanced Docking System Config Flags"
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${ads_PlatformDir}/lib" ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${ads_PlatformDir}/lib"
LIBRARY_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" RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${ads_PlatformDir}/bin"

View File

@@ -415,7 +415,31 @@ void CAutoHideDockContainer::moveContentsToParent()
// to the user and he does not have to search where the widget was inserted. // to the user and he does not have to search where the widget was inserted.
d->DockWidget->setDockArea(nullptr); d->DockWidget->setDockArea(nullptr);
auto DockContainer = dockContainer(); auto DockContainer = dockContainer();
DockContainer->addDockWidget(d->getDockWidgetArea(d->SideTabBarArea), d->DockWidget); auto targetArea = d->getDockWidgetArea(d->SideTabBarArea);
// If the widget has a preferred auto-hide location, try to find an existing
// opened dock area that contains a widget with the same preferred location
// and merge as a tab instead of creating a new split.
auto preferred = d->DockWidget->preferredAutoHideSideBarLocation();
if (preferred != SideBarNone)
{
for (auto area : DockContainer->openedDockAreas())
{
if (!area || area->isAutoHide()) continue;
// Check if any widget in this area has the same preferred location
for (auto dw : area->dockWidgets())
{
if (dw && dw->preferredAutoHideSideBarLocation() == preferred)
{
DockContainer->addDockWidget(CenterDockWidgetArea,
d->DockWidget, area);
return;
}
}
}
}
DockContainer->addDockWidget(targetArea, d->DockWidget);
} }
@@ -482,6 +506,17 @@ void CAutoHideDockContainer::collapseView(bool Enable)
{ {
updateSize(); updateSize();
d->updateResizeHandleSizeLimitMax(); d->updateResizeHandleSizeLimitMax();
// If the parent dock container has native child windows (e.g. because
// an OpenGL or VTK content widget called winId()), this panel is an
// alien (non-native) widget and will always be obscured by those native
// sibling windows regardless of Qt's paint order. Native OS windows are
// rendered above the parent's painted (alien) content by the windowing
// system. To allow raise() to use OS-level Z-order and appear on top,
// this panel must first be promoted to a native window itself.
if (parentWidget() && parentWidget()->internalWinId() && !internalWinId())
{
winId();
}
raise(); raise();
show(); show();
d->DockWidget->dockManager()->setDockWidgetFocused(d->DockWidget); d->DockWidget->dockManager()->setDockWidgetFocused(d->DockWidget);

View File

@@ -84,7 +84,9 @@ if(BUILD_STATIC)
target_compile_definitions( ${library_name} PUBLIC ADS_STATIC) target_compile_definitions( ${library_name} PUBLIC ADS_STATIC)
else() else()
add_library( ${library_name} SHARED ${ads_SRCS} ${ads_HEADERS}) add_library( ${library_name} SHARED ${ads_SRCS} ${ads_HEADERS})
add_windows_version_resources(${library_name}) if(WIN32)
add_windows_version_resources(${library_name})
endif()
target_compile_definitions( ${library_name} PRIVATE ADS_SHARED_EXPORT) target_compile_definitions( ${library_name} PRIVATE ADS_SHARED_EXPORT)
endif() endif()

View File

@@ -176,7 +176,7 @@ public:
/** /**
* Virtual Destructor * Virtual Destructor
*/ */
virtual ~CDockAreaTitleBar(); ~CDockAreaTitleBar() override;
/** /**
* Returns the pointer to the tabBar() * Returns the pointer to the tabBar()

View File

@@ -460,6 +460,12 @@ CDockAreaWidget::CDockAreaWidget(CDockManager* DockManager, CDockContainerWidget
d->createTitleBar(); d->createTitleBar();
d->ContentsLayout = new DockAreaLayout(d->Layout); d->ContentsLayout = new DockAreaLayout(d->Layout);
if (CDockManager::testConfigFlag(CDockManager::UseNativeWindows))
{
winId();
}
if (d->DockManager) if (d->DockManager)
{ {
Q_EMIT d->DockManager->dockAreaCreated(this); Q_EMIT d->DockManager->dockAreaCreated(this);
@@ -603,8 +609,7 @@ void CDockAreaWidget::removeDockWidget(CDockWidget* DockWidget)
{ {
if(CFloatingDockContainer* FloatingDockContainer = DockContainer->floatingWidget()) if(CFloatingDockContainer* FloatingDockContainer = DockContainer->floatingWidget())
{ {
FloatingDockContainer->hide(); FloatingDockContainer->finishDropOperation();
FloatingDockContainer->deleteLater();
} }
} }
} }
@@ -928,6 +933,19 @@ void CDockAreaWidget::updateTitleBarVisibility()
} }
//============================================================================
void CDockAreaWidget::updateWindowTitle()
{
auto currentWidget = d->ContentsLayout->currentWidget();
if (d->TitleBar && currentWidget)
{
d->TitleBar->autoHideTitleLabel()->setText(currentWidget->windowTitle());
}
markTitleBarMenuOutdated();
}
//============================================================================ //============================================================================
void CDockAreaWidget::markTitleBarMenuOutdated() void CDockAreaWidget::markTitleBarMenuOutdated()
{ {
@@ -1481,14 +1499,18 @@ QSize CDockAreaWidget::minimumSizeHint() const
return Super::minimumSizeHint(); return Super::minimumSizeHint();
} }
int extraHeight = 0;
if (d->TitleBar->isVisible()) if (d->TitleBar->isVisible())
{ {
return d->MinSizeHint + QSize(0, d->TitleBar->minimumSizeHint().height()); extraHeight += d->TitleBar->minimumSizeHint().height();
} }
else
if (CDockManager::testConfigFlag(CDockManager::TabsAtBottom) && d->tabBar()->isVisible())
{ {
return d->MinSizeHint; extraHeight += d->tabBar()->minimumSizeHint().height();
} }
return d->MinSizeHint + QSize(0, extraHeight);
} }

View File

@@ -165,6 +165,11 @@ protected:
*/ */
void internalSetCurrentDockWidget(CDockWidget* DockWidget); void internalSetCurrentDockWidget(CDockWidget* DockWidget);
/**
* Call this function to update the window title
*/
void updateWindowTitle();
/** /**
* Marks tabs menu to update * Marks tabs menu to update
*/ */

View File

@@ -907,6 +907,7 @@ void DockContainerWidgetPrivate::addDockAreasToList(const QList<CDockAreaWidget*
{ {
DockArea->titleBarButton(TitleBarButtonClose)->setVisible(true); DockArea->titleBarButton(TitleBarButtonClose)->setVisible(true);
DockArea->titleBarButton(TitleBarButtonAutoHide)->setVisible(true); DockArea->titleBarButton(TitleBarButtonAutoHide)->setVisible(true);
DockArea->titleBarButton(TitleBarButtonUndock)->setVisible(true);
} }
// We need to ensure, that the dock area title bar is visible. The title bar // We need to ensure, that the dock area title bar is visible. The title bar
@@ -1572,9 +1573,21 @@ void CDockContainerWidget::removeDockArea(CDockAreaWidget* area)
d->DockAreas.removeAll(area); d->DockAreas.removeAll(area);
auto Splitter = area->parentSplitter(); auto Splitter = area->parentSplitter();
// Remove are from parent splitter and recursively hide tree of parent // Remove area from parent splitter and recursively hide tree of parent
// splitters if it has no visible content // splitters if it has no visible content.
area->setParent(nullptr); // Use internalWinId() rather than testAttribute(WA_NativeWindow) because
// WA_NativeWindow is only set when a widget is *explicitly* made native
// (e.g. winId() called directly on it). Widgets that became native through
// propagation from a child calling winId() (e.g. a VTK/OpenGL widget) also
// hold a real native window handle but may not have WA_NativeWindow set.
// Setting the parent of such a native window to nullptr would make it an
// invisible top-level OS window, causing drawing artifacts. Reparent to
// the dock manager instead so the window stays off-screen but within the
// application's window hierarchy.
if (area->internalWinId())
area->setParent(d->DockManager);
else
area->setParent(nullptr);
internal::hideEmptyParentSplitters(Splitter); internal::hideEmptyParentSplitters(Splitter);
// Remove this area from cached areas // Remove this area from cached areas
@@ -2204,6 +2217,17 @@ CDockManager* CDockContainerWidget::dockManager() const
} }
//===========================================================================
void CDockContainerWidget::removeFromDockManager()
{
if (d->DockManager)
{
d->DockManager->removeDockContainer(this);
d->DockManager.clear();
}
}
//=========================================================================== //===========================================================================
void CDockContainerWidget::handleAutoHideWidgetEvent(QEvent* e, QWidget* w) void CDockContainerWidget::handleAutoHideWidgetEvent(QEvent* e, QWidget* w)
{ {

View File

@@ -86,6 +86,9 @@ private:
friend AutoHideDockContainerPrivate; friend AutoHideDockContainerPrivate;
friend CAutoHideSideBar; friend CAutoHideSideBar;
private Q_SLOTS:
void removeFromDockManager();
protected: protected:
/** /**
* Handles activation events to update zOrderIndex * Handles activation events to update zOrderIndex

View File

@@ -203,6 +203,10 @@ DockManagerPrivate::DockManagerPrivate(CDockManager* _public) :
//============================================================================ //============================================================================
void DockManagerPrivate::loadStylesheet() void DockManagerPrivate::loadStylesheet()
{ {
if (CDockManager::testConfigFlag(CDockManager::DisableStylesheet))
{
return;
}
initResource(); initResource();
QString Result; QString Result;
QString FileName = ":ads/stylesheets/"; QString FileName = ":ads/stylesheets/";
@@ -545,11 +549,14 @@ CDockManager::CDockManager(QWidget *parent) :
} }
// If the user clicks the main window or drags a floating widget or works with a // If the user clicks the main window or drags a floating widget or works with a
// dialog, then raise the main window, all floating widgets and the focus window // modal dialog, then raise the main window, all floating widgets and the focus window
// itself to bring it into foregreound of any other application // itself to bring it into foreground of any other application.
bool raise = qobject_cast<QMainWindow*>(widget) bool raise = qobject_cast<QMainWindow*>(widget)
|| qobject_cast<QDialog*>(widget)
|| qobject_cast<ads::CFloatingDockContainer*>(widget); || qobject_cast<ads::CFloatingDockContainer*>(widget);
if (auto dialog = qobject_cast<QDialog*>(widget))
{
raise |= dialog->isModal();
}
if (!raise) if (!raise)
{ {
return; return;
@@ -765,7 +772,8 @@ void CDockManager::registerFloatingWidget(CFloatingDockContainer* FloatingWidget
//============================================================================ //============================================================================
void CDockManager::removeFloatingWidget(CFloatingDockContainer* FloatingWidget) void CDockManager::removeFloatingWidget(CFloatingDockContainer* FloatingWidget)
{ {
d->FloatingWidgets.removeAll(FloatingWidget); int removed = d->FloatingWidgets.removeAll(FloatingWidget);
Q_ASSERT(removed == 1);
} }
//============================================================================ //============================================================================
@@ -780,7 +788,8 @@ void CDockManager::removeDockContainer(CDockContainerWidget* DockContainer)
{ {
if (this != DockContainer) if (this != DockContainer)
{ {
d->Containers.removeAll(DockContainer); int removed = d->Containers.removeAll(DockContainer);
Q_ASSERT(removed == 1);
} }
} }

View File

@@ -64,8 +64,13 @@ struct AutoHideTabPrivate;
* of the docking system. The dock manager uses an internal stylesheet to * of the docking system. The dock manager uses an internal stylesheet to
* style its components like splitters, tabs and buttons. If you want to * style its components like splitters, tabs and buttons. If you want to
* disable this stylesheet because your application uses its own, * disable this stylesheet because your application uses its own,
* just call the function for settings the stylesheet with an empty * you can either set the DisableStylesheet config flag before creating
* string. * the dock manager:
* \code
* CDockManager::setConfigFlag(CDockManager::DisableStylesheet, true);
* \endcode
* Or call the function for settings the stylesheet with an empty string
* after creating the dock manager:
* \code * \code
* DockManager->setStyleSheet(""); * DockManager->setStyleSheet("");
* \endcode * \endcode
@@ -217,6 +222,8 @@ public:
ShowTabTextOnlyForActiveTab =0x8000000, //! Set this flag to show label texts in dock area tabs only for active 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 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. TabsAtBottom = 0x20000000, //!< If the flag is set, tabs will be shown at the bottom instead of in the title bar.
UseNativeWindows = 0x40000000, //!< If the flag is set, windows for the dock and area widgets will be native.
DisableStylesheet = 0x80000000, //!< If the flag is set, the dock manager will not apply the default stylesheet
DefaultDockAreaButtons = DockAreaHasCloseButton DefaultDockAreaButtons = DockAreaHasCloseButton

View File

@@ -96,7 +96,8 @@ struct DockWidgetPrivate
WidgetFactory* Factory = nullptr; WidgetFactory* Factory = nullptr;
QPointer<CAutoHideTab> SideTabWidget; QPointer<CAutoHideTab> SideTabWidget;
CDockWidget::eToolBarStyleSource ToolBarStyleSource = CDockWidget::ToolBarStyleFromDockManager; CDockWidget::eToolBarStyleSource ToolBarStyleSource = CDockWidget::ToolBarStyleFromDockManager;
SideBarLocation PreferredAutoHideSideBarLocation = SideBarNone;
/** /**
* Private data constructor * Private data constructor
*/ */
@@ -396,6 +397,11 @@ CDockWidget::CDockWidget(CDockManager *manager, const QString &title, QWidget* p
{ {
setFocusPolicy(Qt::ClickFocus); setFocusPolicy(Qt::ClickFocus);
} }
if (CDockManager::testConfigFlag(CDockManager::UseNativeWindows))
{
winId();
}
} }
@@ -649,6 +655,20 @@ SideBarLocation CDockWidget::autoHideLocation() const
} }
//============================================================================
void CDockWidget::setPreferredAutoHideSideBarLocation(SideBarLocation Location)
{
d->PreferredAutoHideSideBarLocation = Location;
}
//============================================================================
SideBarLocation CDockWidget::preferredAutoHideSideBarLocation() const
{
return d->PreferredAutoHideSideBarLocation;
}
//============================================================================ //============================================================================
bool CDockWidget::isFloating() const bool CDockWidget::isFloating() const
{ {
@@ -878,7 +898,7 @@ bool CDockWidget::event(QEvent *e)
} }
if (d->DockArea) if (d->DockArea)
{ {
d->DockArea->markTitleBarMenuOutdated();//update tabs menu d->DockArea->updateWindowTitle();
} }
auto FloatingWidget = floatingDockContainer(); auto FloatingWidget = floatingDockContainer();
@@ -1332,7 +1352,15 @@ void CDockWidget::setAutoHide(bool Enable, SideBarLocation Location, int TabInde
} }
else else
{ {
auto area = (SideBarNone == Location) ? DockArea->calculateSideTabBarArea() : Location; auto area = Location;
if (SideBarNone == area && d->PreferredAutoHideSideBarLocation != SideBarNone)
{
area = d->PreferredAutoHideSideBarLocation;
}
else if (SideBarNone == area)
{
area = DockArea->calculateSideTabBarArea();
}
dockContainer()->createAndSetupAutoHideContainer(area, this, TabIndex); dockContainer()->createAndSetupAutoHideContainer(area, this, TabIndex);
} }
} }

View File

@@ -422,6 +422,22 @@ public:
*/ */
SideBarLocation autoHideLocation() const; SideBarLocation autoHideLocation() const;
/**
* Sets the preferred auto-hide sidebar location for this dock widget.
* When set to a value other than SideBarNone, the pin button will place
* this widget in the specified sidebar instead of using geometry-based
* detection. When unpinning, widgets with the same preferred location
* will be merged as tabs in the same dock area.
* Set to SideBarNone (default) to use the original geometry-based behavior.
*/
void setPreferredAutoHideSideBarLocation(SideBarLocation Location);
/**
* Returns the preferred auto-hide sidebar location, or SideBarNone
* if no preference is set (geometry-based detection will be used).
*/
SideBarLocation preferredAutoHideSideBarLocation() const;
/** /**
* This property holds whether the dock widget is floating. * This property holds whether the dock widget is floating.
* A dock widget is only floating, if it is the one and only widget inside * A dock widget is only floating, if it is the one and only widget inside

View File

@@ -91,7 +91,7 @@ public:
/** /**
* Virtual Destructor * Virtual Destructor
*/ */
virtual ~CDockWidgetTab(); ~CDockWidgetTab() override;
/** /**
* Returns true, if this is the active tab * Returns true, if this is the active tab

View File

@@ -374,6 +374,7 @@ struct FloatingDockContainerPrivate
QPoint DragStartPos; QPoint DragStartPos;
bool Hiding = false; bool Hiding = false;
bool AutoHideChildren = true; bool AutoHideChildren = true;
bool HideContentOnNextHide = false;
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) #if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
QWidget* MouseEventHandler = nullptr; QWidget* MouseEventHandler = nullptr;
CFloatingWidgetTitleBar* TitleBar = nullptr; CFloatingWidgetTitleBar* TitleBar = nullptr;
@@ -459,7 +460,7 @@ struct FloatingDockContainerPrivate
} }
else else
{ {
_this->setWindowIcon(QApplication::windowIcon()); _this->setWindowIcon(CurrentWidget->windowIcon());
} }
} }
@@ -499,6 +500,9 @@ void FloatingDockContainerPrivate::titleMouseReleaseEvent()
return; return;
} }
// DockManager will be unlinked from this within DropContainer->dropFloatingWidget
const auto OriginalDockManager = this->DockManager.data();
if (DockManager->dockAreaOverlay()->dropAreaUnderCursor() != InvalidDockWidgetArea if (DockManager->dockAreaOverlay()->dropAreaUnderCursor() != InvalidDockWidgetArea
|| DockManager->containerOverlay()->dropAreaUnderCursor() != InvalidDockWidgetArea) || DockManager->containerOverlay()->dropAreaUnderCursor() != InvalidDockWidgetArea)
{ {
@@ -532,8 +536,8 @@ void FloatingDockContainerPrivate::titleMouseReleaseEvent()
DropContainer->dropFloatingWidget(_this, QCursor::pos()); DropContainer->dropFloatingWidget(_this, QCursor::pos());
} }
DockManager->containerOverlay()->hideOverlay(); OriginalDockManager->containerOverlay()->hideOverlay();
DockManager->dockAreaOverlay()->hideOverlay(); OriginalDockManager->dockAreaOverlay()->hideOverlay();
} }
@@ -728,6 +732,11 @@ CFloatingDockContainer::CFloatingDockContainer(CDockManager *DockManager) :
l->addWidget(d->DockContainer); l->addWidget(d->DockContainer);
#endif #endif
if (CDockManager::testConfigFlag(CDockManager::UseNativeWindows))
{
winId();
}
DockManager->registerFloatingWidget(this); DockManager->registerFloatingWidget(this);
} }
@@ -845,6 +854,12 @@ void CFloatingDockContainer::changeEvent(QEvent *event)
this->showMaximized(); this->showMaximized();
} }
} }
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
if (d->TitleBar)
{
d->TitleBar->setVisible(!(isFloating() && isFullScreen()));
}
#endif
break; break;
default: default:
@@ -948,6 +963,15 @@ void CFloatingDockContainer::closeEvent(QCloseEvent *event)
return; return;
} }
// New bug (QWebEngineView reload side effect):
// when a WebEngine-based dock is tabified into a floating container, the
// embedded native/web process can trigger delayed hide/show cycles on the
// floating window. If every non-spontaneous hide propagates to
// DockWidget->toggleView(false), unrelated tabs are marked closed and seem
// to "disappear". We therefore arm HideContentOnNextHide only for the
// explicit close path.
d->HideContentOnNextHide = true;
// In Qt version after 5.9.2 there seems to be a bug that causes the // In Qt version after 5.9.2 there seems to be a bug that causes the
// QWidget::event() function to not receive any NonClientArea mouse // QWidget::event() function to not receive any NonClientArea mouse
// events anymore after a close/show cycle. The bug is reported here: // events anymore after a close/show cycle. The bug is reported here:
@@ -975,6 +999,15 @@ void CFloatingDockContainer::hideEvent(QHideEvent *event)
return; return;
} }
// Only a close operation should propagate hide->toggleView(false) to
// child dock widgets. Generic hide/show cycles (e.g. from platform or
// embedded native content) must not change dock open/closed state.
if (!d->HideContentOnNextHide)
{
return;
}
d->HideContentOnNextHide = false;
if ( d->AutoHideChildren ) if ( d->AutoHideChildren )
{ {
d->Hiding = true; d->Hiding = true;
@@ -1190,8 +1223,9 @@ void CFloatingDockContainer::finishDropOperation()
if (d->DockManager) if (d->DockManager)
{ {
d->DockManager->removeFloatingWidget(this); d->DockManager->removeFloatingWidget(this);
d->DockManager->removeDockContainer(this->dockContainer()); d->DockManager.clear();
} }
this->dockContainer()->removeFromDockManager();
} }
//============================================================================ //============================================================================

View File

@@ -238,6 +238,20 @@ void CFloatingWidgetTitleBar::mouseDoubleClickEvent(QMouseEvent *event)
} }
} }
//============================================================================
QSize CFloatingWidgetTitleBar::sizeHint() const
{
if (isVisible())
{
return QFrame::sizeHint();
}
else
{
// Allow titlebar to collapse when set invisible.
return QSize();
}
}
//============================================================================ //============================================================================
void CFloatingWidgetTitleBar::setMaximizedIcon(bool maximized) void CFloatingWidgetTitleBar::setMaximizedIcon(bool maximized)

View File

@@ -59,6 +59,7 @@ protected:
virtual void mouseReleaseEvent(QMouseEvent *ev) override; virtual void mouseReleaseEvent(QMouseEvent *ev) override;
virtual void mouseMoveEvent(QMouseEvent *ev) override; virtual void mouseMoveEvent(QMouseEvent *ev) override;
virtual void mouseDoubleClickEvent(QMouseEvent *event) override; virtual void mouseDoubleClickEvent(QMouseEvent *event) override;
virtual QSize sizeHint() const override;
void setMaximizeIcon(const QIcon& Icon); void setMaximizeIcon(const QIcon& Icon);
QIcon maximizeIcon() const; QIcon maximizeIcon() const;

View File

@@ -75,7 +75,7 @@ ads--CDockWidgetTab[activeTab="true"] {
} }
ads--CDockWidgetTab QLabel { ads--CDockWidgetTab QLabel {
color: palette(dark); color: palette(text);
} }
ads--CDockWidgetTab[activeTab="true"] QLabel { ads--CDockWidgetTab[activeTab="true"] QLabel {

View File

@@ -85,7 +85,7 @@ ads--CDockWidgetTab[activeTab="true"] {
ads--CDockWidgetTab QLabel { ads--CDockWidgetTab QLabel {
color: palette(dark); color: palette(text);
} }

View File

@@ -88,7 +88,7 @@ ads--CDockWidgetTab[activeTab="true"] {
} }
ads--CDockWidgetTab QLabel { ads--CDockWidgetTab QLabel {
color: palette(dark); color: palette(text);
} }
ads--CDockWidgetTab[activeTab="true"] QLabel { ads--CDockWidgetTab[activeTab="true"] QLabel {
@@ -138,7 +138,7 @@ ads--CDockWidgetTab[focused="true"] > #tabCloseButton:pressed {
} }
ads--CDockWidgetTab[focused="true"] QLabel { ads--CDockWidgetTab[focused="true"] QLabel {
color: palette(light); color: palette(highlighted-text);
} }

View File

@@ -101,7 +101,7 @@ ads--CDockWidgetTab[activeTab="true"] {
} }
ads--CDockWidgetTab QLabel { ads--CDockWidgetTab QLabel {
color: palette(dark); color: palette(text);
} }
ads--CDockWidgetTab[activeTab="true"] QLabel { ads--CDockWidgetTab[activeTab="true"] QLabel {
@@ -148,7 +148,7 @@ ads--CDockWidgetTab[focused="true"] > #tabCloseButton:pressed {
} }
ads--CDockWidgetTab[focused="true"] QLabel { ads--CDockWidgetTab[focused="true"] QLabel {
color: palette(light); color: palette(highlighted-text);
} }