Compare commits
156 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f3f5b668e5 | ||
|
|
82d15fbe29 | ||
|
|
01533a9f85 | ||
|
|
fb9f1d851b | ||
|
|
6fb80fdeb7 | ||
|
|
e94cf9bcb7 | ||
|
|
da0ec6dd4b | ||
|
|
80efed693e | ||
|
|
653f475e72 | ||
|
|
87e3777e37 | ||
|
|
71f66ea6dc | ||
|
|
0b3f419c80 | ||
|
|
f69af82a49 | ||
|
|
854f542164 | ||
|
|
b9265fccec | ||
|
|
b3a272110a | ||
|
|
316e5324ad | ||
|
|
6843703484 | ||
|
|
115a9a5b3d | ||
|
|
a4838a41ac | ||
|
|
74b9d35c7b | ||
|
|
c973482b2b | ||
|
|
188624440b | ||
|
|
72ec61a043 | ||
|
|
0ac19ebdfb | ||
|
|
bc6ffcc02c | ||
|
|
a9246f7ce4 | ||
|
|
3f5697554a | ||
|
|
5e6c82b68d | ||
|
|
268f8655a1 | ||
|
|
1dfabb3bef | ||
|
|
6617cf6f19 | ||
|
|
81523b0346 | ||
|
|
927be9a7d9 | ||
|
|
ada3d6b3b5 | ||
|
|
30bbd26d0a | ||
|
|
8637c89a6b | ||
|
|
11e5f9c95a | ||
|
|
9bfb3fbea1 | ||
|
|
9c95e34df5 | ||
|
|
ceebda7431 | ||
|
|
75288af88c | ||
|
|
7c67d71f68 | ||
|
|
548dfb363a | ||
|
|
9fec2bd515 | ||
|
|
fc04aa2411 | ||
|
|
c3a5e3ef21 | ||
|
|
0e85431405 | ||
|
|
b3b6d20d96 | ||
|
|
272bbe275e | ||
|
|
496aec211e | ||
|
|
b9b72df9d4 | ||
|
|
fcb1846bf5 | ||
|
|
9f1b2c122a | ||
|
|
6ec38b48ef | ||
|
|
b93e723a83 | ||
|
|
1a47918bdb | ||
|
|
ff9d965726 | ||
|
|
77d2cebe39 | ||
|
|
5d380708e1 | ||
|
|
aa7b36dbd1 | ||
|
|
c9123c3640 | ||
|
|
67199a81f4 | ||
|
|
72ee4a53df | ||
|
|
0b963d1540 | ||
|
|
9cd2584de5 | ||
|
|
3f40c997e5 | ||
|
|
6b93ae9c39 | ||
|
|
f5b3c0556d | ||
|
|
5b3841a038 | ||
|
|
9d00a278e6 | ||
|
|
b470dd5f99 | ||
|
|
7b4a19b943 | ||
|
|
8eceed9aa3 | ||
|
|
4188d69356 | ||
|
|
3fc7c195c3 | ||
|
|
f823b67a4a | ||
|
|
064cab405a | ||
|
|
75ad302d21 | ||
|
|
18a4f17fbf | ||
|
|
6266cd3290 | ||
|
|
8a401ebd68 | ||
|
|
805e97946e | ||
|
|
e878bb47ed | ||
|
|
dae852d9f9 | ||
|
|
8a014a6c2d | ||
|
|
9676eeb1bf | ||
|
|
412f13e1c4 | ||
|
|
11a30806f6 | ||
|
|
b9257bbe93 | ||
|
|
78e81b02fd | ||
|
|
52c23dafd5 | ||
|
|
bc37a2788e | ||
|
|
051c379e4a | ||
|
|
844c853768 | ||
|
|
990d3235c5 | ||
|
|
fd76e9e62b | ||
|
|
a3ff1ae8ee | ||
|
|
17dff82d12 | ||
|
|
9af86c4136 | ||
|
|
aa8a52b845 | ||
|
|
9adc524a42 | ||
|
|
549646d113 | ||
|
|
7ba3c1f244 | ||
|
|
c532c24f79 | ||
|
|
c5ea5c80b1 | ||
|
|
1b1c636107 | ||
|
|
2277ba3630 | ||
|
|
5dcd15e2b9 | ||
|
|
a652deba71 | ||
|
|
1cd1e7d6ec | ||
|
|
16bd1a3bd2 | ||
|
|
b6ee26adc2 | ||
|
|
0d6f469a36 | ||
|
|
338b10695a | ||
|
|
239bd4781b | ||
|
|
97571e4be8 | ||
|
|
c57d14d0d7 | ||
|
|
a0849dd19f | ||
|
|
52aedfed31 | ||
|
|
fade9d7b7c | ||
|
|
a8ccbfa407 | ||
|
|
f9f064dd71 | ||
|
|
fac5661280 | ||
|
|
df85c8bee0 | ||
|
|
e0a442263e | ||
|
|
2c9ceb8645 | ||
|
|
b31c5f1f3d | ||
|
|
c14352e7c1 | ||
|
|
f0584ff0c5 | ||
|
|
4a0c176327 | ||
|
|
94a6f5e2f1 | ||
|
|
842cef2675 | ||
|
|
ca64286117 | ||
|
|
5dc935f650 | ||
|
|
ad49745b18 | ||
|
|
938afb7357 | ||
|
|
0edc1b4499 | ||
|
|
d0fc4ace07 | ||
|
|
70a7a7b352 | ||
|
|
b93d2fbd48 | ||
|
|
635b96a2ae | ||
|
|
72fc9cd79a | ||
|
|
268c49af3b | ||
|
|
025bedfb65 | ||
|
|
df97d17844 | ||
|
|
2bf1a51627 | ||
|
|
a4de5c5560 | ||
|
|
0716020ab4 | ||
|
|
7f5e393cfb | ||
|
|
f4c0d38ba4 | ||
|
|
411e4002f1 | ||
|
|
3fd20fad16 | ||
|
|
889d9bff5b | ||
|
|
68b93f6fa9 | ||
|
|
abc8468989 |
14
.astylerc
@@ -1,14 +0,0 @@
|
||||
--style=allman
|
||||
|
||||
--indent=force-tab=4
|
||||
|
||||
--align-pointer=type
|
||||
--align-reference=type
|
||||
|
||||
--pad-oper
|
||||
--pad-header
|
||||
--unpad-paren
|
||||
|
||||
--remove-comment-prefix
|
||||
|
||||
--mode=c
|
||||
321
.cproject
Normal file
@@ -0,0 +1,321 @@
|
||||
<?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=""${workspace_loc:/QtAdvancedDockingSystem/demo}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/QtAdvancedDockingSystem/src}""/>
|
||||
<listOptionValue builtIn="false" value=""${QTDIR}/include""/>
|
||||
<listOptionValue builtIn="false" value=""${QTDIR}/include/QtCore""/>
|
||||
<listOptionValue builtIn="false" value=""${QTDIR}/include/QtWidgets""/>
|
||||
</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=""${workspace_loc:/QtAdvancedDockingSystem/src}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/QtAdvancedDockingSystem/demo}""/>
|
||||
<listOptionValue builtIn="false" value=""${QTDIR}/include""/>
|
||||
<listOptionValue builtIn="false" value=""${QTDIR}/include/QtCore""/>
|
||||
<listOptionValue builtIn="false" value=""${QTDIR}/include/QtWidgets""/>
|
||||
</option>
|
||||
<option id="gnu.cpp.compiler.option.preprocessor.def.56223209" name="Defined symbols (-D)" superClass="gnu.cpp.compiler.option.preprocessor.def" useByScannerDiscovery="false"/>
|
||||
<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=""${workspace_loc:/QtAdvancedDockingSystem/demo}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/QtAdvancedDockingSystem/src}""/>
|
||||
<listOptionValue builtIn="false" value=""${QTDIR}/include""/>
|
||||
<listOptionValue builtIn="false" value=""${QTDIR}/include/QtCore""/>
|
||||
<listOptionValue builtIn="false" value=""${QTDIR}/include/QtWidgets""/>
|
||||
</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"/>
|
||||
<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
@@ -1 +1,2 @@
|
||||
*.pro.user
|
||||
/ build
|
||||
|
||||
27
.project
Normal 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>
|
||||
12
.settings/language.settings.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?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-reference id="org.eclipse.cdt.managedbuilder.core.GCCBuiltinSpecsDetectorMinGW" ref="shared-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>
|
||||
24
.travis.yml
@@ -1,24 +0,0 @@
|
||||
language:
|
||||
- cpp
|
||||
|
||||
compiler:
|
||||
- g++
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-sdk-team
|
||||
packages:
|
||||
- qt5-qmake
|
||||
- qtbase5-dev
|
||||
- qtdeclarative5-dev
|
||||
- libqt5webkit5-dev
|
||||
- libsqlite3-dev
|
||||
|
||||
script:
|
||||
- qmake -qt=qt5 -v
|
||||
- qmake -qt=qt5 -r build.pro
|
||||
- make
|
||||
|
||||
#- sudo apt-get install -qq qtbase5-dev qtdeclarative5-dev libqt5webkit5-dev libsqlite3-dev
|
||||
#- sudo apt-get install -qq qt5-default qttools5-dev-tools
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -1,15 +0,0 @@
|
||||
#include "ads/Internal.h"
|
||||
|
||||
ADS_NAMESPACE_BEGIN
|
||||
|
||||
InternalContentData::InternalContentData() :
|
||||
titleWidget(NULL),
|
||||
contentWidget(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
InternalContentData::~InternalContentData()
|
||||
{
|
||||
}
|
||||
|
||||
ADS_NAMESPACE_END
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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());
|
||||
}
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
@@ -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
|
||||
@@ -1,7 +0,0 @@
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/src/TestCore.h
|
||||
|
||||
SOURCES += \
|
||||
$$PWD/src/main.cpp \
|
||||
$$PWD/src/TestCore.cpp
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -1 +0,0 @@
|
||||
#include <QtTest/QtTest>
|
||||
15
LICENSE.md
Normal 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/>.
|
||||
135
README.md
@@ -1,94 +1,67 @@
|
||||
# Advanced Docking System for Qt
|
||||
[](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.
|
||||
|
||||
\
|
||||
\
|
||||

|
||||
|
||||
### Docking inside floating windows
|
||||
There is no difference between the main window and a floating window. Docking
|
||||
into floating windows is supported.
|
||||
|
||||
\
|
||||
\
|
||||

|
||||
|
||||
### 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.
|
||||
|
||||
\
|
||||
\
|
||||

|
||||
|
||||
### Perspectives for fast switching of the complete main window layout
|
||||
A perspective defines the set and layout of dock windows in the main
|
||||
window. You can save the current layout of the dockmanager into a named
|
||||
perspective to make your own custom perspective. Later you can simply
|
||||
select a perspective from the perspective list to quickly switch the complete
|
||||
main window layout.
|
||||
|
||||
\
|
||||
\
|
||||

|
||||
|
||||
## 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
|
||||

|
||||
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).
|
||||
|
||||
6
ads.pro
Normal file
@@ -0,0 +1,6 @@
|
||||
TEMPLATE = subdirs
|
||||
CONFIG += ordered
|
||||
|
||||
SUBDIRS = \
|
||||
src \
|
||||
demo
|
||||
@@ -1,6 +0,0 @@
|
||||
TEMPLATE = subdirs
|
||||
|
||||
SUBDIRS = \
|
||||
AdvancedDockingSystem \
|
||||
AdvancedDockingSystemDemo \
|
||||
AdvancedDockingSystemUnitTests
|
||||
340
demo/MainWindow.cpp
Normal file
@@ -0,0 +1,340 @@
|
||||
|
||||
/*******************************************************************************
|
||||
** 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 MainWindow.cpp
|
||||
/// \author Uwe Kindler
|
||||
/// \date 13.02.2018
|
||||
/// \brief Implementation of CMainWindow demo class
|
||||
//============================================================================
|
||||
|
||||
|
||||
//============================================================================
|
||||
// INCLUDES
|
||||
//============================================================================
|
||||
#include <MainWindow.h>
|
||||
#include "ui_mainwindow.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <QTime>
|
||||
#include <QLabel>
|
||||
#include <QTextEdit>
|
||||
#include <QCalendarWidget>
|
||||
#include <QFrame>
|
||||
#include <QTreeView>
|
||||
#include <QFileSystemModel>
|
||||
#include <QBoxLayout>
|
||||
#include <QSettings>
|
||||
#include <QDockWidget>
|
||||
#include <QDebug>
|
||||
#include <QResizeEvent>
|
||||
#include <QAction>
|
||||
#include <QWidgetAction>
|
||||
#include <QComboBox>
|
||||
#include <QInputDialog>
|
||||
|
||||
#include <QMap>
|
||||
#include <QElapsedTimer>
|
||||
|
||||
#include "DockManager.h"
|
||||
#include "DockWidget.h"
|
||||
#include "DockAreaWidget.h"
|
||||
#include "AnimatedLabel.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);
|
||||
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->setToggleViewActionMode(ads::CDockWidget::ActionModeShow);
|
||||
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);
|
||||
ViewMenu->addAction(DockWidget->toggleViewAction());
|
||||
return DockWidget;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
/**
|
||||
* Private data class pimpl
|
||||
*/
|
||||
struct MainWindowPrivate
|
||||
{
|
||||
CMainWindow* _this;
|
||||
Ui::MainWindow ui;
|
||||
QAction* SavePerspectiveAction = nullptr;
|
||||
QWidgetAction* PerspectiveListAction = nullptr;
|
||||
QComboBox* PerspectiveComboBox = nullptr;;
|
||||
ads::CDockManager* DockManager = nullptr;
|
||||
|
||||
MainWindowPrivate(CMainWindow* _public) : _this(_public) {}
|
||||
|
||||
/**
|
||||
* Creates the toolbar actions
|
||||
*/
|
||||
void createActions();
|
||||
|
||||
/**
|
||||
* Fill the dock manager with dock widgets
|
||||
*/
|
||||
void createContent();
|
||||
|
||||
/**
|
||||
* Saves the dock manager state and the main window geometry
|
||||
*/
|
||||
void saveState();
|
||||
|
||||
/**
|
||||
* Save the list of perspectives
|
||||
*/
|
||||
void savePerspectives();
|
||||
|
||||
/**
|
||||
* Restores the dock manager state
|
||||
*/
|
||||
void restoreState();
|
||||
|
||||
/**
|
||||
* Restore the perspective listo of the dock manager
|
||||
*/
|
||||
void restorePerspectives();
|
||||
};
|
||||
|
||||
|
||||
//============================================================================
|
||||
void MainWindowPrivate::createContent()
|
||||
{
|
||||
// Test container docking
|
||||
QMenu* ViewMenu = ui.menuView;
|
||||
auto DockWidget = createCalendarDockWidget(ViewMenu);
|
||||
DockWidget->setIcon(_this->style()->standardIcon(QStyle::SP_DialogOpenButton));
|
||||
DockWidget->setFeature(ads::CDockWidget::DockWidgetClosable, false);
|
||||
DockManager->addDockWidget(ads::LeftDockWidgetArea, DockWidget);
|
||||
DockManager->addDockWidget(ads::LeftDockWidgetArea, createLongTextLabelDockWidget(ViewMenu));
|
||||
auto FileSystemWidget = createFileSystemTreeDockWidget(ViewMenu);
|
||||
auto ToolBar = FileSystemWidget->createDefaultToolBar();
|
||||
ToolBar->addAction(ui.actionSaveState);
|
||||
ToolBar->addAction(ui.actionRestoreState);
|
||||
DockManager->addDockWidget(ads::BottomDockWidgetArea, FileSystemWidget);
|
||||
|
||||
FileSystemWidget = createFileSystemTreeDockWidget(ViewMenu);
|
||||
ToolBar = FileSystemWidget->createDefaultToolBar();
|
||||
ToolBar->addAction(ui.actionSaveState);
|
||||
ToolBar->addAction(ui.actionRestoreState);
|
||||
FileSystemWidget->setFeature(ads::CDockWidget::DockWidgetMovable, false);
|
||||
auto TopDockArea = DockManager->addDockWidget(ads::TopDockWidgetArea, FileSystemWidget);
|
||||
DockWidget = createCalendarDockWidget(ViewMenu);
|
||||
DockWidget->setFeature(ads::CDockWidget::DockWidgetClosable, false);
|
||||
DockManager->addDockWidget(ads::CenterDockWidgetArea, DockWidget, TopDockArea);
|
||||
|
||||
// Test dock area docking
|
||||
auto RighDockArea = DockManager->addDockWidget(ads::RightDockWidgetArea, createLongTextLabelDockWidget(ViewMenu), TopDockArea);
|
||||
DockManager->addDockWidget(ads::TopDockWidgetArea, createLongTextLabelDockWidget(ViewMenu), RighDockArea);
|
||||
auto BottomDockArea = DockManager->addDockWidget(ads::BottomDockWidgetArea, createLongTextLabelDockWidget(ViewMenu), RighDockArea);
|
||||
DockManager->addDockWidget(ads::RightDockWidgetArea, createLongTextLabelDockWidget(ViewMenu), RighDockArea);
|
||||
DockManager->addDockWidget(ads::CenterDockWidgetArea, createLongTextLabelDockWidget(ViewMenu), BottomDockArea);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void MainWindowPrivate::createActions()
|
||||
{
|
||||
ui.toolBar->addAction(ui.actionSaveState);
|
||||
ui.actionSaveState->setIcon(_this->style()->standardIcon(QStyle::SP_DialogSaveButton));
|
||||
ui.toolBar->addAction(ui.actionRestoreState);
|
||||
ui.actionRestoreState->setIcon(_this->style()->standardIcon(QStyle::SP_DialogOpenButton));
|
||||
|
||||
SavePerspectiveAction = new QAction("Save Perspective", _this);
|
||||
_this->connect(SavePerspectiveAction, SIGNAL(triggered()), SLOT(savePerspective()));
|
||||
PerspectiveListAction = new QWidgetAction(_this);
|
||||
PerspectiveComboBox = new QComboBox(_this);
|
||||
PerspectiveComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
||||
PerspectiveComboBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
||||
PerspectiveListAction->setDefaultWidget(PerspectiveComboBox);
|
||||
ui.toolBar->addSeparator();
|
||||
ui.toolBar->addAction(PerspectiveListAction);
|
||||
ui.toolBar->addAction(SavePerspectiveAction);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void MainWindowPrivate::saveState()
|
||||
{
|
||||
QSettings Settings("Settings.ini", QSettings::IniFormat);
|
||||
Settings.setValue("mainWindow/Geometry", _this->saveGeometry());
|
||||
Settings.setValue("mainWindow/State", _this->saveState());
|
||||
Settings.setValue("mainWindow/DockingState", DockManager->saveState());
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void MainWindowPrivate::restoreState()
|
||||
{
|
||||
QSettings Settings("Settings.ini", QSettings::IniFormat);
|
||||
_this->restoreGeometry(Settings.value("mainWindow/Geometry").toByteArray());
|
||||
_this->restoreState(Settings.value("mainWindow/State").toByteArray());
|
||||
DockManager->restoreState(Settings.value("mainWindow/DockingState").toByteArray());
|
||||
}
|
||||
|
||||
|
||||
|
||||
//============================================================================
|
||||
void MainWindowPrivate::savePerspectives()
|
||||
{
|
||||
QSettings Settings("Settings.ini", QSettings::IniFormat);
|
||||
DockManager->savePerspectives(Settings);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//============================================================================
|
||||
void MainWindowPrivate::restorePerspectives()
|
||||
{
|
||||
QSettings Settings("Settings.ini", QSettings::IniFormat);
|
||||
DockManager->loadPerspectives(Settings);
|
||||
PerspectiveComboBox->clear();
|
||||
PerspectiveComboBox->addItems(DockManager->perspectiveNames());
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
CMainWindow::CMainWindow(QWidget *parent) :
|
||||
QMainWindow(parent),
|
||||
d(new MainWindowPrivate(this))
|
||||
{
|
||||
using namespace ads;
|
||||
d->ui.setupUi(this);
|
||||
d->createActions();
|
||||
|
||||
// Now create the dock manager and its content
|
||||
d->DockManager = new CDockManager(this);
|
||||
|
||||
// Uncomment the following line to have the old style where the dock
|
||||
// area close button closes the active tab
|
||||
//d->DockManager->setConfigFlags({
|
||||
// CDockManager::DockAreaHasCloseButton | CDockManager::DockAreaCloseButtonClosesTab});
|
||||
connect(d->PerspectiveComboBox, SIGNAL(activated(const QString&)),
|
||||
d->DockManager, SLOT(openPerspective(const QString&)));
|
||||
|
||||
d->createContent();
|
||||
// Default window geometry
|
||||
resize(800, 600);
|
||||
|
||||
d->restoreState();
|
||||
d->restorePerspectives();
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
CMainWindow::~CMainWindow()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CMainWindow::closeEvent(QCloseEvent* event)
|
||||
{
|
||||
d->saveState();
|
||||
QMainWindow::closeEvent(event);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CMainWindow::on_actionSaveState_triggered(bool)
|
||||
{
|
||||
qDebug() << "MainWindow::on_actionSaveState_triggered";
|
||||
d->saveState();
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CMainWindow::on_actionRestoreState_triggered(bool)
|
||||
{
|
||||
qDebug() << "MainWindow::on_actionRestoreState_triggered";
|
||||
d->restoreState();
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CMainWindow::savePerspective()
|
||||
{
|
||||
QString PerspectiveName = QInputDialog::getText(this, "Save Perspective", "Enter unique name:");
|
||||
if (PerspectiveName.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
d->DockManager->addPerspective(PerspectiveName);
|
||||
QSignalBlocker Blocker(d->PerspectiveComboBox);
|
||||
d->PerspectiveComboBox->clear();
|
||||
d->PerspectiveComboBox->addItems(d->DockManager->perspectiveNames());
|
||||
d->PerspectiveComboBox->setCurrentText(PerspectiveName);
|
||||
|
||||
d->savePerspectives();
|
||||
}
|
||||
|
||||
63
demo/MainWindow.h
Normal file
@@ -0,0 +1,63 @@
|
||||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
/*******************************************************************************
|
||||
** Qt Advanced Docking System
|
||||
** Copyright (C) 2017 Uwe Kindler
|
||||
**
|
||||
** This library is free software; you can redistribute it and/or
|
||||
** modify it under the terms of the GNU Lesser General Public
|
||||
** License as published by the Free Software Foundation; either
|
||||
** version 2.1 of the License, or (at your option) any later version.
|
||||
**
|
||||
** This library is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
** Lesser General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU Lesser General Public
|
||||
** License along with this library; If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
|
||||
//============================================================================
|
||||
/// \file MainWindow.h
|
||||
/// \author Uwe Kindler
|
||||
/// \date 13.02.2018
|
||||
/// \brief Declaration of CMainWindow class
|
||||
//============================================================================
|
||||
|
||||
|
||||
//============================================================================
|
||||
// INCLUDES
|
||||
//============================================================================
|
||||
#include <QMainWindow>
|
||||
|
||||
|
||||
|
||||
struct MainWindowPrivate;
|
||||
|
||||
|
||||
/**
|
||||
* Simple main window for demo
|
||||
*/
|
||||
class CMainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
MainWindowPrivate* d;///< private data - pimpl
|
||||
friend struct MainWindowPrivate;
|
||||
|
||||
protected:
|
||||
virtual void closeEvent(QCloseEvent* event) override;
|
||||
|
||||
public:
|
||||
explicit CMainWindow(QWidget *parent = 0);
|
||||
virtual ~CMainWindow();
|
||||
|
||||
private slots:
|
||||
void on_actionSaveState_triggered(bool);
|
||||
void on_actionRestoreState_triggered(bool);
|
||||
void savePerspective();
|
||||
};
|
||||
|
||||
#endif // MAINWINDOW_H
|
||||
31
demo/demo.pro
Normal file
@@ -0,0 +1,31 @@
|
||||
ADS_ROOT = $${PWD}/..
|
||||
ADS_OUT_ROOT = $${OUT_PWD}/..
|
||||
|
||||
TARGET = AdvancedDockingSystemDemo
|
||||
DESTDIR = $${ADS_OUT_ROOT}/lib
|
||||
QT += core gui widgets
|
||||
CONFIG *= c++14
|
||||
|
||||
|
||||
SOURCES += \
|
||||
main.cpp \
|
||||
MainWindow.cpp
|
||||
|
||||
|
||||
HEADERS += \
|
||||
MainWindow.h
|
||||
|
||||
FORMS += \
|
||||
mainwindow.ui
|
||||
|
||||
RESOURCES += main.qrc
|
||||
|
||||
LIBS += -L$${ADS_OUT_ROOT}/lib
|
||||
|
||||
# Dependency: AdvancedDockingSystem (shared)
|
||||
win32:CONFIG(release, debug|release): LIBS += -lqtadvanceddocking
|
||||
else:win32:CONFIG(debug, debug|release): LIBS += -lqtadvanceddockingd
|
||||
else:unix: LIBS += -lAdvancedDockingSystem
|
||||
|
||||
INCLUDEPATH += ../src
|
||||
DEPENDPATH += ../src
|
||||
51
demo/main.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <MainWindow.h>
|
||||
#include <QString>
|
||||
#include <QFile>
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
|
||||
#include <memory>
|
||||
|
||||
|
||||
|
||||
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[])
|
||||
{
|
||||
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
std::shared_ptr<int> b;
|
||||
QApplication a(argc, argv);
|
||||
a.setQuitOnLastWindowClosed(true);
|
||||
qInstallMessageHandler(myMessageOutput);
|
||||
qDebug() << "Message handler test";
|
||||
|
||||
CMainWindow mw;
|
||||
mw.show();
|
||||
return a.exec();
|
||||
}
|
||||
7
demo/main.qrc
Normal file
@@ -0,0 +1,7 @@
|
||||
<RCC>
|
||||
<qresource prefix="/main">
|
||||
<file>bricks.gif</file>
|
||||
<file>bricks_orange.gif</file>
|
||||
<file>bricks.apng</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
@@ -1,79 +1,82 @@
|
||||
<?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>
|
||||
<property name="dockOptions">
|
||||
<set>QMainWindow::AllowTabbedDocks</set>
|
||||
</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>
|
||||
<widget class="QToolBar" name="toolBar">
|
||||
<property name="windowTitle">
|
||||
<string>toolBar</string>
|
||||
</property>
|
||||
<attribute name="toolBarArea">
|
||||
<enum>TopToolBarArea</enum>
|
||||
</attribute>
|
||||
<attribute name="toolBarBreak">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</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>
|
||||
BIN
doc/TabMenu.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
doc/TabMenu_dark.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
doc/floating-widget-dragndrop.png
Normal file
|
After Width: | Height: | Size: 103 KiB |
BIN
doc/floating-widget-dragndrop_dark.png
Normal file
|
After Width: | Height: | Size: 123 KiB |
BIN
doc/grouped-dragging.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
doc/grouped-dragging_dark.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
doc/perspectives.png
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
doc/perspectives_dark.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
doc/preview-dragndrop.png
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
doc/preview-dragndrop_dark.png
Normal file
|
After Width: | Height: | Size: 194 KiB |
BIN
doc/preview_dark.png
Normal file
|
After Width: | Height: | Size: 136 KiB |
494
gnu-lgpl-v2.1.md
Normal 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
license.png
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 191 KiB |
BIN
preview.png
|
Before Width: | Height: | Size: 53 KiB |
558
src/DockAreaTabBar.cpp
Normal file
@@ -0,0 +1,558 @@
|
||||
/*******************************************************************************
|
||||
** 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 DockAreaTabBar.cpp
|
||||
/// \author Uwe Kindler
|
||||
/// \date 24.08.2018
|
||||
/// \brief Implementation of CDockAreaTabBar class
|
||||
//============================================================================
|
||||
|
||||
//============================================================================
|
||||
// INCLUDES
|
||||
//============================================================================
|
||||
#include "DockAreaTabBar.h"
|
||||
|
||||
#include <QMouseEvent>
|
||||
#include <QScrollBar>
|
||||
#include <QDebug>
|
||||
#include <QBoxLayout>
|
||||
#include <QApplication>
|
||||
|
||||
#include "FloatingDockContainer.h"
|
||||
#include "DockAreaWidget.h"
|
||||
#include "DockOverlay.h"
|
||||
#include "DockManager.h"
|
||||
#include "DockWidget.h"
|
||||
#include "DockWidgetTab.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
|
||||
namespace ads
|
||||
{
|
||||
/**
|
||||
* Private data class of CDockAreaTabBar class (pimpl)
|
||||
*/
|
||||
struct DockAreaTabBarPrivate
|
||||
{
|
||||
CDockAreaTabBar* _this;
|
||||
QPoint DragStartMousePos;
|
||||
CDockAreaWidget* DockArea;
|
||||
CFloatingDockContainer* FloatingWidget = nullptr;
|
||||
QWidget* TabsContainerWidget;
|
||||
QBoxLayout* TabsLayout;
|
||||
int CurrentIndex = -1;
|
||||
|
||||
/**
|
||||
* Private data constructor
|
||||
*/
|
||||
DockAreaTabBarPrivate(CDockAreaTabBar* _public);
|
||||
|
||||
/**
|
||||
* Update tabs after current index changed or when tabs are removed.
|
||||
* The function reassigns the stylesheet to update the tabs
|
||||
*/
|
||||
void updateTabs();
|
||||
};
|
||||
// struct DockAreaTabBarPrivate
|
||||
|
||||
//============================================================================
|
||||
DockAreaTabBarPrivate::DockAreaTabBarPrivate(CDockAreaTabBar* _public) :
|
||||
_this(_public)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void DockAreaTabBarPrivate::updateTabs()
|
||||
{
|
||||
// Set active TAB and update all other tabs to be inactive
|
||||
for (int i = 0; i < _this->count(); ++i)
|
||||
{
|
||||
auto TabWidget = _this->tab(i);
|
||||
if (!TabWidget)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i == CurrentIndex)
|
||||
{
|
||||
TabWidget->show();
|
||||
TabWidget->setActiveTab(true);
|
||||
_this->ensureWidgetVisible(TabWidget);
|
||||
}
|
||||
else
|
||||
{
|
||||
TabWidget->setActiveTab(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
CDockAreaTabBar::CDockAreaTabBar(CDockAreaWidget* parent) :
|
||||
QScrollArea(parent),
|
||||
d(new DockAreaTabBarPrivate(this))
|
||||
{
|
||||
d->DockArea = parent;
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Ignored);
|
||||
setFrameStyle(QFrame::NoFrame);
|
||||
setWidgetResizable(true);
|
||||
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
|
||||
d->TabsContainerWidget = new QWidget();
|
||||
d->TabsContainerWidget->setObjectName("tabsContainerWidget");
|
||||
setWidget(d->TabsContainerWidget);
|
||||
|
||||
d->TabsLayout = new QBoxLayout(QBoxLayout::LeftToRight);
|
||||
d->TabsLayout->setContentsMargins(0, 0, 0, 0);
|
||||
d->TabsLayout->setSpacing(0);
|
||||
d->TabsLayout->addStretch(1);
|
||||
d->TabsContainerWidget->setLayout(d->TabsLayout);
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
CDockAreaTabBar::~CDockAreaTabBar()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockAreaTabBar::wheelEvent(QWheelEvent* Event)
|
||||
{
|
||||
Event->accept();
|
||||
const int direction = Event->angleDelta().y();
|
||||
if (direction < 0)
|
||||
{
|
||||
horizontalScrollBar()->setValue(horizontalScrollBar()->value() + 20);
|
||||
}
|
||||
else
|
||||
{
|
||||
horizontalScrollBar()->setValue(horizontalScrollBar()->value() - 20);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockAreaTabBar::mousePressEvent(QMouseEvent* ev)
|
||||
{
|
||||
if (ev->button() == Qt::LeftButton)
|
||||
{
|
||||
ev->accept();
|
||||
d->DragStartMousePos = ev->pos();
|
||||
return;
|
||||
}
|
||||
QScrollArea::mousePressEvent(ev);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockAreaTabBar::mouseReleaseEvent(QMouseEvent* ev)
|
||||
{
|
||||
if (ev->button() == Qt::LeftButton)
|
||||
{
|
||||
qDebug() << "CTabsScrollArea::mouseReleaseEvent";
|
||||
ev->accept();
|
||||
d->FloatingWidget = nullptr;
|
||||
d->DragStartMousePos = QPoint();
|
||||
return;
|
||||
}
|
||||
QScrollArea::mouseReleaseEvent(ev);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockAreaTabBar::mouseMoveEvent(QMouseEvent* ev)
|
||||
{
|
||||
QScrollArea::mouseMoveEvent(ev);
|
||||
if (ev->buttons() != Qt::LeftButton)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (d->FloatingWidget)
|
||||
{
|
||||
d->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 (d->DockArea->dockContainer()->isFloating()
|
||||
&& d->DockArea->dockContainer()->visibleDockAreaCount() == 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int DragDistance = (d->DragStartMousePos - ev->pos()).manhattanLength();
|
||||
if (DragDistance >= CDockManager::startDragDistance())
|
||||
{
|
||||
qDebug() << "CTabsScrollArea::startFloating";
|
||||
startFloating(d->DragStartMousePos);
|
||||
auto Overlay = d->DockArea->dockManager()->containerOverlay();
|
||||
Overlay->setAllowedAreas(OuterDockAreas);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockAreaTabBar::mouseDoubleClickEvent(QMouseEvent *event)
|
||||
{
|
||||
// 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 (d->DockArea->dockContainer()->isFloating() && d->DockArea->dockContainer()->dockAreaCount() == 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
startFloating(event->pos());
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
CFloatingDockContainer* CDockAreaTabBar::makeAreaFloating(const QPoint& Pos)
|
||||
{
|
||||
QSize Size = d->DockArea->size();
|
||||
CFloatingDockContainer* FloatingWidget = new CFloatingDockContainer(d->DockArea);
|
||||
FloatingWidget->startFloating(Pos, Size);
|
||||
auto TopLevelDockWidget = FloatingWidget->topLevelDockWidget();
|
||||
if (TopLevelDockWidget)
|
||||
{
|
||||
TopLevelDockWidget->emitTopLevelChanged(true);
|
||||
}
|
||||
|
||||
return FloatingWidget;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockAreaTabBar::startFloating(const QPoint& Pos)
|
||||
{
|
||||
d->FloatingWidget = makeAreaFloating(Pos);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockAreaTabBar::setCurrentIndex(int index)
|
||||
{
|
||||
if (index == d->CurrentIndex)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (index < -1 || index > (count() - 1))
|
||||
{
|
||||
qWarning() << Q_FUNC_INFO << "Invalid index" << index;
|
||||
return;
|
||||
}
|
||||
|
||||
emit currentChanging(index);
|
||||
d->CurrentIndex = index;
|
||||
d->updateTabs();
|
||||
emit currentChanged(index);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
int CDockAreaTabBar::count() const
|
||||
{
|
||||
// The tab bar contains a stretch item as last item
|
||||
return d->TabsLayout->count() - 1;
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
void CDockAreaTabBar::insertTab(int Index, CDockWidgetTab* Tab)
|
||||
{
|
||||
d->TabsLayout->insertWidget(Index, Tab);
|
||||
connect(Tab, SIGNAL(clicked()), this, SLOT(onTabClicked()));
|
||||
connect(Tab, SIGNAL(closeRequested()), this, SLOT(onTabCloseRequested()));
|
||||
connect(Tab, SIGNAL(closeOtherTabsRequested()), this, SLOT(onCloseOtherTabsRequested()));
|
||||
connect(Tab, SIGNAL(moved(const QPoint&)), this, SLOT(onTabWidgetMoved(const QPoint&)));
|
||||
Tab->installEventFilter(this);
|
||||
emit tabInserted(Index);
|
||||
if (Index <= d->CurrentIndex)
|
||||
{
|
||||
setCurrentIndex(d->CurrentIndex + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
void CDockAreaTabBar::removeTab(CDockWidgetTab* Tab)
|
||||
{
|
||||
if (!count())
|
||||
{
|
||||
return;
|
||||
}
|
||||
qDebug() << "CDockAreaTabBar::removeTab ";
|
||||
int NewCurrentIndex = currentIndex();
|
||||
int RemoveIndex = d->TabsLayout->indexOf(Tab);
|
||||
if (count() == 1)
|
||||
{
|
||||
NewCurrentIndex = -1;
|
||||
}
|
||||
if (NewCurrentIndex > RemoveIndex)
|
||||
{
|
||||
NewCurrentIndex--;
|
||||
}
|
||||
else if (NewCurrentIndex == RemoveIndex)
|
||||
{
|
||||
NewCurrentIndex = -1;
|
||||
// First we walk to the right to search for the next visible tab
|
||||
for (int i = (RemoveIndex + 1); i < count(); ++i)
|
||||
{
|
||||
if (tab(i)->isVisibleTo(this))
|
||||
{
|
||||
NewCurrentIndex = i - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If there is no visible tab right to this tab then we walk to
|
||||
// the left to find a visible tab
|
||||
if (NewCurrentIndex < 0)
|
||||
{
|
||||
for (int i = (RemoveIndex - 1); i >= 0; --i)
|
||||
{
|
||||
if (tab(i)->isVisibleTo(this))
|
||||
{
|
||||
NewCurrentIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
emit removingTab(RemoveIndex);
|
||||
d->TabsLayout->removeWidget(Tab);
|
||||
Tab->disconnect(this);
|
||||
Tab->removeEventFilter(this);
|
||||
qDebug() << "NewCurrentIndex " << NewCurrentIndex;
|
||||
if (NewCurrentIndex != d->CurrentIndex)
|
||||
{
|
||||
setCurrentIndex(NewCurrentIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
d->updateTabs();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
int CDockAreaTabBar::currentIndex() const
|
||||
{
|
||||
return d->CurrentIndex;
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
CDockWidgetTab* CDockAreaTabBar::currentTab() const
|
||||
{
|
||||
if (d->CurrentIndex < 0)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
return qobject_cast<CDockWidgetTab*>(d->TabsLayout->itemAt(d->CurrentIndex)->widget());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
void CDockAreaTabBar::onTabClicked()
|
||||
{
|
||||
CDockWidgetTab* Tab = qobject_cast<CDockWidgetTab*>(sender());
|
||||
if (!Tab)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int index = d->TabsLayout->indexOf(Tab);
|
||||
if (index < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
setCurrentIndex(index);
|
||||
emit tabBarClicked(index);
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
void CDockAreaTabBar::onTabCloseRequested()
|
||||
{
|
||||
CDockWidgetTab* Tab = qobject_cast<CDockWidgetTab*>(sender());
|
||||
int Index = d->TabsLayout->indexOf(Tab);
|
||||
closeTab(Index);
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
void CDockAreaTabBar::onCloseOtherTabsRequested()
|
||||
{
|
||||
auto Sender = qobject_cast<CDockWidgetTab*>(sender());
|
||||
for (int i = 0; i < count(); ++i)
|
||||
{
|
||||
auto Tab = tab(i);
|
||||
if (Tab->isClosable() && !Tab->isHidden() && Tab != Sender)
|
||||
{
|
||||
closeTab(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
CDockWidgetTab* CDockAreaTabBar::tab(int Index) const
|
||||
{
|
||||
if (Index >= count() || Index < 0)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return qobject_cast<CDockWidgetTab*>(d->TabsLayout->itemAt(Index)->widget());
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
void CDockAreaTabBar::onTabWidgetMoved(const QPoint& GlobalPos)
|
||||
{
|
||||
CDockWidgetTab* MovingTab = qobject_cast<CDockWidgetTab*>(sender());
|
||||
if (!MovingTab)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int fromIndex = d->TabsLayout->indexOf(MovingTab);
|
||||
auto MousePos = mapFromGlobal(GlobalPos);
|
||||
int toIndex = -1;
|
||||
// Find tab under mouse
|
||||
for (int i = 0; i < count(); ++i)
|
||||
{
|
||||
CDockWidgetTab* DropTab = tab(i);
|
||||
if (DropTab == MovingTab || !DropTab->isVisibleTo(this)
|
||||
|| !DropTab->geometry().contains(MousePos))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
toIndex = d->TabsLayout->indexOf(DropTab);
|
||||
if (toIndex == fromIndex)
|
||||
{
|
||||
toIndex = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (toIndex < 0)
|
||||
{
|
||||
toIndex = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Now check if the mouse is behind the last tab
|
||||
if (toIndex < 0)
|
||||
{
|
||||
if (MousePos.x() > tab(count() - 1)->geometry().right())
|
||||
{
|
||||
qDebug() << "after all tabs";
|
||||
toIndex = count() - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
toIndex = fromIndex;
|
||||
}
|
||||
}
|
||||
|
||||
d->TabsLayout->removeWidget(MovingTab);
|
||||
d->TabsLayout->insertWidget(toIndex, MovingTab);
|
||||
if (toIndex >= 0)
|
||||
{
|
||||
qDebug() << "tabMoved from " << fromIndex << " to " << toIndex;
|
||||
emit tabMoved(fromIndex, toIndex);
|
||||
setCurrentIndex(toIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
void CDockAreaTabBar::closeTab(int Index)
|
||||
{
|
||||
if (Index < 0 || Index >= count())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto Tab = tab(Index);
|
||||
if (Tab->isHidden())
|
||||
{
|
||||
return;
|
||||
}
|
||||
emit tabCloseRequested(Index);
|
||||
Tab->hide();
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
bool CDockAreaTabBar::eventFilter(QObject *watched, QEvent *event)
|
||||
{
|
||||
bool Result = Super::eventFilter(watched, event);
|
||||
CDockWidgetTab* Tab = qobject_cast<CDockWidgetTab*>(watched);
|
||||
if (!Tab)
|
||||
{
|
||||
return Result;
|
||||
}
|
||||
|
||||
switch (event->type())
|
||||
{
|
||||
case QEvent::Hide:
|
||||
emit tabClosed(d->TabsLayout->indexOf(Tab)); break;
|
||||
case QEvent::Show:
|
||||
emit tabOpened(d->TabsLayout->indexOf(Tab)); break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
bool CDockAreaTabBar::isTabOpen(int Index) const
|
||||
{
|
||||
if (Index < 0 || Index >= count())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return !tab(Index)->isHidden();
|
||||
}
|
||||
|
||||
} // namespace ads
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// EOF DockAreaTabBar.cpp
|
||||
218
src/DockAreaTabBar.h
Normal file
@@ -0,0 +1,218 @@
|
||||
#ifndef DockAreaTabBarH
|
||||
#define DockAreaTabBarH
|
||||
/*******************************************************************************
|
||||
** 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 DockAreaTabBar.h
|
||||
/// \author Uwe Kindler
|
||||
/// \date 24.08.2018
|
||||
/// \brief Declaration of CDockAreaTabBar class
|
||||
//============================================================================
|
||||
|
||||
//============================================================================
|
||||
// INCLUDES
|
||||
//============================================================================
|
||||
#include <QScrollArea>
|
||||
|
||||
namespace ads
|
||||
{
|
||||
class CDockAreaWidget;
|
||||
class CDockWidgetTab;
|
||||
struct DockAreaTabBarPrivate;
|
||||
class CDockAreaTitleBar;
|
||||
class CFloatingDockContainer;
|
||||
|
||||
/**
|
||||
* Custom tabbar implementation for tab area that is shown on top of a
|
||||
* dock area widget.
|
||||
* The tabbar displays the tab widgets of the contained dock widgets.
|
||||
*/
|
||||
class CDockAreaTabBar : public QScrollArea
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
DockAreaTabBarPrivate* d; ///< private data (pimpl)
|
||||
friend class DockAreaTabBarPrivate;
|
||||
friend class CDockAreaTitleBar;
|
||||
|
||||
private slots:
|
||||
void onTabClicked();
|
||||
void onTabCloseRequested();
|
||||
void onCloseOtherTabsRequested();
|
||||
void onTabWidgetMoved(const QPoint& GlobalPos);
|
||||
|
||||
protected:
|
||||
virtual void wheelEvent(QWheelEvent* Event) override;
|
||||
/**
|
||||
* Stores mouse position to detect dragging
|
||||
*/
|
||||
virtual void mousePressEvent(QMouseEvent* ev) override;
|
||||
|
||||
/**
|
||||
* Stores mouse position to detect dragging
|
||||
*/
|
||||
virtual void mouseReleaseEvent(QMouseEvent* ev) override;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Double clicking the title bar also starts floating of the complete area
|
||||
*/
|
||||
virtual void mouseDoubleClickEvent(QMouseEvent *event) override;
|
||||
|
||||
/**
|
||||
* Starts floating
|
||||
*/
|
||||
void startFloating(const QPoint& Pos);
|
||||
|
||||
/**
|
||||
* Makes the dock area loating
|
||||
*/
|
||||
CFloatingDockContainer* makeAreaFloating(const QPoint& Pos);
|
||||
|
||||
|
||||
public:
|
||||
using Super = QScrollArea;
|
||||
/**
|
||||
* Default Constructor
|
||||
*/
|
||||
CDockAreaTabBar(CDockAreaWidget* parent);
|
||||
|
||||
/**
|
||||
* Virtual Destructor
|
||||
*/
|
||||
virtual ~CDockAreaTabBar();
|
||||
|
||||
/**
|
||||
* Inserts the given dock widget tab at the given position.
|
||||
* Inserting a new tab at an index less than or equal to the current index
|
||||
* will increment the current index, but keep the current tab.
|
||||
*/
|
||||
void insertTab(int Index, CDockWidgetTab* Tab);
|
||||
|
||||
/**
|
||||
* Removes the given DockWidgetTab from the tabbar
|
||||
*/
|
||||
void removeTab(CDockWidgetTab* Tab);
|
||||
|
||||
/**
|
||||
* Returns the number of tabs in this tabbar
|
||||
*/
|
||||
int count() const;
|
||||
|
||||
/**
|
||||
* Returns the current index or -1 if no tab is selected
|
||||
*/
|
||||
int currentIndex() const;
|
||||
|
||||
/**
|
||||
* Returns the current tab or a nullptr if no tab is selected.
|
||||
*/
|
||||
CDockWidgetTab* currentTab() const;
|
||||
|
||||
/**
|
||||
* Returns the tab with the given index
|
||||
*/
|
||||
CDockWidgetTab* tab(int Index) const;
|
||||
|
||||
/**
|
||||
* Filters the tab widget events
|
||||
*/
|
||||
virtual bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
|
||||
/**
|
||||
* This function returns true if the tab is open, that means if it is
|
||||
* visible to the user. If the function returns false, the tab is
|
||||
* closed
|
||||
*/
|
||||
bool isTabOpen(int Index) const;
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* This property sets the index of the tab bar's visible tab
|
||||
*/
|
||||
void setCurrentIndex(int Index);
|
||||
|
||||
/**
|
||||
* This function will close the tab given in Index param.
|
||||
* Closing a tab means, the tab will be hidden, it will not be removed
|
||||
*/
|
||||
void closeTab(int Index);
|
||||
|
||||
signals:
|
||||
/**
|
||||
* This signal is emitted when the tab bar's current tab is about to be changed. The new
|
||||
* current has the given index, or -1 if there isn't a new one.
|
||||
*/
|
||||
void currentChanging(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
|
||||
*/
|
||||
void currentChanged(int Index);
|
||||
|
||||
/**
|
||||
* This signal is emitted when user clicks on a tab
|
||||
*/
|
||||
void tabBarClicked(int index);
|
||||
|
||||
/**
|
||||
* This signal is emitted when the close button on a tab is clicked.
|
||||
* The index is the index that should be closed.
|
||||
*/
|
||||
void tabCloseRequested(int index);
|
||||
|
||||
/**
|
||||
* This signal is emitted if a tab has been closed
|
||||
*/
|
||||
void tabClosed(int index);
|
||||
|
||||
/**
|
||||
* This signal is emitted if a tab has been opened.
|
||||
* A tab is opened if it has been made visible
|
||||
*/
|
||||
void tabOpened(int index);
|
||||
|
||||
/**
|
||||
* This signal is emitted when the tab has moved the tab at index position
|
||||
* from to index position to.
|
||||
*/
|
||||
void tabMoved(int from, int to);
|
||||
|
||||
/**
|
||||
* This signal is emitted, just before the tab with the given index is
|
||||
* removed
|
||||
*/
|
||||
void removingTab(int index);
|
||||
|
||||
/**
|
||||
* This signal is emitted if a tab has been inserted
|
||||
*/
|
||||
void tabInserted(int index);
|
||||
}; // class CDockAreaTabBar
|
||||
} // namespace ads
|
||||
//-----------------------------------------------------------------------------
|
||||
#endif // DockAreaTabBarH
|
||||
|
||||
316
src/DockAreaTitleBar.cpp
Normal file
@@ -0,0 +1,316 @@
|
||||
/*******************************************************************************
|
||||
** 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 DockAreaTitleBar.cpp
|
||||
/// \author Uwe Kindler
|
||||
/// \date 12.10.2018
|
||||
/// \brief Implementation of CDockAreaTitleBar class
|
||||
//============================================================================
|
||||
|
||||
//============================================================================
|
||||
// INCLUDES
|
||||
//============================================================================
|
||||
#include "DockAreaTitleBar.h"
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QToolButton>
|
||||
#include <QBoxLayout>
|
||||
#include <QStyle>
|
||||
#include <QMenu>
|
||||
#include <QScrollArea>
|
||||
#include <QMouseEvent>
|
||||
#include <QDebug>
|
||||
|
||||
#include "ads_globals.h"
|
||||
#include "FloatingDockContainer.h"
|
||||
#include "DockAreaWidget.h"
|
||||
#include "DockOverlay.h"
|
||||
#include "DockManager.h"
|
||||
#include "DockWidget.h"
|
||||
#include "DockWidgetTab.h"
|
||||
#include "DockAreaTabBar.h"
|
||||
|
||||
|
||||
namespace ads
|
||||
{
|
||||
using tTileBarButton = QToolButton;
|
||||
/**
|
||||
* Private data class of CDockAreaTitleBar class (pimpl)
|
||||
*/
|
||||
struct DockAreaTitleBarPrivate
|
||||
{
|
||||
CDockAreaTitleBar* _this;
|
||||
tTileBarButton* TabsMenuButton;
|
||||
tTileBarButton* UndockButton;
|
||||
tTileBarButton* CloseButton;
|
||||
QBoxLayout* TopLayout;
|
||||
CDockAreaWidget* DockArea;
|
||||
CDockAreaTabBar* TabBar;
|
||||
bool MenuOutdated = true;
|
||||
QMenu* TabsMenu;
|
||||
|
||||
/**
|
||||
* Private data constructor
|
||||
*/
|
||||
DockAreaTitleBarPrivate(CDockAreaTitleBar* _public);
|
||||
|
||||
/**
|
||||
* Creates the title bar close and menu buttons
|
||||
*/
|
||||
void createButtons();
|
||||
|
||||
/**
|
||||
* Creates the internal TabBar
|
||||
*/
|
||||
void createTabBar();
|
||||
|
||||
/**
|
||||
* Convenience function for DockManager access
|
||||
*/
|
||||
CDockManager* dockManager() const
|
||||
{
|
||||
return DockArea->dockManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given config flag is set
|
||||
*/
|
||||
bool testConfigFlag(CDockManager::eConfigFlag Flag) const
|
||||
{
|
||||
return DockArea->dockManager()->configFlags().testFlag(Flag);
|
||||
}
|
||||
};// struct DockAreaTitleBarPrivate
|
||||
|
||||
|
||||
|
||||
//============================================================================
|
||||
DockAreaTitleBarPrivate::DockAreaTitleBarPrivate(CDockAreaTitleBar* _public) :
|
||||
_this(_public)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void DockAreaTitleBarPrivate::createButtons()
|
||||
{
|
||||
TabsMenuButton = new tTileBarButton();
|
||||
TabsMenuButton->setObjectName("tabsMenuButton");
|
||||
TabsMenuButton->setAutoRaise(true);
|
||||
TabsMenuButton->setPopupMode(QToolButton::InstantPopup);
|
||||
TabsMenuButton->setIcon(_this->style()->standardIcon(QStyle::SP_TitleBarUnshadeButton));
|
||||
|
||||
QMenu* TabsMenu = new QMenu(TabsMenuButton);
|
||||
_this->connect(TabsMenu, SIGNAL(aboutToShow()), SLOT(onTabsMenuAboutToShow()));
|
||||
TabsMenuButton->setMenu(TabsMenu);
|
||||
TabsMenuButton->setToolTip(QObject::tr("List all tabs"));
|
||||
TabsMenuButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
|
||||
TopLayout->addWidget(TabsMenuButton, 0);
|
||||
_this->connect(TabsMenuButton->menu(), SIGNAL(triggered(QAction*)),
|
||||
SLOT(onTabsMenuActionTriggered(QAction*)));
|
||||
|
||||
// Undock button
|
||||
UndockButton = new tTileBarButton();
|
||||
UndockButton->setObjectName("undockButton");
|
||||
UndockButton->setAutoRaise(true);
|
||||
UndockButton->setToolTip(QObject::tr("Detach Group"));
|
||||
UndockButton->setIcon(_this->style()->standardIcon(QStyle::SP_TitleBarNormalButton));
|
||||
UndockButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
|
||||
TopLayout->addWidget(UndockButton, 0);
|
||||
_this->connect(UndockButton, SIGNAL(clicked()), SLOT(onUndockButtonClicked()));
|
||||
|
||||
CloseButton = new tTileBarButton();
|
||||
CloseButton->setObjectName("closeButton");
|
||||
CloseButton->setAutoRaise(true);
|
||||
|
||||
// The standard icons do not look good on high DPI screens
|
||||
QIcon CloseIcon = _this->style()->standardIcon(QStyle::SP_TitleBarCloseButton);
|
||||
QPixmap normalPixmap = _this->style()->standardPixmap(QStyle::SP_TitleBarCloseButton, 0, CloseButton);
|
||||
QPixmap disabledPixmap = internal::createTransparentPixmap(normalPixmap, 0.25);
|
||||
CloseIcon.addPixmap(disabledPixmap, QIcon::Disabled);
|
||||
|
||||
CloseButton->setIcon(CloseIcon);
|
||||
if (testConfigFlag(CDockManager::DockAreaCloseButtonClosesTab))
|
||||
{
|
||||
CloseButton->setToolTip(QObject::tr("Close Active Tab"));
|
||||
}
|
||||
else
|
||||
{
|
||||
CloseButton->setToolTip(QObject::tr("Close Group"));
|
||||
}
|
||||
CloseButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
|
||||
TopLayout->addWidget(CloseButton, 0);
|
||||
_this->connect(CloseButton, SIGNAL(clicked()), SLOT(onCloseButtonClicked()));
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void DockAreaTitleBarPrivate::createTabBar()
|
||||
{
|
||||
TabBar = new CDockAreaTabBar(DockArea);
|
||||
TopLayout->addWidget(TabBar);
|
||||
_this->connect(TabBar, SIGNAL(tabClosed(int)), SLOT(markTabsMenuOutdated()));
|
||||
_this->connect(TabBar, SIGNAL(tabOpened(int)), SLOT(markTabsMenuOutdated()));
|
||||
_this->connect(TabBar, SIGNAL(tabInserted(int)), SLOT(markTabsMenuOutdated()));
|
||||
_this->connect(TabBar, SIGNAL(removingTab(int)), SLOT(markTabsMenuOutdated()));
|
||||
_this->connect(TabBar, SIGNAL(tabMoved(int, int)), SLOT(markTabsMenuOutdated()));
|
||||
_this->connect(TabBar, SIGNAL(currentChanged(int)), SLOT(onCurrentTabChanged(int)));
|
||||
_this->connect(TabBar, SIGNAL(tabBarClicked(int)), SIGNAL(tabBarClicked(int)));
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
CDockAreaTitleBar::CDockAreaTitleBar(CDockAreaWidget* parent) :
|
||||
QFrame(parent),
|
||||
d(new DockAreaTitleBarPrivate(this))
|
||||
{
|
||||
d->DockArea = parent;
|
||||
|
||||
setObjectName("dockAreaTitleBar");
|
||||
d->TopLayout = new QBoxLayout(QBoxLayout::LeftToRight);
|
||||
d->TopLayout->setContentsMargins(0, 0, 0, 0);
|
||||
d->TopLayout->setSpacing(0);
|
||||
setLayout(d->TopLayout);
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
|
||||
d->createTabBar();
|
||||
d->createButtons();
|
||||
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
CDockAreaTitleBar::~CDockAreaTitleBar()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
CDockAreaTabBar* CDockAreaTitleBar::tabBar() const
|
||||
{
|
||||
return d->TabBar;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockAreaTitleBar::markTabsMenuOutdated()
|
||||
{
|
||||
d->MenuOutdated = true;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockAreaTitleBar::onTabsMenuAboutToShow()
|
||||
{
|
||||
if (!d->MenuOutdated)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QMenu* menu = d->TabsMenuButton->menu();
|
||||
menu->clear();
|
||||
for (int i = 0; i < d->TabBar->count(); ++i)
|
||||
{
|
||||
if (!d->TabBar->isTabOpen(i))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
auto Tab = d->TabBar->tab(i);
|
||||
QAction* Action = menu->addAction(Tab->icon(), Tab->text());
|
||||
Action->setData(i);
|
||||
}
|
||||
|
||||
d->MenuOutdated = false;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockAreaTitleBar::onCloseButtonClicked()
|
||||
{
|
||||
qDebug() << "CDockAreaTitleBar::onCloseButtonClicked";
|
||||
if (d->testConfigFlag(CDockManager::DockAreaCloseButtonClosesTab))
|
||||
{
|
||||
d->TabBar->closeTab(d->TabBar->currentIndex());
|
||||
}
|
||||
else
|
||||
{
|
||||
d->DockArea->closeArea();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockAreaTitleBar::onUndockButtonClicked()
|
||||
{
|
||||
d->TabBar->makeAreaFloating(mapFromGlobal(QCursor::pos()));
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockAreaTitleBar::onTabsMenuActionTriggered(QAction* Action)
|
||||
{
|
||||
int Index = Action->data().toInt();
|
||||
d->TabBar->setCurrentIndex(Index);
|
||||
emit tabBarClicked(Index);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockAreaTitleBar::onCurrentTabChanged(int Index)
|
||||
{
|
||||
if (Index < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (d->testConfigFlag(CDockManager::DockAreaCloseButtonClosesTab))
|
||||
{
|
||||
CDockWidget* DockWidget = d->TabBar->tab(Index)->dockWidget();
|
||||
d->CloseButton->setEnabled(DockWidget->features().testFlag(CDockWidget::DockWidgetClosable));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
QAbstractButton* CDockAreaTitleBar::button(TitleBarButton which) const
|
||||
{
|
||||
switch (which)
|
||||
{
|
||||
case TitleBarButtonTabsMenu: return d->TabsMenuButton;
|
||||
case TitleBarButtonUndock: return d->UndockButton;
|
||||
case TitleBarButtonClose: return d->CloseButton;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockAreaTitleBar::setVisible(bool Visible)
|
||||
{
|
||||
Super::setVisible(Visible);
|
||||
}
|
||||
|
||||
|
||||
} // namespace ads
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// EOF DockAreaTitleBar.cpp
|
||||
100
src/DockAreaTitleBar.h
Normal file
@@ -0,0 +1,100 @@
|
||||
#ifndef DockAreaTitleBarH
|
||||
#define DockAreaTitleBarH
|
||||
/*******************************************************************************
|
||||
** 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 DockAreaTitleBar.h
|
||||
/// \author Uwe Kindler
|
||||
/// \date 12.10.2018
|
||||
/// \brief Declaration of CDockAreaTitleBar class
|
||||
//============================================================================
|
||||
|
||||
|
||||
//============================================================================
|
||||
// INCLUDES
|
||||
//============================================================================
|
||||
#include <QFrame>
|
||||
|
||||
#include "ads_globals.h"
|
||||
|
||||
class QAbstractButton;
|
||||
|
||||
namespace ads
|
||||
{
|
||||
class CDockAreaTabBar;
|
||||
class CDockAreaWidget;
|
||||
struct DockAreaTitleBarPrivate;
|
||||
|
||||
/**
|
||||
* Title bar of a dock area
|
||||
*/
|
||||
class CDockAreaTitleBar : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
DockAreaTitleBarPrivate* d; ///< private data (pimpl)
|
||||
friend class DockAreaTitleBarPrivate;
|
||||
|
||||
private slots:
|
||||
void markTabsMenuOutdated();
|
||||
void onTabsMenuAboutToShow();
|
||||
void onCloseButtonClicked();
|
||||
void onUndockButtonClicked();
|
||||
void onTabsMenuActionTriggered(QAction* Action);
|
||||
void onCurrentTabChanged(int Index);
|
||||
|
||||
public:
|
||||
using Super = QFrame;
|
||||
/**
|
||||
* Default Constructor
|
||||
*/
|
||||
CDockAreaTitleBar(CDockAreaWidget* parent);
|
||||
|
||||
/**
|
||||
* Virtual Destructor
|
||||
*/
|
||||
virtual ~CDockAreaTitleBar();
|
||||
|
||||
/**
|
||||
* Returns the pointer to the tabBar()
|
||||
*/
|
||||
CDockAreaTabBar* tabBar() const;
|
||||
|
||||
/**
|
||||
* Returns the button corresponding to the given title bar button identifier
|
||||
*/
|
||||
QAbstractButton* button(TitleBarButton which) const;
|
||||
|
||||
/**
|
||||
* This function is here for debug reasons
|
||||
*/
|
||||
virtual void setVisible(bool Visible) override;
|
||||
|
||||
signals:
|
||||
/**
|
||||
* This signal is emitted if a tab in the tab bar is clicked by the user
|
||||
* or if the user clicks on a tab item in the title bar tab menu.
|
||||
*/
|
||||
void tabBarClicked(int index);
|
||||
}; // class name
|
||||
}
|
||||
// namespace ads
|
||||
//-----------------------------------------------------------------------------
|
||||
#endif // DockAreaTitleBarH
|
||||
785
src/DockAreaWidget.cpp
Normal file
@@ -0,0 +1,785 @@
|
||||
/*******************************************************************************
|
||||
** 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 <DockWidgetTab.h>
|
||||
#include "DockAreaWidget.h"
|
||||
|
||||
#include <QStackedLayout>
|
||||
#include <QScrollBar>
|
||||
#include <QScrollArea>
|
||||
#include <QWheelEvent>
|
||||
#include <QStyle>
|
||||
#include <QPushButton>
|
||||
#include <QDebug>
|
||||
#include <QMenu>
|
||||
#include <QSplitter>
|
||||
#include <QXmlStreamWriter>
|
||||
#include <QVector>
|
||||
#include <QList>
|
||||
|
||||
|
||||
#include "DockContainerWidget.h"
|
||||
#include "DockWidget.h"
|
||||
#include "FloatingDockContainer.h"
|
||||
#include "DockManager.h"
|
||||
#include "DockOverlay.h"
|
||||
#include "DockAreaTabBar.h"
|
||||
#include "DockSplitter.h"
|
||||
#include "DockAreaTitleBar.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
|
||||
namespace ads
|
||||
{
|
||||
static const char* const INDEX_PROPERTY = "index";
|
||||
static const char* const ACTION_PROPERTY = "action";
|
||||
static const char* const DOCKWIDGET_PROPERTY = "dockwidget";
|
||||
static const int APPEND = -1;
|
||||
|
||||
|
||||
/**
|
||||
* New dock area layout mimics stack layout but only inserts the current
|
||||
* widget into the internal QLayout object
|
||||
*/
|
||||
class CDockAreaLayout
|
||||
{
|
||||
private:
|
||||
QBoxLayout* m_ParentLayout;
|
||||
QList<QWidget*> m_Widgets;
|
||||
int m_CurrentIndex = -1;
|
||||
QWidget* m_CurrentWidget = nullptr;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Creates an instance with the given parent layout
|
||||
*/
|
||||
CDockAreaLayout(QBoxLayout* ParentLayout)
|
||||
: m_ParentLayout(ParentLayout)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of widgets in this layout
|
||||
*/
|
||||
int count() const
|
||||
{
|
||||
return m_Widgets.count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the widget at the given index position into the internal widget
|
||||
* list
|
||||
*/
|
||||
void insertWidget(int index, QWidget* Widget)
|
||||
{
|
||||
Widget->setParent(0);
|
||||
if (index < 0)
|
||||
{
|
||||
index = m_Widgets.count();
|
||||
}
|
||||
m_Widgets.insert(index, Widget);
|
||||
if (m_CurrentIndex < 0)
|
||||
{
|
||||
setCurrentIndex(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (index <= m_CurrentIndex )
|
||||
{
|
||||
++m_CurrentIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given widget from the lyout
|
||||
*/
|
||||
void removeWidget(QWidget* Widget)
|
||||
{
|
||||
if (currentWidget() == Widget)
|
||||
{
|
||||
auto LayoutItem = m_ParentLayout->takeAt(1);
|
||||
if (LayoutItem)
|
||||
{
|
||||
LayoutItem->widget()->setParent(0);
|
||||
}
|
||||
m_CurrentWidget = nullptr;
|
||||
m_CurrentIndex = -1;
|
||||
}
|
||||
m_Widgets.removeOne(Widget);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current selected widget
|
||||
*/
|
||||
QWidget* currentWidget() const
|
||||
{
|
||||
return m_CurrentWidget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates the widget with the give index.
|
||||
*/
|
||||
void setCurrentIndex(int index)
|
||||
{
|
||||
QWidget *prev = currentWidget();
|
||||
QWidget *next = widget(index);
|
||||
if (!next || (next == prev && !m_CurrentWidget))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool reenableUpdates = false;
|
||||
QWidget *parent = m_ParentLayout->parentWidget();
|
||||
|
||||
if (parent && parent->updatesEnabled())
|
||||
{
|
||||
reenableUpdates = true;
|
||||
parent->setUpdatesEnabled(false);
|
||||
}
|
||||
|
||||
auto LayoutItem = m_ParentLayout->takeAt(1);
|
||||
if (LayoutItem)
|
||||
{
|
||||
LayoutItem->widget()->setParent(0);
|
||||
}
|
||||
|
||||
m_ParentLayout->addWidget(next);
|
||||
if (prev)
|
||||
{
|
||||
prev->hide();
|
||||
}
|
||||
m_CurrentIndex = index;
|
||||
m_CurrentWidget = next;
|
||||
|
||||
|
||||
if (reenableUpdates)
|
||||
{
|
||||
parent->setUpdatesEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the current active widget
|
||||
*/
|
||||
int currentIndex() const
|
||||
{
|
||||
return m_CurrentIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there are no widgets in the layout
|
||||
*/
|
||||
bool isEmpty() const
|
||||
{
|
||||
return m_Widgets.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the given widget
|
||||
*/
|
||||
int indexOf(QWidget* w) const
|
||||
{
|
||||
return m_Widgets.indexOf(w);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the widget for the given index
|
||||
*/
|
||||
QWidget* widget(int index) const
|
||||
{
|
||||
return (index < m_Widgets.size()) ? m_Widgets.at(index) : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the geometry of the current active widget
|
||||
*/
|
||||
QRect geometry() const
|
||||
{
|
||||
return m_Widgets.empty() ? QRect() : currentWidget()->geometry();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
using DockAreaLayout = CDockAreaLayout;
|
||||
|
||||
|
||||
/**
|
||||
* Private data class of CDockAreaWidget class (pimpl)
|
||||
*/
|
||||
struct DockAreaWidgetPrivate
|
||||
{
|
||||
CDockAreaWidget* _this;
|
||||
QBoxLayout* Layout;
|
||||
DockAreaLayout* ContentsLayout;
|
||||
CDockAreaTitleBar* TitleBar;
|
||||
CDockManager* DockManager = nullptr;
|
||||
bool UpdateCloseButton = false;
|
||||
|
||||
/**
|
||||
* Private data constructor
|
||||
*/
|
||||
DockAreaWidgetPrivate(CDockAreaWidget* _public);
|
||||
|
||||
/**
|
||||
* Creates the layout for top area with tabs and close button
|
||||
*/
|
||||
void createTitleBar();
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
CDockWidgetTab* tabWidgetAt(int index)
|
||||
{
|
||||
return dockWidgetAt(index)->tabWidget();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function for tabbar access
|
||||
*/
|
||||
CDockAreaTabBar* tabBar() const
|
||||
{
|
||||
return TitleBar->tabBar();
|
||||
}
|
||||
|
||||
/**
|
||||
* Udpates the enable state of the close button
|
||||
*/
|
||||
void updateCloseButtonState();
|
||||
};
|
||||
// struct DockAreaWidgetPrivate
|
||||
|
||||
|
||||
//============================================================================
|
||||
DockAreaWidgetPrivate::DockAreaWidgetPrivate(CDockAreaWidget* _public) :
|
||||
_this(_public)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void DockAreaWidgetPrivate::createTitleBar()
|
||||
{
|
||||
TitleBar = new CDockAreaTitleBar(_this);
|
||||
Layout->addWidget(TitleBar);
|
||||
_this->connect(tabBar(), SIGNAL(tabCloseRequested(int)),
|
||||
SLOT(onTabCloseRequested(int)));
|
||||
_this->connect(TitleBar, SIGNAL(tabBarClicked(int)),
|
||||
SLOT(setCurrentIndex(int)));
|
||||
_this->connect(tabBar(), SIGNAL(tabMoved(int, int)),
|
||||
SLOT(reorderDockWidget(int, int)));
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void DockAreaWidgetPrivate::updateCloseButtonState()
|
||||
{
|
||||
if (_this->isHidden())
|
||||
{
|
||||
UpdateCloseButton = true;
|
||||
return;
|
||||
}
|
||||
|
||||
TitleBar->button(TitleBarButtonClose)->setEnabled(
|
||||
_this->features().testFlag(CDockWidget::DockWidgetClosable));
|
||||
UpdateCloseButton = false;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
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->createTitleBar();
|
||||
d->ContentsLayout = new DockAreaLayout(d->Layout);
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
CDockAreaWidget::~CDockAreaWidget()
|
||||
{
|
||||
qDebug() << "~CDockAreaWidget()";
|
||||
delete d->ContentsLayout;
|
||||
delete d;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
CDockManager* CDockAreaWidget::dockManager() const
|
||||
{
|
||||
return d->DockManager;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
CDockContainerWidget* CDockAreaWidget::dockContainer() const
|
||||
{
|
||||
return internal::findParent<CDockContainerWidget*>(this);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
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->tabWidget()->setDockAreaWidget(this);
|
||||
auto TabWidget = DockWidget->tabWidget();
|
||||
// Inserting the tab will change the current index which in turn will
|
||||
// make the tab widget visible in the slot
|
||||
d->tabBar()->blockSignals(true);
|
||||
d->tabBar()->insertTab(index, TabWidget);
|
||||
d->tabBar()->blockSignals(false);
|
||||
TabWidget->setVisible(!DockWidget->isClosed());
|
||||
DockWidget->setProperty(INDEX_PROPERTY, index);
|
||||
if (Activate)
|
||||
{
|
||||
setCurrentIndex(index);
|
||||
}
|
||||
DockWidget->setDockArea(this);
|
||||
d->updateCloseButtonState();
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockAreaWidget::removeDockWidget(CDockWidget* DockWidget)
|
||||
{
|
||||
qDebug() << "CDockAreaWidget::removeDockWidget";
|
||||
auto NextOpenDockWidget = nextOpenDockWidget(DockWidget);
|
||||
|
||||
d->ContentsLayout->removeWidget(DockWidget);
|
||||
auto TabWidget = DockWidget->tabWidget();
|
||||
TabWidget->hide();
|
||||
d->tabBar()->removeTab(TabWidget);
|
||||
if (NextOpenDockWidget)
|
||||
{
|
||||
setCurrentDockWidget(NextOpenDockWidget);
|
||||
}
|
||||
else if (d->ContentsLayout->isEmpty())
|
||||
{
|
||||
qDebug() << "Dock Area empty";
|
||||
dockContainer()->removeDockArea(this);
|
||||
this->deleteLater();
|
||||
}
|
||||
else
|
||||
{
|
||||
// if contents layout is not empty but there are no more open dock
|
||||
// widgets, then we need to hide the dock area because it does not
|
||||
// contain any visible content
|
||||
hideAreaWithNoVisibleContent();
|
||||
}
|
||||
|
||||
d->updateCloseButtonState();
|
||||
updateTitleBarVisibility();
|
||||
auto TopLevelDockWidget = dockContainer()->topLevelDockWidget();
|
||||
if (TopLevelDockWidget)
|
||||
{
|
||||
TopLevelDockWidget->emitTopLevelChanged(true);
|
||||
}
|
||||
|
||||
#if (ADS_DEBUG_LEVEL > 0)
|
||||
CDockContainerWidget* DockContainer = dockContainer();
|
||||
DockContainer->dumpLayout();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockAreaWidget::hideAreaWithNoVisibleContent()
|
||||
{
|
||||
this->toggleView(false);
|
||||
|
||||
// Hide empty parent splitters
|
||||
auto Splitter = internal::findParent<CDockSplitter*>(this);
|
||||
internal::hideEmptyParentSplitters(Splitter);
|
||||
|
||||
//Hide empty floating widget
|
||||
CDockContainerWidget* Container = this->dockContainer();
|
||||
if (!Container->isFloating())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
updateTitleBarVisibility();
|
||||
auto TopLevelWidget = Container->topLevelDockWidget();
|
||||
auto FloatingWidget = Container->floatingWidget();
|
||||
if (TopLevelWidget)
|
||||
{
|
||||
FloatingWidget->updateWindowTitle();
|
||||
CDockWidget::emitTopLevelEventForWidget(TopLevelWidget, true);
|
||||
}
|
||||
else if (Container->openedDockAreas().isEmpty())
|
||||
{
|
||||
FloatingWidget->hide();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockAreaWidget::onTabCloseRequested(int Index)
|
||||
{
|
||||
qDebug() << "CDockAreaWidget::onTabCloseRequested " << Index;
|
||||
dockWidget(Index)->toggleView(false);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
CDockWidget* CDockAreaWidget::currentDockWidget() const
|
||||
{
|
||||
int CurrentIndex = currentIndex();
|
||||
if (CurrentIndex < 0)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return dockWidget(CurrentIndex);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockAreaWidget::setCurrentDockWidget(CDockWidget* DockWidget)
|
||||
{
|
||||
if (dockManager()->isRestoringState())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
internalSetCurrentDockWidget(DockWidget);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockAreaWidget::internalSetCurrentDockWidget(CDockWidget* DockWidget)
|
||||
{
|
||||
int Index = index(DockWidget);
|
||||
if (Index < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
setCurrentIndex(Index);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockAreaWidget::setCurrentIndex(int index)
|
||||
{
|
||||
auto TabBar = d->tabBar();
|
||||
if (index < 0 || index > (TabBar->count() - 1))
|
||||
{
|
||||
qWarning() << Q_FUNC_INFO << "Invalid index" << index;
|
||||
return;
|
||||
}
|
||||
|
||||
emit currentChanging(index);
|
||||
TabBar->setCurrentIndex(index);
|
||||
d->ContentsLayout->setCurrentIndex(index);
|
||||
d->ContentsLayout->currentWidget()->show();
|
||||
emit currentChanged(index);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
int CDockAreaWidget::currentIndex() const
|
||||
{
|
||||
return d->ContentsLayout->currentIndex();
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
QRect CDockAreaWidget::titleBarGeometry() const
|
||||
{
|
||||
return d->TitleBar->geometry();
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
QRect CDockAreaWidget::contentAreaGeometry() const
|
||||
{
|
||||
return d->ContentsLayout->geometry();
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
int CDockAreaWidget::index(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;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
int CDockAreaWidget::openDockWidgetsCount() const
|
||||
{
|
||||
int Count = 0;
|
||||
for (int i = 0; i < d->ContentsLayout->count(); ++i)
|
||||
{
|
||||
if (!dockWidget(i)->isClosed())
|
||||
{
|
||||
++Count;
|
||||
}
|
||||
}
|
||||
return Count;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
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::indexOfFirstOpenDockWidget() const
|
||||
{
|
||||
for (int i = 0; i < d->ContentsLayout->count(); ++i)
|
||||
{
|
||||
if (!dockWidget(i)->isClosed())
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
int CDockAreaWidget::dockWidgetsCount() const
|
||||
{
|
||||
return d->ContentsLayout->count();
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
CDockWidget* CDockAreaWidget::dockWidget(int Index) const
|
||||
{
|
||||
return qobject_cast<CDockWidget*>(d->ContentsLayout->widget(Index));
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockAreaWidget::reorderDockWidget(int fromIndex, int toIndex)
|
||||
{
|
||||
qDebug() << "CDockAreaWidget::reorderDockWidget";
|
||||
if (fromIndex >= d->ContentsLayout->count() || fromIndex < 0
|
||||
|| toIndex >= d->ContentsLayout->count() || toIndex < 0 || fromIndex == toIndex)
|
||||
{
|
||||
qDebug() << "Invalid index for tab movement" << fromIndex << toIndex;
|
||||
return;
|
||||
}
|
||||
|
||||
auto Widget = d->ContentsLayout->widget(fromIndex);
|
||||
d->ContentsLayout->removeWidget(Widget);
|
||||
d->ContentsLayout->insertWidget(toIndex, Widget);
|
||||
setCurrentIndex(toIndex);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockAreaWidget::toggleDockWidgetView(CDockWidget* DockWidget, bool Open)
|
||||
{
|
||||
Q_UNUSED(DockWidget);
|
||||
Q_UNUSED(Open);
|
||||
updateTitleBarVisibility();
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockAreaWidget::updateTitleBarVisibility()
|
||||
{
|
||||
CDockContainerWidget* Container = dockContainer();
|
||||
if (!Container)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
d->TitleBar->setVisible(!Container->isFloating() || !Container->hasTopLevelDockWidget());
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockAreaWidget::saveState(QXmlStreamWriter& s) const
|
||||
{
|
||||
s.writeStartElement("Area");
|
||||
s.writeAttribute("Tabs", QString::number(d->ContentsLayout->count()));
|
||||
auto CurrentDockWidget = currentDockWidget();
|
||||
QString Name = CurrentDockWidget ? CurrentDockWidget->objectName() : "";
|
||||
s.writeAttribute("Current", Name);
|
||||
qDebug() << "CDockAreaWidget::saveState TabCount: " << d->ContentsLayout->count()
|
||||
<< " Current: " << Name;
|
||||
for (int i = 0; i < d->ContentsLayout->count(); ++i)
|
||||
{
|
||||
dockWidget(i)->saveState(s);
|
||||
}
|
||||
s.writeEndElement();
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
CDockWidget* CDockAreaWidget::nextOpenDockWidget(CDockWidget* DockWidget) const
|
||||
{
|
||||
auto OpenDockWidgets = openedDockWidgets();
|
||||
if (OpenDockWidgets.count() > 1 || (OpenDockWidgets.count() == 1 && OpenDockWidgets[0] != DockWidget))
|
||||
{
|
||||
CDockWidget* NextDockWidget;
|
||||
if (OpenDockWidgets.last() == DockWidget)
|
||||
{
|
||||
NextDockWidget = OpenDockWidgets[OpenDockWidgets.count() - 2];
|
||||
}
|
||||
else
|
||||
{
|
||||
int NextIndex = OpenDockWidgets.indexOf(DockWidget) + 1;
|
||||
NextDockWidget = OpenDockWidgets[NextIndex];
|
||||
}
|
||||
|
||||
return NextDockWidget;
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
CDockWidget::DockWidgetFeatures CDockAreaWidget::features() const
|
||||
{
|
||||
CDockWidget::DockWidgetFeatures Features(CDockWidget::AllDockWidgetFeatures);
|
||||
for (const auto DockWidget : dockWidgets())
|
||||
{
|
||||
Features &= DockWidget->features();
|
||||
}
|
||||
|
||||
return Features;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockAreaWidget::toggleView(bool Open)
|
||||
{
|
||||
setVisible(Open);
|
||||
|
||||
emit viewToggled(Open);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockAreaWidget::setVisible(bool Visible)
|
||||
{
|
||||
Super::setVisible(Visible);
|
||||
if (d->UpdateCloseButton)
|
||||
{
|
||||
d->updateCloseButtonState();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
QAbstractButton* CDockAreaWidget::titleBarButton(TitleBarButton which) const
|
||||
{
|
||||
return d->TitleBar->button(which);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockAreaWidget::closeArea()
|
||||
{
|
||||
for (auto DockWidget : openedDockWidgets())
|
||||
{
|
||||
DockWidget->toggleView(false);
|
||||
}
|
||||
}
|
||||
} // namespace ads
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// EOF DockAreaWidget.cpp
|
||||
291
src/DockAreaWidget.h
Normal file
@@ -0,0 +1,291 @@
|
||||
#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>
|
||||
|
||||
#include "ads_globals.h"
|
||||
#include "DockWidget.h"
|
||||
|
||||
class QXmlStreamWriter;
|
||||
class QAbstractButton;
|
||||
|
||||
namespace ads
|
||||
{
|
||||
struct DockAreaWidgetPrivate;
|
||||
class CDockManager;
|
||||
class CDockContainerWidget;
|
||||
struct DockContainerWidgetPrivate;
|
||||
|
||||
|
||||
/**
|
||||
* DockAreaWidget manages multiple instances of DockWidgets.
|
||||
* It displays a title tab, which is clickable and will switch to
|
||||
* the contents associated to the title when clicked.
|
||||
*/
|
||||
class ADS_EXPORT CDockAreaWidget : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
DockAreaWidgetPrivate* d; ///< private data (pimpl)
|
||||
friend struct DockAreaWidgetPrivate;
|
||||
friend class CDockContainerWidget;
|
||||
friend struct DockContainerWidgetPrivate;
|
||||
friend class CDockWidgetTab;
|
||||
friend struct DockWidgetPrivate;
|
||||
friend class CDockWidget;
|
||||
friend struct DockManagerPrivate;
|
||||
|
||||
private slots:
|
||||
void onTabCloseRequested(int Index);
|
||||
|
||||
/**
|
||||
* Reorder the index position of DockWidget at fromIndx to toIndex
|
||||
* if a tab in the tabbar is dragged from one index to another one
|
||||
*/
|
||||
void reorderDockWidget(int fromIndex, int toIndex);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Called from dock widget if it is opened or closed
|
||||
*/
|
||||
void toggleDockWidgetView(CDockWidget* DockWidget, bool Open);
|
||||
|
||||
/**
|
||||
* This is a helper function to get the next open dock widget to activate
|
||||
* if the given DockWidget will be closed or removed.
|
||||
* The function returns the next widget that should be activated or
|
||||
* nullptr in case there are no more open widgets in this area.
|
||||
*/
|
||||
CDockWidget* nextOpenDockWidget(CDockWidget* DockWidget) const;
|
||||
|
||||
/**
|
||||
* Returns the index of the given DockWidget in the internal layout
|
||||
*/
|
||||
int index(CDockWidget* DockWidget);
|
||||
|
||||
/**
|
||||
* Call this function, if you already know, that the dock does not
|
||||
* contain any visible content (any open dock widgets).
|
||||
*/
|
||||
void hideAreaWithNoVisibleContent();
|
||||
|
||||
/**
|
||||
* Updates the dock area layout and components visibility
|
||||
*/
|
||||
void updateTitleBarVisibility();
|
||||
|
||||
/**
|
||||
* This is the internal private function for setting the current widget.
|
||||
* This function is called by the public setCurrentDockWidget() function
|
||||
* and by the dock manager when restoring the state
|
||||
*/
|
||||
void internalSetCurrentDockWidget(CDockWidget* DockWidget);
|
||||
|
||||
protected slots:
|
||||
void toggleView(bool Open);
|
||||
|
||||
public:
|
||||
using Super = QFrame;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Returns the rectangle of the title area
|
||||
*/
|
||||
QRect titleBarGeometry() const;
|
||||
|
||||
/**
|
||||
* Returns the rectangle of the content
|
||||
*/
|
||||
QRect contentAreaGeometry() const;
|
||||
|
||||
/**
|
||||
* Returns the number of dock widgets in this area
|
||||
*/
|
||||
int dockWidgetsCount() 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 the number of dock widgets in this area
|
||||
*/
|
||||
int openDockWidgetsCount() const;
|
||||
|
||||
/**
|
||||
* Returns a list of dock widgets that are not closed
|
||||
*/
|
||||
QList<CDockWidget*> openedDockWidgets() const;
|
||||
|
||||
/**
|
||||
* Returns a dock widget by its index
|
||||
*/
|
||||
CDockWidget* dockWidget(int Index) const;
|
||||
|
||||
/**
|
||||
* Returns the index of the current active dock widget or -1 if there
|
||||
* are is no active dock widget (ie.e if all dock widgets are closed)
|
||||
*/
|
||||
int currentIndex() const;
|
||||
|
||||
/**
|
||||
* Returns the index of the first open dock widgets in the list of
|
||||
* dock widgets.
|
||||
* This function is here for performance reasons. Normally it would
|
||||
* be possible to take the first dock widget from the list returned by
|
||||
* openedDockWidgets() function. But that function enumerates all
|
||||
* dock widgets while this functions stops after the first open dock widget.
|
||||
* If there are no open dock widgets, the function returns -1.
|
||||
*/
|
||||
int indexOfFirstOpenDockWidget() const;
|
||||
|
||||
/**
|
||||
* Returns the current active dock widget or a nullptr if there is no
|
||||
* active dock widget (i.e. if all dock widgets are closed)
|
||||
*/
|
||||
CDockWidget* currentDockWidget() const;
|
||||
|
||||
/**
|
||||
* Shows the tab with the given dock widget
|
||||
*/
|
||||
void setCurrentDockWidget(CDockWidget* DockWidget);
|
||||
|
||||
/**
|
||||
* Saves the state into the given stream
|
||||
*/
|
||||
void saveState(QXmlStreamWriter& Stream) const;
|
||||
|
||||
/**
|
||||
* This functions returns the dock widget features of all dock widget in
|
||||
* this area.
|
||||
* A bitwise and is used to combine the flags of all dock widgets. That
|
||||
* means, if only dock widget does not support a certain flag, the whole
|
||||
* dock are does not support the flag.
|
||||
*/
|
||||
CDockWidget::DockWidgetFeatures features() const;
|
||||
|
||||
/**
|
||||
* Returns the title bar button corresponding to the given title bar
|
||||
* button identifier
|
||||
*/
|
||||
QAbstractButton* titleBarButton(TitleBarButton which) const;
|
||||
|
||||
/**
|
||||
* Update the close button if visibility changed
|
||||
*/
|
||||
virtual void setVisible(bool Visible) override;
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* This activates the tab for the given tab index.
|
||||
* If the dock widget for the given tab is not visible, the this function
|
||||
* call will make it visible.
|
||||
*/
|
||||
void setCurrentIndex(int index);
|
||||
|
||||
/**
|
||||
* Closes the dock area and all dock widgets in this area
|
||||
*/
|
||||
void closeArea();
|
||||
|
||||
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 is about to be changed. The new
|
||||
* current has the given index, or -1 if there isn't a new one.
|
||||
* @param index
|
||||
*/
|
||||
void currentChanging(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);
|
||||
|
||||
/**
|
||||
* This signal is emitted if the visibility of this dock area is toggled
|
||||
* via toggle view function
|
||||
*/
|
||||
void viewToggled(bool Open);
|
||||
}; // class DockAreaWidget
|
||||
}
|
||||
// namespace ads
|
||||
//-----------------------------------------------------------------------------
|
||||
#endif // DockAreaWidgetH
|
||||
1385
src/DockContainerWidget.cpp
Normal file
257
src/DockContainerWidget.h
Normal file
@@ -0,0 +1,257 @@
|
||||
#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"
|
||||
#include "DockWidget.h"
|
||||
|
||||
class QXmlStreamWriter;
|
||||
class QXmlStreamReader;
|
||||
|
||||
namespace ads
|
||||
{
|
||||
class DockContainerWidgetPrivate;
|
||||
class CDockAreaWidget;
|
||||
class CDockWidget;
|
||||
class CDockManager;
|
||||
struct DockManagerPrivate;
|
||||
class CFloatingDockContainer;
|
||||
struct FloatingDockContainerPrivate;
|
||||
|
||||
/**
|
||||
* Container that manages a number of dock areas with single dock widgets
|
||||
* or tabyfied dock widgets in each area
|
||||
*/
|
||||
class ADS_EXPORT CDockContainerWidget : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
DockContainerWidgetPrivate* d; ///< private data (pimpl)
|
||||
friend struct DockContainerWidgetPrivate;
|
||||
friend class CDockManager;
|
||||
friend struct DockManagerPrivate;
|
||||
friend class CDockAreaWidget;
|
||||
friend struct DockAreaWidgetPrivate;
|
||||
friend class CFloatingDockContainer;
|
||||
friend struct FloatingDockContainerPrivate;
|
||||
friend class CDockWidget;
|
||||
Q_PRIVATE_SLOT(d, void onDockAreaViewToggled(bool Visible))
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Handles activation events to update zOrderIndex
|
||||
*/
|
||||
virtual bool event(QEvent *e) override;
|
||||
|
||||
/**
|
||||
* Access function for the internal root splitter
|
||||
*/
|
||||
QSplitter* rootSplitter() const;
|
||||
|
||||
/**
|
||||
* Helper function for creation of the root splitter
|
||||
*/
|
||||
void createRootSplitter();
|
||||
|
||||
/**
|
||||
* Drop floating widget into the container
|
||||
*/
|
||||
void dropFloatingWidget(CFloatingDockContainer* FloatingWidget, const QPoint& TargetPos);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Saves the state into the given stream
|
||||
*/
|
||||
void saveState(QXmlStreamWriter& 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(QXmlStreamReader& Stream, bool Testing);
|
||||
|
||||
/**
|
||||
* This function returns the last added dock area widget for the given
|
||||
* area identifier or 0 if no dock area widget has been added for the given
|
||||
* area
|
||||
*/
|
||||
CDockAreaWidget* lastAddedDockAreaWidget(DockWidgetArea area) const;
|
||||
|
||||
/**
|
||||
* This function returns true if this dock area has only one single
|
||||
* visible dock widget.
|
||||
* A top level widget is a real floating widget. Only the isFloating()
|
||||
* function of top level widgets may returns true.
|
||||
*/
|
||||
bool hasTopLevelDockWidget() const;
|
||||
|
||||
/**
|
||||
* If hasSingleVisibleDockWidget() returns true, this function returns the
|
||||
* one and only visible dock widget. Otherwise it returns a nullptr.
|
||||
*/
|
||||
CDockWidget* topLevelDockWidget() const;
|
||||
|
||||
/**
|
||||
* Returns the top level dock area.
|
||||
*/
|
||||
CDockAreaWidget* topLevelDockArea() const;
|
||||
|
||||
/**
|
||||
* This function returns a list of all dock widgets in this floating widget.
|
||||
* It may be possible, depending on the implementation, that dock widgets,
|
||||
* that are not visible to the user have no parent widget. Therefore simply
|
||||
* calling findChildren() would not work here. Therefore this function
|
||||
* iterates over all dock areas and creates a list that contains all
|
||||
* dock widgets returned from all dock areas.
|
||||
*/
|
||||
QList<CDockWidget*> dockWidgets() const;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Default Constructor
|
||||
*/
|
||||
CDockContainerWidget(CDockManager* DockManager, QWidget* parent = 0);
|
||||
|
||||
/**
|
||||
* Virtual Destructor
|
||||
*/
|
||||
virtual ~CDockContainerWidget();
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Dumps the layout for debugging purposes
|
||||
*/
|
||||
void dumpLayout();
|
||||
|
||||
/**
|
||||
* This functions returns the dock widget features of all dock widget in
|
||||
* this container.
|
||||
* A bitwise and is used to combine the flags of all dock widgets. That
|
||||
* means, if only dock widget does not support a certain flag, the whole
|
||||
* dock are does not support the flag.
|
||||
*/
|
||||
CDockWidget::DockWidgetFeatures features() const;
|
||||
|
||||
/**
|
||||
* If this dock container is in a floating widget, this function returns
|
||||
* the floating widget.
|
||||
* Else, it returns a nullptr.
|
||||
*/
|
||||
CFloatingDockContainer* floatingWidget() const;
|
||||
|
||||
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();
|
||||
|
||||
/**
|
||||
* This signal is emitted if a dock area is opened or closed via
|
||||
* toggleView() function
|
||||
*/
|
||||
void dockAreaViewToggled(CDockAreaWidget* DockArea, bool Open);
|
||||
}; // class DockContainerWidget
|
||||
} // namespace ads
|
||||
//-----------------------------------------------------------------------------
|
||||
#endif // DockContainerWidgetH
|
||||
765
src/DockManager.cpp
Normal file
@@ -0,0 +1,765 @@
|
||||
/*******************************************************************************
|
||||
** 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 <DockWidgetTab.h>
|
||||
#include "DockManager.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QVariant>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QAction>
|
||||
#include <QXmlStreamWriter>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QSettings>
|
||||
#include <QMenu>
|
||||
#include <QApplication>
|
||||
|
||||
#include "FloatingDockContainer.h"
|
||||
#include "DockOverlay.h"
|
||||
#include "DockWidget.h"
|
||||
#include "ads_globals.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;
|
||||
QMap<QString, QByteArray> Perspectives;
|
||||
QMap<QString, QMenu*> ViewMenuGroups;
|
||||
QMenu* ViewMenu;
|
||||
CDockManager::eViewMenuInsertionOrder MenuInsertionOrder = CDockManager::MenuAlphabeticallySorted;
|
||||
bool RestoringState = false;
|
||||
CDockManager::ConfigFlags ConfigFlags{CDockManager::DefaultConfig};
|
||||
|
||||
/**
|
||||
* 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 restoreStateFromXml(const QByteArray &state, int version, bool Testing = internal::Restore);
|
||||
|
||||
/**
|
||||
* Restore state
|
||||
*/
|
||||
bool restoreState(const QByteArray &state, int version);
|
||||
|
||||
void restoreDockWidgetsOpenState();
|
||||
void restoreDockAreasIndices();
|
||||
void emitTopLevelEvents();
|
||||
|
||||
void hideFloatingWidgets()
|
||||
{
|
||||
// Hide updates of floating widgets from use
|
||||
for (auto FloatingWidget : FloatingWidgets)
|
||||
{
|
||||
FloatingWidget->hide();
|
||||
}
|
||||
}
|
||||
|
||||
void markDockWidgetsDirty()
|
||||
{
|
||||
for (auto DockWidget : DockWidgetsMap)
|
||||
{
|
||||
DockWidget->setProperty("dirty", true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the container with the given index
|
||||
*/
|
||||
bool restoreContainer(int Index, QXmlStreamReader& stream, bool Testing);
|
||||
|
||||
/**
|
||||
* Loads the stylesheet
|
||||
*/
|
||||
void loadStylesheet();
|
||||
|
||||
/**
|
||||
* Adds action to menu - optionally in sorted order
|
||||
*/
|
||||
void addActionToMenu(QAction* Action, QMenu* Menu, bool InsertSorted);
|
||||
};
|
||||
// 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::restoreContainer(int Index, QXmlStreamReader& stream, bool Testing)
|
||||
{
|
||||
if (Testing)
|
||||
{
|
||||
Index = 0;
|
||||
}
|
||||
|
||||
bool Result = false;
|
||||
if (Index >= Containers.count())
|
||||
{
|
||||
CFloatingDockContainer* FloatingWidget = new CFloatingDockContainer(_this);
|
||||
Result = FloatingWidget->restoreState(stream, Testing);
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "d->Containers[i]->restoreState ";
|
||||
auto Container = Containers[Index];
|
||||
if (Container->isFloating())
|
||||
{
|
||||
Result = Container->floatingWidget()->restoreState(stream, Testing);
|
||||
}
|
||||
else
|
||||
{
|
||||
Result = Container->restoreState(stream, Testing);
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
bool DockManagerPrivate::checkFormat(const QByteArray &state, int version)
|
||||
{
|
||||
return restoreStateFromXml(state, version, internal::RestoreTesting);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
bool DockManagerPrivate::restoreStateFromXml(const QByteArray &state, int version,
|
||||
bool Testing)
|
||||
{
|
||||
if (state.isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
QXmlStreamReader s(state);
|
||||
s.readNextStartElement();
|
||||
if (s.name() != "QtAdvancedDockingSystem")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
qDebug() << s.attributes().value("Version");
|
||||
bool ok;
|
||||
int v = s.attributes().value("Version").toInt(&ok);
|
||||
if (!ok || v != version)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Result = true;
|
||||
int DockContainers = s.attributes().value("Containers").toInt();
|
||||
qDebug() << DockContainers;
|
||||
int DockContainerCount = 0;
|
||||
while (s.readNextStartElement())
|
||||
{
|
||||
if (s.name() == "Container")
|
||||
{
|
||||
Result = restoreContainer(DockContainerCount, s, Testing);
|
||||
if (!Result)
|
||||
{
|
||||
break;
|
||||
}
|
||||
DockContainerCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Testing)
|
||||
{
|
||||
// Delete remaining empty floating widgets
|
||||
int FloatingWidgetIndex = DockContainerCount - 1;
|
||||
int DeleteCount = FloatingWidgets.count() - FloatingWidgetIndex;
|
||||
for (int i = 0; i < DeleteCount; ++i)
|
||||
{
|
||||
FloatingWidgets[FloatingWidgetIndex + i]->deleteLater();
|
||||
_this->removeDockContainer(FloatingWidgets[FloatingWidgetIndex + i]->dockContainer());
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void DockManagerPrivate::restoreDockWidgetsOpenState()
|
||||
{
|
||||
// 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
|
||||
// They do not belong to any dock container, until the user toggles the
|
||||
// toggle view action the next time
|
||||
for (auto DockWidget : DockWidgetsMap)
|
||||
{
|
||||
if (DockWidget->property("dirty").toBool())
|
||||
{
|
||||
DockWidget->flagAsUnassigned();
|
||||
}
|
||||
else
|
||||
{
|
||||
DockWidget->toggleViewInternal(!DockWidget->property("closed").toBool());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void DockManagerPrivate::restoreDockAreasIndices()
|
||||
{
|
||||
// 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
|
||||
int Count = 0;
|
||||
for (auto DockContainer : Containers)
|
||||
{
|
||||
Count++;
|
||||
for (int i = 0; i < DockContainer->dockAreaCount(); ++i)
|
||||
{
|
||||
CDockAreaWidget* DockArea = DockContainer->dockArea(i);
|
||||
QString DockWidgetName = DockArea->property("currentDockWidget").toString();
|
||||
CDockWidget* DockWidget = nullptr;
|
||||
if (!DockWidgetName.isEmpty())
|
||||
{
|
||||
DockWidget = _this->findDockWidget(DockWidgetName);
|
||||
}
|
||||
|
||||
if (!DockWidget || DockWidget->isClosed())
|
||||
{
|
||||
int Index = DockArea->indexOfFirstOpenDockWidget();
|
||||
if (Index < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
DockArea->setCurrentIndex(Index);
|
||||
}
|
||||
else
|
||||
{
|
||||
DockArea->internalSetCurrentDockWidget(DockWidget);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//============================================================================
|
||||
void DockManagerPrivate::emitTopLevelEvents()
|
||||
{
|
||||
// Finally we need to send the topLevelChanged() signals for all dock
|
||||
// widgets if top level changed
|
||||
for (auto DockContainer : Containers)
|
||||
{
|
||||
CDockWidget* TopLevelDockWidget = DockContainer->topLevelDockWidget();
|
||||
if (TopLevelDockWidget)
|
||||
{
|
||||
TopLevelDockWidget->emitTopLevelChanged(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < DockContainer->dockAreaCount(); ++i)
|
||||
{
|
||||
auto DockArea = DockContainer->dockArea(i);
|
||||
for (auto DockWidget : DockArea->dockWidgets())
|
||||
{
|
||||
DockWidget->emitTopLevelChanged(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
bool DockManagerPrivate::restoreState(const QByteArray &state, int version)
|
||||
{
|
||||
if (!checkFormat(state, version))
|
||||
{
|
||||
qDebug() << "checkFormat: Error checking format!!!!!!!";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Hide updates of floating widgets from use
|
||||
hideFloatingWidgets();
|
||||
markDockWidgetsDirty();
|
||||
|
||||
if (!restoreStateFromXml(state, version))
|
||||
{
|
||||
qDebug() << "restoreState: Error restoring state!!!!!!!";
|
||||
return false;
|
||||
}
|
||||
|
||||
restoreDockWidgetsOpenState();
|
||||
restoreDockAreasIndices();
|
||||
emitTopLevelEvents();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void DockManagerPrivate::addActionToMenu(QAction* Action, QMenu* Menu, bool InsertSorted)
|
||||
{
|
||||
if (InsertSorted)
|
||||
{
|
||||
auto Actions = Menu->actions();
|
||||
auto it = std::find_if(Actions.begin(), Actions.end(),
|
||||
[&Action](const QAction* a)
|
||||
{
|
||||
return a->text().compare(Action->text(), Qt::CaseInsensitive) > 0;
|
||||
});
|
||||
|
||||
if (it == Actions.end())
|
||||
{
|
||||
Menu->addAction(Action);
|
||||
}
|
||||
else
|
||||
{
|
||||
Menu->insertAction(*it, Action);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Menu->addAction(Action);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
CDockManager::CDockManager(QWidget *parent) :
|
||||
CDockContainerWidget(this, parent),
|
||||
d(new DockManagerPrivate(this))
|
||||
{
|
||||
createRootSplitter();
|
||||
QMainWindow* MainWindow = dynamic_cast<QMainWindow*>(parent);
|
||||
if (MainWindow)
|
||||
{
|
||||
MainWindow->setCentralWidget(this);
|
||||
}
|
||||
|
||||
d->ViewMenu = new QMenu(tr("Show View"), 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(eXmlMode XmlMode, int version) const
|
||||
{
|
||||
QByteArray xmldata;
|
||||
QXmlStreamWriter s(&xmldata);
|
||||
s.setAutoFormatting(XmlAutoFormattingEnabled == XmlMode);
|
||||
s.writeStartDocument();
|
||||
s.writeStartElement("QtAdvancedDockingSystem");
|
||||
s.writeAttribute("Version", QString::number(version));
|
||||
s.writeAttribute("Containers", QString::number(d->Containers.count()));
|
||||
for (auto Container : d->Containers)
|
||||
{
|
||||
Container->saveState(s);
|
||||
}
|
||||
|
||||
s.writeEndElement();
|
||||
s.writeEndDocument();
|
||||
|
||||
return xmldata;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
bool CDockManager::restoreState(const QByteArray &state, int version)
|
||||
{
|
||||
// Prevent multiple calls as long as state is not restore. This may
|
||||
// happen, if QApplication::processEvents() is called somewhere
|
||||
if (d->RestoringState)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// We hide the complete dock manager here. Restoring the state means
|
||||
// that DockWidgets are removed from the DockArea internal stack layout
|
||||
// which in turn means, that each time a widget is removed the stack
|
||||
// will show and raise the next available widget which in turn
|
||||
// triggers show events for the dock widgets. To avoid this we hide the
|
||||
// dock manager. Because there will be no processing of application
|
||||
// events until this function is finished, the user will not see this
|
||||
// hiding
|
||||
bool IsHidden = this->isHidden();
|
||||
if (!IsHidden)
|
||||
{
|
||||
hide();
|
||||
}
|
||||
d->RestoringState = true;
|
||||
emit restoringState();
|
||||
bool Result = d->restoreState(state, version);
|
||||
d->RestoringState = false;
|
||||
emit stateRestored();
|
||||
if (!IsHidden)
|
||||
{
|
||||
show();
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
CDockAreaWidget* CDockManager::addDockWidget(DockWidgetArea area,
|
||||
CDockWidget* Dockwidget, CDockAreaWidget* DockAreaWidget)
|
||||
{
|
||||
d->DockWidgetsMap.insert(Dockwidget->objectName(), Dockwidget);
|
||||
return CDockContainerWidget::addDockWidget(area, Dockwidget, DockAreaWidget);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
CDockAreaWidget* CDockManager::addDockWidgetTab(DockWidgetArea area,
|
||||
CDockWidget* Dockwidget)
|
||||
{
|
||||
CDockAreaWidget* AreaWidget = lastAddedDockAreaWidget(area);
|
||||
if (AreaWidget)
|
||||
{
|
||||
return addDockWidget(ads::CenterDockWidgetArea, Dockwidget, AreaWidget);
|
||||
}
|
||||
else
|
||||
{
|
||||
return addDockWidget(area, Dockwidget, AreaWidget);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
CDockAreaWidget* CDockManager::addDockWidgetTabToArea(CDockWidget* Dockwidget,
|
||||
CDockAreaWidget* DockAreaWidget)
|
||||
{
|
||||
return addDockWidget(ads::CenterDockWidgetArea, Dockwidget, DockAreaWidget);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
CDockWidget* CDockManager::findDockWidget(const QString& ObjectName) const
|
||||
{
|
||||
return d->DockWidgetsMap.value(ObjectName, nullptr);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
QMap<QString, CDockWidget*> CDockManager::dockWidgetsMap() const
|
||||
{
|
||||
return d->DockWidgetsMap;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockManager::addPerspective(const QString& UniquePrespectiveName)
|
||||
{
|
||||
d->Perspectives.insert(UniquePrespectiveName, saveState());
|
||||
emit perspectiveListChanged();
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockManager::removePerspective(const QString& Name)
|
||||
{
|
||||
removePerspectives({Name});
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockManager::removePerspectives(const QStringList& Names)
|
||||
{
|
||||
int Count = 0;
|
||||
for (auto Name : Names)
|
||||
{
|
||||
Count += d->Perspectives.remove(Name);
|
||||
}
|
||||
|
||||
if (Count)
|
||||
{
|
||||
emit perspectivesRemoved();
|
||||
emit perspectiveListChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
QStringList CDockManager::perspectiveNames() const
|
||||
{
|
||||
return d->Perspectives.keys();
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockManager::openPerspective(const QString& PerspectiveName)
|
||||
{
|
||||
const auto Iterator = d->Perspectives.find(PerspectiveName);
|
||||
if (d->Perspectives.end() == Iterator)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
emit openingPerspective(PerspectiveName);
|
||||
restoreState(Iterator.value());
|
||||
emit perspectiveOpened(PerspectiveName);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockManager::savePerspectives(QSettings& Settings) const
|
||||
{
|
||||
Settings.beginWriteArray("Perspectives", d->Perspectives.size());
|
||||
int i = 0;
|
||||
for (auto it = d->Perspectives.constBegin(); it != d->Perspectives.constEnd(); ++it)
|
||||
{
|
||||
Settings.setArrayIndex(i);
|
||||
Settings.setValue("Name", it.key());
|
||||
Settings.setValue("State", it.value());
|
||||
++i;
|
||||
}
|
||||
Settings.endArray();
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockManager::loadPerspectives(QSettings& Settings)
|
||||
{
|
||||
d->Perspectives.clear();
|
||||
int Size = Settings.beginReadArray("Perspectives");
|
||||
if (!Size)
|
||||
{
|
||||
Settings.endArray();
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < Size; ++i)
|
||||
{
|
||||
Settings.setArrayIndex(i);
|
||||
QString Name = Settings.value("Name").toString();
|
||||
QByteArray Data = Settings.value("State").toByteArray();
|
||||
if (Name.isEmpty() || Data.isEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
d->Perspectives.insert(Name, Data);
|
||||
}
|
||||
|
||||
Settings.endArray();
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
QAction* CDockManager::addToggleViewActionToMenu(QAction* ToggleViewAction,
|
||||
const QString& Group, const QIcon& GroupIcon)
|
||||
{
|
||||
bool AlphabeticallySorted = (MenuAlphabeticallySorted == d->MenuInsertionOrder);
|
||||
if (!Group.isEmpty())
|
||||
{
|
||||
QMenu* GroupMenu = d->ViewMenuGroups.value(Group, 0);
|
||||
if (!GroupMenu)
|
||||
{
|
||||
GroupMenu = new QMenu(Group, this);
|
||||
GroupMenu->setIcon(GroupIcon);
|
||||
d->addActionToMenu(GroupMenu->menuAction(), d->ViewMenu, AlphabeticallySorted);
|
||||
d->ViewMenuGroups.insert(Group, GroupMenu);
|
||||
}
|
||||
|
||||
d->addActionToMenu(ToggleViewAction, GroupMenu, AlphabeticallySorted);
|
||||
return GroupMenu->menuAction();
|
||||
}
|
||||
else
|
||||
{
|
||||
d->addActionToMenu(ToggleViewAction, d->ViewMenu, AlphabeticallySorted);
|
||||
return ToggleViewAction;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
QMenu* CDockManager::viewMenu() const
|
||||
{
|
||||
return d->ViewMenu;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockManager::setViewMenuInsertionOrder(eViewMenuInsertionOrder Order)
|
||||
{
|
||||
d->MenuInsertionOrder = Order;
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
bool CDockManager::isRestoringState() const
|
||||
{
|
||||
return d->RestoringState;
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
int CDockManager::startDragDistance()
|
||||
{
|
||||
return QApplication::startDragDistance() * 1.5;
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
CDockManager::ConfigFlags CDockManager::configFlags() const
|
||||
{
|
||||
return d->ConfigFlags;
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
void CDockManager::setConfigFlags(const ConfigFlags Flags)
|
||||
{
|
||||
d->ConfigFlags = Flags;
|
||||
}
|
||||
|
||||
} // namespace ads
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// EOF DockManager.cpp
|
||||
371
src/DockManager.h
Normal file
@@ -0,0 +1,371 @@
|
||||
#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"
|
||||
#include <QIcon>
|
||||
|
||||
#include "ads_globals.h"
|
||||
|
||||
class QSettings;
|
||||
class QMenu;
|
||||
|
||||
namespace ads
|
||||
{
|
||||
struct DockManagerPrivate;
|
||||
class CFloatingDockContainer;
|
||||
struct FloatingDockContainerPrivate;
|
||||
class CDockContainerWidget;
|
||||
class CDockOverlay;
|
||||
class CDockAreaTabBar;
|
||||
class CDockWidgetTab;
|
||||
struct DockWidgetTabPrivate;
|
||||
struct DockAreaWidgetPrivate;
|
||||
|
||||
/**
|
||||
* The central dock manager that maintains the complete docking system
|
||||
**/
|
||||
class ADS_EXPORT CDockManager : public CDockContainerWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
DockManagerPrivate* d; ///< private data (pimpl)
|
||||
friend struct DockManagerPrivate;
|
||||
friend class CFloatingDockContainer;
|
||||
friend struct FloatingDockContainerPrivate;
|
||||
friend class CDockContainerWidget;
|
||||
friend class CDockAreaTabBar;
|
||||
friend class CDockWidgetTab;
|
||||
friend struct DockAreaWidgetPrivate;
|
||||
friend struct DockWidgetTabPrivate;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* 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;
|
||||
|
||||
public:
|
||||
enum eViewMenuInsertionOrder
|
||||
{
|
||||
MenuSortedByInsertion,
|
||||
MenuAlphabeticallySorted
|
||||
};
|
||||
|
||||
enum eXmlMode
|
||||
{
|
||||
XmlAutoFormattingDisabled,
|
||||
XmlAutoFormattingEnabled
|
||||
};
|
||||
|
||||
/**
|
||||
* These global configuration flags configure some global dock manager
|
||||
* settings.
|
||||
*/
|
||||
enum eConfigFlag
|
||||
{
|
||||
ActiveTabHasCloseButton = 0x01, //!< If this flag is set, the active tab in a tab area has a close button
|
||||
DockAreaHasCloseButton = 0x02, //!< If the flag is set each dock area has a close button
|
||||
DockAreaCloseButtonClosesTab = 0x04,//!< If the flag is set, the dock area close button closes the active tab, if not set, it closes the complete cock area
|
||||
OpaqueSplitterResize = 0x08, //!< See QSplitter::setOpaqueResize() documentation
|
||||
DefaultConfig = ActiveTabHasCloseButton | DockAreaHasCloseButton | OpaqueSplitterResize, ///< the default configuration
|
||||
};
|
||||
Q_DECLARE_FLAGS(ConfigFlags, eConfigFlag)
|
||||
|
||||
/**
|
||||
* Default Constructor.
|
||||
* If the given parent is a QMainWindow, the dock manager sets itself as the
|
||||
* central widget.
|
||||
* Before you create any dock widgets, you should properly setup the
|
||||
* configuration flags via setConfigFlags()
|
||||
*/
|
||||
CDockManager(QWidget* parent = 0);
|
||||
|
||||
/**
|
||||
* Virtual Destructor
|
||||
*/
|
||||
virtual ~CDockManager();
|
||||
|
||||
/**
|
||||
* This function returns the global configuration flags
|
||||
*/
|
||||
ConfigFlags configFlags() const;
|
||||
|
||||
/**
|
||||
* Sets the global configuration flags for the whole docking system.
|
||||
* Call this function before you create your first dock widget.
|
||||
*/
|
||||
void setConfigFlags(const ConfigFlags Flags);
|
||||
|
||||
/**
|
||||
* 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. If you would like to add a dock widget
|
||||
* tabified, then you need to add it to an existing dock area object
|
||||
* into the CenterDockWidgetArea. The following code shows this:
|
||||
* \code
|
||||
* DockManager->addDockWidget(ads::CenterDockWidgetArea, NewDockWidget,
|
||||
* ExisitingDockArea);
|
||||
* \endcode
|
||||
* \return Returns the dock area widget that contains the new DockWidget
|
||||
*/
|
||||
CDockAreaWidget* addDockWidget(DockWidgetArea area, CDockWidget* Dockwidget,
|
||||
CDockAreaWidget* DockAreaWidget = nullptr);
|
||||
|
||||
/**
|
||||
* This function will add the given Dockwidget to the given dock area as
|
||||
* a new tab.
|
||||
* If no dock area widget exists for the given area identifier, a new
|
||||
* dock area widget is created.
|
||||
*/
|
||||
CDockAreaWidget* addDockWidgetTab(DockWidgetArea area,
|
||||
CDockWidget* Dockwidget);
|
||||
|
||||
/**
|
||||
* This function will add the given Dockwidget to the given DockAreaWidget
|
||||
* as a new tab.
|
||||
*/
|
||||
CDockAreaWidget* addDockWidgetTabToArea(CDockWidget* Dockwidget,
|
||||
CDockAreaWidget* DockAreaWidget);
|
||||
|
||||
/**
|
||||
* 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) const;
|
||||
|
||||
/**
|
||||
* This function returns a readable reference to the internal dock
|
||||
* widgets map so that it is possible to iterate over all dock widgets
|
||||
*/
|
||||
QMap<QString, CDockWidget*> dockWidgetsMap() const;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* into the returned QByteArray.
|
||||
* The XmlMode enables / disables the auto formatting for the XmlStreamWriter.
|
||||
* If auto formatting is enabled, the output is intended and line wrapped.
|
||||
* The XmlMode XmlAutoFormattingDisabled is better if you would like to have
|
||||
* a more compact XML output - i.e. for storage in ini files.
|
||||
*/
|
||||
QByteArray saveState(eXmlMode XmlMode = XmlAutoFormattingDisabled, 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);
|
||||
|
||||
/**
|
||||
* Saves the current perspective to the internal list of perspectives.
|
||||
* A perspective is the current state of the dock manager assigned
|
||||
* with a certain name. This makes it possible for the user,
|
||||
* to switch between different perspectives quickly.
|
||||
* If a perspective with the given name already exists, then
|
||||
* it will be overwritten with the new state.
|
||||
*/
|
||||
void addPerspective(const QString& UniquePrespectiveName);
|
||||
|
||||
/**
|
||||
* Removes the perspective with the given name from the list of perspectives
|
||||
*/
|
||||
void removePerspective(const QString& Name);
|
||||
|
||||
/**
|
||||
* Removes the given perspectives from the dock manager
|
||||
*/
|
||||
void removePerspectives(const QStringList& Names);
|
||||
|
||||
/**
|
||||
* Returns the names of all available perspectives
|
||||
*/
|
||||
QStringList perspectiveNames() const;
|
||||
|
||||
/**
|
||||
* Saves the perspectives to the given settings file.
|
||||
*/
|
||||
void savePerspectives(QSettings& Settings) const;
|
||||
|
||||
/**
|
||||
* Loads the perspectives from the given settings file
|
||||
*/
|
||||
void loadPerspectives(QSettings& Settings);
|
||||
|
||||
/**
|
||||
* Adds a toggle view action to the the internal view menu.
|
||||
* You can either manage the insertion of the toggle view actions in your
|
||||
* application or you can add the actions to the internal view menu and
|
||||
* then simply insert the menu object into your.
|
||||
* \param[in] ToggleViewAction The action to insert. If no group is provided
|
||||
* the action is directly inserted into the menu. If a group
|
||||
* is provided, the action is inserted into the group and the
|
||||
* group is inserted into the menu if it is not existing yet.
|
||||
* \param[in] Group This is the text used for the group menu item
|
||||
* \param[in] GroupIcon The icon used for grouping the workbenches in the
|
||||
* view menu. I.e. if there is a workbench for each device
|
||||
* like for spectrometer devices, it is good to group all these
|
||||
* workbenches under a menu item
|
||||
* \return If Group is not empty, this function returns the GroupAction
|
||||
* for this group. If the group is empty, the function returns
|
||||
* the given ToggleViewAction.
|
||||
*/
|
||||
QAction* addToggleViewActionToMenu(QAction* ToggleViewAction,
|
||||
const QString& Group = QString(), const QIcon& GroupIcon = QIcon());
|
||||
|
||||
/**
|
||||
* This function returns the internal view menu.
|
||||
* To fill the view menu, you can use the addToggleViewActionToMenu()
|
||||
* function.
|
||||
*/
|
||||
QMenu* viewMenu() const;
|
||||
|
||||
/**
|
||||
* Define the insertion order for toggle view menu items.
|
||||
* The order defines how the actions are added to the view menu.
|
||||
* The default insertion order is MenuAlphabeticallySorted to make it
|
||||
* easier for users to find the menu entry for a certain dock widget.
|
||||
* You need to call this function befor you insert the first menu item
|
||||
* into the view menu.
|
||||
*/
|
||||
void setViewMenuInsertionOrder(eViewMenuInsertionOrder Order);
|
||||
|
||||
/**
|
||||
* This function returns true between the restoringState() and
|
||||
* stateRestored() signals.
|
||||
*/
|
||||
bool isRestoringState() const;
|
||||
|
||||
/**
|
||||
* The distance the user needs to move the mouse with the left button
|
||||
* hold down before a dock widget start floating
|
||||
*/
|
||||
static int startDragDistance();
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* Opens the perspective with the given name.
|
||||
*/
|
||||
void openPerspective(const QString& PerspectiveName);
|
||||
|
||||
signals:
|
||||
/**
|
||||
* This signal is emitted if the list of perspectives changed
|
||||
*/
|
||||
void perspectiveListChanged();
|
||||
|
||||
/**
|
||||
* This signal is emitted if perspectives have been removed
|
||||
*/
|
||||
void perspectivesRemoved();
|
||||
|
||||
/**
|
||||
* This signal is emitted, if the restore function is called, just before
|
||||
* the dock manager starts restoring the state.
|
||||
* If this function is called, nothing has changed yet
|
||||
*/
|
||||
void restoringState();
|
||||
|
||||
/**
|
||||
* This signal is emitted if the state changed in restoreState.
|
||||
* The signal is emitted if the restoreState() function is called or
|
||||
* if the openPerspective() function is called
|
||||
*/
|
||||
void stateRestored();
|
||||
|
||||
/**
|
||||
* This signal is emitted, if the dock manager starts opening a
|
||||
* perspective.
|
||||
* Opening a perspective may take more than a second if there are
|
||||
* many complex widgets. The application may use this signal
|
||||
* to show some progress indicator or to change the mouse cursor
|
||||
* into a busy cursor.
|
||||
*/
|
||||
void openingPerspective(const QString& PerspectiveName);
|
||||
|
||||
/**
|
||||
* This signal is emitted if the dock manager finished opening a
|
||||
* perspective
|
||||
*/
|
||||
void perspectiveOpened(const QString& PerspectiveName);
|
||||
}; // class DockManager
|
||||
} // namespace ads
|
||||
//-----------------------------------------------------------------------------
|
||||
#endif // DockManagerH
|
||||
778
src/DockOverlay.cpp
Normal file
@@ -0,0 +1,778 @@
|
||||
/*******************************************************************************
|
||||
** 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 <QMap>
|
||||
#include <QWindow>
|
||||
|
||||
#include "DockAreaWidget.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace ads
|
||||
{
|
||||
|
||||
/**
|
||||
* 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;
|
||||
QColor IconColors[5];
|
||||
bool UpdateRequired = false;
|
||||
double LastDevicePixelRatio = 0.1;
|
||||
|
||||
/**
|
||||
* Private data constructor
|
||||
*/
|
||||
DockOverlayCrossPrivate(CDockOverlayCross* _public) : _this(_public) {}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param area
|
||||
* @return
|
||||
*/
|
||||
QPoint areaGridPosition(const DockWidgetArea area);
|
||||
|
||||
|
||||
/**
|
||||
* Palette based default icon colors
|
||||
*/
|
||||
QColor defaultIconColor(CDockOverlayCross::eIconColor ColorIndex)
|
||||
{
|
||||
QPalette pal = _this->palette();
|
||||
switch (ColorIndex)
|
||||
{
|
||||
case CDockOverlayCross::FrameColor: return pal.color(QPalette::Active, QPalette::Highlight);
|
||||
case CDockOverlayCross::WindowBackgroundColor: return pal.color(QPalette::Active, QPalette::Base);
|
||||
case CDockOverlayCross::OverlayColor:
|
||||
{
|
||||
QColor Color = pal.color(QPalette::Active, QPalette::Highlight);
|
||||
Color.setAlpha(64);
|
||||
return Color;
|
||||
}
|
||||
break;
|
||||
|
||||
case CDockOverlayCross::ArrowColor: return pal.color(QPalette::Active, QPalette::Base);
|
||||
case CDockOverlayCross::ShadowColor: return QColor(0, 0, 0, 64);
|
||||
default:
|
||||
return QColor();
|
||||
}
|
||||
|
||||
return QColor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stylehseet based icon colors
|
||||
*/
|
||||
QColor iconColor(CDockOverlayCross::eIconColor ColorIndex)
|
||||
{
|
||||
QColor Color = IconColors[ColorIndex];
|
||||
if (!Color.isValid())
|
||||
{
|
||||
Color = defaultIconColor(ColorIndex);
|
||||
IconColors[ColorIndex] = Color;
|
||||
}
|
||||
return Color;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
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(createHighDpiDropIndicatorPixmap(size, DockWidgetArea, Mode));
|
||||
l->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
|
||||
l->setAttribute(Qt::WA_TranslucentBackground);
|
||||
l->setProperty("dockWidgetArea", DockWidgetArea);
|
||||
return l;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
void updateDropIndicatorIcon(QWidget* DropIndicatorWidget)
|
||||
{
|
||||
QLabel* l = qobject_cast<QLabel*>(DropIndicatorWidget);
|
||||
const qreal metric = static_cast<qreal>(l->fontMetrics().height()) * 3.f;
|
||||
const QSizeF size(metric, metric);
|
||||
|
||||
int Area = l->property("dockWidgetArea").toInt();
|
||||
l->setPixmap(createHighDpiDropIndicatorPixmap(size, (DockWidgetArea)Area, Mode));
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
QPixmap createHighDpiDropIndicatorPixmap(const QSizeF& size, DockWidgetArea DockWidgetArea,
|
||||
CDockOverlay::eMode Mode)
|
||||
{
|
||||
QColor borderColor = iconColor(CDockOverlayCross::FrameColor);
|
||||
QColor backgroundColor = iconColor(CDockOverlayCross::WindowBackgroundColor);
|
||||
|
||||
double DevicePixelRatio = _this->window()->devicePixelRatioF();
|
||||
QSizeF PixmapSize = size * DevicePixelRatio;
|
||||
QPixmap pm(PixmapSize.toSize());
|
||||
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
|
||||
QColor ShadowColor = iconColor(CDockOverlayCross::ShadowColor);
|
||||
if (ShadowColor.alpha() == 255)
|
||||
{
|
||||
ShadowColor.setAlpha(64);
|
||||
}
|
||||
p.fillRect(ShadowRect, ShadowColor);
|
||||
|
||||
// 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 = iconColor(CDockOverlayCross::OverlayColor);
|
||||
if (Color.alpha() == 255)
|
||||
{
|
||||
Color.setAlpha(64);
|
||||
}
|
||||
p.setBrush(Color);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.drawRect(areaRect);
|
||||
|
||||
pen = p.pen();
|
||||
pen.setWidth(1);
|
||||
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(iconColor(CDockOverlayCross::ArrowColor));
|
||||
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);
|
||||
}
|
||||
|
||||
pm.setDevicePixelRatio(DevicePixelRatio);
|
||||
return pm;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
//============================================================================
|
||||
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->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->titleBarGeometry().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();
|
||||
d->Cross->updateOverlayIcons();
|
||||
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);
|
||||
QPen Pen = painter.pen();
|
||||
Pen.setColor(Color.darker(120));
|
||||
Pen.setStyle(Qt::SolidLine);
|
||||
Pen.setWidth(1);
|
||||
Pen.setCosmetic(true);
|
||||
painter.setPen(Pen);
|
||||
Color = Color.lighter(130);
|
||||
Color.setAlpha(64);
|
||||
painter.setBrush(Color);
|
||||
painter.drawRect(r.adjusted(0, 0, -1, -1));
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
bool CDockOverlay::event(QEvent *e)
|
||||
{
|
||||
bool Result = Super::event(e);
|
||||
if (e->type() == QEvent::Polish)
|
||||
{
|
||||
d->Cross->setupOverlayCross(d->Mode);
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
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, d->createDropIndicatorWidget(TopDockWidgetArea, Mode));
|
||||
areaWidgets.insert(RightDockWidgetArea, d->createDropIndicatorWidget(RightDockWidgetArea, Mode));
|
||||
areaWidgets.insert(BottomDockWidgetArea, d->createDropIndicatorWidget(BottomDockWidgetArea, Mode));
|
||||
areaWidgets.insert(LeftDockWidgetArea, d->createDropIndicatorWidget(LeftDockWidgetArea, Mode));
|
||||
areaWidgets.insert(CenterDockWidgetArea, d->createDropIndicatorWidget(CenterDockWidgetArea, Mode));
|
||||
d->LastDevicePixelRatio = devicePixelRatioF();
|
||||
|
||||
setAreaWidgets(areaWidgets);
|
||||
d->UpdateRequired = false;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockOverlayCross::updateOverlayIcons()
|
||||
{
|
||||
if (windowHandle()->devicePixelRatio() == d->LastDevicePixelRatio)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto Widget : d->DropIndicatorWidgets)
|
||||
{
|
||||
d->updateDropIndicatorIcon(Widget);
|
||||
}
|
||||
d->LastDevicePixelRatio = devicePixelRatioF();
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockOverlayCross::setIconColor(eIconColor ColorIndex, const QColor& Color)
|
||||
{
|
||||
d->IconColors[ColorIndex] = Color;
|
||||
d->UpdateRequired = true;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
QColor CDockOverlayCross::iconColor(eIconColor ColorIndex) const
|
||||
{
|
||||
return d->IconColors[ColorIndex];
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
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*)
|
||||
{
|
||||
if (d->UpdateRequired)
|
||||
{
|
||||
setupOverlayCross(d->Mode);
|
||||
}
|
||||
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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockOverlayCross::setIconColors(const QString& Colors)
|
||||
{
|
||||
static const QMap<QString, int> ColorCompenentStringMap{
|
||||
{"Frame", CDockOverlayCross::FrameColor},
|
||||
{"Background", CDockOverlayCross::WindowBackgroundColor},
|
||||
{"Overlay", CDockOverlayCross::OverlayColor},
|
||||
{"Arrow", CDockOverlayCross::ArrowColor},
|
||||
{"Shadow", CDockOverlayCross::ShadowColor}};
|
||||
|
||||
auto ColorList = Colors.split(' ', QString::SkipEmptyParts);
|
||||
for (const auto& ColorListEntry : ColorList)
|
||||
{
|
||||
auto ComponentColor = ColorListEntry.split('=', QString::SkipEmptyParts);
|
||||
int Component = ColorCompenentStringMap.value(ComponentColor[0], -1);
|
||||
if (Component < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
d->IconColors[Component] = QColor(ComponentColor[1]);
|
||||
}
|
||||
|
||||
d->UpdateRequired = true;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
QString CDockOverlayCross::iconColors() const
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace ads
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
254
src/DockOverlay.h
Normal file
@@ -0,0 +1,254 @@
|
||||
#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 ADS_EXPORT CDockOverlay : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
DockOverlayPrivate* d; //< private data class
|
||||
friend struct DockOverlayPrivate;
|
||||
friend class DockOverlayCross;
|
||||
|
||||
public:
|
||||
using Super = QFrame;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Handle polish events
|
||||
*/
|
||||
virtual bool event(QEvent *e) override;
|
||||
|
||||
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.
|
||||
* You can style the cross icon using the property system.
|
||||
* \code
|
||||
* ads--CDockOverlayCross
|
||||
{
|
||||
qproperty-iconFrameColor: palette(highlight);
|
||||
qproperty-iconBackgroundColor: palette(base);
|
||||
qproperty-iconOverlayColor: palette(highlight);
|
||||
qproperty-iconArrowColor: rgb(227, 227, 227);
|
||||
qproperty-iconShadowColor: rgb(0, 0, 0);
|
||||
}
|
||||
* \endcode
|
||||
* Or you can use the iconColors property to pass in AARRGGBB values as
|
||||
* hex string like shown in the example below.
|
||||
* \code
|
||||
* ads--CDockOverlayCross
|
||||
* {
|
||||
* qproperty-iconColors: "Frame=#ff3d3d3d Background=#ff929292 Overlay=#1f3d3d3d Arrow=#ffb4b4b4 Shadow=#40474747";
|
||||
* }
|
||||
* \endcode
|
||||
*/
|
||||
class CDockOverlayCross : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString iconColors READ iconColors WRITE setIconColors)
|
||||
Q_PROPERTY(QColor iconFrameColor READ iconColor WRITE setIconFrameColor)
|
||||
Q_PROPERTY(QColor iconBackgroundColor READ iconColor WRITE setIconBackgroundColor)
|
||||
Q_PROPERTY(QColor iconOverlayColor READ iconColor WRITE setIconOverlayColor)
|
||||
Q_PROPERTY(QColor iconArrowColor READ iconColor WRITE setIconArrowColor)
|
||||
Q_PROPERTY(QColor iconShadowColor READ iconColor WRITE setIconShadowColor)
|
||||
|
||||
public:
|
||||
enum eIconColor
|
||||
{
|
||||
FrameColor,///< the color of the frame of the small window icon
|
||||
WindowBackgroundColor,///< the background color of the small window in the icon
|
||||
OverlayColor,///< the color that shows the overlay (the dock side) in the icon
|
||||
ArrowColor,///< the arrow that points into the direction
|
||||
ShadowColor///< the color of the shadow rectangle that is painted below the icons
|
||||
};
|
||||
|
||||
private:
|
||||
DockOverlayCrossPrivate* d;
|
||||
friend struct DockOverlayCrossPrivate;
|
||||
friend class CDockOverlay;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* This function returns an empty string and is only here to silence
|
||||
* moc
|
||||
*/
|
||||
QString iconColors() const;
|
||||
|
||||
/**
|
||||
* This is a dummy function for the property system
|
||||
*/
|
||||
QColor iconColor() const {return QColor();}
|
||||
void setIconFrameColor(const QColor& Color) {setIconColor(FrameColor, Color);}
|
||||
void setIconBackgroundColor(const QColor& Color) {setIconColor(WindowBackgroundColor, Color);}
|
||||
void setIconOverlayColor(const QColor& Color) {setIconColor(OverlayColor, Color);}
|
||||
void setIconArrowColor(const QColor& Color) {setIconColor(ArrowColor, Color);}
|
||||
void setIconShadowColor(const QColor& Color) {setIconColor(ShadowColor, Color);}
|
||||
|
||||
public:
|
||||
/**
|
||||
* Creates an overlay cross for the given overlay
|
||||
*/
|
||||
CDockOverlayCross(CDockOverlay* overlay);
|
||||
|
||||
/**
|
||||
* Virtual destructor
|
||||
*/
|
||||
virtual ~CDockOverlayCross();
|
||||
|
||||
/**
|
||||
* Sets a certain icon color
|
||||
*/
|
||||
void setIconColor(eIconColor ColorIndex, const QColor& Color);
|
||||
|
||||
/**
|
||||
* Returns the icon color given by ColorIndex
|
||||
*/
|
||||
QColor iconColor(eIconColor ColorIndex) const;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Recreates the overlay icons.
|
||||
*/
|
||||
void updateOverlayIcons();
|
||||
|
||||
/**
|
||||
* Resets and updates the
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* Updates the current position
|
||||
*/
|
||||
void updatePosition();
|
||||
|
||||
/**
|
||||
* A string with all icon colors to set.
|
||||
* You can use this property to style the overly icon via CSS stylesheet
|
||||
* file. The colors are set via a color identifier and a hex AARRGGBB value like
|
||||
* in the example below.
|
||||
* \code
|
||||
* ads--CDockOverlayCross
|
||||
* {
|
||||
* qproperty-iconColors: "Frame=#ff3d3d3d Background=#ff929292 Overlay=#1f3d3d3d Arrow=#ffb4b4b4 Shadow=#40474747";
|
||||
* }
|
||||
*/
|
||||
void setIconColors(const QString& Colors);
|
||||
|
||||
protected:
|
||||
virtual void showEvent(QShowEvent* e) override;
|
||||
void setAreaWidgets(const QHash<DockWidgetArea, QWidget*>& widgets);
|
||||
|
||||
private:
|
||||
|
||||
}; // CDockOverlayCross
|
||||
|
||||
} // namespace ads
|
||||
#endif // DockOverlayH
|
||||
94
src/DockSplitter.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
/*******************************************************************************
|
||||
** 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))
|
||||
{
|
||||
setProperty("ads-splitter", true);
|
||||
setChildrenCollapsible(false);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
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)->isHidden())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace ads
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// EOF DockSplitter.cpp
|
||||
68
src/DockSplitter.h
Normal file
@@ -0,0 +1,68 @@
|
||||
#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>
|
||||
|
||||
#include "ads_globals.h"
|
||||
|
||||
namespace ads
|
||||
{
|
||||
struct DockSplitterPrivate;
|
||||
|
||||
/**
|
||||
* Splitter used internally instead of QSplitter
|
||||
*/
|
||||
class ADS_EXPORT CDockSplitter : public QSplitter
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
DockSplitterPrivate* d;
|
||||
friend struct 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
|
||||
677
src/DockWidget.cpp
Normal file
@@ -0,0 +1,677 @@
|
||||
/*******************************************************************************
|
||||
** 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 <DockWidgetTab.h>
|
||||
#include "DockWidget.h"
|
||||
|
||||
#include <QBoxLayout>
|
||||
#include <QAction>
|
||||
#include <QSplitter>
|
||||
#include <QStack>
|
||||
#include <QScrollArea>
|
||||
#include <QTextStream>
|
||||
#include <QPointer>
|
||||
#include <QEvent>
|
||||
#include <QDebug>
|
||||
#include <QToolBar>
|
||||
#include <QXmlStreamWriter>
|
||||
|
||||
#include "DockContainerWidget.h"
|
||||
#include "DockAreaWidget.h"
|
||||
#include "DockManager.h"
|
||||
#include "FloatingDockContainer.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;
|
||||
CDockWidgetTab* TabWidget;
|
||||
CDockWidget::DockWidgetFeatures Features = CDockWidget::AllDockWidgetFeatures;
|
||||
CDockManager* DockManager = nullptr;
|
||||
CDockAreaWidget* DockArea = nullptr;
|
||||
QAction* ToggleViewAction;
|
||||
bool Closed = false;
|
||||
QScrollArea* ScrollArea = nullptr;
|
||||
QToolBar* ToolBar = nullptr;
|
||||
Qt::ToolButtonStyle ToolBarStyleDocked = Qt::ToolButtonIconOnly;
|
||||
Qt::ToolButtonStyle ToolBarStyleFloating = Qt::ToolButtonTextUnderIcon;
|
||||
QSize ToolBarIconSizeDocked = QSize(16, 16);
|
||||
QSize ToolBarIconSizeFloating = QSize(24, 24);
|
||||
bool IsFloatingTopLevel = false;
|
||||
|
||||
/**
|
||||
* Private data constructor
|
||||
*/
|
||||
DockWidgetPrivate(CDockWidget* _public);
|
||||
|
||||
/**
|
||||
* Show dock widget
|
||||
*/
|
||||
void showDockWidget();
|
||||
|
||||
/**
|
||||
* Hide dock widget.
|
||||
*/
|
||||
void hideDockWidget();
|
||||
|
||||
/**
|
||||
* Hides a dock area if all dock widgets in the area are closed.
|
||||
* This function updates the current selected tab and hides the parent
|
||||
* dock area if it is empty
|
||||
*/
|
||||
void updateParentDockArea();
|
||||
|
||||
/**
|
||||
* Setup the top tool bar
|
||||
*/
|
||||
void setupToolBar();
|
||||
|
||||
/**
|
||||
* Setup the main scroll area
|
||||
*/
|
||||
void setupScrollArea();
|
||||
};
|
||||
// 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->toggleView(true);
|
||||
DockArea->setCurrentDockWidget(_this);
|
||||
TabWidget->show();
|
||||
QSplitter* Splitter = internal::findParent<QSplitter*>(DockArea);
|
||||
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()
|
||||
{
|
||||
TabWidget->hide();
|
||||
updateParentDockArea();
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void DockWidgetPrivate::updateParentDockArea()
|
||||
{
|
||||
if (!DockArea)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto NextDockWidget = DockArea->nextOpenDockWidget(_this);
|
||||
if (NextDockWidget)
|
||||
{
|
||||
DockArea->setCurrentDockWidget(NextDockWidget);
|
||||
}
|
||||
else
|
||||
{
|
||||
DockArea->hideAreaWithNoVisibleContent();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void DockWidgetPrivate::setupToolBar()
|
||||
{
|
||||
ToolBar = new QToolBar(_this);
|
||||
ToolBar->setObjectName("dockWidgetToolBar");
|
||||
Layout->insertWidget(0, ToolBar);
|
||||
ToolBar->setIconSize(QSize(16, 16));
|
||||
ToolBar->toggleViewAction()->setEnabled(false);
|
||||
ToolBar->toggleViewAction()->setVisible(false);
|
||||
_this->connect(_this, SIGNAL(topLevelChanged(bool)), SLOT(setToolbarFloatingStyle(bool)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
//============================================================================
|
||||
void DockWidgetPrivate::setupScrollArea()
|
||||
{
|
||||
ScrollArea = new QScrollArea(_this);
|
||||
ScrollArea->setObjectName("dockWidgetScrollArea");
|
||||
ScrollArea->setWidgetResizable(true);
|
||||
Layout->addWidget(ScrollArea);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
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);
|
||||
setObjectName(title);
|
||||
|
||||
d->TabWidget = new CDockWidgetTab(this);
|
||||
d->ToggleViewAction = new QAction(title);
|
||||
d->ToggleViewAction->setCheckable(true);
|
||||
connect(d->ToggleViewAction, SIGNAL(triggered(bool)), this,
|
||||
SLOT(toggleView(bool)));
|
||||
setToolbarFloatingStyle(false);
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
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, eInsertMode InsertMode)
|
||||
{
|
||||
QScrollArea* ScrollAreaWidget = qobject_cast<QScrollArea*>(widget);
|
||||
if (ScrollAreaWidget || ForceNoScrollArea != InsertMode)
|
||||
{
|
||||
d->setupScrollArea();
|
||||
d->ScrollArea->setWidget(widget);
|
||||
}
|
||||
else
|
||||
{
|
||||
d->Layout->addWidget(widget);
|
||||
}
|
||||
|
||||
d->Widget = widget;
|
||||
d->Widget->setProperty("dockWidgetContent", true);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
QWidget* CDockWidget::widget() const
|
||||
{
|
||||
return d->Widget;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
CDockWidgetTab* CDockWidget::tabWidget() const
|
||||
{
|
||||
return d->TabWidget;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockWidget::setFeatures(DockWidgetFeatures features)
|
||||
{
|
||||
d->Features = features;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockWidget::setFeature(DockWidgetFeature flag, bool on)
|
||||
{
|
||||
d->Features.setFlag(flag, on);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
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
|
||||
{
|
||||
if (d->DockArea)
|
||||
{
|
||||
return d->DockArea->dockContainer();
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
CDockAreaWidget* CDockWidget::dockAreaWidget() const
|
||||
{
|
||||
return d->DockArea;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
bool CDockWidget::isFloating() const
|
||||
{
|
||||
if (!isInFloatingContainer())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return dockContainer()->topLevelDockWidget() == this;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
bool CDockWidget::isInFloatingContainer() const
|
||||
{
|
||||
auto Container = dockContainer();
|
||||
if (!Container)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Container->isFloating())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
bool CDockWidget::isClosed() const
|
||||
{
|
||||
return d->Closed;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
QAction* CDockWidget::toggleViewAction() const
|
||||
{
|
||||
return d->ToggleViewAction;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockWidget::setToggleViewActionMode(eToggleViewActionMode Mode)
|
||||
{
|
||||
if (ActionModeToggle == Mode)
|
||||
{
|
||||
d->ToggleViewAction->setCheckable(true);
|
||||
d->ToggleViewAction->setIcon(QIcon());
|
||||
}
|
||||
else
|
||||
{
|
||||
d->ToggleViewAction->setCheckable(false);
|
||||
d->ToggleViewAction->setIcon(d->TabWidget->icon());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockWidget::toggleView(bool Open)
|
||||
{
|
||||
// If the toggle view action mode is ActionModeShow, then Open is always
|
||||
// true if the sender is the toggle view action
|
||||
QAction* Sender = qobject_cast<QAction*>(sender());
|
||||
if (Sender == d->ToggleViewAction && !d->ToggleViewAction->isCheckable())
|
||||
{
|
||||
Open = true;
|
||||
}
|
||||
// If the dock widget state is different, then we really need to toggle
|
||||
// the state. If we are in the right state, then we simply make this
|
||||
// dock widget the current dock widget
|
||||
if (d->Closed != !Open)
|
||||
{
|
||||
toggleViewInternal(Open);
|
||||
}
|
||||
else if (Open && d->DockArea)
|
||||
{
|
||||
d->DockArea->setCurrentDockWidget(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockWidget::toggleViewInternal(bool Open)
|
||||
{
|
||||
CDockContainerWidget* DockContainer = dockContainer();
|
||||
CDockWidget* TopLevelDockWidgetBefore = DockContainer
|
||||
? DockContainer->topLevelDockWidget() : nullptr;
|
||||
|
||||
if (Open)
|
||||
{
|
||||
d->showDockWidget();
|
||||
}
|
||||
else
|
||||
{
|
||||
d->hideDockWidget();
|
||||
}
|
||||
d->Closed = !Open;
|
||||
d->ToggleViewAction->blockSignals(true);
|
||||
d->ToggleViewAction->setChecked(Open);
|
||||
d->ToggleViewAction->blockSignals(false);
|
||||
if (d->DockArea)
|
||||
{
|
||||
d->DockArea->toggleDockWidgetView(this, Open);
|
||||
}
|
||||
|
||||
if (Open && TopLevelDockWidgetBefore)
|
||||
{
|
||||
CDockWidget::emitTopLevelEventForWidget(TopLevelDockWidgetBefore, false);
|
||||
}
|
||||
|
||||
// Here we need to call the dockContainer() function again, because if
|
||||
// this dock widget was unassigned before the call to showDockWidget() then
|
||||
// it has a dock container now
|
||||
DockContainer = dockContainer();
|
||||
CDockWidget* TopLevelDockWidgetAfter = DockContainer
|
||||
? DockContainer->topLevelDockWidget() : nullptr;
|
||||
CDockWidget::emitTopLevelEventForWidget(TopLevelDockWidgetAfter, true);
|
||||
CFloatingDockContainer* FloatingContainer = DockContainer->floatingWidget();
|
||||
if (FloatingContainer)
|
||||
{
|
||||
FloatingContainer->updateWindowTitle();
|
||||
}
|
||||
|
||||
if (!Open)
|
||||
{
|
||||
emit closed();
|
||||
}
|
||||
emit viewToggled(Open);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockWidget::setDockArea(CDockAreaWidget* DockArea)
|
||||
{
|
||||
d->DockArea = DockArea;
|
||||
d->ToggleViewAction->setChecked(DockArea != nullptr && !this->isClosed());
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockWidget::saveState(QXmlStreamWriter& s) const
|
||||
{
|
||||
s.writeStartElement("Widget");
|
||||
s.writeAttribute("Name", objectName());
|
||||
s.writeAttribute("Closed", QString::number(d->Closed ? 1 : 0));
|
||||
s.writeEndElement();
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockWidget::flagAsUnassigned()
|
||||
{
|
||||
d->Closed = true;
|
||||
setParent(d->DockManager);
|
||||
setVisible(false);
|
||||
setDockArea(nullptr);
|
||||
tabWidget()->setParent(this);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
bool CDockWidget::event(QEvent *e)
|
||||
{
|
||||
if (e->type() == QEvent::WindowTitleChange)
|
||||
{
|
||||
emit titleChanged(windowTitle());
|
||||
}
|
||||
return QFrame::event(e);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockWidget::setIcon(const QIcon& Icon)
|
||||
{
|
||||
d->TabWidget->setIcon(Icon);
|
||||
if (!d->ToggleViewAction->isCheckable())
|
||||
{
|
||||
d->ToggleViewAction->setIcon(Icon);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
QIcon CDockWidget::icon() const
|
||||
{
|
||||
return d->TabWidget->icon();
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
QToolBar* CDockWidget::toolBar() const
|
||||
{
|
||||
return d->ToolBar;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
QToolBar* CDockWidget::createDefaultToolBar()
|
||||
{
|
||||
if (!d->ToolBar)
|
||||
{
|
||||
d->setupToolBar();
|
||||
}
|
||||
|
||||
return d->ToolBar;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockWidget::setToolBar(QToolBar* ToolBar)
|
||||
{
|
||||
if (d->ToolBar)
|
||||
{
|
||||
delete d->ToolBar;
|
||||
}
|
||||
|
||||
d->ToolBar = ToolBar;
|
||||
d->Layout->insertWidget(0, d->ToolBar);
|
||||
this->connect(this, SIGNAL(topLevelChanged(bool)), SLOT(setToolbarFloatingStyle(bool)));
|
||||
setToolbarFloatingStyle(isFloating());
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockWidget::setToolBarStyle(Qt::ToolButtonStyle Style, eState State)
|
||||
{
|
||||
if (StateFloating == State)
|
||||
{
|
||||
d->ToolBarStyleFloating = Style;
|
||||
}
|
||||
else
|
||||
{
|
||||
d->ToolBarStyleDocked = Style;
|
||||
}
|
||||
|
||||
setToolbarFloatingStyle(isFloating());
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
Qt::ToolButtonStyle CDockWidget::toolBarStyle(eState State) const
|
||||
{
|
||||
if (StateFloating == State)
|
||||
{
|
||||
return d->ToolBarStyleFloating;
|
||||
}
|
||||
else
|
||||
{
|
||||
return d->ToolBarStyleDocked;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockWidget::setToolBarIconSize(const QSize& IconSize, eState State)
|
||||
{
|
||||
if (StateFloating == State)
|
||||
{
|
||||
d->ToolBarIconSizeFloating = IconSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
d->ToolBarIconSizeDocked = IconSize;
|
||||
}
|
||||
|
||||
setToolbarFloatingStyle(isFloating());
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
QSize CDockWidget::toolBarIconSize(eState State) const
|
||||
{
|
||||
if (StateFloating == State)
|
||||
{
|
||||
return d->ToolBarIconSizeFloating;
|
||||
}
|
||||
else
|
||||
{
|
||||
return d->ToolBarIconSizeDocked;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockWidget::setToolbarFloatingStyle(bool Floating)
|
||||
{
|
||||
if (!d->ToolBar)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto IconSize = Floating ? d->ToolBarIconSizeFloating : d->ToolBarIconSizeDocked;
|
||||
if (IconSize != d->ToolBar->iconSize())
|
||||
{
|
||||
d->ToolBar->setIconSize(IconSize);
|
||||
}
|
||||
|
||||
auto ButtonStyle = Floating ? d->ToolBarStyleFloating : d->ToolBarStyleDocked;
|
||||
if (ButtonStyle != d->ToolBar->toolButtonStyle())
|
||||
{
|
||||
d->ToolBar->setToolButtonStyle(ButtonStyle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockWidget::emitTopLevelEventForWidget(CDockWidget* TopLevelDockWidget, bool Floating)
|
||||
{
|
||||
if (TopLevelDockWidget)
|
||||
{
|
||||
TopLevelDockWidget->dockAreaWidget()->updateTitleBarVisibility();
|
||||
TopLevelDockWidget->emitTopLevelChanged(Floating);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockWidget::emitTopLevelChanged(bool Floating)
|
||||
{
|
||||
if (Floating != d->IsFloatingTopLevel)
|
||||
{
|
||||
d->IsFloatingTopLevel = Floating;
|
||||
emit topLevelChanged(d->IsFloatingTopLevel);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockWidget::setClosedState(bool Closed)
|
||||
{
|
||||
d->Closed = Closed;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
QSize CDockWidget::minimumSizeHint() const
|
||||
{
|
||||
return QSize(60, 40);
|
||||
}
|
||||
|
||||
} // namespace ads
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// EOF DockWidget.cpp
|
||||
427
src/DockWidget.h
Normal file
@@ -0,0 +1,427 @@
|
||||
#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>
|
||||
|
||||
#include "ads_globals.h"
|
||||
|
||||
class QToolBar;
|
||||
class QXmlStreamWriter;
|
||||
|
||||
namespace ads
|
||||
{
|
||||
struct DockWidgetPrivate;
|
||||
class CDockWidgetTab;
|
||||
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 ADS_EXPORT CDockWidget : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
DockWidgetPrivate* d; ///< private data (pimpl)
|
||||
friend struct DockWidgetPrivate;
|
||||
|
||||
private slots:
|
||||
/**
|
||||
* Adjusts the toolbar icon sizes according to the floating state
|
||||
*/
|
||||
void setToolbarFloatingStyle(bool topLevel);
|
||||
|
||||
protected:
|
||||
friend class CDockContainerWidget;
|
||||
friend class CDockAreaWidget;
|
||||
friend class CFloatingDockContainer;
|
||||
friend class CDockManager;
|
||||
friend struct DockManagerPrivate;
|
||||
friend struct DockContainerWidgetPrivate;
|
||||
friend class CDockAreaTabBar;
|
||||
friend class CDockWidgetTab;
|
||||
friend struct DockWidgetTabPrivate;
|
||||
|
||||
/**
|
||||
* 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(QXmlStreamWriter& 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();
|
||||
|
||||
/**
|
||||
* Call this function to emit a topLevelChanged() signal and to update
|
||||
* the dock area tool bar visibility
|
||||
*/
|
||||
static void emitTopLevelEventForWidget(CDockWidget* TopLevelDockWidget, bool Floating);
|
||||
|
||||
/**
|
||||
* Use this function to emit a top level changed event.
|
||||
* Do never use emit topLevelChanged(). Always use this function because
|
||||
* it only emits a signal if the floating state has really changed
|
||||
*/
|
||||
void emitTopLevelChanged(bool Floating);
|
||||
|
||||
/**
|
||||
* Internal function for modifying the closed state when restoring
|
||||
* a saved docking state
|
||||
*/
|
||||
void setClosedState(bool Closed);
|
||||
|
||||
/**
|
||||
* Internal toggle view function that does not check if the widget
|
||||
* already is in the given state
|
||||
*/
|
||||
void toggleViewInternal(bool Open);
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the widget for the dock widget to widget.
|
||||
* The InsertMode defines how the widget is inserted into the dock widget.
|
||||
* The content of a dock widget should be resizable do a very small size to
|
||||
* prevent the dock widget from blocking the resizing. To ensure, that a
|
||||
* dock widget can be resized very well, it is better to insert the content+
|
||||
* widget into a scroll area or to provide a widget that is already a scroll
|
||||
* area or that contains a scroll area.
|
||||
* If the InsertMode is AutoScrollArea, the DockWidget tries to automatically
|
||||
* detect how to insert the given widget. If the widget is derived from
|
||||
* QScrollArea (i.e. an QAbstractItemView), then the widget is inserted
|
||||
* directly. If the given widget is not a scroll area, the widget will be
|
||||
* inserted into a scroll area.
|
||||
* To force insertion into a scroll area, you can also provide the InsertMode
|
||||
* ForceScrollArea. To prevent insertion into a scroll area, you can
|
||||
* provide the InsertMode ForceNoScrollArea
|
||||
*/
|
||||
enum eInsertMode
|
||||
{
|
||||
AutoScrollArea,
|
||||
ForceScrollArea,
|
||||
ForceNoScrollArea
|
||||
};
|
||||
|
||||
/**
|
||||
* This mode configures the behavior of the toggle view action.
|
||||
* If the mode if ActionModeToggle, then the toggle view action is
|
||||
* a checkable action to show / hide the dock widget. If the mode
|
||||
* is ActionModeShow, then the action is not checkable an it will
|
||||
* always show the dock widget if clicked. If the mode is ActionModeShow,
|
||||
* the user can only close the DockWidget with the close button.
|
||||
*/
|
||||
enum eToggleViewActionMode
|
||||
{
|
||||
ActionModeToggle,//!< ActionModeToggle
|
||||
ActionModeShow //!< ActionModeShow
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This constructor creates a dock widget with the given title.
|
||||
* The title is the text that is shown in the window title when the dock
|
||||
* widget is floating and it is the title that is shown in the titlebar
|
||||
* or the tab of this dock widget if it is tabified.
|
||||
* The object name of the dock widget is also set to the title. The
|
||||
* object name is required by the dock manager to properly save and restore
|
||||
* the state of the dock widget. That means, the title needs to be unique.
|
||||
* If your title is not unique or if you would like to change the title
|
||||
* during runtime, you need to set a unique object name explicitely
|
||||
* by calling setObjectName() after construction.
|
||||
* Use the layoutFlags to configure the layout of the dock widget.
|
||||
*/
|
||||
CDockWidget(const QString &title, QWidget* parent = 0);
|
||||
|
||||
/**
|
||||
* Virtual Destructor
|
||||
*/
|
||||
virtual ~CDockWidget();
|
||||
|
||||
/**
|
||||
* We return a fixed minimum size hint for all dock widgets
|
||||
*/
|
||||
virtual QSize minimumSizeHint() const override;
|
||||
|
||||
/**
|
||||
* Sets the widget for the dock widget to widget.
|
||||
* The InsertMode defines how the widget is inserted into the dock widget.
|
||||
* The content of a dock widget should be resizable do a very small size to
|
||||
* prevent the dock widget from blocking the resizing. To ensure, that a
|
||||
* dock widget can be resized very well, it is better to insert the content+
|
||||
* widget into a scroll area or to provide a widget that is already a scroll
|
||||
* area or that contains a scroll area.
|
||||
* If the InsertMode is AutoScrollArea, the DockWidget tries to automatically
|
||||
* detect how to insert the given widget. If the widget is derived from
|
||||
* QScrollArea (i.e. an QAbstractItemView), then the widget is inserted
|
||||
* directly. If the given widget is not a scroll area, the widget will be
|
||||
* inserted into a scroll area.
|
||||
* To force insertion into a scroll area, you can also provide the InsertMode
|
||||
* ForceScrollArea. To prevent insertion into a scroll area, you can
|
||||
* provide the InsertMode ForceNoScrollArea
|
||||
*/
|
||||
void setWidget(QWidget* widget, eInsertMode InsertMode = AutoScrollArea);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
CDockWidgetTab* tabWidget() const;
|
||||
|
||||
/**
|
||||
* Sets, whether the dock widget is movable, closable, and floatable.
|
||||
*/
|
||||
void setFeatures(DockWidgetFeatures features);
|
||||
|
||||
/**
|
||||
* Sets the feature flag for this dock widget if on is true; otherwise
|
||||
* clears the flag.
|
||||
*/
|
||||
void setFeature(DockWidgetFeature flag, bool on);
|
||||
|
||||
/**
|
||||
* 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 not 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.
|
||||
* A dock widget is only floating, if it is the one and only widget inside
|
||||
* of a floating container. If there are more than one dock widget in a
|
||||
* floating container, the all dock widgets are docked and not floating.
|
||||
*/
|
||||
bool isFloating() const;
|
||||
|
||||
/**
|
||||
* This function returns true, if this dock widget is in a floating.
|
||||
* The function returns true, if the dock widget is floating and it also
|
||||
* returns true if it is docked inside of a floating container.
|
||||
*/
|
||||
bool isInFloatingContainer() 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;
|
||||
|
||||
/**
|
||||
* Configures the behavior of the toggle view action.
|
||||
* \see eToggleViewActionMode for a detailed description
|
||||
*/
|
||||
void setToggleViewActionMode(eToggleViewActionMode Mode);
|
||||
|
||||
/**
|
||||
* Sets the dock widget icon that is shown in tabs and in toggle view
|
||||
* actions
|
||||
*/
|
||||
void setIcon(const QIcon& Icon);
|
||||
|
||||
/**
|
||||
* Returns the icon that has been assigned to the dock widget
|
||||
*/
|
||||
QIcon icon() const;
|
||||
|
||||
/**
|
||||
* If the WithToolBar layout flag is enabled, then this function returns
|
||||
* the dock widget toolbar. If the flag is disabled, the function returns
|
||||
* a nullptr.
|
||||
* This function returns the dock widget top tool bar.
|
||||
* If no toolbar is assigned, this function returns nullptr. To get a vaild
|
||||
* toolbar you either need to create a default empty toolbar via
|
||||
* createDefaultToolBar() function or you need to assign you custom
|
||||
* toolbar via setToolBar().
|
||||
*/
|
||||
QToolBar* toolBar() const;
|
||||
|
||||
/**
|
||||
* If you would like to use the default top tool bar, then call this
|
||||
* function to create the default tool bar.
|
||||
* After this function the toolBar() function will return a valid toolBar()
|
||||
* object.
|
||||
*/
|
||||
QToolBar* createDefaultToolBar();
|
||||
|
||||
/**
|
||||
* Assign a new tool bar that is shown above the content widget.
|
||||
* The dock widget will become the owner of the tool bar and deletes it
|
||||
* on destruction
|
||||
*/
|
||||
void setToolBar(QToolBar* ToolBar);
|
||||
|
||||
/**
|
||||
* This function sets the tool button style for the given dock widget state.
|
||||
* It is possible to switch the tool button style depending on the state.
|
||||
* If a dock widget is floating, then here are more space and it is
|
||||
* possible to select a style that requires more space like
|
||||
* Qt::ToolButtonTextUnderIcon. For the docked state Qt::ToolButtonIconOnly
|
||||
* might be better.
|
||||
*/
|
||||
void setToolBarStyle(Qt::ToolButtonStyle Style, eState State);
|
||||
|
||||
/**
|
||||
* Returns the tool button style for the given docking state.
|
||||
* \see setToolBarStyle()
|
||||
*/
|
||||
Qt::ToolButtonStyle toolBarStyle(eState State) const;
|
||||
|
||||
/**
|
||||
* This function sets the tool button icon size for the given state.
|
||||
* If a dock widget is floating, there is more space an increasing the
|
||||
* icon size is possible. For docked widgets, small icon sizes, eg. 16 x 16
|
||||
* might be better.
|
||||
*/
|
||||
void setToolBarIconSize(const QSize& IconSize, eState State);
|
||||
|
||||
/**
|
||||
* Returns the icon size for a given docking state.
|
||||
* \see setToolBarIconSize()
|
||||
*/
|
||||
QSize toolBarIconSize(eState State) const;
|
||||
|
||||
|
||||
public: // reimplements QFrame -----------------------------------------------
|
||||
/**
|
||||
* Emits titleChanged signal if title change event occurs
|
||||
*/
|
||||
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 = true);
|
||||
|
||||
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);
|
||||
|
||||
/**
|
||||
* This signal is emitted when the floating property changes.
|
||||
* The topLevel parameter is true if the dock widget is now floating;
|
||||
* otherwise it is false.
|
||||
*/
|
||||
void topLevelChanged(bool topLevel);
|
||||
}; // class DockWidget
|
||||
}
|
||||
// namespace ads
|
||||
//-----------------------------------------------------------------------------
|
||||
#endif // DockWidgetH
|
||||
506
src/DockWidgetTab.cpp
Normal file
@@ -0,0 +1,506 @@
|
||||
/*******************************************************************************
|
||||
** 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 DockWidgetTab.cpp
|
||||
/// \author Uwe Kindler
|
||||
/// \date 27.02.2017
|
||||
/// \brief Implementation of CDockWidgetTab class
|
||||
//============================================================================
|
||||
|
||||
|
||||
//============================================================================
|
||||
// INCLUDES
|
||||
//============================================================================
|
||||
#include <ElidingLabel.h>
|
||||
#include "DockWidgetTab.h"
|
||||
|
||||
#include <QBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QMouseEvent>
|
||||
#include <QStyle>
|
||||
#include <QApplication>
|
||||
#include <QSplitter>
|
||||
#include <QDebug>
|
||||
#include <QToolButton>
|
||||
#include <QPushButton>
|
||||
#include <QMenu>
|
||||
|
||||
#include "ads_globals.h"
|
||||
#include "DockWidget.h"
|
||||
#include "DockAreaWidget.h"
|
||||
#include "FloatingDockContainer.h"
|
||||
#include "DockOverlay.h"
|
||||
#include "DockManager.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace ads
|
||||
{
|
||||
/**
|
||||
* The different dragging states
|
||||
*/
|
||||
enum eDragState
|
||||
{
|
||||
DraggingInactive, //!< DraggingInactive
|
||||
DraggingMousePressed, //!< DraggingMousePressed
|
||||
DraggingTab, //!< DraggingTab
|
||||
DraggingFloatingWidget//!< DraggingFloatingWidget
|
||||
};
|
||||
|
||||
using tTabLabel = CElidingLabel;
|
||||
using tCloseButton = QPushButton;
|
||||
|
||||
/**
|
||||
* Private data class of CDockWidgetTab class (pimpl)
|
||||
*/
|
||||
struct DockWidgetTabPrivate
|
||||
{
|
||||
CDockWidgetTab* _this;
|
||||
CDockWidget* DockWidget;
|
||||
QLabel* IconLabel = nullptr;
|
||||
tTabLabel* TitleLabel;
|
||||
QPoint DragStartMousePosition;
|
||||
bool IsActiveTab = false;
|
||||
CDockAreaWidget* DockArea = nullptr;
|
||||
eDragState DragState = DraggingInactive;
|
||||
CFloatingDockContainer* FloatingWidget = nullptr;
|
||||
QIcon Icon;
|
||||
tCloseButton* CloseButton = nullptr;
|
||||
QSpacerItem* IconTextSpacer;
|
||||
|
||||
/**
|
||||
* Private data constructor
|
||||
*/
|
||||
DockWidgetTabPrivate(CDockWidgetTab* _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->titleBarGeometry().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();
|
||||
|
||||
/**
|
||||
* Returns true if the given config flag is set
|
||||
*/
|
||||
bool testConfigFlag(CDockManager::eConfigFlag Flag) const
|
||||
{
|
||||
return DockArea->dockManager()->configFlags().testFlag(Flag);
|
||||
}
|
||||
};
|
||||
// struct DockWidgetTabPrivate
|
||||
|
||||
|
||||
//============================================================================
|
||||
DockWidgetTabPrivate::DockWidgetTabPrivate(CDockWidgetTab* _public) :
|
||||
_this(_public)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void DockWidgetTabPrivate::createLayout()
|
||||
{
|
||||
TitleLabel = new tTabLabel();
|
||||
TitleLabel->setElideMode(Qt::ElideRight);
|
||||
TitleLabel->setText(DockWidget->windowTitle());
|
||||
TitleLabel->setObjectName("dockWidgetTabLabel");
|
||||
TitleLabel->setAlignment(Qt::AlignCenter);
|
||||
|
||||
CloseButton = new tCloseButton();
|
||||
CloseButton->setObjectName("tabCloseButton");
|
||||
// The standard icons do does not look good on high DPI screens
|
||||
QIcon CloseIcon = _this->style()->standardIcon(QStyle::SP_TitleBarCloseButton);
|
||||
QPixmap normalPixmap = _this->style()->standardPixmap(QStyle::SP_TitleBarCloseButton, 0, CloseButton);
|
||||
QPixmap disabledPixmap = internal::createTransparentPixmap(normalPixmap, 0.25);
|
||||
CloseIcon.addPixmap(disabledPixmap, QIcon::Disabled);
|
||||
CloseButton->setIcon(CloseIcon);
|
||||
CloseButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
CloseButton->setVisible(false);
|
||||
CloseButton->setToolTip(QObject::tr("Close Tab"));
|
||||
_this->connect(CloseButton, SIGNAL(clicked()), SIGNAL(closeRequested()));
|
||||
|
||||
QFontMetrics fm(TitleLabel->font());
|
||||
int Spacing = qRound(fm.height() / 4.0);
|
||||
|
||||
// Fill the layout
|
||||
QBoxLayout* Layout = new QBoxLayout(QBoxLayout::LeftToRight);
|
||||
Layout->setContentsMargins(2 * Spacing,0,0,0);
|
||||
Layout->setSpacing(0);
|
||||
_this->setLayout(Layout);
|
||||
Layout->addWidget(TitleLabel, 1);
|
||||
Layout->addSpacing(Spacing);
|
||||
Layout->addWidget(CloseButton);
|
||||
Layout->addSpacing(qRound(Spacing * 4.0 / 3.0));
|
||||
Layout->setAlignment(Qt::AlignCenter);
|
||||
|
||||
TitleLabel->setVisible(true);
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
void DockWidgetTabPrivate::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 DockWidgetTabPrivate::startFloating()
|
||||
{
|
||||
auto dockContainer = DockWidget->dockContainer();
|
||||
qDebug() << "isFloating " << dockContainer->isFloating();
|
||||
qDebug() << "areaCount " << dockContainer->dockAreaCount();
|
||||
qDebug() << "widgetCount " << DockWidget->dockAreaWidget()->dockWidgetsCount();
|
||||
// if this is the last dock widget inside of this floating widget,
|
||||
// then it does not make any sense, to make it floating because
|
||||
// it is already floating
|
||||
if (dockContainer->isFloating()
|
||||
&& (dockContainer->visibleDockAreaCount() == 1)
|
||||
&& (DockWidget->dockAreaWidget()->dockWidgetsCount() == 1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
qDebug() << "startFloating";
|
||||
DragState = DraggingFloatingWidget;
|
||||
QSize Size = DockArea->size();
|
||||
CFloatingDockContainer* FloatingWidget = nullptr;
|
||||
if (DockArea->dockWidgetsCount() > 1)
|
||||
{
|
||||
// If section widget has multiple tabs, we take only one tab
|
||||
FloatingWidget = new CFloatingDockContainer(DockWidget);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 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;
|
||||
DockWidget->emitTopLevelChanged(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
CDockWidgetTab::CDockWidgetTab(CDockWidget* DockWidget, QWidget *parent) :
|
||||
QFrame(parent),
|
||||
d(new DockWidgetTabPrivate(this))
|
||||
{
|
||||
setAttribute(Qt::WA_NoMousePropagation, true);
|
||||
d->DockWidget = DockWidget;
|
||||
d->createLayout();
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
CDockWidgetTab::~CDockWidgetTab()
|
||||
{
|
||||
qDebug() << "~CDockWidgetTab()";
|
||||
delete d;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockWidgetTab::mousePressEvent(QMouseEvent* ev)
|
||||
{
|
||||
if (ev->button() == Qt::LeftButton)
|
||||
{
|
||||
ev->accept();
|
||||
d->DragStartMousePosition = ev->pos();
|
||||
d->DragState = DraggingMousePressed;
|
||||
emit clicked();
|
||||
return;
|
||||
}
|
||||
QFrame::mousePressEvent(ev);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockWidgetTab::mouseReleaseEvent(QMouseEvent* ev)
|
||||
{
|
||||
// End of tab moving, emit signal
|
||||
if (d->isDraggingState(DraggingTab) && d->DockArea)
|
||||
{
|
||||
emit moved(ev->globalPos());
|
||||
}
|
||||
|
||||
d->DragStartMousePosition = QPoint();
|
||||
d->DragState = DraggingInactive;
|
||||
QFrame::mouseReleaseEvent(ev);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockWidgetTab::mouseMoveEvent(QMouseEvent* ev)
|
||||
{
|
||||
if (!(ev->buttons() & Qt::LeftButton) || d->isDraggingState(DraggingInactive))
|
||||
{
|
||||
d->DragState = DraggingInactive;
|
||||
QFrame::mouseMoveEvent(ev);
|
||||
return;
|
||||
}
|
||||
|
||||
// move floating window
|
||||
if (d->isDraggingState(DraggingFloatingWidget))
|
||||
{
|
||||
d->FloatingWidget->moveFloating();
|
||||
QFrame::mouseMoveEvent(ev);
|
||||
return;
|
||||
}
|
||||
|
||||
// move tab
|
||||
if (d->isDraggingState(DraggingTab))
|
||||
{
|
||||
// Moving the tab is always allowed because it does not mean moving the
|
||||
// dock widget around
|
||||
d->moveTab(ev);
|
||||
}
|
||||
|
||||
// Maybe a fixed drag distance is better here ?
|
||||
int DragDistanceY = qAbs(d->DragStartMousePosition.y() - ev->pos().y());
|
||||
if (DragDistanceY >= CDockManager::startDragDistance())
|
||||
{
|
||||
// If this is the last dock area in a dock container with only
|
||||
// one single dock widget it does not make sense to move it to a new
|
||||
// floating widget and leave this one empty
|
||||
if (d->DockArea->dockContainer()->isFloating()
|
||||
&& d->DockArea->openDockWidgetsCount() == 1
|
||||
&& d->DockArea->dockContainer()->visibleDockAreaCount() == 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Floating is only allowed for widgets that are movable
|
||||
if (d->DockWidget->features().testFlag(CDockWidget::DockWidgetMovable))
|
||||
{
|
||||
d->startFloating();
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (d->DockArea->openDockWidgetsCount() > 1
|
||||
&& (ev->pos() - d->DragStartMousePosition).manhattanLength() >= QApplication::startDragDistance()) // Wait a few pixels before start moving
|
||||
{
|
||||
d->DragState = DraggingTab;
|
||||
return;
|
||||
}
|
||||
|
||||
QFrame::mouseMoveEvent(ev);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockWidgetTab::contextMenuEvent(QContextMenuEvent* ev)
|
||||
{
|
||||
ev->accept();
|
||||
std::cout << "CDockAreaTabBar::onTabContextMenuRequested" << std::endl;
|
||||
|
||||
d->DragStartMousePosition = ev->pos();
|
||||
QMenu Menu(this);
|
||||
Menu.addAction(tr("Detach"), this, SLOT(onDetachActionTriggered()));
|
||||
Menu.addSeparator();
|
||||
auto Action = Menu.addAction(tr("Close"), this, SIGNAL(closeRequested()));
|
||||
Action->setEnabled(isClosable());
|
||||
Menu.addAction(tr("Close Others"), this, SIGNAL(closeOtherTabsRequested()));
|
||||
Menu.exec(mapToGlobal(ev->pos()));
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
bool CDockWidgetTab::isActiveTab() const
|
||||
{
|
||||
return d->IsActiveTab;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockWidgetTab::setActiveTab(bool active)
|
||||
{
|
||||
bool DockWidgetClosable = d->DockWidget->features().testFlag(CDockWidget::DockWidgetClosable);
|
||||
bool TabHasCloseButton = d->testConfigFlag(CDockManager::ActiveTabHasCloseButton);
|
||||
d->CloseButton->setVisible(active && DockWidgetClosable && TabHasCloseButton);
|
||||
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* CDockWidgetTab::dockWidget() const
|
||||
{
|
||||
return d->DockWidget;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockWidgetTab::setDockAreaWidget(CDockAreaWidget* DockArea)
|
||||
{
|
||||
d->DockArea = DockArea;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
CDockAreaWidget* CDockWidgetTab::dockAreaWidget() const
|
||||
{
|
||||
return d->DockArea;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockWidgetTab::setIcon(const QIcon& Icon)
|
||||
{
|
||||
QBoxLayout* Layout = qobject_cast<QBoxLayout*>(layout());
|
||||
if (!d->IconLabel && Icon.isNull())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!d->IconLabel)
|
||||
{
|
||||
d->IconLabel = new QLabel();
|
||||
d->IconLabel->setAlignment(Qt::AlignVCenter);
|
||||
d->IconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
|
||||
d->IconLabel->setToolTip(d->TitleLabel->toolTip());
|
||||
Layout->insertWidget(0, d->IconLabel, Qt::AlignVCenter);
|
||||
Layout->insertSpacing(1, qRound(1.5 * Layout->contentsMargins().left() / 2.0));
|
||||
}
|
||||
else if (Icon.isNull())
|
||||
{
|
||||
// Remove icon label and spacer item
|
||||
Layout->removeWidget(d->IconLabel);
|
||||
Layout->removeItem(Layout->itemAt(0));
|
||||
delete d->IconLabel;
|
||||
d->IconLabel = nullptr;
|
||||
}
|
||||
|
||||
d->Icon = Icon;
|
||||
if (d->IconLabel)
|
||||
{
|
||||
d->IconLabel->setPixmap(Icon.pixmap(this->windowHandle(), QSize(16, 16)));
|
||||
d->IconLabel->setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
const QIcon& CDockWidgetTab::icon() const
|
||||
{
|
||||
return d->Icon;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
QString CDockWidgetTab::text() const
|
||||
{
|
||||
return d->TitleLabel->text();
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockWidgetTab::mouseDoubleClickEvent(QMouseEvent *event)
|
||||
{
|
||||
// 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 (!d->DockArea->dockContainer()->isFloating() || d->DockArea->dockWidgetsCount() > 1)
|
||||
{
|
||||
d->DragStartMousePosition = event->pos();
|
||||
d->startFloating();
|
||||
}
|
||||
|
||||
Super::mouseDoubleClickEvent(event);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CDockWidgetTab::setVisible(bool visible)
|
||||
{
|
||||
// Just here for debugging to insert debug output
|
||||
Super::setVisible(visible);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
bool CDockWidgetTab::isClosable() const
|
||||
{
|
||||
return d->DockWidget && d->DockWidget->features().testFlag(CDockWidget::DockWidgetClosable);
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
void CDockWidgetTab::onDetachActionTriggered()
|
||||
{
|
||||
d->DragStartMousePosition = mapFromGlobal(QCursor::pos());
|
||||
d->startFloating();
|
||||
}
|
||||
|
||||
} // namespace ads
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// EOF DockWidgetTab.cpp
|
||||
146
src/DockWidgetTab.h
Normal file
@@ -0,0 +1,146 @@
|
||||
#ifndef DockWidgetTabH
|
||||
#define DockWidgetTabH
|
||||
/*******************************************************************************
|
||||
** 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 DockWidgetTab.h
|
||||
/// \author Uwe Kindler
|
||||
/// \date 27.02.2017
|
||||
/// \brief Declaration of CDockWidgetTab class
|
||||
//============================================================================
|
||||
|
||||
|
||||
//============================================================================
|
||||
// INCLUDES
|
||||
//============================================================================
|
||||
#include <QFrame>
|
||||
|
||||
#include "ads_globals.h"
|
||||
|
||||
namespace ads
|
||||
{
|
||||
class CDockWidget;
|
||||
class CDockAreaWidget;
|
||||
struct DockWidgetTabPrivate;
|
||||
|
||||
/**
|
||||
* A dock widget tab that shows a title and an icon.
|
||||
* The dock widget tab is shown in the dock area title bar to switch between
|
||||
* tabbed dock widgets
|
||||
*/
|
||||
class ADS_EXPORT CDockWidgetTab : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool activeTab READ isActiveTab WRITE setActiveTab NOTIFY activeTabChanged)
|
||||
|
||||
private:
|
||||
DockWidgetTabPrivate* d; ///< private data (pimpl)
|
||||
friend struct DockWidgetTabPrivate;
|
||||
|
||||
private slots:
|
||||
void onDetachActionTriggered();
|
||||
|
||||
protected:
|
||||
virtual void mousePressEvent(QMouseEvent* ev) override;
|
||||
virtual void mouseReleaseEvent(QMouseEvent* ev) override;
|
||||
virtual void mouseMoveEvent(QMouseEvent* ev) override;
|
||||
virtual void contextMenuEvent(QContextMenuEvent* ev) override;
|
||||
|
||||
/**
|
||||
* Double clicking the tab widget makes the assigned dock widget floating
|
||||
*/
|
||||
virtual void mouseDoubleClickEvent(QMouseEvent *event) override;
|
||||
|
||||
public:
|
||||
using Super = QFrame;
|
||||
/**
|
||||
* Default Constructor
|
||||
* param[in] DockWidget The dock widget this title bar belongs to
|
||||
* param[in] parent The parent widget of this title bar
|
||||
*/
|
||||
CDockWidgetTab(CDockWidget* DockWidget, QWidget* parent = 0);
|
||||
|
||||
/**
|
||||
* Virtual Destructor
|
||||
*/
|
||||
virtual ~CDockWidgetTab();
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Sets the icon to show in title bar
|
||||
*/
|
||||
void setIcon(const QIcon& Icon);
|
||||
|
||||
/**
|
||||
* Returns the icon
|
||||
*/
|
||||
const QIcon& icon() const;
|
||||
|
||||
/**
|
||||
* Returns the tab text
|
||||
*/
|
||||
QString text() const;
|
||||
|
||||
/**
|
||||
* This function returns true if the assigned dock widget is closeable
|
||||
*/
|
||||
bool isClosable() const;
|
||||
|
||||
public slots:
|
||||
virtual void setVisible(bool visible);
|
||||
|
||||
signals:
|
||||
void activeTabChanged();
|
||||
void clicked();
|
||||
void closeRequested();
|
||||
void closeOtherTabsRequested();
|
||||
void moved(const QPoint& GlobalPos);
|
||||
}; // class DockWidgetTab
|
||||
}
|
||||
// namespace ads
|
||||
//-----------------------------------------------------------------------------
|
||||
#endif // DockWidgetTabH
|
||||
200
src/ElidingLabel.cpp
Normal file
@@ -0,0 +1,200 @@
|
||||
/*******************************************************************************
|
||||
** 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 ElidingLabel.cpp
|
||||
/// \author Uwe Kindler
|
||||
/// \date 05.11.2018
|
||||
/// \brief Implementation of CElidingLabel
|
||||
//============================================================================
|
||||
|
||||
//============================================================================
|
||||
// INCLUDES
|
||||
//============================================================================
|
||||
#include <ElidingLabel.h>
|
||||
#include <QMouseEvent>
|
||||
|
||||
|
||||
namespace ads
|
||||
{
|
||||
/**
|
||||
* Private data of public CClickableLabel
|
||||
*/
|
||||
struct ElidingLabelPrivate
|
||||
{
|
||||
CElidingLabel* _this;
|
||||
Qt::TextElideMode ElideMode = Qt::ElideNone;
|
||||
QString Text;
|
||||
|
||||
ElidingLabelPrivate(CElidingLabel* _public) : _this(_public) {}
|
||||
|
||||
void elideText(int Width);
|
||||
|
||||
/**
|
||||
* Convenience function to check if the
|
||||
*/
|
||||
bool isModeElideNone() const
|
||||
{
|
||||
return Qt::ElideNone == ElideMode;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//============================================================================
|
||||
void ElidingLabelPrivate::elideText(int Width)
|
||||
{
|
||||
if (isModeElideNone())
|
||||
{
|
||||
return;
|
||||
}
|
||||
QFontMetrics fm = _this->fontMetrics();
|
||||
QString str = fm.elidedText(Text, ElideMode, Width - _this->margin() * 2 - _this->indent());
|
||||
if (str == "…")
|
||||
{
|
||||
str = Text.at(0);
|
||||
}
|
||||
_this->QLabel::setText(str);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
CElidingLabel::CElidingLabel(QWidget* parent, Qt::WindowFlags f)
|
||||
: QLabel(parent, f),
|
||||
d(new ElidingLabelPrivate(this))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
CElidingLabel::CElidingLabel(const QString& text, QWidget* parent, Qt::WindowFlags f)
|
||||
: QLabel(text, parent,f),
|
||||
d(new ElidingLabelPrivate(this))
|
||||
{
|
||||
d->Text = text;
|
||||
setToolTip(text);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
CElidingLabel::~CElidingLabel()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
Qt::TextElideMode CElidingLabel::elideMode() const
|
||||
{
|
||||
return d->ElideMode;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CElidingLabel::setElideMode(Qt::TextElideMode mode)
|
||||
{
|
||||
d->ElideMode = mode;
|
||||
d->elideText(size().width());
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CElidingLabel::mouseReleaseEvent(QMouseEvent* event)
|
||||
{
|
||||
Super::mouseReleaseEvent(event);
|
||||
if (event->button() != Qt::LeftButton)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
emit clicked();
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CElidingLabel::mouseDoubleClickEvent( QMouseEvent *ev )
|
||||
{
|
||||
Q_UNUSED(ev)
|
||||
emit doubleClicked();
|
||||
Super::mouseDoubleClickEvent(ev);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CElidingLabel::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
if (!d->isModeElideNone())
|
||||
{
|
||||
d->elideText(event->size().width());
|
||||
}
|
||||
Super::resizeEvent(event);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
QSize CElidingLabel::minimumSizeHint() const
|
||||
{
|
||||
if (pixmap() != nullptr || d->isModeElideNone())
|
||||
{
|
||||
return QLabel::minimumSizeHint();
|
||||
}
|
||||
const QFontMetrics &fm = fontMetrics();
|
||||
QSize size(fm.width(d->Text.left(2) + "…"), fm.height());
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
QSize CElidingLabel::sizeHint() const
|
||||
{
|
||||
if (pixmap() != nullptr || d->isModeElideNone())
|
||||
{
|
||||
return QLabel::sizeHint();
|
||||
}
|
||||
const QFontMetrics& fm = fontMetrics();
|
||||
QSize size(fm.width(d->Text), QLabel::sizeHint().height());
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CElidingLabel::setText(const QString &text)
|
||||
{
|
||||
if (d->isModeElideNone())
|
||||
{
|
||||
Super::setText(text);
|
||||
}
|
||||
else
|
||||
{
|
||||
d->Text = text;
|
||||
setToolTip( text );
|
||||
d->elideText(this->size().width());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
QString CElidingLabel::text() const
|
||||
{
|
||||
return d->Text;
|
||||
}
|
||||
} // namespace QtLabb
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// EOF ClickableLabel.cpp
|
||||
97
src/ElidingLabel.h
Normal file
@@ -0,0 +1,97 @@
|
||||
#ifndef ElidingLabelH
|
||||
#define ElidingLabelH
|
||||
/*******************************************************************************
|
||||
** 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 ElidingLabel.h
|
||||
/// \author Uwe Kindler
|
||||
/// \date 05.11.2018
|
||||
/// \brief Declaration of CElidingLabel
|
||||
//============================================================================
|
||||
|
||||
//============================================================================
|
||||
// INCLUDES
|
||||
//============================================================================
|
||||
#include <QLabel>
|
||||
|
||||
namespace ads
|
||||
{
|
||||
struct ElidingLabelPrivate;
|
||||
|
||||
/**
|
||||
* A QLabel that supports eliding text.
|
||||
* Because the functions setText() and text() are no virtual functions setting
|
||||
* and reading the text via a pointer to the base class QLabel does not work
|
||||
* properly
|
||||
*/
|
||||
class CElidingLabel : public QLabel
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
ElidingLabelPrivate* d;
|
||||
friend class ElidingLabelPrivate;
|
||||
|
||||
protected:
|
||||
virtual void mouseReleaseEvent(QMouseEvent* event) override;
|
||||
virtual void resizeEvent( QResizeEvent *event ) override;
|
||||
virtual void mouseDoubleClickEvent( QMouseEvent *ev ) override;
|
||||
|
||||
public:
|
||||
using Super = QLabel;
|
||||
|
||||
CElidingLabel(QWidget* parent = 0, Qt::WindowFlags f = 0);
|
||||
CElidingLabel(const QString& text, QWidget* parent = 0, Qt::WindowFlags f = 0);
|
||||
virtual ~CElidingLabel();
|
||||
|
||||
/**
|
||||
* Returns the text elide mode.
|
||||
* The default mode is ElideNone
|
||||
*/
|
||||
Qt::TextElideMode elideMode() const;
|
||||
|
||||
/**
|
||||
* Sets the text elide mode
|
||||
*/
|
||||
void setElideMode(Qt::TextElideMode mode);
|
||||
|
||||
|
||||
public: // reimplements QLabel ----------------------------------------------
|
||||
virtual QSize minimumSizeHint() const override;
|
||||
virtual QSize sizeHint() const override;
|
||||
void setText(const QString &text);
|
||||
QString text() const;
|
||||
|
||||
signals:
|
||||
/**
|
||||
* This signal is emitted if the user clicks on the label (i.e. pressed
|
||||
* down then released while the mouse cursor is inside the label)
|
||||
*/
|
||||
void clicked();
|
||||
|
||||
/**
|
||||
* This signal is emitted if the user does a double click on the label
|
||||
*/
|
||||
void doubleClicked();
|
||||
}; //class CElidingLabel
|
||||
|
||||
} // namespace QtLabb
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
#endif // ElidingLabelH
|
||||
550
src/FloatingDockContainer.cpp
Normal file
@@ -0,0 +1,550 @@
|
||||
/*******************************************************************************
|
||||
** 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 <QAbstractButton>
|
||||
#include <QElapsedTimer>
|
||||
|
||||
#include "DockContainerWidget.h"
|
||||
#include "DockAreaWidget.h"
|
||||
#include "DockManager.h"
|
||||
#include "DockWidget.h"
|
||||
#include "DockOverlay.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace ads
|
||||
{
|
||||
static unsigned int zOrderCounter = 0;
|
||||
/**
|
||||
* The different dragging states
|
||||
*/
|
||||
enum eDragState
|
||||
{
|
||||
StateInactive, //!< DraggingInactive
|
||||
StateMousePressed, //!< DraggingMousePressed
|
||||
StateDraggingActive//!< DraggingFloatingWidget
|
||||
};
|
||||
|
||||
/**
|
||||
* Private data class of CFloatingDockContainer class (pimpl)
|
||||
*/
|
||||
struct FloatingDockContainerPrivate
|
||||
{
|
||||
CFloatingDockContainer* _this;
|
||||
CDockContainerWidget* DockContainer;
|
||||
unsigned int zOrderIndex = ++zOrderCounter;
|
||||
QPointer<CDockManager> DockManager;
|
||||
eDragState DraggingState = StateInactive;
|
||||
QPoint DragStartMousePosition;
|
||||
CDockContainerWidget* DropContainer = nullptr;
|
||||
CDockAreaWidget* SingleDockArea = nullptr;
|
||||
|
||||
/**
|
||||
* Private data constructor
|
||||
*/
|
||||
FloatingDockContainerPrivate(CFloatingDockContainer* _public);
|
||||
|
||||
void titleMouseReleaseEvent();
|
||||
void updateDropOverlays(const QPoint& GlobalPos);
|
||||
|
||||
/**
|
||||
* Tests is a certain state is active
|
||||
*/
|
||||
bool isState(eDragState StateId) const
|
||||
{
|
||||
return StateId == DraggingState;
|
||||
}
|
||||
|
||||
void setState(eDragState StateId)
|
||||
{
|
||||
DraggingState = StateId;
|
||||
}
|
||||
};
|
||||
// struct FloatingDockContainerPrivate
|
||||
|
||||
|
||||
|
||||
//============================================================================
|
||||
FloatingDockContainerPrivate::FloatingDockContainerPrivate(CFloatingDockContainer* _public) :
|
||||
_this(_public)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//============================================================================
|
||||
void FloatingDockContainerPrivate::titleMouseReleaseEvent()
|
||||
{
|
||||
setState(StateInactive);
|
||||
if (!DropContainer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (DockManager->dockAreaOverlay()->dropAreaUnderCursor() != InvalidDockWidgetArea
|
||||
|| DockManager->containerOverlay()->dropAreaUnderCursor() != InvalidDockWidgetArea)
|
||||
{
|
||||
// Resize the floating widget to the size of the highlighted drop area
|
||||
// rectangle
|
||||
CDockOverlay* Overlay = DockManager->containerOverlay();
|
||||
if (!Overlay->dropOverlayRect().isValid())
|
||||
{
|
||||
Overlay = DockManager->dockAreaOverlay();
|
||||
}
|
||||
|
||||
QRect Rect = Overlay->dropOverlayRect();
|
||||
int FrameWidth = (_this->frameSize().width() - _this->rect().width()) / 2;
|
||||
int TitleBarHeight = _this->frameSize().height() - _this->rect().height() - FrameWidth;
|
||||
if (Rect.isValid())
|
||||
{
|
||||
QPoint TopLeft = Overlay->mapToGlobal(Rect.topLeft());
|
||||
TopLeft.ry() += TitleBarHeight;
|
||||
_this->setGeometry(QRect(TopLeft, QSize(Rect.width(), Rect.height() - TitleBarHeight)));
|
||||
QApplication::processEvents();
|
||||
}
|
||||
DropContainer->dropFloatingWidget(_this, QCursor::pos());
|
||||
}
|
||||
|
||||
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);
|
||||
DockWidgetArea ContainerArea = ContainerOverlay->showOverlay(TopContainer);
|
||||
ContainerOverlay->enableDropPreview(ContainerArea != InvalidDockWidgetArea);
|
||||
auto DockArea = TopContainer->dockAreaAt(GlobalPos);
|
||||
if (DockArea && DockArea->isVisible() && VisibleDockAreas > 0)
|
||||
{
|
||||
DockAreaOverlay->enableDropPreview(true);
|
||||
DockAreaOverlay->setAllowedAreas((VisibleDockAreas == 1) ?
|
||||
NoDockWidgetArea : AllDockAreas);
|
||||
DockWidgetArea Area = DockAreaOverlay->showOverlay(DockArea);
|
||||
|
||||
// A CenterDockWidgetArea for the dockAreaOverlay() indicates that
|
||||
// the mouse is in the title bar. If the ContainerArea is valid
|
||||
// then we ignore the dock area of the dockAreaOverlay() and disable
|
||||
// the drop preview
|
||||
if ((Area == CenterDockWidgetArea) && (ContainerArea != InvalidDockWidgetArea))
|
||||
{
|
||||
DockAreaOverlay->enableDropPreview(false);
|
||||
ContainerOverlay->enableDropPreview(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
ContainerOverlay->enableDropPreview(InvalidDockWidgetArea == Area);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DockAreaOverlay->hideOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
CFloatingDockContainer::CFloatingDockContainer(CDockManager* DockManager) :
|
||||
QWidget(DockManager, Qt::Window),
|
||||
d(new FloatingDockContainerPrivate(this))
|
||||
{
|
||||
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);
|
||||
switch (d->DraggingState)
|
||||
{
|
||||
case StateMousePressed:
|
||||
d->setState(StateDraggingActive);
|
||||
d->updateDropOverlays(QCursor::pos());
|
||||
break;
|
||||
|
||||
case StateDraggingActive:
|
||||
d->updateDropOverlays(QCursor::pos());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CFloatingDockContainer::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
qDebug() << "CFloatingDockContainer closeEvent";
|
||||
d->setState(StateInactive);
|
||||
|
||||
if (isClosable())
|
||||
{
|
||||
QWidget::closeEvent(event);
|
||||
}
|
||||
else
|
||||
{
|
||||
event->ignore();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CFloatingDockContainer::hideEvent(QHideEvent *event)
|
||||
{
|
||||
Super::hideEvent(event);
|
||||
for (auto DockArea : d->DockContainer->openedDockAreas())
|
||||
{
|
||||
for (auto DockWidget : DockArea->openedDockWidgets())
|
||||
{
|
||||
DockWidget->toggleView(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CFloatingDockContainer::showEvent(QShowEvent *event)
|
||||
{
|
||||
Super::showEvent(event);
|
||||
/*for (auto DockArea : d->DockContainer->openedDockAreas())
|
||||
{
|
||||
for (auto DockWidget : DockArea->openedDockWidgets())
|
||||
{
|
||||
DockWidget->setToggleViewActionChecked(true);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
bool CFloatingDockContainer::event(QEvent *e)
|
||||
{
|
||||
switch (d->DraggingState)
|
||||
{
|
||||
case StateInactive:
|
||||
if (e->type() == QEvent::NonClientAreaMouseButtonPress && QGuiApplication::mouseButtons() == Qt::LeftButton)
|
||||
{
|
||||
qDebug() << "FloatingWidget::event Event::NonClientAreaMouseButtonPress" << e->type();
|
||||
d->setState(StateMousePressed);
|
||||
}
|
||||
break;
|
||||
|
||||
case StateMousePressed:
|
||||
switch (e->type())
|
||||
{
|
||||
case QEvent::NonClientAreaMouseButtonDblClick:
|
||||
qDebug() << "FloatingWidget::event QEvent::NonClientAreaMouseButtonDblClick";
|
||||
d->setState(StateInactive);
|
||||
break;
|
||||
|
||||
case QEvent::Resize:
|
||||
// If the first event after the mouse press is a resize event, then
|
||||
// the user resizes the window instead of dragging it around.
|
||||
// But there is one exception. If the window is maximized,
|
||||
// then dragging the window via title bar will cause the widget to
|
||||
// leave the maximized state. This in turn will trigger a resize event.
|
||||
// To know, if the resize event was triggered by user via moving a
|
||||
// corner of the window frame or if it was caused by a windows state
|
||||
// change, we check, if we are not in maximized state.
|
||||
if (!isMaximized())
|
||||
{
|
||||
d->setState(StateInactive);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case StateDraggingActive:
|
||||
if (e->type() == QEvent::NonClientAreaMouseButtonRelease)
|
||||
{
|
||||
qDebug() << "FloatingWidget::event QEvent::NonClientAreaMouseButtonRelease";
|
||||
d->titleMouseReleaseEvent();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
#if (ADS_DEBUG_LEVEL > 0)
|
||||
qDebug() << "CFloatingDockContainer::event " << e->type();
|
||||
#endif
|
||||
return QWidget::event(e);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
bool CFloatingDockContainer::eventFilter(QObject *watched, QEvent *event)
|
||||
{
|
||||
Q_UNUSED(watched);
|
||||
if (event->type() == QEvent::MouseButtonRelease && d->isState(StateDraggingActive))
|
||||
{
|
||||
qDebug() << "FloatingWidget::eventFilter QEvent::MouseButtonRelease";
|
||||
d->titleMouseReleaseEvent();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CFloatingDockContainer::startFloating(const QPoint& Pos, const QSize& Size)
|
||||
{
|
||||
resize(Size);
|
||||
d->setState(StateDraggingActive);
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
bool CFloatingDockContainer::isClosable() const
|
||||
{
|
||||
return d->DockContainer->features().testFlag(CDockWidget::DockWidgetClosable);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CFloatingDockContainer::onDockAreasAddedOrRemoved()
|
||||
{
|
||||
qDebug() << "CFloatingDockContainer::onDockAreasAddedOrRemoved()";
|
||||
auto TopLevelDockArea = d->DockContainer->topLevelDockArea();
|
||||
if (TopLevelDockArea)
|
||||
{
|
||||
d->SingleDockArea = TopLevelDockArea;
|
||||
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::updateWindowTitle()
|
||||
{
|
||||
auto TopLevelDockArea = d->DockContainer->topLevelDockArea();
|
||||
if (TopLevelDockArea)
|
||||
{
|
||||
this->setWindowTitle(TopLevelDockArea->currentDockWidget()->windowTitle());
|
||||
}
|
||||
else
|
||||
{
|
||||
this->setWindowTitle(qApp->applicationDisplayName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void CFloatingDockContainer::onDockAreaCurrentChanged(int Index)
|
||||
{
|
||||
Q_UNUSED(Index);
|
||||
this->setWindowTitle(d->SingleDockArea->currentDockWidget()->windowTitle());
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
bool CFloatingDockContainer::restoreState(QXmlStreamReader& Stream, bool Testing)
|
||||
{
|
||||
if (!d->DockContainer->restoreState(Stream, Testing))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
onDockAreasAddedOrRemoved();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
bool CFloatingDockContainer::hasTopLevelDockWidget() const
|
||||
{
|
||||
return d->DockContainer->hasTopLevelDockWidget();
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
CDockWidget* CFloatingDockContainer::topLevelDockWidget() const
|
||||
{
|
||||
return d->DockContainer->topLevelDockWidget();
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
QList<CDockWidget*> CFloatingDockContainer::dockWidgets() const
|
||||
{
|
||||
return d->DockContainer->dockWidgets();
|
||||
}
|
||||
|
||||
|
||||
} // namespace ads
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// EOF FloatingDockContainer.cpp
|
||||
174
src/FloatingDockContainer.h
Normal file
@@ -0,0 +1,174 @@
|
||||
#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>
|
||||
|
||||
#include "ads_globals.h"
|
||||
|
||||
class QXmlStreamReader;
|
||||
|
||||
namespace ads
|
||||
{
|
||||
struct FloatingDockContainerPrivate;
|
||||
class CDockManager;
|
||||
struct DockManagerPrivate;
|
||||
class CDockAreaWidget;
|
||||
class CDockContainerWidget;
|
||||
class CDockWidget;
|
||||
class CDockManager;
|
||||
class CDockAreaTabBar;
|
||||
class CDockWidgetTab;
|
||||
struct DockWidgetTabPrivate;
|
||||
class CDockAreaTitleBar;
|
||||
struct DockAreaTitleBarPrivate;
|
||||
|
||||
/**
|
||||
* 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 ADS_EXPORT CFloatingDockContainer : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
FloatingDockContainerPrivate* d; ///< private data (pimpl)
|
||||
friend struct FloatingDockContainerPrivate;
|
||||
friend class CDockManager;
|
||||
friend struct DockManagerPrivate;
|
||||
friend class CDockAreaTabBar;
|
||||
friend struct DockWidgetTabPrivate;
|
||||
friend class CDockWidgetTab;
|
||||
friend class CDockAreaTitleBar;
|
||||
friend struct DockAreaTitleBarPrivate;
|
||||
friend class CDockWidget;
|
||||
friend class CDockAreaWidget;
|
||||
|
||||
private slots:
|
||||
void onDockAreasAddedOrRemoved();
|
||||
void onDockAreaCurrentChanged(int Index);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* 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(QXmlStreamReader& Stream, bool Testing);
|
||||
|
||||
/**
|
||||
* Call this function to update the window title
|
||||
*/
|
||||
void updateWindowTitle();
|
||||
|
||||
|
||||
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:
|
||||
using Super = QWidget;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* This function returns true, if it can be closed.
|
||||
* It can be closed, if all dock widgets in all dock areas can be closed
|
||||
*/
|
||||
bool isClosable() const;
|
||||
|
||||
/**
|
||||
* This function returns true, if this floating widget has only one single
|
||||
* visible dock widget in a single visible dock area.
|
||||
* The single dock widget is a real top level floating widget because no
|
||||
* other widgets are docked.
|
||||
*/
|
||||
bool hasTopLevelDockWidget() const;
|
||||
|
||||
/**
|
||||
* This function returns the first dock widget in the first dock area.
|
||||
* If the function hasSingleDockWidget() returns true, then this function
|
||||
* returns this single dock widget.
|
||||
*/
|
||||
CDockWidget* topLevelDockWidget() const;
|
||||
|
||||
/**
|
||||
* This function returns a list of all dock widget in this floating widget.
|
||||
* This is a simple convenience function that simply calls the dockWidgets()
|
||||
* function of the internal container widget.
|
||||
*/
|
||||
QList<CDockWidget*> dockWidgets() const;
|
||||
}; // class FloatingDockContainer
|
||||
}
|
||||
// namespace ads
|
||||
//-----------------------------------------------------------------------------
|
||||
#endif // FloatingDockContainerH
|
||||
7
src/ads.qrc
Normal file
@@ -0,0 +1,7 @@
|
||||
<RCC>
|
||||
<qresource prefix="/ads">
|
||||
<file>stylesheets/default.css</file>
|
||||
<file>images/close-button.svg</file>
|
||||
<file>images/close-button-disabled.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
100
src/ads_globals.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
/*******************************************************************************
|
||||
** 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 <QPainter>
|
||||
|
||||
#include "DockSplitter.h"
|
||||
#include "ads_globals.h"
|
||||
|
||||
|
||||
namespace ads
|
||||
{
|
||||
|
||||
namespace internal
|
||||
{
|
||||
//============================================================================
|
||||
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 CDockInsertParam(Qt::Vertical, false);
|
||||
case RightDockWidgetArea: return CDockInsertParam(Qt::Horizontal, true);
|
||||
case CenterDockWidgetArea:
|
||||
case BottomDockWidgetArea: return CDockInsertParam(Qt::Vertical, true);
|
||||
case LeftDockWidgetArea: return CDockInsertParam(Qt::Horizontal, false);
|
||||
default: CDockInsertParam(Qt::Vertical, false);
|
||||
} // switch (Area)
|
||||
|
||||
return CDockInsertParam(Qt::Vertical, false);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
QPixmap createTransparentPixmap(const QPixmap& Source, qreal Opacity)
|
||||
{
|
||||
QPixmap TransparentPixmap(Source.size());
|
||||
TransparentPixmap.fill(Qt::transparent);
|
||||
QPainter p(&TransparentPixmap);
|
||||
p.setOpacity(Opacity);
|
||||
p.drawPixmap(0, 0, Source);
|
||||
return TransparentPixmap;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
void hideEmptyParentSplitters(CDockSplitter* Splitter)
|
||||
{
|
||||
while (Splitter && Splitter->isVisible())
|
||||
{
|
||||
if (!Splitter->hasVisibleContent())
|
||||
{
|
||||
Splitter->hide();
|
||||
}
|
||||
Splitter = internal::findParent<CDockSplitter*>(Splitter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace internal
|
||||
} // namespace ads
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// EOF ads_globals.cpp
|
||||
145
src/ads_globals.h
Normal file
@@ -0,0 +1,145 @@
|
||||
#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>
|
||||
#include <QtCore/QtGlobal>
|
||||
#include <QPixmap>
|
||||
#include <QWidget>
|
||||
|
||||
#ifdef ADS_SHARED_EXPORT
|
||||
#define ADS_EXPORT Q_DECL_EXPORT
|
||||
#else
|
||||
#define ADS_EXPORT Q_DECL_IMPORT
|
||||
#endif
|
||||
|
||||
#define ADS_DEBUG_LEVEL 0
|
||||
|
||||
class QSplitter;
|
||||
|
||||
namespace ads
|
||||
{
|
||||
class CDockSplitter;
|
||||
|
||||
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)
|
||||
|
||||
|
||||
enum TitleBarButton
|
||||
{
|
||||
TitleBarButtonTabsMenu,
|
||||
TitleBarButtonUndock,
|
||||
TitleBarButtonClose
|
||||
};
|
||||
|
||||
namespace internal
|
||||
{
|
||||
static const bool RestoreTesting = true;
|
||||
static const bool Restore = false;
|
||||
|
||||
/**
|
||||
* Replace the from widget in the given splitter with the To widget
|
||||
*/
|
||||
void replaceSplitterWidget(QSplitter* Splitter, QWidget* From, QWidget* To);
|
||||
|
||||
/**
|
||||
* This function walks the splitter tree upwards to hides all splitters
|
||||
* that do not have visible content
|
||||
*/
|
||||
void hideEmptyParentSplitters(CDockSplitter* FirstParentSplitter);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* It is not safe to use this function in in CDockWidget because only
|
||||
* the current dock widget has a parent. All dock widgets that are not the
|
||||
* current dock widget in a dock area have no parent.
|
||||
*/
|
||||
template <class T>
|
||||
T findParent(const QWidget* w)
|
||||
{
|
||||
QWidget* parentWidget = w->parentWidget();
|
||||
while (parentWidget)
|
||||
{
|
||||
T ParentImpl = qobject_cast<T>(parentWidget);
|
||||
if (ParentImpl)
|
||||
{
|
||||
return ParentImpl;
|
||||
}
|
||||
parentWidget = parentWidget->parentWidget();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a semi transparent pixmap from the given pixmap Source.
|
||||
* The Opacity parameter defines the opacity from completely transparent (0.0)
|
||||
* to completely opaque (1.0)
|
||||
*/
|
||||
QPixmap createTransparentPixmap(const QPixmap& Source, qreal Opacity);
|
||||
|
||||
} // namespace internal
|
||||
} // namespace ads
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
#endif // ads_globalsH
|
||||
122
src/images/close-button-disabled.svg
Normal file
@@ -0,0 +1,122 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="Capa_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
width="512"
|
||||
height="512"
|
||||
viewBox="0 0 512 512"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="close-button-disabled.svg"
|
||||
inkscape:version="0.92.3 (2405546, 2018-03-11)"><metadata
|
||||
id="metadata897"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs895" /><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
id="namedview893"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:zoom="0.85862966"
|
||||
inkscape:cx="345.29142"
|
||||
inkscape:cy="32.731258"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Capa_1" />
|
||||
<g
|
||||
id="g860"
|
||||
transform="matrix(0.71708683,0,0,0.71708683,128,128)"
|
||||
style="stroke:none;stroke-opacity:1;fill:#000000;fill-opacity:0.50196081">
|
||||
<g
|
||||
id="close"
|
||||
style="stroke:none;stroke-opacity:1;fill:#000000;fill-opacity:0.50196081">
|
||||
<polygon
|
||||
points="357,321.3 214.2,178.5 357,35.7 321.3,0 178.5,142.8 35.7,0 0,35.7 142.8,178.5 0,321.3 35.7,357 178.5,214.2 321.3,357 "
|
||||
id="polygon857"
|
||||
style="stroke:none;stroke-opacity:1;fill:#000000;fill-opacity:0.50196081" />
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="g862"
|
||||
transform="translate(0,155)">
|
||||
</g>
|
||||
<g
|
||||
id="g864"
|
||||
transform="translate(0,155)">
|
||||
</g>
|
||||
<g
|
||||
id="g866"
|
||||
transform="translate(0,155)">
|
||||
</g>
|
||||
<g
|
||||
id="g868"
|
||||
transform="translate(0,155)">
|
||||
</g>
|
||||
<g
|
||||
id="g870"
|
||||
transform="translate(0,155)">
|
||||
</g>
|
||||
<g
|
||||
id="g872"
|
||||
transform="translate(0,155)">
|
||||
</g>
|
||||
<g
|
||||
id="g874"
|
||||
transform="translate(0,155)">
|
||||
</g>
|
||||
<g
|
||||
id="g876"
|
||||
transform="translate(0,155)">
|
||||
</g>
|
||||
<g
|
||||
id="g878"
|
||||
transform="translate(0,155)">
|
||||
</g>
|
||||
<g
|
||||
id="g880"
|
||||
transform="translate(0,155)">
|
||||
</g>
|
||||
<g
|
||||
id="g882"
|
||||
transform="translate(0,155)">
|
||||
</g>
|
||||
<g
|
||||
id="g884"
|
||||
transform="translate(0,155)">
|
||||
</g>
|
||||
<g
|
||||
id="g886"
|
||||
transform="translate(0,155)">
|
||||
</g>
|
||||
<g
|
||||
id="g888"
|
||||
transform="translate(0,155)">
|
||||
</g>
|
||||
<g
|
||||
id="g890"
|
||||
transform="translate(0,155)">
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
119
src/images/close-button.svg
Normal file
@@ -0,0 +1,119 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="Capa_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
width="512"
|
||||
height="512"
|
||||
viewBox="0 0 512 512"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="close-button.svg"
|
||||
inkscape:version="0.92.3 (2405546, 2018-03-11)"><metadata
|
||||
id="metadata897"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs895" /><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
id="namedview893"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:zoom="0.85862966"
|
||||
inkscape:cx="345.29142"
|
||||
inkscape:cy="32.731258"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Capa_1" />
|
||||
<g
|
||||
id="g860"
|
||||
transform="matrix(0.71708683,0,0,0.71708683,128,128)">
|
||||
<g
|
||||
id="close">
|
||||
<polygon
|
||||
points="357,321.3 214.2,178.5 357,35.7 321.3,0 178.5,142.8 35.7,0 0,35.7 142.8,178.5 0,321.3 35.7,357 178.5,214.2 321.3,357 "
|
||||
id="polygon857" />
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="g862"
|
||||
transform="translate(0,155)">
|
||||
</g>
|
||||
<g
|
||||
id="g864"
|
||||
transform="translate(0,155)">
|
||||
</g>
|
||||
<g
|
||||
id="g866"
|
||||
transform="translate(0,155)">
|
||||
</g>
|
||||
<g
|
||||
id="g868"
|
||||
transform="translate(0,155)">
|
||||
</g>
|
||||
<g
|
||||
id="g870"
|
||||
transform="translate(0,155)">
|
||||
</g>
|
||||
<g
|
||||
id="g872"
|
||||
transform="translate(0,155)">
|
||||
</g>
|
||||
<g
|
||||
id="g874"
|
||||
transform="translate(0,155)">
|
||||
</g>
|
||||
<g
|
||||
id="g876"
|
||||
transform="translate(0,155)">
|
||||
</g>
|
||||
<g
|
||||
id="g878"
|
||||
transform="translate(0,155)">
|
||||
</g>
|
||||
<g
|
||||
id="g880"
|
||||
transform="translate(0,155)">
|
||||
</g>
|
||||
<g
|
||||
id="g882"
|
||||
transform="translate(0,155)">
|
||||
</g>
|
||||
<g
|
||||
id="g884"
|
||||
transform="translate(0,155)">
|
||||
</g>
|
||||
<g
|
||||
id="g886"
|
||||
transform="translate(0,155)">
|
||||
</g>
|
||||
<g
|
||||
id="g888"
|
||||
transform="translate(0,155)">
|
||||
</g>
|
||||
<g
|
||||
id="g890"
|
||||
transform="translate(0,155)">
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |