Compare commits
235 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d30fc9c3c | ||
|
|
ca1d3fcd38 | ||
|
|
b82d23e59c | ||
|
|
6d8e396e92 | ||
|
|
e4a71982d9 | ||
|
|
3b8775fd86 | ||
|
|
d5ffd8f6a7 | ||
|
|
296c7edbd0 | ||
|
|
4600af712b | ||
|
|
21d8a3dcdb | ||
|
|
0c9773ab54 | ||
|
|
65a645b2cc | ||
|
|
a61d5bd8c0 | ||
|
|
22f609cfa6 | ||
|
|
225b1ff2bb | ||
|
|
00e4dc14ba | ||
|
|
8ef696f6f2 | ||
|
|
b6d7f868ac | ||
|
|
537828ef3d | ||
|
|
6444e7424f | ||
|
|
3cd6d766f8 | ||
|
|
82c98a3f91 | ||
|
|
c11a496278 | ||
|
|
d27783e2f1 | ||
|
|
efd88565a9 | ||
|
|
f3bb1b17d0 | ||
|
|
518cee9d0a | ||
|
|
6302ab03d8 | ||
|
|
2afca346b0 | ||
|
|
89ff0ad311 | ||
|
|
6caeac2cb4 | ||
|
|
a0f0640c9f | ||
|
|
d3f5879119 | ||
|
|
df74686287 | ||
|
|
75d58b3ea3 | ||
|
|
bedd25021c | ||
|
|
0e566cb8a3 | ||
|
|
23f80334f6 | ||
|
|
7063909cee | ||
|
|
a284e3bd65 | ||
|
|
823887daf8 | ||
|
|
78a4166e42 | ||
|
|
3d3b694040 | ||
|
|
ad06241c9d | ||
|
|
6a1ea6c7de | ||
|
|
931719c474 | ||
|
|
70ef00932a | ||
|
|
a1bc4a063b | ||
|
|
abf7224582 | ||
|
|
7ab4c9af2e | ||
|
|
d5fefaa35f | ||
|
|
3c4af9c6af | ||
|
|
12ec819aeb | ||
|
|
e6fc1c14bd | ||
|
|
6fb45c055c | ||
|
|
535b926095 | ||
|
|
b15bc26a63 | ||
|
|
1d90e8e823 | ||
|
|
6b3027401d | ||
|
|
e35bd65a91 | ||
|
|
8888f6800b | ||
|
|
2afe62ec77 | ||
|
|
4b27af959b | ||
|
|
0df1a41a1d | ||
|
|
21badd592e | ||
|
|
a110d53a53 | ||
|
|
0270993782 | ||
|
|
2c0d899b2a | ||
|
|
1abe101ef7 | ||
|
|
0ebc170cfa | ||
|
|
ee6ddfadc3 | ||
|
|
720b5f0c72 | ||
|
|
aceabd8455 | ||
|
|
ab4869a0e1 | ||
|
|
adf5793ccd | ||
|
|
31e26c2c1c | ||
|
|
de05ddd203 | ||
|
|
03a8eaa44f | ||
|
|
48e79f12a7 | ||
|
|
89aa3d5251 | ||
|
|
87b0596ebc | ||
|
|
a08857804a | ||
|
|
dbca6d79cf | ||
|
|
a24221c002 | ||
|
|
5f89dd0465 | ||
|
|
0b82ff30fe | ||
|
|
130b0de646 | ||
|
|
b5b251dffb | ||
|
|
0c44accb44 | ||
|
|
bbdf0ef29d | ||
|
|
401e8cf492 | ||
|
|
83cdc69fc8 | ||
|
|
1e3af06bb0 | ||
|
|
2c8abee668 | ||
|
|
5af7492b67 | ||
|
|
8d1465a81f | ||
|
|
5ead4684f5 | ||
|
|
fc91502162 | ||
|
|
3a99bdcfb4 | ||
|
|
487e23e190 | ||
|
|
511132ee4f | ||
|
|
edc89555bc | ||
|
|
ad30211dae | ||
|
|
48406da6ea | ||
|
|
aff0bd6e25 | ||
|
|
3969d28d92 | ||
|
|
d0c100995e | ||
|
|
f54e4c8ac2 | ||
|
|
b39cd2d81b | ||
|
|
0e8e563654 | ||
|
|
2f041a0eed | ||
|
|
2c7b5982b5 | ||
|
|
ffa0105d3e | ||
|
|
bac0698581 | ||
|
|
be3180df6f | ||
|
|
6179832a2b | ||
|
|
13853573ea | ||
|
|
b6b4c626e8 | ||
|
|
bd41ec1627 | ||
|
|
b54dab7df2 | ||
|
|
e66ef604a7 | ||
|
|
e03674fd4b | ||
|
|
3f69fedd1f | ||
|
|
a614e3cc3d | ||
|
|
ebde50b492 | ||
|
|
2a6bd306cb | ||
|
|
8a27a5596b | ||
|
|
f835ffd978 | ||
|
|
97215705f5 | ||
|
|
6ee97e64d7 | ||
|
|
a1c4812619 | ||
|
|
b0c8edbd82 | ||
|
|
a4190ecbf0 | ||
|
|
d8c6efaada | ||
|
|
0312682e07 | ||
|
|
8d14068df7 | ||
|
|
fe1d9a493f | ||
|
|
e55ad49db8 | ||
|
|
018ce2001e | ||
|
|
c8fe4c46dd | ||
|
|
1781fa671d | ||
|
|
75910e910e | ||
|
|
899e06be1c | ||
|
|
66687dc8b6 | ||
|
|
b8fe620276 | ||
|
|
1a50ea9892 | ||
|
|
0a096869fe | ||
|
|
44dc76bd19 | ||
|
|
aedbaec497 | ||
|
|
04aa622111 | ||
|
|
3564229482 | ||
|
|
637db7f4f9 | ||
|
|
f6d3d6d34a | ||
|
|
92369bdb26 | ||
|
|
8f95447108 | ||
|
|
0e3c3bab45 | ||
|
|
3a5c965306 | ||
|
|
0c88457037 | ||
|
|
46fa22dc6a | ||
|
|
f3d32399e5 | ||
|
|
b320bb17d1 | ||
|
|
81afe2d3cb | ||
|
|
5fad43377b | ||
|
|
f543318232 | ||
|
|
ab385a782a | ||
|
|
c370875128 | ||
|
|
1c261515db | ||
|
|
37cbae84ca | ||
|
|
f5759716b4 | ||
|
|
f645fe725a | ||
|
|
fdedd7d92a | ||
|
|
044a43d793 | ||
|
|
6846c96146 | ||
|
|
8fe9461872 | ||
|
|
fbde4edcd2 | ||
|
|
68742681f7 | ||
|
|
bbb3f99bc3 | ||
|
|
e0f6f3013f | ||
|
|
6eb497fb64 | ||
|
|
ae15757765 | ||
|
|
be294b4867 | ||
|
|
70738f7549 | ||
|
|
42dc529ce1 | ||
|
|
65058d3a48 | ||
|
|
48c4106b7f | ||
|
|
175b48569f | ||
|
|
89c6abb5ce | ||
|
|
55f23799bc | ||
|
|
646211cc4c | ||
|
|
423bab9954 | ||
|
|
831d90ebf5 | ||
|
|
abdc0dc0dd | ||
|
|
175c836c93 | ||
|
|
76304172ab | ||
|
|
0eb3978aee | ||
|
|
73f42d55ca | ||
|
|
059a055483 | ||
|
|
dcf1ee393e | ||
|
|
04aecb3693 | ||
|
|
533d174abc | ||
|
|
48fb999bd0 | ||
|
|
5443e5f998 | ||
|
|
543d226ba3 | ||
|
|
03b1848b43 | ||
|
|
05ab8d2067 | ||
|
|
c28a27c81c | ||
|
|
1b42048135 | ||
|
|
bfe6b9bd26 | ||
|
|
08a8cee1c6 | ||
|
|
835a532e75 | ||
|
|
d383ade03c | ||
|
|
6d9c4cee02 | ||
|
|
a565239c4a | ||
|
|
ba69f3e6b9 | ||
|
|
14c29f695c | ||
|
|
703a9b3e12 | ||
|
|
0eca1b0433 | ||
|
|
691c9683ce | ||
|
|
1a11e5ddcd | ||
|
|
a4d281dbb6 | ||
|
|
8361f90dce | ||
|
|
04b4ff8b4b | ||
|
|
121248c3c5 | ||
|
|
c44d0c87e3 | ||
|
|
c6cf9487ba | ||
|
|
c78cc17730 | ||
|
|
81a0234b05 | ||
|
|
f72a8568c5 | ||
|
|
11aec65967 | ||
|
|
40636d1e05 | ||
|
|
75841415a3 | ||
|
|
edc799bc54 | ||
|
|
1d0b20337f | ||
|
|
4a6d2d3514 | ||
|
|
8443414ae3 |
3
.github/workflows/linux-builds.yml
vendored
@@ -6,7 +6,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, ubuntu-18.04, ubuntu-16.04]
|
os: [ubuntu-latest, ubuntu-20.04, ubuntu-18.04]
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
@@ -16,6 +16,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
sudo apt-get update --fix-missing
|
sudo apt-get update --fix-missing
|
||||||
sudo apt-get install qt5-default
|
sudo apt-get install qt5-default
|
||||||
|
sudo apt-get install qtbase5-private-dev
|
||||||
- name: qmake
|
- name: qmake
|
||||||
run: qmake
|
run: qmake
|
||||||
- name: make
|
- name: make
|
||||||
|
|||||||
2
.gitignore
vendored
@@ -382,3 +382,5 @@ MigrationBackup/
|
|||||||
FodyWeavers.xsd
|
FodyWeavers.xsd
|
||||||
/ build
|
/ build
|
||||||
/Settings.ini
|
/Settings.ini
|
||||||
|
.vscode/settings.json
|
||||||
|
/.settings
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@@ -65,7 +65,7 @@ matrix:
|
|||||||
services:
|
services:
|
||||||
- xvfb
|
- xvfb
|
||||||
compiler: gcc
|
compiler: gcc
|
||||||
addons:
|
addons:
|
||||||
apt:
|
apt:
|
||||||
sources:
|
sources:
|
||||||
- ubuntu-toolchain-r-test
|
- ubuntu-toolchain-r-test
|
||||||
|
|||||||
@@ -1,20 +1,41 @@
|
|||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.5)
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH})
|
|
||||||
include(GetGitRevisionDescription)
|
# By default, the version information is extracted from the git index. However,
|
||||||
git_describe(GitTagVersion --tags)
|
# we can override this behavior by explicitly setting ADS_VERSION and
|
||||||
string(REGEX REPLACE "^([0-9]+)\\..*" "\\1" VERSION_MAJOR "${GitTagVersion}")
|
# skipping the git checks. This is useful for cases where this project is being
|
||||||
string(REGEX REPLACE "^[0-9]+\\.([0-9]+).*" "\\1" VERSION_MINOR "${GitTagVersion}")
|
# used independently of its original git repo (e.g. vendored in another project)
|
||||||
string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" VERSION_PATCH "${GitTagVersion}")
|
if(NOT ADS_VERSION)
|
||||||
set(VERSION_SHORT "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
|
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH})
|
||||||
|
include(GetGitRevisionDescription)
|
||||||
|
git_describe(GitTagVersion --tags)
|
||||||
|
string(REGEX REPLACE "^([0-9]+)\\..*" "\\1" VERSION_MAJOR "${GitTagVersion}")
|
||||||
|
string(REGEX REPLACE "^[0-9]+\\.([0-9]+).*" "\\1" VERSION_MINOR "${GitTagVersion}")
|
||||||
|
string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" VERSION_PATCH "${GitTagVersion}")
|
||||||
|
set(VERSION_SHORT "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
|
||||||
|
else()
|
||||||
|
string(REGEX MATCHALL "[\.]" VERSION_DOT_MATCHES ${ADS_VERSION})
|
||||||
|
list(LENGTH VERSION_DOT_MATCHES VERSION_DOT_COUNT)
|
||||||
|
if(VERSION_DOT_COUNT EQUAL 2)
|
||||||
|
set(VERSION_SHORT ${ADS_VERSION})
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "ADS_VERSION must be in major.minor.patch format, e.g. 3.8.1. Got ${ADS_VERSION}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
project(QtADS LANGUAGES CXX VERSION ${VERSION_SHORT})
|
project(QtADS LANGUAGES CXX VERSION ${VERSION_SHORT})
|
||||||
|
|
||||||
option(BUILD_STATIC "Build the static library" OFF)
|
option(BUILD_STATIC "Build the static library" OFF)
|
||||||
option(BUILD_EXAMPLES "Build the examples" ON)
|
option(BUILD_EXAMPLES "Build the examples" ON)
|
||||||
|
|
||||||
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4")
|
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4")
|
||||||
set(ads_PlatformDir "x86")
|
set(ads_PlatformDir "x86")
|
||||||
else()
|
else()
|
||||||
set(ads_PlatformDir "x64")
|
set(ads_PlatformDir "x64")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
|
|
||||||
if(BUILD_EXAMPLES)
|
if(BUILD_EXAMPLES)
|
||||||
add_subdirectory(examples)
|
add_subdirectory(examples)
|
||||||
add_subdirectory(demo)
|
add_subdirectory(demo)
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
from .ads import ads
|
|
||||||
from .._version import *
|
|
||||||
|
|
||||||
import inspect
|
|
||||||
|
|
||||||
for name, member in inspect.getmembers(ads):
|
|
||||||
if not name.startswith('_'):
|
|
||||||
globals()[name] = member
|
|
||||||
|
|
||||||
del ads
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
from .rc import *
|
|
||||||
from ._version import get_versions
|
|
||||||
__version__ = get_versions()['version']
|
|
||||||
short_version = __version__
|
|
||||||
version = __version__
|
|
||||||
full_version = __version__
|
|
||||||
git_revision = get_versions()['full-revisionid']
|
|
||||||
release = not get_versions()['dirty']
|
|
||||||
del get_versions
|
|
||||||
@@ -1,520 +0,0 @@
|
|||||||
|
|
||||||
# This file helps to compute a version number in source trees obtained from
|
|
||||||
# git-archive tarball (such as those provided by githubs download-from-tag
|
|
||||||
# feature). Distribution tarballs (built by setup.py sdist) and build
|
|
||||||
# directories (produced by setup.py build) will contain a much shorter file
|
|
||||||
# that just contains the computed version number.
|
|
||||||
|
|
||||||
# This file is released into the public domain. Generated by
|
|
||||||
# versioneer-0.18 (https://github.com/warner/python-versioneer)
|
|
||||||
|
|
||||||
"""Git implementation of _version.py."""
|
|
||||||
|
|
||||||
import errno
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
|
|
||||||
|
|
||||||
def get_keywords():
|
|
||||||
"""Get the keywords needed to look up the version information."""
|
|
||||||
# these strings will be replaced by git during git-archive.
|
|
||||||
# setup.py/versioneer.py will grep for the variable names, so they must
|
|
||||||
# each be defined on a line of their own. _version.py will just call
|
|
||||||
# get_keywords().
|
|
||||||
git_refnames = "$Format:%d$"
|
|
||||||
git_full = "$Format:%H$"
|
|
||||||
git_date = "$Format:%ci$"
|
|
||||||
keywords = {"refnames": git_refnames, "full": git_full, "date": git_date}
|
|
||||||
return keywords
|
|
||||||
|
|
||||||
|
|
||||||
class VersioneerConfig:
|
|
||||||
"""Container for Versioneer configuration parameters."""
|
|
||||||
|
|
||||||
|
|
||||||
def get_config():
|
|
||||||
"""Create, populate and return the VersioneerConfig() object."""
|
|
||||||
# these strings are filled in when 'setup.py versioneer' creates
|
|
||||||
# _version.py
|
|
||||||
cfg = VersioneerConfig()
|
|
||||||
cfg.VCS = "git"
|
|
||||||
cfg.style = "pep440"
|
|
||||||
cfg.tag_prefix = ""
|
|
||||||
cfg.parentdir_prefix = "None"
|
|
||||||
cfg.versionfile_source = "PyQtAds/_version.py"
|
|
||||||
cfg.verbose = False
|
|
||||||
return cfg
|
|
||||||
|
|
||||||
|
|
||||||
class NotThisMethod(Exception):
|
|
||||||
"""Exception raised if a method is not valid for the current scenario."""
|
|
||||||
|
|
||||||
|
|
||||||
LONG_VERSION_PY = {}
|
|
||||||
HANDLERS = {}
|
|
||||||
|
|
||||||
|
|
||||||
def register_vcs_handler(vcs, method): # decorator
|
|
||||||
"""Create decorator to mark a method as the handler of a VCS."""
|
|
||||||
def decorate(f):
|
|
||||||
"""Store f in HANDLERS[vcs][method]."""
|
|
||||||
if vcs not in HANDLERS:
|
|
||||||
HANDLERS[vcs] = {}
|
|
||||||
HANDLERS[vcs][method] = f
|
|
||||||
return f
|
|
||||||
return decorate
|
|
||||||
|
|
||||||
|
|
||||||
def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False,
|
|
||||||
env=None):
|
|
||||||
"""Call the given command(s)."""
|
|
||||||
assert isinstance(commands, list)
|
|
||||||
p = None
|
|
||||||
for c in commands:
|
|
||||||
try:
|
|
||||||
dispcmd = str([c] + args)
|
|
||||||
# remember shell=False, so use git.cmd on windows, not just git
|
|
||||||
p = subprocess.Popen([c] + args, cwd=cwd, env=env,
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stderr=(subprocess.PIPE if hide_stderr
|
|
||||||
else None))
|
|
||||||
break
|
|
||||||
except EnvironmentError:
|
|
||||||
e = sys.exc_info()[1]
|
|
||||||
if e.errno == errno.ENOENT:
|
|
||||||
continue
|
|
||||||
if verbose:
|
|
||||||
print("unable to run %s" % dispcmd)
|
|
||||||
print(e)
|
|
||||||
return None, None
|
|
||||||
else:
|
|
||||||
if verbose:
|
|
||||||
print("unable to find command, tried %s" % (commands,))
|
|
||||||
return None, None
|
|
||||||
stdout = p.communicate()[0].strip()
|
|
||||||
if sys.version_info[0] >= 3:
|
|
||||||
stdout = stdout.decode()
|
|
||||||
if p.returncode != 0:
|
|
||||||
if verbose:
|
|
||||||
print("unable to run %s (error)" % dispcmd)
|
|
||||||
print("stdout was %s" % stdout)
|
|
||||||
return None, p.returncode
|
|
||||||
return stdout, p.returncode
|
|
||||||
|
|
||||||
|
|
||||||
def versions_from_parentdir(parentdir_prefix, root, verbose):
|
|
||||||
"""Try to determine the version from the parent directory name.
|
|
||||||
|
|
||||||
Source tarballs conventionally unpack into a directory that includes both
|
|
||||||
the project name and a version string. We will also support searching up
|
|
||||||
two directory levels for an appropriately named parent directory
|
|
||||||
"""
|
|
||||||
rootdirs = []
|
|
||||||
|
|
||||||
for i in range(3):
|
|
||||||
dirname = os.path.basename(root)
|
|
||||||
if dirname.startswith(parentdir_prefix):
|
|
||||||
return {"version": dirname[len(parentdir_prefix):],
|
|
||||||
"full-revisionid": None,
|
|
||||||
"dirty": False, "error": None, "date": None}
|
|
||||||
else:
|
|
||||||
rootdirs.append(root)
|
|
||||||
root = os.path.dirname(root) # up a level
|
|
||||||
|
|
||||||
if verbose:
|
|
||||||
print("Tried directories %s but none started with prefix %s" %
|
|
||||||
(str(rootdirs), parentdir_prefix))
|
|
||||||
raise NotThisMethod("rootdir doesn't start with parentdir_prefix")
|
|
||||||
|
|
||||||
|
|
||||||
@register_vcs_handler("git", "get_keywords")
|
|
||||||
def git_get_keywords(versionfile_abs):
|
|
||||||
"""Extract version information from the given file."""
|
|
||||||
# the code embedded in _version.py can just fetch the value of these
|
|
||||||
# keywords. When used from setup.py, we don't want to import _version.py,
|
|
||||||
# so we do it with a regexp instead. This function is not used from
|
|
||||||
# _version.py.
|
|
||||||
keywords = {}
|
|
||||||
try:
|
|
||||||
f = open(versionfile_abs, "r")
|
|
||||||
for line in f.readlines():
|
|
||||||
if line.strip().startswith("git_refnames ="):
|
|
||||||
mo = re.search(r'=\s*"(.*)"', line)
|
|
||||||
if mo:
|
|
||||||
keywords["refnames"] = mo.group(1)
|
|
||||||
if line.strip().startswith("git_full ="):
|
|
||||||
mo = re.search(r'=\s*"(.*)"', line)
|
|
||||||
if mo:
|
|
||||||
keywords["full"] = mo.group(1)
|
|
||||||
if line.strip().startswith("git_date ="):
|
|
||||||
mo = re.search(r'=\s*"(.*)"', line)
|
|
||||||
if mo:
|
|
||||||
keywords["date"] = mo.group(1)
|
|
||||||
f.close()
|
|
||||||
except EnvironmentError:
|
|
||||||
pass
|
|
||||||
return keywords
|
|
||||||
|
|
||||||
|
|
||||||
@register_vcs_handler("git", "keywords")
|
|
||||||
def git_versions_from_keywords(keywords, tag_prefix, verbose):
|
|
||||||
"""Get version information from git keywords."""
|
|
||||||
if not keywords:
|
|
||||||
raise NotThisMethod("no keywords at all, weird")
|
|
||||||
date = keywords.get("date")
|
|
||||||
if date is not None:
|
|
||||||
# git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant
|
|
||||||
# datestamp. However we prefer "%ci" (which expands to an "ISO-8601
|
|
||||||
# -like" string, which we must then edit to make compliant), because
|
|
||||||
# it's been around since git-1.5.3, and it's too difficult to
|
|
||||||
# discover which version we're using, or to work around using an
|
|
||||||
# older one.
|
|
||||||
date = date.strip().replace(" ", "T", 1).replace(" ", "", 1)
|
|
||||||
refnames = keywords["refnames"].strip()
|
|
||||||
if refnames.startswith("$Format"):
|
|
||||||
if verbose:
|
|
||||||
print("keywords are unexpanded, not using")
|
|
||||||
raise NotThisMethod("unexpanded keywords, not a git-archive tarball")
|
|
||||||
refs = set([r.strip() for r in refnames.strip("()").split(",")])
|
|
||||||
# starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of
|
|
||||||
# just "foo-1.0". If we see a "tag: " prefix, prefer those.
|
|
||||||
TAG = "tag: "
|
|
||||||
tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)])
|
|
||||||
if not tags:
|
|
||||||
# Either we're using git < 1.8.3, or there really are no tags. We use
|
|
||||||
# a heuristic: assume all version tags have a digit. The old git %d
|
|
||||||
# expansion behaves like git log --decorate=short and strips out the
|
|
||||||
# refs/heads/ and refs/tags/ prefixes that would let us distinguish
|
|
||||||
# between branches and tags. By ignoring refnames without digits, we
|
|
||||||
# filter out many common branch names like "release" and
|
|
||||||
# "stabilization", as well as "HEAD" and "master".
|
|
||||||
tags = set([r for r in refs if re.search(r'\d', r)])
|
|
||||||
if verbose:
|
|
||||||
print("discarding '%s', no digits" % ",".join(refs - tags))
|
|
||||||
if verbose:
|
|
||||||
print("likely tags: %s" % ",".join(sorted(tags)))
|
|
||||||
for ref in sorted(tags):
|
|
||||||
# sorting will prefer e.g. "2.0" over "2.0rc1"
|
|
||||||
if ref.startswith(tag_prefix):
|
|
||||||
r = ref[len(tag_prefix):]
|
|
||||||
if verbose:
|
|
||||||
print("picking %s" % r)
|
|
||||||
return {"version": r,
|
|
||||||
"full-revisionid": keywords["full"].strip(),
|
|
||||||
"dirty": False, "error": None,
|
|
||||||
"date": date}
|
|
||||||
# no suitable tags, so version is "0+unknown", but full hex is still there
|
|
||||||
if verbose:
|
|
||||||
print("no suitable tags, using unknown + full revision id")
|
|
||||||
return {"version": "0+unknown",
|
|
||||||
"full-revisionid": keywords["full"].strip(),
|
|
||||||
"dirty": False, "error": "no suitable tags", "date": None}
|
|
||||||
|
|
||||||
|
|
||||||
@register_vcs_handler("git", "pieces_from_vcs")
|
|
||||||
def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
|
|
||||||
"""Get version from 'git describe' in the root of the source tree.
|
|
||||||
|
|
||||||
This only gets called if the git-archive 'subst' keywords were *not*
|
|
||||||
expanded, and _version.py hasn't already been rewritten with a short
|
|
||||||
version string, meaning we're inside a checked out source tree.
|
|
||||||
"""
|
|
||||||
GITS = ["git"]
|
|
||||||
if sys.platform == "win32":
|
|
||||||
GITS = ["git.cmd", "git.exe"]
|
|
||||||
|
|
||||||
out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root,
|
|
||||||
hide_stderr=True)
|
|
||||||
if rc != 0:
|
|
||||||
if verbose:
|
|
||||||
print("Directory %s not under git control" % root)
|
|
||||||
raise NotThisMethod("'git rev-parse --git-dir' returned error")
|
|
||||||
|
|
||||||
# if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty]
|
|
||||||
# if there isn't one, this yields HEX[-dirty] (no NUM)
|
|
||||||
describe_out, rc = run_command(GITS, ["describe", "--tags", "--dirty",
|
|
||||||
"--always", "--long",
|
|
||||||
"--match", "%s*" % tag_prefix],
|
|
||||||
cwd=root)
|
|
||||||
# --long was added in git-1.5.5
|
|
||||||
if describe_out is None:
|
|
||||||
raise NotThisMethod("'git describe' failed")
|
|
||||||
describe_out = describe_out.strip()
|
|
||||||
full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root)
|
|
||||||
if full_out is None:
|
|
||||||
raise NotThisMethod("'git rev-parse' failed")
|
|
||||||
full_out = full_out.strip()
|
|
||||||
|
|
||||||
pieces = {}
|
|
||||||
pieces["long"] = full_out
|
|
||||||
pieces["short"] = full_out[:7] # maybe improved later
|
|
||||||
pieces["error"] = None
|
|
||||||
|
|
||||||
# parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty]
|
|
||||||
# TAG might have hyphens.
|
|
||||||
git_describe = describe_out
|
|
||||||
|
|
||||||
# look for -dirty suffix
|
|
||||||
dirty = git_describe.endswith("-dirty")
|
|
||||||
pieces["dirty"] = dirty
|
|
||||||
if dirty:
|
|
||||||
git_describe = git_describe[:git_describe.rindex("-dirty")]
|
|
||||||
|
|
||||||
# now we have TAG-NUM-gHEX or HEX
|
|
||||||
|
|
||||||
if "-" in git_describe:
|
|
||||||
# TAG-NUM-gHEX
|
|
||||||
mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe)
|
|
||||||
if not mo:
|
|
||||||
# unparseable. Maybe git-describe is misbehaving?
|
|
||||||
pieces["error"] = ("unable to parse git-describe output: '%s'"
|
|
||||||
% describe_out)
|
|
||||||
return pieces
|
|
||||||
|
|
||||||
# tag
|
|
||||||
full_tag = mo.group(1)
|
|
||||||
if not full_tag.startswith(tag_prefix):
|
|
||||||
if verbose:
|
|
||||||
fmt = "tag '%s' doesn't start with prefix '%s'"
|
|
||||||
print(fmt % (full_tag, tag_prefix))
|
|
||||||
pieces["error"] = ("tag '%s' doesn't start with prefix '%s'"
|
|
||||||
% (full_tag, tag_prefix))
|
|
||||||
return pieces
|
|
||||||
pieces["closest-tag"] = full_tag[len(tag_prefix):]
|
|
||||||
|
|
||||||
# distance: number of commits since tag
|
|
||||||
pieces["distance"] = int(mo.group(2))
|
|
||||||
|
|
||||||
# commit: short hex revision ID
|
|
||||||
pieces["short"] = mo.group(3)
|
|
||||||
|
|
||||||
else:
|
|
||||||
# HEX: no tags
|
|
||||||
pieces["closest-tag"] = None
|
|
||||||
count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"],
|
|
||||||
cwd=root)
|
|
||||||
pieces["distance"] = int(count_out) # total number of commits
|
|
||||||
|
|
||||||
# commit date: see ISO-8601 comment in git_versions_from_keywords()
|
|
||||||
date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"],
|
|
||||||
cwd=root)[0].strip()
|
|
||||||
pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1)
|
|
||||||
|
|
||||||
return pieces
|
|
||||||
|
|
||||||
|
|
||||||
def plus_or_dot(pieces):
|
|
||||||
"""Return a + if we don't already have one, else return a ."""
|
|
||||||
if "+" in pieces.get("closest-tag", ""):
|
|
||||||
return "."
|
|
||||||
return "+"
|
|
||||||
|
|
||||||
|
|
||||||
def render_pep440(pieces):
|
|
||||||
"""Build up version string, with post-release "local version identifier".
|
|
||||||
|
|
||||||
Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you
|
|
||||||
get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty
|
|
||||||
|
|
||||||
Exceptions:
|
|
||||||
1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty]
|
|
||||||
"""
|
|
||||||
if pieces["closest-tag"]:
|
|
||||||
rendered = pieces["closest-tag"]
|
|
||||||
if pieces["distance"] or pieces["dirty"]:
|
|
||||||
rendered += plus_or_dot(pieces)
|
|
||||||
rendered += "%d.g%s" % (pieces["distance"], pieces["short"])
|
|
||||||
if pieces["dirty"]:
|
|
||||||
rendered += ".dirty"
|
|
||||||
else:
|
|
||||||
# exception #1
|
|
||||||
rendered = "0+untagged.%d.g%s" % (pieces["distance"],
|
|
||||||
pieces["short"])
|
|
||||||
if pieces["dirty"]:
|
|
||||||
rendered += ".dirty"
|
|
||||||
return rendered
|
|
||||||
|
|
||||||
|
|
||||||
def render_pep440_pre(pieces):
|
|
||||||
"""TAG[.post.devDISTANCE] -- No -dirty.
|
|
||||||
|
|
||||||
Exceptions:
|
|
||||||
1: no tags. 0.post.devDISTANCE
|
|
||||||
"""
|
|
||||||
if pieces["closest-tag"]:
|
|
||||||
rendered = pieces["closest-tag"]
|
|
||||||
if pieces["distance"]:
|
|
||||||
rendered += ".post.dev%d" % pieces["distance"]
|
|
||||||
else:
|
|
||||||
# exception #1
|
|
||||||
rendered = "0.post.dev%d" % pieces["distance"]
|
|
||||||
return rendered
|
|
||||||
|
|
||||||
|
|
||||||
def render_pep440_post(pieces):
|
|
||||||
"""TAG[.postDISTANCE[.dev0]+gHEX] .
|
|
||||||
|
|
||||||
The ".dev0" means dirty. Note that .dev0 sorts backwards
|
|
||||||
(a dirty tree will appear "older" than the corresponding clean one),
|
|
||||||
but you shouldn't be releasing software with -dirty anyways.
|
|
||||||
|
|
||||||
Exceptions:
|
|
||||||
1: no tags. 0.postDISTANCE[.dev0]
|
|
||||||
"""
|
|
||||||
if pieces["closest-tag"]:
|
|
||||||
rendered = pieces["closest-tag"]
|
|
||||||
if pieces["distance"] or pieces["dirty"]:
|
|
||||||
rendered += ".post%d" % pieces["distance"]
|
|
||||||
if pieces["dirty"]:
|
|
||||||
rendered += ".dev0"
|
|
||||||
rendered += plus_or_dot(pieces)
|
|
||||||
rendered += "g%s" % pieces["short"]
|
|
||||||
else:
|
|
||||||
# exception #1
|
|
||||||
rendered = "0.post%d" % pieces["distance"]
|
|
||||||
if pieces["dirty"]:
|
|
||||||
rendered += ".dev0"
|
|
||||||
rendered += "+g%s" % pieces["short"]
|
|
||||||
return rendered
|
|
||||||
|
|
||||||
|
|
||||||
def render_pep440_old(pieces):
|
|
||||||
"""TAG[.postDISTANCE[.dev0]] .
|
|
||||||
|
|
||||||
The ".dev0" means dirty.
|
|
||||||
|
|
||||||
Exceptions:
|
|
||||||
1: no tags. 0.postDISTANCE[.dev0]
|
|
||||||
"""
|
|
||||||
if pieces["closest-tag"]:
|
|
||||||
rendered = pieces["closest-tag"]
|
|
||||||
if pieces["distance"] or pieces["dirty"]:
|
|
||||||
rendered += ".post%d" % pieces["distance"]
|
|
||||||
if pieces["dirty"]:
|
|
||||||
rendered += ".dev0"
|
|
||||||
else:
|
|
||||||
# exception #1
|
|
||||||
rendered = "0.post%d" % pieces["distance"]
|
|
||||||
if pieces["dirty"]:
|
|
||||||
rendered += ".dev0"
|
|
||||||
return rendered
|
|
||||||
|
|
||||||
|
|
||||||
def render_git_describe(pieces):
|
|
||||||
"""TAG[-DISTANCE-gHEX][-dirty].
|
|
||||||
|
|
||||||
Like 'git describe --tags --dirty --always'.
|
|
||||||
|
|
||||||
Exceptions:
|
|
||||||
1: no tags. HEX[-dirty] (note: no 'g' prefix)
|
|
||||||
"""
|
|
||||||
if pieces["closest-tag"]:
|
|
||||||
rendered = pieces["closest-tag"]
|
|
||||||
if pieces["distance"]:
|
|
||||||
rendered += "-%d-g%s" % (pieces["distance"], pieces["short"])
|
|
||||||
else:
|
|
||||||
# exception #1
|
|
||||||
rendered = pieces["short"]
|
|
||||||
if pieces["dirty"]:
|
|
||||||
rendered += "-dirty"
|
|
||||||
return rendered
|
|
||||||
|
|
||||||
|
|
||||||
def render_git_describe_long(pieces):
|
|
||||||
"""TAG-DISTANCE-gHEX[-dirty].
|
|
||||||
|
|
||||||
Like 'git describe --tags --dirty --always -long'.
|
|
||||||
The distance/hash is unconditional.
|
|
||||||
|
|
||||||
Exceptions:
|
|
||||||
1: no tags. HEX[-dirty] (note: no 'g' prefix)
|
|
||||||
"""
|
|
||||||
if pieces["closest-tag"]:
|
|
||||||
rendered = pieces["closest-tag"]
|
|
||||||
rendered += "-%d-g%s" % (pieces["distance"], pieces["short"])
|
|
||||||
else:
|
|
||||||
# exception #1
|
|
||||||
rendered = pieces["short"]
|
|
||||||
if pieces["dirty"]:
|
|
||||||
rendered += "-dirty"
|
|
||||||
return rendered
|
|
||||||
|
|
||||||
|
|
||||||
def render(pieces, style):
|
|
||||||
"""Render the given version pieces into the requested style."""
|
|
||||||
if pieces["error"]:
|
|
||||||
return {"version": "unknown",
|
|
||||||
"full-revisionid": pieces.get("long"),
|
|
||||||
"dirty": None,
|
|
||||||
"error": pieces["error"],
|
|
||||||
"date": None}
|
|
||||||
|
|
||||||
if not style or style == "default":
|
|
||||||
style = "pep440" # the default
|
|
||||||
|
|
||||||
if style == "pep440":
|
|
||||||
rendered = render_pep440(pieces)
|
|
||||||
elif style == "pep440-pre":
|
|
||||||
rendered = render_pep440_pre(pieces)
|
|
||||||
elif style == "pep440-post":
|
|
||||||
rendered = render_pep440_post(pieces)
|
|
||||||
elif style == "pep440-old":
|
|
||||||
rendered = render_pep440_old(pieces)
|
|
||||||
elif style == "git-describe":
|
|
||||||
rendered = render_git_describe(pieces)
|
|
||||||
elif style == "git-describe-long":
|
|
||||||
rendered = render_git_describe_long(pieces)
|
|
||||||
else:
|
|
||||||
raise ValueError("unknown style '%s'" % style)
|
|
||||||
|
|
||||||
return {"version": rendered, "full-revisionid": pieces["long"],
|
|
||||||
"dirty": pieces["dirty"], "error": None,
|
|
||||||
"date": pieces.get("date")}
|
|
||||||
|
|
||||||
|
|
||||||
def get_versions():
|
|
||||||
"""Get version information or return default if unable to do so."""
|
|
||||||
# I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have
|
|
||||||
# __file__, we can work backwards from there to the root. Some
|
|
||||||
# py2exe/bbfreeze/non-CPython implementations don't do __file__, in which
|
|
||||||
# case we can only use expanded keywords.
|
|
||||||
|
|
||||||
cfg = get_config()
|
|
||||||
verbose = cfg.verbose
|
|
||||||
|
|
||||||
try:
|
|
||||||
return git_versions_from_keywords(get_keywords(), cfg.tag_prefix,
|
|
||||||
verbose)
|
|
||||||
except NotThisMethod:
|
|
||||||
pass
|
|
||||||
|
|
||||||
try:
|
|
||||||
root = os.path.realpath(__file__)
|
|
||||||
# versionfile_source is the relative path from the top of the source
|
|
||||||
# tree (where the .git directory might live) to this file. Invert
|
|
||||||
# this to find the root from __file__.
|
|
||||||
for i in cfg.versionfile_source.split('/'):
|
|
||||||
root = os.path.dirname(root)
|
|
||||||
except NameError:
|
|
||||||
return {"version": "0+unknown", "full-revisionid": None,
|
|
||||||
"dirty": None,
|
|
||||||
"error": "unable to find root of source tree",
|
|
||||||
"date": None}
|
|
||||||
|
|
||||||
try:
|
|
||||||
pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose)
|
|
||||||
return render(pieces, cfg.style)
|
|
||||||
except NotThisMethod:
|
|
||||||
pass
|
|
||||||
|
|
||||||
try:
|
|
||||||
if cfg.parentdir_prefix:
|
|
||||||
return versions_from_parentdir(cfg.parentdir_prefix, root, verbose)
|
|
||||||
except NotThisMethod:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return {"version": "0+unknown", "full-revisionid": None,
|
|
||||||
"dirty": None,
|
|
||||||
"error": "unable to compute version", "date": None}
|
|
||||||
317
README.md
@@ -1,21 +1,54 @@
|
|||||||
# Advanced Docking System for Qt
|

