Compare commits

...

46 Commits
3.1.0 ... 3.2.6

Author SHA1 Message Date
Uwe Kindler
7c2d1891a2 Added showcase section to README.md 2020-04-01 12:31:37 +02:00
Uwe Kindler
998fe9fa11 Added Q_OS_WIN around the installation of the eventFilter in FloatingDockContainer because it is only required for Windows 2020-04-01 08:36:33 +02:00
Uwe Kindler
28dc374fc2 Added support for proper handling on non client escape key presses on Windows 2020-04-01 08:12:45 +02:00
Uwe Kindler
1b6e449b4a Merge branch 'dev' 2020-03-28 19:32:59 +01:00
Uwe Kindler
0e88467f94 DockAreaWidget now properly considers minimumSizeHint() of contained DockWidgets
Added setMinimumSizeHintMode() test in demo MainWindow
2020-03-28 19:32:07 +01:00
Uwe Kindler
d0f4ce3248 Added support to adhere the minimumSizeHint() of the content widget of a dock widget 2020-03-26 07:09:01 +01:00
Uwe Kindler
542618fd4e Removed unneeded includes from DockManager.h and fixed includes to use "" instead of <> to prvent conflicts with external libraries 2020-03-26 06:53:13 +01:00
Uwe Kindler
661d0c4356 Removed dead code in comment of DockAreaWidget 2020-03-25 14:38:10 +01:00
Uwe Kindler
dceaa155c4 Merge branch 'master' of https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System 2020-03-25 14:35:26 +01:00
Uwe Kindler
c541f2c69b DockWidget now also emits closed() signal if DockWidgetDeleteOnClose flag is set 2020-03-25 14:34:42 +01:00
Uwe Kindler
37d305e50d Fixed documentation of toolBarIconSize() function of DockWidget 2020-03-25 14:23:27 +01:00
K Lauer
4adef2b774 FIX: DockContainerWidget::dropWidget API change not reflected in Python bindings (#133)
* FIX: dropWidget API change not reflected in Python bindings

* Add missing methods to Python bindings (3.2.4)
2020-03-17 09:12:00 +01:00
Uwe Kindler
1c2383f8eb Fixed regression #132 - Inner drop areas don't work with multi tab DockWidgets 2020-03-12 20:29:03 +01:00
Uwe Kindler
6c687d28de Fixxed isse #131 - Crash on dropping in same area multiple times 2020-03-12 10:23:41 +01:00
Uwe Kindler
708add3ff5 Fixed two linter warnings in CDockManager 2020-03-12 08:01:57 +01:00
Uwe Kindler
e85b4167bd Changed CDockManager::addToggleViewActionToMenu to support later setting of group icon 2020-03-10 09:11:36 +01:00
Uwe Kindler
59c783831a Fixed wrong test for VisibleDockAreas 2020-03-07 16:18:19 +01:00
Uwe Kindler
4cb1931ace Fixed display of container dock overlay if only one single visible dock area is in a container 2020-03-02 11:21:44 +01:00
Uwe Kindler
fe10b570d3 Fixed reception of escape key press events 2020-02-26 20:51:29 +01:00
Uwe Kindler
f3c5d51380 Improved documentation for setting dock manager flags 2020-02-26 13:25:17 +01:00
Uwe Kindler
34cb2ae917 Fixed escape key press detection 2020-02-25 09:16:21 +01:00
Hugo Slepicka
8cc9cc25ad Fixed and updated Python integration (#127)
* FIX: Add Q_OS_MACOS flag to moc compiler.

* (Python) Demo and example from @n-elie.

* FIX: Addressing some sip files that were inconsistent with the header files.

* (Python) Addressing comments by @n-elie and switching to use WS_X11 for platform checks.

* (Python) Wrap definition of tFloatingWidgetBase to avoid 'Already Defined' error and fix include path for sip/linux/FloatingWidgetTitleBar.sip.

* Remove simple.py

* Fix case sensitive ui file loading in Linux

* Add windows case in get_moc_args

* Remove conda recipe

Co-authored-by: n-elie <40382614+n-elie@users.noreply.github.com>
2020-02-25 07:22:51 +01:00
Hugo Slepicka
c90fb9413c FIX: Update Python SIP bindings 3.2.1 & Linux Crash (#126)
* (Python) WIP: attempt to update to 3.2.0

* (Python) MAINT: move demo.py to demo/ to avoid PyQtAds import issue

* (Python) STY: remove trailing whitespace

* (Python) Missing/incorrect /Transfer/ annotations

* (Python) n-elie's fix for setup.py moc generation

* (Python) FIX: Q_OS_LINUX is not defined by default with moc

* (Python) FIX: split FloatingDockContainer.sip to be platform-specific

%If (Platform) blocks around the class definition in
FloatingDockContainer.sip did not seem to work.

Co-authored-by: K Lauer <klauer@users.noreply.github.com>
2020-02-21 22:47:09 +01:00
Rodrigo Oliva
dec170ed24 Don't select a new tab if the tab being closed is not the current tab. (#125) 2020-02-21 22:46:21 +01:00
Uwe Kindler
3ffbbfb6d0 Merge branch 'master' of https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System 2020-02-19 22:51:53 +01:00
Uwe Kindler
e8332575f8 Improved tab dragging, added support for undocking if mouse leaves tabbar during tb dragging 2020-02-19 22:48:17 +01:00
Uwe Kindler
8c12d912b4 Improved tab dragging, added support for undocking if mouse leaves tabbar during tb dragging 2020-02-19 22:29:29 +01:00
Uwe Kindler
fd28f0f751 Update DockAreaTab bar to undock / shwo undock preview as soon as a tab is dragged outside of the tab bar 2020-02-19 22:07:17 +01:00
Uwe Kindler
3f09d5c6ea Fixed initial position for CFloatingDockContainer when dragging a tab outside of the tabbar 2020-02-19 18:36:57 +01:00
Uwe Kindler
3407945f19 Merge branch 'master' of https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System 2020-02-17 10:42:02 +01:00
Uwe Kindler
ce01e6b4a6 Fixed QT_VESION typo in DockOverlay.cpp 2020-02-17 10:41:13 +01:00
Sanakan8472
3428a4b8b4 Added CDockManager::floatingWidgetCreated event (#121)
This allows to subscribe to events of the newly created window.
A common use case is to show a message box if a dock container with many modified documents in it is closed. This allows for the user to decide whether he wants to save / discard all the changes or cancel the closing of the window.
2020-02-17 08:15:11 +01:00
Uwe Kindler
2b9377b5ee Fixed drag canceling via ESC key 2020-02-17 08:08:25 +01:00
Uwe Kindler
d4a18003d9 Properly implemented handling of DockWidget flag DockWidgetIsMovable for NonOpaque undocking - creating the drag preview is allowed even if the DockWidget is not floatable 2020-02-16 14:37:14 +01:00
Uwe Kindler
2c15d5dacd Fixed regression caused by setDockArea() function removed from DockWidgetTab 2020-02-14 22:56:48 +01:00
Uwe Kindler
f236de3277 Replaced all dynamic_casts with qobject_casts 2020-02-13 18:56:04 +01:00
Uwe Kindler
93394577d0 Merge branch 'master' into custom_titlebar 2020-02-13 13:53:56 +01:00
Uwe Kindler
f387c6aebc Merge branch 'master' of https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System 2020-02-13 13:51:11 +01:00
Uwe Kindler
41173d067b Switched QScopedPointer to std::unique_ptr in DockComponentsFactory 2020-02-13 13:45:40 +01:00
Uwe Kindler
5b60e39ed3 Removed unneeded functions 2020-02-13 13:04:08 +01:00
Uwe Kindler
1916bd726d Fixed build issue for older Qt versions 2020-02-11 15:46:19 +01:00
Uwe Kindler
3efc5f2ada Added DockComponentsFactory.h documentation, changed DockComponentsFactory showcase in MainWindow after discussion on GitHub 2020-02-11 15:38:49 +01:00
Uwe Kindler
3eba02597c Added missing svg icon 2020-02-11 11:57:53 +01:00
Uwe Kindler
65eeffd5e1 Added showcase for DockComponentsFactory - a help button is injected into a title bar 2020-02-11 09:31:57 +01:00
Uwe Kindler
ff1439c719 Added CDockComponentsFactory for creation of components for the docking framework 2020-02-11 08:32:49 +01:00
mvidelgauz
7ba20f37b7 Icon of floating window (#116)
* FloatingContainerHasWidgetTitle and FloatingContainerHasWidgetIcon config flags
2020-02-10 20:07:36 +01:00
51 changed files with 1430 additions and 689 deletions

View File

@@ -42,6 +42,7 @@ set(ads_SRCS
src/FloatingDockContainer.cpp src/FloatingDockContainer.cpp
src/FloatingDragPreview.cpp src/FloatingDragPreview.cpp
src/IconProvider.cpp src/IconProvider.cpp
src/DockComponentsFactory.cpp
src/ads.qrc src/ads.qrc
src/linux/FloatingWidgetTitleBar.cpp src/linux/FloatingWidgetTitleBar.cpp
) )
@@ -61,6 +62,7 @@ set(ads_INSTALL_INCLUDE
src/FloatingDockContainer.h src/FloatingDockContainer.h
src/FloatingDragPreview.h src/FloatingDragPreview.h
src/IconProvider.h src/IconProvider.h
src/DockComponentsFactory.h
src/linux/FloatingWidgetTitleBar.h src/linux/FloatingWidgetTitleBar.h
) )
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4") if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4")

View File

@@ -48,6 +48,9 @@ of his docking system project.
- [KDDockWidgets](#kddockwidgets) - [KDDockWidgets](#kddockwidgets)
- [QtitanDocking](#qtitandocking) - [QtitanDocking](#qtitandocking)
- [Donation](#donation) - [Donation](#donation)
- [Showcase](#showcase)
- [Qt Design Studio](#qt-design-studio)
- [QmixElements](#qmixelements)
### Docking everywhere - no central widget ### Docking everywhere - no central widget
@@ -273,3 +276,21 @@ If this project help you reduce time to develop or if you just like it, you can
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=85R64TMMSY9T6"> <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=85R64TMMSY9T6">
<img src="doc/donate.png" alt="Donate with PayPal" width="160"/> <img src="doc/donate.png" alt="Donate with PayPal" width="160"/>
</a> </a>
## Showcase
### [Qt Design Studio](https://www.qt.io/ui-design-tools)
Taken from the [Qt Blog](https://www.qt.io/blog/qt-design-studio-1.5-beta-released):
> The most obvious change in [Qt Design Studio 1.5](https://www.qt.io/blog/qt-design-studio-1.5-beta-released) is the integration of dock widgets using the Qt Advanced Docking System. This allows the user to fully customize the workspace and also to undock any view into its own top level window. This especially improves the usability when using multiple screens.
![Qt Design Studio](doc/qt_design_studio.png)
### [QmixElements](https://www.cetoni.com/products/qmixelements/)
The QmixElements software from [CETONI](https://www.cetoni.com) is a comprehensive,
plugin-based and modular laboratory automation software for controlling CETONI devices using a joint graphical user interface. The software features a powerful script system to automate processes. This [blog post](https://www.cetoni.com/blog/qmixelements-advanced-docking-system/) gives a nice overview about the use of the Qt
Advanced Docking System in the QmixElements sofware.
![QmixElements](doc/qmix_elements.png)

316
demo.py
View File

@@ -1,316 +0,0 @@
import datetime
import logging
from PyQt5.QtCore import (QCoreApplication, QDir, Qt, QSettings, QSignalBlocker,
QRect)
from PyQt5.QtGui import QGuiApplication
from PyQt5.QtWidgets import (QCalendarWidget, QFileSystemModel, QFrame, QLabel,
QMenu, QTreeView, QAction, QWidgetAction,
QComboBox, QStyle, QSizePolicy, QInputDialog)
from PyQt5 import QtWidgets
from PyQtAds import QtAds
class _State:
label_count = 0
calendar_count = 0
file_system_count = 0
def create_long_text_label_dock_widget(view_menu: QMenu) -> QtAds.CDockWidget:
'''
Create long text label dock widget
Parameters
----------
view_menu : QMenu
Returns
-------
value : QtAds.CDockWidget
'''
label = QLabel()
label.setWordWrap(True)
label.setAlignment(Qt.AlignTop | Qt.AlignLeft)
label.setText('''\
Label {} {} - Lorem ipsum dolor sit amet, consectetuer
adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum
sociis natoque penatibus et magnis dis parturient montes, nascetur
ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium
quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla
vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut,
imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis
pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi.
Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu,
consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra
quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet.
'''.format(_State.label_count, str(datetime.datetime.now())))
_State.label_count += 1
dock_widget = QtAds.CDockWidget("Label {}".format(_State.label_count))
dock_widget.setWidget(label)
view_menu.addAction(dock_widget.toggleViewAction())
return dock_widget
def create_calendar_dock_widget(view_menu: QMenu) -> QtAds.CDockWidget:
'''
Create calendar dock widget
Parameters
----------
view_menu : QMenu
Returns
-------
value : QtAds.CDockWidget
'''
widget = QCalendarWidget()
dock_widget = QtAds.CDockWidget("Calendar {}".format(_State.calendar_count))
_State.calendar_count += 1
dock_widget.setWidget(widget)
dock_widget.setToggleViewActionMode(QtAds.CDockWidget.ActionModeShow)
view_menu.addAction(dock_widget.toggleViewAction())
return dock_widget
def create_file_system_tree_dock_widget(view_menu: QMenu) -> QtAds.CDockWidget:
'''
Create file system tree dock widget
Parameters
----------
view_menu : QMenu
Returns
-------
value : QtAds.CDockWidget
'''
widget = QTreeView()
widget.setFrameShape(QFrame.NoFrame)
m = QFileSystemModel(widget)
m.setRootPath(QDir.currentPath())
widget.setModel(m)
dock_widget = QtAds.CDockWidget("Filesystem {}".format(_State.file_system_count))
_State.file_system_count += 1
dock_widget.setWidget(widget)
view_menu.addAction(dock_widget.toggleViewAction())
return dock_widget
class MainWindow(QtWidgets.QMainWindow):
save_perspective_action: QAction
perspective_list_action: QWidgetAction
perspective_combo_box: QComboBox
dock_manager: QtAds.CDockManager
def __init__(self, parent=None):
super().__init__(parent)
self.save_perspective_action = None
self.perspective_list_action = None
self.perspective_combo_box = None
self.dock_manager = None
self.setup_ui()
self.dock_manager = QtAds.CDockManager(self)
self.perspective_combo_box.activated[str].connect(self.dock_manager.openPerspective)
self.create_content()
self.resize(800, 600)
self.restore_state()
self.restore_perspectives()
def setup_ui(self):
self.setObjectName("MainWindow")
self.resize(400, 300)
self.setDockOptions(QtWidgets.QMainWindow.AllowTabbedDocks)
self.centralWidget = QtWidgets.QWidget(self)
self.centralWidget.setObjectName("centralWidget")
self.setCentralWidget(self.centralWidget)
self.status_bar = QtWidgets.QStatusBar(self)
self.status_bar.setObjectName("statusBar")
self.setStatusBar(self.status_bar)
self.menu_bar = QtWidgets.QMenuBar(self)
self.menu_bar.setGeometry(QRect(0, 0, 400, 21))
self.menu_bar.setObjectName("menuBar")
self.menu_file = QtWidgets.QMenu(self.menu_bar)
self.menu_file.setObjectName("menuFile")
self.menu_view = QtWidgets.QMenu(self.menu_bar)
self.menu_view.setObjectName("menuView")
self.menu_about = QtWidgets.QMenu(self.menu_bar)
self.menu_about.setObjectName("menuAbout")
self.setMenuBar(self.menu_bar)
self.tool_bar = QtWidgets.QToolBar(self)
self.tool_bar.setObjectName("toolBar")
self.addToolBar(Qt.TopToolBarArea, self.tool_bar)
self.action_exit = QtWidgets.QAction(self)
self.action_exit.setObjectName("actionExit")
self.action_save_state = QtWidgets.QAction(self)
self.action_save_state.setObjectName("actionSaveState")
self.action_save_state.triggered.connect(self.saveState)
self.action_restore_state = QtWidgets.QAction(self)
self.action_restore_state.setObjectName("actionRestoreState")
self.action_restore_state.triggered.connect(self.restore_state)
self.menu_file.addAction(self.action_exit)
self.menu_file.addAction(self.action_save_state)
self.menu_file.addAction(self.action_restore_state)
self.menu_bar.addAction(self.menu_file.menuAction())
self.menu_bar.addAction(self.menu_view.menuAction())
self.menu_bar.addAction(self.menu_about.menuAction())
self.setWindowTitle("MainWindow")
self.menu_file.setTitle("File")
self.menu_view.setTitle("View")
self.menu_about.setTitle("About")
self.tool_bar.setWindowTitle("toolBar")
self.action_exit.setText("Exit")
self.action_save_state.setText("Save State")
self.action_restore_state.setText("Restore State")
self.create_actions()
def create_actions(self):
'''
Creates the toolbar actions
'''
self.tool_bar.addAction(self.action_save_state)
self.action_save_state.setIcon(self.style().standardIcon(QStyle.SP_DialogSaveButton))
self.tool_bar.addAction(self.action_restore_state)
self.action_restore_state.setIcon(self.style().standardIcon(QStyle.SP_DialogOpenButton))
self.save_perspective_action = QAction("Save Perspective", self)
self.save_perspective_action.triggered.connect(self.save_perspective)
self.perspective_list_action = QWidgetAction(self)
self.perspective_combo_box = QComboBox(self)
self.perspective_combo_box.setSizeAdjustPolicy(QComboBox.AdjustToContents)
self.perspective_combo_box.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
self.perspective_list_action.setDefaultWidget(self.perspective_combo_box)
self.tool_bar.addSeparator()
self.tool_bar.addAction(self.perspective_list_action)
self.tool_bar.addAction(self.save_perspective_action)
def create_content(self):
'''
Fill the dock manager with dock widgets
'''
# Test container docking
view_menu = self.menu_view
dock_widget = create_calendar_dock_widget(view_menu)
dock_widget.setIcon(self.style().standardIcon(QStyle.SP_DialogOpenButton))
dock_widget.setFeature(QtAds.CDockWidget.DockWidgetClosable, False)
self.dock_manager.addDockWidget(QtAds.LeftDockWidgetArea, dock_widget)
self.dock_manager.addDockWidget(QtAds.LeftDockWidgetArea, create_long_text_label_dock_widget(view_menu))
file_system_widget = create_file_system_tree_dock_widget(view_menu)
tool_bar = file_system_widget.createDefaultToolBar()
tool_bar.addAction(self.action_save_state)
tool_bar.addAction(self.action_restore_state)
self.dock_manager.addDockWidget(QtAds.BottomDockWidgetArea, file_system_widget)
file_system_widget = create_file_system_tree_dock_widget(view_menu)
tool_bar = file_system_widget.createDefaultToolBar()
tool_bar.addAction(self.action_save_state)
tool_bar.addAction(self.action_restore_state)
file_system_widget.setFeature(QtAds.CDockWidget.DockWidgetMovable, False)
top_dock_area = self.dock_manager.addDockWidget(QtAds.TopDockWidgetArea, file_system_widget)
dock_widget = create_calendar_dock_widget(view_menu)
dock_widget.setFeature(QtAds.CDockWidget.DockWidgetClosable, False)
dock_widget.setTabToolTip("Tab ToolTip\nHodie est dies magna")
self.dock_manager.addDockWidget(QtAds.CenterDockWidgetArea, dock_widget, top_dock_area)
# Test dock area docking
right_dock_area = self.dock_manager.addDockWidget(
QtAds.RightDockWidgetArea,
create_long_text_label_dock_widget(view_menu), top_dock_area)
self.dock_manager.addDockWidget(
QtAds.TopDockWidgetArea,
create_long_text_label_dock_widget(view_menu), right_dock_area)
bottom_dock_area = self.dock_manager.addDockWidget(
QtAds.BottomDockWidgetArea,
create_long_text_label_dock_widget(view_menu), right_dock_area)
self.dock_manager.addDockWidget(
QtAds.RightDockWidgetArea,
create_long_text_label_dock_widget(view_menu), right_dock_area)
self.dock_manager.addDockWidget(
QtAds.CenterDockWidgetArea,
create_long_text_label_dock_widget(view_menu), bottom_dock_area)
def save_state(self):
'''
Saves the dock manager state and the main window geometry
'''
settings = QSettings("Settings.ini", QSettings.IniFormat)
settings.setValue("mainWindow/Geometry", self.saveGeometry())
settings.setValue("mainWindow/State", self.saveState())
settings.setValue("mainWindow/DockingState", self.dock_manager.saveState())
def save_perspectives(self):
'''
Save the list of perspectives
'''
settings = QSettings("Settings.ini", QSettings.IniFormat)
self.dock_manager.savePerspectives(settings)
def restore_state(self):
'''
Restores the dock manager state
'''
settings = QSettings("Settings.ini", QSettings.IniFormat)
geom = settings.value("mainWindow/Geometry")
if geom is not None:
self.restoreGeometry(geom)
state = settings.value("mainWindow/State")
if state is not None:
self.restoreState(state)
state = settings.value("mainWindow/DockingState")
if state is not None:
self.dock_manager.restore_state(state)
def restore_perspectives(self):
'''
Restore the perspective listo of the dock manager
'''
settings = QSettings("Settings.ini", QSettings.IniFormat)
self.dock_manager.loadPerspectives(settings)
self.perspective_combo_box.clear()
self.perspective_combo_box.addItems(self.dock_manager.perspectiveNames())
def save_perspective(self):
perspective_name, ok = QInputDialog.getText(self, 'Save perspective', 'Enter unique name:')
if ok and perspective_name:
self.dock_manager.addPerspective(perspective_name)
_ = QSignalBlocker(self.perspective_combo_box)
self.perspective_combo_box.clear()
self.perspective_combo_box.addItems(self.dock_manager.perspectiveNames())
self.perspective_combo_box.setCurrentText(perspective_name)
self.save_perspectives()
def main(app_):
main_window = MainWindow()
main_window.show()
state = main_window.dock_manager.saveState()
# print('This is what the saved state looks like in XML:')
# print(str(state, 'utf-8'))
# print()
# main_window.dock_manager.restore_state(state)
return main_window
if __name__ == '__main__':
# logging.basicConfig(level='DEBUG')
QCoreApplication.setAttribute(Qt.AA_UseHighDpiPixmaps)
QGuiApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
app = QtWidgets.QApplication([])
window = main(app)
window.show()
app.exec_()

View File

@@ -73,6 +73,8 @@
#include "DockAreaTitleBar.h" #include "DockAreaTitleBar.h"
#include "DockAreaTabBar.h" #include "DockAreaTabBar.h"
#include "FloatingDockContainer.h" #include "FloatingDockContainer.h"
#include "DockComponentsFactory.h"
//============================================================================ //============================================================================
@@ -142,6 +144,25 @@ static QIcon svgIcon(const QString& File)
} }
//============================================================================
class CCustomComponentsFactory : public ads::CDockComponentsFactory
{
public:
using Super = ads::CDockComponentsFactory;
ads::CDockAreaTitleBar* createDockAreaTitleBar(ads::CDockAreaWidget* DockArea) const override
{
auto TitleBar = new ads::CDockAreaTitleBar(DockArea);
auto CustomButton = new QToolButton(DockArea);
CustomButton->setToolTip(QObject::tr("Help"));
CustomButton->setIcon(svgIcon(":/adsdemo/images/help_outline.svg"));
CustomButton->setAutoRaise(true);
int Index = TitleBar->indexOf(TitleBar->button(ads::TitleBarButtonTabsMenu));
TitleBar->insertWidget(Index + 1, CustomButton);
return TitleBar;
}
};
//============================================================================ //============================================================================
static ads::CDockWidget* createCalendarDockWidget(QMenu* ViewMenu) static ads::CDockWidget* createCalendarDockWidget(QMenu* ViewMenu)
{ {
@@ -202,28 +223,46 @@ static ads::CDockWidget* createEditorWidget(QMenu* ViewMenu)
return DockWidget; return DockWidget;
} }
//===========================================================================
/**
* Custom QTableWidget with a minimum size hint to test CDockWidget
* setMinimumSizeHintMode() function of CDockWidget
*/
class CMinSizeTableWidget : public QTableWidget
{
public:
using QTableWidget::QTableWidget;
virtual QSize minimumSizeHint() const override
{
return QSize(300, 100);
}
};
//============================================================================ //============================================================================
static ads::CDockWidget* createTableWidget(QMenu* ViewMenu) static ads::CDockWidget* createTableWidget(QMenu* ViewMenu)
{ {
static int TableCount = 0; static int TableCount = 0;
QTableWidget* w = new QTableWidget(); auto w = new CMinSizeTableWidget();
ads::CDockWidget* DockWidget = new ads::CDockWidget(QString("Table %1").arg(TableCount++)); ads::CDockWidget* DockWidget = new ads::CDockWidget(QString("Table %1").arg(TableCount++));
static int colCount = 5; static int colCount = 5;
static int rowCount = 30; static int rowCount = 30;
w->setColumnCount(colCount); w->setColumnCount(colCount);
w->setRowCount(rowCount); w->setRowCount(rowCount);
for (int col = 0; col < colCount; ++col) for (int col = 0; col < colCount; ++col)
{ {
w->setHorizontalHeaderItem(col, new QTableWidgetItem(QString("Col %1").arg(col+1))); w->setHorizontalHeaderItem(col, new QTableWidgetItem(QString("Col %1").arg(col+1)));
for (int row = 0; row < rowCount; ++row) for (int row = 0; row < rowCount; ++row)
{ {
w->setItem(row, col, new QTableWidgetItem(QString("T %1-%2").arg(row + 1).arg(col+1))); w->setItem(row, col, new QTableWidgetItem(QString("T %1-%2").arg(row + 1).arg(col+1)));
} }
} }
DockWidget->setWidget(w); DockWidget->setWidget(w);
DockWidget->setIcon(svgIcon(":/adsdemo/images/grid_on.svg")); DockWidget->setIcon(svgIcon(":/adsdemo/images/grid_on.svg"));
ViewMenu->addAction(DockWidget->toggleViewAction()); DockWidget->setMinimumSizeHintMode(ads::CDockWidget::MinimumSizeHintFromContent);
return DockWidget; ViewMenu->addAction(DockWidget->toggleViewAction());
return DockWidget;
} }
@@ -309,6 +348,8 @@ void MainWindowPrivate::createContent()
auto ToolBar = FileSystemWidget->createDefaultToolBar(); auto ToolBar = FileSystemWidget->createDefaultToolBar();
ToolBar->addAction(ui.actionSaveState); ToolBar->addAction(ui.actionSaveState);
ToolBar->addAction(ui.actionRestoreState); ToolBar->addAction(ui.actionRestoreState);
FileSystemWidget->setFeature(ads::CDockWidget::DockWidgetFloatable, false);
appendFeaturStringToWindowTitle(FileSystemWidget);
DockManager->addDockWidget(ads::BottomDockWidgetArea, FileSystemWidget); DockManager->addDockWidget(ads::BottomDockWidgetArea, FileSystemWidget);
FileSystemWidget = createFileSystemTreeDockWidget(ViewMenu); FileSystemWidget = createFileSystemTreeDockWidget(ViewMenu);
@@ -318,9 +359,13 @@ void MainWindowPrivate::createContent()
FileSystemWidget->setFeature(ads::CDockWidget::DockWidgetMovable, false); FileSystemWidget->setFeature(ads::CDockWidget::DockWidgetMovable, false);
FileSystemWidget->setFeature(ads::CDockWidget::DockWidgetFloatable, false); FileSystemWidget->setFeature(ads::CDockWidget::DockWidgetFloatable, false);
appendFeaturStringToWindowTitle(FileSystemWidget); appendFeaturStringToWindowTitle(FileSystemWidget);
auto TopDockArea = DockManager->addDockWidget(ads::TopDockWidgetArea, FileSystemWidget);
// We create a calender widget and clear all flags to prevent the dock area // Test custom factory - we inject a help button into the title bar
ads::CDockComponentsFactory::setFactory(new CCustomComponentsFactory());
auto TopDockArea = DockManager->addDockWidget(ads::TopDockWidgetArea, FileSystemWidget);
ads::CDockComponentsFactory::resetDefaultFactory();
// We create a calendar widget and clear all flags to prevent the dock area
// from closing // from closing
DockWidget = createCalendarDockWidget(ViewMenu); DockWidget = createCalendarDockWidget(ViewMenu);
DockWidget->setFeature(ads::CDockWidget::DockWidgetClosable, false); DockWidget->setFeature(ads::CDockWidget::DockWidgetClosable, false);
@@ -458,9 +503,9 @@ CMainWindow::CMainWindow(QWidget *parent) :
// a QToolButton instead of a QPushButton // a QToolButton instead of a QPushButton
// CDockManager::setConfigFlags(CDockManager::configFlags() | CDockManager::TabCloseButtonIsToolButton); // CDockManager::setConfigFlags(CDockManager::configFlags() | CDockManager::TabCloseButtonIsToolButton);
// comment the following line if you want to use opaque undocking and // uncomment the following line if you want to use opaque undocking and
// opaque splitter resizing // opaque splitter resizing
CDockManager::setConfigFlags(CDockManager::DefaultNonOpaqueConfig); // CDockManager::setConfigFlags(CDockManager::DefaultOpaqueConfig);
// uncomment the following line if you want a fixed tab width that does // uncomment the following line if you want a fixed tab width that does
// not change if the visibility of the close button changes // not change if the visibility of the close button changes
@@ -481,6 +526,12 @@ CMainWindow::CMainWindow(QWidget *parent) :
// uncomment the following line if you want to show tabs menu button on DockArea's title bar only when there are more than one tab and at least of them has elided title // uncomment the following line if you want to show tabs menu button on DockArea's title bar only when there are more than one tab and at least of them has elided title
//CDockManager::setConfigFlag(CDockManager::DockAreaDynamicTabsMenuButtonVisibility, true); //CDockManager::setConfigFlag(CDockManager::DockAreaDynamicTabsMenuButtonVisibility, true);
// uncomment the following line if you want floating container to always show application title instead of active dock widget's title
//CDockManager::setConfigFlag(CDockManager::FloatingContainerHasWidgetTitle, false);
// uncomment the following line if you want floating container to show active dock widget's icon instead of always showing application icon
//CDockManager::setConfigFlag(CDockManager::FloatingContainerHasWidgetIcon, true);
// Now create the dock manager and its content // Now create the dock manager and its content
d->DockManager = new CDockManager(this); d->DockManager = new CDockManager(this);

505
demo/demo.py Normal file
View File

@@ -0,0 +1,505 @@
import datetime
import logging
import os
import sys
from PyQt5 import uic
from PyQt5.QtCore import (QCoreApplication, QDir, Qt, QSettings, QSignalBlocker,
QRect, QPoint, qDebug, qInstallMessageHandler,
QtDebugMsg, QtInfoMsg, QtWarningMsg,
QtCriticalMsg, QtFatalMsg)
from PyQt5.QtGui import (QGuiApplication, QIcon, QCloseEvent)
from PyQt5.QtWidgets import (QCalendarWidget, QFileSystemModel, QFrame, QLabel,
QMenu, QTreeView, QAction, QWidgetAction,
QComboBox, QStyle, QSizePolicy, QInputDialog, QMenu,
QToolButton, QWidget, QPlainTextEdit,
QTableWidget, QTableWidgetItem, QApplication,
QMessageBox)
try:
from PyQt5.QAxContainer import QAxWidget
except ImportError:
ACTIVEX_AVAILABLE = False
else:
ACTIVEX_AVAILABLE = True
from PyQtAds import QtAds
import rc # pyrcc5 demo.qrc -o rc.py
UI_FILE = os.path.join(os.path.dirname(__file__), 'mainwindow.ui')
MainWindowUI, MainWindowBase = uic.loadUiType(UI_FILE)
class _State:
label_count = 0
calendar_count = 0
file_system_count = 0
editor_count = 0
table_count = 0
activex_count = 0
def features_string(dock_widget: QtAds.CDockWidget) -> str:
'''Function returns a features string with closable (c), movable (m) and floatable (f)
features. i.e. The following string is for a not closable but movable and floatable
widget: c- m+ f+'''
f = dock_widget.features()
closable = f & QtAds.CDockWidget.DockWidgetClosable
movable = f & QtAds.CDockWidget.DockWidgetMovable
floatable = f &QtAds.CDockWidget.DockWidgetFloatable
return "c{} m{} f{}".format("+" if closable else "-",
"+" if movable else "-",
"+" if floatable else "-")
def append_feature_string_to_window_title(dock_widget: QtAds.CDockWidget):
'''Appends the string returned by features_string() to the window title of
the given DockWidget'''
dock_widget.setWindowTitle(dock_widget.windowTitle() + " ({})".format(features_string(dock_widget)))
def svg_icon(filename: str):
'''Helper function to create an SVG icon'''
# This is a workaround, because because in item views SVG icons are not
# properly scaled an look blurry or pixelate
icon = QIcon(filename)
icon.addPixmap(icon.pixmap(92))
return icon
def create_long_text_label_dock_widget(view_menu: QMenu) -> QtAds.CDockWidget:
label = QLabel()
label.setWordWrap(True)
label.setAlignment(Qt.AlignTop | Qt.AlignLeft)
label.setText('''\
Label {} {} - Lorem ipsum dolor sit amet, consectetuer
adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum
sociis natoque penatibus et magnis dis parturient montes, nascetur
ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium
quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla
vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut,
imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis
pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi.
Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu,
consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra
quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet.
'''.format(_State.label_count, datetime.datetime.now().strftime("%H:%M:%S:%f")))
dock_widget = QtAds.CDockWidget("Label {}".format(_State.label_count))
_State.label_count += 1
dock_widget.setWidget(label)
view_menu.addAction(dock_widget.toggleViewAction())
return dock_widget
def create_calendar_dock_widget(view_menu: QMenu) -> QtAds.CDockWidget:
widget = QCalendarWidget()
dock_widget = QtAds.CDockWidget("Calendar {}".format(_State.calendar_count))
_State.calendar_count += 1
dock_widget.setWidget(widget)
dock_widget.setToggleViewActionMode(QtAds.CDockWidget.ActionModeShow)
dock_widget.setIcon(svg_icon(":/adsdemo/images/date_range.svg"))
view_menu.addAction(dock_widget.toggleViewAction())
return dock_widget
def create_file_system_tree_dock_widget(view_menu: QMenu) -> QtAds.CDockWidget:
widget = QTreeView()
widget.setFrameShape(QFrame.NoFrame)
m = QFileSystemModel(widget)
m.setRootPath(QDir.currentPath())
widget.setModel(m)
dock_widget = QtAds.CDockWidget("Filesystem {}".format(_State.file_system_count))
_State.file_system_count += 1
dock_widget.setWidget(widget)
view_menu.addAction(dock_widget.toggleViewAction())
return dock_widget
def create_editor_widget(view_menu: QMenu) -> QtAds.CDockWidget:
widget = QPlainTextEdit()
widget.setPlaceholderText("This is an editor. If you close the editor, it will be "
"deleted. Enter your text here.")
widget.setStyleSheet("border: none")
dock_widget = QtAds.CDockWidget("Editor {}".format(_State.editor_count))
_State.editor_count += 1
dock_widget.setWidget(widget)
dock_widget.setIcon(svg_icon(":/adsdemo/images/edit.svg"))
dock_widget.setFeature(QtAds.CDockWidget.CustomCloseHandling, True)
view_menu.addAction(dock_widget.toggleViewAction())
options_menu = QMenu(dock_widget)
options_menu.setTitle("Options")
options_menu.setToolTip(options_menu.title())
options_menu.setIcon(svg_icon(":/adsdemo/images/custom-menu-button.svg"))
menu_action = options_menu.menuAction()
# The object name of the action will be set for the QToolButton that
# is created in the dock area title bar. You can use this name for CSS
# styling
menu_action.setObjectName("options_menu")
dock_widget.setTitleBarActions([options_menu.menuAction()])
a = options_menu.addAction("Clear Editor")
a.triggered.connect(widget.clear)
return dock_widget
def create_table_widget(view_menu: QMenu) -> QtAds.CDockWidget:
widget = QTableWidget()
dock_widget = QtAds.CDockWidget("Table {}".format(_State.table_count))
_State.table_count += 1
COLCOUNT = 5
ROWCOUNT = 30
widget.setColumnCount(COLCOUNT)
widget.setRowCount(ROWCOUNT)
for col in range(ROWCOUNT):
widget.setHorizontalHeaderItem(col, QTableWidgetItem("Col {}".format(col+1)))
for row in range(ROWCOUNT):
widget.setItem(row, col, QTableWidgetItem("T {:}-{:}".format(row+1, col+1)))
dock_widget.setWidget(widget)
dock_widget.setIcon(svg_icon(":/adsdemo/images/grid_on.svg"))
view_menu.addAction(dock_widget.toggleViewAction())
return dock_widget
if ACTIVEX_AVAILABLE:
def create_activex_widget(view_menu: QMenu, parent: QWidget = None) -> QtAds.CDockWidget:
widget = QAxWidget("{6bf52a52-394a-11d3-b153-00c04f79faa6}", parent)
dock_widget = QtAds.CDockWidget("Active X {}".format(_State.activex_count))
_State.activex_count += 1
dock_widget.setWidget(widget)
view_menu.addAction(dock_widget.toggleViewAction())
return dock_widget
class CustomComponentsFactory(QtAds.CDockComponentsFactory):
def createDockAreaTitleBar(self, dock_area: QtAds.CDockAreaWidget) -> QtAds.CDockAreaTitleBar:
title_bar = QtAds.CDockAreaTitleBar(dock_area)
custom_button = QToolButton(dock_area)
custom_button.setToolTip("Help")
custom_button.setIcon(svg_icon(":/adsdemo/images/help_outline.svg"))
custom_button.setAutoRaise(True)
index = title_bar.indexOf(title_bar.button(QtAds.TitleBarButtonTabsMenu))
title_bar.insertWidget(index + 1, custom_button)
return title_bar
class MainWindow(MainWindowUI, MainWindowBase):
save_perspective_action: QAction
perspective_list_action: QWidgetAction
perspective_combo_box: QComboBox
dock_manager: QtAds.CDockManager
def __init__(self, parent=None):
super().__init__(parent)
self.save_perspective_action = None
self.perspective_list_action = None
self.perspective_combo_box = None
self.dock_manager = None
self.setupUi(self)
self.create_actions()
# uncomment the following line if the tab close button should be
# a QToolButton instead of a QPushButton
# QtAds.CDockManager.setConfigFlags(QtAds.CDockManager.configFlags() | QtAds.CDockManager.TabCloseButtonIsToolButton)
# uncomment the following line if you want a fixed tab width that does
# not change if the visibility of the close button changes
# QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.RetainTabSizeWhenCloseButtonHidden, True)
# uncomment the follwing line if you want to use non opaque undocking and splitter
# movements
# QtAds.CDockManager.setConfigFlags(QtAds.CDockManager.DefaultNonOpaqueConfig)
# Now create the dock manager and its content
self.dock_manager = QtAds.CDockManager(self)
# Uncomment the following line to have the old style where the dock
# area close button closes the active tab
# QtAds.CDockManager.setConfigFlags(QtAds.CDockManager.DockAreaHasCloseButton
# | QtAds.CDockManager.DockAreaCloseButtonClosesTab)
self.perspective_combo_box.activated[str].connect(self.dock_manager.openPerspective)
self.create_content()
# Default window geometry - center on screen
self.resize(1280, 720)
self.setGeometry(QStyle.alignedRect(
Qt.LeftToRight, Qt.AlignCenter, self.frameSize(),
QGuiApplication.primaryScreen().availableGeometry()))
# self.restore_state()
self.restore_perspectives()
def create_content(self):
# Test container docking
view_menu = self.menuView
dock_widget = create_calendar_dock_widget(view_menu)
dock_widget.setFeature(QtAds.CDockWidget.DockWidgetClosable, False)
dock_widget.setFeature(QtAds.CDockWidget.DockWidgetMovable, False)
dock_widget.setFeature(QtAds.CDockWidget.DockWidgetFloatable, False)
special_dock_area = self.dock_manager.addDockWidget(QtAds.LeftDockWidgetArea, dock_widget)
# For this Special Dock Area we want to avoid dropping on the center of it (i.e. we don't want this widget to be ever tabbified):
special_dock_area.setAllowedAreas(QtAds.OuterDockAreas)
# special_dock_area.setAllowedAreas(QtAds.LeftDockWidgetArea | QtAds.RightDockWidgetArea) # just for testing
self.dock_manager.addDockWidget(QtAds.LeftDockWidgetArea, create_long_text_label_dock_widget(view_menu))
file_system_widget = create_file_system_tree_dock_widget(view_menu)
tool_bar = file_system_widget.createDefaultToolBar()
tool_bar.addAction(self.actionSaveState)
tool_bar.addAction(self.actionRestoreState)
file_system_widget.setFeature(QtAds.CDockWidget.DockWidgetFloatable, False)
append_feature_string_to_window_title(file_system_widget)
self.dock_manager.addDockWidget(QtAds.BottomDockWidgetArea, file_system_widget)
file_system_widget = create_file_system_tree_dock_widget(view_menu)
tool_bar = file_system_widget.createDefaultToolBar()
tool_bar.addAction(self.actionSaveState)
tool_bar.addAction(self.actionRestoreState)
file_system_widget.setFeature(QtAds.CDockWidget.DockWidgetMovable, False)
file_system_widget.setFeature(QtAds.CDockWidget.DockWidgetFloatable, False)
append_feature_string_to_window_title(file_system_widget)
# Test custom factory - we inject a help button into the title bar
self.factory = CustomComponentsFactory()
QtAds.CDockComponentsFactory.setFactory(self.factory)
top_dock_area = self.dock_manager.addDockWidget(QtAds.TopDockWidgetArea, file_system_widget)
QtAds.CDockComponentsFactory.resetDefaultFactory()
# We create a calendar widget and clear all flags to prevent the dock area
# from closing
dock_widget = create_calendar_dock_widget(view_menu)
dock_widget.setFeature(QtAds.CDockWidget.DockWidgetClosable, False)
dock_widget.setFeature(QtAds.CDockWidget.DockWidgetMovable, False)
dock_widget.setFeature(QtAds.CDockWidget.DockWidgetFloatable, False)
dock_widget.setTabToolTip("Tab ToolTip\nHodie est dies magna")
dock_area = self.dock_manager.addDockWidget(QtAds.CenterDockWidgetArea, dock_widget, top_dock_area)
# Now we add a custom button to the dock area title bar that will create
# new editor widgets when clicked
custom_button = QToolButton(dock_area)
custom_button.setToolTip("Create Editor")
custom_button.setIcon(svg_icon(":/adsdemo/images/plus.svg"))
custom_button.setAutoRaise(True)
title_bar = dock_area.titleBar()
index = title_bar.indexOf(title_bar.tabBar())
title_bar.insertWidget(index + 1, custom_button)
def on_button_clicked():
dock_widget = create_editor_widget(self.menuView)
dock_widget.setFeature(QtAds.CDockWidget.DockWidgetDeleteOnClose, True)
self.dock_manager.addDockWidgetTabToArea(dock_widget, dock_area)
dock_widget.closeRequested.connect(self.on_editor_close_requested)
custom_button.clicked.connect(on_button_clicked)
# Test dock area docking
right_dock_area = self.dock_manager.addDockWidget(
QtAds.RightDockWidgetArea,
create_long_text_label_dock_widget(view_menu), top_dock_area)
self.dock_manager.addDockWidget(
QtAds.TopDockWidgetArea,
create_long_text_label_dock_widget(view_menu), right_dock_area)
bottom_dock_area = self.dock_manager.addDockWidget(
QtAds.BottomDockWidgetArea,
create_long_text_label_dock_widget(view_menu), right_dock_area)
self.dock_manager.addDockWidget(
QtAds.CenterDockWidgetArea,
create_long_text_label_dock_widget(view_menu), right_dock_area)
self.dock_manager.addDockWidget(
QtAds.CenterDockWidgetArea,
create_long_text_label_dock_widget(view_menu), bottom_dock_area)
action = self.menuView.addAction("Set {} floating".format(dock_widget.windowTitle()))
action.triggered.connect(dock_widget.setFloating)
if ACTIVEX_AVAILABLE:
flags = self.dock_manager.configFlags()
if flags & QtAds.CDockManager.OpaqueUndocking:
self.dock_manager.addDockWidget(QtAds.CenterDockWidgetArea,
create_activex_widget(view_menu), right_dock_area)
for dock_widget in self.dock_manager.dockWidgetsMap().values():
dock_widget.viewToggled.connect(self.on_view_toggled)
dock_widget.visibilityChanged.connect(self.on_view_visibility_changed)
def create_actions(self):
self.toolBar.addAction(self.actionSaveState)
self.toolBar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
self.actionSaveState.setIcon(svg_icon(":/adsdemo/images/save.svg"))
self.toolBar.addAction(self.actionRestoreState)
self.actionRestoreState.setIcon(svg_icon(":/adsdemo/images/restore.svg"))
self.save_perspective_action = QAction("Create Perspective", self)
self.save_perspective_action.setIcon(svg_icon(":/adsdemo/images/picture_in_picture.svg"))
self.save_perspective_action.triggered.connect(self.save_perspective)
self.perspective_list_action = QWidgetAction(self)
self.perspective_combo_box = QComboBox(self)
self.perspective_combo_box.setSizeAdjustPolicy(QComboBox.AdjustToContents)
self.perspective_combo_box.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
self.perspective_list_action.setDefaultWidget(self.perspective_combo_box)
self.toolBar.addSeparator()
self.toolBar.addAction(self.perspective_list_action)
self.toolBar.addAction(self.save_perspective_action)
a = self.toolBar.addAction("Create Editor")
a.setToolTip("Creates floating dynamic dockable editor windows that are deleted on close")
a.setIcon(svg_icon(":/adsdemo/images/note_add.svg"))
a.triggered.connect(self.create_editor)
a = self.toolBar.addAction("Create Table")
a.setToolTip("Creates floating dynamic dockable table with millions of entries")
a.setIcon(svg_icon(":/adsdemo/images/grid_on.svg"))
a.triggered.connect(self.create_table)
def closeEvent(self, event: QCloseEvent):
self.save_state()
super().closeEvent(event)
def on_action_save_state_triggered(state: bool):
qDebug("MainWindow::on_action_save_state_triggered")
self.save_state()
def on_action_restore_state_triggered(state: bool):
qDebug("MainWindow::on_action_restore_state_triggered")
self.restore_state()
def save_perspective(self):
perspective_name, ok = QInputDialog.getText(self, "Save perspective",
"Enter unique name:")
if ok and perspective_name:
self.dock_manager.addPerspective(perspective_name)
_ = QSignalBlocker(self.perspective_combo_box)
self.perspective_combo_box.clear()
self.perspective_combo_box.addItems(self.dock_manager.perspectiveNames())
self.perspective_combo_box.setCurrentText(perspective_name)
self.save_perspectives()
def on_view_toggled(self, open: bool):
dock_widget = self.sender()
if dock_widget is None:
return
qDebug("{} view_toggled({})".format(dock_widget.objectName(), open))
def on_view_visibility_changed(self, visible: bool):
dock_widget = self.sender()
if dock_widget is None:
return
qDebug("{} visibility_changed({})".format(dock_widget.objectName(), visible))
def create_editor(self):
dock_widget = create_editor_widget(self.menuView)
dock_widget.setFeature(QtAds.CDockWidget.DockWidgetDeleteOnClose, True)
floating_widget = self.dock_manager.addDockWidgetFloating(dock_widget)
floating_widget.move(QPoint(20, 20))
dock_widget.closeRequested.connect(self.on_editor_close_requested)
def on_editor_close_requested(self):
dock_widget = self.sender()
result = QMessageBox.question(self, "Close Editor",
"Editor {} contains unsaved changes? Would you like to close it?".format(dock_widget.windowTitle()))
if result == QMessageBox.Yes:
dock_widget.closeDockWidget()
def create_table(self):
dock_widget = create_table_widget(self.menuView)
dock_widget.setFeature(QtAds.CDockWidget.DockWidgetDeleteOnClose, True)
floating_widget = self.dock_manager.addDockWidgetFloating(dock_widget)
floating_widget.move(QPoint(40, 40))
def save_state(self):
'''
Saves the dock manager state and the main window geometry
'''
settings = QSettings("Settings.ini", QSettings.IniFormat)
settings.setValue("mainWindow/Geometry", self.saveGeometry())
settings.setValue("mainWindow/State", self.saveState())
settings.setValue("mainWindow/DockingState", self.dock_manager.saveState())
def restore_state(self):
'''
Restores the dock manager state
'''
settings = QSettings("Settings.ini", QSettings.IniFormat)
geom = settings.value("mainWindow/Geometry")
if geom is not None:
self.restoreGeometry(geom)
state = settings.value("mainWindow/State")
if state is not None:
self.restoreState(state)
state = settings.value("mainWindow/DockingState")
if state is not None:
self.dock_manager.restore_state(state)
def save_perspectives(self):
'''
Save the list of perspectives
'''
settings = QSettings("Settings.ini", QSettings.IniFormat)
self.dock_manager.savePerspectives(settings)
def restore_perspectives(self):
'''
Restore the perspective listo of the dock manager
'''
settings = QSettings("Settings.ini", QSettings.IniFormat)
self.dock_manager.loadPerspectives(settings)
self.perspective_combo_box.clear()
self.perspective_combo_box.addItems(self.dock_manager.perspectiveNames())
def save_perspective(self):
perspective_name, ok = QInputDialog.getText(self, 'Save perspective', 'Enter unique name:')
if ok and perspective_name:
self.dock_manager.addPerspective(perspective_name)
_ = QSignalBlocker(self.perspective_combo_box)
self.perspective_combo_box.clear()
self.perspective_combo_box.addItems(self.dock_manager.perspectiveNames())
self.perspective_combo_box.setCurrentText(perspective_name)
self.save_perspectives()
def my_message_output(type, context, msg):
if type == QtDebugMsg:
print("Debug: {} ({}:{}, {})".format(msg, context.file, context.line, context.function))
elif type == QtInfoMsg:
print("Info: {} ({}:{}, {})".format(msg, context.file, context.line, context.function))
elif type == QtWarningMsg:
print("Warning: {} ({}:{}, {})".format(msg, context.file, context.line, context.function))
elif type == QtCriticalMsg:
print("Critical: {} ({}:{}, {})".format(msg, context.file, context.line, context.function))
elif type == QtFatalMsg:
print("Fatal: {} ({}:{}, {})".format(msg, context.file, context.line, context.function))
if __name__ == '__main__':
QCoreApplication.setAttribute(Qt.AA_UseHighDpiPixmaps)
QGuiApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
app = QApplication(sys.argv)
app.setQuitOnLastWindowClosed(True)
with open(os.path.join(os.path.dirname(__file__), "app.css"), "r") as style_sheet_file:
app.setStyleSheet(style_sheet_file.read())
qInstallMessageHandler(my_message_output)
qDebug("Message handler test")
mw = MainWindow()
mw.show()
app.exec_()

View File

@@ -12,5 +12,6 @@
<file>images/custom-menu-button.svg</file> <file>images/custom-menu-button.svg</file>
<file>app.css</file> <file>app.css</file>
<file>images/plus.svg</file> <file>images/plus.svg</file>
<file>images/help_outline.svg</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@@ -0,0 +1,6 @@
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0,0,1024,1024">
<desc>help_outline icon - Licensed under Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0) - Created with Iconfu.com - Derivative work of Material icons (Copyright Google Inc.)</desc>
<g fill="#03b8e5" fill-rule="nonzero" style="mix-blend-mode: normal">
<path d="M938.67,512c0,235.52 -191.15,426.67 -426.67,426.67c-235.52,0 -426.67,-191.15 -426.67,-426.67c0,-235.52 191.15,-426.67 426.67,-426.67c235.52,0 426.67,191.15 426.67,426.67zM853.33,512c0,-188.16 -153.17,-341.33 -341.33,-341.33c-188.16,0 -341.33,153.17 -341.33,341.33c0,188.16 153.17,341.33 341.33,341.33c188.16,0 341.33,-153.17 341.33,-341.33zM682.67,426.67c0,106.67 -128,117.33 -128,213.33h-85.34c0,-138.67 128,-128 128,-213.33c0,-46.93 -38.4,-85.34 -85.33,-85.34c-46.93,0 -85.33,38.41 -85.33,85.34h-85.34c0,-94.29 76.38,-170.67 170.67,-170.67c94.29,0 170.67,76.38 170.67,170.67zM469.33,682.67h85.34v85.33h-85.34z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
doc/qmix_elements.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 539 KiB

BIN
doc/qt_design_studio.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

52
example/example.py Normal file
View File

@@ -0,0 +1,52 @@
import datetime
import logging
import os
import sys
from PyQt5 import uic
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QLabel
from PyQtAds import QtAds
UI_FILE = os.path.join(os.path.dirname(__file__), 'MainWindow.ui')
MainWindowUI, MainWindowBase = uic.loadUiType(UI_FILE)
class MainWindow(MainWindowUI, MainWindowBase):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self)
# Create the dock manager. Because the parent parameter is a QMainWindow
# the dock manager registers itself as the central widget.
self.dock_manager = QtAds.CDockManager(self)
# Create example content label - this can be any application specific
# widget
l = QLabel()
l.setWordWrap(True)
l.setAlignment(Qt.AlignTop | Qt.AlignLeft);
l.setText("Lorem ipsum dolor sit amet, consectetuer adipiscing elit. ")
# Create a dock widget with the title Label 1 and set the created label
# as the dock widget content
dock_widget = QtAds.CDockWidget("Label 1")
dock_widget.setWidget(l)
# Add the toggleViewAction of the dock widget to the menu to give
# the user the possibility to show the dock widget if it has been closed
self.menuView.addAction(dock_widget.toggleViewAction())
# Add the dock widget to the top dock widget area
self.dock_manager.addDockWidget(QtAds.TopDockWidgetArea, dock_widget)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec_()

View File

@@ -1,41 +0,0 @@
{% set data = load_setup_py_data() %}
package:
name: pyqtads
version: {{ data.get('version') }}
source:
path: ../
build:
number: 0
script: python setup.py install --single-version-externally-managed --record=record.txt --conda-recipe
requirements:
build:
- python
- setuptools
- pyqt>=5.9
- sip>=4.19
run:
- python
- pyqt>=5.9
- sip>=4.19
- pywin32 [win]
test:
imports:
- PyQtAds
about:
home: {{ data.get('url') }}
license: {{ data.get('license') }}
license_family: LGPL
license_file: 'LICENSE.md'
summary: {{ data.get('description') }}
description: {{ data.get('description') }}
doc_url: ''
dev_url: {{ data.get('url') }}
extra:
recipe-maintainers: 'nicolas.elie@cnrs.fr'

View File

@@ -18,7 +18,7 @@ from PyQt5.pyrcc_main import processResourceFile
MODULE_NAME = "ads" MODULE_NAME = "ads"
SRC_PATH = "PyQtAds" SRC_PATH = "PyQtAds"
REQUIRE_PYQT = True REQUIRE_PYQT = True
if "--conda-recipe" in sys.argv: if "--conda-recipe" in sys.argv:
REQUIRE_PYQT = False REQUIRE_PYQT = False
@@ -40,7 +40,7 @@ class HostPythonConfiguration(object):
else: else:
self.data_dir = sys.prefix + '/share' self.data_dir = sys.prefix + '/share'
self.lib_dir = sys.prefix + '/lib' self.lib_dir = sys.prefix + '/lib'
class TargetQtConfiguration(object): class TargetQtConfiguration(object):
def __init__(self, qmake): def __init__(self, qmake):
@@ -63,12 +63,12 @@ class TargetQtConfiguration(object):
setattr(self, name, value) setattr(self, name, value)
pipe.close() pipe.close()
class build_ext(sipdistutils.build_ext): class build_ext(sipdistutils.build_ext):
description = "Builds the " + MODULE_NAME + " module." description = "Builds the " + MODULE_NAME + " module."
user_options = sipdistutils.build_ext.user_options + [ user_options = sipdistutils.build_ext.user_options + [
('qmake-bin=', None, "Path to qmake binary"), ('qmake-bin=', None, "Path to qmake binary"),
('sip-bin=', None, "Path to sip binary"), ('sip-bin=', None, "Path to sip binary"),
@@ -78,7 +78,7 @@ class build_ext(sipdistutils.build_ext):
('sip-dir=', None, "Path to module's SIP files"), ('sip-dir=', None, "Path to module's SIP files"),
('inc-dir=', None, "Path to module's include files") ('inc-dir=', None, "Path to module's include files")
] ]
def initialize_options (self): def initialize_options (self):
super().initialize_options() super().initialize_options()
self.qmake_bin = 'qmake' self.qmake_bin = 'qmake'
@@ -92,16 +92,16 @@ class build_ext(sipdistutils.build_ext):
self.inc_dir = None self.inc_dir = None
self.pyconfig = HostPythonConfiguration() self.pyconfig = HostPythonConfiguration()
self.qtconfig = TargetQtConfiguration(self.qmake_bin) self.qtconfig = TargetQtConfiguration(self.qmake_bin)
self.config = sipconfig.Configuration() self.config = sipconfig.Configuration()
self.config.default_mod_dir = ("/usr/local/lib/python%i.%i/dist-packages" % self.config.default_mod_dir = ("/usr/local/lib/python%i.%i/dist-packages" %
(sys.version_info.major, sys.version_info.minor)) (sys.version_info.major, sys.version_info.minor))
def finalize_options (self): def finalize_options (self):
super().finalize_options() super().finalize_options()
if not self.qt_include_dir: if not self.qt_include_dir:
self.qt_include_dir = self.qtconfig.QT_INSTALL_HEADERS self.qt_include_dir = self.qtconfig.QT_INSTALL_HEADERS
if not self.qt_libinfix: if not self.qt_libinfix:
try: try:
with open(os.path.join(self.qtconfig.QT_INSTALL_PREFIX, 'mkspecs', 'qconfig.pri'), 'r') as f: with open(os.path.join(self.qtconfig.QT_INSTALL_PREFIX, 'mkspecs', 'qconfig.pri'), 'r') as f:
@@ -113,16 +113,16 @@ class build_ext(sipdistutils.build_ext):
if not self.pyqt_sip_dir: if not self.pyqt_sip_dir:
self.pyqt_sip_dir = os.path.join(self.pyconfig.data_dir, 'sip', 'PyQt5') self.pyqt_sip_dir = os.path.join(self.pyconfig.data_dir, 'sip', 'PyQt5')
if not self.pyqt_sip_flags: if not self.pyqt_sip_flags:
self.pyqt_sip_flags = PYQT_CONFIGURATION.get('sip_flags', '') self.pyqt_sip_flags = PYQT_CONFIGURATION.get('sip_flags', '')
if not self.sip_files_dir: if not self.sip_files_dir:
self.sip_files_dir = os.path.abspath(os.path.join(".", "sip")) self.sip_files_dir = os.path.abspath(os.path.join(".", "sip"))
if not self.sip_inc_dir: if not self.sip_inc_dir:
self.sip_inc_dir = self.pyconfig.venv_inc_dir self.sip_inc_dir = self.pyconfig.venv_inc_dir
if not self.inc_dir: if not self.inc_dir:
self.inc_dir = os.path.abspath(os.path.join(".", "src")) self.inc_dir = os.path.abspath(os.path.join(".", "src"))
@@ -138,12 +138,12 @@ class build_ext(sipdistutils.build_ext):
if not self.pyqt_sip_flags: if not self.pyqt_sip_flags:
raise SystemExit('Could not find PyQt SIP flags. ' raise SystemExit('Could not find PyQt SIP flags. '
'Please specify via --pyqt-sip-flags=') 'Please specify via --pyqt-sip-flags=')
def _find_sip(self): def _find_sip(self):
"""override _find_sip to allow for manually speficied sip path.""" """override _find_sip to allow for manually speficied sip path."""
return self.sip_bin or super()._find_sip() return self.sip_bin or super()._find_sip()
def _sip_compile(self, sip_bin, source, sbf): def _sip_compile(self, sip_bin, source, sbf):
cmd = [sip_bin] cmd = [sip_bin]
if hasattr(self, 'sip_opts'): if hasattr(self, 'sip_opts'):
cmd += self.sip_opts cmd += self.sip_opts
@@ -157,11 +157,11 @@ class build_ext(sipdistutils.build_ext):
"-c", self._sip_output_dir(), "-c", self._sip_output_dir(),
"-b", sbf, "-b", sbf,
"-w", "-o"] "-w", "-o"]
cmd += shlex.split(self.pyqt_sip_flags) # use same SIP flags as for PyQt5 cmd += shlex.split(self.pyqt_sip_flags) # use same SIP flags as for PyQt5
cmd.append(source) cmd.append(source)
self.spawn(cmd) self.spawn(cmd)
def swig_sources (self, sources, extension=None): def swig_sources (self, sources, extension=None):
if not self.extensions: if not self.extensions:
return return
@@ -179,7 +179,7 @@ class build_ext(sipdistutils.build_ext):
extension.libraries += ['Qt5Core' + self.qt_libinfix, extension.libraries += ['Qt5Core' + self.qt_libinfix,
'Qt5Gui' + self.qt_libinfix, 'Qt5Gui' + self.qt_libinfix,
'Qt5Widgets' + self.qt_libinfix] 'Qt5Widgets' + self.qt_libinfix]
if sys.platform == 'win32': if sys.platform == 'win32':
extension.library_dirs += [self.qtconfig.QT_INSTALL_LIBS, extension.library_dirs += [self.qtconfig.QT_INSTALL_LIBS,
self.inc_dir, self._sip_output_dir()] self.inc_dir, self._sip_output_dir()]
@@ -192,30 +192,46 @@ class build_ext(sipdistutils.build_ext):
extension.extra_compile_args += ['-std=c++11'] extension.extra_compile_args += ['-std=c++11']
return super().swig_sources(sources, extension) return super().swig_sources(sources, extension)
def build_extension(self, ext): def build_extension(self, ext):
cppsources = [source for source in ext.sources if source.endswith(".cpp")] cppsources = [source for source in ext.sources if source.endswith(".cpp")]
dir_util.mkpath(self.build_temp, dry_run=self.dry_run) dir_util.mkpath(self.build_temp, dry_run=self.dry_run)
def get_moc_args(out_file, source):
if sys.platform.startswith('linux'):
return ["moc", "-D", "Q_OS_LINUX=1", "-o", out_file, source]
if sys.platform.startswith('darwin'):
return ["moc", "-D", "Q_OS_MACOS=1", "-o", out_file, source]
if sys.platform.startswith('win'):
return ["moc", "-D", "Q_OS_WIN=1", "-o", out_file, source]
return ["moc", "-o", out_file, source]
# Run moc on all header files. # Run moc on all header files.
for source in cppsources: for source in cppsources:
# *.cpp -> *.moc
moc_file = os.path.basename(source).replace(".cpp", ".moc")
out_file = os.path.join(self.build_temp, moc_file)
if newer(source, out_file) or self.force:
spawn.spawn(get_moc_args(out_file, source), dry_run=self.dry_run)
header = source.replace(".cpp", ".h") header = source.replace(".cpp", ".h")
if os.path.exists(header): if os.path.exists(header):
# *.h -> moc_*.cpp
moc_file = "moc_" + os.path.basename(header).replace(".h", ".cpp") moc_file = "moc_" + os.path.basename(header).replace(".h", ".cpp")
out_file = os.path.join(self.build_temp, moc_file) out_file = os.path.join(self.build_temp, moc_file)
if newer(header, out_file) or self.force: if newer(header, out_file) or self.force:
call_arg = ["moc", "-o", out_file, header] spawn.spawn(get_moc_args(out_file, header), dry_run=self.dry_run)
spawn.spawn(call_arg, dry_run=self.dry_run)
if os.path.getsize(out_file) > 0: if os.path.getsize(out_file) > 0:
ext.sources.append(out_file) ext.sources.append(out_file)
# Add the temp build directory to include path, for compiler to find # Add the temp build directory to include path, for compiler to find
# the created .moc files # the created .moc files
ext.include_dirs += [self._sip_output_dir()] ext.include_dirs += [self._sip_output_dir()]
sipdistutils.build_ext.build_extension(self, ext) sipdistutils.build_ext.build_extension(self, ext)
@@ -253,7 +269,7 @@ ext_modules = [Extension('PyQtAds.QtAds.ads', cpp_sources + sip_sources)]
install_requires = ["PyQt5"] install_requires = ["PyQt5"]
if sys.platform == 'win32': if sys.platform == 'win32':
install_requires.append("pywin32") install_requires.append("pywin32")
with open('README.md', 'r') as f: with open('README.md', 'r') as f:
LONG_DESCRIPTION = f.read() LONG_DESCRIPTION = f.read()

View File

@@ -1,82 +0,0 @@
import logging
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtCore import Qt
from PyQtAds import QtAds
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setup_ui()
self.dock_manager = QtAds.CDockManager(self)
self.dock_widgets = []
for label_text, area in (
('1 Top', QtAds.TopDockWidgetArea),
('2 Bottom', QtAds.BottomDockWidgetArea),
('3 Left', QtAds.LeftDockWidgetArea),
('4 Right', QtAds.RightDockWidgetArea),
):
# Create example content label - this can be any application specific
# widget
label = QtWidgets.QLabel()
label.setWordWrap(True)
label.setAlignment(Qt.AlignTop | Qt.AlignLeft)
label.setText(f"{label_text}: Lorem ipsum dolor sit amet, consectetuer adipiscing elit. ")
# Create a dock widget with the title Label 1 and set the created label
# as the dock widget content
dock_widget = QtAds.CDockWidget(label_text)
dock_widget.setWidget(label)
self.dock_widgets.append(dock_widget)
# Add the toggleViewAction of the dock widget to the menu to give
# the user the possibility to show the dock widget if it has been closed
self.menu_view.addAction(dock_widget.toggleViewAction())
# Add the dock widget to the top dock widget area
self.dock_manager.addDockWidget(area, dock_widget)
def setup_ui(self):
self.setWindowTitle("MainWindow")
self.setObjectName("MainWindow")
self.resize(400, 300)
self.central_widget = QtWidgets.QWidget(self)
self.central_widget.setObjectName("central_widget")
self.setCentralWidget(self.central_widget)
self.menu_bar = QtWidgets.QMenuBar(self)
self.menu_bar.setGeometry(QtCore.QRect(0, 0, 400, 21))
self.menu_bar.setObjectName("menuBar")
self.menu_view = QtWidgets.QMenu(self.menu_bar)
self.menu_view.setObjectName("menu_view")
self.menu_view.setTitle("View")
self.setMenuBar(self.menu_bar)
self.status_bar = QtWidgets.QStatusBar(self)
self.status_bar.setObjectName("statusBar")
self.setStatusBar(self.status_bar)
self.menu_bar.addAction(self.menu_view.menuAction())
def main(app):
main = MainWindow()
main.show()
state = main.dock_manager.saveState()
print('This is what the saved state looks like in XML:')
print(state)
print()
main.dock_manager.restoreState(state)
return main
if __name__ == '__main__':
logging.basicConfig(level='DEBUG')
app = QtWidgets.QApplication([])
window = main(app)
window.show()
print('shown')
app.exec_()

View File

@@ -13,14 +13,6 @@ class CDockAreaTabBar : QScrollArea
protected: protected:
virtual void wheelEvent(QWheelEvent* Event); virtual void wheelEvent(QWheelEvent* Event);
virtual void mousePressEvent(QMouseEvent* ev);
virtual void mouseReleaseEvent(QMouseEvent* ev);
virtual void mouseMoveEvent(QMouseEvent* ev);
virtual void mouseDoubleClickEvent(QMouseEvent *event);
void startFloating(const QPoint& Offset);
ads::IFloatingWidget* makeAreaFloating(const QPoint& Offset,
ads::eDragState DragState);
ads::eDragState dragState() const;
public: public:
CDockAreaTabBar(ads::CDockAreaWidget* parent /TransferThis/); CDockAreaTabBar(ads::CDockAreaWidget* parent /TransferThis/);
@@ -35,6 +27,7 @@ public:
bool isTabOpen(int Index) const; bool isTabOpen(int Index) const;
virtual QSize minimumSizeHint() const; virtual QSize minimumSizeHint() const;
virtual QSize sizeHint() const; virtual QSize sizeHint() const;
void elidedChanged(bool elided);
public slots: public slots:
void setCurrentIndex(int Index); void setCurrentIndex(int Index);
@@ -54,4 +47,4 @@ signals:
}; };
%End %End

View File

@@ -11,6 +11,13 @@ class CDockAreaTitleBar : QFrame
#include <DockAreaTitleBar.h> #include <DockAreaTitleBar.h>
%End %End
protected:
virtual void mousePressEvent(QMouseEvent* ev);
virtual void mouseReleaseEvent(QMouseEvent* ev);
virtual void mouseMoveEvent(QMouseEvent* ev);
virtual void mouseDoubleClickEvent(QMouseEvent *event);
virtual void contextMenuEvent(QContextMenuEvent *event);
public slots: public slots:
void markTabsMenuOutdated(); void markTabsMenuOutdated();
@@ -20,7 +27,10 @@ public:
virtual ~CDockAreaTitleBar(); virtual ~CDockAreaTitleBar();
ads::CDockAreaTabBar* tabBar() const; ads::CDockAreaTabBar* tabBar() const;
QAbstractButton* button(ads::TitleBarButton which) const; QAbstractButton* button(ads::TitleBarButton which) const;
void updateDockWidgetActionsButtons();
virtual void setVisible(bool Visible); virtual void setVisible(bool Visible);
void insertWidget(int index, QWidget *widget /Transfer/ );
int indexOf(QWidget *widget) const;
signals: signals:

View File

@@ -42,12 +42,16 @@ public:
int currentIndex() const; int currentIndex() const;
int indexOfFirstOpenDockWidget() const; int indexOfFirstOpenDockWidget() const;
ads::CDockWidget* currentDockWidget() const; ads::CDockWidget* currentDockWidget() const;
void setCurrentDockWidget(ads::CDockWidget* DockWidget /Transfer/); void setCurrentDockWidget(ads::CDockWidget* DockWidget);
void saveState(QXmlStreamWriter& Stream) const; void saveState(QXmlStreamWriter& Stream) const;
ads::CDockWidget::DockWidgetFeatures features() const; ads::CDockWidget::DockWidgetFeatures features(ads::eBitwiseOperator Mode = ads::BitwiseAnd) const;
QAbstractButton* titleBarButton(ads::TitleBarButton which) const; QAbstractButton* titleBarButton(ads::TitleBarButton which) const;
virtual void setVisible(bool Visible); virtual void setVisible(bool Visible);
void setAllowedAreas(DockWidgetAreas areas);
DockWidgetAreas allowedAreas() const;
CDockAreaTitleBar* titleBar() const;
public slots: public slots:
void setCurrentIndex(int index); void setCurrentIndex(int index);
void closeArea(); void closeArea();

View File

@@ -0,0 +1,26 @@
%If (Qt_5_0_0 -)
namespace ads
{
class CDockComponentsFactory
{
%TypeHeaderCode
#include <DockComponentsFactory.h>
%End
public:
virtual ~CDockComponentsFactory();
virtual CDockWidgetTab* createDockWidgetTab(CDockWidget* DockWidget /Transfer/ ) const;
virtual CDockAreaTabBar* createDockAreaTabBar(CDockAreaWidget* DockArea /Transfer/ ) const;
virtual CDockAreaTitleBar* createDockAreaTitleBar(CDockAreaWidget* DockArea /Transfer/ ) const;
static const CDockComponentsFactory* factory();
static void setFactory(CDockComponentsFactory* Factory);
static void resetDefaultFactory();
};
};
%End

View File

@@ -23,13 +23,12 @@ protected:
QSplitter* rootSplitter() const; QSplitter* rootSplitter() const;
void createRootSplitter(); void createRootSplitter();
void dropFloatingWidget(ads::CFloatingDockContainer* FloatingWidget, const QPoint& TargetPos); void dropFloatingWidget(ads::CFloatingDockContainer* FloatingWidget, const QPoint& TargetPos);
void dropWidget(QWidget* widget, const QPoint& TargetPos); void dropWidget(QWidget* Widget, DockWidgetArea DropArea, CDockAreaWidget* TargetAreaWidget);
void addDockArea(ads::CDockAreaWidget* DockAreaWidget /Transfer/, ads::DockWidgetArea area = ads::CenterDockWidgetArea); void addDockArea(ads::CDockAreaWidget* DockAreaWidget /Transfer/, ads::DockWidgetArea area = ads::CenterDockWidgetArea);
void removeDockArea(ads::CDockAreaWidget* area /Transfer/); void removeDockArea(ads::CDockAreaWidget* area /TransferBack/);
void saveState(QXmlStreamWriter& Stream) const; void saveState(QXmlStreamWriter& Stream) const;
bool restoreState(CDockingStateReader& Stream, bool Testing); bool restoreState(CDockingStateReader& Stream, bool Testing);
ads::CDockAreaWidget* lastAddedDockAreaWidget(ads::DockWidgetArea area) const; ads::CDockAreaWidget* lastAddedDockAreaWidget(ads::DockWidgetArea area) const;
bool hasTopLevelDockWidget() const;
ads::CDockWidget* topLevelDockWidget() const; ads::CDockWidget* topLevelDockWidget() const;
ads::CDockAreaWidget* topLevelDockArea() const; ads::CDockAreaWidget* topLevelDockArea() const;
QList<ads::CDockWidget*> dockWidgets() const; QList<ads::CDockWidget*> dockWidgets() const;
@@ -88,6 +87,7 @@ public:
* If all dock widgets in a dock area are closed, the dock area will be closed * If all dock widgets in a dock area are closed, the dock area will be closed
*/ */
QList<ads::CDockAreaWidget*> openedDockAreas() const; QList<ads::CDockAreaWidget*> openedDockAreas() const;
bool hasTopLevelDockWidget() const;
/** /**
* Returns the number of dock areas in this container * Returns the number of dock areas in this container

View File

@@ -134,6 +134,8 @@ protected:
ads::CDockOverlay* containerOverlay() const; ads::CDockOverlay* containerOverlay() const;
ads::CDockOverlay* dockAreaOverlay() const; ads::CDockOverlay* dockAreaOverlay() const;
virtual void showEvent(QShowEvent *event);
public: public:
enum eViewMenuInsertionOrder enum eViewMenuInsertionOrder
{ {
@@ -143,7 +145,7 @@ public:
enum eConfigFlag enum eConfigFlag
{ {
ActiveTabHasCloseButton, ActiveTabHasCloseButton,
DockAreaHasCloseButton, DockAreaHasCloseButton,
DockAreaCloseButtonClosesTab, DockAreaCloseButtonClosesTab,
OpaqueSplitterResize, OpaqueSplitterResize,
@@ -152,13 +154,22 @@ public:
TabCloseButtonIsToolButton, TabCloseButtonIsToolButton,
AllTabsHaveCloseButton, AllTabsHaveCloseButton,
RetainTabSizeWhenCloseButtonHidden, RetainTabSizeWhenCloseButtonHidden,
OpaqueUndocking, OpaqueUndocking,
DragPreviewIsDynamic, DragPreviewIsDynamic,
DragPreviewShowsContentPixmap, DragPreviewShowsContentPixmap,
DragPreviewHasWindowFrame, DragPreviewHasWindowFrame,
DefaultConfig, AlwaysShowTabs,
DefaultNonOpaqueConfig, DockAreaHasUndockButton,
NonOpaqueWithWindowFrame, DockAreaHasTabsMenuButton,
DockAreaHideDisabledButtons,
DockAreaDynamicTabsMenuButtonVisibility,
FloatingContainerHasWidgetTitle,
FloatingContainerHasWidgetIcon,
DefaultDockAreaButtons,
DefaultBaseConfig,
DefaultOpaqueConfig,
DefaultNonOpaqueConfig,
NonOpaqueWithWindowFrame,
}; };
typedef QFlags<ads::CDockManager::eConfigFlag> ConfigFlags; typedef QFlags<ads::CDockManager::eConfigFlag> ConfigFlags;
@@ -167,6 +178,7 @@ public:
static ads::CDockManager::ConfigFlags configFlags(); static ads::CDockManager::ConfigFlags configFlags();
static void setConfigFlags(const ads::CDockManager::ConfigFlags Flags); static void setConfigFlags(const ads::CDockManager::ConfigFlags Flags);
static void setConfigFlag(ads::CDockManager::eConfigFlag Flag, bool On = true); static void setConfigFlag(ads::CDockManager::eConfigFlag Flag, bool On = true);
static bool testConfigFlag(eConfigFlag Flag);
static ads::CIconProvider& iconProvider(); static ads::CIconProvider& iconProvider();
ads::CDockAreaWidget* addDockWidget(ads::DockWidgetArea area, ads::CDockWidget* Dockwidget /Transfer/, ads::CDockAreaWidget* addDockWidget(ads::DockWidgetArea area, ads::CDockWidget* Dockwidget /Transfer/,
ads::CDockAreaWidget* DockAreaWidget /Transfer/ = 0); ads::CDockAreaWidget* DockAreaWidget /Transfer/ = 0);
@@ -180,7 +192,7 @@ public:
QMap<QString, ads::CDockWidget*> dockWidgetsMap() const; QMap<QString, ads::CDockWidget*> dockWidgetsMap() const;
const QList<ads::CDockContainerWidget*> dockContainers() const; const QList<ads::CDockContainerWidget*> dockContainers() const;
const QList<ads::CFloatingDockContainer*> floatingWidgets() const; const QList<ads::CFloatingDockContainer*> floatingWidgets() const;
virtual unsigned int zOrderIndex() const; unsigned int zOrderIndex() const;
QByteArray saveState(int version = 1) const; QByteArray saveState(int version = 1) const;
bool restoreState(const QByteArray &state, int version = 1); bool restoreState(const QByteArray &state, int version = 1);
void addPerspective(const QString& UniquePrespectiveName); void addPerspective(const QString& UniquePrespectiveName);
@@ -206,6 +218,7 @@ signals:
void stateRestored(); void stateRestored();
void openingPerspective(const QString& PerspectiveName); void openingPerspective(const QString& PerspectiveName);
void perspectiveOpened(const QString& PerspectiveName); void perspectiveOpened(const QString& PerspectiveName);
void floatingWidgetCreated(CFloatingDockContainer* FloatingWidget);
void dockAreaCreated(ads::CDockAreaWidget* DockArea); void dockAreaCreated(ads::CDockAreaWidget* DockArea);
void dockWidgetAboutToBeRemoved(ads::CDockWidget* DockWidget); void dockWidgetAboutToBeRemoved(ads::CDockWidget* DockWidget);
void dockWidgetRemoved(ads::CDockWidget* DockWidget); void dockWidgetRemoved(ads::CDockWidget* DockWidget);

View File

@@ -24,9 +24,11 @@ public:
void setAllowedAreas(ads::DockWidgetAreas areas); void setAllowedAreas(ads::DockWidgetAreas areas);
ads::DockWidgetAreas allowedAreas() const; ads::DockWidgetAreas allowedAreas() const;
ads::DockWidgetArea dropAreaUnderCursor() const; ads::DockWidgetArea dropAreaUnderCursor() const;
ads::DockWidgetArea visibleDropAreaUnderCursor() const;
ads::DockWidgetArea showOverlay(QWidget* target); ads::DockWidgetArea showOverlay(QWidget* target);
void hideOverlay(); void hideOverlay();
void enableDropPreview(bool Enable); void enableDropPreview(bool Enable);
bool dropPreviewEnabled() const;
QRect dropOverlayRect() const; QRect dropOverlayRect() const;
virtual bool event(QEvent *e); virtual bool event(QEvent *e);
@@ -58,6 +60,8 @@ protected:
void setIconOverlayColor(const QColor& Color); void setIconOverlayColor(const QColor& Color);
void setIconArrowColor(const QColor& Color); void setIconArrowColor(const QColor& Color);
void setIconShadowColor(const QColor& Color); void setIconShadowColor(const QColor& Color);
virtual void showEvent(QShowEvent* e);
void setAreaWidgets(const QHash<ads::DockWidgetArea, QWidget*>& widgets);
public: public:
CDockOverlayCross(ads::CDockOverlay* overlay /TransferThis/); CDockOverlayCross(ads::CDockOverlay* overlay /TransferThis/);

View File

@@ -16,6 +16,9 @@ public:
CDockSplitter(Qt::Orientation orientation, QWidget *parent /TransferThis/ = 0); CDockSplitter(Qt::Orientation orientation, QWidget *parent /TransferThis/ = 0);
virtual ~CDockSplitter(); virtual ~CDockSplitter();
bool hasVisibleContent() const; bool hasVisibleContent() const;
QWidget* firstWidget() const;
QWidget* lastWidget() const;
}; };
}; };

View File

@@ -12,15 +12,16 @@ class CDockWidget : QFrame
%End %End
protected: protected:
void setDockManager(ads::CDockManager* DockManager /Transfer/); void setDockManager(ads::CDockManager* DockManager /Transfer/ );
void setDockArea(ads::CDockAreaWidget* DockArea /Transfer/); void setDockArea(ads::CDockAreaWidget* DockArea /Transfer/ );
void setToggleViewActionChecked(bool Checked); void setToggleViewActionChecked(bool Checked);
void saveState(QXmlStreamWriter& Stream) const; void saveState(QXmlStreamWriter& Stream) const;
void flagAsUnassigned(); void flagAsUnassigned();
static void emitTopLevelEventForWidget(ads::CDockWidget* TopLevelDockWidget, bool Floating); static void emitTopLevelEventForWidget(ads::CDockWidget* TopLevelDockWidget, bool Floating);
void emitTopLevelChanged(bool Floating); void emitTopLevelChanged(bool Floating);
void setClosedState(bool Closed); void setClosedState(bool Closed);
void toggleViewInternal(bool Open); void toggleViewInternal(bool Open);
bool closeDockWidgetInternal(bool ForceClose = false);
public: public:
enum DockWidgetFeature enum DockWidgetFeature
@@ -29,7 +30,9 @@ public:
DockWidgetMovable, DockWidgetMovable,
DockWidgetFloatable, DockWidgetFloatable,
DockWidgetDeleteOnClose, DockWidgetDeleteOnClose,
AllDockWidgetFeatures, CustomCloseHandling,
DefaultDockWidgetFeatures,
AllDockWidgetFeatures,
NoDockWidgetFeatures NoDockWidgetFeatures
}; };
typedef QFlags<ads::CDockWidget::DockWidgetFeature> DockWidgetFeatures; typedef QFlags<ads::CDockWidget::DockWidgetFeature> DockWidgetFeatures;
@@ -47,6 +50,12 @@ public:
ForceScrollArea, ForceScrollArea,
ForceNoScrollArea ForceNoScrollArea
}; };
enum eMinimumSizeHintMode
{
MinimumSizeHintFromDockWidget,
MinimumSizeHintFromContent
};
enum eToggleViewActionMode enum eToggleViewActionMode
{ {
@@ -73,15 +82,19 @@ public:
bool isClosed() const; bool isClosed() const;
QAction* toggleViewAction() const; QAction* toggleViewAction() const;
void setToggleViewActionMode(ads::CDockWidget::eToggleViewActionMode Mode); void setToggleViewActionMode(ads::CDockWidget::eToggleViewActionMode Mode);
void setMinimumSizeHintMode(ads::CDockWidget::eMinimumSizeHintMode Mode);
void setIcon(const QIcon& Icon); void setIcon(const QIcon& Icon);
QIcon icon() const; QIcon icon() const;
QToolBar* toolBar() const; QToolBar* toolBar() const;
QToolBar* createDefaultToolBar(); QToolBar* createDefaultToolBar();
void setToolBar(QToolBar* ToolBar); void setToolBar(QToolBar* ToolBar /Transfer/ );
void setToolBarStyle(Qt::ToolButtonStyle Style, ads::CDockWidget::eState State); void setToolBarStyle(Qt::ToolButtonStyle Style, ads::CDockWidget::eState State);
Qt::ToolButtonStyle toolBarStyle(ads::CDockWidget::eState State) const; Qt::ToolButtonStyle toolBarStyle(ads::CDockWidget::eState State) const;
void setToolBarIconSize(const QSize& IconSize, ads::CDockWidget::eState State); void setToolBarIconSize(const QSize& IconSize, ads::CDockWidget::eState State);
QSize toolBarIconSize(eState State) const; QSize toolBarIconSize(eState State) const;
void setTitleBarActions(QList<QAction*> actions);
virtual QList<QAction*> titleBarActions() const;
void setTabToolTip(const QString &text); void setTabToolTip(const QString &text);
public: public:
@@ -91,12 +104,14 @@ public slots:
void toggleView(bool Open = true); void toggleView(bool Open = true);
void setFloating(); void setFloating();
void deleteDockWidget(); void deleteDockWidget();
void closeDockWidget();
signals: signals:
void viewToggled(bool Open); void viewToggled(bool Open);
void closed(); void closed();
void titleChanged(const QString& Title); void titleChanged(const QString& Title);
void topLevelChanged(bool topLevel); void topLevelChanged(bool topLevel);
void closeRequested();
void visibilityChanged(bool visible); void visibilityChanged(bool visible);
void featuresChanged(ads::CDockWidget::DockWidgetFeatures features); void featuresChanged(ads::CDockWidget::DockWidgetFeatures features);
}; };

View File

@@ -23,13 +23,14 @@ public:
virtual ~CDockWidgetTab(); virtual ~CDockWidgetTab();
bool isActiveTab() const; bool isActiveTab() const;
void setActiveTab(bool active); void setActiveTab(bool active);
ads::CDockWidget* dockWidget() const;
void setDockAreaWidget(ads::CDockAreaWidget* DockArea /Transfer/); void setDockAreaWidget(ads::CDockAreaWidget* DockArea /Transfer/);
ads::CDockAreaWidget* dockAreaWidget() const; ads::CDockAreaWidget* dockAreaWidget() const;
ads::CDockWidget* dockWidget() const;
void setIcon(const QIcon& Icon); void setIcon(const QIcon& Icon);
const QIcon& icon() const; const QIcon& icon() const;
QString text() const; QString text() const;
void setText(const QString& title); void setText(const QString& title);
bool isTitleElided() const;
bool isClosable() const; bool isClosable() const;
virtual bool event(QEvent *e); virtual bool event(QEvent *e);
@@ -43,6 +44,7 @@ signals:
void closeRequested(); void closeRequested();
void closeOtherTabsRequested(); void closeOtherTabsRequested();
void moved(const QPoint& GlobalPos); void moved(const QPoint& GlobalPos);
void elidedChanged(bool elided);
}; // class DockWidgetTab }; // class DockWidgetTab
}; };
// namespace ads // namespace ads

View File

@@ -22,7 +22,7 @@ public:
virtual ~CElidingLabel(); virtual ~CElidingLabel();
Qt::TextElideMode elideMode() const; Qt::TextElideMode elideMode() const;
void setElideMode(Qt::TextElideMode mode); void setElideMode(Qt::TextElideMode mode);
bool isElided() const;
public: public:
virtual QSize minimumSizeHint() const; virtual QSize minimumSizeHint() const;
@@ -33,6 +33,7 @@ public:
signals: signals:
void clicked(); void clicked();
void doubleClicked(); void doubleClicked();
void elidedChanged(bool elided);
}; };
}; };

View File

@@ -1,7 +1,17 @@
// NOTE: there is a separate sip/linux/FloatingDockContainer.sip as the base
// class for CFloatingDockContainer changes for Linux.
%Import QtWidgets/QtWidgetsmod.sip %Import QtWidgets/QtWidgetsmod.sip
%If (Qt_5_0_0 -) %If (Qt_5_0_0 -)
%If (WS_X11)
typedef QDockWidget tFloatingWidgetBase;
%End
%If (!WS_X11)
typedef QWidget tFloatingWidgetBase;
%End
namespace ads namespace ads
{ {
@@ -20,7 +30,7 @@ public:
}; };
class CFloatingDockContainer : QWidget, ads::IFloatingWidget class CFloatingDockContainer : tFloatingWidgetBase, ads::IFloatingWidget
{ {
%TypeHeaderCode %TypeHeaderCode
@@ -46,7 +56,6 @@ protected:
virtual void closeEvent(QCloseEvent *event); virtual void closeEvent(QCloseEvent *event);
virtual void hideEvent(QHideEvent *event); virtual void hideEvent(QHideEvent *event);
virtual void showEvent(QShowEvent *event); virtual void showEvent(QShowEvent *event);
virtual bool eventFilter(QObject *watched, QEvent *event);
public: public:
CFloatingDockContainer(ads::CDockManager* DockManager /TransferThis/); CFloatingDockContainer(ads::CDockManager* DockManager /TransferThis/);

View File

@@ -12,6 +12,10 @@ class CFloatingDragPreview : QWidget, ads::IFloatingWidget
#include <FloatingDragPreview.h> #include <FloatingDragPreview.h>
%End %End
protected:
virtual void moveEvent(QMoveEvent *event);
virtual void paintEvent(QPaintEvent *e);
CFloatingDragPreview(QWidget* Content /TransferThis/, QWidget* parent /TransferThis/);
public: public:
CFloatingDragPreview(ads::CDockWidget* Content /TransferThis/ ); CFloatingDragPreview(ads::CDockWidget* Content /TransferThis/ );
@@ -21,6 +25,7 @@ public:
virtual bool eventFilter(QObject* watched, QEvent* event); virtual bool eventFilter(QObject* watched, QEvent* event);
public: // implements IFloatingWidget
virtual void startFloating(const QPoint& DragStartMousePos, const QSize& Size, virtual void startFloating(const QPoint& DragStartMousePos, const QSize& Size,
ads::eDragState DragState, QWidget* MouseEventHandler); ads::eDragState DragState, QWidget* MouseEventHandler);
@@ -35,4 +40,4 @@ signals:
}; };
%End %End

View File

@@ -1,13 +1,13 @@
%Module(name=PyQtAds.QtAds.ads, call_super_init=True, keyword_arguments="Optional", use_limited_api=True) %Module(name=PyQtAds.QtAds.ads, call_super_init=True, keyword_arguments="Optional", use_limited_api=True)
%Import QtCore/QtCoremod.sip %Import QtCore/QtCoremod.sip
%DefaultSupertype sip.simplewrapper %DefaultSupertype sip.simplewrapper
%Platforms {Linux macOS Windows}
%Include ads_globals.sip %Include ads_globals.sip
%Include DockWidget.sip %Include DockWidget.sip
%Include DockAreaTabBar.sip %Include DockAreaTabBar.sip
%Include DockAreaTitleBar.sip %Include DockAreaTitleBar.sip
%Include DockAreaWidget.sip %Include DockAreaWidget.sip
%Include DockComponentsFactory.sip
%Include DockContainerWidget.sip %Include DockContainerWidget.sip
%Include DockingStateReader.sip %Include DockingStateReader.sip
%Include DockManager.sip %Include DockManager.sip
@@ -18,6 +18,6 @@
%Include FloatingDockContainer.sip %Include FloatingDockContainer.sip
%Include FloatingDragPreview.sip %Include FloatingDragPreview.sip
%Include IconProvider.sip %Include IconProvider.sip
%If (Linux) %If (WS_X11)
%Include linux/FloatingWidgetTitleBar.sip %Include linux/FloatingWidgetTitleBar.sip
%End %End

View File

@@ -8,6 +8,13 @@ namespace ads
#include <ads_globals.h> #include <ads_globals.h>
%End %End
enum eStateFileVersion
{
InitialVerison,
Version1,
CurrentVersion
};
enum DockWidgetArea enum DockWidgetArea
{ {
NoDockWidgetArea, NoDockWidgetArea,
@@ -49,6 +56,12 @@ namespace ads
IconCount, IconCount,
}; };
enum eBitwiseOperator
{
BitwiseAnd,
BitwiseOr
};
}; };
%End %End

View File

@@ -5,7 +5,7 @@
namespace ads namespace ads
{ {
%TypeHeaderCode %TypeHeaderCode
#include <FloatingWidgetTitleBar.h> #include <linux/FloatingWidgetTitleBar.h>
%End %End
class CFloatingWidgetTitleBar : QWidget class CFloatingWidgetTitleBar : QWidget

View File

@@ -35,6 +35,7 @@
#include <QDebug> #include <QDebug>
#include <QBoxLayout> #include <QBoxLayout>
#include <QApplication> #include <QApplication>
#include <QtGlobal>
#include "FloatingDockContainer.h" #include "FloatingDockContainer.h"
#include "DockAreaWidget.h" #include "DockAreaWidget.h"
@@ -69,6 +70,16 @@ struct DockAreaTabBarPrivate
* The function reassigns the stylesheet to update the tabs * The function reassigns the stylesheet to update the tabs
*/ */
void updateTabs(); void updateTabs();
/**
* Convenience function to access first tab
*/
CDockWidgetTab* firstTab() const {return _this->tab(0);}
/**
* Convenience function to access last tab
*/
CDockWidgetTab* lastTab() const {return _this->tab(_this->count() - 1);}
}; };
// struct DockAreaTabBarPrivate // struct DockAreaTabBarPrivate
@@ -366,6 +377,8 @@ void CDockAreaTabBar::onTabWidgetMoved(const QPoint& GlobalPos)
int fromIndex = d->TabsLayout->indexOf(MovingTab); int fromIndex = d->TabsLayout->indexOf(MovingTab);
auto MousePos = mapFromGlobal(GlobalPos); auto MousePos = mapFromGlobal(GlobalPos);
MousePos.rx() = qMax(d->firstTab()->geometry().left(), MousePos.x());
MousePos.rx() = qMin(d->lastTab()->geometry().right(), MousePos.x());
int toIndex = -1; int toIndex = -1;
// Find tab under mouse // Find tab under mouse
for (int i = 0; i < count(); ++i) for (int i = 0; i < count(); ++i)
@@ -381,38 +394,23 @@ void CDockAreaTabBar::onTabWidgetMoved(const QPoint& GlobalPos)
if (toIndex == fromIndex) if (toIndex == fromIndex)
{ {
toIndex = -1; toIndex = -1;
continue;
}
if (toIndex < 0)
{
toIndex = 0;
} }
break; break;
} }
// Now check if the mouse is behind the last tab if (toIndex > -1)
if (toIndex < 0)
{
if (MousePos.x() > tab(count() - 1)->geometry().right())
{
ADS_PRINT("after all tabs");
toIndex = count() - 1;
}
else
{
toIndex = fromIndex;
}
}
d->TabsLayout->removeWidget(MovingTab);
d->TabsLayout->insertWidget(toIndex, MovingTab);
if (toIndex >= 0)
{ {
d->TabsLayout->removeWidget(MovingTab);
d->TabsLayout->insertWidget(toIndex, MovingTab);
ADS_PRINT("tabMoved from " << fromIndex << " to " << toIndex); ADS_PRINT("tabMoved from " << fromIndex << " to " << toIndex);
emit tabMoved(fromIndex, toIndex); emit tabMoved(fromIndex, toIndex);
setCurrentIndex(toIndex); setCurrentIndex(toIndex);
} }
else
{
// Ensure that the moved tab is reset to its start position
d->TabsLayout->update();
}
} }
//=========================================================================== //===========================================================================

View File

@@ -49,6 +49,7 @@
#include "DockWidgetTab.h" #include "DockWidgetTab.h"
#include "DockAreaTabBar.h" #include "DockAreaTabBar.h"
#include "IconProvider.h" #include "IconProvider.h"
#include "DockComponentsFactory.h"
#include <iostream> #include <iostream>
@@ -234,7 +235,7 @@ void DockAreaTitleBarPrivate::createButtons()
#endif #endif
_this->connect(TabsMenu, SIGNAL(aboutToShow()), SLOT(onTabsMenuAboutToShow())); _this->connect(TabsMenu, SIGNAL(aboutToShow()), SLOT(onTabsMenuAboutToShow()));
TabsMenuButton->setMenu(TabsMenu); TabsMenuButton->setMenu(TabsMenu);
internal::setToolTip(TabsMenuButton, QObject::tr("List all tabs")); internal::setToolTip(TabsMenuButton, QObject::tr("List All Tabs"));
TabsMenuButton->setSizePolicy(ButtonSizePolicy); TabsMenuButton->setSizePolicy(ButtonSizePolicy);
Layout->addWidget(TabsMenuButton, 0); Layout->addWidget(TabsMenuButton, 0);
_this->connect(TabsMenuButton->menu(), SIGNAL(triggered(QAction*)), _this->connect(TabsMenuButton->menu(), SIGNAL(triggered(QAction*)),
@@ -273,7 +274,7 @@ void DockAreaTitleBarPrivate::createButtons()
//============================================================================ //============================================================================
void DockAreaTitleBarPrivate::createTabBar() void DockAreaTitleBarPrivate::createTabBar()
{ {
TabBar = new CDockAreaTabBar(DockArea); TabBar = componentsFactory()->createDockAreaTabBar(DockArea);
TabBar->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred); TabBar->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
Layout->addWidget(TabBar); Layout->addWidget(TabBar);
_this->connect(TabBar, SIGNAL(tabClosed(int)), SLOT(markTabsMenuOutdated())); _this->connect(TabBar, SIGNAL(tabClosed(int)), SLOT(markTabsMenuOutdated()));
@@ -599,7 +600,11 @@ void CDockAreaTitleBar::mouseMoveEvent(QMouseEvent* ev)
// If one single dock widget in this area is not floatable then the whole // If one single dock widget in this area is not floatable then the whole
// area is not floatable // area is not floatable
if (!d->DockArea->features().testFlag(CDockWidget::DockWidgetFloatable)) // If we do non opaque undocking, then we can create the floating drag
// preview if the dock widget is movable
auto Features = d->DockArea->features();
if (!Features.testFlag(CDockWidget::DockWidgetFloatable)
&& !(Features.testFlag(CDockWidget::DockWidgetMovable) && !CDockManager::testConfigFlag(CDockManager::OpaqueUndocking)))
{ {
return; return;
} }

View File

@@ -28,9 +28,10 @@
//============================================================================ //============================================================================
// INCLUDES // INCLUDES
//============================================================================ //============================================================================
#include "DockWidgetTab.h"
#include "DockAreaWidget.h" #include "DockAreaWidget.h"
#include <iostream>
#include <QStackedLayout> #include <QStackedLayout>
#include <QScrollBar> #include <QScrollBar>
#include <QScrollArea> #include <QScrollArea>
@@ -53,8 +54,8 @@
#include "DockAreaTabBar.h" #include "DockAreaTabBar.h"
#include "DockSplitter.h" #include "DockSplitter.h"
#include "DockAreaTitleBar.h" #include "DockAreaTitleBar.h"
#include "DockComponentsFactory.h"
#include <iostream> #include "DockWidgetTab.h"
namespace ads namespace ads
@@ -246,6 +247,7 @@ struct DockAreaWidgetPrivate
CDockManager* DockManager = nullptr; CDockManager* DockManager = nullptr;
bool UpdateTitleBarButtons = false; bool UpdateTitleBarButtons = false;
DockWidgetAreas AllowedAreas = AllDockAreas; DockWidgetAreas AllowedAreas = AllDockAreas;
QSize MinSizeHint;
/** /**
* Private data constructor * Private data constructor
@@ -262,7 +264,7 @@ struct DockAreaWidgetPrivate
*/ */
CDockWidget* dockWidgetAt(int index) CDockWidget* dockWidgetAt(int index)
{ {
return dynamic_cast<CDockWidget*>(ContentsLayout->widget(index)); return qobject_cast<CDockWidget*>(ContentsLayout->widget(index));
} }
/** /**
@@ -302,6 +304,20 @@ struct DockAreaWidgetPrivate
* Udpates the enable state of the close and detach button * Udpates the enable state of the close and detach button
*/ */
void updateTitleBarButtonStates(); void updateTitleBarButtonStates();
/**
* Scans all contained dock widgets for the max. minimum size hint
*/
void updateMinimumSizeHint()
{
MinSizeHint = QSize();
for (int i = 0; i < ContentsLayout->count(); ++i)
{
auto Widget = ContentsLayout->widget(i);
MinSizeHint.setHeight(qMax(MinSizeHint.height(), Widget->minimumSizeHint().height()));
MinSizeHint.setWidth(qMax(MinSizeHint.width(), Widget->minimumSizeHint().width()));
}
}
}; };
// struct DockAreaWidgetPrivate // struct DockAreaWidgetPrivate
@@ -317,7 +333,7 @@ DockAreaWidgetPrivate::DockAreaWidgetPrivate(CDockAreaWidget* _public) :
//============================================================================ //============================================================================
void DockAreaWidgetPrivate::createTitleBar() void DockAreaWidgetPrivate::createTitleBar()
{ {
TitleBar = new CDockAreaTitleBar(_this); TitleBar = componentsFactory()->createDockAreaTitleBar(_this);
Layout->addWidget(TitleBar); Layout->addWidget(TitleBar);
QObject::connect(tabBar(), &CDockAreaTabBar::tabCloseRequested, _this, &CDockAreaWidget::onTabCloseRequested); QObject::connect(tabBar(), &CDockAreaTabBar::tabCloseRequested, _this, &CDockAreaWidget::onTabCloseRequested);
QObject::connect(TitleBar, &CDockAreaTitleBar::tabBarClicked, _this, &CDockAreaWidget::setCurrentIndex); QObject::connect(TitleBar, &CDockAreaTitleBar::tabBarClicked, _this, &CDockAreaWidget::setCurrentIndex);
@@ -406,6 +422,8 @@ void CDockAreaWidget::insertDockWidget(int index, CDockWidget* DockWidget,
d->tabBar()->blockSignals(false); d->tabBar()->blockSignals(false);
TabWidget->setVisible(!DockWidget->isClosed()); TabWidget->setVisible(!DockWidget->isClosed());
DockWidget->setProperty(INDEX_PROPERTY, index); DockWidget->setProperty(INDEX_PROPERTY, index);
d->MinSizeHint.setHeight(qMax(d->MinSizeHint.height(), DockWidget->minimumSizeHint().height()));
d->MinSizeHint.setWidth(qMax(d->MinSizeHint.width(), DockWidget->minimumSizeHint().width()));
if (Activate) if (Activate)
{ {
setCurrentIndex(index); setCurrentIndex(index);
@@ -446,6 +464,7 @@ void CDockAreaWidget::removeDockWidget(CDockWidget* DockWidget)
d->updateTitleBarButtonStates(); d->updateTitleBarButtonStates();
updateTitleBarVisibility(); updateTitleBarVisibility();
d->updateMinimumSizeHint();
auto TopLevelDockWidget = DockContainer->topLevelDockWidget(); auto TopLevelDockWidget = DockContainer->topLevelDockWidget();
if (TopLevelDockWidget) if (TopLevelDockWidget)
{ {
@@ -496,7 +515,6 @@ void CDockAreaWidget::onTabCloseRequested(int Index)
auto* DockWidget = dockWidget(Index); auto* DockWidget = dockWidget(Index);
if (DockWidget->features().testFlag(CDockWidget::DockWidgetDeleteOnClose)) if (DockWidget->features().testFlag(CDockWidget::DockWidgetDeleteOnClose))
{ {
//DockWidget->deleteDockWidget();
DockWidget->closeDockWidgetInternal(); DockWidget->closeDockWidgetInternal();
} }
else else
@@ -863,6 +881,13 @@ CDockAreaTitleBar* CDockAreaWidget::titleBar() const
{ {
return d->TitleBar; return d->TitleBar;
} }
//============================================================================
QSize CDockAreaWidget::minimumSizeHint() const
{
return d->MinSizeHint.isValid() ? d->MinSizeHint : Super::minimumSizeHint();
}
} // namespace ads } // namespace ads
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------

View File

@@ -164,6 +164,13 @@ public:
*/ */
CDockContainerWidget* dockContainer() const; CDockContainerWidget* dockContainer() const;
/**
* Returns the largest minimumSizeHint() of the dock widgets in this
* area.
* The minimum size hint is updated if a dock widget is removed or added.
*/
virtual QSize minimumSizeHint() const override;
/** /**
* Returns the rectangle of the title area * Returns the rectangle of the title area
*/ */

View File

@@ -0,0 +1,69 @@
//============================================================================
/// \file DockComponentsFactory.cpp
/// \author Uwe Kindler
/// \date 10.02.2020
/// \brief Implementation of DockComponentsFactory
//============================================================================
//============================================================================
// INCLUDES
//============================================================================
#include <DockComponentsFactory.h>
#include <memory>
#include "DockWidgetTab.h"
#include "DockAreaTabBar.h"
#include "DockAreaTitleBar.h"
#include "DockWidget.h"
#include "DockAreaWidget.h"
namespace ads
{
static std::unique_ptr<CDockComponentsFactory> DefaultFactory(new CDockComponentsFactory());
//============================================================================
CDockWidgetTab* CDockComponentsFactory::createDockWidgetTab(CDockWidget* DockWidget) const
{
return new CDockWidgetTab(DockWidget);
}
//============================================================================
CDockAreaTabBar* CDockComponentsFactory::createDockAreaTabBar(CDockAreaWidget* DockArea) const
{
return new CDockAreaTabBar(DockArea);
}
//============================================================================
CDockAreaTitleBar* CDockComponentsFactory::createDockAreaTitleBar(CDockAreaWidget* DockArea) const
{
return new CDockAreaTitleBar(DockArea);
}
//============================================================================
const CDockComponentsFactory* CDockComponentsFactory::factory()
{
return DefaultFactory.get();
}
//============================================================================
void CDockComponentsFactory::setFactory(CDockComponentsFactory* Factory)
{
DefaultFactory.reset(Factory);
}
//============================================================================
void CDockComponentsFactory::resetDefaultFactory()
{
DefaultFactory.reset(new CDockComponentsFactory());
}
} // namespace ads
//---------------------------------------------------------------------------
// EOF DockComponentsFactory.cpp

View File

@@ -0,0 +1,90 @@
#ifndef DockComponentsFactoryH
#define DockComponentsFactoryH
//============================================================================
/// \file DockComponentsFactory.h
/// \author Uwe Kindler
/// \date 10.02.2020
/// \brief Declaration of DockComponentsFactory
//============================================================================
//============================================================================
// INCLUDES
//============================================================================
#include "ads_globals.h"
namespace ads
{
class CDockWidgetTab;
class CDockAreaTitleBar;
class CDockAreaTabBar;
class CDockAreaWidget;
class CDockWidget;
/**
* Factory for creation of certain GUI elements for the docking framework.
* A default unique instance provided by CDockComponentsFactory is used for
* creation of all supported components. To inject your custom components,
* you can create your own derived dock components factory and register
* it via setDefaultFactory() function.
* \code
* CDockComponentsFactory::setDefaultFactory(new MyComponentsFactory()));
* \endcode
*/
class ADS_EXPORT CDockComponentsFactory
{
public:
/**
* Force virtual destructor
*/
virtual ~CDockComponentsFactory() {}
/**
* This default implementation just creates a dock widget tab with
* new CDockWidgetTab(DockWIdget).
*/
virtual CDockWidgetTab* createDockWidgetTab(CDockWidget* DockWidget) const;
/**
* This default implementation just creates a dock area tab bar with
* new CDockAreaTabBar(DockArea).
*/
virtual CDockAreaTabBar* createDockAreaTabBar(CDockAreaWidget* DockArea) const;
/**
* This default implementation just creates a dock area title bar with
* new CDockAreaTitleBar(DockArea).
*/
virtual CDockAreaTitleBar* createDockAreaTitleBar(CDockAreaWidget* DockArea) const;
/**
* Returns the default components factory
*/
static const CDockComponentsFactory* factory();
/**
* Sets a new default factory for creation of GUI elements.
* This function takes ownership of the given Factory.
*/
static void setFactory(CDockComponentsFactory* Factory);
/**
* Resets the current factory to the
*/
static void resetDefaultFactory();
};
/**
* Convenience function to ease factory instance access
*/
inline const CDockComponentsFactory* componentsFactory()
{
return CDockComponentsFactory::factory();
}
} // namespace ads
//---------------------------------------------------------------------------
#endif // DockComponentsFactoryH

View File

@@ -679,6 +679,23 @@ void DockContainerWidgetPrivate::moveToContainer(QWidget* Widget, DockWidgetArea
} }
else else
{ {
// We check, if we insert the dropped widget into the same place that
// it already has and do nothing, if it is the same place. It would
// also work without this check, but it looks nicer with the check
// because there will be no layout updates
auto Splitter = internal::findParent<CDockSplitter*>(DroppedDockArea);
auto InsertParam = internal::dockAreaInsertParameters(area);
if (Splitter == RootSplitter && InsertParam.orientation() == Splitter->orientation())
{
if (InsertParam.append() && Splitter->lastWidget() == DroppedDockArea)
{
return;
}
else if (!InsertParam.append() && Splitter->firstWidget() == DroppedDockArea)
{
return;
}
}
DroppedDockArea->dockContainer()->removeDockArea(DroppedDockArea); DroppedDockArea->dockContainer()->removeDockArea(DroppedDockArea);
NewDockArea = DroppedDockArea; NewDockArea = DroppedDockArea;
} }
@@ -736,7 +753,7 @@ void DockContainerWidgetPrivate::appendDockAreas(const QList<CDockAreaWidget*> N
//============================================================================ //============================================================================
void DockContainerWidgetPrivate::saveChildNodesState(QXmlStreamWriter& s, QWidget* Widget) void DockContainerWidgetPrivate::saveChildNodesState(QXmlStreamWriter& s, QWidget* Widget)
{ {
QSplitter* Splitter = dynamic_cast<QSplitter*>(Widget); QSplitter* Splitter = qobject_cast<QSplitter*>(Widget);
if (Splitter) if (Splitter)
{ {
s.writeStartElement("Splitter"); s.writeStartElement("Splitter");
@@ -759,7 +776,7 @@ void DockContainerWidgetPrivate::saveChildNodesState(QXmlStreamWriter& s, QWidge
} }
else else
{ {
CDockAreaWidget* DockArea = dynamic_cast<CDockAreaWidget*>(Widget); CDockAreaWidget* DockArea = qobject_cast<CDockAreaWidget*>(Widget);
if (DockArea) if (DockArea)
{ {
DockArea->saveState(s); DockArea->saveState(s);
@@ -1046,7 +1063,7 @@ void DockContainerWidgetPrivate::addDockArea(CDockAreaWidget* NewDockArea, DockW
void DockContainerWidgetPrivate::dumpRecursive(int level, QWidget* widget) void DockContainerWidgetPrivate::dumpRecursive(int level, QWidget* widget)
{ {
#if defined(QT_DEBUG) #if defined(QT_DEBUG)
QSplitter* Splitter = dynamic_cast<QSplitter*>(widget); QSplitter* Splitter = qobject_cast<QSplitter*>(widget);
QByteArray buf; QByteArray buf;
buf.fill(' ', level * 4); buf.fill(' ', level * 4);
if (Splitter) if (Splitter)
@@ -1069,7 +1086,7 @@ void DockContainerWidgetPrivate::dumpRecursive(int level, QWidget* widget)
} }
else else
{ {
CDockAreaWidget* DockArea = dynamic_cast<CDockAreaWidget*>(widget); CDockAreaWidget* DockArea = qobject_cast<CDockAreaWidget*>(widget);
if (!DockArea) if (!DockArea)
{ {
return; return;
@@ -1285,7 +1302,7 @@ void CDockContainerWidget::removeDockArea(CDockAreaWidget* area)
} }
QWidget* widget = Splitter->widget(0); QWidget* widget = Splitter->widget(0);
QSplitter* ChildSplitter = dynamic_cast<QSplitter*>(widget); QSplitter* ChildSplitter = qobject_cast<QSplitter*>(widget);
// If the one and only content widget of the splitter is not a splitter // If the one and only content widget of the splitter is not a splitter
// then we are finished // then we are finished
if (!ChildSplitter) if (!ChildSplitter)
@@ -1436,41 +1453,16 @@ void CDockContainerWidget::dropFloatingWidget(CFloatingDockContainer* FloatingWi
//============================================================================ //============================================================================
void CDockContainerWidget::dropWidget(QWidget* Widget, const QPoint& TargetPos) void CDockContainerWidget::dropWidget(QWidget* Widget, DockWidgetArea DropArea, CDockAreaWidget* TargetAreaWidget)
{ {
ADS_PRINT("CDockContainerWidget::dropFloatingWidget");
CDockWidget* SingleDockWidget = topLevelDockWidget(); CDockWidget* SingleDockWidget = topLevelDockWidget();
CDockAreaWidget* DockArea = dockAreaAt(TargetPos); if (TargetAreaWidget)
auto dropArea = InvalidDockWidgetArea;
auto ContainerDropArea = d->DockManager->containerOverlay()->dropAreaUnderCursor();
if (DockArea)
{ {
auto dropOverlay = d->DockManager->dockAreaOverlay(); d->moveToNewSection(Widget, TargetAreaWidget, DropArea);
dropOverlay->setAllowedAreas(DockArea->allowedAreas());
dropArea = dropOverlay->showOverlay(DockArea);
if (ContainerDropArea != InvalidDockWidgetArea &&
ContainerDropArea != dropArea)
{
dropArea = InvalidDockWidgetArea;
}
if (dropArea != InvalidDockWidgetArea)
{
ADS_PRINT("Dock Area Drop Content: " << dropArea);
d->moveToNewSection(Widget, DockArea, dropArea);
}
} }
else
// mouse is over container
if (InvalidDockWidgetArea == dropArea)
{ {
dropArea = ContainerDropArea; d->moveToContainer(Widget, DropArea);
ADS_PRINT("Container Drop Content: " << dropArea);
if (dropArea != InvalidDockWidgetArea)
{
d->moveToContainer(Widget, dropArea);
}
} }
// If there was a top level widget before the drop, then it is not top // If there was a top level widget before the drop, then it is not top
@@ -1573,7 +1565,7 @@ bool CDockContainerWidget::restoreState(CDockingStateReader& s, bool Testing)
d->Layout->replaceWidget(d->RootSplitter, NewRootSplitter); d->Layout->replaceWidget(d->RootSplitter, NewRootSplitter);
QSplitter* OldRoot = d->RootSplitter; QSplitter* OldRoot = d->RootSplitter;
d->RootSplitter = dynamic_cast<QSplitter*>(NewRootSplitter); d->RootSplitter = qobject_cast<QSplitter*>(NewRootSplitter);
OldRoot->deleteLater(); OldRoot->deleteLater();
return true; return true;

View File

@@ -96,9 +96,13 @@ protected:
void dropFloatingWidget(CFloatingDockContainer* FloatingWidget, const QPoint& TargetPos); void dropFloatingWidget(CFloatingDockContainer* FloatingWidget, const QPoint& TargetPos);
/** /**
* Drop a dock area or a dock widget given in widget parameter * Drop a dock area or a dock widget given in widget parameter.
* If the TargetAreaWidget is a nullptr, then the DropArea indicates
* the drop area for the container. If the given TargetAreaWidget is not
* a nullptr, then the DropArea indicates the drop area in the given
* TargetAreaWidget
*/ */
void dropWidget(QWidget* Widget, const QPoint& TargetPos); void dropWidget(QWidget* Widget, DockWidgetArea DropArea, CDockAreaWidget* TargetAreaWidget);
/** /**
* Adds the given dock area to this container widget * Adds the given dock area to this container widget

View File

@@ -58,7 +58,7 @@
namespace ads namespace ads
{ {
static CDockManager::ConfigFlags StaticConfigFlags = CDockManager::DefaultOpaqueConfig; static CDockManager::ConfigFlags StaticConfigFlags = CDockManager::DefaultNonOpaqueConfig;
/** /**
* Private data class of CDockManager class (pimpl) * Private data class of CDockManager class (pimpl)
@@ -411,7 +411,7 @@ CDockManager::CDockManager(QWidget *parent) :
d(new DockManagerPrivate(this)) d(new DockManagerPrivate(this))
{ {
createRootSplitter(); createRootSplitter();
QMainWindow* MainWindow = dynamic_cast<QMainWindow*>(parent); QMainWindow* MainWindow = qobject_cast<QMainWindow*>(parent);
if (MainWindow) if (MainWindow)
{ {
MainWindow->setCentralWidget(this); MainWindow->setCentralWidget(this);
@@ -440,6 +440,7 @@ CDockManager::~CDockManager()
void CDockManager::registerFloatingWidget(CFloatingDockContainer* FloatingWidget) void CDockManager::registerFloatingWidget(CFloatingDockContainer* FloatingWidget)
{ {
d->FloatingWidgets.append(FloatingWidget); d->FloatingWidgets.append(FloatingWidget);
emit floatingWidgetCreated(FloatingWidget);
ADS_PRINT("d->FloatingWidgets.count() " << d->FloatingWidgets.count()); ADS_PRINT("d->FloatingWidgets.count() " << d->FloatingWidgets.count());
} }
@@ -777,6 +778,10 @@ QAction* CDockManager::addToggleViewActionToMenu(QAction* ToggleViewAction,
d->addActionToMenu(GroupMenu->menuAction(), d->ViewMenu, AlphabeticallySorted); d->addActionToMenu(GroupMenu->menuAction(), d->ViewMenu, AlphabeticallySorted);
d->ViewMenuGroups.insert(Group, GroupMenu); d->ViewMenuGroups.insert(Group, GroupMenu);
} }
else if (GroupMenu->icon().isNull() && !GroupIcon.isNull())
{
GroupMenu->setIcon(GroupIcon);
}
d->addActionToMenu(ToggleViewAction, GroupMenu, AlphabeticallySorted); d->addActionToMenu(ToggleViewAction, GroupMenu, AlphabeticallySorted);
return GroupMenu->menuAction(); return GroupMenu->menuAction();

View File

@@ -30,18 +30,11 @@
//============================================================================ //============================================================================
// INCLUDES // INCLUDES
//============================================================================ //============================================================================
#include <ads_globals.h> #include "ads_globals.h"
#include <DockContainerWidget.h> #include "DockContainerWidget.h"
#include <DockWidget.h> #include "DockWidget.h"
#include <FloatingDockContainer.h> #include "FloatingDockContainer.h"
#include <qbytearray.h>
#include <qflags.h>
#include <qlist.h>
#include <qmap.h>
#include <qobjectdefs.h>
#include <qstring.h>
#include <qstringlist.h>
#include <QtGui/qicon.h>
class QSettings; class QSettings;
class QMenu; class QMenu;
@@ -59,6 +52,7 @@ class CDockWidgetTab;
struct DockWidgetTabPrivate; struct DockWidgetTabPrivate;
struct DockAreaWidgetPrivate; struct DockAreaWidgetPrivate;
class CIconProvider; class CIconProvider;
class CDockComponentsFactory;
/** /**
* The central dock manager that maintains the complete docking system. * The central dock manager that maintains the complete docking system.
@@ -141,6 +135,7 @@ public:
/** /**
* These global configuration flags configure some global dock manager * These global configuration flags configure some global dock manager
* settings. * settings.
* Set the dock manager flags, before you create the dock manager instance.
*/ */
enum eConfigFlag enum eConfigFlag
{ {
@@ -162,6 +157,8 @@ public:
DockAreaHasTabsMenuButton = 0x8000, //!< If the flag is set each dock area has a tabs menu button DockAreaHasTabsMenuButton = 0x8000, //!< If the flag is set each dock area has a tabs menu button
DockAreaHideDisabledButtons = 0x10000, //!< If the flag is set disabled dock area buttons will not appear on the tollbar at all (enabling them will bring them back) DockAreaHideDisabledButtons = 0x10000, //!< If the flag is set disabled dock area buttons will not appear on the tollbar at all (enabling them will bring them back)
DockAreaDynamicTabsMenuButtonVisibility = 0x20000, //!< If the flag is set dock area will disable a tabs menu button when there is only one tab in the area DockAreaDynamicTabsMenuButtonVisibility = 0x20000, //!< If the flag is set dock area will disable a tabs menu button when there is only one tab in the area
FloatingContainerHasWidgetTitle = 0x40000,
FloatingContainerHasWidgetIcon = 0x80000,
DefaultDockAreaButtons = DockAreaHasCloseButton DefaultDockAreaButtons = DockAreaHasCloseButton
@@ -170,7 +167,8 @@ public:
DefaultBaseConfig = DefaultDockAreaButtons DefaultBaseConfig = DefaultDockAreaButtons
| ActiveTabHasCloseButton | ActiveTabHasCloseButton
| XmlCompressionEnabled,///< default base configuration settings | XmlCompressionEnabled
| FloatingContainerHasWidgetTitle,///< default base configuration settings
DefaultOpaqueConfig = DefaultBaseConfig DefaultOpaqueConfig = DefaultBaseConfig
| OpaqueSplitterResize | OpaqueSplitterResize
@@ -191,12 +189,12 @@ public:
* Before you create any dock widgets, you should properly setup the * Before you create any dock widgets, you should properly setup the
* configuration flags via setConfigFlags(). * configuration flags via setConfigFlags().
*/ */
CDockManager(QWidget* parent = 0); CDockManager(QWidget* parent = nullptr);
/** /**
* Virtual Destructor * Virtual Destructor
*/ */
virtual ~CDockManager(); virtual ~CDockManager() override;
/** /**
* This function returns the global configuration flags * This function returns the global configuration flags
@@ -205,12 +203,14 @@ public:
/** /**
* Sets the global configuration flags for the whole docking system. * Sets the global configuration flags for the whole docking system.
* Call this function before you create your first dock widget. * Call this function before you create the dock manager and before
* your create the first dock widget.
*/ */
static void setConfigFlags(const ConfigFlags Flags); static void setConfigFlags(const ConfigFlags Flags);
/** /**
* Set a certain config flag * Set a certain config flag.
* \see setConfigFlags()
*/ */
static void setConfigFlag(eConfigFlag Flag, bool On = true); static void setConfigFlag(eConfigFlag Flag, bool On = true);
@@ -446,10 +446,17 @@ signals:
/** /**
* This signal is emitted if the dock manager finished opening a * This signal is emitted if the dock manager finished opening a
* perspective * perspective.
*/ */
void perspectiveOpened(const QString& PerspectiveName); void perspectiveOpened(const QString& PerspectiveName);
/**
* This signal is emitted, if a new floating widget has been created.
* An application can use this signal to e.g. subscribe to events of
* the newly created window.
*/
void floatingWidgetCreated(CFloatingDockContainer* FloatingWidget);
/** /**
* This signal is emitted, if a new DockArea has been created. * This signal is emitted, if a new DockArea has been created.
* An application can use this signal to set custom icons or custom * An application can use this signal to set custom icons or custom

View File

@@ -380,7 +380,7 @@ DockWidgetArea CDockOverlay::dropAreaUnderCursor() const
return Result; return Result;
} }
CDockAreaWidget* DockArea = dynamic_cast<CDockAreaWidget*>(d->TargetWidget.data()); CDockAreaWidget* DockArea = qobject_cast<CDockAreaWidget*>(d->TargetWidget.data());
if (!DockArea) if (!DockArea)
{ {
return Result; return Result;
@@ -396,6 +396,20 @@ DockWidgetArea CDockOverlay::dropAreaUnderCursor() const
} }
//============================================================================
DockWidgetArea CDockOverlay::visibleDropAreaUnderCursor() const
{
if (isHidden() || !d->DropPreviewEnabled)
{
return InvalidDockWidgetArea;
}
else
{
return dropAreaUnderCursor();
}
}
//============================================================================ //============================================================================
DockWidgetArea CDockOverlay::showOverlay(QWidget* target) DockWidgetArea CDockOverlay::showOverlay(QWidget* target)
{ {
@@ -632,7 +646,7 @@ void CDockOverlayCross::updateOverlayIcons()
{ {
d->updateDropIndicatorIcon(Widget); d->updateDropIndicatorIcon(Widget);
} }
#if QT_VESION >= 0x050600 #if QT_VERSION >= 0x050600
d->LastDevicePixelRatio = devicePixelRatioF(); d->LastDevicePixelRatio = devicePixelRatioF();
#else #else
d->LastDevicePixelRatio = devicePixelRatio(); d->LastDevicePixelRatio = devicePixelRatio();

View File

@@ -81,6 +81,13 @@ public:
*/ */
DockWidgetArea dropAreaUnderCursor() const; DockWidgetArea dropAreaUnderCursor() const;
/**
* This function returns the same like dropAreaUnderCursor() if this
* overlay is not hidden and if drop preview is enabled and returns
* InvalidDockWidgetArea if it is hidden or drop preview is disabled.
*/
DockWidgetArea visibleDropAreaUnderCursor() const;
/** /**
* Show the drop overly for the given target widget * Show the drop overly for the given target widget
*/ */

View File

@@ -88,6 +88,20 @@ bool CDockSplitter::hasVisibleContent() const
return false; return false;
} }
//============================================================================
QWidget* CDockSplitter::firstWidget() const
{
return (count() > 0) ? widget(0) : nullptr;
}
//============================================================================
QWidget* CDockSplitter::lastWidget() const
{
return (count() > 0) ? widget(count() - 1) : nullptr;
}
} // namespace ads } // namespace ads
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------

View File

@@ -61,6 +61,16 @@ public:
* Returns true, if any of the internal widgets is visible * Returns true, if any of the internal widgets is visible
*/ */
bool hasVisibleContent() const; bool hasVisibleContent() const;
/**
* Returns first widget or nullptr if splitter is empty
*/
QWidget* firstWidget() const;
/**
* Returns last widget of nullptr is splitter is empty
*/
QWidget* lastWidget() const;
}; // class CDockSplitter }; // class CDockSplitter
} // namespace ads } // namespace ads

View File

@@ -54,6 +54,7 @@
#include "DockManager.h" #include "DockManager.h"
#include "FloatingDockContainer.h" #include "FloatingDockContainer.h"
#include "DockSplitter.h" #include "DockSplitter.h"
#include "DockComponentsFactory.h"
#include "ads_globals.h" #include "ads_globals.h"
@@ -81,6 +82,7 @@ struct DockWidgetPrivate
QSize ToolBarIconSizeFloating = QSize(24, 24); QSize ToolBarIconSizeFloating = QSize(24, 24);
bool IsFloatingTopLevel = false; bool IsFloatingTopLevel = false;
QList<QAction*> TitleBarActions; QList<QAction*> TitleBarActions;
CDockWidget::eMinimumSizeHintMode MinimumSizeHintMode = CDockWidget::MinimumSizeHintFromDockWidget;
/** /**
* Private data constructor * Private data constructor
@@ -172,6 +174,12 @@ void DockWidgetPrivate::updateParentDockArea()
return; return;
} }
// we don't need to change the current tab if the
// current dock widget is not the one being closed
if (DockArea->currentDockWidget() != _this){
return;
}
auto NextDockWidget = DockArea->nextOpenDockWidget(_this); auto NextDockWidget = DockArea->nextOpenDockWidget(_this);
if (NextDockWidget) if (NextDockWidget)
{ {
@@ -220,7 +228,7 @@ CDockWidget::CDockWidget(const QString &title, QWidget *parent) :
setWindowTitle(title); setWindowTitle(title);
setObjectName(title); setObjectName(title);
d->TabWidget = new CDockWidgetTab(this); d->TabWidget = componentsFactory()->createDockWidgetTab(this);
d->ToggleViewAction = new QAction(title, this); d->ToggleViewAction = new QAction(title, this);
d->ToggleViewAction->setCheckable(true); d->ToggleViewAction->setCheckable(true);
connect(d->ToggleViewAction, SIGNAL(triggered(bool)), this, connect(d->ToggleViewAction, SIGNAL(triggered(bool)), this,
@@ -417,6 +425,13 @@ void CDockWidget::setToggleViewActionMode(eToggleViewActionMode Mode)
} }
//============================================================================
void CDockWidget::setMinimumSizeHintMode(eMinimumSizeHintMode Mode)
{
d->MinimumSizeHintMode = Mode;
}
//============================================================================ //============================================================================
void CDockWidget::toggleView(bool Open) void CDockWidget::toggleView(bool Open)
{ {
@@ -746,7 +761,14 @@ void CDockWidget::setClosedState(bool Closed)
//============================================================================ //============================================================================
QSize CDockWidget::minimumSizeHint() const QSize CDockWidget::minimumSizeHint() const
{ {
return QSize(60, 40); if (d->MinimumSizeHintMode == CDockWidget::MinimumSizeHintFromDockWidget || !d->Widget)
{
return QSize(60, 40);
}
else
{
return d->Widget->minimumSizeHint();
}
} }
@@ -808,6 +830,7 @@ bool CDockWidget::closeDockWidgetInternal(bool ForceClose)
} }
} }
deleteDockWidget(); deleteDockWidget();
emit closed();
} }
else else
{ {

View File

@@ -189,6 +189,23 @@ public:
ForceNoScrollArea ForceNoScrollArea
}; };
/**
* The mode of the minimumSizeHint() that is returned by the DockWidget
* minimumSizeHint() function.
* To ensure, that a dock widget does not block resizing, the dock widget
* reimplements minimumSizeHint() function to return a very small minimum
* size hint. If you would like to adhere the minimumSizeHint() from the
* content widget, the set the minimumSizeHintMode() to
* MinimumSizeHintFromContent.
*/
enum eMinimumSizeHintMode
{
MinimumSizeHintFromDockWidget,
MinimumSizeHintFromContent
};
/** /**
* This mode configures the behavior of the toggle view action. * This mode configures the behavior of the toggle view action.
* If the mode if ActionModeToggle, then the toggle view action is * If the mode if ActionModeToggle, then the toggle view action is
@@ -225,7 +242,8 @@ public:
virtual ~CDockWidget(); virtual ~CDockWidget();
/** /**
* We return a fixed minimum size hint for all dock widgets * We return a fixed minimum size hint or the size hint of the content
* widget if minimum size hint mode is MinimumSizeHintFromContent
*/ */
virtual QSize minimumSizeHint() const override; virtual QSize minimumSizeHint() const override;
@@ -334,6 +352,13 @@ public:
*/ */
void setToggleViewActionMode(eToggleViewActionMode Mode); void setToggleViewActionMode(eToggleViewActionMode Mode);
/**
* Configures the minimum size hint that is returned by the
* minimumSizeHint() function.
* \see eMinimumSizeHintMode for a detailed description
*/
void setMinimumSizeHintMode(eMinimumSizeHintMode Mode);
/** /**
* Sets the dock widget icon that is shown in tabs and in toggle view * Sets the dock widget icon that is shown in tabs and in toggle view
* actions * actions
@@ -346,13 +371,10 @@ public:
QIcon icon() const; QIcon icon() const;
/** /**
* If the WithToolBar layout flag is enabled, then this function returns
* the dock widget toolbar. If the flag is disabled, the function returns
* a nullptr.
* This function returns the dock widget top tool bar. * This function returns the dock widget top tool bar.
* If no toolbar is assigned, this function returns nullptr. To get a vaild * If no toolbar is assigned, this function returns nullptr. To get a vaild
* toolbar you either need to create a default empty toolbar via * toolbar you either need to create a default empty toolbar via
* createDefaultToolBar() function or you need to assign you custom * createDefaultToolBar() function or you need to assign your custom
* toolbar via setToolBar(). * toolbar via setToolBar().
*/ */
QToolBar* toolBar() const; QToolBar* toolBar() const;

View File

@@ -68,6 +68,7 @@ struct DockWidgetTabPrivate
QLabel* IconLabel = nullptr; QLabel* IconLabel = nullptr;
tTabLabel* TitleLabel; tTabLabel* TitleLabel;
QPoint GlobalDragStartMousePosition; QPoint GlobalDragStartMousePosition;
QPoint DragStartMousePosition;
bool IsActiveTab = false; bool IsActiveTab = false;
CDockAreaWidget* DockArea = nullptr; CDockAreaWidget* DockArea = nullptr;
eDragState DragState = DraggingInactive; eDragState DragState = DraggingInactive;
@@ -150,6 +151,15 @@ struct DockWidgetTabPrivate
return w; return w;
} }
} }
/**
* Saves the drag start position in global and local coordinates
*/
void saveDragStartMousePosition(const QPoint& GlobalPos)
{
GlobalDragStartMousePosition = GlobalPos;
DragStartMousePosition = _this->mapFromGlobal(GlobalPos);
}
}; };
// struct DockWidgetTabPrivate // struct DockWidgetTabPrivate
@@ -205,6 +215,8 @@ void DockWidgetTabPrivate::moveTab(QMouseEvent* ev)
QPoint Distance = ev->globalPos() - GlobalDragStartMousePosition; QPoint Distance = ev->globalPos() - GlobalDragStartMousePosition;
Distance.setY(0); Distance.setY(0);
auto TargetPos = Distance + TabDragStartPosition; auto TargetPos = Distance + TabDragStartPosition;
TargetPos.rx() = qMax(TargetPos.x(), 0);
TargetPos.rx() = qMin(_this->parentWidget()->rect().right() - _this->width() + 1, TargetPos.rx());
_this->move(TargetPos); _this->move(TargetPos);
_this->raise(); _this->raise();
} }
@@ -229,7 +241,6 @@ bool DockWidgetTabPrivate::startFloating(eDragState DraggingState)
ADS_PRINT("startFloating"); ADS_PRINT("startFloating");
DragState = DraggingState; DragState = DraggingState;
auto DragStartMousePosition = _this->mapFromGlobal(GlobalDragStartMousePosition);
QSize Size = DockArea->size(); QSize Size = DockArea->size();
IFloatingWidget* FloatingWidget = nullptr; IFloatingWidget* FloatingWidget = nullptr;
bool OpaqueUndocking = CDockManager::configFlags().testFlag(CDockManager::OpaqueUndocking) || bool OpaqueUndocking = CDockManager::configFlags().testFlag(CDockManager::OpaqueUndocking) ||
@@ -287,7 +298,7 @@ void CDockWidgetTab::mousePressEvent(QMouseEvent* ev)
if (ev->button() == Qt::LeftButton) if (ev->button() == Qt::LeftButton)
{ {
ev->accept(); ev->accept();
d->GlobalDragStartMousePosition = ev->globalPos(); d->saveDragStartMousePosition(ev->globalPos());
d->DragState = DraggingMousePressed; d->DragState = DraggingMousePressed;
emit clicked(); emit clicked();
return; return;
@@ -304,6 +315,7 @@ void CDockWidgetTab::mouseReleaseEvent(QMouseEvent* ev)
{ {
auto CurrentDragState = d->DragState; auto CurrentDragState = d->DragState;
d->GlobalDragStartMousePosition = QPoint(); d->GlobalDragStartMousePosition = QPoint();
d->DragStartMousePosition = QPoint();
d->DragState = DraggingInactive; d->DragState = DraggingInactive;
switch (CurrentDragState) switch (CurrentDragState)
@@ -354,9 +366,11 @@ void CDockWidgetTab::mouseMoveEvent(QMouseEvent* ev)
d->moveTab(ev); d->moveTab(ev);
} }
auto MappedPos = mapToParent(ev->pos());
bool MouseOutsideBar = (MappedPos.x() < 0) || (MappedPos.x() > parentWidget()->rect().right());
// Maybe a fixed drag distance is better here ? // Maybe a fixed drag distance is better here ?
int DragDistanceY = qAbs(d->GlobalDragStartMousePosition.y() - ev->globalPos().y()); int DragDistanceY = qAbs(d->GlobalDragStartMousePosition.y() - ev->globalPos().y());
if (DragDistanceY >= CDockManager::startDragDistance()) if (DragDistanceY >= CDockManager::startDragDistance() || MouseOutsideBar)
{ {
// If this is the last dock area in a dock container with only // If this is the last dock area in a dock container with only
// one single dock widget it does not make sense to move it to a new // one single dock widget it does not make sense to move it to a new
@@ -368,14 +382,19 @@ void CDockWidgetTab::mouseMoveEvent(QMouseEvent* ev)
return; return;
} }
// Floating is only allowed for widgets that are movable
if (d->DockWidget->features().testFlag(CDockWidget::DockWidgetFloatable)) // Floating is only allowed for widgets that are floatable
// If we do non opaque undocking, then can create the drag preview
// if the widget is movable.
auto Features = d->DockWidget->features();
if (Features.testFlag(CDockWidget::DockWidgetFloatable)
|| (Features.testFlag(CDockWidget::DockWidgetMovable) && !CDockManager::testConfigFlag(CDockManager::OpaqueUndocking)))
{ {
// If we undock, we need to restore the initial position of this // If we undock, we need to restore the initial position of this
// tab because it looks strange if it remains on its dragged position // tab because it looks strange if it remains on its dragged position
if (d->isDraggingState(DraggingTab) && !CDockManager::configFlags().testFlag(CDockManager::OpaqueUndocking)) if (d->isDraggingState(DraggingTab) && !CDockManager::configFlags().testFlag(CDockManager::OpaqueUndocking))
{ {
this->move(d->TabDragStartPosition); parentWidget()->layout()->update();
} }
d->startFloating(); d->startFloating();
} }
@@ -407,7 +426,7 @@ void CDockWidgetTab::contextMenuEvent(QContextMenuEvent* ev)
return; return;
} }
d->GlobalDragStartMousePosition = ev->globalPos(); d->saveDragStartMousePosition(ev->globalPos());
QMenu Menu(this); QMenu Menu(this);
const bool isFloatable = d->DockWidget->features().testFlag(CDockWidget::DockWidgetFloatable); const bool isFloatable = d->DockWidget->features().testFlag(CDockWidget::DockWidgetFloatable);
@@ -537,7 +556,7 @@ void CDockWidgetTab::mouseDoubleClickEvent(QMouseEvent *event)
if ((!d->DockArea->dockContainer()->isFloating() || d->DockArea->dockWidgetsCount() > 1) if ((!d->DockArea->dockContainer()->isFloating() || d->DockArea->dockWidgetsCount() > 1)
&& d->DockWidget->features().testFlag(CDockWidget::DockWidgetFloatable)) && d->DockWidget->features().testFlag(CDockWidget::DockWidgetFloatable))
{ {
d->GlobalDragStartMousePosition = event->globalPos(); d->saveDragStartMousePosition(event->globalPos());
d->startFloating(DraggingInactive); d->startFloating(DraggingInactive);
} }
@@ -580,7 +599,8 @@ void CDockWidgetTab::detachDockWidget()
{ {
return; return;
} }
d->GlobalDragStartMousePosition = QCursor::pos();
d->saveDragStartMousePosition(QCursor::pos());
d->startFloating(DraggingInactive); d->startFloating(DraggingInactive);
} }

View File

@@ -94,11 +94,6 @@ public:
*/ */
void setActiveTab(bool active); void setActiveTab(bool active);
/**
* Returns the dock widget this title widget belongs to
*/
CDockWidget* dockWidget() const;
/** /**
* Sets the dock area widget the dockWidget returned by dockWidget() * Sets the dock area widget the dockWidget returned by dockWidget()
* function belongs to. * function belongs to.
@@ -112,6 +107,11 @@ public:
*/ */
CDockAreaWidget* dockAreaWidget() const; CDockAreaWidget* dockAreaWidget() const;
/**
* Returns the dock widget this title widget belongs to
*/
CDockWidget* dockWidget() const;
/** /**
* Sets the icon to show in title bar * Sets the icon to show in title bar
*/ */

View File

@@ -38,6 +38,7 @@
#include <QDebug> #include <QDebug>
#include <QAbstractButton> #include <QAbstractButton>
#include <QElapsedTimer> #include <QElapsedTimer>
#include <QTime>
#include "DockContainerWidget.h" #include "DockContainerWidget.h"
#include "DockAreaWidget.h" #include "DockAreaWidget.h"
@@ -66,6 +67,7 @@ struct FloatingDockContainerPrivate
QPoint DragStartMousePosition; QPoint DragStartMousePosition;
CDockContainerWidget *DropContainer = nullptr; CDockContainerWidget *DropContainer = nullptr;
CDockAreaWidget *SingleDockArea = nullptr; CDockAreaWidget *SingleDockArea = nullptr;
QPoint DragStartPos;
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
QWidget* MouseEventHandler = nullptr; QWidget* MouseEventHandler = nullptr;
CFloatingWidgetTitleBar* TitleBar = nullptr; CFloatingWidgetTitleBar* TitleBar = nullptr;
@@ -79,6 +81,14 @@ struct FloatingDockContainerPrivate
void titleMouseReleaseEvent(); void titleMouseReleaseEvent();
void updateDropOverlays(const QPoint &GlobalPos); void updateDropOverlays(const QPoint &GlobalPos);
/**
* Returns true if the given config flag is set
*/
static bool testConfigFlag(CDockManager::eConfigFlag Flag)
{
return CDockManager::configFlags().testFlag(Flag);
}
/** /**
* Tests is a certain state is active * Tests is a certain state is active
*/ */
@@ -100,6 +110,40 @@ struct FloatingDockContainerPrivate
_this->setWindowTitle(Text); _this->setWindowTitle(Text);
#endif #endif
} }
/**
* Reflect the current dock widget title in the floating widget windowTitle()
* depending on the CDockManager::FloatingContainerHasWidgetTitle flag
*/
void reflectCurrentWidget(CDockWidget* CurrentWidget)
{
// reflect CurrentWidget's title if configured to do so, otherwise display application name as window title
if (testConfigFlag(CDockManager::FloatingContainerHasWidgetTitle))
{
setWindowTitle(CurrentWidget->windowTitle());
}
else
{
setWindowTitle(qApp->applicationDisplayName());
}
// reflect CurrentWidget's icon if configured to do so, otherwise display application icon as window icon
QIcon CurrentWidgetIcon = CurrentWidget->icon();
if (testConfigFlag(CDockManager::FloatingContainerHasWidgetIcon)
&& !CurrentWidgetIcon.isNull())
{
_this->setWindowIcon(CurrentWidget->icon());
}
else
{
_this->setWindowIcon(QApplication::windowIcon());
}
}
/**
* Handles escape key press when dragging around the floating widget
*/
void handleEscapeKey();
}; };
// struct FloatingDockContainerPrivate // struct FloatingDockContainerPrivate
@@ -231,6 +275,17 @@ void FloatingDockContainerPrivate::updateDropOverlays(const QPoint &GlobalPos)
} }
} }
//============================================================================
void FloatingDockContainerPrivate::handleEscapeKey()
{
ADS_PRINT("FloatingDockContainerPrivate::handleEscapeKey()");
setState(DraggingInactive);
DockManager->containerOverlay()->hideOverlay();
DockManager->dockAreaOverlay()->hideOverlay();
}
//============================================================================ //============================================================================
CFloatingDockContainer::CFloatingDockContainer(CDockManager *DockManager) : CFloatingDockContainer::CFloatingDockContainer(CDockManager *DockManager) :
tFloatingWidgetBase(DockManager), tFloatingWidgetBase(DockManager),
@@ -330,6 +385,9 @@ void CFloatingDockContainer::moveEvent(QMoveEvent *event)
switch (d->DraggingState) switch (d->DraggingState)
{ {
case DraggingMousePressed: case DraggingMousePressed:
#ifdef Q_OS_WIN
qApp->installEventFilter(this);
#endif
d->setState(DraggingFloatingWidget); d->setState(DraggingFloatingWidget);
d->updateDropOverlays(QCursor::pos()); d->updateDropOverlays(QCursor::pos());
break; break;
@@ -347,6 +405,8 @@ void CFloatingDockContainer::moveEvent(QMoveEvent *event)
default: default:
break; break;
} }
} }
//============================================================================ //============================================================================
@@ -410,6 +470,7 @@ void CFloatingDockContainer::showEvent(QShowEvent *event)
Super::showEvent(event); Super::showEvent(event);
} }
//============================================================================ //============================================================================
bool CFloatingDockContainer::event(QEvent *e) bool CFloatingDockContainer::event(QEvent *e)
{ {
@@ -425,21 +486,17 @@ bool CFloatingDockContainer::event(QEvent *e)
// It is really great to work around the whole NonClientMouseArea // It is really great to work around the whole NonClientMouseArea
// bugs // bugs
#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 2)) #if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 2))
if (e->type() if (e->type() == QEvent::NonClientAreaMouseButtonPress /*&& QGuiApplication::mouseButtons().testFlag(Qt::LeftButton)*/)
== QEvent::NonClientAreaMouseButtonPress /*&& QGuiApplication::mouseButtons().testFlag(Qt::LeftButton)*/)
{
ADS_PRINT("FloatingWidget::event Event::NonClientAreaMouseButtonPress" << e->type());
d->setState(DraggingMousePressed);
}
#else #else
if (e->type() == QEvent::NonClientAreaMouseButtonPress && QGuiApplication::mouseButtons().testFlag(Qt::LeftButton)) if (e->type() == QEvent::NonClientAreaMouseButtonPress && QGuiApplication::mouseButtons().testFlag(Qt::LeftButton))
#endif
{ {
ADS_PRINT("FloatingWidget::event Event::NonClientAreaMouseButtonPress" << e->type()); ADS_PRINT("FloatingWidget::event Event::NonClientAreaMouseButtonPress" << e->type());
d->DragStartPos = pos();
d->setState(DraggingMousePressed); d->setState(DraggingMousePressed);
} }
#endif
} }
break; break;
case DraggingMousePressed: case DraggingMousePressed:
switch (e->type()) switch (e->type())
@@ -482,12 +539,49 @@ bool CFloatingDockContainer::event(QEvent *e)
} }
#if (ADS_DEBUG_LEVEL > 0) #if (ADS_DEBUG_LEVEL > 0)
qDebug() << "CFloatingDockContainer::event " << e->type(); qDebug() << QTime::currentTime() << "CFloatingDockContainer::event " << e->type();
#endif #endif
return QWidget::event(e); return QWidget::event(e);
} }
//============================================================================
bool CFloatingDockContainer::eventFilter(QObject *watched, QEvent *e)
{
Q_UNUSED(watched);
// I have not found a way to detect non client area key press events to
// handle escape key presses. On Windows, if the escape key is pressed while
// dragging around a widget, the widget position is reset to its start position
// which in turn generates a QEvent::NonClientAreaMouseButtonRelease event
// if the mouse is outside of the widget after the move to its initial position
// or a QEvent::MouseButtonRelease event, if the mouse is inside of teh widget
// after the position has been reset.
// So we can install an event filter on the application to get these events
// here to properly cancel dragging and hide the overlays.
// If we are in DraggingFloatingWidget state, it means the widget
// has been dragged already but if the position is the same like
// the start position, then this is an indication that the escape
// key has been pressed.
if (e->type() == QEvent::MouseButtonRelease || e->type() == QEvent::NonClientAreaMouseButtonRelease)
{
ADS_PRINT("CFloatingDockContainer::eventFilter QEvent::MouseButtonRelease or"
"QEvent::NonClientAreaMouseButtonRelease" << "d->DragggingState " << d->DraggingState);
qApp->removeEventFilter(this);
if (d->DragStartPos == pos())
{
d->handleEscapeKey();
return true;
}
return false;
}
#if (ADS_DEBUG_LEVEL > 0)
qDebug() << QTime::currentTime() << "CFloatingDockContainer::eventFilter " << e->type();
#endif
return false;
}
//============================================================================ //============================================================================
void CFloatingDockContainer::startFloating(const QPoint &DragStartMousePos, void CFloatingDockContainer::startFloating(const QPoint &DragStartMousePos,
const QSize &Size, eDragState DragState, QWidget *MouseEventHandler) const QSize &Size, eDragState DragState, QWidget *MouseEventHandler)
@@ -537,8 +631,8 @@ void CFloatingDockContainer::onDockAreasAddedOrRemoved()
if (TopLevelDockArea) if (TopLevelDockArea)
{ {
d->SingleDockArea = TopLevelDockArea; d->SingleDockArea = TopLevelDockArea;
d->setWindowTitle( CDockWidget* CurrentWidget = d->SingleDockArea->currentDockWidget();
d->SingleDockArea->currentDockWidget()->windowTitle()); d->reflectCurrentWidget(CurrentWidget);
connect(d->SingleDockArea, SIGNAL(currentChanged(int)), this, connect(d->SingleDockArea, SIGNAL(currentChanged(int)), this,
SLOT(onDockAreaCurrentChanged(int))); SLOT(onDockAreaCurrentChanged(int)));
} }
@@ -551,6 +645,7 @@ void CFloatingDockContainer::onDockAreasAddedOrRemoved()
d->SingleDockArea = nullptr; d->SingleDockArea = nullptr;
} }
d->setWindowTitle(qApp->applicationDisplayName()); d->setWindowTitle(qApp->applicationDisplayName());
setWindowIcon(QApplication::windowIcon());
} }
} }
@@ -560,11 +655,13 @@ void CFloatingDockContainer::updateWindowTitle()
auto TopLevelDockArea = d->DockContainer->topLevelDockArea(); auto TopLevelDockArea = d->DockContainer->topLevelDockArea();
if (TopLevelDockArea) if (TopLevelDockArea)
{ {
d->setWindowTitle(TopLevelDockArea->currentDockWidget()->windowTitle()); CDockWidget* CurrentWidget = TopLevelDockArea->currentDockWidget();
d->reflectCurrentWidget(CurrentWidget);
} }
else else
{ {
d->setWindowTitle(qApp->applicationDisplayName()); d->setWindowTitle(qApp->applicationDisplayName());
setWindowIcon(QApplication::windowIcon());
} }
} }
@@ -572,7 +669,8 @@ void CFloatingDockContainer::updateWindowTitle()
void CFloatingDockContainer::onDockAreaCurrentChanged(int Index) void CFloatingDockContainer::onDockAreaCurrentChanged(int Index)
{ {
Q_UNUSED(Index); Q_UNUSED(Index);
d->setWindowTitle(d->SingleDockArea->currentDockWidget()->windowTitle()); CDockWidget* CurrentWidget = d->SingleDockArea->currentDockWidget();
d->reflectCurrentWidget(CurrentWidget);
} }
//============================================================================ //============================================================================

View File

@@ -180,6 +180,7 @@ protected: // reimplements QWidget
virtual void closeEvent(QCloseEvent *event) override; virtual void closeEvent(QCloseEvent *event) override;
virtual void hideEvent(QHideEvent *event) override; virtual void hideEvent(QHideEvent *event) override;
virtual void showEvent(QShowEvent *event) override; virtual void showEvent(QShowEvent *event) override;
virtual bool eventFilter(QObject *watched, QEvent *event) override;
public: public:
using Super = QWidget; using Super = QWidget;

View File

@@ -40,6 +40,7 @@ struct FloatingDragPreviewPrivate
qreal WindowOpacity; qreal WindowOpacity;
bool Hidden = false; bool Hidden = false;
QPixmap ContentPreviewPixmap; QPixmap ContentPreviewPixmap;
bool Canceled = false;
/** /**
@@ -59,6 +60,7 @@ struct FloatingDragPreviewPrivate
*/ */
void cancelDragging() void cancelDragging()
{ {
Canceled = true;
emit _this->draggingCanceled(); emit _this->draggingCanceled();
DockManager->containerOverlay()->hideOverlay(); DockManager->containerOverlay()->hideOverlay();
DockManager->dockAreaOverlay()->hideOverlay(); DockManager->dockAreaOverlay()->hideOverlay();
@@ -85,11 +87,6 @@ void FloatingDragPreviewPrivate::updateDropOverlays(const QPoint &GlobalPos)
continue; continue;
} }
/*if (DockContainer == ContainerWidget)
{
continue;
}*/
QPoint MappedPos = ContainerWidget->mapFromGlobal(GlobalPos); QPoint MappedPos = ContainerWidget->mapFromGlobal(GlobalPos);
if (ContainerWidget->rect().contains(MappedPos)) if (ContainerWidget->rect().contains(MappedPos))
{ {
@@ -148,6 +145,14 @@ void FloatingDragPreviewPrivate::updateDropOverlays(const QPoint &GlobalPos)
else else
{ {
DockAreaOverlay->hideOverlay(); DockAreaOverlay->hideOverlay();
// If there is only one single visible dock area in a container, then
// it does not make sense to show a dock overlay because the dock area
// would be removed and inserted at the same position
if (VisibleDockAreas <= 1)
{
ContainerOverlay->hide();
}
if (DockArea == ContentSourceArea && InvalidDockWidgetArea == ContainerDropArea) if (DockArea == ContentSourceArea && InvalidDockWidgetArea == ContainerDropArea)
{ {
DropContainer = nullptr; DropContainer = nullptr;
@@ -207,9 +212,9 @@ CFloatingDragPreview::CFloatingDragPreview(QWidget* Content, QWidget* parent) :
connect(qApp, SIGNAL(applicationStateChanged(Qt::ApplicationState)), connect(qApp, SIGNAL(applicationStateChanged(Qt::ApplicationState)),
SLOT(onApplicationStateChanged(Qt::ApplicationState))); SLOT(onApplicationStateChanged(Qt::ApplicationState)));
// We need to install an event filter for the given Content // The only safe way to receive escape key presses is to install an event
// widget to receive the escape key press // filter for the application object
Content->installEventFilter(this); qApp->installEventFilter(this);
} }
@@ -281,35 +286,46 @@ void CFloatingDragPreview::moveEvent(QMoveEvent *event)
void CFloatingDragPreview::finishDragging() void CFloatingDragPreview::finishDragging()
{ {
ADS_PRINT("CFloatingDragPreview::finishDragging"); ADS_PRINT("CFloatingDragPreview::finishDragging");
auto DockDropArea = d->DockManager->dockAreaOverlay()->dropAreaUnderCursor(); auto DockDropArea = d->DockManager->dockAreaOverlay()->visibleDropAreaUnderCursor();
auto ContainerDropArea = d->DockManager->containerOverlay()->dropAreaUnderCursor(); auto ContainerDropArea = d->DockManager->containerOverlay()->visibleDropAreaUnderCursor();
bool DropPossible = (DockDropArea != InvalidDockWidgetArea) || (ContainerDropArea != InvalidDockWidgetArea); if (d->DropContainer && (DockDropArea != InvalidDockWidgetArea))
if (d->DropContainer && DropPossible)
{ {
d->DropContainer->dropWidget(d->Content, QCursor::pos()); d->DropContainer->dropWidget(d->Content, DockDropArea, d->DropContainer->dockAreaAt(QCursor::pos()));
}
else if (d->DropContainer && (ContainerDropArea != InvalidDockWidgetArea))
{
d->DropContainer->dropWidget(d->Content, ContainerDropArea, nullptr);
} }
else else
{ {
CDockWidget* DockWidget = qobject_cast<CDockWidget*>(d->Content); CDockWidget* DockWidget = qobject_cast<CDockWidget*>(d->Content);
CFloatingDockContainer* FloatingWidget; CFloatingDockContainer* FloatingWidget = nullptr;
if (DockWidget)
if (DockWidget && DockWidget->features().testFlag(CDockWidget::DockWidgetFloatable))
{ {
FloatingWidget = new CFloatingDockContainer(DockWidget); FloatingWidget = new CFloatingDockContainer(DockWidget);
} }
else else
{ {
CDockAreaWidget* DockArea = qobject_cast<CDockAreaWidget*>(d->Content); CDockAreaWidget* DockArea = qobject_cast<CDockAreaWidget*>(d->Content);
FloatingWidget = new CFloatingDockContainer(DockArea); if (DockArea->features().testFlag(CDockWidget::DockWidgetFloatable))
{
FloatingWidget = new CFloatingDockContainer(DockArea);
}
} }
FloatingWidget->setGeometry(this->geometry());
FloatingWidget->show(); if (FloatingWidget)
if (!CDockManager::configFlags().testFlag(CDockManager::DragPreviewHasWindowFrame))
{ {
QApplication::processEvents(); FloatingWidget->setGeometry(this->geometry());
int FrameHeight = FloatingWidget->frameGeometry().height() - FloatingWidget->geometry().height(); FloatingWidget->show();
QRect FixedGeometry = this->geometry(); if (!CDockManager::configFlags().testFlag(CDockManager::DragPreviewHasWindowFrame))
FixedGeometry.adjust(0, FrameHeight, 0, 0); {
FloatingWidget->setGeometry(FixedGeometry); QApplication::processEvents();
int FrameHeight = FloatingWidget->frameGeometry().height() - FloatingWidget->geometry().height();
QRect FixedGeometry = this->geometry();
FixedGeometry.adjust(0, FrameHeight, 0, 0);
FloatingWidget->setGeometry(FixedGeometry);
}
} }
} }
@@ -368,12 +384,12 @@ void CFloatingDragPreview::onApplicationStateChanged(Qt::ApplicationState state)
bool CFloatingDragPreview::eventFilter(QObject *watched, QEvent *event) bool CFloatingDragPreview::eventFilter(QObject *watched, QEvent *event)
{ {
Q_UNUSED(watched); Q_UNUSED(watched);
if (event->type() == QEvent::KeyPress) if (!d->Canceled && event->type() == QEvent::KeyPress)
{ {
QKeyEvent* e = static_cast<QKeyEvent*>(event); QKeyEvent* e = static_cast<QKeyEvent*>(event);
if (e->key() == Qt::Key_Escape) if (e->key() == Qt::Key_Escape)
{ {
d->Content->removeEventFilter(this); watched->removeEventFilter(this);
d->cancelDragging(); d->cancelDragging();
} }
} }
@@ -383,7 +399,6 @@ bool CFloatingDragPreview::eventFilter(QObject *watched, QEvent *event)
} // namespace ads } // namespace ads
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------

View File

@@ -43,7 +43,8 @@ HEADERS += \
DockSplitter.h \ DockSplitter.h \
DockAreaTitleBar.h \ DockAreaTitleBar.h \
ElidingLabel.h \ ElidingLabel.h \
IconProvider.h IconProvider.h \
DockComponentsFactory.h
SOURCES += \ SOURCES += \
@@ -61,7 +62,8 @@ SOURCES += \
DockSplitter.cpp \ DockSplitter.cpp \
DockAreaTitleBar.cpp \ DockAreaTitleBar.cpp \
ElidingLabel.cpp \ ElidingLabel.cpp \
IconProvider.cpp IconProvider.cpp \
DockComponentsFactory.cpp
unix { unix {