Compare commits

...

68 Commits

Author SHA1 Message Date
Renato Araujo Oliveira Filho
8c76540b86 Fix Python bindings build on MacOs 2020-08-31 11:37:42 -03:00
Sergio Martins
6cf527403e Merge branch '1.0' into master 2020-08-30 17:53:04 +01:00
Sergio Martins
92843b4972 Merge branch '1.0' into master 2020-08-30 14:46:28 +01:00
Allen Winter
b2df16dcb0 Merge branch '1.0' 2020-08-27 12:23:38 -04:00
Sergio Martins
8bb15f5447 Updated ChangeLog 2020-08-27 15:30:17 +01:00
Sergio Martins
2977f0b8c6 Introduce Config::Flag_AlwaysTitleBarWhenFloating
Allows to improve on Flag_HideTitleBarWhenTabsVisible.

Flag_HideTitleBarWhenTabsVisible will hide title bar regardless
if the window is floating or docked. By enabling Flag_AlwaysTitleBarWhenFloating
the title bar will only be hidden if docked.

Demo: ./bin/kddockwidgets_example -qt

Fixes #64
2020-08-27 15:14:55 +01:00
Sergio Martins
251423116f Merge branch '1.0' into master 2020-08-27 14:54:18 +01:00
Sergio Martins
04e2a71b66 Merge branch '1.0' into master 2020-08-25 22:00:25 +01:00
Sergio Martins
a06fec51c8 Merge branch '1.0' into master 2020-08-25 21:54:32 +01:00
Sergio Martins
83eda81263 Minor code simplification 2020-08-24 17:58:42 +01:00
Sergio Martins
1bb476ca95 Merge branch '1.0' into master 2020-08-24 14:38:06 +01:00
Sergio Martins
f0ac455ec1 Allow the window being dragged to be transparent 2020-08-23 17:44:57 +01:00
Sergio Martins
44b2fc6944 indicators: Improve style for segmented indicators 2020-08-23 17:23:37 +01:00
Sergio Martins
113e6453ea example: Show usage of segmented indicators 2020-08-23 16:57:48 +01:00
Sergio Martins
283d814dc6 indicators: Also install the headers
So that the example app can show how to switch style type
2020-08-23 16:43:54 +01:00
Sergio Martins
034dc25483 indicators: Remove type()
There's no point in using it
2020-08-23 16:22:21 +01:00
Sergio Martins
b1333b2c4c ChangeLog: Update the changelog for 1.1 2020-08-23 13:52:16 +01:00
Sergio Martins
ff58ff72f9 Merge branch '1.0' into master 2020-08-23 13:47:52 +01:00
Sergio Martins
00b4dbc821 Add a Segmented Indicator
Just a POC of a new indicator style
2020-08-23 13:24:27 +01:00
Sergio Martins
35076bbdb6 indicators: Update the indicator's visibility in the base class
So derived classes don't have to.
Doesn't affect ClassicIndicators, since it uses a separate
window for them. But useful for new indicator types.
2020-08-22 19:58:45 +01:00
Sergio Martins
2dc98804f4 qml|indicators: Use frame geometry instead of frame rect
The rect is always at 0,0, not very useful.
2020-08-22 19:53:20 +01:00
Sergio Martins
961600fe74 qtquick: fix build
MainWindow.h is specific to QtWidgets
2020-08-22 19:19:59 +01:00
Sergio Martins
7fdca76a4c indicators: Use enum class for the type 2020-08-22 19:16:24 +01:00
Sergio Martins
32592f6ad1 classic indicators: Remove unneeded hidding in ctor
Already done in base class
2020-08-22 19:13:26 +01:00
Sergio Martins
1545e98e6d Merge branch '1.0' into master 2020-08-22 18:58:42 +01:00
Allen Winter
85b3feb10b Merge branch '1.0' 2020-08-15 10:15:26 -04:00
Sergio Martins
d9fdd20caa Add DockWidgetBase::isFocusedChanged
Fixes: #60
2020-08-15 00:46:41 +01:00
Sergio Martins
9e2d001ac5 Add DockWidget::isFocused() for issue #60
Needs a signal now
2020-08-14 23:51:35 +01:00
Sergio Martins
750f9b6142 tests: Add strong-focus to the widget 2020-08-14 23:00:14 +01:00
Sergio Martins
1c3cf8cf2d Merge branch '1.0' into master 2020-08-14 18:47:42 +01:00
Sergio Martins
db19cb40d7 Merge branch '1.0' into master 2020-08-14 18:30:00 +01:00
Sergio Martins
037fa76fff Fix building user apps out of repo
Our example was being built as part of KDDW, so that worked fine.
Now it can also be built with an installed KDDW.

Problem was the folder hiearchy in the installed include dir
didn't match the folder hiearchy in the source repo

Fixes #62
2020-08-14 17:49:24 +01:00
Sergio Martins
caa2ece751 qml: Move the 1st test from tst_docks to tst_common 2020-08-13 00:46:55 +01:00
Sergio Martins
22f2975f3c tests: createMainWindow() works for qml now too 2020-08-13 00:46:55 +01:00
Sergio Martins
d3323d824a tests: Make createMainWindow return MainWindowBase
Instead of QMainWindow, so it works with qml too
2020-08-13 00:46:55 +01:00
Allen Winter
41d1ea8f61 various fixes for the OBS config 2020-08-12 18:07:40 -04:00
Sergio Martins
1e85b1e7fb tests: Share EnsureTopLevelsDeleted with qml too
So we can start to copy tests to tst_common without hacking them
2020-08-12 22:45:59 +01:00
Sergio Martins
75464708af Remove unneeded debug 2020-08-12 22:00:23 +01:00
Sergio Martins
f007451442 Fix case where floatAction() wouldn't trigger
Fixes #58
2020-08-12 21:21:04 +01:00
Allen Winter
f591270d46 CMakeLists.txt - increase numbers. this will be v1.1 some day 2020-08-12 13:00:35 -04:00
Allen Winter
390423d0c1 Merge branch '1.0' 2020-08-12 12:59:09 -04:00
Sergio Martins
1aed8c7429 Merge branch '1.0' into master 2020-08-12 11:09:06 +01:00
Sergio Martins
0aee56114b qml: Dropping a dragged window now works 2020-08-11 23:40:07 +01:00
Sergio Martins
b3ec7423e8 qml: The inner indicators are now centered 2020-08-11 22:19:51 +01:00
Sergio Martins
179cd4cf45 qml: Add properties for outter/inner indicators visibility 2020-08-11 22:19:51 +01:00
Sergio Martins
75d26c3cce qml: Draw the inner indicators too 2020-08-11 22:19:51 +01:00
Allen Winter
9071664ef2 .../ClassicIndicatorsOverlay.qml - copyright+license header 2020-08-11 08:14:00 -04:00
Sergio Martins
1980f9c42c qml: Fix namespaced build 2020-08-11 09:59:25 +01:00
Sergio Martins
2c917dcd7c qml: Show 4 proper indicators when dragging in a floating window
They don't do much yet, but at least appear.
2020-08-11 01:35:28 +01:00
Allen Winter
5ccf15b9ed Merge branch '1.0' 2020-08-10 17:07:05 -04:00
Sergio Martins
7eaac87640 qml: Add a ClassicIndicatorsOverlay.qml file
Doesn't do much yet
2020-08-10 19:05:59 +01:00
Sergio Martins
5b9f08c754 Remove mapTo/From global from Layouting::Widget
Use the ones from QWidgetAdapter instead
2020-08-10 19:05:02 +01:00
Sergio Martins
02c165163a DragController: Use the geometry of the QWindow
While this works fine for QtWidgets, where the geometry of the
top-level QWidget is the same as the QWindow, it doesn't work
for QtQuick, where the geometry of the root item starts at 0,0
2020-08-10 19:03:15 +01:00
Sergio Martins
4bbc0f1d4f qml: Create drop indicator overlay too
The actual impl isn't done yet though
2020-08-09 22:45:15 +01:00
Sergio Martins
cf692797b6 qml: ClassicIndicators are now abstracted and built
The QtQuick counterpart just needs to be implemented
2020-08-09 20:27:05 +01:00
Sergio Martins
0e4ec055b2 Remove unused function 2020-08-09 20:05:38 +01:00
Sergio Martins
cf7c25cd88 Remove old cruft "rubberBandIsTopLevel" which we don't use 2020-08-09 19:23:52 +01:00
Sergio Martins
60b5eb00a3 Move IndicatorWindow to its own file
As it's the part that will be different with QtQuick.
ClassicIndicators can be shared.
2020-08-09 19:19:31 +01:00
Sergio Martins
0705b4da72 qml: Make Indicator class a implementation detail of QtWidgets
No longer accessed by ClassicIndicators
2020-08-09 18:57:23 +01:00
Sergio Martins
09b3e685a9 IndicatorWindow: Make some members private 2020-08-09 18:53:30 +01:00
Sergio Martins
64791eec82 qml: decouple ClassicIndicator from QRubberBand
the rubber band is now returned from the factory, meaning it can
be a QQuickItem when QtQuick is being used

Still need to remove the QtWidget'isms from IndicatorWindow
though
2020-08-09 18:39:56 +01:00
Sergio Martins
dc3aa354ec Minor code styling: Remove unneeded indentation 2020-08-09 18:13:23 +01:00
Sergio Martins
890784ba5a Finish FocusScope support
When clicking on a TitleBar the focus will be redirected to either:
- Last widget that had focus inside the scope
- To the DockWidget. Implies the user setting a guest widget that
accepts focus

Fixes: #56
2020-08-08 17:34:06 +01:00
Sergio Martins
c9468bef8a example: Add some helper code to debug focus 2020-08-08 17:34:06 +01:00
Sergio Martins
0b4c017f22 qml: Add subs regarding focus 2020-08-08 17:34:06 +01:00
Sergio Martins
6de558773f Merge branch '1.0' into master 2020-08-08 17:33:53 +01:00
Sergio Martins
94258abf43 Decouple title bar icon from tab bar icon
You can now use different icons, or even just set an icon for the
title bar but not tab bar

Fixes #57
2020-08-08 14:05:42 +01:00
Sergio Martins
10026ba191 Add FocusScope behaviour to TitleBar
Github issue #56 is not a KDDW bug, it's how Qt works. QtWidgets don't
have focus scope. But let's workaround and handroll our own FocusScope.

Now the title bar can be colored differently if the dock widget it controls
contains any focused children.

This just implements half of the story. You have to focus a child
for the title bar to change color. Clicking the title bar directly
isn't done yet. Needs to be figured out. What do we focus when clicking it?
TitleBars usually don't care about keyboard focus. Probably we
just use the user's widget as a focus proxy.
2020-08-08 12:33:23 +01:00
66 changed files with 1892 additions and 521 deletions

View File

@@ -61,11 +61,11 @@ else()
LANGUAGES CXX)
endif()
set(${PROJECT_NAME}_VERSION_MAJOR 0)
set(${PROJECT_NAME}_VERSION_MINOR 9)
set(${PROJECT_NAME}_VERSION_PATCH 99)
set(${PROJECT_NAME}_VERSION_MAJOR 1)
set(${PROJECT_NAME}_VERSION_MINOR 0)
set(${PROJECT_NAME}_VERSION_PATCH 95)
set(${PROJECT_NAME}_VERSION ${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}.${${PROJECT_NAME}_VERSION_PATCH})
set(${PROJECT_NAME}_SOVERSION "1.0")
set(${PROJECT_NAME}_SOVERSION "1.1")
include(FeatureSummary)
@@ -83,6 +83,8 @@ if(OPTION_DEVELOPER_MODE)
set(${PROJECT_NAME}_TESTS ON)
endif()
# option(OPTION_QTQUICK "Build for QtQuick instead of QtWidgets" OFF)
find_package(Qt5Widgets 5.9 REQUIRED)
set(CMAKE_AUTOMOC ON)

View File

@@ -26,3 +26,12 @@
- Static build support
- Namespaced Qt support
- Dozens of crash fixes, bugs and much more...
* v1.1.0 (, 2020)
- New drop indicator style type: Segmented Indicators
- Added FocusScope support
- Added DockWidget::isFocused() and DockWidgetBase::isFocusedChanged()
- Added Config::Flag_AlwaysTitleBarWhenFloating, which complements Flag_HideTitleBarWhenTabsVisible
* Roadmap
- QtQuick support

View File

@@ -23,6 +23,19 @@ endif()
if (NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 17)
endif()
# On macOS, check if Qt is a framework build. This affects how include paths should be handled.
get_target_property(QtCore_is_framework Qt5::Core FRAMEWORK)
if (QtCore_is_framework)
# Get the path to the framework dir.
get_filename_component(QT_FRAMEWORK_INCLUDE_DIR "${QT_INCLUDE_DIR}/../" ABSOLUTE)
list(GET Qt5Core_INCLUDE_DIRS 0 QT_INCLUDE_DIR)
# QT_INCLUDE_DIR points to the QtCore.framework directory, so we need to adjust this to point
# to the actual include directory, which has include files for non-framework parts of Qt.
get_filename_component(QT_INCLUDE_DIR "${QT_INCLUDE_DIR}/../../include" ABSOLUTE)
endif()
# Flags that we will pass to shiboken-generator
# --generator-set=shiboken: tells the generator that we want to use shiboken to generate code,
# a doc generator is also available

View File

@@ -41,7 +41,9 @@ public:
{
QPainter p(this);
QPen pen(Qt::black);
QBrush brush(Qt::yellow);
const QColor focusedBackgroundColor = Qt::yellow;
const QColor backgroundColor = focusedBackgroundColor.darker(115);
QBrush brush(isFocused() ? focusedBackgroundColor : backgroundColor);
pen.setWidth(4);
p.setPen(pen);
p.setBrush(brush);

View File

@@ -14,6 +14,7 @@
#include <QPainter>
#include <QDebug>
#include <QFile>
#include <QLineEdit>
static QHash<QString, QImage> s_images; /// clazy:exclude=non-pod-global-static
@@ -33,6 +34,15 @@ MyWidget::MyWidget(const QString &backgroundFile, const QString &logoFile, QWidg
it = s_images.insert(logoFile, QImage(logoFile));
m_logo = it.value();
}
setFocusPolicy(Qt::StrongFocus);
#if 0
// Uncomment to show focus propagation working
new QLineEdit(this);
auto l2 = new QLineEdit(this);
l2->move(0, 100);
setFocusProxy(l2);
#endif
}
MyWidget::~MyWidget()

View File

@@ -48,6 +48,9 @@ int main(int argc, char **argv)
QCommandLineOption noTitleBars("t", QCoreApplication::translate("main", "Hide titlebars when tabs are visible"));
parser.addOption(noTitleBars);
QCommandLineOption alwaysTitleBarWhenFloating("q", QCoreApplication::translate("main", "Don't hide title bars if floating, even if Flag_HideTitleBarWhenTabsVisible is specified."));
parser.addOption(alwaysTitleBarWhenFloating);
QCommandLineOption alwaysTabs("z", QCoreApplication::translate("main", "Show tabs even if there's only one"));
parser.addOption(alwaysTabs);
@@ -78,6 +81,9 @@ int main(int argc, char **argv)
QCommandLineOption maximizeButton("b", QCoreApplication::translate("main", "DockWidgets have maximize/restore buttons instead of float/dock button"));
parser.addOption(maximizeButton);
QCommandLineOption segmentedIndicators("y", QCoreApplication::translate("main", "Use segmented indicators instead of classical"));
parser.addOption(segmentedIndicators);
parser.addPositionalArgument("savedlayout", QCoreApplication::translate("main", "loads the specified json file at startup"));
#ifdef KDDOCKWIDGETS_SUPPORTS_NESTED_MAINWINDOWS
@@ -104,6 +110,9 @@ int main(int argc, char **argv)
Config::self().setSeparatorThickness(10);
}
if (parser.isSet(segmentedIndicators))
KDDockWidgets::DefaultWidgetFactory::s_dropIndicatorType = KDDockWidgets::DropIndicatorType::Segmented;
MainWindowOptions options = MainWindowOption_None;
#if defined(DOCKS_DEVELOPER_MODE)
options = parser.isSet(centralFrame) ? MainWindowOption_HasCentralFrame
@@ -117,6 +126,17 @@ int main(int argc, char **argv)
if (parser.isSet(alwaysTabs))
flags |= KDDockWidgets::Config::Flag_AlwaysShowTabs;
if (parser.isSet(alwaysTitleBarWhenFloating)) {
flags |= KDDockWidgets::Config::Flag_AlwaysTitleBarWhenFloating;
if (!(flags & KDDockWidgets::Config::Flag_HideTitleBarWhenTabsVisible)) {
qWarning() << "Flag_AlwaysTitleBarWhenFloating is unneeded if Flag_HideTitleBarWhenTabsVisible isn't used."
<< "As floating windows already have title bars by default.";
}
}
if (parser.isSet(customStyle))
flags |= KDDockWidgets::Config::Flag_TitleBarIsFocusable; // also showing title bar focus with -p, just to not introduce another switch
if (parser.isSet(reorderTabsOption))
flags |= KDDockWidgets::Config::Flag_AllowReorderTabs;

