Compare commits

..

66 Commits

Author SHA1 Message Date
githubuser0xFFFF
78e81b02fd Update README.md 2017-08-17 13:35:03 +02:00
Uwe Kindler
52c23dafd5 Changed license to LGPL v2.1 2017-06-09 22:04:02 +02:00
Uwe Kindler
bc37a2788e Fixed placing of drop overlay cross, resizing of floating widget to drop
area rectangle size prior to insertion
2017-04-11 23:26:33 +02:00
Uwe Kindler
051c379e4a Fixed startFloating() for FloatingDockContainer if only one DockArea is visble 2017-04-01 22:31:24 +02:00
Uwe Kindler
844c853768 Fixed a bug with shoing and hiding empty splitters, added some pictures to main page 2017-03-29 15:45:36 +02:00
Uwe Kindler
990d3235c5 Removed external dependencies for build, fixed build system, updated preview images to reflect the current state of the library 2017-03-29 12:18:49 +02:00
Uwe Kindler
fd76e9e62b Improved code documentation 2017-03-29 11:09:05 +02:00
Uwe Kindler
a3ff1ae8ee Fixed restore procedure and deletion of floating widgts 2017-03-28 22:38:47 +02:00
Uwe Kindler
17dff82d12 Moved stylesheet from demo into main library 2017-03-28 13:05:18 +02:00
Uwe Kindler
9af86c4136 Replaced std:cout based debug output with qDebug() 2017-03-28 12:01:27 +02:00
Uwe Kindler
aa8a52b845 Fixed bug with FloatingWidget deletion, fixed handling of unassigned DockWidgets after restoreState() call 2017-03-28 10:57:03 +02:00
Uwe Kindler
9adc524a42 Fixed creation of superfluous splitter when docking into container 2017-03-28 08:48:44 +02:00
Uwe Kindler
549646d113 Fixed some small bugs in drop indicator painting 2017-03-27 16:16:22 +02:00
Uwe Kindler
7ba3c1f244 Improved painting of drop indicators 2017-03-27 15:56:15 +02:00
Uwe Kindler
c532c24f79 Improved indicator pixmaps 2017-03-27 13:56:14 +02:00
Uwe Kindler
c5ea5c80b1 Fixed display of drop overlay 2017-03-27 13:18:16 +02:00
Uwe Kindler
1b1c636107 Improved serialization support 2017-03-27 10:41:27 +02:00
Uwe Kindler
2277ba3630 Added initial support for serialization 2017-03-24 16:17:55 +01:00
Uwe Kindler
5dcd15e2b9 Implemented RootSplitter 2017-03-24 12:54:43 +01:00
Uwe Kindler
a652deba71 Started inserting RootSplitter 2017-03-24 10:18:25 +01:00
Uwe Kindler
1cd1e7d6ec Started implementing serialization and deserialization 2017-03-23 15:57:15 +01:00
Uwe Kindler
16bd1a3bd2 Implemented proper hiding and showing of dock widgets 2017-03-23 10:23:53 +01:00
Uwe Kindler
b6ee26adc2 Improved hide / show functionality of dock widgets 2017-03-22 16:08:44 +01:00
Uwe Kindler
0d6f469a36 Startet implementing and refactoring hide show code 2017-03-21 11:27:26 +01:00
Uwe Kindler
338b10695a Merge branch 'master' of https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System 2017-03-13 15:34:49 +01:00
Uwe Kindler
239bd4781b Added signals for dock area added and removed 2017-03-13 15:34:40 +01:00
Uwe Kindler
97571e4be8 Refactoring of project structure 2017-03-12 21:41:50 +01:00
Uwe Kindler
c57d14d0d7 Implemented restoring of hidden floating widgets 2017-03-11 21:38:31 +01:00
githubuser0xFFFF
a0849dd19f Set theme jekyll-theme-midnight 2017-03-07 22:15:43 +01:00
Uwe Kindler
52aedfed31 Improved design of drop overlay cross 2017-03-03 15:47:03 +01:00
Uwe Kindler
fade9d7b7c Improved handling of tabified widgets, improced CSS to decrease height of title bars 2017-03-03 14:27:37 +01:00
Uwe Kindler
a8ccbfa407 Implemented support for dropping into a dock are 2017-03-03 13:42:41 +01:00
Uwe Kindler
f9f064dd71 Implemented support for container docking 2017-03-02 15:49:53 +01:00
Uwe Kindler
fac5661280 Improved support when moving widgets out of FloatingWidget 2017-03-02 11:43:48 +01:00
Uwe Kindler
df85c8bee0 Initial and basic implementation of dock overlays 2017-03-01 16:13:37 +01:00
Uwe Kindler
e0a442263e Implemented support for dragging a complete dock area including all dock widgets 2017-03-01 14:36:46 +01:00
Uwe Kindler
2c9ceb8645 Implemented initial support for floating widgets 2017-03-01 14:09:56 +01:00
Uwe Kindler
b31c5f1f3d Added initial support for docking into dock areas 2017-02-28 22:41:34 +01:00
Uwe Kindler
c14352e7c1 Implemented proper tab handling 2017-02-28 15:23:02 +01:00
Uwe Kindler
f0584ff0c5 COntinued implementation of new advanced docking system, added stylesheet 2017-02-27 14:15:20 +01:00
Uwe Kindler
4a0c176327 Implemented support for adding dock widgets to DockContainerWidget 2017-02-26 18:13:56 +01:00
Uwe Kindler
94a6f5e2f1 Started implementing clean improved docking system 2017-02-24 22:44:02 +01:00
Uwe Kindler
842cef2675 Remove macro usage for ads namespace 2017-02-23 22:26:13 +01:00
Uwe Kindler
ca64286117 Moved include files from include to src directory 2017-02-23 21:46:29 +01:00
Uwe Kindler
5dc935f650 Started refactoring of SectionConetent into SectionContentWidget 2017-02-23 21:41:51 +01:00
Uwe Kindler
ad49745b18 Refactorings to improve code, started to refactor secion content 2017-02-22 22:33:48 +01:00
Uwe Kindler
938afb7357 Implemented support for dropping mutiple sections into sections 2017-02-20 23:57:32 +01:00
Uwe Kindler
0edc1b4499 Fixed typo in README.md 2017-02-20 10:08:19 +01:00
Uwe Kindler
d0fc4ace07 Switched license to GPLv3 2017-02-20 10:06:55 +01:00
Uwe Kindler
70a7a7b352 Started implementing support for docking multiple sections into section 2017-02-20 07:36:27 +01:00
Uwe Kindler
b93d2fbd48 Some cleanup and support for dropping floating window with multiple setcion into section 2017-02-17 07:40:12 +01:00
Uwe Kindler
635b96a2ae Fixed some issues with mouse double clicks, remove unused and supefluous code 2017-02-15 21:54:26 +01:00
Uwe Kindler
72fc9cd79a Implemented initial support for dropping multiple sections at the same time 2017-02-14 23:55:38 +01:00
Uwe Kindler
268c49af3b Added debug output 2017-02-12 20:06:45 +01:00
Uwe Kindler
025bedfb65 Preparing dropping of section widgets with multiple sections 2017-02-12 19:59:29 +01:00
Uwe Kindler
df97d17844 Fixed movement of witget on start of floating 2017-02-11 22:05:23 +01:00
Uwe Kindler
2bf1a51627 Fixed container removal from MainCOntainerWidget 2017-02-10 21:23:14 +01:00
Uwe Kindler
a4de5c5560 Added suppport for floating widgets with native toolbar to support the windows desktop docking and split system 2017-02-08 23:03:13 +01:00
Uwe Kindler
0716020ab4 Created CFloatingTitleWidget 2017-02-07 23:51:29 +01:00
Uwe Kindler
7f5e393cfb Startet refactoring common container stuff into CContainerWidget 2017-02-03 22:56:08 +01:00
Uwe Kindler
f4c0d38ba4 Continued refactoring to prepare split of functionality of ContainerWidget 2017-01-30 22:44:27 +01:00
Uwe Kindler
411e4002f1 Improved handling and display of border drop overlays. Now border drop overlay items are visible as soon as a floating widget enters container widget area, some refactoring to improve code quality 2017-01-28 12:18:16 +01:00
Uwe Kindler
3fd20fad16 Continued refactoring, created private ContainerWidget class 2017-01-21 22:22:20 +01:00
Uwe Kindler
889d9bff5b Some refactorings to cleanup code, moved ContainerWidget private data into private data class 2017-01-20 22:43:18 +01:00
Uwe Kindler
68b93f6fa9 Started refactoring to improve code quality, overall designa nd to gain knowledge of the impolemented functionality 2017-01-17 07:57:24 +01:00
mfreiholz
abc8468989 fixes build scripts for linux 2016-10-23 11:15:06 +02:00
86 changed files with 6783 additions and 5347 deletions

315
.cproject Normal file
View File

@@ -0,0 +1,315 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?fileVersion 4.0.0?><cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
<storageModule moduleId="org.eclipse.cdt.core.settings">
<cconfiguration id="cdt.managedbuild.toolchain.gnu.mingw.base.1119687795">
<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.toolchain.gnu.mingw.base.1119687795" moduleId="org.eclipse.cdt.core.settings" name="Default">
<externalSettings/>
<extensions>
<extension id="org.eclipse.cdt.core.PE" point="org.eclipse.cdt.core.BinaryParser"/>
<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
</extensions>
</storageModule>
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
<configuration artifactName="${ProjName}" buildProperties="" description="" id="cdt.managedbuild.toolchain.gnu.mingw.base.1119687795" name="Default" parent="org.eclipse.cdt.build.core.emptycfg">
<folderInfo id="cdt.managedbuild.toolchain.gnu.mingw.base.1119687795.1949777584" name="/" resourcePath="">
<toolChain id="cdt.managedbuild.toolchain.gnu.mingw.base.88699815" name="MinGW GCC" superClass="cdt.managedbuild.toolchain.gnu.mingw.base">
<targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.PE" id="cdt.managedbuild.target.gnu.platform.mingw.base.519267520" name="Debug Platform" osList="win32" superClass="cdt.managedbuild.target.gnu.platform.mingw.base"/>
<builder id="cdt.managedbuild.toolchain.gnu.mingw.base.1119687795.1200539104" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" superClass="org.eclipse.cdt.build.core.settings.default.builder"/>
<tool id="cdt.managedbuild.tool.gnu.assembler.mingw.base.1438677059" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.mingw.base">
<option id="gnu.both.asm.option.include.paths.403127333" name="Include paths (-I)" superClass="gnu.both.asm.option.include.paths" valueType="includePath">
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/QtAdvancedDockingSystem/demo}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/QtAdvancedDockingSystem/src}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${QTDIR}/include&quot;"/>
</option>
<inputType id="cdt.managedbuild.tool.gnu.assembler.input.1249325593" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
</tool>
<tool id="cdt.managedbuild.tool.gnu.archiver.mingw.base.933824900" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.mingw.base"/>
<tool id="cdt.managedbuild.tool.gnu.cpp.compiler.mingw.base.1947822681" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.mingw.base">
<option id="gnu.cpp.compiler.option.include.paths.349616932" name="Include paths (-I)" superClass="gnu.cpp.compiler.option.include.paths" useByScannerDiscovery="false" valueType="includePath">
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/QtAdvancedDockingSystem/src}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/QtAdvancedDockingSystem/demo}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${QTDIR}/include&quot;"/>
</option>
<option id="gnu.cpp.compiler.option.preprocessor.def.56223209" name="Defined symbols (-D)" superClass="gnu.cpp.compiler.option.preprocessor.def" useByScannerDiscovery="false" valueType="definedSymbols"/>
<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.1318830536" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
</tool>
<tool id="cdt.managedbuild.tool.gnu.c.compiler.mingw.base.389117097" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.mingw.base">
<option id="gnu.c.compiler.option.include.paths.1871729602" name="Include paths (-I)" superClass="gnu.c.compiler.option.include.paths" useByScannerDiscovery="false" valueType="includePath">
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/QtAdvancedDockingSystem/demo}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/QtAdvancedDockingSystem/src}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${QTDIR}/include&quot;"/>
</option>
<option id="gnu.c.compiler.option.preprocessor.def.symbols.806805509" name="Defined symbols (-D)" superClass="gnu.c.compiler.option.preprocessor.def.symbols" useByScannerDiscovery="false" valueType="definedSymbols"/>
<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.1568363924" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
</tool>
<tool id="cdt.managedbuild.tool.gnu.c.linker.mingw.base.1734874312" name="MinGW C Linker" superClass="cdt.managedbuild.tool.gnu.c.linker.mingw.base"/>
<tool id="cdt.managedbuild.tool.gnu.cpp.linker.mingw.base.985493173" name="MinGW C++ Linker" superClass="cdt.managedbuild.tool.gnu.cpp.linker.mingw.base">
<inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.266071128" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input">
<additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
<additionalInput kind="additionalinput" paths="$(LIBS)"/>
</inputType>
</tool>
</toolChain>
</folderInfo>
</configuration>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
</cconfiguration>
</storageModule>
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
<project id="QtAdvancedDockingSystem.null.1346550114" name="QtAdvancedDockingSystem"/>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
<storageModule moduleId="refreshScope"/>
<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets">
<buildTargets>
<target name="Build all" path=" build" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>mingw32-make</buildCommand>
<buildArguments>-j</buildArguments>
<buildTarget>all</buildTarget>
<stopOnError>false</stopOnError>
<useDefaultCommand>false</useDefaultCommand>
<runAllBuilders>false</runAllBuilders>
</target>
<target name="Clean" path=" build" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>make</buildCommand>
<buildTarget>clean</buildTarget>
<stopOnError>false</stopOnError>
<useDefaultCommand>true</useDefaultCommand>
<runAllBuilders>false</runAllBuilders>
</target>
<target name="Debug Build" path=" build" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>mingw32-make</buildCommand>
<buildArguments>-j6</buildArguments>
<buildTarget>debug</buildTarget>
<stopOnError>false</stopOnError>
<useDefaultCommand>false</useDefaultCommand>
<runAllBuilders>false</runAllBuilders>
</target>
<target name="qmake" path=" build" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>qmake</buildCommand>
<buildArguments>-recursive ../ads.pro</buildArguments>
<buildTarget/>
<stopOnError>true</stopOnError>
<useDefaultCommand>false</useDefaultCommand>
<runAllBuilders>false</runAllBuilders>
</target>
<target name="Release Build" path=" build" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>mingw32-make</buildCommand>
<buildArguments>-j4</buildArguments>
<buildTarget>release</buildTarget>
<stopOnError>false</stopOnError>
<useDefaultCommand>false</useDefaultCommand>
<runAllBuilders>false</runAllBuilders>
</target>
<target name="Build all" path=" build/src" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>mingw32-make</buildCommand>
<buildArguments>-j</buildArguments>
<buildTarget>all</buildTarget>
<stopOnError>false</stopOnError>
<useDefaultCommand>false</useDefaultCommand>
<runAllBuilders>false</runAllBuilders>
</target>
<target name="Clean" path=" build/src" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>make</buildCommand>
<buildArguments/>
<buildTarget>clean</buildTarget>
<stopOnError>false</stopOnError>
<useDefaultCommand>true</useDefaultCommand>
<runAllBuilders>false</runAllBuilders>
</target>
<target name="Debug Build" path=" build/src" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>mingw32-make</buildCommand>
<buildArguments>-j6</buildArguments>
<buildTarget>debug</buildTarget>
<stopOnError>false</stopOnError>
<useDefaultCommand>false</useDefaultCommand>
<runAllBuilders>false</runAllBuilders>
</target>
<target name="qmake" path=" build/src" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>qmake</buildCommand>
<buildArguments>-recursive ../../src/src.pro</buildArguments>
<buildTarget/>
<stopOnError>true</stopOnError>
<useDefaultCommand>false</useDefaultCommand>
<runAllBuilders>false</runAllBuilders>
</target>
<target name="Release Build" path=" build/src" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>mingw32-make</buildCommand>
<buildArguments>-j4</buildArguments>
<buildTarget>release</buildTarget>
<stopOnError>false</stopOnError>
<useDefaultCommand>false</useDefaultCommand>
<runAllBuilders>false</runAllBuilders>
</target>
<target name="Build all" path=" build/demo" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>mingw32-make</buildCommand>
<buildArguments>-j</buildArguments>
<buildTarget>all</buildTarget>
<stopOnError>false</stopOnError>
<useDefaultCommand>false</useDefaultCommand>
<runAllBuilders>false</runAllBuilders>
</target>
<target name="Clean" path=" build/demo" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>make</buildCommand>
<buildArguments/>
<buildTarget>clean</buildTarget>
<stopOnError>false</stopOnError>
<useDefaultCommand>true</useDefaultCommand>
<runAllBuilders>false</runAllBuilders>
</target>
<target name="Debug Build" path=" build/demo" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>mingw32-make</buildCommand>
<buildArguments>-j6</buildArguments>
<buildTarget>debug</buildTarget>
<stopOnError>false</stopOnError>
<useDefaultCommand>false</useDefaultCommand>
<runAllBuilders>false</runAllBuilders>
</target>
<target name="qmake" path=" build/demo" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>qmake</buildCommand>
<buildArguments>-recursive ../../demo/demo.pro</buildArguments>
<buildTarget/>
<stopOnError>true</stopOnError>
<useDefaultCommand>false</useDefaultCommand>
<runAllBuilders>false</runAllBuilders>
</target>
<target name="Release Build" path=" build/demo" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>mingw32-make</buildCommand>
<buildArguments>-j4</buildArguments>
<buildTarget>release</buildTarget>
<stopOnError>false</stopOnError>
<useDefaultCommand>false</useDefaultCommand>
<runAllBuilders>false</runAllBuilders>
</target>
<target name="Build all" path=" build/AdvancedDockingSystemDemo" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>mingw32-make</buildCommand>
<buildArguments>-j</buildArguments>
<buildTarget>all</buildTarget>
<stopOnError>false</stopOnError>
<useDefaultCommand>false</useDefaultCommand>
<runAllBuilders>false</runAllBuilders>
</target>
<target name="Clean" path=" build/AdvancedDockingSystemDemo" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>make</buildCommand>
<buildTarget>clean</buildTarget>
<stopOnError>false</stopOnError>
<useDefaultCommand>true</useDefaultCommand>
<runAllBuilders>false</runAllBuilders>
</target>
<target name="Debug Build" path=" build/AdvancedDockingSystemDemo" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>mingw32-make</buildCommand>
<buildArguments>-j6</buildArguments>
<buildTarget>debug</buildTarget>
<stopOnError>false</stopOnError>
<useDefaultCommand>false</useDefaultCommand>
<runAllBuilders>false</runAllBuilders>
</target>
<target name="qmake" path=" build/AdvancedDockingSystemDemo" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>qmake</buildCommand>
<buildArguments>-recursive ../../AdvancedDockingSystemDemo/AdvancedDockingSystemDemo.pro</buildArguments>
<stopOnError>true</stopOnError>
<useDefaultCommand>false</useDefaultCommand>
<runAllBuilders>false</runAllBuilders>
</target>
<target name="Release Build" path=" build/AdvancedDockingSystemDemo" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>mingw32-make</buildCommand>
<buildArguments>-j4</buildArguments>
<buildTarget>release</buildTarget>
<stopOnError>false</stopOnError>
<useDefaultCommand>false</useDefaultCommand>
<runAllBuilders>false</runAllBuilders>
</target>
<target name="Build all" path=" build/AdvancedDockingSystemDemo_v2" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>mingw32-make</buildCommand>
<buildArguments>-j</buildArguments>
<buildTarget>all</buildTarget>
<stopOnError>false</stopOnError>
<useDefaultCommand>false</useDefaultCommand>
<runAllBuilders>false</runAllBuilders>
</target>
<target name="Clean" path=" build/AdvancedDockingSystemDemo_v2" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>make</buildCommand>
<buildTarget>clean</buildTarget>
<stopOnError>false</stopOnError>
<useDefaultCommand>true</useDefaultCommand>
<runAllBuilders>false</runAllBuilders>
</target>
<target name="Debug Build" path=" build/AdvancedDockingSystemDemo_v2" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>mingw32-make</buildCommand>
<buildArguments>-j6</buildArguments>
<buildTarget>debug</buildTarget>
<stopOnError>false</stopOnError>
<useDefaultCommand>false</useDefaultCommand>
<runAllBuilders>false</runAllBuilders>
</target>
<target name="qmake" path=" build/AdvancedDockingSystemDemo_v2" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>qmake</buildCommand>
<buildArguments>-recursive ../../AdvancedDockingSystemDemo_v2/AdvancedDockingSystemDemo_v2.pro</buildArguments>
<stopOnError>true</stopOnError>
<useDefaultCommand>false</useDefaultCommand>
<runAllBuilders>false</runAllBuilders>
</target>
<target name="Release Build" path=" build/AdvancedDockingSystemDemo_v2" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>mingw32-make</buildCommand>
<buildArguments>-j4</buildArguments>
<buildTarget>release</buildTarget>
<stopOnError>false</stopOnError>
<useDefaultCommand>false</useDefaultCommand>
<runAllBuilders>false</runAllBuilders>
</target>
<target name="Build all" path=" build/AdvancedDockingSystem" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>mingw32-make</buildCommand>
<buildArguments>-j</buildArguments>
<buildTarget>all</buildTarget>
<stopOnError>false</stopOnError>
<useDefaultCommand>false</useDefaultCommand>
<runAllBuilders>false</runAllBuilders>
</target>
<target name="Clean" path=" build/AdvancedDockingSystem" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>make</buildCommand>
<buildTarget>clean</buildTarget>
<stopOnError>false</stopOnError>
<useDefaultCommand>true</useDefaultCommand>
<runAllBuilders>false</runAllBuilders>
</target>
<target name="Debug Build" path=" build/AdvancedDockingSystem" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>mingw32-make</buildCommand>
<buildArguments>-j6</buildArguments>
<buildTarget>debug</buildTarget>
<stopOnError>false</stopOnError>
<useDefaultCommand>false</useDefaultCommand>
<runAllBuilders>false</runAllBuilders>
</target>
<target name="qmake" path=" build/AdvancedDockingSystem" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>qmake</buildCommand>
<buildArguments>-recursive ../../AdvancedDockingSystem/src.pro</buildArguments>
<buildTarget/>
<stopOnError>true</stopOnError>
<useDefaultCommand>false</useDefaultCommand>
<runAllBuilders>false</runAllBuilders>
</target>
<target name="Release Build" path=" build/AdvancedDockingSystem" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>mingw32-make</buildCommand>
<buildArguments>-j4</buildArguments>
<buildTarget>release</buildTarget>
<stopOnError>false</stopOnError>
<useDefaultCommand>false</useDefaultCommand>
<runAllBuilders>false</runAllBuilders>
</target>
</buildTargets>
</storageModule>
<storageModule moduleId="scannerConfiguration">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.mingw.base.1119687795;cdt.managedbuild.toolchain.gnu.mingw.base.1119687795.1949777584;cdt.managedbuild.tool.gnu.cpp.compiler.mingw.base.1947822681;cdt.managedbuild.tool.gnu.cpp.compiler.input.1318830536">
<autodiscovery enabled="false" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile"/>
</scannerConfigBuildInfo>
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.mingw.base.1119687795;cdt.managedbuild.toolchain.gnu.mingw.base.1119687795.1949777584;cdt.managedbuild.tool.gnu.c.compiler.mingw.base.389117097;cdt.managedbuild.tool.gnu.c.compiler.input.1568363924">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
</scannerConfigBuildInfo>
</storageModule>
</cproject>

1
.gitignore vendored
View File

@@ -1 +1,2 @@
*.pro.user
/ build

27
.project Normal file
View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>QtAdvancedDockingSystem</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
<triggers>clean,full,incremental,</triggers>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
<triggers>full,incremental,</triggers>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.cdt.core.cnature</nature>
<nature>org.eclipse.cdt.core.ccnature</nature>
<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project>
<configuration id="cdt.managedbuild.toolchain.gnu.mingw.base.1119687795" name="Default">
<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
<provider class="org.eclipse.cdt.managedbuilder.internal.language.settings.providers.GCCBuiltinSpecsDetectorMinGW" console="false" env-hash="-445604897520096165" id="org.eclipse.cdt.managedbuilder.core.GCCBuiltinSpecsDetectorMinGW" keep-relative-paths="false" name="CDT GCC Built-in Compiler Settings MinGW" parameter="${COMMAND} ${FLAGS} -E -P -v -dD -std=c++14 &quot;${INPUTS}&quot;" prefer-non-shared="true">
<language-scope id="org.eclipse.cdt.core.gcc"/>
<language-scope id="org.eclipse.cdt.core.g++"/>
</provider>
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
<provider class="org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuildCommandParser" id="org.eclipse.cdt.managedbuilder.core.GCCBuildCommandParser" keep-relative-paths="false" name="CDT GCC Build Output Parser" parameter="(g?cc)|([gc]\+\+)|(clang)" prefer-non-shared="true"/>
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
</extension>
</configuration>
</project>

View File

@@ -1,24 +0,0 @@
SOURCES += \
$$PWD/src/API.cpp \
$$PWD/src/ContainerWidget.cpp \
$$PWD/src/SectionWidget.cpp \
$$PWD/src/SectionContent.cpp \
$$PWD/src/SectionTitleWidget.cpp \
$$PWD/src/SectionContentWidget.cpp \
$$PWD/src/DropOverlay.cpp \
$$PWD/src/FloatingWidget.cpp \
$$PWD/src/Internal.cpp \
$$PWD/src/Serialization.cpp
HEADERS += \
$$PWD/include/ads/API.h \
$$PWD/include/ads/ContainerWidget.h \
$$PWD/include/ads/SectionWidget.h \
$$PWD/include/ads/SectionContent.h \
$$PWD/include/ads/SectionTitleWidget.h \
$$PWD/include/ads/SectionContentWidget.h \
$$PWD/include/ads/DropOverlay.h \
$$PWD/include/ads/FloatingWidget.h \
$$PWD/include/ads/Internal.h \
$$PWD/include/ads/Serialization.h

View File

@@ -1,35 +0,0 @@
TARGET = AdvancedDockingSystem
TEMPLATE = lib
VERSION = 1.0.0
CONFIG += adsBuildShared
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
greaterThan(QT_MAJOR_VERSION, 4): DEFINES += ADS_NAMESPACE_ENABLED
adsBuildShared {
CONFIG += shared
DEFINES += ADS_EXPORT
}
!adsBuildShared {
CONFIG += staticlib
}
INCLUDEPATH += $$PWD/include
windows {
# MinGW
*-g++* {
QMAKE_CXXFLAGS += -std=c++11
QMAKE_CXXFLAGS += -Wall -Wextra -pedantic
}
# MSVC
*-msvc* {
}
}
RESOURCES += \
res/ads.qrc
include(AdvancedDockingSystem.pri)

View File

@@ -1,74 +0,0 @@
#ifndef ADS_API_H
#define ADS_API_H
#include <QFlags>
class QWidget;
class QSplitter;
// DLL Export API
#ifdef _WIN32
#if defined(ADS_IMPORT)
#define ADS_EXPORT_API
#elif defined(ADS_EXPORT)
#define ADS_EXPORT_API __declspec(dllexport)
#else
#define ADS_EXPORT_API __declspec(dllimport)
#endif
#else
#define ADS_EXPORT_API
#endif
// Use namespace
// Disabled with Qt4, it makes problems with signals and slots.
#ifdef ADS_NAMESPACE_ENABLED
#define ADS_NAMESPACE_BEGIN namespace ads {
#define ADS_NAMESPACE_END }
#define ADS_NS ::ads
#else
#define ADS_NAMESPACE_BEGIN
#define ADS_NAMESPACE_END
#define ADS_NS
#endif
// Always enable "serialization" namespace.
// It is not required for signals and slots.
#define ADS_NAMESPACE_SER_BEGIN namespace ads { namespace serialization {
#define ADS_NAMESPACE_SER_END }}
#define ADS_NS_SER ::ads::serialization
// Width of the native window frame border (based on OS).
#define ADS_WINDOW_FRAME_BORDER_WIDTH 7
// Beautiful C++ stuff.
#define ADS_Expects(cond)
#define ADS_Ensures(cond)
// Indicates whether ADS should include animations.
//#define ADS_ANIMATIONS_ENABLED 1
//#define ADS_ANIMATION_DURATION 150
ADS_NAMESPACE_BEGIN
class ContainerWidget;
class SectionWidget;
enum DropArea
{
InvalidDropArea = 0,
TopDropArea = 1,
RightDropArea = 2,
BottomDropArea = 4,
LeftDropArea = 8,
CenterDropArea = 16,
AllAreas = TopDropArea | RightDropArea | BottomDropArea | LeftDropArea | CenterDropArea
};
Q_DECLARE_FLAGS(DropAreas, DropArea)
void deleteEmptySplitter(ContainerWidget* container);
ContainerWidget* findParentContainerWidget(QWidget* w);
SectionWidget* findParentSectionWidget(QWidget* w);
QSplitter* findParentSplitter(QWidget* w);
QSplitter* findImmediateSplitter(QWidget* w);
ADS_NAMESPACE_END
#endif

View File

@@ -1,191 +0,0 @@
#ifndef ADS_CONTAINERWIDGET_H
#define ADS_CONTAINERWIDGET_H
#include <QList>
#include <QHash>
#include <QPointer>
#include <QFrame>
class QPoint;
class QSplitter;
class QMenu;
class QGridLayout;
#include "ads/API.h"
#include "ads/Internal.h"
#include "ads/SectionContent.h"
#include "ads/FloatingWidget.h"
#include "ads/Serialization.h"
ADS_NAMESPACE_BEGIN
class SectionWidget;
class DropOverlay;
class InternalContentData;
/*!
* ContainerWidget is the main container to provide the docking
* functionality. It manages multiple sections with all possible areas.
*/
class ADS_EXPORT_API ContainerWidget : public QFrame
{
Q_OBJECT
friend class SectionContent;
friend class SectionWidget;
friend class FloatingWidget;
friend class SectionTitleWidget;
friend class SectionContentWidget;
public:
explicit ContainerWidget(QWidget *parent = NULL);
virtual ~ContainerWidget();
//
// Public API
//
/*!
* Adds the section-content <em>sc</em> to this container-widget into the section-widget <em>sw</em>.
* If <em>sw</em> is not NULL, the <em>area</em> is used to indicate how the content should be arranged.
* Returns a pointer to the SectionWidget of the added SectionContent. Do not use it for anything else than adding more
* SectionContent elements with this method.
*/
SectionWidget* addSectionContent(const SectionContent::RefPtr& sc, SectionWidget* sw = NULL, DropArea area = CenterDropArea);
/*!
* Completely removes the <em>sc</em> from this ContainerWidget.
* This container will no longer hold a reference to the content.
* The content can be safely deleted.
*/
bool removeSectionContent(const SectionContent::RefPtr& sc);
/*!
* Shows the specific SectionContent in UI.
* Independed of the current state, whether it is used inside a section or is floating.
*/
bool showSectionContent(const SectionContent::RefPtr& sc);
/*!
* Closes the specified SectionContent from UI.
* Independed of the current state, whether it is used inside a section or is floating.
*/
bool hideSectionContent(const SectionContent::RefPtr& sc);
/*!
* Selects the specific SectionContent as current, if it is part of a SectionWidget.
* If SC is floating, it does nothing (or should we show it?)
*/
bool raiseSectionContent(const SectionContent::RefPtr& sc);
/*!
* Indicates whether the SectionContent <em>sc</em> is visible.
*/
bool isSectionContentVisible(const SectionContent::RefPtr& sc);
/*!
* Creates a QMenu based on available SectionContents.
* The caller is responsible to delete the menu.
*/
QMenu* createContextMenu() const;
/*!
* Serializes the current state of contents and returns it as a plain byte array.
* \see restoreState(const QByteArray&)
*/
QByteArray saveState() const;
/*!
* Deserilizes the state of contents from <em>data</em>, which was written with <em>saveState()</em>.
* \see saveState()
*/
bool restoreState(const QByteArray& data);
//
// Advanced Public API
// You usually should not need access to this methods
//
// Outer DropAreas
QRect outerTopDropRect() const;
QRect outerRightDropRect() const;
QRect outerBottomDropRect() const;
QRect outerLeftDropRect() const;
/*!
* \brief contents
* \return List of known SectionContent for this ContainerWidget.
*/
QList<SectionContent::RefPtr> contents() const;
QPointer<DropOverlay> dropOverlay() const;
private:
//
// Internal Stuff Begins Here
//
SectionWidget* newSectionWidget();
SectionWidget* dropContent(const InternalContentData& data, SectionWidget* targetSection, DropArea area, bool autoActive = true);
void addSection(SectionWidget* section);
SectionWidget* sectionAt(const QPoint& pos) const;
SectionWidget* dropContentOuterHelper(QLayout* l, const InternalContentData& data, Qt::Orientation orientation, bool append);
// Serialization
QByteArray saveHierarchy() const;
void saveFloatingWidgets(QDataStream& out) const;
void saveSectionWidgets(QDataStream& out, QWidget* widget) const;
bool saveSectionIndex(ADS_NS_SER::SectionIndexData &sid) const;
bool restoreHierarchy(const QByteArray& data);
bool restoreFloatingWidgets(QDataStream& in, int version, QList<FloatingWidget*>& floatings);
bool restoreSectionWidgets(QDataStream& in, int version, QSplitter* currentSplitter, QList<SectionWidget*>& sections, QList<SectionContent::RefPtr>& contentsToHide);
bool takeContent(const SectionContent::RefPtr& sc, InternalContentData& data);
private slots:
void onActiveTabChanged();
void onActionToggleSectionContentVisibility(bool visible);
signals:
void orientationChanged();
/*!
* Emits whenever the "isActiveTab" state of a SectionContent changes.
* Whenever the users sets another tab as active, this signal gets invoked
* for the old tab and the new active tab (the order is unspecified).
*/
void activeTabChanged(const SectionContent::RefPtr& sc, bool active);
/*!
* Emits whenever the visibility of a SectionContent changes.
* \see showSectionContent(), hideSectionContent()
* \since 0.2
*/
void sectionContentVisibilityChanged(const SectionContent::RefPtr& sc, bool visible);
private:
// Elements inside container.
QList<SectionWidget*> _sections;
QList<FloatingWidget*> _floatings;
QHash<int, HiddenSectionItem> _hiddenSectionContents;
// Helper lookup maps, restricted to this container.
QHash<int, SectionContent::WeakPtr> _scLookupMapById;
QHash<QString, SectionContent::WeakPtr> _scLookupMapByName;
QHash<int, SectionWidget*> _swLookupMapById;
// Layout stuff
QGridLayout* _mainLayout;
Qt::Orientation _orientation;
QPointer<QSplitter> _splitter; // $mfreiholz: I'd like to remove this variable entirely,
// because it changes during user interaction anyway.
// Drop overlay stuff.
QPointer<DropOverlay> _dropOverlay;
};
ADS_NAMESPACE_END
#endif

View File