|
||||||
|
|
||||||
[](https://travis-ci.org/githubuser0xFFFF/Qt-Advanced-Docking-System)
|

|
||||||
|
|
||||||
|
------------------
|
||||||
|
|
||||||
|
[](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/actions?query=workflow%3Alinux-builds)
|
||||||
[](https://ci.appveyor.com/project/githubuser0xFFFF/qt-advanced-docking-system/branch/master)
|
[](https://ci.appveyor.com/project/githubuser0xFFFF/qt-advanced-docking-system/branch/master)
|
||||||
[](gnu-lgpl-v2.1.md)
|
[](gnu-lgpl-v2.1.md)
|
||||||
|
|
||||||
[What's new](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/releases/latest) •
|
|
||||||
[Documentation](doc/user-guide.md)
|
|
||||||
|
|
||||||
Qt Advanced Docking System lets you create customizable layouts using a full
|
Qt Advanced Docking System lets you create customizable layouts using a full
|
||||||
featured window docking system similar to what is found in many popular
|
featured window docking system similar to what is found in many popular
|
||||||
integrated development environments (IDEs) such as Visual Studio.
|
integrated development environments (IDEs) such as Visual Studio.
|
||||||
|
|
||||||
|
- [What's new](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/releases/latest)
|
||||||
|
- [Documentation](doc/user-guide.md)
|
||||||
|
- Original Repository: https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System
|
||||||
|
|
||||||
[](https://www.youtube.com/watch?v=7pdNfafg3Qc)
|
[](https://www.youtube.com/watch?v=7pdNfafg3Qc)
|
||||||
|
|
||||||
## New and Noteworthy
|
## New and Noteworthy
|
||||||
|
|
||||||
The [release 3.5.0](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/releases/tag/3.5.0)
|
The [release 3.8](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/releases/latest)
|
||||||
|
adds the following features:
|
||||||
|
|
||||||
|
- option to close tabs with the middle mouse button
|
||||||
|
- `DeleteContentOnClose` flag for dynamic deletion and creation of dock widget
|
||||||
|
content
|
||||||
|
- improved focus highlighting functionality
|
||||||
|
|
||||||
|
The [release 3.7](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/releases/tag/3.7.2)
|
||||||
|
adds the following features:
|
||||||
|
|
||||||
|
- support for **Qt6.**
|
||||||
|
- support for [empty dock area](doc/user-guide.md#empty-dock-area)
|
||||||
|
|
||||||
|
The [release 3.6](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/releases/tag/3.6.3)
|
||||||
|
adds some nice new features:
|
||||||
|
|
||||||
|
- support for [central widget](doc/user-guide.md#central-widget) concept
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- support for [native floating widgets](doc/user-guide.md#floatingcontainerforcenativetitlebar-linux-only) on Linux
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Both features are contributions from ADS users. Read the [documentation](doc/user-guide.md)
|
||||||
|
to learn more about both new features.
|
||||||
|
|
||||||
|
The [release 3.5](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/releases/tag/3.5.0)
|
||||||
adds the new [focus highlighting](doc/user-guide.md#focushighlighting) feature.
|
adds the new [focus highlighting](doc/user-guide.md#focushighlighting) feature.
|
||||||
This optional feature enables highlighting of the focused dock widget like you
|
This optional feature enables highlighting of the focused dock widget like you
|
||||||
know it from Visual Studio.
|
know it from Visual Studio.
|
||||||
@@ -40,24 +73,34 @@ know it from Visual Studio.
|
|||||||
- [Tab-menu for easy handling of many tabbed dock widgets](#tab-menu-for-easy-handling-of-many-tabbed-dock-widgets)
|
- [Tab-menu for easy handling of many tabbed dock widgets](#tab-menu-for-easy-handling-of-many-tabbed-dock-widgets)
|
||||||
- [Many different ways to detach dock widgets](#many-different-ways-to-detach-dock-widgets)
|
- [Many different ways to detach dock widgets](#many-different-ways-to-detach-dock-widgets)
|
||||||
- [Supports deletion of dynamically created dock widgets](#supports-deletion-of-dynamically-created-dock-widgets)
|
- [Supports deletion of dynamically created dock widgets](#supports-deletion-of-dynamically-created-dock-widgets)
|
||||||
- [Python PyQt5 Bindings](#python-pyqt5-bindings)
|
- [Python Bindings](#python-bindings)
|
||||||
|
- [PySide6](#pyside6)
|
||||||
|
- [PyQt5](#pyqt5)
|
||||||
- [Tested Compatible Environments](#tested-compatible-environments)
|
- [Tested Compatible Environments](#tested-compatible-environments)
|
||||||
|
- [Supported Qt Versions](#supported-qt-versions)
|
||||||
- [Windows](#windows)
|
- [Windows](#windows)
|
||||||
- [macOS](#macos)
|
- [macOS](#macos)
|
||||||
- [Linux](#linux)
|
- [Linux](#linux)
|
||||||
- [Build](#build)
|
- [Build](#build)
|
||||||
- [Getting started / Example](#getting-started--example)
|
- [Getting started / Example](#getting-started--example)
|
||||||
- [Developers](#developers)
|
|
||||||
- [License information](#license-information)
|
- [License information](#license-information)
|
||||||
- [Alternative Docking System Implementations](#alternative-docking-system-implementations)
|
|
||||||
- [KDDockWidgets](#kddockwidgets)
|
|
||||||
- [QtitanDocking](#qtitandocking)
|
|
||||||
- [Donation](#donation)
|
- [Donation](#donation)
|
||||||
- [Showcase](#showcase)
|
- [Showcase](#showcase)
|
||||||
- [Qt Creator IDE](#qt-creator-ide)
|
- [Qt Creator IDE](#qt-creator-ide)
|
||||||
- [Qt Design Studio](#qt-design-studio)
|
- [Qt Design Studio](#qt-design-studio)
|
||||||
- [QmixElements](#qmixelements)
|
- [CETONI Elements](#cetoni-elements)
|
||||||
- [ezEditor](#ezeditor)
|
- [ezEditor](#ezeditor)
|
||||||
|
- [D-Tect X](#d-tect-x)
|
||||||
|
- [HiveWE](#hivewe)
|
||||||
|
- [Ramses Composer](#ramses-composer)
|
||||||
|
- [Plot Juggler](#plot-juggler)
|
||||||
|
- [Notepad Next](#notepad-next)
|
||||||
|
- [MetGem](#metgem)
|
||||||
|
- [PRE Workbench](#pre-workbench)
|
||||||
|
- [Alternative Docking System Implementations](#alternative-docking-system-implementations)
|
||||||
|
- [KDDockWidgets](#kddockwidgets)
|
||||||
|
- [QtitanDocking](#qtitandocking)
|
||||||
|
- [DockingPanes](#dockingpanes)
|
||||||
|
|
||||||
### Docking everywhere - no central widget
|
### Docking everywhere - no central widget
|
||||||
|
|
||||||
@@ -65,8 +108,8 @@ 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
|
border of the main window or you can dock into each dock area - so you are
|
||||||
free to dock almost everywhere.
|
free to dock almost everywhere.
|
||||||
|
|
||||||
\
|

|
||||||
\
|
|
||||||

|

|
||||||
|
|
||||||
### Docking inside floating windows
|
### Docking inside floating windows
|
||||||
@@ -74,8 +117,8 @@ free to dock almost everywhere.
|
|||||||
There is no difference between the main window and a floating window. Docking
|
There is no difference between the main window and a floating window. Docking
|
||||||
into floating windows is supported.
|
into floating windows is supported.
|
||||||
|
|
||||||
\
|

|
||||||
\
|
|
||||||

|

|
||||||
|
|
||||||
### Grouped dragging
|
### Grouped dragging
|
||||||
@@ -84,8 +127,8 @@ 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
|
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.
|
a floating widget or from one dock area to another one.
|
||||||
|
|
||||||
\
|

|
||||||
\
|
|
||||||

|

|
||||||
|
|
||||||
### Perspectives for fast switching of the complete main window layout
|
### Perspectives for fast switching of the complete main window layout
|
||||||
@@ -96,13 +139,13 @@ perspective to make your own custom perspective. Later you can simply
|
|||||||
select a perspective from the perspective list to quickly switch the complete
|
select a perspective from the perspective list to quickly switch the complete
|
||||||
main window layout.
|
main window layout.
|
||||||
|
|
||||||
\
|

|
||||||
\
|
|
||||||

|

|
||||||
|
|
||||||
### Opaque and non-opaque splitter resizing
|
### Opaque and non-opaque splitter resizing
|
||||||
|
|
||||||
The advanced docking system uses standard QSplitters as resize separators and thus supports opaque and non-opaque resizing functionality of QSplitter. In some rare cases, for very complex widgets or on slow machines resizing via separator on the fly may cause flicking and glaring of rendered content inside a widget. The global dock manager flag `OpaqueSplitterResize` configures the resizing behaviour of the splitters. If this flag is set, then widgets are resized dynamically (opaquely) while interactively moving the splitters.
|
The advanced docking system uses standard QSplitters as resize separators and thus supports opaque and non-opaque resizing functionality of QSplitter. In some rare cases, for very complex widgets or on slow machines resizing via separator on the fly may cause flicking and glaring of rendered content inside a widget. The global dock manager flag `OpaqueSplitterResize` configures the resizing behaviour of the splitters. If this flag is set, then widgets are resized dynamically (opaquely) while interactively moving the splitters.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -112,7 +155,7 @@ If this flag is cleared, the widget resizing is deferred until the mouse button
|
|||||||
|
|
||||||
### Opaque and non-opaque undocking
|
### Opaque and non-opaque undocking
|
||||||
|
|
||||||
By default, opaque undocking is active. That means, as soon as you drag a dock widget or a dock area with a number of dock widgets it will be undocked and moved into a floating widget and then the floating widget will be dragged around. That means undocking will take place immediatelly. You can compare this with opaque splitter resizing. If the flag `OpaqueUndocking` is cleared, then non-opaque undocking is active. In this mode, undocking is more like a standard drag and drop operation. That means, the dragged dock widget or dock area is not undocked immediatelly. Instead, a drag preview widget is created and dragged around to indicate the future position of the dock widget or dock area. The actual dock operation is only executed when the mouse button is released. That makes it possible, to cancel an active drag operation with the escape key.
|
By default, opaque undocking is active. That means, as soon as you drag a dock widget or a dock area with a number of dock widgets it will be undocked and moved into a floating widget and then the floating widget will be dragged around. That means undocking will take place immediately. You can compare this with opaque splitter resizing. If the flag `OpaqueUndocking` is cleared, then non-opaque undocking is active. In this mode, undocking is more like a standard drag and drop operation. That means, the dragged dock widget or dock area is not undocked immediately. Instead, a drag preview widget is created and dragged around to indicate the future position of the dock widget or dock area. The actual dock operation is only executed when the mouse button is released. That makes it possible, to cancel an active drag operation with the escape key.
|
||||||
|
|
||||||
The drag preview widget can be configured by a number of global dock manager flags:
|
The drag preview widget can be configured by a number of global dock manager flags:
|
||||||
- `DragPreviewIsDynamic`: if this flag is enabled, the preview will be adjusted dynamically to the drop area
|
- `DragPreviewIsDynamic`: if this flag is enabled, the preview will be adjusted dynamically to the drop area
|
||||||
@@ -139,22 +182,52 @@ You can detach dock widgets and also dock areas in the following ways:
|
|||||||
|
|
||||||
Normally clicking the close button of a dock widget will just hide the widget and the user can show it again using the toggleView() action of the dock widget. This is meant for user interfaces with a static amount of widgets. But the advanced docking system also supports dynamic dock widgets that will get deleted on close. If you set the dock widget flag `DockWidgetDeleteOnClose` for a certain dock widget, then it will be deleted as soon as you close this dock widget. This enables the implementation of user interfaces with dynamically created editors, like in word processing applications or source code development tools.
|
Normally clicking the close button of a dock widget will just hide the widget and the user can show it again using the toggleView() action of the dock widget. This is meant for user interfaces with a static amount of widgets. But the advanced docking system also supports dynamic dock widgets that will get deleted on close. If you set the dock widget flag `DockWidgetDeleteOnClose` for a certain dock widget, then it will be deleted as soon as you close this dock widget. This enables the implementation of user interfaces with dynamically created editors, like in word processing applications or source code development tools.
|
||||||
|
|
||||||
### Python PyQt5 Bindings
|
## Python Bindings
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The Advanced Docking System comes with a complete Python integration based on
|
Thanks to the contribution of several users, the Advanced Docking System comes
|
||||||
PyQt5 bindings. The package is available via [conda-forge](https://github.com/conda-forge/pyqtads-feedstock). The python integration has been contributed to this project
|
with a complete Python integration. Python bindings are available for **PyQt5** and
|
||||||
by the following people:
|
**PySide6**.
|
||||||
|
|
||||||
|
### PySide6
|
||||||
|
|
||||||
|
A PySide6 ADS package is available via PyPi and can be installed on Windows,
|
||||||
|
macOS, and Linux with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install PySide6-QtAds
|
||||||
|
```
|
||||||
|
|
||||||
|
Sample code is available [here](https://github.com/mborgerson/Qt-Advanced-Docking-System/tree/pyside6/examples). To run the samples, you'll also need to install latest qtpy
|
||||||
|
from source (pip install https://github.com/spyder-ide/qtpy/archive/refs/heads/master.zip).
|
||||||
|
The PySide6 bindings were contributed by:
|
||||||
|
|
||||||
|
- [mborgerson](https://github.com/mborgerson)
|
||||||
|
|
||||||
|
For more information about the PySide6 bindings read [this](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/issues/298) issue.
|
||||||
|
|
||||||
|
### PyQt5
|
||||||
|
|
||||||
|
A package is available via [conda-forge](https://github.com/conda-forge/pyqtads-feedstock).
|
||||||
|
The python integration has been contributed to this project by the following people:
|
||||||
|
|
||||||
- [n-elie](https://github.com/n-elie)
|
- [n-elie](https://github.com/n-elie)
|
||||||
- [Hugo Slepicka](https://github.com/hhslepicka)
|
- [Hugo Slepicka](https://github.com/hhslepicka)
|
||||||
- [K Lauer](https://github.com/klauer)
|
- [K Lauer](https://github.com/klauer)
|
||||||
|
|
||||||
Latest working version: [3.5.1](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/releases/tag/3.5.1)
|
A Python integration is also available via PyPi. You can install the
|
||||||
|
[PyQtAds](https://pypi.org/project/PyQtAds/) package via pip. This feature has been
|
||||||
|
contributed to this project by:
|
||||||
|
|
||||||
|
- [Mira Weller](https://github.com/luelista)
|
||||||
|
|
||||||
## Tested Compatible Environments
|
## Tested Compatible Environments
|
||||||
|
|
||||||
|
### Supported Qt Versions
|
||||||
|
|
||||||
|
The library supports **Qt5** and **Qt6**.
|
||||||
|
|
||||||
### Windows
|
### Windows
|
||||||
|
|
||||||
Windows 10 [](https://ci.appveyor.com/project/githubuser0xFFFF/qt-advanced-docking-system/branch/master)
|
Windows 10 [](https://ci.appveyor.com/project/githubuser0xFFFF/qt-advanced-docking-system/branch/master)
|
||||||
@@ -171,20 +244,34 @@ The application can be compiled for macOS. A user reported, that the library wor
|
|||||||
|
|
||||||
### Linux
|
### Linux
|
||||||
|
|
||||||
Ubuntu [](https://travis-ci.org/githubuser0xFFFF/Qt-Advanced-Docking-System)
|
[](https://travis-ci.org/githubuser0xFFFF/Qt-Advanced-Docking-System)
|
||||||
[](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/actions?query=workflow%3Alinux-builds)
|
[](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/actions?query=workflow%3Alinux-builds)
|
||||||
|
|
||||||
The application can be compiled for Linux and has been developed and tested with **Kubuntu 18.04** and **Kubuntu 19.10**.
|
Unfortunately, there is no such thing as a Linux operating system. Linux is a heterogeneous environment with a variety of different distributions. So it is not possible to support "Linux" like this is possible for Windows. It is only possible to support and test a small subset of Linux distributions. The library can be compiled for and has been developed and tested with the following Linux distributions:
|
||||||
|
|
||||||
|
- **Kubuntu 18.04 and 19.10**
|
||||||
|
- **Ubuntu 18.04, 19.10 and 20.04**
|
||||||
|
|
||||||
|
There are some requirements for the Linux distribution that have to be met:
|
||||||
|
|
||||||
|
- an X server that supports ARGB visuals and a compositing window manager. This is required to display the translucent dock overlays ([https://doc.qt.io/qt-5/qwidget.html#creating-translucent-windows](https://doc.qt.io/qt-5/qwidget.html#creating-translucent-windows)). If your Linux distribution does not support this, or if you disable this feature, you will very likely see issue [#95](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/issues/95).
|
||||||
|
- Wayland is not properly supported by Qt yet. If you use Wayland, then you should set the session type to x11: `XDG_SESSION_TYPE=x11 ./AdvancedDockingSystemDemo`. You will find more details about this in issue [#288](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/issues/288).
|
||||||
|
|
||||||
|
Screenshot Kubuntu:
|
||||||

|

|
||||||
|
|
||||||
and with **Ubuntu 19.10**
|
Screenshot Ubuntu:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
Open the `ads.pro` with QtCreator and start the build, that's it.
|
The Linux build requires private header files. Make sure that they are installed:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt install qtbase5-private-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Open the `ads.pro` file with QtCreator and start the build, that's it.
|
||||||
You can run the demo project and test it yourself.
|
You can run the demo project and test it yourself.
|
||||||
|
|
||||||
## Getting started / Example
|
## Getting started / Example
|
||||||
@@ -231,8 +318,9 @@ MainWindow::MainWindow(QWidget *parent) :
|
|||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
// Create the dock manager. Because the parent parameter is a QMainWindow
|
// Create the dock manager after the ui is setup. Because the
|
||||||
// the dock manager registers itself as the central widget.
|
// parent parameter is a QMainWindow the dock manager registers
|
||||||
|
// itself as the central widget as such the ui must be set up first.
|
||||||
m_DockManager = new ads::CDockManager(this);
|
m_DockManager = new ads::CDockManager(this);
|
||||||
|
|
||||||
// Create example content label - this can be any application specific
|
// Create example content label - this can be any application specific
|
||||||
@@ -261,41 +349,11 @@ MainWindow::~MainWindow()
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Developers
|
|
||||||
|
|
||||||
- Uwe Kindler, Project Maintainer
|
|
||||||
- Manuel Freiholz
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
## License information
|
## License information
|
||||||
|
|
||||||
[](gnu-lgpl-v2.1.md)
|
[](gnu-lgpl-v2.1.md)
|
||||||
This project uses the [LGPLv2.1 license](gnu-lgpl-v2.1.md)
|
This project uses the [LGPLv2.1 license](gnu-lgpl-v2.1.md)
|
||||||
|
|
||||||
|
|
||||||
## Alternative Docking System Implementations
|
|
||||||
|
|
||||||
If this Qt Advanced Docking System does not fit to your needs you may consider some of the alternative docking system solutions for Qt.
|
|
||||||
|
|
||||||
### KDDockWidgets
|
|
||||||
|
|
||||||
This is an advanced docking framework for Qt from [KDAB](https://www.kdab.com/). The interesting thing is, that they separated GUI code from logic, so they can easily provide a QtQuick backend in the future.
|
|
||||||
|
|
||||||
- [Blog post about KDDockWidgets](https://www.kdab.com/kddockwidgets/)
|
|
||||||
- [GitHub project](https://github.com/KDAB/KDDockWidgets)
|
|
||||||
|
|
||||||
|
|
||||||
### QtitanDocking
|
|
||||||
|
|
||||||
This is a commercial component from [Developer Machines](https://www.devmachines.com/) for Qt Framework that allows to create a Microsoft like dockable user interface. They also offer a lot of other interesting and useful components for Qt.
|
|
||||||
|
|
||||||
- [Product page](https://www.devmachines.com/qtitandocking-overview.html)
|
|
||||||
|
|
||||||
## Donation
|
## Donation
|
||||||
|
|
||||||
If this project help you reduce time to develop or if you just like it, you can give me a cup of coffee :coffee::wink:.
|
If this project help you reduce time to develop or if you just like it, you can give me a cup of coffee :coffee::wink:.
|
||||||
@@ -311,7 +369,7 @@ If this project help you reduce time to develop or if you just like it, you can
|
|||||||
From version 4.12 on, Qt Creator uses the Advanced Docking Framework for its
|
From version 4.12 on, Qt Creator uses the Advanced Docking Framework for its
|
||||||
Qt Quick Designer. This improves the usability when using multiple screens.
|
Qt Quick Designer. This improves the usability when using multiple screens.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### [Qt Design Studio](https://www.qt.io/ui-design-tools)
|
### [Qt Design Studio](https://www.qt.io/ui-design-tools)
|
||||||
|
|
||||||
@@ -319,15 +377,16 @@ Taken from the [Qt Blog](https://www.qt.io/blog/qt-design-studio-1.5-beta-releas
|
|||||||
|
|
||||||
> The most obvious change in [Qt Design Studio 1.5](https://www.qt.io/blog/qt-design-studio-1.5-beta-released) is the integration of dock widgets using the Qt Advanced Docking System. This allows the user to fully customize the workspace and also to undock any view into its own top level window. This especially improves the usability when using multiple screens.
|
> The most obvious change in [Qt Design Studio 1.5](https://www.qt.io/blog/qt-design-studio-1.5-beta-released) is the integration of dock widgets using the Qt Advanced Docking System. This allows the user to fully customize the workspace and also to undock any view into its own top level window. This especially improves the usability when using multiple screens.
|
||||||
|
|
||||||

|
[](https://youtu.be/za9KBWcFXEw?t=84)
|
||||||
|
|
||||||
### [QmixElements](https://www.cetoni.com/products/qmixelements/)
|
### [CETONI Elements](https://cetoni.com/cetoni-elements/)
|
||||||
|
|
||||||
The QmixElements software from [CETONI](https://www.cetoni.com) is a comprehensive,
|
The CETONI Elements software from [CETONI](https://www.cetoni.com) is a comprehensive,
|
||||||
plugin-based and modular laboratory automation software for controlling CETONI devices using a joint graphical user interface. The software features a powerful script system to automate processes. This [blog post](https://www.cetoni.com/blog/qmixelements-advanced-docking-system/) gives a nice overview about the use of the Qt
|
plugin-based and modular laboratory automation software for controlling CETONI devices using a joint graphical user interface. The software features a powerful script system to automate processes. The software uses the advanced docking system to give the user the freedom to arrange all the views and windows that are provided by the various plugins.
|
||||||
Advanced Docking System in the QmixElements sofware.
|
|
||||||
|
|
||||||

|
[learn more...](https://cetoni.com/cetoni-elements/)
|
||||||
|
|
||||||
|
[](https://www.youtube.com/watch?v=7pdNfafg3Qc)
|
||||||
|
|
||||||
### [ezEditor](https://github.com/ezEngine/ezEngine)
|
### [ezEditor](https://github.com/ezEngine/ezEngine)
|
||||||
|
|
||||||
@@ -335,4 +394,118 @@ The ezEditor is a full blown graphical editor used for editing scenes and
|
|||||||
importing and authoring assets for the [ezEngine](https://github.com/ezEngine/ezEngine) -
|
importing and authoring assets for the [ezEngine](https://github.com/ezEngine/ezEngine) -
|
||||||
an open source C++ game engine in active development.
|
an open source C++ game engine in active development.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
### [D-Tect X](https://www.duerr-ndt.com/products/ndt-software/d-tect-xray-inspection-software.html)
|
||||||
|
|
||||||
|
D-Tect X is a X-ray inspection software for industrial radiography. It is a state-of-the-art 64-bit application which supports GPU (Graphics Processing Unit) acceleration and takes full advantage of computers with multiple CPU cores. A large set of tools assist the user in image analysis and evaluation. Thanks to the Qt Advanced Docking System the flexible and intuitive user interface can be completely customized to each user’s preference.
|
||||||
|
|
||||||
|
[learn more...](https://www.duerr-ndt.com/products/ndt-software/d-tect-xray-inspection-software.html)
|
||||||
|
|
||||||
|
[](https://youtu.be/mOor7GmmIJo?t=13)
|
||||||
|
|
||||||
|
### [HiveWE](https://github.com/stijnherfst/HiveWE)
|
||||||
|
|
||||||
|
HiveWE is a Warcraft III world editor. It focusses on speed and ease of use,
|
||||||
|
especially for large maps where the regular World Editor is often too slow and clunky.
|
||||||
|
It has a JASS editor with syntax highlighting, tabs, code completion and more.
|
||||||
|
The JASS editor uses the Qt Advanced Docking System for the management and layout
|
||||||
|
of the open editor windows.
|
||||||
|
|
||||||
|
[learn more...](https://github.com/stijnherfst/HiveWE)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### [Ramses Composer](https://github.com/GENIVI/ramses-composer)
|
||||||
|
|
||||||
|
Ramses Composer is the authoring tool for the open source [RAMSES](https://github.com/GENIVI/ramses)
|
||||||
|
rendering ecosystem.
|
||||||
|
|
||||||
|
Ramses is a low-level rendering engine which is optimized for embedded hardware
|
||||||
|
mobile devices, automotive ECUs, IoT electronics. Ramses was initially developed
|
||||||
|
at the BMW Group and open-sourced in 2018 as part of a collaboration initiative
|
||||||
|
with the Genivi Alliance. It is an important part of the BMW infotainment cluster
|
||||||
|
and digital portfolio.
|
||||||
|
|
||||||
|
[learn more...](https://github.com/GENIVI/ramses-composer)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### [Plot Juggler](https://github.com/facontidavide/PlotJuggler)
|
||||||
|
|
||||||
|
PlotJuggler is a fast, powerful and intuitive tool to visualize time series.
|
||||||
|
It makes it easy to visualize data but also to analyze it. You can manipulate
|
||||||
|
your time series using a simple and extendable Transform Editor. Some of the
|
||||||
|
highlights are:
|
||||||
|
|
||||||
|
- Simple Drag & Drop user interface.
|
||||||
|
- Load data from file.
|
||||||
|
- Connect to live streaming of data.
|
||||||
|
- Save the visualization layout and configurations to re-use them later.
|
||||||
|
- Fast OpenGL visualization.
|
||||||
|
- Can handle thousands of timeseries and millions of data points.
|
||||||
|
- Transform your data using a simple editor: derivative, moving average, integral, etc…
|
||||||
|
- PlotJuggler can be easily extended using plugins.
|
||||||
|
|
||||||
|
[read more...](https://github.com/facontidavide/PlotJuggler)
|
||||||
|
|
||||||
|
[](https://vimeo.com/480588113#t=46s)
|
||||||
|
|
||||||
|
### [Notepad Next](https://github.com/dail8859/NotepadNext)
|
||||||
|
|
||||||
|
Notepad Next is a cross-platform reimplementation of Notepad++ that uses the
|
||||||
|
Advanced Docking System to arrange the open source files on the screen.
|
||||||
|
|
||||||
|
[read more...](https://github.com/dail8859/NotepadNext)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### [MetGem](https://metgem.github.io/)
|
||||||
|
|
||||||
|
MetGem is an open-source software for tandem mass-spectrometry data visualization.
|
||||||
|
It's key features are standalone molecular networking and t-SNE based projections.
|
||||||
|
MetGem uses the Qt-Advanced-Docking-System to manage docks and to create independent
|
||||||
|
molecular network views.
|
||||||
|
|
||||||
|
[read more...](https://metgem.github.io/)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### [PRE Workbench](https://luelista.github.io/pre_workbench/)
|
||||||
|
|
||||||
|
Protocol Reverse Engineering Workbench is a software to support researchers in reverse engineering protocols and documenting the results. It supports various sources to import protocol traffic from, helps the discovery process by displaying different views and heuristic-based highlighting on data, and aids in documenting and sharing findings.
|
||||||
|
|
||||||
|
PRE Workbench is a Python software and uses the ADS PyQt integration.
|
||||||
|
|
||||||
|
[read more...](https://luelista.github.io/pre_workbench/)
|
||||||
|
|
||||||
|
[](https://youtu.be/U3op5UreV1Q)
|
||||||
|
|
||||||
|
## Alternative Docking System Implementations
|
||||||
|
|
||||||
|
If this Qt Advanced Docking System does not fit to your needs you may consider some of the alternative docking system solutions for Qt.
|
||||||
|
|
||||||
|
### KDDockWidgets
|
||||||
|
|
||||||
|
This is an advanced docking framework for Qt from [KDAB](https://www.kdab.com/). The interesting thing is, that they separated GUI code from logic, so they can easily provide a QtQuick backend in the future.
|
||||||
|
|
||||||
|
- [Blog post about KDDockWidgets](https://www.kdab.com/kddockwidgets/)
|
||||||
|
- [GitHub project](https://github.com/KDAB/KDDockWidgets)
|
||||||
|
|
||||||
|
**License:** dual-licensed, available under both commercial and GPL license.
|
||||||
|
|
||||||
|
### QtitanDocking
|
||||||
|
|
||||||
|
This is a commercial component from [Developer Machines](https://www.devmachines.com/) for Qt Framework that allows to create a Microsoft like dockable user interface. They also offer a lot of other interesting and useful components for Qt. The library is available
|
||||||
|
|
||||||
|
- [Product page](https://www.devmachines.com/qtitandocking-overview.html)
|
||||||
|
|
||||||
|
**License:** Commercial license
|
||||||
|
|
||||||
|
### DockingPanes
|
||||||
|
|
||||||
|
DockingPanes is a library for Qt Widgets that implements docking windows that have the look and feel of Visual Studio. It provides a simple API which allows an application to make use of docking windows with a few calls.
|
||||||
|
|
||||||
|
- [GitHub project](https://github.com/KestrelRadarSensors/dockingpanes)
|
||||||
|
|
||||||
|
**License:** GPL
|
||||||
|
|||||||
28
ads.pri
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
|
||||||
|
CONFIG(debug, debug|release){
|
||||||
|
win32-g++ {
|
||||||
|
versionAtLeast(QT_VERSION, 5.15.0) {
|
||||||
|
LIBS += -lqtadvanceddocking
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LIBS += -lqtadvanceddockingd
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else:msvc {
|
||||||
|
LIBS += -lqtadvanceddockingd
|
||||||
|
}
|
||||||
|
else:mac {
|
||||||
|
LIBS += -lqtadvanceddocking_debug
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LIBS += -lqtadvanceddocking
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
LIBS += -lqtadvanceddocking
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unix:!macx {
|
||||||
|
LIBS += -lxcb
|
||||||
|
}
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.5)
|
||||||
project(ads_demo VERSION ${VERSION_SHORT})
|
project(ads_demo VERSION ${VERSION_SHORT})
|
||||||
|
|
||||||
find_package(Qt5 5.5 COMPONENTS Core Gui Widgets REQUIRED)
|
find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
|
||||||
if(WIN32)
|
find_package(Qt${QT_VERSION_MAJOR} 5.5 COMPONENTS Core Gui Widgets REQUIRED)
|
||||||
find_package(Qt5 5.5 COMPONENTS AxContainer REQUIRED)
|
if(WIN32 AND QT_VERSION_MAJOR LESS 6)
|
||||||
|
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS AxContainer REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||||
set(ads_demo_SRCS
|
set(ads_demo_SRCS
|
||||||
@@ -16,9 +17,11 @@ set(ads_demo_SRCS
|
|||||||
)
|
)
|
||||||
add_executable(AdvancedDockingSystemDemo WIN32 ${ads_demo_SRCS})
|
add_executable(AdvancedDockingSystemDemo WIN32 ${ads_demo_SRCS})
|
||||||
target_include_directories(AdvancedDockingSystemDemo PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../src")
|
target_include_directories(AdvancedDockingSystemDemo PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../src")
|
||||||
target_link_libraries(AdvancedDockingSystemDemo PUBLIC Qt5::Core Qt5::Gui Qt5::Widgets)
|
target_link_libraries(AdvancedDockingSystemDemo PUBLIC Qt${QT_VERSION_MAJOR}::Core
|
||||||
if(WIN32)
|
Qt${QT_VERSION_MAJOR}::Gui
|
||||||
target_link_libraries(AdvancedDockingSystemDemo PUBLIC Qt5::AxContainer)
|
Qt${QT_VERSION_MAJOR}::Widgets)
|
||||||
|
if(WIN32 AND QT_VERSION_MAJOR LESS 6)
|
||||||
|
target_link_libraries(AdvancedDockingSystemDemo PUBLIC Qt${QT_VERSION_MAJOR}::AxContainer)
|
||||||
endif()
|
endif()
|
||||||
target_link_libraries(AdvancedDockingSystemDemo PRIVATE qtadvanceddocking)
|
target_link_libraries(AdvancedDockingSystemDemo PRIVATE qtadvanceddocking)
|
||||||
set_target_properties(AdvancedDockingSystemDemo PROPERTIES
|
set_target_properties(AdvancedDockingSystemDemo PROPERTIES
|
||||||
|
|||||||
@@ -58,11 +58,15 @@
|
|||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QToolButton>
|
#include <QToolButton>
|
||||||
|
#include <QToolBar>
|
||||||
|
#include <QPointer>
|
||||||
|
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||||
#include <QAxWidget>
|
#include <QAxWidget>
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QElapsedTimer>
|
#include <QElapsedTimer>
|
||||||
@@ -75,7 +79,7 @@
|
|||||||
#include "FloatingDockContainer.h"
|
#include "FloatingDockContainer.h"
|
||||||
#include "DockComponentsFactory.h"
|
#include "DockComponentsFactory.h"
|
||||||
#include "StatusDialog.h"
|
#include "StatusDialog.h"
|
||||||
|
#include "DockSplitter.h"
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -109,7 +113,7 @@ static void appendFeaturStringToWindowTitle(ads::CDockWidget* DockWidget)
|
|||||||
static QIcon svgIcon(const QString& File)
|
static QIcon svgIcon(const QString& File)
|
||||||
{
|
{
|
||||||
// This is a workaround, because in item views SVG icons are not
|
// This is a workaround, because in item views SVG icons are not
|
||||||
// properly scaled an look blurry or pixelate
|
// properly scaled and look blurry or pixelate
|
||||||
QIcon SvgIcon(File);
|
QIcon SvgIcon(File);
|
||||||
SvgIcon.addPixmap(SvgIcon.pixmap(92));
|
SvgIcon.addPixmap(SvgIcon.pixmap(92));
|
||||||
return SvgIcon;
|
return SvgIcon;
|
||||||
@@ -166,6 +170,8 @@ struct MainWindowPrivate
|
|||||||
QComboBox* PerspectiveComboBox = nullptr;
|
QComboBox* PerspectiveComboBox = nullptr;
|
||||||
ads::CDockManager* DockManager = nullptr;
|
ads::CDockManager* DockManager = nullptr;
|
||||||
ads::CDockWidget* WindowTitleTestDockWidget = nullptr;
|
ads::CDockWidget* WindowTitleTestDockWidget = nullptr;
|
||||||
|
QPointer<ads::CDockWidget> LastDockedEditor;
|
||||||
|
QPointer<ads::CDockWidget> LastCreatedFloatingEditor;
|
||||||
|
|
||||||
MainWindowPrivate(CMainWindow* _public) : _this(_public) {}
|
MainWindowPrivate(CMainWindow* _public) : _this(_public) {}
|
||||||
|
|
||||||
@@ -352,6 +358,7 @@ struct MainWindowPrivate
|
|||||||
|
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
|
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||||
/**
|
/**
|
||||||
* Creates an ActiveX widget on windows
|
* Creates an ActiveX widget on windows
|
||||||
*/
|
*/
|
||||||
@@ -365,6 +372,7 @@ struct MainWindowPrivate
|
|||||||
return DockWidget;
|
return DockWidget;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -399,6 +407,9 @@ void MainWindowPrivate::createContent()
|
|||||||
// Test custom factory - we inject a help button into the title bar
|
// Test custom factory - we inject a help button into the title bar
|
||||||
ads::CDockComponentsFactory::setFactory(new CCustomComponentsFactory());
|
ads::CDockComponentsFactory::setFactory(new CCustomComponentsFactory());
|
||||||
auto TopDockArea = DockManager->addDockWidget(ads::TopDockWidgetArea, FileSystemWidget);
|
auto TopDockArea = DockManager->addDockWidget(ads::TopDockWidgetArea, FileSystemWidget);
|
||||||
|
// Uncomment the next line if you would like to test the
|
||||||
|
// HideSingleWidgetTitleBar functionality
|
||||||
|
// TopDockArea->setDockAreaFlag(ads::CDockAreaWidget::HideSingleWidgetTitleBar, true);
|
||||||
ads::CDockComponentsFactory::resetDefaultFactory();
|
ads::CDockComponentsFactory::resetDefaultFactory();
|
||||||
|
|
||||||
// We create a calendar widget and clear all flags to prevent the dock area
|
// We create a calendar widget and clear all flags to prevent the dock area
|
||||||
@@ -406,6 +417,23 @@ void MainWindowPrivate::createContent()
|
|||||||
DockWidget = createCalendarDockWidget();
|
DockWidget = createCalendarDockWidget();
|
||||||
DockWidget->setTabToolTip(QString("Tab ToolTip\nHodie est dies magna"));
|
DockWidget->setTabToolTip(QString("Tab ToolTip\nHodie est dies magna"));
|
||||||
auto DockArea = DockManager->addDockWidget(ads::CenterDockWidgetArea, DockWidget, TopDockArea);
|
auto DockArea = DockManager->addDockWidget(ads::CenterDockWidgetArea, DockWidget, TopDockArea);
|
||||||
|
// Now we create a action to test resizing of DockArea widget
|
||||||
|
auto Action = ui.menuTests->addAction(QString("Resize %1").arg(DockWidget->windowTitle()));
|
||||||
|
QObject::connect(Action, &QAction::triggered, [DockArea]()
|
||||||
|
{
|
||||||
|
// Resizing only works, if the Splitter is visible and has a valid
|
||||||
|
// sizes
|
||||||
|
auto Splitter = ads::internal::findParent<ads::CDockSplitter*>(DockArea);
|
||||||
|
if (!Splitter)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// We change the sizes of the splitter that contains the Calendar 1 widget
|
||||||
|
// to resize the dock widget
|
||||||
|
int Width = Splitter->width();
|
||||||
|
Splitter->setSizes({Width * 2/3, Width * 1/3});
|
||||||
|
});
|
||||||
|
DockWidget->setWindowTitle(QString("My " + DockWidget->windowTitle()));
|
||||||
|
|
||||||
// Now we add a custom button to the dock area title bar that will create
|
// Now we add a custom button to the dock area title bar that will create
|
||||||
// new editor widgets when clicked
|
// new editor widgets when clicked
|
||||||
@@ -430,20 +458,47 @@ void MainWindowPrivate::createContent()
|
|||||||
DockManager->addDockWidget(ads::TopDockWidgetArea, createLongTextLabelDockWidget(), RighDockArea);
|
DockManager->addDockWidget(ads::TopDockWidgetArea, createLongTextLabelDockWidget(), RighDockArea);
|
||||||
auto BottomDockArea = DockManager->addDockWidget(ads::BottomDockWidgetArea, createLongTextLabelDockWidget(), RighDockArea);
|
auto BottomDockArea = DockManager->addDockWidget(ads::BottomDockWidgetArea, createLongTextLabelDockWidget(), RighDockArea);
|
||||||
DockManager->addDockWidget(ads::CenterDockWidgetArea, createLongTextLabelDockWidget(), RighDockArea);
|
DockManager->addDockWidget(ads::CenterDockWidgetArea, createLongTextLabelDockWidget(), RighDockArea);
|
||||||
DockManager->addDockWidget(ads::CenterDockWidgetArea, createLongTextLabelDockWidget(), BottomDockArea);
|
auto LabelDockWidget = createLongTextLabelDockWidget();
|
||||||
|
std::cout << "DockWidget " << LabelDockWidget->objectName().toStdString() << std::endl;
|
||||||
|
DockManager->addDockWidget(ads::CenterDockWidgetArea, LabelDockWidget, BottomDockArea);
|
||||||
|
|
||||||
auto Action = ui.menuTests->addAction(QString("Set %1 Floating").arg(DockWidget->windowTitle()));
|
// Tests CustomCloseHandling without DeleteOnClose
|
||||||
|
LabelDockWidget->setFeature(ads::CDockWidget::CustomCloseHandling, true);
|
||||||
|
QObject::connect(LabelDockWidget, &ads::CDockWidget::closeRequested, [LabelDockWidget, this]()
|
||||||
|
{
|
||||||
|
int Result = QMessageBox::question(_this, "Custom Close Request",
|
||||||
|
"Do you really want to close this dock widget?");
|
||||||
|
if (QMessageBox::Yes == Result)
|
||||||
|
{
|
||||||
|
LabelDockWidget->closeDockWidget();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Action = ui.menuTests->addAction(QString("Set %1 Floating").arg(DockWidget->windowTitle()));
|
||||||
DockWidget->connect(Action, SIGNAL(triggered()), SLOT(setFloating()));
|
DockWidget->connect(Action, SIGNAL(triggered()), SLOT(setFloating()));
|
||||||
Action = ui.menuTests->addAction(QString("Set %1 As Current Tab").arg(DockWidget->windowTitle()));
|
Action = ui.menuTests->addAction(QString("Set %1 As Current Tab").arg(DockWidget->windowTitle()));
|
||||||
DockWidget->connect(Action, SIGNAL(triggered()), SLOT(setAsCurrentTab()));
|
DockWidget->connect(Action, SIGNAL(triggered()), SLOT(setAsCurrentTab()));
|
||||||
Action = ui.menuTests->addAction(QString("Raise %1").arg(DockWidget->windowTitle()));
|
Action = ui.menuTests->addAction(QString("Raise %1").arg(DockWidget->windowTitle()));
|
||||||
DockWidget->connect(Action, SIGNAL(triggered()), SLOT(raise()));
|
DockWidget->connect(Action, SIGNAL(triggered()), SLOT(raise()));
|
||||||
|
|
||||||
|
// Test hidden floating dock widget
|
||||||
|
DockWidget = createLongTextLabelDockWidget();
|
||||||
|
DockManager->addDockWidgetFloating(DockWidget);
|
||||||
|
DockWidget->toggleView(false);
|
||||||
|
|
||||||
|
// Test visible floating dock widget
|
||||||
|
DockWidget = createCalendarDockWidget();
|
||||||
|
DockManager->addDockWidgetFloating(DockWidget);
|
||||||
|
DockWidget->setWindowTitle(QString("My " + DockWidget->windowTitle()));
|
||||||
|
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
|
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||||
if (!ads::CDockManager::testConfigFlag(ads::CDockManager::OpaqueUndocking))
|
if (!ads::CDockManager::testConfigFlag(ads::CDockManager::OpaqueUndocking))
|
||||||
{
|
{
|
||||||
DockManager->addDockWidget(ads::CenterDockWidgetArea, createActiveXWidget(), RighDockArea);
|
DockManager->addDockWidget(ads::CenterDockWidgetArea, createActiveXWidget(), RighDockArea);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (auto DockWidget : DockManager->dockWidgetsMap())
|
for (auto DockWidget : DockManager->dockWidgetsMap())
|
||||||
@@ -489,6 +544,14 @@ void MainWindowPrivate::createActions()
|
|||||||
_this->connect(a, SIGNAL(triggered()), SLOT(createEditor()));
|
_this->connect(a, SIGNAL(triggered()), SLOT(createEditor()));
|
||||||
ui.menuTests->addAction(a);
|
ui.menuTests->addAction(a);
|
||||||
|
|
||||||
|
a = ui.toolBar->addAction("Create Editor Tab");
|
||||||
|
a->setProperty("Floating", false);
|
||||||
|
a->setToolTip("Creates a editor tab and inserts it as second tab into an area");
|
||||||
|
a->setIcon(svgIcon(":/adsdemo/images/tab.svg"));
|
||||||
|
a->setProperty("Tabbed", true);
|
||||||
|
_this->connect(a, SIGNAL(triggered()), SLOT(createEditor()));
|
||||||
|
ui.menuTests->addAction(a);
|
||||||
|
|
||||||
a = ui.toolBar->addAction("Create Floating Table");
|
a = ui.toolBar->addAction("Create Floating Table");
|
||||||
a->setToolTip("Creates floating dynamic dockable table with millions of entries");
|
a->setToolTip("Creates floating dynamic dockable table with millions of entries");
|
||||||
a->setIcon(svgIcon(":/adsdemo/images/grid_on.svg"));
|
a->setIcon(svgIcon(":/adsdemo/images/grid_on.svg"));
|
||||||
@@ -600,12 +663,20 @@ CMainWindow::CMainWindow(QWidget *parent) :
|
|||||||
// uncomment if you would like to enable an equal distribution of the
|
// uncomment if you would like to enable an equal distribution of the
|
||||||
// available size of a splitter to all contained dock widgets
|
// available size of a splitter to all contained dock widgets
|
||||||
// CDockManager::setConfigFlag(CDockManager::EqualSplitOnInsertion, true);
|
// CDockManager::setConfigFlag(CDockManager::EqualSplitOnInsertion, true);
|
||||||
|
|
||||||
|
// uncomment if you would like to close tabs with the middle mouse button, web browser style
|
||||||
|
// CDockManager::setConfigFlag(CDockManager::MiddleMouseButtonClosesTab, true);
|
||||||
|
|
||||||
// Now create the dock manager and its content
|
// Now create the dock manager and its content
|
||||||
d->DockManager = new CDockManager(this);
|
d->DockManager = new CDockManager(this);
|
||||||
|
|
||||||
connect(d->PerspectiveComboBox, SIGNAL(activated(const QString&)),
|
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||||
d->DockManager, SLOT(openPerspective(const QString&)));
|
connect(d->PerspectiveComboBox, SIGNAL(activated(QString)),
|
||||||
|
d->DockManager, SLOT(openPerspective(QString)));
|
||||||
|
#else
|
||||||
|
connect(d->PerspectiveComboBox, SIGNAL(textActivated(QString)),
|
||||||
|
d->DockManager, SLOT(openPerspective(QString)));
|
||||||
|
#endif
|
||||||
|
|
||||||
d->createContent();
|
d->createContent();
|
||||||
// Default window geometry - center on screen
|
// Default window geometry - center on screen
|
||||||
@@ -631,6 +702,9 @@ CMainWindow::~CMainWindow()
|
|||||||
void CMainWindow::closeEvent(QCloseEvent* event)
|
void CMainWindow::closeEvent(QCloseEvent* event)
|
||||||
{
|
{
|
||||||
d->saveState();
|
d->saveState();
|
||||||
|
// Delete dock manager here to delete all floating widgets. This ensures
|
||||||
|
// that all top level windows of the dock manager are properly closed
|
||||||
|
d->DockManager->deleteLater();
|
||||||
QMainWindow::closeEvent(event);
|
QMainWindow::closeEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -703,19 +777,51 @@ void CMainWindow::createEditor()
|
|||||||
QObject* Sender = sender();
|
QObject* Sender = sender();
|
||||||
QVariant vFloating = Sender->property("Floating");
|
QVariant vFloating = Sender->property("Floating");
|
||||||
bool Floating = vFloating.isValid() ? vFloating.toBool() : true;
|
bool Floating = vFloating.isValid() ? vFloating.toBool() : true;
|
||||||
|
QVariant vTabbed = Sender->property("Tabbed");
|
||||||
|
bool Tabbed = vTabbed.isValid() ? vTabbed.toBool() : true;
|
||||||
auto DockWidget = d->createEditorWidget();
|
auto DockWidget = d->createEditorWidget();
|
||||||
DockWidget->setFeature(ads::CDockWidget::DockWidgetDeleteOnClose, true);
|
DockWidget->setFeature(ads::CDockWidget::DockWidgetDeleteOnClose, true);
|
||||||
|
DockWidget->setFeature(ads::CDockWidget::DockWidgetForceCloseWithArea, true);
|
||||||
connect(DockWidget, SIGNAL(closeRequested()), SLOT(onEditorCloseRequested()));
|
connect(DockWidget, SIGNAL(closeRequested()), SLOT(onEditorCloseRequested()));
|
||||||
|
|
||||||
if (Floating)
|
if (Floating)
|
||||||
{
|
{
|
||||||
auto FloatingWidget = d->DockManager->addDockWidgetFloating(DockWidget);
|
auto FloatingWidget = d->DockManager->addDockWidgetFloating(DockWidget);
|
||||||
FloatingWidget->move(QPoint(20, 20));
|
FloatingWidget->move(QPoint(20, 20));
|
||||||
|
d->LastCreatedFloatingEditor = DockWidget;
|
||||||
|
d->LastDockedEditor.clear();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
d->DockManager->addDockWidget(ads::TopDockWidgetArea, DockWidget);
|
ads::CDockAreaWidget* EditorArea = d->LastDockedEditor ? d->LastDockedEditor->dockAreaWidget() : nullptr;
|
||||||
}
|
if (EditorArea)
|
||||||
|
{
|
||||||
|
if (Tabbed)
|
||||||
|
{
|
||||||
|
// Test inserting the dock widget tab at a given position instead
|
||||||
|
// of appending it. This function inserts the new dock widget as
|
||||||
|
// first tab
|
||||||
|
d->DockManager->addDockWidgetTabToArea(DockWidget, EditorArea, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
d->DockManager->setConfigFlag(ads::CDockManager::EqualSplitOnInsertion, true);
|
||||||
|
d->DockManager->addDockWidget(ads::RightDockWidgetArea, DockWidget, EditorArea);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (d->LastCreatedFloatingEditor)
|
||||||
|
{
|
||||||
|
d->DockManager->addDockWidget(ads::RightDockWidgetArea, DockWidget, d->LastCreatedFloatingEditor->dockAreaWidget());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
d->DockManager->addDockWidget(ads::TopDockWidgetArea, DockWidget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d->LastDockedEditor = DockWidget;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,12 @@ TARGET = AdvancedDockingSystemDemo
|
|||||||
DESTDIR = $${ADS_OUT_ROOT}/lib
|
DESTDIR = $${ADS_OUT_ROOT}/lib
|
||||||
QT += core gui widgets
|
QT += core gui widgets
|
||||||
|
|
||||||
win32 {
|
include(../ads.pri)
|
||||||
QT += axcontainer
|
|
||||||
|
lessThan(QT_MAJOR_VERSION, 6) {
|
||||||
|
win32 {
|
||||||
|
QT += axcontainer
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CONFIG += c++14
|
CONFIG += c++14
|
||||||
@@ -34,21 +38,6 @@ RESOURCES += demo.qrc
|
|||||||
|
|
||||||
LIBS += -L$${ADS_OUT_ROOT}/lib
|
LIBS += -L$${ADS_OUT_ROOT}/lib
|
||||||
|
|
||||||
# Dependency: AdvancedDockingSystem (shared)
|
|
||||||
CONFIG(debug, debug|release){
|
|
||||||
win32 {
|
|
||||||
LIBS += -lqtadvanceddockingd
|
|
||||||
}
|
|
||||||
else:mac {
|
|
||||||
LIBS += -lqtadvanceddocking_debug
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
LIBS += -lqtadvanceddocking
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
LIBS += -lqtadvanceddocking
|
|
||||||
}
|
|
||||||
|
|
||||||
INCLUDEPATH += ../src
|
INCLUDEPATH += ../src
|
||||||
DEPENDPATH += ../src
|
DEPENDPATH += ../src
|
||||||
|
|||||||
@@ -17,5 +17,6 @@
|
|||||||
<file>images/create_floating_editor.svg</file>
|
<file>images/create_floating_editor.svg</file>
|
||||||
<file>images/create_floating_table.svg</file>
|
<file>images/create_floating_table.svg</file>
|
||||||
<file>images/docked_editor.svg</file>
|
<file>images/docked_editor.svg</file>
|
||||||
|
<file>images/tab.svg</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
6
demo/images/tab.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0,0,1024,1024">
|
||||||
|
<desc>tab icon - Licensed under Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0) - Created with Iconfu.com - Derivative work of Material icons (Copyright Google Inc.)</desc>
|
||||||
|
<g fill="#03b8e5" fill-rule="nonzero" style="mix-blend-mode: normal">
|
||||||
|
<path d="M981.33,213.33v597.34c0,46.93 -38.4,85.33 -85.33,85.33h-768c-46.93,0 -85.33,-38.4 -85.33,-85.33v-597.34c0,-46.93 38.4,-85.33 85.33,-85.33h768c46.93,0 85.33,38.4 85.33,85.33zM896,384h-341.33v-170.67h-426.67v597.34h768z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 628 B |
@@ -35,9 +35,11 @@ void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QS
|
|||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||||
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||||
#if QT_VERSION >= 0x050600
|
#if QT_VERSION >= 0x050600
|
||||||
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
std::shared_ptr<int> b;
|
std::shared_ptr<int> b;
|
||||||
QApplication a(argc, argv);
|
QApplication a(argc, argv);
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ def append_feature_string_to_window_title(dock_widget: QtAds.CDockWidget):
|
|||||||
def svg_icon(filename: str):
|
def svg_icon(filename: str):
|
||||||
'''Helper function to create an SVG icon'''
|
'''Helper function to create an SVG icon'''
|
||||||
# This is a workaround, because because in item views SVG icons are not
|
# This is a workaround, because because in item views SVG icons are not
|
||||||
# properly scaled an look blurry or pixelate
|
# properly scaled and look blurry or pixelate
|
||||||
icon = QIcon(filename)
|
icon = QIcon(filename)
|
||||||
icon.addPixmap(icon.pixmap(92))
|
icon.addPixmap(icon.pixmap(92))
|
||||||
return icon
|
return icon
|
||||||
@@ -104,6 +104,8 @@ class MainWindow(MainWindowUI, MainWindowBase):
|
|||||||
self.perspective_list_action = None
|
self.perspective_list_action = None
|
||||||
self.perspective_combo_box = None
|
self.perspective_combo_box = None
|
||||||
self.dock_manager = None
|
self.dock_manager = None
|
||||||
|
self.window_title_test_dock_widget = None
|
||||||
|
self.last_docked_editor = None
|
||||||
|
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
self.create_actions()
|
self.create_actions()
|
||||||
@@ -184,6 +186,7 @@ class MainWindow(MainWindowUI, MainWindowBase):
|
|||||||
# special_dock_area.setAllowedAreas(QtAds.LeftDockWidgetArea | QtAds.RightDockWidgetArea) # just for testing
|
# special_dock_area.setAllowedAreas(QtAds.LeftDockWidgetArea | QtAds.RightDockWidgetArea) # just for testing
|
||||||
|
|
||||||
dock_widget = self.create_long_text_label_dock_widget()
|
dock_widget = self.create_long_text_label_dock_widget()
|
||||||
|
self.window_title_test_dock_widget = dock_widget
|
||||||
dock_widget.setFeature(QtAds.CDockWidget.DockWidgetFocusable, False)
|
dock_widget.setFeature(QtAds.CDockWidget.DockWidgetFocusable, False)
|
||||||
self.dock_manager.addDockWidget(QtAds.LeftDockWidgetArea, dock_widget)
|
self.dock_manager.addDockWidget(QtAds.LeftDockWidgetArea, dock_widget)
|
||||||
file_system_widget = self.create_file_system_tree_dock_widget()
|
file_system_widget = self.create_file_system_tree_dock_widget()
|
||||||
@@ -202,6 +205,9 @@ class MainWindow(MainWindowUI, MainWindowBase):
|
|||||||
# Test custom factory - we inject a help button into the title bar
|
# Test custom factory - we inject a help button into the title bar
|
||||||
QtAds.CDockComponentsFactory.setFactory(CCustomComponentsFactory())
|
QtAds.CDockComponentsFactory.setFactory(CCustomComponentsFactory())
|
||||||
top_dock_area = self.dock_manager.addDockWidget(QtAds.TopDockWidgetArea, file_system_widget)
|
top_dock_area = self.dock_manager.addDockWidget(QtAds.TopDockWidgetArea, file_system_widget)
|
||||||
|
# Uncomment the next line if you would like to test the
|
||||||
|
# HideSingleWidgetTitleBar functionality
|
||||||
|
# top_dock_area.setDockAreaFlag(QtAds.CDockAreaWidget.HideSingleWidgetTitleBar, True)
|
||||||
QtAds.CDockComponentsFactory.resetDefaultFactory()
|
QtAds.CDockComponentsFactory.resetDefaultFactory()
|
||||||
|
|
||||||
# We create a calendar widget and clear all flags to prevent the dock area
|
# We create a calendar widget and clear all flags to prevent the dock area
|
||||||
@@ -209,6 +215,17 @@ class MainWindow(MainWindowUI, MainWindowBase):
|
|||||||
dock_widget = self.create_calendar_dock_widget()
|
dock_widget = self.create_calendar_dock_widget()
|
||||||
dock_widget.setTabToolTip("Tab ToolTip\nHodie est dies magna")
|
dock_widget.setTabToolTip("Tab ToolTip\nHodie est dies magna")
|
||||||
dock_area = self.dock_manager.addDockWidget(QtAds.CenterDockWidgetArea, dock_widget, top_dock_area)
|
dock_area = self.dock_manager.addDockWidget(QtAds.CenterDockWidgetArea, dock_widget, top_dock_area)
|
||||||
|
# Now we create a action to test resizing of DockArea widget
|
||||||
|
action = self.menuTests.addAction("Resize {}".format(dock_widget.windowTitle()))
|
||||||
|
def action_triggered():
|
||||||
|
splitter = QtAds.internal.findParent(QtAds.CDockSplitter, dock_area)
|
||||||
|
if not splitter:
|
||||||
|
return
|
||||||
|
# We change the sizes of the splitter that contains the Calendar 1 widget
|
||||||
|
# to resize the dock widget
|
||||||
|
width = splitter.width()
|
||||||
|
splitter.setSizes([width * 2/3, width * 1/3])
|
||||||
|
action.triggered.connect(action_triggered)
|
||||||
|
|
||||||
# Now we add a custom button to the dock area title bar that will create
|
# Now we add a custom button to the dock area title bar that will create
|
||||||
# new editor widgets when clicked
|
# new editor widgets when clicked
|
||||||
@@ -310,6 +327,7 @@ class MainWindow(MainWindowUI, MainWindowBase):
|
|||||||
|
|
||||||
def closeEvent(self, event: QCloseEvent):
|
def closeEvent(self, event: QCloseEvent):
|
||||||
self.save_state()
|
self.save_state()
|
||||||
|
self.dock_manager.deleteLater()
|
||||||
super().closeEvent(event)
|
super().closeEvent(event)
|
||||||
|
|
||||||
def on_actionSaveState_triggered(self, state: bool):
|
def on_actionSaveState_triggered(self, state: bool):
|
||||||
@@ -350,16 +368,22 @@ class MainWindow(MainWindowUI, MainWindowBase):
|
|||||||
def create_editor(self):
|
def create_editor(self):
|
||||||
sender = self.sender()
|
sender = self.sender()
|
||||||
floating = sender.property("Floating")
|
floating = sender.property("Floating")
|
||||||
print("Floating:", floating)
|
|
||||||
dock_widget = self.create_editor_widget()
|
dock_widget = self.create_editor_widget()
|
||||||
dock_widget.setFeature(QtAds.CDockWidget.DockWidgetDeleteOnClose, True)
|
dock_widget.setFeature(QtAds.CDockWidget.DockWidgetDeleteOnClose, True)
|
||||||
|
dock_widget.setFeature(QtAds.CDockWidget.DockWidgetForceCloseWithArea, True)
|
||||||
dock_widget.closeRequested.connect(self.on_editor_close_requested)
|
dock_widget.closeRequested.connect(self.on_editor_close_requested)
|
||||||
|
|
||||||
if floating:
|
if floating:
|
||||||
floating_widget = self.dock_manager.addDockWidgetFloating(dock_widget)
|
floating_widget = self.dock_manager.addDockWidgetFloating(dock_widget)
|
||||||
floating_widget.move(QPoint(20, 20))
|
floating_widget.move(QPoint(20, 20))
|
||||||
else:
|
else:
|
||||||
self.dock_manager.addDockWidget(QtAds.TopDockWidgetArea, dock_widget)
|
editor_area = self.last_docked_editor.dockAreaWidget() if self.last_docked_editor is not None else None
|
||||||
|
if editor_area is not None:
|
||||||
|
self.dock_manager.setConfigFlag(QtAds.CDockManager.EqualSplitOnInsertion, True)
|
||||||
|
self.dock_manager.addDockWidget(QtAds.RightDockWidgetArea, dock_widget, editor_area)
|
||||||
|
else:
|
||||||
|
self.dock_manager.addDockWidget(QtAds.TopDockWidgetArea, dock_widget)
|
||||||
|
self.last_docked_editor = dock_widget
|
||||||
|
|
||||||
def on_editor_close_requested(self):
|
def on_editor_close_requested(self):
|
||||||
dock_widget = self.sender()
|
dock_widget = self.sender()
|
||||||
@@ -378,6 +402,16 @@ class MainWindow(MainWindowUI, MainWindowBase):
|
|||||||
dialog = CStatusDialog(self.dock_manager)
|
dialog = CStatusDialog(self.dock_manager)
|
||||||
dialog.exec_()
|
dialog.exec_()
|
||||||
|
|
||||||
|
|
||||||
|
def toggle_dock_widget_window_title(self):
|
||||||
|
title = self.window_title_test_dock_widget.windowTitle()
|
||||||
|
i = title.find(" (Test) ")
|
||||||
|
if i == -1:
|
||||||
|
title += " (Test) "
|
||||||
|
else:
|
||||||
|
title = title[i]
|
||||||
|
self.window_title_test_dock_widget.setWindowTitle(title)
|
||||||
|
|
||||||
def save_state(self):
|
def save_state(self):
|
||||||
'''
|
'''
|
||||||
Saves the dock manager state and the main window geometry
|
Saves the dock manager state and the main window geometry
|
||||||
168
doc/ads_icon.svg
@@ -1,123 +1,77 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<svg
|
<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"
|
version="1.1"
|
||||||
viewBox="0,0,1024,1024"
|
viewBox="0 0 1023.99 1023.99"
|
||||||
id="svg1145"
|
id="svg26"
|
||||||
sodipodi:docname="ads_icon.svg"
|
sodipodi:docname="ads_icon.svg"
|
||||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
|
||||||
inkscape:export-filename="C:\CodingXP\cetoni_projects\QtAdvancedDockingSystem\doc\ads_icon_256.png"
|
width="1023.99"
|
||||||
inkscape:export-xdpi="24"
|
height="1023.99"
|
||||||
inkscape:export-ydpi="24">
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
<metadata
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
id="metadata1151">
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
<rdf:RDF>
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title />
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<defs
|
<defs
|
||||||
id="defs1149" />
|
id="defs30" />
|
||||||
<sodipodi:namedview
|
<sodipodi:namedview
|
||||||
|
id="namedview28"
|
||||||
pagecolor="#ffffff"
|
pagecolor="#ffffff"
|
||||||
bordercolor="#666666"
|
bordercolor="#666666"
|
||||||
borderopacity="1"
|
borderopacity="1.0"
|
||||||
objecttolerance="10"
|
|
||||||
gridtolerance="10"
|
|
||||||
guidetolerance="10"
|
|
||||||
inkscape:pageopacity="0"
|
|
||||||
inkscape:pageshadow="2"
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
showgrid="false"
|
||||||
|
showguides="true"
|
||||||
|
inkscape:guide-bbox="true"
|
||||||
|
inkscape:zoom="0.12999302"
|
||||||
|
inkscape:cx="3277.099"
|
||||||
|
inkscape:cy="-257.70614"
|
||||||
inkscape:window-width="1920"
|
inkscape:window-width="1920"
|
||||||
inkscape:window-height="1017"
|
inkscape:window-height="1017"
|
||||||
id="namedview1147"
|
|
||||||
showgrid="false"
|
|
||||||
inkscape:zoom="0.32593202"
|
|
||||||
inkscape:cx="256.13402"
|
|
||||||
inkscape:cy="235.82602"
|
|
||||||
inkscape:window-x="1912"
|
inkscape:window-x="1912"
|
||||||
inkscape:window-y="-8"
|
inkscape:window-y="-8"
|
||||||
inkscape:window-maximized="1"
|
inkscape:window-maximized="1"
|
||||||
inkscape:current-layer="svg1145" />
|
inkscape:current-layer="svg26"
|
||||||
|
fit-margin-top="0"
|
||||||
|
fit-margin-left="0"
|
||||||
|
fit-margin-right="0"
|
||||||
|
fit-margin-bottom="0" />
|
||||||
<desc
|
<desc
|
||||||
id="desc1129">window_size icon - Licensed under Iconfu Standard License v1.0 (https://www.iconfu.com/iconfu_standard_license) - Incors GmbH</desc>
|
id="desc2">electric_iron icon - Licensed under Iconfu Standard License v1.0 (https://www.iconfu.com/iconfu_standard_license) - Incors GmbH</desc>
|
||||||
<path
|
<text
|
||||||
id="path1797-1"
|
xml:space="preserve"
|
||||||
visibility="hidden"
|
style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none"
|
||||||
d="M 71.53142,291.89723 H 950.1828 l 0.6743,0.6742 v 586.08 l -0.6743,0.6743 H 71.53142 l -0.6743,-0.6743 v -586.08 z"
|
x="1251.1022"
|
||||||
inkscape:connector-curvature="0"
|
y="1305.4956"
|
||||||
style="visibility:hidden;mix-blend-mode:normal;fill:#ffffff;fill-rule:nonzero;stroke-width:0.99999988;fill-opacity:1" />
|
id="text9788"><tspan
|
||||||
<rect
|
sodipodi:role="line"
|
||||||
transform="rotate(-90)"
|
id="tspan9786"
|
||||||
y="412.13489"
|
x="1251.1022"
|
||||||
x="-911.34436"
|
y="1305.4956" /></text>
|
||||||
height="560.90375"
|
<g
|
||||||
width="327.70862"
|
id="g94691"
|
||||||
id="rect1739-8"
|
transform="translate(581.23034,1750.5233)">
|
||||||
style="opacity:1;fill:#ffd292;fill-opacity:1;stroke:none;stroke-width:37.79526901;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke" />
|
<path
|
||||||
<path
|
d="m 191.63966,-726.53328 h -521.75 c -138.69,0 -251.12,-112.43001 -251.12,-251.12 v -521.75002 c 0,-138.69 112.43,-251.12 251.12,-251.12 h 521.75 c 138.69,0 251.12,112.43 251.12,251.12 v 521.75002 c 0,138.68999 -112.43,251.12 -251.12,251.12 z"
|
||||||
id="path1799"
|
fill="#707070"
|
||||||
d="M 988.40121,578.11175 V 651.2546 H 409.96541 V 578.11175 Z"
|
id="path4-5"
|
||||||
inkscape:connector-curvature="0"
|
style="mix-blend-mode:normal;fill:#e0e0e0;fill-opacity:1;fill-rule:nonzero" />
|
||||||
style="mix-blend-mode:normal;fill:#546e7a;fill-rule:nonzero;stroke-width:0.99999988" />
|
<path
|
||||||
<rect
|
id="path927"
|
||||||
style="opacity:1;fill:#b3c3cb;fill-opacity:1;stroke:none;stroke-width:37.79526901;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
style="mix-blend-mode:normal;fill:#009ddd;fill-opacity:1;fill-rule:nonzero"
|
||||||
id="rect1739-6-1"
|
d="m -175.90039,-1515.8633 v 256 h 469.3301 v -256 z" />
|
||||||
width="655.41724"
|
<path
|
||||||
height="338.99207"
|
id="path16513"
|
||||||
x="-914.85529"
|
style="mix-blend-mode:normal;fill:#ff9833;fill-opacity:1;fill-rule:nonzero"
|
||||||
y="38.243896"
|
d="m 80.099609,-1217.1934 v 256.00004 H 293.42969 v -256.00004 z" />
|
||||||
transform="rotate(-90)" />
|
<path
|
||||||
<path
|
id="path16513-5"
|
||||||
id="path1801-7"
|
style="mix-blend-mode:normal;fill:#accb01;fill-opacity:1;fill-rule:nonzero"
|
||||||
d="m 73.1429,73.14287 h 877.7142 c 40.2857,0 73.1429,32.86857 73.1429,73.14285 V 914.2857 c 0,20.12571 -16.4458,36.57142 -36.5715,36.57142 H 36.5714 C 16.4343,950.85712 0,934.42284 0,914.2857 V 146.28572 C 0,106.00001 32.8571,73.14287 73.1429,73.14287 Z m 0,219.42856 V 877.71427 H 950.8571 V 292.57143 Z"
|
d="m -175.90039,-1217.1933 v 256 H 37.42969 v -256 z" />
|
||||||
inkscape:connector-curvature="0"
|
<path
|
||||||
style="mix-blend-mode:normal;fill:#546e7a;fill-rule:nonzero;stroke-width:0.99999988" />
|
id="path24788"
|
||||||
<path
|
style="mix-blend-mode:normal;fill:#0083c3;fill-opacity:1;fill-rule:nonzero"
|
||||||
id="path1799-6"
|
d="m -431.90039,-1515.8633 v 554.66994 h 213.33008 v -554.66994 z" />
|
||||||
d="M 359.8801,276.79402 H 433.023 V 886.78767 H 359.8801 Z"
|
</g>
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
style="mix-blend-mode:normal;fill:#546e7a;fill-rule:nonzero;stroke-width:0.99999988" />
|
|
||||||
<circle
|
|
||||||
r="36.81749"
|
|
||||||
cy="169.78555"
|
|
||||||
cx="920.66248"
|
|
||||||
id="path1917-42"
|
|
||||||
style="opacity:1;fill:#ffa726;fill-opacity:1;stroke:none;stroke-width:63.79999924;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers" />
|
|
||||||
<circle
|
|
||||||
r="36.81749"
|
|
||||||
cy="169.78555"
|
|
||||||
cx="817.22302"
|
|
||||||
id="path1917-4-6"
|
|
||||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:63.79999924;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers" />
|
|
||||||
<path
|
|
||||||
id="path1799-8"
|
|
||||||
d="m 615.67076,292.13464 v 73.14285 H 433.023 v -73.14285 z"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
style="mix-blend-mode:normal;fill:#95abb6;fill-rule:nonzero;stroke-width:0.99999994;fill-opacity:1" />
|
|
||||||
<path
|
|
||||||
id="path1799-8-1"
|
|
||||||
d="m 798.31852,292.13464 v 73.14285 H 615.67076 v -73.14285 z"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
style="mix-blend-mode:normal;fill:#c7d4d9;fill-rule:nonzero;stroke-width:0.99999994;fill-opacity:1" />
|
|
||||||
<path
|
|
||||||
id="path1799-8-7"
|
|
||||||
d="m 255.79066,292.57143 v 73.14285 H 73.1429 v -73.14285 z"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
style="mix-blend-mode:normal;fill:#dfe5e9;fill-opacity:1;fill-rule:nonzero;stroke-width:0.99999994" />
|
|
||||||
<path
|
|
||||||
id="path1799-8-7-6"
|
|
||||||
d="m 616.34083,651.15739 v 73.14285 H 433.69307 v -73.14285 z"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
style="mix-blend-mode:normal;fill:#ffba56;fill-opacity:1;fill-rule:nonzero;stroke-width:0.99999994" />
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 2.8 KiB |
88
doc/ads_logo.svg
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
version="1.1"
|
||||||
|
viewBox="0 0 6907.3028 1023.99"
|
||||||
|
id="svg26"
|
||||||
|
sodipodi:docname="ads_logo.svg"
|
||||||
|
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
|
||||||
|
width="6907.3027"
|
||||||
|
height="1023.99"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<defs
|
||||||
|
id="defs30" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview28"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
showgrid="false"
|
||||||
|
showguides="true"
|
||||||
|
inkscape:guide-bbox="true"
|
||||||
|
inkscape:zoom="0.12999302"
|
||||||
|
inkscape:cx="3277.099"
|
||||||
|
inkscape:cy="-257.70614"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1017"
|
||||||
|
inkscape:window-x="1912"
|
||||||
|
inkscape:window-y="-8"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg26"
|
||||||
|
fit-margin-top="0"
|
||||||
|
fit-margin-left="0"
|
||||||
|
fit-margin-right="0"
|
||||||
|
fit-margin-bottom="0" />
|
||||||
|
<desc
|
||||||
|
id="desc2">electric_iron icon - Licensed under Iconfu Standard License v1.0 (https://www.iconfu.com/iconfu_standard_license) - Incors GmbH</desc>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none"
|
||||||
|
x="1251.1022"
|
||||||
|
y="1305.4956"
|
||||||
|
id="text9788"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan9786"
|
||||||
|
x="1251.1022"
|
||||||
|
y="1305.4956" /></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:628.003px;line-height:1.25;font-family:sans-serif;fill:#8f918f;fill-opacity:1;stroke:none;stroke-width:1"
|
||||||
|
x="1178.9221"
|
||||||
|
y="718.37329"
|
||||||
|
id="text15608"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan15606"
|
||||||
|
x="1178.9221"
|
||||||
|
y="718.37329"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:628.003px;font-family:'Segoe UI';-inkscape-font-specification:'Segoe UI Light';fill:#8f918f;fill-opacity:1;stroke-width:1">Qt Advanced Docking</tspan></text>
|
||||||
|
<g
|
||||||
|
id="g94691"
|
||||||
|
transform="translate(581.23034,1750.5233)">
|
||||||
|
<path
|
||||||
|
d="m 191.63966,-726.53328 h -521.75 c -138.69,0 -251.12,-112.43001 -251.12,-251.12 v -521.75002 c 0,-138.69 112.43,-251.12 251.12,-251.12 h 521.75 c 138.69,0 251.12,112.43 251.12,251.12 v 521.75002 c 0,138.68999 -112.43,251.12 -251.12,251.12 z"
|
||||||
|
fill="#707070"
|
||||||
|
id="path4-5"
|
||||||
|
style="mix-blend-mode:normal;fill:#e0e0e0;fill-opacity:1;fill-rule:nonzero" />
|
||||||
|
<path
|
||||||
|
id="path927"
|
||||||
|
style="mix-blend-mode:normal;fill:#009ddd;fill-opacity:1;fill-rule:nonzero"
|
||||||
|
d="m -175.90039,-1515.8633 v 256 h 469.3301 v -256 z" />
|
||||||
|
<path
|
||||||
|
id="path16513"
|
||||||
|
style="mix-blend-mode:normal;fill:#ff9833;fill-opacity:1;fill-rule:nonzero"
|
||||||
|
d="m 80.099609,-1217.1934 v 256.00004 H 293.42969 v -256.00004 z" />
|
||||||
|
<path
|
||||||
|
id="path16513-5"
|
||||||
|
style="mix-blend-mode:normal;fill:#accb01;fill-opacity:1;fill-rule:nonzero"
|
||||||
|
d="m -175.90039,-1217.1933 v 256 H 37.42969 v -256 z" />
|
||||||
|
<path
|
||||||
|
id="path24788"
|
||||||
|
style="mix-blend-mode:normal;fill:#0083c3;fill-opacity:1;fill-rule:nonzero"
|
||||||
|
d="m -431.90039,-1515.8633 v 554.66994 h 213.33008 v -554.66994 z" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.4 KiB |
@@ -12,20 +12,6 @@ styles as much as possible.
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
### Overview
|
|
||||||
|
|
||||||
- [Features](#features)
|
|
||||||
- [Overview](#overview)
|
|
||||||
- [Docking everywhere - no central widget](#docking-everywhere---no-central-widget)
|
|
||||||
- [Docking inside floating windows](#docking-inside-floating-windows)
|
|
||||||
- [Grouped dragging](#grouped-dragging)
|
|
||||||
- [Perspectives for fast switching of the complete main window layout](#perspectives-for-fast-switching-of-the-complete-main-window-layout)
|
|
||||||
- [Opaque and non-opaque splitter resizing](#opaque-and-non-opaque-splitter-resizing)
|
|
||||||
- [Opaque and non-opaque undocking](#opaque-and-non-opaque-undocking)
|
|
||||||
- [Tab-menu for easy handling of many tabbed dock widgets](#tab-menu-for-easy-handling-of-many-tabbed-dock-widgets)
|
|
||||||
- [Many different ways to detach dock widgets](#many-different-ways-to-detach-dock-widgets)
|
|
||||||
- [Supports deletion of dynamically created dock widgets](#supports-deletion-of-dynamically-created-dock-widgets)
|
|
||||||
|
|
||||||
### Docking everywhere - no central widget
|
### Docking everywhere - no central widget
|
||||||
|
|
||||||
There is no central widget like in the Qt docking system. You can dock on every
|
There is no central widget like in the Qt docking system. You can dock on every
|
||||||
@@ -106,3 +92,10 @@ You can detach dock widgets and also dock areas in the following ways:
|
|||||||
### Supports deletion of dynamically created dock widgets
|
### Supports deletion of dynamically created dock widgets
|
||||||
|
|
||||||
Normally clicking the close button of a dock widget will just hide the widget and the user can show it again using the toggleView() action of the dock widget. This is meant for user interfaces with a static amount of widgets. But the advanced docking system also supports dynamic dock widgets that will get deleted on close. If you set the dock widget flag `DockWidgetDeleteOnClose` for a certain dock widget, then it will be deleted as soon as you close this dock widget. This enables the implementation of user interfaces with dynamically created editors, like in word processing applications or source code development tools.
|
Normally clicking the close button of a dock widget will just hide the widget and the user can show it again using the toggleView() action of the dock widget. This is meant for user interfaces with a static amount of widgets. But the advanced docking system also supports dynamic dock widgets that will get deleted on close. If you set the dock widget flag `DockWidgetDeleteOnClose` for a certain dock widget, then it will be deleted as soon as you close this dock widget. This enables the implementation of user interfaces with dynamically created editors, like in word processing applications or source code development tools.
|
||||||
|
|
||||||
|
### Python PyQt5 Bindings
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The Advanced Docking System comes with a complete Python integration based on
|
||||||
|
PyQt5 bindings. The package is available via [conda-forge](https://github.com/conda-forge/pyqtads-feedstock).
|
||||||
|
|||||||
@@ -4,21 +4,21 @@
|
|||||||
"extensionType": [
|
"extensionType": [
|
||||||
"library"
|
"library"
|
||||||
],
|
],
|
||||||
"version": "3.0.0",
|
"version": "3.8.2",
|
||||||
"vendor": {
|
"vendor": {
|
||||||
"name": "githubuser0xFFFF",
|
"name": "githubuser0xFFFF",
|
||||||
"url": "https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System"
|
"url": "https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System"
|
||||||
},
|
},
|
||||||
"contact": "https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/issues",
|
"contact": "https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/issues",
|
||||||
"icon": "https://raw.githubusercontent.com/githubuser0xFFFF/Qt-Advanced-Docking-System/master/doc/ads_icon_512.png",
|
"icon": "https://raw.githubusercontent.com/githubuser0xFFFF/Qt-Advanced-Docking-System/master/doc/ads_icon.svg",
|
||||||
"licenses": [
|
"licenses": [
|
||||||
{ "licenseType": "LGPLv2.1",
|
{ "licenseType": "LGPL-2.1-only",
|
||||||
"licenseUrl": "https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html" }
|
"licenseUrl": "https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html" }
|
||||||
],
|
],
|
||||||
"created": "2017-03-30",
|
"created": "2017-03-30",
|
||||||
"lastUpdate": "2020-01-16",
|
"lastUpdate": "2022-03-02",
|
||||||
"platforms": [
|
"platforms": [
|
||||||
"Windows 7-10", "Kubuntu 18.04", "Kubuntu 19.10", "Ubuntu 19.10"
|
"Windows 7-11", "Kubuntu 18.04", "Kubuntu 19.10", "Ubuntu 19.10", "Ubuntu 20.04"
|
||||||
],
|
],
|
||||||
"qtVersions": [
|
"qtVersions": [
|
||||||
"5.5.1 or newer"
|
"5.5.1 or newer"
|
||||||
|
|||||||
BIN
doc/central_widget.gif
Normal file
|
After Width: | Height: | Size: 572 KiB |
BIN
doc/cfg_flag_EqualSplitOnInsertion_false.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
doc/cfg_flag_EqualSplitOnInsertion_true.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
doc/cfg_flag_FloatingContainerForceNativeTitleBar_false.png
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
BIN
doc/cfg_flag_FloatingContainerForceNativeTitleBar_true.png
Normal file
|
After Width: | Height: | Size: 9.3 KiB |
BIN
doc/cfg_flag_MiddleMouseButtonClosesTab.gif
Normal file
|
After Width: | Height: | Size: 257 KiB |
|
Before Width: | Height: | Size: 539 KiB |
BIN
doc/showcase_d-tect-x.jpg
Normal file
|
After Width: | Height: | Size: 97 KiB |
BIN
doc/showcase_d-tect-x.png
Normal file
|
After Width: | Height: | Size: 489 KiB |
|
Before Width: | Height: | Size: 385 KiB After Width: | Height: | Size: 385 KiB |
BIN
doc/showcase_hivewe.png
Normal file
|
After Width: | Height: | Size: 208 KiB |
BIN
doc/showcase_metgem.png
Normal file
|
After Width: | Height: | Size: 168 KiB |
BIN
doc/showcase_notepad_next.png
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
doc/showcase_plot_juggler.png
Normal file
|
After Width: | Height: | Size: 264 KiB |
BIN
doc/showcase_pre_workbench.png
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
doc/showcase_qmix_elements.png
Normal file
|
After Width: | Height: | Size: 529 KiB |
|
Before Width: | Height: | Size: 157 KiB After Width: | Height: | Size: 157 KiB |
BIN
doc/showcase_qt_design_studio_video.png
Normal file
|
After Width: | Height: | Size: 816 KiB |
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 98 KiB |
BIN
doc/showcase_ramses_composer.png
Normal file
|
After Width: | Height: | Size: 450 KiB |
BIN
doc/ukraine.jpg
Normal file
|
After Width: | Height: | Size: 115 KiB |
@@ -24,20 +24,38 @@
|
|||||||
- [`FloatingContainerHasWidgetIcon`](#floatingcontainerhaswidgeticon)
|
- [`FloatingContainerHasWidgetIcon`](#floatingcontainerhaswidgeticon)
|
||||||
- [`HideSingleCentralWidgetTitleBar`](#hidesinglecentralwidgettitlebar)
|
- [`HideSingleCentralWidgetTitleBar`](#hidesinglecentralwidgettitlebar)
|
||||||
- [`FocusHighlighting`](#focushighlighting)
|
- [`FocusHighlighting`](#focushighlighting)
|
||||||
|
- [`EqualSplitOnInsertion`](#equalsplitoninsertion)
|
||||||
|
- [`FloatingContainerForceNativeTitleBar` (Linux only)](#floatingcontainerforcenativetitlebar-linux-only)
|
||||||
|
- [`FloatingContainerForceQWidgetTitleBar` (Linux only)](#floatingcontainerforceqwidgettitlebar-linux-only)
|
||||||
|
- [`MiddleMouseButtonClosesTab`](#middlemousebuttonclosestab)
|
||||||
|
- [DockWidget Feature Flags](#dockwidget-feature-flags)
|
||||||
|
- [`DockWidgetClosable`](#dockwidgetclosable)
|
||||||
|
- [`DockWidgetMovable`](#dockwidgetmovable)
|
||||||
|
- [`DockWidgetFloatable`](#dockwidgetfloatable)
|
||||||
|
- [`DockWidgetDeleteOnClose`](#dockwidgetdeleteonclose)
|
||||||
|
- [`CustomCloseHandling`](#customclosehandling)
|
||||||
|
- [`DockWidgetFocusable`](#dockwidgetfocusable)
|
||||||
|
- [`DockWidgetForceCloseWithArea`](#dockwidgetforceclosewitharea)
|
||||||
|
- [`NoTab`](#notab)
|
||||||
|
- [`DeleteContentOnClose`](#deletecontentonclose)
|
||||||
|
- [Central Widget](#central-widget)
|
||||||
|
- [Empty Dock Area](#empty-dock-area)
|
||||||
|
- [Custom Close Handling](#custom-close-handling)
|
||||||
- [Styling](#styling)
|
- [Styling](#styling)
|
||||||
- [Disabling the Internal Style Sheet](#disabling-the-internal-style-sheet)
|
- [Disabling the Internal Style Sheet](#disabling-the-internal-style-sheet)
|
||||||
|
|
||||||
## Configuration Flags
|
## Configuration Flags
|
||||||
|
|
||||||
The Advanced Docking System has a number of global configuration options to
|
The Advanced Docking System has a number of global configuration options to
|
||||||
configure the design and the functionality of the docking system. Eachs
|
configure the design and the functionality of the docking system. Each
|
||||||
configuration will be explained in detail in the following sections.
|
configuration will be explained in detail in the following sections.
|
||||||
|
|
||||||
### Setting Configuration Flags
|
### Setting Configuration Flags
|
||||||
|
|
||||||
You should set the configuration flags before you create the dock manager
|
You must set the configuration flags before creating the dock manager
|
||||||
instance. That means, setting the configurations flags is the first thing
|
instance otherwise the manager will not be created correctly and will
|
||||||
you do, if you use the library.
|
crash upon being created. That means, setting the configurations flags
|
||||||
|
is the first thing you must do, if you use the library.
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
CDockManager::setConfigFlags(CDockManager::DefaultOpaqueConfig);
|
CDockManager::setConfigFlags(CDockManager::DefaultOpaqueConfig);
|
||||||
@@ -49,9 +67,9 @@ d->DockManager = new CDockManager(this);
|
|||||||
If you set the configurations flags, you can set individual flags using the
|
If you set the configurations flags, you can set individual flags using the
|
||||||
function `CDockManager::setConfigFlag` or you can set all flags using
|
function `CDockManager::setConfigFlag` or you can set all flags using
|
||||||
the function `CDockManager::setConfigFlags`. Instead of settings all
|
the function `CDockManager::setConfigFlags`. Instead of settings all
|
||||||
flags individualy, it is better to pick a predefined set of configuration
|
flags individually, it is better to pick a predefined set of configuration
|
||||||
flags and then modify individual flags. The following predefined
|
flags and then modify individual flags. The following predefined
|
||||||
configurations are avilable
|
configurations are available
|
||||||
|
|
||||||
- `DefaultNonOpaqueConfig` - uses non opaque splitter resizing and non opaque docking
|
- `DefaultNonOpaqueConfig` - uses non opaque splitter resizing and non opaque docking
|
||||||
- `DefaultOpaqueConfig` - uses opaque splitter resizing and opaque docking
|
- `DefaultOpaqueConfig` - uses opaque splitter resizing and opaque docking
|
||||||
@@ -142,11 +160,11 @@ constant, that means, if enabled, the tabs need more space.
|
|||||||
|
|
||||||
### `OpaqueUndocking`
|
### `OpaqueUndocking`
|
||||||
|
|
||||||
If this flag is set, opaque undocking is active. That means, as soon as you drag a dock widget or a dock area with a number of dock widgets it will be undocked and moved into a floating widget and then the floating widget will be dragged around. That means undocking will take place immediatelly. You can compare this with opaque splitter resizing.
|
If this flag is set, opaque undocking is active. That means, as soon as you drag a dock widget or a dock area with a number of dock widgets it will be undocked and moved into a floating widget and then the floating widget will be dragged around. That means undocking will take place immediately. You can compare this with opaque splitter resizing.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
If you would like to test opaque undocking, you should set the pedefined config
|
If you would like to test opaque undocking, you should set the predefined config
|
||||||
flags `CDockManager::DefaultOpaqueConfig`.
|
flags `CDockManager::DefaultOpaqueConfig`.
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
@@ -269,7 +287,8 @@ current dock widget.
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
otherwise it displays application name as window title.
|
otherwise it displays the title set with `CDockManager::setFloatingContainersTitle` or
|
||||||
|
application name as window title.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -305,7 +324,7 @@ still has a titlebar to drag it out of the main window.
|
|||||||
|
|
||||||
If this is enabled, the docking system is able to highlight the tab and the
|
If this is enabled, the docking system is able to highlight the tab and the
|
||||||
components of a dock area with a different style (i.e. a different color).
|
components of a dock area with a different style (i.e. a different color).
|
||||||
This option is disabled by default and needs to be enabled explicitely
|
This option is disabled by default and needs to be enabled explicitly
|
||||||
because it adds some overhead. The dock manager needs to react on focus
|
because it adds some overhead. The dock manager needs to react on focus
|
||||||
changes and dock widget dragging to highlight the right dock widget. You should
|
changes and dock widget dragging to highlight the right dock widget. You should
|
||||||
enable it only, if you really need it for your application.
|
enable it only, if you really need it for your application.
|
||||||
@@ -325,7 +344,7 @@ be set to true and you can use this property to style the focused dock
|
|||||||
widget differently. The picture above uses the following styling:
|
widget differently. The picture above uses the following styling:
|
||||||
|
|
||||||
```css
|
```css
|
||||||
/* Color the tab with the nhighlight color */
|
/* Color the tab with the highlight color */
|
||||||
ads--CDockWidgetTab[focused="true"]
|
ads--CDockWidgetTab[focused="true"]
|
||||||
{
|
{
|
||||||
background: palette(highlight);
|
background: palette(highlight);
|
||||||
@@ -409,6 +428,188 @@ bool CMainWindow::eventFilter(QObject *watched, QEvent *event)
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `EqualSplitOnInsertion`
|
||||||
|
|
||||||
|
This flag configures how the space is distributed if a new dock widget is
|
||||||
|
inserted into an existing dock area. The flag is disabled by default. If 3
|
||||||
|
dock widgets are inserted with the following code
|
||||||
|
|
||||||
|
```c++
|
||||||
|
d->DockManager->addDockWidget(ads::RightDockWidgetArea, DockWidget, EditorArea);
|
||||||
|
```
|
||||||
|
|
||||||
|
then this is the result, if the flag is disabled:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
If the flag is enabled, then the space is equally distributed to all widgets
|
||||||
|
in a splitter:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
### `FloatingContainerForceNativeTitleBar` (Linux only)
|
||||||
|
|
||||||
|
Since release 3.6 the library supports native title bars and window decorations
|
||||||
|
for floating widgets on Linux (thanks to a user contribution).
|
||||||
|
Native title bars and window decorations are supported by most Linux window
|
||||||
|
managers, such as Compiz or Xfwm. Some window managers like KWin do not properly
|
||||||
|
support this feature. Native floating widgets look better because of the native
|
||||||
|
styling and the support all window manager features like snapping to window
|
||||||
|
borders or maximizing. The library tries to detect the window manager during
|
||||||
|
runtime and activates native window decorations if possible:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
If you would like to overwrite this autodetection, then you can activate this
|
||||||
|
flag to force native window titlebars. You can overwrite autodetection and this
|
||||||
|
flag, if you set the environment variable `ADS_UseNativeTitle` to 0 or 1.
|
||||||
|
|
||||||
|
### `FloatingContainerForceQWidgetTitleBar` (Linux only)
|
||||||
|
|
||||||
|
If your window manager (i.e. KWin) does not properly support native floating
|
||||||
|
windows, the docking library falls back to QWidget based floating widget
|
||||||
|
title bars.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
If you would like to overwrite autodetection, then you can activate this flag
|
||||||
|
to force QWidget based title bars. You can overwrite autodetection and this
|
||||||
|
flag, if you set the environment variable `ADS_UseNativeTitle` to 0 or 1.
|
||||||
|
|
||||||
|
### `MiddleMouseButtonClosesTab`
|
||||||
|
|
||||||
|
If the flag is set, the user can use the mouse middle button to close the tab
|
||||||
|
under the mouse. So you do not need to exactly hit the tab close button to
|
||||||
|
close tab. Just click with the middle mouse button on a tab like this is
|
||||||
|
possible in various web browsers.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## DockWidget Feature Flags
|
||||||
|
|
||||||
|
### `DockWidgetClosable`
|
||||||
|
|
||||||
|
If set, the dock widget will have a close button.
|
||||||
|
|
||||||
|
### `DockWidgetMovable`
|
||||||
|
|
||||||
|
If a dock widget is movable, then it and can be moved to a new position in the
|
||||||
|
current dock container. Disable this flag to prevent moving of a dock widget
|
||||||
|
via mouse. If the `OpaqueUndocking` configuration flag is set, then dock widgets
|
||||||
|
are immediately undocked into floating widgets. That means, moving is only
|
||||||
|
possible in this case, if the dock widget is also floatable (feature flag
|
||||||
|
`DockWidgetFloatable` is set).
|
||||||
|
|
||||||
|
### `DockWidgetFloatable`
|
||||||
|
|
||||||
|
If set, a dock widget can be dragged into a floating window.
|
||||||
|
|
||||||
|
### `DockWidgetDeleteOnClose`
|
||||||
|
|
||||||
|
Deletes the dock widget and its content when it is closed.
|
||||||
|
|
||||||
|
### `CustomCloseHandling`
|
||||||
|
|
||||||
|
Clicking the close button will not close the dock widget but emits the
|
||||||
|
`closeRequested()` signal instead. This allows the application to implement
|
||||||
|
a custom close handling.
|
||||||
|
|
||||||
|
### `DockWidgetFocusable`
|
||||||
|
|
||||||
|
If this is enabled, a dock widget can get focus highlighting.
|
||||||
|
|
||||||
|
### `DockWidgetForceCloseWithArea`
|
||||||
|
|
||||||
|
A dock widget will be closed when the dock area hosting it is closed. If the
|
||||||
|
`DockWidgetDeleteOnClose` feature is enabled for a dock widget, then it will
|
||||||
|
be deleted, if the user clicks the close button of this dock widget. If the
|
||||||
|
user clicks the close button of the dock area that contains this widget,
|
||||||
|
then only the visibility of the dock widget is toggled. If this feature flag
|
||||||
|
is set, the closing the dock area also closes the dock widget. That means, if
|
||||||
|
the dock widget feature `DockWidgetDeleteOnClose` is set for the dock widgets
|
||||||
|
in a dock area, then all dock widgets will be deleted if the dock area is closed.
|
||||||
|
|
||||||
|
### `NoTab`
|
||||||
|
|
||||||
|
A dock widget tab will never be shown if this flag is set.
|
||||||
|
|
||||||
|
### `DeleteContentOnClose`
|
||||||
|
|
||||||
|
Deletes only the contained widget on close, keeping the dock widget intact and
|
||||||
|
in place. Attempts to rebuild the contents widget on show if there is a widget
|
||||||
|
factory set. See [issue #365](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/pull/365) for more details.
|
||||||
|
|
||||||
|
## Central Widget
|
||||||
|
|
||||||
|
The Advanced Docking System has been developed to overcome the limitations of
|
||||||
|
the native Qt docking system with its central widget concept. This was the
|
||||||
|
reason that until version 3.6 of the library, there was no support for such
|
||||||
|
thing like a central widget. Thanks to the contribution of a user the library
|
||||||
|
now supports a central widget.
|
||||||
|
|
||||||
|
In the Advanced Docking System a central widget is a docking widget that is
|
||||||
|
neither closable nor movable or floatable. A central widget has no title bar
|
||||||
|
and so it is not possible for the user to hide, close or drag the central
|
||||||
|
widget. If there is a central widget, then also the distribution of the sizes
|
||||||
|
for the dock widgets around the central widget is different:
|
||||||
|
|
||||||
|
- **no central widget (default)** - on resizing the available space is
|
||||||
|
distributed to all dock widgets - the size of all dock widgets
|
||||||
|
shrinks or grows
|
||||||
|
- **with central widget** - on resizing only the central widget is resized - the
|
||||||
|
dock widgets around the central widget keep their size (see the animation below)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
To set a central widget, you just need to pass your central dock widget
|
||||||
|
to the dock manager `setCentralWidget` function:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
auto* CentralDockArea = DockManager->setCentralWidget(CentralDockWidget);
|
||||||
|
```
|
||||||
|
|
||||||
|
See the `centralwidget` example to learn how it works.
|
||||||
|
|
||||||
|
> ##### Note
|
||||||
|
> The central widget needs to be the first dock widget that is added to the
|
||||||
|
> dock manager. The function does not work and returns a `nullptr` if there
|
||||||
|
> are already other dock widgets registered. So `setCentralWidget` should be
|
||||||
|
> the first function that you call when adding dock widgets.
|
||||||
|
|
||||||
|
## Empty Dock Area
|
||||||
|
|
||||||
|
Some applications require a fixed DockArea that is always visible, even if it
|
||||||
|
does not contain any DockWidgets. I.e. the DockArea is in this case a kind
|
||||||
|
of central widget that is always visible (see this
|
||||||
|
[issue](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/issues/199)).
|
||||||
|
|
||||||
|
Since version 3.7.1 the advanced docking system supports this feature. The
|
||||||
|
`emptydockarea` example shows how this can be implemented with the library. You
|
||||||
|
just need to create a dock widget and set the feature flag `CDockWidget::NoTab`.
|
||||||
|
This permanently hides the tab widget of this area and removes it from the tab
|
||||||
|
menu. For this special dock widget you should also disable all other features
|
||||||
|
(movable, closable and floatable) to prevent closing and moving of this widget.
|
||||||
|
If you use the `CDockManager::setCentralWidget` function like in the example
|
||||||
|
code below, then you don't need to disable these features because this is done
|
||||||
|
in the `setCentralWidget` function.
|
||||||
|
|
||||||
|
```c++
|
||||||
|
QLabel* label = new QLabel();
|
||||||
|
label->setText("This is a DockArea which is always visible, even if it does not contain any DockWidgets.");
|
||||||
|
label->setAlignment(Qt::AlignCenter);
|
||||||
|
CDockWidget* CentralDockWidget = new CDockWidget("CentralWidget");
|
||||||
|
CentralDockWidget->setWidget(label);
|
||||||
|
CentralDockWidget->setFeature(ads::CDockWidget::NoTab, true);// set the flag before adding the widget to dock manager
|
||||||
|
auto* CentralDockArea = DockManager->setCentralWidget(CentralDockWidget);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Custom Close Handling
|
||||||
|
|
||||||
|
Normally clicking the close button of a dock widget will just hide the widget and the user can show it again using the `toggleView()` action of the dock widget. This is meant for user interfaces with a static amount of widgets. But the advanced docking system also supports dynamic dock widgets that will get deleted on close. If you set the dock widget flag `DockWidgetDeleteOnClose` for a certain dock widget, then it will be deleted as soon as you close this dock widget. This enables the implementation of user interfaces with dynamically created editors, like in word processing applications or source code development tools.
|
||||||
|
|
||||||
|
When an entire area is closed, the default behavior is to hide the dock widgets it contains regardless of the `DockWidgetDeleteOnClose` flag except if there is only one dock widget. In this special case, the `DockWidgetDeleteOnClose` flag is followed. This behavior can be changed by setting the `DockWidgetForceCloseWithArea` flag to all the dock widgets that needs to be closed with their area.
|
||||||
|
|
||||||
## Styling
|
## Styling
|
||||||
|
|
||||||
The Advanced Docking System supports styling via [Qt Style Sheets](https://doc.qt.io/qt-5/stylesheet.html). All components like splitters, tabs, buttons, titlebar and
|
The Advanced Docking System supports styling via [Qt Style Sheets](https://doc.qt.io/qt-5/stylesheet.html). All components like splitters, tabs, buttons, titlebar and
|
||||||
@@ -425,3 +626,4 @@ just call the function for settings the stylesheet with an empty string.
|
|||||||
```c++
|
```c++
|
||||||
DockManager->setStyleSheet("");
|
DockManager->setStyleSheet("");
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.5)
|
||||||
project(QtADSExamples LANGUAGES CXX VERSION ${VERSION_SHORT})
|
project(QtADSExamples LANGUAGES CXX VERSION ${VERSION_SHORT})
|
||||||
add_subdirectory(simple)
|
add_subdirectory(simple)
|
||||||
|
add_subdirectory(hideshow)
|
||||||
add_subdirectory(sidebar)
|
add_subdirectory(sidebar)
|
||||||
add_subdirectory(deleteonclose)
|
add_subdirectory(deleteonclose)
|
||||||
|
add_subdirectory(centralwidget)
|
||||||
|
add_subdirectory(emptydockarea)
|
||||||
|
add_subdirectory(dockindock)
|
||||||
28
examples/centralwidget/CMakeLists.txt
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
project(ads_example_centralwidget VERSION ${VERSION_SHORT})
|
||||||
|
find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
|
||||||
|
find_package(Qt${QT_VERSION_MAJOR} 5.5 COMPONENTS Core Gui Widgets REQUIRED)
|
||||||
|
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||||
|
add_executable(CentralWidgetExample WIN32
|
||||||
|
main.cpp
|
||||||
|
mainwindow.cpp
|
||||||
|
mainwindow.ui
|
||||||
|
)
|
||||||
|
target_include_directories(CentralWidgetExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src")
|
||||||
|
target_link_libraries(CentralWidgetExample PRIVATE qtadvanceddocking)
|
||||||
|
target_link_libraries(CentralWidgetExample PUBLIC Qt${QT_VERSION_MAJOR}::Core
|
||||||
|
Qt${QT_VERSION_MAJOR}::Gui
|
||||||
|
Qt${QT_VERSION_MAJOR}::Widgets)
|
||||||
|
set_target_properties(CentralWidgetExample PROPERTIES
|
||||||
|
AUTOMOC ON
|
||||||
|
AUTORCC ON
|
||||||
|
AUTOUIC ON
|
||||||
|
CXX_STANDARD 14
|
||||||
|
CXX_STANDARD_REQUIRED ON
|
||||||
|
CXX_EXTENSIONS OFF
|
||||||
|
VERSION ${VERSION_SHORT}
|
||||||
|
EXPORT_NAME "Qt Advanced Docking System Central Widget Example"
|
||||||
|
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${ads_PlatformDir}/lib"
|
||||||
|
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${ads_PlatformDir}/lib"
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${ads_PlatformDir}/bin"
|
||||||
|
)
|
||||||
34
examples/centralwidget/centralwidget.pro
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
ADS_OUT_ROOT = $${OUT_PWD}/../..
|
||||||
|
|
||||||
|
QT += core gui widgets
|
||||||
|
|
||||||
|
TARGET = CentralWidgetExample
|
||||||
|
DESTDIR = $${ADS_OUT_ROOT}/lib
|
||||||
|
TEMPLATE = app
|
||||||
|
CONFIG += c++14
|
||||||
|
CONFIG += debug_and_release
|
||||||
|
adsBuildStatic {
|
||||||
|
DEFINES += ADS_STATIC
|
||||||
|
}
|
||||||
|
|
||||||
|
# The following define makes your compiler emit warnings if you use
|
||||||
|
# any Qt feature that has been marked deprecated (the exact warnings
|
||||||
|
# depend on your compiler). Please consult the documentation of the
|
||||||
|
# deprecated API in order to know how to port your code away from it.
|
||||||
|
DEFINES += QT_DEPRECATED_WARNINGS
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
main.cpp \
|
||||||
|
mainwindow.cpp
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
mainwindow.h
|
||||||
|
|
||||||
|
FORMS += \
|
||||||
|
mainwindow.ui
|
||||||
|
|
||||||
|
LIBS += -L$${ADS_OUT_ROOT}/lib
|
||||||
|
include(../../ads.pri)
|
||||||
|
INCLUDEPATH += ../../src
|
||||||
|
DEPENDPATH += ../../src
|
||||||
|
|
||||||
10
examples/centralwidget/main.cpp
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#include <mainwindow.h>
|
||||||
|
#include <QApplication>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QApplication a(argc, argv);
|
||||||
|
CMainWindow w;
|
||||||
|
w.show();
|
||||||
|
return a.exec();
|
||||||
|
}
|
||||||
106
examples/centralwidget/main.py
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from PyQt5 import uic
|
||||||
|
from PyQt5.QtCore import Qt, QTimer, QDir, QSignalBlocker
|
||||||
|
from PyQt5.QtGui import QCloseEvent, QIcon
|
||||||
|
from PyQt5.QtWidgets import (QApplication, QLabel, QCalendarWidget, QFrame, QTreeView,
|
||||||
|
QTableWidget, QFileSystemModel, QPlainTextEdit, QToolBar,
|
||||||
|
QWidgetAction, QComboBox, QAction, QSizePolicy, QInputDialog)
|
||||||
|
|
||||||
|
from PyQtAds import QtAds
|
||||||
|
|
||||||
|
UI_FILE = os.path.join(os.path.dirname(__file__), 'mainwindow.ui')
|
||||||
|
MainWindowUI, MainWindowBase = uic.loadUiType(UI_FILE)
|
||||||
|
|
||||||
|
class MainWindow(MainWindowUI, MainWindowBase):
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self.setupUi(self)
|
||||||
|
|
||||||
|
QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.OpaqueSplitterResize, True)
|
||||||
|
QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.XmlCompressionEnabled, False)
|
||||||
|
QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.FocusHighlighting, True)
|
||||||
|
self.dock_manager = QtAds.CDockManager(self)
|
||||||
|
|
||||||
|
# Set central widget
|
||||||
|
text_edit = QPlainTextEdit()
|
||||||
|
text_edit.setPlaceholderText("This is the central editor. Enter your text here.")
|
||||||
|
central_dock_widget = QtAds.CDockWidget("CentralWidget")
|
||||||
|
central_dock_widget.setWidget(text_edit)
|
||||||
|
central_dock_area = self.dock_manager.setCentralWidget(central_dock_widget)
|
||||||
|
central_dock_area.setAllowedAreas(QtAds.DockWidgetArea.OuterDockAreas)
|
||||||
|
|
||||||
|
# create other dock widgets
|
||||||
|
table = QTableWidget()
|
||||||
|
table.setColumnCount(3)
|
||||||
|
table.setRowCount(10)
|
||||||
|
table_dock_widget = QtAds.CDockWidget("Table 1")
|
||||||
|
table_dock_widget.setWidget(table)
|
||||||
|
table_dock_widget.setMinimumSizeHintMode(QtAds.CDockWidget.MinimumSizeHintFromDockWidget)
|
||||||
|
table_dock_widget.resize(250, 150)
|
||||||
|
table_dock_widget.setMinimumSize(200, 150)
|
||||||
|
table_area = self.dock_manager.addDockWidget(QtAds.DockWidgetArea.LeftDockWidgetArea, table_dock_widget)
|
||||||
|
self.menuView.addAction(table_dock_widget.toggleViewAction())
|
||||||
|
|
||||||
|
table = QTableWidget()
|
||||||
|
table.setColumnCount(5)
|
||||||
|
table.setRowCount(1020)
|
||||||
|
table_dock_widget = QtAds.CDockWidget("Table 2")
|
||||||
|
table_dock_widget.setWidget(table)
|
||||||
|
table_dock_widget.setMinimumSizeHintMode(QtAds.CDockWidget.MinimumSizeHintFromDockWidget)
|
||||||
|
table_dock_widget.resize(250, 150)
|
||||||
|
table_dock_widget.setMinimumSize(200, 150)
|
||||||
|
table_area = self.dock_manager.addDockWidget(QtAds.DockWidgetArea.BottomDockWidgetArea, table_dock_widget, table_area)
|
||||||
|
self.menuView.addAction(table_dock_widget.toggleViewAction())
|
||||||
|
|
||||||
|
properties_table = QTableWidget()
|
||||||
|
properties_table.setColumnCount(3)
|
||||||
|
properties_table.setRowCount(10)
|
||||||
|
properties_dock_widget = QtAds.CDockWidget("Properties")
|
||||||
|
properties_dock_widget.setWidget(properties_table)
|
||||||
|
properties_dock_widget.setMinimumSizeHintMode(QtAds.CDockWidget.MinimumSizeHintFromDockWidget)
|
||||||
|
properties_dock_widget.resize(250, 150)
|
||||||
|
properties_dock_widget.setMinimumSize(200, 150)
|
||||||
|
self.dock_manager.addDockWidget(QtAds.DockWidgetArea.RightDockWidgetArea, properties_dock_widget, central_dock_area)
|
||||||
|
self.menuView.addAction(properties_dock_widget.toggleViewAction())
|
||||||
|
|
||||||
|
self.create_perspective_ui()
|
||||||
|
|
||||||
|
def create_perspective_ui(self):
|
||||||
|
save_perspective_action = QAction("Create Perspective", self)
|
||||||
|
save_perspective_action.triggered.connect(self.save_perspective)
|
||||||
|
perspective_list_action = QWidgetAction(self)
|
||||||
|
self.perspective_combobox = QComboBox(self)
|
||||||
|
self.perspective_combobox.setSizeAdjustPolicy(QComboBox.AdjustToContents)
|
||||||
|
self.perspective_combobox.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
|
||||||
|
self.perspective_combobox.activated[str].connect(self.dock_manager.openPerspective)
|
||||||
|
perspective_list_action.setDefaultWidget(self.perspective_combobox)
|
||||||
|
self.toolBar.addSeparator()
|
||||||
|
self.toolBar.addAction(perspective_list_action)
|
||||||
|
self.toolBar.addAction(save_perspective_action)
|
||||||
|
|
||||||
|
def save_perspective(self):
|
||||||
|
perspective_name, ok = QInputDialog.getText(self, "Save Perspective", "Enter Unique name:")
|
||||||
|
if not ok or not perspective_name:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.dock_manager.addPerspective(perspective_name)
|
||||||
|
blocker = QSignalBlocker(self.perspective_combobox)
|
||||||
|
self.perspective_combobox.clear()
|
||||||
|
self.perspective_combobox.addItems(self.dock_manager.perspectiveNames())
|
||||||
|
self.perspective_combobox.setCurrentText(perspective_name)
|
||||||
|
|
||||||
|
def closeEvent(self, event: QCloseEvent):
|
||||||
|
self.dock_manager.deleteLater()
|
||||||
|
super().closeEvent(event)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app = QApplication(sys.argv)
|
||||||
|
|
||||||
|
w = MainWindow()
|
||||||
|
w.show()
|
||||||
|
app.exec_()
|
||||||
133
examples/centralwidget/mainwindow.cpp
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
#include "mainwindow.h"
|
||||||
|
|
||||||
|
#include "ui_mainwindow.h"
|
||||||
|
|
||||||
|
#include <QWidgetAction>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QCalendarWidget>
|
||||||
|
#include <QTreeView>
|
||||||
|
#include <QFileSystemModel>
|
||||||
|
#include <QTableWidget>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QRadioButton>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QInputDialog>
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QPlainTextEdit>
|
||||||
|
#include <QToolBar>
|
||||||
|
|
||||||
|
#include "DockAreaWidget.h"
|
||||||
|
#include "DockAreaTitleBar.h"
|
||||||
|
#include "DockAreaTabBar.h"
|
||||||
|
#include "FloatingDockContainer.h"
|
||||||
|
#include "DockComponentsFactory.h"
|
||||||
|
|
||||||
|
using namespace ads;
|
||||||
|
|
||||||
|
|
||||||
|
CMainWindow::CMainWindow(QWidget *parent)
|
||||||
|
: QMainWindow(parent)
|
||||||
|
, ui(new Ui::CMainWindow)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
CDockManager::setConfigFlag(CDockManager::OpaqueSplitterResize, true);
|
||||||
|
CDockManager::setConfigFlag(CDockManager::XmlCompressionEnabled, false);
|
||||||
|
CDockManager::setConfigFlag(CDockManager::FocusHighlighting, true);
|
||||||
|
DockManager = new CDockManager(this);
|
||||||
|
|
||||||
|
// Set central widget
|
||||||
|
QPlainTextEdit* w = new QPlainTextEdit();
|
||||||
|
w->setPlaceholderText("This is the central editor. Enter your text here.");
|
||||||
|
CDockWidget* CentralDockWidget = new CDockWidget("CentralWidget");
|
||||||
|
CentralDockWidget->setWidget(w);
|
||||||
|
auto* CentralDockArea = DockManager->setCentralWidget(CentralDockWidget);
|
||||||
|
CentralDockArea->setAllowedAreas(DockWidgetArea::OuterDockAreas);
|
||||||
|
|
||||||
|
// create other dock widgets
|
||||||
|
QTableWidget* table = new QTableWidget();
|
||||||
|
table->setColumnCount(3);
|
||||||
|
table->setRowCount(10);
|
||||||
|
CDockWidget* TableDockWidget = new CDockWidget("Table 1");
|
||||||
|
TableDockWidget->setWidget(table);
|
||||||
|
TableDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget);
|
||||||
|
TableDockWidget->resize(250, 150);
|
||||||
|
TableDockWidget->setMinimumSize(200,150);
|
||||||
|
auto TableArea = DockManager->addDockWidget(DockWidgetArea::LeftDockWidgetArea, TableDockWidget);
|
||||||
|
ui->menuView->addAction(TableDockWidget->toggleViewAction());
|
||||||
|
|
||||||
|
table = new QTableWidget();
|
||||||
|
table->setColumnCount(5);
|
||||||
|
table->setRowCount(1020);
|
||||||
|
TableDockWidget = new CDockWidget("Table 2");
|
||||||
|
TableDockWidget->setWidget(table);
|
||||||
|
TableDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget);
|
||||||
|
TableDockWidget->resize(250, 150);
|
||||||
|
TableDockWidget->setMinimumSize(200,150);
|
||||||
|
DockManager->addDockWidget(DockWidgetArea::BottomDockWidgetArea, TableDockWidget, TableArea);
|
||||||
|
ui->menuView->addAction(TableDockWidget->toggleViewAction());
|
||||||
|
|
||||||
|
QTableWidget* propertiesTable = new QTableWidget();
|
||||||
|
propertiesTable->setColumnCount(3);
|
||||||
|
propertiesTable->setRowCount(10);
|
||||||
|
CDockWidget* PropertiesDockWidget = new CDockWidget("Properties");
|
||||||
|
PropertiesDockWidget->setWidget(propertiesTable);
|
||||||
|
PropertiesDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget);
|
||||||
|
PropertiesDockWidget->resize(250, 150);
|
||||||
|
PropertiesDockWidget->setMinimumSize(200,150);
|
||||||
|
DockManager->addDockWidget(DockWidgetArea::RightDockWidgetArea, PropertiesDockWidget, CentralDockArea);
|
||||||
|
ui->menuView->addAction(PropertiesDockWidget->toggleViewAction());
|
||||||
|
|
||||||
|
createPerspectiveUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
CMainWindow::~CMainWindow()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CMainWindow::createPerspectiveUi()
|
||||||
|
{
|
||||||
|
SavePerspectiveAction = new QAction("Create Perspective", 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);
|
||||||
|
connect(PerspectiveComboBox, SIGNAL(activated(const QString&)),
|
||||||
|
DockManager, SLOT(openPerspective(const QString&)));
|
||||||
|
PerspectiveListAction->setDefaultWidget(PerspectiveComboBox);
|
||||||
|
ui->toolBar->addSeparator();
|
||||||
|
ui->toolBar->addAction(PerspectiveListAction);
|
||||||
|
ui->toolBar->addAction(SavePerspectiveAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CMainWindow::savePerspective()
|
||||||
|
{
|
||||||
|
QString PerspectiveName = QInputDialog::getText(this, "Save Perspective", "Enter unique name:");
|
||||||
|
if (PerspectiveName.isEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DockManager->addPerspective(PerspectiveName);
|
||||||
|
QSignalBlocker Blocker(PerspectiveComboBox);
|
||||||
|
PerspectiveComboBox->clear();
|
||||||
|
PerspectiveComboBox->addItems(DockManager->perspectiveNames());
|
||||||
|
PerspectiveComboBox->setCurrentText(PerspectiveName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
void CMainWindow::closeEvent(QCloseEvent* event)
|
||||||
|
{
|
||||||
|
// Delete dock manager here to delete all floating widgets. This ensures
|
||||||
|
// that all top level windows of the dock manager are properly closed
|
||||||
|
DockManager->deleteLater();
|
||||||
|
QMainWindow::closeEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
43
examples/centralwidget/mainwindow.h
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#ifndef MAINWINDOW_H
|
||||||
|
#define MAINWINDOW_H
|
||||||
|
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <QWidgetAction>
|
||||||
|
|
||||||
|
#include "DockManager.h"
|
||||||
|
#include "DockAreaWidget.h"
|
||||||
|
#include "DockWidget.h"
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
namespace Ui { class CMainWindow; }
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
class CMainWindow : public QMainWindow
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
CMainWindow(QWidget *parent = nullptr);
|
||||||
|
~CMainWindow();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void closeEvent(QCloseEvent* event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QAction* SavePerspectiveAction = nullptr;
|
||||||
|
QWidgetAction* PerspectiveListAction = nullptr;
|
||||||
|
QComboBox* PerspectiveComboBox = nullptr;
|
||||||
|
|
||||||
|
Ui::CMainWindow *ui;
|
||||||
|
|
||||||
|
ads::CDockManager* DockManager;
|
||||||
|
ads::CDockAreaWidget* StatusDockArea;
|
||||||
|
ads::CDockWidget* TimelineDockWidget;
|
||||||
|
|
||||||
|
void createPerspectiveUi();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void savePerspective();
|
||||||
|
};
|
||||||
|
#endif // MAINWINDOW_H
|
||||||
47
examples/centralwidget/mainwindow.ui
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>CMainWindow</class>
|
||||||
|
<widget class="QMainWindow" name="CMainWindow">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>1284</width>
|
||||||
|
<height>757</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>MainWindow</string>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="centralwidget"/>
|
||||||
|
<widget class="QMenuBar" name="menubar">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>1284</width>
|
||||||
|
<height>21</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<widget class="QMenu" name="menuView">
|
||||||
|
<property name="title">
|
||||||
|
<string>View</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<addaction name="menuView"/>
|
||||||
|
</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>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
@@ -1,13 +1,16 @@
|
|||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.5)
|
||||||
project(ads_example_deleteonclose VERSION ${VERSION_SHORT})
|
project(ads_example_deleteonclose VERSION ${VERSION_SHORT})
|
||||||
find_package(Qt5 5.5 COMPONENTS Core Gui Widgets REQUIRED)
|
find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
|
||||||
|
find_package(Qt${QT_VERSION_MAJOR} 5.5 COMPONENTS Core Gui Widgets REQUIRED)
|
||||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||||
add_executable(DeleteOnCloseTest WIN32
|
add_executable(DeleteOnCloseTest WIN32
|
||||||
main.cpp
|
main.cpp
|
||||||
)
|
)
|
||||||
target_include_directories(DeleteOnCloseTest PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src")
|
target_include_directories(DeleteOnCloseTest PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src")
|
||||||
target_link_libraries(DeleteOnCloseTest PRIVATE qtadvanceddocking)
|
target_link_libraries(DeleteOnCloseTest PRIVATE qtadvanceddocking)
|
||||||
target_link_libraries(DeleteOnCloseTest PUBLIC Qt5::Core Qt5::Gui Qt5::Widgets)
|
target_link_libraries(DeleteOnCloseTest PUBLIC Qt${QT_VERSION_MAJOR}::Core
|
||||||
|
Qt${QT_VERSION_MAJOR}::Gui
|
||||||
|
Qt${QT_VERSION_MAJOR}::Widgets)
|
||||||
set_target_properties(DeleteOnCloseTest PROPERTIES
|
set_target_properties(DeleteOnCloseTest PROPERTIES
|
||||||
AUTOMOC ON
|
AUTOMOC ON
|
||||||
CXX_STANDARD 14
|
CXX_STANDARD 14
|
||||||
|
|||||||
@@ -17,23 +17,7 @@ SOURCES += main.cpp
|
|||||||
|
|
||||||
|
|
||||||
LIBS += -L$${ADS_OUT_ROOT}/lib
|
LIBS += -L$${ADS_OUT_ROOT}/lib
|
||||||
|
include(../../ads.pri)
|
||||||
# Dependency: AdvancedDockingSystem (shared)
|
|
||||||
CONFIG(debug, debug|release){
|
|
||||||
win32 {
|
|
||||||
LIBS += -lqtadvanceddockingd
|
|
||||||
}
|
|
||||||
else:mac {
|
|
||||||
LIBS += -lqtadvanceddocking_debug
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
LIBS += -lqtadvanceddocking
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
LIBS += -lqtadvanceddocking
|
|
||||||
}
|
|
||||||
|
|
||||||
INCLUDEPATH += ../../src
|
INCLUDEPATH += ../../src
|
||||||
DEPENDPATH += ../../src
|
DEPENDPATH += ../../src
|
||||||
|
|
||||||
|
|||||||
@@ -5,33 +5,75 @@
|
|||||||
#include <QMenuBar>
|
#include <QMenuBar>
|
||||||
#include "DockManager.h"
|
#include "DockManager.h"
|
||||||
|
|
||||||
|
|
||||||
|
class MainWindow : public QMainWindow
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
ads::CDockManager* m_DockManager = nullptr;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void closeEvent(QCloseEvent *event) override
|
||||||
|
{
|
||||||
|
QMainWindow::closeEvent(event);
|
||||||
|
if (m_DockManager)
|
||||||
|
{
|
||||||
|
m_DockManager->deleteLater();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
void setDockManager(ads::CDockManager* DockManager) {m_DockManager = DockManager;}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
QApplication a(argc, argv);
|
QApplication a(argc, argv);
|
||||||
QMainWindow w;
|
MainWindow w;
|
||||||
|
|
||||||
ads::CDockManager::setConfigFlag(ads::CDockManager::FocusHighlighting, true);
|
ads::CDockManager::setConfigFlag(ads::CDockManager::FocusHighlighting, true);
|
||||||
ads::CDockManager::setConfigFlag(ads::CDockManager::AllTabsHaveCloseButton, true);
|
ads::CDockManager::setConfigFlag(ads::CDockManager::AllTabsHaveCloseButton, true);
|
||||||
auto dockManager = new ads::CDockManager(&w);
|
auto dockManager = new ads::CDockManager(&w);
|
||||||
|
w.setDockManager(dockManager);
|
||||||
QObject::connect(dockManager, &ads::CDockManager::focusedDockWidgetChanged, [] (ads::CDockWidget* old, ads::CDockWidget* now) {
|
QObject::connect(dockManager, &ads::CDockManager::focusedDockWidgetChanged, [] (ads::CDockWidget* old, ads::CDockWidget* now) {
|
||||||
static int Count = 0;
|
static int Count = 0;
|
||||||
qDebug() << Count++ << " CDockManager::focusedDockWidgetChanged old: " << (old ? old->objectName() : "-") << " now: " << now->objectName() << " visible: " << now->isVisible();
|
qDebug() << Count++ << " CDockManager::focusedDockWidgetChanged old: " << (old ? old->objectName() : "-") << " now: " << now->objectName() << " visible: " << now->isVisible();
|
||||||
now->widget()->setFocus();
|
now->widget()->setFocus();
|
||||||
});
|
});
|
||||||
|
|
||||||
QAction *action = new QAction("New Delete On Close", &w);
|
QAction *action = new QAction("New [DockWidgetDeleteOnClose]", &w);
|
||||||
w.menuBar()->addAction(action);
|
w.menuBar()->addAction(action);
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
QObject::connect(action, &QAction::triggered, [&]() {
|
QObject::connect(action, &QAction::triggered, [&]() {
|
||||||
auto dw = new ads::CDockWidget(QStringLiteral("test doc %1").arg(i++), &w);
|
auto dw = new ads::CDockWidget(QStringLiteral("test %1 [DockWidgetDeleteOnClose]").arg(i++), &w);
|
||||||
auto editor = new QTextEdit(QStringLiteral("lorem ipsum..."), dw);
|
auto editor = new QTextEdit(QStringLiteral("lorem ipsum..."), dw);
|
||||||
dw->setWidget(editor);
|
dw->setWidget(editor);
|
||||||
dw->setFeature(ads::CDockWidget::DockWidgetDeleteOnClose, true);
|
dw->setFeature(ads::CDockWidget::DockWidgetDeleteOnClose, true);
|
||||||
auto area = dockManager->addDockWidgetTab(ads::CenterDockWidgetArea, dw);
|
auto area = dockManager->addDockWidgetTab(ads::CenterDockWidgetArea, dw);
|
||||||
qDebug() << "doc dock widget created!" << dw << area;
|
qDebug() << "doc dock widget created!" << dw << area;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
auto dw = new ads::CDockWidget(QStringLiteral("test %1 [DeleteContentOnClose]").arg(i++), &w);
|
||||||
|
auto editor = new QTextEdit(QStringLiteral("recreated lorem ipsum......"), dw);
|
||||||
|
dw->setWidget(editor);
|
||||||
|
dw->setFeature(ads::CDockWidget::DeleteContentOnClose, true);
|
||||||
|
dw->setWidgetFactory([](QWidget* dw)
|
||||||
|
{
|
||||||
|
static int timesRecreated = 0;
|
||||||
|
return new QTextEdit(QStringLiteral("recreated lorem ipsum... times %1").arg(++timesRecreated), dw);
|
||||||
|
});
|
||||||
|
auto area = dockManager->addDockWidgetTab(ads::CenterDockWidgetArea, dw);
|
||||||
|
qDebug() << "DeleteContentOnClose dock widget created!" << dw << area;
|
||||||
|
|
||||||
|
action = new QAction("Toggle [DeleteContentOnClose]", &w);
|
||||||
|
w.menuBar()->addAction(action);
|
||||||
|
|
||||||
|
QObject::connect(action, &QAction::triggered, [dw]() {
|
||||||
|
dw->toggleView(dw->isClosed());
|
||||||
|
qDebug() << QString("dock widget %1! contents widget %2!").arg(dw->isClosed() ? "closed" : "open", dw->widget() ? "created" : "deleted");
|
||||||
|
});
|
||||||
|
|
||||||
action = new QAction("New", &w);
|
action = new QAction("New", &w);
|
||||||
w.menuBar()->addAction(action);
|
w.menuBar()->addAction(action);
|
||||||
QObject::connect(action, &QAction::triggered, [&]() {
|
QObject::connect(action, &QAction::triggered, [&]() {
|
||||||
|
|||||||
73
examples/deleteonclose/main.py
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
from PyQtAds import QtAds
|
||||||
|
from PyQt5.QtGui import QCloseEvent
|
||||||
|
from PyQt5.QtCore import (qDebug, pyqtSlot, QObject, pyqtSignal)
|
||||||
|
from PyQt5.QtWidgets import (QMainWindow, QAction, QTextEdit, QApplication,
|
||||||
|
QMenuBar)
|
||||||
|
|
||||||
|
|
||||||
|
class MainWindow(QMainWindow):
|
||||||
|
dock_manager = None
|
||||||
|
|
||||||
|
def closeEvent(self, event: QCloseEvent):
|
||||||
|
super().closeEvent(event)
|
||||||
|
if self.dock_manager is not None:
|
||||||
|
self.dock_manager.deleteLater()
|
||||||
|
|
||||||
|
def setDockManager(self, dock_manager: QtAds.CDockManager):
|
||||||
|
self.dock_manager = dock_manager
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app = QApplication(sys.argv)
|
||||||
|
w = MainWindow()
|
||||||
|
|
||||||
|
QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.FocusHighlighting, True)
|
||||||
|
QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.AllTabsHaveCloseButton, True)
|
||||||
|
dock_manager = QtAds.CDockManager(w)
|
||||||
|
w.setDockManager(dock_manager)
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
|
||||||
|
def on_focused_dock_widget_changed(old: QtAds.CDockWidget, now: QtAds.CDockWidget):
|
||||||
|
global count
|
||||||
|
qDebug( "{:d} CDockManager::focusedDockWidgetChanged old: {} now: {} visible: {}".format(
|
||||||
|
count,
|
||||||
|
old.objectName() if old else "-",
|
||||||
|
now.objectName(),
|
||||||
|
now.isVisible()))
|
||||||
|
count += 1
|
||||||
|
now.widget().setFocus()
|
||||||
|
|
||||||
|
dock_manager.focusedDockWidgetChanged.connect(on_focused_dock_widget_changed)
|
||||||
|
|
||||||
|
action = QAction("New Delete On Close", w)
|
||||||
|
w.menuBar().addAction(action)
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
def on_action_triggered():
|
||||||
|
global i
|
||||||
|
dw = QtAds.CDockWidget("test doc {:d}".format(i))
|
||||||
|
i += 1
|
||||||
|
editor = QTextEdit("lorem ipsum...", dw)
|
||||||
|
dw.setWidget(editor)
|
||||||
|
dw.setFeature(QtAds.CDockWidget.DockWidgetDeleteOnClose, True)
|
||||||
|
area = dock_manager.addDockWidgetTab(QtAds.CenterDockWidgetArea, dw)
|
||||||
|
qDebug("doc dock widget created! {} {}".format(dw, area))
|
||||||
|
action.triggered.connect(on_action_triggered)
|
||||||
|
|
||||||
|
action = QAction("New", w)
|
||||||
|
w.menuBar().addAction(action)
|
||||||
|
def on_action2_triggered():
|
||||||
|
global i
|
||||||
|
dw = QtAds.CDockWidget("test {:d}".format(i))
|
||||||
|
i += 1
|
||||||
|
editor = QTextEdit("lorem ipsum...", dw)
|
||||||
|
dw.setWidget(editor)
|
||||||
|
area = dock_manager.addDockWidgetTab(QtAds.CenterDockWidgetArea, dw)
|
||||||
|
qDebug("dock widget created! {} {}".format(dw, area))
|
||||||
|
action.triggered.connect(on_action2_triggered)
|
||||||
|
|
||||||
|
w.show()
|
||||||
|
app.exec_()
|
||||||
31
examples/dockindock/CMakeLists.txt
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
project(ads_example_dockindock VERSION ${VERSION_SHORT})
|
||||||
|
find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
|
||||||
|
find_package(Qt${QT_VERSION_MAJOR} 5.5 COMPONENTS Core Gui Widgets REQUIRED)
|
||||||
|
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||||
|
add_executable(DockInDockExample WIN32
|
||||||
|
dockindock.cpp
|
||||||
|
dockindockmanager.cpp
|
||||||
|
perspectiveactions.cpp
|
||||||
|
perspectives.cpp
|
||||||
|
main.cpp
|
||||||
|
mainframe.cpp
|
||||||
|
)
|
||||||
|
target_include_directories(DockInDockExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src")
|
||||||
|
target_link_libraries(DockInDockExample PRIVATE qtadvanceddocking)
|
||||||
|
target_link_libraries(DockInDockExample PUBLIC Qt${QT_VERSION_MAJOR}::Core
|
||||||
|
Qt${QT_VERSION_MAJOR}::Gui
|
||||||
|
Qt${QT_VERSION_MAJOR}::Widgets)
|
||||||
|
set_target_properties(DockInDockExample PROPERTIES
|
||||||
|
AUTOMOC ON
|
||||||
|
AUTORCC ON
|
||||||
|
AUTOUIC ON
|
||||||
|
CXX_STANDARD 14
|
||||||
|
CXX_STANDARD_REQUIRED ON
|
||||||
|
CXX_EXTENSIONS OFF
|
||||||
|
VERSION ${VERSION_SHORT}
|
||||||
|
EXPORT_NAME "Qt Advanced Docking System Simple Example"
|
||||||
|
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${ads_PlatformDir}/lib"
|
||||||
|
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${ads_PlatformDir}/lib"
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${ads_PlatformDir}/bin"
|
||||||
|
)
|
||||||
300
examples/dockindock/dockindock.cpp
Normal file
@@ -0,0 +1,300 @@
|
|||||||
|
#include "dockindock.h"
|
||||||
|
#include "perspectives.h"
|
||||||
|
#include "dockindockmanager.h"
|
||||||
|
#include "perspectiveactions.h"
|
||||||
|
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QInputDialog>
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
using namespace QtAdsUtl;
|
||||||
|
|
||||||
|
DockInDockWidget::DockInDockWidget( QWidget* parent, bool canCreateNewGroups, PerspectivesManager* perspectivesManager ) :
|
||||||
|
DockInDockWidget( parent, (DockInDockWidget*)NULL, perspectivesManager )
|
||||||
|
{
|
||||||
|
m_canCreateNewGroups = canCreateNewGroups;
|
||||||
|
m_topLevelDockWidget = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
DockInDockWidget::DockInDockWidget( QWidget* parent, DockInDockWidget* topLevelDockWidget, PerspectivesManager* perspectivesManager ) :
|
||||||
|
baseClass( parent ),
|
||||||
|
m_topLevelDockWidget( topLevelDockWidget ),
|
||||||
|
m_canCreateNewGroups( (topLevelDockWidget) ? topLevelDockWidget->m_canCreateNewGroups : false ),
|
||||||
|
m_perspectivesManager( perspectivesManager )
|
||||||
|
{
|
||||||
|
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||||
|
layout->setContentsMargins( 0,0,0,0 );
|
||||||
|
layout->addWidget( m_mgr = new DockInDockManager(*this) );
|
||||||
|
}
|
||||||
|
|
||||||
|
DockInDockWidget::~DockInDockWidget()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ads::CDockAreaWidget* DockInDockWidget::addTabWidget( QWidget* widget, const QString& name, ads::CDockAreaWidget* after )
|
||||||
|
{
|
||||||
|
return addTabWidget( widget, name, QIcon(), after );
|
||||||
|
}
|
||||||
|
|
||||||
|
ads::CDockAreaWidget* DockInDockWidget::addTabWidget( QWidget* widget, const QString& name, QIcon icon, ads::CDockAreaWidget* after )
|
||||||
|
{
|
||||||
|
for ( auto existing : getTopLevelDockWidget()->getManager()->allDockWidgets(true,true) )
|
||||||
|
{
|
||||||
|
if ( existing.second->objectName() == name )
|
||||||
|
{
|
||||||
|
QMessageBox::critical( this, "Error", "Name '" + name + "' already in use" );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ads::CDockWidget* DockWidget = new ads::CDockWidget(name);
|
||||||
|
DockWidget->setWidget(widget);
|
||||||
|
DockWidget->setIcon( icon );
|
||||||
|
|
||||||
|
// Add the dock widget to the top dock widget area
|
||||||
|
return m_mgr->addDockWidget(ads::CenterDockWidgetArea, DockWidget, after);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DockInDockWidget::isTopLevel()
|
||||||
|
{
|
||||||
|
return objectName().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DockInDockWidget::getGroupNameError( const QString& groupName )
|
||||||
|
{
|
||||||
|
if ( groupName.isEmpty() )
|
||||||
|
{
|
||||||
|
return "Group must have a non-empty name";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<DockInDockManager*> dockManagers = m_mgr->allManagers( true, true );
|
||||||
|
for ( auto mgr : dockManagers )
|
||||||
|
{
|
||||||
|
if ( mgr->getGroupName() == groupName )
|
||||||
|
return "Group name '" + groupName + "' already used";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
DockInDockWidget* DockInDockWidget::createGroup( const QString& groupName, ads::CDockAreaWidget*& insertPos )
|
||||||
|
{
|
||||||
|
return createGroup( groupName, QIcon(), insertPos );
|
||||||
|
}
|
||||||
|
|
||||||
|
DockInDockWidget* DockInDockWidget::createGroup( const QString& groupName, QIcon icon, ads::CDockAreaWidget*& insertPos )
|
||||||
|
{
|
||||||
|
QString error = getGroupNameError( groupName );
|
||||||
|
if ( !error.isEmpty() )
|
||||||
|
{
|
||||||
|
QMessageBox::critical( NULL, "Error", error );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
DockInDockWidget* child = new DockInDockWidget( this, m_topLevelDockWidget, m_perspectivesManager );
|
||||||
|
child->setObjectName( groupName );
|
||||||
|
|
||||||
|
ads::CDockWidget* DockWidget = new ads::CDockWidget(groupName);
|
||||||
|
DockWidget->setWidget(child);
|
||||||
|
DockWidget->setIcon(icon);
|
||||||
|
|
||||||
|
insertPos = m_mgr->addDockWidget(ads::CenterDockWidgetArea, DockWidget, insertPos);
|
||||||
|
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockInDockWidget::destroyGroup( DockInDockWidget* widgetToRemove )
|
||||||
|
{
|
||||||
|
auto topLevelWidget = widgetToRemove->getTopLevelDockWidget();
|
||||||
|
|
||||||
|
if ( topLevelWidget && topLevelWidget != widgetToRemove )
|
||||||
|
{
|
||||||
|
// reaffect all child docks to top-level
|
||||||
|
for ( auto dockwidget : widgetToRemove->getManager()->getWidgetsInGUIOrder() ) // don't use allDockWidgets to preserve sub-groups
|
||||||
|
{
|
||||||
|
MoveDockWidgetAction::move( dockwidget, topLevelWidget->getManager() );
|
||||||
|
}
|
||||||
|
assert( widgetToRemove->getManager()->allDockWidgets( true, true ).empty() );
|
||||||
|
|
||||||
|
// find widget's parent:
|
||||||
|
for ( auto dockwidget : topLevelWidget->getManager()->allDockWidgets( true, true ) )
|
||||||
|
{
|
||||||
|
if ( dockwidget.second->widget() == widgetToRemove )
|
||||||
|
{
|
||||||
|
dockwidget.first->removeDockWidget( dockwidget.second );
|
||||||
|
delete dockwidget.second;
|
||||||
|
//delete widgetToRemove; automatically deleted when dockWidget is deleted
|
||||||
|
widgetToRemove = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert( widgetToRemove == NULL );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assert( false );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockInDockWidget::attachViewMenu( QMenu* menu )
|
||||||
|
{
|
||||||
|
connect( menu, SIGNAL(aboutToShow()), this, SLOT(autoFillAttachedViewMenu()) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockInDockWidget::autoFillAttachedViewMenu()
|
||||||
|
{
|
||||||
|
QMenu* menu = dynamic_cast<QMenu*>( QObject::sender() );
|
||||||
|
|
||||||
|
if ( menu )
|
||||||
|
{
|
||||||
|
menu->clear();
|
||||||
|
setupViewMenu( menu );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assert( false );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockInDockWidget::setupViewMenu( QMenu* menu )
|
||||||
|
{
|
||||||
|
std::vector<DockInDockManager*> dockManagers = m_mgr->allManagers( true, true );
|
||||||
|
|
||||||
|
bool hasPerspectivesMenu = false;
|
||||||
|
if ( getTopLevelDockWidget() == this )
|
||||||
|
hasPerspectivesMenu = (m_perspectivesManager != NULL);
|
||||||
|
else
|
||||||
|
assert( false );
|
||||||
|
|
||||||
|
QMenu* organize = menu;
|
||||||
|
if ( hasPerspectivesMenu )
|
||||||
|
organize = menu->addMenu( "Organize" );
|
||||||
|
|
||||||
|
setupMenu( organize, dockManagers );
|
||||||
|
|
||||||
|
if ( hasPerspectivesMenu )
|
||||||
|
{
|
||||||
|
QMenu* perspectives = menu->addMenu( "Perspectives" );
|
||||||
|
fillPerspectivesMenu( perspectives );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockInDockWidget::setupMenu( QMenu* menu, const std::vector<DockInDockManager*>& moveTo )
|
||||||
|
{
|
||||||
|
m_mgr->fillViewMenu( menu, moveTo );
|
||||||
|
menu->addSeparator();
|
||||||
|
auto moveMenu = menu->addMenu( "Move" );
|
||||||
|
m_mgr->fillMoveMenu( moveMenu, moveTo );
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockInDockWidget::fillPerspectivesMenu( QMenu* menu )
|
||||||
|
{
|
||||||
|
menu->addAction( "Create perspective...", this, SLOT(createPerspective()) );
|
||||||
|
|
||||||
|
QStringList perspectiveNames;
|
||||||
|
if ( m_perspectivesManager )
|
||||||
|
perspectiveNames = m_perspectivesManager->perspectiveNames();
|
||||||
|
|
||||||
|
if ( !perspectiveNames.isEmpty() )
|
||||||
|
{
|
||||||
|
QMenu* load = menu->addMenu( "Load perspective" );
|
||||||
|
for (const auto& name : perspectiveNames)
|
||||||
|
{
|
||||||
|
load->addAction(new LoadPerspectiveAction( load, name, *this));
|
||||||
|
}
|
||||||
|
QMenu* remove = menu->addMenu( "Remove perspective" );
|
||||||
|
for (const auto& name : perspectiveNames)
|
||||||
|
{
|
||||||
|
remove->addAction( new RemovePerspectiveAction( remove, name, *this ));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockInDockWidget::setNewPerspectiveDefaultName( const QString& defaultName )
|
||||||
|
{
|
||||||
|
m_newPerspectiveDefaultName = defaultName;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockInDockWidget::createPerspective()
|
||||||
|
{
|
||||||
|
if ( !m_perspectivesManager )
|
||||||
|
return;
|
||||||
|
|
||||||
|
QString name = m_newPerspectiveDefaultName;
|
||||||
|
if ( !m_newPerspectiveDefaultName.isEmpty() )
|
||||||
|
{
|
||||||
|
int index = 2;
|
||||||
|
while ( m_perspectivesManager->perspectiveNames().contains( name ) )
|
||||||
|
{
|
||||||
|
name = m_newPerspectiveDefaultName + " (" + QString::number(index) + ")";
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while ( true )
|
||||||
|
{
|
||||||
|
bool ok = false;
|
||||||
|
name = QInputDialog::getText( NULL, "Create perspective", "Enter perspective name", QLineEdit::Normal, name, &ok );
|
||||||
|
if ( ok )
|
||||||
|
{
|
||||||
|
if ( name.isEmpty() )
|
||||||
|
{
|
||||||
|
QMessageBox::critical( NULL, "Error", "Perspective name cannot be empty" );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if ( m_perspectivesManager->perspectiveNames().contains( name ) )
|
||||||
|
{
|
||||||
|
if ( QMessageBox::critical( NULL, "Error", "Perspective '" + name + "' already exists, overwrite it?", QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == QMessageBox::No )
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_perspectivesManager->addPerspective( name, *this );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dumpStatus( std::ostream& str, ads::CDockWidget* widget, const std::string& tab, std::string suffix )
|
||||||
|
{
|
||||||
|
DockInDockManager* asMgr = DockInDockManager::dockInAManager( widget );
|
||||||
|
if ( asMgr )
|
||||||
|
{
|
||||||
|
asMgr->parent().dumpStatus( str, tab );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
str << tab << widget->objectName().toStdString() << suffix << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockInDockWidget::dumpStatus( std::ostream& str, std::string tab )
|
||||||
|
{
|
||||||
|
str << tab << "Group: " << getManager()->getGroupName().toStdString() << std::endl;
|
||||||
|
tab += " ";
|
||||||
|
std::set<ads::CDockWidget*> visibleWidgets;
|
||||||
|
for ( auto widget : getManager()->getWidgetsInGUIOrder() )
|
||||||
|
{
|
||||||
|
visibleWidgets.insert( widget );
|
||||||
|
::dumpStatus( str, widget, tab, "" );
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( auto closed : getManager()->dockWidgetsMap() )
|
||||||
|
{
|
||||||
|
if ( visibleWidgets.find( closed ) == visibleWidgets.end() )
|
||||||
|
{
|
||||||
|
::dumpStatus( str, closed, tab, " (closed)" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
80
examples/dockindock/dockindock.h
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QMap>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
|
class QMenu;
|
||||||
|
|
||||||
|
namespace ads
|
||||||
|
{
|
||||||
|
class CDockAreaWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace QtAdsUtl
|
||||||
|
{
|
||||||
|
|
||||||
|
class DockInDockManager;
|
||||||
|
class PerspectivesManager;
|
||||||
|
// tab of tab example for https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/issues/306
|
||||||
|
class DockInDockWidget : public QWidget
|
||||||
|
{
|
||||||
|
typedef QWidget baseClass;
|
||||||
|
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
DockInDockWidget( QWidget* parent, bool canCreateNewGroups, PerspectivesManager* perspectivesManager );
|
||||||
|
~DockInDockWidget() override;
|
||||||
|
|
||||||
|
ads::CDockAreaWidget* addTabWidget( QWidget* widget, const QString& name, ads::CDockAreaWidget* after );
|
||||||
|
DockInDockWidget* createGroup( const QString& groupName, ads::CDockAreaWidget*& insertPos );
|
||||||
|
|
||||||
|
ads::CDockAreaWidget* addTabWidget( QWidget* widget, const QString& name, QIcon icon, ads::CDockAreaWidget* after );
|
||||||
|
DockInDockWidget* createGroup( const QString& groupName, QIcon icon, ads::CDockAreaWidget*& insertPos );
|
||||||
|
|
||||||
|
QString getGroupNameError( const QString& groupName );
|
||||||
|
void destroyGroup( DockInDockWidget* widget );
|
||||||
|
|
||||||
|
/** Manually fill a given view menu */
|
||||||
|
void setupViewMenu( QMenu* menu );
|
||||||
|
|
||||||
|
/** Attach a view menu that will be automatically fill */
|
||||||
|
void attachViewMenu( QMenu* menu );
|
||||||
|
|
||||||
|
bool isTopLevel();
|
||||||
|
void setupMenu( QMenu* menu, const std::vector<DockInDockManager*>& moveTo );
|
||||||
|
|
||||||
|
inline DockInDockManager* getManager() { return m_mgr; }
|
||||||
|
inline DockInDockWidget* getTopLevelDockWidget() { return m_topLevelDockWidget; }
|
||||||
|
|
||||||
|
inline bool canCreateNewGroups() const { return m_canCreateNewGroups; }
|
||||||
|
|
||||||
|
void dumpStatus( std::ostream& str, std::string tab = "" );
|
||||||
|
|
||||||
|
inline PerspectivesManager* getPerspectivesManager() { return m_perspectivesManager; }
|
||||||
|
|
||||||
|
void setNewPerspectiveDefaultName( const QString& defaultName );
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void autoFillAttachedViewMenu();
|
||||||
|
void createPerspective();
|
||||||
|
|
||||||
|
private:
|
||||||
|
DockInDockManager* m_mgr;
|
||||||
|
DockInDockWidget* m_topLevelDockWidget;
|
||||||
|
|
||||||
|
bool m_canCreateNewGroups;
|
||||||
|
|
||||||
|
DockInDockWidget( QWidget* parent, DockInDockWidget* topLevelDockWidget, PerspectivesManager* perspectivesManager );
|
||||||
|
|
||||||
|
PerspectivesManager* m_perspectivesManager;
|
||||||
|
QString m_newPerspectiveDefaultName;
|
||||||
|
|
||||||
|
void fillPerspectivesMenu( QMenu* menu );
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
35
examples/dockindock/dockindock.pro
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
ADS_OUT_ROOT = $${OUT_PWD}/../..
|
||||||
|
|
||||||
|
QT += core gui widgets
|
||||||
|
|
||||||
|
TARGET = DockInDock
|
||||||
|
DESTDIR = $${ADS_OUT_ROOT}/lib
|
||||||
|
TEMPLATE = app
|
||||||
|
CONFIG += c++14
|
||||||
|
CONFIG += debug_and_release
|
||||||
|
adsBuildStatic {
|
||||||
|
DEFINES += ADS_STATIC
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINES += QT_DEPRECATED_WARNINGS
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
dockindock.cpp \
|
||||||
|
dockindockmanager.cpp \
|
||||||
|
perspectiveactions.cpp \
|
||||||
|
perspectives.cpp \
|
||||||
|
main.cpp \
|
||||||
|
mainframe.cpp
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
dockindock.h \
|
||||||
|
dockindockmanager.h \
|
||||||
|
perspectiveactions.h \
|
||||||
|
perspectives.h \
|
||||||
|
mainframe.h
|
||||||
|
|
||||||
|
LIBS += -L$${ADS_OUT_ROOT}/lib
|
||||||
|
include(../../ads.pri)
|
||||||
|
INCLUDEPATH += ../../src
|
||||||
|
DEPENDPATH += ../../src
|
||||||
|
|
||||||
203
examples/dockindock/dockindock.py
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (QApplication, QWidget, QVBoxLayout, QMessageBox,
|
||||||
|
QInputDialog, QMenu, QLineEdit)
|
||||||
|
from PyQt5.QtGui import QIcon
|
||||||
|
from PyQtAds import QtAds
|
||||||
|
|
||||||
|
from dockindockmanager import DockInDockManager
|
||||||
|
from perspectiveactions import LoadPerspectiveAction, RemovePerspectiveAction
|
||||||
|
|
||||||
|
|
||||||
|
class DockInDockWidget(QWidget):
|
||||||
|
def __init__(self, parent, perspectives_manager: 'PerspectivesManager', can_create_new_groups: bool = False, top_level_widget = None):
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
if top_level_widget is not None:
|
||||||
|
self.__can_create_new_groups = top_level_widget.can_create_new_groups
|
||||||
|
else:
|
||||||
|
self.__can_create_new_groups = can_create_new_groups
|
||||||
|
self.__top_level_dock_widget = top_level_widget if top_level_widget else self
|
||||||
|
self.__perspectives_manager = perspectives_manager
|
||||||
|
self.__new_perspective_default_name: str = ''
|
||||||
|
|
||||||
|
layout = QVBoxLayout(self)
|
||||||
|
layout.setContentsMargins(0,0,0,0)
|
||||||
|
self.__mgr = DockInDockManager(self)
|
||||||
|
layout.addWidget(self.__mgr)
|
||||||
|
|
||||||
|
def getManager(self) -> 'DockInDockManager':
|
||||||
|
return self.__mgr
|
||||||
|
|
||||||
|
def getTopLevelDockWidget(self) -> 'DockInDockWidget':
|
||||||
|
return self.__top_level_dock_widget
|
||||||
|
|
||||||
|
def canCreateNewGroups(self) -> bool:
|
||||||
|
return self.__can_create_new_groups
|
||||||
|
|
||||||
|
def getPerspectivesManager(self) -> 'PerspectivesManager':
|
||||||
|
return self.__perspectives_manager
|
||||||
|
|
||||||
|
def addTabWidget(self, widget: QWidget, name: str, after: QtAds.CDockAreaWidget, icon = QIcon()) -> QtAds.CDockAreaWidget:
|
||||||
|
for existing in self.getTopLevelDockWidget().getManager().allDockWidgets(True, True):
|
||||||
|
if existing[1].objectName() == name:
|
||||||
|
QMessageBox.critical(self, "Error", "Name '" + name + "' already in use")
|
||||||
|
return
|
||||||
|
|
||||||
|
dock_widget = QtAds.CDockWidget(name)
|
||||||
|
dock_widget.setWidget(widget)
|
||||||
|
dock_widget.setIcon(icon)
|
||||||
|
|
||||||
|
# Add the dock widget to the top dock widget area
|
||||||
|
return self.__mgr.addDockWidget(QtAds.CenterDockWidgetArea, dock_widget, after)
|
||||||
|
|
||||||
|
def isTopLevel(self) -> bool:
|
||||||
|
return not self.objectName()
|
||||||
|
|
||||||
|
def getGroupNameError(self, group_name: str) -> str:
|
||||||
|
if not group_name:
|
||||||
|
return "Group must have a non-empty name"
|
||||||
|
|
||||||
|
dock_managers = self.__mgr.allManagers(True, True)
|
||||||
|
for mgr in dock_managers:
|
||||||
|
if mgr.getGroupName() == group_name:
|
||||||
|
return "Group name '" + group_name + "' already used"
|
||||||
|
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def createGroup(self, group_name: str, insert_pos: QtAds.CDockAreaWidget, icon = QIcon()) -> 'DockInDockWidget':
|
||||||
|
error = self.getGroupNameError(group_name)
|
||||||
|
if error:
|
||||||
|
QMessageBox.critical(None, "Error", error)
|
||||||
|
return
|
||||||
|
|
||||||
|
child = DockInDockWidget(self, self.__top_level_dock_widget, self.__perspectives_manager)
|
||||||
|
child.setObjectName(group_name)
|
||||||
|
|
||||||
|
dock_widget = QtAds.CDockWidget(group_name)
|
||||||
|
dock_widget.setWidget(child)
|
||||||
|
dock_widget.setIcon(icon)
|
||||||
|
|
||||||
|
insert_pos = self.__mgr.addDockWidget(QtAds.CenterDockWidgetArea, dock_widget, insert_pos)
|
||||||
|
|
||||||
|
return child, insert_pos
|
||||||
|
|
||||||
|
def destroyGroup(self, widget_to_remove: 'DockInDockWidget') -> None:
|
||||||
|
top_level_widget = widget_to_remove.getTopLevelDockWidget()
|
||||||
|
|
||||||
|
if top_level_widget and top_level_widget != widget_to_remove:
|
||||||
|
for dock_widget in widget_to_remove.getManager().getWidgetsInGUIOrder(): #don't use allDockWidgets to preserve sub-groups
|
||||||
|
MoveDockWidgetAction.move(dock_widget, top_level_widget.getManager())
|
||||||
|
assert not widget_to_remove.getManager().allDockWidgets(True, True)
|
||||||
|
|
||||||
|
# find widget's parent:
|
||||||
|
for dock_widget in top_level_widget.getManager().allDockWidgets(True, True):
|
||||||
|
if dockwidget[1].widget() == widget_to_remove:
|
||||||
|
dockwidget[0].removeDockWidget(dockwidget[1])
|
||||||
|
del dockwidget[1]
|
||||||
|
# delete widgetToRemove; automatically deleted when dockWidget is deleted
|
||||||
|
widget_to_remove = None
|
||||||
|
break
|
||||||
|
|
||||||
|
assert widget_to_remove == None
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
def attachViewMenu(self, menu: QMenu) -> None:
|
||||||
|
menu.aboutToShow.connect(self.autoFillAttachedViewMenu)
|
||||||
|
|
||||||
|
def autoFillAttachedViewMenu(self) -> None:
|
||||||
|
menu = self.sender()
|
||||||
|
|
||||||
|
if menu:
|
||||||
|
menu.clear()
|
||||||
|
self.setupViewMenu(menu)
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
def setupViewMenu(self, menu):
|
||||||
|
dock_managers = self.__mgr.allManagers(True, True)
|
||||||
|
|
||||||
|
has_perspectives_menu = False
|
||||||
|
if self.getTopLevelDockWidget() == self:
|
||||||
|
has_perspectives_menu = (self.__perspectives_manager != None)
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
organize = menu
|
||||||
|
if has_perspectives_menu:
|
||||||
|
organize = menu.addMenu("Organize")
|
||||||
|
|
||||||
|
self.setupMenu(organize, dock_managers)
|
||||||
|
|
||||||
|
if has_perspectives_menu:
|
||||||
|
perspectives = menu.addMenu("Perspectives")
|
||||||
|
self.fillPerspectivesMenu(perspectives)
|
||||||
|
|
||||||
|
def setupMenu(self, menu: QMenu, move_to: 'list[DockInDockManager]') -> None:
|
||||||
|
self.__mgr.fillViewMenu(menu, move_to)
|
||||||
|
menu.addSeparator()
|
||||||
|
move_menu = menu.addMenu("Move")
|
||||||
|
self.__mgr.fillMoveMenu(move_menu, move_to)
|
||||||
|
|
||||||
|
def fillPerspectivesMenu(self, menu: QMenu):
|
||||||
|
menu.addAction("Create perspective...", self.createPerspective)
|
||||||
|
perspectives_names = []
|
||||||
|
if self.__perspectives_manager:
|
||||||
|
perspectives_names = self.__perspectives_manager.perspectiveNames()
|
||||||
|
|
||||||
|
if perspectives_names:
|
||||||
|
load = menu.addMenu("Load perspective")
|
||||||
|
for name in perspectives_names:
|
||||||
|
load.addAction(LoadPerspectiveAction(load, name, self))
|
||||||
|
remove = menu.addMenu("Remove perspective")
|
||||||
|
for name in perspectives_names:
|
||||||
|
remove.addAction(RemovePerspectiveAction(remove, name, self))
|
||||||
|
|
||||||
|
def setNewPerspectiveDefaultName(default_name: str) -> None:
|
||||||
|
self.__new_perspective_default_name = default_name
|
||||||
|
|
||||||
|
def createPerspective(self) -> None:
|
||||||
|
if not self.__perspectives_manager:
|
||||||
|
return
|
||||||
|
|
||||||
|
name = self.__new_perspective_default_name
|
||||||
|
if self.__new_perspective_default_name:
|
||||||
|
index = 2
|
||||||
|
while name in self.__perspectives_manager.perspectiveNames():
|
||||||
|
name = f"{self.__new_perspective_default_name}({index})"
|
||||||
|
index += 1
|
||||||
|
|
||||||
|
while True:
|
||||||
|
name, ok = QInputDialog.getText(None, "Create perspective", "Enter perspective name", QLineEdit.Normal, name)
|
||||||
|
if ok:
|
||||||
|
if not name:
|
||||||
|
QMessageBox.critical(None, "Error", "Perspective name cannot be empty")
|
||||||
|
continue
|
||||||
|
elif name in self.__perspectives_manager.perspectiveNames():
|
||||||
|
if QMessageBox.critical(None, "Error", f"Perspective '{name}' already exists, overwrite it?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) == QMessageBox.No:
|
||||||
|
continue
|
||||||
|
|
||||||
|
self.__perspectives_manager.addPerspective(name, self)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
def dumpStatus(self, echo: callable = print, widget: QtAds.CDockWidget = None, tab: str = '', suffix: str = '') -> str:
|
||||||
|
if widget is not None:
|
||||||
|
as_mgr = DockInDockManager.dockInAManager(widget)
|
||||||
|
if as_mgr:
|
||||||
|
as_mgr.parent().dumpStatus(tab=tab)
|
||||||
|
else:
|
||||||
|
echo(tab + widget.objectName() + suffix)
|
||||||
|
else:
|
||||||
|
echo(tab + "Group: " + self.getManager().getGroupName())
|
||||||
|
tab += " "
|
||||||
|
visible_widgets = set()
|
||||||
|
for widget in self.getManager().getWidgetsInGUIOrder():
|
||||||
|
visible_widgets.add(widget)
|
||||||
|
self.dumpStatus(widget=widget, tab=tab)
|
||||||
|
|
||||||
|
for closed in self.getManager().dockWidgetsMap().values():
|
||||||
|
if not closed in visible_widgets:
|
||||||
|
self.dumpStatus(widget=closed, tab=tab, suffix=" (closed)")
|
||||||
334
examples/dockindock/dockindockmanager.cpp
Normal file
@@ -0,0 +1,334 @@
|
|||||||
|
#include "dockindockmanager.h"
|
||||||
|
#include "dockindock.h"
|
||||||
|
|
||||||
|
#include "DockAreaWidget.h"
|
||||||
|
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QInputDialog>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
using namespace QtAdsUtl;
|
||||||
|
|
||||||
|
/////////////////////////////////////
|
||||||
|
// DockInDockManager
|
||||||
|
/////////////////////////////////////
|
||||||
|
DockInDockManager::DockInDockManager( DockInDockWidget& parent ) :
|
||||||
|
baseClass( &parent ),
|
||||||
|
m_parent( parent )
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DockInDockManager::~DockInDockManager()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockInDockManager::fillViewMenu( QMenu* menu, const std::vector<DockInDockManager*>& moveTo )
|
||||||
|
{
|
||||||
|
auto widgetsMap = dockWidgetsMap();
|
||||||
|
for ( auto iter = widgetsMap.begin(); iter != widgetsMap.end(); ++iter )
|
||||||
|
{
|
||||||
|
auto widget = iter.value()->widget();
|
||||||
|
auto action = iter.value()->toggleViewAction();
|
||||||
|
|
||||||
|
DockInDockWidget* asMgr = dynamic_cast<DockInDockWidget*>( widget );
|
||||||
|
if ( asMgr )
|
||||||
|
{
|
||||||
|
auto subMenu = menu->addMenu( iter.key() );
|
||||||
|
|
||||||
|
subMenu->addAction( action );
|
||||||
|
subMenu->addSeparator();
|
||||||
|
|
||||||
|
asMgr->setupMenu( subMenu, moveTo );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
menu->addAction(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( parent().canCreateNewGroups() )
|
||||||
|
{
|
||||||
|
// see how this works, to create it in the right place,
|
||||||
|
// and also to have load perspective work when some groups are missing
|
||||||
|
menu->addSeparator();
|
||||||
|
menu->addAction( new QtAdsUtl::CreateChildDockAction( m_parent, menu ) );
|
||||||
|
|
||||||
|
if ( parent().getTopLevelDockWidget()->getManager() != this )
|
||||||
|
menu->addAction( new QtAdsUtl::DestroyGroupAction( &m_parent, menu ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockInDockManager::fillMoveMenu( QMenu* menu, const std::vector<DockInDockManager*>& moveTo )
|
||||||
|
{
|
||||||
|
auto widgetsMap = dockWidgetsMap();
|
||||||
|
for ( auto iter = widgetsMap.begin(); iter != widgetsMap.end(); ++iter )
|
||||||
|
{
|
||||||
|
auto subMenu = menu->addMenu( iter.key() );
|
||||||
|
|
||||||
|
for ( auto mgr : moveTo )
|
||||||
|
{
|
||||||
|
// iterate over all possible target managers
|
||||||
|
if ( mgr == this )
|
||||||
|
{
|
||||||
|
// if dock is already in mgr, no reason to move it there
|
||||||
|
}
|
||||||
|
else if ( mgr == dockInAManager( iter.value() ) )
|
||||||
|
{
|
||||||
|
// if target is the group itself, can't move it there, would make no sense
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
subMenu->addAction( new MoveDockWidgetAction( iter.value(), mgr, subMenu ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockInDockManager::addPerspectiveRec( const QString& name )
|
||||||
|
{
|
||||||
|
std::vector<DockInDockManager*> managers = allManagers( true, true );
|
||||||
|
|
||||||
|
for ( auto child : managers )
|
||||||
|
child->addPerspective( name );
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockInDockManager::openPerspectiveRec( const QString& name )
|
||||||
|
{
|
||||||
|
std::vector<DockInDockManager*> managers = allManagers( true, true );
|
||||||
|
|
||||||
|
for ( auto child : managers )
|
||||||
|
child->openPerspective( name );
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DockInDockManager::getGroupName()
|
||||||
|
{
|
||||||
|
return parent().objectName();
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CHILD_PREFIX QString("Child-")
|
||||||
|
QString DockInDockManager::getPersistGroupName()
|
||||||
|
{
|
||||||
|
QString group = "Top";
|
||||||
|
if ( !getGroupName().isEmpty() )
|
||||||
|
group = CHILD_PREFIX + getGroupName();
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DockInDockManager::getGroupNameFromPersistGroupName( QString persistGroupName )
|
||||||
|
{
|
||||||
|
if ( persistGroupName.startsWith( CHILD_PREFIX ) )
|
||||||
|
{
|
||||||
|
persistGroupName = persistGroupName.mid( CHILD_PREFIX.size() );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assert( false );
|
||||||
|
}
|
||||||
|
return persistGroupName;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockInDockManager::loadPerspectivesRec(QSettings& Settings)
|
||||||
|
{
|
||||||
|
std::vector<DockInDockManager*> children = allManagers( true, true );
|
||||||
|
|
||||||
|
for ( auto mgr : children )
|
||||||
|
{
|
||||||
|
Settings.beginGroup(mgr->getPersistGroupName());
|
||||||
|
mgr->loadPerspectives( Settings );
|
||||||
|
Settings.endGroup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockInDockManager::savePerspectivesRec(QSettings& Settings) const
|
||||||
|
{
|
||||||
|
std::vector<DockInDockManager*> children = allManagers( true, true );
|
||||||
|
|
||||||
|
for ( auto mgr : children )
|
||||||
|
{
|
||||||
|
Settings.beginGroup(mgr->getPersistGroupName());
|
||||||
|
mgr->savePerspectives( Settings );
|
||||||
|
Settings.endGroup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockInDockManager::removePerspectivesRec()
|
||||||
|
{
|
||||||
|
std::vector<DockInDockManager*> managers = allManagers( true, true );
|
||||||
|
|
||||||
|
for ( auto child : managers )
|
||||||
|
child->removePerspectives( child->perspectiveNames() );
|
||||||
|
}
|
||||||
|
|
||||||
|
DockInDockManager* DockInDockManager::dockInAManager( ads::CDockWidget* widget )
|
||||||
|
{
|
||||||
|
DockInDockWidget* dockWidget = widget ? dynamic_cast<DockInDockWidget*>( widget->widget() ) : NULL;
|
||||||
|
return ( dockWidget ) ? dockWidget->getManager() : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockInDockManager::childManagers( std::vector<DockInDockManager*>& managers, bool rec ) const
|
||||||
|
{
|
||||||
|
auto widgets = getWidgetsInGUIOrder();
|
||||||
|
for ( auto widget : widgets )
|
||||||
|
{
|
||||||
|
DockInDockManager* asMgr = dockInAManager( widget );
|
||||||
|
if ( asMgr )
|
||||||
|
{
|
||||||
|
managers.push_back( asMgr );
|
||||||
|
if ( rec )
|
||||||
|
asMgr->childManagers( managers, rec );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<DockInDockManager*> DockInDockManager::allManagers( bool includeThis, bool rec ) const
|
||||||
|
{
|
||||||
|
std::vector<DockInDockManager*> managers;
|
||||||
|
if ( includeThis )
|
||||||
|
managers.push_back( const_cast<DockInDockManager*>(this) );
|
||||||
|
childManagers( managers, rec );
|
||||||
|
return managers;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<DockInDockManager*,ads::CDockWidget*>> DockInDockManager::allDockWidgets( bool includeThis, bool rec ) const
|
||||||
|
{
|
||||||
|
std::vector<std::pair<DockInDockManager*,ads::CDockWidget*>> widgets;
|
||||||
|
for ( auto mgr : allManagers( includeThis, rec ) )
|
||||||
|
{
|
||||||
|
for ( auto widget : mgr->getWidgetsInGUIOrder() )
|
||||||
|
widgets.push_back( std::make_pair(mgr, widget) );
|
||||||
|
}
|
||||||
|
return widgets;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMap<QString,QStringList> DockInDockManager::getGroupContents()
|
||||||
|
{
|
||||||
|
QMap<QString,QStringList> result;
|
||||||
|
std::vector<DockInDockManager*> managers = allManagers( true, true );
|
||||||
|
for ( auto mgr : managers )
|
||||||
|
{
|
||||||
|
result[mgr->getPersistGroupName()] = mgr->dockWidgetsMap().keys();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ads::CDockAreaWidget* DockInDockManager::getInsertDefaultPos()
|
||||||
|
{
|
||||||
|
ads::CDockAreaWidget* defaultPos = NULL;
|
||||||
|
if ( dockAreaCount() != 0 )
|
||||||
|
defaultPos = dockArea(dockAreaCount()-1);
|
||||||
|
return defaultPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<ads::CDockWidget*> DockInDockManager::getWidgetsInGUIOrder() const
|
||||||
|
{
|
||||||
|
std::vector<ads::CDockWidget*> result;
|
||||||
|
result.reserve( dockWidgetsMap().size() );
|
||||||
|
for ( int i = 0; i != dockAreaCount(); ++i )
|
||||||
|
{
|
||||||
|
for ( auto widget : dockArea(i)->dockWidgets() )
|
||||||
|
result.push_back( widget );
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////
|
||||||
|
// CreateChildDockAction
|
||||||
|
/////////////////////////////////////
|
||||||
|
CreateChildDockAction::CreateChildDockAction( DockInDockWidget& dockInDock, QMenu* menu ) :
|
||||||
|
QAction("New group...", menu),
|
||||||
|
m_dockInDock( dockInDock )
|
||||||
|
{
|
||||||
|
connect( this, SIGNAL(triggered()), this, SLOT(createGroup()) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateChildDockAction::createGroup()
|
||||||
|
{
|
||||||
|
QString name = "";
|
||||||
|
while ( true )
|
||||||
|
{
|
||||||
|
bool ok = false;
|
||||||
|
name = QInputDialog::getText( NULL, this->text(), "Enter group name", QLineEdit::Normal, name, &ok );
|
||||||
|
if ( ok )
|
||||||
|
{
|
||||||
|
QString error = "";
|
||||||
|
if ( m_dockInDock.getTopLevelDockWidget() )
|
||||||
|
error = m_dockInDock.getTopLevelDockWidget()->getGroupNameError( name );
|
||||||
|
else
|
||||||
|
assert( false );
|
||||||
|
|
||||||
|
if ( error.isEmpty() )
|
||||||
|
{
|
||||||
|
ads::CDockAreaWidget* insertPos = NULL;
|
||||||
|
m_dockInDock.createGroup( name, insertPos );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QMessageBox::critical( NULL, "Error", error );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////
|
||||||
|
// DestroyGroupAction
|
||||||
|
/////////////////////////////////////
|
||||||
|
DestroyGroupAction::DestroyGroupAction( DockInDockWidget* widget, QMenu* menu ) :
|
||||||
|
QAction("Destroy " + widget->getManager()->getGroupName(), menu),
|
||||||
|
m_widget( widget )
|
||||||
|
{
|
||||||
|
connect( this, SIGNAL(triggered()), this, SLOT(destroyGroup()) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void DestroyGroupAction::destroyGroup()
|
||||||
|
{
|
||||||
|
m_widget->getTopLevelDockWidget()->destroyGroup( m_widget );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////
|
||||||
|
// MoveDockWidgetAction
|
||||||
|
/////////////////////////////////////
|
||||||
|
MoveDockWidgetAction::MoveDockWidgetAction( ads::CDockWidget* widget, DockInDockManager* moveTo, QMenu* menu ) :
|
||||||
|
QAction(menu),
|
||||||
|
m_widget( widget ),
|
||||||
|
m_moveTo( moveTo )
|
||||||
|
{
|
||||||
|
if ( moveTo->parent().isTopLevel() )
|
||||||
|
{
|
||||||
|
setText( "To top" );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setText( "To " + moveTo->parent().objectName() );
|
||||||
|
}
|
||||||
|
connect( this, SIGNAL(triggered()), this, SLOT(move()) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void MoveDockWidgetAction::move()
|
||||||
|
{
|
||||||
|
move( m_widget, m_moveTo );
|
||||||
|
}
|
||||||
|
|
||||||
|
void MoveDockWidgetAction::move( ads::CDockWidget* widget, DockInDockManager* moveTo )
|
||||||
|
{
|
||||||
|
if ( widget && moveTo )
|
||||||
|
{
|
||||||
|
widget->dockManager()->removeDockWidget( widget );
|
||||||
|
moveTo->addDockWidget(ads::CenterDockWidgetArea, widget, moveTo->getInsertDefaultPos());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assert( false );
|
||||||
|
}
|
||||||
|
}
|
||||||
96
examples/dockindock/dockindockmanager.h
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "DockManager.h"
|
||||||
|
|
||||||
|
#include <QAction>
|
||||||
|
#include <QMap>
|
||||||
|
|
||||||
|
namespace QtAdsUtl
|
||||||
|
{
|
||||||
|
|
||||||
|
class DockInDockWidget;
|
||||||
|
class DockInDockManager : public ads::CDockManager
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
typedef ads::CDockManager baseClass;
|
||||||
|
|
||||||
|
public:
|
||||||
|
DockInDockManager( DockInDockWidget& parent );
|
||||||
|
~DockInDockManager() override;
|
||||||
|
|
||||||
|
void fillViewMenu( QMenu* menu, const std::vector<DockInDockManager*>& moveTo );
|
||||||
|
void fillMoveMenu( QMenu* menu, const std::vector<DockInDockManager*>& moveTo );
|
||||||
|
|
||||||
|
void addPerspectiveRec( const QString& name );
|
||||||
|
void openPerspectiveRec( const QString& name );
|
||||||
|
void removePerspectivesRec();
|
||||||
|
void loadPerspectivesRec(QSettings& Settings);
|
||||||
|
void savePerspectivesRec(QSettings& Settings) const;
|
||||||
|
|
||||||
|
static DockInDockManager* dockInAManager( ads::CDockWidget* widget );
|
||||||
|
|
||||||
|
inline DockInDockWidget& parent() { return m_parent; }
|
||||||
|
|
||||||
|
void childManagers( std::vector<DockInDockManager*>& managers, bool rec ) const;
|
||||||
|
std::vector<DockInDockManager*> allManagers( bool includeThis, bool rec ) const;
|
||||||
|
std::vector<std::pair<DockInDockManager*,ads::CDockWidget*>> allDockWidgets( bool includeThis, bool rec ) const;
|
||||||
|
|
||||||
|
QString getGroupName();
|
||||||
|
QString getPersistGroupName();
|
||||||
|
static QString getGroupNameFromPersistGroupName( QString persistGroupName );
|
||||||
|
|
||||||
|
QMap<QString,QStringList> getGroupContents();
|
||||||
|
|
||||||
|
ads::CDockAreaWidget* getInsertDefaultPos();
|
||||||
|
|
||||||
|
std::vector<ads::CDockWidget*> getWidgetsInGUIOrder() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
DockInDockWidget& m_parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CreateChildDockAction : public QAction
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
CreateChildDockAction( DockInDockWidget& dockInDock, QMenu* menu );
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void createGroup();
|
||||||
|
|
||||||
|
private:
|
||||||
|
DockInDockWidget& m_dockInDock;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DestroyGroupAction : public QAction
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
DestroyGroupAction( DockInDockWidget* widget, QMenu* menu );
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void destroyGroup();
|
||||||
|
|
||||||
|
private:
|
||||||
|
DockInDockWidget* m_widget;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MoveDockWidgetAction : public QAction
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
MoveDockWidgetAction( ads::CDockWidget* widget, DockInDockManager* moveTo, QMenu* menu );
|
||||||
|
|
||||||
|
static void move( ads::CDockWidget* widget, DockInDockManager* moveTo );
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void move();
|
||||||
|
|
||||||
|
private:
|
||||||
|
ads::CDockWidget* m_widget;
|
||||||
|
DockInDockManager* m_moveTo;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
214
examples/dockindock/dockindockmanager.py
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
from PyQt5.QtWidgets import QAction, QMenu, QInputDialog, QLineEdit
|
||||||
|
from PyQt5.QtCore import QSettings
|
||||||
|
|
||||||
|
from PyQtAds import QtAds
|
||||||
|
|
||||||
|
CHILD_PREFIX = "Child-"
|
||||||
|
|
||||||
|
class DockInDockManager(QtAds.CDockManager):
|
||||||
|
def __init__(self, parent: 'DockInDockWidget'):
|
||||||
|
super().__init__()
|
||||||
|
self.__parent = parent
|
||||||
|
|
||||||
|
def parent(self) -> 'DockInDockWidget':
|
||||||
|
return self.__parent
|
||||||
|
|
||||||
|
def fillViewMenu(self, menu: QMenu, move_to: 'dict[DockInDockManager]') -> None:
|
||||||
|
from dockindock import DockInDockWidget # Prevent cyclic import
|
||||||
|
|
||||||
|
widgets_map = self.dockWidgetsMap()
|
||||||
|
for key, value in widgets_map.items():
|
||||||
|
widget = value.widget()
|
||||||
|
action = value.toggleViewAction()
|
||||||
|
|
||||||
|
if isinstance(widget, DockInDockWidget):
|
||||||
|
sub_menu = menu.addMenu(key)
|
||||||
|
|
||||||
|
sub_menu.addAction(action)
|
||||||
|
sub_menu.addSeparator()
|
||||||
|
|
||||||
|
widget.setupMenu(sub_menu, move_to)
|
||||||
|
else:
|
||||||
|
menu.addAction(action)
|
||||||
|
|
||||||
|
if self.parent().canCreateNewGroups():
|
||||||
|
# see how this works, to create it in the right place,
|
||||||
|
# and also to have load perspective work when some groups are missing
|
||||||
|
menu.addSeparator()
|
||||||
|
menu.addAction(CreateChildDockAction(self.__parent, menu))
|
||||||
|
|
||||||
|
if self.parent().getTopLevelDockWidget().getManager() != self:
|
||||||
|
menu.addAction(DestroyGroupAction( self.parent, menu))
|
||||||
|
|
||||||
|
def fillMoveMenu(self, menu: QMenu, move_to: 'list[DockInDockManager]') -> None:
|
||||||
|
widgets_map = self.dockWidgetsMap()
|
||||||
|
for key, value in widgets_map.items():
|
||||||
|
sub_menu = menu.addMenu(key)
|
||||||
|
|
||||||
|
for mgr in move_to:
|
||||||
|
# iterate over all possible target managers
|
||||||
|
if mgr == self:
|
||||||
|
pass # if dock is already in mgr, no reason to move it there
|
||||||
|
elif mgr == DockInDockManager.dockInAManager(value):
|
||||||
|
pass # if target is the group itself, can't move it there, would make no sense
|
||||||
|
else:
|
||||||
|
sub_menu.addAction(MoveDockWidgetAction(value, mgr, sub_menu))
|
||||||
|
|
||||||
|
def addPerspectiveRec(self, name: str) -> None:
|
||||||
|
managers = self.allManagers(True, True)
|
||||||
|
|
||||||
|
for child in managers:
|
||||||
|
child.addPerspective(name)
|
||||||
|
|
||||||
|
def openPerspectiveRec(self, name: str) -> None:
|
||||||
|
managers = self.allManagers(True, True)
|
||||||
|
|
||||||
|
for child in managers:
|
||||||
|
child.openPerspective(name)
|
||||||
|
|
||||||
|
def getGroupName(self) -> str:
|
||||||
|
return self.parent().objectName()
|
||||||
|
|
||||||
|
def getPersistGroupName(self) -> str:
|
||||||
|
group = "Top"
|
||||||
|
if self.getGroupName():
|
||||||
|
group = CHILD_PREFIX + self.getGroupName()
|
||||||
|
return group
|
||||||
|
|
||||||
|
def getGroupNameFromPersistGroupName(self, persist_group_name) -> str:
|
||||||
|
if persist_group_name.startswith(CHILD_PREFIX):
|
||||||
|
persist_group_name = persist_group_name[len(CHILD_PREFIX):]
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
return persist_group_name
|
||||||
|
|
||||||
|
def loadPerspectivesRec(self, settings: QSettings) -> None:
|
||||||
|
children = self.allManagers(True, True)
|
||||||
|
|
||||||
|
for mgr in children:
|
||||||
|
settings.beginGroup(mgr.getPersistGroupName())
|
||||||
|
mgr.loadPerspectives(settings)
|
||||||
|
settings.endGroup()
|
||||||
|
|
||||||
|
def savePerspectivesRec(self, settings: QSettings) -> None:
|
||||||
|
children = self.allManagers(True, True)
|
||||||
|
|
||||||
|
for mgr in children:
|
||||||
|
settings.beginGroup(mgr.getPersistGroupName())
|
||||||
|
mgr.savePerspectives(settings)
|
||||||
|
settings.endGroup()
|
||||||
|
|
||||||
|
def removePerspectivesRec(self, settings: QSettings) -> None:
|
||||||
|
children = self.allManagers(True, True)
|
||||||
|
|
||||||
|
for mgr in children:
|
||||||
|
child.removePerspectives(child.perspectiveNames())
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def dockInAManager(widget) -> 'DockInDockManager':
|
||||||
|
from dockindock import DockInDockWidget # Prevent cyclic import
|
||||||
|
|
||||||
|
dock_widget = widget.widget() if widget else None
|
||||||
|
return dock_widget.getManager() if isinstance(dock_widget, DockInDockWidget) else None
|
||||||
|
|
||||||
|
def childManagers(self, managers: 'list[DockInDockManager]', rec: bool) -> None:
|
||||||
|
widgets = self.getWidgetsInGUIOrder()
|
||||||
|
for widget in widgets:
|
||||||
|
as_mgr = DockInDockManager.dockInAManager(widget)
|
||||||
|
if as_mgr:
|
||||||
|
managers.append(as_mgr)
|
||||||
|
if rec:
|
||||||
|
as_mgr.childManagers(managers, rec)
|
||||||
|
|
||||||
|
def allManagers(self, include_self: bool, rec: bool) -> 'list[DockInDockManager]':
|
||||||
|
managers = []
|
||||||
|
if include_self:
|
||||||
|
managers.append(self)
|
||||||
|
self.childManagers(managers, rec)
|
||||||
|
return managers
|
||||||
|
|
||||||
|
def allDockWidgets(self, include_self: bool, rec: bool) -> 'list[tuple[DockInDockManager, QtAds.CDockWidget]]':
|
||||||
|
widgets = []
|
||||||
|
for mgr in self.allManagers(include_self, rec):
|
||||||
|
for widget in mgr.getWidgetsInGUIOrder():
|
||||||
|
widgets.append((mgr, widget))
|
||||||
|
return widgets
|
||||||
|
|
||||||
|
def getGroupContents(self) -> 'dict[str, list[str]]':
|
||||||
|
result = {}
|
||||||
|
managers = self.allManagers(True, True)
|
||||||
|
for mgr in managers:
|
||||||
|
result[mgr.getPersistGroupName()] = mgr.dockWidgetsMap().keys()
|
||||||
|
return result
|
||||||
|
|
||||||
|
def getInsertDefaultPos(self) -> QtAds.CDockAreaWidget:
|
||||||
|
default_pos = None
|
||||||
|
if self.dockAreaCount() != 0:
|
||||||
|
default_pos = self.dockArea(self.dockAreaCount()-1)
|
||||||
|
return default_pos
|
||||||
|
|
||||||
|
def getWidgetsInGUIOrder(self) -> 'list[QtAds.CDockWidget]':
|
||||||
|
result = []
|
||||||
|
for i in range(self.dockAreaCount()):
|
||||||
|
for widget in self.dockArea(i).dockWidgets():
|
||||||
|
result.append(widget)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class CreateChildDockAction(QAction):
|
||||||
|
def __init__(self, dock_in_dock: 'DockInDockWidget', menu: QMenu):
|
||||||
|
super().__init__("New group...", menu)
|
||||||
|
self.__dock_in_dock = dock_in_dock
|
||||||
|
self.triggered.connect(self.createGroup)
|
||||||
|
|
||||||
|
def createGroup(self) -> None:
|
||||||
|
name = ""
|
||||||
|
while True:
|
||||||
|
name, ok = QInputDialog.getText(None, self.text(), "Enter group name", QLineEdit.Normal, name)
|
||||||
|
if ok:
|
||||||
|
error = ""
|
||||||
|
if self.__dock_in_dock.getTopLevelDockWidget():
|
||||||
|
error = self.__dock_in_dock.getTopLevelDockWidget().getGroupNameError(name)
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
if not error:
|
||||||
|
self.__dock_in_dock.createGroup(name, None)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
QMessageBox.critical(None, "Error", error)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
class DestroyGroupAction(QAction):
|
||||||
|
def __init__(self, widget: 'DockInDockWidget', menu: QMenu):
|
||||||
|
super().__init__("Destroy" + widget.getManager().getGroupName(), menu)
|
||||||
|
self.__widget = widget
|
||||||
|
self.triggered.connect(self.destroyGroup)
|
||||||
|
|
||||||
|
def destroyGroup(self) -> None:
|
||||||
|
self.__widget.getTopLevelDockWidget().destroyGroup(self.__widget)
|
||||||
|
|
||||||
|
|
||||||
|
class MoveDockWidgetAction(QAction):
|
||||||
|
def __init__(self, widget: 'DockInDockWidget', move_to: DockInDockManager, menu: QMenu):
|
||||||
|
super().__init__(menu)
|
||||||
|
self.__widget = widget
|
||||||
|
self.__move_to = move_to
|
||||||
|
|
||||||
|
if move_to.parent().isTopLevel():
|
||||||
|
self.setText("To top")
|
||||||
|
else:
|
||||||
|
self.setText(f"To {move_to.parent().objectName()}")
|
||||||
|
self.triggered.connect(self._move)
|
||||||
|
|
||||||
|
def _move(self) -> None:
|
||||||
|
self.move(self.__widget, self.__move_to)
|
||||||
|
|
||||||
|
def move(self, widget: QtAds.CDockWidget, move_to: QtAds.CDockManager) -> None:
|
||||||
|
if widget and move_to:
|
||||||
|
widget.dockManager().removeDockWidget(widget)
|
||||||
|
move_to.addDockWidget(QtAds.CenterDockWidgetArea, widget, move_to.getInsertDefaultPos())
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
11
examples/dockindock/main.cpp
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#include <QApplication>
|
||||||
|
#include "../../examples/simple/MainWindow.h"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QApplication a(argc, argv);
|
||||||
|
MainWindow w;
|
||||||
|
w.show();
|
||||||
|
|
||||||
|
return a.exec();
|
||||||
|
}
|
||||||
72
examples/dockindock/main.py
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import atexit
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel
|
||||||
|
from PyQt5.QtCore import Qt
|
||||||
|
from PyQtAds import QtAds
|
||||||
|
|
||||||
|
from perspectives import PerspectivesManager
|
||||||
|
from dockindock import DockInDockWidget
|
||||||
|
|
||||||
|
class MainWindow(QMainWindow):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self.perspectives_manager = PerspectivesManager("persist")
|
||||||
|
self.resize(400, 400)
|
||||||
|
self.dock_manager = DockInDockWidget(self, self.perspectives_manager, can_create_new_groups=True)
|
||||||
|
self.setCentralWidget(self.dock_manager)
|
||||||
|
self.dock_manager.attachViewMenu(self.menuBar().addMenu("View"))
|
||||||
|
|
||||||
|
previous_dock_widget = None
|
||||||
|
for i in range(3):
|
||||||
|
l = QLabel()
|
||||||
|
l.setWordWrap(True)
|
||||||
|
l.setAlignment(Qt.AlignTop | Qt.AlignLeft)
|
||||||
|
l.setText("Lorem ipsum dolor sit amet, consectetuer adipiscing elit. ")
|
||||||
|
|
||||||
|
previous_dock_widget = self.dock_manager.addTabWidget(l, f"Top label {i}", previous_dock_widget)
|
||||||
|
|
||||||
|
last_top_level_dock = previous_dock_widget
|
||||||
|
|
||||||
|
for j in range(2):
|
||||||
|
group_manager, _ = self.dock_manager.createGroup(f"Group {j}", last_top_level_dock)
|
||||||
|
|
||||||
|
previous_dock_widget = None
|
||||||
|
|
||||||
|
for i in range(3):
|
||||||
|
# Create example content label - this can be any application specific widget
|
||||||
|
l = QLabel()
|
||||||
|
l.setWordWrap(True)
|
||||||
|
l.setAlignment(Qt.AlignTop | Qt.AlignLeft)
|
||||||
|
l.setText("Lorem ipsum dolor sit amet, consectetuer adipiscing elit. ")
|
||||||
|
|
||||||
|
previous_dock_widget = group_manager.addTabWidget(l, f"ZInner {j}/{i}", previous_dock_widget)
|
||||||
|
|
||||||
|
# create sub-group
|
||||||
|
sub_group, _ = group_manager.createGroup(f"SubGroup {j}", previous_dock_widget)
|
||||||
|
previous_dock_widget = None
|
||||||
|
for i in range(3):
|
||||||
|
# Create example content label - this can be any application specific widget
|
||||||
|
l = QLabel()
|
||||||
|
l.setWordWrap(True)
|
||||||
|
l.setAlignment(Qt.AlignTop | Qt.AlignLeft)
|
||||||
|
l.setText("Lorem ipsum dolor sit amet, consectetuer adipiscing elit. ")
|
||||||
|
|
||||||
|
previous_dock_widget = sub_group.addTabWidget(l, f"SubInner {j}/{i}", previous_dock_widget)
|
||||||
|
|
||||||
|
self.perspectives_manager.loadPerspectives()
|
||||||
|
|
||||||
|
atexit.register(self.cleanup)
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
self.perspectives_manager.savePerspectives()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app = QApplication(sys.argv)
|
||||||
|
|
||||||
|
w = MainWindow()
|
||||||
|
w.show()
|
||||||
|
app.exec_()
|
||||||
76
examples/dockindock/mainframe.cpp
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
#include "mainframe.h"
|
||||||
|
|
||||||
|
#include "dockindock.h"
|
||||||
|
#include "perspectives.h"
|
||||||
|
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QMenuBar>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QSettings>
|
||||||
|
|
||||||
|
MainWindow::MainWindow(QWidget *parent) :
|
||||||
|
QMainWindow(parent),
|
||||||
|
m_perspectivesManager( new QtAdsUtl::PerspectivesManager( "persist" ) )
|
||||||
|
{
|
||||||
|
resize( 400, 400 );
|
||||||
|
|
||||||
|
setCentralWidget( m_dockManager = new QtAdsUtl::DockInDockWidget(this,true,m_perspectivesManager.get()) );
|
||||||
|
|
||||||
|
m_dockManager->attachViewMenu( menuBar()->addMenu( "View" ) );
|
||||||
|
|
||||||
|
ads::CDockAreaWidget* previousDockWidget = NULL;
|
||||||
|
for ( int i = 0; i != 3; ++i )
|
||||||
|
{
|
||||||
|
// Create example content label - this can be any application specific
|
||||||
|
// widget
|
||||||
|
QLabel* l = new QLabel();
|
||||||
|
l->setWordWrap(true);
|
||||||
|
l->setAlignment(Qt::AlignTop | Qt::AlignLeft);
|
||||||
|
l->setText("Lorem ipsum dolor sit amet, consectetuer adipiscing elit. ");
|
||||||
|
|
||||||
|
previousDockWidget = m_dockManager->addTabWidget( l, "Top label " + QString::number(i), previousDockWidget );
|
||||||
|
}
|
||||||
|
|
||||||
|
auto lastTopLevelDock = previousDockWidget;
|
||||||
|
|
||||||
|
for ( int j = 0; j != 2; ++j )
|
||||||
|
{
|
||||||
|
QtAdsUtl::DockInDockWidget* groupManager = m_dockManager->createGroup( "Group " + QString::number(j), lastTopLevelDock );
|
||||||
|
|
||||||
|
previousDockWidget = NULL;
|
||||||
|
for ( int i = 0; i != 3; ++i )
|
||||||
|
{
|
||||||
|
// Create example content label - this can be any application specific
|
||||||
|
// widget
|
||||||
|
QLabel* l = new QLabel();
|
||||||
|
l->setWordWrap(true);
|
||||||
|
l->setAlignment(Qt::AlignTop | Qt::AlignLeft);
|
||||||
|
l->setText("Lorem ipsum dolor sit amet, consectetuer adipiscing elit. ");
|
||||||
|
|
||||||
|
previousDockWidget = groupManager->addTabWidget( l, "ZInner " + QString::number(j) + "/" + QString::number(i), previousDockWidget );
|
||||||
|
}
|
||||||
|
|
||||||
|
// create sub-group
|
||||||
|
auto subGroup = groupManager->createGroup( "SubGroup " + QString::number(j), previousDockWidget );
|
||||||
|
previousDockWidget = NULL;
|
||||||
|
for ( int i = 0; i != 3; ++i )
|
||||||
|
{
|
||||||
|
// Create example content label - this can be any application specific
|
||||||
|
// widget
|
||||||
|
QLabel* l = new QLabel();
|
||||||
|
l->setWordWrap(true);
|
||||||
|
l->setAlignment(Qt::AlignTop | Qt::AlignLeft);
|
||||||
|
l->setText("Lorem ipsum dolor sit amet, consectetuer adipiscing elit. ");
|
||||||
|
|
||||||
|
previousDockWidget = subGroup->addTabWidget( l, "SubInner " + QString::number(j) + "/" + QString::number(i), previousDockWidget );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_perspectivesManager->loadPerspectives();
|
||||||
|
}
|
||||||
|
|
||||||
|
MainWindow::~MainWindow()
|
||||||
|
{
|
||||||
|
m_perspectivesManager->savePerspectives();
|
||||||
|
}
|
||||||
|
|
||||||
30
examples/dockindock/mainframe.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include <QAction>
|
||||||
|
#include <QSettings>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
namespace QtAdsUtl
|
||||||
|
{
|
||||||
|
class DockInDockWidget;
|
||||||
|
class PerspectivesManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MainWindow : public QMainWindow
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit MainWindow(QWidget *parent = 0);
|
||||||
|
~MainWindow();
|
||||||
|
|
||||||
|
QtAdsUtl::DockInDockWidget* m_dockManager;
|
||||||
|
std::unique_ptr<QtAdsUtl::PerspectivesManager> m_perspectivesManager;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
39
examples/dockindock/perspectiveactions.cpp
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#include "perspectiveactions.h"
|
||||||
|
#include "dockindock.h"
|
||||||
|
#include "perspectives.h"
|
||||||
|
|
||||||
|
#include <QMenu>
|
||||||
|
|
||||||
|
using namespace QtAdsUtl;
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
// LoadPerspectiveAction
|
||||||
|
//////////////////////////////
|
||||||
|
LoadPerspectiveAction::LoadPerspectiveAction( QMenu* parent, const QString& name, QtAdsUtl::DockInDockWidget& dockManager ) :
|
||||||
|
QAction( name, parent ),
|
||||||
|
name( name ),
|
||||||
|
dockManager( dockManager )
|
||||||
|
{
|
||||||
|
connect( this, SIGNAL(triggered()), this, SLOT(load()) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoadPerspectiveAction::load()
|
||||||
|
{
|
||||||
|
dockManager.getPerspectivesManager()->openPerspective( name, dockManager );
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
// RemovePerspectiveAction
|
||||||
|
//////////////////////////////
|
||||||
|
RemovePerspectiveAction::RemovePerspectiveAction( QMenu* parent, const QString& name, QtAdsUtl::DockInDockWidget& dockManager ) :
|
||||||
|
QAction( name, parent ),
|
||||||
|
name( name ),
|
||||||
|
dockManager( dockManager )
|
||||||
|
{
|
||||||
|
connect( this, SIGNAL(triggered()), this, SLOT(remove()) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemovePerspectiveAction::remove()
|
||||||
|
{
|
||||||
|
dockManager.getPerspectivesManager()->removePerspective( name );
|
||||||
|
}
|
||||||
38
examples/dockindock/perspectiveactions.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QAction>
|
||||||
|
|
||||||
|
namespace QtAdsUtl
|
||||||
|
{
|
||||||
|
|
||||||
|
class DockInDockWidget;
|
||||||
|
class LoadPerspectiveAction : public QAction
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
LoadPerspectiveAction( QMenu* parent, const QString& name, QtAdsUtl::DockInDockWidget& dockManager );
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void load();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString name;
|
||||||
|
QtAdsUtl::DockInDockWidget& dockManager;
|
||||||
|
};
|
||||||
|
|
||||||
|
class RemovePerspectiveAction : public QAction
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
RemovePerspectiveAction( QMenu* parent, const QString& name, QtAdsUtl::DockInDockWidget& dockManager );
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void remove();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString name;
|
||||||
|
QtAdsUtl::DockInDockWidget& dockManager;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
25
examples/dockindock/perspectiveactions.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
from PyQt5.QtWidgets import QAction, QMenu
|
||||||
|
|
||||||
|
|
||||||
|
class LoadPerspectiveAction(QAction):
|
||||||
|
def __init__(self, parent: QMenu, name: str, dock_manager: 'DockInDockWidget'):
|
||||||
|
super().__init__(name, parent)
|
||||||
|
self.name = name
|
||||||
|
self.dock_manager = dock_manager
|
||||||
|
|
||||||
|
self.triggered.connect(self.load)
|
||||||
|
|
||||||
|
def load(self):
|
||||||
|
self.dock_manager.getPerspectivesManager().openPerspective(self.name, self.dock_manager)
|
||||||
|
|
||||||
|
|
||||||
|
class RemovePerspectiveAction(QAction):
|
||||||
|
def __init__(self, parent: QMenu, name: str, dock_manager: 'DockInDockWidget'):
|
||||||
|
super().__init__(name, parent)
|
||||||
|
self.name = name
|
||||||
|
self.dock_manager = dock_manager
|
||||||
|
|
||||||
|
self.triggered.connect(self.remove)
|
||||||
|
|
||||||
|
def remove(self):
|
||||||
|
self.dock_manager.getPerspectivesManager().removePerspective(self.name)
|
||||||
280
examples/dockindock/perspectives.cpp
Normal file
@@ -0,0 +1,280 @@
|
|||||||
|
#include "perspectives.h"
|
||||||
|
#include "dockindock.h"
|
||||||
|
#include "dockindockmanager.h"
|
||||||
|
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QDir>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#define GROUP_PREFIX QString("Group")
|
||||||
|
|
||||||
|
using namespace QtAdsUtl;
|
||||||
|
|
||||||
|
PerspectivesManager::PerspectivesManager( const QString& perspectivesFolder ) :
|
||||||
|
m_perspectivesFolder( perspectivesFolder )
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
PerspectivesManager::~PerspectivesManager()
|
||||||
|
{
|
||||||
|
// remove temp files:
|
||||||
|
for ( auto perspective : m_perspectives )
|
||||||
|
{
|
||||||
|
QString fileName = perspective.settings->fileName();
|
||||||
|
perspective.settings.reset();
|
||||||
|
QFile::remove(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QStringList PerspectivesManager::perspectiveNames() const
|
||||||
|
{
|
||||||
|
return m_perspectives.keys();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerspectivesManager::addPerspective( const QString& name, DockInDockWidget& widget )
|
||||||
|
{
|
||||||
|
if ( !m_perspectivesFolder.isEmpty() )
|
||||||
|
{
|
||||||
|
m_perspectives[name].settings = getSettingsObject( getSettingsFileName( name, true ) );
|
||||||
|
m_perspectives[name].groups = widget.getManager()->getGroupContents();
|
||||||
|
|
||||||
|
// save perspective internally
|
||||||
|
widget.getManager()->addPerspectiveRec( name );
|
||||||
|
// store it in QSettings object
|
||||||
|
widget.getManager()->savePerspectivesRec( *(m_perspectives[name].settings) );
|
||||||
|
// remove internal perspectives
|
||||||
|
widget.getManager()->removePerspectives( widget.getManager()->perspectiveNames() );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assert( false );
|
||||||
|
}
|
||||||
|
|
||||||
|
emit perspectivesListChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ads::CDockWidget* findWidget( QString name, const std::vector<DockInDockManager*>& managers )
|
||||||
|
{
|
||||||
|
for ( auto mgr : managers )
|
||||||
|
{
|
||||||
|
auto widget = mgr->findDockWidget(name);
|
||||||
|
if ( widget )
|
||||||
|
return widget;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerspectivesManager::openPerspective( const QString& name, DockInDockWidget& widget )
|
||||||
|
{
|
||||||
|
assert( widget.getTopLevelDockWidget() == &widget );
|
||||||
|
|
||||||
|
if ( !m_perspectivesFolder.isEmpty() )
|
||||||
|
{
|
||||||
|
if ( m_perspectives.contains( name ) )
|
||||||
|
{
|
||||||
|
emit openingPerspective();
|
||||||
|
|
||||||
|
if ( widget.canCreateNewGroups() )
|
||||||
|
{
|
||||||
|
auto curGroups = widget.getManager()->allManagers(true,true);
|
||||||
|
for ( auto group : m_perspectives[name].groups.keys() )
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
for ( auto curgroup : curGroups )
|
||||||
|
{
|
||||||
|
if ( curgroup->getPersistGroupName() == group )
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( !found )
|
||||||
|
{
|
||||||
|
group = DockInDockManager::getGroupNameFromPersistGroupName( group );
|
||||||
|
|
||||||
|
// restore group in file but not in GUI yet
|
||||||
|
ads::CDockAreaWidget* insertPos = NULL;
|
||||||
|
widget.createGroup( group, insertPos );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
curGroups = widget.getManager()->allManagers(false,true);
|
||||||
|
for ( auto curgroup : curGroups )
|
||||||
|
{
|
||||||
|
if ( !m_perspectives[name].groups.keys().contains( curgroup->getPersistGroupName() ) )
|
||||||
|
{
|
||||||
|
widget.destroyGroup( &curgroup->parent() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto managers = widget.getManager()->allManagers(true,true);
|
||||||
|
for ( auto group : m_perspectives[name].groups.keys() )
|
||||||
|
{
|
||||||
|
for ( auto mgr : managers )
|
||||||
|
{
|
||||||
|
if ( mgr->getPersistGroupName() == group )
|
||||||
|
{
|
||||||
|
for ( QString widgetName : m_perspectives[name].groups[group] )
|
||||||
|
{
|
||||||
|
ads::CDockWidget* widget = findWidget( widgetName, { mgr } );
|
||||||
|
if ( widget )
|
||||||
|
{
|
||||||
|
// OK, widget is already in the good manager!
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
widget = findWidget( widgetName, managers );
|
||||||
|
if ( widget )
|
||||||
|
{
|
||||||
|
// move dock widget in the same group as it used to be when perspective was saved
|
||||||
|
// this guarantee load/open perspectives will work smartly
|
||||||
|
MoveDockWidgetAction::move( widget, mgr );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// internally load perspectives from QSettings
|
||||||
|
widget.getManager()->loadPerspectivesRec( *(m_perspectives[name].settings) );
|
||||||
|
// load perspective (update GUI)
|
||||||
|
widget.getManager()->openPerspectiveRec( name );
|
||||||
|
// remove internal perspectives
|
||||||
|
widget.getManager()->removePerspectives( widget.getManager()->perspectiveNames() );
|
||||||
|
|
||||||
|
emit openedPerspective();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assert( false );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerspectivesManager::removePerspectives()
|
||||||
|
{
|
||||||
|
m_perspectives.clear();
|
||||||
|
emit perspectivesListChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerspectivesManager::removePerspective( const QString& name )
|
||||||
|
{
|
||||||
|
m_perspectives.remove( name );
|
||||||
|
emit perspectivesListChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PerspectivesManager::getSettingsFileName( const QString& perspective, bool temp ) const
|
||||||
|
{
|
||||||
|
auto name = ( perspective.isEmpty() ) ? "perspectives.ini" : "perspective_" + perspective + (temp?".tmp":".ini");
|
||||||
|
|
||||||
|
return m_perspectivesFolder + "/" + name;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<QSettings> PerspectivesManager::getSettingsObject( const QString& filePath ) const
|
||||||
|
{
|
||||||
|
return std::make_shared<QSettings>(filePath, QSettings::IniFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerspectivesManager::loadPerspectives()
|
||||||
|
{
|
||||||
|
if ( !m_perspectivesFolder.isEmpty() )
|
||||||
|
{
|
||||||
|
QDir().mkpath( m_perspectivesFolder );
|
||||||
|
|
||||||
|
m_perspectives.clear();
|
||||||
|
|
||||||
|
auto mainSettings = getSettingsObject( getSettingsFileName( "", false ) );
|
||||||
|
std::string debug = mainSettings->fileName().toStdString();
|
||||||
|
|
||||||
|
int Size = mainSettings->beginReadArray("Perspectives");
|
||||||
|
|
||||||
|
for (int i = 0; i < Size; ++i)
|
||||||
|
{
|
||||||
|
mainSettings->setArrayIndex(i);
|
||||||
|
QString perspective = mainSettings->value("Name").toString();
|
||||||
|
|
||||||
|
if ( !perspective.isEmpty() )
|
||||||
|
{
|
||||||
|
// load perspective file:
|
||||||
|
auto toLoad = getSettingsFileName( perspective, false );
|
||||||
|
auto loaded = getSettingsFileName( perspective, true );
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
std::string debug1 = loaded.toStdString();
|
||||||
|
std::string debug2 = toLoad.toStdString();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QFile::remove( loaded );
|
||||||
|
if ( !QFile::copy( toLoad, loaded ) )
|
||||||
|
assert( false );
|
||||||
|
|
||||||
|
m_perspectives[perspective].settings = getSettingsObject( loaded );
|
||||||
|
|
||||||
|
// load group info:
|
||||||
|
mainSettings->beginGroup(GROUP_PREFIX);
|
||||||
|
for (const auto& key : mainSettings->allKeys())
|
||||||
|
{
|
||||||
|
m_perspectives[perspective].groups[key] = mainSettings->value( key ).toStringList();
|
||||||
|
}
|
||||||
|
mainSettings->endGroup();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assert( false );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mainSettings->endArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
emit perspectivesListChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerspectivesManager::savePerspectives() const
|
||||||
|
{
|
||||||
|
if ( !m_perspectivesFolder.isEmpty() )
|
||||||
|
{
|
||||||
|
auto mainSettings = getSettingsObject( getSettingsFileName( "", false ) );
|
||||||
|
|
||||||
|
// Save list of perspective and group organization
|
||||||
|
mainSettings->beginWriteArray("Perspectives", m_perspectives.size());
|
||||||
|
int i = 0;
|
||||||
|
for ( auto perspective : m_perspectives.keys() )
|
||||||
|
{
|
||||||
|
mainSettings->setArrayIndex(i);
|
||||||
|
mainSettings->setValue("Name", perspective);
|
||||||
|
mainSettings->beginGroup(GROUP_PREFIX);
|
||||||
|
for ( auto group : m_perspectives[perspective].groups.keys() )
|
||||||
|
{
|
||||||
|
mainSettings->setValue( group, m_perspectives[perspective].groups[group] );
|
||||||
|
}
|
||||||
|
mainSettings->endGroup();
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
mainSettings->endArray();
|
||||||
|
|
||||||
|
// Save perspectives themselves
|
||||||
|
for ( auto perspectiveName : m_perspectives.keys() )
|
||||||
|
{
|
||||||
|
auto toSave = getSettingsFileName( perspectiveName, false );
|
||||||
|
QSettings& settings = *(m_perspectives[perspectiveName].settings);
|
||||||
|
settings.sync();
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
std::string debug1 = settings.fileName().toStdString();
|
||||||
|
std::string debug2 = toSave.toStdString();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QFile::remove( toSave );
|
||||||
|
if ( !QFile::copy( settings.fileName(), toSave ) )
|
||||||
|
assert( false );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
59
examples/dockindock/perspectives.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QMap>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class QMenu;
|
||||||
|
class QSettings;
|
||||||
|
|
||||||
|
namespace QtAdsUtl
|
||||||
|
{
|
||||||
|
|
||||||
|
class DockInDockWidget;
|
||||||
|
class PerspectivesManager : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
PerspectivesManager( const QString& perspectivesFolder );
|
||||||
|
virtual ~PerspectivesManager();
|
||||||
|
|
||||||
|
QStringList perspectiveNames() const;
|
||||||
|
|
||||||
|
void addPerspective( const QString& name, DockInDockWidget& widget );
|
||||||
|
void openPerspective( const QString& name, DockInDockWidget& widget );
|
||||||
|
void removePerspectives();
|
||||||
|
void removePerspective( const QString& name );
|
||||||
|
|
||||||
|
void loadPerspectives();
|
||||||
|
void savePerspectives() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void perspectivesListChanged();
|
||||||
|
void openingPerspective();
|
||||||
|
void openedPerspective();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
// Partially bypass ADS perspective management, store list here
|
||||||
|
// and then ADS will only have one perspective loaded
|
||||||
|
// this is because all docking widgets must exist when a perspective is loaded
|
||||||
|
// we will guarantee that!
|
||||||
|
class PerspectiveInfo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::shared_ptr<QSettings> settings;
|
||||||
|
QMap<QString,QStringList> groups;
|
||||||
|
};
|
||||||
|
QMap<QString,PerspectiveInfo> m_perspectives;
|
||||||
|
|
||||||
|
QString m_perspectivesFolder;
|
||||||
|
QString getSettingsFileName( const QString& perspective, bool temp ) const;
|
||||||
|
std::shared_ptr<QSettings> getSettingsObject( const QString& filePath ) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
203
examples/dockindock/perspectives.py
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
import os
|
||||||
|
import tempfile
|
||||||
|
import shutil
|
||||||
|
import atexit
|
||||||
|
|
||||||
|
from PyQt5.QtCore import pyqtSignal, QSettings, QObject
|
||||||
|
from PyQtAds import QtAds
|
||||||
|
|
||||||
|
from dockindockmanager import DockInDockManager
|
||||||
|
from dockindock import DockInDockWidget
|
||||||
|
|
||||||
|
GROUP_PREFIX = "Group"
|
||||||
|
|
||||||
|
def findWidget(name, managers: 'list[DockInDockManager]') -> QtAds.CDockWidget:
|
||||||
|
for mgr in managers:
|
||||||
|
widget = mgr.findDockWidget(name)
|
||||||
|
if widget:
|
||||||
|
return widget
|
||||||
|
|
||||||
|
|
||||||
|
class PerspectiveInfo:
|
||||||
|
# Partially bypass ADS perspective management, store list here
|
||||||
|
# and then ADS will only have one perspective loaded
|
||||||
|
# this is because all docking widgets must exist when a perspective is loaded
|
||||||
|
# we will guarantee that!
|
||||||
|
|
||||||
|
settings = QSettings()
|
||||||
|
groups: 'dict[str, list[str]]' = {}
|
||||||
|
|
||||||
|
|
||||||
|
class PerspectivesManager(QObject):
|
||||||
|
perspectivesListChanged = pyqtSignal()
|
||||||
|
openingPerspective = pyqtSignal()
|
||||||
|
openedPerspective = pyqtSignal()
|
||||||
|
|
||||||
|
def __init__(self, perspectives_folder):
|
||||||
|
super().__init__()
|
||||||
|
self.__perspectives_folder = perspectives_folder
|
||||||
|
self.__perspectives = {}
|
||||||
|
atexit.register(self.cleanup)
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
for perspective in self.__perspectives.values():
|
||||||
|
filename = perspective.settings.fileName()
|
||||||
|
try:
|
||||||
|
os.remove(filename)
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def perspectiveNames(self) -> 'list[str]':
|
||||||
|
return self.__perspectives.keys()
|
||||||
|
|
||||||
|
def addPerspective(self, name: str, widget: DockInDockWidget) -> None:
|
||||||
|
if self.__perspectives_folder:
|
||||||
|
self.__perspectives[name] = perspective = PerspectiveInfo()
|
||||||
|
perspective.settings = self.getSettingsObject(self.getSettingsFileName(name, True))
|
||||||
|
perspective.groups = widget.getManager().getGroupContents()
|
||||||
|
|
||||||
|
# save perspective internally
|
||||||
|
widget.getManager().addPerspectiveRec(name)
|
||||||
|
# store it in QSettings object
|
||||||
|
widget.getManager().savePerspectivesRec(perspective.settings)
|
||||||
|
# remove internal perspectives
|
||||||
|
widget.getManager().removePerspectives(widget.getManager().perspectiveNames())
|
||||||
|
|
||||||
|
self.perspectivesListChanged.emit()
|
||||||
|
|
||||||
|
def openPerspective(name: str, widget: DockInDockWidget) -> None:
|
||||||
|
assert widget.getTopLevelDockWidget() == widget
|
||||||
|
|
||||||
|
if self.__perspectives_folder:
|
||||||
|
if name in self.__perspectives:
|
||||||
|
self.openingPerspective.emit()
|
||||||
|
|
||||||
|
if widget.canCreateNewGroups():
|
||||||
|
cur_groups = widget.getManager().allManagers(True, True)
|
||||||
|
for group in self.__perspectives[name].groups.keys():
|
||||||
|
found = False
|
||||||
|
for curgroup in cur_groups:
|
||||||
|
if curgroup.getPerspectiveGroupName() == group:
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
if not found:
|
||||||
|
group = DockInDockManager.getGroupNameFromPersistGroupName(group)
|
||||||
|
|
||||||
|
# restore group in file but not in GUI yet
|
||||||
|
widget.createGroup(group, None)
|
||||||
|
|
||||||
|
cur_groups = widget.getManager().allManagers(False, True)
|
||||||
|
for curgroup in cur_groups:
|
||||||
|
if curgroup.getPersistGroupName() not in self.__perspectives[name].groups.keys():
|
||||||
|
widget.destroyGroup(curgroup.parent())
|
||||||
|
|
||||||
|
managers = widget.getManager().allManagers(True, True)
|
||||||
|
for group in self.__perspectives[name].groups().keys():
|
||||||
|
for mgr in managers:
|
||||||
|
if mgr.getPersistGroupName() == group:
|
||||||
|
for widget_name in self.__perspectives[name].groups[group]:
|
||||||
|
widget = findWidget(widget_name, [mgr])
|
||||||
|
if widget:
|
||||||
|
pass # OK, widget is already in the good manager!
|
||||||
|
else:
|
||||||
|
widget = findWidget(widget_name, managers)
|
||||||
|
if widget:
|
||||||
|
# move dock widget in the same group as it used to be when perspective was saved
|
||||||
|
# this guarantee load/open perspectives will work smartly
|
||||||
|
MoveDockWidgetAction.move(widget, mgr)
|
||||||
|
|
||||||
|
# internally load perspectives from QSettings
|
||||||
|
widget.getManager().loadPerspectivesRec(self.__perspectives[name].settings)
|
||||||
|
# load perspective (update GUI)
|
||||||
|
widget.getManager().openPerspectiveRec(name)
|
||||||
|
# remove internal perspectives
|
||||||
|
widget.getManager().removePerspectives(widget.getManager().perspectiveNames())
|
||||||
|
|
||||||
|
self.openedPerspective().emit()
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
def removePerspectives(self) -> None:
|
||||||
|
self.__perspectives.clear()
|
||||||
|
self.perspectivesListChanged.emit()
|
||||||
|
|
||||||
|
def removePerspective(self, name: str) -> None:
|
||||||
|
del self.__perspectives[name]
|
||||||
|
self.perspectivesListChanged.emit()
|
||||||
|
|
||||||
|
def getSettingsFileName(self, perspective: str, temp: bool) -> str:
|
||||||
|
name = "perspectives.ini" if not perspective else f"perspectives_{perspective + '.tmp' if temp else perspective + '.ini'}"
|
||||||
|
|
||||||
|
return os.path.join(self.__perspectives_folder, name)
|
||||||
|
|
||||||
|
def getSettingsObject(self, file_path: str) -> QSettings:
|
||||||
|
return QSettings(file_path, QSettings.IniFormat)
|
||||||
|
|
||||||
|
def loadPerspectives(self) -> None:
|
||||||
|
if self.__perspectives_folder:
|
||||||
|
tempfile.mktemp(dir=self.__perspectives_folder)
|
||||||
|
|
||||||
|
self.__perspectives.clear()
|
||||||
|
|
||||||
|
main_settings = self.getSettingsObject(self.getSettingsFileName("", False))
|
||||||
|
debug = main_settings.fileName()
|
||||||
|
|
||||||
|
size = main_settings.beginReadArray("Perspectives")
|
||||||
|
|
||||||
|
for i in range(0, size):
|
||||||
|
main_settings.setArrayIndex(i)
|
||||||
|
perspective = main_settings.value("Name")
|
||||||
|
|
||||||
|
if perspective:
|
||||||
|
to_load = self.getSettingsFileName(perspective, False)
|
||||||
|
loaded = self.getSettingsFileName(perspective, True)
|
||||||
|
|
||||||
|
try:
|
||||||
|
os.remove(loaded)
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
if not shutil.copy(to_load, loaded):
|
||||||
|
assert False
|
||||||
|
|
||||||
|
self.__perspectives[perspective] = PerspectiveInfo()
|
||||||
|
self.__perspectives[perspective].settings = self.getSettingsObject(loaded)
|
||||||
|
|
||||||
|
# load group info:
|
||||||
|
main_settings.beginGroup(GROUP_PREFIX)
|
||||||
|
for key in main_settings.allKeys():
|
||||||
|
self.__perspectives[perspective].groups[key] = main_settings.value(key)
|
||||||
|
main_settings.endGroup()
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
main_settings.endArray()
|
||||||
|
|
||||||
|
self.perspectivesListChanged.emit()
|
||||||
|
|
||||||
|
def savePerspectives(self) -> None:
|
||||||
|
if self.__perspectives_folder:
|
||||||
|
main_settings = self.getSettingsObject(self.getSettingsFileName("", False))
|
||||||
|
|
||||||
|
# Save list of perspective and group organization
|
||||||
|
main_settings.beginWriteArray("Perspectives", len(self.__perspectives))
|
||||||
|
for i, perspective in enumerate(self.__perspectives.keys()):
|
||||||
|
main_settings.setArrayIndex(i)
|
||||||
|
main_settings.setValue("Name", perspective)
|
||||||
|
main_settings.beginGroup(GROUP_PREFIX)
|
||||||
|
for group in self.__perspectives[perspective].groups.keys():
|
||||||
|
main_settings.setValue(group, list(self.__perspectives[perspective].groups[group]))
|
||||||
|
main_settings.endGroup()
|
||||||
|
main_settings.endArray()
|
||||||
|
|
||||||
|
# Save perspectives themselves
|
||||||
|
for perspective_name in self.__perspectives.keys():
|
||||||
|
to_save = self.getSettingsFileName(perspective_name, False)
|
||||||
|
settings = self.__perspectives[perspective_name].settings
|
||||||
|
settings.sync()
|
||||||
|
|
||||||
|
try:
|
||||||
|
os.remove(to_save)
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
if not shutil.copy(settings.fileName(), to_save):
|
||||||
|
assert False
|
||||||
28
examples/emptydockarea/CMakeLists.txt
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
project(ads_example_centralwidget VERSION ${VERSION_SHORT})
|
||||||
|
find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
|
||||||
|
find_package(Qt${QT_VERSION_MAJOR} 5.5 COMPONENTS Core Gui Widgets REQUIRED)
|
||||||
|
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||||
|
add_executable(EmptyDockAreaExample WIN32
|
||||||
|
main.cpp
|
||||||
|
mainwindow.cpp
|
||||||
|
mainwindow.ui
|
||||||
|
)
|
||||||
|
target_include_directories(EmptyDockAreaExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src")
|
||||||
|
target_link_libraries(EmptyDockAreaExample PRIVATE qtadvanceddocking)
|
||||||
|
target_link_libraries(EmptyDockAreaExample PUBLIC Qt${QT_VERSION_MAJOR}::Core
|
||||||
|
Qt${QT_VERSION_MAJOR}::Gui
|
||||||
|
Qt${QT_VERSION_MAJOR}::Widgets)
|
||||||
|
set_target_properties(EmptyDockAreaExample PROPERTIES
|
||||||
|
AUTOMOC ON
|
||||||
|
AUTORCC ON
|
||||||
|
AUTOUIC ON
|
||||||
|
CXX_STANDARD 14
|
||||||
|
CXX_STANDARD_REQUIRED ON
|
||||||
|
CXX_EXTENSIONS OFF
|
||||||
|
VERSION ${VERSION_SHORT}
|
||||||
|
EXPORT_NAME "Qt Advanced Docking System Empty Dock Area Example"
|
||||||
|
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${ads_PlatformDir}/lib"
|
||||||
|
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${ads_PlatformDir}/lib"
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${ads_PlatformDir}/bin"
|
||||||
|
)
|
||||||
34
examples/emptydockarea/emptydockarea.pro
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
ADS_OUT_ROOT = $${OUT_PWD}/../..
|
||||||
|
|
||||||
|
QT += core gui widgets
|
||||||
|
|
||||||
|
TARGET = EmptyDockareaExample
|
||||||
|
DESTDIR = $${ADS_OUT_ROOT}/lib
|
||||||
|
TEMPLATE = app
|
||||||
|
CONFIG += c++14
|
||||||
|
CONFIG += debug_and_release
|
||||||
|
adsBuildStatic {
|
||||||
|
DEFINES += ADS_STATIC
|
||||||
|
}
|
||||||
|
|
||||||
|
# The following define makes your compiler emit warnings if you use
|
||||||
|
# any Qt feature that has been marked deprecated (the exact warnings
|
||||||
|
# depend on your compiler). Please consult the documentation of the
|
||||||
|
# deprecated API in order to know how to port your code away from it.
|
||||||
|
DEFINES += QT_DEPRECATED_WARNINGS
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
main.cpp \
|
||||||
|
mainwindow.cpp
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
mainwindow.h
|
||||||
|
|
||||||
|
FORMS += \
|
||||||
|
mainwindow.ui
|
||||||
|
|
||||||
|
LIBS += -L$${ADS_OUT_ROOT}/lib
|
||||||
|
include(../../ads.pri)
|
||||||
|
INCLUDEPATH += ../../src
|
||||||
|
DEPENDPATH += ../../src
|
||||||
|
|
||||||
10
examples/emptydockarea/main.cpp
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#include <mainwindow.h>
|
||||||
|
#include <QApplication>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QApplication a(argc, argv);
|
||||||
|
CMainWindow w;
|
||||||
|
w.show();
|
||||||
|
return a.exec();
|
||||||
|
}
|
||||||
108
examples/emptydockarea/main.py
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
from PyQt5 import uic
|
||||||
|
from PyQt5.QtCore import Qt, QSignalBlocker
|
||||||
|
from PyQt5.QtWidgets import (QApplication, QMainWindow, QLabel, QComboBox, QTableWidget,
|
||||||
|
QAction, QWidgetAction, QSizePolicy, QInputDialog)
|
||||||
|
from PyQt5.QtGui import QCloseEvent
|
||||||
|
from PyQtAds import QtAds
|
||||||
|
|
||||||
|
|
||||||
|
UI_FILE = os.path.join(os.path.dirname(__file__), 'mainwindow.ui')
|
||||||
|
MainWindowUI, MainWindowBase = uic.loadUiType(UI_FILE)
|
||||||
|
|
||||||
|
|
||||||
|
class CMainWindow(MainWindowUI, MainWindowBase):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self.setupUi(self)
|
||||||
|
|
||||||
|
QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.OpaqueSplitterResize, True)
|
||||||
|
QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.XmlCompressionEnabled, False)
|
||||||
|
QtAds.CDockManager.setConfigFlag(QtAds.CDockManager.FocusHighlighting, True)
|
||||||
|
self.dock_manager = QtAds.CDockManager(self)
|
||||||
|
|
||||||
|
# Set central widget
|
||||||
|
label = QLabel()
|
||||||
|
label.setText("This is a DockArea which is always visible, even if it does not contain any DockWidgets.")
|
||||||
|
label.setAlignment(Qt.AlignCenter)
|
||||||
|
central_dock_widget = QtAds.CDockWidget("CentralWidget")
|
||||||
|
central_dock_widget.setWidget(label)
|
||||||
|
central_dock_widget.setFeature(QtAds.CDockWidget.NoTab, True)
|
||||||
|
central_dock_area = self.dock_manager.setCentralWidget(central_dock_widget)
|
||||||
|
|
||||||
|
# create other dock widgets
|
||||||
|
table = QTableWidget()
|
||||||
|
table.setColumnCount(3)
|
||||||
|
table.setRowCount(10)
|
||||||
|
table_dock_widget = QtAds.CDockWidget("Table 1")
|
||||||
|
table_dock_widget.setWidget(table)
|
||||||
|
table_dock_widget.setMinimumSizeHintMode(QtAds.CDockWidget.MinimumSizeHintFromDockWidget)
|
||||||
|
table_dock_widget.resize(250, 150)
|
||||||
|
table_dock_widget.setMinimumSize(200,150)
|
||||||
|
self.dock_manager.addDockWidgetTabToArea(table_dock_widget, central_dock_area)
|
||||||
|
table_area = self.dock_manager.addDockWidget(QtAds.DockWidgetArea.LeftDockWidgetArea, table_dock_widget)
|
||||||
|
self.menuView.addAction(table_dock_widget.toggleViewAction())
|
||||||
|
|
||||||
|
table = QTableWidget()
|
||||||
|
table.setColumnCount(5)
|
||||||
|
table.setRowCount(1020)
|
||||||
|
table_dock_widget = QtAds.CDockWidget("Table 2")
|
||||||
|
table_dock_widget.setWidget(table)
|
||||||
|
table_dock_widget.setMinimumSizeHintMode(QtAds.CDockWidget.MinimumSizeHintFromDockWidget)
|
||||||
|
table_dock_widget.resize(250, 150)
|
||||||
|
table_dock_widget.setMinimumSize(200,150)
|
||||||
|
self.dock_manager.addDockWidget(QtAds.DockWidgetArea.BottomDockWidgetArea, table_dock_widget, table_area)
|
||||||
|
self.menuView.addAction(table_dock_widget.toggleViewAction())
|
||||||
|
|
||||||
|
properties_table = QTableWidget()
|
||||||
|
properties_table.setColumnCount(3)
|
||||||
|
properties_table.setRowCount(10)
|
||||||
|
properties_dock_widget = QtAds.CDockWidget("Properties")
|
||||||
|
properties_dock_widget.setWidget(properties_table)
|
||||||
|
properties_dock_widget.setMinimumSizeHintMode(QtAds.CDockWidget.MinimumSizeHintFromDockWidget)
|
||||||
|
properties_dock_widget.resize(250, 150)
|
||||||
|
properties_dock_widget.setMinimumSize(200,150)
|
||||||
|
self.dock_manager.addDockWidget(QtAds.DockWidgetArea.RightDockWidgetArea, properties_dock_widget, central_dock_area)
|
||||||
|
self.menuView.addAction(properties_dock_widget.toggleViewAction())
|
||||||
|
|
||||||
|
self.createPerspectiveUi()
|
||||||
|
|
||||||
|
def createPerspectiveUi(self):
|
||||||
|
save_perspective_action = QAction("Create Perspective", self)
|
||||||
|
save_perspective_action.triggered.connect(self.savePerspective)
|
||||||
|
perspective_list_action = QWidgetAction(self)
|
||||||
|
self.perspective_combo_box = QComboBox(self)
|
||||||
|
self.perspective_combo_box.setSizeAdjustPolicy(QComboBox.AdjustToContents)
|
||||||
|
self.perspective_combo_box.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
|
||||||
|
self.perspective_combo_box.activated[str].connect(self.dock_manager.openPerspective)
|
||||||
|
perspective_list_action.setDefaultWidget(self.perspective_combo_box)
|
||||||
|
self.toolBar.addSeparator()
|
||||||
|
self.toolBar.addAction(perspective_list_action)
|
||||||
|
self.toolBar.addAction(save_perspective_action)
|
||||||
|
|
||||||
|
def savePerspective(self):
|
||||||
|
perspective_name, ok = QInputDialog.getText(self, "Save Perspective", "Enter unique name:")
|
||||||
|
if not perspective_name or not ok:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.dock_manager.addPerspective(perspective_name)
|
||||||
|
blocker = QSignalBlocker(self.perspective_combo_box)
|
||||||
|
self.perspective_combo_box.clear()
|
||||||
|
self.perspective_combo_box.addItems(self.dock_manager.perspectiveNames())
|
||||||
|
self.perspective_combo_box.setCurrentText(perspective_name)
|
||||||
|
|
||||||
|
def closeEvent(self, event: QCloseEvent):
|
||||||
|
# Delete dock manager here to delete all floating widgets. This ensures
|
||||||
|
# that all top level windows of the dock manager are properly closed
|
||||||
|
self.dock_manager.deleteLater()
|
||||||
|
super().closeEvent(event)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app = QApplication(sys.argv)
|
||||||
|
|
||||||
|
w = CMainWindow()
|
||||||
|
w.show()
|
||||||
|
app.exec_()
|
||||||
135
examples/emptydockarea/mainwindow.cpp
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
#include "mainwindow.h"
|
||||||
|
|
||||||
|
#include "ui_mainwindow.h"
|
||||||
|
|
||||||
|
#include <QWidgetAction>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QCalendarWidget>
|
||||||
|
#include <QTreeView>
|
||||||
|
#include <QFileSystemModel>
|
||||||
|
#include <QTableWidget>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QRadioButton>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QInputDialog>
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QPlainTextEdit>
|
||||||
|
#include <QToolBar>
|
||||||
|
|
||||||
|
#include "DockAreaWidget.h"
|
||||||
|
#include "DockAreaTitleBar.h"
|
||||||
|
#include "DockAreaTabBar.h"
|
||||||
|
#include "FloatingDockContainer.h"
|
||||||
|
#include "DockComponentsFactory.h"
|
||||||
|
|
||||||
|
using namespace ads;
|
||||||
|
|
||||||
|
|
||||||
|
CMainWindow::CMainWindow(QWidget *parent)
|
||||||
|
: QMainWindow(parent)
|
||||||
|
, ui(new Ui::CMainWindow)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
CDockManager::setConfigFlag(CDockManager::OpaqueSplitterResize, true);
|
||||||
|
CDockManager::setConfigFlag(CDockManager::XmlCompressionEnabled, false);
|
||||||
|
CDockManager::setConfigFlag(CDockManager::FocusHighlighting, true);
|
||||||
|
DockManager = new CDockManager(this);
|
||||||
|
|
||||||
|
// Set central widget
|
||||||
|
QLabel* label = new QLabel();
|
||||||
|
label->setText("This is a DockArea which is always visible, even if it does not contain any DockWidgets.");
|
||||||
|
label->setAlignment(Qt::AlignCenter);
|
||||||
|
CDockWidget* CentralDockWidget = new CDockWidget("CentralWidget");
|
||||||
|
CentralDockWidget->setWidget(label);
|
||||||
|
CentralDockWidget->setFeature(ads::CDockWidget::NoTab, true);
|
||||||
|
auto* CentralDockArea = DockManager->setCentralWidget(CentralDockWidget);
|
||||||
|
|
||||||
|
// create other dock widgets
|
||||||
|
QTableWidget* table = new QTableWidget();
|
||||||
|
table->setColumnCount(3);
|
||||||
|
table->setRowCount(10);
|
||||||
|
CDockWidget* TableDockWidget = new CDockWidget("Table 1");
|
||||||
|
TableDockWidget->setWidget(table);
|
||||||
|
TableDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget);
|
||||||
|
TableDockWidget->resize(250, 150);
|
||||||
|
TableDockWidget->setMinimumSize(200,150);
|
||||||
|
DockManager->addDockWidgetTabToArea(TableDockWidget, CentralDockArea);
|
||||||
|
auto TableArea = DockManager->addDockWidget(DockWidgetArea::LeftDockWidgetArea, TableDockWidget);
|
||||||
|
ui->menuView->addAction(TableDockWidget->toggleViewAction());
|
||||||
|
|
||||||
|
table = new QTableWidget();
|
||||||
|
table->setColumnCount(5);
|
||||||
|
table->setRowCount(1020);
|
||||||
|
TableDockWidget = new CDockWidget("Table 2");
|
||||||
|
TableDockWidget->setWidget(table);
|
||||||
|
TableDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget);
|
||||||
|
TableDockWidget->resize(250, 150);
|
||||||
|
TableDockWidget->setMinimumSize(200,150);
|
||||||
|
DockManager->addDockWidget(DockWidgetArea::BottomDockWidgetArea, TableDockWidget, TableArea);
|
||||||
|
ui->menuView->addAction(TableDockWidget->toggleViewAction());
|
||||||
|
|
||||||
|
QTableWidget* propertiesTable = new QTableWidget();
|
||||||
|
propertiesTable->setColumnCount(3);
|
||||||
|
propertiesTable->setRowCount(10);
|
||||||
|
CDockWidget* PropertiesDockWidget = new CDockWidget("Properties");
|
||||||
|
PropertiesDockWidget->setWidget(propertiesTable);
|
||||||
|
PropertiesDockWidget->setMinimumSizeHintMode(CDockWidget::MinimumSizeHintFromDockWidget);
|
||||||
|
PropertiesDockWidget->resize(250, 150);
|
||||||
|
PropertiesDockWidget->setMinimumSize(200,150);
|
||||||
|
DockManager->addDockWidget(DockWidgetArea::RightDockWidgetArea, PropertiesDockWidget, CentralDockArea);
|
||||||
|
ui->menuView->addAction(PropertiesDockWidget->toggleViewAction());
|
||||||
|
|
||||||
|
createPerspectiveUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
CMainWindow::~CMainWindow()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CMainWindow::createPerspectiveUi()
|
||||||
|
{
|
||||||
|
SavePerspectiveAction = new QAction("Create Perspective", 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);
|
||||||
|
connect(PerspectiveComboBox, SIGNAL(activated(const QString&)),
|
||||||
|
DockManager, SLOT(openPerspective(const QString&)));
|
||||||
|
PerspectiveListAction->setDefaultWidget(PerspectiveComboBox);
|
||||||
|
ui->toolBar->addSeparator();
|
||||||
|
ui->toolBar->addAction(PerspectiveListAction);
|
||||||
|
ui->toolBar->addAction(SavePerspectiveAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CMainWindow::savePerspective()
|
||||||
|
{
|
||||||
|
QString PerspectiveName = QInputDialog::getText(this, "Save Perspective", "Enter unique name:");
|
||||||
|
if (PerspectiveName.isEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DockManager->addPerspective(PerspectiveName);
|
||||||
|
QSignalBlocker Blocker(PerspectiveComboBox);
|
||||||
|
PerspectiveComboBox->clear();
|
||||||
|
PerspectiveComboBox->addItems(DockManager->perspectiveNames());
|
||||||
|
PerspectiveComboBox->setCurrentText(PerspectiveName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
void CMainWindow::closeEvent(QCloseEvent* event)
|
||||||
|
{
|
||||||
|
// Delete dock manager here to delete all floating widgets. This ensures
|
||||||
|
// that all top level windows of the dock manager are properly closed
|
||||||
|
DockManager->deleteLater();
|
||||||
|
QMainWindow::closeEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
43
examples/emptydockarea/mainwindow.h
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#ifndef MAINWINDOW_H
|
||||||
|
#define MAINWINDOW_H
|
||||||
|
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <QWidgetAction>
|
||||||
|
|
||||||
|
#include "DockManager.h"
|
||||||
|
#include "DockAreaWidget.h"
|
||||||
|
#include "DockWidget.h"
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
namespace Ui { class CMainWindow; }
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
class CMainWindow : public QMainWindow
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
CMainWindow(QWidget *parent = nullptr);
|
||||||
|
~CMainWindow();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void closeEvent(QCloseEvent* event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QAction* SavePerspectiveAction = nullptr;
|
||||||
|
QWidgetAction* PerspectiveListAction = nullptr;
|
||||||
|
QComboBox* PerspectiveComboBox = nullptr;
|
||||||
|
|
||||||
|
Ui::CMainWindow *ui;
|
||||||
|
|
||||||
|
ads::CDockManager* DockManager;
|
||||||
|
ads::CDockAreaWidget* StatusDockArea;
|
||||||
|
ads::CDockWidget* TimelineDockWidget;
|
||||||
|
|
||||||
|
void createPerspectiveUi();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void savePerspective();
|
||||||
|
};
|
||||||
|
#endif // MAINWINDOW_H
|
||||||
47
examples/emptydockarea/mainwindow.ui
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>CMainWindow</class>
|
||||||
|
<widget class="QMainWindow" name="CMainWindow">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>1284</width>
|
||||||
|
<height>757</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>MainWindow</string>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="centralwidget"/>
|
||||||
|
<widget class="QMenuBar" name="menubar">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>1284</width>
|
||||||
|
<height>21</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<widget class="QMenu" name="menuView">
|
||||||
|
<property name="title">
|
||||||
|
<string>View</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<addaction name="menuView"/>
|
||||||
|
</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>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
TEMPLATE = subdirs
|
TEMPLATE = subdirs
|
||||||
|
|
||||||
SUBDIRS = \
|
SUBDIRS = \
|
||||||
|
centralwidget \
|
||||||
simple \
|
simple \
|
||||||
|
hideshow \
|
||||||
sidebar \
|
sidebar \
|
||||||
deleteonclose
|
deleteonclose \
|
||||||
|
emptydockarea \
|
||||||
|
dockindock
|
||||||
|
|||||||
28
examples/hideshow/CMakeLists.txt
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
project(ads_example_hideshow VERSION ${VERSION_SHORT})
|
||||||
|
find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
|
||||||
|
find_package(Qt${QT_VERSION_MAJOR} 5.5 COMPONENTS Core Gui Widgets REQUIRED)
|
||||||
|
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||||
|
add_executable(HideShowExample WIN32
|
||||||
|
main.cpp
|
||||||
|
MainWindow.cpp
|
||||||
|
MainWindow.ui
|
||||||
|
)
|
||||||
|
target_include_directories(HideShowExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src")
|
||||||
|
target_link_libraries(HideShowExample PRIVATE qtadvanceddocking)
|
||||||
|
target_link_libraries(HideShowExample PUBLIC Qt${QT_VERSION_MAJOR}::Core
|
||||||
|
Qt${QT_VERSION_MAJOR}::Gui
|
||||||
|
Qt${QT_VERSION_MAJOR}::Widgets)
|
||||||
|
set_target_properties(HideShowExample PROPERTIES
|
||||||
|
AUTOMOC ON
|
||||||
|
AUTORCC ON
|
||||||
|
AUTOUIC ON
|
||||||
|
CXX_STANDARD 14
|
||||||
|
CXX_STANDARD_REQUIRED ON
|
||||||
|
CXX_EXTENSIONS OFF
|
||||||
|
VERSION ${VERSION_SHORT}
|
||||||
|
EXPORT_NAME "Qt Advanced Docking System Hide,Show Example"
|
||||||
|
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${ads_PlatformDir}/lib"
|
||||||
|
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${ads_PlatformDir}/lib"
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${ads_PlatformDir}/bin"
|
||||||
|
)
|
||||||
78
examples/hideshow/MainWindow.cpp
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
#include "../../examples/hideshow/MainWindow.h"
|
||||||
|
|
||||||
|
#include "ui_MainWindow.h"
|
||||||
|
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QPushButton>
|
||||||
|
|
||||||
|
MainWindow::MainWindow(QWidget *parent) :
|
||||||
|
QMainWindow(parent),
|
||||||
|
ui(new Ui::MainWindow)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
ui->centralWidget->setLayout( m_layout = new QStackedLayout() );
|
||||||
|
|
||||||
|
m_welcomeWidget = new QWidget(this);
|
||||||
|
auto welcomeLayout = new QVBoxLayout(m_welcomeWidget);
|
||||||
|
welcomeLayout->addStretch();
|
||||||
|
QPushButton* openButton = new QPushButton("Open project");
|
||||||
|
welcomeLayout->addWidget( openButton );
|
||||||
|
welcomeLayout->addStretch();
|
||||||
|
|
||||||
|
connect( openButton, SIGNAL(clicked()), this, SLOT(openProject()) );
|
||||||
|
|
||||||
|
m_DockManager = new ads::CDockManager(ui->centralWidget);
|
||||||
|
|
||||||
|
// Create example content label - this can be any application specific
|
||||||
|
// widget
|
||||||
|
QLabel* l = new QLabel();
|
||||||
|
l->setWordWrap(true);
|
||||||
|
l->setAlignment(Qt::AlignTop | Qt::AlignLeft);
|
||||||
|
l->setText("Lorem ipsum dolor sit amet, consectetuer adipiscing elit. ");
|
||||||
|
|
||||||
|
// Create a dock widget with the title Label 1 and set the created label
|
||||||
|
// as the dock widget content
|
||||||
|
ads::CDockWidget* DockWidget = new ads::CDockWidget("Label 1");
|
||||||
|
DockWidget->setWidget(l);
|
||||||
|
|
||||||
|
// Add the toggleViewAction of the dock widget to the menu to give
|
||||||
|
// the user the possibility to show the dock widget if it has been closed
|
||||||
|
ui->menuView->addAction(DockWidget->toggleViewAction());
|
||||||
|
|
||||||
|
connect( ui->actionOpen, SIGNAL(triggered()), this, SLOT(openProject()) );
|
||||||
|
connect( ui->actionClose, SIGNAL(triggered()), this, SLOT(closeProject()) );
|
||||||
|
|
||||||
|
// Add the dock widget to the top dock widget area
|
||||||
|
m_DockManager->addDockWidget(ads::TopDockWidgetArea, DockWidget);
|
||||||
|
|
||||||
|
ui->centralWidget->layout()->addWidget( m_welcomeWidget );
|
||||||
|
ui->centralWidget->layout()->addWidget( m_DockManager );
|
||||||
|
|
||||||
|
closeProject();
|
||||||
|
}
|
||||||
|
|
||||||
|
MainWindow::~MainWindow()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::openProject()
|
||||||
|
{
|
||||||
|
ui->actionOpen->setEnabled(false);
|
||||||
|
ui->actionClose->setEnabled(true);
|
||||||
|
ui->menuView->setEnabled(true);
|
||||||
|
|
||||||
|
m_layout->setCurrentWidget( m_DockManager );
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::closeProject()
|
||||||
|
{
|
||||||
|
ui->actionOpen->setEnabled(true);
|
||||||
|
ui->actionClose->setEnabled(false);
|
||||||
|
ui->menuView->setEnabled(false);
|
||||||
|
|
||||||
|
m_DockManager->hideManagerAndFloatingWidgets();
|
||||||
|
m_layout->setCurrentWidget( m_welcomeWidget );
|
||||||
|
}
|
||||||
|
|
||||||
33
examples/hideshow/MainWindow.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#ifndef MAINWINDOW_H
|
||||||
|
#define MAINWINDOW_H
|
||||||
|
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include <QStackedLayout>
|
||||||
|
#include "DockManager.h"
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
namespace Ui {
|
||||||
|
class MainWindow;
|
||||||
|
}
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
class MainWindow : public QMainWindow
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit MainWindow(QWidget *parent = 0);
|
||||||
|
~MainWindow();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void openProject();
|
||||||
|
void closeProject();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::MainWindow *ui;
|
||||||
|
QWidget* m_welcomeWidget;
|
||||||
|
ads::CDockManager* m_DockManager;
|
||||||
|
QStackedLayout* m_layout;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MAINWINDOW_H
|
||||||
56
examples/hideshow/MainWindow.ui
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<?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="QMenuBar" name="menuBar">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>26</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<widget class="QMenu" name="menuView">
|
||||||
|
<property name="title">
|
||||||
|
<string>View</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QMenu" name="menuFile">
|
||||||
|
<property name="title">
|
||||||
|
<string>File</string>
|
||||||
|
</property>
|
||||||
|
<addaction name="actionOpen"/>
|
||||||
|
<addaction name="actionClose"/>
|
||||||
|
</widget>
|
||||||
|
<addaction name="menuFile"/>
|
||||||
|
<addaction name="menuView"/>
|
||||||
|
</widget>
|
||||||
|
<widget class="QStatusBar" name="statusBar"/>
|
||||||
|
<action name="actionOpen">
|
||||||
|
<property name="text">
|
||||||
|
<string>Open project</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionClose">
|
||||||
|
<property name="text">
|
||||||
|
<string>Close project</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
</widget>
|
||||||
|
<layoutdefault spacing="6" margin="11"/>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
31
examples/hideshow/hideshow.pro
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
ADS_OUT_ROOT = $${OUT_PWD}/../..
|
||||||
|
|
||||||
|
QT += core gui widgets
|
||||||
|
|
||||||
|
TARGET = HideShowExample
|
||||||
|
DESTDIR = $${ADS_OUT_ROOT}/lib
|
||||||
|
TEMPLATE = app
|
||||||
|
CONFIG += c++14
|
||||||
|
CONFIG += debug_and_release
|
||||||
|
adsBuildStatic {
|
||||||
|
DEFINES += ADS_STATIC
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINES += QT_DEPRECATED_WARNINGS
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
main.cpp \
|
||||||
|
MainWindow.cpp
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
MainWindow.h
|
||||||
|
|
||||||
|
FORMS += \
|
||||||
|
MainWindow.ui
|
||||||
|
|
||||||
|
|
||||||
|
LIBS += -L$${ADS_OUT_ROOT}/lib
|
||||||
|
include(../../ads.pri)
|
||||||
|
INCLUDEPATH += ../../src
|
||||||
|
DEPENDPATH += ../../src
|
||||||
|
|
||||||
11
examples/hideshow/main.cpp
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#include <QApplication>
|
||||||
|
#include "../../examples/hideshow/MainWindow.h"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QApplication a(argc, argv);
|
||||||
|
MainWindow w;
|
||||||
|
w.show();
|
||||||
|
|
||||||
|
return a.exec();
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.5)
|
||||||
project(ads_example_sidebar VERSION ${VERSION_SHORT})
|
project(ads_example_sidebar VERSION ${VERSION_SHORT})
|
||||||
find_package(Qt5 5.5 COMPONENTS Core Gui Widgets REQUIRED)
|
find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
|
||||||
|
find_package(Qt${QT_VERSION_MAJOR} 5.5 COMPONENTS Core Gui Widgets REQUIRED)
|
||||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||||
add_executable(SidebarExample WIN32
|
add_executable(SidebarExample WIN32
|
||||||
main.cpp
|
main.cpp
|
||||||
@@ -9,7 +10,9 @@ add_executable(SidebarExample WIN32
|
|||||||
)
|
)
|
||||||
target_include_directories(SidebarExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src")
|
target_include_directories(SidebarExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src")
|
||||||
target_link_libraries(SidebarExample PRIVATE qtadvanceddocking)
|
target_link_libraries(SidebarExample PRIVATE qtadvanceddocking)
|
||||||
target_link_libraries(SidebarExample PUBLIC Qt5::Core Qt5::Gui Qt5::Widgets)
|
target_link_libraries(SidebarExample PUBLIC Qt${QT_VERSION_MAJOR}::Core
|
||||||
|
Qt${QT_VERSION_MAJOR}::Gui
|
||||||
|
Qt${QT_VERSION_MAJOR}::Widgets)
|
||||||
set_target_properties(SidebarExample PROPERTIES
|
set_target_properties(SidebarExample PROPERTIES
|
||||||
AUTOMOC ON
|
AUTOMOC ON
|
||||||
AUTORCC ON
|
AUTORCC ON
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ MainWindow::MainWindow(QWidget *parent) :
|
|||||||
QPlainTextEdit* te = new QPlainTextEdit();
|
QPlainTextEdit* te = new QPlainTextEdit();
|
||||||
te->setPlaceholderText("Please enter your text here into this QPlainTextEdit...");
|
te->setPlaceholderText("Please enter your text here into this QPlainTextEdit...");
|
||||||
DockWidget = new ads::CDockWidget("Editor 1");
|
DockWidget = new ads::CDockWidget("Editor 1");
|
||||||
|
DockWidget->setWidget(te);
|
||||||
ui->menuView->addAction(DockWidget->toggleViewAction());
|
ui->menuView->addAction(DockWidget->toggleViewAction());
|
||||||
m_DockManager->addDockWidget(ads::BottomDockWidgetArea, DockWidget);
|
m_DockManager->addDockWidget(ads::BottomDockWidgetArea, DockWidget);
|
||||||
}
|
}
|
||||||
@@ -51,3 +52,13 @@ MainWindow::~MainWindow()
|
|||||||
{
|
{
|
||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void MainWindow::closeEvent(QCloseEvent *event)
|
||||||
|
{
|
||||||
|
QMainWindow::closeEvent(event);
|
||||||
|
if (m_DockManager)
|
||||||
|
{
|
||||||
|
m_DockManager->deleteLater();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ public:
|
|||||||
explicit MainWindow(QWidget *parent = 0);
|
explicit MainWindow(QWidget *parent = 0);
|
||||||
~MainWindow();
|
~MainWindow();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void closeEvent(QCloseEvent *event) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::MainWindow *ui;
|
Ui::MainWindow *ui;
|
||||||
ads::CDockManager* m_DockManager;
|
ads::CDockManager* m_DockManager;
|
||||||
|
|||||||
@@ -25,23 +25,7 @@ FORMS += \
|
|||||||
|
|
||||||
|
|
||||||
LIBS += -L$${ADS_OUT_ROOT}/lib
|
LIBS += -L$${ADS_OUT_ROOT}/lib
|
||||||
|
include(../../ads.pri)
|
||||||
# Dependency: AdvancedDockingSystem (shared)
|
|
||||||
CONFIG(debug, debug|release){
|
|
||||||
win32 {
|
|
||||||
LIBS += -lqtadvanceddockingd
|
|
||||||
}
|
|
||||||
else:mac {
|
|
||||||
LIBS += -lqtadvanceddocking_debug
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
LIBS += -lqtadvanceddocking
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
LIBS += -lqtadvanceddocking
|
|
||||||
}
|
|
||||||
|
|
||||||
INCLUDEPATH += ../../src
|
INCLUDEPATH += ../../src
|
||||||
DEPENDPATH += ../../src
|
DEPENDPATH += ../../src
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.5)
|
||||||
project(ads_example_simple VERSION ${VERSION_SHORT})
|
project(ads_example_simple VERSION ${VERSION_SHORT})
|
||||||
find_package(Qt5 5.5 COMPONENTS Core Gui Widgets REQUIRED)
|
find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
|
||||||
|
find_package(Qt${QT_VERSION_MAJOR} 5.5 COMPONENTS Core Gui Widgets REQUIRED)
|
||||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||||
add_executable(SimpleExample WIN32
|
add_executable(SimpleExample WIN32
|
||||||
main.cpp
|
main.cpp
|
||||||
@@ -9,7 +10,9 @@ add_executable(SimpleExample WIN32
|
|||||||
)
|
)
|
||||||
target_include_directories(SimpleExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src")
|
target_include_directories(SimpleExample PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../src")
|
||||||
target_link_libraries(SimpleExample PRIVATE qtadvanceddocking)
|
target_link_libraries(SimpleExample PRIVATE qtadvanceddocking)
|
||||||
target_link_libraries(SimpleExample PUBLIC Qt5::Core Qt5::Gui Qt5::Widgets)
|
target_link_libraries(SimpleExample PUBLIC Qt${QT_VERSION_MAJOR}::Core
|
||||||
|
Qt${QT_VERSION_MAJOR}::Gui
|
||||||
|
Qt${QT_VERSION_MAJOR}::Widgets)
|
||||||
set_target_properties(SimpleExample PROPERTIES
|
set_target_properties(SimpleExample PROPERTIES
|
||||||
AUTOMOC ON
|
AUTOMOC ON
|
||||||
AUTORCC ON
|
AUTORCC ON
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ MainWindow::MainWindow(QWidget *parent) :
|
|||||||
QMainWindow(parent),
|
QMainWindow(parent),
|
||||||
ui(new Ui::MainWindow)
|
ui(new Ui::MainWindow)
|
||||||
{
|
{
|
||||||
/*ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
// Create the dock manager. Because the parent parameter is a QMainWindow
|
// Create the dock manager. Because the parent parameter is a QMainWindow
|
||||||
// the dock manager registers itself as the central widget.
|
// the dock manager registers itself as the central widget.
|
||||||
@@ -32,70 +32,11 @@ MainWindow::MainWindow(QWidget *parent) :
|
|||||||
ui->menuView->addAction(DockWidget->toggleViewAction());
|
ui->menuView->addAction(DockWidget->toggleViewAction());
|
||||||
|
|
||||||
// Add the dock widget to the top dock widget area
|
// Add the dock widget to the top dock widget area
|
||||||
m_DockManager->addDockWidget(ads::TopDockWidgetArea, DockWidget);*/
|
m_DockManager->addDockWidget(ads::TopDockWidgetArea, DockWidget);
|
||||||
|
|
||||||
ui->setupUi(this);
|
|
||||||
|
|
||||||
// Create the dock manager. Because the parent parameter is a QMainWindow
|
|
||||||
// the dock manager registers itself as the central widget.
|
|
||||||
m_DockManager1 = new ads::CDockManager(this);
|
|
||||||
|
|
||||||
// Create example content label - this can be any application specific
|
|
||||||
// widget
|
|
||||||
ads::CDockWidget* DockWidget;
|
|
||||||
{
|
|
||||||
QLabel* l = new QLabel();
|
|
||||||
l->setWordWrap(true);
|
|
||||||
l->setAlignment(Qt::AlignTop | Qt::AlignLeft);
|
|
||||||
l->setText("Lorem ipsum dolor sit amet, consectetuer adipiscing elit. ");
|
|
||||||
|
|
||||||
// Create a dock widget with the title Label 1 and set the created label
|
|
||||||
// as the dock widget content
|
|
||||||
DockWidget = new ads::CDockWidget("Label 1");
|
|
||||||
DockWidget->setWidget(l);
|
|
||||||
}
|
|
||||||
|
|
||||||
ads::CDockWidget* DockWidget2;
|
|
||||||
{
|
|
||||||
QLabel* l = new QLabel();
|
|
||||||
l->setWordWrap(true);
|
|
||||||
l->setAlignment(Qt::AlignTop | Qt::AlignLeft);
|
|
||||||
l->setText("Lorem ipsum dolor sit amet, consectetuer adipiscing elit. ");
|
|
||||||
|
|
||||||
// Create a dock widget with the title Label 1 and set the created label
|
|
||||||
// as the dock widget content
|
|
||||||
DockWidget2 = new ads::CDockWidget("Label 2");
|
|
||||||
DockWidget2->setWidget(l);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Add the toggleViewAction of the dock widget to the menu to give
|
|
||||||
// the user the possibility to show the dock widget if it has been closed
|
|
||||||
ui->menuView->addAction(DockWidget->toggleViewAction());
|
|
||||||
|
|
||||||
// Add the dock widget to the top dock widget area
|
|
||||||
m_DockManager1->addDockWidget(ads::TopDockWidgetArea, DockWidget);
|
|
||||||
|
|
||||||
|
|
||||||
auto funcRemoveFirstManager = [=]()
|
|
||||||
{
|
|
||||||
m_DockManager1->removeDockWidget(DockWidget);
|
|
||||||
|
|
||||||
delete m_DockManager1;
|
|
||||||
m_DockManager1 = nullptr;
|
|
||||||
};
|
|
||||||
QTimer::singleShot(3000, funcRemoveFirstManager);
|
|
||||||
|
|
||||||
auto funcAddSecondManager = [=]()
|
|
||||||
{
|
|
||||||
m_DockManager2 = new ads::CDockManager(this);
|
|
||||||
|
|
||||||
m_DockManager2->addDockWidget(ads::TopDockWidgetArea, DockWidget);
|
|
||||||
};
|
|
||||||
QTimer::singleShot(5000, funcAddSecondManager);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MainWindow::~MainWindow()
|
MainWindow::~MainWindow()
|
||||||
{
|
{
|
||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,8 +20,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::MainWindow *ui;
|
Ui::MainWindow *ui;
|
||||||
ads::CDockManager* m_DockManager1;
|
ads::CDockManager* m_DockManager;
|
||||||
ads::CDockManager* m_DockManager2;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // MAINWINDOW_H
|
#endif // MAINWINDOW_H
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
from PyQt5 import uic
|
from PyQt5 import uic
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt, QTimer
|
||||||
|
from PyQt5.QtGui import QCloseEvent
|
||||||
from PyQt5.QtWidgets import QApplication, QLabel
|
from PyQt5.QtWidgets import QApplication, QLabel
|
||||||
|
|
||||||
from PyQtAds import QtAds
|
from PyQtAds import QtAds
|
||||||
@@ -33,6 +34,7 @@ class MainWindow(MainWindowUI, MainWindowBase):
|
|||||||
# as the dock widget content
|
# as the dock widget content
|
||||||
dock_widget = QtAds.CDockWidget("Label 1")
|
dock_widget = QtAds.CDockWidget("Label 1")
|
||||||
dock_widget.setWidget(l)
|
dock_widget.setWidget(l)
|
||||||
|
|
||||||
|
|
||||||
# Add the toggleViewAction of the dock widget to the menu to give
|
# Add the toggleViewAction of the dock widget to the menu to give
|
||||||
# the user the possibility to show the dock widget if it has been closed
|
# the user the possibility to show the dock widget if it has been closed
|
||||||
@@ -25,23 +25,7 @@ FORMS += \
|
|||||||
|
|
||||||
|
|
||||||
LIBS += -L$${ADS_OUT_ROOT}/lib
|
LIBS += -L$${ADS_OUT_ROOT}/lib
|
||||||
|
include(../../ads.pri)
|
||||||
# Dependency: AdvancedDockingSystem (shared)
|
|
||||||
CONFIG(debug, debug|release){
|
|
||||||
win32 {
|
|
||||||
LIBS += -lqtadvanceddockingd
|
|
||||||
}
|
|
||||||
else:mac {
|
|
||||||
LIBS += -lqtadvanceddocking_debug
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
LIBS += -lqtadvanceddocking
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
LIBS += -lqtadvanceddocking
|
|
||||||
}
|
|
||||||
|
|
||||||
INCLUDEPATH += ../../src
|
INCLUDEPATH += ../../src
|
||||||
DEPENDPATH += ../../src
|
DEPENDPATH += ../../src
|
||||||
|
|
||||||
|
|||||||
27
project.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
from pyqtbuild import PyQtBindings, PyQtProject
|
||||||
|
from sipbuild import Option
|
||||||
|
|
||||||
|
class PyQtAds(PyQtProject):
|
||||||
|
def __init__(self):
|
||||||
|
""" Initialise the project. """
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.bindings_factories = [ads]
|
||||||
|
|
||||||
|
class ads(PyQtBindings):
|
||||||
|
def __init__(self, project):
|
||||||
|
""" Initialise the bindings. """
|
||||||
|
|
||||||
|
super().__init__(project, 'ads')
|
||||||
|
|
||||||
|
def apply_user_defaults(self, tool):
|
||||||
|
""" Set default values for user options that haven't been set yet. """
|
||||||
|
|
||||||
|
resource_file = os.path.join(self.project.root_dir,'src','ads.qrc')
|
||||||
|
print("Adding resource file to qmake project: ", resource_file)
|
||||||
|
self.builder_settings.append('RESOURCES += '+resource_file)
|
||||||
|
|
||||||
|
super().apply_user_defaults(tool)
|
||||||
65
pyproject.toml
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
# Specify the build system.
|
||||||
|
[build-system]
|
||||||
|
requires = ["sip >=6.0.2, <6.6", "PyQt-builder >=1.6, <2", "PyQt5>=5.15.4", "PyQt5-sip<13,>=12.8"]
|
||||||
|
build-backend = "sipbuild.api"
|
||||||
|
|
||||||
|
# Specify the PEP 566 metadata for the project.
|
||||||
|
[tool.sip.metadata]
|
||||||
|
name = "PyQtAds"
|
||||||
|
version = "3.8.2"
|
||||||
|
summary = "Python bindings for Qt Advanced Docking System"
|
||||||
|
home-page = "https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/"
|
||||||
|
license = "LGPL v2.1"
|
||||||
|
description-file = "README.md"
|
||||||
|
requires-dist = "PyQt5 (>=5.15.4)"
|
||||||
|
description-content-type = "text/markdown"
|
||||||
|
|
||||||
|
[tool.sip.project]
|
||||||
|
tag-prefix = "QtAds"
|
||||||
|
|
||||||
|
[tool.sip.bindings.ads]
|
||||||
|
define-macros = ["ADS_SHARED_EXPORT"]
|
||||||
|
sip-file = "ads.sip"
|
||||||
|
include-dirs = ["src"]
|
||||||
|
qmake-QT = ["widgets"]
|
||||||
|
headers = [
|
||||||
|
"src/DockAreaTabBar.h",
|
||||||
|
"src/DockAreaTitleBar.h",
|
||||||
|
"src/DockAreaTitleBar_p.h",
|
||||||
|
"src/DockAreaWidget.h",
|
||||||
|
"src/DockComponentsFactory.h",
|
||||||
|
"src/DockContainerWidget.h",
|
||||||
|
"src/DockFocusController.h",
|
||||||
|
"src/DockManager.h",
|
||||||
|
"src/DockOverlay.h",
|
||||||
|
"src/DockSplitter.h",
|
||||||
|
"src/DockWidget.h",
|
||||||
|
"src/DockWidgetTab.h",
|
||||||
|
"src/DockingStateReader.h",
|
||||||
|
"src/ElidingLabel.h",
|
||||||
|
"src/FloatingDockContainer.h",
|
||||||
|
"src/FloatingDragPreview.h",
|
||||||
|
"src/IconProvider.h",
|
||||||
|
"src/ads_globals.h",
|
||||||
|
# "src/linux/FloatingWidgetTitleBar.h",
|
||||||
|
]
|
||||||
|
sources = [
|
||||||
|
"src/DockAreaTabBar.cpp",
|
||||||
|
"src/DockAreaTitleBar.cpp",
|
||||||
|
"src/DockAreaWidget.cpp",
|
||||||
|
"src/DockComponentsFactory.cpp",
|
||||||
|
"src/DockContainerWidget.cpp",
|
||||||
|
"src/DockFocusController.cpp",
|
||||||
|
"src/DockManager.cpp",
|
||||||
|
"src/DockOverlay.cpp",
|
||||||
|
"src/DockSplitter.cpp",
|
||||||
|
"src/DockWidget.cpp",
|
||||||
|
"src/DockWidgetTab.cpp",
|
||||||
|
"src/DockingStateReader.cpp",
|
||||||
|
"src/ElidingLabel.cpp",
|
||||||
|
"src/FloatingDockContainer.cpp",
|
||||||
|
"src/FloatingDragPreview.cpp",
|
||||||
|
"src/IconProvider.cpp",
|
||||||
|
"src/ads_globals.cpp",
|
||||||
|
# "src/linux/FloatingWidgetTitleBar.cpp",
|
||||||
|
]
|
||||||
@@ -28,6 +28,13 @@ protected slots:
|
|||||||
void toggleView(bool Open);
|
void toggleView(bool Open);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
enum eDockAreaFlag
|
||||||
|
{
|
||||||
|
HideSingleWidgetTitleBar,
|
||||||
|
DefaultFlags
|
||||||
|
};
|
||||||
|
typedef QFlags<ads::CDockAreaWidget::eDockAreaFlag> DockAreaFlags;
|
||||||
|
|
||||||
CDockAreaWidget(ads::CDockManager* DockManager /TransferThis/, ads::CDockContainerWidget* parent /TransferThis/);
|
CDockAreaWidget(ads::CDockManager* DockManager /TransferThis/, ads::CDockContainerWidget* parent /TransferThis/);
|
||||||
virtual ~CDockAreaWidget();
|
virtual ~CDockAreaWidget();
|
||||||
ads::CDockManager* dockManager() const;
|
ads::CDockManager* dockManager() const;
|
||||||
@@ -52,6 +59,12 @@ public:
|
|||||||
void setAllowedAreas(DockWidgetAreas areas);
|
void setAllowedAreas(DockWidgetAreas areas);
|
||||||
DockWidgetAreas allowedAreas() const;
|
DockWidgetAreas allowedAreas() const;
|
||||||
CDockAreaTitleBar* titleBar() const;
|
CDockAreaTitleBar* titleBar() const;
|
||||||
|
|
||||||
|
DockAreaFlags dockAreaFlags() const;
|
||||||
|
void setDockAreaFlags(DockAreaFlags Flags);
|
||||||
|
void setDockAreaFlag(eDockAreaFlag Flag, bool On);
|
||||||
|
|
||||||
|
bool isCentralWidgetArea() const;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void setCurrentIndex(int index);
|
void setCurrentIndex(int index);
|
||||||
|
|||||||