View File

@@ -0,0 +1,34 @@
#
# This file is part of KDDockWidgets.
#
# SPDX-FileCopyrightText: 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
# Author: Sergio Martins <sergio.martins@kdab.com>
#
# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
#
# Contact KDAB at <info@kdab.com> for commercial licensing options.
#
cmake_minimum_required(VERSION 3.7)
project(kddockwidgets_example_quick)
set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIRS ON)
if(NOT TARGET kddockwidgets)
# This will look for Qt, do find_package yourself manually before
# if you want to look for a specific Qt version for instance.
find_package(KDDockWidgets REQUIRED)
endif()
qt5_add_resources(RESOURCES_EXAMPLE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/resources_example.qrc)
add_executable(kddockwidgets_example_quick
main.cpp
${RESOURCES_EXAMPLE_SRC}
)
target_link_libraries(kddockwidgets_example_quick
PRIVATE
KDAB::kddockwidgets
)

View File

@@ -0,0 +1,20 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sergio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
import QtQuick 2.9
Rectangle {
color: "blue"
anchors.fill: parent
Text {
text: "Guest Widget!"
}
}

View File

@@ -0,0 +1,20 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sergio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
import QtQuick 2.9
Rectangle {
color: "lightblue"
anchors.fill: parent
Text {
text: "Guest Widget #1 !"
}
}

View File

@@ -0,0 +1,20 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sergio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
import QtQuick 2.9
Rectangle {
color: "pink"
anchors.fill: parent
Text {
text: "Guest Widget #2!"
}
}

View File

@@ -0,0 +1,20 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sergio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
import QtQuick 2.9
Rectangle {
color: "gray"
anchors.fill: parent
Text {
text: "Guest Widget #3!"
}
}

53
examples/qtquick/main.cpp Normal file
View File

@@ -0,0 +1,53 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sergio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#include "private/DockRegistry_p.h"
#include "private/quick/DockWidgetQuick.h"
#include "Config.h"
#include <QQuickView>
#include <QGuiApplication>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQuickView view;
KDDockWidgets::Config::self().setQmlEngine(view.engine());
view.resize(1000, 800);
view.show();
view.setResizeMode(QQuickView::SizeRootObjectToView);
auto dw1 = new KDDockWidgets::DockWidgetQuick("Dock #1");
dw1->setWidget(QStringLiteral("qrc:/Guest1.qml"));
dw1->resize(QSize(800, 800));
dw1->show();
auto dw2 = new KDDockWidgets::DockWidgetQuick("Dock #2");
dw2->setWidget(QStringLiteral("qrc:/Guest2.qml"));
dw2->resize(QSize(800, 800));
dw2->show();
auto dw3 = new KDDockWidgets::DockWidgetQuick("Dock #3");
dw3->setWidget(QStringLiteral("qrc:/Guest3.qml"));
dw3->resize(QSize(800, 800));
dw3->show();
view.setSource(QUrl("qrc:/main.qml"));
dw1->addDockWidgetToContainingWindow(dw3, KDDockWidgets::Location_OnRight);
KDDockWidgets::MainWindowBase *mainWindow = KDDockWidgets::DockRegistry::self()->mainwindows().constFirst();
mainWindow->addDockWidget(dw2, KDDockWidgets::Location_OnTop);
return app.exec();
}

23
examples/qtquick/main.qml Normal file
View File

@@ -0,0 +1,23 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sergio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
import QtQuick 2.6
import com.kdab.dockwidgets 1.0 as KDDW
KDDW.MainWindow {
id: root
Component.onCompleted: {
root.init("MyWindowName-1");
}
}

View File

@@ -0,0 +1,8 @@
<RCC>
<qresource prefix="/">
<file>main.qml</file>
<file>Guest1.qml</file>
<file>Guest2.qml</file>
<file>Guest3.qml</file>
</qresource>
</RCC>

View File

@@ -24,6 +24,7 @@ add_definitions(-DQT_NO_SIGNALS_SLOTS_KEYWORDS
set(DOCKSLIBS_SRCS
Config.cpp
Qt5Qt6Compat_p.h
FocusScope.cpp
FrameworkWidgetFactory.cpp
DockWidgetBase.cpp
MainWindowBase.cpp
@@ -43,6 +44,8 @@ set(DOCKSLIBS_SRCS
private/Frame.cpp
private/DropAreaWithCentralFrame.cpp
private/WidgetResizeHandler.cpp
private/indicators/ClassicIndicators.cpp
private/indicators/ClassicIndicatorsWindow.cpp
)
set(DOCKS_INSTALLABLE_INCLUDES
@@ -51,6 +54,7 @@ set(DOCKS_INSTALLABLE_INCLUDES
FrameworkWidgetFactory.h
DockWidgetBase.h
KDDockWidgets.h
FocusScope.h
QWidgetAdapter.h
LayoutSaver.h
LayoutSaver_p.h
@@ -99,7 +103,7 @@ else()
private/widgets/TitleBarWidget.cpp
private/widgets/DockWidget.cpp
private/widgets/QWidgetAdapter_widgets.cpp
private/indicators/ClassicIndicators.cpp
private/indicators/SegmentedIndicators.cpp
# private/indicators/AnimatedIndicators.cpp
)
@@ -199,6 +203,9 @@ install(FILES private/multisplitter/Separator_qwidget.h DESTINATION include/kddo
install(FILES private/multisplitter/multisplitter_export.h DESTINATION include/kddockwidgets/private/multisplitter)
install(FILES ${DOCKS_INSTALLABLE_PRIVATE_WIDGET_INCLUDES} DESTINATION include/kddockwidgets/private/widgets)
install(FILES private/indicators/ClassicIndicators_p.h DESTINATION include/kddockwidgets/private/indicators)
install(FILES private/indicators/SegmentedIndicators_p.h DESTINATION include/kddockwidgets/private/indicators)
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
KDDockWidgetsConfigVersion.cmake

View File

@@ -49,6 +49,7 @@ public:
MainWindowFactoryFunc m_mainWindowFactoryFunc = nullptr;
FrameworkWidgetFactory *m_frameworkWidgetFactory;
Flags m_flags = Flag_Default;
qreal m_draggedWindowOpacity = Q_QNAN;
};
Config::Config()
@@ -142,6 +143,16 @@ void Config::setSeparatorThickness(int value)
Layouting::Config::self().setSeparatorThickness(value);
}
void Config::setDraggedWindowOpacity(qreal opacity)
{
d->m_draggedWindowOpacity = opacity;
}
qreal Config::draggedWindowOpacity() const
{
return d->m_draggedWindowOpacity;
}
void Config::setQmlEngine(QQmlEngine *qmlEngine)
{
if (d->m_qmlEngine) {

View File

@@ -56,12 +56,14 @@ public:
Flag_None = 0, ///> No option set
Flag_NativeTitleBar = 1, ///> Enables the Native OS title bar on OSes that support it (Windows 10, macOS), ignored otherwise. This is mutually exclusive with Flag_AeroSnap
Flag_AeroSnapWithClientDecos = 2, ///> Enables AeroSnap even if we're not using the native title bar. Only supported on Windows 10.
Flag_AlwaysTitleBarWhenFloating = 4, ///> Floating windows will have a title bar even if Flag_HideTitleBarWhenTabsVisible is specified. Unneeded if Flag_HideTitleBarWhenTabsVisible isn't specified, as that's the default already.
Flag_HideTitleBarWhenTabsVisible = 8, ///> Hides the title bar if there's tabs visible. The empty space in the tab bar becomes draggable.
Flag_AlwaysShowTabs = 16, ///> Always show tabs, even if there's only one,
Flag_AllowReorderTabs = 32, /// Allows user to re-order tabs by dragging them
Flag_TabsHaveCloseButton = 64, /// Tabs will have a close button. Equivalent to QTabWidget::setTabsClosable(true).
Flag_DoubleClickMaximizes = 128, /// Double clicking the titlebar will maximize a floating window instead of re-docking it
Flag_TitleBarHasMaximizeButton = 256, /// The title bar will have a maximize/restore button when floating. This is mutually-exclusive with the floating button (since many apps behave that way).
Flag_TitleBarIsFocusable = 512, /// You can click the title bar and it will focus the last focused widget in the focus scope. If no previously focused widget then it focuses the user's dock widget guest, which should accept focus or use a focus proxy.
Flag_LazyResize = 1024, /// The dock widgets are resized in a lazy manner. The actual resize only happens when you release the mouse button.
Flag_Default = Flag_AeroSnapWithClientDecos ///> The defaults
};
@@ -130,6 +132,14 @@ public:
///Note: Only use this function at startup before creating any DockWidget or MainWindow.
void setSeparatorThickness(int value);
///@brief sets the dragged window opacity
///1.0 is fully opaque while 0.0 is fully transparent
void setDraggedWindowOpacity(qreal opacity);
///@brief returns the opacity to use when dragging dock widgets
///By default it's 1.0, fully opaque
qreal draggedWindowOpacity() const;
///@brief Sets the QQmlEngine to use. Applicable only when using QtQuick.
void setQmlEngine(QQmlEngine *);
QQmlEngine* qmlEngine() const;

View File

@@ -80,7 +80,6 @@ public:
QPoint defaultCenterPosForFloating();
void updateTitle();
void updateIcon();
void toggle(bool enabled);
void updateToggleAction();
void updateFloatAction();
@@ -101,7 +100,8 @@ public:
const QString name;
QStringList affinities;
QString title;
QIcon icon;
QIcon titleBarIcon;
QIcon tabBarIcon;
QWidgetOrQuick *widget = nullptr;
DockWidgetBase *const q;
DockWidgetBase::Options options;
@@ -355,16 +355,26 @@ void DockWidgetBase::setAsCurrentTab()
frame->setCurrentDockWidget(this);
}
void DockWidgetBase::setIcon(const QIcon &icon)
void DockWidgetBase::setIcon(const QIcon &icon, IconPlaces places)
{
d->icon = icon;
d->updateIcon();
if (places & IconPlace::TitleBar)
d->titleBarIcon = icon;
if (places & IconPlace::TabBar)
d->tabBarIcon = icon;
Q_EMIT iconChanged();
}
QIcon DockWidgetBase::icon() const
QIcon DockWidgetBase::icon(IconPlace place) const
{
return d->icon;
if (place == IconPlace::TitleBar)
return d->titleBarIcon;
if (place == IconPlace::TabBar)
return d->tabBarIcon;
return {};
}
void DockWidgetBase::forceClose()
@@ -420,6 +430,12 @@ bool DockWidgetBase::isMainWindow() const
return qobject_cast<MainWindowBase*>(widget());
}
bool DockWidgetBase::isFocused() const
{
auto f = this->frame();
return f && f->isFocused() && isCurrentTab();
}
void DockWidgetBase::setAffinityName(const QString &affinity)
{
setAffinities({ affinity });
@@ -514,6 +530,11 @@ void DockWidgetBase::saveLastFloatingGeometry()
}
}
void DockWidgetBase::updateFloatAction()
{
d->updateFloatAction();
}
QPoint DockWidgetBase::Private::defaultCenterPosForFloating()
{
MainWindowBase::List mainWindows = DockRegistry::self()->mainwindows();
@@ -534,10 +555,6 @@ void DockWidgetBase::Private::updateTitle()
toggleAction->setText(title);
}
void DockWidgetBase::Private::updateIcon()
{
}
void DockWidgetBase::Private::toggle(bool enabled)
{
if (enabled) {

View File

@@ -72,6 +72,14 @@ public:
};
Q_DECLARE_FLAGS(Options, Option)
enum class IconPlace {
TitleBar = 1,
TabBar = 2,
All = TitleBar | TabBar
};
Q_ENUM(IconPlace)
Q_DECLARE_FLAGS(IconPlaces, IconPlace)
/**
* @brief constructs a new DockWidget
* @param uniqueName the name of the dockwidget, should be unique. Use title for user visible text.
@@ -223,14 +231,22 @@ public:
/**
* @brief Sets an icon to show on title bars and tab bars.
* By default none is shown.
* @param places Specifies where the icon will be shown (TitleBar, TabBar or both)
*
* By default there's no icon set.
*
* @sa icon()
*/
void setIcon(const QIcon &icon);
void setIcon(const QIcon &icon, IconPlaces places = IconPlace::All);
/**
* @brief Returns the dock widget's icon.
* @brief Returns the dock widget's titlebar or tabbar icon (depending on the passed @p place)
*
* By default it's null.
*
* @sa setIcon()
*/
QIcon icon() const;
QIcon icon(IconPlace place = IconPlace::TitleBar) const;
/**
* @brief Like QWidget::close() but the hosted widget won't be asked if we
@@ -304,6 +320,12 @@ public:
*/
bool isMainWindow() const;
///@brief Returns whether This or any child of this dock widget is focused
///Not to be confused with QWidget::hasFocus(), which just refers to 1 widget. This includes
///variant includes children.
///@sa isFocusedChanged()
bool isFocused() const;
Q_SIGNALS:
///@brief signal emitted when the parent changed
void parentChanged();
@@ -327,6 +349,10 @@ Q_SIGNALS:
///@sa setOptions(), options()
void optionsChanged(KDDockWidgets::DockWidgetBase::Options);
///@brief emitted when isFocused changes
///@sa isFocused
void isFocusedChanged(bool);
protected:
void onParentChanged();
void onShown(bool spontaneous);
@@ -392,6 +418,9 @@ private:
///@brief If this dock widget is floating, then it saves its geometry
void saveLastFloatingGeometry();
///@brief Updates the floatAction state
void updateFloatAction();
class Private;
Private *const d;
};

130
src/FocusScope.cpp Normal file
View File

@@ -0,0 +1,130 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sérgio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
/**
* @file
* @brief FocusScope
*
* @author Sérgio Martins \<sergio.martins@kdab.com\>
*/
#include "FocusScope.h"
#include "TitleBar_p.h"
#include <QObject>
#include <QApplication>
#include <QPointer>
using namespace KDDockWidgets;
// Our Private inherits from QObject since FocusScope can't (Since Frame is already QObject)
class FocusScope::Private : public QObject
{
public:
Private(FocusScope *qq, QWidgetAdapter *thisWidget)
: q(qq)
, m_thisWidget(thisWidget)
{
connect(qApp, &QGuiApplication::focusObjectChanged,
this, &Private::onFocusObjectChanged);
onFocusObjectChanged(qApp->focusObject());
m_inCtor = false;
}
void setIsFocused(bool);
void onFocusObjectChanged(QObject *);
bool isInFocusScope(WidgetType *) const;
FocusScope *const q;
QWidgetAdapter *const m_thisWidget;
bool m_isFocused = false;
bool m_inCtor = true;
QPointer<WidgetType> m_lastFocusedInScope;
};
FocusScope::FocusScope(QWidgetAdapter *thisWidget)
: d(new Private(this, thisWidget))
{
}
FocusScope::~FocusScope()
{
delete d;
}
bool FocusScope::isFocused() const
{
return d->m_isFocused;
}
WidgetType *FocusScope::focusedWidget() const
{
return d->m_lastFocusedInScope;
}
void FocusScope::focus(Qt::FocusReason reason)
{
if (d->m_lastFocusedInScope) {
d->m_lastFocusedInScope->setFocus(reason);
} else {
if (auto frame = qobject_cast<Frame*>(d->m_thisWidget)) {
if (DockWidgetBase *dw = frame->currentDockWidget()) {
if (auto guest = dw->widget()) {
if (guest->focusPolicy() != Qt::NoFocus)
guest->setFocus(reason);
}
}
} else {
// Not a use case right now
d->m_thisWidget->setFocus(reason);
}
}
}
void FocusScope::Private::setIsFocused(bool is)
{
if (is != m_isFocused) {
m_isFocused = is;
if (!m_inCtor) // Hack so we don't call pure-virtual
Q_EMIT q->isFocusedChanged();
}
}
void FocusScope::Private::onFocusObjectChanged(QObject *obj)
{
auto widget = qobject_cast<WidgetType*>(obj);
if (!widget)
return;
const bool is = isInFocusScope(widget);
if (is && m_lastFocusedInScope != widget && !qobject_cast<TitleBar*>(obj)) {
m_lastFocusedInScope = widget;
Q_EMIT q->focusedWidgetChanged();
}
setIsFocused(is);
}
bool FocusScope::Private::isInFocusScope(WidgetType *widget) const
{
WidgetType *p = widget;
while (p) {
if (p == m_thisWidget)
return true;
p = KDDockWidgets::Private::parentWidget(p);
}
return false;
}

61
src/FocusScope.h Normal file
View File

@@ -0,0 +1,61 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sérgio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
/**
* @file
* @brief FocusScope
*
* @author Sérgio Martins \<sergio.martins@kdab.com\>
*/
#ifndef KD_DOCKWIDGETS_FOCUSSCOPE_H
#define KD_DOCKWIDGETS_FOCUSSCOPE_H
#include "docks_export.h"
#include "QWidgetAdapter.h"
namespace KDDockWidgets
{
///@brief Allows to implement a similar functionality to QtQuick's FocusScope item, in QtWidgets
class FocusScope
{
public:
///@brief constructor
explicit FocusScope(QWidgetAdapter *thisWidget);
~FocusScope();
///@brief Returns true if this FocusScope is focused.
///This is similar to the QWidget::hasFocus(), except that it counts with the children being focused too.
///i.e: If any child is focused then this FocusScope has focus too.
bool isFocused() const;
///@brief Returns the widget that's focused in this scope
///The widget itself might not have focus as in QWidget::hasFocus(), but will get actual focus
///as soon as this scope is focused.
WidgetType* focusedWidget() const;
///@brief Sets focus on this scope.
///
/// This will call QWidget::focus() on the last QWidget that was focused in this scope.
void focus(Qt::FocusReason = Qt::OtherFocusReason);
/*Q_SIGNALS:*/
///@brief reimplement in the 1st QObject derived class
virtual void isFocusedChanged() = 0;
virtual void focusedWidgetChanged() = 0;
private:
class Private;
Private *const d;
};
}
#endif

View File

@@ -15,6 +15,7 @@
#include "multisplitter/Separator_p.h"
#include "FloatingWindow_p.h"
#include "Config.h"
#include "indicators/ClassicIndicators_p.h"
#ifdef KDDOCKWIDGETS_QTWIDGETS
# include "indicators/ClassicIndicators_p.h"
@@ -25,6 +26,8 @@
# include "widgets/TabWidgetWidget_p.h"
# include "multisplitter/Separator_qwidget.h"
# include "widgets/FloatingWindowWidget_p.h"
# include "indicators/SegmentedIndicators_p.h"
# include <QRubberBand>
#else
# include "quick/FrameQuick_p.h"
# include "quick/DockWidgetQuick.h"
@@ -35,6 +38,8 @@
using namespace KDDockWidgets;
DropIndicatorType DefaultWidgetFactory::s_dropIndicatorType = DropIndicatorType::Classic;
FrameworkWidgetFactory::~FrameworkWidgetFactory()
{
}
@@ -82,8 +87,21 @@ FloatingWindow *DefaultWidgetFactory::createFloatingWindow(Frame *frame, MainWin
DropIndicatorOverlayInterface *DefaultWidgetFactory::createDropIndicatorOverlay(DropArea *dropArea) const
{
switch (s_dropIndicatorType) {
case DropIndicatorType::Classic:
return new ClassicIndicators(dropArea);
case DropIndicatorType::Segmented:
return new SegmentedIndicators(dropArea);
}
return new ClassicIndicators(dropArea);
}
QWidgetOrQuick *DefaultWidgetFactory::createRubberBand(QWidgetOrQuick *parent) const
{
return new QRubberBand(QRubberBand::Rectangle, parent);
}
#else
Frame *DefaultWidgetFactory::createFrame(QWidgetOrQuick *parent, FrameOptions options) const
@@ -116,9 +134,9 @@ FloatingWindow *DefaultWidgetFactory::createFloatingWindow(Frame *frame, MainWin
return new FloatingWindowQuick(frame, parent);
}
DropIndicatorOverlayInterface *DefaultWidgetFactory::createDropIndicatorOverlay(DropArea *) const
DropIndicatorOverlayInterface *DefaultWidgetFactory::createDropIndicatorOverlay(DropArea *dropArea) const
{
return nullptr;
return new ClassicIndicators(dropArea);
}
TabBar *DefaultWidgetFactory::createTabBar(TabWidget *parent) const
@@ -140,4 +158,9 @@ Layouting::Separator *DefaultWidgetFactory::createSeparator(Layouting::Widget *p
return new Layouting::SeparatorQuick(parent);
}
QWidgetOrQuick *DefaultWidgetFactory::createRubberBand(QWidgetOrQuick *parent) const
{
return new QWidgetOrQuick(parent);
}
#endif

View File

@@ -117,6 +117,10 @@ public:
/// Override to provide your own DropIndicatorOverlayInterface sub-class.
///@param dropArea Just forward to DropIndicatorOverlayInterface's constructor.
virtual DropIndicatorOverlayInterface *createDropIndicatorOverlay(DropArea *dropArea) const = 0;
///@brief Called internally by the framework to create a RubberBand to show as drop zone
///Returns a rubber band
virtual QWidgetOrQuick *createRubberBand(QWidgetOrQuick *parent) const = 0;
private:
Q_DISABLE_COPY(FrameworkWidgetFactory)
};
@@ -137,6 +141,9 @@ public:
FloatingWindow *createFloatingWindow(MainWindowBase *parent = nullptr) const override;
FloatingWindow *createFloatingWindow(Frame *frame, MainWindowBase *parent = nullptr) const override;
DropIndicatorOverlayInterface *createDropIndicatorOverlay(DropArea*) const override;
QWidgetOrQuick *createRubberBand(QWidgetOrQuick *parent) const override;
static DropIndicatorType s_dropIndicatorType;
private:
Q_DISABLE_COPY(DefaultWidgetFactory)
};

View File