@@ -1,86 +0,0 @@
#ifndef DROP_OVERLAY_H
#define DROP_OVERLAY_H
#include <QPointer>
#include <QHash>
#include <QRect>
#include <QFrame>
class QGridLayout;
#include "ads/API.h"
ADS_NAMESPACE_BEGIN
class DropOverlayCross;
/*!
* DropOverlay paints a translucent rectangle over another widget. The geometry
* of the rectangle is based on the mouse location.
*/
class ADS_EXPORT_API DropOverlay : public QFrame
{
Q_OBJECT
friend class DropOverlayCross;
public:
DropOverlay(QWidget* parent);
virtual ~DropOverlay();
void setAllowedAreas(DropAreas areas);
DropAreas allowedAreas() const;
void setAreaWidgets(const QHash<DropArea, QWidget*>& widgets);
DropArea cursorLocation() const;
DropArea showDropOverlay(QWidget* target);
void showDropOverlay(QWidget* target, const QRect& targetAreaRect);
void hideDropOverlay();
protected:
virtual void paintEvent(QPaintEvent *e);
virtual void showEvent(QShowEvent* e);
virtual void hideEvent(QHideEvent* e);
virtual void resizeEvent(QResizeEvent* e);
virtual void moveEvent(QMoveEvent* e);
private:
DropAreas _allowedAreas;
DropOverlayCross* _cross;
bool _fullAreaDrop;
QPointer<QWidget> _target;
QRect _targetRect;
DropArea _lastLocation;
};
/*!
* DropOverlayCross shows a cross with 5 different drop area possibilities.
* I could have handled everything inside DropOverlay, but because of some
* styling issues it's better to have a separate class for the cross.
*/
class DropOverlayCross : public QWidget
{
Q_OBJECT
friend class DropOverlay;
public:
DropOverlayCross(DropOverlay* overlay);
virtual ~DropOverlayCross();
void setAreaWidgets(const QHash<DropArea, QWidget*>& widgets);
DropArea cursorLocation() const;
protected:
virtual void showEvent(QShowEvent* e);
private:
void reset();
private:
DropOverlay* _overlay;
QHash<DropArea, QWidget*> _widgets;
QGridLayout* _grid;
};
ADS_NAMESPACE_END
#endif

View File

@@ -1,46 +0,0 @@
#ifndef FLOATINGWIDGET_H
#define FLOATINGWIDGET_H
#include <QWidget>
class QBoxLayout;
#include "ads/API.h"
#include "ads/SectionContent.h"
ADS_NAMESPACE_BEGIN
class ContainerWidget;
class SectionTitleWidget;
class SectionContentWidget;
class InternalContentData;
// FloatingWidget holds and displays SectionContent as a floating window.
// It can be resized, moved and dropped back into a SectionWidget.
class FloatingWidget : public QWidget
{
Q_OBJECT
friend class ContainerWidget;
public:
FloatingWidget(ContainerWidget* container, SectionContent::RefPtr sc, SectionTitleWidget* titleWidget, SectionContentWidget* contentWidget, QWidget* parent = NULL);
virtual ~FloatingWidget();
SectionContent::RefPtr content() const { return _content; }
public://private:
bool takeContent(InternalContentData& data);
private slots:
void onCloseButtonClicked();
private:
ContainerWidget* _container;
SectionContent::RefPtr _content;
SectionTitleWidget* _titleWidget;
SectionContentWidget* _contentWidget;
QBoxLayout* _titleLayout;
};
ADS_NAMESPACE_END
#endif

View File

@@ -1,54 +0,0 @@
#ifndef ADS_INTERNAL_HEADER
#define ADS_INTERNAL_HEADER
#include <QSharedPointer>
#include <QWeakPointer>
#include "ads/API.h"
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
#else
#include "ads/SectionContent.h"
#endif
#define SCLookupMapById(X) X->_scLookupMapById
#define SCLookupMapByName(X) X->_scLookupMapByName
#define SWLookupMapById(X) X->_swLookupMapById
ADS_NAMESPACE_BEGIN
class SectionContent;
class SectionTitleWidget;
class SectionContentWidget;
class InternalContentData
{
public:
typedef QSharedPointer<InternalContentData> RefPtr;
typedef QWeakPointer<InternalContentData> WeakPtr;
InternalContentData();
~InternalContentData();
QSharedPointer<SectionContent> content;
SectionTitleWidget* titleWidget;
SectionContentWidget* contentWidget;
};
class HiddenSectionItem
{
public:
HiddenSectionItem() :
preferredSectionId(-1),
preferredSectionIndex(-1)
{}
int preferredSectionId;
int preferredSectionIndex;
InternalContentData data;
};
ADS_NAMESPACE_END
#endif

View File

@@ -1,78 +0,0 @@
#ifndef SECTIONCONTENT_H
#define SECTIONCONTENT_H
#include <QSharedPointer>
#include <QWeakPointer>
#include <QPointer>
class QWidget;
#include "ads/API.h"
ADS_NAMESPACE_BEGIN
class ContainerWidget;
class ADS_EXPORT_API SectionContent
{
friend class ContainerWidget;
private:
SectionContent();
SectionContent(const SectionContent&);
SectionContent& operator=(const SectionContent&);
public:
typedef QSharedPointer<SectionContent> RefPtr;
typedef QWeakPointer<SectionContent> WeakPtr;
enum Flag
{
None = 0,
Closeable = 1,
AllFlags = Closeable
};
Q_DECLARE_FLAGS(Flags, Flag)
/*!
* Creates new content, associates it to <em>container</em> and takes ownership of
* <em>title</em>- and <em>content</em>- widgets.
* \param uniqueName An unique identifier across the entire process.
* \param container The parent ContainerWidget in which this content will be active.
* \param title The widget to use as title.
* \param content The widget to use as content.
* \return May return a invalid ref-pointer in case of invalid parameters.
*/
static RefPtr newSectionContent(const QString& uniqueName, ContainerWidget* container, QWidget* title, QWidget* content);
virtual ~SectionContent();
int uid() const;
QString uniqueName() const;
ContainerWidget* containerWidget() const;
QWidget* titleWidget() const;
QWidget* contentWidget() const;
Flags flags() const;
QString visibleTitle() const;
QString title() const;
void setTitle(const QString& title);
void setFlags(const Flags f);
private:
const int _uid;
QString _uniqueName;
QPointer<ContainerWidget> _containerWidget;
QPointer<QWidget> _titleWidget;
QPointer<QWidget> _contentWidget;
// Optional attributes
QString _title;
Flags _flags;
/* Note: This method could be a problem in static build environment
* since it may begin with 0 for every module which uses ADS.
*/
static int GetNextUid();
};
ADS_NAMESPACE_END
#endif

View File

@@ -1,28 +0,0 @@
#ifndef SECTION_CONTENT_WIDGET_H
#define SECTION_CONTENT_WIDGET_H
#include <QFrame>
#include "ads/API.h"
#include "ads/SectionContent.h"
ADS_NAMESPACE_BEGIN
class ContainerWidget;
class SectionWidget;
class SectionContentWidget : public QFrame
{
Q_OBJECT
friend class ContainerWidget;
public:
SectionContentWidget(SectionContent::RefPtr c, QWidget* parent = 0);
virtual ~SectionContentWidget();
private:
SectionContent::RefPtr _content;
};
ADS_NAMESPACE_END
#endif

View File

@@ -1,54 +0,0 @@
#ifndef SECTION_TITLE_WIDGET_H
#define SECTION_TITLE_WIDGET_H
#include <QPointer>
#include <QPoint>
#include <QFrame>
#include "ads/API.h"
#include "ads/SectionContent.h"
ADS_NAMESPACE_BEGIN
class ContainerWidget;
class SectionWidget;
class FloatingWidget;
class SectionTitleWidget : public QFrame
{
Q_OBJECT
Q_PROPERTY(bool activeTab READ isActiveTab WRITE setActiveTab NOTIFY activeTabChanged)
friend class ContainerWidget;
friend class SectionWidget;
SectionContent::RefPtr _content;
// Drag & Drop (Floating)
QPointer<FloatingWidget> _fw;
QPoint _dragStartPos;
// Drag & Drop (Title/Tabs)
bool _tabMoving;
// Property values
bool _activeTab;
public:
SectionTitleWidget(SectionContent::RefPtr content, QWidget* parent);
virtual ~SectionTitleWidget();
bool isActiveTab() const;
void setActiveTab(bool active);
protected:
virtual void mousePressEvent(QMouseEvent* ev);
virtual void mouseReleaseEvent(QMouseEvent* ev);
virtual void mouseMoveEvent(QMouseEvent* ev);
signals:
void activeTabChanged();
void clicked();
};
ADS_NAMESPACE_END
#endif

View File

@@ -1,103 +0,0 @@
#ifndef SECTION_WIDGET_H
#define SECTION_WIDGET_H
#include <QDebug>
#include <QPointer>
#include <QList>
#include <QFrame>
#include <QScrollArea>
class QBoxLayout;
class QStackedLayout;
class QPushButton;
class QMenu;
#include "ads/API.h"
#include "ads/Internal.h"
#include "ads/SectionContent.h"
ADS_NAMESPACE_BEGIN
class ContainerWidget;
class SectionTitleWidget;
class SectionContentWidget;
// SectionWidget manages multiple instances of SectionContent.
// It displays a title TAB, which is clickable and will switch to
// the contents associated to the title when clicked.
class ADS_EXPORT_API SectionWidget : public QFrame
{
Q_OBJECT
friend class ContainerWidget;
explicit SectionWidget(ContainerWidget* parent);
public:
virtual ~SectionWidget();
int uid() const;
ContainerWidget* containerWidget() const;
QRect titleAreaGeometry() const;
QRect contentAreaGeometry() const;
const QList<SectionContent::RefPtr>& contents() const { return _contents; }
void addContent(const SectionContent::RefPtr& c);
void addContent(const InternalContentData& data, bool autoActivate);
bool takeContent(int uid, InternalContentData& data);
int indexOfContent(const SectionContent::RefPtr& c) const;
int indexOfContentByUid(int uid) const;
int indexOfContentByTitlePos(const QPoint& pos, QWidget* exclude = NULL) const;
int currentIndex() const;
void moveContent(int from, int to);
protected:
virtual void showEvent(QShowEvent*);
public slots:
void setCurrentIndex(int index);
private slots:
void onSectionTitleClicked();
void onCloseButtonClicked();
void onTabsMenuActionTriggered(bool);
void updateTabsMenu();
private:
const int _uid;
QPointer<ContainerWidget> _container;
QList<SectionContent::RefPtr> _contents;
QList<SectionTitleWidget*> _sectionTitles;
QList<SectionContentWidget*> _sectionContents;
QBoxLayout* _topLayout;
QScrollArea* _tabsScrollArea;
QWidget* _tabsContainerWidget;
QBoxLayout* _tabsLayout;
QPushButton* _tabsMenuButton;
QPushButton* _closeButton;
int _tabsLayoutInitCount; // used for calculations on _tabsLayout modification calls.
QStackedLayout *_contentsLayout;
QPoint _mousePressPoint;
SectionContent::RefPtr _mousePressContent;
SectionTitleWidget* _mousePressTitleWidget;
static int GetNextUid();
};
/* Custom scrollable implementation for tabs */
class SectionWidgetTabsScrollArea : public QScrollArea
{
public:
SectionWidgetTabsScrollArea(SectionWidget* sectionWidget, QWidget* parent = NULL);
virtual ~SectionWidgetTabsScrollArea();
protected:
virtual void wheelEvent(QWheelEvent*);
};
ADS_NAMESPACE_END
#endif

View File

@@ -1,165 +0,0 @@
#ifndef ADS_SERIALIZATION_H
#define ADS_SERIALIZATION_H
#include <QtGlobal>
#include <QList>
#include <QString>
#include <QDataStream>
#include <QBuffer>
#include "ads/API.h"
ADS_NAMESPACE_SER_BEGIN
enum EntryType
{
ET_Unknown = 0x00000000,
ET_Hierarchy = 0x00000001,
ET_SectionIndex = 0x00000002,
// Begin of custom entry types (e.g. CustomType + 42)
ET_Custom = 0x0000ffff
};
class ADS_EXPORT_API HeaderEntity
{
public:
static qint32 MAGIC;
static qint32 MAJOR_VERSION;
static qint32 MINOR_VERSION;
HeaderEntity();
qint32 magic;
qint32 majorVersion;
qint32 minorVersion;
};
QDataStream& operator<<(QDataStream& out, const HeaderEntity& data);
QDataStream& operator>>(QDataStream& in, HeaderEntity& data);
class ADS_EXPORT_API OffsetsHeaderEntity
{
public:
OffsetsHeaderEntity();
qint64 entriesCount;
QList<class OffsetsHeaderEntryEntity> entries;
};
QDataStream& operator<<(QDataStream& out, const OffsetsHeaderEntity& data);
QDataStream& operator>>(QDataStream& in, OffsetsHeaderEntity& data);
class ADS_EXPORT_API OffsetsHeaderEntryEntity
{
public:
OffsetsHeaderEntryEntity();
qint32 type;
qint64 offset;
qint64 contentSize;
};
QDataStream& operator<<(QDataStream& out, const OffsetsHeaderEntryEntity& data);
QDataStream& operator>>(QDataStream& in, OffsetsHeaderEntryEntity& data);
class ADS_EXPORT_API SectionEntity
{
public:
SectionEntity();
qint32 x;
qint32 y;
qint32 width;
qint32 height;
qint32 currentIndex;
qint32 sectionContentsCount;
QList<class SectionContentEntity> sectionContents;
};
QDataStream& operator<<(QDataStream& out, const SectionEntity& data);
QDataStream& operator>>(QDataStream& in, SectionEntity& data);
class ADS_EXPORT_API SectionContentEntity
{
public:
SectionContentEntity();
QString uniqueName;
bool visible;
qint32 preferredIndex;
};
QDataStream& operator<<(QDataStream& out, const SectionContentEntity& data);
QDataStream& operator>>(QDataStream& in, SectionContentEntity& data);
class ADS_EXPORT_API FloatingContentEntity
{
public:
FloatingContentEntity();
QString uniqueName;
qint32 xpos;
qint32 ypos;
qint32 width;
qint32 height;
bool visible;
};
QDataStream& operator<<(QDataStream& out, const FloatingContentEntity& data);
QDataStream& operator>>(QDataStream& in, FloatingContentEntity& data);
// Type: OffsetHeaderEntry::Hierarchy
class ADS_EXPORT_API HierarchyData
{
public:
HierarchyData();
QByteArray data;
};
QDataStream& operator<<(QDataStream& out, const HierarchyData& data);
QDataStream& operator>>(QDataStream& in, HierarchyData& data);
// Type: OffsetHeaderEntry::SectionIndex
class ADS_EXPORT_API SectionIndexData
{
public:
SectionIndexData();
qint32 sectionsCount;
QList<SectionEntity> sections;
};
QDataStream& operator<<(QDataStream& out, const SectionIndexData& data);
QDataStream& operator>>(QDataStream& in, SectionIndexData& data);
/*!
* \brief The InMemoryWriter class writes into a QByteArray.
*/
class ADS_EXPORT_API InMemoryWriter
{
public:
InMemoryWriter();
bool write(qint32 entryType, const QByteArray& data);
bool write(const SectionIndexData& data);
QByteArray toByteArray() const;
qint32 offsetsCount() const { return _offsetsHeader.entriesCount; }
private:
QBuffer _contentBuffer;
OffsetsHeaderEntity _offsetsHeader;
};
/*!
* \brief The InMemoryReader class
*/
class ADS_EXPORT_API InMemoryReader
{
public:
InMemoryReader(const QByteArray& data);
bool initReadHeader();
bool read(qint32 entryType, QByteArray &data);
bool read(SectionIndexData& sid);
qint32 offsetsCount() const { return _offsetsHeader.entriesCount; }
private:
QByteArray _data;
OffsetsHeaderEntity _offsetsHeader;
};
ADS_NAMESPACE_SER_END
#endif

View File

@@ -1,7 +0,0 @@
<RCC>
<qresource prefix="/ads">
<file>stylesheets/default-windows.css</file>
<file>stylesheets/vendor-partsolutions.css</file>
<file>stylesheets/modern-windows.css</file>
</qresource>
</RCC>

View File

@@ -1,77 +0,0 @@
/*
* Default style sheet on Windows Platforms
* Note: Always use CSS-classes with and without "ads--" namespace to support Qt4 & Qt5
*/
ads--ContainerWidget,
ContainerWidget
{
background: palette(dark);
}
ads--ContainerWidget QSplitter::handle,
ContainerWidget QSplitter::handle
{
background: palette(dark);
}
ads--SectionWidget,
SectionWidget
{
background: palette(window);
border: 1px solid palette(light);
}
ads--SectionWidget #tabsMenuButton::menu-indicator,
SectionWidget #tabsMenuButton::menu-indicator
{
image: none;
}
ads--SectionTitleWidget,
SectionTitleWidget
{
background: palette(window);
border-color: palette(light);
border-style: solid;
border-width: 0 1px 0 0;
padding: 0 9px;
}
ads--SectionTitleWidget[activeTab="true"],
SectionTitleWidget[activeTab="true"]
{
/* background: palette(light);*/
/* background: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, 255, 255, 255), stop:1 rgba(240, 240, 240, 255));*/
background: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:0.5, stop:0 palette(window), stop:1 palette(light));
}
ads--SectionContentWidget,
SectionContentWidget
{
background: palette(light);
border-color: palette(light);
border-style: solid;
border-width: 1px 0 0 0;
}
/* Special: QLabels inside SectionTitleWidget
*/
ads--SectionTitleWidget QLabel,
SectionTitleWidget QLabel
{
color: palette(dark);
}
ads--SectionTitleWidget[activeTab="true"] QLabel,
SectionTitleWidget[activeTab="true"] QLabel
{
color: palette(foreground);
}
/* Special: QLabels inside SectionTitleWidget, which is floating
*/
ads--FloatingWidget ads--SectionTitleWidget QLabel,
FloatingWidget SectionTitleWidget QLabel
{
color: palette(foreground);
}

View File

@@ -1,35 +0,0 @@
QSplitter::handle {
background: palette(light);
}
ads--ContainerWidget, ContainerWidget {
background: palette(light);
}
ads--SectionWidget, SectionWidget {
background: palette(light);
}
ads--SectionTitleWidget, SectionTitleWidget {
background: #ffffff;
}
ads--SectionTitleWidget QLabel, SectionTitleWidget QLabel {
color: #000000;
}
ads--SectionTitleWidget[activeTab="true"], SectionTitleWidget[activeTab="true"] {
background: #000000;
border-right: 1px solid #000000;
padding: 9px;
}
ads--SectionTitleWidget[activeTab="true"] QLabel, SectionTitleWidget[activeTab="true"] QLabel {
color: #ffffff;
}
ads--SectionContentWidget, SectionContentWidget {
border: 1px solid #000000;
}
QAbstractItemView {
border: 0;
}

View File

@@ -1,63 +0,0 @@
/*
* Style sheet used by CADENAS PARTsolutions product line
* Requires Qt4 compatibility
*/
QSplitter::handle:vertical {
image: url(:/img/splitter-horizontal.png);
}
QSplitter::handle:horizontal {
image: url(:/img/splitter-vertical.png);
}
ads--ContainerWidget, ContainerWidget {
background: #9ab6ca;
border: 0;
}
ads--SectionWidget, SectionWidget {
background: #7c9eb3;
border-color: #ffffff;
border-style: solid;
border-width: 1px;
}
ads--SectionTitleWidget, SectionTitleWidget {
background: #7c9eb3;
border-right: 1px solid #E7F3F8;
padding: 6px 6px;
}
ads--SectionTitleWidget[activeTab="true"], SectionTitleWidget[activeTab="true"] {
background: #E7F3F8;
border: 1px solid #E7F3F8;
}
ads--SectionWidget QPushButton#closeButton, SectionWidget QPushButton#closeButton,
ads--FloatingWidget QPushButton#closeButton, FloatingWidget QPushButton#closeButton {
background: #ff0000;
border: 1px solid red;
}
ads--SectionContentWidget, SectionContentWidget {
background: #ffffff;
border: 0px solid #E7F3F8;
}
/* Special */
IconTitleWidget {
padding: 0;
margin: 0;
}
ads--SectionTitleWidget QLabel, SectionTitleWidget QLabel {
color: #ffffff;
background: #7c9eb3;
}
ads--SectionTitleWidget[activeTab="true"] QLabel, SectionTitleWidget[activeTab="true"] QLabel {
color: #000000;
background: #E7F3F8;
}

View File

@@ -1,115 +0,0 @@
#include "ads/API.h"
#include <QWidget>
#include <QSplitter>
#include <QLayout>
#include <QVariant>
#include "ads/ContainerWidget.h"
#include "ads/SectionWidget.h"
ADS_NAMESPACE_BEGIN
static bool splitterContainsSectionWidget(QSplitter* splitter)
{
for (int i = 0; i < splitter->count(); ++i)
{
QWidget* w = splitter->widget(i);
QSplitter* sp = qobject_cast<QSplitter*>(w);
SectionWidget* sw = NULL;
if (sp && splitterContainsSectionWidget(sp))
return true;
else if ((sw = qobject_cast<SectionWidget*>(w)) != NULL)
return true;
}
return false;
}
void deleteEmptySplitter(ContainerWidget* container)
{
bool doAgain = false;
do
{
doAgain = false;
QList<QSplitter*> splitters = container->findChildren<QSplitter*>();
for (int i = 0; i < splitters.count(); ++i)
{
QSplitter* sp = splitters.at(i);
if (!sp->property("ads-splitter").toBool())
continue;
if (sp->count() > 0 && splitterContainsSectionWidget(sp))
continue;
delete splitters[i];
doAgain = true;
break;
}
}
while (doAgain);
}
ContainerWidget* findParentContainerWidget(QWidget* w)
{
ContainerWidget* cw = 0;
QWidget* next = w;
do
{
if ((cw = dynamic_cast<ContainerWidget*>(next)) != 0)
{
break;
}
next = next->parentWidget();
}
while (next);
return cw;
}
SectionWidget* findParentSectionWidget(class QWidget* w)
{
SectionWidget* cw = 0;
QWidget* next = w;
do
{
if ((cw = dynamic_cast<SectionWidget*>(next)) != 0)
{
break;
}
next = next->parentWidget();
}
while (next);
return cw;
}
QSplitter* findParentSplitter(class QWidget* w)
{
QSplitter* cw = 0;
QWidget* next = w;
do
{
if ((cw = dynamic_cast<QSplitter*>(next)) != 0)
{
break;
}
next = next->parentWidget();
}
while (next);
return cw;
}
QSplitter* findImmediateSplitter(class QWidget* w)
{
QSplitter* sp = NULL;
QLayout* l = w->layout();
if (!l || l->count() <= 0)
return sp;
for (int i = 0; i < l->count(); ++i)
{
QLayoutItem* li = l->itemAt(0);
if (!li->widget())
continue;
if ((sp = dynamic_cast<QSplitter*>(li->widget())) != NULL)
break;
}
return sp;
}
ADS_NAMESPACE_END

File diff suppressed because it is too large Load Diff

View File

@@ -1,441 +0,0 @@
#include "ads/DropOverlay.h"
#include <QPointer>
#include <QPaintEvent>
#include <QResizeEvent>
#include <QMoveEvent>
#include <QPainter>
#include <QGridLayout>
#include <QCursor>
#include <QIcon>
#include <QLabel>
ADS_NAMESPACE_BEGIN
// Helper /////////////////////////////////////////////////////////////
static QPixmap createDropIndicatorPixmap(const QPalette& pal, const QSizeF& size, DropArea dropArea)
{
const QColor borderColor = pal.color(QPalette::Active, QPalette::Highlight);
const QColor backgroundColor = pal.color(QPalette::Active, QPalette::Base);
const QColor areaBackgroundColor = pal.color(QPalette::Active, QPalette::Highlight).lighter(150);
QPixmap pm(size.width(), size.height());
pm.fill(QColor(0, 0, 0, 0));
QPainter p(&pm);
QPen pen = p.pen();
QRectF baseRect(pm.rect());
// Fill
p.fillRect(baseRect, backgroundColor);
// Drop area rect.
if (true)
{
p.save();
QRectF areaRect;
QLineF areaLine;
QLinearGradient gradient;
switch (dropArea)
{
case TopDropArea:
areaRect = QRectF(baseRect.x(), baseRect.y(), baseRect.width(), baseRect.height() * .5f);
areaLine = QLineF(areaRect.bottomLeft(), areaRect.bottomRight());
gradient.setStart(areaRect.topLeft());
gradient.setFinalStop(areaRect.bottomLeft());
gradient.setColorAt(0.f, areaBackgroundColor);
gradient.setColorAt(1.f, areaBackgroundColor.lighter(120));
break;
case RightDropArea:
areaRect = QRectF(baseRect.width() * .5f, baseRect.y(), baseRect.width() * .5f, baseRect.height());
areaLine = QLineF(areaRect.topLeft(), areaRect.bottomLeft());
gradient.setStart(areaRect.topLeft());
gradient.setFinalStop(areaRect.topRight());
gradient.setColorAt(0.f, areaBackgroundColor.lighter(120));
gradient.setColorAt(1.f, areaBackgroundColor);
break;
case BottomDropArea:
areaRect = QRectF(baseRect.x(), baseRect.height() * .5f, baseRect.width(), baseRect.height() * .5f);
areaLine = QLineF(areaRect.topLeft(), areaRect.topRight());
gradient.setStart(areaRect.topLeft());
gradient.setFinalStop(areaRect.bottomLeft());
gradient.setColorAt(0.f, areaBackgroundColor.lighter(120));
gradient.setColorAt(1.f, areaBackgroundColor);
break;
case LeftDropArea:
areaRect = QRectF(baseRect.x(), baseRect.y(), baseRect.width() * .5f, baseRect.height());
areaLine = QLineF(areaRect.topRight(), areaRect.bottomRight());
gradient.setStart(areaRect.topLeft());
gradient.setFinalStop(areaRect.topRight());
gradient.setColorAt(0.f, areaBackgroundColor);
gradient.setColorAt(1.f, areaBackgroundColor.lighter(120));
break;
default:
break;
}
if (areaRect.isValid())
{
p.fillRect(areaRect, gradient);
pen = p.pen();
pen.setColor(borderColor);
pen.setStyle(Qt::DashLine);
p.setPen(pen);
p.drawLine(areaLine);
}
p.restore();
}
// Border
if (true)
{
p.save();
pen = p.pen();
pen.setColor(borderColor);
pen.setWidth(1);
p.setPen(pen);
p.drawRect(baseRect.adjusted(0, 0, -pen.width(), -pen.width()));
p.restore();
}
return pm;
}
static QWidget* createDropIndicatorWidget(DropArea dropArea)
{
QLabel* l = new QLabel();
l->setObjectName("DropAreaLabel");
const qreal metric = static_cast<qreal>(l->fontMetrics().height()) * 2.f;
const QSizeF size(metric, metric);
l->setPixmap(createDropIndicatorPixmap(l->palette(), size, dropArea));
return l;
}
///////////////////////////////////////////////////////////////////////
DropOverlay::DropOverlay(QWidget* parent) :
QFrame(parent),
_allowedAreas(InvalidDropArea),
_cross(new DropOverlayCross(this)),
_fullAreaDrop(false),
_lastLocation(InvalidDropArea)
{
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
setWindowOpacity(0.2);
setWindowTitle("DropOverlay");
QBoxLayout* l = new QBoxLayout(QBoxLayout::TopToBottom);
l->setContentsMargins(0, 0, 0, 0);
l->setSpacing(0);
setLayout(l);
// Cross with default drop area widgets.
QHash<DropArea, QWidget*> areaWidgets;
areaWidgets.insert(ADS_NS::TopDropArea, createDropIndicatorWidget(TopDropArea)); //createDropWidget(":/img/split-top.png"));
areaWidgets.insert(ADS_NS::RightDropArea, createDropIndicatorWidget(RightDropArea));//createDropWidget(":/img/split-right.png"));
areaWidgets.insert(ADS_NS::BottomDropArea, createDropIndicatorWidget(BottomDropArea));//createDropWidget(":/img/split-bottom.png"));
areaWidgets.insert(ADS_NS::LeftDropArea, createDropIndicatorWidget(LeftDropArea));//createDropWidget(":/img/split-left.png"));
areaWidgets.insert(ADS_NS::CenterDropArea, createDropIndicatorWidget(CenterDropArea));//createDropWidget(":/img/dock-center.png"));
_cross->setAreaWidgets(areaWidgets);
_cross->setVisible(false);
setVisible(false);
}
DropOverlay::~DropOverlay()
{
}
void DropOverlay::setAllowedAreas(DropAreas areas)
{
if (areas == _allowedAreas)
return;
_allowedAreas = areas;
_cross->reset();
}
DropAreas DropOverlay::allowedAreas() const
{
return _allowedAreas;
}
void DropOverlay::setAreaWidgets(const QHash<DropArea, QWidget*>& widgets)
{
_cross->setAreaWidgets(widgets);
}
DropArea DropOverlay::cursorLocation() const
{
return _cross->cursorLocation();
}
DropArea DropOverlay::showDropOverlay(QWidget* target)
{
if (_target == target)
{
// Hint: We could update geometry of overlay here.
DropArea da = cursorLocation();
if (da != _lastLocation)
{
repaint();
_lastLocation = da;
}
return da;
}
hideDropOverlay();
_fullAreaDrop = false;
_target = target;
_targetRect = QRect();
_lastLocation = InvalidDropArea;
// Move it over the target.
resize(target->size());
move(target->mapToGlobal(target->rect().topLeft()));
show();
return cursorLocation();
}
void DropOverlay::showDropOverlay(QWidget* target, const QRect& targetAreaRect)
{
if (_target == target && _targetRect == targetAreaRect)
{
return;
}
hideDropOverlay();
_fullAreaDrop = true;
_target = target;
_targetRect = targetAreaRect;
_lastLocation = InvalidDropArea;
// Move it over the target's area.
resize(targetAreaRect.size());
move(target->mapToGlobal(QPoint(targetAreaRect.x(), targetAreaRect.y())));
show();
return;
}
void DropOverlay::hideDropOverlay()
{
hide();
_fullAreaDrop = false;
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
_target.clear();
#else
_target = 0;
#endif
_targetRect = QRect();
_lastLocation = InvalidDropArea;
}
void DropOverlay::paintEvent(QPaintEvent*)
{
QPainter p(this);
const QColor areaColor = palette().color(QPalette::Active, QPalette::Highlight);//QColor(0, 100, 255)
// Always draw drop-rect over the entire rect()
if (_fullAreaDrop)
{
QRect r = rect();
p.fillRect(r, QBrush(areaColor, Qt::Dense4Pattern));
p.setBrush(QBrush(areaColor));
p.drawRect(r);
return;
}
// Draw rect based on location
QRect r = rect();
const DropArea da = cursorLocation();
switch (da)
{
case ADS_NS::TopDropArea:
r.setHeight(r.height() / 2);
break;
case ADS_NS::RightDropArea:
r.setX(r.width() / 2);
break;
case ADS_NS::BottomDropArea:
r.setY(r.height() / 2);
break;
case ADS_NS::LeftDropArea:
r.setWidth(r.width() / 2);
break;
case ADS_NS::CenterDropArea:
r = rect();
break;
default:
r = QRect();
}
if (!r.isNull())
{
p.fillRect(r, QBrush(areaColor, Qt::Dense4Pattern));
p.setBrush(QBrush(areaColor));
p.drawRect(r);
}
// Draw rect over the entire size + border.
// auto r = rect();
// r.setWidth(r.width() - 1);
// r.setHeight(r.height() - 1);
// p.fillRect(r, QBrush(QColor(0, 100, 255), Qt::Dense4Pattern));
// p.setBrush(QBrush(QColor(0, 100, 255)));
// p.drawRect(r);
}
void DropOverlay::showEvent(QShowEvent*)
{
_cross->show();
}
void DropOverlay::hideEvent(QHideEvent*)
{
_cross->hide();
}
void DropOverlay::resizeEvent(QResizeEvent* e)
{
_cross->resize(e->size());
}
void DropOverlay::moveEvent(QMoveEvent* e)
{
_cross->move(e->pos());
}
///////////////////////////////////////////////////////////////////////
static QPair<QPoint, int> gridPosForArea(const DropArea area)
{
switch (area)
{
case ADS_NS::TopDropArea:
return qMakePair(QPoint(0, 1), (int) Qt::AlignHCenter | Qt::AlignBottom);
case ADS_NS::RightDropArea:
return qMakePair(QPoint(1, 2), (int) Qt::AlignLeft | Qt::AlignVCenter);
case ADS_NS::BottomDropArea:
return qMakePair(QPoint(2, 1), (int) Qt::AlignHCenter | Qt::AlignTop);
case ADS_NS::LeftDropArea:
return qMakePair(QPoint(1, 0), (int) Qt::AlignRight | Qt::AlignVCenter);
case ADS_NS::CenterDropArea:
return qMakePair(QPoint(1, 1), (int) Qt::AlignCenter);
default:
return QPair<QPoint, int>();
}
}
DropOverlayCross::DropOverlayCross(DropOverlay* overlay) :
QWidget(overlay->parentWidget()),
_overlay(overlay),
_widgets()
{
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
setWindowTitle("DropOverlayCross");
setAttribute(Qt::WA_TranslucentBackground);
_grid = new QGridLayout();
_grid->setContentsMargins(0, 0, 0, 0);
_grid->setSpacing(6);
QBoxLayout* bl1 = new QBoxLayout(QBoxLayout::TopToBottom);
bl1->setContentsMargins(0, 0, 0, 0);
bl1->setSpacing(0);
setLayout(bl1);
QBoxLayout* bl2 = new QBoxLayout(QBoxLayout::LeftToRight);
bl2->setContentsMargins(0, 0, 0, 0);
bl2->setSpacing(0);
bl1->addStretch(1);
bl1->addLayout(bl2);
bl2->addStretch(1);
bl2->addLayout(_grid, 0);
bl2->addStretch(1);
bl1->addStretch(1);
}
DropOverlayCross::~DropOverlayCross()
{
}
void DropOverlayCross::setAreaWidgets(const QHash<DropArea, QWidget*>& widgets)
{
// Delete old widgets.
QMutableHashIterator<DropArea, QWidget*> i(_widgets);
while (i.hasNext())
{
i.next();
QWidget* widget = i.value();
_grid->removeWidget(widget);
delete widget;
i.remove();
}
// Insert new widgets into grid.
_widgets = widgets;
QHashIterator<DropArea, QWidget*> i2(_widgets);
while (i2.hasNext())
{
i2.next();
const DropArea area = i2.key();
QWidget* widget = i2.value();
const QPair<QPoint, int> opts = gridPosForArea(area);
_grid->addWidget(widget, opts.first.x(), opts.first.y(), (Qt::Alignment) opts.second);
}
reset();
}
DropArea DropOverlayCross::cursorLocation() const
{
const QPoint pos = mapFromGlobal(QCursor::pos());
QHashIterator<DropArea, QWidget*> i(_widgets);
while (i.hasNext())
{
i.next();
if (_overlay->allowedAreas().testFlag(i.key())
&& i.value()
&& i.value()->isVisible()
&& i.value()->geometry().contains(pos))
{
return i.key();
}
}
return InvalidDropArea;
}
void DropOverlayCross::showEvent(QShowEvent*)
{
resize(_overlay->size());
move(_overlay->pos());
}
void DropOverlayCross::reset()
{
QList<DropArea> allAreas;
allAreas << ADS_NS::TopDropArea << ADS_NS::RightDropArea << ADS_NS::BottomDropArea << ADS_NS::LeftDropArea << ADS_NS::CenterDropArea;
const DropAreas allowedAreas = _overlay->allowedAreas();
// Update visibility of area widgets based on allowedAreas.
for (int i = 0; i < allAreas.count(); ++i)
{
const QPair<QPoint, int> opts = gridPosForArea(allAreas.at(i));
QLayoutItem* item = _grid->itemAtPosition(opts.first.x(), opts.first.y());
QWidget* w = NULL;
if (item && (w = item->widget()) != NULL)
{
w->setVisible(allowedAreas.testFlag(allAreas.at(i)));
}
}
}
ADS_NAMESPACE_END

View File

@@ -1,84 +0,0 @@
#include "ads/FloatingWidget.h"
#include <QBoxLayout>
#include <QPushButton>
#include <QSizePolicy>
#include <QMouseEvent>
#include <QStyle>
#include "ads/ContainerWidget.h"
#include "ads/SectionTitleWidget.h"
#include "ads/SectionContentWidget.h"
#include "ads/Internal.h"
ADS_NAMESPACE_BEGIN
FloatingWidget::FloatingWidget(ContainerWidget* container, SectionContent::RefPtr sc, SectionTitleWidget* titleWidget, SectionContentWidget* contentWidget, QWidget* parent) :
QWidget(parent, Qt::CustomizeWindowHint | Qt::Tool),
_container(container),
_content(sc),
_titleWidget(titleWidget),
_contentWidget(contentWidget)
{
QBoxLayout* l = new QBoxLayout(QBoxLayout::TopToBottom);
l->setContentsMargins(0, 0, 0, 0);
l->setSpacing(0);
setLayout(l);
// Title + Controls
_titleLayout = new QBoxLayout(QBoxLayout::LeftToRight);
_titleLayout->addWidget(titleWidget, 1);
l->addLayout(_titleLayout, 0);
titleWidget->setActiveTab(false);
if (sc->flags().testFlag(SectionContent::Closeable))
{
QPushButton* closeButton = new QPushButton();
closeButton->setObjectName("closeButton");
closeButton->setFlat(true);
closeButton->setIcon(style()->standardIcon(QStyle::SP_TitleBarCloseButton));
closeButton->setToolTip(tr("Close"));
closeButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
_titleLayout->addWidget(closeButton);
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
QObject::connect(closeButton, &QPushButton::clicked, this, &FloatingWidget::onCloseButtonClicked);
#else
QObject::connect(closeButton, SIGNAL(clicked(bool)), this, SLOT(onCloseButtonClicked()));
#endif
}
// Content
l->addWidget(contentWidget, 1);
contentWidget->show();
// _container->_floatingWidgets.append(this);
}
FloatingWidget::~FloatingWidget()
{
_container->_floatings.removeAll(this); // Note: I don't like this here, but we have to remove it from list...
}
bool FloatingWidget::takeContent(InternalContentData& data)
{
data.content = _content;
data.titleWidget = _titleWidget;
data.contentWidget = _contentWidget;
_titleLayout->removeWidget(_titleWidget);
_titleWidget->setParent(_container);
_titleWidget = NULL;
layout()->removeWidget(_contentWidget);
_contentWidget->setParent(_container);
_contentWidget = NULL;
return true;
}
void FloatingWidget::onCloseButtonClicked()
{
_container->hideSectionContent(_content);
}
ADS_NAMESPACE_END

View File

@@ -1,15 +0,0 @@
#include "ads/Internal.h"
ADS_NAMESPACE_BEGIN
InternalContentData::InternalContentData() :
titleWidget(NULL),
contentWidget(NULL)
{
}
InternalContentData::~InternalContentData()
{
}
ADS_NAMESPACE_END

View File

@@ -1,115 +0,0 @@
#include "ads/SectionContent.h"
#include <QWidget>
#include <QLabel>
#include "ads/Internal.h"
#include "ads/ContainerWidget.h"
ADS_NAMESPACE_BEGIN
SectionContent::SectionContent() :
_uid(GetNextUid()),
_flags(AllFlags)
{
}
SectionContent::RefPtr SectionContent::newSectionContent(const QString& uniqueName, ContainerWidget* container, QWidget* title, QWidget* content)
{
if (uniqueName.isEmpty())
{
qFatal("Can not create SectionContent with empty uniqueName");
return RefPtr();
}
else if (SCLookupMapByName(container).contains(uniqueName))
{
qFatal("Can not create SectionContent with already used uniqueName");
return RefPtr();
}
else if (!container || !title || !content)
{
qFatal("Can not create SectionContent with NULL values");
return RefPtr();
}
QSharedPointer<SectionContent> sc(new SectionContent());
sc->_uniqueName = uniqueName;
sc->_containerWidget = container;
sc->_titleWidget = title;
sc->_contentWidget = content;
SCLookupMapById(container).insert(sc->uid(), sc);
SCLookupMapByName(container).insert(sc->uniqueName(), sc);
return sc;
}
SectionContent::~SectionContent()
{
if (_containerWidget)
{
SCLookupMapById(_containerWidget).remove(_uid);
SCLookupMapByName(_containerWidget).remove(_uniqueName);
}
delete _titleWidget;
delete _contentWidget;
}
int SectionContent::uid() const
{
return _uid;
}
QString SectionContent::uniqueName() const
{
return _uniqueName;
}
ContainerWidget* SectionContent::containerWidget() const
{
return _containerWidget;
}
QWidget* SectionContent::titleWidget() const
{
return _titleWidget;
}
QWidget* SectionContent::contentWidget() const
{
return _contentWidget;
}
SectionContent::Flags SectionContent::flags() const
{
return _flags;
}
QString SectionContent::visibleTitle() const
{
if (_title.isEmpty())
return _uniqueName;
return _title;
}
QString SectionContent::title() const
{
return _title;
}
void SectionContent::setTitle(const QString& title)
{
_title = title;
}
void SectionContent::setFlags(const Flags f)
{
_flags = f;
}
int SectionContent::GetNextUid()
{
static int NextUid = 0;
return ++NextUid;
}
ADS_NAMESPACE_END

View File

@@ -1,23 +0,0 @@
#include "ads/SectionContentWidget.h"
#include <QBoxLayout>
ADS_NAMESPACE_BEGIN
SectionContentWidget::SectionContentWidget(SectionContent::RefPtr c, QWidget* parent) :
QFrame(parent),
_content(c)
{
QBoxLayout* l = new QBoxLayout(QBoxLayout::TopToBottom);
l->setContentsMargins(0, 0, 0, 0);
l->setSpacing(0);
l->addWidget(_content->contentWidget());
setLayout(l);
}
SectionContentWidget::~SectionContentWidget()
{
layout()->removeWidget(_content->contentWidget());
}
ADS_NAMESPACE_END

View File

@@ -1,291 +0,0 @@
#include "ads/SectionTitleWidget.h"
#include <QString>
#include <QApplication>
#include <QBoxLayout>
#include <QMouseEvent>
#include <QMimeData>
#include <QDrag>
#include <QCursor>
#include <QStyle>
#include <QSplitter>
#ifdef ADS_ANIMATIONS_ENABLED
#include <QPropertyAnimation>
#include <QParallelAnimationGroup>
#endif
#include "ads/Internal.h"
#include "ads/DropOverlay.h"
#include "ads/SectionContent.h"
#include "ads/SectionWidget.h"
#include "ads/FloatingWidget.h"
#include "ads/ContainerWidget.h"
ADS_NAMESPACE_BEGIN
SectionTitleWidget::SectionTitleWidget(SectionContent::RefPtr content, QWidget* parent) :
QFrame(parent),
_content(content),
_tabMoving(false),
_activeTab(false)
{
QBoxLayout* l = new QBoxLayout(QBoxLayout::LeftToRight);
l->setContentsMargins(0, 0, 0, 0);
l->setSpacing(0);
l->addWidget(content->titleWidget());
setLayout(l);
}
SectionTitleWidget::~SectionTitleWidget()
{
layout()->removeWidget(_content->titleWidget());
}
bool SectionTitleWidget::isActiveTab() const
{
return _activeTab;
}
void SectionTitleWidget::setActiveTab(bool active)
{
if (active != _activeTab)
{
_activeTab = active;
style()->unpolish(this);
style()->polish(this);
update();
emit activeTabChanged();
}
}
void SectionTitleWidget::mousePressEvent(QMouseEvent* ev)
{
if (ev->button() == Qt::LeftButton)
{
ev->accept();
_dragStartPos = ev->pos();
return;
}
QFrame::mousePressEvent(ev);
}
void SectionTitleWidget::mouseReleaseEvent(QMouseEvent* ev)
{
SectionWidget* section = NULL;
ContainerWidget* cw = findParentContainerWidget(this);
// Drop contents of FloatingWidget into SectionWidget.
if (_fw)
{
SectionWidget* sw = cw->sectionAt(cw->mapFromGlobal(ev->globalPos()));
if (sw)
{
cw->_dropOverlay->setAllowedAreas(ADS_NS::AllAreas);
DropArea loc = cw->_dropOverlay->showDropOverlay(sw);
if (loc != InvalidDropArea)
{
#if !defined(ADS_ANIMATIONS_ENABLED)
InternalContentData data;
_fw->takeContent(data);
_fw->deleteLater();
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
_fw.clear();
#else
_fw = 0;
#endif
cw->dropContent(data, sw, loc, true);
#else
QPropertyAnimation* moveAnim = new QPropertyAnimation(_fw, "pos", this);
moveAnim->setStartValue(_fw->pos());
moveAnim->setEndValue(sw->mapToGlobal(sw->rect().topLeft()));
moveAnim->setDuration(ADS_ANIMATION_DURATION);
QPropertyAnimation* resizeAnim = new QPropertyAnimation(_fw, "size", this);
resizeAnim->setStartValue(_fw->size());
resizeAnim->setEndValue(sw->size());
resizeAnim->setDuration(ADS_ANIMATION_DURATION);
QParallelAnimationGroup* animGroup = new QParallelAnimationGroup(this);
QObject::connect(animGroup, &QPropertyAnimation::finished, [this, data, sw, loc]()
{
InternalContentData data = _fw->takeContent();
_fw->deleteLater();
_fw.clear();
cw->dropContent(data, sw, loc);
});
animGroup->addAnimation(moveAnim);
animGroup->addAnimation(resizeAnim);
animGroup->start(QAbstractAnimation::DeleteWhenStopped);
#endif
}
}
// Mouse is over a outer-edge drop area
else
{
DropArea dropArea = ADS_NS::InvalidDropArea;
if (cw->outerTopDropRect().contains(cw->mapFromGlobal(ev->globalPos())))
dropArea = ADS_NS::TopDropArea;
if (cw->outerRightDropRect().contains(cw->mapFromGlobal(ev->globalPos())))
dropArea = ADS_NS::RightDropArea;
if (cw->outerBottomDropRect().contains(cw->mapFromGlobal(ev->globalPos())))
dropArea = ADS_NS::BottomDropArea;
if (cw->outerLeftDropRect().contains(cw->mapFromGlobal(ev->globalPos())))
dropArea = ADS_NS::LeftDropArea;
if (dropArea != ADS_NS::InvalidDropArea)
{
#if !defined(ADS_ANIMATIONS_ENABLED)
InternalContentData data;
_fw->takeContent(data);
_fw->deleteLater();
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
_fw.clear();
#else
_fw = 0;
#endif
cw->dropContent(data, NULL, dropArea, true);
#else
#endif
}
}
}
// End of tab moving, change order now
else if (_tabMoving
&& (section = findParentSectionWidget(this)) != NULL)
{
// Find tab under mouse
QPoint pos = ev->globalPos();
pos = section->mapFromGlobal(pos);
const int fromIndex = section->indexOfContent(_content);
const int toIndex = section->indexOfContentByTitlePos(pos, this);
section->moveContent(fromIndex, toIndex);
}
if (!_dragStartPos.isNull())
emit clicked();
// Reset
_dragStartPos = QPoint();
_tabMoving = false;
cw->_dropOverlay->hideDropOverlay();
QFrame::mouseReleaseEvent(ev);
}
void SectionTitleWidget::mouseMoveEvent(QMouseEvent* ev)
{
ContainerWidget* cw = findParentContainerWidget(this);
SectionWidget* section = NULL;
// Move already existing FloatingWidget
if (_fw && (ev->buttons() & Qt::LeftButton))
{
ev->accept();
const QPoint moveToPos = ev->globalPos() - (_dragStartPos + QPoint(ADS_WINDOW_FRAME_BORDER_WIDTH, ADS_WINDOW_FRAME_BORDER_WIDTH));
_fw->move(moveToPos);
// Show drop indicator
if (true)
{
// Mouse is over a SectionWidget
section = cw->sectionAt(cw->mapFromGlobal(QCursor::pos()));
if (section)
{
cw->_dropOverlay->setAllowedAreas(ADS_NS::AllAreas);
cw->_dropOverlay->showDropOverlay(section);
}
// Mouse is at the edge of the ContainerWidget
// Top, Right, Bottom, Left
else if (cw->outerTopDropRect().contains(cw->mapFromGlobal(QCursor::pos())))
{
cw->_dropOverlay->setAllowedAreas(ADS_NS::TopDropArea);
cw->_dropOverlay->showDropOverlay(cw, cw->outerTopDropRect());
}
else if (cw->outerRightDropRect().contains(cw->mapFromGlobal(QCursor::pos())))
{
cw->_dropOverlay->setAllowedAreas(ADS_NS::RightDropArea);
cw->_dropOverlay->showDropOverlay(cw, cw->outerRightDropRect());
}
else if (cw->outerBottomDropRect().contains(cw->mapFromGlobal(QCursor::pos())))
{
cw->_dropOverlay->setAllowedAreas(ADS_NS::BottomDropArea);
cw->_dropOverlay->showDropOverlay(cw, cw->outerBottomDropRect());
}
else if (cw->outerLeftDropRect().contains(cw->mapFromGlobal(QCursor::pos())))
{
cw->_dropOverlay->setAllowedAreas(ADS_NS::LeftDropArea);
cw->_dropOverlay->showDropOverlay(cw, cw->outerLeftDropRect());
}
else
{
cw->_dropOverlay->hideDropOverlay();
}
}
return;
}
// Begin to drag/float the SectionContent.
else if (!_fw && !_dragStartPos.isNull() && (ev->buttons() & Qt::LeftButton)
&& (section = findParentSectionWidget(this)) != NULL
&& !section->titleAreaGeometry().contains(section->mapFromGlobal(ev->globalPos())))
{
ev->accept();
// Create floating widget.
InternalContentData data;
if (!section->takeContent(_content->uid(), data))
{
qWarning() << "THIS SHOULD NOT HAPPEN!!" << _content->uid() << _content->uniqueName();
return;
}
_fw = new FloatingWidget(cw, data.content, data.titleWidget, data.contentWidget, cw);
_fw->resize(section->size());
cw->_floatings.append(_fw); // Note: I don't like this...
const QPoint moveToPos = ev->globalPos() - (_dragStartPos + QPoint(ADS_WINDOW_FRAME_BORDER_WIDTH, ADS_WINDOW_FRAME_BORDER_WIDTH));
_fw->move(moveToPos);
_fw->show();
// Delete old section, if it is empty now.
if (section->contents().isEmpty())
{
delete section;
section = NULL;
}
deleteEmptySplitter(cw);
return;
}
// Handle movement of this tab
else if (_tabMoving
&& (section = findParentSectionWidget(this)) != NULL)
{
ev->accept();
int left, top, right, bottom;
getContentsMargins(&left, &top, &right, &bottom);
QPoint moveToPos = mapToParent(ev->pos()) - _dragStartPos;
moveToPos.setY(0/* + top*/);
move(moveToPos);
return;
}
// Begin to drag title inside the title area to switch its position inside the SectionWidget.
else if (!_dragStartPos.isNull() && (ev->buttons() & Qt::LeftButton)
&& (ev->pos() - _dragStartPos).manhattanLength() >= QApplication::startDragDistance() // Wait a few pixels before start moving
&& (section = findParentSectionWidget(this)) != NULL
&& section->titleAreaGeometry().contains(section->mapFromGlobal(ev->globalPos())))
{
ev->accept();
_tabMoving = true;
raise(); // Raise current title-widget above other tabs
return;
}
QFrame::mouseMoveEvent(ev);
}
ADS_NAMESPACE_END

View File

@@ -1,458 +0,0 @@
#include "ads/SectionWidget.h"
#include <QApplication>
#include <QBoxLayout>
#include <QStackedLayout>
#include <QMouseEvent>
#include <QWheelEvent>
#include <QDragEnterEvent>
#include <QMimeData>
#include <QPainter>
#include <QStyle>
#include <QSplitter>
#include <QPushButton>
#include <QScrollBar>
#include <QMenu>
#if defined(ADS_ANIMATIONS_ENABLED)
#include <QGraphicsDropShadowEffect>
#endif
#include "ads/Internal.h"
#include "ads/DropOverlay.h"
#include "ads/SectionContent.h"
#include "ads/SectionTitleWidget.h"
#include "ads/SectionContentWidget.h"
#include "ads/FloatingWidget.h"
#include "ads/ContainerWidget.h"
ADS_NAMESPACE_BEGIN
SectionWidget::SectionWidget(ContainerWidget* parent) :
QFrame(parent),
_uid(GetNextUid()),
_container(parent),
_tabsLayout(NULL),
_tabsLayoutInitCount(0),
_contentsLayout(NULL),
_mousePressTitleWidget(NULL)
{
QBoxLayout* l = new QBoxLayout(QBoxLayout::TopToBottom);
l->setContentsMargins(0, 0, 0, 0);
l->setSpacing(0);
setLayout(l);
/* top area with tabs and close button */
_topLayout = new QBoxLayout(QBoxLayout::LeftToRight);
_topLayout->setContentsMargins(0, 0, 0, 0);
_topLayout->setSpacing(0);
l->addLayout(_topLayout);
_tabsScrollArea = new SectionWidgetTabsScrollArea(this);
_topLayout->addWidget(_tabsScrollArea, 1);
_tabsContainerWidget = new QWidget();
_tabsContainerWidget->setObjectName("tabsContainerWidget");
_tabsScrollArea->setWidget(_tabsContainerWidget);
_tabsLayout = new QBoxLayout(QBoxLayout::LeftToRight);
_tabsLayout->setContentsMargins(0, 0, 0, 0);
_tabsLayout->setSpacing(0);
_tabsLayout->addStretch(1);
_tabsContainerWidget->setLayout(_tabsLayout);
_tabsMenuButton = new QPushButton();
_tabsMenuButton->setObjectName("tabsMenuButton");
_tabsMenuButton->setFlat(true);
_tabsMenuButton->setIcon(style()->standardIcon(QStyle::SP_TitleBarUnshadeButton));
_tabsMenuButton->setMaximumWidth(_tabsMenuButton->iconSize().width());
_topLayout->addWidget(_tabsMenuButton, 0);
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
//QObject::connect(_tabsMenuButton, &QPushButton::clicked, this, &SectionWidget::onTabsMenuButtonClicked);
#else
//QObject::connect(_tabsMenuButton, SIGNAL(clicked()), this, SLOT(onTabsMenuButtonClicked()));
#endif
_closeButton = new QPushButton();
_closeButton->setObjectName("closeButton");
_closeButton->setFlat(true);
_closeButton->setIcon(style()->standardIcon(QStyle::SP_TitleBarCloseButton));
_closeButton->setToolTip(tr("Close"));
_closeButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
_topLayout->addWidget(_closeButton, 0);
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
QObject::connect(_closeButton, &QPushButton::clicked, this, &SectionWidget::onCloseButtonClicked);
#else
QObject::connect(_closeButton, SIGNAL(clicked(bool)), this, SLOT(onCloseButtonClicked()));
#endif
_tabsLayoutInitCount = _tabsLayout->count();
/* central area with contents */
_contentsLayout = new QStackedLayout();
_contentsLayout->setContentsMargins(0, 0, 0, 0);
_contentsLayout->setSpacing(0);
l->addLayout(_contentsLayout, 1);
#if defined(ADS_ANIMATIONS_ENABLED)
QGraphicsDropShadowEffect* shadow = new QGraphicsDropShadowEffect(this);
shadow->setOffset(0, 0);
shadow->setBlurRadius(8);
setGraphicsEffect(shadow);
#endif
SWLookupMapById(_container).insert(_uid, this);
}
SectionWidget::~SectionWidget()
{
if (_container)
{
SWLookupMapById(_container).remove(_uid);
_container->_sections.removeAll(this); // Note: I don't like this here, but we have to remove it from list...
}
// Delete empty QSplitter.
QSplitter* splitter = findParentSplitter(this);
if (splitter && splitter->count() == 0)
{
splitter->deleteLater();
}
}
int SectionWidget::uid() const
{
return _uid;
}
ContainerWidget* SectionWidget::containerWidget() const
{
return _container;
}
QRect SectionWidget::titleAreaGeometry() const
{
return _topLayout->geometry();
}
QRect SectionWidget::contentAreaGeometry() const
{
return _contentsLayout->geometry();
}
void SectionWidget::addContent(const SectionContent::RefPtr& c)
{
_contents.append(c);
SectionTitleWidget* title = new SectionTitleWidget(c, NULL);
_sectionTitles.append(title);
_tabsLayout->insertWidget(_tabsLayout->count() - _tabsLayoutInitCount, title);
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
QObject::connect(title, &SectionTitleWidget::clicked, this, &SectionWidget::onSectionTitleClicked);
#else
QObject::connect(title, SIGNAL(clicked()), this, SLOT(onSectionTitleClicked()));
#endif
SectionContentWidget* content = new SectionContentWidget(c, NULL);
_sectionContents.append(content);
_contentsLayout->addWidget(content);
// Active first TAB.
if (_contents.size() == 1)
setCurrentIndex(0);
// Switch to newest.
// else
// setCurrentIndex(_contentsLayout->count() - 1);
updateTabsMenu();
}
void SectionWidget::addContent(const InternalContentData& data, bool autoActivate)
{
_contents.append(data.content);
// Add title-widget to tab-bar
// #FIX: Make it visible, since it is possible that it was hidden previously.
_sectionTitles.append(data.titleWidget);
_tabsLayout->insertWidget(_tabsLayout->count() - _tabsLayoutInitCount, data.titleWidget);
data.titleWidget->setVisible(true);
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
QObject::connect(data.titleWidget, &SectionTitleWidget::clicked, this, &SectionWidget::onSectionTitleClicked);
#else
QObject::connect(data.titleWidget, SIGNAL(clicked()), this, SLOT(onSectionTitleClicked()));
#endif
// Add content-widget to stack.
// Visibility is managed by QStackedWidget.
_sectionContents.append(data.contentWidget);
_contentsLayout->addWidget(data.contentWidget);
// Activate first TAB.
if (_contents.size() == 1)
setCurrentIndex(0);
// Switch to just added TAB.
else if (autoActivate)
setCurrentIndex(_contents.count() - 1);
// Mark it as inactive tab.
else
data.titleWidget->setActiveTab(false); // or: setCurrentIndex(currentIndex())
updateTabsMenu();
}
bool SectionWidget::takeContent(int uid, InternalContentData& data)
{
// Find SectionContent.
SectionContent::RefPtr sc;
int index = -1;
for (int i = 0; i < _contents.count(); i++)
{
if (_contents[i]->uid() != uid)
continue;
index = i;
sc = _contents.takeAt(i);
break;
}
if (!sc)
return false;
// Title wrapper widget (TAB)
SectionTitleWidget* title = _sectionTitles.takeAt(index);
if (title)
{
_tabsLayout->removeWidget(title);
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
title->setAttribute(Qt::WA_WState_Created, false); /* fix: floating rubberband #16 */
#endif
title->disconnect(this);
title->setParent(_container);
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
title->setAttribute(Qt::WA_WState_Created, true); /* fix: floating rubberband #16 */
#endif
}
// Content wrapper widget (CONTENT)
SectionContentWidget* content = _sectionContents.takeAt(index);
if (content)
{
_contentsLayout->removeWidget(content);
content->disconnect(this);
content->setParent(_container);
}
// Select the previous tab as activeTab.
if (_contents.size() > 0 && title->isActiveTab())
{
if (index > 0)
setCurrentIndex(index - 1);
else
setCurrentIndex(0);
}
updateTabsMenu();
data.content = sc;
data.titleWidget = title;
data.contentWidget = content;
return !data.content.isNull();
}
int SectionWidget::indexOfContent(const SectionContent::RefPtr& c) const
{
return _contents.indexOf(c);
}
int SectionWidget::indexOfContentByUid(int uid) const
{
for (int i = 0; i < _contents.count(); ++i)
{
if (_contents[i]->uid() == uid)
return i;
}
return -1;
}
int SectionWidget::indexOfContentByTitlePos(const QPoint& p, QWidget* exclude) const
{
int index = -1;
for (int i = 0; i < _sectionTitles.size(); ++i)
{
if (_sectionTitles[i]->geometry().contains(p) && (exclude == NULL || _sectionTitles[i] != exclude))
{
index = i;
break;
}
}
return index;
}
int SectionWidget::currentIndex() const
{
return _contentsLayout->currentIndex();
}
void SectionWidget::moveContent(int from, int to)
{
if (from >= _contents.size() || from < 0 || to >= _contents.size() || to < 0 || from == to)
{
qDebug() << "Invalid index for tab movement" << from << to;
_tabsLayout->update();
return;
}
_contents.move(from, to);
_sectionTitles.move(from, to);
_sectionContents.move(from, to);
QLayoutItem* liFrom = NULL;
liFrom = _tabsLayout->takeAt(from);
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
_tabsLayout->insertItem(to, liFrom);
#else
_tabsLayout->insertWidget(to, liFrom->widget());
delete liFrom;
liFrom = NULL;
#endif
liFrom = _contentsLayout->takeAt(from);
_contentsLayout->insertWidget(to, liFrom->widget());
delete liFrom;
updateTabsMenu();
}
void SectionWidget::showEvent(QShowEvent*)
{
_tabsScrollArea->ensureWidgetVisible(_sectionTitles.at(currentIndex()));
}
void SectionWidget::setCurrentIndex(int index)
{
if (index < 0 || index > _contents.count() - 1)
{
qWarning() << Q_FUNC_INFO << "Invalid index" << index;
return;
}
// Set active TAB
for (int i = 0; i < _tabsLayout->count(); ++i)
{
QLayoutItem* item = _tabsLayout->itemAt(i);
if (item->widget())
{
SectionTitleWidget* stw = dynamic_cast<SectionTitleWidget*>(item->widget());
if (stw)
{
if (i == index)
{
stw->setActiveTab(true);
_tabsScrollArea->ensureWidgetVisible(stw);
if (stw->_content->flags().testFlag(SectionContent::Closeable))
_closeButton->setEnabled(true);
else
_closeButton->setEnabled(false);
}
else
stw->setActiveTab(false);
}
}
}
// Set active CONTENT
_contentsLayout->setCurrentIndex(index);
}
void SectionWidget::onSectionTitleClicked()
{
SectionTitleWidget* stw = qobject_cast<SectionTitleWidget*>(sender());
if (stw)
{
int index = _tabsLayout->indexOf(stw);
setCurrentIndex(index);
}
}
void SectionWidget::onCloseButtonClicked()
{
const int index = currentIndex();
if (index < 0 || index > _contents.size() - 1)
return;
SectionContent::RefPtr sc = _contents.at(index);
if (sc.isNull())
return;
_container->hideSectionContent(sc);
}
void SectionWidget::onTabsMenuActionTriggered(bool)
{
QAction* a = qobject_cast<QAction*>(sender());
if (a)
{
const int uid = a->data().toInt();
const int index = indexOfContentByUid(uid);
if (index >= 0)
setCurrentIndex(index);
}
}
void SectionWidget::updateTabsMenu()
{
QMenu* m = new QMenu();
for (int i = 0; i < _contents.count(); ++i)
{
const SectionContent::RefPtr& sc = _contents.at(i);
QAction* a = m->addAction(QIcon(), sc->visibleTitle());
a->setData(sc->uid());
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
QObject::connect(a, &QAction::triggered, this, &SectionWidget::onTabsMenuActionTriggered);
#else
QObject::connect(a, SIGNAL(triggered(bool)), this, SLOT(onTabsMenuActionTriggered(bool)));
#endif
}
QMenu* old = _tabsMenuButton->menu();
_tabsMenuButton->setMenu(m);
delete old;
}
int SectionWidget::GetNextUid()
{
static int NextUid = 0;
return ++NextUid;
}
/*****************************************************************************/
SectionWidgetTabsScrollArea::SectionWidgetTabsScrollArea(SectionWidget*,
QWidget* parent) :
QScrollArea(parent)
{
/* Important: QSizePolicy::Ignored makes the QScrollArea behaves
like a QLabel and automatically fits into the layout. */
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Ignored);
setFrameStyle(QFrame::NoFrame);
setWidgetResizable(true);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
}
SectionWidgetTabsScrollArea::~SectionWidgetTabsScrollArea()
{
}
void SectionWidgetTabsScrollArea::wheelEvent(QWheelEvent* e)
{
e->accept();
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
const int direction = e->angleDelta().y();
#else
const int direction = e->delta();
#endif
if (direction < 0)
horizontalScrollBar()->setValue(horizontalScrollBar()->value() + 20);
else
horizontalScrollBar()->setValue(horizontalScrollBar()->value() - 20);
}
ADS_NAMESPACE_END

View File

@@ -1,440 +0,0 @@
#include "ads/Serialization.h"
#include <QDebug>
ADS_NAMESPACE_SER_BEGIN
/*
\namespace ads::serialization
Serialization of ContainerWidget
--------------------------------
# Data Format Header
quint32 Magic
quint32 Major Version
quint32 Minor Version
# Offsets of available contents
qint32 Number of offset headers
LOOP
qint32 Type (e.g. Hierachy, SectionIndex)
qint64 Offset
qint64 Length
# Type: Hierachy
# Used to recreate the GUI geometry and state.
int Number of floating widgets
LOOP Floating widgets
QString Unique name of content
QByteArray Geometry of floating widget
bool Visibility
int Number of layout items (Valid values: 0, 1)
IF 0
int Number of hidden contents
LOOP Contents
QString Unique name of content
ELSEIF 1
... todo ...
ENDIF
# Type: SectionIndex
# Can be used for quick lookups on details for SectionWidgets.
# It includes sizes and its contents.
qint32 Number of section-widgets
LOOP
qint32 Width
qint32 Height
qint32 Current active tab index
qint32 Number of contents
LOOP
QString Unique name of content
bool Visibility
qint32 Preferred tab index
*/
///////////////////////////////////////////////////////////////////////////////
qint32 HeaderEntity::MAGIC = 0x00001337;
qint32 HeaderEntity::MAJOR_VERSION = 2;
qint32 HeaderEntity::MINOR_VERSION = 0;
HeaderEntity::HeaderEntity() :
magic(0), majorVersion(0), minorVersion(0)
{
}
QDataStream& operator<<(QDataStream& out, const HeaderEntity& data)
{
out << data.magic;
out << data.majorVersion;
out << data.minorVersion;
return out;
}
QDataStream& operator>>(QDataStream& in, HeaderEntity& data)
{
in >> data.magic;
in >> data.majorVersion;
in >> data.minorVersion;
return in;
}
///////////////////////////////////////////////////////////////////////////////
OffsetsHeaderEntity::OffsetsHeaderEntity() :
entriesCount(0)
{
}
QDataStream& operator<<(QDataStream& out, const OffsetsHeaderEntity& data)
{
out << data.entriesCount;
for (int i = 0; i < data.entriesCount; ++i)
{
out << data.entries.at(i);
}
return out;
}
QDataStream& operator>>(QDataStream& in, OffsetsHeaderEntity& data)
{
in >> data.entriesCount;
for (int i = 0; i < data.entriesCount; ++i)
{
OffsetsHeaderEntryEntity entry;
in >> entry;
data.entries.append(entry);
}
return in;
}
///////////////////////////////////////////////////////////////////////////////
OffsetsHeaderEntryEntity::OffsetsHeaderEntryEntity() :
type(ET_Unknown), offset(0), contentSize(0)
{
}
QDataStream& operator<<(QDataStream& out, const OffsetsHeaderEntryEntity& data)
{
out << data.type;
out << data.offset;
out << data.contentSize;
return out;
}
QDataStream& operator>>(QDataStream& in, OffsetsHeaderEntryEntity& data)
{
in >> data.type;
in >> data.offset;
in >> data.contentSize;
return in;
}
///////////////////////////////////////////////////////////////////////////////
SectionEntity::SectionEntity() :
x(0), y(0), width(0), height(0), currentIndex(0), sectionContentsCount(0)
{
}
QDataStream& operator<<(QDataStream& out, const SectionEntity& data)
{
out << data.x;
out << data.y;
out << data.width;
out << data.height;
out << data.currentIndex;
out << data.sectionContentsCount;
for (int i = 0; i < data.sectionContentsCount; ++i)
{
out << data.sectionContents.at(i);
}
return out;
}
QDataStream& operator>>(QDataStream& in, SectionEntity& data)
{
in >> data.x;
in >> data.y;
in >> data.width;
in >> data.height;
in >> data.currentIndex;
in >> data.sectionContentsCount;
for (int i = 0; i < data.sectionContentsCount; ++i)
{
SectionContentEntity sc;
in >> sc;
data.sectionContents.append(sc);
}
return in;
}
///////////////////////////////////////////////////////////////////////////////
SectionContentEntity::SectionContentEntity() :
visible(false), preferredIndex(0)
{
}
QDataStream& operator<<(QDataStream& out, const SectionContentEntity& data)
{
out << data.uniqueName;
out << data.visible;
out << data.preferredIndex;
return out;
}
QDataStream& operator>>(QDataStream& in, SectionContentEntity& data)
{
in >> data.uniqueName;
in >> data.visible;
in >> data.preferredIndex;
return in;
}
///////////////////////////////////////////////////////////////////////////////
FloatingContentEntity::FloatingContentEntity() :
xpos(0), ypos(0), width(0), height(0), visible(false)
{
}
QDataStream& operator<<(QDataStream& out, const FloatingContentEntity& data)
{
out << data.uniqueName;
out << data.xpos;
out << data.ypos;
out << data.width;
out << data.height;
out << data.visible;
return out;
}
QDataStream& operator>>(QDataStream& in, FloatingContentEntity& data)
{
in >> data.uniqueName;
in >> data.xpos;
in >> data.ypos;
in >> data.width;
in >> data.height;
in >> data.visible;
return in;
}
///////////////////////////////////////////////////////////////////////////////
HierarchyData::HierarchyData()
{
}
QDataStream& operator<<(QDataStream& out, const HierarchyData& data)
{
out << data.data;
return out;
}
QDataStream& operator>>(QDataStream& in, HierarchyData& data)
{
in >> data.data;
return in;
}
///////////////////////////////////////////////////////////////////////////////
SectionIndexData::SectionIndexData() :
sectionsCount(0)
{
}
QDataStream& operator<<(QDataStream& out, const SectionIndexData& data)
{
out << data.sectionsCount;
for (int i = 0; i < data.sectionsCount; ++i)
{
out << data.sections.at(i);
}
return out;
}
QDataStream& operator>>(QDataStream& in, SectionIndexData& data)
{
in >> data.sectionsCount;
for (int i = 0; i < data.sectionsCount; ++i)
{
SectionEntity s;
in >> s;
data.sections.append(s);
}
return in;
}
///////////////////////////////////////////////////////////////////////////////
InMemoryWriter::InMemoryWriter()
{
_contentBuffer.open(QIODevice::ReadWrite);
}
bool InMemoryWriter::write(qint32 entryType, const QByteArray& data)
{
OffsetsHeaderEntryEntity entry;
entry.type = entryType;
entry.offset = _contentBuffer.pos(); // Relative offset!
entry.contentSize = data.size();
_contentBuffer.write(data);
_offsetsHeader.entries.append(entry);
_offsetsHeader.entriesCount += 1;
return true;
}
bool InMemoryWriter::write(const SectionIndexData& data)
{
OffsetsHeaderEntryEntity entry;
entry.type = ET_SectionIndex;
entry.offset = _contentBuffer.pos(); // Relative offset!
QDataStream out(&_contentBuffer);
out.setVersion(QDataStream::Qt_4_5);
out << data;
entry.contentSize = _contentBuffer.size() - entry.offset;
_offsetsHeader.entries.append(entry);
_offsetsHeader.entriesCount += 1;
return true;
}
QByteArray InMemoryWriter::toByteArray() const
{
QByteArray data;
QDataStream out(&data, QIODevice::ReadWrite);
out.setVersion(QDataStream::Qt_4_5);
// Basic format header.
HeaderEntity header;
header.magic = HeaderEntity::MAGIC;
header.majorVersion = HeaderEntity::MAJOR_VERSION;
header.minorVersion = HeaderEntity::MINOR_VERSION;
out << header;
// Offsets-Header
// - Save begin pos
// - Write OffsetsHeader
// - Convert relative- to absolute-offsets
// - Seek back to begin-pos and write OffsetsHeader again.
// Use a copy of OffsetsHeader to keep the _offsetsHeader relative.
const qint64 posOffsetHeaders = out.device()->pos();
OffsetsHeaderEntity offsetsHeader = _offsetsHeader;
out << offsetsHeader;
// Now we know the size of the entire header.
// We can update the relative- to absolute-offsets now.
const qint64 allHeaderSize = out.device()->pos();
for (int i = 0; i < offsetsHeader.entriesCount; ++i)
{
offsetsHeader.entries[i].offset += allHeaderSize; // Absolute offset!
}
// Seek back and write again with absolute offsets.
// TODO Thats not nice, but it works...
out.device()->seek(posOffsetHeaders);
out << offsetsHeader;
// Write contents.
out.writeRawData(_contentBuffer.data().constData(), _contentBuffer.size());
return data;
}
///////////////////////////////////////////////////////////////////////////////
InMemoryReader::InMemoryReader(const QByteArray& data) :
_data(data)
{
}
bool InMemoryReader::initReadHeader()
{
QDataStream in(_data);
in.setVersion(QDataStream::Qt_4_5);
// Basic format header.
HeaderEntity header;
in >> header;
if (header.magic != HeaderEntity::MAGIC)
{
qWarning() << QString("invalid format (magic=%1)").arg(header.magic);
return false;
}
if (header.majorVersion != HeaderEntity::MAJOR_VERSION)
{
qWarning() << QString("format is too new (major=%1; minor=%2)")
.arg(header.majorVersion).arg(header.minorVersion);
return false;
}
// OffsetsHeader.
in >> _offsetsHeader;
return !in.atEnd();
}
bool InMemoryReader::read(qint32 entryType, QByteArray& data)
{
// Find offset for "type".
int index = -1;
for (int i = 0; i < _offsetsHeader.entriesCount; ++i)
{
if (_offsetsHeader.entries.at(i).type == entryType)
{
index = i;
break;
}
}
if (index < 0)
return false;
else if (_offsetsHeader.entries.at(index).offset == 0)
return false;
const OffsetsHeaderEntryEntity& entry = _offsetsHeader.entries.at(index);
QDataStream in(_data);
in.setVersion(QDataStream::Qt_4_5);
in.device()->seek(entry.offset);
char* buff = new char[entry.contentSize];
in.readRawData(buff, entry.contentSize);
data.append(buff, entry.contentSize);
delete[] buff;
return true;
}
bool InMemoryReader::read(SectionIndexData& sid)
{
QByteArray sidData;
if (!read(ET_SectionIndex, sidData) || sidData.isEmpty())
return false;
QDataStream in(sidData);
in.setVersion(QDataStream::Qt_4_5);
in >> sid;
return in.atEnd();
}
///////////////////////////////////////////////////////////////////////////////
ADS_NAMESPACE_SER_END

View File

@@ -1,55 +0,0 @@
TARGET = AdvancedDockingSystemDemo
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
greaterThan(QT_MAJOR_VERSION, 4): DEFINES += ADS_NAMESPACE_ENABLED
windows {
# MinGW
*-g++* {
QMAKE_CXXFLAGS += -std=c++11
}
# MSVC
*-msvc* {
}
}
SOURCES += \
src/main.cpp \
src/mainwindow.cpp \
src/icontitlewidget.cpp \
src/dialogs/SectionContentListModel.cpp \
src/dialogs/SectionContentListWidget.cpp
HEADERS += \
src/mainwindow.h \
src/icontitlewidget.h \
src/dialogs/SectionContentListModel.h \
src/dialogs/SectionContentListWidget.h
FORMS += \
src/mainwindow.ui \
src/dialogs/SectionContentListWidget.ui
# Dependency: AdvancedDockingSystem (staticlib)
#win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../AdvancedDockingSystem/release/ -lAdvancedDockingSystem
#else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../AdvancedDockingSystem/debug/ -lAdvancedDockingSystem
#else:unix: LIBS += -L$$OUT_PWD/../AdvancedDockingSystem/ -lAdvancedDockingSystem
#INCLUDEPATH += $$PWD/../AdvancedDockingSystem/include
#DEPENDPATH += $$PWD/../AdvancedDockingSystem/include
#win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../AdvancedDockingSystem/release/libAdvancedDockingSystem.a
#else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../AdvancedDockingSystem/debug/libAdvancedDockingSystem.a
#else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../AdvancedDockingSystem/release/AdvancedDockingSystem.lib
#else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../AdvancedDockingSystem/debug/AdvancedDockingSystem.lib
#else:unix: PRE_TARGETDEPS += $$OUT_PWD/../AdvancedDockingSystem/libAdvancedDockingSystem.a
# Dependency: AdvancedDockingSystem (shared)
win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../AdvancedDockingSystem/release/ -lAdvancedDockingSystem1
else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../AdvancedDockingSystem/debug/ -lAdvancedDockingSystem1
else:unix: LIBS += -L$$OUT_PWD/../AdvancedDockingSystem/ -lAdvancedDockingSystem1
INCLUDEPATH += $$PWD/../AdvancedDockingSystem/include
DEPENDPATH += $$PWD/../AdvancedDockingSystem/include

View File

@@ -1,96 +0,0 @@
#include "SectionContentListModel.h"
SectionContentListModel::SectionContentListModel(QObject* parent) :
QAbstractTableModel(parent)
{
_headers.insert(UidColumn, "UID");
_headers.insert(UniqueNameColumn, "Unique Name");
_headers.insert(TitleColumn, "Title");
_headers.insert(VisibleColumn, "Visible");
}
SectionContentListModel::~SectionContentListModel()
{
}
void SectionContentListModel::init(ADS_NS::ContainerWidget* cw)
{
#if QT_VERSION >= 0x050000
beginResetModel();
_cw = cw;
_contents = _cw->contents();
endResetModel();
#else
_cw = cw;
_contents = _cw->contents();
reset();
#endif
}
int SectionContentListModel::columnCount(const QModelIndex& parent) const
{
Q_UNUSED(parent)
return _headers.count();
}
QVariant SectionContentListModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
return _headers.value(section);
return QVariant();
}
int SectionContentListModel::rowCount(const QModelIndex& parent) const
{
Q_UNUSED(parent)
return _contents.count();
}
QVariant SectionContentListModel::data(const QModelIndex& index, int role) const
{
if (!index.isValid() || index.row() > rowCount(index) - 1)
return QVariant();
const ADS_NS::SectionContent::RefPtr sc = _contents.at(index.row());
if (sc.isNull())
return QVariant();
switch (role)
{
case Qt::DisplayRole:
{
switch (index.column())
{
case UidColumn:
return sc->uid();
case UniqueNameColumn:
return sc->uniqueName();
case TitleColumn:
return sc->title();
case VisibleColumn:
return _cw->isSectionContentVisible(sc);
}
}
}
return QVariant();
}
bool SectionContentListModel::removeRows(int row, int count, const QModelIndex& parent)
{
if (row > rowCount(parent) - 1)
return false;
const int first = row;
const int last = row + count - 1;
beginRemoveRows(parent, first, last);
for (int i = last; i >= first; --i)
{
const ADS_NS::SectionContent::RefPtr sc = _contents.at(i);
_cw->removeSectionContent(sc);
_contents.removeAt(i);
}
endRemoveRows();
return true;
}

View File

@@ -1,48 +0,0 @@
#ifndef ADS_SECTIONCONTENTMODEL_H
#define ADS_SECTIONCONTENTMODEL_H
#include <QHash>
#include <QList>
#include <QString>
#include <QAbstractTableModel>
#include "ads/API.h"
#include "ads/ContainerWidget.h"
#include "ads/SectionContent.h"
ADS_NAMESPACE_BEGIN
class ContainerWidget;
ADS_NAMESPACE_END
class SectionContentListModel : public QAbstractTableModel
{
Q_OBJECT
public:
enum Column
{
UidColumn,
UniqueNameColumn,
TitleColumn,
VisibleColumn
};
SectionContentListModel(QObject* parent);
virtual ~SectionContentListModel();
void init(ADS_NS::ContainerWidget* cw);
virtual int columnCount(const QModelIndex &parent) const;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
virtual int rowCount(const QModelIndex &parent) const;
virtual QVariant data(const QModelIndex &index, int role) const;
virtual bool removeRows(int row, int count, const QModelIndex &parent);
private:
QHash<int, QString> _headers;
ADS_NS::ContainerWidget* _cw;
QList<ADS_NS::SectionContent::RefPtr> _contents;
};
#endif

View File

@@ -1,38 +0,0 @@
#include "SectionContentListWidget.h"
#include "SectionContentListModel.h"
SectionContentListWidget::SectionContentListWidget(QWidget* parent) :
QDialog(parent)
{
_ui.setupUi(this);
connect(_ui.deleteButton, SIGNAL(clicked(bool)), this, SLOT(onDeleteButtonClicked()));
}
void SectionContentListWidget::setValues(const SectionContentListWidget::Values& v)
{
_v = v;
// Reset
QAbstractItemModel* m = _ui.tableView->model();
if (m)
{
_ui.tableView->setModel(NULL);
delete m;
m = NULL;
}
// Fill.
SectionContentListModel* sclm = new SectionContentListModel(this);
sclm->init(_v.cw);
_ui.tableView->setModel(sclm);
}
void SectionContentListWidget::onDeleteButtonClicked()
{
const QModelIndex mi = _ui.tableView->currentIndex();
if (!mi.isValid())
return;
_ui.tableView->model()->removeRows(mi.row(), 1, mi.parent());
}

View File

@@ -1,33 +0,0 @@
#ifndef SECTIONCONTENTLISTWIDGET
#define SECTIONCONTENTLISTWIDGET
#include <QDialog>
#include "ui_SectionContentListWidget.h"
#include "ads/API.h"
#include "ads/ContainerWidget.h"
#include "ads/SectionContent.h"
class SectionContentListWidget : public QDialog
{
Q_OBJECT
public:
class Values
{
public:
ADS_NS::ContainerWidget* cw;
};
SectionContentListWidget(QWidget* parent);
void setValues(const Values& v);
private slots:
void onDeleteButtonClicked();
private:
Ui::SectionContentListWidgetForm _ui;
Values _v;
};
#endif

View File

@@ -1,61 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SectionContentListWidgetForm</class>
<widget class="QWidget" name="SectionContentListWidgetForm">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>522</width>
<height>258</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QTableView" name="tableView">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="deleteButton">
<property name="text">
<string>Delete</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -1,65 +0,0 @@
#include "icontitlewidget.h"
#include <QIcon>
#include <QString>
#include <QBoxLayout>
#include <QLabel>
#include <QStyle>
IconTitleWidget::IconTitleWidget(const QIcon& icon, const QString& title, QWidget *parent) :
QFrame(parent)
{
QBoxLayout* l = new QBoxLayout(QBoxLayout::LeftToRight);
l->setContentsMargins(0, 0, 0, 0);
setLayout(l);
_iconLabel = new QLabel();
l->addWidget(_iconLabel);
_titleLabel = new QLabel();
l->addWidget(_titleLabel, 1);
setIcon(icon);
setTitle(title);
}
void IconTitleWidget::setIcon(const QIcon& icon)
{
if (icon.isNull())
{
_iconLabel->setPixmap(QPixmap());
_iconLabel->setVisible(false);
}
else
{
_iconLabel->setPixmap(icon.pixmap(16, 16));
_iconLabel->setVisible(true);
}
}
void IconTitleWidget::setTitle(const QString& title)
{
if (title.isEmpty())
{
_titleLabel->setText(QString());
_titleLabel->setVisible(false);
}
else
{
_titleLabel->setText(title);
_titleLabel->setVisible(true);
}
}
void IconTitleWidget::polishUpdate()
{
QList<QWidget*> widgets;
widgets.append(_iconLabel);
widgets.append(_titleLabel);
foreach (QWidget* w, widgets)
{
w->style()->unpolish(w);
w->style()->polish(w);
w->update();
}
}

View File

@@ -1,26 +0,0 @@
#ifndef ICONTITLEWIDGET_H
#define ICONTITLEWIDGET_H
#include <QFrame>
class QIcon;
class QString;
class QLabel;
class IconTitleWidget : public QFrame
{
Q_OBJECT
public:
explicit IconTitleWidget(const QIcon& icon, const QString& title, QWidget *parent = 0);
public slots:
void setIcon(const QIcon& icon);
void setTitle(const QString& title);
void polishUpdate();
public:
QLabel* _iconLabel;
QLabel* _titleLabel;
};
#endif // ICONTITLEWIDGET_H

View File

@@ -1,28 +0,0 @@
#include <QString>
#include <QFile>
#include <QApplication>
#include "mainwindow.h"
static void initStyleSheet(QApplication& a)
{
//Q_INIT_RESOURCE(ads); // If static linked.
QFile f(":ads/stylesheets/default-windows.css");
if (f.open(QFile::ReadOnly))
{
const QByteArray ba = f.readAll();
f.close();
a.setStyleSheet(QString(ba));
}
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setQuitOnLastWindowClosed(true);
initStyleSheet(a);
MainWindow mw;
mw.show();
return a.exec();
}

View File

@@ -1,209 +0,0 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QTime>
#include <QLabel>
#include <QTextEdit>
#include <QCalendarWidget>
#include <QFrame>
#include <QTreeView>
#include <QFileSystemModel>
#include <QBoxLayout>
#include "ads/SectionWidget.h"
#include "ads/DropOverlay.h"
#include "dialogs/SectionContentListWidget.h"
#include "icontitlewidget.h"
///////////////////////////////////////////////////////////////////////
static int CONTENT_COUNT = 0;
static ADS_NS::SectionContent::RefPtr createLongTextLabelSC(ADS_NS::ContainerWidget* container)
{
QWidget* w = new QWidget();
QBoxLayout* bl = new QBoxLayout(QBoxLayout::TopToBottom);
w->setLayout(bl);
QLabel* l = new QLabel();
l->setWordWrap(true);
l->setAlignment(Qt::AlignTop | Qt::AlignLeft);
l->setText(QString("Lorem Ipsum ist ein einfacher Demo-Text für die Print- und Schriftindustrie. Lorem Ipsum ist in der Industrie bereits der Standard Demo-Text seit 1500, als ein unbekannter Schriftsteller eine Hand voll Wörter nahm und diese durcheinander warf um ein Musterbuch zu erstellen. Es hat nicht nur 5 Jahrhunderte überlebt, sondern auch in Spruch in die elektronische Schriftbearbeitung geschafft (bemerke, nahezu unverändert). Bekannt wurde es 1960, mit dem erscheinen von Letrase, welches Passagen von Lorem Ipsum enhielt, so wie Desktop Software wie Aldus PageMaker - ebenfalls mit Lorem Ipsum."));
bl->addWidget(l);
const int index = ++CONTENT_COUNT;
ADS_NS::SectionContent::RefPtr sc = ADS_NS::SectionContent::newSectionContent(QString("uname-%1").arg(index), container, new IconTitleWidget(QIcon(), QString("Label %1").arg(index)), w);
sc->setTitle("Ein Label " + QString::number(index));
return sc;
}
static ADS_NS::SectionContent::RefPtr createCalendarSC(ADS_NS::ContainerWidget* container)
{
QCalendarWidget* w = new QCalendarWidget();
const int index = ++CONTENT_COUNT;
return ADS_NS::SectionContent::newSectionContent(QString("uname-%1").arg(index), container, new IconTitleWidget(QIcon(), QString("Calendar %1").arg(index)), w);
}
static ADS_NS::SectionContent::RefPtr createFileSystemTreeSC(ADS_NS::ContainerWidget* container)
{
QTreeView* w = new QTreeView();
w->setFrameShape(QFrame::NoFrame);
// QFileSystemModel* m = new QFileSystemModel(w);
// m->setRootPath(QDir::currentPath());
// w->setModel(m);
const int index = ++CONTENT_COUNT;
return ADS_NS::SectionContent::newSectionContent(QString("uname-%1").arg(index), container, new IconTitleWidget(QIcon(), QString("Filesystem %1").arg(index)), w);
}
static void storeDataHelper(const QString& fname, const QByteArray& ba)
{
QFile f(fname + QString(".dat"));
if (f.open(QFile::WriteOnly))
{
f.write(ba);
f.close();
}
}
static QByteArray loadDataHelper(const QString& fname)
{
QFile f(fname + QString(".dat"));
if (f.open(QFile::ReadOnly))
{
QByteArray ba = f.readAll();
f.close();
return ba;
}
return QByteArray();
}
///////////////////////////////////////////////////////////////////////
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
// Setup actions.
QObject::connect(ui->actionContentList, SIGNAL(triggered()), this, SLOT(showSectionContentListDialog()));
// ADS - Create main container (ContainerWidget).
_container = new ADS_NS::ContainerWidget();
#if QT_VERSION >= 0x050000
QObject::connect(_container, &ADS_NS::ContainerWidget::activeTabChanged, this, &MainWindow::onActiveTabChanged);
QObject::connect(_container, &ADS_NS::ContainerWidget::sectionContentVisibilityChanged, this, &MainWindow::onSectionContentVisibilityChanged);
#else
QObject::connect(_container, SIGNAL(activeTabChanged(const SectionContent::RefPtr&, bool)), this, SLOT(onActiveTabChanged(const SectionContent::RefPtr&, bool)));
QObject::connect(_container, SIGNAL(sectionContentVisibilityChanged(SectionContent::RefPtr,bool)), this, SLOT(onSectionContentVisibilityChanged(SectionContent::RefPtr,bool)));
#endif
setCentralWidget(_container);
// Optional: Use custom drop area widgets.
if (false)
{
QHash<ADS_NS::DropArea, QWidget*> areaWidgets;
areaWidgets.insert(ADS_NS::TopDropArea, new QPushButton("TOP"));
areaWidgets.insert(ADS_NS::RightDropArea, new QPushButton("RIGHT"));
areaWidgets.insert(ADS_NS::BottomDropArea, new QPushButton("BOTTOM"));
areaWidgets.insert(ADS_NS::LeftDropArea, new QPushButton("LEFT"));
areaWidgets.insert(ADS_NS::CenterDropArea, new QPushButton("CENTER"));
_container->dropOverlay()->setAreaWidgets(areaWidgets);
}
// ADS - Adding some contents.
if (true)
{
// Test #1: Use high-level public API
ADS_NS::ContainerWidget* cw = _container;
ADS_NS::SectionWidget* sw = NULL;
sw = _container->addSectionContent(createLongTextLabelSC(cw), sw, ADS_NS::CenterDropArea);
sw = _container->addSectionContent(createCalendarSC(cw), sw, ADS_NS::RightDropArea);
sw = _container->addSectionContent(createFileSystemTreeSC(cw), sw, ADS_NS::CenterDropArea);
_container->addSectionContent(createCalendarSC(_container));
_container->addSectionContent(createLongTextLabelSC(_container));
_container->addSectionContent(createLongTextLabelSC(_container));
_container->addSectionContent(createLongTextLabelSC(_container));
ADS_NS::SectionContent::RefPtr sc = createLongTextLabelSC(cw);
sc->setFlags(ADS_NS::SectionContent::AllFlags ^ ADS_NS::SectionContent::Closeable);
_container->addSectionContent(sc);
}
else if (false)
{
// Issue #2: If the first drop is not into CenterDropArea, the application crashes.
ADS_NS::ContainerWidget* cw = _container;
ADS_NS::SectionWidget* sw = NULL;
sw = _container->addSectionContent(createLongTextLabelSC(cw), sw, ADS_NS::LeftDropArea);
sw = _container->addSectionContent(createCalendarSC(cw), sw, ADS_NS::LeftDropArea);
sw = _container->addSectionContent(createLongTextLabelSC(cw), sw, ADS_NS::CenterDropArea);
sw = _container->addSectionContent(createLongTextLabelSC(cw), sw, ADS_NS::CenterDropArea);
sw = _container->addSectionContent(createLongTextLabelSC(cw), sw, ADS_NS::CenterDropArea);
sw = _container->addSectionContent(createLongTextLabelSC(cw), sw, ADS_NS::RightDropArea);
sw = _container->addSectionContent(createLongTextLabelSC(cw), sw, ADS_NS::BottomDropArea);
}
// Default window geometry
resize(800, 600);
restoreGeometry(loadDataHelper("MainWindow"));
// ADS - Restore geometries and states of contents.
_container->restoreState(loadDataHelper("ContainerWidget"));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::showSectionContentListDialog()
{
SectionContentListWidget::Values v;
v.cw = _container;
SectionContentListWidget w(this);
w.setValues(v);
w.exec();
}
void MainWindow::onActiveTabChanged(const ADS_NS::SectionContent::RefPtr& sc, bool active)
{
Q_UNUSED(active);
IconTitleWidget* itw = dynamic_cast<IconTitleWidget*>(sc->titleWidget());
if (itw)
{
itw->polishUpdate();
}
}
void MainWindow::onSectionContentVisibilityChanged(const ADS_NS::SectionContent::RefPtr& sc, bool visible)
{
qDebug() << Q_FUNC_INFO << sc->uniqueName() << visible;
}
void MainWindow::onActionAddSectionContentTriggered()
{
return;
}
void MainWindow::contextMenuEvent(QContextMenuEvent* e)
{
Q_UNUSED(e);
QMenu* m = _container->createContextMenu();
m->exec(QCursor::pos());
delete m;
}
void MainWindow::closeEvent(QCloseEvent* e)
{
Q_UNUSED(e);
storeDataHelper("MainWindow", saveGeometry());
storeDataHelper("ContainerWidget", _container->saveState());
}

View File

@@ -1,43 +0,0 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "ads/API.h"
#include "ads/ContainerWidget.h"
#include "ads/SectionContent.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
virtual ~MainWindow();
public slots:
void showSectionContentListDialog();
private slots:
#if QT_VERSION >= 0x050000
void onActiveTabChanged(const ADS_NS::SectionContent::RefPtr& sc, bool active);
void onSectionContentVisibilityChanged(const ADS_NS::SectionContent::RefPtr& sc, bool visible);
#else
void onActiveTabChanged(const SectionContent::RefPtr& sc, bool active);
void onSectionContentVisibilityChanged(const SectionContent::RefPtr& sc, bool visible);
#endif
void onActionAddSectionContentTriggered();
protected:
virtual void contextMenuEvent(QContextMenuEvent* e);
virtual void closeEvent(QCloseEvent* e);
private:
Ui::MainWindow *ui;
ADS_NS::ContainerWidget* _container;
};
#endif // MAINWINDOW_H

View File

@@ -1,7 +0,0 @@
HEADERS += \
$$PWD/src/TestCore.h
SOURCES += \
$$PWD/src/main.cpp \
$$PWD/src/TestCore.cpp

View File

@@ -1,14 +0,0 @@
TARGET = AdvancedDockingSystemUnitTests
QT += core gui testlib
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
greaterThan(QT_MAJOR_VERSION, 4): DEFINES += ADS_NAMESPACE_ENABLED
DEFINES += ADS_IMPORT
INCLUDEPATH += $$PWD/src
INCLUDEPATH += $$PWD/../AdvancedDockingSystem/include
DEPENDPATH += $$PWD/../AdvancedDockingSystem/include
include(AdvancedDockingSystemUnitTests.pri)
include(../AdvancedDockingSystem/AdvancedDockingSystem.pri)

View File

@@ -1,68 +0,0 @@
#include "TestCore.h"
#include "ads/API.h"
#include "ads/Serialization.h"
void TestCore::serialization()
{
QList<QByteArray> datas;
datas.append(QByteArray("Custom Data Here!!!"));
datas.append(QByteArray("Even More..."));
datas.append(QByteArray("lalalaalalalalalalal").toBase64());
// WRITE some data.
ADS_NS_SER::InMemoryWriter writer;
for (int i = 0; i < datas.count(); ++i)
{
QVERIFY(writer.write(ADS_NS_SER::ET_Custom + i, datas.at(i)));
}
// Type: SectionIndexData
ADS_NS_SER::SectionIndexData sid;
for (int i = 0; i < 1; ++i)
{
ADS_NS_SER::SectionEntity se;
se.x = i;
se.y = i;
se.width = 100 + i;
se.height = 100 + i;
se.currentIndex = i;
for (int j = 0; j < 1; ++j)
{
ADS_NS_SER::SectionContentEntity sce;
sce.uniqueName = QString("uname-%1-%2").arg(i).arg(j);
sce.preferredIndex = 8;
sce.visible = true;
se.sectionContents.append(sce);
se.sectionContentsCount += 1;
}
sid.sections.append(se);
sid.sectionsCount += 1;
}
QVERIFY(writer.write(sid));
QVERIFY(writer.offsetsCount() == datas.count() + 1);
const QByteArray writtenData = writer.toByteArray();
QVERIFY(writtenData.size() > 0);
// READ and validate written data.
ADS_NS_SER::InMemoryReader reader(writtenData);
QVERIFY(reader.initReadHeader());
QVERIFY(reader.offsetsCount() == datas.count() + 1);
for (int i = 0; i < datas.count(); ++i)
{
QByteArray readData;
QVERIFY(reader.read(ADS_NS_SER::ET_Custom + i, readData));
QVERIFY(readData == datas.at(i));
}
// Type: SectionIndexData
ADS_NS_SER::SectionIndexData sidRead;
QVERIFY(reader.read(sidRead));
// TODO compare sidRead with sid
}
QTEST_MAIN(TestCore)

View File

@@ -1,14 +0,0 @@
#ifndef TEST_CORE_H
#define TEST_CORE_H
#include <QtTest/QtTest>
class TestCore : public QObject
{
Q_OBJECT
private slots:
void serialization();
};
#endif

View File

@@ -1 +0,0 @@
#include <QtTest/QtTest>

BIN
ContainerWidget.dat Normal file

Binary file not shown.

15
LICENSE.md Normal file
View File

@@ -0,0 +1,15 @@
Qt Advanced Docking System
Copyright (C) 2017 Uwe Kindler
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; If not, see <http://www.gnu.org/licenses/>.

BIN
MainWindow.dat Normal file

Binary file not shown.

119
README.md
View File

@@ -1,94 +1,53 @@
# Advanced Docking System for Qt
[![Gitter](https://badges.gitter.im/mfreiholz/Qt-Advanced-Docking-System.svg)](https://gitter.im/mfreiholz/Qt-Advanced-Docking-System?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
Manages content widgets more like Visual Studio or similar programs.
I also try to get everything done with basic Qt functionality.
Basic usage of QWidgets an QLayouts and using basic styles as much as possible.
Qt Advanced Docking System lets you create customizable layouts using a full
featured window docking system similar to what is found in many popular
integrated development environements (IDEs) such as Visual Studio.
Everything is implemented with standard Qt functionality without any
platform specific code. Basic usage of QWidgets an QLayouts and using basic
styles as much as possible.
This work is based on and inspired by the
[Advanced Docking System for Qt](https://github.com/mfreiholz/Qt-Advanced-Docking-System)
from Manuel Freiholz. I did an almost complete rewrite of his code to improve
code quality, readibility and to fix all issues from the issue tracker
of his docking system project.
## Features
### Docking everywhere - no central widget
There is no central widget like in the Qt docking system. You can dock on every
border of the main window or you can dock into each dock area - so you are
free to dock almost everywhere.
![Layout of widgets](preview.png)
![Layout of widgets](preview.png)
![Dropping widgets](preview-dragndrop.png)
### Docking inside floating windows
There is no difference between the main window and a floating window. Docking
into floating windows is supported.
![Docking inside floating windows](floating-widget-dragndrop.png)
### Grouped dragging
When dragging the titlebar of a dock, all the tabs that are tabbed with it are
going to be dragged. So you can move complete groups of tabbed widgets into
a floating widget or from one dock area to another one.
![Grouped dragging](grouped-dragging.png)
## Tested Compatible Environments
- Windows 7 / 8 / 8.1 / 10
- Ubuntu 15.10
- Windows 10
## Build
Open the `build.pro` with QtCreator and start the build, that's it.
Open the `ads.pro` with QtCreator and start the build, that's it.
You can run the demo project and test it yourself.
## Release & Development
The `master` branch is not guaranteed to be stable or does not even build, since it is the main working branch.
If you want a version that builds, you should always use a release/beta tag.
## Getting started / Example
The following example shows the minimum code required to use ADS.
_MyWindow.h_
```cpp
#include <QMainWindow>
#include "ads/API.h"
#include "ads/ContainerWidget.h"
#include "ads/SectionContent.h"
class MyWindow : public QMainWindow
{
Q_OBJECT
public:
MyWindow(QWidget* parent);
private:
// The main container for dockings.
ADS_NS::ContainerWidget* _container;
// You always want to keep a reference of your content,
// in case you need to perform any action on it (show, hide, ...)
ADS_NS::SectionContent::RefPtr _sc1;
};
```
_MyWindow.cpp_
```cpp
#include "MyWindow.h"
#include <QLabel>
MyWindow::MyWindow(QWidget* parent) : QMainWindow(parent)
{
_container = new ADS_NS::ContainerWidget();
setCentralWidget(_container);
_sc1 = ADS_NS::SectionContent::newSectionContent(QString("Unique-Internal-Name"), _container, new QLabel("Visible Title"), new QLabel("Content Widget"));
_container->addSectionContent(_sc1, NULL, ADS_NS::CenterDropArea);
}
static void initStyleSheet(QApplication& a)
{
//Q_INIT_RESOURCE(ads); // If static linked.
QFile f(":ads/stylesheets/default-windows.css");
if (f.open(QFile::ReadOnly))
{
const QByteArray ba = f.readAll();
f.close();
a.setStyleSheet(QString(ba));
}
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setQuitOnLastWindowClosed(true);
initStyleSheet(a);
MainWindow mw;
mw.show();
return a.exec();
}
```
## Developers
[Manuel Freiholz](https://mfreiholz.de), Project Maintainer
- Uwe Kindler, Project Maintainer
- Manuel Freiholz
## License information
![WTFPL](license.png)
This project uses the [LGPLv2.1 license](gnu-lgpl-v2.1.md)
This projects uses the [WTFPL license](http://www.wtfpl.net/)
(Do **W**hat **T**he **F**uck You Want To **P**ublic **L**icense)
Using it? Let us know by creating a [new issue](https://github.com/mfreiholz/qt-docks/issues/new) (You don't have to, of course).

5
ads.pro Normal file
View File

@@ -0,0 +1,5 @@
TEMPLATE = subdirs
SUBDIRS = \
src \
demo

View File

@@ -1,6 +0,0 @@
TEMPLATE = subdirs
SUBDIRS = \
AdvancedDockingSystem \
AdvancedDockingSystemDemo \
AdvancedDockingSystemUnitTests

37
demo/demo.pro Normal file
View File

@@ -0,0 +1,37 @@
ADS_ROOT = $${PWD}/..
ADS_OUT_ROOT = $${OUT_PWD}/..
TARGET = AdvancedDockingSystemDemo
DESTDIR = $${ADS_OUT_ROOT}/lib
QT += core gui widgets
windows {
# MinGW
*-g++* {
QMAKE_CXXFLAGS += -std=c++11
}
# MSVC
*-msvc* {
}
}
SOURCES += \
main.cpp \
mainwindow.cpp
HEADERS += \
mainwindow.h
FORMS += \
mainwindow.ui
LIBS += -L$${ADS_OUT_ROOT}/lib
# Dependency: AdvancedDockingSystem (shared)
win32:CONFIG(release, debug|release): LIBS += -lAdvancedDockingSystem
else:win32:CONFIG(debug, debug|release): LIBS += -lAdvancedDockingSystemd
else:unix: LIBS += -lAdvancedDockingSystem
INCLUDEPATH += ../src
DEPENDPATH += ../src

47
demo/main.cpp Normal file
View File

@@ -0,0 +1,47 @@
#include <QString>
#include <QFile>
#include <QApplication>
#include <QDebug>
#include <memory>
#include "mainwindow.h"
void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
QByteArray localMsg = msg.toLocal8Bit();
switch (type) {
case QtDebugMsg:
fprintf(stdout, "Debug: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
break;
case QtInfoMsg:
fprintf(stdout, "Info: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
break;
case QtWarningMsg:
fprintf(stderr, "Warning: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
break;
case QtCriticalMsg:
fprintf(stderr, "Critical: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
break;
case QtFatalMsg:
fprintf(stderr, "Fatal: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
abort();
}
fflush(stderr);
fflush(stdout);
}
int main(int argc, char *argv[])
{
std::shared_ptr<int> b;
QApplication a(argc, argv);
a.setQuitOnLastWindowClosed(true);
qInstallMessageHandler(myMessageOutput);
qDebug() << "Message handler test";
MainWindow mw;
mw.show();
return a.exec();
}

148
demo/mainwindow.cpp Normal file
View File

@@ -0,0 +1,148 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QTime>
#include <QLabel>
#include <QTextEdit>
#include <QCalendarWidget>
#include <QFrame>
#include <QTreeView>
#include <QFileSystemModel>
#include <QBoxLayout>
#include <QSettings>
#include <QDockWidget>
#include <QDebug>
#include "DockManager.h"
#include "DockWidget.h"
#include "DockAreaWidget.h"
static ads::CDockWidget* createLongTextLabelDockWidget(QMenu* ViewMenu)
{
static int LabelCount = 0;
QLabel* l = new QLabel();
l->setWordWrap(true);
l->setAlignment(Qt::AlignTop | Qt::AlignLeft);
l->setText(QString("Label %1 %2 - 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.")
.arg(LabelCount)
.arg(QTime::currentTime().toString("hh:mm:ss:zzz")));
ads::CDockWidget* DockWidget = new ads::CDockWidget(QString("Label %1").arg(LabelCount++));
DockWidget->setWidget(l);
DockWidget->setObjectName(DockWidget->windowTitle());
ViewMenu->addAction(DockWidget->toggleViewAction());
return DockWidget;
}
static ads::CDockWidget* createCalendarDockWidget(QMenu* ViewMenu)
{
static int CalendarCount = 0;
QCalendarWidget* w = new QCalendarWidget();
ads::CDockWidget* DockWidget = new ads::CDockWidget(QString("Calendar %1").arg(CalendarCount++));
DockWidget->setWidget(w);
DockWidget->setObjectName(DockWidget->windowTitle());
ViewMenu->addAction(DockWidget->toggleViewAction());
return DockWidget;
}
static ads::CDockWidget* createFileSystemTreeDockWidget(QMenu* ViewMenu)
{
static int FileSystemCount = 0;
QTreeView* w = new QTreeView();
w->setFrameShape(QFrame::NoFrame);
QFileSystemModel* m = new QFileSystemModel(w);
m->setRootPath(QDir::currentPath());
w->setModel(m);
ads::CDockWidget* DockWidget = new ads::CDockWidget(QString("Filesystem %1").arg(FileSystemCount++));
DockWidget->setWidget(w);
DockWidget->setObjectName(DockWidget->windowTitle());
ViewMenu->addAction(DockWidget->toggleViewAction());
return DockWidget;
}
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
m_DockManager = new ads::CDockManager(this);
createContent();
// Default window geometry
resize(800, 600);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::createContent()
{
// Test container docking
QMenu* ViewMenu = this->ui->menuView;
auto DockWidget = createCalendarDockWidget(ViewMenu);
DockWidget->setFeatures(DockWidget->features().setFlag(ads::CDockWidget::DockWidgetClosable, false));
m_DockManager->addDockWidget(ads::LeftDockWidgetArea, DockWidget);
m_DockManager->addDockWidget(ads::LeftDockWidgetArea, createLongTextLabelDockWidget(ViewMenu));
m_DockManager->addDockWidget(ads::BottomDockWidgetArea, createFileSystemTreeDockWidget(ViewMenu));
auto TopDockArea = m_DockManager->addDockWidget(ads::TopDockWidgetArea, createFileSystemTreeDockWidget(ViewMenu));
DockWidget = createCalendarDockWidget(ViewMenu);
DockWidget->setFeatures(DockWidget->features().setFlag(ads::CDockWidget::DockWidgetClosable, false));
m_DockManager->addDockWidget(ads::CenterDockWidgetArea, DockWidget, TopDockArea);
// Test dock area docking
auto RighDockArea = m_DockManager->addDockWidget(ads::RightDockWidgetArea, createLongTextLabelDockWidget(ViewMenu), TopDockArea);
m_DockManager->addDockWidget(ads::TopDockWidgetArea, createLongTextLabelDockWidget(ViewMenu), RighDockArea);
auto BottomDockArea = m_DockManager->addDockWidget(ads::BottomDockWidgetArea, createLongTextLabelDockWidget(ViewMenu), RighDockArea);
m_DockManager->addDockWidget(ads::RightDockWidgetArea, createLongTextLabelDockWidget(ViewMenu), RighDockArea);
m_DockManager->addDockWidget(ads::CenterDockWidgetArea, createLongTextLabelDockWidget(ViewMenu), BottomDockArea);
}
void MainWindow::closeEvent(QCloseEvent* event)
{
/*QSettings Settings("Settings.ini", QSettings::IniFormat);
Settings.setValue("mainWindow/Geometry", saveGeometry());
Settings.setValue("mainWindow/DockingState", m_DockManager->saveState());*/
QMainWindow::closeEvent(event);
}
void MainWindow::on_actionSaveState_triggered(bool)
{
qDebug() << "MainWindow::on_actionSaveState_triggered";
QSettings Settings("Settings.ini", QSettings::IniFormat);
Settings.setValue("mainWindow/Geometry", saveGeometry());
Settings.setValue("mainWindow/DockingState", m_DockManager->saveState());
}
void MainWindow::on_actionRestoreState_triggered(bool)
{
qDebug() << "MainWindow::on_actionRestoreState_triggered";
QSettings Settings("Settings.ini", QSettings::IniFormat);
restoreGeometry(Settings.value("mainWindow/Geometry").toByteArray());
m_DockManager->restoreState(Settings.value("mainWindow/DockingState").toByteArray());
}

30
demo/mainwindow.h Normal file
View File

@@ -0,0 +1,30 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "DockManager.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
protected:
virtual void closeEvent(QCloseEvent* event) override;
public:
explicit MainWindow(QWidget *parent = 0);
virtual ~MainWindow();
private:
Ui::MainWindow *ui;
ads::CDockManager* m_DockManager;
void createContent();
private slots:
void on_actionSaveState_triggered(bool);
void on_actionRestoreState_triggered(bool);
};
#endif // MAINWINDOW_H

View File

@@ -1,79 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralWidget"/>
<widget class="QStatusBar" name="statusBar"/>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>21</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
<addaction name="actionExit"/>
</widget>
<widget class="QMenu" name="menuView">
<property name="title">
<string>View</string>
</property>
<addaction name="actionContentList"/>
<addaction name="actionDemo_2"/>
<addaction name="actionDemo_3"/>
</widget>
<widget class="QMenu" name="menuAbout">
<property name="title">
<string>About</string>
</property>
</widget>
<addaction name="menuFile"/>
<addaction name="menuView"/>
<addaction name="menuAbout"/>
</widget>
<action name="actionAddSectionContent">
<property name="text">
<string>Add SectionContent</string>
</property>
</action>
<action name="actionContentList">
<property name="text">
<string>Contents...</string>
</property>
</action>
<action name="actionDemo_2">
<property name="text">
<string>Demo 2</string>
</property>
</action>
<action name="actionDemo_3">
<property name="text">
<string>Demo 3</string>
</property>
</action>
<action name="actionExit">
<property name="text">
<string>Exit</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralWidget"/>
<widget class="QStatusBar" name="statusBar"/>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>21</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
<addaction name="actionExit"/>
<addaction name="actionSaveState"/>
<addaction name="actionRestoreState"/>
</widget>
<widget class="QMenu" name="menuView">
<property name="title">
<string>View</string>
</property>
</widget>
<widget class="QMenu" name="menuAbout">
<property name="title">
<string>About</string>
</property>
</widget>
<addaction name="menuFile"/>
<addaction name="menuView"/>
<addaction name="menuAbout"/>
</widget>
<action name="actionExit">
<property name="text">
<string>Exit</string>
</property>
</action>
<action name="actionSaveState">
<property name="text">
<string>Save State</string>
</property>
</action>
<action name="actionRestoreState">
<property name="text">
<string>Restore State</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

494
gnu-lgpl-v2.1.md Normal file
View File

@@ -0,0 +1,494 @@
GNU Lesser General Public License
=================================
_Version 2.1, February 1999_
_Copyright © 1991, 1999 Free Software Foundation, Inc._
_51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA_
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
_This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1._
### Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: **(1)** we copyright the
library, and **(2)** we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the “Lesser” General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
“work based on the library” and a “work that uses the library”. The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
### TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
**0.** This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called “this License”).
Each licensee is addressed as “you”.
A “library” means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The “Library”, below, refers to any such software library or work
which has been distributed under these terms. A “work based on the
Library” means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term “modification”.)
“Source code” for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
**1.** You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
**2.** You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
* **a)** The modified work must itself be a software library.
* **b)** You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
* **c)** You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
* **d)** If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
**3.** You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
**4.** You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
**5.** A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a “work that uses the Library”. Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a “work that uses the Library” with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a “work that uses the
library”. The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a “work that uses the Library” uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
**6.** As an exception to the Sections above, you may also combine or
link a “work that uses the Library” with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
* **a)** Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable “work that
uses the Library”, as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
* **b)** Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
* **c)** Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
* **d)** If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
* **e)** Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the “work that uses the
Library” must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
**7.** You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
* **a)** Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
* **b)** Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
**8.** You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
**9.** You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
**10.** Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
**11.** If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
**12.** If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
**13.** The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
“any later version”, you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
**14.** If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
### NO WARRANTY
**15.** BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY “AS IS” WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
**16.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
_END OF TERMS AND CONDITIONS_
### How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
“copyright” line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a “copyright disclaimer” for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

BIN
grouped-dragging.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 191 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 57 KiB

723
src/DockAreaWidget.cpp Normal file
View File

@@ -0,0 +1,723 @@
/*******************************************************************************
** Qt Advanced Docking System
** Copyright (C) 2017 Uwe Kindler
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation; either
** version 2.1 of the License, or (at your option) any later version.
**
** This library is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public
** License along with this library; If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
//============================================================================
/// \file DockAreaWidget.cpp
/// \author Uwe Kindler
/// \date 24.02.2017
/// \brief Implementation of CDockAreaWidget class
//============================================================================
//============================================================================
// INCLUDES
//============================================================================
#include "DockAreaWidget.h"
#include <QStackedLayout>
#include <QScrollBar>
#include <QScrollArea>
#include <QWheelEvent>
#include <QStyle>
#include <QPushButton>
#include <QDebug>
#include <QMenu>
#include <QSplitter>
#include "DockContainerWidget.h"
#include "DockWidget.h"
#include "DockWidgetTitleBar.h"
#include "FloatingDockContainer.h"
#include "DockManager.h"
#include "DockOverlay.h"
namespace ads
{
static const char* const INDEX_PROPERTY = "index";
static const char* const ACTION_PROPERTY = "action";
static const int APPEND = -1;
/**
* Custom scroll bar implementation for dock area tab bar
* This scroll area enables floating of a whole dock area including all
* dock widgets
*/
class CTabsScrollArea : public QScrollArea
{
private:
QPoint m_DragStartMousePos;
CDockAreaWidget* m_DockArea;
CFloatingDockContainer* m_FloatingWidget = nullptr;
public:
CTabsScrollArea(CDockAreaWidget* parent)
: QScrollArea(parent),
m_DockArea(parent)
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Ignored);
setFrameStyle(QFrame::NoFrame);
setWidgetResizable(true);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
}
protected:
virtual void wheelEvent(QWheelEvent* Event) override
{
Event->accept();
const int direction = Event->angleDelta().y();
if (direction < 0)
{
horizontalScrollBar()->setValue(horizontalScrollBar()->value() + 20);
}
else
{
horizontalScrollBar()->setValue(horizontalScrollBar()->value() - 20);
}
}
/**
* Stores mouse position to detect dragging
*/
virtual void mousePressEvent(QMouseEvent* ev) override
{
if (ev->button() == Qt::LeftButton)
{
ev->accept();
m_DragStartMousePos = ev->pos();
return;
}
QScrollArea::mousePressEvent(ev);
}
/**
* Stores mouse position to detect dragging
*/
virtual void mouseReleaseEvent(QMouseEvent* ev) override
{
if (ev->button() == Qt::LeftButton)
{
qDebug() << "CTabsScrollArea::mouseReleaseEvent";
ev->accept();
m_FloatingWidget = nullptr;
m_DragStartMousePos = QPoint();
return;
}
QScrollArea::mouseReleaseEvent(ev);
}
/**
* Starts floating the complete docking area including all dock widgets,
* if it is not the last dock area in a floating widget
*/
virtual void mouseMoveEvent(QMouseEvent* ev) override
{
QScrollArea::mouseMoveEvent(ev);
if (ev->buttons() != Qt::LeftButton)
{
return;
}
if (m_FloatingWidget)
{
m_FloatingWidget->moveFloating();
return;
}
// If this is the last dock area in a dock container it does not make
// sense to move it to a new floating widget and leave this one
// empty
if (m_DockArea->dockContainer()->isFloating()
&& m_DockArea->dockContainer()->visibleDockAreaCount() == 1)
{
return;
}
if (!this->geometry().contains(ev->pos()))
{
qDebug() << "CTabsScrollArea::startFloating";
startFloating(m_DragStartMousePos);
auto Overlay = m_DockArea->dockManager()->containerOverlay();
Overlay->setAllowedAreas(OuterDockAreas);
}
return;
}
/**
* Double clicking the title bar also starts floating of the complete area
*/
virtual void mouseDoubleClickEvent(QMouseEvent *event) override
{
// If this is the last dock area in a dock container it does not make
// sense to move it to a new floating widget and leave this one
// empty
if (m_DockArea->dockContainer()->isFloating() && m_DockArea->dockContainer()->dockAreaCount() == 1)
{
return;
}
startFloating(event->pos());
}
/**
* Starts floating
*/
void startFloating(const QPoint& Pos)
{
QSize Size = m_DockArea->size();
CFloatingDockContainer* FloatingWidget = new CFloatingDockContainer(m_DockArea);
FloatingWidget->startFloating(Pos, Size);
m_FloatingWidget = FloatingWidget;
}
}; // class CTabsScrollArea
/**
* Private data class of CDockAreaWidget class (pimpl)
*/
struct DockAreaWidgetPrivate
{
CDockAreaWidget* _this;
QBoxLayout* Layout;
QFrame* TitleBar;
QBoxLayout* TopLayout;
QStackedLayout* ContentsLayout;
QScrollArea* TabsScrollArea;
QWidget* TabsContainerWidget;
QBoxLayout* TabsLayout;
QPushButton* TabsMenuButton;
QPushButton* CloseButton;
int TabsLayoutInitCount;
CDockManager* DockManager = nullptr;
/**
* Private data constructor
*/
DockAreaWidgetPrivate(CDockAreaWidget* _public);
/**
* Creates the layout for top area with tabs and close button
*/
void createTabBar();
/**
* Returns the dock widget with the given index
*/
CDockWidget* dockWidgetAt(int index)
{
return dynamic_cast<CDockWidget*>(ContentsLayout->widget(index));
}
/**
* Convenience function to ease title widget access by index
*/
CDockWidgetTitleBar* titleWidgetAt(int index)
{
return dockWidgetAt(index)->titleBar();
}
/**
* Adds a tabs menu entry for the given dock widget
* If menu is 0, a menu entry is added to the menu of the TabsMenuButton
* member. If menu is a valid menu pointer, the entry will be added to
* the given menu
*/
void addTabsMenuEntry(CDockWidget* DockWidget, int Index = -1, QMenu* menu = 0);
/**
* Returns the tab action of the given dock widget
*/
QAction* dockWidgetTabAction(CDockWidget* DockWidget) const
{
return qvariant_cast<QAction*>(DockWidget->property(ACTION_PROPERTY));
}
/**
* Returns the index of the given dock widget
*/
int dockWidgetIndex(CDockWidget* DockWidget) const
{
return DockWidget->property(INDEX_PROPERTY).toInt();
}
/**
* Update the tabs menu if dock widget order changed or if dock widget has
* been removed
*/
void updateTabsMenu();
/**
* Updates the tab bar visibility depending on the number of dock widgets
* in this area
*/
void updateTabBar();
};
// struct DockAreaWidgetPrivate
//============================================================================
DockAreaWidgetPrivate::DockAreaWidgetPrivate(CDockAreaWidget* _public) :
_this(_public)
{
}
//============================================================================
void DockAreaWidgetPrivate::createTabBar()
{
TitleBar = new QFrame(_this);
TitleBar->setObjectName("dockAreaTitleBar");
TopLayout = new QBoxLayout(QBoxLayout::LeftToRight);
TopLayout->setContentsMargins(0, 0, 0, 0);
TopLayout->setSpacing(0);
TitleBar->setLayout(TopLayout);
Layout->addWidget(TitleBar);
TabsScrollArea = new CTabsScrollArea(_this);
TopLayout->addWidget(TabsScrollArea, 1);
TabsContainerWidget = new QWidget();
TabsContainerWidget->setObjectName("tabsContainerWidget");
TabsScrollArea->setWidget(TabsContainerWidget);
TabsLayout = new QBoxLayout(QBoxLayout::LeftToRight);
TabsLayout->setContentsMargins(0, 0, 0, 0);
TabsLayout->setSpacing(0);
TabsLayout->addStretch(1);
TabsContainerWidget->setLayout(TabsLayout);
TabsMenuButton = new QPushButton();
TabsMenuButton->setObjectName("tabsMenuButton");
TabsMenuButton->setFlat(true);
TabsMenuButton->setIcon(_this->style()->standardIcon(QStyle::SP_TitleBarUnshadeButton));
TabsMenuButton->setMaximumWidth(TabsMenuButton->iconSize().width());
TabsMenuButton->setMenu(new QMenu(TabsMenuButton));
TopLayout->addWidget(TabsMenuButton, 0);
_this->connect(TabsMenuButton->menu(), SIGNAL(triggered(QAction*)),
SLOT(onTabsMenuActionTriggered(QAction*)));
CloseButton = new QPushButton();
CloseButton->setObjectName("closeButton");
CloseButton->setFlat(true);
CloseButton->setIcon(_this->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
CloseButton->setToolTip(_this->tr("Close"));
CloseButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
TopLayout->addWidget(CloseButton, 0);
_this->connect(CloseButton, SIGNAL(clicked()), SLOT(onCloseButtonClicked()));
TabsLayoutInitCount = TabsLayout->count();
}
//============================================================================
void DockAreaWidgetPrivate::updateTabBar()
{
CDockContainerWidget* Container = _this->dockContainer();
if (!Container)
{
return;
}
if (Container->isFloating() && (Container->dockAreaCount() == 1) && (_this->count() == 1))
{
TitleBar->setVisible(false);
}
else
{
TitleBar->setVisible(true);
}
}
//============================================================================
void DockAreaWidgetPrivate::addTabsMenuEntry(CDockWidget* DockWidget,
int Index, QMenu* menu)
{
menu = menu ? menu : TabsMenuButton->menu();
QAction* Action;
if (Index >= 0 && Index < menu->actions().count())
{
Action = new QAction(DockWidget->windowTitle());
menu->insertAction(menu->actions().at(Index), Action);
}
else
{
Action = menu->addAction(DockWidget->windowTitle());
}
QVariant vAction = QVariant::fromValue(Action);
DockWidget->setProperty(ACTION_PROPERTY, vAction);
}
//============================================================================
void DockAreaWidgetPrivate::updateTabsMenu()
{
QMenu* menu = TabsMenuButton->menu();
menu->clear();
for (int i = 0; i < ContentsLayout->count(); ++i)
{
addTabsMenuEntry(dockWidgetAt(i), APPEND, menu);
}
}
//============================================================================
CDockAreaWidget::CDockAreaWidget(CDockManager* DockManager, CDockContainerWidget* parent) :
QFrame(parent),
d(new DockAreaWidgetPrivate(this))
{
d->DockManager = DockManager;
d->Layout = new QBoxLayout(QBoxLayout::TopToBottom);
d->Layout->setContentsMargins(0, 0, 0, 0);
d->Layout->setSpacing(0);
setLayout(d->Layout);
d->createTabBar();
d->ContentsLayout = new QStackedLayout();
d->ContentsLayout->setContentsMargins(0, 0, 0, 0);
d->ContentsLayout->setSpacing(0);
d->Layout->addLayout(d->ContentsLayout, 1);
}
//============================================================================
CDockAreaWidget::~CDockAreaWidget()
{
qDebug() << "~CDockAreaWidget()";
delete d;
}
//============================================================================
CDockManager* CDockAreaWidget::dockManager() const
{
return d->DockManager;
}
//============================================================================
CDockContainerWidget* CDockAreaWidget::dockContainer() const
{
QWidget* Parent = parentWidget();
while (Parent)
{
CDockContainerWidget* Container = dynamic_cast<CDockContainerWidget*>(Parent);
if (Container)
{
return Container;
}
Parent = Parent->parentWidget();
}
return 0;
}
//============================================================================
void CDockAreaWidget::addDockWidget(CDockWidget* DockWidget)
{
insertDockWidget(d->ContentsLayout->count(), DockWidget);
}
//============================================================================
void CDockAreaWidget::insertDockWidget(int index, CDockWidget* DockWidget,
bool Activate)
{
d->ContentsLayout->insertWidget(index, DockWidget);
DockWidget->titleBar()->setDockAreaWidget(this);
auto TitleBar = DockWidget->titleBar();
d->TabsLayout->insertWidget(index, TitleBar);
TitleBar->show();
connect(TitleBar, SIGNAL(clicked()), this, SLOT(onDockWidgetTitleClicked()));
DockWidget->setProperty(INDEX_PROPERTY, index);
if (Activate)
{
setCurrentIndex(index);
}
d->addTabsMenuEntry(DockWidget, index);
DockWidget->setDockArea(this);
}
//============================================================================
void CDockAreaWidget::removeDockWidget(CDockWidget* DockWidget)
{
qDebug() << "CDockAreaWidget::removeDockWidget";
d->ContentsLayout->removeWidget(DockWidget);
auto TitleBar = DockWidget->titleBar();
TitleBar->hide();
d->TabsLayout->removeWidget(TitleBar);
disconnect(TitleBar, SIGNAL(clicked()), this, SLOT(onDockWidgetTitleClicked()));
setCurrentIndex(d->ContentsLayout->currentIndex());
d->updateTabsMenu();
CDockContainerWidget* DockContainer = dockContainer();
if (d->ContentsLayout->isEmpty())
{
qDebug() << "Dock Area empty";
dockContainer()->removeDockArea(this);
this->deleteLater();;
}
d->updateTabBar();
DockWidget->setDockArea(nullptr);
DockContainer->dumpLayout();
}
//============================================================================
void CDockAreaWidget::onDockWidgetTitleClicked()
{
CDockWidgetTitleBar* TitleWidget = qobject_cast<CDockWidgetTitleBar*>(sender());
if (!TitleWidget)
{
return;
}
int index = d->TabsLayout->indexOf(TitleWidget);
setCurrentIndex(index);
}
//============================================================================
void CDockAreaWidget::onCloseButtonClicked()
{
currentDockWidget()->toggleView(false);
}
//============================================================================
CDockWidget* CDockAreaWidget::currentDockWidget() const
{
return dockWidget(currentIndex());
}
//============================================================================
void CDockAreaWidget::setCurrentDockWidget(CDockWidget* DockWidget)
{
int Index = tabIndex(DockWidget);
if (Index < 0)
{
return;
}
setCurrentIndex(Index);
}
//============================================================================
void CDockAreaWidget::setCurrentIndex(int index)
{
if (index < 0 || index > (d->TabsLayout->count() - 1))
{
qWarning() << Q_FUNC_INFO << "Invalid index" << index;
return;
}
// Set active TAB and update all other tabs to be inactive
for (int i = 0; i < d->TabsLayout->count(); ++i)
{
QLayoutItem* item = d->TabsLayout->itemAt(i);
if (!item->widget())
{
continue;
}
auto TitleWidget = dynamic_cast<CDockWidgetTitleBar*>(item->widget());
if (!TitleWidget)
{
continue;
}
if (i == index)
{
TitleWidget->show();
TitleWidget->setActiveTab(true);
d->TabsScrollArea->ensureWidgetVisible(TitleWidget);
auto Features = TitleWidget->dockWidget()->features();
d->CloseButton->setVisible(Features.testFlag(CDockWidget::DockWidgetClosable));
}
else
{
TitleWidget->setActiveTab(false);
}
}
d->ContentsLayout->setCurrentIndex(index);
d->ContentsLayout->currentWidget()->show();
emit currentChanged(index);
}
//============================================================================
int CDockAreaWidget::currentIndex() const
{
return d->ContentsLayout->currentIndex();
}
//============================================================================
QRect CDockAreaWidget::titleAreaGeometry() const
{
return d->TopLayout->geometry();
}
//============================================================================
QRect CDockAreaWidget::contentAreaGeometry() const
{
return d->ContentsLayout->geometry();
}
//============================================================================
int CDockAreaWidget::tabIndex(CDockWidget* DockWidget)
{
return d->ContentsLayout->indexOf(DockWidget);
}
//============================================================================
QList<CDockWidget*> CDockAreaWidget::dockWidgets() const
{
QList<CDockWidget*> DockWidgetList;
for (int i = 0; i < d->ContentsLayout->count(); ++i)
{
DockWidgetList.append(dockWidget(i));
}
return DockWidgetList;
}
//============================================================================
QList<CDockWidget*> CDockAreaWidget::openedDockWidgets() const
{
QList<CDockWidget*> DockWidgetList;
for (int i = 0; i < d->ContentsLayout->count(); ++i)
{
CDockWidget* DockWidget = dockWidget(i);
if (!DockWidget->isClosed())
{
DockWidgetList.append(dockWidget(i));
}
}
return DockWidgetList;
}
//============================================================================
int CDockAreaWidget::indexOfContentByTitlePos(const QPoint& p, QWidget* exclude) const
{
for (int i = 0; i < d->ContentsLayout->count(); ++i)
{
auto TitleWidget = d->titleWidgetAt(i);
if (TitleWidget->geometry().contains(p) && (!exclude || TitleWidget != exclude))
{
return i;
}
}
return -1;
}
//============================================================================
int CDockAreaWidget::count() const
{
return d->ContentsLayout->count();
}
//============================================================================
CDockWidget* CDockAreaWidget::dockWidget(int Index) const
{
return dynamic_cast<CDockWidget*>(d->ContentsLayout->widget(Index));
}
//============================================================================
void CDockAreaWidget::reorderDockWidget(int fromIndex, int toIndex)
{
if (fromIndex >= d->ContentsLayout->count() || fromIndex < 0
|| toIndex >= d->ContentsLayout->count() || toIndex < 0 || fromIndex == toIndex)
{
qDebug() << "Invalid index for tab movement" << fromIndex << toIndex;
d->TabsLayout->update();
return;
}
CDockWidget* DockWidget = dockWidget(fromIndex);
// reorder tabs menu action to match new order of contents
auto Menu = d->TabsMenuButton->menu();
auto TabsAction = d->dockWidgetTabAction(DockWidget);
Menu->removeAction(TabsAction);
if (toIndex >= Menu->actions().count())
{
Menu->addAction(TabsAction);
}
else
{
Menu->insertAction(Menu->actions().at(toIndex), TabsAction);
}
// now reorder contents and title bars
QLayoutItem* liFrom = nullptr;
liFrom = d->TabsLayout->takeAt(fromIndex);
d->TabsLayout->insertItem(toIndex, liFrom);
liFrom = d->ContentsLayout->takeAt(fromIndex);
d->ContentsLayout->insertWidget(toIndex, liFrom->widget());
delete liFrom;
}
//============================================================================
void CDockAreaWidget::onTabsMenuActionTriggered(QAction* Action)
{
int Index = d->TabsMenuButton->menu()->actions().indexOf(Action);
setCurrentIndex(Index);
}
//============================================================================
void CDockAreaWidget::updateDockArea()
{
d->updateTabBar();
}
//============================================================================
void CDockAreaWidget::saveState(QDataStream& stream) const
{
stream << d->ContentsLayout->count() << d->ContentsLayout->currentIndex();
qDebug() << "CDockAreaWidget::saveState TabCount: " << d->ContentsLayout->count()
<< " CurrentIndex: " << d->ContentsLayout->currentIndex();
for (int i = 0; i < d->ContentsLayout->count(); ++i)
{
dockWidget(i)->saveState(stream);
}
}
} // namespace ads
//---------------------------------------------------------------------------
// EOF DockAreaWidget.cpp

196
src/DockAreaWidget.h Normal file
View File

@@ -0,0 +1,196 @@
#ifndef DockAreaWidgetH
#define DockAreaWidgetH
/*******************************************************************************
** Qt Advanced Docking System
** Copyright (C) 2017 Uwe Kindler
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation; either
** version 2.1 of the License, or (at your option) any later version.
**
** This library is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public
** License along with this library; If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
//============================================================================
/// \file DockAreaWidget.h
/// \author Uwe Kindler
/// \date 24.02.2017
/// \brief Declaration of CDockAreaWidget class
//============================================================================
//============================================================================
// INCLUDES
//============================================================================
#include <QFrame>
namespace ads
{
struct DockAreaWidgetPrivate;
class CDockManager;
class CDockContainerWidget;
class CDockWidget;
/**
* DockAreaWidget manages multiple instances of DckWidgets.
* It displays a title tab, which is clickable and will switch to
* the contents associated to the title when clicked.
*/
class CDockAreaWidget : public QFrame
{
Q_OBJECT
private:
DockAreaWidgetPrivate* d; ///< private data (pimpl)
friend class DockAreaWidgetPrivate;
private slots:
void onDockWidgetTitleClicked();
void onTabsMenuActionTriggered(QAction* Action);
void onCloseButtonClicked();
public:
/**
* Default Constructor
*/
CDockAreaWidget(CDockManager* DockManager, CDockContainerWidget* parent);
/**
* Virtual Destructor
*/
virtual ~CDockAreaWidget();
/**
* Returns the dock manager object this dock area belongs to
*/
CDockManager* dockManager() const;
/**
* Returns the dock container widget this dock area widget belongs to or 0
* if there is no
*/
CDockContainerWidget* dockContainer() const;
/**
* Inserts a dock widget into dock area.
* All dockwidgets in the dock area tabified in a stacked layout with tabs.
* The index indicates the index of the new dockwidget in the tabbar and
* in the stacked layout. If the Activate parameter is true, the new
* DockWidget will be the active one in the stacked layout
*/
void insertDockWidget(int index, CDockWidget* DockWidget, bool Activate = true);
/**
* Add a new dock widget to dock area.
* All dockwidgets in the dock area tabified in a stacked layout with tabs
*/
void addDockWidget(CDockWidget* DockWidget);
/**
* Removes the given dock widget from the dock area
*/
void removeDockWidget(CDockWidget* DockWidget);
/**
* Returns the rectangle of the title area
*/
QRect titleAreaGeometry() const;
/**
* Returns the rectangle of the content
*/
QRect contentAreaGeometry() const;
/**
* Returns the tab index of the given DockWidget
*/
int tabIndex(CDockWidget* DockWidget);
/**
* Returns the index of contents of the title widget that is located at
* mouse position pos
*/
int indexOfContentByTitlePos(const QPoint& pos, QWidget* exclude = nullptr) const;
/**
* Returns a list of all dock widgets in this dock area.
* This list contains open and closed dock widgets.
*/
QList<CDockWidget*> dockWidgets() const;
/**
* Returns a list of dock widgets that are not closed
*/
QList<CDockWidget*> openedDockWidgets() const;
/**
* Returns the number of dock widgets in this area
*/
int count() const;
/**
* Returns a dock widget by its index
*/
CDockWidget* dockWidget(int Index) const;
/**
* Reorder the index position of DockWidget at fromIndx to toIndex.
*/
void reorderDockWidget(int fromIndex, int toIndex);
/**
* Returns the index of the current active dock widget
*/
int currentIndex() const;
/**
* Returns the current active dock widget
*/
CDockWidget* currentDockWidget() const;
/**
* Shows the tab with tghe given dock widget
*/
void setCurrentDockWidget(CDockWidget* DockWidget);
/**
* Saves the state into the given stream
*/
void saveState(QDataStream& Stream) const;
public slots:
/**
* This sets the index position of the current tab page.
*/
void setCurrentIndex(int index);
/**
* Updates the dock area layout and components visibility
*/
void updateDockArea();
signals:
/**
* This signal is emitted when user clicks on a tab at an index.
*/
void tabBarClicked(int index);
/**
* This signal is emitted when the tab bar's current tab changes. The new
* current has the given index, or -1 if there isn't a new one
* @param index
*/
void currentChanged(int index);
}; // class DockAreaWidget
}
// namespace ads
//-----------------------------------------------------------------------------
#endif // DockAreaWidgetH

922
src/DockContainerWidget.cpp Normal file
View File

@@ -0,0 +1,922 @@
/*******************************************************************************
** Qt Advanced Docking System
** Copyright (C) 2017 Uwe Kindler
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation; either
** version 2.1 of the License, or (at your option) any later version.
**
** This library is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public
** License along with this library; If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
//============================================================================
/// \file DockContainerWidget.cpp
/// \author Uwe Kindler
/// \date 24.02.2017
/// \brief Implementation of CDockContainerWidget class
//============================================================================
//============================================================================
// INCLUDES
//============================================================================
#include "DockContainerWidget.h"
#include <QEvent>
#include <QList>
#include <QGridLayout>
#include <QPointer>
#include <QVariant>
#include <QDebug>
#include "DockManager.h"
#include "DockAreaWidget.h"
#include "DockWidget.h"
#include "FloatingDockContainer.h"
#include "DockOverlay.h"
#include "DockStateSerialization.h"
#include "ads_globals.h"
#include "DockSplitter.h"
namespace ads
{
static unsigned int zOrderCounter = 0;
/**
* Helper function to ease insertion of dock area into splitter
*/
static void insertWidgetIntoSplitter(QSplitter* Splitter, QWidget* widget, bool Append)
{
if (Append)
{
Splitter->addWidget(widget);
}
else
{
Splitter->insertWidget(0, widget);
}
}
/**
* Private data class of CDockContainerWidget class (pimpl)
*/
struct DockContainerWidgetPrivate
{
CDockContainerWidget* _this;
QPointer<CDockManager> DockManager;
unsigned int zOrderIndex = 0;
QList<CDockAreaWidget*> DockAreas;
QGridLayout* Layout = nullptr;
QSplitter* RootSplitter;
bool isFloating = false;
/**
* Private data constructor
*/
DockContainerWidgetPrivate(CDockContainerWidget* _public);
/**
* Adds dock widget to container and returns the dock area that contains
* the inserted dock widget
*/
CDockAreaWidget* dockWidgetIntoContainer(DockWidgetArea area, CDockWidget* Dockwidget);
/**
* Adds dock widget to a existing DockWidgetArea
*/
CDockAreaWidget* dockWidgetIntoDockArea(DockWidgetArea area, CDockWidget* Dockwidget,
CDockAreaWidget* TargetDockArea);
/**
* Add dock area to this container
*/
void addDockArea(CDockAreaWidget* NewDockWidget, DockWidgetArea area = CenterDockWidgetArea);
/**
* Drop floating widget into container
*/
void dropIntoContainer(CFloatingDockContainer* FloatingWidget, DockWidgetArea area);
/**
* Drop floating widget into dock area
*/
void dropIntoSection(CFloatingDockContainer* FloatingWidget,
CDockAreaWidget* TargetArea, DockWidgetArea area);
/**
* Adds new dock areas to the internal dock area list
*/
void addDockAreasToList(const QList<CDockAreaWidget*> NewDockAreas);
/**
* Save state of child nodes
*/
void saveChildNodesState(QDataStream& Stream, QWidget* Widget);
/**
* Restore state of child nodes.
* \param[in] Stream The data stream that contains the serialized state
* \param[out] CreatedWidget The widget created from parsed data or 0 if
* the parsed widget was an empty splitter
* \param[in] Testing If Testing is true, only the stream data is
* parsed without modifiying anything.
*/
bool restoreChildNodes(QDataStream& Stream, QWidget*& CreatedWidget,
bool Testing);
/**
* Restores a splitter.
* \see restoreChildNodes() for details
*/
bool restoreSplitter(QDataStream& Stream, QWidget*& CreatedWidget,
bool Testing);
/**
* Restores a dock area.
* \see restoreChildNodes() for details
*/
bool restoreDockArea(QDataStream& Stream, QWidget*& CreatedWidget,
bool Testing);
/**
* Helper function for recursive dumping of layout
*/
void dumpRecursive(int level, QWidget* widget);
}; // struct DockContainerWidgetPrivate
//============================================================================
DockContainerWidgetPrivate::DockContainerWidgetPrivate(CDockContainerWidget* _public) :
_this(_public)
{
}
//============================================================================
void DockContainerWidgetPrivate::dropIntoContainer(CFloatingDockContainer* FloatingWidget,
DockWidgetArea area)
{
auto InsertParam = internal::dockAreaInsertParameters(area);
auto NewDockAreas = FloatingWidget->dockContainer()->findChildren<CDockAreaWidget*>(
QString(), Qt::FindChildrenRecursively);
QSplitter* Splitter = RootSplitter;
if (DockAreas.count() <= 1)
{
Splitter->setOrientation(InsertParam.orientation());
}
else if (Splitter->orientation() != InsertParam.orientation())
{
QSplitter* NewSplitter = internal::newSplitter(InsertParam.orientation());
QLayoutItem* li = Layout->replaceWidget(Splitter, NewSplitter);
NewSplitter->addWidget(Splitter);
Splitter = NewSplitter;
delete li;
}
// Now we can insert the floating widget content into this container
auto FloatingSplitter = FloatingWidget->dockContainer()->rootSplitter();
if (FloatingSplitter->count() == 1)
{
insertWidgetIntoSplitter(Splitter, FloatingSplitter->widget(0), InsertParam.append());
}
else if (FloatingSplitter->orientation() == InsertParam.orientation())
{
while (FloatingSplitter->count())
{
insertWidgetIntoSplitter(Splitter, FloatingSplitter->widget(0), InsertParam.append());
}
}
else
{
insertWidgetIntoSplitter(Splitter, FloatingSplitter, InsertParam.append());
}
RootSplitter = Splitter;
addDockAreasToList(NewDockAreas);
FloatingWidget->deleteLater();
_this->dumpLayout();
}
//============================================================================
void DockContainerWidgetPrivate::dropIntoSection(CFloatingDockContainer* FloatingWidget,
CDockAreaWidget* TargetArea, DockWidgetArea area)
{
CDockContainerWidget* FloatingContainer = FloatingWidget->dockContainer();
if (area == CenterDockWidgetArea)
{
auto NewDockWidgets = FloatingContainer->findChildren<CDockWidget*>(
QString(), Qt::FindChildrenRecursively);
for (auto DockWidget : NewDockWidgets)
{
TargetArea->insertDockWidget(0, DockWidget, false);
}
TargetArea->setCurrentIndex(0); // make the topmost widget active
FloatingWidget->deleteLater();
TargetArea->updateDockArea();
return;
}
auto InsertParam = internal::dockAreaInsertParameters(area);
auto NewDockAreas = FloatingWidget->dockContainer()->findChildren<CDockAreaWidget*>(
QString(), Qt::FindChildrenRecursively);
QSplitter* TargetAreaSplitter = internal::findParent<QSplitter*>(TargetArea);
if (!TargetAreaSplitter)
{
QSplitter* Splitter = internal::newSplitter(InsertParam.orientation());
Layout->replaceWidget(TargetArea, Splitter);
Splitter->addWidget(TargetArea);
TargetAreaSplitter = Splitter;
}
int AreaIndex = TargetAreaSplitter->indexOf(TargetArea);
auto Widget = FloatingWidget->dockContainer()->findChild<QWidget*>(QString(), Qt::FindDirectChildrenOnly);
auto FloatingSplitter = dynamic_cast<QSplitter*>(Widget);
if (TargetAreaSplitter->orientation() == InsertParam.orientation())
{
if ((FloatingSplitter->orientation() != InsertParam.orientation()) && FloatingSplitter->count() > 1)
{
TargetAreaSplitter->insertWidget(AreaIndex + InsertParam.insertOffset(), Widget);
}
else
{
int InsertIndex = AreaIndex + InsertParam.insertOffset();
while (FloatingSplitter->count())
{
TargetAreaSplitter->insertWidget(InsertIndex++, FloatingSplitter->widget(0));
}
}
}
else
{
QSplitter* NewSplitter = internal::newSplitter(InsertParam.orientation());
if ((FloatingSplitter->orientation() != InsertParam.orientation()) && FloatingSplitter->count() > 1)
{
NewSplitter->addWidget(Widget);
}
else
{
while (FloatingSplitter->count())
{
NewSplitter->addWidget(FloatingSplitter->widget(0));
}
}
insertWidgetIntoSplitter(NewSplitter, TargetArea, !InsertParam.append());
TargetAreaSplitter->insertWidget(AreaIndex, NewSplitter);
}
FloatingWidget->deleteLater();
addDockAreasToList(NewDockAreas);
_this->dumpLayout();
}
//============================================================================
void DockContainerWidgetPrivate::addDockAreasToList(const QList<CDockAreaWidget*> NewDockAreas)
{
int CountBefore = DockAreas.count();
int NewAreaCount = NewDockAreas.count();
DockAreas.append(NewDockAreas);
// We need to ensure, that the dock area title bar is visible. The title bar
// is invisible, if the dock are is a single dock area in a floating widget.
if (1 == CountBefore)
{
DockAreas.at(0)->updateDockArea();
}
if (1 == NewAreaCount)
{
DockAreas.last()->updateDockArea();
}
emit _this->dockAreasAdded();
}
//============================================================================
void DockContainerWidgetPrivate::saveChildNodesState(QDataStream& stream, QWidget* Widget)
{
QSplitter* Splitter = dynamic_cast<QSplitter*>(Widget);
if (Splitter)
{
stream << internal::SplitterMarker << Splitter->orientation() << Splitter->count();
qDebug() << "NodeSplitter orient: " << Splitter->orientation()
<< " WidgetCont: " << Splitter->count();
for (int i = 0; i < Splitter->count(); ++i)
{
saveChildNodesState(stream, Splitter->widget(i));
}
stream << Splitter->sizes();
}
else
{
stream << internal::DockAreaMarker;
CDockAreaWidget* DockArea = dynamic_cast<CDockAreaWidget*>(Widget);
if (DockArea)
{
DockArea->saveState(stream);
}
}
}
//============================================================================
bool DockContainerWidgetPrivate::restoreSplitter(QDataStream& stream,
QWidget*& CreatedWidget, bool Testing)
{
int Orientation;
int WidgetCount;
stream >> Orientation >> WidgetCount;
qDebug() << "Restore NodeSplitter Orientation: " << Orientation <<
" WidgetCount: " << WidgetCount;
QSplitter* Splitter = nullptr;
if (!Testing)
{
Splitter = internal::newSplitter((Qt::Orientation)Orientation);
}
bool Visible = false;
for (int i = 0; i < WidgetCount; ++i)
{
QWidget* ChildNode;
if (!restoreChildNodes(stream, ChildNode, Testing))
{
return false;
}
if (Testing)
{
continue;
}
qDebug() << "ChildNode isVisible " << ChildNode->isVisible()
<< " isVisibleTo " << ChildNode->isVisibleTo(Splitter);
Splitter->addWidget(ChildNode);
Visible |= ChildNode->isVisibleTo(Splitter);
}
QList<int> Sizes;
stream >> Sizes;
if (!Testing)
{
if (!Splitter->count())
{
delete Splitter;
Splitter = nullptr;
}
else
{
Splitter->setSizes(Sizes);
Splitter->setVisible(Visible);
}
CreatedWidget = Splitter;
}
else
{
CreatedWidget = nullptr;
}
return true;
}
//============================================================================
bool DockContainerWidgetPrivate::restoreDockArea(QDataStream& stream,
QWidget*& CreatedWidget, bool Testing)
{
int Tabs;
int CurrentIndex;
stream >> Tabs >> CurrentIndex;
qDebug() << "Restore NodeDockArea Tabs: " << Tabs << " CurrentIndex: "
<< CurrentIndex;
CDockAreaWidget* DockArea = nullptr;
if (!Testing)
{
DockArea = new CDockAreaWidget(DockManager, _this);
}
for (int i = 0; i < Tabs; ++i)
{
int Marker;
stream >> Marker;
if (Marker != internal::DockWidgetMarker)
{
return false;
}
QString ObjectName;
bool Closed;
stream >> ObjectName >> Closed;
qDebug() << "Restore DockWidget " << ObjectName << " Closed: " << Closed;
CDockWidget* DockWidget = DockManager->findDockWidget(ObjectName);
if (!DockWidget || Testing)
{
continue;
}
qDebug() << "Dock Widget found - parent " << DockWidget->parent();
DockArea->addDockWidget(DockWidget);
DockArea->hide();
DockWidget->setToggleViewActionChecked(!Closed);
DockWidget->setProperty("closed", Closed);
DockWidget->setProperty("dirty", false);
}
if (Testing)
{
return true;
}
if (!DockArea->count())
{
delete DockArea;
DockArea = nullptr;
}
else
{
DockArea->setProperty("currentIndex", CurrentIndex);
}
CreatedWidget = DockArea;
DockAreas.append(DockArea);
return true;
}
//============================================================================
bool DockContainerWidgetPrivate::restoreChildNodes(QDataStream& stream,
QWidget*& CreatedWidget, bool Testing)
{
int Marker;
stream >> Marker;
if (internal::SplitterMarker == Marker)
{
return restoreSplitter(stream, CreatedWidget, Testing);
}
else if (internal::DockAreaMarker == Marker)
{
return restoreDockArea(stream, CreatedWidget, Testing);
}
else
{
return false;
}
}
//============================================================================
CDockAreaWidget* DockContainerWidgetPrivate::dockWidgetIntoContainer(DockWidgetArea area,
CDockWidget* Dockwidget)
{
CDockAreaWidget* NewDockArea = new CDockAreaWidget(DockManager, _this);
NewDockArea->addDockWidget(Dockwidget);
addDockArea(NewDockArea, area);
return NewDockArea;
}
//============================================================================
void DockContainerWidgetPrivate::addDockArea(CDockAreaWidget* NewDockArea, DockWidgetArea area)
{
auto InsertParam = internal::dockAreaInsertParameters(area);
// As long as we have only one dock area in the splitter we can adjust
// its orientation
if (DockAreas.count() <= 1)
{
RootSplitter->setOrientation(InsertParam.orientation());
}
QSplitter* Splitter = RootSplitter;
if (Splitter->orientation() == InsertParam.orientation())
{
insertWidgetIntoSplitter(Splitter, NewDockArea, InsertParam.append());
}
else
{
QSplitter* NewSplitter = internal::newSplitter(InsertParam.orientation());
if (InsertParam.append())
{
QLayoutItem* li = Layout->replaceWidget(Splitter, NewSplitter);
NewSplitter->addWidget(Splitter);
NewSplitter->addWidget(NewDockArea);
delete li;
}
else
{
NewSplitter->addWidget(NewDockArea);
QLayoutItem* li = Layout->replaceWidget(Splitter, NewSplitter);
NewSplitter->addWidget(Splitter);
delete li;
}
RootSplitter = NewSplitter;
}
DockAreas.append(NewDockArea);
NewDockArea->updateDockArea();
emit _this->dockAreasAdded();
}
//============================================================================
void DockContainerWidgetPrivate::dumpRecursive(int level, QWidget* widget)
{
#if defined(QT_DEBUG)
QSplitter* Splitter = dynamic_cast<QSplitter*>(widget);
QByteArray buf;
buf.fill(' ', level * 4);
if (Splitter)
{
qDebug("%sSplitter %s", (const char*)buf, (Splitter->orientation() == Qt::Vertical)
? "-" : "|");
for (int i = 0; i < Splitter->count(); ++i)
{
dumpRecursive(level + 1, Splitter->widget(i));
}
}
else
{
CDockAreaWidget* DockArea = dynamic_cast<CDockAreaWidget*>(widget);
if (!DockArea)
{
return;
}
qDebug("%sDockArea", (const char*)buf);
}
#else
Q_UNUSED(level);
Q_UNUSED(widget);
#endif
}
//============================================================================
CDockAreaWidget* DockContainerWidgetPrivate::dockWidgetIntoDockArea(DockWidgetArea area,
CDockWidget* Dockwidget, CDockAreaWidget* TargetDockArea)
{
if (CenterDockWidgetArea == area)
{
TargetDockArea->addDockWidget(Dockwidget);
return TargetDockArea;
}
CDockAreaWidget* NewDockArea = new CDockAreaWidget(DockManager, _this);
NewDockArea->addDockWidget(Dockwidget);
auto InsertParam = internal::dockAreaInsertParameters(area);
QSplitter* TargetAreaSplitter = internal::findParent<QSplitter*>(TargetDockArea);
int index = TargetAreaSplitter ->indexOf(TargetDockArea);
if (TargetAreaSplitter->orientation() == InsertParam.orientation())
{
qDebug() << "TargetAreaSplitter->orientation() == InsertParam.orientation()";
TargetAreaSplitter->insertWidget(index + InsertParam.insertOffset(), NewDockArea);
}
else
{
qDebug() << "TargetAreaSplitter->orientation() != InsertParam.orientation()";
QSplitter* NewSplitter = internal::newSplitter(InsertParam.orientation());
NewSplitter->addWidget(TargetDockArea);
insertWidgetIntoSplitter(NewSplitter, NewDockArea, InsertParam.append());
TargetAreaSplitter->insertWidget(index, NewSplitter);
}
DockAreas.append(NewDockArea);
emit _this->dockAreasAdded();
return NewDockArea;
}
//============================================================================
CDockContainerWidget::CDockContainerWidget(CDockManager* DockManager, QWidget *parent) :
QFrame(parent),
d(new DockContainerWidgetPrivate(this))
{
d->isFloating = dynamic_cast<CFloatingDockContainer*>(parent) != 0;
//setStyleSheet("background: green;");
d->DockManager = DockManager;
if (DockManager != this)
{
d->DockManager->registerDockContainer(this);
}
d->Layout = new QGridLayout();
d->Layout->setContentsMargins(0, 1, 0, 1);
d->Layout->setSpacing(0);
setLayout(d->Layout);
d->RootSplitter = internal::newSplitter(Qt::Horizontal);
d->Layout->addWidget(d->RootSplitter);
}
//============================================================================
CDockContainerWidget::~CDockContainerWidget()
{
if (d->DockManager)
{
d->DockManager->removeDockContainer(this);
}
delete d;
}
//============================================================================
CDockAreaWidget* CDockContainerWidget::addDockWidget(DockWidgetArea area, CDockWidget* Dockwidget,
CDockAreaWidget* DockAreaWidget)
{
CDockAreaWidget* OldDockArea = Dockwidget->dockAreaWidget();
if (OldDockArea)
{
OldDockArea->removeDockWidget(Dockwidget);
}
Dockwidget->setDockManager(d->DockManager);
if (DockAreaWidget)
{
return d->dockWidgetIntoDockArea(area, Dockwidget, DockAreaWidget);
}
else
{
return d->dockWidgetIntoContainer(area, Dockwidget);
}
}
//============================================================================
unsigned int CDockContainerWidget::zOrderIndex() const
{
return d->zOrderIndex;
}
//============================================================================
bool CDockContainerWidget::isInFrontOf(CDockContainerWidget* Other) const
{
return this->zOrderIndex() > Other->zOrderIndex();
}
//============================================================================
bool CDockContainerWidget::event(QEvent *e)
{
bool Result = QWidget::event(e);
if (e->type() == QEvent::WindowActivate)
{
d->zOrderIndex = ++zOrderCounter;
}
else if (e->type() == QEvent::Show && !d->zOrderIndex)
{
d->zOrderIndex = ++zOrderCounter;
}
return Result;
}
//============================================================================
void CDockContainerWidget::addDockArea(CDockAreaWidget* DockAreaWidget,
DockWidgetArea area)
{
CDockContainerWidget* Container = DockAreaWidget->dockContainer();
if (Container && Container != this)
{
Container->removeDockArea(DockAreaWidget);
}
d->addDockArea(DockAreaWidget, area);
}
//============================================================================
void CDockContainerWidget::removeDockArea(CDockAreaWidget* area)
{
qDebug() << "CDockContainerWidget::removeDockArea";
d->DockAreas.removeAll(area);
QSplitter* Splitter = internal::findParent<QSplitter*>(area);
area->setParent(0);
if (Splitter == d->RootSplitter || Splitter->count() != 1)
{
emit dockAreasRemoved();
return;
}
// It the splitter contains only one single widget, then we do not need
// it anymore and can replace it with its content
qDebug() << "Replacing splitter with content";
QWidget* widget = Splitter->widget(0);
widget->setParent(this);
QSplitter* ParentSplitter = internal::findParent<QSplitter*>(Splitter);
internal::replaceSplitterWidget(ParentSplitter, Splitter, widget);
delete Splitter;
dumpLayout();
emit dockAreasRemoved();
}
//============================================================================
CDockAreaWidget* CDockContainerWidget::dockAreaAt(const QPoint& GlobalPos) const
{
for (const auto& DockArea : d->DockAreas)
{
if (DockArea->rect().contains(DockArea->mapFromGlobal(GlobalPos)))
{
return DockArea;
}
}
return 0;
}
//============================================================================
CDockAreaWidget* CDockContainerWidget::dockArea(int Index) const
{
return (Index < dockAreaCount()) ? d->DockAreas[Index] : 0;
}
//============================================================================
bool CDockContainerWidget::isFloating() const
{
return d->isFloating;
}
//============================================================================
int CDockContainerWidget::dockAreaCount() const
{
return d->DockAreas.count();
}
//============================================================================
int CDockContainerWidget::visibleDockAreaCount() const
{
// TODO Cache or precalculate this to speed it up because it is used during
// movement of floating widget
int Result = 0;
for (auto DockArea : d->DockAreas)
{
Result += DockArea->isVisible() ? 1 : 0;
}
return Result;
}
//============================================================================
void CDockContainerWidget::dropFloatingWidget(CFloatingDockContainer* FloatingWidget,
const QPoint& TargetPos)
{
qDebug() << "CDockContainerWidget::dropFloatingWidget";
CDockAreaWidget* DockArea = dockAreaAt(TargetPos);
auto dropArea = InvalidDockWidgetArea;
if (DockArea)
{
auto dropOverlay = d->DockManager->dockAreaOverlay();
dropOverlay->setAllowedAreas(AllDockAreas);
dropArea = dropOverlay->showOverlay(DockArea);
if (dropArea != InvalidDockWidgetArea)
{
qDebug() << "Dock Area Drop Content: " << dropArea;
d->dropIntoSection(FloatingWidget, DockArea, dropArea);
}
}
// mouse is over container
if (InvalidDockWidgetArea == dropArea)
{
dropArea = d->DockManager->containerOverlay()->dropAreaUnderCursor();
qDebug() << "Container Drop Content: " << dropArea;
if (dropArea != InvalidDockWidgetArea)
{
d->dropIntoContainer(FloatingWidget, dropArea);
}
}
}
//============================================================================
QList<CDockAreaWidget*> CDockContainerWidget::openedDockAreas() const
{
QList<CDockAreaWidget*> Result;
for (auto DockArea : d->DockAreas)
{
if (DockArea->isVisible())
{
Result.append(DockArea);
}
}
return Result;
}
//============================================================================
void CDockContainerWidget::saveState(QDataStream& stream) const
{
qDebug() << "CDockContainerWidget::saveState isFloating "
<< isFloating();
stream << internal::ContainerMarker;
stream << isFloating();
if (isFloating())
{
CFloatingDockContainer* FloatingWidget = internal::findParent<CFloatingDockContainer*>(this);
stream << FloatingWidget->saveGeometry();
}
d->saveChildNodesState(stream, d->RootSplitter);
}
//============================================================================
bool CDockContainerWidget::restoreState(QDataStream& stream, bool Testing)
{
bool IsFloating;
int Marker;
stream >> Marker;
if (Marker != internal::ContainerMarker)
{
return false;
}
stream >> IsFloating;
qDebug() << "Restore CDockContainerWidget Floating" << IsFloating;
QWidget* NewRootSplitter;
if (!Testing)
{
d->DockAreas.clear();
}
if (IsFloating)
{
qDebug() << "Restore floating widget";
QByteArray Geometry;
stream >> Geometry;
if (!Testing)
{
CFloatingDockContainer* FloatingWidget = internal::findParent<CFloatingDockContainer*>(this);
FloatingWidget->restoreGeometry(Geometry);
FloatingWidget->show();
}
}
if (!d->restoreChildNodes(stream, NewRootSplitter, Testing))
{
return false;
}
if (Testing)
{
return true;
}
// If the root splitter is empty, rostoreChildNodes returns a 0 pointer
// and we need to create a new empty root splitter
if (!NewRootSplitter)
{
NewRootSplitter = internal::newSplitter(Qt::Horizontal);
}
d->Layout->replaceWidget(d->RootSplitter, NewRootSplitter);
QSplitter* OldRoot = d->RootSplitter;
d->RootSplitter = dynamic_cast<QSplitter*>(NewRootSplitter);
OldRoot->deleteLater();
return true;
}
//============================================================================
QSplitter* CDockContainerWidget::rootSplitter() const
{
return d->RootSplitter;
}
//============================================================================
void CDockContainerWidget::dumpLayout()
{
d->dumpRecursive(0, d->RootSplitter);
}
} // namespace ads
//---------------------------------------------------------------------------
// EOF DockContainerWidget.cpp

180
src/DockContainerWidget.h Normal file
View File

@@ -0,0 +1,180 @@
#ifndef DockContainerWidgetH
#define DockContainerWidgetH
/*******************************************************************************
** Qt Advanced Docking System
** Copyright (C) 2017 Uwe Kindler
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation; either
** version 2.1 of the License, or (at your option) any later version.
**
** This library is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public
** License along with this library; If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
//============================================================================
/// \file DockContainerWidget.h
/// \author Uwe Kindler
/// \date 24.02.2017
/// \brief Declaration of CDockContainerWidget class
//============================================================================
//============================================================================
// INCLUDES
//============================================================================
#include <QFrame>
#include "ads_globals.h"
namespace ads
{
struct DockContainerWidgetPrivate;
class CDockAreaWidget;
class CDockWidget;
class CDockManager;
class CFloatingDockContainer;
/**
* Container that manages a number of dock areas with single dock widgets
* or tabyfied dock widgets in each area
*/
class CDockContainerWidget : public QFrame
{
Q_OBJECT
private:
DockContainerWidgetPrivate* d; ///< private data (pimpl)
friend class DockContainerWidgetPrivate;
protected:
/**
* Handles activation events to update zOrderIndex
*/
virtual bool event(QEvent *e) override;
/**
* Access function for the internal root splitter
*/
QSplitter* rootSplitter() const;
public:
/**
* Default Constructor
*/
CDockContainerWidget(CDockManager* DockManager, QWidget* parent = 0);
/**
* Virtual Destructor
*/
virtual ~CDockContainerWidget();
/**
* Drop floating widget into the container
*/
void dropFloatingWidget(CFloatingDockContainer* FloatingWidget, const QPoint& TargetPos);
/**
* Adds dockwidget into the given area.
* If DockAreaWidget is not null, then the area parameter indicates the area
* into the DockAreaWidget. If DockAreaWidget is null, the Dockwidget will
* be dropped into the container.
* \return Returns the dock area widget that contains the new DockWidget
*/
CDockAreaWidget* addDockWidget(DockWidgetArea area, CDockWidget* Dockwidget,
CDockAreaWidget* DockAreaWidget = nullptr);
/**
* Adds the given dock area to this container widget
*/
void addDockArea(CDockAreaWidget* DockAreaWidget, DockWidgetArea area = CenterDockWidgetArea);
/**
* Removes the given dock area from this container
*/
void removeDockArea(CDockAreaWidget* area);
/**
* Returns the current zOrderIndex
*/
virtual unsigned int zOrderIndex() const;
/**
* This function returns true if this container widgets z order index is
* higher than the index of the container widget given in Other parameter
*/
bool isInFrontOf(CDockContainerWidget* Other) const;
/**
* Returns the dock area at teh given global position or 0 if there is no
* dock area at this position
*/
CDockAreaWidget* dockAreaAt(const QPoint& GlobalPos) const;
/**
* Returns the dock area at the given Index or 0 if the index is out of
* range
*/
CDockAreaWidget* dockArea(int Index) const;
/**
* Returns the list of dock areas that are not closed
* If all dock widgets in a dock area are closed, the dock area will be closed
*/
QList<CDockAreaWidget*> openedDockAreas() const;
/**
* Returns the number of dock areas in this container
*/
int dockAreaCount() const;
/**
* Returns the number of visible dock areas
*/
int visibleDockAreaCount() const;
/**
* This function returns true, if this container is in a floating widget
*/
bool isFloating() const;
/**
* Saves the state into the given stream
*/
void saveState(QDataStream& Stream) const;
/**
* Restores the state from given stream.
* If Testing is true, the function only parses the data from the given
* stream but does not restore anything. You can use this check for
* faulty files before you start restoring the state
*/
bool restoreState(QDataStream& Stream, bool Testing);
/**
* Dumps the layout for debugging purposes
*/
void dumpLayout();
signals:
/**
* This signal is emitted if one or multiple dock areas has been added to
* the internal list of dock areas.
* If multiple dock areas are inserted, this signal is emitted only once
*/
void dockAreasAdded();
/**
* This signal is emitted if one or multiple dock areas has been removed
*/
void dockAreasRemoved();
}; // class DockContainerWidget
} // namespace ads
//-----------------------------------------------------------------------------
#endif // DockContainerWidgetH

399
src/DockManager.cpp Normal file
View File

@@ -0,0 +1,399 @@
/*******************************************************************************
** Qt Advanced Docking System
** Copyright (C) 2017 Uwe Kindler
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation; either
** version 2.1 of the License, or (at your option) any later version.
**
** This library is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public
** License along with this library; If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
//============================================================================
/// \file DockManager.cpp
/// \author Uwe Kindler
/// \date 26.02.2017
/// \brief Implementation of CDockManager class
//============================================================================
//============================================================================
// INCLUDES
//============================================================================
#include "DockManager.h"
#include <QMainWindow>
#include <QList>
#include <QMap>
#include <QVariant>
#include <QDebug>
#include <QFile>
#include <QApplication>
#include <QAction>
#include "FloatingDockContainer.h"
#include "DockOverlay.h"
#include "DockWidget.h"
#include "ads_globals.h"
#include "DockStateSerialization.h"
#include "DockWidgetTitleBar.h"
#include "DockAreaWidget.h"
namespace ads
{
/**
* Private data class of CDockManager class (pimpl)
*/
struct DockManagerPrivate
{
CDockManager* _this;
QList<CFloatingDockContainer*> FloatingWidgets;
QList<CDockContainerWidget*> Containers;
CDockOverlay* ContainerOverlay;
CDockOverlay* DockAreaOverlay;
QMap<QString, CDockWidget*> DockWidgetsMap;
/**
* Private data constructor
*/
DockManagerPrivate(CDockManager* _public);
/**
* Checks if the given data stream is a valid docking system state
* file.
*/
bool checkFormat(const QByteArray &state, int version);
/**
* Restores the state
*/
bool restoreState(const QByteArray &state, int version);
/**
* Restores the container with the given index
*/
bool restoreContainer(int Index, QDataStream& stream, bool Testing);
/**
* Loads the stylesheet
*/
void loadStylesheet();
};
// struct DockManagerPrivate
//============================================================================
DockManagerPrivate::DockManagerPrivate(CDockManager* _public) :
_this(_public)
{
}
//============================================================================
void DockManagerPrivate::loadStylesheet()
{
QString Result;
QFile StyleSheetFile(":ads/stylesheets/default.css");
StyleSheetFile.open(QIODevice::ReadOnly);
QTextStream StyleSheetStream(&StyleSheetFile);
Result = StyleSheetStream.readAll();
StyleSheetFile.close();
_this->setStyleSheet(Result);
}
//============================================================================
bool DockManagerPrivate::checkFormat(const QByteArray &state, int version)
{
if (state.isEmpty())
{
return false;
}
QByteArray sd = state;
QDataStream stream(&sd, QIODevice::ReadOnly);
int marker;
int v;
stream >> marker;
stream >> v;
if (stream.status() != QDataStream::Ok || marker != internal::VersionMarker || v != version)
{
return false;
}
int Result = true;
int ContainerCount;
stream >> ContainerCount;
int i;
for (i = 0; i < ContainerCount; ++i)
{
if (!Containers[0]->restoreState(stream, internal::RestoreTesting))
{
Result = false;
break;
}
}
return Result;
}
//============================================================================
bool DockManagerPrivate::restoreContainer(int Index, QDataStream& stream, bool Testing)
{
if (Index >= Containers.count())
{
CFloatingDockContainer* FloatingWidget = new CFloatingDockContainer(_this);
return FloatingWidget->restoreState(stream, Testing);
}
else
{
qDebug() << "d->Containers[i]->restoreState ";
return Containers[Index]->restoreState(stream, Testing);
}
}
//============================================================================
bool DockManagerPrivate::restoreState(const QByteArray &state, int version)
{
if (state.isEmpty())
{
return false;
}
QByteArray sd = state;
QDataStream stream(&sd, QIODevice::ReadOnly);
int marker;
int v;
stream >> marker;
stream >> v;
if (stream.status() != QDataStream::Ok || marker != internal::VersionMarker || v != version)
{
return false;
}
int Result = true;
int ContainerCount;
stream >> ContainerCount;
qDebug() << "ContainerCount " << ContainerCount;
int i;
for (i = 0; i < ContainerCount; ++i)
{
Result = restoreContainer(i, stream, internal::Restore);
if (!Result)
{
break;
}
}
// Delete remaining empty floating widgets
int FloatingWidgetIndex = i - 1;
int DeleteCount = FloatingWidgets.count() - FloatingWidgetIndex;
for (int i = 0; i < DeleteCount; ++i)
{
FloatingWidgets[FloatingWidgetIndex + i]->deleteLater();
}
return Result;
}
//============================================================================
CDockManager::CDockManager(QWidget *parent) :
CDockContainerWidget(this, parent),
d(new DockManagerPrivate(this))
{
QMainWindow* MainWindow = dynamic_cast<QMainWindow*>(parent);
if (MainWindow)
{
MainWindow->setCentralWidget(this);
}
d->DockAreaOverlay = new CDockOverlay(this, CDockOverlay::ModeDockAreaOverlay);
d->ContainerOverlay = new CDockOverlay(this, CDockOverlay::ModeContainerOverlay);
d->Containers.append(this);
d->loadStylesheet();
}
//============================================================================
CDockManager::~CDockManager()
{
auto FloatingWidgets = d->FloatingWidgets;
for (auto FloatingWidget : FloatingWidgets)
{
delete FloatingWidget;
}
delete d;
}
//============================================================================
void CDockManager::registerFloatingWidget(CFloatingDockContainer* FloatingWidget)
{
d->FloatingWidgets.append(FloatingWidget);
qDebug() << "d->FloatingWidgets.count() " << d->FloatingWidgets.count();
}
//============================================================================
void CDockManager::removeFloatingWidget(CFloatingDockContainer* FloatingWidget)
{
d->FloatingWidgets.removeAll(FloatingWidget);
}
//============================================================================
void CDockManager::registerDockContainer(CDockContainerWidget* DockContainer)
{
d->Containers.append(DockContainer);
}
//============================================================================
void CDockManager::removeDockContainer(CDockContainerWidget* DockContainer)
{
if (this != DockContainer)
{
d->Containers.removeAll(DockContainer);
}
}
//============================================================================
CDockOverlay* CDockManager::containerOverlay() const
{
return d->ContainerOverlay;
}
//============================================================================
CDockOverlay* CDockManager::dockAreaOverlay() const
{
return d->DockAreaOverlay;
}
//============================================================================
const QList<CDockContainerWidget*> CDockManager::dockContainers() const
{
return d->Containers;
}
//============================================================================
const QList<CFloatingDockContainer*> CDockManager::floatingWidgets() const
{
return d->FloatingWidgets;
}
//============================================================================
unsigned int CDockManager::zOrderIndex() const
{
return 0;
}
//============================================================================
QByteArray CDockManager::saveState(int version) const
{
QByteArray data;
QDataStream stream(&data, QIODevice::WriteOnly);
stream << internal::VersionMarker;
stream << version;
stream << d->Containers.count();
for (auto Container : d->Containers)
{
Container->saveState(stream);
}
return data;
}
//============================================================================
bool CDockManager::restoreState(const QByteArray &state, int version)
{
if (!d->checkFormat(state, version))
{
qDebug() << "checkFormat: Error checking format!!!!!!!";
return false;
}
for (auto DockWidget : d->DockWidgetsMap)
{
DockWidget->setProperty("dirty", true);
}
if (!d->restoreState(state, version))
{
qDebug() << "restoreState: Error restoring state!!!!!!!";
return false;
}
// All dock widgets, that have not been processed in the restore state
// function are invisible to the user now and have no assigned dock area
// The do not belong to any dock container, until the user toggles the
// toggle view action the next time
for (auto DockWidget : d->DockWidgetsMap)
{
if (DockWidget->property("dirty").toBool())
{
DockWidget->flagAsUnassigned();
}
else if (!DockWidget->property("closed").toBool())
{
DockWidget->toggleView(true);
}
}
// Now all dock areas are properly restored and we setup the index of
// The dock areas because the previous toggleView() action has changed
// the dock area index
for (auto DockContainer : d->Containers)
{
for (int i = 0; i < DockContainer->dockAreaCount(); ++i)
{
CDockAreaWidget* DockArea = DockContainer->dockArea(i);
int CurrentIndex = DockArea->property("currentIndex").toInt();
if (CurrentIndex < DockArea->count() && DockArea->count() > 1 && CurrentIndex > -1)
{
DockArea->setCurrentIndex(CurrentIndex);
}
}
}
return true;
}
//============================================================================
CDockAreaWidget* CDockManager::addDockWidget(DockWidgetArea area,
CDockWidget* Dockwidget, CDockAreaWidget* DockAreaWidget)
{
d->DockWidgetsMap.insert(Dockwidget->objectName(), Dockwidget);
return CDockContainerWidget::addDockWidget(area, Dockwidget, DockAreaWidget);
}
//============================================================================
CDockWidget* CDockManager::findDockWidget(const QString& ObjectName)
{
return d->DockWidgetsMap.value(ObjectName, nullptr);
}
} // namespace ads
//---------------------------------------------------------------------------
// EOF DockManager.cpp

151
src/DockManager.h Normal file
View File

@@ -0,0 +1,151 @@
#ifndef DockManagerH
#define DockManagerH
/*******************************************************************************
** Qt Advanced Docking System
** Copyright (C) 2017 Uwe Kindler
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation; either
** version 2.1 of the License, or (at your option) any later version.
**
** This library is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public
** License along with this library; If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
//============================================================================
/// \file DockManager.h
/// \author Uwe Kindler
/// \date 26.02.2017
/// \brief Declaration of CDockManager class
//============================================================================
//============================================================================
// INCLUDES
//============================================================================
#include "DockContainerWidget.h"
namespace ads
{
struct DockManagerPrivate;
class CFloatingDockContainer;
class CDockContainerWidget;
class CDockOverlay;
/**
* The central dock manager that maintains the complete docking system
**/
class CDockManager : public CDockContainerWidget
{
Q_OBJECT
private:
DockManagerPrivate* d; ///< private data (pimpl)
friend class DockManagerPrivate;
protected:
public:
/**
* Default Constructor.
* If the given parent is a QMainWindow, the dock manager sets itself as the
* central widget
*/
CDockManager(QWidget* parent = 0);
/**
* Virtual Destructor
*/
virtual ~CDockManager();
/**
* Registers the given floating widget in the internal list of
* floating widgets
*/
void registerFloatingWidget(CFloatingDockContainer* FloatingWidget);
/**
* Remove the given floating widget from the list of registered floating
* widgets
*/
void removeFloatingWidget(CFloatingDockContainer* FloatingWidget);
/**
* Registers the given dock container widget
*/
void registerDockContainer(CDockContainerWidget* DockContainer);
/**
* Remove dock container from the internal list of registered dock
* containers
*/
void removeDockContainer(CDockContainerWidget* DockContainer);
/**
* Overlay for containers
*/
CDockOverlay* containerOverlay() const;
/**
* Overlay for dock areas
*/
CDockOverlay* dockAreaOverlay() const;
/**
* Adds dockwidget into the given area.
* If DockAreaWidget is not null, then the area parameter indicates the area
* into the DockAreaWidget. If DockAreaWidget is null, the Dockwidget will
* be dropped into the container.
* \return Returns the dock area widget that contains the new DockWidget
*/
CDockAreaWidget* addDockWidget(DockWidgetArea area, CDockWidget* Dockwidget,
CDockAreaWidget* DockAreaWidget = nullptr);
/**
* Searches for a registered doc widget with the given ObjectName
* \return Return the found dock widget or nullptr if a dock widget with the
* given name is not registered
*/
CDockWidget* findDockWidget(const QString& ObjectName);
/**
* Returns the list of all active and visible dock containers
* Dock containers are the main dock manager and all floating widgets
*/
const QList<CDockContainerWidget*> dockContainers() const;
/**
* Returns the list of all floating widgets
*/
const QList<CFloatingDockContainer*> floatingWidgets() const;
/**
* This function always return 0 because the main window is always behind
* any floating widget
*/
virtual unsigned int zOrderIndex() const;
/**
* Saves the current state of the dockmanger and all its dock widgets
*/
QByteArray saveState(int version = 0) const;
/**
* Restores the state of this dockmanagers dockwidgets.
* The version number is compared with that stored in state. If they do
* not match, the dockmanager's state is left unchanged, and this function
* returns false; otherwise, the state is restored, and this function
* returns true.
*/
bool restoreState(const QByteArray &state, int version = 0);
}; // class DockManager
} // namespace ads
//-----------------------------------------------------------------------------
#endif // DockManagerH

617
src/DockOverlay.cpp Normal file
View File

@@ -0,0 +1,617 @@
/*******************************************************************************
** Qt Advanced Docking System
** Copyright (C) 2017 Uwe Kindler
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation; either
** version 2.1 of the License, or (at your option) any later version.
**
** This library is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public
** License along with this library; If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
//============================================================================
// INCLUDES
//============================================================================
#include "DockOverlay.h"
#include <QPointer>
#include <QPaintEvent>
#include <QResizeEvent>
#include <QMoveEvent>
#include <QPainter>
#include <QGridLayout>
#include <QCursor>
#include <QIcon>
#include <QLabel>
#include <QtGlobal>
#include <QDebug>
#include "DockAreaWidget.h"
namespace ads
{
//============================================================================
static QPixmap createDropIndicatorPixmap(const QPalette& pal, const QSizeF& size, DockWidgetArea DockWidgetArea,
CDockOverlay::eMode Mode)
{
QColor borderColor = pal.color(QPalette::Active, QPalette::Highlight);
QColor backgroundColor = pal.color(QPalette::Active, QPalette::Base);
QPixmap pm(size.width(), size.height());
pm.fill(QColor(0, 0, 0, 0));
QPainter p(&pm);
QPen pen = p.pen();
QRectF ShadowRect(pm.rect());
QRectF baseRect;
baseRect.setSize(ShadowRect.size() * 0.7);
baseRect.moveCenter(ShadowRect.center());
// Fill
p.fillRect(ShadowRect, QColor(0, 0, 0, 64));
// Drop area rect.
p.save();
QRectF areaRect;
QLineF areaLine;
QRectF nonAreaRect;
switch (DockWidgetArea)
{
case TopDockWidgetArea:
areaRect = QRectF(baseRect.x(), baseRect.y(), baseRect.width(), baseRect.height() * .5f);
nonAreaRect = QRectF(baseRect.x(), ShadowRect.height() * .5f, baseRect.width(), baseRect.height() * .5f);
areaLine = QLineF(areaRect.bottomLeft(), areaRect.bottomRight());
break;
case RightDockWidgetArea:
areaRect = QRectF(ShadowRect.width() * .5f, baseRect.y(), baseRect.width() * .5f, baseRect.height());
nonAreaRect = QRectF(baseRect.x(), baseRect.y(), baseRect.width() * .5f, baseRect.height());
areaLine = QLineF(areaRect.topLeft(), areaRect.bottomLeft());
break;
case BottomDockWidgetArea:
areaRect = QRectF(baseRect.x(), ShadowRect.height() * .5f, baseRect.width(), baseRect.height() * .5f);
nonAreaRect = QRectF(baseRect.x(), baseRect.y(), baseRect.width(), baseRect.height() * .5f);
areaLine = QLineF(areaRect.topLeft(), areaRect.topRight());
break;
case LeftDockWidgetArea:
areaRect = QRectF(baseRect.x(), baseRect.y(), baseRect.width() * .5f, baseRect.height());
nonAreaRect = QRectF(ShadowRect.width() * .5f, baseRect.y(), baseRect.width() * .5f, baseRect.height());
areaLine = QLineF(areaRect.topRight(), areaRect.bottomRight());
break;
default:
break;
}
QSizeF baseSize = baseRect.size();
if (CDockOverlay::ModeContainerOverlay == Mode && DockWidgetArea != CenterDockWidgetArea)
{
baseRect = areaRect;
}
p.fillRect(baseRect, backgroundColor);
if (areaRect.isValid())
{
pen = p.pen();
pen.setColor(borderColor);
QColor Color = borderColor;
Color.setAlpha(64);
p.setBrush(Color);
p.setPen(Qt::NoPen);
p.drawRect(areaRect);
pen = p.pen();
pen.setColor(borderColor);
pen.setStyle(Qt::DashLine);
p.setPen(pen);
p.drawLine(areaLine);
}
p.restore();
p.save();
// Draw outer border
pen = p.pen();
pen.setColor(borderColor);
pen.setWidth(1);
p.setBrush(Qt::NoBrush);
p.setPen(pen);
p.drawRect(baseRect);
// draw window title bar
p.setBrush(borderColor);
QRectF FrameRect(baseRect.topLeft(), QSizeF(baseRect.width(), baseSize.height() / 10));
p.drawRect(FrameRect);
p.restore();
// Draw arrow for outer container drop indicators
if (CDockOverlay::ModeContainerOverlay == Mode && DockWidgetArea != CenterDockWidgetArea)
{
QRectF ArrowRect;
ArrowRect.setSize(baseSize);
ArrowRect.setWidth(ArrowRect.width() / 4.6);
ArrowRect.setHeight(ArrowRect.height() / 2);
ArrowRect.moveCenter(QPointF(0, 0));
QPolygonF Arrow;
Arrow << ArrowRect.topLeft()
<< QPointF( ArrowRect.right(), ArrowRect.center().y())
<< ArrowRect.bottomLeft();
p.setPen(Qt::NoPen);
p.setBrush(backgroundColor);
p.setRenderHint(QPainter::Antialiasing, true);
p.translate(nonAreaRect.center().x(), nonAreaRect.center().y());
switch (DockWidgetArea)
{
case TopDockWidgetArea:
p.rotate(-90);
break;
case RightDockWidgetArea:
break;
case BottomDockWidgetArea:
p.rotate(90);
break;
case LeftDockWidgetArea:
p.rotate(180);
break;
default:
break;
}
p.drawPolygon(Arrow);
}
return pm;
}
//============================================================================
QWidget* createDropIndicatorWidget(DockWidgetArea DockWidgetArea,
CDockOverlay::eMode Mode)
{
QLabel* l = new QLabel();
l->setObjectName("DockWidgetAreaLabel");
const qreal metric = static_cast<qreal>(l->fontMetrics().height()) * 3.f;
const QSizeF size(metric, metric);
l->setPixmap(createDropIndicatorPixmap(l->palette(), size, DockWidgetArea,
Mode));
l->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
l->setAttribute(Qt::WA_TranslucentBackground);
return l;
}
/**
* Private data class of CDockOverlay
*/
struct DockOverlayPrivate
{
CDockOverlay* _this;
DockWidgetAreas AllowedAreas = InvalidDockWidgetArea;
CDockOverlayCross* Cross;
QPointer<QWidget> TargetWidget;
QRect TargetRect;
DockWidgetArea LastLocation = InvalidDockWidgetArea;
bool DropPreviewEnabled = true;
CDockOverlay::eMode Mode = CDockOverlay::ModeDockAreaOverlay;
QRect DropAreaRect;
/**
* Private data constructor
*/
DockOverlayPrivate(CDockOverlay* _public) : _this(_public) {}
};
/**
* Private data of CDockOverlayCross class
*/
struct DockOverlayCrossPrivate
{
CDockOverlayCross* _this;
CDockOverlay::eMode Mode = CDockOverlay::ModeDockAreaOverlay;
CDockOverlay* DockOverlay;
QHash<DockWidgetArea, QWidget*> DropIndicatorWidgets;
QGridLayout* GridLayout;
/**
* Private data constructor
*/
DockOverlayCrossPrivate(CDockOverlayCross* _public) : _this(_public) {}
/**
*
* @param area
* @return
*/
QPoint areaGridPosition(const DockWidgetArea area);
};
//============================================================================
CDockOverlay::CDockOverlay(QWidget* parent, eMode Mode) :
QFrame(parent),
d(new DockOverlayPrivate(this))
{
d->Mode = Mode;
d->Cross = new CDockOverlayCross(this);
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
setWindowOpacity(1);
setWindowTitle("DockOverlay");
setAttribute(Qt::WA_NoSystemBackground);
setAttribute(Qt::WA_TranslucentBackground);
d->Cross->setupOverlayCross(Mode);
d->Cross->setVisible(false);
setVisible(false);
}
//============================================================================
CDockOverlay::~CDockOverlay()
{
delete d;
}
//============================================================================
void CDockOverlay::setAllowedAreas(DockWidgetAreas areas)
{
if (areas == d->AllowedAreas)
return;
d->AllowedAreas = areas;
d->Cross->reset();
}
//============================================================================
DockWidgetAreas CDockOverlay::allowedAreas() const
{
return d->AllowedAreas;
}
//============================================================================
DockWidgetArea CDockOverlay::dropAreaUnderCursor() const
{
DockWidgetArea Result = d->Cross->cursorLocation();
if (Result != InvalidDockWidgetArea)
{
return Result;
}
CDockAreaWidget* DockArea = dynamic_cast<CDockAreaWidget*>(d->TargetWidget.data());
if (!DockArea)
{
return Result;
}
if (DockArea->titleAreaGeometry().contains(DockArea->mapFromGlobal(QCursor::pos())))
{
return CenterDockWidgetArea;
}
return Result;
}
//============================================================================
DockWidgetArea CDockOverlay::showOverlay(QWidget* target)
{
if (d->TargetWidget == target)
{
// Hint: We could update geometry of overlay here.
DockWidgetArea da = dropAreaUnderCursor();
if (da != d->LastLocation)
{
repaint();
d->LastLocation = da;
}
return da;
}
d->TargetWidget = target;
d->TargetRect = QRect();
d->LastLocation = InvalidDockWidgetArea;
// Move it over the target.
resize(target->size());
QPoint TopLeft = target->mapToGlobal(target->rect().topLeft());
move(TopLeft);
show();
d->Cross->updatePosition();
return dropAreaUnderCursor();
}
//============================================================================
void CDockOverlay::hideOverlay()
{
hide();
d->TargetWidget.clear();
d->TargetRect = QRect();
d->LastLocation = InvalidDockWidgetArea;
}
//============================================================================
void CDockOverlay::enableDropPreview(bool Enable)
{
d->DropPreviewEnabled = Enable;
update();
}
//============================================================================
void CDockOverlay::paintEvent(QPaintEvent* event)
{
Q_UNUSED(event);
// Draw rect based on location
if (!d->DropPreviewEnabled)
{
d->DropAreaRect = QRect();
return;
}
QRect r = rect();
const DockWidgetArea da = dropAreaUnderCursor();
double Factor = (CDockOverlay::ModeContainerOverlay == d->Mode) ?
3 : 2;
switch (da)
{
case TopDockWidgetArea: r.setHeight(r.height() / Factor); break;
case RightDockWidgetArea: r.setX(r.width() * (1 - 1 / Factor)); break;
case BottomDockWidgetArea: r.setY(r.height() * (1 - 1 / Factor)); break;
case LeftDockWidgetArea: r.setWidth(r.width() / Factor); break;
case CenterDockWidgetArea: r = rect();break;
default: return;
}
QPainter painter(this);
QColor Color = palette().color(QPalette::Active, QPalette::Highlight);
Color.setAlpha(64);
painter.setPen(Qt::NoPen);
painter.fillRect(r, Color);
d->DropAreaRect = r;
}
//============================================================================
QRect CDockOverlay::dropOverlayRect() const
{
return d->DropAreaRect;
}
//============================================================================
void CDockOverlay::showEvent(QShowEvent* e)
{
d->Cross->show();
QFrame::showEvent(e);
}
//============================================================================
void CDockOverlay::hideEvent(QHideEvent* e)
{
d->Cross->hide();
QFrame::hideEvent(e);
}
//============================================================================
static int areaAlignment(const DockWidgetArea area)
{
switch (area)
{
case TopDockWidgetArea: return (int) Qt::AlignHCenter | Qt::AlignBottom;
case RightDockWidgetArea: return (int) Qt::AlignLeft | Qt::AlignVCenter;
case BottomDockWidgetArea: return (int) Qt::AlignHCenter | Qt::AlignTop;
case LeftDockWidgetArea: return (int) Qt::AlignRight | Qt::AlignVCenter;
case CenterDockWidgetArea: return (int) Qt::AlignCenter;
default: return Qt::AlignCenter;
}
}
//============================================================================
// DockOverlayCrossPrivate
//============================================================================
QPoint DockOverlayCrossPrivate::areaGridPosition(const DockWidgetArea area)
{
if (CDockOverlay::ModeDockAreaOverlay == Mode)
{
switch (area)
{
case TopDockWidgetArea: return QPoint(1, 2);
case RightDockWidgetArea: return QPoint(2, 3);
case BottomDockWidgetArea: return QPoint(3, 2);
case LeftDockWidgetArea: return QPoint(2, 1);
case CenterDockWidgetArea: return QPoint(2, 2);
default: return QPoint();
}
}
else
{
switch (area)
{
case TopDockWidgetArea: return QPoint(0, 2);
case RightDockWidgetArea: return QPoint(2, 4);
case BottomDockWidgetArea: return QPoint(4, 2);
case LeftDockWidgetArea: return QPoint(2, 0);
case CenterDockWidgetArea: return QPoint(2, 2);
default: return QPoint();
}
}
}
//============================================================================
CDockOverlayCross::CDockOverlayCross(CDockOverlay* overlay) :
QWidget(overlay->parentWidget()),
d(new DockOverlayCrossPrivate(this))
{
d->DockOverlay = overlay;
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
setWindowTitle("DockOverlayCross");
setAttribute(Qt::WA_TranslucentBackground);
d->GridLayout = new QGridLayout();
d->GridLayout->setSpacing(0);
setLayout(d->GridLayout);
}
//============================================================================
CDockOverlayCross::~CDockOverlayCross()
{
delete d;
}
//============================================================================
void CDockOverlayCross::setupOverlayCross(CDockOverlay::eMode Mode)
{
d->Mode = Mode;
QHash<DockWidgetArea, QWidget*> areaWidgets;
areaWidgets.insert(TopDockWidgetArea, createDropIndicatorWidget(TopDockWidgetArea, Mode));
areaWidgets.insert(RightDockWidgetArea, createDropIndicatorWidget(RightDockWidgetArea, Mode));
areaWidgets.insert(BottomDockWidgetArea, createDropIndicatorWidget(BottomDockWidgetArea, Mode));
areaWidgets.insert(LeftDockWidgetArea, createDropIndicatorWidget(LeftDockWidgetArea, Mode));
areaWidgets.insert(CenterDockWidgetArea, createDropIndicatorWidget(CenterDockWidgetArea, Mode));
setAreaWidgets(areaWidgets);
}
//============================================================================
void CDockOverlayCross::setAreaWidgets(const QHash<DockWidgetArea, QWidget*>& widgets)
{
// Delete old widgets.
QMutableHashIterator<DockWidgetArea, QWidget*> i(d->DropIndicatorWidgets);
while (i.hasNext())
{
i.next();
QWidget* widget = i.value();
d->GridLayout->removeWidget(widget);
delete widget;
i.remove();
}
// Insert new widgets into grid.
d->DropIndicatorWidgets = widgets;
QHashIterator<DockWidgetArea, QWidget*> i2(d->DropIndicatorWidgets);
while (i2.hasNext())
{
i2.next();
const DockWidgetArea area = i2.key();
QWidget* widget = i2.value();
QPoint p = d->areaGridPosition(area);
d->GridLayout->addWidget(widget, p.x(), p.y(), (Qt::Alignment) areaAlignment(area));
}
if (CDockOverlay::ModeDockAreaOverlay == d->Mode)
{
d->GridLayout->setContentsMargins(0, 0, 0, 0);
d->GridLayout->setRowStretch(0, 1);
d->GridLayout->setRowStretch(1, 0);
d->GridLayout->setRowStretch(2, 0);
d->GridLayout->setRowStretch(3, 0);
d->GridLayout->setRowStretch(4, 1);
d->GridLayout->setColumnStretch(0, 1);
d->GridLayout->setColumnStretch(1, 0);
d->GridLayout->setColumnStretch(2, 0);
d->GridLayout->setColumnStretch(3, 0);
d->GridLayout->setColumnStretch(4, 1);
}
else
{
d->GridLayout->setContentsMargins(4, 4, 4, 4);
d->GridLayout->setRowStretch(0, 0);
d->GridLayout->setRowStretch(1, 1);
d->GridLayout->setRowStretch(2, 1);
d->GridLayout->setRowStretch(3, 1);
d->GridLayout->setRowStretch(4, 0);
d->GridLayout->setColumnStretch(0, 0);
d->GridLayout->setColumnStretch(1, 1);
d->GridLayout->setColumnStretch(2, 1);
d->GridLayout->setColumnStretch(3, 1);
d->GridLayout->setColumnStretch(4, 0);
}
reset();
}
//============================================================================
DockWidgetArea CDockOverlayCross::cursorLocation() const
{
const QPoint pos = mapFromGlobal(QCursor::pos());
QHashIterator<DockWidgetArea, QWidget*> i(d->DropIndicatorWidgets);
while (i.hasNext())
{
i.next();
if (d->DockOverlay->allowedAreas().testFlag(i.key())
&& i.value()
&& i.value()->isVisible()
&& i.value()->geometry().contains(pos))
{
return i.key();
}
}
return InvalidDockWidgetArea;
}
//============================================================================
void CDockOverlayCross::showEvent(QShowEvent*)
{
this->updatePosition();
}
//============================================================================
void CDockOverlayCross::updatePosition()
{
resize(d->DockOverlay->size());
QPoint TopLeft = d->DockOverlay->pos();
QPoint Offest((this->width() - d->DockOverlay->width()) / 2,
(this->height() - d->DockOverlay->height()) / 2);
QPoint CrossTopLeft = TopLeft - Offest;
move(CrossTopLeft);
}
//============================================================================
void CDockOverlayCross::reset()
{
QList<DockWidgetArea> allAreas;
allAreas << TopDockWidgetArea << RightDockWidgetArea
<< BottomDockWidgetArea << LeftDockWidgetArea << CenterDockWidgetArea;
const DockWidgetAreas allowedAreas = d->DockOverlay->allowedAreas();
// Update visibility of area widgets based on allowedAreas.
for (int i = 0; i < allAreas.count(); ++i)
{
QPoint p = d->areaGridPosition(allAreas.at(i));
QLayoutItem* item = d->GridLayout->itemAtPosition(p.x(), p.y());
QWidget* w = nullptr;
if (item && (w = item->widget()) != nullptr)
{
w->setVisible(allowedAreas.testFlag(allAreas.at(i)));
}
}
}
} // namespace ads
//----------------------------------------------------------------------------

166
src/DockOverlay.h Normal file
View File

@@ -0,0 +1,166 @@
#ifndef DockOverlayH
#define DockOverlayH
/*******************************************************************************
** Qt Advanced Docking System
** Copyright (C) 2017 Uwe Kindler
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation; either
** version 2.1 of the License, or (at your option) any later version.
**
** This library is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public
** License along with this library; If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
//============================================================================
// INCLUDES
//============================================================================
#include <QPointer>
#include <QHash>
#include <QRect>
#include <QFrame>
class QGridLayout;
#include "ads_globals.h"
namespace ads
{
struct DockOverlayPrivate;
class CDockOverlayCross;
/*!
* DockOverlay paints a translucent rectangle over another widget. The geometry
* of the rectangle is based on the mouse location.
*/
class CDockOverlay : public QFrame
{
Q_OBJECT
private:
DockOverlayPrivate* d; //< private data class
friend class DockOverlayPrivate;
friend class DockOverlayCross;
public:
enum eMode
{
ModeDockAreaOverlay,
ModeContainerOverlay
};
/**
* Creates a dock overlay
*/
CDockOverlay(QWidget* parent, eMode Mode = ModeDockAreaOverlay);
/**
* Virtual destructor
*/
virtual ~CDockOverlay();
/**
* Configures the areas that are allowed for docking
*/
void setAllowedAreas(DockWidgetAreas areas);
/**
* Returns flags with all allowed drop areas
*/
DockWidgetAreas allowedAreas() const;
/**
* Returns the drop area under the current cursor location
*/
DockWidgetArea dropAreaUnderCursor() const;
/**
* Show the drop overly for the given target widget
*/
DockWidgetArea showOverlay(QWidget* target);
/**
* Hides the overlay
*/
void hideOverlay();
/**
* Enables / disables the semi transparent overlay rectangle that represents
* the future area of the dropped widget
*/
void enableDropPreview(bool Enable);
/**
* The drop overlay rectangle for the target area
*/
QRect dropOverlayRect() const;
protected:
virtual void paintEvent(QPaintEvent *e) override;
virtual void showEvent(QShowEvent* e) override;
virtual void hideEvent(QHideEvent* e) override;
};
struct DockOverlayCrossPrivate;
/*!
* DockOverlayCross shows a cross with 5 different drop area possibilities.
* I could have handled everything inside DockOverlay, but because of some
* styling issues it's better to have a separate class for the cross.
*/
class CDockOverlayCross : public QWidget
{
Q_OBJECT
private:
DockOverlayCrossPrivate* d;
friend class DockOverlayCrossPrivate;
friend class CDockOverlay;
public:
/**
* Creates an overlay corss for the given overlay
*/
CDockOverlayCross(CDockOverlay* overlay);
/**
* Virtual destructor
*/
virtual ~CDockOverlayCross();
/**
* Returns the dock widget area depending on the current cursor location.
* The function checks, if the mouse cursor is inside of any drop indicator
* widget and returns the corresponding DockWidgetArea.
*/
DockWidgetArea cursorLocation() const;
/**
* Sets up the overlay cross for the given overlay mode
*/
void setupOverlayCross(CDockOverlay::eMode Mode);
/**
* Resets and updates the
*/
void reset();
/**
* Updates the current position
*/
void updatePosition();
protected:
virtual void showEvent(QShowEvent* e) override;
void setAreaWidgets(const QHash<DockWidgetArea, QWidget*>& widgets);
private:
}; // CDockOverlayCross
} // namespace ads
#endif // DockOverlayH

93
src/DockSplitter.cpp Normal file
View File

@@ -0,0 +1,93 @@
/*******************************************************************************
** Qt Advanced Docking System
** Copyright (C) 2017 Uwe Kindler
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation; either
** version 2.1 of the License, or (at your option) any later version.
**
** This library is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public
** License along with this library; If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
//============================================================================
/// \file DockSplitter.cpp
/// \author Uwe Kindler
/// \date 24.03.2017
/// \brief Implementation of CDockSplitter
//============================================================================
//============================================================================
// INCLUDES
//============================================================================
#include "DockSplitter.h"
#include <QDebug>
#include <QChildEvent>
#include "DockAreaWidget.h"
namespace ads
{
/**
* Private dock splitter data
*/
struct DockSplitterPrivate
{
CDockSplitter* _this;
int VisibleContentCount = 0;
DockSplitterPrivate(CDockSplitter* _public) : _this(_public) {}
};
//============================================================================
CDockSplitter::CDockSplitter(QWidget *parent)
: QSplitter(parent),
d(new DockSplitterPrivate(this))
{
}
//============================================================================
CDockSplitter::CDockSplitter(Qt::Orientation orientation, QWidget *parent)
: QSplitter(orientation, parent),
d(new DockSplitterPrivate(this))
{
}
//============================================================================
CDockSplitter::~CDockSplitter()
{
qDebug() << "~CDockSplitter";
delete d;
}
//============================================================================
bool CDockSplitter::hasVisibleContent() const
{
// TODO Cache or precalculate this to speed up
for (int i = 0; i < count(); ++i)
{
if (widget(i)->isVisibleTo(this))
{
return true;
}
}
return false;
}
} // namespace ads
//---------------------------------------------------------------------------
// EOF DockSplitter.cpp

66
src/DockSplitter.h Normal file
View File

@@ -0,0 +1,66 @@
#ifndef DockSplitterH
#define DockSplitterH
/*******************************************************************************
** Qt Advanced Docking System
** Copyright (C) 2017 Uwe Kindler
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation; either
** version 2.1 of the License, or (at your option) any later version.
**
** This library is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public
** License along with this library; If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
//============================================================================
/// \file DockSplitter.h
/// \author Uwe Kindler
/// \date 24.03.2017
/// \brief Declaration of CDockSplitter
//============================================================================
//============================================================================
// INCLUDES
//============================================================================
#include <QSplitter>
namespace ads
{
struct DockSplitterPrivate;
/**
* Splitter used internally instead of QSplitter
*/
class CDockSplitter : public QSplitter
{
Q_OBJECT
private:
DockSplitterPrivate* d;
friend class DockSplitterPrivate;
public:
CDockSplitter(QWidget *parent = Q_NULLPTR);
CDockSplitter(Qt::Orientation orientation, QWidget *parent = Q_NULLPTR);
/**
* Prints debug info
*/
virtual ~CDockSplitter();
/**
* Returns true, if any of the internal widgets is visible
*/
bool hasVisibleContent() const;
}; // class CDockSplitter
} // namespace ads
//---------------------------------------------------------------------------
#endif // DockSplitterH

View File

@@ -0,0 +1,32 @@
/*******************************************************************************
** Qt Advanced Docking System
** Copyright (C) 2017 Uwe Kindler
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation; either
** version 2.1 of the License, or (at your option) any later version.
**
** This library is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public
** License along with this library; If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
//============================================================================
/// \file DockStateSerialization.cpp
/// \author Uwe Kindler
/// \date 26.02.2017
/// \brief Serialization related data, constants and stuff
//============================================================================
//============================================================================
// INCLUDES
//============================================================================
#include "DockStateSerialization.h"

View File

@@ -0,0 +1,55 @@
#ifndef DockStateSerializationH
#define DockStateSerializationH
/*******************************************************************************
** Qt Advanced Docking System
** Copyright (C) 2017 Uwe Kindler
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation; either
** version 2.1 of the License, or (at your option) any later version.
**
** This library is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public
** License along with this library; If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
//============================================================================
/// \file DockStateSerialization.h
/// \author Uwe Kindler
/// \date 26.02.2017
/// \brief Declaration of serialization related data, constants and stuff
//============================================================================
//============================================================================
// INCLUDES
//============================================================================
namespace ads
{
namespace internal
{
// sentinel values used to validate state data
enum VersionMarkers
{
VersionMarker = 0xff,
ContainerMarker = 0xfe,
SplitterMarker = 0xfd,
DockAreaMarker = 0xfc,
DockWidgetMarker = 0xfb
};
static const bool RestoreTesting = true;
static const bool Restore = false;
} // internal
} // namespace ads
//-----------------------------------------------------------------------------
#endif // DockManagerH

396
src/DockWidget.cpp Normal file
View File

@@ -0,0 +1,396 @@
/*******************************************************************************
** Qt Advanced Docking System
** Copyright (C) 2017 Uwe Kindler
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation; either
** version 2.1 of the License, or (at your option) any later version.
**
** This library is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public
** License along with this library; If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
//============================================================================
/// \file DockWidget.cpp
/// \author Uwe Kindler
/// \date 26.02.2017
/// \brief Implementation of CDockWidget class
//============================================================================
//============================================================================
// INCLUDES
//============================================================================
#include "DockWidget.h"
#include <QBoxLayout>
#include <QAction>
#include <QSplitter>
#include <QStack>
#include <QTextStream>
#include <QPointer>
#include <QEvent>
#include <QDebug>
#include "DockWidgetTitleBar.h"
#include "DockContainerWidget.h"
#include "DockAreaWidget.h"
#include "DockManager.h"
#include "FloatingDockContainer.h"
#include "DockStateSerialization.h"
#include "DockSplitter.h"
#include "ads_globals.h"
namespace ads
{
/**
* Private data class of CDockWidget class (pimpl)
*/
struct DockWidgetPrivate
{
CDockWidget* _this;
QBoxLayout* Layout;
QWidget* Widget = nullptr;
CDockWidgetTitleBar* TitleWidget;
CDockWidget::DockWidgetFeatures Features = CDockWidget::AllDockWidgetFeatures;
CDockManager* DockManager = nullptr;
CDockAreaWidget* DockArea = nullptr;
QAction* ToggleViewAction;
bool Closed = false;
/**
* Private data constructor
*/
DockWidgetPrivate(CDockWidget* _public);
/**
* Show dock widget
*/
void showDockWidget();
/**
* Hide dock widget.
*/
void hideDockWidget();
/**
* Hides a parent splitter if all dock widgets in the splitter are closed
*/
void hideEmptyParentSplitters();
/**
* Hides a dock area if all dock widgets in the area are closed
*/
void hideEmptyParentDockArea();
/**
* Hides a floating widget if all dock areas are empty - that means,
* if all dock widgets in all dock areas are closed
*/
void hideEmptyFloatingWidget();
};
// struct DockWidgetPrivate
//============================================================================
DockWidgetPrivate::DockWidgetPrivate(CDockWidget* _public) :
_this(_public)
{
}
//============================================================================
void DockWidgetPrivate::showDockWidget()
{
if (!DockArea)
{
CFloatingDockContainer* FloatingWidget = new CFloatingDockContainer(_this);
FloatingWidget->resize(_this->size());
FloatingWidget->show();
}
else
{
DockArea->show();
DockArea->setCurrentIndex(DockArea->tabIndex(_this));
QSplitter* Splitter = internal::findParent<QSplitter*>(_this);
while (Splitter && !Splitter->isVisible())
{
Splitter->show();
Splitter = internal::findParent<QSplitter*>(Splitter);
}
CDockContainerWidget* Container = DockArea->dockContainer();
if (Container->isFloating())
{
CFloatingDockContainer* FloatingWidget = internal::findParent<
CFloatingDockContainer*>(Container);
FloatingWidget->show();
}
}
}
//============================================================================
void DockWidgetPrivate::hideDockWidget()
{
TitleWidget->hide();
hideEmptyParentDockArea();
hideEmptyParentSplitters();
hideEmptyFloatingWidget();
}
//============================================================================
void DockWidgetPrivate::hideEmptyParentSplitters()
{
auto Splitter = internal::findParent<CDockSplitter*>(_this);
while (Splitter && Splitter->isVisible())
{
if (!Splitter->hasVisibleContent())
{
Splitter->hide();
}
Splitter = internal::findParent<CDockSplitter*>(Splitter);
}
}
//============================================================================
void DockWidgetPrivate::hideEmptyParentDockArea()
{
auto OpenDockWidgets = DockArea->openedDockWidgets();
if (OpenDockWidgets.count() > 1)
{
CDockWidget* NextDockWidget;
if (OpenDockWidgets.last() == _this)
{
NextDockWidget = OpenDockWidgets[OpenDockWidgets.count() - 2];
}
else
{
int NextIndex = OpenDockWidgets.indexOf(_this) + 1;
NextDockWidget = OpenDockWidgets[NextIndex];
}
DockArea->setCurrentDockWidget(NextDockWidget);
}
else
{
DockArea->hide();
}
}
//============================================================================
void DockWidgetPrivate::hideEmptyFloatingWidget()
{
CDockContainerWidget* Container = _this->dockContainer();
if (Container->isFloating() && Container->openedDockAreas().isEmpty())
{
CFloatingDockContainer* FloatingWidget = internal::findParent<CFloatingDockContainer*>(Container);
FloatingWidget->hide();
}
}
//============================================================================
CDockWidget::CDockWidget(const QString &title, QWidget *parent) :
QFrame(parent),
d(new DockWidgetPrivate(this))
{
d->Layout = new QBoxLayout(QBoxLayout::TopToBottom);
d->Layout->setContentsMargins(0, 0, 0, 0);
d->Layout->setSpacing(0);
setLayout(d->Layout);
setWindowTitle(title);
d->TitleWidget = new CDockWidgetTitleBar(this);
d->ToggleViewAction = new QAction(title);
d->ToggleViewAction->setCheckable(true);
connect(d->ToggleViewAction, SIGNAL(triggered(bool)), this,
SLOT(toggleView(bool)));
}
//============================================================================
CDockWidget::~CDockWidget()
{
qDebug() << "~CDockWidget()";
delete d;
}
//============================================================================
void CDockWidget::setToggleViewActionChecked(bool Checked)
{
QAction* Action = d->ToggleViewAction;
Action->blockSignals(true);
Action->setChecked(Checked);
Action->blockSignals(false);
}
//============================================================================
void CDockWidget::setWidget(QWidget* widget)
{
if (d->Widget)
{
d->Layout->replaceWidget(d->Widget, widget);
}
else
{
d->Layout->addWidget(widget);
}
d->Widget = widget;
}
//============================================================================
QWidget* CDockWidget::widget() const
{
return d->Widget;
}
//============================================================================
CDockWidgetTitleBar* CDockWidget::titleBar() const
{
return d->TitleWidget;
}
//============================================================================
void CDockWidget::setFeatures(DockWidgetFeatures features)
{
d->Features = features;
}
//============================================================================
CDockWidget::DockWidgetFeatures CDockWidget::features() const
{
return d->Features;
}
//============================================================================
CDockManager* CDockWidget::dockManager() const
{
return d->DockManager;
}
//============================================================================
void CDockWidget::setDockManager(CDockManager* DockManager)
{
d->DockManager = DockManager;
}
//============================================================================
CDockContainerWidget* CDockWidget::dockContainer() const
{
return internal::findParent<CDockContainerWidget*>(this);
}
//============================================================================
CDockAreaWidget* CDockWidget::dockAreaWidget() const
{
return internal::findParent<CDockAreaWidget*>(this);
}
//============================================================================
bool CDockWidget::isFloating() const
{
return dockContainer() ? dockContainer()->isFloating() : false;
}
//============================================================================
bool CDockWidget::isClosed() const
{
return d->Closed;
}
//============================================================================
QAction* CDockWidget::toggleViewAction() const
{
return d->ToggleViewAction;
}
//============================================================================
void CDockWidget::toggleView(bool Open)
{
if (Open)
{
d->showDockWidget();
}
else
{
d->hideDockWidget();
}
d->Closed = !Open;
d->ToggleViewAction->blockSignals(true);
d->ToggleViewAction->setChecked(Open);
d->ToggleViewAction->blockSignals(false);
if (!Open)
{
emit closed();
}
emit viewToggled(Open);
}
//============================================================================
void CDockWidget::setDockArea(CDockAreaWidget* DockArea)
{
d->DockArea = DockArea;
d->ToggleViewAction->setChecked(DockArea != nullptr);
}
//============================================================================
void CDockWidget::saveState(QDataStream& stream) const
{
stream << internal::DockWidgetMarker;
qDebug() << "CDockWidget::saveState " << objectName() << " closed " << d->Closed;
stream << objectName() << d->Closed;
}
//============================================================================
void CDockWidget::flagAsUnassigned()
{
setParent(d->DockManager);
setDockArea(nullptr);
titleBar()->setParent(this);
}
//============================================================================
bool CDockWidget::event(QEvent *e)
{
if (e->type() == QEvent::WindowTitleChange)
{
emit titleChanged(windowTitle());
}
return QFrame::event(e);
}
} // namespace ads
//---------------------------------------------------------------------------
// EOF DockWidget.cpp

221
src/DockWidget.h Normal file
View File

@@ -0,0 +1,221 @@
#ifndef DockWidgetH
#define DockWidgetH
/*******************************************************************************
** Qt Advanced Docking System
** Copyright (C) 2017 Uwe Kindler
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation; either
** version 2.1 of the License, or (at your option) any later version.
**
** This library is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public
** License along with this library; If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
//============================================================================
/// \file DockWidget.h
/// \author Uwe Kindler
/// \date 26.02.2017
/// \brief Declaration of CDockWidget class
//============================================================================
//============================================================================
// INCLUDES
//============================================================================
#include <QFrame>
namespace ads
{
struct DockWidgetPrivate;
class CDockWidgetTitleBar;
class CDockManager;
class CDockContainerWidget;
class CDockAreaWidget;
struct DockContainerWidgetPrivate;
/**
* The QDockWidget class provides a widget that can be docked inside a
* CDockManager or floated as a top-level window on the desktop.
*/
class CDockWidget : public QFrame
{
Q_OBJECT
private:
DockWidgetPrivate* d; ///< private data (pimpl)
friend class DockWidgetPrivate;
protected:
friend class CDockContainerWidget;
friend class CDockAreaWidget;
friend class CFloatingDockContainer;
friend class CDockManager;
friend class DockContainerWidgetPrivate;
/**
* Assigns the dock manager that manages this dock widget
*/
void setDockManager(CDockManager* DockManager);
/**
* If this dock widget is inserted into a dock area, the dock area will
* be registered on this widget via this function. If a dock widget is
* removed from a dock area, this function will be called with nullptr
* value.
*/
void setDockArea(CDockAreaWidget* DockArea);
/**
* This function changes the toggle view action without emitting any
* signal
*/
void setToggleViewActionChecked(bool Checked);
/**
* Saves the state into the given stream
*/
void saveState(QDataStream& Stream) const;
/**
* This is a helper function for the dock manager to flag this widget
* as unassigned.
* When calling the restore function, it may happen, that the saved state
* contains less dock widgets then currently available. All widgets whose
* data is not contained in the saved state, are flagged as unassigned
* after the restore process. If the user shows an unassigned dock widget,
* a floating widget will be created to take up the dock widget.
*/
void flagAsUnassigned();
public:
enum DockWidgetFeature
{
DockWidgetClosable = 0x01,
DockWidgetMovable = 0x02,
DockWidgetFloatable = 0x04,
AllDockWidgetFeatures = DockWidgetClosable | DockWidgetMovable | DockWidgetFloatable,
NoDockWidgetFeatures = 0x00
};
Q_DECLARE_FLAGS(DockWidgetFeatures, DockWidgetFeature)
enum eState
{
StateHidden,
StateDocked,
StateFloating
};
/**
* Default Constructor
*/
CDockWidget(const QString &title, QWidget* parent = 0);
/**
* Virtual Destructor
*/
virtual ~CDockWidget();
/**
* Sets the widget for the dock widget to widget.
*/
void setWidget(QWidget* widget);
/**
* Returns the widget for the dock widget. This function returns zero if
* the widget has not been set.
*/
QWidget* widget() const;
/**
* Returns the title bar widget of this dock widget
*/
CDockWidgetTitleBar* titleBar() const;
/**
* Sets, whether the dock widget is movable, closable, and floatable.
*/
void setFeatures(DockWidgetFeatures features);
/**
* This property holds whether the dock widget is movable, closable, and
* floatable.
* By default, this property is set to a combination of DockWidgetClosable,
* DockWidgetMovable and DockWidgetFloatable.
*/
DockWidgetFeatures features() const;
/**
* Returns the dock manager that manages the dock widget or 0 if the widget
* has not been assigned to any dock manager yet
*/
CDockManager* dockManager() const;
/**
* Returns the dock container widget this dock area widget belongs to or 0
* if this dock widget has nt been docked yet
*/
CDockContainerWidget* dockContainer() const;
/**
* Returns the dock area widget this dock widget belongs to or 0
* if this dock widget has not been docked yet
*/
CDockAreaWidget* dockAreaWidget() const;
/**
* This property holds whether the dock widget is floating.
*/
bool isFloating() const;
/**
* Returns true, if this dock widget is closed.
*/
bool isClosed() const;
/**
* Returns a checkable action that can be used to show or close this dock widget.
* The action's text is set to the dock widget's window title.
*/
QAction* toggleViewAction() const;
/**
* Emits titleChanged signal if title change event occures
*/
virtual bool event(QEvent *e) override;
public slots:
/**
* This property controls whether the dock widget is open or closed.
* The toogleViewAction triggers this slot
*/
void toggleView(bool Open);
signals:
/**
* This signal is emitted if the dock widget is opened or closed
*/
void viewToggled(bool Open);
/**
* This signal is emitted if the dock widget is closed
*/
void closed();
/**
* This signal is emitted if the window title of this dock widget
* changed
*/
void titleChanged(const QString& Title);
}; // class DockWidget
}
// namespace ads
//-----------------------------------------------------------------------------
#endif // DockWidgetH

356
src/DockWidgetTitleBar.cpp Normal file
View File

@@ -0,0 +1,356 @@
/*******************************************************************************
** Qt Advanced Docking System
** Copyright (C) 2017 Uwe Kindler
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation; either
** version 2.1 of the License, or (at your option) any later version.
**
** This library is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public
** License along with this library; If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
//============================================================================
/// \file DockWidgetTitleBar.cpp
/// \author Uwe Kindler
/// \date 27.02.2017
/// \brief Implementation of CDockWidgetTitleBar class
//============================================================================
//============================================================================
// INCLUDES
//============================================================================
#include "DockWidgetTitleBar.h"
#include <QBoxLayout>
#include <QLabel>
#include <QMouseEvent>
#include <QStyle>
#include <QApplication>
#include <QSplitter>
#include <QDebug>
#include "ads_globals.h"
#include "DockWidget.h"
#include "DockAreaWidget.h"
#include "FloatingDockContainer.h"
#include "DockOverlay.h"
#include "DockManager.h"
namespace ads
{
/**
* The different dragging states
*/
enum eDragState
{
DraggingInactive, //!< DraggingInactive
DraggingMousePressed, //!< DraggingMousePressed
DraggingTab, //!< DraggingTab
DraggingFloatingWidget//!< DraggingFloatingWidget
};
/**
* Private data class of CDockWidgetTitleBar class (pimpl)
*/
struct DockWidgetTitleBarPrivate
{
CDockWidgetTitleBar* _this;
CDockWidget* DockWidget;
QLabel* IconLabel;
QLabel* TitleLabel;
QPoint DragStartMousePosition;
bool IsActiveTab = false;
CDockAreaWidget* DockArea = nullptr;
eDragState DragState = DraggingInactive;
CFloatingDockContainer* FloatingWidget = nullptr;
/**
* Private data constructor
*/
DockWidgetTitleBarPrivate(CDockWidgetTitleBar* _public);
/**
* Creates the complete layout including all controls
*/
void createLayout();
/**
* Moves the tab depending on the position in the given mouse event
*/
void moveTab(QMouseEvent* ev);
/**
* Test function for current drag state
*/
bool isDraggingState(eDragState dragState)
{
return this->DragState == dragState;
}
/**
* Returns true if the given global point is inside the title area geometry
* rectangle.
* The position is given as global position.
*/
bool titleAreaGeometryContains(const QPoint& GlobalPos) const
{
return DockArea->titleAreaGeometry().contains(DockArea->mapFromGlobal(GlobalPos));
}
/**
* Starts floating of the dock widget that belongs to this title bar
* Returns true, if floating has been started and false if floating
* is not possible for any reason
*/
bool startFloating();
};
// struct DockWidgetTitleBarPrivate
//============================================================================
DockWidgetTitleBarPrivate::DockWidgetTitleBarPrivate(CDockWidgetTitleBar* _public) :
_this(_public)
{
}
//============================================================================
void DockWidgetTitleBarPrivate::createLayout()
{
QBoxLayout* l = new QBoxLayout(QBoxLayout::LeftToRight);
l->setContentsMargins(0, 0, 0, 0);
_this->setLayout(l);
IconLabel = new QLabel();
IconLabel->setAlignment(Qt::AlignVCenter);
l->addWidget(IconLabel, Qt::AlignVCenter);
TitleLabel = new QLabel();
l->addWidget(TitleLabel, 1);
IconLabel->setVisible(false);
TitleLabel->setVisible(true);
TitleLabel->setText(DockWidget->windowTitle());
}
//============================================================================
void DockWidgetTitleBarPrivate::moveTab(QMouseEvent* ev)
{
ev->accept();
int left, top, right, bottom;
_this->getContentsMargins(&left, &top, &right, &bottom);
QPoint moveToPos = _this->mapToParent(ev->pos()) - DragStartMousePosition;
moveToPos.setY(0);
_this->move(moveToPos);
_this->raise();
}
//============================================================================
bool DockWidgetTitleBarPrivate::startFloating()
{
qDebug() << "isFloating " << DockWidget->dockContainer()->isFloating();
qDebug() << "areaCount " << DockWidget->dockContainer()->dockAreaCount();
qDebug() << "widgetCount " << DockWidget->dockAreaWidget()->count();
// if this is the last dock widget inside of this floating widget,
// then it does not make any sense, to make if floating because
// it is already floating
if (DockWidget->dockContainer()->isFloating()
&& (DockWidget->dockContainer()->visibleDockAreaCount() == 1)
&& (DockWidget->dockAreaWidget()->count() == 1))
{
return false;
}
qDebug() << "startFloating";
DragState = DraggingFloatingWidget;
QSize Size = DockArea->size();
CFloatingDockContainer* FloatingWidget = nullptr;
if (DockArea->count() > 1)
{
// If section widget has multiple tabs, we take only one tab
FloatingWidget = new CFloatingDockContainer(DockWidget);
}
else
{
qDebug() << "DockWidgetTitleBarPrivate::startFloating DockArea";
// If section widget has only one content widget, we can move the complete
// dock area into floating widget
FloatingWidget = new CFloatingDockContainer(DockArea);
}
FloatingWidget->startFloating(DragStartMousePosition, Size);
auto Overlay = DockWidget->dockManager()->containerOverlay();
Overlay->setAllowedAreas(OuterDockAreas);
this->FloatingWidget = FloatingWidget;
return true;
}
//============================================================================
CDockWidgetTitleBar::CDockWidgetTitleBar(CDockWidget* DockWidget, QWidget *parent) :
QFrame(parent),
d(new DockWidgetTitleBarPrivate(this))
{
setAttribute(Qt::WA_NoMousePropagation, true);
d->DockWidget = DockWidget;
d->createLayout();
}
//============================================================================
CDockWidgetTitleBar::~CDockWidgetTitleBar()
{
qDebug() << "~CDockWidgetTitleBar()";
delete d;
}
//============================================================================
void CDockWidgetTitleBar::mousePressEvent(QMouseEvent* ev)
{
if (ev->button() == Qt::LeftButton)
{
qDebug() << "CDockWidgetTitleBar::mousePressEvent";
ev->accept();
d->DragStartMousePosition = ev->pos();
d->DragState = DraggingMousePressed;
return;
}
QFrame::mousePressEvent(ev);
}
//============================================================================
void CDockWidgetTitleBar::mouseReleaseEvent(QMouseEvent* ev)
{
qDebug() << "CDockWidgetTitleBar::mouseReleaseEvent";
// End of tab moving, change order now
if (d->isDraggingState(DraggingTab) && d->DockArea)
{
// Find tab under mouse
QPoint pos = d->DockArea->mapFromGlobal(ev->globalPos());
int fromIndex = d->DockArea->tabIndex(d->DockWidget);
int toIndex = d->DockArea->indexOfContentByTitlePos(pos, this);
if (-1 == toIndex)
{
toIndex = d->DockArea->count() - 1;
}
qDebug() << "Move tab from " << fromIndex << " to " << toIndex;
d->DockArea->reorderDockWidget(fromIndex, toIndex);
}
if (!d->DragStartMousePosition.isNull())
{
emit clicked();
}
d->DragStartMousePosition = QPoint();
d->DragState = DraggingInactive;
QFrame::mouseReleaseEvent(ev);
}
//============================================================================
void CDockWidgetTitleBar::mouseMoveEvent(QMouseEvent* ev)
{
/*std::cout << "CDockWidgetTitleBar::mouseMoveEventmouseMoveEvent DragState "
<< d->DragState << std::endl;*/
if (!(ev->buttons() & Qt::LeftButton) || d->isDraggingState(DraggingInactive))
{
d->DragState = DraggingInactive;
QFrame::mouseMoveEvent(ev);
return;
}
if (d->isDraggingState(DraggingFloatingWidget))
{
//std::cout << "DraggingFloatingWidget" << std::endl;
d->FloatingWidget->moveFloating();
QFrame::mouseMoveEvent(ev);
return;
}
// move tab
if (d->isDraggingState(DraggingTab))
{
d->moveTab(ev);
}
bool MouseInsideTitleArea = d->titleAreaGeometryContains(ev->globalPos());
if (!MouseInsideTitleArea)
{
d->startFloating();
return;
}
else if (d->DockArea->count() > 1
&& (ev->pos() - d->DragStartMousePosition).manhattanLength() >= QApplication::startDragDistance()) // Wait a few pixels before start moving
{
d->DragState = DraggingTab;
return;
}
QFrame::mouseMoveEvent(ev);
}
//============================================================================
bool CDockWidgetTitleBar::isActiveTab() const
{
return d->IsActiveTab;
}
//============================================================================
void CDockWidgetTitleBar::setActiveTab(bool active)
{
if (d->IsActiveTab == active)
{
return;
}
d->IsActiveTab = active;
style()->unpolish(this);
style()->polish(this);
d->TitleLabel->style()->unpolish(d->TitleLabel);
d->TitleLabel->style()->polish(d->TitleLabel);
update();
emit activeTabChanged();
}
//============================================================================
CDockWidget* CDockWidgetTitleBar::dockWidget() const
{
return d->DockWidget;
}
//============================================================================
void CDockWidgetTitleBar::setDockAreaWidget(CDockAreaWidget* DockArea)
{
d->DockArea = DockArea;
}
//============================================================================
CDockAreaWidget* CDockWidgetTitleBar::dockAreaWidget() const
{
return d->DockArea;
}
} // namespace ads
//---------------------------------------------------------------------------
// EOF DockWidgetTitleBar.cpp

106
src/DockWidgetTitleBar.h Normal file
View File

@@ -0,0 +1,106 @@
#ifndef DockWidgetTitleBarH
#define DockWidgetTitleBarH
/*******************************************************************************
** Qt Advanced Docking System
** Copyright (C) 2017 Uwe Kindler
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation; either
** version 2.1 of the License, or (at your option) any later version.
**
** This library is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public
** License along with this library; If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
//============================================================================
/// \file DockWidgetTitleBar.h
/// \author Uwe Kindler
/// \date 27.02.2017
/// \brief Declaration of CDockWidgetTitleBar class
//============================================================================
//============================================================================
// INCLUDES
//============================================================================
#include <QFrame>
namespace ads
{
class CDockWidget;
class CDockAreaWidget;
struct DockWidgetTitleBarPrivate;
/**
* A dock widget title bar that shows a title and an icon
*/
class CDockWidgetTitleBar : public QFrame
{
Q_OBJECT
Q_PROPERTY(bool activeTab READ isActiveTab WRITE setActiveTab NOTIFY activeTabChanged)
private:
DockWidgetTitleBarPrivate* d; ///< private data (pimpl)
friend class DockWidgetTitleBarPrivate;
protected:
virtual void mousePressEvent(QMouseEvent* ev) override;
virtual void mouseReleaseEvent(QMouseEvent* ev) override;
virtual void mouseMoveEvent(QMouseEvent* ev) override;
public:
/**
* Default Constructor
* param[in] DockWidget The dock widget this title bar belongs to
* param[in] parent The parent widget of this title bar
*/
CDockWidgetTitleBar(CDockWidget* DockWidget, QWidget* parent = 0);
/**
* Virtual Destructor
*/
virtual ~CDockWidgetTitleBar();
/**
* Returns true, if this is the active tab
*/
bool isActiveTab() const;
/**
* Set this true to make this tab the active tab
*/
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()
* function belongs to.
*/
void setDockAreaWidget(CDockAreaWidget* DockArea);
/**
* Returns the dock area widget this title bar belongs to.
* \return This function returns 0 if the dock widget that owns this title
* bar widget has not been added to any dock area yet.
*/
CDockAreaWidget* dockAreaWidget() const;
signals:
void activeTabChanged();
void clicked();
}; // class DockWidgetTitleBar
}
// namespace ads
//-----------------------------------------------------------------------------
#endif // DockWidgetTitleBarH

View File

@@ -0,0 +1,425 @@
/*******************************************************************************
** Qt Advanced Docking System
** Copyright (C) 2017 Uwe Kindler
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation; either
** version 2.1 of the License, or (at your option) any later version.
**
** This library is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public
** License along with this library; If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
//============================================================================
/// \file FloatingDockContainer.cpp
/// \author Uwe Kindler
/// \date 01.03.2017
/// \brief Implementation of CFloatingDockContainer class
//============================================================================
//============================================================================
// INCLUDES
//============================================================================
#include "FloatingDockContainer.h"
#include <QBoxLayout>
#include <QApplication>
#include <QMouseEvent>
#include <QPointer>
#include <QAction>
#include <QDebug>
#include "DockContainerWidget.h"
#include "DockAreaWidget.h"
#include "DockManager.h"
#include "DockWidget.h"
#include "DockOverlay.h"
#include <iostream>
namespace ads
{
static unsigned int zOrderCounter = 0;
/**
* Private data class of CFloatingDockContainer class (pimpl)
*/
struct FloatingDockContainerPrivate
{
CFloatingDockContainer* _this;
CDockContainerWidget* DockContainer;
unsigned int zOrderIndex = ++zOrderCounter;
QPointer<CDockManager> DockManager;
bool DraggingActive = false;
QPoint DragStartMousePosition;
CDockContainerWidget* DropContainer = nullptr;
CDockAreaWidget* SingleDockArea = nullptr;
/**
* Private data constructor
*/
FloatingDockContainerPrivate(CFloatingDockContainer* _public);
void titleMouseReleaseEvent();
void updateDropOverlays(const QPoint& GlobalPos);
void setDraggingActive(bool Active);
};
// struct FloatingDockContainerPrivate
//============================================================================
FloatingDockContainerPrivate::FloatingDockContainerPrivate(CFloatingDockContainer* _public) :
_this(_public)
{
}
//============================================================================
void FloatingDockContainerPrivate::titleMouseReleaseEvent()
{
setDraggingActive(false);
if (!DropContainer)
{
return;
}
// Resize the floating widget to the size of the highlighted drop area
// rectangle
QRect Rect = DockManager->containerOverlay()->dropOverlayRect();
if (!Rect.isValid())
{
Rect = DockManager->dockAreaOverlay()->rect();
}
if (Rect.isValid())
{
_this->resize(Rect.size());
}
DropContainer->dropFloatingWidget(_this, QCursor::pos());
DockManager->containerOverlay()->hideOverlay();
DockManager->dockAreaOverlay()->hideOverlay();
}
//============================================================================
void FloatingDockContainerPrivate::updateDropOverlays(const QPoint& GlobalPos)
{
if (!_this->isVisible() || !DockManager)
{
return;
}
auto Containers = DockManager->dockContainers();
CDockContainerWidget* TopContainer = nullptr;
for (auto ContainerWidget : Containers)
{
if (!ContainerWidget->isVisible())
{
continue;
}
if (DockContainer == ContainerWidget)
{
continue;
}
QPoint MappedPos = ContainerWidget->mapFromGlobal(GlobalPos);
if (ContainerWidget->rect().contains(MappedPos))
{
if (!TopContainer || ContainerWidget->isInFrontOf(TopContainer))
{
TopContainer = ContainerWidget;
}
}
}
DropContainer = TopContainer;
auto ContainerOverlay = DockManager->containerOverlay();
auto DockAreaOverlay = DockManager->dockAreaOverlay();
if (!TopContainer)
{
ContainerOverlay->hideOverlay();
DockAreaOverlay->hideOverlay();
return;
}
int VisibleDockAreas = TopContainer->visibleDockAreaCount();
ContainerOverlay->setAllowedAreas(VisibleDockAreas > 1 ?
OuterDockAreas : AllDockAreas);
ContainerOverlay->showOverlay(TopContainer);
auto DockArea = TopContainer->dockAreaAt(GlobalPos);
if (DockArea && DockArea->isVisible() && VisibleDockAreas > 0)
{
DockAreaOverlay->setAllowedAreas((VisibleDockAreas == 1) ?
NoDockWidgetArea : AllDockAreas);
DockWidgetArea Area = DockAreaOverlay->showOverlay(DockArea);
ContainerOverlay->enableDropPreview(InvalidDockWidgetArea == Area);
}
else
{
DockAreaOverlay->hideOverlay();
}
}
//============================================================================
void FloatingDockContainerPrivate::setDraggingActive(bool Active)
{
DraggingActive = Active;
}
//============================================================================
CFloatingDockContainer::CFloatingDockContainer(CDockManager* DockManager) :
QWidget(DockManager, Qt::Window),
d(new FloatingDockContainerPrivate(this))
{
//setAttribute(Qt::WA_DeleteOnClose);
d->DockManager = DockManager;
QBoxLayout* l = new QBoxLayout(QBoxLayout::TopToBottom);
l->setContentsMargins(0, 0, 0, 0);
l->setSpacing(0);
setLayout(l);
d->DockContainer = new CDockContainerWidget(DockManager, this);
connect(d->DockContainer, SIGNAL(dockAreasAdded()), this, SLOT(onDockAreasAddedOrRemoved()));
connect(d->DockContainer, SIGNAL(dockAreasRemoved()), this, SLOT(onDockAreasAddedOrRemoved()));
l->addWidget(d->DockContainer);
DockManager->registerFloatingWidget(this);
// We install an event filter to detect mouse release events because we
// do not receive mouse release event if the floating widget is behind
// the drop overlay cross
qApp->installEventFilter(this);
}
//============================================================================
CFloatingDockContainer::CFloatingDockContainer(CDockAreaWidget* DockArea) :
CFloatingDockContainer(DockArea->dockManager())
{
d->DockContainer->addDockArea(DockArea);
}
//============================================================================
CFloatingDockContainer::CFloatingDockContainer(CDockWidget* DockWidget) :
CFloatingDockContainer(DockWidget->dockManager())
{
d->DockContainer->addDockWidget(CenterDockWidgetArea, DockWidget);
}
//============================================================================
CFloatingDockContainer::~CFloatingDockContainer()
{
qDebug() << "~CFloatingDockContainer";
if (d->DockManager)
{
d->DockManager->removeFloatingWidget(this);
}
delete d;
}
//============================================================================
CDockContainerWidget* CFloatingDockContainer::dockContainer() const
{
return d->DockContainer;
}
//============================================================================
void CFloatingDockContainer::changeEvent(QEvent *event)
{
QWidget::changeEvent(event);
if ((event->type() == QEvent::ActivationChange) && isActiveWindow())
{
qDebug() << "FloatingWidget::changeEvent QEvent::ActivationChange ";
d->zOrderIndex = ++zOrderCounter;
return;
}
}
//============================================================================
void CFloatingDockContainer::moveEvent(QMoveEvent *event)
{
QWidget::moveEvent(event);
if (!qApp->mouseButtons().testFlag(Qt::LeftButton))
{
if (d->DraggingActive)
{
d->setDraggingActive(false);
}
return;
}
if (d->DraggingActive)
{
d->updateDropOverlays(QCursor::pos());
}
}
//============================================================================
void CFloatingDockContainer::closeEvent(QCloseEvent *event)
{
d->setDraggingActive(false);
QWidget::closeEvent(event);
}
//============================================================================
void CFloatingDockContainer::hideEvent(QHideEvent *event)
{
QWidget::hideEvent(event);
auto OpenDockAreas = d->DockContainer->openedDockAreas();
for (auto DockArea : OpenDockAreas)
{
auto OpenDockWidgets = DockArea->openedDockWidgets();
for (auto DockWidget : OpenDockWidgets)
{
DockWidget->setToggleViewActionChecked(false);
}
}
}
//============================================================================
void CFloatingDockContainer::showEvent(QShowEvent *event)
{
QWidget::showEvent(event);
CDockContainerWidget* DockContainer = dockContainer();
for (int i = 0; i < DockContainer->dockAreaCount(); ++i)
{
auto DockArea = DockContainer->dockArea(i);
for (auto DockWidget : DockArea->openedDockWidgets())
{
DockWidget->setToggleViewActionChecked(true);
}
}
}
//============================================================================
bool CFloatingDockContainer::event(QEvent *e)
{
if ((e->type() == QEvent::NonClientAreaMouseButtonPress))
{
if (QGuiApplication::mouseButtons() == Qt::LeftButton)
{
qDebug() << "FloatingWidget::event Event::NonClientAreaMouseButtonPress" << e->type();
d->setDraggingActive(true);
}
}
else if (e->type() == QEvent::NonClientAreaMouseButtonDblClick)
{
qDebug() << "FloatingWidget::event QEvent::NonClientAreaMouseButtonDblClick";
d->setDraggingActive(false);
}
else if ((e->type() == QEvent::NonClientAreaMouseButtonRelease) && d->DraggingActive)
{
qDebug() << "FloatingWidget::event QEvent::NonClientAreaMouseButtonRelease";
d->titleMouseReleaseEvent();
}
return QWidget::event(e);
}
//============================================================================
bool CFloatingDockContainer::eventFilter(QObject *watched, QEvent *event)
{
Q_UNUSED(watched);
if (event->type() == QEvent::MouseButtonRelease && d->DraggingActive)
{
qDebug() << "FloatingWidget::eventFilter QEvent::MouseButtonRelease";
d->titleMouseReleaseEvent();
}
return false;
}
//============================================================================
void CFloatingDockContainer::startFloating(const QPoint& Pos, const QSize& Size)
{
resize(Size);
d->setDraggingActive(true);
QPoint TargetPos = QCursor::pos() - Pos;
move(TargetPos);
show();
d->DragStartMousePosition = Pos;
}
//============================================================================
void CFloatingDockContainer::moveFloating()
{
int BorderSize = (frameSize().width() - size().width()) / 2;
const QPoint moveToPos = QCursor::pos() - d->DragStartMousePosition - QPoint(BorderSize, 0);
move(moveToPos);
}
//============================================================================
void CFloatingDockContainer::onDockAreasAddedOrRemoved()
{
qDebug() << "CFloatingDockContainer::onDockAreasAddedOrRemoved()";
if (d->DockContainer->dockAreaCount() == 1)
{
d->SingleDockArea = d->DockContainer->dockArea(0);
this->setWindowTitle(d->SingleDockArea->currentDockWidget()->windowTitle());
connect(d->SingleDockArea, SIGNAL(currentChanged(int)), this,
SLOT(onDockAreaCurrentChanged(int)));
}
else
{
if (d->SingleDockArea)
{
disconnect(d->SingleDockArea, SIGNAL(currentChanged(int)), this,
SLOT(onDockAreaCurrentChanged(int)));
d->SingleDockArea = nullptr;
}
this->setWindowTitle(qApp->applicationDisplayName());
}
}
//============================================================================
void CFloatingDockContainer::onDockAreaCurrentChanged(int Index)
{
Q_UNUSED(Index);
this->setWindowTitle(d->SingleDockArea->currentDockWidget()->windowTitle());
}
//============================================================================
bool CFloatingDockContainer::restoreState(QDataStream& Stream, bool Testing)
{
if (!d->DockContainer->restoreState(Stream, Testing))
{
return false;
}
onDockAreasAddedOrRemoved();
return true;
}
} // namespace ads
//---------------------------------------------------------------------------
// EOF FloatingDockContainer.cpp

118
src/FloatingDockContainer.h Normal file
View File

@@ -0,0 +1,118 @@
#ifndef FloatingDockContainerH
#define FloatingDockContainerH
/*******************************************************************************
** Qt Advanced Docking System
** Copyright (C) 2017 Uwe Kindler
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation; either
** version 2.1 of the License, or (at your option) any later version.
**
** This library is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public
** License along with this library; If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
//============================================================================
/// \file FloatingDockContainer.h
/// \author Uwe Kindler
/// \date 01.03.2017
/// \brief Declaration of CFloatingDockContainer class
//============================================================================
//============================================================================
// INCLUDES
//============================================================================
#include <QWidget>
namespace ads
{
struct FloatingDockContainerPrivate;
class CDockAreaWidget;
class CDockContainerWidget;
class CDockWidget;
class CDockManager;
/**
* This implements a floating widget that is a dock container that accepts
* docking of dock widgets like the main window and that can be docked into
* another dock container
*/
class CFloatingDockContainer : public QWidget
{
Q_OBJECT
private:
FloatingDockContainerPrivate* d; ///< private data (pimpl)
friend class FloatingDockContainerPrivate;
private slots:
void onDockAreasAddedOrRemoved();
void onDockAreaCurrentChanged(int Index);
protected: // reimplements QWidget
virtual void changeEvent(QEvent *event) override;
virtual void moveEvent(QMoveEvent *event) override;
virtual bool event(QEvent *e) override;
virtual void closeEvent(QCloseEvent *event) override;
virtual void hideEvent(QHideEvent *event) override;
virtual void showEvent(QShowEvent *event) override;
virtual bool eventFilter(QObject *watched, QEvent *event) override;
public:
/**
* Create empty flatingb widget - required for restore state
*/
CFloatingDockContainer(CDockManager* DockManager);
/**
* Create floating widget with the given dock area
*/
CFloatingDockContainer(CDockAreaWidget* DockArea);
/**
* Create floating widget with the given dock widget
*/
CFloatingDockContainer(CDockWidget* DockWidget);
/**
* Virtual Destructor
*/
virtual ~CFloatingDockContainer();
/**
* Access function for the internal dock container
*/
CDockContainerWidget* dockContainer() const;
/**
* Starts floating at the given global position.
* Use moveToGlobalPos() to move the widget to a new position
* depending on the start position given in Pos parameter
*/
void startFloating(const QPoint& Pos, const QSize& Size = QSize());
/**
* Moves the widget to a new position relative to the position given when
* startFloating() was called
*/
void moveFloating();
/**
* Restores the state from given stream.
* If Testing is true, the function only parses the data from the given
* stream but does not restore anything. You can use this check for
* faulty files before you start restoring the state
*/
bool restoreState(QDataStream& Stream, bool Testing);
}; // class FloatingDockContainer
}
// namespace ads
//-----------------------------------------------------------------------------
#endif // FloatingDockContainerH

5
src/ads.qrc Normal file
View File

@@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/ads">
<file>stylesheets/default.css</file>
</qresource>
</RCC>

82
src/ads_globals.cpp Normal file
View File

@@ -0,0 +1,82 @@
/*******************************************************************************
** Qt Advanced Docking System
** Copyright (C) 2017 Uwe Kindler
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation; either
** version 2.1 of the License, or (at your option) any later version.
**
** This library is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public
** License along with this library; If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
//============================================================================
/// \file ads_globals.cpp
/// \author Uwe Kindler
/// \date 24.02.2017
/// \brief Implementation of
//============================================================================
//============================================================================
// INCLUDES
//============================================================================
#include <QVariant>
#include "DockSplitter.h"
#include "ads_globals.h"
namespace ads
{
namespace internal
{
//============================================================================
QSplitter* newSplitter(Qt::Orientation orientation, QWidget* parent)
{
QSplitter* s = new CDockSplitter(orientation, parent);
s->setProperty("ads-splitter", QVariant(true));
s->setChildrenCollapsible(false);
s->setOpaqueResize(false);
return s;
}
//============================================================================
void replaceSplitterWidget(QSplitter* Splitter, QWidget* From, QWidget* To)
{
int index = Splitter->indexOf(From);
From->setParent(0);
Splitter->insertWidget(index, To);
}
//============================================================================
CDockInsertParam dockAreaInsertParameters(DockWidgetArea Area)
{
switch (Area)
{
case TopDockWidgetArea: return QPair<Qt::Orientation, bool>(Qt::Vertical, false);
case RightDockWidgetArea: return QPair<Qt::Orientation, bool>(Qt::Horizontal, true);
case CenterDockWidgetArea:
case BottomDockWidgetArea: return QPair<Qt::Orientation, bool>(Qt::Vertical, true);
case LeftDockWidgetArea: return QPair<Qt::Orientation, bool>(Qt::Horizontal, false);
default: QPair<Qt::Orientation, bool>(Qt::Vertical, false);
} // switch (Area)
return CDockInsertParam(Qt::Vertical, false);
}
} // namespace internal
} // namespace ads
//---------------------------------------------------------------------------
// EOF ads_globals.cpp

112
src/ads_globals.h Normal file
View File

@@ -0,0 +1,112 @@
#ifndef ads_globalsH
#define ads_globalsH
/*******************************************************************************
** Qt Advanced Docking System
** Copyright (C) 2017 Uwe Kindler
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation; either
** version 2.1 of the License, or (at your option) any later version.
**
** This library is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public
** License along with this library; If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
//============================================================================
/// \file ads_globals.h
/// \author Uwe Kindler
/// \date 24.02.2017
/// \brief Declaration of
//============================================================================
//============================================================================
// INCLUDES
//============================================================================
#include <QPair>
class QSplitter;
namespace ads
{
enum DockWidgetArea
{
NoDockWidgetArea = 0x00,
LeftDockWidgetArea = 0x01,
RightDockWidgetArea = 0x02,
TopDockWidgetArea = 0x04,
BottomDockWidgetArea = 0x08,
CenterDockWidgetArea = 0x10,
InvalidDockWidgetArea = NoDockWidgetArea,
OuterDockAreas = TopDockWidgetArea | LeftDockWidgetArea | RightDockWidgetArea | BottomDockWidgetArea,
AllDockAreas = OuterDockAreas | CenterDockWidgetArea
};
Q_DECLARE_FLAGS(DockWidgetAreas, DockWidgetArea)
namespace internal
{
/**
* Helper function to create new splitter widgets
*/
QSplitter* newSplitter(Qt::Orientation orientation, QWidget* parent = 0);
/**
* Replace the from widget in the given splitter with the To widget
*/
void replaceSplitterWidget(QSplitter* Splitter, QWidget* From, QWidget* To);
/**
* Convenience class for QPair to provide better naming than first and
* second
*/
class CDockInsertParam : public QPair<Qt::Orientation, bool>
{
public:
using QPair::QPair;
Qt::Orientation orientation() const {return this->first;}
bool append() const {return this->second;}
int insertOffset() const {return append() ? 1 : 0;}
};
/**
* Returns the insertion parameters for the given dock area
*/
CDockInsertParam dockAreaInsertParameters(DockWidgetArea Area);
/**
* Searches for the parent widget of the given type.
* Returns the parent widget of the given widget or 0 if the widget is not
* child of any widget of type T
*/
template <class T>
T findParent(const QWidget* w)
{
QWidget* parentWidget = w->parentWidget();
while (parentWidget)
{
T ParentImpl = dynamic_cast<T>(parentWidget);
if (ParentImpl)
{
return ParentImpl;
}
parentWidget = parentWidget->parentWidget();
}
return 0;
}
} // namespace internal
} // namespace ads
//---------------------------------------------------------------------------
#endif // ads_globalsH

55
src/src.pro Normal file
View File

@@ -0,0 +1,55 @@
ADS_ROOT = $${PWD}/..
ADS_OUT_ROOT = $${OUT_PWD}/..
TARGET = $$qtLibraryTarget(AdvancedDockingSystem)
TEMPLATE = lib
DESTDIR = $${ADS_OUT_ROOT}/lib
QT += core gui widgets
CONFIG += adsBuildShared
adsBuildShared {
CONFIG += shared
DEFINES += ADS_EXPORT
}
!adsBuildShared {
CONFIG += staticlib
}
windows {
# MinGW
*-g++* {
QMAKE_CXXFLAGS += -std=c++11
QMAKE_CXXFLAGS += -Wall -Wextra -pedantic
}
# MSVC
*-msvc* {
}
}
RESOURCES += ads.qrc
HEADERS += \
ads_globals.h \
DockAreaWidget.h \
DockContainerWidget.h \
DockManager.h \
DockWidget.h \
DockWidgetTitleBar.h \
FloatingDockContainer.h \
DockOverlay.h \
DockSplitter.h
SOURCES += \
ads_globals.cpp \
DockAreaWidget.cpp \
DockContainerWidget.cpp \
DockManager.cpp \
DockWidget.cpp \
DockWidgetTitleBar.cpp \
FloatingDockContainer.cpp \
DockOverlay.cpp \
DockSplitter.cpp

View File

@@ -0,0 +1,66 @@
/*
* Default style sheet on Windows Platforms
* Note: Always use CSS-classes with and without "ads--" namespace to support Qt4 & Qt5
*/
ads--CDockContainerWidget
{
background: palette(dark);
}
ads--CDockContainerWidget QSplitter::handle
{
background: palette(dark);
}
ads--CDockAreaWidget
{
background: palette(window);
border: 1px solid white;
}
ads--CDockAreaWidget #tabsMenuButton::menu-indicator
{
image: none;
}
ads--CDockWidgetTitleBar
{
background: palette(window);
border-color: palette(light);
border-style: solid;
border-width: 0 1px 0 0;
padding: 0 9px;
}
ads--CDockWidgetTitleBar[activeTab="true"]
{
background: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:0.5, stop:0 palette(window), stop:1 palette(light));
}
ads--CDockWidgetTitleBar QLabel
{
color: palette(dark);
}
ads--CDockWidgetTitleBar[activeTab="true"] QLabel
{
color: palette(foreground);
}
ads--CDockWidget
{
background: palette(light);
border-color: palette(light);
border-style: solid;
border-width: 1px 0 0 0;
}
QPushButton#closeButton,
QPushButton#tabsMenuButton
{
padding: 2px;
}