@@ -73,6 +73,11 @@ namespace KDDockWidgets
SizePolicy, ///< Uses the item's sizeHint() and sizePolicy()
};
enum class DropIndicatorType {
Classic, ///< The default
Segmented
};
///@internal
inline QString locationStr(Location loc)
{

View File

@@ -0,0 +1 @@
#include "../../../../private/indicators/ClassicIndicators_p.h"

View File

@@ -0,0 +1 @@
#include "../../../../private/indicators/SegmentedIndicators_p.h"

View File

@@ -14,6 +14,7 @@
#include "Logging_p.h"
#include "Position_p.h"
#include "MultiSplitter_p.h"
#include "QWidgetAdapter.h"
#include <QPointer>
#include <QDebug>
@@ -52,6 +53,9 @@ DockRegistry::DockRegistry(QObject *parent)
KDDockWidgets::registerQmlTypes();
#endif
connect(qApp, &QGuiApplication::focusObjectChanged,
this, &DockRegistry::onFocusObjectChanged);
initKDDockWidgetResources();
}
@@ -65,6 +69,35 @@ void DockRegistry::maybeDelete()
delete this;
}
void DockRegistry::onFocusObjectChanged(QObject *obj)
{
DockWidgetBase *const unfocusedDW = m_focusedDockWidget.data();
DockWidgetBase *newFocusedDockWidget = nullptr;
// Check if it's inside a dock widget:
auto p = qobject_cast<WidgetType*>(obj);
while (p) {
if (auto dw = qobject_cast<DockWidgetBase*>(p)) {
newFocusedDockWidget = dw;
break;
}
p = KDDockWidgets::Private::parentWidget(p);
}
// Nothing changed
if (m_focusedDockWidget.data() == newFocusedDockWidget)
return;
m_focusedDockWidget = newFocusedDockWidget;
if (unfocusedDW)
Q_EMIT unfocusedDW->isFocusedChanged(false);
if (m_focusedDockWidget)
Q_EMIT m_focusedDockWidget->isFocusedChanged(true);
}
bool DockRegistry::isEmpty() const
{
return m_dockWidgets.isEmpty() && m_mainWindows.isEmpty() && m_nestedWindows.isEmpty();

View File

@@ -18,6 +18,7 @@
#include <QVector>
#include <QObject>
#include <QPointer>
/**
* DockRegistry is a singleton that knows about all DockWidgets.
@@ -48,6 +49,8 @@ public:
void registerFrame(Frame *);
void unregisterFrame(Frame *);
DockWidgetBase *focusedDockWidget() const;
DockWidgetBase *dockByName(const QString &) const;
MainWindowBase *mainWindowByName(const QString &) const;
@@ -167,12 +170,14 @@ protected:
private:
explicit DockRegistry(QObject *parent = nullptr);
void maybeDelete();
void onFocusObjectChanged(QObject *);
bool m_isProcessingAppQuitEvent = false;
DockWidgetBase::List m_dockWidgets;
MainWindowBase::List m_mainWindows;
Frame::List m_frames;
QVector<FloatingWindow*> m_nestedWindows;
QVector<MultiSplitter*> m_layouts;
QPointer<DockWidgetBase> m_focusedDockWidget;
};
}

View File

@@ -427,14 +427,16 @@ template <typename T>
static WidgetType* qtTopLevelUnderCursor_impl(QPoint globalPos, const QVector<QWindow*> &windows, T windowBeingDragged)
{
for (int i = windows.size() -1; i >= 0; --i) {
auto tl = KDDockWidgets::Private::widgetForWindow(windows.at(i));
QWindow *window = windows.at(i);
auto tl = KDDockWidgets::Private::widgetForWindow(window);
if (!tl->isVisible() || tl == windowBeingDragged || KDDockWidgets::Private::isMinimized(tl))
continue;
if (windowBeingDragged && KDDockWidgets::Private::windowForWidget(windowBeingDragged) == KDDockWidgets::Private::windowForWidget(tl))
continue;
if (KDDockWidgets::Private::geometry(tl).contains(globalPos)) {
if (window->geometry().contains(globalPos)) {
qCDebug(toplevels) << Q_FUNC_INFO << "Found top-level" << tl;
return tl;
}

View File

@@ -27,33 +27,45 @@ DropIndicatorOverlayInterface::DropIndicatorOverlayInterface(DropArea *dropArea)
void DropIndicatorOverlayInterface::setWindowBeingDragged(const FloatingWindow *window)
{
if (window != m_windowBeingDragged) {
m_windowBeingDragged = window;
if (m_windowBeingDragged) {
setGeometry(m_dropArea->QWidgetAdapter::rect());
raise();
} else {
setHoveredFrame(nullptr);
}
if (window == m_windowBeingDragged)
return;
updateVisibility();
m_windowBeingDragged = window;
if (m_windowBeingDragged) {
setGeometry(m_dropArea->QWidgetAdapter::rect());
raise();
} else {
setHoveredFrame(nullptr);
}
setVisible(m_windowBeingDragged != nullptr);
updateVisibility();
}
QRect DropIndicatorOverlayInterface::hoveredFrameRect() const
{
return m_hoveredFrameRect;
}
void DropIndicatorOverlayInterface::setHoveredFrame(Frame *frame)
{
if (frame != m_hoveredFrame) {
if (m_hoveredFrame)
disconnect(m_hoveredFrame, &QObject::destroyed, this, &DropIndicatorOverlayInterface::onFrameDestroyed);
if (frame == m_hoveredFrame)
return;
m_hoveredFrame = frame;
if (m_hoveredFrame)
connect(frame, &QObject::destroyed, this, &DropIndicatorOverlayInterface::onFrameDestroyed);
if (m_hoveredFrame)
disconnect(m_hoveredFrame, &QObject::destroyed, this, &DropIndicatorOverlayInterface::onFrameDestroyed);
updateVisibility();
Q_EMIT hoveredFrameChanged(m_hoveredFrame);
onHoveredFrameChanged(m_hoveredFrame);
m_hoveredFrame = frame;
if (m_hoveredFrame) {
connect(frame, &QObject::destroyed, this, &DropIndicatorOverlayInterface::onFrameDestroyed);
setHoveredFrameRect(m_hoveredFrame->QWidgetAdapter::geometry());
} else {
setHoveredFrameRect(QRect());
}
updateVisibility();
Q_EMIT hoveredFrameChanged(m_hoveredFrame);
onHoveredFrameChanged(m_hoveredFrame);
}
bool DropIndicatorOverlayInterface::isHovered() const
@@ -61,6 +73,11 @@ bool DropIndicatorOverlayInterface::isHovered() const
return m_windowBeingDragged != nullptr;
}
DropIndicatorOverlayInterface::DropLocation DropIndicatorOverlayInterface::currentDropLocation() const
{
return m_currentDropLocation;
}
KDDockWidgets::Location DropIndicatorOverlayInterface::multisplitterLocationFor(DropIndicatorOverlayInterface::DropLocation dropLoc)
{
switch (dropLoc) {
@@ -92,10 +109,25 @@ void DropIndicatorOverlayInterface::onFrameDestroyed()
void DropIndicatorOverlayInterface::onHoveredFrameChanged(Frame *)
{
}
void DropIndicatorOverlayInterface::setCurrentDropLocation(DropIndicatorOverlayInterface::DropLocation location)
{
m_currentDropLocation = location;
if (m_currentDropLocation != location) {
m_currentDropLocation = location;
Q_EMIT currentDropLocationChanged();
}
}
void DropIndicatorOverlayInterface::hover(QPoint globalPos)
{
hover_impl(globalPos);
}
void DropIndicatorOverlayInterface::setHoveredFrameRect(QRect rect)
{
if (m_hoveredFrameRect != rect) {
m_hoveredFrameRect = rect;
Q_EMIT hoveredFrameRectChanged();
}
}

View File

@@ -22,17 +22,12 @@ namespace KDDockWidgets {
class FloatingWindow;
class DropArea;
class DOCKS_EXPORT_FOR_UNIT_TESTS DropIndicatorOverlayInterface : public QWidgetAdapter
class DOCKS_EXPORT DropIndicatorOverlayInterface : public QWidgetAdapter
{
Q_OBJECT
Q_PROPERTY(QRect hoveredFrameRect READ hoveredFrameRect NOTIFY hoveredFrameRectChanged)
Q_PROPERTY(KDDockWidgets::DropIndicatorOverlayInterface::DropLocation currentDropLocation READ currentDropLocation NOTIFY currentDropLocationChanged)
public:
enum Type {
TypeNone = 0,
TypeClassic = 1,
TypeAnimated = 2
};
Q_ENUM(Type)
enum DropLocation {
DropLocation_None = 0,
DropLocation_Left,
@@ -43,36 +38,45 @@ public:
DropLocation_OutterLeft,
DropLocation_OutterTop,
DropLocation_OutterRight,
DropLocation_OutterBottom
DropLocation_OutterBottom,
DropLocation_First = DropLocation_Left,
DropLocation_Last = DropLocation_OutterBottom,
};
Q_ENUM(DropLocation)
explicit DropIndicatorOverlayInterface(DropArea *dropArea);
void setHoveredFrame(Frame *);
void setWindowBeingDragged(const FloatingWindow *);
QRect hoveredFrameRect() const;
bool isHovered() const;
DropLocation currentDropLocation() const { return m_currentDropLocation; }
DropLocation currentDropLocation() const;
Frame *hoveredFrame() const { return m_hoveredFrame; }
void setCurrentDropLocation(DropIndicatorOverlayInterface::DropLocation location);
virtual Type indicatorType() const = 0;
virtual void hover(QPoint globalPos) = 0;
void hover(QPoint globalPos);
virtual QPoint posForIndicator(DropLocation) const = 0; // Used by unit-tests only
virtual QPoint posForIndicator(DropLocation) const { return {}; }; // Used by unit-tests only
static KDDockWidgets::Location multisplitterLocationFor(DropLocation);
Q_SIGNALS:
void hoveredFrameChanged(KDDockWidgets::Frame *);
void hoveredFrameRectChanged();
void currentDropLocationChanged();
private:
void onFrameDestroyed();
void setHoveredFrameRect(QRect);
QRect m_hoveredFrameRect;
DropLocation m_currentDropLocation = DropLocation_None;
protected:
virtual void hover_impl(QPoint globalPos) = 0;
virtual void onHoveredFrameChanged(Frame *);
virtual void updateVisibility() = 0;
virtual void updateVisibility() {};
Frame *m_hoveredFrame = nullptr;
DropLocation m_currentDropLocation = DropLocation_None;
QPointer<const FloatingWindow> m_windowBeingDragged;
DropArea *const m_dropArea;
};

View File

@@ -316,7 +316,8 @@ void FloatingWindow::updateTitleBarVisibility()
if (KDDockWidgets::usesNativeTitleBar()) {
visible = false;
} else {
if (Config::self().flags() & Config::Flag_HideTitleBarWhenTabsVisible) {
const auto flags = Config::self().flags();
if ((flags & Config::Flag_HideTitleBarWhenTabsVisible) && !(flags & Config::Flag_AlwaysTitleBarWhenFloating)) {
if (hasSingleFrame()) {
visible = !frames().first()->hasTabsVisible();
}
@@ -417,3 +418,14 @@ QRect FloatingWindow::dragRect() const
return rect;
}
bool FloatingWindow::event(QEvent *ev)
{
if (ev->type() == QEvent::ActivationChange) {
// Since QWidget is missing a signal for window activation
Q_EMIT activatedChanged();
}
return QWidgetAdapter::event(ev);
}

View File

@@ -124,6 +124,7 @@ public:
QRect dragRect() const;
Q_SIGNALS:
void activatedChanged();
void numFramesChanged();
void windowStateChanged(QWindowStateChangeEvent *);
protected:
@@ -131,6 +132,7 @@ protected:
bool nativeEvent(const QByteArray &eventType, void *message, long *result) override;
#endif
bool event(QEvent *ev) override;
void onCloseEvent(QCloseEvent *) override;
DropArea *const m_dropArea;

View File

@@ -48,6 +48,7 @@ static FrameOptions actualOptions(FrameOptions options)
Frame::Frame(QWidgetOrQuick *parent, FrameOptions options)
: LayoutGuestWidget(parent)
, FocusScope(this)
, m_titleBar(Config::self().frameworkWidgetFactory()->createTitleBar(this))
, m_options(actualOptions(options))
{
@@ -233,6 +234,10 @@ void Frame::onDockWidgetCountChanged()
// We don't really keep track of the state, so emit even if the visibility didn't change. No biggie.
if (!(m_options & FrameOption_AlwaysShowsTabs))
Q_EMIT hasTabsVisibleChanged();
const DockWidgetBase::List docks = dockWidgets();
for (DockWidgetBase *dock : docks)
dock->updateFloatAction();
}
Q_EMIT numDockWidgetsChanged();
@@ -280,7 +285,7 @@ void Frame::updateTitleBarVisibility()
bool Frame::containsMouse(QPoint globalPos) const
{
return QWidgetAdapter::rect().contains(LayoutGuestWidgetBase::mapFromGlobal(globalPos));
return QWidgetAdapter::rect().contains(KDDockWidgets::QWidgetAdapter::mapFromGlobal(globalPos));
}
TitleBar *Frame::titleBar() const

View File

@@ -24,6 +24,7 @@
#include "LayoutSaver_p.h"
#include "multisplitter/Widget_qwidget.h"
#include "multisplitter/Item_p.h"
#include "FocusScope.h"
#include <QWidget>
#include <QVector>
@@ -47,7 +48,9 @@ class FloatingWindow;
* inside a MultiSplitter (DropArea). Be it a MultiSplitter belonging to a MainWindow or belonging
* to a FloatingWindow.
*/
class DOCKS_EXPORT Frame : public LayoutGuestWidget
class DOCKS_EXPORT Frame
: public LayoutGuestWidget
, public FocusScope
{
Q_OBJECT
Q_PROPERTY(KDDockWidgets::TitleBar* titleBar READ titleBar CONSTANT)
@@ -223,6 +226,8 @@ Q_SIGNALS:
void hasTabsVisibleChanged();
void layoutInvalidated();
void isInMainWindowChanged();
void isFocusedChanged() override; // override from non-QObject
void focusedWidgetChanged() override;
protected:
/**

View File

@@ -220,11 +220,10 @@ void MultiSplitter::addMultiSplitter(MultiSplitter *sourceMultiSplitter, Locatio
void MultiSplitter::removeItem(Layouting::Item *item)
{
if (!item)
if (!item) {
qWarning() << Q_FUNC_INFO << "nullptr item";
if (!item)
return;
}
item->parentContainer()->removeItem(item);
}

View File

@@ -28,7 +28,12 @@ TitleBar::TitleBar(Frame *parent)
, m_floatingWindow(nullptr)
{
connect(m_frame, &Frame::numDockWidgetsChanged, this, &TitleBar::updateCloseButton);
connect(m_frame, &Frame::isFocusedChanged, this, &TitleBar::isFocusedChanged);
init();
if (Config::self().flags() & Config::Flag_TitleBarIsFocusable)
setFocusPolicy(Qt::StrongFocus);
}
TitleBar::TitleBar(FloatingWindow *parent)
@@ -41,6 +46,7 @@ TitleBar::TitleBar(FloatingWindow *parent)
connect(m_floatingWindow, &FloatingWindow::numFramesChanged, this, &TitleBar::updateFloatButton);
connect(m_floatingWindow, &FloatingWindow::numFramesChanged, this, &TitleBar::updateMaximizeButton);
connect(m_floatingWindow, &FloatingWindow::windowStateChanged, this, &TitleBar::updateMaximizeButton);
connect(m_floatingWindow, &FloatingWindow::activatedChanged , this, &TitleBar::isFocusedChanged);
init();
}
@@ -48,6 +54,10 @@ void TitleBar::init()
{
qCDebug(creation) << "TitleBar" << this;
setFixedHeight(30);
connect(this, &TitleBar::isFocusedChanged, this, [this] {
// repaint
update();
});
}
TitleBar::~TitleBar()
@@ -79,6 +89,17 @@ void TitleBar::toggleMaximized()
m_floatingWindow->showMaximized();
}
void TitleBar::focusInEvent(QFocusEvent *ev)
{
QWidgetAdapter::focusInEvent(ev);
if (!m_frame || !(Config::self().flags() & Config::Flag_TitleBarIsFocusable))
return;
// For some reason QWidget::setFocusProxy() isn't working, so forward manually
m_frame->FocusScope::focus(ev->reason());
}
void TitleBar::setTitle(const QString &title)
{
if (title != m_title) {
@@ -129,7 +150,7 @@ std::unique_ptr<WindowBeingDragged> TitleBar::makeWindow()
QRect r = m_frame->QWidgetAdapter::geometry();
qCDebug(hovering) << "TitleBar::makeWindow original geometry" << r;
r.moveTopLeft(static_cast<Layouting::Widget*>(m_frame)->mapToGlobal(QPoint(0, 0))); // TODO: Remove static_cast if it compiles. Ambiguous base for now
r.moveTopLeft(m_frame->mapToGlobal(QPoint(0, 0))); // TODO: Remove static_cast if it compiles. Ambiguous base for now
auto floatingWindow = Config::self().frameworkWidgetFactory()->createFloatingWindow(m_frame);
floatingWindow->setSuggestedGeometry(r);
@@ -174,6 +195,16 @@ bool TitleBar::hasIcon() const
return !m_icon.isNull();
}
bool TitleBar::isFocused() const
{
if (m_frame)
return m_frame->isFocused();
else if (m_floatingWindow)
return m_floatingWindow->isActiveWindow();
return false;
}
QIcon TitleBar::icon() const
{
return m_icon;

View File

@@ -69,6 +69,11 @@ public:
///@brief returns whether this title bar has an icon
bool hasIcon() const;
///@brief returns whether any of the DockWidgets this TitleBar controls has a child focus
///Not to be confused with QWidget::hasFocus(), which just refers to 1 widget. This works more
/// like QtQuick's FocusScope
bool isFocused() const;
///@brief the icon
QIcon icon() const;
@@ -86,6 +91,7 @@ public:
Q_SIGNALS:
void titleChanged();
void iconChanged();
void isFocusedChanged();
protected:
Q_INVOKABLE void onCloseClicked();
@@ -102,6 +108,8 @@ protected:
virtual bool isFloatButtonVisible() const { return true; }
virtual bool isFloatButtonEnabled() const { return true; }
void focusInEvent(QFocusEvent *event) override;
private:
friend class TestDocks;

View File

@@ -47,11 +47,21 @@ WindowBeingDragged::WindowBeingDragged(FloatingWindow *fw, Draggable *draggable)
, m_draggable(bestDraggable(draggable)->asWidget())
{
init();
// Set opacity while dragging, if needed
const qreal opacity = Config::self().draggedWindowOpacity();
if (!qIsNaN(opacity) && !qFuzzyCompare(1.0, opacity))
fw->setWindowOpacity(opacity);
}
WindowBeingDragged::~WindowBeingDragged()
{
grabMouse(false);
// Restore opacity to fully opaque if needed
const qreal opacity = Config::self().draggedWindowOpacity();
if (!qIsNaN(opacity) && !qFuzzyCompare(1.0, opacity))
m_floatingWindow->setWindowOpacity(1);
}
void WindowBeingDragged::init()

View File

@@ -14,245 +14,24 @@
#include "DragController_p.h"
#include "Frame_p.h"
#include "Logging_p.h"
#include "Utils_p.h"
#include <QPainter>
#include <QRubberBand>
#define INDICATOR_WIDTH 40
#define OUTTER_INDICATOR_MARGIN 10
//#define KDDOCKWIDGETS_RUBBERBAND_IS_TOPLEVEL 1
#include "Config.h"
#include "FrameworkWidgetFactory.h"
#include "ClassicIndicatorsWindow_p.h"
using namespace KDDockWidgets;
namespace KDDockWidgets {
class IndicatorWindow;
}
void Indicator::paintEvent(QPaintEvent *)
static IndicatorWindow* createIndicatorWindow(ClassicIndicators *classicIndicators)
{
QPainter p(this);
if (m_hovered)
p.drawImage(rect(), m_imageActive, rect());
else
p.drawImage(rect(), m_image, rect());
}
void Indicator::setHovered(bool hovered)
{
if (hovered != m_hovered) {
m_hovered = hovered;
update();
if (hovered) {
q->setDropLocation(m_dropLocation);
} else if (q->currentDropLocation() == m_dropLocation) {
q->setDropLocation(DropIndicatorOverlayInterface::DropLocation_None);
}
}
}
QString Indicator::iconName(bool active) const
{
QString suffix = active ? QStringLiteral("_active")
: QString();
QString name;
switch (m_dropLocation) {
case DropIndicatorOverlayInterface::DropLocation_Center:
name = QStringLiteral("center");
break;
case DropIndicatorOverlayInterface::DropLocation_Left:
name = QStringLiteral("inner_left");
break;
case DropIndicatorOverlayInterface::DropLocation_Right:
name = QStringLiteral("inner_right");
break;
case DropIndicatorOverlayInterface::DropLocation_Bottom:
name = QStringLiteral("inner_bottom");
break;
case DropIndicatorOverlayInterface::DropLocation_Top:
name = QStringLiteral("inner_top");
break;
case DropIndicatorOverlayInterface::DropLocation_OutterLeft:
name = QStringLiteral("outter_left");
break;
case DropIndicatorOverlayInterface::DropLocation_OutterBottom:
name = QStringLiteral("outter_bottom");
break;
case DropIndicatorOverlayInterface::DropLocation_OutterRight:
name = QStringLiteral("outter_right");
break;
case DropIndicatorOverlayInterface::DropLocation_OutterTop:
name = QStringLiteral("outter_top");
break;
case DropIndicatorOverlayInterface::DropLocation_None:
return QString();
}
return name + suffix;
}
QString Indicator::iconFileName(bool active) const
{
const QString name = iconName(active);
return KDDockWidgets::windowManagerHasTranslucency() ? QStringLiteral(":/img/classic_indicators/%1.png").arg(name)
: QStringLiteral(":/img/classic_indicators/opaque/%1.png").arg(name);
}
IndicatorWindow::IndicatorWindow(ClassicIndicators *classicIndicators_, QWidget *)
: QWidget(nullptr, Qt::Tool | Qt::BypassWindowManagerHint)
, classicIndicators(classicIndicators_)
, m_center(new Indicator(classicIndicators, this, DropIndicatorOverlayInterface::DropLocation_Center)) // Each indicator is not a top-level. Otherwise there's noticeable delay.
, m_left(new Indicator(classicIndicators, this, DropIndicatorOverlayInterface::DropLocation_Left))
, m_right(new Indicator(classicIndicators, this, DropIndicatorOverlayInterface::DropLocation_Right))
, m_bottom(new Indicator(classicIndicators, this, DropIndicatorOverlayInterface::DropLocation_Bottom))
, m_top(new Indicator(classicIndicators, this, DropIndicatorOverlayInterface::DropLocation_Top))
, m_outterLeft(new Indicator(classicIndicators, this, DropIndicatorOverlayInterface::DropLocation_OutterLeft))
, m_outterRight(new Indicator(classicIndicators, this, DropIndicatorOverlayInterface::DropLocation_OutterRight))
, m_outterBottom(new Indicator(classicIndicators, this, DropIndicatorOverlayInterface::DropLocation_OutterBottom))
, m_outterTop(new Indicator(classicIndicators, this, DropIndicatorOverlayInterface::DropLocation_OutterTop))
{
setWindowFlag(Qt::FramelessWindowHint, true);
setAttribute(Qt::WA_TranslucentBackground);
updatePosition();
m_indicators << m_center << m_left << m_right << m_top << m_bottom
<< m_outterBottom << m_outterTop << m_outterLeft << m_outterRight;
setObjectName(QStringLiteral("_docks_IndicatorWindow_Overlay"));
}
bool IndicatorWindow::event(QEvent *e)
{
if (e->type() == QEvent::Show) {
updatePosition();
}
return QWidget::event(e);
}
Indicator *IndicatorWindow::indicatorForLocation(DropIndicatorOverlayInterface::DropLocation loc) const
{
switch (loc) {
case DropIndicatorOverlayInterface::DropLocation_Center:
return m_center;
case DropIndicatorOverlayInterface::DropLocation_Left:
return m_left;
case DropIndicatorOverlayInterface::DropLocation_Right:
return m_right;
case DropIndicatorOverlayInterface::DropLocation_Bottom:
return m_bottom;
case DropIndicatorOverlayInterface::DropLocation_Top:
return m_top;
case DropIndicatorOverlayInterface::DropLocation_OutterLeft:
return m_outterLeft;
case DropIndicatorOverlayInterface::DropLocation_OutterBottom:
return m_outterBottom;
case DropIndicatorOverlayInterface::DropLocation_OutterRight:
return m_outterRight;
case DropIndicatorOverlayInterface::DropLocation_OutterTop:
return m_outterTop;
case DropIndicatorOverlayInterface::DropLocation_None:
return nullptr;
}
return nullptr;
}
void IndicatorWindow::updateMask()
{
QRegion region;
if (!KDDockWidgets::windowManagerHasTranslucency()) {
for (Indicator *indicator : qAsConst(m_indicators)) {
if (indicator->isVisible())
region = region.united(QRegion(indicator->geometry(), QRegion::Rectangle));
}
}
setMask(region);
}
void IndicatorWindow::resizeEvent(QResizeEvent *ev)
{
QWidget::resizeEvent(ev);
updatePositions();
}
void IndicatorWindow::updateIndicatorVisibility(bool visible)
{
Frame *hoveredFrame = classicIndicators->m_hoveredFrame;
const bool isTheOnlyFrame = hoveredFrame && hoveredFrame->isTheOnlyFrame();
const bool innerShouldBeVisible = visible && hoveredFrame;
const bool outterShouldBeVisible = visible && !isTheOnlyFrame;
for (Indicator *indicator : { m_center, m_left, m_right, m_bottom, m_top })
indicator->setVisible(innerShouldBeVisible);
for (Indicator *indicator : { m_outterTop, m_outterLeft, m_outterRight, m_outterBottom })
indicator->setVisible(outterShouldBeVisible);
updateMask();
}
void IndicatorWindow::hover(QPoint globalPos)
{
for (Indicator *indicator : qAsConst(m_indicators)) {
if (indicator->isVisible())
indicator->setHovered(indicator->rect().contains(indicator->mapFromGlobal(globalPos)));
}
}
void IndicatorWindow::updatePosition()
{
QRect rect = classicIndicators->rect();
QPoint pos = classicIndicators->mapToGlobal(QPoint(0, 0));
rect.moveTo(pos);
setGeometry(rect);
}
void IndicatorWindow::updatePositions()
{
QRect r = rect();
const int indicatorWidth = m_outterBottom->width();
const int halfIndicatorWidth = m_outterBottom->width() / 2;
m_outterLeft->move(r.x() + OUTTER_INDICATOR_MARGIN, r.center().y() - halfIndicatorWidth);
m_outterBottom->move(r.center().x() - halfIndicatorWidth, r.y() + height() - indicatorWidth - OUTTER_INDICATOR_MARGIN);
m_outterTop->move(r.center().x() - halfIndicatorWidth, r.y() + OUTTER_INDICATOR_MARGIN);
m_outterRight->move(r.x() + width() - indicatorWidth - OUTTER_INDICATOR_MARGIN, r.center().y() - halfIndicatorWidth);
Frame *hoveredFrame = classicIndicators->m_hoveredFrame;
if (hoveredFrame) {
QRect hoveredRect = hoveredFrame->QWidget::geometry();
m_center->move(r.topLeft() + hoveredRect.center() - QPoint(halfIndicatorWidth, halfIndicatorWidth));
m_top->move(m_center->pos() - QPoint(0, indicatorWidth + OUTTER_INDICATOR_MARGIN));
m_right->move(m_center->pos() + QPoint(indicatorWidth + OUTTER_INDICATOR_MARGIN, 0));
m_bottom->move(m_center->pos() + QPoint(0, indicatorWidth + OUTTER_INDICATOR_MARGIN));
m_left->move(m_center->pos() - QPoint(indicatorWidth + OUTTER_INDICATOR_MARGIN, 0));
}
}
Indicator::Indicator(ClassicIndicators *classicIndicators, IndicatorWindow *parent, ClassicIndicators::DropLocation location)
: QWidget(parent)
, q(classicIndicators)
, m_dropLocation(location)
{
m_image = QImage(iconFileName(/*active=*/ false)).scaled(INDICATOR_WIDTH, INDICATOR_WIDTH);
m_imageActive = QImage(iconFileName(/*active=*/ true)).scaled(INDICATOR_WIDTH, INDICATOR_WIDTH);
setFixedSize(m_image.size());
setVisible(true);
auto window = new IndicatorWindow(classicIndicators);
window->setObjectName(QStringLiteral("_docks_IndicatorWindow_Overlay"));
return window;
}
ClassicIndicators::ClassicIndicators(DropArea *dropArea)
: DropIndicatorOverlayInterface(dropArea) // Is parented on the drop-area, not a toplevel.
, m_rubberBand(new QRubberBand(QRubberBand::Rectangle, rubberBandIsTopLevel() ? nullptr : dropArea))
, m_indicatorWindow(new IndicatorWindow(this, /*parent=*/ nullptr)) // Top-level so the indicators can appear above the window being dragged.
, m_rubberBand(Config::self().frameworkWidgetFactory()->createRubberBand(dropArea))
, m_indicatorWindow(createIndicatorWindow(this))
{
setVisible(false);
if (rubberBandIsTopLevel())
m_rubberBand->setWindowOpacity(0.5);
}
ClassicIndicators::~ClassicIndicators()
@@ -260,20 +39,30 @@ ClassicIndicators::~ClassicIndicators()
delete m_indicatorWindow;
}
DropIndicatorOverlayInterface::Type ClassicIndicators::indicatorType() const
{
return TypeClassic;
}
void ClassicIndicators::hover(QPoint globalPos)
void ClassicIndicators::hover_impl(QPoint globalPos)
{
m_indicatorWindow->hover(globalPos);
}
QPoint ClassicIndicators::posForIndicator(DropIndicatorOverlayInterface::DropLocation loc) const
{
Indicator *indicator = m_indicatorWindow->indicatorForLocation(loc);
return indicator->mapToGlobal(indicator->rect().center());
return m_indicatorWindow->posForIndicator(loc);
}
bool ClassicIndicators::innerIndicatorsVisible() const
{
return m_innerIndicatorsVisible;
}
bool ClassicIndicators::outterIndicatorsVisible() const
{
return m_outterIndicatorsVisible;
}
bool ClassicIndicators::onResize(QSize)
{
m_indicatorWindow->resize(window()->size());
return false;
}
void ClassicIndicators::updateVisibility()
@@ -281,29 +70,26 @@ void ClassicIndicators::updateVisibility()
if (isHovered()) {
m_indicatorWindow->updatePositions();
m_indicatorWindow->setVisible(true);
m_indicatorWindow->updateIndicatorVisibility(true);
updateWindowPosition();
updateIndicatorsVisibility(true);
raiseIndicators();
} else {
m_rubberBand->setVisible(false);
m_indicatorWindow->setVisible(false);
m_indicatorWindow->updateIndicatorVisibility(false);
updateIndicatorsVisibility(false);
}
}
void ClassicIndicators::showEvent(QShowEvent *e)
void ClassicIndicators::updateIndicatorsVisibility(bool visible)
{
QWidget::showEvent(e);
}
Frame *hoveredFrame = m_hoveredFrame;
const bool isTheOnlyFrame = hoveredFrame && hoveredFrame->isTheOnlyFrame();
void ClassicIndicators::hideEvent(QHideEvent *e)
{
QWidget::hideEvent(e);
}
m_innerIndicatorsVisible = visible && hoveredFrame;
m_outterIndicatorsVisible = visible && !isTheOnlyFrame;
void ClassicIndicators::resizeEvent(QResizeEvent *ev)
{
QWidget::resizeEvent(ev);
m_indicatorWindow->resize(window()->size());
Q_EMIT innerIndicatorsVisibleChanged();
Q_EMIT outterIndicatorsVisibleChanged();
}
void ClassicIndicators::raiseIndicators()
@@ -346,12 +132,8 @@ void ClassicIndicators::setDropLocation(ClassicIndicators::DropLocation location
}
if (location == DropLocation_Center) {
m_rubberBand->setGeometry(geometryForRubberband(m_hoveredFrame ? m_hoveredFrame->QWidget::geometry() : rect()));
m_rubberBand->setGeometry(m_hoveredFrame ? m_hoveredFrame->QWidgetAdapter::geometry() : rect());
m_rubberBand->setVisible(true);
if (rubberBandIsTopLevel()) {
m_rubberBand->raise();
raiseIndicators();
}
return;
}
@@ -384,32 +166,14 @@ void ClassicIndicators::setDropLocation(ClassicIndicators::DropLocation location
QRect rect = m_dropArea->rectForDrop(m_windowBeingDragged, multisplitterLocation,
m_dropArea->itemForFrame(relativeToFrame));
m_rubberBand->setGeometry(geometryForRubberband(rect));
m_rubberBand->setGeometry(rect);
m_rubberBand->setVisible(true);
if (rubberBandIsTopLevel()) {
m_rubberBand->raise();
raiseIndicators();
}
}
QRect ClassicIndicators::geometryForRubberband(QRect localRect) const
void ClassicIndicators::updateWindowPosition()
{
if (!rubberBandIsTopLevel())
return localRect;
QPoint topLeftLocal = localRect.topLeft();
QPoint topLeftGlobal = m_dropArea->QWidget::mapToGlobal(topLeftLocal);
localRect.moveTopLeft(topLeftGlobal);
return localRect;
}
bool ClassicIndicators::rubberBandIsTopLevel() const
{
#ifdef KDDOCKWIDGETS_RUBBERBAND_IS_TOPLEVEL
return true;
#else
return false;
#endif
QRect rect = this->rect();
QPoint pos = mapToGlobal(QPoint(0, 0));
rect.moveTo(pos);
m_indicatorWindow->setGeometry(rect);
}

View File

@@ -0,0 +1,318 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sérgio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#include "ClassicIndicatorsWindow_p.h"
#include "ClassicIndicators_p.h"
#include "Utils_p.h"
using namespace KDDockWidgets;
namespace KDDockWidgets
{
static QString iconName(DropIndicatorOverlayInterface::DropLocation loc, bool active)
{
QString suffix = active ? QStringLiteral("_active")
: QString();
QString name;
switch (loc) {
case DropIndicatorOverlayInterface::DropLocation_Center:
name = QStringLiteral("center");
break;
case DropIndicatorOverlayInterface::DropLocation_Left:
name = QStringLiteral("inner_left");
break;
case DropIndicatorOverlayInterface::DropLocation_Right:
name = QStringLiteral("inner_right");
break;
case DropIndicatorOverlayInterface::DropLocation_Bottom:
name = QStringLiteral("inner_bottom");
break;
case DropIndicatorOverlayInterface::DropLocation_Top:
name = QStringLiteral("inner_top");
break;
case DropIndicatorOverlayInterface::DropLocation_OutterLeft:
name = QStringLiteral("outter_left");
break;
case DropIndicatorOverlayInterface::DropLocation_OutterBottom:
name = QStringLiteral("outter_bottom");
break;
case DropIndicatorOverlayInterface::DropLocation_OutterRight:
name = QStringLiteral("outter_right");
break;
case DropIndicatorOverlayInterface::DropLocation_OutterTop:
name = QStringLiteral("outter_top");
break;
case DropIndicatorOverlayInterface::DropLocation_None:
return QString();
}
return name + suffix;
}
}
#ifdef KDDOCKWIDGETS_QTWIDGETS
#include <QPainter>
#define INDICATOR_WIDTH 40
#define OUTTER_INDICATOR_MARGIN 10
void Indicator::paintEvent(QPaintEvent *)
{
QPainter p(this);
if (m_hovered)
p.drawImage(rect(), m_imageActive, rect());
else
p.drawImage(rect(), m_image, rect());
}
void Indicator::setHovered(bool hovered)
{
if (hovered != m_hovered) {
m_hovered = hovered;
update();
if (hovered) {
q->setDropLocation(m_dropLocation);
} else if (q->currentDropLocation() == m_dropLocation) {
q->setDropLocation(DropIndicatorOverlayInterface::DropLocation_None);
}
}
}
QString Indicator::iconName(bool active) const
{
return KDDockWidgets::iconName(m_dropLocation, active);
}
QString Indicator::iconFileName(bool active) const
{
const QString name = iconName(active);
return KDDockWidgets::windowManagerHasTranslucency() ? QStringLiteral(":/img/classic_indicators/%1.png").arg(name)
: QStringLiteral(":/img/classic_indicators/opaque/%1.png").arg(name);
}
IndicatorWindow::IndicatorWindow(ClassicIndicators *classicIndicators_)
: QWidget(nullptr, Qt::Tool | Qt::BypassWindowManagerHint)
, classicIndicators(classicIndicators_)
, m_center(new Indicator(classicIndicators, this, DropIndicatorOverlayInterface::DropLocation_Center)) // Each indicator is not a top-level. Otherwise there's noticeable delay.
, m_left(new Indicator(classicIndicators, this, DropIndicatorOverlayInterface::DropLocation_Left))
, m_right(new Indicator(classicIndicators, this, DropIndicatorOverlayInterface::DropLocation_Right))
, m_bottom(new Indicator(classicIndicators, this, DropIndicatorOverlayInterface::DropLocation_Bottom))
, m_top(new Indicator(classicIndicators, this, DropIndicatorOverlayInterface::DropLocation_Top))
, m_outterLeft(new Indicator(classicIndicators, this, DropIndicatorOverlayInterface::DropLocation_OutterLeft))
, m_outterRight(new Indicator(classicIndicators, this, DropIndicatorOverlayInterface::DropLocation_OutterRight))
, m_outterBottom(new Indicator(classicIndicators, this, DropIndicatorOverlayInterface::DropLocation_OutterBottom))
, m_outterTop(new Indicator(classicIndicators, this, DropIndicatorOverlayInterface::DropLocation_OutterTop))
{
setWindowFlag(Qt::FramelessWindowHint, true);
setAttribute(Qt::WA_TranslucentBackground);
connect(classicIndicators, &ClassicIndicators::innerIndicatorsVisibleChanged,
this, &IndicatorWindow::updateIndicatorVisibility);
connect(classicIndicators, &ClassicIndicators::outterIndicatorsVisibleChanged,
this, &IndicatorWindow::updateIndicatorVisibility);
m_indicators << m_center << m_left << m_right << m_top << m_bottom
<< m_outterBottom << m_outterTop << m_outterLeft << m_outterRight;
}
Indicator *IndicatorWindow::indicatorForLocation(DropIndicatorOverlayInterface::DropLocation loc) const
{
switch (loc) {
case DropIndicatorOverlayInterface::DropLocation_Center:
return m_center;
case DropIndicatorOverlayInterface::DropLocation_Left:
return m_left;
case DropIndicatorOverlayInterface::DropLocation_Right:
return m_right;
case DropIndicatorOverlayInterface::DropLocation_Bottom:
return m_bottom;
case DropIndicatorOverlayInterface::DropLocation_Top:
return m_top;
case DropIndicatorOverlayInterface::DropLocation_OutterLeft:
return m_outterLeft;
case DropIndicatorOverlayInterface::DropLocation_OutterBottom:
return m_outterBottom;
case DropIndicatorOverlayInterface::DropLocation_OutterRight:
return m_outterRight;
case DropIndicatorOverlayInterface::DropLocation_OutterTop:
return m_outterTop;
case DropIndicatorOverlayInterface::DropLocation_None:
return nullptr;
}
return nullptr;
}
void IndicatorWindow::updateMask()
{
QRegion region;
if (!KDDockWidgets::windowManagerHasTranslucency()) {
for (Indicator *indicator : qAsConst(m_indicators)) {
if (indicator->isVisible())
region = region.united(QRegion(indicator->geometry(), QRegion::Rectangle));
}
}
setMask(region);
}
void IndicatorWindow::resizeEvent(QResizeEvent *ev)
{
QWidget::resizeEvent(ev);
updatePositions();
}
void IndicatorWindow::updateIndicatorVisibility()
{
for (Indicator *indicator : { m_center, m_left, m_right, m_bottom, m_top })
indicator->setVisible(classicIndicators->innerIndicatorsVisible());
for (Indicator *indicator : { m_outterTop, m_outterLeft, m_outterRight, m_outterBottom })
indicator->setVisible(classicIndicators->outterIndicatorsVisible());
updateMask();
}
QPoint IndicatorWindow::posForIndicator(DropIndicatorOverlayInterface::DropLocation loc) const
{
Indicator *indicator = indicatorForLocation(loc);
return indicator->mapToGlobal(indicator->rect().center());
}
void IndicatorWindow::hover(QPoint globalPos)
{
for (Indicator *indicator : qAsConst(m_indicators)) {
if (indicator->isVisible())
indicator->setHovered(indicator->rect().contains(indicator->mapFromGlobal(globalPos)));
}
}
void IndicatorWindow::updatePositions()
{
QRect r = rect();
const int indicatorWidth = m_outterBottom->width();
const int halfIndicatorWidth = m_outterBottom->width() / 2;
m_outterLeft->move(r.x() + OUTTER_INDICATOR_MARGIN, r.center().y() - halfIndicatorWidth);
m_outterBottom->move(r.center().x() - halfIndicatorWidth, r.y() + height() - indicatorWidth - OUTTER_INDICATOR_MARGIN);
m_outterTop->move(r.center().x() - halfIndicatorWidth, r.y() + OUTTER_INDICATOR_MARGIN);
m_outterRight->move(r.x() + width() - indicatorWidth - OUTTER_INDICATOR_MARGIN, r.center().y() - halfIndicatorWidth);
Frame *hoveredFrame = classicIndicators->m_hoveredFrame;
if (hoveredFrame) {
QRect hoveredRect = hoveredFrame->QWidget::geometry();
m_center->move(r.topLeft() + hoveredRect.center() - QPoint(halfIndicatorWidth, halfIndicatorWidth));
m_top->move(m_center->pos() - QPoint(0, indicatorWidth + OUTTER_INDICATOR_MARGIN));
m_right->move(m_center->pos() + QPoint(indicatorWidth + OUTTER_INDICATOR_MARGIN, 0));
m_bottom->move(m_center->pos() + QPoint(0, indicatorWidth + OUTTER_INDICATOR_MARGIN));
m_left->move(m_center->pos() - QPoint(indicatorWidth + OUTTER_INDICATOR_MARGIN, 0));
}
}
Indicator::Indicator(ClassicIndicators *classicIndicators, IndicatorWindow *parent, ClassicIndicators::DropLocation location)
: QWidget(parent)
, q(classicIndicators)
, m_dropLocation(location)
{
m_image = QImage(iconFileName(/*active=*/ false)).scaled(INDICATOR_WIDTH, INDICATOR_WIDTH);
m_imageActive = QImage(iconFileName(/*active=*/ true)).scaled(INDICATOR_WIDTH, INDICATOR_WIDTH);
setFixedSize(m_image.size());
setVisible(true);
}
#else
#include <QQmlContext>
IndicatorWindow::IndicatorWindow(KDDockWidgets::ClassicIndicators *classicIndicators)
: QQuickView()
, m_classicIndicators(classicIndicators)
{
setColor(Qt::transparent);
setFlags(flags() | Qt::FramelessWindowHint);
rootContext()->setContextProperty(QStringLiteral("_window"), QVariant::fromValue<QObject*>(this));
setSource(QUrl(QStringLiteral("qrc:/kddockwidgets/private/quick/qml/ClassicIndicatorsOverlay.qml")));
}
void IndicatorWindow::hover(QPoint pt)
{
QQuickItem *item = indicatorForPos(pt);
if (item) {
const auto loc = DropIndicatorOverlayInterface::DropLocation(item->property("indicatorType").toInt());
classicIndicators()->setDropLocation(loc);
} else {
classicIndicators()->setDropLocation(DropIndicatorOverlayInterface::DropLocation_None);
}
}
QQuickItem *IndicatorWindow::indicatorForPos(QPoint pt) const
{
const QVector<QQuickItem *> indicators = indicatorItems();
Q_ASSERT(indicators.size() == 9);
for (QQuickItem *item : indicators) {
QRect rect(0, 0, int(item->width()), int(item->height()));
rect.moveTopLeft(item->mapToGlobal(QPointF(0, 0)).toPoint());
if (rect.contains(pt))
return item;
}
return nullptr;
}
void IndicatorWindow::updatePositions()
{
// Not needed to implement, the Indicators use QML anchors
}
QPoint IndicatorWindow::posForIndicator(KDDockWidgets::DropIndicatorOverlayInterface::DropLocation) const
{
qDebug() << Q_FUNC_INFO;
return {};
}
QString IndicatorWindow::iconName(int loc, bool active) const
{
return KDDockWidgets::iconName(DropIndicatorOverlayInterface::DropLocation(loc), active);
}
ClassicIndicators *IndicatorWindow::classicIndicators() const
{
return m_classicIndicators;
}
QVector<QQuickItem *> IndicatorWindow::indicatorItems() const
{
QVector<QQuickItem *> indicators;
indicators.reserve(9);
QQuickItem *root = rootObject();
const QList<QQuickItem*> items = root->childItems();
for (QQuickItem *item : items) {
if (QString::fromLatin1(item->metaObject()->className()).startsWith(QLatin1String("ClassicIndicator_QMLTYPE"))) {
indicators.push_back(item);
} else if (item->objectName() == QLatin1String("innerIndicators")) {
const QList<QQuickItem*> innerIndicators = item->childItems();
for (QQuickItem *innerItem : innerIndicators) {
if (QString::fromLatin1(innerItem->metaObject()->className()).startsWith(QLatin1String("ClassicIndicator_QMLTYPE"))) {
indicators.push_back(innerItem);
}
}
}
}
return indicators;
}
#endif // QtQuick

View File

@@ -0,0 +1,108 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sérgio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#ifndef KD_INDICATORS_CLASSICINDICATORS_WINDOW_P_H
#define KD_INDICATORS_CLASSICINDICATORS_WINDOW_P_H
#include "DropIndicatorOverlayInterface_p.h"
#ifdef KDDOCKWIDGETS_QTWIDGETS
#include <QImage>
#include <QWidget>
#include <QResizeEvent>
namespace KDDockWidgets {
class Indicator;
class ClassicIndicators;
class IndicatorWindow : public QWidget
{
Q_OBJECT
public:
explicit IndicatorWindow(ClassicIndicators *classicIndicators);
void hover(QPoint globalPos);
void updatePositions();
QPoint posForIndicator(DropIndicatorOverlayInterface::DropLocation) const;
private:
void updateIndicatorVisibility();
void resizeEvent(QResizeEvent *ev) override;
// When the compositor doesn't support translucency, we use a mask instead
// Only happens on Linux
void updateMask();
Indicator *indicatorForLocation(DropIndicatorOverlayInterface::DropLocation loc) const;
ClassicIndicators *const classicIndicators;
Indicator *const m_center;
Indicator *const m_left;
Indicator *const m_right;
Indicator *const m_bottom;
Indicator *const m_top;
Indicator *const m_outterLeft;
Indicator *const m_outterRight;
Indicator *const m_outterBottom;
Indicator *const m_outterTop;
QVector<Indicator *> m_indicators;
};
class Indicator : public QWidget
{
Q_OBJECT
public:
typedef QList<Indicator *> List;
explicit Indicator(ClassicIndicators *classicIndicators, IndicatorWindow *parent,
DropIndicatorOverlayInterface::DropLocation location);
void paintEvent(QPaintEvent *) override;
void setHovered(bool hovered);
QString iconName(bool active) const;
QString iconFileName(bool active) const;
QImage m_image;
QImage m_imageActive;
ClassicIndicators *const q;
bool m_hovered = false;
const DropIndicatorOverlayInterface::DropLocation m_dropLocation;
};
}
#else
#include <QQuickView>
namespace KDDockWidgets
{
class ClassicIndicators;
class IndicatorWindow : public QQuickView
{
Q_OBJECT
Q_PROPERTY(KDDockWidgets::ClassicIndicators* classicIndicators READ classicIndicators CONSTANT)
public:
explicit IndicatorWindow(ClassicIndicators *);
void hover(QPoint);
void updatePositions();
QPoint posForIndicator(DropIndicatorOverlayInterface::DropLocation) const;
Q_INVOKABLE QString iconName(int loc, bool active) const;
KDDockWidgets::ClassicIndicators* classicIndicators() const;
private:
QQuickItem *indicatorForPos(QPoint) const;
QVector<QQuickItem*> indicatorItems() const;
ClassicIndicators *const m_classicIndicators;
};
}
#endif
#endif

View File

@@ -12,92 +12,47 @@
#ifndef KD_INDICATORS_CLASSICINDICATORS_P_H
#define KD_INDICATORS_CLASSICINDICATORS_P_H
#include "DropIndicatorOverlayInterface_p.h"
QT_BEGIN_NAMESPACE
class QRubberBand;
QT_END_NAMESPACE
#include "../DropIndicatorOverlayInterface_p.h"
namespace KDDockWidgets {
class IndicatorWindow;
class Indicator;
class ClassicIndicators : public DropIndicatorOverlayInterface
class DOCKS_EXPORT ClassicIndicators : public DropIndicatorOverlayInterface
{
Q_OBJECT
Q_PROPERTY(bool innerIndicatorsVisible READ innerIndicatorsVisible NOTIFY innerIndicatorsVisibleChanged)
Q_PROPERTY(bool outterIndicatorsVisible READ outterIndicatorsVisible NOTIFY outterIndicatorsVisibleChanged)
public:
explicit ClassicIndicators(DropArea *dropArea);
~ClassicIndicators() override;
Type indicatorType() const override;
void hover(QPoint globalPos) override;
void hover_impl(QPoint globalPos) override;
QPoint posForIndicator(DropLocation) const override;
bool innerIndicatorsVisible() const;
bool outterIndicatorsVisible() const;
protected:
void showEvent(QShowEvent *) override;
void hideEvent(QHideEvent *) override;
void resizeEvent(QResizeEvent *) override;
bool onResize(QSize newSize) override;
void updateVisibility() override;
Q_SIGNALS:
void innerIndicatorsVisibleChanged();
void outterIndicatorsVisibleChanged();
private:
friend class KDDockWidgets::Indicator;
friend class KDDockWidgets::IndicatorWindow;
void updateIndicatorsVisibility(bool visible);
void raiseIndicators();
void setDropLocation(DropLocation);
QRect geometryForRubberband(QRect localRect) const;
bool rubberBandIsTopLevel() const;
void updateWindowPosition();
QRubberBand *const m_rubberBand;
QWidgetOrQuick *const m_rubberBand;
IndicatorWindow *const m_indicatorWindow;
};
class IndicatorWindow : public QWidget
{
Q_OBJECT
public:
explicit IndicatorWindow(ClassicIndicators *classicIndicators, QWidget * = nullptr);
void hover(QPoint globalPos);
void updatePosition();
void updatePositions();
void updateIndicatorVisibility(bool visible);
void resizeEvent(QResizeEvent *ev) override;
bool event(QEvent *e) override;
Indicator *indicatorForLocation(DropIndicatorOverlayInterface::DropLocation loc) const;
// When the compositor doesn't support translucency, we use a mask instead
// Only happens on Linux
void updateMask();
ClassicIndicators *const classicIndicators;
Indicator *const m_center;
Indicator *const m_left;
Indicator *const m_right;
Indicator *const m_bottom;
Indicator *const m_top;
Indicator *const m_outterLeft;
Indicator *const m_outterRight;
Indicator *const m_outterBottom;
Indicator *const m_outterTop;
QVector<Indicator *> m_indicators;
};
class Indicator : public QWidget
{
Q_OBJECT
public:
typedef QList<Indicator *> List;
explicit Indicator(ClassicIndicators *classicIndicators, IndicatorWindow *parent, ClassicIndicators::DropLocation location);
void paintEvent(QPaintEvent *) override;
void setHovered(bool hovered);
QString iconName(bool active) const;
QString iconFileName(bool active) const;
QImage m_image;
QImage m_imageActive;
ClassicIndicators *const q;
bool m_hovered = false;
const ClassicIndicators::DropLocation m_dropLocation;
bool m_innerIndicatorsVisible = false;
bool m_outterIndicatorsVisible = false;
};
}

View File

@@ -0,0 +1,182 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sérgio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#include "SegmentedIndicators_p.h"
#include "DropArea_p.h"
#include "Config.h"
#include <QPainter>
#include <QPainterPath>
#define SEGMENT_GIRTH 50
#define SEGMENT_PEN_WIDTH 4
using namespace KDDockWidgets;
SegmentedIndicators::SegmentedIndicators(DropArea *dropArea)
: DropIndicatorOverlayInterface(dropArea)
{
// If the app didn't choose opacity then we choose a suitable default value.
// ClassicIndicators works fine with an opaque dragged window because the indicators have higher Z,
// However for SegmentedIndicators the indicators are in the main window, so lower Z. Make the
// dragged window translucent a bit, so we can see the indicators
const bool userChoseOpacity = !qIsNaN(Config::self().draggedWindowOpacity());
if (!userChoseOpacity)
Config::self().setDraggedWindowOpacity(0.7);
}
SegmentedIndicators::~SegmentedIndicators()
{
}
void SegmentedIndicators::hover_impl(QPoint pt)
{
m_hoveredPt = mapFromGlobal(pt);
updateSegments();
setCurrentDropLocation(dropLocationForPos(m_hoveredPt));
}
DropIndicatorOverlayInterface::DropLocation SegmentedIndicators::dropLocationForPos(QPoint pos) const
{
for (auto it = m_segments.cbegin(), end = m_segments.cend(); it != end; ++it) {
if (it.value().containsPoint(pos, Qt::OddEvenFill)) {
return it.key();
}
}
return DropLocation_None;
}
void SegmentedIndicators::paintEvent(QPaintEvent *)
{
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing, true);
drawSegments(&p);
}
QVector<QPolygon> SegmentedIndicators::segmentsForRect(QRect r, QPolygon &center, bool useOffset) const
{
const int penWidth = SEGMENT_PEN_WIDTH;
const int halfPenWidth = penWidth / 2;
const int l = SEGMENT_GIRTH;
const int top = (r.y() == 0 && useOffset) ? l : r.y();
const int left = (r.x() == 0 && useOffset) ? l : r.x();
const int right = (rect().right() == r.right() && useOffset) ? r.right() - l : r.right();
const int bottom = (rect().bottom() == r.bottom() && useOffset) ? r.bottom() - l : r.bottom();
const QPoint topLeft = { left + halfPenWidth, top + halfPenWidth };
const QPoint topRight = { right, top + halfPenWidth };
const QPoint bottomLeft = { left + halfPenWidth, bottom };
const QPoint bottomRight = { right, bottom };
const QVector<QPoint> leftPoints = { topLeft, bottomLeft,
QPoint(left, bottom) + QPoint(l, -l),
topLeft + QPoint(l, l), topLeft
};
const QVector<QPoint> rightPoints = { topRight, bottomRight,
bottomRight + QPoint(-l, -l),
topRight + QPoint(-l, l)
};
const QVector<QPoint> topPoints = { topLeft, topRight,
topRight + QPoint(-l, l),
topLeft + QPoint(l, l)
};
const QVector<QPoint> bottomPoints = { bottomLeft, bottomRight,
bottomRight + QPoint(-l, -l),
bottomLeft + QPoint(l, -l)
};
{
QPolygon bounds = QVector<QPoint> { topLeft + QPoint(l, l),
topRight + QPoint(-l, l),
bottomRight + QPoint(-l, -l),
bottomLeft + QPoint(l, -l)
};
const int maxWidth = bounds.boundingRect().width();
const QPoint centerPos = bounds.boundingRect().center();
// Build the center
const int indicatorWidth = qMin(300, maxWidth - 100);
const int indicatorHeight = qMin(160, int(indicatorWidth * 0.60));
const int tabWidth = int(indicatorWidth * 0.267);
const int tabHeight = int(indicatorHeight * 0.187);
const int centerRectLeft = centerPos.x() - indicatorWidth / 2;
const int centerRectRight = centerPos.x() + indicatorWidth / 2;
const int centerRectBottom = centerPos.y() + indicatorHeight / 2;
const int centerRectTop = centerPos.y() - indicatorHeight / 2;
center = QVector<QPoint> { { centerRectLeft, centerRectTop },
{ centerRectLeft + tabWidth, centerRectTop },
{ centerRectLeft + tabWidth, centerRectTop + tabHeight },
{ centerRectRight, centerRectTop + tabHeight },
{ centerRectRight, centerRectBottom },
{ centerRectLeft, centerRectBottom },
};
}
return { leftPoints, topPoints, rightPoints, bottomPoints };
}
void SegmentedIndicators::updateSegments()
{
m_segments.clear();
const bool hasMultipleFrames = m_dropArea->count() > 1;
const bool needsInnerIndicators = hoveredFrameRect().isValid();
const bool needsOutterIndicators = hasMultipleFrames || !needsInnerIndicators;
QPolygon center;
if (needsInnerIndicators) {
const bool useOffset = needsOutterIndicators;
auto segments = segmentsForRect(hoveredFrameRect(), /*by-ref*/center, useOffset);
for (int i = 0; i < 4; ++i)
m_segments.insert(DropLocation(DropLocation_Left + i), segments[i]);
m_segments.insert(DropLocation_Center, center);
}
if (needsOutterIndicators) {
auto segments = segmentsForRect(rect(), /*unused*/center);
for (int i = 0; i < 4; ++i)
m_segments.insert(DropLocation(DropLocation_OutterLeft + i), segments[i]);
}
update();
}
void SegmentedIndicators::drawSegments(QPainter *p)
{
for (int i = DropLocation_First; i <= DropLocation_Last; ++i)
drawSegment(p, m_segments.value(DropLocation(i)));
}
void SegmentedIndicators::drawSegment(QPainter *p, const QPolygon &segment)
{
if (segment.isEmpty())
return;
QPen pen(Qt::black);
pen.setWidth(SEGMENT_PEN_WIDTH);
p->setPen(pen);
QColor brush(0xbb, 0xd5, 0xee, 200);
if (segment.containsPoint(m_hoveredPt, Qt::OddEvenFill))
brush = QColor(0x3574c5);
p->setBrush(brush);
p->drawPolygon(segment);
}

View File

@@ -0,0 +1,46 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2019-2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sérgio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
#ifndef KD_SEGMENTED_INDICATORS_P_H
#define KD_SEGMENTED_INDICATORS_P_H
#include "../DropIndicatorOverlayInterface_p.h"
#include <QHash>
#include <QPolygon>
namespace KDDockWidgets {
class DOCKS_EXPORT SegmentedIndicators : public DropIndicatorOverlayInterface
{
Q_OBJECT
public:
explicit SegmentedIndicators(DropArea *dropArea);
~SegmentedIndicators() override;
void hover_impl(QPoint globalPos) override;
DropLocation dropLocationForPos(QPoint pos) const;
protected:
void paintEvent(QPaintEvent *) override;
private:
QVector<QPolygon> segmentsForRect(QRect, QPolygon &center, bool useOffset = false) const;
void updateSegments();
void drawSegments(QPainter *p);
void drawSegment(QPainter *p, const QPolygon &segment);
QPoint m_hoveredPt = {};
QHash<DropLocation, QPolygon> m_segments;
};
}
#endif

View File

@@ -70,8 +70,6 @@ public:
virtual void show() = 0;
virtual void hide() = 0;
virtual void update() = 0;
virtual QPoint mapFromGlobal(QPoint) const = 0;
virtual QPoint mapToGlobal(QPoint) const = 0;
QSize size() const {
return geometry().size();

View File

@@ -121,18 +121,6 @@ void Widget_quick::update()
m_thisWidget->update();
}
QPoint Widget_quick::mapFromGlobal(QPoint p) const
{
qWarning() << Q_FUNC_INFO << "Implement me!";
return p;
}
QPoint Widget_quick::mapToGlobal(QPoint p) const
{
qWarning() << Q_FUNC_INFO << "Implement me!";
return p;
}
QQuickItem *Widget_quick::createQQuickItem(const QString &filename, QQuickItem *parent) const
{
auto p = parent;

View File

@@ -15,7 +15,9 @@
#include <QQuickItem>
QT_BEGIN_NAMESPACE
class QQuickItem;
QT_END_NAMESPACE
///@file
///@brief A Layouting::Widget that's deals in QQuickItem
@@ -52,8 +54,6 @@ public:
void setWidth(int width) override;
void setHeight(int height) override;
void update() override;
QPoint mapFromGlobal(QPoint p) const override;
QPoint mapToGlobal(QPoint p) const override;
static QSize widgetMinSize(const QWidget *w);

View File

@@ -121,13 +121,3 @@ void Widget_qwidget::update()
{
m_thisWidget->update();
}
QPoint Widget_qwidget::mapFromGlobal(QPoint p) const
{
return m_thisWidget->mapFromGlobal(p);
}
QPoint Widget_qwidget::mapToGlobal(QPoint p) const
{
return m_thisWidget->mapToGlobal(p);
}

View File

@@ -55,8 +55,6 @@ public:
void setWidth(int width) override;
void setHeight(int height) override;
void update() override;
QPoint mapFromGlobal(QPoint p) const override;
QPoint mapToGlobal(QPoint p) const override;
private:
QWidget *const m_thisWidget;

View File

@@ -22,17 +22,36 @@
using namespace KDDockWidgets;
namespace KDDockWidgets {
class QuickView : public QQuickView
{
using QQuickView::QQuickView;
bool event(QEvent *ev) override
{
if (ev->type() == QEvent::FocusAboutToChange) {
// qquickwindow.cpp::event(FocusAboutToChange) removes the item grabber. Inibit that
return true;
}
return QQuickView::event(ev);
}
};
}
FloatingWindowQuick::FloatingWindowQuick(MainWindowBase *parent)
: FloatingWindow(parent)
, m_quickWindow(new QQuickView(Config::self().qmlEngine(), nullptr))
, m_quickWindow(new QuickView(Config::self().qmlEngine(), nullptr))
{
init();
}
FloatingWindowQuick::FloatingWindowQuick(Frame *frame, MainWindowBase *parent)
: FloatingWindow(frame, parent)
, m_quickWindow(new QQuickView(Config::self().qmlEngine(), nullptr))
, m_quickWindow(new QuickView(Config::self().qmlEngine(), nullptr))
{
init();
}

View File

@@ -16,8 +16,9 @@
using namespace KDDockWidgets;
MainWindowQuick::MainWindowQuick(const QString &uniqueName, QWidgetAdapter *parent)
: MainWindowBase(uniqueName, MainWindowOption_None, parent)
MainWindowQuick::MainWindowQuick(const QString &uniqueName, MainWindowOptions options,
QWidgetAdapter *parent)
: MainWindowBase(uniqueName, options, parent)
{
QWidgetAdapter::makeItemFillParent(this);
QWidgetAdapter::makeItemFillParent(dropArea());

View File

@@ -24,7 +24,9 @@ class DOCKS_EXPORT MainWindowQuick : public MainWindowBase
{
Q_OBJECT
public:
explicit MainWindowQuick(const QString &uniqueName, QWidgetAdapter *parent);
explicit MainWindowQuick(const QString &uniqueName,
MainWindowOptions options = MainWindowOption_HasCentralFrame,
QWidgetAdapter *parent = nullptr);
};
}

View File

@@ -31,5 +31,5 @@ void MainWindowWrapper::init(const QString &uniqueName)
return;
}
m_mainWindow = new MainWindowQuick(uniqueName, this);
m_mainWindow = new MainWindowQuick(uniqueName, MainWindowOption_None, this);
}

View File

@@ -58,6 +58,12 @@ void QWidgetAdapter::raiseAndActivate()
}
}
void QWidgetAdapter::setWindowOpacity(qreal level)
{
if (QWindow *w = windowHandle())
w->setOpacity(level);
}
bool QWidgetAdapter::onResize(QSize) { return false; }
void QWidgetAdapter::onLayoutRequest() {}
void QWidgetAdapter::onMousePress() {}
@@ -176,6 +182,14 @@ bool QWidgetAdapter::isMaximized() const
return false;
}
bool KDDockWidgets::QWidgetAdapter::isActiveWindow() const
{
if (QWindow *w = windowHandle())
return w->isActive();
return false;
}
void QWidgetAdapter::showMaximized()
{
if (QWindow *w = windowHandle())
@@ -216,6 +230,28 @@ QWidgetAdapter *QWidgetAdapter::parentWidget() const
return nullptr;
}
QPoint QWidgetAdapter::mapToGlobal(QPoint pt) const
{
return QQuickItem::mapToGlobal(pt).toPoint();
}
QPoint QWidgetAdapter::mapFromGlobal(QPoint pt) const
{
return QQuickItem::mapFromGlobal(pt).toPoint();
}
void QWidgetAdapter::setWindowTitle(const QString &title)
{
if (QWindow *window = windowHandle())
window->setTitle(title);
}
void QWidgetAdapter::setWindowIcon(const QIcon &icon)
{
if (QWindow *window = windowHandle())
window->setIcon(icon);
}
void QWidgetAdapter::close()
{
QCloseEvent ev;
@@ -323,6 +359,16 @@ void QWidgetAdapter::setFlag(Qt::WindowType f, bool on)
}
}
Qt::FocusPolicy QWidgetAdapter::focusPolicy() const
{
return Qt::NoFocus;
}
void QWidgetAdapter::setFocusProxy(QQuickItem *)
{
qWarning() << Q_FUNC_INFO << "Implement me";
}
QQuickItem* KDDockWidgets::Private::widgetForWindow(QWindow *window)
{
if (!window)

View File

@@ -89,7 +89,6 @@ public:
QRect geometry() const;
QRect rect() const;
void show();
void setEnabled(bool) {}
void setFixedHeight(int);
void setFixedWidth(int);
void raise();
@@ -114,18 +113,19 @@ public:
void resize(QSize);
bool isWindow() const { return parentItem() == nullptr; }
bool isMaximized() const;
bool isActiveWindow() const;
void showMaximized();
void showNormal();
QWindow *windowHandle() const;
QWidgetAdapter *window() const;
QWidgetAdapter *parentWidget() const;
QPoint mapToGlobal(QPoint) const { return {}; }
QPoint mapFromGlobal(QPoint) const { return {}; }
QPoint mapToGlobal(QPoint pt) const;
QPoint mapFromGlobal(QPoint) const;
bool testAttribute(Qt::WidgetAttribute) { return false; }
void setWindowTitle(const QString &) {}
void setWindowIcon(const QIcon &) {}
void setWindowTitle(const QString &);
void setWindowIcon(const QIcon &);
void close();
QQuickItem *childAt(QPoint) const;
void move(int x, int y);
@@ -134,9 +134,13 @@ public:
void activateWindow();
void setSizePolicy(QSizePolicy);
QSizePolicy sizePolicy() const;
Qt::FocusPolicy focusPolicy() const;
void setFocusPolicy(Qt::FocusPolicy) {}
void setFocusProxy(QQuickItem*);
virtual QSize sizeHint() const;
Qt::WindowFlags windowFlags() const;
void setWindowOpacity(qreal);
static QQuickItem *createItem(QQmlEngine *, const QString &filename);
static void makeItemFillParent(QQuickItem *item);

View File

@@ -12,7 +12,9 @@
#include "QmlTypes.h"
#include "DropAreaWithCentralFrame_p.h"
#include "quick/MainWindowWrapper_p.h"
#include "DropIndicatorOverlayInterface_p.h"
#include "TitleBar_p.h"
#include "indicators/ClassicIndicators_p.h"
#include <QQmlEngine>
#include <QDebug>
@@ -24,5 +26,8 @@ void KDDockWidgets::registerQmlTypes()
qmlRegisterType<MainWindowWrapper>("com.kdab.dockwidgets", 1, 0, "MainWindow");
qmlRegisterUncreatableType<TitleBar>("com.kdab.dockwidgets", 1, 0, "TitleBar", QStringLiteral("Enum access only"));
qmlRegisterUncreatableType<DropIndicatorOverlayInterface>("com.kdab.dockwidgets", 1, 0, "DropIndicatorOverlayInterface", QStringLiteral("Enum access only"));
qRegisterMetaType<DropArea*>();
qRegisterMetaType<KDDockWidgets::ClassicIndicators*>();
}

View File

@@ -0,0 +1,13 @@
import QtQuick 2.9
import com.kdab.dockwidgets 1.0
Image {
id: root
property int indicatorType: DropIndicatorOverlayInterface.DropLocation_None
readonly property bool isHovered: _window.classicIndicators.currentDropLocation === indicatorType
source: "qrc:/img/classic_indicators/" + _window.iconName(indicatorType, isHovered) + ".png";
width: 64
height: 64
}

View File

@@ -0,0 +1,115 @@
/*
This file is part of KDDockWidgets.
SPDX-FileCopyrightText: 2020 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
Author: Sergio Martins <sergio.martins@kdab.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/
import QtQuick 2.9
import com.kdab.dockwidgets 1.0
Item {
id: root
anchors.fill: parent
readonly property int outterMargin: 10
readonly property int innerMargin: 10
readonly property QtObject innerIndicators: innerIndicators
ClassicIndicator {
visible: _window.classicIndicators.outterIndicatorsVisible
indicatorType: DropIndicatorOverlayInterface.DropLocation_OutterLeft
anchors {
left: parent.left
leftMargin: outterMargin
verticalCenter: parent.verticalCenter
}
}
ClassicIndicator {
visible: _window.classicIndicators.outterIndicatorsVisible
indicatorType: DropIndicatorOverlayInterface.DropLocation_OutterRight
anchors {
right: parent.right
rightMargin: outterMargin
verticalCenter: parent.verticalCenter
}
}
ClassicIndicator {
visible: _window.classicIndicators.outterIndicatorsVisible
indicatorType: DropIndicatorOverlayInterface.DropLocation_OutterTop
anchors {
top: parent.top
topMargin: outterMargin
horizontalCenter: parent.horizontalCenter
}
}
ClassicIndicator {
visible: _window.classicIndicators.outterIndicatorsVisible
indicatorType: DropIndicatorOverlayInterface.DropLocation_OutterBottom
anchors {
bottom: parent.bottom
bottomMargin: outterMargin
horizontalCenter: parent.horizontalCenter
}
}
Item {
id: innerIndicators
objectName: "innerIndicators"
x: _window.classicIndicators.hoveredFrameRect.x + (_window.classicIndicators.hoveredFrameRect.width / 2)
y: _window.classicIndicators.hoveredFrameRect.y + (_window.classicIndicators.hoveredFrameRect.height / 2)
width: (centerIndicator * 3) + (2 * innerMargin)
height: width
visible: _window.classicIndicators.innerIndicatorsVisible
ClassicIndicator {
indicatorType: DropIndicatorOverlayInterface.DropLocation_Left
anchors {
right: centerIndicator.left
rightMargin: innerMargin
verticalCenter: parent.verticalCenter
}
}
ClassicIndicator {
id: centerIndicator
indicatorType: DropIndicatorOverlayInterface.DropLocation_Center
anchors.centerIn: parent
}
ClassicIndicator {
indicatorType: DropIndicatorOverlayInterface.DropLocation_Right
anchors {
left: centerIndicator.right
leftMargin: innerMargin
verticalCenter: parent.verticalCenter
}
}
ClassicIndicator {
indicatorType: DropIndicatorOverlayInterface.DropLocation_Top
anchors {
bottom: centerIndicator.top
bottomMargin: innerMargin
horizontalCenter: parent.horizontalCenter
}
}
ClassicIndicator {
indicatorType: DropIndicatorOverlayInterface.DropLocation_Bottom
anchors {
top: centerIndicator.bottom
topMargin: innerMargin
horizontalCenter: parent.horizontalCenter
}
}
}
}

View File

@@ -172,7 +172,7 @@ void TabWidget::insertDockWidget(DockWidgetBase *dock, int index)
}
QPointer<Frame> oldFrame = dock->frame();
insertDockWidget(index, dock, dock->icon(), dock->title());
insertDockWidget(index, dock, dock->icon(DockWidgetBase::IconPlace::TabBar), dock->title());
setCurrentDockWidget(index);
if (oldFrame && oldFrame->beingDeletedLater()) {

View File

@@ -7,5 +7,7 @@
<file>private/quick/qml/Separator.qml</file>
<file>private/quick/qml/TitleBarBase.qml</file>
<file>private/quick/qml/TitleBar.qml</file>
<file>private/quick/qml/ClassicIndicatorsOverlay.qml</file>
<file>private/quick/qml/ClassicIndicator.qml</file>
</qresource>
</RCC>

View File

@@ -10,7 +10,6 @@
*/
#include "Testing.h"
#include "MainWindow.h"
#include "DockRegistry_p.h"
#include <QApplication>

View File

@@ -14,12 +14,15 @@
#include "Testing.h"
#include "utils.h"
#include "DockWidgetBase.h"
#include "private/MultiSplitter_p.h"
#include <QtTest/QtTest>
#include <QObject>
#include <QApplication>
using namespace KDDockWidgets;
using namespace KDDockWidgets::Tests;
class TestCommon : public QObject
{
@@ -43,7 +46,10 @@ private Q_SLOTS:
void TestCommon::tst_simple1()
{
// TODO
// Simply create a MainWindow
EnsureTopLevelsDeleted e;
auto m = createMainWindow();
m->multiSplitter()->checkSanity();
}
int main(int argc, char *argv[])

View File

@@ -47,6 +47,7 @@
#include <QMenuBar>
#include <QStyleFactory>
#include <QCursor>
#include <QLineEdit>
#ifdef Q_OS_WIN
# include <Windows.h>
@@ -266,42 +267,6 @@ static QWidget *createWidget(int minLength, const QString &objname = QString())
return w;
}
struct EnsureTopLevelsDeleted
{
EnsureTopLevelsDeleted()
: m_originalFlags(Config::self().flags())
, m_originalSeparatorThickness(Config::self().separatorThickness())
{
}
~EnsureTopLevelsDeleted()
{
if (topLevels().size() != 0) {
qWarning() << "There's still top-level widgets present!" << topLevels();
}
// Other cleanup, since we use this class everywhere
Config::self().setDockWidgetFactoryFunc(nullptr);
Config::self().setFlags(m_originalFlags);
Config::self().setSeparatorThickness(m_originalSeparatorThickness);
}
QWidgetList topLevels() const
{
QWidgetList result;
for (QWidget *w : qApp->topLevelWidgets()) {
if (!qobject_cast<QToolButton*>(w))
result << w;
}
return result;
}
const Config::Flags m_originalFlags;
const int m_originalSeparatorThickness;
};
class TestDocks : public QObject
{
Q_OBJECT
@@ -323,7 +288,6 @@ public:
static void nestDockWidget(DockWidgetBase *dock, DropArea *dropArea, Frame *relativeTo, KDDockWidgets::Location location);
private Q_SLOTS:
void tst_simple1();
void tst_simple2();
void tst_shutdown();
void tst_mainWindowAlwaysHasCentralWidget();
@@ -463,7 +427,8 @@ private Q_SLOTS:
void tst_maximumSizePolicy();
void tst_tabsNotClickable();
void tst_stuckSeparator();
void setWidget();
void tst_isFocused();
void tst_setWidget();
private:
std::unique_ptr<MultiSplitter> createMultiSplitterFromSetup(MultiSplitterSetup setup, QHash<QWidget *, Frame *> &frameMap) const;
@@ -584,7 +549,7 @@ DockWidgetBase *createAndNestDockWidget(DropArea *dropArea, Frame *relativeTo, K
return dock;
}
std::unique_ptr<MainWindow> createSimpleNestedMainWindow(DockWidgetBase * *centralDock, DockWidgetBase * *leftDock, DockWidgetBase * *rightDock)
std::unique_ptr<MainWindowBase> createSimpleNestedMainWindow(DockWidgetBase * *centralDock, DockWidgetBase * *leftDock, DockWidgetBase * *rightDock)
{
auto window = createMainWindow({900, 500});
*centralDock = createDockWidget("centralDock", Qt::green);
@@ -661,7 +626,7 @@ void TestDocks::tst_dock2FloatingWidgetsTabbed()
QTest::qWait(1000); // Test is flaky otherwise
auto fw2 = dock2->floatingWindow();
drag(fw2->titleBar(), static_cast<Layouting::Widget*>(frame2)->mapToGlobal(QPoint(10, 10)), dock3->window()->geometry().center());
drag(fw2->titleBar(), frame2->mapToGlobal(QPoint(10, 10)), dock3->window()->geometry().center());
QVERIFY(Testing::waitForDeleted(frame1));
QVERIFY(Testing::waitForDeleted(frame2));
@@ -810,7 +775,7 @@ void TestDocks::tst_close()
// 2. Test that closing the single frame of a main window doesn't close the main window itself
{
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None); // Remove central frame
QPointer<MainWindow> mainWindowPtr = m.get();
QPointer<MainWindowBase> mainWindowPtr = m.get();
dock1 = createDockWidget("hello", Qt::green);
m->addDockWidget(dock1, Location_OnLeft);
@@ -823,7 +788,7 @@ void TestDocks::tst_close()
// 2.1 Test closing the frame instead
{
auto m = createMainWindow(QSize(800, 500), MainWindowOption_None); // Remove central frame
QPointer<MainWindow> mainWindowPtr = m.get();
QPointer<MainWindowBase> mainWindowPtr = m.get();
dock1 = createDockWidget("hello", Qt::green);
m->addDockWidget(dock1, Location_OnLeft);
@@ -837,7 +802,7 @@ void TestDocks::tst_close()
// 2.2 Repeat, but with a central frame
{
auto m = createMainWindow(QSize(800, 500));
QPointer<MainWindow> mainWindowPtr = m.get();
QPointer<MainWindowBase> mainWindowPtr = m.get();
dock1 = createDockWidget("hello", Qt::green);
m->addDockWidget(dock1, Location_OnLeft);
@@ -2579,14 +2544,6 @@ void TestDocks::tst_setVisibleFalseWhenSideBySide()
Testing::waitForDeleted(window);
}
void TestDocks::tst_simple1()
{
// Simply create a MainWindow
EnsureTopLevelsDeleted e;
auto m = createMainWindow();
m->multiSplitter()->checkSanity();
}
void TestDocks::tst_simple2()
{
// Simply create a MainWindow, and dock something on top
@@ -5259,6 +5216,50 @@ void TestDocks::tst_floatingAction()
Testing::waitForDeleted(fw);
}
{
// 3. A floating window with two tabs
auto dock1 = createDockWidget("dock1", new QPushButton("one"));
auto dock2 = createDockWidget("dock2", new QPushButton("two"));
bool dock1IsFloating = dock1->floatAction()->isChecked();
bool dock2IsFloating = dock2->floatAction()->isChecked();
connect(dock1->floatAction(), &QAction::toggled, [&dock1IsFloating] (bool t) {
Q_ASSERT(dock1IsFloating != t);
dock1IsFloating = t;
});
connect(dock2->floatAction(), &QAction::toggled, [&dock2IsFloating] (bool t) {
Q_ASSERT(dock2IsFloating != t);
dock2IsFloating = t;
});
auto fw2 = dock2->floatingWindow();
QVERIFY(dock1->isFloating());
QVERIFY(dock2->isFloating());
QVERIFY(dock1->floatAction()->isChecked());
QVERIFY(dock2->floatAction()->isChecked());
dock1->addDockWidgetAsTab(dock2);
QVERIFY(!dock1->isFloating());
QVERIFY(!dock2->isFloating());
QVERIFY(!dock1->floatAction()->isChecked());
QVERIFY(!dock2->floatAction()->isChecked());
dock2->setFloating(true);
QVERIFY(dock1->isFloating());
QVERIFY(dock1->floatAction()->isChecked());
QVERIFY(dock2->isFloating());
QVERIFY(dock2->floatAction()->isChecked());
QVERIFY(dock1IsFloating);
QVERIFY(dock2IsFloating);
delete fw2;
delete dock1->window();
delete dock2->window();
}
}
void TestDocks::tst_dockableMainWindows()
@@ -5751,7 +5752,65 @@ void TestDocks::tst_stuckSeparator()
}
}
void TestDocks::setWidget()
void TestDocks::tst_isFocused()
{
EnsureTopLevelsDeleted e;
// 1. Create 2 floating windows
auto dock1 = createDockWidget(QStringLiteral("dock1"), new QLineEdit());
auto dock2 = createDockWidget(QStringLiteral("dock2"), new QLineEdit());
dock1->window()->move(400, 200);
// 2. Raise dock1 and focus its line edit
dock1->raise();
dock1->widget()->setFocus(Qt::OtherFocusReason);
Testing::waitForEvent(dock1->widget(), QEvent::FocusIn);
QVERIFY(dock1->isFocused());
QVERIFY(!dock2->isFocused());
// 3. Raise dock3 and focus its line edit
dock2->raise();
dock2->widget()->setFocus(Qt::OtherFocusReason);
Testing::waitForEvent(dock2->widget(), QEvent::FocusIn);
QVERIFY(!dock1->isFocused());
QVERIFY(dock2->isFocused());
// 4. Tab dock1, it's current tab now
auto oldFw1 = dock1->window();
dock2->addDockWidgetAsTab(dock1);
delete oldFw1;
QVERIFY(dock1->isFocused());
QVERIFY(!dock2->isFocused());
// 5. Set dock2 as current tab again
dock2->raise();
QVERIFY(!dock1->isFocused());
QVERIFY(dock2->isFocused());
// 6. Create dock3, focus it
auto dock3 = createDockWidget(QStringLiteral("dock3"), new QLineEdit());
auto oldFw3 = dock3->window();
dock3->raise();
dock3->widget()->setFocus(Qt::OtherFocusReason);
Testing::waitForEvent(dock2->widget(), QEvent::FocusIn);
QVERIFY(!dock1->isFocused());
QVERIFY(!dock2->isFocused());
QVERIFY(dock3->isFocused());
// 4. Add dock3 to the 1st window, nested, focus 2 again
dock2->addDockWidgetToContainingWindow(dock3, Location_OnLeft);
delete oldFw3;
dock2->raise();
dock2->widget()->setFocus(Qt::OtherFocusReason);
Testing::waitForEvent(dock2->widget(), QEvent::FocusIn);
QVERIFY(!dock1->isFocused());
QVERIFY(dock2->isFocused());
QVERIFY(!dock3->isFocused());
delete dock2->window();
}
void TestDocks::tst_setWidget()
{
EnsureTopLevelsDeleted e;
auto dw = new DockWidget(QStringLiteral("FOO"));

View File

@@ -23,8 +23,10 @@
#ifdef KDDOCKWIDGETS_QTQUICK
# include "private/quick/DockWidgetQuick.h"
# include "private/quick/MainWindowQuick_p.h"
#else
# include "DockWidget.h"
# include "MainWindow.h"
#endif
using namespace KDDockWidgets;
@@ -46,8 +48,7 @@ void NonClosableWidget::closeEvent(QCloseEvent *ev)
ev->ignore(); // don't allow to close
}
#ifdef KDDOCKWIDGETS_QTWIDGETS
std::unique_ptr<KDDockWidgets::MainWindow> KDDockWidgets::Tests::createMainWindow(QSize sz, KDDockWidgets::MainWindowOptions options, const QString &name)
std::unique_ptr<KDDockWidgets::MainWindowBase> KDDockWidgets::Tests::createMainWindow(QSize sz, KDDockWidgets::MainWindowOptions options, const QString &name)
{
static int count = 0;
count++;
@@ -55,17 +56,17 @@ std::unique_ptr<KDDockWidgets::MainWindow> KDDockWidgets::Tests::createMainWindo
const QString mainWindowName = name.isEmpty() ? QStringLiteral("MyMainWindow%1").arg(count)
: name;
auto ptr = std::unique_ptr<MainWindow>(new MainWindow(mainWindowName, options));
auto ptr = std::unique_ptr<MainWindowType>(new MainWindowType(mainWindowName, options));
ptr->show();
ptr->resize(sz);
return ptr;
}
#endif
DockWidgetBase *KDDockWidgets::Tests::createDockWidget(const QString &name, QWidgetOrQuick *w,
DockWidgetBase::Options options, bool show,
const QString &affinityName)
{
w->setFocusPolicy(Qt::StrongFocus);
auto dock = new DockWidgetType(name, options);
dock->setAffinityName(affinityName);
dock->DockWidgetBase::setWidget(w);
@@ -90,32 +91,41 @@ DockWidgetBase *KDDockWidgets::Tests::createDockWidget(const QString &name, QCol
return createDockWidget(name, new MyWidget(name, color));
};
static QWidgetOrQuick *createGuestWidget(int i)
{
#ifdef KDDOCKWIDGETS_QTWIDGETS
std::unique_ptr<MainWindow> KDDockWidgets::Tests::createMainWindow(QVector<DockDescriptor> &docks)
return new QPushButton(QStringLiteral("%1").arg(i));
#else
Q_UNUSED(i);
return new QWidgetAdapter();
#endif
}
std::unique_ptr<MainWindowBase> KDDockWidgets::Tests::createMainWindow(QVector<DockDescriptor> &docks)
{
static int count = 0;
count++;
auto m = std::unique_ptr<MainWindow>(new MainWindow(QStringLiteral("MyMainWindow%1").arg(count), MainWindowOption_None));
auto m = std::unique_ptr<MainWindowType>(new MainWindowType(QStringLiteral("MyMainWindow%1").arg(count), MainWindowOption_None));
auto layout = m->multiSplitter();
m->show();
m->resize(QSize(700, 700));
int i = 0;
for (DockDescriptor &desc : docks) {
desc.createdDock = createDockWidget(QStringLiteral("%1-%2").arg(i).arg(count), new QPushButton(QStringLiteral("%1").arg(i)), {}, false);
desc.createdDock = createDockWidget(QStringLiteral("%1-%2").arg(i).arg(count), createGuestWidget(i), {}, false);
DockWidgetBase *relativeTo = nullptr;
if (desc.relativeToIndex != -1)
relativeTo = docks.at(desc.relativeToIndex).createdDock;
m->addDockWidget(desc.createdDock, desc.loc, relativeTo, desc.option);
qDebug() << "Added" <<i;
layout->checkSanity();
++i;
}
return m;
}
#endif
MyWidget::MyWidget(const QString &, QColor c)
: QWidgetOrQuick()
, c(c)

View File

@@ -12,12 +12,16 @@
#ifndef KDDOCKWIDGETS_TESTS_UTILS_H
#define KDDOCKWIDGETS_TESTS_UTILS_H
#include "MainWindow.h"
#include "KDDockWidgets.h"
#include "DropIndicatorOverlayInterface_p.h"
#include "DockWidgetBase.h"
#include "Config.h"
#ifdef KDDOCKWIDGETS_QTWIDGETS
# include <QWidget>
# include <QToolButton>
#endif
#include <QWidget>
#include <QPointer>
#include <QVector>
@@ -45,16 +49,51 @@ struct DockDescriptor {
KDDockWidgets::AddingOption option;
};
struct EnsureTopLevelsDeleted
{
EnsureTopLevelsDeleted()
: m_originalFlags(Config::self().flags())
, m_originalSeparatorThickness(Config::self().separatorThickness())
{
}
~EnsureTopLevelsDeleted()
{
if (topLevels().size() != 0) {
qWarning() << "There's still top-level widgets present!" << topLevels();
}
// Other cleanup, since we use this class everywhere
Config::self().setDockWidgetFactoryFunc(nullptr);
Config::self().setFlags(m_originalFlags);
Config::self().setSeparatorThickness(m_originalSeparatorThickness);
}
QWidgetList topLevels() const
{
QWidgetList result;
#ifdef KDDOCKWIDGETS_QTWIDGETS
for (QWidget *w : qApp->topLevelWidgets()) {
if (!qobject_cast<QToolButton*>(w))
result << w;
}
#endif
return result;
}
const Config::Flags m_originalFlags;
const int m_originalSeparatorThickness;
};
bool shouldBlacklistWarning(const QString &msg, const QString &category = {});
#ifdef KDDOCKWIDGETS_QTWIDGETS
std::unique_ptr<KDDockWidgets::MainWindow> createMainWindow(QSize sz = {600, 600},
std::unique_ptr<MainWindowBase> createMainWindow(QSize sz = {600, 600},
KDDockWidgets::MainWindowOptions options = MainWindowOption_HasCentralFrame, const QString &name = {});
std::unique_ptr<KDDockWidgets::MainWindow> createMainWindow(QVector<DockDescriptor> &docks);
#endif
std::unique_ptr<KDDockWidgets::MainWindowBase> createMainWindow(QVector<DockDescriptor> &docks);
KDDockWidgets::DockWidgetBase *createDockWidget(const QString &name, QWidgetOrQuick *w,
DockWidgetBase::Options options = {}, bool show = true,
const QString &affinityName = {});