forked from qt-creator/qt-creator
Add Advanced Docking System library
This library is a fork of the following repository https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System Development started from the following commit 1de42a9766134eecd2611c2b4e209d3e0ede74d2 Incorporate all commits until 3ffbbfb6d01ff211d8349027221a19b1419296b5 - Rename variables and files to follow the Qt style guide - General code cleanup (remove goto, fix typos, add overrides, remove explicit slots specifier, replace string-based with functor-based connections, replace dynamic_cast with qobject_cast) - Replace most preprocessor instructions Q_OS_LINUX/Q_OS_MACOS with Utils::HostOsInfo - Remove all QT_VERSION preprocessor instructions below 5.11 - Change loading and storage of workspaces. Store workspaces in separate file instead of a list in the Settings.ini - Add workspace dialog, model and view for managing workspaces - Rename XML tags and use enum/bool instead of ascii art/numbers as attribute values. Use base64 instead of hex for storing geometry info - Remove internal style sheets - Add more build systems (qmake, qbs) - Adapt copyright header - Remove unix specific build rules - Replace ADS_PRINT with QLoggingCategory - Replace Java-style with STL-style iterators Change-Id: Icf8c2fbaccec9680df83c6e2100e3446a090a437 Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
This commit is contained in:
committed by
Henning Gründl
parent
61932b5d4b
commit
8f686e985c
@@ -1,5 +1,6 @@
|
|||||||
add_subdirectory(3rdparty)
|
add_subdirectory(3rdparty)
|
||||||
|
|
||||||
|
add_subdirectory(advanceddockingsystem)
|
||||||
add_subdirectory(aggregation)
|
add_subdirectory(aggregation)
|
||||||
add_subdirectory(extensionsystem)
|
add_subdirectory(extensionsystem)
|
||||||
add_subdirectory(utils)
|
add_subdirectory(utils)
|
||||||
|
|||||||
31
src/libs/advanceddockingsystem/CMakeLists.txt
Normal file
31
src/libs/advanceddockingsystem/CMakeLists.txt
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
add_qtc_library(AdvancedDockingSystem
|
||||||
|
DEPENDS Qt5::Widgets Qt5::Core Qt5::Gui Utils
|
||||||
|
SOURCES
|
||||||
|
ads_globals.cpp ads_globals.h
|
||||||
|
dockareatabbar.cpp dockareatabbar.h
|
||||||
|
dockareatitlebar.cpp dockareatitlebar.h
|
||||||
|
dockareawidget.cpp dockareawidget.h
|
||||||
|
dockcomponentsfactory.cpp dockcomponentsfactory.h
|
||||||
|
dockcontainerwidget.cpp dockcontainerwidget.h
|
||||||
|
dockingstatereader.cpp dockingstatereader.h
|
||||||
|
dockmanager.cpp dockmanager.h
|
||||||
|
dockoverlay.cpp dockoverlay.h
|
||||||
|
docksplitter.cpp docksplitter.h
|
||||||
|
dockwidget.cpp dockwidget.h
|
||||||
|
dockwidgettab.cpp dockwidgettab.h
|
||||||
|
elidinglabel.cpp elidinglabel.h
|
||||||
|
floatingdockcontainer.cpp floatingdockcontainer.h
|
||||||
|
floatingdragpreview.cpp floatingdragpreview.h
|
||||||
|
iconprovider.cpp iconprovider.h
|
||||||
|
workspacedialog.cpp workspacedialog.h
|
||||||
|
workspacemodel.cpp workspacemodel.h
|
||||||
|
workspaceview.cpp workspaceview.h
|
||||||
|
workspacedialog.ui
|
||||||
|
resources.qrc
|
||||||
|
)
|
||||||
|
|
||||||
|
extend_qtc_target(AdvancedDockingSystem
|
||||||
|
INCLUDES linux
|
||||||
|
SOURCES
|
||||||
|
linux/floatingwidgettitlebar.cpp linux/floatingwidgettitlebar.h
|
||||||
|
)
|
||||||
514
src/libs/advanceddockingsystem/LICENSE.LGPLv21
Normal file
514
src/libs/advanceddockingsystem/LICENSE.LGPLv21
Normal file
@@ -0,0 +1,514 @@
|
|||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
|
||||||
|
The Qt Toolkit is Copyright (C) 2015 The Qt Company Ltd.
|
||||||
|
Contact: http://www.qt.io/licensing/
|
||||||
|
|
||||||
|
You may use, distribute and copy the Qt Toolkit under the terms of
|
||||||
|
GNU Lesser General Public License version 2.1, which is displayed below.
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
Version 2.1, February 1999
|
||||||
|
|
||||||
|
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
[This is the first released version of the Lesser GPL. It also counts
|
||||||
|
as the successor of the GNU Library Public License, version 2, hence
|
||||||
|
the version number 2.1.]
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
Licenses are intended to guarantee your freedom to share and change
|
||||||
|
free software--to make sure the software is free for all its users.
|
||||||
|
|
||||||
|
This license, the Lesser General Public License, applies to some
|
||||||
|
specially designated software packages--typically libraries--of the
|
||||||
|
Free Software Foundation and other authors who decide to use it. You
|
||||||
|
can use it too, but we suggest you first think carefully about whether
|
||||||
|
this license or the ordinary General Public License is the better
|
||||||
|
strategy to use in any particular case, based on the explanations below.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom of use,
|
||||||
|
not price. Our General Public Licenses are designed to make sure that
|
||||||
|
you have the freedom to distribute copies of free software (and charge
|
||||||
|
for this service if you wish); that you receive source code or can get
|
||||||
|
it if you want it; that you can change the software and use pieces of
|
||||||
|
it in new free programs; and that you are informed that you can do
|
||||||
|
these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
distributors to deny you these rights or to ask you to surrender these
|
||||||
|
rights. These restrictions translate to certain responsibilities for
|
||||||
|
you if you distribute copies of the library or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of the library, whether gratis
|
||||||
|
or for a fee, you must give the recipients all the rights that we gave
|
||||||
|
you. You must make sure that they, too, receive or can get the source
|
||||||
|
code. If you link other code with the library, you must provide
|
||||||
|
complete object files to the recipients, so that they can relink them
|
||||||
|
with the library after making changes to the library and recompiling
|
||||||
|
it. And you must show them these terms so they know their rights.
|
||||||
|
|
||||||
|
We protect your rights with a two-step method: (1) we copyright the
|
||||||
|
library, and (2) we offer you this license, which gives you legal
|
||||||
|
permission to copy, distribute and/or modify the library.
|
||||||
|
|
||||||
|
To protect each distributor, we want to make it very clear that
|
||||||
|
there is no warranty for the free library. Also, if the library is
|
||||||
|
modified by someone else and passed on, the recipients should know
|
||||||
|
that what they have is not the original version, so that the original
|
||||||
|
author's reputation will not be affected by problems that might be
|
||||||
|
introduced by others.
|
||||||
|
|
||||||
|
Finally, software patents pose a constant threat to the existence of
|
||||||
|
any free program. We wish to make sure that a company cannot
|
||||||
|
effectively restrict the users of a free program by obtaining a
|
||||||
|
restrictive license from a patent holder. Therefore, we insist that
|
||||||
|
any patent license obtained for a version of the library must be
|
||||||
|
consistent with the full freedom of use specified in this license.
|
||||||
|
|
||||||
|
Most GNU software, including some libraries, is covered by the
|
||||||
|
ordinary GNU General Public License. This license, the GNU Lesser
|
||||||
|
General Public License, applies to certain designated libraries, and
|
||||||
|
is quite different from the ordinary General Public License. We use
|
||||||
|
this license for certain libraries in order to permit linking those
|
||||||
|
libraries into non-free programs.
|
||||||
|
|
||||||
|
When a program is linked with a library, whether statically or using
|
||||||
|
a shared library, the combination of the two is legally speaking a
|
||||||
|
combined work, a derivative of the original library. The ordinary
|
||||||
|
General Public License therefore permits such linking only if the
|
||||||
|
entire combination fits its criteria of freedom. The Lesser General
|
||||||
|
Public License permits more lax criteria for linking other code with
|
||||||
|
the library.
|
||||||
|
|
||||||
|
We call this license the "Lesser" General Public License because it
|
||||||
|
does Less to protect the user's freedom than the ordinary General
|
||||||
|
Public License. It also provides other free software developers Less
|
||||||
|
of an advantage over competing non-free programs. These disadvantages
|
||||||
|
are the reason we use the ordinary General Public License for many
|
||||||
|
libraries. However, the Lesser license provides advantages in certain
|
||||||
|
special circumstances.
|
||||||
|
|
||||||
|
For example, on rare occasions, there may be a special need to
|
||||||
|
encourage the widest possible use of a certain library, so that it becomes
|
||||||
|
a de-facto standard. To achieve this, non-free programs must be
|
||||||
|
allowed to use the library. A more frequent case is that a free
|
||||||
|
library does the same job as widely used non-free libraries. In this
|
||||||
|
case, there is little to gain by limiting the free library to free
|
||||||
|
software only, so we use the Lesser General Public License.
|
||||||
|
|
||||||
|
In other cases, permission to use a particular library in non-free
|
||||||
|
programs enables a greater number of people to use a large body of
|
||||||
|
free software. For example, permission to use the GNU C Library in
|
||||||
|
non-free programs enables many more people to use the whole GNU
|
||||||
|
operating system, as well as its variant, the GNU/Linux operating
|
||||||
|
system.
|
||||||
|
|
||||||
|
Although the Lesser General Public License is Less protective of the
|
||||||
|
users' freedom, it does ensure that the user of a program that is
|
||||||
|
linked with the Library has the freedom and the wherewithal to run
|
||||||
|
that program using a modified version of the Library.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow. Pay close attention to the difference between a
|
||||||
|
"work based on the library" and a "work that uses the library". The
|
||||||
|
former contains code derived from the library, whereas the latter must
|
||||||
|
be combined with the library in order to run.
|
||||||
|
|
||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License Agreement applies to any software library or other
|
||||||
|
program which contains a notice placed by the copyright holder or
|
||||||
|
other authorized party saying it may be distributed under the terms of
|
||||||
|
this Lesser General Public License (also called "this License").
|
||||||
|
Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
A "library" means a collection of software functions and/or data
|
||||||
|
prepared so as to be conveniently linked with application programs
|
||||||
|
(which use some of those functions and data) to form executables.
|
||||||
|
|
||||||
|
The "Library", below, refers to any such software library or work
|
||||||
|
which has been distributed under these terms. A "work based on the
|
||||||
|
Library" means either the Library or any derivative work under
|
||||||
|
copyright law: that is to say, a work containing the Library or a
|
||||||
|
portion of it, either verbatim or with modifications and/or translated
|
||||||
|
straightforwardly into another language. (Hereinafter, translation is
|
||||||
|
included without limitation in the term "modification".)
|
||||||
|
|
||||||
|
"Source code" for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For a library, complete source code means
|
||||||
|
all the source code for all modules it contains, plus any associated
|
||||||
|
interface definition files, plus the scripts used to control compilation
|
||||||
|
and installation of the library.
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running a program using the Library is not restricted, and output from
|
||||||
|
such a program is covered only if its contents constitute a work based
|
||||||
|
on the Library (independent of the use of the Library in a tool for
|
||||||
|
writing it). Whether that is true depends on what the Library does
|
||||||
|
and what the program that uses the Library does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Library's
|
||||||
|
complete source code as you receive it, in any medium, provided that
|
||||||
|
you conspicuously and appropriately publish on each copy an
|
||||||
|
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||||
|
all the notices that refer to this License and to the absence of any
|
||||||
|
warranty; and distribute a copy of this License along with the
|
||||||
|
Library.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy,
|
||||||
|
and you may at your option offer warranty protection in exchange for a
|
||||||
|
fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Library or any portion
|
||||||
|
of it, thus forming a work based on the Library, and copy and
|
||||||
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
above, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The modified work must itself be a software library.
|
||||||
|
|
||||||
|
b) You must cause the files modified to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
c) You must cause the whole of the work to be licensed at no
|
||||||
|
charge to all third parties under the terms of this License.
|
||||||
|
|
||||||
|
d) If a facility in the modified Library refers to a function or a
|
||||||
|
table of data to be supplied by an application program that uses
|
||||||
|
the facility, other than as an argument passed when the facility
|
||||||
|
is invoked, then you must make a good faith effort to ensure that,
|
||||||
|
in the event an application does not supply such function or
|
||||||
|
table, the facility still operates, and performs whatever part of
|
||||||
|
its purpose remains meaningful.
|
||||||
|
|
||||||
|
(For example, a function in a library to compute square roots has
|
||||||
|
a purpose that is entirely well-defined independent of the
|
||||||
|
application. Therefore, Subsection 2d requires that any
|
||||||
|
application-supplied function or table used by this function must
|
||||||
|
be optional: if the application does not supply it, the square
|
||||||
|
root function must still compute square roots.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Library,
|
||||||
|
and can be reasonably considered independent and separate works in
|
||||||
|
themselves, then this License, and its terms, do not apply to those
|
||||||
|
sections when you distribute them as separate works. But when you
|
||||||
|
distribute the same sections as part of a whole which is a work based
|
||||||
|
on the Library, the distribution of the whole must be on the terms of
|
||||||
|
this License, whose permissions for other licensees extend to the
|
||||||
|
entire whole, and thus to each and every part regardless of who wrote
|
||||||
|
it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest
|
||||||
|
your rights to work written entirely by you; rather, the intent is to
|
||||||
|
exercise the right to control the distribution of derivative or
|
||||||
|
collective works based on the Library.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Library
|
||||||
|
with the Library (or with a work based on the Library) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||||
|
License instead of this License to a given copy of the Library. To do
|
||||||
|
this, you must alter all the notices that refer to this License, so
|
||||||
|
that they refer to the ordinary GNU General Public License, version 2,
|
||||||
|
instead of to this License. (If a newer version than version 2 of the
|
||||||
|
ordinary GNU General Public License has appeared, then you can specify
|
||||||
|
that version instead if you wish.) Do not make any other change in
|
||||||
|
these notices.
|
||||||
|
|
||||||
|
Once this change is made in a given copy, it is irreversible for
|
||||||
|
that copy, so the ordinary GNU General Public License applies to all
|
||||||
|
subsequent copies and derivative works made from that copy.
|
||||||
|
|
||||||
|
This option is useful when you wish to copy part of the code of
|
||||||
|
the Library into a program that is not a library.
|
||||||
|
|
||||||
|
4. You may copy and distribute the Library (or a portion or
|
||||||
|
derivative of it, under Section 2) in object code or executable form
|
||||||
|
under the terms of Sections 1 and 2 above provided that you accompany
|
||||||
|
it with the complete corresponding machine-readable source code, which
|
||||||
|
must be distributed under the terms of Sections 1 and 2 above on a
|
||||||
|
medium customarily used for software interchange.
|
||||||
|
|
||||||
|
If distribution of object code is made by offering access to copy
|
||||||
|
from a designated place, then offering equivalent access to copy the
|
||||||
|
source code from the same place satisfies the requirement to
|
||||||
|
distribute the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
5. A program that contains no derivative of any portion of the
|
||||||
|
Library, but is designed to work with the Library by being compiled or
|
||||||
|
linked with it, is called a "work that uses the Library". Such a
|
||||||
|
work, in isolation, is not a derivative work of the Library, and
|
||||||
|
therefore falls outside the scope of this License.
|
||||||
|
|
||||||
|
However, linking a "work that uses the Library" with the Library
|
||||||
|
creates an executable that is a derivative of the Library (because it
|
||||||
|
contains portions of the Library), rather than a "work that uses the
|
||||||
|
library". The executable is therefore covered by this License.
|
||||||
|
Section 6 states terms for distribution of such executables.
|
||||||
|
|
||||||
|
When a "work that uses the Library" uses material from a header file
|
||||||
|
that is part of the Library, the object code for the work may be a
|
||||||
|
derivative work of the Library even though the source code is not.
|
||||||
|
Whether this is true is especially significant if the work can be
|
||||||
|
linked without the Library, or if the work is itself a library. The
|
||||||
|
threshold for this to be true is not precisely defined by law.
|
||||||
|
|
||||||
|
If such an object file uses only numerical parameters, data
|
||||||
|
structure layouts and accessors, and small macros and small inline
|
||||||
|
functions (ten lines or less in length), then the use of the object
|
||||||
|
file is unrestricted, regardless of whether it is legally a derivative
|
||||||
|
work. (Executables containing this object code plus portions of the
|
||||||
|
Library will still fall under Section 6.)
|
||||||
|
|
||||||
|
Otherwise, if the work is a derivative of the Library, you may
|
||||||
|
distribute the object code for the work under the terms of Section 6.
|
||||||
|
Any executables containing that work also fall under Section 6,
|
||||||
|
whether or not they are linked directly with the Library itself.
|
||||||
|
|
||||||
|
6. As an exception to the Sections above, you may also combine or
|
||||||
|
link a "work that uses the Library" with the Library to produce a
|
||||||
|
work containing portions of the Library, and distribute that work
|
||||||
|
under terms of your choice, provided that the terms permit
|
||||||
|
modification of the work for the customer's own use and reverse
|
||||||
|
engineering for debugging such modifications.
|
||||||
|
|
||||||
|
You must give prominent notice with each copy of the work that the
|
||||||
|
Library is used in it and that the Library and its use are covered by
|
||||||
|
this License. You must supply a copy of this License. If the work
|
||||||
|
during execution displays copyright notices, you must include the
|
||||||
|
copyright notice for the Library among them, as well as a reference
|
||||||
|
directing the user to the copy of this License. Also, you must do one
|
||||||
|
of these things:
|
||||||
|
|
||||||
|
a) Accompany the work with the complete corresponding
|
||||||
|
machine-readable source code for the Library including whatever
|
||||||
|
changes were used in the work (which must be distributed under
|
||||||
|
Sections 1 and 2 above); and, if the work is an executable linked
|
||||||
|
with the Library, with the complete machine-readable "work that
|
||||||
|
uses the Library", as object code and/or source code, so that the
|
||||||
|
user can modify the Library and then relink to produce a modified
|
||||||
|
executable containing the modified Library. (It is understood
|
||||||
|
that the user who changes the contents of definitions files in the
|
||||||
|
Library will not necessarily be able to recompile the application
|
||||||
|
to use the modified definitions.)
|
||||||
|
|
||||||
|
b) Use a suitable shared library mechanism for linking with the
|
||||||
|
Library. A suitable mechanism is one that (1) uses at run time a
|
||||||
|
copy of the library already present on the user's computer system,
|
||||||
|
rather than copying library functions into the executable, and (2)
|
||||||
|
will operate properly with a modified version of the library, if
|
||||||
|
the user installs one, as long as the modified version is
|
||||||
|
interface-compatible with the version that the work was made with.
|
||||||
|
|
||||||
|
c) Accompany the work with a written offer, valid for at
|
||||||
|
least three years, to give the same user the materials
|
||||||
|
specified in Subsection 6a, above, for a charge no more
|
||||||
|
than the cost of performing this distribution.
|
||||||
|
|
||||||
|
d) If distribution of the work is made by offering access to copy
|
||||||
|
from a designated place, offer equivalent access to copy the above
|
||||||
|
specified materials from the same place.
|
||||||
|
|
||||||
|
e) Verify that the user has already received a copy of these
|
||||||
|
materials or that you have already sent this user a copy.
|
||||||
|
|
||||||
|
For an executable, the required form of the "work that uses the
|
||||||
|
Library" must include any data and utility programs needed for
|
||||||
|
reproducing the executable from it. However, as a special exception,
|
||||||
|
the materials to be distributed need not include anything that is
|
||||||
|
normally distributed (in either source or binary form) with the major
|
||||||
|
components (compiler, kernel, and so on) of the operating system on
|
||||||
|
which the executable runs, unless that component itself accompanies
|
||||||
|
the executable.
|
||||||
|
|
||||||
|
It may happen that this requirement contradicts the license
|
||||||
|
restrictions of other proprietary libraries that do not normally
|
||||||
|
accompany the operating system. Such a contradiction means you cannot
|
||||||
|
use both them and the Library together in an executable that you
|
||||||
|
distribute.
|
||||||
|
|
||||||
|
7. You may place library facilities that are a work based on the
|
||||||
|
Library side-by-side in a single library together with other library
|
||||||
|
facilities not covered by this License, and distribute such a combined
|
||||||
|
library, provided that the separate distribution of the work based on
|
||||||
|
the Library and of the other library facilities is otherwise
|
||||||
|
permitted, and provided that you do these two things:
|
||||||
|
|
||||||
|
a) Accompany the combined library with a copy of the same work
|
||||||
|
based on the Library, uncombined with any other library
|
||||||
|
facilities. This must be distributed under the terms of the
|
||||||
|
Sections above.
|
||||||
|
|
||||||
|
b) Give prominent notice with the combined library of the fact
|
||||||
|
that part of it is a work based on the Library, and explaining
|
||||||
|
where to find the accompanying uncombined form of the same work.
|
||||||
|
|
||||||
|
8. You may not copy, modify, sublicense, link with, or distribute
|
||||||
|
the Library except as expressly provided under this License. Any
|
||||||
|
attempt otherwise to copy, modify, sublicense, link with, or
|
||||||
|
distribute the Library is void, and will automatically terminate your
|
||||||
|
rights under this License. However, parties who have received copies,
|
||||||
|
or rights, from you under this License will not have their licenses
|
||||||
|
terminated so long as such parties remain in full compliance.
|
||||||
|
|
||||||
|
9. You are not required to accept this License, since you have not
|
||||||
|
signed it. However, nothing else grants you permission to modify or
|
||||||
|
distribute the Library or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Library (or any work based on the
|
||||||
|
Library), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Library or works based on it.
|
||||||
|
|
||||||
|
10. Each time you redistribute the Library (or any work based on the
|
||||||
|
Library), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute, link with or modify the Library
|
||||||
|
subject to these terms and conditions. You may not impose any further
|
||||||
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
|
You are not responsible for enforcing compliance by third parties with
|
||||||
|
this License.
|
||||||
|
|
||||||
|
11. If, as a consequence of a court judgment or allegation of patent
|
||||||
|
infringement or for any other reason (not limited to patent issues),
|
||||||
|
conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot
|
||||||
|
distribute so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you
|
||||||
|
may not distribute the Library at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Library by
|
||||||
|
all those who receive copies directly or indirectly through you, then
|
||||||
|
the only way you could satisfy both it and this License would be to
|
||||||
|
refrain entirely from distribution of the Library.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under any
|
||||||
|
particular circumstance, the balance of the section is intended to apply,
|
||||||
|
and the section as a whole is intended to apply in other circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of any
|
||||||
|
such claims; this section has the sole purpose of protecting the
|
||||||
|
integrity of the free software distribution system which is
|
||||||
|
implemented by public license practices. Many people have made
|
||||||
|
generous contributions to the wide range of software distributed
|
||||||
|
through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing
|
||||||
|
to distribute software through any other system and a licensee cannot
|
||||||
|
impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to
|
||||||
|
be a consequence of the rest of this License.
|
||||||
|
|
||||||
|
12. If the distribution and/or use of the Library is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Library under this License may add
|
||||||
|
an explicit geographical distribution limitation excluding those countries,
|
||||||
|
so that distribution is permitted only in or among countries not thus
|
||||||
|
excluded. In such case, this License incorporates the limitation as if
|
||||||
|
written in the body of this License.
|
||||||
|
|
||||||
|
13. The Free Software Foundation may publish revised and/or new
|
||||||
|
versions of the Lesser General Public License from time to time.
|
||||||
|
Such new versions will be similar in spirit to the present version,
|
||||||
|
but may differ in detail to address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Library
|
||||||
|
specifies a version number of this License which applies to it and
|
||||||
|
"any later version", you have the option of following the terms and
|
||||||
|
conditions either of that version or of any later version published by
|
||||||
|
the Free Software Foundation. If the Library does not specify a
|
||||||
|
license version number, you may choose any version ever published by
|
||||||
|
the Free Software Foundation.
|
||||||
|
|
||||||
|
14. If you wish to incorporate parts of the Library into other free
|
||||||
|
programs whose distribution conditions are incompatible with these,
|
||||||
|
write to the author to ask for permission. For software which is
|
||||||
|
copyrighted by the Free Software Foundation, write to the Free
|
||||||
|
Software Foundation; we sometimes make exceptions for this. Our
|
||||||
|
decision will be guided by the two goals of preserving the free status
|
||||||
|
of all derivatives of our free software and of promoting the sharing
|
||||||
|
and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||||
|
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||||
|
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||||
|
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||||
|
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||||
|
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||||
|
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||||
|
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||||
|
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||||
|
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||||
|
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||||
|
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||||
|
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||||
|
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||||
|
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||||
|
DAMAGES.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Libraries
|
||||||
|
|
||||||
|
If you develop a new library, and you want it to be of the greatest
|
||||||
|
possible use to the public, we recommend making it free software that
|
||||||
|
everyone can redistribute and change. You can do so by permitting
|
||||||
|
redistribution under these terms (or, alternatively, under the terms of the
|
||||||
|
ordinary General Public License).
|
||||||
|
|
||||||
|
To apply these terms, attach the following notices to the library. It is
|
||||||
|
safest to attach them to the start of each source file to most effectively
|
||||||
|
convey the exclusion of warranty; and each file should have at least the
|
||||||
|
"copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the library's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your
|
||||||
|
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||||
|
necessary. Here is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||||
|
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||||
|
|
||||||
|
<signature of Ty Coon>, 1 April 1990
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
That's all there is to it!
|
||||||
|
|
||||||
|
|
||||||
120
src/libs/advanceddockingsystem/ads_globals.cpp
Normal file
120
src/libs/advanceddockingsystem/ads_globals.cpp
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 Uwe Kindler
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "ads_globals.h"
|
||||||
|
|
||||||
|
#include "dockmanager.h"
|
||||||
|
#include "docksplitter.h"
|
||||||
|
#include "iconprovider.h"
|
||||||
|
|
||||||
|
#include <QAbstractButton>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
namespace ADS {
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
void replaceSplitterWidget(QSplitter *splitter, QWidget *from, QWidget *to)
|
||||||
|
{
|
||||||
|
int index = splitter->indexOf(from);
|
||||||
|
from->setParent(nullptr);
|
||||||
|
splitter->insertWidget(index, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
DockInsertParam dockAreaInsertParameters(DockWidgetArea area)
|
||||||
|
{
|
||||||
|
switch (area) {
|
||||||
|
case TopDockWidgetArea:
|
||||||
|
return DockInsertParam(Qt::Vertical, false);
|
||||||
|
case RightDockWidgetArea:
|
||||||
|
return DockInsertParam(Qt::Horizontal, true);
|
||||||
|
case CenterDockWidgetArea:
|
||||||
|
case BottomDockWidgetArea:
|
||||||
|
return DockInsertParam(Qt::Vertical, true);
|
||||||
|
case LeftDockWidgetArea:
|
||||||
|
return DockInsertParam(Qt::Horizontal, false);
|
||||||
|
default:
|
||||||
|
DockInsertParam(Qt::Vertical, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return DockInsertParam(Qt::Vertical, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap createTransparentPixmap(const QPixmap &source, qreal opacity)
|
||||||
|
{
|
||||||
|
QPixmap transparentPixmap(source.size());
|
||||||
|
transparentPixmap.fill(Qt::transparent);
|
||||||
|
QPainter painter(&transparentPixmap);
|
||||||
|
painter.setOpacity(opacity);
|
||||||
|
painter.drawPixmap(0, 0, source);
|
||||||
|
return transparentPixmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hideEmptyParentSplitters(DockSplitter *splitter)
|
||||||
|
{
|
||||||
|
while (splitter && splitter->isVisible()) {
|
||||||
|
if (!splitter->hasVisibleContent()) {
|
||||||
|
splitter->hide();
|
||||||
|
}
|
||||||
|
splitter = internal::findParent<DockSplitter *>(splitter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setButtonIcon(QAbstractButton* button,
|
||||||
|
QStyle::StandardPixmap standarPixmap,
|
||||||
|
ADS::eIcon customIconId)
|
||||||
|
{
|
||||||
|
// First we try to use custom icons if available
|
||||||
|
QIcon icon = DockManager::iconProvider().customIcon(customIconId);
|
||||||
|
if (!icon.isNull()) {
|
||||||
|
button->setIcon(icon);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Utils::HostOsInfo::isLinuxHost()) {
|
||||||
|
button->setIcon(button->style()->standardIcon(standarPixmap));
|
||||||
|
} else {
|
||||||
|
// The standard icons does not look good on high DPI screens so we create
|
||||||
|
// our own "standard" icon here.
|
||||||
|
QPixmap normalPixmap = button->style()->standardPixmap(standarPixmap, nullptr, button);
|
||||||
|
icon.addPixmap(internal::createTransparentPixmap(normalPixmap, 0.25), QIcon::Disabled);
|
||||||
|
icon.addPixmap(normalPixmap, QIcon::Normal);
|
||||||
|
button->setIcon(icon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace ADS
|
||||||
232
src/libs/advanceddockingsystem/ads_globals.h
Normal file
232
src/libs/advanceddockingsystem/ads_globals.h
Normal file
@@ -0,0 +1,232 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 Uwe Kindler
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QPair>
|
||||||
|
#include <QPixmap>
|
||||||
|
#include <QStyle>
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QtCore/QtGlobal>
|
||||||
|
|
||||||
|
class QAbstractButton;
|
||||||
|
|
||||||
|
#ifndef ADS_STATIC
|
||||||
|
#ifdef ADVANCEDDOCKINGSYSTEM_LIBRARY
|
||||||
|
#define ADS_EXPORT Q_DECL_EXPORT
|
||||||
|
#else
|
||||||
|
#define ADS_EXPORT Q_DECL_IMPORT
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define ADS_EXPORT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//#define ADS_DEBUG_PRINT
|
||||||
|
|
||||||
|
// Define ADS_DEBUG_PRINT to enable a lot of debug output
|
||||||
|
#ifdef ADS_DEBUG_PRINT
|
||||||
|
#define ADS_PRINT(s) qDebug() << s
|
||||||
|
#else
|
||||||
|
#define ADS_PRINT(s)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Set ADS_DEBUG_LEVEL to enable additional debug output and to enable layout
|
||||||
|
// dumps to qDebug and std::cout after layout changes
|
||||||
|
#define ADS_DEBUG_LEVEL 0
|
||||||
|
|
||||||
|
class QSplitter;
|
||||||
|
|
||||||
|
namespace ADS {
|
||||||
|
|
||||||
|
enum eStateFileVersion { InitialVerison = 0, Version1 = 1, CurrentVersion = Version1 };
|
||||||
|
|
||||||
|
class DockSplitter;
|
||||||
|
|
||||||
|
enum DockWidgetArea {
|
||||||
|
NoDockWidgetArea = 0x00,
|
||||||
|
LeftDockWidgetArea = 0x01,
|
||||||
|
RightDockWidgetArea = 0x02,
|
||||||
|
TopDockWidgetArea = 0x04,
|
||||||
|
BottomDockWidgetArea = 0x08,
|
||||||
|
CenterDockWidgetArea = 0x10,
|
||||||
|
|
||||||
|
InvalidDockWidgetArea = NoDockWidgetArea,
|
||||||
|
OuterDockAreas = TopDockWidgetArea | LeftDockWidgetArea | RightDockWidgetArea
|
||||||
|
| BottomDockWidgetArea,
|
||||||
|
AllDockAreas = OuterDockAreas | CenterDockWidgetArea
|
||||||
|
};
|
||||||
|
Q_DECLARE_FLAGS(DockWidgetAreas, DockWidgetArea)
|
||||||
|
|
||||||
|
enum eTitleBarButton { TitleBarButtonTabsMenu, TitleBarButtonUndock, TitleBarButtonClose };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The different dragging states
|
||||||
|
*/
|
||||||
|
enum eDragState {
|
||||||
|
DraggingInactive, //!< DraggingInactive
|
||||||
|
DraggingMousePressed, //!< DraggingMousePressed
|
||||||
|
DraggingTab, //!< DraggingTab
|
||||||
|
DraggingFloatingWidget //!< DraggingFloatingWidget
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The different icons used in the UI
|
||||||
|
*/
|
||||||
|
enum eIcon {
|
||||||
|
TabCloseIcon, //!< TabCloseIcon
|
||||||
|
DockAreaMenuIcon, //!< DockAreaMenuIcon
|
||||||
|
DockAreaUndockIcon, //!< DockAreaUndockIcon
|
||||||
|
DockAreaCloseIcon, //!< DockAreaCloseIcon
|
||||||
|
|
||||||
|
IconCount, //!< just a delimiter for range checks
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For bitwise combination of dock wdget features
|
||||||
|
*/
|
||||||
|
enum eBitwiseOperator
|
||||||
|
{
|
||||||
|
BitwiseAnd,
|
||||||
|
BitwiseOr
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
const bool restoreTesting = true;
|
||||||
|
const bool restore = false;
|
||||||
|
const char *const closedProperty = "close";
|
||||||
|
const char *const dirtyProperty = "dirty";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace the from widget in the given splitter with the To widget
|
||||||
|
*/
|
||||||
|
void replaceSplitterWidget(QSplitter *splitter, QWidget *from, QWidget *to);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function walks the splitter tree upwards to hides all splitters
|
||||||
|
* that do not have visible content
|
||||||
|
*/
|
||||||
|
void hideEmptyParentSplitters(DockSplitter *firstParentSplitter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience class for QPair to provide better naming than first and
|
||||||
|
* second
|
||||||
|
*/
|
||||||
|
class DockInsertParam : public QPair<Qt::Orientation, bool>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using QPair::QPair;
|
||||||
|
Qt::Orientation orientation() const { return this->first; }
|
||||||
|
bool append() const { return this->second; }
|
||||||
|
int insertOffset() const { return append() ? 1 : 0; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the insertion parameters for the given dock area
|
||||||
|
*/
|
||||||
|
DockInsertParam dockAreaInsertParameters(DockWidgetArea area);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches for the parent widget of the given type.
|
||||||
|
* Returns the parent widget of the given widget or 0 if the widget is not
|
||||||
|
* child of any widget of type T
|
||||||
|
*
|
||||||
|
* It is not safe to use this function in in DockWidget because only
|
||||||
|
* the current dock widget has a parent. All dock widgets that are not the
|
||||||
|
* current dock widget in a dock area have no parent.
|
||||||
|
*/
|
||||||
|
template<class T>
|
||||||
|
T findParent(const QWidget *widget)
|
||||||
|
{
|
||||||
|
QWidget *parentWidget = widget->parentWidget();
|
||||||
|
while (parentWidget) {
|
||||||
|
T parentImpl = qobject_cast<T>(parentWidget);
|
||||||
|
if (parentImpl) {
|
||||||
|
return parentImpl;
|
||||||
|
}
|
||||||
|
parentWidget = parentWidget->parentWidget();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a semi transparent pixmap from the given pixmap Source.
|
||||||
|
* The Opacity parameter defines the opacity from completely transparent (0.0)
|
||||||
|
* to completely opaque (1.0)
|
||||||
|
*/
|
||||||
|
QPixmap createTransparentPixmap(const QPixmap &source, qreal opacity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function for settings flags in a QFlags instance.
|
||||||
|
*/
|
||||||
|
template<class T>
|
||||||
|
void setFlag(T &flags, typename T::enum_type flag, bool on = true)
|
||||||
|
{
|
||||||
|
flags.setFlag(flag, on);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function for settings tooltips without cluttering the code with
|
||||||
|
* tests for preprocessor macros
|
||||||
|
*/
|
||||||
|
template <class QObjectPtr>
|
||||||
|
void setToolTip(QObjectPtr obj, const QString &tip)
|
||||||
|
{
|
||||||
|
#ifndef QT_NO_TOOLTIP
|
||||||
|
obj->setToolTip(tip);
|
||||||
|
#else
|
||||||
|
Q_UNUSED(obj);
|
||||||
|
Q_UNUSED(tip);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to set the icon of a certain button.
|
||||||
|
* Use this function to set the icons for the dock area and dock widget buttons.
|
||||||
|
* The function first uses the CustomIconId to get an icon from the
|
||||||
|
* IconProvider. You can register your custom icons with the icon provider, if
|
||||||
|
* you do not want to use the default buttons and if you do not want to use
|
||||||
|
* stylesheets.
|
||||||
|
* If the IconProvider does not return a valid icon (icon is null), the function
|
||||||
|
* fetches the given standard pixmap from the QStyle.
|
||||||
|
* param[in] Button The button whose icons are to be set
|
||||||
|
* param[in] StandardPixmap The standard pixmap to be used for the button
|
||||||
|
* param[in] CustomIconId The identifier for the custom icon.
|
||||||
|
*/
|
||||||
|
void setButtonIcon(QAbstractButton *button, QStyle::StandardPixmap standarPixmap,
|
||||||
|
ADS::eIcon CustomIconId);
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace ADS
|
||||||
58
src/libs/advanceddockingsystem/advanceddockingsystem-lib.pri
Normal file
58
src/libs/advanceddockingsystem/advanceddockingsystem-lib.pri
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
shared {
|
||||||
|
DEFINES += ADVANCEDDOCKINGSYSTEM_LIBRARY
|
||||||
|
} else {
|
||||||
|
DEFINES += BUILD_ADVANCEDDOCKINGSYSTEM_STATIC_LIB
|
||||||
|
}
|
||||||
|
|
||||||
|
## Input
|
||||||
|
RESOURCES += \
|
||||||
|
resources.qrc
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
ads_globals.h \
|
||||||
|
dockareatabbar.h \
|
||||||
|
dockareatitlebar.h \
|
||||||
|
dockareawidget.h \
|
||||||
|
dockcomponentsfactory.h \
|
||||||
|
dockcontainerwidget.h \
|
||||||
|
dockingstatereader.h \
|
||||||
|
dockmanager.h \
|
||||||
|
dockoverlay.h \
|
||||||
|
docksplitter.h \
|
||||||
|
dockwidget.h \
|
||||||
|
dockwidgettab.h \
|
||||||
|
elidinglabel.h \
|
||||||
|
floatingdockcontainer.h \
|
||||||
|
floatingdragpreview.h \
|
||||||
|
iconprovider.h \
|
||||||
|
workspacedialog.h \
|
||||||
|
workspacemodel.h \
|
||||||
|
workspaceview.h
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
ads_globals.cpp \
|
||||||
|
dockareatabbar.cpp \
|
||||||
|
dockareatitlebar.cpp \
|
||||||
|
dockareawidget.cpp \
|
||||||
|
dockcomponentsfactory.cpp \
|
||||||
|
dockcontainerwidget.cpp \
|
||||||
|
dockingstatereader.cpp \
|
||||||
|
dockmanager.cpp \
|
||||||
|
dockoverlay.cpp \
|
||||||
|
docksplitter.cpp \
|
||||||
|
dockwidget.cpp \
|
||||||
|
dockwidgettab.cpp \
|
||||||
|
elidinglabel.cpp \
|
||||||
|
floatingdockcontainer.cpp \
|
||||||
|
floatingdragpreview.cpp \
|
||||||
|
iconprovider.cpp \
|
||||||
|
workspacedialog.cpp \
|
||||||
|
workspacemodel.cpp \
|
||||||
|
workspaceview.cpp
|
||||||
|
|
||||||
|
FORMS += \
|
||||||
|
workspacedialog.ui
|
||||||
|
|
||||||
|
include(linux/linux.pri)
|
||||||
|
|
||||||
|
DISTFILES += advanceddockingsystem.pri
|
||||||
6
src/libs/advanceddockingsystem/advanceddockingsystem.pro
Normal file
6
src/libs/advanceddockingsystem/advanceddockingsystem.pro
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
unix:QMAKE_CXXFLAGS_DEBUG += -O3
|
||||||
|
|
||||||
|
INCLUDEPATH += $$PWD $$PWD/linux
|
||||||
|
|
||||||
|
include(../../qtcreatorlibrary.pri)
|
||||||
|
include(advanceddockingsystem-lib.pri)
|
||||||
48
src/libs/advanceddockingsystem/advanceddockingsystem.qbs
Normal file
48
src/libs/advanceddockingsystem/advanceddockingsystem.qbs
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import qbs 1.0
|
||||||
|
|
||||||
|
QtcLibrary {
|
||||||
|
name: "AdvancedDockingSystem"
|
||||||
|
|
||||||
|
cpp.optimization: "fast"
|
||||||
|
cpp.defines: base.concat("ADVANCEDDOCKINGSYSTEM_LIBRARY")
|
||||||
|
cpp.includePaths: base.concat([".", linux.prefix])
|
||||||
|
|
||||||
|
Depends { name: "Qt"; submodules: ["widgets", "core", "gui"] }
|
||||||
|
Depends { name: "Utils" }
|
||||||
|
|
||||||
|
Group {
|
||||||
|
name: "General"
|
||||||
|
files: [
|
||||||
|
"ads_globals.cpp", "ads_globals.h",
|
||||||
|
"dockareaareatabbar.cpp", "dockareatabbar.h",
|
||||||
|
"dockareatitlebar.cpp", "dockareatitlebar.h",
|
||||||
|
"dockareawidget.cpp", "dockareawidget.h",
|
||||||
|
"dockcomponentsfactory.cpp", "dockcomponentsfactory.h",
|
||||||
|
"dockcontainerwidget.cpp", "dockcontainerwidget.h",
|
||||||
|
"dockingstatereader.cpp", "dockingstatereader.h",
|
||||||
|
"dockmanager.cpp", "dockmanager.h",
|
||||||
|
"dockoverlay.cpp", "dockoverlay.h",
|
||||||
|
"docksplitter.cpp", "docksplitter.h",
|
||||||
|
"dockwidget.cpp", "dockwidget.h",
|
||||||
|
"dockwidgettab.cpp", "dockwidgettab.h",
|
||||||
|
"elidinglabel.cpp", "elidinglabel.h",
|
||||||
|
"floatingdockcontainer.cpp", "floatingdockcontainer.h",
|
||||||
|
"floatingdragpreview.cpp", "floatingdragpreview.h",
|
||||||
|
"iconprovider.cpp", "iconprovider.h",
|
||||||
|
"workspacedialog.cpp", "workspacedialog.h",
|
||||||
|
"workspacemodel.cpp", "workspacemodel.h",
|
||||||
|
"workspaceview.cpp", "workspaceview.h",
|
||||||
|
"workspacedialog.ui",
|
||||||
|
"resources.qrc"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Group {
|
||||||
|
name: "Linux"
|
||||||
|
id: linux
|
||||||
|
prefix: "linux/"
|
||||||
|
files: [
|
||||||
|
"floatingwidgettitlebar.cpp", "floatingwidgettitlebar.h"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
QTC_LIB_NAME = AdvancedDockingSystem
|
||||||
|
QTC_LIB_DEPENDS += utils
|
||||||
|
INCLUDEPATH *= $$IDE_SOURCE_TREE/src/libs/advanceddockingsystem
|
||||||
402
src/libs/advanceddockingsystem/dockareatabbar.cpp
Normal file
402
src/libs/advanceddockingsystem/dockareatabbar.cpp
Normal file
@@ -0,0 +1,402 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 Uwe Kindler
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "dockareatabbar.h"
|
||||||
|
|
||||||
|
#include "dockareawidget.h"
|
||||||
|
#include "dockmanager.h"
|
||||||
|
#include "dockoverlay.h"
|
||||||
|
#include "dockwidget.h"
|
||||||
|
#include "dockwidgettab.h"
|
||||||
|
#include "floatingdockcontainer.h"
|
||||||
|
#include "floatingdragpreview.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QBoxLayout>
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
#include <QMouseEvent>
|
||||||
|
#include <QScrollBar>
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtDebugMsg)
|
||||||
|
|
||||||
|
namespace ADS
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Private data class of DockAreaTabBar class (pimpl)
|
||||||
|
*/
|
||||||
|
struct DockAreaTabBarPrivate
|
||||||
|
{
|
||||||
|
DockAreaTabBar *q;
|
||||||
|
DockAreaWidget *m_dockArea;
|
||||||
|
QWidget *m_tabsContainerWidget;
|
||||||
|
QBoxLayout *m_tabsLayout;
|
||||||
|
int m_currentIndex = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private data constructor
|
||||||
|
*/
|
||||||
|
DockAreaTabBarPrivate(DockAreaTabBar *parent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update tabs after current index changed or when tabs are removed.
|
||||||
|
* The function reassigns the stylesheet to update the tabs
|
||||||
|
*/
|
||||||
|
void updateTabs();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience function to access first tab
|
||||||
|
*/
|
||||||
|
DockWidgetTab *firstTab() const {return q->tab(0);}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience function to access last tab
|
||||||
|
*/
|
||||||
|
DockWidgetTab *lastTab() const {return q->tab(q->count() - 1);}
|
||||||
|
};
|
||||||
|
// struct DockAreaTabBarPrivate
|
||||||
|
|
||||||
|
DockAreaTabBarPrivate::DockAreaTabBarPrivate(DockAreaTabBar *parent)
|
||||||
|
: q(parent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void DockAreaTabBarPrivate::updateTabs()
|
||||||
|
{
|
||||||
|
// Set active TAB and update all other tabs to be inactive
|
||||||
|
for (int i = 0; i < q->count(); ++i) {
|
||||||
|
auto tabWidget = q->tab(i);
|
||||||
|
if (!tabWidget)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (i == m_currentIndex) {
|
||||||
|
tabWidget->show();
|
||||||
|
tabWidget->setActiveTab(true);
|
||||||
|
q->ensureWidgetVisible(tabWidget);
|
||||||
|
} else {
|
||||||
|
tabWidget->setActiveTab(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DockAreaTabBar::DockAreaTabBar(DockAreaWidget *parent)
|
||||||
|
: QScrollArea(parent)
|
||||||
|
, d(new DockAreaTabBarPrivate(this))
|
||||||
|
{
|
||||||
|
d->m_dockArea = parent;
|
||||||
|
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
||||||
|
setFrameStyle(QFrame::NoFrame);
|
||||||
|
setWidgetResizable(true);
|
||||||
|
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
|
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
|
|
||||||
|
d->m_tabsContainerWidget = new QWidget();
|
||||||
|
d->m_tabsContainerWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
||||||
|
d->m_tabsContainerWidget->setObjectName("tabsContainerWidget");
|
||||||
|
d->m_tabsLayout = new QBoxLayout(QBoxLayout::LeftToRight);
|
||||||
|
d->m_tabsLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
d->m_tabsLayout->setSpacing(0);
|
||||||
|
d->m_tabsLayout->addStretch(1);
|
||||||
|
d->m_tabsContainerWidget->setLayout(d->m_tabsLayout);
|
||||||
|
setWidget(d->m_tabsContainerWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
DockAreaTabBar::~DockAreaTabBar() { delete d; }
|
||||||
|
|
||||||
|
void DockAreaTabBar::wheelEvent(QWheelEvent *event)
|
||||||
|
{
|
||||||
|
event->accept();
|
||||||
|
const int direction = event->angleDelta().y();
|
||||||
|
if (direction < 0) {
|
||||||
|
horizontalScrollBar()->setValue(horizontalScrollBar()->value() + 20);
|
||||||
|
} else {
|
||||||
|
horizontalScrollBar()->setValue(horizontalScrollBar()->value() - 20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaTabBar::setCurrentIndex(int index)
|
||||||
|
{
|
||||||
|
if (index == d->m_currentIndex)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (index < -1 || index > (count() - 1)) {
|
||||||
|
qWarning() << Q_FUNC_INFO << "Invalid index" << index;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit currentChanging(index);
|
||||||
|
d->m_currentIndex = index;
|
||||||
|
d->updateTabs();
|
||||||
|
updateGeometry();
|
||||||
|
emit currentChanged(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
int DockAreaTabBar::count() const
|
||||||
|
{
|
||||||
|
// The tab bar contains a stretch item as last item
|
||||||
|
return d->m_tabsLayout->count() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaTabBar::insertTab(int index, DockWidgetTab *dockWidgetTab)
|
||||||
|
{
|
||||||
|
d->m_tabsLayout->insertWidget(index, dockWidgetTab);
|
||||||
|
connect(dockWidgetTab, &DockWidgetTab::clicked, this, &DockAreaTabBar::onTabClicked);
|
||||||
|
connect(dockWidgetTab,
|
||||||
|
&DockWidgetTab::closeRequested,
|
||||||
|
this,
|
||||||
|
&DockAreaTabBar::onTabCloseRequested);
|
||||||
|
connect(dockWidgetTab,
|
||||||
|
&DockWidgetTab::closeOtherTabsRequested,
|
||||||
|
this,
|
||||||
|
&DockAreaTabBar::onCloseOtherTabsRequested);
|
||||||
|
connect(dockWidgetTab, &DockWidgetTab::moved, this, &DockAreaTabBar::onTabWidgetMoved);
|
||||||
|
connect(dockWidgetTab,
|
||||||
|
&DockWidgetTab::elidedChanged,
|
||||||
|
this,
|
||||||
|
&DockAreaTabBar::elidedChanged);
|
||||||
|
dockWidgetTab->installEventFilter(this);
|
||||||
|
emit tabInserted(index);
|
||||||
|
if (index <= d->m_currentIndex || d->m_currentIndex == -1) {
|
||||||
|
setCurrentIndex(d->m_currentIndex + 1);
|
||||||
|
}
|
||||||
|
updateGeometry();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaTabBar::removeTab(DockWidgetTab *dockWidgetTab)
|
||||||
|
{
|
||||||
|
if (!count())
|
||||||
|
return;
|
||||||
|
|
||||||
|
qCInfo(adsLog) << Q_FUNC_INFO;
|
||||||
|
int newCurrentIndex = currentIndex();
|
||||||
|
int removeIndex = d->m_tabsLayout->indexOf(dockWidgetTab);
|
||||||
|
if (count() == 1)
|
||||||
|
newCurrentIndex = -1;
|
||||||
|
|
||||||
|
if (newCurrentIndex > removeIndex) {
|
||||||
|
newCurrentIndex--;
|
||||||
|
} else if (newCurrentIndex == removeIndex) {
|
||||||
|
newCurrentIndex = -1;
|
||||||
|
// First we walk to the right to search for the next visible tab
|
||||||
|
for (int i = (removeIndex + 1); i < count(); ++i) {
|
||||||
|
if (tab(i)->isVisibleTo(this)) {
|
||||||
|
newCurrentIndex = i - 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is no visible tab right to this tab then we walk to
|
||||||
|
// the left to find a visible tab
|
||||||
|
if (newCurrentIndex < 0) {
|
||||||
|
for (int i = (removeIndex - 1); i >= 0; --i) {
|
||||||
|
if (tab(i)->isVisibleTo(this)) {
|
||||||
|
newCurrentIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emit removingTab(removeIndex);
|
||||||
|
d->m_tabsLayout->removeWidget(dockWidgetTab);
|
||||||
|
dockWidgetTab->disconnect(this);
|
||||||
|
dockWidgetTab->removeEventFilter(this);
|
||||||
|
qCInfo(adsLog) << "NewCurrentIndex " << newCurrentIndex;
|
||||||
|
if (newCurrentIndex != d->m_currentIndex) {
|
||||||
|
setCurrentIndex(newCurrentIndex);
|
||||||
|
} else {
|
||||||
|
d->updateTabs();
|
||||||
|
}
|
||||||
|
updateGeometry();
|
||||||
|
}
|
||||||
|
|
||||||
|
int DockAreaTabBar::currentIndex() const { return d->m_currentIndex; }
|
||||||
|
|
||||||
|
DockWidgetTab *DockAreaTabBar::currentTab() const
|
||||||
|
{
|
||||||
|
if (d->m_currentIndex < 0) {
|
||||||
|
return nullptr;
|
||||||
|
} else {
|
||||||
|
return qobject_cast<DockWidgetTab *>(
|
||||||
|
d->m_tabsLayout->itemAt(d->m_currentIndex)->widget());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaTabBar::onTabClicked()
|
||||||
|
{
|
||||||
|
DockWidgetTab *tab = qobject_cast<DockWidgetTab *>(sender());
|
||||||
|
if (!tab)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int index = d->m_tabsLayout->indexOf(tab);
|
||||||
|
if (index < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
setCurrentIndex(index);
|
||||||
|
emit tabBarClicked(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaTabBar::onTabCloseRequested()
|
||||||
|
{
|
||||||
|
DockWidgetTab *tab = qobject_cast<DockWidgetTab *>(sender());
|
||||||
|
int index = d->m_tabsLayout->indexOf(tab);
|
||||||
|
closeTab(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaTabBar::onCloseOtherTabsRequested()
|
||||||
|
{
|
||||||
|
auto senderTab = qobject_cast<DockWidgetTab *>(sender());
|
||||||
|
for (int i = 0; i < count(); ++i) {
|
||||||
|
auto currentTab = tab(i);
|
||||||
|
if (currentTab->isClosable() && !currentTab->isHidden() && currentTab != senderTab) {
|
||||||
|
// If the dock widget is deleted with the closeTab() call, its tab it will no longer
|
||||||
|
// be in the layout, and thus the index needs to be updated to not skip any tabs
|
||||||
|
int offset = currentTab->dockWidget()->features().testFlag(
|
||||||
|
DockWidget::DockWidgetDeleteOnClose)
|
||||||
|
? 1
|
||||||
|
: 0;
|
||||||
|
closeTab(i);
|
||||||
|
// If the the dock widget blocks closing, i.e. if the flag
|
||||||
|
// CustomCloseHandling is set, and the dock widget is still open,
|
||||||
|
// then we do not need to correct the index
|
||||||
|
if (currentTab->dockWidget()->isClosed()) {
|
||||||
|
i -= offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DockWidgetTab *DockAreaTabBar::tab(int index) const
|
||||||
|
{
|
||||||
|
if (index >= count() || index < 0)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return qobject_cast<DockWidgetTab *>(d->m_tabsLayout->itemAt(index)->widget());
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaTabBar::onTabWidgetMoved(const QPoint &globalPosition)
|
||||||
|
{
|
||||||
|
DockWidgetTab *movingTab = qobject_cast<DockWidgetTab *>(sender());
|
||||||
|
if (!movingTab)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int fromIndex = d->m_tabsLayout->indexOf(movingTab);
|
||||||
|
auto mousePos = mapFromGlobal(globalPosition);
|
||||||
|
mousePos.rx() = qMax(d->firstTab()->geometry().left(), mousePos.x());
|
||||||
|
mousePos.rx() = qMin(d->lastTab()->geometry().right(), mousePos.x());
|
||||||
|
int toIndex = -1;
|
||||||
|
// Find tab under mouse
|
||||||
|
for (int i = 0; i < count(); ++i) {
|
||||||
|
DockWidgetTab *dropTab = tab(i);
|
||||||
|
if (dropTab == movingTab || !dropTab->isVisibleTo(this)
|
||||||
|
|| !dropTab->geometry().contains(mousePos))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
toIndex = d->m_tabsLayout->indexOf(dropTab);
|
||||||
|
if (toIndex == fromIndex)
|
||||||
|
toIndex = -1;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toIndex > -1) {
|
||||||
|
d->m_tabsLayout->removeWidget(movingTab);
|
||||||
|
d->m_tabsLayout->insertWidget(toIndex, movingTab);
|
||||||
|
qCInfo(adsLog) << "tabMoved from" << fromIndex << "to" << toIndex;
|
||||||
|
emit tabMoved(fromIndex, toIndex);
|
||||||
|
setCurrentIndex(toIndex);
|
||||||
|
} else {
|
||||||
|
// Ensure that the moved tab is reset to its start position
|
||||||
|
d->m_tabsLayout->update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaTabBar::closeTab(int index)
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= count())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto dockWidgetTab = tab(index);
|
||||||
|
if (dockWidgetTab->isHidden())
|
||||||
|
return;
|
||||||
|
|
||||||
|
emit tabCloseRequested(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DockAreaTabBar::eventFilter(QObject *watched, QEvent *event)
|
||||||
|
{
|
||||||
|
bool result = Super::eventFilter(watched, event);
|
||||||
|
DockWidgetTab *dockWidgetTab = qobject_cast<DockWidgetTab *>(watched);
|
||||||
|
if (!dockWidgetTab)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
switch (event->type()) {
|
||||||
|
case QEvent::Hide:
|
||||||
|
emit tabClosed(d->m_tabsLayout->indexOf(dockWidgetTab));
|
||||||
|
updateGeometry();
|
||||||
|
break;
|
||||||
|
case QEvent::Show:
|
||||||
|
emit tabOpened(d->m_tabsLayout->indexOf(dockWidgetTab));
|
||||||
|
updateGeometry();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DockAreaTabBar::isTabOpen(int index) const
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= count())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return !tab(index)->isHidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize DockAreaTabBar::minimumSizeHint() const
|
||||||
|
{
|
||||||
|
QSize size = sizeHint();
|
||||||
|
size.setWidth(10);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize DockAreaTabBar::sizeHint() const
|
||||||
|
{
|
||||||
|
return d->m_tabsContainerWidget->sizeHint();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
217
src/libs/advanceddockingsystem/dockareatabbar.h
Normal file
217
src/libs/advanceddockingsystem/dockareatabbar.h
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 Uwe Kindler
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ads_globals.h"
|
||||||
|
|
||||||
|
#include <QScrollArea>
|
||||||
|
|
||||||
|
namespace ADS {
|
||||||
|
|
||||||
|
class DockAreaWidget;
|
||||||
|
class DockWidgetTab;
|
||||||
|
struct DockAreaTabBarPrivate;
|
||||||
|
class DockAreaTitleBar;
|
||||||
|
class FloatingDockContainer;
|
||||||
|
class AbstractFloatingWidget;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom tabbar implementation for tab area that is shown on top of a
|
||||||
|
* dock area widget.
|
||||||
|
* The tabbar displays the tab widgets of the contained dock widgets.
|
||||||
|
* We cannot use QTabBar here because it does a lot of fancy animations
|
||||||
|
* that will crash the application if a tab is removed while the animation
|
||||||
|
* has not finished. And we need to remove a tab, if the user drags a
|
||||||
|
* a dock widget out of a group of tabbed widgets
|
||||||
|
*/
|
||||||
|
class ADS_EXPORT DockAreaTabBar : public QScrollArea
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private:
|
||||||
|
DockAreaTabBarPrivate *d; ///< private data (pimpl)
|
||||||
|
friend struct DockAreaTabBarPrivate;
|
||||||
|
friend class DockAreaTitleBar;
|
||||||
|
|
||||||
|
void onTabClicked();
|
||||||
|
void onTabCloseRequested();
|
||||||
|
void onCloseOtherTabsRequested();
|
||||||
|
void onTabWidgetMoved(const QPoint &globalPos);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void wheelEvent(QWheelEvent *event) override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using Super = QScrollArea;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default Constructor
|
||||||
|
*/
|
||||||
|
DockAreaTabBar(DockAreaWidget *parent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual Destructor
|
||||||
|
*/
|
||||||
|
virtual ~DockAreaTabBar() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts the given dock widget tab at the given position.
|
||||||
|
* Inserting a new tab at an index less than or equal to the current index
|
||||||
|
* will increment the current index, but keep the current tab.
|
||||||
|
*/
|
||||||
|
void insertTab(int Index, DockWidgetTab *tab);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the given DockWidgetTab from the tabbar
|
||||||
|
*/
|
||||||
|
void removeTab(DockWidgetTab *tab);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of tabs in this tabbar
|
||||||
|
*/
|
||||||
|
int count() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current index or -1 if no tab is selected
|
||||||
|
*/
|
||||||
|
int currentIndex() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current tab or a nullptr if no tab is selected.
|
||||||
|
*/
|
||||||
|
DockWidgetTab *currentTab() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the tab with the given index
|
||||||
|
*/
|
||||||
|
DockWidgetTab *tab(int index) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters the tab widget events
|
||||||
|
*/
|
||||||
|
virtual bool eventFilter(QObject *watched, QEvent *event) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function returns true if the tab is open, that means if it is
|
||||||
|
* visible to the user. If the function returns false, the tab is
|
||||||
|
* closed
|
||||||
|
*/
|
||||||
|
bool isTabOpen(int index) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides the minimumSizeHint() function of QScrollArea
|
||||||
|
* The minimumSizeHint() is bigger than the sizeHint () for the scroll
|
||||||
|
* area because even if the scrollbars are invisible, the required speace
|
||||||
|
* is reserved in the minimumSizeHint(). This override simply returns
|
||||||
|
* sizeHint();
|
||||||
|
*/
|
||||||
|
virtual QSize minimumSizeHint() const override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The function provides a sizeHint that matches the height of the
|
||||||
|
* internal viewport.
|
||||||
|
*/
|
||||||
|
virtual QSize sizeHint() const override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This property sets the index of the tab bar's visible tab
|
||||||
|
*/
|
||||||
|
void setCurrentIndex(int index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function will close the tab given in Index param.
|
||||||
|
* Closing a tab means, the tab will be hidden, it will not be removed
|
||||||
|
*/
|
||||||
|
void closeTab(int index);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
/**
|
||||||
|
* This signal is emitted when the tab bar's current tab is about to be changed. The new
|
||||||
|
* current has the given index, or -1 if there isn't a new one.
|
||||||
|
*/
|
||||||
|
void currentChanging(int index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This signal is emitted when the tab bar's current tab changes. The new
|
||||||
|
* current has the given index, or -1 if there isn't a new one
|
||||||
|
*/
|
||||||
|
void currentChanged(int index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This signal is emitted when user clicks on a tab
|
||||||
|
*/
|
||||||
|
void tabBarClicked(int index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This signal is emitted when the close button on a tab is clicked.
|
||||||
|
* The index is the index that should be closed.
|
||||||
|
*/
|
||||||
|
void tabCloseRequested(int index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This signal is emitted if a tab has been closed
|
||||||
|
*/
|
||||||
|
void tabClosed(int index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This signal is emitted if a tab has been opened.
|
||||||
|
* A tab is opened if it has been made visible
|
||||||
|
*/
|
||||||
|
void tabOpened(int index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This signal is emitted when the tab has moved the tab at index position
|
||||||
|
* from to index position to.
|
||||||
|
*/
|
||||||
|
void tabMoved(int from, int to);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This signal is emitted, just before the tab with the given index is
|
||||||
|
* removed
|
||||||
|
*/
|
||||||
|
void removingTab(int index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This signal is emitted if a tab has been inserted
|
||||||
|
*/
|
||||||
|
void tabInserted(int index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This signal is emitted when a tab title elide state has been changed
|
||||||
|
*/
|
||||||
|
void elidedChanged(bool elided);
|
||||||
|
}; // class DockAreaTabBar
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
577
src/libs/advanceddockingsystem/dockareatitlebar.cpp
Normal file
577
src/libs/advanceddockingsystem/dockareatitlebar.cpp
Normal file
@@ -0,0 +1,577 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 Uwe Kindler
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "dockareatitlebar.h"
|
||||||
|
|
||||||
|
#include "ads_globals.h"
|
||||||
|
#include "dockareatabbar.h"
|
||||||
|
#include "dockareawidget.h"
|
||||||
|
#include "dockmanager.h"
|
||||||
|
#include "dockoverlay.h"
|
||||||
|
#include "dockwidget.h"
|
||||||
|
#include "dockwidgettab.h"
|
||||||
|
#include "floatingdockcontainer.h"
|
||||||
|
#include "floatingdragpreview.h"
|
||||||
|
#include "iconprovider.h"
|
||||||
|
#include "dockcomponentsfactory.h"
|
||||||
|
|
||||||
|
#include <QBoxLayout>
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QMouseEvent>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QScrollArea>
|
||||||
|
#include <QStyle>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtDebugMsg)
|
||||||
|
|
||||||
|
namespace ADS
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Private data class of DockAreaTitleBar class (pimpl)
|
||||||
|
*/
|
||||||
|
struct DockAreaTitleBarPrivate
|
||||||
|
{
|
||||||
|
DockAreaTitleBar *q;
|
||||||
|
QPointer<TitleBarButtonType> m_tabsMenuButton;
|
||||||
|
QPointer<TitleBarButtonType> m_undockButton;
|
||||||
|
QPointer<TitleBarButtonType> m_closeButton;
|
||||||
|
QBoxLayout *m_layout;
|
||||||
|
DockAreaWidget *m_dockArea;
|
||||||
|
DockAreaTabBar *m_tabBar;
|
||||||
|
bool m_menuOutdated = true;
|
||||||
|
QMenu *m_tabsMenu;
|
||||||
|
QList<TitleBarButtonType *> m_dockWidgetActionsButtons;
|
||||||
|
|
||||||
|
QPoint m_dragStartMousePos;
|
||||||
|
eDragState m_dragState = DraggingInactive;
|
||||||
|
AbstractFloatingWidget *m_floatingWidget = nullptr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private data constructor
|
||||||
|
*/
|
||||||
|
DockAreaTitleBarPrivate(DockAreaTitleBar *parent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the title bar close and menu buttons
|
||||||
|
*/
|
||||||
|
void createButtons();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the internal TabBar
|
||||||
|
*/
|
||||||
|
void createTabBar();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience function for DockManager access
|
||||||
|
*/
|
||||||
|
DockManager *dockManager() const { return m_dockArea->dockManager(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the given config flag is set
|
||||||
|
*/
|
||||||
|
static bool testConfigFlag(DockManager::eConfigFlag flag)
|
||||||
|
{
|
||||||
|
return DockManager::configFlags().testFlag(flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test function for current drag state
|
||||||
|
*/
|
||||||
|
bool isDraggingState(eDragState dragState) const { return this->m_dragState == dragState; }
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts floating
|
||||||
|
*/
|
||||||
|
void startFloating(const QPoint &offset);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes the dock area floating
|
||||||
|
*/
|
||||||
|
AbstractFloatingWidget *makeAreaFloating(const QPoint &offset, eDragState dragState);
|
||||||
|
}; // struct DockAreaTitleBarPrivate
|
||||||
|
|
||||||
|
|
||||||
|
DockAreaTitleBarPrivate::DockAreaTitleBarPrivate(DockAreaTitleBar *parent)
|
||||||
|
: q(parent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void DockAreaTitleBarPrivate::createButtons()
|
||||||
|
{
|
||||||
|
QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
|
||||||
|
// Tabs menu button
|
||||||
|
m_tabsMenuButton = new TitleBarButton(testConfigFlag(DockManager::DockAreaHasTabsMenuButton));
|
||||||
|
m_tabsMenuButton->setObjectName("tabsMenuButton");
|
||||||
|
m_tabsMenuButton->setAutoRaise(true);
|
||||||
|
m_tabsMenuButton->setPopupMode(QToolButton::InstantPopup);
|
||||||
|
internal::setButtonIcon(m_tabsMenuButton,
|
||||||
|
QStyle::SP_TitleBarUnshadeButton,
|
||||||
|
ADS::DockAreaMenuIcon);
|
||||||
|
QMenu *tabsMenu = new QMenu(m_tabsMenuButton);
|
||||||
|
#ifndef QT_NO_TOOLTIP
|
||||||
|
tabsMenu->setToolTipsVisible(true);
|
||||||
|
#endif
|
||||||
|
QObject::connect(tabsMenu, &QMenu::aboutToShow, q, &DockAreaTitleBar::onTabsMenuAboutToShow);
|
||||||
|
m_tabsMenuButton->setMenu(tabsMenu);
|
||||||
|
internal::setToolTip(m_tabsMenuButton, QObject::tr("List All Tabs"));
|
||||||
|
m_tabsMenuButton->setSizePolicy(sizePolicy);
|
||||||
|
m_layout->addWidget(m_tabsMenuButton, 0);
|
||||||
|
QObject::connect(m_tabsMenuButton->menu(),
|
||||||
|
&QMenu::triggered,
|
||||||
|
q,
|
||||||
|
&DockAreaTitleBar::onTabsMenuActionTriggered);
|
||||||
|
|
||||||
|
// Undock button
|
||||||
|
m_undockButton = new TitleBarButton(testConfigFlag(DockManager::DockAreaHasUndockButton));
|
||||||
|
m_undockButton->setObjectName("undockButton");
|
||||||
|
m_undockButton->setAutoRaise(true);
|
||||||
|
internal::setToolTip(m_undockButton, QObject::tr("Detach Group"));
|
||||||
|
internal::setButtonIcon(m_undockButton,
|
||||||
|
QStyle::SP_TitleBarNormalButton,
|
||||||
|
ADS::DockAreaUndockIcon);
|
||||||
|
m_undockButton->setSizePolicy(sizePolicy);
|
||||||
|
m_layout->addWidget(m_undockButton, 0);
|
||||||
|
QObject::connect(m_undockButton,
|
||||||
|
&QToolButton::clicked,
|
||||||
|
q,
|
||||||
|
&DockAreaTitleBar::onUndockButtonClicked);
|
||||||
|
|
||||||
|
// Close button
|
||||||
|
m_closeButton = new TitleBarButton(testConfigFlag(DockManager::DockAreaHasCloseButton));
|
||||||
|
m_closeButton->setObjectName("closeButton");
|
||||||
|
m_closeButton->setAutoRaise(true);
|
||||||
|
internal::setButtonIcon(m_closeButton,
|
||||||
|
QStyle::SP_TitleBarCloseButton,
|
||||||
|
ADS::DockAreaCloseIcon);
|
||||||
|
if (testConfigFlag(DockManager::DockAreaCloseButtonClosesTab)) {
|
||||||
|
internal::setToolTip(m_closeButton, QObject::tr("Close Active Tab"));
|
||||||
|
} else {
|
||||||
|
internal::setToolTip(m_closeButton, QObject::tr("Close Group"));
|
||||||
|
}
|
||||||
|
m_closeButton->setSizePolicy(sizePolicy);
|
||||||
|
m_closeButton->setIconSize(QSize(16, 16));
|
||||||
|
m_layout->addWidget(m_closeButton, 0);
|
||||||
|
QObject::connect(m_closeButton,
|
||||||
|
&QToolButton::clicked,
|
||||||
|
q,
|
||||||
|
&DockAreaTitleBar::onCloseButtonClicked);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaTitleBarPrivate::createTabBar()
|
||||||
|
{
|
||||||
|
m_tabBar = componentsFactory()->createDockAreaTabBar(m_dockArea);
|
||||||
|
m_tabBar->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
|
||||||
|
m_layout->addWidget(m_tabBar);
|
||||||
|
QObject::connect(m_tabBar,
|
||||||
|
&DockAreaTabBar::tabClosed,
|
||||||
|
q,
|
||||||
|
&DockAreaTitleBar::markTabsMenuOutdated);
|
||||||
|
QObject::connect(m_tabBar,
|
||||||
|
&DockAreaTabBar::tabOpened,
|
||||||
|
q,
|
||||||
|
&DockAreaTitleBar::markTabsMenuOutdated);
|
||||||
|
QObject::connect(m_tabBar,
|
||||||
|
&DockAreaTabBar::tabInserted,
|
||||||
|
q,
|
||||||
|
&DockAreaTitleBar::markTabsMenuOutdated);
|
||||||
|
QObject::connect(m_tabBar,
|
||||||
|
&DockAreaTabBar::removingTab,
|
||||||
|
q,
|
||||||
|
&DockAreaTitleBar::markTabsMenuOutdated);
|
||||||
|
QObject::connect(m_tabBar,
|
||||||
|
&DockAreaTabBar::tabMoved,
|
||||||
|
q,
|
||||||
|
&DockAreaTitleBar::markTabsMenuOutdated);
|
||||||
|
QObject::connect(m_tabBar,
|
||||||
|
&DockAreaTabBar::currentChanged,
|
||||||
|
q,
|
||||||
|
&DockAreaTitleBar::onCurrentTabChanged);
|
||||||
|
QObject::connect(m_tabBar,
|
||||||
|
&DockAreaTabBar::tabBarClicked,
|
||||||
|
q,
|
||||||
|
&DockAreaTitleBar::tabBarClicked);
|
||||||
|
QObject::connect(m_tabBar,
|
||||||
|
&DockAreaTabBar::elidedChanged,
|
||||||
|
q,
|
||||||
|
&DockAreaTitleBar::markTabsMenuOutdated);
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractFloatingWidget *DockAreaTitleBarPrivate::makeAreaFloating(const QPoint &offset,
|
||||||
|
eDragState dragState)
|
||||||
|
{
|
||||||
|
QSize size = m_dockArea->size();
|
||||||
|
m_dragState = dragState;
|
||||||
|
bool opaqueUndocking = DockManager::configFlags().testFlag(DockManager::OpaqueUndocking)
|
||||||
|
|| (DraggingFloatingWidget != dragState);
|
||||||
|
FloatingDockContainer *floatingDockContainer = nullptr;
|
||||||
|
AbstractFloatingWidget *floatingWidget;
|
||||||
|
if (opaqueUndocking) {
|
||||||
|
floatingWidget = floatingDockContainer = new FloatingDockContainer(m_dockArea);
|
||||||
|
} else {
|
||||||
|
auto w = new FloatingDragPreview(m_dockArea);
|
||||||
|
QObject::connect(w, &FloatingDragPreview::draggingCanceled, [=]() {
|
||||||
|
m_dragState = DraggingInactive;
|
||||||
|
});
|
||||||
|
floatingWidget = w;
|
||||||
|
}
|
||||||
|
|
||||||
|
floatingWidget->startFloating(offset, size, dragState, nullptr);
|
||||||
|
if (floatingDockContainer) {
|
||||||
|
auto topLevelDockWidget = floatingDockContainer->topLevelDockWidget();
|
||||||
|
if (topLevelDockWidget) {
|
||||||
|
topLevelDockWidget->emitTopLevelChanged(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return floatingWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaTitleBarPrivate::startFloating(const QPoint &offset)
|
||||||
|
{
|
||||||
|
m_floatingWidget = makeAreaFloating(offset, DraggingFloatingWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
TitleBarButton::TitleBarButton(bool visible, QWidget *parent)
|
||||||
|
: TitleBarButtonType(parent),
|
||||||
|
m_visible(visible),
|
||||||
|
m_hideWhenDisabled(DockAreaTitleBarPrivate::testConfigFlag(DockManager::DockAreaHideDisabledButtons))
|
||||||
|
{}
|
||||||
|
|
||||||
|
void TitleBarButton::setVisible(bool visible)
|
||||||
|
{
|
||||||
|
// 'visible' can stay 'true' if and only if this button is configured to generaly visible:
|
||||||
|
visible = visible && m_visible;
|
||||||
|
|
||||||
|
// 'visible' can stay 'true' unless: this button is configured to be invisible when it
|
||||||
|
// is disabled and it is currently disabled:
|
||||||
|
if (visible && m_hideWhenDisabled) {
|
||||||
|
visible = isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
Super::setVisible(visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TitleBarButton::event(QEvent *event)
|
||||||
|
{
|
||||||
|
if (QEvent::EnabledChange == event->type() && m_hideWhenDisabled) {
|
||||||
|
// force setVisible() call
|
||||||
|
// Calling setVisible() directly here doesn't work well when button is expected to be shown first time
|
||||||
|
QMetaObject::invokeMethod(this, "setVisible", Qt::QueuedConnection, Q_ARG(bool, isEnabled()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Super::event(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
SpacerWidget::SpacerWidget(QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
{
|
||||||
|
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||||
|
setStyleSheet("border: none; background: none;");
|
||||||
|
}
|
||||||
|
|
||||||
|
DockAreaTitleBar::DockAreaTitleBar(DockAreaWidget *parent)
|
||||||
|
: QFrame(parent)
|
||||||
|
, d(new DockAreaTitleBarPrivate(this))
|
||||||
|
{
|
||||||
|
d->m_dockArea = parent;
|
||||||
|
|
||||||
|
setObjectName("dockAreaTitleBar");
|
||||||
|
d->m_layout = new QBoxLayout(QBoxLayout::LeftToRight);
|
||||||
|
d->m_layout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
d->m_layout->setSpacing(0);
|
||||||
|
setLayout(d->m_layout);
|
||||||
|
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
||||||
|
|
||||||
|
d->createTabBar();
|
||||||
|
d->m_layout->addWidget(new SpacerWidget(this));
|
||||||
|
d->createButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
DockAreaTitleBar::~DockAreaTitleBar() {
|
||||||
|
if (!d->m_closeButton.isNull())
|
||||||
|
delete d->m_closeButton;
|
||||||
|
|
||||||
|
if (!d->m_tabsMenuButton.isNull())
|
||||||
|
delete d->m_tabsMenuButton;
|
||||||
|
|
||||||
|
if (!d->m_undockButton.isNull())
|
||||||
|
delete d->m_undockButton;
|
||||||
|
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
DockAreaTabBar *DockAreaTitleBar::tabBar() const { return d->m_tabBar; }
|
||||||
|
|
||||||
|
void DockAreaTitleBar::markTabsMenuOutdated() {
|
||||||
|
if (DockAreaTitleBarPrivate::testConfigFlag(DockManager::DockAreaDynamicTabsMenuButtonVisibility)) {
|
||||||
|
bool hasElidedTabTitle = false;
|
||||||
|
for (int i = 0; i < d->m_tabBar->count(); ++i) {
|
||||||
|
if (!d->m_tabBar->isTabOpen(i))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
DockWidgetTab* tab = d->m_tabBar->tab(i);
|
||||||
|
if (tab->isTitleElided()) {
|
||||||
|
hasElidedTabTitle = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool visible = (hasElidedTabTitle && (d->m_tabBar->count() > 1));
|
||||||
|
QMetaObject::invokeMethod(d->m_tabsMenuButton, "setVisible", Qt::QueuedConnection, Q_ARG(bool, visible));
|
||||||
|
}
|
||||||
|
d->m_menuOutdated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaTitleBar::onTabsMenuAboutToShow()
|
||||||
|
{
|
||||||
|
if (!d->m_menuOutdated) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMenu *menu = d->m_tabsMenuButton->menu();
|
||||||
|
menu->clear();
|
||||||
|
for (int i = 0; i < d->m_tabBar->count(); ++i) {
|
||||||
|
if (!d->m_tabBar->isTabOpen(i))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto tab = d->m_tabBar->tab(i);
|
||||||
|
QAction *action = menu->addAction(tab->icon(), tab->text());
|
||||||
|
internal::setToolTip(action, tab->toolTip());
|
||||||
|
action->setData(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
d->m_menuOutdated = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaTitleBar::onCloseButtonClicked()
|
||||||
|
{
|
||||||
|
qCInfo(adsLog) << Q_FUNC_INFO;
|
||||||
|
if (d->testConfigFlag(DockManager::DockAreaCloseButtonClosesTab)) {
|
||||||
|
d->m_tabBar->closeTab(d->m_tabBar->currentIndex());
|
||||||
|
} else {
|
||||||
|
d->m_dockArea->closeArea();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaTitleBar::onUndockButtonClicked()
|
||||||
|
{
|
||||||
|
if (d->m_dockArea->features().testFlag(DockWidget::DockWidgetFloatable)) {
|
||||||
|
d->makeAreaFloating(mapFromGlobal(QCursor::pos()), DraggingInactive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaTitleBar::onTabsMenuActionTriggered(QAction *action)
|
||||||
|
{
|
||||||
|
int index = action->data().toInt();
|
||||||
|
d->m_tabBar->setCurrentIndex(index);
|
||||||
|
emit tabBarClicked(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaTitleBar::updateDockWidgetActionsButtons()
|
||||||
|
{
|
||||||
|
DockWidget* dockWidget = d->m_tabBar->currentTab()->dockWidget();
|
||||||
|
if (!d->m_dockWidgetActionsButtons.isEmpty()) {
|
||||||
|
for (auto button : d->m_dockWidgetActionsButtons) {
|
||||||
|
d->m_layout->removeWidget(button);
|
||||||
|
delete button;
|
||||||
|
}
|
||||||
|
d->m_dockWidgetActionsButtons.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto actions = dockWidget->titleBarActions();
|
||||||
|
if (actions.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
int insertIndex = indexOf(d->m_tabsMenuButton);
|
||||||
|
for (auto action : actions) {
|
||||||
|
auto button = new TitleBarButton(true, this);
|
||||||
|
button->setDefaultAction(action);
|
||||||
|
button->setAutoRaise(true);
|
||||||
|
button->setPopupMode(QToolButton::InstantPopup);
|
||||||
|
button->setObjectName(action->objectName());
|
||||||
|
d->m_layout->insertWidget(insertIndex++, button, 0);
|
||||||
|
d->m_dockWidgetActionsButtons.append(button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaTitleBar::onCurrentTabChanged(int index)
|
||||||
|
{
|
||||||
|
if (index < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (d->testConfigFlag(DockManager::DockAreaCloseButtonClosesTab)) {
|
||||||
|
DockWidget *dockWidget = d->m_tabBar->tab(index)->dockWidget();
|
||||||
|
d->m_closeButton->setEnabled(
|
||||||
|
dockWidget->features().testFlag(DockWidget::DockWidgetClosable));
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDockWidgetActionsButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
QAbstractButton *DockAreaTitleBar::button(eTitleBarButton which) const
|
||||||
|
{
|
||||||
|
switch (which) {
|
||||||
|
case TitleBarButtonTabsMenu:
|
||||||
|
return d->m_tabsMenuButton;
|
||||||
|
case TitleBarButtonUndock:
|
||||||
|
return d->m_undockButton;
|
||||||
|
case TitleBarButtonClose:
|
||||||
|
return d->m_closeButton;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaTitleBar::setVisible(bool visible)
|
||||||
|
{
|
||||||
|
Super::setVisible(visible);
|
||||||
|
markTabsMenuOutdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DockAreaTitleBar::mousePressEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
if (event->button() == Qt::LeftButton) {
|
||||||
|
event->accept();
|
||||||
|
d->m_dragStartMousePos = event->pos();
|
||||||
|
d->m_dragState = DraggingMousePressed;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Super::mousePressEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaTitleBar::mouseReleaseEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
if (event->button() == Qt::LeftButton) {
|
||||||
|
qCInfo(adsLog) << Q_FUNC_INFO;
|
||||||
|
event->accept();
|
||||||
|
auto CurrentDragState = d->m_dragState;
|
||||||
|
d->m_dragStartMousePos = QPoint();
|
||||||
|
d->m_dragState = DraggingInactive;
|
||||||
|
if (DraggingFloatingWidget == CurrentDragState)
|
||||||
|
d->m_floatingWidget->finishDragging();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Super::mouseReleaseEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaTitleBar::mouseMoveEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
Super::mouseMoveEvent(event);
|
||||||
|
if (!(event->buttons() & Qt::LeftButton) || d->isDraggingState(DraggingInactive)) {
|
||||||
|
d->m_dragState = DraggingInactive;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// move floating window
|
||||||
|
if (d->isDraggingState(DraggingFloatingWidget)) {
|
||||||
|
d->m_floatingWidget->moveFloating();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is the last dock area in a dock container it does not make
|
||||||
|
// sense to move it to a new floating widget and leave this one empty
|
||||||
|
if (d->m_dockArea->dockContainer()->isFloating()
|
||||||
|
&& d->m_dockArea->dockContainer()->visibleDockAreaCount() == 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If one single dock widget in this area is not floatable then the whole
|
||||||
|
// area is not floatable
|
||||||
|
// If we do non opaque undocking, then we can create the floating drag
|
||||||
|
// preview if the dock widget is movable
|
||||||
|
auto features = d->m_dockArea->features();
|
||||||
|
if (!features.testFlag(DockWidget::DockWidgetFloatable)
|
||||||
|
&& !(features.testFlag(DockWidget::DockWidgetMovable)
|
||||||
|
&& !DockManager::testConfigFlag(DockManager::OpaqueUndocking))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dragDistance = (d->m_dragStartMousePos - event->pos()).manhattanLength();
|
||||||
|
if (dragDistance >= DockManager::startDragDistance()) {
|
||||||
|
qCInfo(adsLog) << "TabsScrollArea::startFloating";
|
||||||
|
d->startFloating(d->m_dragStartMousePos);
|
||||||
|
auto overlay = d->m_dockArea->dockManager()->containerOverlay();
|
||||||
|
overlay->setAllowedAreas(OuterDockAreas);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaTitleBar::mouseDoubleClickEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
// If this is the last dock area in a dock container it does not make
|
||||||
|
// sense to move it to a new floating widget and leave this one empty
|
||||||
|
if (d->m_dockArea->dockContainer()->isFloating()
|
||||||
|
&& d->m_dockArea->dockContainer()->dockAreaCount() == 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!d->m_dockArea->features().testFlag(DockWidget::DockWidgetFloatable))
|
||||||
|
return;
|
||||||
|
|
||||||
|
d->makeAreaFloating(event->pos(), DraggingInactive);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaTitleBar::contextMenuEvent(QContextMenuEvent *event)
|
||||||
|
{
|
||||||
|
event->accept();
|
||||||
|
if (d->isDraggingState(DraggingFloatingWidget))
|
||||||
|
return;
|
||||||
|
|
||||||
|
QMenu menu(this);
|
||||||
|
auto action = menu.addAction(tr("Detach Area"),
|
||||||
|
this,
|
||||||
|
&DockAreaTitleBar::onUndockButtonClicked);
|
||||||
|
action->setEnabled(d->m_dockArea->features().testFlag(DockWidget::DockWidgetFloatable));
|
||||||
|
menu.addSeparator();
|
||||||
|
action = menu.addAction(tr("Close Area"), this, &DockAreaTitleBar::onCloseButtonClicked);
|
||||||
|
action->setEnabled(d->m_dockArea->features().testFlag(DockWidget::DockWidgetClosable));
|
||||||
|
menu.addAction(tr("Close Other Areas"), d->m_dockArea, &DockAreaWidget::closeOtherAreas);
|
||||||
|
menu.exec(event->globalPos());
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaTitleBar::insertWidget(int index, QWidget *widget)
|
||||||
|
{
|
||||||
|
d->m_layout->insertWidget(index, widget);
|
||||||
|
}
|
||||||
|
|
||||||
|
int DockAreaTitleBar::indexOf(QWidget *widget) const
|
||||||
|
{
|
||||||
|
return d->m_layout->indexOf(widget);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
207
src/libs/advanceddockingsystem/dockareatitlebar.h
Normal file
207
src/libs/advanceddockingsystem/dockareatitlebar.h
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 Uwe Kindler
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ads_globals.h"
|
||||||
|
|
||||||
|
#include <QFrame>
|
||||||
|
#include <QToolButton>
|
||||||
|
|
||||||
|
class QAbstractButton;
|
||||||
|
|
||||||
|
namespace ADS {
|
||||||
|
|
||||||
|
class DockAreaTabBar;
|
||||||
|
class DockAreaWidget;
|
||||||
|
struct DockAreaTitleBarPrivate;
|
||||||
|
|
||||||
|
using TitleBarButtonType = QToolButton;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Title bar button of a dock area that customizes TitleBarButtonType appearance/behavior
|
||||||
|
* according to various config flags such as:
|
||||||
|
* DockManager::DockAreaHas_xxx_Button - if set to 'false' keeps the button always invisible
|
||||||
|
* DockManager::DockAreaHideDisabledButtons - if set to 'true' hides button when it is disabled
|
||||||
|
*/
|
||||||
|
class TitleBarButton : public TitleBarButtonType
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
bool m_visible = true;
|
||||||
|
bool m_hideWhenDisabled = false;
|
||||||
|
public:
|
||||||
|
using Super = TitleBarButtonType;
|
||||||
|
TitleBarButton(bool visible = true, QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjust this visibility change request with our internal settings:
|
||||||
|
*/
|
||||||
|
virtual void setVisible(bool visible) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* Handle EnabledChanged signal to set button invisible if the configured
|
||||||
|
*/
|
||||||
|
bool event(QEvent *event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This spacer widget is here because of the following problem.
|
||||||
|
* The dock area title bar handles mouse dragging and moving the floating widget.
|
||||||
|
* The problem is, that if the title bar becomes invisible, i.e. if the dock
|
||||||
|
* area contains only one single dock widget and the dock area is moved
|
||||||
|
* into a floating widget, then mouse events are not handled anymore and dragging
|
||||||
|
* of the floating widget stops.
|
||||||
|
*/
|
||||||
|
class SpacerWidget : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
SpacerWidget(QWidget *parent = nullptr);
|
||||||
|
virtual QSize sizeHint() const override {return QSize(0, 0);}
|
||||||
|
virtual QSize minimumSizeHint() const override {return QSize(0, 0);}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Title bar of a dock area.
|
||||||
|
* The title bar contains a tabbar with all tabs for a dock widget group and
|
||||||
|
* with a tabs menu button, a undock button and a close button.
|
||||||
|
*/
|
||||||
|
class ADS_EXPORT DockAreaTitleBar : public QFrame
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private:
|
||||||
|
DockAreaTitleBarPrivate *d; ///< private data (pimpl)
|
||||||
|
friend struct DockAreaTitleBarPrivate;
|
||||||
|
|
||||||
|
void onTabsMenuAboutToShow();
|
||||||
|
void onCloseButtonClicked();
|
||||||
|
void onUndockButtonClicked();
|
||||||
|
void onTabsMenuActionTriggered(QAction *action);
|
||||||
|
void onCurrentTabChanged(int index);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* Stores mouse position to detect dragging
|
||||||
|
*/
|
||||||
|
virtual void mousePressEvent(QMouseEvent *event) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores mouse position to detect dragging
|
||||||
|
*/
|
||||||
|
virtual void mouseReleaseEvent(QMouseEvent *event) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts floating the complete docking area including all dock widgets,
|
||||||
|
* if it is not the last dock area in a floating widget
|
||||||
|
*/
|
||||||
|
virtual void mouseMoveEvent(QMouseEvent *event) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Double clicking the title bar also starts floating of the complete area
|
||||||
|
*/
|
||||||
|
virtual void mouseDoubleClickEvent(QMouseEvent *event) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show context menu
|
||||||
|
*/
|
||||||
|
virtual void contextMenuEvent(QContextMenuEvent *event) override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Call this slot to tell the title bar that it should update the tabs menu
|
||||||
|
* the next time it is shown.
|
||||||
|
*/
|
||||||
|
void markTabsMenuOutdated();
|
||||||
|
|
||||||
|
using Super = QFrame;
|
||||||
|
/**
|
||||||
|
* Default Constructor
|
||||||
|
*/
|
||||||
|
DockAreaTitleBar(DockAreaWidget *parent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual Destructor
|
||||||
|
*/
|
||||||
|
virtual ~DockAreaTitleBar() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the pointer to the tabBar()
|
||||||
|
*/
|
||||||
|
DockAreaTabBar *tabBar() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the button corresponding to the given title bar button identifier
|
||||||
|
*/
|
||||||
|
QAbstractButton *button(eTitleBarButton which) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the visibility of the dock widget actions in the title bar
|
||||||
|
*/
|
||||||
|
void updateDockWidgetActionsButtons();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks the tabs menu outdated before it calls its base class
|
||||||
|
* implementation
|
||||||
|
*/
|
||||||
|
virtual void setVisible(bool visible) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts a custom widget at position index into this title bar.
|
||||||
|
* If index is negative, the widget is added at the end.
|
||||||
|
* You can use this function to insert custom widgets into the title bar.
|
||||||
|
*/
|
||||||
|
void insertWidget(int index, QWidget *widget);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches for widget widget in this title bar.
|
||||||
|
* You can use this function, to get the position of the default
|
||||||
|
* widget in the tile bar.
|
||||||
|
* \code
|
||||||
|
* int tabBarIndex = TitleBar->indexOf(TitleBar->tabBar());
|
||||||
|
* int closeButtonIndex = TitleBar->indexOf(TitleBar->button(TitleBarButtonClose));
|
||||||
|
* \endcode
|
||||||
|
*/
|
||||||
|
int indexOf(QWidget *widget) const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
/**
|
||||||
|
* This signal is emitted if a tab in the tab bar is clicked by the user
|
||||||
|
* or if the user clicks on a tab item in the title bar tab menu.
|
||||||
|
*/
|
||||||
|
void tabBarClicked(int index);
|
||||||
|
}; // class DockAreaTitleBar
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
686
src/libs/advanceddockingsystem/dockareawidget.cpp
Normal file
686
src/libs/advanceddockingsystem/dockareawidget.cpp
Normal file
@@ -0,0 +1,686 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 Uwe Kindler
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "dockareawidget.h"
|
||||||
|
|
||||||
|
#include "dockareatabbar.h"
|
||||||
|
#include "dockareatitlebar.h"
|
||||||
|
#include "dockcomponentsfactory.h"
|
||||||
|
#include "dockcontainerwidget.h"
|
||||||
|
#include "dockmanager.h"
|
||||||
|
#include "dockoverlay.h"
|
||||||
|
#include "docksplitter.h"
|
||||||
|
#include "dockwidget.h"
|
||||||
|
#include "dockwidgettab.h"
|
||||||
|
#include "floatingdockcontainer.h"
|
||||||
|
|
||||||
|
#include <QList>
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QScrollArea>
|
||||||
|
#include <QScrollBar>
|
||||||
|
#include <QSplitter>
|
||||||
|
#include <QStackedLayout>
|
||||||
|
#include <QStyle>
|
||||||
|
#include <QVector>
|
||||||
|
#include <QWheelEvent>
|
||||||
|
#include <QXmlStreamWriter>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtDebugMsg)
|
||||||
|
|
||||||
|
namespace ADS
|
||||||
|
{
|
||||||
|
static const char *const INDEX_PROPERTY = "index";
|
||||||
|
static const char *const ACTION_PROPERTY = "action";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dock area layout mimics stack layout but only inserts the current
|
||||||
|
* widget into the internal QLayout object.
|
||||||
|
* \warning Only the current widget has a parent. All other widgets
|
||||||
|
* do not have a parent. That means, a widget that is in this layout may
|
||||||
|
* return nullptr for its parent() function if it is not the current widget.
|
||||||
|
*/
|
||||||
|
class DockAreaLayout
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
QBoxLayout *m_parentLayout;
|
||||||
|
QList<QWidget *> m_widgets;
|
||||||
|
int m_currentIndex = -1;
|
||||||
|
QWidget *m_currentWidget = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Creates an instance with the given parent layout
|
||||||
|
*/
|
||||||
|
DockAreaLayout(QBoxLayout *parentLayout)
|
||||||
|
: m_parentLayout(parentLayout)
|
||||||
|
{}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of widgets in this layout
|
||||||
|
*/
|
||||||
|
int count() const { return m_widgets.count(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts the widget at the given index position into the internal widget
|
||||||
|
* list
|
||||||
|
*/
|
||||||
|
void insertWidget(int index, QWidget *widget)
|
||||||
|
{
|
||||||
|
widget->setParent(nullptr);
|
||||||
|
if (index < 0) {
|
||||||
|
index = m_widgets.count();
|
||||||
|
}
|
||||||
|
m_widgets.insert(index, widget);
|
||||||
|
if (m_currentIndex < 0) {
|
||||||
|
setCurrentIndex(index);
|
||||||
|
} else {
|
||||||
|
if (index <= m_currentIndex) {
|
||||||
|
++m_currentIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the given widget from the layout
|
||||||
|
*/
|
||||||
|
void removeWidget(QWidget *widget)
|
||||||
|
{
|
||||||
|
if (currentWidget() == widget) {
|
||||||
|
auto layoutItem = m_parentLayout->takeAt(1);
|
||||||
|
if (layoutItem) {
|
||||||
|
layoutItem->widget()->setParent(nullptr);
|
||||||
|
}
|
||||||
|
m_currentWidget = nullptr;
|
||||||
|
m_currentIndex = -1;
|
||||||
|
}
|
||||||
|
m_widgets.removeOne(widget);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current selected widget
|
||||||
|
*/
|
||||||
|
QWidget *currentWidget() const { return m_currentWidget; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activates the widget with the give index.
|
||||||
|
*/
|
||||||
|
void setCurrentIndex(int index)
|
||||||
|
{
|
||||||
|
QWidget *prev = currentWidget();
|
||||||
|
QWidget *next = widget(index);
|
||||||
|
if (!next || (next == prev && !m_currentWidget)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool reenableUpdates = false;
|
||||||
|
QWidget *parent = m_parentLayout->parentWidget();
|
||||||
|
|
||||||
|
if (parent && parent->updatesEnabled()) {
|
||||||
|
reenableUpdates = true;
|
||||||
|
parent->setUpdatesEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
auto layoutItem = m_parentLayout->takeAt(1);
|
||||||
|
if (layoutItem) {
|
||||||
|
layoutItem->widget()->setParent(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_parentLayout->addWidget(next);
|
||||||
|
if (prev) {
|
||||||
|
prev->hide();
|
||||||
|
}
|
||||||
|
m_currentIndex = index;
|
||||||
|
m_currentWidget = next;
|
||||||
|
|
||||||
|
if (reenableUpdates) {
|
||||||
|
parent->setUpdatesEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the index of the current active widget
|
||||||
|
*/
|
||||||
|
int currentIndex() const { return m_currentIndex; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if there are no widgets in the layout
|
||||||
|
*/
|
||||||
|
bool isEmpty() const { return m_widgets.empty(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the index of the given widget
|
||||||
|
*/
|
||||||
|
int indexOf(QWidget *widget) const { return m_widgets.indexOf(widget); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the widget for the given index
|
||||||
|
*/
|
||||||
|
QWidget *widget(int index) const
|
||||||
|
{
|
||||||
|
return (index < m_widgets.size()) ? m_widgets.at(index) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the geometry of the current active widget
|
||||||
|
*/
|
||||||
|
QRect geometry() const { return m_widgets.empty() ? QRect() : currentWidget()->geometry(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private data class of DockAreaWidget class (pimpl)
|
||||||
|
*/
|
||||||
|
struct DockAreaWidgetPrivate
|
||||||
|
{
|
||||||
|
DockAreaWidget *q = nullptr;
|
||||||
|
QBoxLayout *m_layout = nullptr;
|
||||||
|
DockAreaLayout *m_contentsLayout = nullptr;
|
||||||
|
DockAreaTitleBar *m_titleBar = nullptr;
|
||||||
|
DockManager *m_dockManager = nullptr;
|
||||||
|
bool m_updateTitleBarButtons = false;
|
||||||
|
DockWidgetAreas m_allowedAreas = AllDockAreas;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private data constructor
|
||||||
|
*/
|
||||||
|
DockAreaWidgetPrivate(DockAreaWidget *parent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the layout for top area with tabs and close button
|
||||||
|
*/
|
||||||
|
void createTitleBar();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the dock widget with the given index
|
||||||
|
*/
|
||||||
|
DockWidget *dockWidgetAt(int index)
|
||||||
|
{
|
||||||
|
return qobject_cast<DockWidget *>(m_contentsLayout->widget(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience function to ease title widget access by index
|
||||||
|
*/
|
||||||
|
DockWidgetTab *tabWidgetAt(int index) { return dockWidgetAt(index)->tabWidget(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the tab action of the given dock widget
|
||||||
|
*/
|
||||||
|
QAction *dockWidgetTabAction(DockWidget *dockWidget) const
|
||||||
|
{
|
||||||
|
return qvariant_cast<QAction *>(dockWidget->property(ACTION_PROPERTY));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the index of the given dock widget
|
||||||
|
*/
|
||||||
|
int dockWidgetIndex(DockWidget *dockWidget) const
|
||||||
|
{
|
||||||
|
return dockWidget->property(INDEX_PROPERTY).toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience function for tabbar access
|
||||||
|
*/
|
||||||
|
DockAreaTabBar *tabBar() const { return m_titleBar->tabBar(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Udpates the enable state of the close and detach button
|
||||||
|
*/
|
||||||
|
void updateTitleBarButtonStates();
|
||||||
|
};
|
||||||
|
// struct DockAreaWidgetPrivate
|
||||||
|
|
||||||
|
DockAreaWidgetPrivate::DockAreaWidgetPrivate(DockAreaWidget *parent)
|
||||||
|
: q(parent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void DockAreaWidgetPrivate::createTitleBar()
|
||||||
|
{
|
||||||
|
m_titleBar = componentsFactory()->createDockAreaTitleBar(q);
|
||||||
|
m_layout->addWidget(m_titleBar);
|
||||||
|
QObject::connect(tabBar(),
|
||||||
|
&DockAreaTabBar::tabCloseRequested,
|
||||||
|
q,
|
||||||
|
&DockAreaWidget::onTabCloseRequested);
|
||||||
|
QObject::connect(m_titleBar,
|
||||||
|
&DockAreaTitleBar::tabBarClicked,
|
||||||
|
q,
|
||||||
|
&DockAreaWidget::setCurrentIndex);
|
||||||
|
QObject::connect(tabBar(), &DockAreaTabBar::tabMoved, q, &DockAreaWidget::reorderDockWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaWidgetPrivate::updateTitleBarButtonStates()
|
||||||
|
{
|
||||||
|
if (q->isHidden()) {
|
||||||
|
m_updateTitleBarButtons = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_titleBar->button(TitleBarButtonClose)
|
||||||
|
->setEnabled(q->features().testFlag(DockWidget::DockWidgetClosable));
|
||||||
|
m_titleBar->button(TitleBarButtonUndock)
|
||||||
|
->setEnabled(q->features().testFlag(DockWidget::DockWidgetFloatable));
|
||||||
|
m_titleBar->updateDockWidgetActionsButtons();
|
||||||
|
m_updateTitleBarButtons = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DockAreaWidget::DockAreaWidget(DockManager *dockManager, DockContainerWidget *parent)
|
||||||
|
: QFrame(parent)
|
||||||
|
, d(new DockAreaWidgetPrivate(this))
|
||||||
|
{
|
||||||
|
d->m_dockManager = dockManager;
|
||||||
|
d->m_layout = new QBoxLayout(QBoxLayout::TopToBottom);
|
||||||
|
d->m_layout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
d->m_layout->setSpacing(0);
|
||||||
|
setLayout(d->m_layout);
|
||||||
|
|
||||||
|
d->createTitleBar();
|
||||||
|
d->m_contentsLayout = new DockAreaLayout(d->m_layout);
|
||||||
|
if (d->m_dockManager) {
|
||||||
|
emit d->m_dockManager->dockAreaCreated(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DockAreaWidget::~DockAreaWidget()
|
||||||
|
{
|
||||||
|
qCInfo(adsLog) << Q_FUNC_INFO;
|
||||||
|
delete d->m_contentsLayout;
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
DockManager *DockAreaWidget::dockManager() const { return d->m_dockManager; }
|
||||||
|
|
||||||
|
DockContainerWidget *DockAreaWidget::dockContainer() const
|
||||||
|
{
|
||||||
|
return internal::findParent<DockContainerWidget *>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaWidget::addDockWidget(DockWidget *dockWidget)
|
||||||
|
{
|
||||||
|
insertDockWidget(d->m_contentsLayout->count(), dockWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaWidget::insertDockWidget(int index, DockWidget *dockWidget, bool activate)
|
||||||
|
{
|
||||||
|
d->m_contentsLayout->insertWidget(index, dockWidget);
|
||||||
|
dockWidget->tabWidget()->setDockAreaWidget(this);
|
||||||
|
auto tabWidget = dockWidget->tabWidget();
|
||||||
|
// Inserting the tab will change the current index which in turn will
|
||||||
|
// make the tab widget visible in the slot
|
||||||
|
d->tabBar()->blockSignals(true);
|
||||||
|
d->tabBar()->insertTab(index, tabWidget);
|
||||||
|
d->tabBar()->blockSignals(false);
|
||||||
|
tabWidget->setVisible(!dockWidget->isClosed());
|
||||||
|
dockWidget->setProperty(INDEX_PROPERTY, index);
|
||||||
|
if (activate) {
|
||||||
|
setCurrentIndex(index);
|
||||||
|
}
|
||||||
|
dockWidget->setDockArea(this);
|
||||||
|
d->updateTitleBarButtonStates();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaWidget::removeDockWidget(DockWidget *dockWidget)
|
||||||
|
{
|
||||||
|
qCInfo(adsLog) << Q_FUNC_INFO;
|
||||||
|
auto nextOpen = nextOpenDockWidget(dockWidget);
|
||||||
|
|
||||||
|
d->m_contentsLayout->removeWidget(dockWidget);
|
||||||
|
auto tabWidget = dockWidget->tabWidget();
|
||||||
|
tabWidget->hide();
|
||||||
|
d->tabBar()->removeTab(tabWidget);
|
||||||
|
DockContainerWidget *dockContainerWidget = dockContainer();
|
||||||
|
if (nextOpen) {
|
||||||
|
setCurrentDockWidget(nextOpen);
|
||||||
|
} else if (d->m_contentsLayout->isEmpty() && dockContainerWidget->dockAreaCount() > 1) {
|
||||||
|
qCInfo(adsLog) << "Dock Area empty";
|
||||||
|
dockContainerWidget->removeDockArea(this);
|
||||||
|
this->deleteLater();
|
||||||
|
} else {
|
||||||
|
// if contents layout is not empty but there are no more open dock
|
||||||
|
// widgets, then we need to hide the dock area because it does not
|
||||||
|
// contain any visible content
|
||||||
|
hideAreaWithNoVisibleContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
d->updateTitleBarButtonStates();
|
||||||
|
updateTitleBarVisibility();
|
||||||
|
auto topLevelDockWidget = dockContainerWidget->topLevelDockWidget();
|
||||||
|
if (topLevelDockWidget) {
|
||||||
|
topLevelDockWidget->emitTopLevelChanged(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if (ADS_DEBUG_LEVEL > 0)
|
||||||
|
dockContainerWidget->dumpLayout();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaWidget::hideAreaWithNoVisibleContent()
|
||||||
|
{
|
||||||
|
this->toggleView(false);
|
||||||
|
|
||||||
|
// Hide empty parent splitters
|
||||||
|
auto splitter = internal::findParent<DockSplitter *>(this);
|
||||||
|
internal::hideEmptyParentSplitters(splitter);
|
||||||
|
|
||||||
|
//Hide empty floating widget
|
||||||
|
DockContainerWidget *container = this->dockContainer();
|
||||||
|
if (!container->isFloating()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTitleBarVisibility();
|
||||||
|
auto topLevelWidget = container->topLevelDockWidget();
|
||||||
|
auto floatingWidget = container->floatingWidget();
|
||||||
|
if (topLevelWidget) {
|
||||||
|
floatingWidget->updateWindowTitle();
|
||||||
|
DockWidget::emitTopLevelEventForWidget(topLevelWidget, true);
|
||||||
|
} else if (container->openedDockAreas().isEmpty()) {
|
||||||
|
floatingWidget->hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaWidget::onTabCloseRequested(int index)
|
||||||
|
{
|
||||||
|
qCInfo(adsLog) << Q_FUNC_INFO << "index" << index;
|
||||||
|
auto *currentDockWidget = dockWidget(index);
|
||||||
|
if (currentDockWidget->features().testFlag(DockWidget::DockWidgetDeleteOnClose)) {
|
||||||
|
currentDockWidget->closeDockWidgetInternal();
|
||||||
|
} else {
|
||||||
|
currentDockWidget->toggleView(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DockWidget *DockAreaWidget::currentDockWidget() const
|
||||||
|
{
|
||||||
|
int currentIdx = currentIndex();
|
||||||
|
if (currentIdx < 0) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dockWidget(currentIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaWidget::setCurrentDockWidget(DockWidget *dockWidget)
|
||||||
|
{
|
||||||
|
if (dockManager()->isRestoringState()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
internalSetCurrentDockWidget(dockWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaWidget::internalSetCurrentDockWidget(DockWidget *dockWidget)
|
||||||
|
{
|
||||||
|
int index = indexOf(dockWidget);
|
||||||
|
if (index < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCurrentIndex(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaWidget::setCurrentIndex(int index)
|
||||||
|
{
|
||||||
|
auto currentTabBar = d->tabBar();
|
||||||
|
if (index < 0 || index > (currentTabBar->count() - 1)) {
|
||||||
|
qWarning() << Q_FUNC_INFO << "Invalid index" << index;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cw = d->m_contentsLayout->currentWidget();
|
||||||
|
auto nw = d->m_contentsLayout->widget(index);
|
||||||
|
if (cw == nw && !nw->isHidden()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit currentChanging(index);
|
||||||
|
currentTabBar->setCurrentIndex(index);
|
||||||
|
d->m_contentsLayout->setCurrentIndex(index);
|
||||||
|
d->m_contentsLayout->currentWidget()->show();
|
||||||
|
emit currentChanged(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
int DockAreaWidget::currentIndex() const { return d->m_contentsLayout->currentIndex(); }
|
||||||
|
|
||||||
|
QRect DockAreaWidget::titleBarGeometry() const { return d->m_titleBar->geometry(); }
|
||||||
|
|
||||||
|
QRect DockAreaWidget::contentAreaGeometry() const { return d->m_contentsLayout->geometry(); }
|
||||||
|
|
||||||
|
int DockAreaWidget::indexOf(DockWidget *dockWidget)
|
||||||
|
{
|
||||||
|
return d->m_contentsLayout->indexOf(dockWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<DockWidget *> DockAreaWidget::dockWidgets() const
|
||||||
|
{
|
||||||
|
QList<DockWidget *> dockWidgetList;
|
||||||
|
for (int i = 0; i < d->m_contentsLayout->count(); ++i) {
|
||||||
|
dockWidgetList.append(dockWidget(i));
|
||||||
|
}
|
||||||
|
return dockWidgetList;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DockAreaWidget::openDockWidgetsCount() const
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
for (int i = 0; i < d->m_contentsLayout->count(); ++i) {
|
||||||
|
if (!dockWidget(i)->isClosed()) {
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<DockWidget *> DockAreaWidget::openedDockWidgets() const
|
||||||
|
{
|
||||||
|
QList<DockWidget *> dockWidgetList;
|
||||||
|
for (int i = 0; i < d->m_contentsLayout->count(); ++i) {
|
||||||
|
DockWidget *currentDockWidget = dockWidget(i);
|
||||||
|
if (!currentDockWidget->isClosed()) {
|
||||||
|
dockWidgetList.append(dockWidget(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dockWidgetList;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DockAreaWidget::indexOfFirstOpenDockWidget() const
|
||||||
|
{
|
||||||
|
for (int i = 0; i < d->m_contentsLayout->count(); ++i) {
|
||||||
|
if (!dockWidget(i)->isClosed()) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DockAreaWidget::dockWidgetsCount() const { return d->m_contentsLayout->count(); }
|
||||||
|
|
||||||
|
DockWidget *DockAreaWidget::dockWidget(int index) const
|
||||||
|
{
|
||||||
|
return qobject_cast<DockWidget *>(d->m_contentsLayout->widget(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaWidget::reorderDockWidget(int fromIndex, int toIndex)
|
||||||
|
{
|
||||||
|
qCInfo(adsLog) << Q_FUNC_INFO;
|
||||||
|
if (fromIndex >= d->m_contentsLayout->count() || fromIndex < 0
|
||||||
|
|| toIndex >= d->m_contentsLayout->count() || toIndex < 0 || fromIndex == toIndex) {
|
||||||
|
qCInfo(adsLog) << "Invalid index for tab movement" << fromIndex << toIndex;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto widget = d->m_contentsLayout->widget(fromIndex);
|
||||||
|
d->m_contentsLayout->removeWidget(widget);
|
||||||
|
d->m_contentsLayout->insertWidget(toIndex, widget);
|
||||||
|
setCurrentIndex(toIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaWidget::toggleDockWidgetView(DockWidget *dockWidget, bool open)
|
||||||
|
{
|
||||||
|
Q_UNUSED(dockWidget)
|
||||||
|
Q_UNUSED(open)
|
||||||
|
updateTitleBarVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaWidget::updateTitleBarVisibility()
|
||||||
|
{
|
||||||
|
DockContainerWidget *container = dockContainer();
|
||||||
|
if (!container) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DockManager::configFlags().testFlag(DockManager::AlwaysShowTabs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d->m_titleBar) {
|
||||||
|
d->m_titleBar->setVisible(!container->isFloating() || !container->hasTopLevelDockWidget());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaWidget::markTitleBarMenuOutdated()
|
||||||
|
{
|
||||||
|
if (d->m_titleBar) {
|
||||||
|
d->m_titleBar->markTabsMenuOutdated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaWidget::saveState(QXmlStreamWriter &stream) const
|
||||||
|
{
|
||||||
|
stream.writeStartElement("area");
|
||||||
|
stream.writeAttribute("tabs", QString::number(d->m_contentsLayout->count()));
|
||||||
|
auto localDockWidget = currentDockWidget();
|
||||||
|
QString name = localDockWidget ? localDockWidget->objectName() : "";
|
||||||
|
stream.writeAttribute("current", name);
|
||||||
|
qCInfo(adsLog) << Q_FUNC_INFO << "TabCount: " << d->m_contentsLayout->count()
|
||||||
|
<< " Current: " << name;
|
||||||
|
for (int i = 0; i < d->m_contentsLayout->count(); ++i) {
|
||||||
|
dockWidget(i)->saveState(stream);
|
||||||
|
}
|
||||||
|
stream.writeEndElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
DockWidget *DockAreaWidget::nextOpenDockWidget(DockWidget *dockWidget) const
|
||||||
|
{
|
||||||
|
auto openDockWidgets = openedDockWidgets();
|
||||||
|
if (openDockWidgets.count() > 1
|
||||||
|
|| (openDockWidgets.count() == 1 && openDockWidgets[0] != dockWidget)) {
|
||||||
|
DockWidget *nextDockWidget;
|
||||||
|
if (openDockWidgets.last() == dockWidget) {
|
||||||
|
nextDockWidget = openDockWidgets[openDockWidgets.count() - 2];
|
||||||
|
} else {
|
||||||
|
int nextIndex = openDockWidgets.indexOf(dockWidget) + 1;
|
||||||
|
nextDockWidget = openDockWidgets[nextIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
return nextDockWidget;
|
||||||
|
} else {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DockWidget::DockWidgetFeatures DockAreaWidget::features(eBitwiseOperator mode) const
|
||||||
|
{
|
||||||
|
if (BitwiseAnd == mode) {
|
||||||
|
DockWidget::DockWidgetFeatures features(DockWidget::AllDockWidgetFeatures);
|
||||||
|
for (const auto dockWidget : dockWidgets()) {
|
||||||
|
features &= dockWidget->features();
|
||||||
|
}
|
||||||
|
return features;
|
||||||
|
} else {
|
||||||
|
DockWidget::DockWidgetFeatures features(DockWidget::NoDockWidgetFeatures);
|
||||||
|
for (const auto dockWidget : dockWidgets()) {
|
||||||
|
features |= dockWidget->features();
|
||||||
|
}
|
||||||
|
return features;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaWidget::toggleView(bool open)
|
||||||
|
{
|
||||||
|
setVisible(open);
|
||||||
|
|
||||||
|
emit viewToggled(open);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaWidget::setVisible(bool visible)
|
||||||
|
{
|
||||||
|
Super::setVisible(visible);
|
||||||
|
if (d->m_updateTitleBarButtons) {
|
||||||
|
d->updateTitleBarButtonStates();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaWidget::setAllowedAreas(DockWidgetAreas areas)
|
||||||
|
{
|
||||||
|
d->m_allowedAreas = areas;
|
||||||
|
}
|
||||||
|
|
||||||
|
DockWidgetAreas DockAreaWidget::allowedAreas() const
|
||||||
|
{
|
||||||
|
return d->m_allowedAreas;
|
||||||
|
}
|
||||||
|
|
||||||
|
QAbstractButton *DockAreaWidget::titleBarButton(eTitleBarButton which) const
|
||||||
|
{
|
||||||
|
return d->m_titleBar->button(which);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaWidget::closeArea()
|
||||||
|
{
|
||||||
|
// If there is only one single dock widget and this widget has the
|
||||||
|
// DeleteOnClose feature, then we delete the dock widget now
|
||||||
|
auto openDockWidgets = openedDockWidgets();
|
||||||
|
if (openDockWidgets.count() == 1
|
||||||
|
&& openDockWidgets[0]->features().testFlag(DockWidget::DockWidgetDeleteOnClose)) {
|
||||||
|
openDockWidgets[0]->closeDockWidgetInternal();
|
||||||
|
} else {
|
||||||
|
for (auto dockWidget : openedDockWidgets()) {
|
||||||
|
dockWidget->toggleView(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockAreaWidget::closeOtherAreas() { dockContainer()->closeOtherAreas(this); }
|
||||||
|
|
||||||
|
DockAreaTitleBar *DockAreaWidget::titleBar() const { return d->m_titleBar; }
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
319
src/libs/advanceddockingsystem/dockareawidget.h
Normal file
319
src/libs/advanceddockingsystem/dockareawidget.h
Normal file
@@ -0,0 +1,319 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 Uwe Kindler
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ads_globals.h"
|
||||||
|
#include "dockwidget.h"
|
||||||
|
|
||||||
|
#include <QFrame>
|
||||||
|
|
||||||
|
class QXmlStreamWriter;
|
||||||
|
class QAbstractButton;
|
||||||
|
|
||||||
|
namespace ADS {
|
||||||
|
|
||||||
|
struct DockAreaWidgetPrivate;
|
||||||
|
class DockManager;
|
||||||
|
class DockContainerWidget;
|
||||||
|
class DockContainerWidgetPrivate;
|
||||||
|
class DockAreaTitleBar;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DockAreaWidget manages multiple instances of DockWidgets.
|
||||||
|
* It displays a title tab, which is clickable and will switch to
|
||||||
|
* the contents associated to the title when clicked.
|
||||||
|
*/
|
||||||
|
class ADS_EXPORT DockAreaWidget : public QFrame
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private:
|
||||||
|
DockAreaWidgetPrivate *d; ///< private data (pimpl)
|
||||||
|
friend struct DockAreaWidgetPrivate;
|
||||||
|
friend class DockContainerWidget;
|
||||||
|
friend class DockContainerWidgetPrivate;
|
||||||
|
friend class DockWidgetTab;
|
||||||
|
friend struct DockWidgetPrivate;
|
||||||
|
friend class DockWidget;
|
||||||
|
friend struct DockManagerPrivate;
|
||||||
|
friend class DockManager;
|
||||||
|
|
||||||
|
void onTabCloseRequested(int index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reorder the index position of DockWidget at fromIndx to toIndex
|
||||||
|
* if a tab in the tabbar is dragged from one index to another one
|
||||||
|
*/
|
||||||
|
void reorderDockWidget(int fromIndex, int toIndex);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* Inserts a dock widget into dock area.
|
||||||
|
* All dockwidgets in the dock area tabified in a stacked layout with tabs.
|
||||||
|
* The index indicates the index of the new dockwidget in the tabbar and
|
||||||
|
* in the stacked layout. If the Activate parameter is true, the new
|
||||||
|
* DockWidget will be the active one in the stacked layout
|
||||||
|
*/
|
||||||
|
void insertDockWidget(int index, DockWidget *dockWidget, bool activate = true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new dock widget to dock area.
|
||||||
|
* All dockwidgets in the dock area tabified in a stacked layout with tabs
|
||||||
|
*/
|
||||||
|
void addDockWidget(DockWidget *dockWidget);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the given dock widget from the dock area
|
||||||
|
*/
|
||||||
|
void removeDockWidget(DockWidget *dockWidget);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called from dock widget if it is opened or closed
|
||||||
|
*/
|
||||||
|
void toggleDockWidgetView(DockWidget *dockWidget, bool open);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a helper function to get the next open dock widget to activate
|
||||||
|
* if the given DockWidget will be closed or removed.
|
||||||
|
* The function returns the next widget that should be activated or
|
||||||
|
* nullptr in case there are no more open widgets in this area.
|
||||||
|
*/
|
||||||
|
DockWidget *nextOpenDockWidget(DockWidget *dockWidget) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the index of the given DockWidget in the internal layout
|
||||||
|
*/
|
||||||
|
int indexOf(DockWidget *dockWidget);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call this function, if you already know, that the dock does not
|
||||||
|
* contain any visible content (any open dock widgets).
|
||||||
|
*/
|
||||||
|
void hideAreaWithNoVisibleContent();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the dock area layout and components visibility
|
||||||
|
*/
|
||||||
|
void updateTitleBarVisibility();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the internal private function for setting the current widget.
|
||||||
|
* This function is called by the public setCurrentDockWidget() function
|
||||||
|
* and by the dock manager when restoring the state
|
||||||
|
*/
|
||||||
|
void internalSetCurrentDockWidget(DockWidget *dockWidget);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks tabs menu to update
|
||||||
|
*/
|
||||||
|
void markTitleBarMenuOutdated();
|
||||||
|
|
||||||
|
void toggleView(bool open);
|
||||||
|
|
||||||
|
public:
|
||||||
|
using Super = QFrame;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default Constructor
|
||||||
|
*/
|
||||||
|
DockAreaWidget(DockManager *dockManager, DockContainerWidget *parent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual Destructor
|
||||||
|
*/
|
||||||
|
virtual ~DockAreaWidget() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the dock manager object this dock area belongs to
|
||||||
|
*/
|
||||||
|
DockManager *dockManager() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the dock container widget this dock area widget belongs to or 0
|
||||||
|
* if there is no
|
||||||
|
*/
|
||||||
|
DockContainerWidget *dockContainer() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the rectangle of the title area
|
||||||
|
*/
|
||||||
|
QRect titleBarGeometry() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the rectangle of the content
|
||||||
|
*/
|
||||||
|
QRect contentAreaGeometry() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of dock widgets in this area
|
||||||
|
*/
|
||||||
|
int dockWidgetsCount() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of all dock widgets in this dock area.
|
||||||
|
* This list contains open and closed dock widgets.
|
||||||
|
*/
|
||||||
|
QList<DockWidget *> dockWidgets() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of open dock widgets in this area
|
||||||
|
*/
|
||||||
|
int openDockWidgetsCount() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of dock widgets that are not closed.
|
||||||
|
*/
|
||||||
|
QList<DockWidget *> openedDockWidgets() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a dock widget by its index
|
||||||
|
*/
|
||||||
|
DockWidget *dockWidget(int indexOf) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the index of the current active dock widget or -1 if there
|
||||||
|
* are is no active dock widget (ie.e if all dock widgets are closed)
|
||||||
|
*/
|
||||||
|
int currentIndex() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the index of the first open dock widgets in the list of
|
||||||
|
* dock widgets.
|
||||||
|
* This function is here for performance reasons. Normally it would
|
||||||
|
* be possible to take the first dock widget from the list returned by
|
||||||
|
* openedDockWidgets() function. But that function enumerates all
|
||||||
|
* dock widgets while this functions stops after the first open dock widget.
|
||||||
|
* If there are no open dock widgets, the function returns -1.
|
||||||
|
*/
|
||||||
|
int indexOfFirstOpenDockWidget() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current active dock widget or a nullptr if there is no
|
||||||
|
* active dock widget (i.e. if all dock widgets are closed)
|
||||||
|
*/
|
||||||
|
DockWidget *currentDockWidget() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows the tab with the given dock widget
|
||||||
|
*/
|
||||||
|
void setCurrentDockWidget(DockWidget *dockWidget);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the state into the given stream
|
||||||
|
*/
|
||||||
|
void saveState(QXmlStreamWriter &stream) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This functions returns the dock widget features of all dock widget in
|
||||||
|
* this area.
|
||||||
|
* A bitwise and is used to combine the flags of all dock widgets. That
|
||||||
|
* means, if only one single dock widget does not support a certain flag,
|
||||||
|
* the whole dock are does not support the flag. I.e. if one single
|
||||||
|
* dock widget in this area is not closable, the whole dock are is not
|
||||||
|
* closable.
|
||||||
|
*/
|
||||||
|
DockWidget::DockWidgetFeatures features(eBitwiseOperator mode = BitwiseAnd) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the title bar button corresponding to the given title bar
|
||||||
|
* button identifier
|
||||||
|
*/
|
||||||
|
QAbstractButton *titleBarButton(eTitleBarButton which) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the close button if visibility changed
|
||||||
|
*/
|
||||||
|
virtual void setVisible(bool visible) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the areas of this particular dock area that are allowed for docking
|
||||||
|
*/
|
||||||
|
void setAllowedAreas(DockWidgetAreas areas);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns flags with all allowed drop areas of this particular dock area
|
||||||
|
*/
|
||||||
|
DockWidgetAreas allowedAreas() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the title bar of this dock area
|
||||||
|
*/
|
||||||
|
DockAreaTitleBar *titleBar() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This activates the tab for the given tab index.
|
||||||
|
* If the dock widget for the given tab is not visible, the this function
|
||||||
|
* call will make it visible.
|
||||||
|
*/
|
||||||
|
void setCurrentIndex(int indexOf);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the dock area and all dock widgets in this area
|
||||||
|
*/
|
||||||
|
void closeArea();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function closes all other areas except of this area
|
||||||
|
*/
|
||||||
|
void closeOtherAreas();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
/**
|
||||||
|
* This signal is emitted when user clicks on a tab at an index.
|
||||||
|
*/
|
||||||
|
void tabBarClicked(int indexOf);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This signal is emitted when the tab bar's current tab is about to be changed. The new
|
||||||
|
* current has the given index, or -1 if there isn't a new one.
|
||||||
|
* @param index
|
||||||
|
*/
|
||||||
|
void currentChanging(int indexOf);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This signal is emitted when the tab bar's current tab changes. The new
|
||||||
|
* current has the given index, or -1 if there isn't a new one
|
||||||
|
* @param index
|
||||||
|
*/
|
||||||
|
void currentChanged(int indexOf);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This signal is emitted if the visibility of this dock area is toggled
|
||||||
|
* via toggle view function
|
||||||
|
*/
|
||||||
|
void viewToggled(bool open);
|
||||||
|
}; // class DockAreaWidget
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
80
src/libs/advanceddockingsystem/dockcomponentsfactory.cpp
Normal file
80
src/libs/advanceddockingsystem/dockcomponentsfactory.cpp
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 Uwe Kindler
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "dockcomponentsfactory.h"
|
||||||
|
|
||||||
|
#include "dockwidgettab.h"
|
||||||
|
#include "dockareatabbar.h"
|
||||||
|
#include "dockareatitlebar.h"
|
||||||
|
#include "dockwidget.h"
|
||||||
|
#include "dockareawidget.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace ADS
|
||||||
|
{
|
||||||
|
static std::unique_ptr<DockComponentsFactory> g_defaultFactory(new DockComponentsFactory());
|
||||||
|
|
||||||
|
DockWidgetTab *DockComponentsFactory::createDockWidgetTab(DockWidget *dockWidget) const
|
||||||
|
{
|
||||||
|
return new DockWidgetTab(dockWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
DockAreaTabBar *DockComponentsFactory::createDockAreaTabBar(DockAreaWidget *dockArea) const
|
||||||
|
{
|
||||||
|
return new DockAreaTabBar(dockArea);
|
||||||
|
}
|
||||||
|
|
||||||
|
DockAreaTitleBar *DockComponentsFactory::createDockAreaTitleBar(DockAreaWidget *dockArea) const
|
||||||
|
{
|
||||||
|
return new DockAreaTitleBar(dockArea);
|
||||||
|
}
|
||||||
|
|
||||||
|
const DockComponentsFactory *DockComponentsFactory::factory()
|
||||||
|
{
|
||||||
|
return g_defaultFactory.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockComponentsFactory::setFactory(DockComponentsFactory *factory)
|
||||||
|
{
|
||||||
|
g_defaultFactory.reset(factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockComponentsFactory::resetDefaultFactory()
|
||||||
|
{
|
||||||
|
g_defaultFactory.reset(new DockComponentsFactory());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
109
src/libs/advanceddockingsystem/dockcomponentsfactory.h
Normal file
109
src/libs/advanceddockingsystem/dockcomponentsfactory.h
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 Uwe Kindler
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ads_globals.h"
|
||||||
|
|
||||||
|
namespace ADS {
|
||||||
|
|
||||||
|
class DockWidgetTab;
|
||||||
|
class DockAreaTitleBar;
|
||||||
|
class DockAreaTabBar;
|
||||||
|
class DockAreaWidget;
|
||||||
|
class DockWidget;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory for creation of certain GUI elements for the docking framework.
|
||||||
|
* A default unique instance provided by DockComponentsFactory is used for
|
||||||
|
* creation of all supported components. To inject your custom components,
|
||||||
|
* you can create your own derived dock components factory and register
|
||||||
|
* it via setDefaultFactory() function.
|
||||||
|
* \code
|
||||||
|
* CDockComponentsFactory::setDefaultFactory(new MyComponentsFactory()));
|
||||||
|
* \endcode
|
||||||
|
*/
|
||||||
|
class ADS_EXPORT DockComponentsFactory
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Force virtual destructor
|
||||||
|
*/
|
||||||
|
virtual ~DockComponentsFactory() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This default implementation just creates a dock widget tab with
|
||||||
|
* new DockWidgetTab(dockWidget).
|
||||||
|
*/
|
||||||
|
virtual DockWidgetTab *createDockWidgetTab(DockWidget *dockWidget) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This default implementation just creates a dock area tab bar with
|
||||||
|
* new DockAreaTabBar(dockArea).
|
||||||
|
*/
|
||||||
|
virtual DockAreaTabBar *createDockAreaTabBar(DockAreaWidget *dockArea) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This default implementation just creates a dock area title bar with
|
||||||
|
* new DockAreaTitleBar(dockArea).
|
||||||
|
*/
|
||||||
|
virtual DockAreaTitleBar *createDockAreaTitleBar(DockAreaWidget *dockArea) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default components factory
|
||||||
|
*/
|
||||||
|
static const DockComponentsFactory *factory();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a new default factory for creation of GUI elements.
|
||||||
|
* This function takes ownership of the given Factory.
|
||||||
|
*/
|
||||||
|
static void setFactory(DockComponentsFactory* factory);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the current factory to the
|
||||||
|
*/
|
||||||
|
static void resetDefaultFactory();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience function to ease factory instance access
|
||||||
|
*/
|
||||||
|
inline const DockComponentsFactory *componentsFactory()
|
||||||
|
{
|
||||||
|
return DockComponentsFactory::factory();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
1459
src/libs/advanceddockingsystem/dockcontainerwidget.cpp
Normal file
1459
src/libs/advanceddockingsystem/dockcontainerwidget.cpp
Normal file
File diff suppressed because it is too large
Load Diff
290
src/libs/advanceddockingsystem/dockcontainerwidget.h
Normal file
290
src/libs/advanceddockingsystem/dockcontainerwidget.h
Normal file
@@ -0,0 +1,290 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 Uwe Kindler
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ads_globals.h"
|
||||||
|
#include "dockwidget.h"
|
||||||
|
|
||||||
|
#include <QFrame>
|
||||||
|
|
||||||
|
class QXmlStreamWriter;
|
||||||
|
|
||||||
|
namespace ADS {
|
||||||
|
|
||||||
|
class DockContainerWidgetPrivate;
|
||||||
|
class DockAreaWidget;
|
||||||
|
class DockWidget;
|
||||||
|
class DockManager;
|
||||||
|
struct DockManagerPrivate;
|
||||||
|
class FloatingDockContainer;
|
||||||
|
struct FloatingDockContainerPrivate;
|
||||||
|
class FloatingDragPreview;
|
||||||
|
struct FloatingDragPreviewPrivate;
|
||||||
|
class DockingStateReader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Container that manages a number of dock areas with single dock widgets
|
||||||
|
* or tabyfied dock widgets in each area.
|
||||||
|
* Each window that support docking has a DockContainerWidget. That means
|
||||||
|
* the main application window and all floating windows contain
|
||||||
|
* a DockContainerWidget.
|
||||||
|
*/
|
||||||
|
class ADS_EXPORT DockContainerWidget : public QFrame
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private:
|
||||||
|
DockContainerWidgetPrivate *d; ///< private data (pimpl)
|
||||||
|
friend class DockContainerWidgetPrivate;
|
||||||
|
friend class DockManager;
|
||||||
|
friend struct DockManagerPrivate;
|
||||||
|
friend class DockAreaWidget;
|
||||||
|
friend struct DockAreaWidgetPrivate;
|
||||||
|
friend class FloatingDockContainer;
|
||||||
|
friend struct FloatingDockContainerPrivate;
|
||||||
|
friend class DockWidget;
|
||||||
|
friend class FloatingDragPreview;
|
||||||
|
friend struct FloatingDragPreviewPrivate;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* Handles activation events to update zOrderIndex
|
||||||
|
*/
|
||||||
|
virtual bool event(QEvent *event) override;
|
||||||
|
|
||||||
|
public: // TODO temporary
|
||||||
|
/**
|
||||||
|
* Access function for the internal root splitter
|
||||||
|
*/
|
||||||
|
QSplitter *rootSplitter() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* Helper function for creation of the root splitter
|
||||||
|
*/
|
||||||
|
void createRootSplitter();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drop floating widget into the container
|
||||||
|
*/
|
||||||
|
void dropFloatingWidget(FloatingDockContainer *floatingWidget, const QPoint &targetPos);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drop a dock area or a dock widget given in widget parameter
|
||||||
|
*/
|
||||||
|
void dropWidget(QWidget *widget, const QPoint &targetPos);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the given dock area to this container widget
|
||||||
|
*/
|
||||||
|
void addDockArea(DockAreaWidget *dockAreaWidget, DockWidgetArea area = CenterDockWidgetArea);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the given dock area from this container
|
||||||
|
*/
|
||||||
|
void removeDockArea(DockAreaWidget *area);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function replaces the goto construct. Still need to write a good description.
|
||||||
|
*/
|
||||||
|
void emitAndExit() const; // TODO rename
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the state into the given stream
|
||||||
|
*/
|
||||||
|
void saveState(QXmlStreamWriter &stream) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restores the state from given stream.
|
||||||
|
* If Testing is true, the function only parses the data from the given
|
||||||
|
* stream but does not restore anything. You can use this check for
|
||||||
|
* faulty files before you start restoring the state
|
||||||
|
*/
|
||||||
|
bool restoreState(DockingStateReader &stream, bool testing);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function returns the last added dock area widget for the given
|
||||||
|
* area identifier or 0 if no dock area widget has been added for the given
|
||||||
|
* area
|
||||||
|
*/
|
||||||
|
DockAreaWidget *lastAddedDockAreaWidget(DockWidgetArea area) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If hasSingleVisibleDockWidget() returns true, this function returns the
|
||||||
|
* one and only visible dock widget. Otherwise it returns a nullptr.
|
||||||
|
*/
|
||||||
|
DockWidget *topLevelDockWidget() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the top level dock area.
|
||||||
|
*/
|
||||||
|
DockAreaWidget *topLevelDockArea() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function returns a list of all dock widgets in this floating widget.
|
||||||
|
* It may be possible, depending on the implementation, that dock widgets,
|
||||||
|
* that are not visible to the user have no parent widget. Therefore simply
|
||||||
|
* calling findChildren() would not work here. Therefore this function
|
||||||
|
* iterates over all dock areas and creates a list that contains all
|
||||||
|
* dock widgets returned from all dock areas.
|
||||||
|
*/
|
||||||
|
QList<DockWidget *> dockWidgets() const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Default Constructor
|
||||||
|
*/
|
||||||
|
DockContainerWidget(DockManager *dockManager, QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual Destructor
|
||||||
|
*/
|
||||||
|
virtual ~DockContainerWidget() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds dockwidget into the given area.
|
||||||
|
* If DockAreaWidget is not null, then the area parameter indicates the area
|
||||||
|
* into the DockAreaWidget. If DockAreaWidget is null, the Dockwidget will
|
||||||
|
* be dropped into the container.
|
||||||
|
* \return Returns the dock area widget that contains the new DockWidget
|
||||||
|
*/
|
||||||
|
DockAreaWidget *addDockWidget(DockWidgetArea area,
|
||||||
|
DockWidget *dockWidget,
|
||||||
|
DockAreaWidget *dockAreaWidget = nullptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes dockwidget
|
||||||
|
*/
|
||||||
|
void removeDockWidget(DockWidget *dockWidget);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current zOrderIndex
|
||||||
|
*/
|
||||||
|
virtual unsigned int zOrderIndex() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function returns true if this container widgets z order index is
|
||||||
|
* higher than the index of the container widget given in Other parameter
|
||||||
|
*/
|
||||||
|
bool isInFrontOf(DockContainerWidget *other) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the dock area at the given global position or 0 if there is no
|
||||||
|
* dock area at this position
|
||||||
|
*/
|
||||||
|
DockAreaWidget *dockAreaAt(const QPoint &globalPos) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the dock area at the given Index or 0 if the index is out of
|
||||||
|
* range
|
||||||
|
*/
|
||||||
|
DockAreaWidget *dockArea(int index) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of dock areas that are not closed
|
||||||
|
* If all dock widgets in a dock area are closed, the dock area will be closed
|
||||||
|
*/
|
||||||
|
QList<DockAreaWidget *> openedDockAreas() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function returns true if this dock area has only one single
|
||||||
|
* visible dock widget.
|
||||||
|
* A top level widget is a real floating widget. Only the isFloating()
|
||||||
|
* function of top level widgets may returns true.
|
||||||
|
*/
|
||||||
|
bool hasTopLevelDockWidget() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of dock areas in this container
|
||||||
|
*/
|
||||||
|
int dockAreaCount() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of visible dock areas
|
||||||
|
*/
|
||||||
|
int visibleDockAreaCount() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function returns true, if this container is in a floating widget
|
||||||
|
*/
|
||||||
|
bool isFloating() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dumps the layout for debugging purposes
|
||||||
|
*/
|
||||||
|
void dumpLayout() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This functions returns the dock widget features of all dock widget in
|
||||||
|
* this container.
|
||||||
|
* A bitwise and is used to combine the flags of all dock widgets. That
|
||||||
|
* means, if only dock widget does not support a certain flag, the whole
|
||||||
|
* dock are does not support the flag.
|
||||||
|
*/
|
||||||
|
DockWidget::DockWidgetFeatures features() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this dock container is in a floating widget, this function returns
|
||||||
|
* the floating widget.
|
||||||
|
* Else, it returns a nullptr.
|
||||||
|
*/
|
||||||
|
FloatingDockContainer *floatingWidget() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call this function to close all dock areas except the KeepOpenArea
|
||||||
|
*/
|
||||||
|
void closeOtherAreas(DockAreaWidget *keepOpenArea);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
/**
|
||||||
|
* This signal is emitted if one or multiple dock areas has been added to
|
||||||
|
* the internal list of dock areas.
|
||||||
|
* If multiple dock areas are inserted, this signal is emitted only once
|
||||||
|
*/
|
||||||
|
void dockAreasAdded();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This signal is emitted if one or multiple dock areas has been removed
|
||||||
|
*/
|
||||||
|
void dockAreasRemoved();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This signal is emitted if a dock area is opened or closed via
|
||||||
|
* toggleView() function
|
||||||
|
*/
|
||||||
|
void dockAreaViewToggled(DockAreaWidget *dockArea, bool open);
|
||||||
|
}; // class DockContainerWidget
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
50
src/libs/advanceddockingsystem/dockingstatereader.cpp
Normal file
50
src/libs/advanceddockingsystem/dockingstatereader.cpp
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 Uwe Kindler
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "dockingstatereader.h"
|
||||||
|
|
||||||
|
namespace ADS {
|
||||||
|
|
||||||
|
void DockingStateReader::setFileVersion(int fileVersion)
|
||||||
|
{
|
||||||
|
m_fileVersion = fileVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DockingStateReader::fileVersion() const
|
||||||
|
{
|
||||||
|
return m_fileVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
64
src/libs/advanceddockingsystem/dockingstatereader.h
Normal file
64
src/libs/advanceddockingsystem/dockingstatereader.h
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 Uwe Kindler
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QXmlStreamReader>
|
||||||
|
|
||||||
|
namespace ADS {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extends QXmlStreamReader with file version information
|
||||||
|
*/
|
||||||
|
class DockingStateReader : public QXmlStreamReader
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
int m_fileVersion;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using QXmlStreamReader::QXmlStreamReader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the file version for this state reader
|
||||||
|
*/
|
||||||
|
void setFileVersion(int fileVersion);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the file version set via setFileVersion
|
||||||
|
*/
|
||||||
|
int fileVersion() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
819
src/libs/advanceddockingsystem/dockmanager.cpp
Normal file
819
src/libs/advanceddockingsystem/dockmanager.cpp
Normal file
@@ -0,0 +1,819 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 Uwe Kindler
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "dockmanager.h"
|
||||||
|
|
||||||
|
#include "ads_globals.h"
|
||||||
|
#include "dockareawidget.h"
|
||||||
|
#include "dockingstatereader.h"
|
||||||
|
#include "dockoverlay.h"
|
||||||
|
#include "dockwidget.h"
|
||||||
|
#include "dockwidgettab.h"
|
||||||
|
#include "floatingdockcontainer.h"
|
||||||
|
#include "iconprovider.h"
|
||||||
|
|
||||||
|
#include "workspacedialog.h"
|
||||||
|
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <QAction>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QList>
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include <QMap>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QVariant>
|
||||||
|
#include <QXmlStreamWriter>
|
||||||
|
|
||||||
|
static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtDebugMsg)
|
||||||
|
|
||||||
|
namespace ADS
|
||||||
|
{
|
||||||
|
static DockManager::ConfigFlags g_staticConfigFlags = DockManager::DefaultNonOpaqueConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private data class of DockManager class (pimpl)
|
||||||
|
*/
|
||||||
|
struct DockManagerPrivate
|
||||||
|
{
|
||||||
|
DockManager *q;
|
||||||
|
QList<FloatingDockContainer *> m_floatingWidgets;
|
||||||
|
QList<DockContainerWidget *> m_containers;
|
||||||
|
DockOverlay *m_containerOverlay;
|
||||||
|
DockOverlay *m_dockAreaOverlay;
|
||||||
|
QMap<QString, DockWidget *> m_dockWidgetsMap;
|
||||||
|
bool m_restoringState = false;
|
||||||
|
QVector<FloatingDockContainer *> m_uninitializedFloatingWidgets;
|
||||||
|
|
||||||
|
QString m_workspaceName;
|
||||||
|
bool m_workspaceListDirty = true;
|
||||||
|
QStringList m_workspaces;
|
||||||
|
QHash<QString, QDateTime> m_workspaceDateTimes;
|
||||||
|
QString m_workspaceToRestoreAtStartup;
|
||||||
|
bool m_autorestoreLastWorkspace; // This option is set in the Workspace Manager!
|
||||||
|
QSettings *m_settings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private data constructor
|
||||||
|
*/
|
||||||
|
DockManagerPrivate(DockManager *parent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given data stream is a valid docking system state
|
||||||
|
* file.
|
||||||
|
*/
|
||||||
|
bool checkFormat(const QByteArray &state, int version);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restores the state
|
||||||
|
*/
|
||||||
|
bool restoreStateFromXml(const QByteArray &state,
|
||||||
|
int version,
|
||||||
|
bool testing = internal::restore);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore state
|
||||||
|
*/
|
||||||
|
bool restoreState(const QByteArray &state, int version);
|
||||||
|
|
||||||
|
void restoreDockWidgetsOpenState();
|
||||||
|
void restoreDockAreasIndices();
|
||||||
|
void emitTopLevelEvents();
|
||||||
|
|
||||||
|
void hideFloatingWidgets()
|
||||||
|
{
|
||||||
|
// Hide updates of floating widgets from user
|
||||||
|
for (auto floatingWidget : m_floatingWidgets) { // TODO qAsConst()
|
||||||
|
floatingWidget->hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void markDockWidgetsDirty()
|
||||||
|
{
|
||||||
|
for (auto dockWidget : m_dockWidgetsMap) { // TODO qAsConst()
|
||||||
|
dockWidget->setProperty("dirty", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restores the container with the given index
|
||||||
|
*/
|
||||||
|
bool restoreContainer(int index, DockingStateReader &stream, bool testing);
|
||||||
|
|
||||||
|
void workspaceLoadingProgress();
|
||||||
|
};
|
||||||
|
// struct DockManagerPrivate
|
||||||
|
|
||||||
|
DockManagerPrivate::DockManagerPrivate(DockManager *parent)
|
||||||
|
: q(parent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool DockManagerPrivate::restoreContainer(int index, DockingStateReader &stream, bool testing)
|
||||||
|
{
|
||||||
|
if (testing) {
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool result = false;
|
||||||
|
if (index >= m_containers.count()) {
|
||||||
|
FloatingDockContainer *floatingWidget = new FloatingDockContainer(q);
|
||||||
|
result = floatingWidget->restoreState(stream, testing);
|
||||||
|
} else {
|
||||||
|
qCInfo(adsLog) << "d->m_containers[i]->restoreState ";
|
||||||
|
auto container = m_containers[index];
|
||||||
|
if (container->isFloating()) {
|
||||||
|
result = container->floatingWidget()->restoreState(stream, testing);
|
||||||
|
} else {
|
||||||
|
result = container->restoreState(stream, testing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DockManagerPrivate::checkFormat(const QByteArray &state, int version)
|
||||||
|
{
|
||||||
|
return restoreStateFromXml(state, version, internal::restoreTesting);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DockManagerPrivate::restoreStateFromXml(const QByteArray &state, int version, bool testing)
|
||||||
|
{
|
||||||
|
Q_UNUSED(version) // TODO version is not needed, why is it in here in the first place?
|
||||||
|
|
||||||
|
if (state.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DockingStateReader stateReader(state);
|
||||||
|
stateReader.readNextStartElement();
|
||||||
|
if (stateReader.name() != "QtAdvancedDockingSystem") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
qCInfo(adsLog) << stateReader.attributes().value("version");
|
||||||
|
bool ok;
|
||||||
|
int v = stateReader.attributes().value("version").toInt(&ok);
|
||||||
|
if (!ok || v > CurrentVersion) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
stateReader.setFileVersion(v);
|
||||||
|
bool result = true;
|
||||||
|
#ifdef ADS_DEBUG_PRINT
|
||||||
|
int dockContainers = stateReader.attributes().value("containers").toInt();
|
||||||
|
qCInfo(adsLog) << dockContainers;
|
||||||
|
#endif
|
||||||
|
int dockContainerCount = 0;
|
||||||
|
while (stateReader.readNextStartElement()) {
|
||||||
|
if (stateReader.name() == "container") {
|
||||||
|
result = restoreContainer(dockContainerCount, stateReader, testing);
|
||||||
|
if (!result) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dockContainerCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!testing) {
|
||||||
|
// Delete remaining empty floating widgets
|
||||||
|
int floatingWidgetIndex = dockContainerCount - 1;
|
||||||
|
int deleteCount = m_floatingWidgets.count() - floatingWidgetIndex;
|
||||||
|
for (int i = 0; i < deleteCount; ++i) {
|
||||||
|
m_floatingWidgets[floatingWidgetIndex + i]->deleteLater();
|
||||||
|
q->removeDockContainer(m_floatingWidgets[floatingWidgetIndex + i]->dockContainer());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockManagerPrivate::restoreDockWidgetsOpenState()
|
||||||
|
{
|
||||||
|
// All dock widgets, that have not been processed in the restore state
|
||||||
|
// function are invisible to the user now and have no assigned dock area
|
||||||
|
// They do not belong to any dock container, until the user toggles the
|
||||||
|
// toggle view action the next time
|
||||||
|
for (auto dockWidget : m_dockWidgetsMap) {
|
||||||
|
if (dockWidget->property(internal::dirtyProperty).toBool()) {
|
||||||
|
dockWidget->flagAsUnassigned();
|
||||||
|
emit dockWidget->viewToggled(false);
|
||||||
|
} else {
|
||||||
|
dockWidget->toggleViewInternal(
|
||||||
|
!dockWidget->property(internal::closedProperty).toBool());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockManagerPrivate::restoreDockAreasIndices()
|
||||||
|
{
|
||||||
|
// Now all dock areas are properly restored and we setup the index of
|
||||||
|
// The dock areas because the previous toggleView() action has changed
|
||||||
|
// the dock area index
|
||||||
|
int count = 0;
|
||||||
|
for (auto dockContainer : m_containers) {
|
||||||
|
count++;
|
||||||
|
for (int i = 0; i < dockContainer->dockAreaCount(); ++i) {
|
||||||
|
DockAreaWidget *dockArea = dockContainer->dockArea(i);
|
||||||
|
QString dockWidgetName = dockArea->property("currentDockWidget").toString();
|
||||||
|
DockWidget *dockWidget = nullptr;
|
||||||
|
if (!dockWidgetName.isEmpty()) {
|
||||||
|
dockWidget = q->findDockWidget(dockWidgetName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dockWidget || dockWidget->isClosed()) {
|
||||||
|
int index = dockArea->indexOfFirstOpenDockWidget();
|
||||||
|
if (index < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
dockArea->setCurrentIndex(index);
|
||||||
|
} else {
|
||||||
|
dockArea->internalSetCurrentDockWidget(dockWidget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockManagerPrivate::emitTopLevelEvents()
|
||||||
|
{
|
||||||
|
// Finally we need to send the topLevelChanged() signals for all dock
|
||||||
|
// widgets if top level changed
|
||||||
|
for (auto dockContainer : m_containers) {
|
||||||
|
DockWidget *topLevelDockWidget = dockContainer->topLevelDockWidget();
|
||||||
|
if (topLevelDockWidget) {
|
||||||
|
topLevelDockWidget->emitTopLevelChanged(true);
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < dockContainer->dockAreaCount(); ++i) {
|
||||||
|
auto dockArea = dockContainer->dockArea(i);
|
||||||
|
for (auto dockWidget : dockArea->dockWidgets()) {
|
||||||
|
dockWidget->emitTopLevelChanged(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DockManagerPrivate::restoreState(const QByteArray &state, int version)
|
||||||
|
{
|
||||||
|
QByteArray currentState = state.startsWith("<?xml") ? state : qUncompress(state);
|
||||||
|
if (!checkFormat(currentState, version)) {
|
||||||
|
qCInfo(adsLog) << "checkFormat: Error checking format!!!";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide updates of floating widgets from use
|
||||||
|
hideFloatingWidgets();
|
||||||
|
markDockWidgetsDirty();
|
||||||
|
|
||||||
|
if (!restoreStateFromXml(currentState, version)) {
|
||||||
|
qCInfo(adsLog) << "restoreState: Error restoring state!!!";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
restoreDockWidgetsOpenState();
|
||||||
|
restoreDockAreasIndices();
|
||||||
|
emitTopLevelEvents();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DockManager::DockManager(QWidget *parent)
|
||||||
|
: DockContainerWidget(this, parent)
|
||||||
|
, d(new DockManagerPrivate(this))
|
||||||
|
{
|
||||||
|
connect(this, &DockManager::workspaceListChanged, this, [=] {
|
||||||
|
d->m_workspaceListDirty = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
createRootSplitter();
|
||||||
|
QMainWindow *mainWindow = qobject_cast<QMainWindow *>(parent);
|
||||||
|
if (mainWindow) {
|
||||||
|
mainWindow->setCentralWidget(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
d->m_dockAreaOverlay = new DockOverlay(this, DockOverlay::ModeDockAreaOverlay);
|
||||||
|
d->m_containerOverlay = new DockOverlay(this, DockOverlay::ModeContainerOverlay);
|
||||||
|
d->m_containers.append(this);
|
||||||
|
//d->loadStylesheet();
|
||||||
|
}
|
||||||
|
|
||||||
|
DockManager::~DockManager()
|
||||||
|
{
|
||||||
|
// If the factory default workspace is still loaded, create a default workspace just in case
|
||||||
|
// the layout changed as there is no tracking of layout changes.
|
||||||
|
if (isFactoryDefaultWorkspace(d->m_workspaceName)
|
||||||
|
&& !isDefaultWorkspace(d->m_workspaceName)) {
|
||||||
|
createWorkspace(Constants::DEFAULT_NAME);
|
||||||
|
openWorkspace(Constants::DEFAULT_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit aboutToUnloadWorkspace(d->m_workspaceName);
|
||||||
|
save();
|
||||||
|
|
||||||
|
for (auto floatingWidget : d->m_floatingWidgets) {
|
||||||
|
delete floatingWidget;
|
||||||
|
}
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
DockManager::ConfigFlags DockManager::configFlags() { return g_staticConfigFlags; }
|
||||||
|
|
||||||
|
void DockManager::setConfigFlags(const ConfigFlags flags) { g_staticConfigFlags = flags; }
|
||||||
|
|
||||||
|
void DockManager::setConfigFlag(eConfigFlag flag, bool on)
|
||||||
|
{
|
||||||
|
internal::setFlag(g_staticConfigFlags, flag, on);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DockManager::testConfigFlag(eConfigFlag flag)
|
||||||
|
{
|
||||||
|
return configFlags().testFlag(flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
IconProvider &DockManager::iconProvider()
|
||||||
|
{
|
||||||
|
static IconProvider instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DockManager::startDragDistance()
|
||||||
|
{
|
||||||
|
return static_cast<int>(QApplication::startDragDistance() * 1.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockManager::setSettings(QSettings *settings) { d->m_settings = settings; }
|
||||||
|
|
||||||
|
DockAreaWidget *DockManager::addDockWidget(DockWidgetArea area,
|
||||||
|
DockWidget *dockWidget,
|
||||||
|
DockAreaWidget *dockAreaWidget)
|
||||||
|
{
|
||||||
|
d->m_dockWidgetsMap.insert(dockWidget->objectName(), dockWidget);
|
||||||
|
return DockContainerWidget::addDockWidget(area, dockWidget, dockAreaWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
DockAreaWidget *DockManager::addDockWidgetTab(DockWidgetArea area, DockWidget *dockWidget)
|
||||||
|
{
|
||||||
|
DockAreaWidget *areaWidget = lastAddedDockAreaWidget(area);
|
||||||
|
if (areaWidget) {
|
||||||
|
return addDockWidget(ADS::CenterDockWidgetArea, dockWidget, areaWidget);
|
||||||
|
} else if (!openedDockAreas().isEmpty()) {
|
||||||
|
return addDockWidget(area, dockWidget, openedDockAreas().last());
|
||||||
|
} else {
|
||||||
|
return addDockWidget(area, dockWidget, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DockAreaWidget *DockManager::addDockWidgetTabToArea(DockWidget *dockWidget,
|
||||||
|
DockAreaWidget *dockAreaWidget)
|
||||||
|
{
|
||||||
|
return addDockWidget(ADS::CenterDockWidgetArea, dockWidget, dockAreaWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
FloatingDockContainer *DockManager::addDockWidgetFloating(DockWidget *dockWidget)
|
||||||
|
{
|
||||||
|
d->m_dockWidgetsMap.insert(dockWidget->objectName(), dockWidget);
|
||||||
|
DockAreaWidget *oldDockArea = dockWidget->dockAreaWidget();
|
||||||
|
if (oldDockArea) {
|
||||||
|
oldDockArea->removeDockWidget(dockWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
dockWidget->setDockManager(this);
|
||||||
|
FloatingDockContainer *floatingWidget = new FloatingDockContainer(dockWidget);
|
||||||
|
floatingWidget->resize(dockWidget->size());
|
||||||
|
if (isVisible()) {
|
||||||
|
floatingWidget->show();
|
||||||
|
} else {
|
||||||
|
d->m_uninitializedFloatingWidgets.append(floatingWidget);
|
||||||
|
}
|
||||||
|
return floatingWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockManager::registerFloatingWidget(FloatingDockContainer *floatingWidget)
|
||||||
|
{
|
||||||
|
d->m_floatingWidgets.append(floatingWidget);
|
||||||
|
emit floatingWidgetCreated(floatingWidget);
|
||||||
|
qCInfo(adsLog) << "d->FloatingWidgets.count() " << d->m_floatingWidgets.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockManager::removeFloatingWidget(FloatingDockContainer *floatingWidget)
|
||||||
|
{
|
||||||
|
d->m_floatingWidgets.removeAll(floatingWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockManager::registerDockContainer(DockContainerWidget *dockContainer)
|
||||||
|
{
|
||||||
|
d->m_containers.append(dockContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockManager::removeDockContainer(DockContainerWidget *dockContainer)
|
||||||
|
{
|
||||||
|
if (this != dockContainer) {
|
||||||
|
d->m_containers.removeAll(dockContainer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DockOverlay *DockManager::containerOverlay() const { return d->m_containerOverlay; }
|
||||||
|
|
||||||
|
DockOverlay *DockManager::dockAreaOverlay() const { return d->m_dockAreaOverlay; }
|
||||||
|
|
||||||
|
const QList<DockContainerWidget *> DockManager::dockContainers() const
|
||||||
|
{
|
||||||
|
return d->m_containers;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QList<FloatingDockContainer *> DockManager::floatingWidgets() const
|
||||||
|
{
|
||||||
|
return d->m_floatingWidgets;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int DockManager::zOrderIndex() const { return 0; }
|
||||||
|
|
||||||
|
QByteArray DockManager::saveState(int version) const
|
||||||
|
{
|
||||||
|
QByteArray xmlData;
|
||||||
|
QXmlStreamWriter stream(&xmlData);
|
||||||
|
auto configFlags = DockManager::configFlags();
|
||||||
|
stream.setAutoFormatting(configFlags.testFlag(XmlAutoFormattingEnabled));
|
||||||
|
stream.writeStartDocument();
|
||||||
|
stream.writeStartElement("QtAdvancedDockingSystem");
|
||||||
|
stream.writeAttribute("version", QString::number(version));
|
||||||
|
stream.writeAttribute("containers", QString::number(d->m_containers.count()));
|
||||||
|
for (auto container : d->m_containers) {
|
||||||
|
container->saveState(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.writeEndElement();
|
||||||
|
stream.writeEndDocument();
|
||||||
|
return xmlData;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DockManager::restoreState(const QByteArray &state, int version)
|
||||||
|
{
|
||||||
|
// Prevent multiple calls as long as state is not restore. This may
|
||||||
|
// happen, if QApplication::processEvents() is called somewhere
|
||||||
|
if (d->m_restoringState) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We hide the complete dock manager here. Restoring the state means
|
||||||
|
// that DockWidgets are removed from the DockArea internal stack layout
|
||||||
|
// which in turn means, that each time a widget is removed the stack
|
||||||
|
// will show and raise the next available widget which in turn
|
||||||
|
// triggers show events for the dock widgets. To avoid this we hide the
|
||||||
|
// dock manager. Because there will be no processing of application
|
||||||
|
// events until this function is finished, the user will not see this
|
||||||
|
// hiding
|
||||||
|
bool isHidden = this->isHidden();
|
||||||
|
if (!isHidden) {
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
d->m_restoringState = true;
|
||||||
|
emit restoringState();
|
||||||
|
bool result = d->restoreState(state, version);
|
||||||
|
d->m_restoringState = false;
|
||||||
|
emit stateRestored();
|
||||||
|
if (!isHidden) {
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockManager::showEvent(QShowEvent *event)
|
||||||
|
{
|
||||||
|
Super::showEvent(event);
|
||||||
|
if (d->m_uninitializedFloatingWidgets.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto floatingWidget : d->m_uninitializedFloatingWidgets) {
|
||||||
|
floatingWidget->show();
|
||||||
|
}
|
||||||
|
d->m_uninitializedFloatingWidgets.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
DockWidget *DockManager::findDockWidget(const QString &objectName) const
|
||||||
|
{
|
||||||
|
return d->m_dockWidgetsMap.value(objectName, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockManager::removeDockWidget(DockWidget *dockWidget)
|
||||||
|
{
|
||||||
|
emit dockWidgetAboutToBeRemoved(dockWidget);
|
||||||
|
d->m_dockWidgetsMap.remove(dockWidget->objectName());
|
||||||
|
DockContainerWidget::removeDockWidget(dockWidget);
|
||||||
|
emit dockWidgetRemoved(dockWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
QMap<QString, DockWidget *> DockManager::dockWidgetsMap() const { return d->m_dockWidgetsMap; }
|
||||||
|
|
||||||
|
bool DockManager::isRestoringState() const { return d->m_restoringState; }
|
||||||
|
|
||||||
|
void DockManager::showWorkspaceMananger()
|
||||||
|
{
|
||||||
|
// Save current workspace
|
||||||
|
save();
|
||||||
|
|
||||||
|
WorkspaceDialog workspaceDialog(this, parentWidget());
|
||||||
|
workspaceDialog.setAutoLoadWorkspace(autoRestorLastWorkspace());
|
||||||
|
workspaceDialog.exec();
|
||||||
|
|
||||||
|
QTC_ASSERT(d->m_settings, return );
|
||||||
|
d->m_settings->setValue(Constants::AUTO_RESTORE_WORKSPACE_SETTINGS_KEY,
|
||||||
|
workspaceDialog.autoLoadWorkspace());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DockManager::isFactoryDefaultWorkspace(const QString &workspace) const
|
||||||
|
{
|
||||||
|
return workspace == QLatin1String(Constants::FACTORY_DEFAULT_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DockManager::isDefaultWorkspace(const QString &workspace) const
|
||||||
|
{
|
||||||
|
return workspace == QLatin1String(Constants::DEFAULT_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DockManager::save()
|
||||||
|
{
|
||||||
|
if (isFactoryDefaultWorkspace(activeWorkspace()))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
emit aboutToSaveWorkspace();
|
||||||
|
|
||||||
|
bool result = write(saveState(), parentWidget());
|
||||||
|
if (result) {
|
||||||
|
d->m_workspaceDateTimes.insert(activeWorkspace(), QDateTime::currentDateTime());
|
||||||
|
} else {
|
||||||
|
QMessageBox::warning(parentWidget(),
|
||||||
|
tr("Cannot Save Session"),
|
||||||
|
tr("Could not save session to file %1")
|
||||||
|
.arg(workspaceNameToFileName(d->m_workspaceName)
|
||||||
|
.toUserOutput()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DockManager::activeWorkspace() const { return d->m_workspaceName; }
|
||||||
|
|
||||||
|
QString DockManager::lastWorkspace() const
|
||||||
|
{
|
||||||
|
QTC_ASSERT(d->m_settings, return {});
|
||||||
|
return d->m_settings->value(Constants::STARTUP_WORKSPACE_SETTINGS_KEY).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DockManager::autoRestorLastWorkspace() const
|
||||||
|
{
|
||||||
|
QTC_ASSERT(d->m_settings, return false);
|
||||||
|
return d->m_settings->value(Constants::AUTO_RESTORE_WORKSPACE_SETTINGS_KEY).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString m_dirName = QLatin1String("workspaces");
|
||||||
|
const QString m_fileExt = QLatin1String(".wrk"); // TODO
|
||||||
|
|
||||||
|
QStringList DockManager::workspaces()
|
||||||
|
{
|
||||||
|
if (d->m_workspaces.isEmpty() || d->m_workspaceListDirty) {
|
||||||
|
auto tmp = QSet<QString>::fromList(d->m_workspaces);
|
||||||
|
|
||||||
|
QTC_ASSERT(d->m_settings, return {});
|
||||||
|
QDir workspaceDir(QFileInfo(d->m_settings->fileName()).path() + QLatin1Char('/')
|
||||||
|
+ m_dirName);
|
||||||
|
QFileInfoList workspaceFiles
|
||||||
|
= workspaceDir.entryInfoList(QStringList() << QLatin1String("*.wrk"),
|
||||||
|
QDir::NoFilter,
|
||||||
|
QDir::Time); // TODO Choose different extension
|
||||||
|
for (const QFileInfo &fileInfo : workspaceFiles) {
|
||||||
|
QString filename = fileInfo.completeBaseName();
|
||||||
|
filename.replace("_", " ");
|
||||||
|
d->m_workspaceDateTimes.insert(filename, fileInfo.lastModified());
|
||||||
|
//if (name != QLatin1String(Constants::DEFAULT_NAME))
|
||||||
|
tmp.insert(filename);
|
||||||
|
}
|
||||||
|
//d->m_workspaces.prepend(QLatin1String(Constants::DEFAULT_NAME));
|
||||||
|
|
||||||
|
d->m_workspaceListDirty = false;
|
||||||
|
d->m_workspaces = tmp.toList();
|
||||||
|
}
|
||||||
|
return d->m_workspaces;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDateTime DockManager::workspaceDateTime(const QString &workspace) const
|
||||||
|
{
|
||||||
|
return d->m_workspaceDateTimes.value(workspace);
|
||||||
|
}
|
||||||
|
|
||||||
|
Utils::FilePath DockManager::workspaceNameToFileName(const QString &workspaceName) const
|
||||||
|
{
|
||||||
|
QTC_ASSERT(d->m_settings, return {});
|
||||||
|
QString workspaceNameCopy = workspaceName;
|
||||||
|
return Utils::FilePath::fromString(
|
||||||
|
QFileInfo(d->m_settings->fileName()).path() + QLatin1Char('/') + m_dirName
|
||||||
|
+ QLatin1Char('/') + workspaceNameCopy.replace(" ", "_") + QLatin1String(".wrk"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates \a workspace, but does not actually create the file.
|
||||||
|
*/
|
||||||
|
bool DockManager::createWorkspace(const QString &workspace)
|
||||||
|
{
|
||||||
|
if (workspaces().contains(workspace))
|
||||||
|
return false;
|
||||||
|
d->m_workspaces.insert(1, workspace);
|
||||||
|
d->m_workspaceDateTimes.insert(workspace, QDateTime::currentDateTime());
|
||||||
|
|
||||||
|
emit workspaceListChanged();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DockManager::openWorkspace(const QString &workspace)
|
||||||
|
{
|
||||||
|
// Do nothing if we have that workspace already loaded, exception if the
|
||||||
|
// workspace is the default virgin workspace we still want to be able to
|
||||||
|
// load the default workspace.
|
||||||
|
if (workspace == d->m_workspaceName) // && !isFactoryDefaultWorkspace(workspace))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!workspaces().contains(workspace))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check if the currently active workspace isn't empty and try to save it
|
||||||
|
if (!d->m_workspaceName.isEmpty()) {
|
||||||
|
// Allow everyone to set something in the workspace and before saving
|
||||||
|
emit aboutToUnloadWorkspace(d->m_workspaceName);
|
||||||
|
if (!save()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try loading the file
|
||||||
|
QByteArray data;
|
||||||
|
Utils::FilePath fileName = workspaceNameToFileName(workspace);
|
||||||
|
if (fileName.exists()) {
|
||||||
|
QFile file(fileName.toString());
|
||||||
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
|
QMessageBox::warning(parentWidget(),
|
||||||
|
tr("Cannot Restore Workspace"),
|
||||||
|
tr("Could not restore workspace %1")
|
||||||
|
.arg(fileName.toUserOutput()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
data = file.readAll();
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
emit openingWorkspace(workspace);
|
||||||
|
// If data was loaded from file try to restore its state
|
||||||
|
if (!data.isNull() && !restoreState(data)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
d->m_workspaceName = workspace;
|
||||||
|
emit workspaceLoaded(workspace);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Shows a dialog asking the user to confirm deleting the workspace \p workspace
|
||||||
|
*/
|
||||||
|
bool DockManager::confirmWorkspaceDelete(const QStringList &workspace)
|
||||||
|
{
|
||||||
|
const QString title = workspace.size() == 1 ? tr("Delete Workspace")
|
||||||
|
: tr("Delete Workspaces");
|
||||||
|
const QString question = workspace.size() == 1
|
||||||
|
? tr("Delete workspace %1?").arg(workspace.first())
|
||||||
|
: tr("Delete these workspaces?\n %1")
|
||||||
|
.arg(workspace.join("\n "));
|
||||||
|
return QMessageBox::question(parentWidget(),
|
||||||
|
title,
|
||||||
|
question,
|
||||||
|
QMessageBox::Yes | QMessageBox::No)
|
||||||
|
== QMessageBox::Yes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes \a workspace name from workspace list and the file from disk.
|
||||||
|
*/
|
||||||
|
bool DockManager::deleteWorkspace(const QString &workspace)
|
||||||
|
{
|
||||||
|
// Remove workspace from internal list
|
||||||
|
if (!d->m_workspaces.contains(workspace))
|
||||||
|
return false;
|
||||||
|
d->m_workspaces.removeOne(workspace);
|
||||||
|
|
||||||
|
emit workspacesRemoved();
|
||||||
|
emit workspaceListChanged();
|
||||||
|
|
||||||
|
// Remove corresponding workspace file
|
||||||
|
QFile fi(workspaceNameToFileName(workspace).toString());
|
||||||
|
if (fi.exists())
|
||||||
|
return fi.remove();
|
||||||
|
|
||||||
|
return false; // TODO If we allow temporary workspaces without writing them to file
|
||||||
|
// directly, this needs to be true otherwise in all those cases it will return false.
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockManager::deleteWorkspaces(const QStringList &workspaces)
|
||||||
|
{
|
||||||
|
for (const QString &workspace : workspaces)
|
||||||
|
deleteWorkspace(workspace);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DockManager::cloneWorkspace(const QString &original, const QString &clone)
|
||||||
|
{
|
||||||
|
if (!d->m_workspaces.contains(original))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
QFile fi(workspaceNameToFileName(original).toString());
|
||||||
|
// If the file does not exist, we can still clone
|
||||||
|
if (!fi.exists() || fi.copy(workspaceNameToFileName(clone).toString())) {
|
||||||
|
d->m_workspaces.insert(1, clone);
|
||||||
|
d->m_workspaceDateTimes
|
||||||
|
.insert(clone, workspaceNameToFileName(clone).toFileInfo().lastModified());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DockManager::renameWorkspace(const QString &original, const QString &newName)
|
||||||
|
{
|
||||||
|
if (!cloneWorkspace(original, newName))
|
||||||
|
return false;
|
||||||
|
if (original == activeWorkspace())
|
||||||
|
openWorkspace(newName);
|
||||||
|
return deleteWorkspace(original);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DockManager::write(const QByteArray &data, QString *errorString) const
|
||||||
|
{
|
||||||
|
Utils::FilePath fileName = workspaceNameToFileName(activeWorkspace());
|
||||||
|
|
||||||
|
QDir tmp;
|
||||||
|
tmp.mkpath(fileName.toFileInfo().path());
|
||||||
|
Utils::FileSaver fileSaver(fileName.toString(), QIODevice::Text);
|
||||||
|
if (!fileSaver.hasError()) {
|
||||||
|
fileSaver.write(data);
|
||||||
|
}
|
||||||
|
bool ok = fileSaver.finalize();
|
||||||
|
|
||||||
|
if (!ok && errorString) {
|
||||||
|
*errorString = fileSaver.errorString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef QT_GUI_LIB
|
||||||
|
bool DockManager::write(const QByteArray &data, QWidget *parent) const
|
||||||
|
{
|
||||||
|
QString errorString;
|
||||||
|
const bool success = write(data, &errorString);
|
||||||
|
if (!success)
|
||||||
|
QMessageBox::critical(parent,
|
||||||
|
QCoreApplication::translate("Utils::FileSaverBase", "File Error"),
|
||||||
|
errorString);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
#endif // QT_GUI_LIB
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
478
src/libs/advanceddockingsystem/dockmanager.h
Normal file
478
src/libs/advanceddockingsystem/dockmanager.h
Normal file
@@ -0,0 +1,478 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 Uwe Kindler
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ads_globals.h"
|
||||||
|
#include "dockcontainerwidget.h"
|
||||||
|
#include "dockwidget.h"
|
||||||
|
#include "floatingdockcontainer.h"
|
||||||
|
|
||||||
|
#include <utils/persistentsettings.h>
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QFlags>
|
||||||
|
#include <QList>
|
||||||
|
#include <QMap>
|
||||||
|
#include <QString>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QtGui/QIcon>
|
||||||
|
#include <qobjectdefs.h>
|
||||||
|
|
||||||
|
class QSettings;
|
||||||
|
class QMenu;
|
||||||
|
|
||||||
|
namespace ADS {
|
||||||
|
|
||||||
|
namespace Constants {
|
||||||
|
const char FACTORY_DEFAULT_NAME[] = "factorydefault";
|
||||||
|
const char DEFAULT_NAME[] = "default";
|
||||||
|
const char STARTUP_WORKSPACE_SETTINGS_KEY[] = "QML/Designer/StartupWorkspace";
|
||||||
|
const char AUTO_RESTORE_WORKSPACE_SETTINGS_KEY[] = "QML/Designer/AutoRestoreLastWorkspace";
|
||||||
|
} // namespace Constants
|
||||||
|
|
||||||
|
struct DockManagerPrivate;
|
||||||
|
class FloatingDockContainer;
|
||||||
|
struct FloatingDockContainerPrivate;
|
||||||
|
class DockComponentsFactory;
|
||||||
|
class DockContainerWidget;
|
||||||
|
class DockContainerWidgetPrivate;
|
||||||
|
class DockOverlay;
|
||||||
|
class DockAreaTabBar;
|
||||||
|
class DockWidgetTab;
|
||||||
|
struct DockWidgetTabPrivate;
|
||||||
|
struct DockAreaWidgetPrivate;
|
||||||
|
class IconProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The central dock manager that maintains the complete docking system.
|
||||||
|
* With the configuration flags you can globally control the functionality
|
||||||
|
* of the docking system. The dock manager uses an internal stylesheet to
|
||||||
|
* style its components like splitters, tabs and buttons. If you want to
|
||||||
|
* disable this stylesheet because your application uses its own,
|
||||||
|
* just call the function for settings the stylesheet with an empty
|
||||||
|
* string.
|
||||||
|
* \code
|
||||||
|
* dockManager->setStyleSheet("");
|
||||||
|
* \endcode
|
||||||
|
**/
|
||||||
|
class ADS_EXPORT DockManager : public DockContainerWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private:
|
||||||
|
DockManagerPrivate *d; ///< private data (pimpl)
|
||||||
|
friend struct DockManagerPrivate;
|
||||||
|
friend class FloatingDockContainer;
|
||||||
|
friend struct FloatingDockContainerPrivate;
|
||||||
|
friend class DockContainerWidget;
|
||||||
|
friend class DockContainerWidgetPrivate;
|
||||||
|
friend class DockAreaTabBar;
|
||||||
|
friend class DockWidgetTab;
|
||||||
|
friend struct DockAreaWidgetPrivate;
|
||||||
|
friend struct DockWidgetTabPrivate;
|
||||||
|
friend class FloatingDragPreview;
|
||||||
|
friend struct FloatingDragPreviewPrivate;
|
||||||
|
friend class DockAreaTitleBar;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* Registers the given floating widget in the internal list of
|
||||||
|
* floating widgets
|
||||||
|
*/
|
||||||
|
void registerFloatingWidget(FloatingDockContainer *floatingWidget);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the given floating widget from the list of registered floating
|
||||||
|
* widgets
|
||||||
|
*/
|
||||||
|
void removeFloatingWidget(FloatingDockContainer *floatingWidget);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the given dock container widget
|
||||||
|
*/
|
||||||
|
void registerDockContainer(DockContainerWidget *dockContainer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove dock container from the internal list of registered dock
|
||||||
|
* containers
|
||||||
|
*/
|
||||||
|
void removeDockContainer(DockContainerWidget *dockContainer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overlay for containers
|
||||||
|
*/
|
||||||
|
DockOverlay *containerOverlay() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overlay for dock areas
|
||||||
|
*/
|
||||||
|
DockOverlay *dockAreaOverlay() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the floating widgets that has been created floating
|
||||||
|
*/
|
||||||
|
virtual void showEvent(QShowEvent *event) override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using Super = DockContainerWidget;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These global configuration flags configure some global dock manager
|
||||||
|
* settings.
|
||||||
|
*/
|
||||||
|
enum eConfigFlag {
|
||||||
|
ActiveTabHasCloseButton
|
||||||
|
= 0x0001, //!< If this flag is set, the active tab in a tab area has a close button
|
||||||
|
DockAreaHasCloseButton
|
||||||
|
= 0x0002, //!< If the flag is set each dock area has a close button
|
||||||
|
DockAreaCloseButtonClosesTab
|
||||||
|
= 0x0004, //!< If the flag is set, the dock area close button closes the active tab, if not set, it closes the complete dock area
|
||||||
|
OpaqueSplitterResize
|
||||||
|
= 0x0008, //!< See QSplitter::setOpaqueResize() documentation
|
||||||
|
XmlAutoFormattingEnabled
|
||||||
|
= 0x0010, //!< If enabled, the XML writer automatically adds line-breaks and indentation to empty sections between elements (ignorable whitespace).
|
||||||
|
XmlCompressionEnabled
|
||||||
|
= 0x0020, //!< If enabled, the XML output will be compressed and is not human readable anymore
|
||||||
|
TabCloseButtonIsToolButton
|
||||||
|
= 0x0040, //! If enabled the tab close buttons will be QToolButtons instead of QPushButtons - disabled by default
|
||||||
|
AllTabsHaveCloseButton
|
||||||
|
= 0x0080, //!< if this flag is set, then all tabs that are closable show a close button
|
||||||
|
RetainTabSizeWhenCloseButtonHidden
|
||||||
|
= 0x0100, //!< if this flag is set, the space for the close button is reserved even if the close button is not visible
|
||||||
|
OpaqueUndocking
|
||||||
|
= 0x0200, ///< If enabled, the widgets are immediately undocked into floating widgets, if disabled, only a draw preview is undocked and the real undocking is deferred until the mouse is released
|
||||||
|
DragPreviewIsDynamic
|
||||||
|
= 0x0400, ///< If opaque undocking is disabled, this flag defines the behavior of the drag preview window, if this flag is enabled, the preview will be adjusted dynamically to the drop area
|
||||||
|
DragPreviewShowsContentPixmap
|
||||||
|
= 0x0800, ///< If opaque undocking is disabled, the created drag preview window shows a copy of the content of the dock widget / dock are that is dragged
|
||||||
|
DragPreviewHasWindowFrame
|
||||||
|
= 0x1000, ///< If opaque undocking is disabled, then this flag configures if the drag preview is frameless or looks like a real window
|
||||||
|
AlwaysShowTabs
|
||||||
|
= 0x2000, ///< If this option is enabled, the tab of a dock widget is always displayed - even if it is the only visible dock widget in a floating widget.
|
||||||
|
DockAreaHasUndockButton
|
||||||
|
= 0x4000, //!< If the flag is set each dock area has an undock button
|
||||||
|
DockAreaHasTabsMenuButton
|
||||||
|
= 0x8000, //!< If the flag is set each dock area has a tabs menu button
|
||||||
|
DockAreaHideDisabledButtons
|
||||||
|
= 0x10000, //!< If the flag is set disabled dock area buttons will not appear on the tollbar at all (enabling them will bring them back)
|
||||||
|
DockAreaDynamicTabsMenuButtonVisibility
|
||||||
|
= 0x20000, //!< If the flag is set dock area will disable a tabs menu button when there is only one tab in the area
|
||||||
|
FloatingContainerHasWidgetTitle
|
||||||
|
= 0x40000,
|
||||||
|
FloatingContainerHasWidgetIcon
|
||||||
|
= 0x80000,
|
||||||
|
|
||||||
|
DefaultDockAreaButtons = DockAreaHasCloseButton
|
||||||
|
| DockAreaHasUndockButton
|
||||||
|
| DockAreaHasTabsMenuButton,///< default configuration of dock area title bar buttons
|
||||||
|
|
||||||
|
DefaultBaseConfig = DefaultDockAreaButtons
|
||||||
|
| ActiveTabHasCloseButton
|
||||||
|
| XmlCompressionEnabled
|
||||||
|
| FloatingContainerHasWidgetTitle,///< default base configuration settings
|
||||||
|
|
||||||
|
DefaultOpaqueConfig = DefaultBaseConfig
|
||||||
|
| OpaqueSplitterResize
|
||||||
|
| OpaqueUndocking, ///< the default configuration with opaque operations - this may cause issues if ActiveX or Qt 3D windows are involved
|
||||||
|
|
||||||
|
DefaultNonOpaqueConfig = DefaultBaseConfig
|
||||||
|
| DragPreviewShowsContentPixmap, ///< the default configuration for non opaque operations
|
||||||
|
|
||||||
|
NonOpaqueWithWindowFrame = DefaultNonOpaqueConfig
|
||||||
|
| DragPreviewHasWindowFrame ///< the default configuration for non opaque operations that show a real window with frame
|
||||||
|
};
|
||||||
|
Q_DECLARE_FLAGS(ConfigFlags, eConfigFlag)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default Constructor.
|
||||||
|
* If the given parent is a QMainWindow, the dock manager sets itself as the
|
||||||
|
* central widget.
|
||||||
|
* Before you create any dock widgets, you should properly setup the
|
||||||
|
* configuration flags via setConfigFlags().
|
||||||
|
*/
|
||||||
|
DockManager(QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual Destructor
|
||||||
|
*/
|
||||||
|
virtual ~DockManager() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function returns the global configuration flags
|
||||||
|
*/
|
||||||
|
static ConfigFlags configFlags();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the global configuration flags for the whole docking system.
|
||||||
|
* Call this function before you create your first dock widget.
|
||||||
|
*/
|
||||||
|
static void setConfigFlags(const ConfigFlags flags);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a certain config flag
|
||||||
|
*/
|
||||||
|
static void setConfigFlag(eConfigFlag flag, bool on = true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the given config flag is set
|
||||||
|
*/
|
||||||
|
static bool testConfigFlag(eConfigFlag flag);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the global icon provider.
|
||||||
|
* The icon provider enables the use of custom icons in case using
|
||||||
|
* styleheets for icons is not an option.
|
||||||
|
*/
|
||||||
|
static IconProvider &iconProvider();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The distance the user needs to move the mouse with the left button
|
||||||
|
* hold down before a dock widget start floating
|
||||||
|
*/
|
||||||
|
static int startDragDistance();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the QtCreator settings.
|
||||||
|
*/
|
||||||
|
void setSettings(QSettings *settings);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds dockwidget into the given area.
|
||||||
|
* If DockAreaWidget is not null, then the area parameter indicates the area
|
||||||
|
* into the DockAreaWidget. If DockAreaWidget is null, the Dockwidget will
|
||||||
|
* be dropped into the container. If you would like to add a dock widget
|
||||||
|
* tabified, then you need to add it to an existing dock area object
|
||||||
|
* into the CenterDockWidgetArea. The following code shows this:
|
||||||
|
* \code
|
||||||
|
* DockManager->addDockWidget(ads::CenterDockWidgetArea, NewDockWidget,
|
||||||
|
* ExisitingDockArea);
|
||||||
|
* \endcode
|
||||||
|
* \return Returns the dock area widget that contains the new DockWidget
|
||||||
|
*/
|
||||||
|
DockAreaWidget *addDockWidget(DockWidgetArea area,
|
||||||
|
DockWidget *dockWidget,
|
||||||
|
DockAreaWidget *dockAreaWidget = nullptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function will add the given Dockwidget to the given dock area as
|
||||||
|
* a new tab.
|
||||||
|
* If no dock area widget exists for the given area identifier, a new
|
||||||
|
* dock area widget is created.
|
||||||
|
*/
|
||||||
|
DockAreaWidget *addDockWidgetTab(DockWidgetArea area, DockWidget *dockWidget);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function will add the given Dockwidget to the given DockAreaWidget
|
||||||
|
* as a new tab.
|
||||||
|
*/
|
||||||
|
DockAreaWidget *addDockWidgetTabToArea(DockWidget *dockWidget, DockAreaWidget *dockAreaWidget);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the given DockWidget floating and returns the created
|
||||||
|
* CFloatingDockContainer instance.
|
||||||
|
*/
|
||||||
|
FloatingDockContainer *addDockWidgetFloating(DockWidget *dockWidget);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches for a registered doc widget with the given ObjectName
|
||||||
|
* \return Return the found dock widget or nullptr if a dock widget with the
|
||||||
|
* given name is not registered
|
||||||
|
*/
|
||||||
|
DockWidget *findDockWidget(const QString &objectName) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the given Dock from the dock manager
|
||||||
|
*/
|
||||||
|
void removeDockWidget(DockWidget *dockWidget);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function returns a readable reference to the internal dock
|
||||||
|
* widgets map so that it is possible to iterate over all dock widgets
|
||||||
|
*/
|
||||||
|
QMap<QString, DockWidget *> dockWidgetsMap() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of all active and visible dock containers
|
||||||
|
* Dock containers are the main dock manager and all floating widgets
|
||||||
|
*/
|
||||||
|
const QList<DockContainerWidget *> dockContainers() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of all floating widgets
|
||||||
|
*/
|
||||||
|
const QList<FloatingDockContainer *> floatingWidgets() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function always return 0 because the main window is always behind
|
||||||
|
* any floating widget
|
||||||
|
*/
|
||||||
|
virtual unsigned int zOrderIndex() const override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the current state of the dockmanger and all its dock widgets
|
||||||
|
* into the returned QByteArray.
|
||||||
|
* The XmlMode enables / disables the auto formatting for the XmlStreamWriter.
|
||||||
|
* If auto formatting is enabled, the output is intended and line wrapped.
|
||||||
|
* The XmlMode XmlAutoFormattingDisabled is better if you would like to have
|
||||||
|
* a more compact XML output - i.e. for storage in ini files.
|
||||||
|
*/
|
||||||
|
QByteArray saveState(int version = Version1) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restores the state of this dockmanagers dockwidgets.
|
||||||
|
* The version number is compared with that stored in state. If they do
|
||||||
|
* not match, the dockmanager's state is left unchanged, and this function
|
||||||
|
* returns false; otherwise, the state is restored, and this function
|
||||||
|
* returns true.
|
||||||
|
*/
|
||||||
|
bool restoreState(const QByteArray &state, int version = Version1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function returns true between the restoringState() and
|
||||||
|
* stateRestored() signals.
|
||||||
|
*/
|
||||||
|
bool isRestoringState() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
/**
|
||||||
|
* This signal is emitted if the list of perspectives changed
|
||||||
|
*/
|
||||||
|
void workspaceListChanged();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This signal is emitted if perspectives have been removed
|
||||||
|
*/
|
||||||
|
void workspacesRemoved();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This signal is emitted, if the restore function is called, just before
|
||||||
|
* the dock manager starts restoring the state.
|
||||||
|
* If this function is called, nothing has changed yet
|
||||||
|
*/
|
||||||
|
void restoringState();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This signal is emitted if the state changed in restoreState.
|
||||||
|
* The signal is emitted if the restoreState() function is called or
|
||||||
|
* if the openWorkspace() function is called
|
||||||
|
*/
|
||||||
|
void stateRestored();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This signal is emitted, if the dock manager starts opening a
|
||||||
|
* perspective.
|
||||||
|
* Opening a perspective may take more than a second if there are
|
||||||
|
* many complex widgets. The application may use this signal
|
||||||
|
* to show some progress indicator or to change the mouse cursor
|
||||||
|
* into a busy cursor.
|
||||||
|
*/
|
||||||
|
void openingWorkspace(const QString &workspaceName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This signal is emitted if the dock manager finished opening a
|
||||||
|
* perspective
|
||||||
|
*/
|
||||||
|
void workspaceOpened(const QString &workspaceName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This signal is emitted, if a new floating widget has been created.
|
||||||
|
* An application can use this signal to e.g. subscribe to events of
|
||||||
|
* the newly created window.
|
||||||
|
*/
|
||||||
|
void floatingWidgetCreated(FloatingDockContainer *floatingWidget);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This signal is emitted, if a new DockArea has been created.
|
||||||
|
* An application can use this signal to set custom icons or custom
|
||||||
|
* tooltips for the DockArea buttons.
|
||||||
|
*/
|
||||||
|
void dockAreaCreated(DockAreaWidget *dockArea);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This signal is emitted just before the given dock widget is removed
|
||||||
|
* from the
|
||||||
|
*/
|
||||||
|
void dockWidgetAboutToBeRemoved(DockWidget *dockWidget);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This signal is emitted if a dock widget has been removed with the remove
|
||||||
|
* removeDockWidget() function.
|
||||||
|
* If this signal is emitted, the dock widget has been removed from the
|
||||||
|
* docking system but it is not deleted yet.
|
||||||
|
*/
|
||||||
|
void dockWidgetRemoved(DockWidget *dockWidget);
|
||||||
|
|
||||||
|
public:
|
||||||
|
void showWorkspaceMananger();
|
||||||
|
|
||||||
|
// higher level workspace management
|
||||||
|
QString activeWorkspace() const;
|
||||||
|
QString lastWorkspace() const;
|
||||||
|
bool autoRestorLastWorkspace() const;
|
||||||
|
QStringList workspaces();
|
||||||
|
QDateTime workspaceDateTime(const QString &workspace) const;
|
||||||
|
Utils::FilePath workspaceNameToFileName(const QString &workspaceName) const;
|
||||||
|
|
||||||
|
bool createWorkspace(const QString &workspace);
|
||||||
|
|
||||||
|
bool openWorkspace(const QString &workspace);
|
||||||
|
|
||||||
|
bool confirmWorkspaceDelete(const QStringList &workspaces);
|
||||||
|
bool deleteWorkspace(const QString &workspace);
|
||||||
|
void deleteWorkspaces(const QStringList &workspaces);
|
||||||
|
|
||||||
|
bool cloneWorkspace(const QString &original, const QString &clone);
|
||||||
|
bool renameWorkspace(const QString &original, const QString &newName);
|
||||||
|
|
||||||
|
bool save();
|
||||||
|
|
||||||
|
bool isFactoryDefaultWorkspace(const QString &workspace) const;
|
||||||
|
bool isDefaultWorkspace(const QString &workspace) const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void aboutToUnloadWorkspace(QString workspaceName);
|
||||||
|
void aboutToLoadWorkspace(QString workspaceName);
|
||||||
|
void workspaceLoaded(QString workspaceName);
|
||||||
|
void aboutToSaveWorkspace();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool write(const QByteArray &data, QString *errorString) const;
|
||||||
|
#ifdef QT_GUI_LIB
|
||||||
|
bool write(const QByteArray &data, QWidget *parent) const;
|
||||||
|
#endif
|
||||||
|
}; // class DockManager
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
773
src/libs/advanceddockingsystem/dockoverlay.cpp
Normal file
773
src/libs/advanceddockingsystem/dockoverlay.cpp
Normal file
@@ -0,0 +1,773 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 Uwe Kindler
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "dockoverlay.h"
|
||||||
|
|
||||||
|
#include "dockareawidget.h"
|
||||||
|
|
||||||
|
#include <utils/hostosinfo.h>
|
||||||
|
|
||||||
|
#include <QCursor>
|
||||||
|
#include <QGridLayout>
|
||||||
|
#include <QIcon>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QMap>
|
||||||
|
#include <QMoveEvent>
|
||||||
|
#include <QPaintEvent>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QPointer>
|
||||||
|
#include <QResizeEvent>
|
||||||
|
#include <QWindow>
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace ADS {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private data class of DockOverlay
|
||||||
|
*/
|
||||||
|
struct DockOverlayPrivate
|
||||||
|
{
|
||||||
|
DockOverlay *q;
|
||||||
|
DockWidgetAreas m_allowedAreas = InvalidDockWidgetArea;
|
||||||
|
DockOverlayCross *m_cross;
|
||||||
|
QPointer<QWidget> m_targetWidget;
|
||||||
|
DockWidgetArea m_lastLocation = InvalidDockWidgetArea;
|
||||||
|
bool m_dropPreviewEnabled = true;
|
||||||
|
DockOverlay::eMode m_mode = DockOverlay::ModeDockAreaOverlay;
|
||||||
|
QRect m_dropAreaRect;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private data constructor
|
||||||
|
*/
|
||||||
|
DockOverlayPrivate(DockOverlay *parent)
|
||||||
|
: q(parent)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private data of DockOverlayCross class
|
||||||
|
*/
|
||||||
|
struct DockOverlayCrossPrivate
|
||||||
|
{
|
||||||
|
DockOverlayCross *q;
|
||||||
|
DockOverlay::eMode m_mode = DockOverlay::ModeDockAreaOverlay;
|
||||||
|
DockOverlay *m_dockOverlay;
|
||||||
|
QHash<DockWidgetArea, QWidget *> m_dropIndicatorWidgets;
|
||||||
|
QGridLayout *m_gridLayout;
|
||||||
|
QColor m_iconColors[5];
|
||||||
|
bool m_updateRequired = false;
|
||||||
|
double m_lastDevicePixelRatio = 0.1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private data constructor
|
||||||
|
*/
|
||||||
|
DockOverlayCrossPrivate(DockOverlayCross *parent)
|
||||||
|
: q(parent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param area
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
QPoint areaGridPosition(const DockWidgetArea area);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Palette based default icon colors
|
||||||
|
*/
|
||||||
|
QColor defaultIconColor(DockOverlayCross::eIconColor colorIndex)
|
||||||
|
{
|
||||||
|
QPalette palette = q->palette();
|
||||||
|
switch (colorIndex) {
|
||||||
|
case DockOverlayCross::FrameColor:
|
||||||
|
return palette.color(QPalette::Active, QPalette::Highlight);
|
||||||
|
case DockOverlayCross::WindowBackgroundColor:
|
||||||
|
return palette.color(QPalette::Active, QPalette::Base);
|
||||||
|
case DockOverlayCross::OverlayColor: {
|
||||||
|
QColor color = palette.color(QPalette::Active, QPalette::Highlight);
|
||||||
|
color.setAlpha(64);
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
case DockOverlayCross::ArrowColor:
|
||||||
|
return palette.color(QPalette::Active, QPalette::Base);
|
||||||
|
case DockOverlayCross::ShadowColor:
|
||||||
|
return QColor(0, 0, 0, 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
return QColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stylehseet based icon colors
|
||||||
|
*/
|
||||||
|
QColor iconColor(DockOverlayCross::eIconColor colorIndex)
|
||||||
|
{
|
||||||
|
QColor color = m_iconColors[colorIndex];
|
||||||
|
if (!color.isValid()) {
|
||||||
|
color = defaultIconColor(colorIndex);
|
||||||
|
m_iconColors[colorIndex] = color;
|
||||||
|
}
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function that returns the drop indicator width depending on the
|
||||||
|
* operating system
|
||||||
|
*/
|
||||||
|
qreal dropIndicatiorWidth(QLabel *label) const
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
Q_UNUSED(label)
|
||||||
|
return 40;
|
||||||
|
#else
|
||||||
|
return static_cast<qreal>(label->fontMetrics().height()) * 3.f;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget *createDropIndicatorWidget(DockWidgetArea dockWidgetArea, DockOverlay::eMode mode)
|
||||||
|
{
|
||||||
|
QLabel *label = new QLabel();
|
||||||
|
label->setObjectName("DockWidgetAreaLabel");
|
||||||
|
|
||||||
|
const qreal metric = dropIndicatiorWidth(label);
|
||||||
|
const QSizeF size(metric, metric);
|
||||||
|
|
||||||
|
label->setPixmap(createHighDpiDropIndicatorPixmap(size, dockWidgetArea, mode));
|
||||||
|
label->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
|
||||||
|
label->setAttribute(Qt::WA_TranslucentBackground);
|
||||||
|
label->setProperty("dockWidgetArea", dockWidgetArea);
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateDropIndicatorIcon(QWidget *dropIndicatorWidget)
|
||||||
|
{
|
||||||
|
QLabel *label = qobject_cast<QLabel *>(dropIndicatorWidget);
|
||||||
|
const qreal metric = dropIndicatiorWidth(label);
|
||||||
|
const QSizeF size(metric, metric);
|
||||||
|
|
||||||
|
int area = label->property("dockWidgetArea").toInt();
|
||||||
|
label->setPixmap(createHighDpiDropIndicatorPixmap(size,
|
||||||
|
static_cast<DockWidgetArea>(area),
|
||||||
|
m_mode)); // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap createHighDpiDropIndicatorPixmap(const QSizeF &size,
|
||||||
|
DockWidgetArea dockWidgetArea,
|
||||||
|
DockOverlay::eMode mode)
|
||||||
|
{
|
||||||
|
QColor borderColor = iconColor(DockOverlayCross::FrameColor);
|
||||||
|
QColor backgroundColor = iconColor(DockOverlayCross::WindowBackgroundColor);
|
||||||
|
double devicePixelRatio = q->window()->devicePixelRatioF();
|
||||||
|
QSizeF pixmapSize = size * devicePixelRatio;
|
||||||
|
QPixmap pixmap(pixmapSize.toSize());
|
||||||
|
pixmap.fill(QColor(0, 0, 0, 0));
|
||||||
|
|
||||||
|
QPainter painter(&pixmap);
|
||||||
|
QPen pen = painter.pen();
|
||||||
|
QRectF shadowRect(pixmap.rect());
|
||||||
|
QRectF baseRect;
|
||||||
|
baseRect.setSize(shadowRect.size() * 0.7);
|
||||||
|
baseRect.moveCenter(shadowRect.center());
|
||||||
|
|
||||||
|
// Fill
|
||||||
|
QColor shadowColor = iconColor(DockOverlayCross::ShadowColor);
|
||||||
|
if (shadowColor.alpha() == 255) {
|
||||||
|
shadowColor.setAlpha(64);
|
||||||
|
}
|
||||||
|
painter.fillRect(shadowRect, shadowColor);
|
||||||
|
|
||||||
|
// Drop area rect.
|
||||||
|
painter.save();
|
||||||
|
QRectF areaRect;
|
||||||
|
QLineF areaLine;
|
||||||
|
QRectF nonAreaRect;
|
||||||
|
switch (dockWidgetArea) {
|
||||||
|
case TopDockWidgetArea:
|
||||||
|
areaRect = QRectF(baseRect.x(), baseRect.y(), baseRect.width(), baseRect.height() * 0.5);
|
||||||
|
nonAreaRect = QRectF(baseRect.x(),
|
||||||
|
shadowRect.height() * 0.5,
|
||||||
|
baseRect.width(),
|
||||||
|
baseRect.height() * 0.5);
|
||||||
|
areaLine = QLineF(areaRect.bottomLeft(), areaRect.bottomRight());
|
||||||
|
break;
|
||||||
|
case RightDockWidgetArea:
|
||||||
|
areaRect = QRectF(shadowRect.width() * 0.5,
|
||||||
|
baseRect.y(),
|
||||||
|
baseRect.width() * 0.5,
|
||||||
|
baseRect.height());
|
||||||
|
nonAreaRect = QRectF(baseRect.x(),
|
||||||
|
baseRect.y(),
|
||||||
|
baseRect.width() * 0.5,
|
||||||
|
baseRect.height());
|
||||||
|
areaLine = QLineF(areaRect.topLeft(), areaRect.bottomLeft());
|
||||||
|
break;
|
||||||
|
case BottomDockWidgetArea:
|
||||||
|
areaRect = QRectF(baseRect.x(),
|
||||||
|
shadowRect.height() * 0.5,
|
||||||
|
baseRect.width(),
|
||||||
|
baseRect.height() * 0.5);
|
||||||
|
nonAreaRect = QRectF(baseRect.x(),
|
||||||
|
baseRect.y(),
|
||||||
|
baseRect.width(),
|
||||||
|
baseRect.height() * 0.5);
|
||||||
|
areaLine = QLineF(areaRect.topLeft(), areaRect.topRight());
|
||||||
|
break;
|
||||||
|
case LeftDockWidgetArea:
|
||||||
|
areaRect = QRectF(baseRect.x(), baseRect.y(), baseRect.width() * 0.5, baseRect.height());
|
||||||
|
nonAreaRect = QRectF(shadowRect.width() * 0.5,
|
||||||
|
baseRect.y(),
|
||||||
|
baseRect.width() * 0.5,
|
||||||
|
baseRect.height());
|
||||||
|
areaLine = QLineF(areaRect.topRight(), areaRect.bottomRight());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSizeF baseSize = baseRect.size();
|
||||||
|
if (DockOverlay::ModeContainerOverlay == mode && dockWidgetArea != CenterDockWidgetArea) {
|
||||||
|
baseRect = areaRect;
|
||||||
|
}
|
||||||
|
|
||||||
|
painter.fillRect(baseRect, backgroundColor);
|
||||||
|
if (areaRect.isValid()) {
|
||||||
|
pen = painter.pen();
|
||||||
|
pen.setColor(borderColor);
|
||||||
|
QColor color = iconColor(DockOverlayCross::OverlayColor);
|
||||||
|
if (color.alpha() == 255) {
|
||||||
|
color.setAlpha(64);
|
||||||
|
}
|
||||||
|
painter.setBrush(color);
|
||||||
|
painter.setPen(Qt::NoPen);
|
||||||
|
painter.drawRect(areaRect);
|
||||||
|
|
||||||
|
pen = painter.pen();
|
||||||
|
pen.setWidth(1);
|
||||||
|
pen.setColor(borderColor);
|
||||||
|
pen.setStyle(Qt::DashLine);
|
||||||
|
painter.setPen(pen);
|
||||||
|
painter.drawLine(areaLine);
|
||||||
|
}
|
||||||
|
painter.restore();
|
||||||
|
|
||||||
|
painter.save();
|
||||||
|
// Draw outer border
|
||||||
|
pen = painter.pen();
|
||||||
|
pen.setColor(borderColor);
|
||||||
|
pen.setWidth(1);
|
||||||
|
painter.setBrush(Qt::NoBrush);
|
||||||
|
painter.setPen(pen);
|
||||||
|
painter.drawRect(baseRect);
|
||||||
|
|
||||||
|
// draw window title bar
|
||||||
|
painter.setBrush(borderColor);
|
||||||
|
QRectF frameRect(baseRect.topLeft(), QSizeF(baseRect.width(), baseSize.height() / 10));
|
||||||
|
painter.drawRect(frameRect);
|
||||||
|
painter.restore();
|
||||||
|
|
||||||
|
// Draw arrow for outer container drop indicators
|
||||||
|
if (DockOverlay::ModeContainerOverlay == mode && dockWidgetArea != CenterDockWidgetArea) {
|
||||||
|
QRectF arrowRect;
|
||||||
|
arrowRect.setSize(baseSize);
|
||||||
|
arrowRect.setWidth(arrowRect.width() / 4.6);
|
||||||
|
arrowRect.setHeight(arrowRect.height() / 2);
|
||||||
|
arrowRect.moveCenter(QPointF(0, 0));
|
||||||
|
QPolygonF arrow;
|
||||||
|
arrow << arrowRect.topLeft() << QPointF(arrowRect.right(), arrowRect.center().y())
|
||||||
|
<< arrowRect.bottomLeft();
|
||||||
|
painter.setPen(Qt::NoPen);
|
||||||
|
painter.setBrush(iconColor(DockOverlayCross::ArrowColor));
|
||||||
|
painter.setRenderHint(QPainter::Antialiasing, true);
|
||||||
|
painter.translate(nonAreaRect.center().x(), nonAreaRect.center().y());
|
||||||
|
|
||||||
|
switch (dockWidgetArea) {
|
||||||
|
case TopDockWidgetArea:
|
||||||
|
painter.rotate(-90);
|
||||||
|
break;
|
||||||
|
case RightDockWidgetArea:
|
||||||
|
break;
|
||||||
|
case BottomDockWidgetArea:
|
||||||
|
painter.rotate(90);
|
||||||
|
break;
|
||||||
|
case LeftDockWidgetArea:
|
||||||
|
painter.rotate(180);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
painter.drawPolygon(arrow);
|
||||||
|
}
|
||||||
|
|
||||||
|
pixmap.setDevicePixelRatio(devicePixelRatio);
|
||||||
|
return pixmap;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
DockOverlay::DockOverlay(QWidget *parent, eMode mode)
|
||||||
|
: QFrame(parent)
|
||||||
|
, d(new DockOverlayPrivate(this))
|
||||||
|
{
|
||||||
|
d->m_mode = mode;
|
||||||
|
d->m_cross = new DockOverlayCross(this);
|
||||||
|
|
||||||
|
if (Utils::HostOsInfo::isLinuxHost())
|
||||||
|
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint
|
||||||
|
| Qt::X11BypassWindowManagerHint);
|
||||||
|
else
|
||||||
|
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
|
||||||
|
|
||||||
|
setWindowOpacity(1);
|
||||||
|
setWindowTitle("DockOverlay");
|
||||||
|
setAttribute(Qt::WA_NoSystemBackground);
|
||||||
|
setAttribute(Qt::WA_TranslucentBackground);
|
||||||
|
|
||||||
|
d->m_cross->setVisible(false);
|
||||||
|
setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
DockOverlay::~DockOverlay()
|
||||||
|
{
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockOverlay::setAllowedAreas(DockWidgetAreas areas)
|
||||||
|
{
|
||||||
|
if (areas == d->m_allowedAreas)
|
||||||
|
return;
|
||||||
|
d->m_allowedAreas = areas;
|
||||||
|
d->m_cross->reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
DockWidgetAreas DockOverlay::allowedAreas() const
|
||||||
|
{
|
||||||
|
return d->m_allowedAreas;
|
||||||
|
}
|
||||||
|
|
||||||
|
DockWidgetArea DockOverlay::dropAreaUnderCursor() const
|
||||||
|
{
|
||||||
|
DockWidgetArea result = d->m_cross->cursorLocation();
|
||||||
|
if (result != InvalidDockWidgetArea) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DockAreaWidget *dockArea = qobject_cast<DockAreaWidget *>(d->m_targetWidget.data());
|
||||||
|
if (!dockArea) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dockArea->allowedAreas().testFlag(CenterDockWidgetArea)
|
||||||
|
&& dockArea->titleBarGeometry().contains(dockArea->mapFromGlobal(QCursor::pos()))) {
|
||||||
|
return CenterDockWidgetArea;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DockWidgetArea DockOverlay::showOverlay(QWidget *target)
|
||||||
|
{
|
||||||
|
if (d->m_targetWidget == target) {
|
||||||
|
// Hint: We could update geometry of overlay here.
|
||||||
|
DockWidgetArea dockWidgetArea = dropAreaUnderCursor();
|
||||||
|
if (dockWidgetArea != d->m_lastLocation) {
|
||||||
|
repaint();
|
||||||
|
d->m_lastLocation = dockWidgetArea;
|
||||||
|
}
|
||||||
|
return dockWidgetArea;
|
||||||
|
}
|
||||||
|
|
||||||
|
d->m_targetWidget = target;
|
||||||
|
d->m_lastLocation = InvalidDockWidgetArea;
|
||||||
|
|
||||||
|
// Move it over the target.
|
||||||
|
resize(target->size());
|
||||||
|
QPoint topLeft = target->mapToGlobal(target->rect().topLeft());
|
||||||
|
move(topLeft);
|
||||||
|
show();
|
||||||
|
d->m_cross->updatePosition();
|
||||||
|
d->m_cross->updateOverlayIcons();
|
||||||
|
return dropAreaUnderCursor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockOverlay::hideOverlay()
|
||||||
|
{
|
||||||
|
hide();
|
||||||
|
d->m_targetWidget.clear();
|
||||||
|
d->m_lastLocation = InvalidDockWidgetArea;
|
||||||
|
d->m_dropAreaRect = QRect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockOverlay::enableDropPreview(bool enable)
|
||||||
|
{
|
||||||
|
d->m_dropPreviewEnabled = enable;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DockOverlay::dropPreviewEnabled() const
|
||||||
|
{
|
||||||
|
return d->m_dropPreviewEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockOverlay::paintEvent(QPaintEvent *event)
|
||||||
|
{
|
||||||
|
Q_UNUSED(event)
|
||||||
|
// Draw rect based on location
|
||||||
|
if (!d->m_dropPreviewEnabled) {
|
||||||
|
d->m_dropAreaRect = QRect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect rectangle = rect();
|
||||||
|
const DockWidgetArea dockWidgetArea = dropAreaUnderCursor();
|
||||||
|
double factor = (DockOverlay::ModeContainerOverlay == d->m_mode) ? 3 : 2;
|
||||||
|
|
||||||
|
switch (dockWidgetArea) {
|
||||||
|
case TopDockWidgetArea:
|
||||||
|
rectangle.setHeight(static_cast<int>(rectangle.height() / factor));
|
||||||
|
break;
|
||||||
|
case RightDockWidgetArea:
|
||||||
|
rectangle.setX(static_cast<int>(rectangle.width() * (1 - 1 / factor)));
|
||||||
|
break;
|
||||||
|
case BottomDockWidgetArea:
|
||||||
|
rectangle.setY(static_cast<int>(rectangle.height() * (1 - 1 / factor)));
|
||||||
|
break;
|
||||||
|
case LeftDockWidgetArea:
|
||||||
|
rectangle.setWidth(static_cast<int>(rectangle.width() / factor));
|
||||||
|
break;
|
||||||
|
case CenterDockWidgetArea:
|
||||||
|
rectangle = rect();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QPainter painter(this);
|
||||||
|
QColor color = palette().color(QPalette::Active, QPalette::Highlight);
|
||||||
|
QPen pen = painter.pen();
|
||||||
|
pen.setColor(color.darker(120));
|
||||||
|
pen.setStyle(Qt::SolidLine);
|
||||||
|
pen.setWidth(1);
|
||||||
|
pen.setCosmetic(true);
|
||||||
|
painter.setPen(pen);
|
||||||
|
color = color.lighter(130);
|
||||||
|
color.setAlpha(64);
|
||||||
|
painter.setBrush(color);
|
||||||
|
painter.drawRect(rectangle.adjusted(0, 0, -1, -1));
|
||||||
|
d->m_dropAreaRect = rectangle;
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect DockOverlay::dropOverlayRect() const
|
||||||
|
{
|
||||||
|
return d->m_dropAreaRect;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockOverlay::showEvent(QShowEvent *event)
|
||||||
|
{
|
||||||
|
d->m_cross->show();
|
||||||
|
QFrame::showEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockOverlay::hideEvent(QHideEvent *event)
|
||||||
|
{
|
||||||
|
d->m_cross->hide();
|
||||||
|
QFrame::hideEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DockOverlay::event(QEvent *event)
|
||||||
|
{
|
||||||
|
bool result = Super::event(event);
|
||||||
|
if (event->type() == QEvent::Polish) {
|
||||||
|
d->m_cross->setupOverlayCross(d->m_mode);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int areaAlignment(const DockWidgetArea area)
|
||||||
|
{
|
||||||
|
switch (area) {
|
||||||
|
case TopDockWidgetArea:
|
||||||
|
return Qt::AlignHCenter | Qt::AlignBottom;
|
||||||
|
case RightDockWidgetArea:
|
||||||
|
return Qt::AlignLeft | Qt::AlignVCenter;
|
||||||
|
case BottomDockWidgetArea:
|
||||||
|
return Qt::AlignHCenter | Qt::AlignTop;
|
||||||
|
case LeftDockWidgetArea:
|
||||||
|
return Qt::AlignRight | Qt::AlignVCenter;
|
||||||
|
case CenterDockWidgetArea:
|
||||||
|
return Qt::AlignCenter;
|
||||||
|
default:
|
||||||
|
return Qt::AlignCenter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DockOverlayCrossPrivate
|
||||||
|
QPoint DockOverlayCrossPrivate::areaGridPosition(const DockWidgetArea area)
|
||||||
|
{
|
||||||
|
if (DockOverlay::ModeDockAreaOverlay == m_mode) {
|
||||||
|
switch (area) {
|
||||||
|
case TopDockWidgetArea:
|
||||||
|
return QPoint(1, 2);
|
||||||
|
case RightDockWidgetArea:
|
||||||
|
return QPoint(2, 3);
|
||||||
|
case BottomDockWidgetArea:
|
||||||
|
return QPoint(3, 2);
|
||||||
|
case LeftDockWidgetArea:
|
||||||
|
return QPoint(2, 1);
|
||||||
|
case CenterDockWidgetArea:
|
||||||
|
return QPoint(2, 2);
|
||||||
|
default:
|
||||||
|
return QPoint();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (area) {
|
||||||
|
case TopDockWidgetArea:
|
||||||
|
return QPoint(0, 2);
|
||||||
|
case RightDockWidgetArea:
|
||||||
|
return QPoint(2, 4);
|
||||||
|
case BottomDockWidgetArea:
|
||||||
|
return QPoint(4, 2);
|
||||||
|
case LeftDockWidgetArea:
|
||||||
|
return QPoint(2, 0);
|
||||||
|
case CenterDockWidgetArea:
|
||||||
|
return QPoint(2, 2);
|
||||||
|
default:
|
||||||
|
return QPoint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DockOverlayCross::DockOverlayCross(DockOverlay *overlay)
|
||||||
|
: QWidget(overlay->parentWidget())
|
||||||
|
, d(new DockOverlayCrossPrivate(this))
|
||||||
|
{
|
||||||
|
d->m_dockOverlay = overlay;
|
||||||
|
|
||||||
|
if (Utils::HostOsInfo::isLinuxHost())
|
||||||
|
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint
|
||||||
|
| Qt::X11BypassWindowManagerHint);
|
||||||
|
else
|
||||||
|
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
|
||||||
|
|
||||||
|
setWindowTitle("DockOverlayCross");
|
||||||
|
setAttribute(Qt::WA_TranslucentBackground);
|
||||||
|
|
||||||
|
d->m_gridLayout = new QGridLayout();
|
||||||
|
d->m_gridLayout->setSpacing(0);
|
||||||
|
setLayout(d->m_gridLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
DockOverlayCross::~DockOverlayCross()
|
||||||
|
{
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockOverlayCross::setupOverlayCross(DockOverlay::eMode mode)
|
||||||
|
{
|
||||||
|
d->m_mode = mode;
|
||||||
|
|
||||||
|
QHash<DockWidgetArea, QWidget *> areaWidgets;
|
||||||
|
areaWidgets.insert(TopDockWidgetArea, d->createDropIndicatorWidget(TopDockWidgetArea, mode));
|
||||||
|
areaWidgets.insert(RightDockWidgetArea, d->createDropIndicatorWidget(RightDockWidgetArea, mode));
|
||||||
|
areaWidgets.insert(BottomDockWidgetArea,
|
||||||
|
d->createDropIndicatorWidget(BottomDockWidgetArea, mode));
|
||||||
|
areaWidgets.insert(LeftDockWidgetArea, d->createDropIndicatorWidget(LeftDockWidgetArea, mode));
|
||||||
|
areaWidgets.insert(CenterDockWidgetArea,
|
||||||
|
d->createDropIndicatorWidget(CenterDockWidgetArea, mode));
|
||||||
|
d->m_lastDevicePixelRatio = devicePixelRatioF();
|
||||||
|
setAreaWidgets(areaWidgets);
|
||||||
|
d->m_updateRequired = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockOverlayCross::updateOverlayIcons()
|
||||||
|
{
|
||||||
|
if (windowHandle()->devicePixelRatio() == d->m_lastDevicePixelRatio) { // TODO
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto Widget : d->m_dropIndicatorWidgets) {
|
||||||
|
d->updateDropIndicatorIcon(Widget);
|
||||||
|
}
|
||||||
|
d->m_lastDevicePixelRatio = devicePixelRatioF();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockOverlayCross::setIconColor(eIconColor colorIndex, const QColor &color)
|
||||||
|
{
|
||||||
|
d->m_iconColors[colorIndex] = color;
|
||||||
|
d->m_updateRequired = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor DockOverlayCross::iconColor(eIconColor colorIndex) const
|
||||||
|
{
|
||||||
|
return d->m_iconColors[colorIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockOverlayCross::setAreaWidgets(const QHash<DockWidgetArea, QWidget *> &widgets)
|
||||||
|
{
|
||||||
|
// Delete old widgets.
|
||||||
|
const auto values = d->m_dropIndicatorWidgets.values();
|
||||||
|
for (auto widget : values) {
|
||||||
|
d->m_gridLayout->removeWidget(widget);
|
||||||
|
delete widget;
|
||||||
|
}
|
||||||
|
d->m_dropIndicatorWidgets.clear();
|
||||||
|
|
||||||
|
// Insert new widgets into grid.
|
||||||
|
d->m_dropIndicatorWidgets = widgets;
|
||||||
|
|
||||||
|
const QHash<DockWidgetArea, QWidget *> areas = d->m_dropIndicatorWidgets;
|
||||||
|
QHash<DockWidgetArea, QWidget *>::const_iterator constIt;
|
||||||
|
for (constIt = areas.begin(); constIt != areas.end(); ++constIt) {
|
||||||
|
const DockWidgetArea area = constIt.key();
|
||||||
|
QWidget *widget = constIt.value();
|
||||||
|
QPoint position = d->areaGridPosition(area);
|
||||||
|
d->m_gridLayout->addWidget(widget,
|
||||||
|
position.x(),
|
||||||
|
position.y(),
|
||||||
|
static_cast<Qt::Alignment>(areaAlignment(area)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DockOverlay::ModeDockAreaOverlay == d->m_mode) {
|
||||||
|
d->m_gridLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
d->m_gridLayout->setRowStretch(0, 1);
|
||||||
|
d->m_gridLayout->setRowStretch(1, 0);
|
||||||
|
d->m_gridLayout->setRowStretch(2, 0);
|
||||||
|
d->m_gridLayout->setRowStretch(3, 0);
|
||||||
|
d->m_gridLayout->setRowStretch(4, 1);
|
||||||
|
|
||||||
|
d->m_gridLayout->setColumnStretch(0, 1);
|
||||||
|
d->m_gridLayout->setColumnStretch(1, 0);
|
||||||
|
d->m_gridLayout->setColumnStretch(2, 0);
|
||||||
|
d->m_gridLayout->setColumnStretch(3, 0);
|
||||||
|
d->m_gridLayout->setColumnStretch(4, 1);
|
||||||
|
} else {
|
||||||
|
d->m_gridLayout->setContentsMargins(4, 4, 4, 4);
|
||||||
|
d->m_gridLayout->setRowStretch(0, 0);
|
||||||
|
d->m_gridLayout->setRowStretch(1, 1);
|
||||||
|
d->m_gridLayout->setRowStretch(2, 1);
|
||||||
|
d->m_gridLayout->setRowStretch(3, 1);
|
||||||
|
d->m_gridLayout->setRowStretch(4, 0);
|
||||||
|
|
||||||
|
d->m_gridLayout->setColumnStretch(0, 0);
|
||||||
|
d->m_gridLayout->setColumnStretch(1, 1);
|
||||||
|
d->m_gridLayout->setColumnStretch(2, 1);
|
||||||
|
d->m_gridLayout->setColumnStretch(3, 1);
|
||||||
|
d->m_gridLayout->setColumnStretch(4, 0);
|
||||||
|
}
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
DockWidgetArea DockOverlayCross::cursorLocation() const
|
||||||
|
{
|
||||||
|
const QPoint position = mapFromGlobal(QCursor::pos());
|
||||||
|
|
||||||
|
const QHash<DockWidgetArea, QWidget *> areas = d->m_dropIndicatorWidgets;
|
||||||
|
QHash<DockWidgetArea, QWidget *>::const_iterator constIt;
|
||||||
|
for (constIt = areas.begin(); constIt != areas.end(); ++constIt)
|
||||||
|
{
|
||||||
|
if (d->m_dockOverlay->allowedAreas().testFlag(constIt.key()) && constIt.value()
|
||||||
|
&& constIt.value()->isVisible() && constIt.value()->geometry().contains(position)) {
|
||||||
|
return constIt.key();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return InvalidDockWidgetArea;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockOverlayCross::showEvent(QShowEvent *)
|
||||||
|
{
|
||||||
|
if (d->m_updateRequired) {
|
||||||
|
setupOverlayCross(d->m_mode);
|
||||||
|
}
|
||||||
|
this->updatePosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockOverlayCross::updatePosition()
|
||||||
|
{
|
||||||
|
resize(d->m_dockOverlay->size());
|
||||||
|
QPoint topLeft = d->m_dockOverlay->pos();
|
||||||
|
QPoint offest((this->width() - d->m_dockOverlay->width()) / 2,
|
||||||
|
(this->height() - d->m_dockOverlay->height()) / 2);
|
||||||
|
QPoint crossTopLeft = topLeft - offest;
|
||||||
|
move(crossTopLeft);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockOverlayCross::reset()
|
||||||
|
{
|
||||||
|
const QList<DockWidgetArea> allAreas{TopDockWidgetArea,
|
||||||
|
RightDockWidgetArea,
|
||||||
|
BottomDockWidgetArea,
|
||||||
|
LeftDockWidgetArea,
|
||||||
|
CenterDockWidgetArea};
|
||||||
|
const DockWidgetAreas allowedAreas = d->m_dockOverlay->allowedAreas();
|
||||||
|
|
||||||
|
// Update visibility of area widgets based on allowedAreas.
|
||||||
|
for (auto area : allAreas) {
|
||||||
|
QPoint position = d->areaGridPosition(area);
|
||||||
|
QLayoutItem *item = d->m_gridLayout->itemAtPosition(position.x(), position.y());
|
||||||
|
QWidget *widget = nullptr;
|
||||||
|
if (item && (widget = item->widget()) != nullptr) {
|
||||||
|
widget->setVisible(allowedAreas.testFlag(area));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockOverlayCross::setIconColors(const QString &colors)
|
||||||
|
{
|
||||||
|
static const QMap<QString, int>
|
||||||
|
colorCompenentStringMap{{"Frame", DockOverlayCross::FrameColor},
|
||||||
|
{"Background", DockOverlayCross::WindowBackgroundColor},
|
||||||
|
{"Overlay", DockOverlayCross::OverlayColor},
|
||||||
|
{"Arrow", DockOverlayCross::ArrowColor},
|
||||||
|
{"Shadow", DockOverlayCross::ShadowColor}};
|
||||||
|
|
||||||
|
auto colorList = colors.split(' ', QString::SkipEmptyParts);
|
||||||
|
for (const auto &colorListEntry : colorList) {
|
||||||
|
auto componentColor = colorListEntry.split('=', QString::SkipEmptyParts);
|
||||||
|
int component = colorCompenentStringMap.value(componentColor[0], -1);
|
||||||
|
if (component < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
d->m_iconColors[component] = QColor(componentColor[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
d->m_updateRequired = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DockOverlayCross::iconColors() const
|
||||||
|
{
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
263
src/libs/advanceddockingsystem/dockoverlay.h
Normal file
263
src/libs/advanceddockingsystem/dockoverlay.h
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 Uwe Kindler
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ads_globals.h"
|
||||||
|
|
||||||
|
#include <QFrame>
|
||||||
|
#include <QHash>
|
||||||
|
#include <QPointer>
|
||||||
|
#include <QRect>
|
||||||
|
|
||||||
|
class QGridLayout;
|
||||||
|
|
||||||
|
namespace ADS {
|
||||||
|
|
||||||
|
struct DockOverlayPrivate;
|
||||||
|
class DockOverlayCross;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DockOverlay paints a translucent rectangle over another widget. The geometry
|
||||||
|
* of the rectangle is based on the mouse location.
|
||||||
|
*/
|
||||||
|
class ADS_EXPORT DockOverlay : public QFrame
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private:
|
||||||
|
DockOverlayPrivate *d; //< private data class
|
||||||
|
friend struct DockOverlayPrivate;
|
||||||
|
friend class DockOverlayCross;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using Super = QFrame;
|
||||||
|
|
||||||
|
enum eMode { ModeDockAreaOverlay, ModeContainerOverlay };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a dock overlay
|
||||||
|
*/
|
||||||
|
DockOverlay(QWidget *parent, eMode Mode = ModeDockAreaOverlay);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor
|
||||||
|
*/
|
||||||
|
virtual ~DockOverlay() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the areas that are allowed for docking
|
||||||
|
*/
|
||||||
|
void setAllowedAreas(DockWidgetAreas areas);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns flags with all allowed drop areas
|
||||||
|
*/
|
||||||
|
DockWidgetAreas allowedAreas() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the drop area under the current cursor location
|
||||||
|
*/
|
||||||
|
DockWidgetArea dropAreaUnderCursor() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the drop overly for the given target widget
|
||||||
|
*/
|
||||||
|
DockWidgetArea showOverlay(QWidget *target);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hides the overlay
|
||||||
|
*/
|
||||||
|
void hideOverlay();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables / disables the semi transparent overlay rectangle that represents
|
||||||
|
* the future area of the dropped widget
|
||||||
|
*/
|
||||||
|
void enableDropPreview(bool enable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if drop preview is enabled
|
||||||
|
*/
|
||||||
|
bool dropPreviewEnabled() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The drop overlay rectangle for the target area
|
||||||
|
*/
|
||||||
|
QRect dropOverlayRect() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle polish events
|
||||||
|
*/
|
||||||
|
virtual bool event(QEvent *event) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void paintEvent(QPaintEvent *event) override;
|
||||||
|
virtual void showEvent(QShowEvent *event) override;
|
||||||
|
virtual void hideEvent(QHideEvent *event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DockOverlayCrossPrivate;
|
||||||
|
/**
|
||||||
|
* DockOverlayCross shows a cross with 5 different drop area possibilities.
|
||||||
|
* I could have handled everything inside DockOverlay, but because of some
|
||||||
|
* styling issues it's better to have a separate class for the cross.
|
||||||
|
* You can style the cross icon using the property system.
|
||||||
|
* \code
|
||||||
|
* ADS--DockOverlayCross
|
||||||
|
{
|
||||||
|
qproperty-iconFrameColor: palette(highlight);
|
||||||
|
qproperty-iconBackgroundColor: palette(base);
|
||||||
|
qproperty-iconOverlayColor: palette(highlight);
|
||||||
|
qproperty-iconArrowColor: rgb(227, 227, 227);
|
||||||
|
qproperty-iconShadowColor: rgb(0, 0, 0);
|
||||||
|
}
|
||||||
|
* \endcode
|
||||||
|
* Or you can use the iconColors property to pass in AARRGGBB values as
|
||||||
|
* hex string like shown in the example below.
|
||||||
|
* \code
|
||||||
|
* ADS--DockOverlayCross
|
||||||
|
* {
|
||||||
|
* qproperty-iconColors: "Frame=#ff3d3d3d Background=#ff929292 Overlay=#1f3d3d3d Arrow=#ffb4b4b4 Shadow=#40474747";
|
||||||
|
* }
|
||||||
|
* \endcode
|
||||||
|
*/
|
||||||
|
class DockOverlayCross : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(QString iconColors READ iconColors WRITE setIconColors)
|
||||||
|
Q_PROPERTY(QColor iconFrameColor READ iconColor WRITE setIconFrameColor)
|
||||||
|
Q_PROPERTY(QColor iconBackgroundColor READ iconColor WRITE setIconBackgroundColor)
|
||||||
|
Q_PROPERTY(QColor iconOverlayColor READ iconColor WRITE setIconOverlayColor)
|
||||||
|
Q_PROPERTY(QColor iconArrowColor READ iconColor WRITE setIconArrowColor)
|
||||||
|
Q_PROPERTY(QColor iconShadowColor READ iconColor WRITE setIconShadowColor)
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum eIconColor {
|
||||||
|
FrameColor, ///< the color of the frame of the small window icon
|
||||||
|
WindowBackgroundColor, ///< the background color of the small window in the icon
|
||||||
|
OverlayColor, ///< the color that shows the overlay (the dock side) in the icon
|
||||||
|
ArrowColor, ///< the arrow that points into the direction
|
||||||
|
ShadowColor ///< the color of the shadow rectangle that is painted below the icons
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
DockOverlayCrossPrivate *d;
|
||||||
|
friend struct DockOverlayCrossPrivate;
|
||||||
|
friend class DockOverlay;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* This function returns an empty string and is only here to silence
|
||||||
|
* moc
|
||||||
|
*/
|
||||||
|
QString iconColors() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a dummy function for the property system
|
||||||
|
*/
|
||||||
|
QColor iconColor() const { return QColor(); }
|
||||||
|
void setIconFrameColor(const QColor &color) { setIconColor(FrameColor, color); }
|
||||||
|
void setIconBackgroundColor(const QColor &color) { setIconColor(WindowBackgroundColor, color); }
|
||||||
|
void setIconOverlayColor(const QColor &color) { setIconColor(OverlayColor, color); }
|
||||||
|
void setIconArrowColor(const QColor &color) { setIconColor(ArrowColor, color); }
|
||||||
|
void setIconShadowColor(const QColor &color) { setIconColor(ShadowColor, color); }
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Creates an overlay cross for the given overlay
|
||||||
|
*/
|
||||||
|
DockOverlayCross(DockOverlay *overlay);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual destructor
|
||||||
|
*/
|
||||||
|
virtual ~DockOverlayCross() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a certain icon color
|
||||||
|
*/
|
||||||
|
void setIconColor(eIconColor colorIndex, const QColor &color);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the icon color given by ColorIndex
|
||||||
|
*/
|
||||||
|
QColor iconColor(eIconColor colorIndex) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the dock widget area depending on the current cursor location.
|
||||||
|
* The function checks, if the mouse cursor is inside of any drop indicator
|
||||||
|
* widget and returns the corresponding DockWidgetArea.
|
||||||
|
*/
|
||||||
|
DockWidgetArea cursorLocation() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up the overlay cross for the given overlay mode
|
||||||
|
*/
|
||||||
|
void setupOverlayCross(DockOverlay::eMode mode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recreates the overlay icons.
|
||||||
|
*/
|
||||||
|
void updateOverlayIcons();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets and updates the
|
||||||
|
*/
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the current position
|
||||||
|
*/
|
||||||
|
void updatePosition();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A string with all icon colors to set.
|
||||||
|
* You can use this property to style the overly icon via CSS stylesheet
|
||||||
|
* file. The colors are set via a color identifier and a hex AARRGGBB value like
|
||||||
|
* in the example below.
|
||||||
|
* \code
|
||||||
|
* ADS--DockOverlayCross
|
||||||
|
* {
|
||||||
|
* qproperty-iconColors: "Frame=#ff3d3d3d Background=#ff929292 Overlay=#1f3d3d3d Arrow=#ffb4b4b4 Shadow=#40474747";
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
void setIconColors(const QString &colors);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void showEvent(QShowEvent *event) override;
|
||||||
|
void setAreaWidgets(const QHash<DockWidgetArea, QWidget *> &widgets);
|
||||||
|
}; // DockOverlayCross
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
92
src/libs/advanceddockingsystem/docksplitter.cpp
Normal file
92
src/libs/advanceddockingsystem/docksplitter.cpp
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 Uwe Kindler
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "docksplitter.h"
|
||||||
|
|
||||||
|
#include "dockareawidget.h"
|
||||||
|
|
||||||
|
#include <QChildEvent>
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
|
||||||
|
static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtDebugMsg)
|
||||||
|
|
||||||
|
namespace ADS
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Private dock splitter data
|
||||||
|
*/
|
||||||
|
struct DockSplitterPrivate
|
||||||
|
{
|
||||||
|
DockSplitter *q;
|
||||||
|
int m_visibleContentCount = 0;
|
||||||
|
|
||||||
|
DockSplitterPrivate(DockSplitter *parent)
|
||||||
|
: q(parent)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
DockSplitter::DockSplitter(QWidget *parent)
|
||||||
|
: QSplitter(parent)
|
||||||
|
, d(new DockSplitterPrivate(this))
|
||||||
|
{
|
||||||
|
//setProperty("ads-splitter", true); // TODO
|
||||||
|
setProperty("minisplitter", true);
|
||||||
|
setChildrenCollapsible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
DockSplitter::DockSplitter(Qt::Orientation orientation, QWidget *parent)
|
||||||
|
: QSplitter(orientation, parent)
|
||||||
|
, d(new DockSplitterPrivate(this))
|
||||||
|
{}
|
||||||
|
|
||||||
|
DockSplitter::~DockSplitter()
|
||||||
|
{
|
||||||
|
qCInfo(adsLog) << Q_FUNC_INFO;
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DockSplitter::hasVisibleContent() const
|
||||||
|
{
|
||||||
|
// TODO Cache or precalculate this to speed up
|
||||||
|
for (int i = 0; i < count(); ++i) {
|
||||||
|
if (!widget(i)->isHidden()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
72
src/libs/advanceddockingsystem/docksplitter.h
Normal file
72
src/libs/advanceddockingsystem/docksplitter.h
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 Uwe Kindler
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ads_globals.h"
|
||||||
|
|
||||||
|
#include <QSplitter>
|
||||||
|
|
||||||
|
namespace ADS {
|
||||||
|
|
||||||
|
struct DockSplitterPrivate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splitter used internally instead of QSplitter with some additional
|
||||||
|
* fuctionality.
|
||||||
|
*/
|
||||||
|
class ADS_EXPORT DockSplitter : public QSplitter
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private:
|
||||||
|
DockSplitterPrivate *d;
|
||||||
|
friend struct DockSplitterPrivate;
|
||||||
|
|
||||||
|
public:
|
||||||
|
DockSplitter(QWidget *parent = nullptr);
|
||||||
|
DockSplitter(Qt::Orientation orientation, QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints debug info
|
||||||
|
*/
|
||||||
|
virtual ~DockSplitter() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true, if any of the internal widgets is visible
|
||||||
|
*/
|
||||||
|
bool hasVisibleContent() const;
|
||||||
|
}; // class DockSplitter
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
625
src/libs/advanceddockingsystem/dockwidget.cpp
Normal file
625
src/libs/advanceddockingsystem/dockwidget.cpp
Normal file
@@ -0,0 +1,625 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 Uwe Kindler
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "dockwidget.h"
|
||||||
|
|
||||||
|
#include "ads_globals.h"
|
||||||
|
#include "dockareawidget.h"
|
||||||
|
#include "dockcomponentsfactory.h"
|
||||||
|
#include "dockcontainerwidget.h"
|
||||||
|
#include "dockmanager.h"
|
||||||
|
#include "docksplitter.h"
|
||||||
|
#include "dockwidgettab.h"
|
||||||
|
#include "floatingdockcontainer.h"
|
||||||
|
|
||||||
|
#include <QAction>
|
||||||
|
#include <QBoxLayout>
|
||||||
|
#include <QEvent>
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
#include <QPointer>
|
||||||
|
#include <QScrollArea>
|
||||||
|
#include <QSplitter>
|
||||||
|
#include <QStack>
|
||||||
|
#include <QTextStream>
|
||||||
|
#include <QToolBar>
|
||||||
|
#include <QXmlStreamWriter>
|
||||||
|
|
||||||
|
static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtDebugMsg)
|
||||||
|
|
||||||
|
namespace ADS
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Private data class of DockWidget class (pimpl)
|
||||||
|
*/
|
||||||
|
struct DockWidgetPrivate
|
||||||
|
{
|
||||||
|
DockWidget *q = nullptr;
|
||||||
|
QBoxLayout *m_layout = nullptr;
|
||||||
|
QWidget *m_widget = nullptr;
|
||||||
|
DockWidgetTab *m_tabWidget = nullptr;
|
||||||
|
DockWidget::DockWidgetFeatures m_features = DockWidget::DefaultDockWidgetFeatures;
|
||||||
|
DockManager *m_dockManager = nullptr;
|
||||||
|
DockAreaWidget *m_dockArea = nullptr;
|
||||||
|
QAction *m_toggleViewAction = nullptr;
|
||||||
|
bool m_closed = false;
|
||||||
|
QScrollArea *m_scrollArea = nullptr;
|
||||||
|
QToolBar *m_toolBar = nullptr;
|
||||||
|
Qt::ToolButtonStyle m_toolBarStyleDocked = Qt::ToolButtonIconOnly;
|
||||||
|
Qt::ToolButtonStyle m_toolBarStyleFloating = Qt::ToolButtonTextUnderIcon;
|
||||||
|
QSize m_toolBarIconSizeDocked = QSize(16, 16);
|
||||||
|
QSize m_toolBarIconSizeFloating = QSize(24, 24);
|
||||||
|
bool m_isFloatingTopLevel = false;
|
||||||
|
QList<QAction *> m_titleBarActions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private data constructor
|
||||||
|
*/
|
||||||
|
DockWidgetPrivate(DockWidget *parent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show dock widget
|
||||||
|
*/
|
||||||
|
void showDockWidget();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide dock widget.
|
||||||
|
*/
|
||||||
|
void hideDockWidget();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hides a dock area if all dock widgets in the area are closed.
|
||||||
|
* This function updates the current selected tab and hides the parent
|
||||||
|
* dock area if it is empty
|
||||||
|
*/
|
||||||
|
void updateParentDockArea();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup the top tool bar
|
||||||
|
*/
|
||||||
|
void setupToolBar();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup the main scroll area
|
||||||
|
*/
|
||||||
|
void setupScrollArea();
|
||||||
|
};
|
||||||
|
// struct DockWidgetPrivate
|
||||||
|
|
||||||
|
DockWidgetPrivate::DockWidgetPrivate(DockWidget *parent)
|
||||||
|
: q(parent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void DockWidgetPrivate::showDockWidget()
|
||||||
|
{
|
||||||
|
if (!m_dockArea) {
|
||||||
|
FloatingDockContainer *floatingWidget = new FloatingDockContainer(q);
|
||||||
|
floatingWidget->resize(q->size());
|
||||||
|
floatingWidget->show();
|
||||||
|
} else {
|
||||||
|
m_dockArea->setCurrentDockWidget(q);
|
||||||
|
m_dockArea->toggleView(true);
|
||||||
|
m_tabWidget->show();
|
||||||
|
QSplitter *splitter = internal::findParent<QSplitter *>(m_dockArea);
|
||||||
|
while (splitter && !splitter->isVisible()) {
|
||||||
|
splitter->show();
|
||||||
|
splitter = internal::findParent<QSplitter *>(splitter);
|
||||||
|
}
|
||||||
|
|
||||||
|
DockContainerWidget *container = m_dockArea->dockContainer();
|
||||||
|
if (container->isFloating()) {
|
||||||
|
FloatingDockContainer *floatingWidget
|
||||||
|
= internal::findParent<FloatingDockContainer *>(container);
|
||||||
|
floatingWidget->show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockWidgetPrivate::hideDockWidget()
|
||||||
|
{
|
||||||
|
m_tabWidget->hide();
|
||||||
|
updateParentDockArea();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockWidgetPrivate::updateParentDockArea()
|
||||||
|
{
|
||||||
|
if (!m_dockArea) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto nextDockWidget = m_dockArea->nextOpenDockWidget(q);
|
||||||
|
if (nextDockWidget) {
|
||||||
|
m_dockArea->setCurrentDockWidget(nextDockWidget);
|
||||||
|
} else {
|
||||||
|
m_dockArea->hideAreaWithNoVisibleContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockWidgetPrivate::setupToolBar()
|
||||||
|
{
|
||||||
|
m_toolBar = new QToolBar(q);
|
||||||
|
m_toolBar->setObjectName("dockWidgetToolBar");
|
||||||
|
m_layout->insertWidget(0, m_toolBar);
|
||||||
|
m_toolBar->setIconSize(QSize(16, 16));
|
||||||
|
m_toolBar->toggleViewAction()->setEnabled(false);
|
||||||
|
m_toolBar->toggleViewAction()->setVisible(false);
|
||||||
|
QObject::connect(q, &DockWidget::topLevelChanged, q, &DockWidget::setToolbarFloatingStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockWidgetPrivate::setupScrollArea()
|
||||||
|
{
|
||||||
|
m_scrollArea = new QScrollArea(q);
|
||||||
|
m_scrollArea->setObjectName("dockWidgetScrollArea");
|
||||||
|
m_scrollArea->setWidgetResizable(true);
|
||||||
|
m_layout->addWidget(m_scrollArea);
|
||||||
|
}
|
||||||
|
|
||||||
|
DockWidget::DockWidget(const QString &uniqueId, QWidget *parent)
|
||||||
|
: QFrame(parent)
|
||||||
|
, d(new DockWidgetPrivate(this))
|
||||||
|
{
|
||||||
|
d->m_layout = new QBoxLayout(QBoxLayout::TopToBottom);
|
||||||
|
d->m_layout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
d->m_layout->setSpacing(0);
|
||||||
|
setLayout(d->m_layout);
|
||||||
|
setWindowTitle(uniqueId); // temporarily use unique id as title
|
||||||
|
setObjectName(uniqueId);
|
||||||
|
|
||||||
|
d->m_tabWidget = componentsFactory()->createDockWidgetTab(this);
|
||||||
|
d->m_toggleViewAction = new QAction(uniqueId, this);
|
||||||
|
d->m_toggleViewAction->setCheckable(true);
|
||||||
|
connect(d->m_toggleViewAction, &QAction::triggered, this, &DockWidget::toggleView);
|
||||||
|
setToolbarFloatingStyle(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
DockWidget::~DockWidget()
|
||||||
|
{
|
||||||
|
qCInfo(adsLog) << Q_FUNC_INFO;
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockWidget::setToggleViewActionChecked(bool checked)
|
||||||
|
{
|
||||||
|
QAction *action = d->m_toggleViewAction;
|
||||||
|
//action->blockSignals(true);
|
||||||
|
action->setChecked(checked);
|
||||||
|
//action->blockSignals(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockWidget::setWidget(QWidget *widget, eInsertMode insertMode)
|
||||||
|
{
|
||||||
|
QScrollArea *scrollAreaWidget = qobject_cast<QScrollArea *>(widget);
|
||||||
|
if (scrollAreaWidget || ForceNoScrollArea == insertMode) {
|
||||||
|
d->m_layout->addWidget(widget);
|
||||||
|
if (scrollAreaWidget && scrollAreaWidget->viewport()) {
|
||||||
|
scrollAreaWidget->viewport()->setProperty("dockWidgetContent", true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
d->setupScrollArea();
|
||||||
|
d->m_scrollArea->setWidget(widget);
|
||||||
|
}
|
||||||
|
|
||||||
|
d->m_widget = widget;
|
||||||
|
d->m_widget->setProperty("dockWidgetContent", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget *DockWidget::takeWidget()
|
||||||
|
{
|
||||||
|
// TODO Shouldn't m_widget being set to nullptr?!
|
||||||
|
d->m_scrollArea->takeWidget();
|
||||||
|
d->m_layout->removeWidget(d->m_widget);
|
||||||
|
d->m_widget->setParent(nullptr);
|
||||||
|
return d->m_widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget *DockWidget::widget() const { return d->m_widget; }
|
||||||
|
|
||||||
|
DockWidgetTab *DockWidget::tabWidget() const { return d->m_tabWidget; }
|
||||||
|
|
||||||
|
void DockWidget::setFeatures(DockWidgetFeatures features)
|
||||||
|
{
|
||||||
|
if (d->m_features == features) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
d->m_features = features;
|
||||||
|
emit featuresChanged(d->m_features);
|
||||||
|
d->m_tabWidget->onDockWidgetFeaturesChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockWidget::setFeature(DockWidgetFeature flag, bool on)
|
||||||
|
{
|
||||||
|
auto currentFeatures = features();
|
||||||
|
internal::setFlag(currentFeatures, flag, on);
|
||||||
|
setFeatures(currentFeatures);
|
||||||
|
}
|
||||||
|
|
||||||
|
DockWidget::DockWidgetFeatures DockWidget::features() const { return d->m_features; }
|
||||||
|
|
||||||
|
DockManager *DockWidget::dockManager() const { return d->m_dockManager; }
|
||||||
|
|
||||||
|
void DockWidget::setDockManager(DockManager *dockManager) { d->m_dockManager = dockManager; }
|
||||||
|
|
||||||
|
DockContainerWidget *DockWidget::dockContainer() const
|
||||||
|
{
|
||||||
|
if (d->m_dockArea) {
|
||||||
|
return d->m_dockArea->dockContainer();
|
||||||
|
} else {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DockAreaWidget *DockWidget::dockAreaWidget() const { return d->m_dockArea; }
|
||||||
|
|
||||||
|
bool DockWidget::isFloating() const
|
||||||
|
{
|
||||||
|
if (!isInFloatingContainer()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dockContainer()->topLevelDockWidget() == this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DockWidget::isInFloatingContainer() const
|
||||||
|
{
|
||||||
|
auto container = dockContainer();
|
||||||
|
if (!container) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!container->isFloating()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DockWidget::isClosed() const { return d->m_closed; }
|
||||||
|
|
||||||
|
QAction *DockWidget::toggleViewAction() const { return d->m_toggleViewAction; }
|
||||||
|
|
||||||
|
void DockWidget::setToggleViewActionMode(eToggleViewActionMode mode)
|
||||||
|
{
|
||||||
|
if (ActionModeToggle == mode) {
|
||||||
|
d->m_toggleViewAction->setCheckable(true);
|
||||||
|
d->m_toggleViewAction->setIcon(QIcon());
|
||||||
|
} else {
|
||||||
|
d->m_toggleViewAction->setCheckable(false);
|
||||||
|
d->m_toggleViewAction->setIcon(d->m_tabWidget->icon());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockWidget::toggleView(bool open)
|
||||||
|
{
|
||||||
|
// If the toggle view action mode is ActionModeShow, then Open is always
|
||||||
|
// true if the sender is the toggle view action
|
||||||
|
QAction *action = qobject_cast<QAction *>(sender());
|
||||||
|
if (action == d->m_toggleViewAction && !d->m_toggleViewAction->isCheckable()) {
|
||||||
|
open = true;
|
||||||
|
}
|
||||||
|
// If the dock widget state is different, then we really need to toggle
|
||||||
|
// the state. If we are in the right state, then we simply make this
|
||||||
|
// dock widget the current dock widget
|
||||||
|
if (d->m_closed != !open) {
|
||||||
|
toggleViewInternal(open);
|
||||||
|
} else if (open && d->m_dockArea) {
|
||||||
|
d->m_dockArea->setCurrentDockWidget(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockWidget::toggleViewInternal(bool open)
|
||||||
|
{
|
||||||
|
DockContainerWidget *dockContainerWidget = dockContainer();
|
||||||
|
DockWidget *topLevelDockWidgetBefore = dockContainerWidget
|
||||||
|
? dockContainerWidget->topLevelDockWidget()
|
||||||
|
: nullptr;
|
||||||
|
|
||||||
|
if (open) {
|
||||||
|
d->showDockWidget();
|
||||||
|
} else {
|
||||||
|
d->hideDockWidget();
|
||||||
|
}
|
||||||
|
d->m_closed = !open;
|
||||||
|
//d->m_toggleViewAction->blockSignals(true);
|
||||||
|
d->m_toggleViewAction->setChecked(open);
|
||||||
|
//d->m_toggleViewAction->blockSignals(false);
|
||||||
|
if (d->m_dockArea) {
|
||||||
|
d->m_dockArea->toggleDockWidgetView(this, open);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (open && topLevelDockWidgetBefore) {
|
||||||
|
DockWidget::emitTopLevelEventForWidget(topLevelDockWidgetBefore, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here we need to call the dockContainer() function again, because if
|
||||||
|
// this dock widget was unassigned before the call to showDockWidget() then
|
||||||
|
// it has a dock container now
|
||||||
|
dockContainerWidget = dockContainer();
|
||||||
|
DockWidget *topLevelDockWidgetAfter = dockContainerWidget
|
||||||
|
? dockContainerWidget->topLevelDockWidget()
|
||||||
|
: nullptr;
|
||||||
|
DockWidget::emitTopLevelEventForWidget(topLevelDockWidgetAfter, true);
|
||||||
|
FloatingDockContainer *floatingContainer = dockContainerWidget->floatingWidget();
|
||||||
|
if (floatingContainer) {
|
||||||
|
floatingContainer->updateWindowTitle();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!open) {
|
||||||
|
emit closed();
|
||||||
|
}
|
||||||
|
emit viewToggled(open);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockWidget::setDockArea(DockAreaWidget *dockArea)
|
||||||
|
{
|
||||||
|
d->m_dockArea = dockArea;
|
||||||
|
d->m_toggleViewAction->setChecked(dockArea != nullptr && !this->isClosed());
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockWidget::saveState(QXmlStreamWriter &stream) const
|
||||||
|
{
|
||||||
|
stream.writeStartElement("widget");
|
||||||
|
stream.writeAttribute("name", objectName());
|
||||||
|
stream.writeAttribute("closed", QVariant::fromValue(d->m_closed).toString());
|
||||||
|
stream.writeEndElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockWidget::flagAsUnassigned()
|
||||||
|
{
|
||||||
|
d->m_closed = true;
|
||||||
|
setParent(d->m_dockManager);
|
||||||
|
setVisible(false);
|
||||||
|
setDockArea(nullptr);
|
||||||
|
tabWidget()->setParent(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DockWidget::event(QEvent *event)
|
||||||
|
{
|
||||||
|
switch (event->type()) {
|
||||||
|
case QEvent::Hide:
|
||||||
|
emit visibilityChanged(false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QEvent::Show:
|
||||||
|
emit visibilityChanged(geometry().right() >= 0 && geometry().bottom() >= 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QEvent::WindowTitleChange :
|
||||||
|
{
|
||||||
|
const auto title = windowTitle();
|
||||||
|
if (d->m_tabWidget) {
|
||||||
|
d->m_tabWidget->setText(title);
|
||||||
|
}
|
||||||
|
if (d->m_toggleViewAction) {
|
||||||
|
d->m_toggleViewAction->setText(title);
|
||||||
|
}
|
||||||
|
if (d->m_dockArea) {
|
||||||
|
d->m_dockArea->markTitleBarMenuOutdated(); // update tabs menu
|
||||||
|
}
|
||||||
|
emit titleChanged(title);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Super::event(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef QT_NO_TOOLTIP
|
||||||
|
void DockWidget::setTabToolTip(const QString &text)
|
||||||
|
{
|
||||||
|
if (d->m_tabWidget) {
|
||||||
|
d->m_tabWidget->setToolTip(text);
|
||||||
|
}
|
||||||
|
if (d->m_toggleViewAction) {
|
||||||
|
d->m_toggleViewAction->setToolTip(text);
|
||||||
|
}
|
||||||
|
if (d->m_dockArea) {
|
||||||
|
d->m_dockArea->markTitleBarMenuOutdated(); //update tabs menu
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void DockWidget::setIcon(const QIcon &icon)
|
||||||
|
{
|
||||||
|
d->m_tabWidget->setIcon(icon);
|
||||||
|
if (!d->m_toggleViewAction->isCheckable()) {
|
||||||
|
d->m_toggleViewAction->setIcon(icon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QIcon DockWidget::icon() const { return d->m_tabWidget->icon(); }
|
||||||
|
|
||||||
|
QToolBar *DockWidget::toolBar() const { return d->m_toolBar; }
|
||||||
|
|
||||||
|
QToolBar *DockWidget::createDefaultToolBar()
|
||||||
|
{
|
||||||
|
if (!d->m_toolBar) {
|
||||||
|
d->setupToolBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
return d->m_toolBar;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockWidget::setToolBar(QToolBar *toolBar)
|
||||||
|
{
|
||||||
|
if (d->m_toolBar) {
|
||||||
|
delete d->m_toolBar;
|
||||||
|
}
|
||||||
|
|
||||||
|
d->m_toolBar = toolBar;
|
||||||
|
d->m_layout->insertWidget(0, d->m_toolBar);
|
||||||
|
connect(this, &DockWidget::topLevelChanged, this, &DockWidget::setToolbarFloatingStyle);
|
||||||
|
setToolbarFloatingStyle(isFloating());
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockWidget::setToolBarStyle(Qt::ToolButtonStyle style, eState state)
|
||||||
|
{
|
||||||
|
if (StateFloating == state) {
|
||||||
|
d->m_toolBarStyleFloating = style;
|
||||||
|
} else {
|
||||||
|
d->m_toolBarStyleDocked = style;
|
||||||
|
}
|
||||||
|
|
||||||
|
setToolbarFloatingStyle(isFloating());
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::ToolButtonStyle DockWidget::toolBarStyle(eState state) const
|
||||||
|
{
|
||||||
|
if (StateFloating == state) {
|
||||||
|
return d->m_toolBarStyleFloating;
|
||||||
|
} else {
|
||||||
|
return d->m_toolBarStyleDocked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockWidget::setToolBarIconSize(const QSize &iconSize, eState state)
|
||||||
|
{
|
||||||
|
if (StateFloating == state) {
|
||||||
|
d->m_toolBarIconSizeFloating = iconSize;
|
||||||
|
} else {
|
||||||
|
d->m_toolBarIconSizeDocked = iconSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
setToolbarFloatingStyle(isFloating());
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize DockWidget::toolBarIconSize(eState state) const
|
||||||
|
{
|
||||||
|
if (StateFloating == state) {
|
||||||
|
return d->m_toolBarIconSizeFloating;
|
||||||
|
} else {
|
||||||
|
return d->m_toolBarIconSizeDocked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockWidget::setToolbarFloatingStyle(bool floating)
|
||||||
|
{
|
||||||
|
if (!d->m_toolBar) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto iconSize = floating ? d->m_toolBarIconSizeFloating : d->m_toolBarIconSizeDocked;
|
||||||
|
if (iconSize != d->m_toolBar->iconSize()) {
|
||||||
|
d->m_toolBar->setIconSize(iconSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto buttonStyle = floating ? d->m_toolBarStyleFloating : d->m_toolBarStyleDocked;
|
||||||
|
if (buttonStyle != d->m_toolBar->toolButtonStyle()) {
|
||||||
|
d->m_toolBar->setToolButtonStyle(buttonStyle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockWidget::emitTopLevelEventForWidget(DockWidget *topLevelDockWidget, bool floating)
|
||||||
|
{
|
||||||
|
if (topLevelDockWidget) {
|
||||||
|
topLevelDockWidget->dockAreaWidget()->updateTitleBarVisibility();
|
||||||
|
topLevelDockWidget->emitTopLevelChanged(floating);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockWidget::emitTopLevelChanged(bool floating)
|
||||||
|
{
|
||||||
|
if (floating != d->m_isFloatingTopLevel) {
|
||||||
|
d->m_isFloatingTopLevel = floating;
|
||||||
|
emit topLevelChanged(d->m_isFloatingTopLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockWidget::setClosedState(bool closed) { d->m_closed = closed; }
|
||||||
|
|
||||||
|
QSize DockWidget::minimumSizeHint() const { return QSize(60, 40); }
|
||||||
|
|
||||||
|
void DockWidget::setFloating()
|
||||||
|
{
|
||||||
|
if (isClosed()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
d->m_tabWidget->detachDockWidget();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockWidget::deleteDockWidget()
|
||||||
|
{
|
||||||
|
dockManager()->removeDockWidget(this);
|
||||||
|
deleteLater();
|
||||||
|
d->m_closed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockWidget::closeDockWidget()
|
||||||
|
{
|
||||||
|
closeDockWidgetInternal(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DockWidget::closeDockWidgetInternal(bool forceClose)
|
||||||
|
{
|
||||||
|
if (!forceClose) {
|
||||||
|
emit closeRequested();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!forceClose && features().testFlag(DockWidget::CustomCloseHandling)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (features().testFlag(DockWidget::DockWidgetDeleteOnClose)) {
|
||||||
|
// If the dock widget is floating, then we check if we also need to
|
||||||
|
// delete the floating widget
|
||||||
|
if (isFloating()) {
|
||||||
|
FloatingDockContainer* floatingWidget = internal::findParent<
|
||||||
|
FloatingDockContainer *>(this);
|
||||||
|
if (floatingWidget->dockWidgets().count() == 1) {
|
||||||
|
floatingWidget->deleteLater();
|
||||||
|
} else {
|
||||||
|
floatingWidget->hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deleteDockWidget();
|
||||||
|
} else {
|
||||||
|
toggleView(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockWidget::setTitleBarActions(QList<QAction *> actions)
|
||||||
|
{
|
||||||
|
d->m_titleBarActions = actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QAction *> DockWidget::titleBarActions() const
|
||||||
|
{
|
||||||
|
return d->m_titleBarActions;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
491
src/libs/advanceddockingsystem/dockwidget.h
Normal file
491
src/libs/advanceddockingsystem/dockwidget.h
Normal file
@@ -0,0 +1,491 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 Uwe Kindler
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ads_globals.h"
|
||||||
|
|
||||||
|
#include <QFrame>
|
||||||
|
|
||||||
|
class QToolBar;
|
||||||
|
class QXmlStreamWriter;
|
||||||
|
|
||||||
|
namespace ADS {
|
||||||
|
|
||||||
|
struct DockWidgetPrivate;
|
||||||
|
class DockWidgetTab;
|
||||||
|
class DockManager;
|
||||||
|
class DockContainerWidget;
|
||||||
|
class DockAreaWidget;
|
||||||
|
class DockContainerWidgetPrivate;
|
||||||
|
class FloatingDockContainer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The QDockWidget class provides a widget that can be docked inside a
|
||||||
|
* DockManager or floated as a top-level window on the desktop.
|
||||||
|
*/
|
||||||
|
class ADS_EXPORT DockWidget : public QFrame
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private:
|
||||||
|
DockWidgetPrivate *d; ///< private data (pimpl)
|
||||||
|
friend struct DockWidgetPrivate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjusts the toolbar icon sizes according to the floating state
|
||||||
|
*/
|
||||||
|
void setToolbarFloatingStyle(bool topLevel);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
friend class DockContainerWidget;
|
||||||
|
friend class DockAreaWidget;
|
||||||
|
friend class FloatingDockContainer;
|
||||||
|
friend class DockManager;
|
||||||
|
friend struct DockManagerPrivate;
|
||||||
|
friend class DockContainerWidgetPrivate;
|
||||||
|
friend class DockAreaTabBar;
|
||||||
|
friend class DockWidgetTab;
|
||||||
|
friend struct DockWidgetTabPrivate;
|
||||||
|
friend struct DockAreaTitleBarPrivate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assigns the dock manager that manages this dock widget
|
||||||
|
*/
|
||||||
|
void setDockManager(DockManager *dockManager);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this dock widget is inserted into a dock area, the dock area will
|
||||||
|
* be registered on this widget via this function. If a dock widget is
|
||||||
|
* removed from a dock area, this function will be called with nullptr
|
||||||
|
* value.
|
||||||
|
*/
|
||||||
|
void setDockArea(DockAreaWidget *dockArea);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function changes the toggle view action without emitting any
|
||||||
|
* signal
|
||||||
|
*/
|
||||||
|
void setToggleViewActionChecked(bool checked);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the state into the given stream
|
||||||
|
*/
|
||||||
|
void saveState(QXmlStreamWriter &stream) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a helper function for the dock manager to flag this widget
|
||||||
|
* as unassigned.
|
||||||
|
* When calling the restore function, it may happen, that the saved state
|
||||||
|
* contains less dock widgets then currently available. All widgets whose
|
||||||
|
* data is not contained in the saved state, are flagged as unassigned
|
||||||
|
* after the restore process. If the user shows an unassigned dock widget,
|
||||||
|
* a floating widget will be created to take up the dock widget.
|
||||||
|
*/
|
||||||
|
void flagAsUnassigned();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call this function to emit a topLevelChanged() signal and to update
|
||||||
|
* the dock area tool bar visibility
|
||||||
|
*/
|
||||||
|
static void emitTopLevelEventForWidget(DockWidget *topLevelDockWidget, bool floating);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this function to emit a top level changed event.
|
||||||
|
* Do never use emit topLevelChanged(). Always use this function because
|
||||||
|
* it only emits a signal if the floating state has really changed
|
||||||
|
*/
|
||||||
|
void emitTopLevelChanged(bool floating);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal function for modifying the closed state when restoring
|
||||||
|
* a saved docking state
|
||||||
|
*/
|
||||||
|
void setClosedState(bool closed);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal toggle view function that does not check if the widget
|
||||||
|
* already is in the given state
|
||||||
|
*/
|
||||||
|
void toggleViewInternal(bool open);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal close dock widget implementation.
|
||||||
|
* The function returns true if the dock widget has been closed or hidden
|
||||||
|
*/
|
||||||
|
bool closeDockWidgetInternal(bool forceClose = false);
|
||||||
|
|
||||||
|
public:
|
||||||
|
using Super = QFrame;
|
||||||
|
|
||||||
|
enum DockWidgetFeature {
|
||||||
|
DockWidgetClosable = 0x01,
|
||||||
|
DockWidgetMovable = 0x02,///< this feature is not properly implemented yet and is ignored
|
||||||
|
DockWidgetFloatable = 0x04,
|
||||||
|
DockWidgetDeleteOnClose = 0x08, ///< deletes the dock widget when it is closed
|
||||||
|
CustomCloseHandling = 0x10,
|
||||||
|
DefaultDockWidgetFeatures = DockWidgetClosable | DockWidgetMovable | DockWidgetFloatable,
|
||||||
|
AllDockWidgetFeatures = DefaultDockWidgetFeatures | DockWidgetDeleteOnClose | CustomCloseHandling,
|
||||||
|
NoDockWidgetFeatures = 0x00
|
||||||
|
};
|
||||||
|
Q_DECLARE_FLAGS(DockWidgetFeatures, DockWidgetFeature)
|
||||||
|
|
||||||
|
enum eState { StateHidden, StateDocked, StateFloating };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the widget for the dock widget to widget.
|
||||||
|
* The InsertMode defines how the widget is inserted into the dock widget.
|
||||||
|
* The content of a dock widget should be resizable do a very small size to
|
||||||
|
* prevent the dock widget from blocking the resizing. To ensure, that a
|
||||||
|
* dock widget can be resized very well, it is better to insert the content+
|
||||||
|
* widget into a scroll area or to provide a widget that is already a scroll
|
||||||
|
* area or that contains a scroll area.
|
||||||
|
* If the InsertMode is AutoScrollArea, the DockWidget tries to automatically
|
||||||
|
* detect how to insert the given widget. If the widget is derived from
|
||||||
|
* QScrollArea (i.e. an QAbstractItemView), then the widget is inserted
|
||||||
|
* directly. If the given widget is not a scroll area, the widget will be
|
||||||
|
* inserted into a scroll area.
|
||||||
|
* To force insertion into a scroll area, you can also provide the InsertMode
|
||||||
|
* ForceScrollArea. To prevent insertion into a scroll area, you can
|
||||||
|
* provide the InsertMode ForceNoScrollArea
|
||||||
|
*/
|
||||||
|
enum eInsertMode { AutoScrollArea, ForceScrollArea, ForceNoScrollArea };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This mode configures the behavior of the toggle view action.
|
||||||
|
* If the mode if ActionModeToggle, then the toggle view action is
|
||||||
|
* a checkable action to show / hide the dock widget. If the mode
|
||||||
|
* is ActionModeShow, then the action is not checkable an it will
|
||||||
|
* always show the dock widget if clicked. If the mode is ActionModeShow,
|
||||||
|
* the user can only close the DockWidget with the close button.
|
||||||
|
*/
|
||||||
|
enum eToggleViewActionMode {
|
||||||
|
ActionModeToggle, //!< ActionModeToggle
|
||||||
|
ActionModeShow //!< ActionModeShow
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This constructor creates a dock widget with the given title.
|
||||||
|
* The title is the text that is shown in the window title when the dock
|
||||||
|
* widget is floating and it is the title that is shown in the titlebar
|
||||||
|
* or the tab of this dock widget if it is tabified.
|
||||||
|
* The object name of the dock widget is also set to the title. The
|
||||||
|
* object name is required by the dock manager to properly save and restore
|
||||||
|
* the state of the dock widget. That means, the title needs to be unique.
|
||||||
|
* If your title is not unique or if you would like to change the title
|
||||||
|
* during runtime, you need to set a unique object name explicitly
|
||||||
|
* by calling setObjectName() after construction.
|
||||||
|
* Use the layoutFlags to configure the layout of the dock widget.
|
||||||
|
*/
|
||||||
|
DockWidget(const QString &uniqueId, QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual Destructor
|
||||||
|
*/
|
||||||
|
virtual ~DockWidget() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We return a fixed minimum size hint for all dock widgets
|
||||||
|
*/
|
||||||
|
virtual QSize minimumSizeHint() const override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the widget for the dock widget to widget.
|
||||||
|
* The InsertMode defines how the widget is inserted into the dock widget.
|
||||||
|
* The content of a dock widget should be resizable do a very small size to
|
||||||
|
* prevent the dock widget from blocking the resizing. To ensure, that a
|
||||||
|
* dock widget can be resized very well, it is better to insert the content+
|
||||||
|
* widget into a scroll area or to provide a widget that is already a scroll
|
||||||
|
* area or that contains a scroll area.
|
||||||
|
* If the InsertMode is AutoScrollArea, the DockWidget tries to automatically
|
||||||
|
* detect how to insert the given widget. If the widget is derived from
|
||||||
|
* QScrollArea (i.e. an QAbstractItemView), then the widget is inserted
|
||||||
|
* directly. If the given widget is not a scroll area, the widget will be
|
||||||
|
* inserted into a scroll area.
|
||||||
|
* To force insertion into a scroll area, you can also provide the InsertMode
|
||||||
|
* ForceScrollArea. To prevent insertion into a scroll area, you can
|
||||||
|
* provide the InsertMode ForceNoScrollArea
|
||||||
|
*/
|
||||||
|
void setWidget(QWidget *widget, eInsertMode insertMode = AutoScrollArea);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the widget from the dock and give ownership back to the caller
|
||||||
|
*/
|
||||||
|
QWidget *takeWidget();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the widget for the dock widget. This function returns zero if
|
||||||
|
* the widget has not been set.
|
||||||
|
*/
|
||||||
|
QWidget *widget() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the tab widget of this dock widget that is shown in the dock
|
||||||
|
* area title bar
|
||||||
|
*/
|
||||||
|
DockWidgetTab *tabWidget() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets, whether the dock widget is movable, closable, and floatable.
|
||||||
|
*/
|
||||||
|
void setFeatures(DockWidgetFeatures features);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the feature flag for this dock widget if on is true; otherwise
|
||||||
|
* clears the flag.
|
||||||
|
*/
|
||||||
|
void setFeature(DockWidgetFeature flag, bool on);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This property holds whether the dock widget is movable, closable, and
|
||||||
|
* floatable.
|
||||||
|
* By default, this property is set to a combination of DockWidgetClosable,
|
||||||
|
* DockWidgetMovable and DockWidgetFloatable.
|
||||||
|
*/
|
||||||
|
DockWidgetFeatures features() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the dock manager that manages the dock widget or 0 if the widget
|
||||||
|
* has not been assigned to any dock manager yet
|
||||||
|
*/
|
||||||
|
DockManager *dockManager() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the dock container widget this dock area widget belongs to or 0
|
||||||
|
* if this dock widget has not been docked yet
|
||||||
|
*/
|
||||||
|
DockContainerWidget *dockContainer() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the dock area widget this dock widget belongs to or 0
|
||||||
|
* if this dock widget has not been docked yet
|
||||||
|
*/
|
||||||
|
DockAreaWidget *dockAreaWidget() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This property holds whether the dock widget is floating.
|
||||||
|
* A dock widget is only floating, if it is the one and only widget inside
|
||||||
|
* of a floating container. If there are more than one dock widget in a
|
||||||
|
* floating container, the all dock widgets are docked and not floating.
|
||||||
|
*/
|
||||||
|
bool isFloating() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function returns true, if this dock widget is in a floating.
|
||||||
|
* The function returns true, if the dock widget is floating and it also
|
||||||
|
* returns true if it is docked inside of a floating container.
|
||||||
|
*/
|
||||||
|
bool isInFloatingContainer() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true, if this dock widget is closed.
|
||||||
|
*/
|
||||||
|
bool isClosed() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a checkable action that can be used to show or close this dock widget.
|
||||||
|
* The action's text is set to the dock widget's window title.
|
||||||
|
*/
|
||||||
|
QAction *toggleViewAction() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the behavior of the toggle view action.
|
||||||
|
* \see eToggleViewActionMode for a detailed description
|
||||||
|
*/
|
||||||
|
void setToggleViewActionMode(eToggleViewActionMode mode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the dock widget icon that is shown in tabs and in toggle view
|
||||||
|
* actions
|
||||||
|
*/
|
||||||
|
void setIcon(const QIcon &icon);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the icon that has been assigned to the dock widget
|
||||||
|
*/
|
||||||
|
QIcon icon() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the WithToolBar layout flag is enabled, then this function returns
|
||||||
|
* the dock widget toolbar. If the flag is disabled, the function returns
|
||||||
|
* a nullptr.
|
||||||
|
* This function returns the dock widget top tool bar.
|
||||||
|
* If no toolbar is assigned, this function returns nullptr. To get a valid
|
||||||
|
* toolbar you either need to create a default empty toolbar via
|
||||||
|
* createDefaultToolBar() function or you need to assign you custom
|
||||||
|
* toolbar via setToolBar().
|
||||||
|
*/
|
||||||
|
QToolBar *toolBar() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If you would like to use the default top tool bar, then call this
|
||||||
|
* function to create the default tool bar.
|
||||||
|
* After this function the toolBar() function will return a valid toolBar()
|
||||||
|
* object.
|
||||||
|
*/
|
||||||
|
QToolBar *createDefaultToolBar();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assign a new tool bar that is shown above the content widget.
|
||||||
|
* The dock widget will become the owner of the tool bar and deletes it
|
||||||
|
* on destruction
|
||||||
|
*/
|
||||||
|
void setToolBar(QToolBar *toolBar);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function sets the tool button style for the given dock widget state.
|
||||||
|
* It is possible to switch the tool button style depending on the state.
|
||||||
|
* If a dock widget is floating, then here are more space and it is
|
||||||
|
* possible to select a style that requires more space like
|
||||||
|
* Qt::ToolButtonTextUnderIcon. For the docked state Qt::ToolButtonIconOnly
|
||||||
|
* might be better.
|
||||||
|
*/
|
||||||
|
void setToolBarStyle(Qt::ToolButtonStyle style, eState state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the tool button style for the given docking state.
|
||||||
|
* \see setToolBarStyle()
|
||||||
|
*/
|
||||||
|
Qt::ToolButtonStyle toolBarStyle(eState state) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function sets the tool button icon size for the given state.
|
||||||
|
* If a dock widget is floating, there is more space an increasing the
|
||||||
|
* icon size is possible. For docked widgets, small icon sizes, eg. 16 x 16
|
||||||
|
* might be better.
|
||||||
|
*/
|
||||||
|
void setToolBarIconSize(const QSize &iconSize, eState state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the icon size for a given docking state.
|
||||||
|
* \see setToolBarIconSize()
|
||||||
|
*/
|
||||||
|
QSize toolBarIconSize(eState state) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the actions that will be shown in the dock area title bar
|
||||||
|
* if this dock widget is the active tab.
|
||||||
|
* You should not add to many actions to the title bar, because this
|
||||||
|
* will remove the available space for the tabs. If you have a number
|
||||||
|
* of actions, just add an action with a menu to show a popup menu
|
||||||
|
* button in the title bar.
|
||||||
|
*/
|
||||||
|
void setTitleBarActions(QList<QAction *> actions);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of actions that will be inserted into the dock area title
|
||||||
|
* bar if this dock widget becomes the current widget
|
||||||
|
*/
|
||||||
|
virtual QList<QAction *> titleBarActions() const;
|
||||||
|
|
||||||
|
#ifndef QT_NO_TOOLTIP
|
||||||
|
/**
|
||||||
|
* This is function sets text tooltip for title bar widget
|
||||||
|
* and tooltip for toggle view action
|
||||||
|
*/
|
||||||
|
void setTabToolTip(const QString &text);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public: // reimplements QFrame
|
||||||
|
/**
|
||||||
|
* Emits titleChanged signal if title change event occurs
|
||||||
|
*/
|
||||||
|
virtual bool event(QEvent *event) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This property controls whether the dock widget is open or closed.
|
||||||
|
* The toogleViewAction triggers this slot
|
||||||
|
*/
|
||||||
|
void toggleView(bool open = true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function will make a docked widget floating
|
||||||
|
*/
|
||||||
|
void setFloating();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function will delete the dock widget and its content from the
|
||||||
|
* docking system
|
||||||
|
*/
|
||||||
|
void deleteDockWidget();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the dock widget
|
||||||
|
*/
|
||||||
|
void closeDockWidget();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
/**
|
||||||
|
* This signal is emitted if the dock widget is opened or closed
|
||||||
|
*/
|
||||||
|
void viewToggled(bool open);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This signal is emitted if the dock widget is closed
|
||||||
|
*/
|
||||||
|
void closed();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This signal is emitted if the window title of this dock widget
|
||||||
|
* changed
|
||||||
|
*/
|
||||||
|
void titleChanged(const QString &title);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This signal is emitted when the floating property changes.
|
||||||
|
* The topLevel parameter is true if the dock widget is now floating;
|
||||||
|
* otherwise it is false.
|
||||||
|
*/
|
||||||
|
void topLevelChanged(bool topLevel);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This signal is emitted, if close is requested
|
||||||
|
*/
|
||||||
|
void closeRequested();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This signal is emitted when the dock widget becomes visible (or invisible).
|
||||||
|
* This happens when the widget is hidden or shown, as well as when it is
|
||||||
|
* docked in a tabbed dock area and its tab becomes selected or unselected.
|
||||||
|
*/
|
||||||
|
void visibilityChanged(bool visible);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This signal is emitted when the features property changes.
|
||||||
|
* The features parameter gives the new value of the property.
|
||||||
|
*/
|
||||||
|
void featuresChanged(DockWidgetFeatures features);
|
||||||
|
}; // class DockWidget
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
525
src/libs/advanceddockingsystem/dockwidgettab.cpp
Normal file
525
src/libs/advanceddockingsystem/dockwidgettab.cpp
Normal file
@@ -0,0 +1,525 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 Uwe Kindler
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "dockwidgettab.h"
|
||||||
|
|
||||||
|
#include "ads_globals.h"
|
||||||
|
#include "dockareawidget.h"
|
||||||
|
#include "dockmanager.h"
|
||||||
|
#include "dockoverlay.h"
|
||||||
|
#include "dockwidget.h"
|
||||||
|
#include "elidinglabel.h"
|
||||||
|
#include "floatingdockcontainer.h"
|
||||||
|
#include "floatingdragpreview.h"
|
||||||
|
#include "iconprovider.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QBoxLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QMouseEvent>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QSplitter>
|
||||||
|
#include <QStyle>
|
||||||
|
#include <QToolButton>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtDebugMsg)
|
||||||
|
|
||||||
|
namespace ADS
|
||||||
|
{
|
||||||
|
using TabLabelType = ElidingLabel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private data class of DockWidgetTab class (pimpl)
|
||||||
|
*/
|
||||||
|
struct DockWidgetTabPrivate
|
||||||
|
{
|
||||||
|
DockWidgetTab *q;
|
||||||
|
DockWidget *m_dockWidget;
|
||||||
|
QLabel *m_iconLabel = nullptr;
|
||||||
|
TabLabelType *m_titleLabel;
|
||||||
|
QPoint m_globalDragStartMousePosition;
|
||||||
|
QPoint m_dragStartMousePosition;
|
||||||
|
bool m_isActiveTab = false;
|
||||||
|
DockAreaWidget *m_dockArea = nullptr;
|
||||||
|
eDragState m_dragState = DraggingInactive;
|
||||||
|
AbstractFloatingWidget *m_floatingWidget = nullptr;
|
||||||
|
QIcon m_icon;
|
||||||
|
QAbstractButton *m_closeButton = nullptr;
|
||||||
|
QSpacerItem *m_iconTextSpacer;
|
||||||
|
QPoint m_tabDragStartPosition;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private data constructor
|
||||||
|
*/
|
||||||
|
DockWidgetTabPrivate(DockWidgetTab *parent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the complete layout including all controls
|
||||||
|
*/
|
||||||
|
void createLayout();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves the tab depending on the position in the given mouse event
|
||||||
|
*/
|
||||||
|
void moveTab(QMouseEvent *event);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test function for current drag state
|
||||||
|
*/
|
||||||
|
bool isDraggingState(eDragState dragState) const { return this->m_dragState == dragState; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts floating of the dock widget that belongs to this title bar
|
||||||
|
* Returns true, if floating has been started and false if floating
|
||||||
|
* is not possible for any reason
|
||||||
|
*/
|
||||||
|
bool startFloating(eDragState draggingState = DraggingFloatingWidget);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the given config flag is set
|
||||||
|
*/
|
||||||
|
bool testConfigFlag(DockManager::eConfigFlag flag) const
|
||||||
|
{
|
||||||
|
return DockManager::configFlags().testFlag(flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the close button as QPushButton or as QToolButton
|
||||||
|
*/
|
||||||
|
QAbstractButton *createCloseButton() const
|
||||||
|
{
|
||||||
|
if (testConfigFlag(DockManager::TabCloseButtonIsToolButton)) {
|
||||||
|
auto button = new QToolButton();
|
||||||
|
button->setAutoRaise(true);
|
||||||
|
return button;
|
||||||
|
} else {
|
||||||
|
return new QPushButton();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
AbstractFloatingWidget *createFloatingWidget(T *widget, bool opaqueUndocking)
|
||||||
|
{
|
||||||
|
if (opaqueUndocking) {
|
||||||
|
return new FloatingDockContainer(widget);
|
||||||
|
} else {
|
||||||
|
auto w = new FloatingDragPreview(widget);
|
||||||
|
QObject::connect(w, &FloatingDragPreview::draggingCanceled, q, [=]() {
|
||||||
|
m_dragState = DraggingInactive;
|
||||||
|
});
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the drag start position in global and local coordinates
|
||||||
|
*/
|
||||||
|
void saveDragStartMousePosition(const QPoint &globalPos)
|
||||||
|
{
|
||||||
|
m_globalDragStartMousePosition = globalPos;
|
||||||
|
m_dragStartMousePosition = q->mapFromGlobal(globalPos);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// struct DockWidgetTabPrivate
|
||||||
|
|
||||||
|
DockWidgetTabPrivate::DockWidgetTabPrivate(DockWidgetTab *parent)
|
||||||
|
: q(parent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void DockWidgetTabPrivate::createLayout()
|
||||||
|
{
|
||||||
|
m_titleLabel = new TabLabelType();
|
||||||
|
m_titleLabel->setElideMode(Qt::ElideRight);
|
||||||
|
m_titleLabel->setText(m_dockWidget->windowTitle());
|
||||||
|
m_titleLabel->setObjectName("dockWidgetTabLabel");
|
||||||
|
m_titleLabel->setAlignment(Qt::AlignCenter);
|
||||||
|
QObject::connect(m_titleLabel, &ElidingLabel::elidedChanged, q, &DockWidgetTab::elidedChanged);
|
||||||
|
|
||||||
|
m_closeButton = createCloseButton();
|
||||||
|
m_closeButton->setObjectName("tabCloseButton");
|
||||||
|
internal::setButtonIcon(m_closeButton, QStyle::SP_TitleBarCloseButton, TabCloseIcon);
|
||||||
|
m_closeButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||||
|
q->onDockWidgetFeaturesChanged();
|
||||||
|
internal::setToolTip(m_closeButton, QObject::tr("Close Tab"));
|
||||||
|
QObject::connect(m_closeButton,
|
||||||
|
&QAbstractButton::clicked,
|
||||||
|
q,
|
||||||
|
&DockWidgetTab::closeRequested);
|
||||||
|
|
||||||
|
QFontMetrics fontMetrics(m_titleLabel->font());
|
||||||
|
int spacing = qRound(fontMetrics.height() / 4.0);
|
||||||
|
|
||||||
|
// Fill the layout
|
||||||
|
QBoxLayout *boxLayout = new QBoxLayout(QBoxLayout::LeftToRight);
|
||||||
|
boxLayout->setContentsMargins(2 * spacing, 0, 0, 0);
|
||||||
|
boxLayout->setSpacing(0);
|
||||||
|
q->setLayout(boxLayout);
|
||||||
|
boxLayout->addWidget(m_titleLabel, 1);
|
||||||
|
boxLayout->addSpacing(spacing);
|
||||||
|
boxLayout->addWidget(m_closeButton);
|
||||||
|
boxLayout->addSpacing(qRound(spacing * 4.0 / 3.0));
|
||||||
|
boxLayout->setAlignment(Qt::AlignCenter);
|
||||||
|
|
||||||
|
m_titleLabel->setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockWidgetTabPrivate::moveTab(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
event->accept();
|
||||||
|
QPoint distance = event->globalPos() - m_globalDragStartMousePosition;
|
||||||
|
distance.setY(0);
|
||||||
|
auto targetPos = distance + m_tabDragStartPosition;
|
||||||
|
targetPos.rx() = qMax(targetPos.x(), 0);
|
||||||
|
targetPos.rx() = qMin(q->parentWidget()->rect().right() - q->width() + 1, targetPos.rx());
|
||||||
|
q->move(targetPos);
|
||||||
|
q->raise();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DockWidgetTabPrivate::startFloating(eDragState draggingState)
|
||||||
|
{
|
||||||
|
auto dockContainer = m_dockWidget->dockContainer();
|
||||||
|
qCInfo(adsLog) << "isFloating " << dockContainer->isFloating();
|
||||||
|
qCInfo(adsLog) << "areaCount " << dockContainer->dockAreaCount();
|
||||||
|
qCInfo(adsLog) << "widgetCount " << m_dockWidget->dockAreaWidget()->dockWidgetsCount();
|
||||||
|
// if this is the last dock widget inside of this floating widget,
|
||||||
|
// then it does not make any sense, to make it floating because
|
||||||
|
// it is already floating
|
||||||
|
if (dockContainer->isFloating() && (dockContainer->visibleDockAreaCount() == 1)
|
||||||
|
&& (m_dockWidget->dockAreaWidget()->dockWidgetsCount() == 1)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
qCInfo(adsLog) << "startFloating";
|
||||||
|
m_dragState = draggingState;
|
||||||
|
QSize size = m_dockArea->size();
|
||||||
|
AbstractFloatingWidget *floatingWidget = nullptr;
|
||||||
|
bool opaqueUndocking = DockManager::configFlags().testFlag(DockManager::OpaqueUndocking)
|
||||||
|
|| (DraggingFloatingWidget != draggingState);
|
||||||
|
|
||||||
|
// If section widget has multiple tabs, we take only one tab
|
||||||
|
// If it has only one single tab, we can move the complete
|
||||||
|
// dock area into floating widget
|
||||||
|
if (m_dockArea->dockWidgetsCount() > 1) {
|
||||||
|
floatingWidget = createFloatingWidget(m_dockWidget, opaqueUndocking);
|
||||||
|
} else {
|
||||||
|
floatingWidget = createFloatingWidget(m_dockArea, opaqueUndocking);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DraggingFloatingWidget == draggingState) {
|
||||||
|
floatingWidget->startFloating(m_dragStartMousePosition, size, DraggingFloatingWidget, q);
|
||||||
|
auto Overlay = m_dockWidget->dockManager()->containerOverlay();
|
||||||
|
Overlay->setAllowedAreas(OuterDockAreas);
|
||||||
|
this->m_floatingWidget = floatingWidget;
|
||||||
|
} else {
|
||||||
|
floatingWidget->startFloating(m_dragStartMousePosition, size, DraggingInactive, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DockWidgetTab::DockWidgetTab(DockWidget *dockWidget, QWidget *parent)
|
||||||
|
: QFrame(parent)
|
||||||
|
, d(new DockWidgetTabPrivate(this))
|
||||||
|
{
|
||||||
|
setAttribute(Qt::WA_NoMousePropagation, true);
|
||||||
|
d->m_dockWidget = dockWidget;
|
||||||
|
d->createLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
DockWidgetTab::~DockWidgetTab()
|
||||||
|
{
|
||||||
|
qCInfo(adsLog) << Q_FUNC_INFO;
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockWidgetTab::mousePressEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
if (event->button() == Qt::LeftButton) {
|
||||||
|
event->accept();
|
||||||
|
d->saveDragStartMousePosition(event->globalPos());
|
||||||
|
d->m_dragState = DraggingMousePressed;
|
||||||
|
emit clicked();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Super::mousePressEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockWidgetTab::mouseReleaseEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
if (event->button() == Qt::LeftButton) {
|
||||||
|
auto currentDragState = d->m_dragState;
|
||||||
|
d->m_globalDragStartMousePosition = QPoint();
|
||||||
|
d->m_dragStartMousePosition = QPoint();
|
||||||
|
d->m_dragState = DraggingInactive;
|
||||||
|
|
||||||
|
switch (currentDragState) {
|
||||||
|
case DraggingTab:
|
||||||
|
// End of tab moving, emit signal
|
||||||
|
if (d->m_dockArea) {
|
||||||
|
emit moved(event->globalPos());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DraggingFloatingWidget:
|
||||||
|
d->m_floatingWidget->finishDragging();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:; // do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Super::mouseReleaseEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockWidgetTab::mouseMoveEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
if (!(event->buttons() & Qt::LeftButton) || d->isDraggingState(DraggingInactive)) {
|
||||||
|
d->m_dragState = DraggingInactive;
|
||||||
|
Super::mouseMoveEvent(event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// move floating window
|
||||||
|
if (d->isDraggingState(DraggingFloatingWidget)) {
|
||||||
|
d->m_floatingWidget->moveFloating();
|
||||||
|
Super::mouseMoveEvent(event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// move tab
|
||||||
|
if (d->isDraggingState(DraggingTab)) {
|
||||||
|
// Moving the tab is always allowed because it does not mean moving the
|
||||||
|
// dock widget around
|
||||||
|
d->moveTab(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mappedPos = mapToParent(event->pos());
|
||||||
|
bool mouseOutsideBar = (mappedPos.x() < 0) || (mappedPos.x() > parentWidget()->rect().right());
|
||||||
|
// Maybe a fixed drag distance is better here ?
|
||||||
|
int dragDistanceY = qAbs(d->m_globalDragStartMousePosition.y() - event->globalPos().y());
|
||||||
|
if (dragDistanceY >= DockManager::startDragDistance() || mouseOutsideBar) {
|
||||||
|
// If this is the last dock area in a dock container with only
|
||||||
|
// one single dock widget it does not make sense to move it to a new
|
||||||
|
// floating widget and leave this one empty
|
||||||
|
if (d->m_dockArea->dockContainer()->isFloating()
|
||||||
|
&& d->m_dockArea->openDockWidgetsCount() == 1
|
||||||
|
&& d->m_dockArea->dockContainer()->visibleDockAreaCount() == 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Floating is only allowed for widgets that are floatable
|
||||||
|
// If we do non opaque undocking, then can create the drag preview
|
||||||
|
// if the widget is movable.
|
||||||
|
auto features = d->m_dockWidget->features();
|
||||||
|
if (features.testFlag(DockWidget::DockWidgetFloatable)
|
||||||
|
|| (features.testFlag(DockWidget::DockWidgetMovable)
|
||||||
|
&& !DockManager::testConfigFlag(DockManager::OpaqueUndocking))) {
|
||||||
|
// If we undock, we need to restore the initial position of this
|
||||||
|
// tab because it looks strange if it remains on its dragged position
|
||||||
|
if (d->isDraggingState(DraggingTab)
|
||||||
|
&& !DockManager::configFlags().testFlag(DockManager::OpaqueUndocking)) {
|
||||||
|
parentWidget()->layout()->update();
|
||||||
|
}
|
||||||
|
d->startFloating();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else if (d->m_dockArea->openDockWidgetsCount() > 1
|
||||||
|
&& (event->globalPos() - d->m_globalDragStartMousePosition).manhattanLength()
|
||||||
|
>= QApplication::startDragDistance()) // Wait a few pixels before start moving
|
||||||
|
{
|
||||||
|
// If we start dragging the tab, we save its initial position to
|
||||||
|
// restore it later
|
||||||
|
if (DraggingTab != d->m_dragState) {
|
||||||
|
d->m_tabDragStartPosition = this->pos();
|
||||||
|
}
|
||||||
|
d->m_dragState = DraggingTab;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Super::mouseMoveEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockWidgetTab::contextMenuEvent(QContextMenuEvent *event)
|
||||||
|
{
|
||||||
|
event->accept();
|
||||||
|
if (d->isDraggingState(DraggingFloatingWidget)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
d->saveDragStartMousePosition(event->globalPos());
|
||||||
|
QMenu menu(this);
|
||||||
|
|
||||||
|
const bool isFloatable = d->m_dockWidget->features().testFlag(DockWidget::DockWidgetFloatable);
|
||||||
|
const bool isNotOnlyTabInContainer = !d->m_dockArea->dockContainer()->hasTopLevelDockWidget();
|
||||||
|
const bool isDetachable = isFloatable && isNotOnlyTabInContainer;
|
||||||
|
|
||||||
|
auto action = menu.addAction(tr("Detach"), this, &DockWidgetTab::detachDockWidget);
|
||||||
|
action->setEnabled(isDetachable);
|
||||||
|
menu.addSeparator();
|
||||||
|
action = menu.addAction(tr("Close"), this, &DockWidgetTab::closeRequested);
|
||||||
|
action->setEnabled(isClosable());
|
||||||
|
menu.addAction(tr("Close Others"), this, &DockWidgetTab::closeOtherTabsRequested);
|
||||||
|
menu.exec(event->globalPos());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DockWidgetTab::isActiveTab() const { return d->m_isActiveTab; }
|
||||||
|
|
||||||
|
void DockWidgetTab::setActiveTab(bool active)
|
||||||
|
{
|
||||||
|
bool dockWidgetClosable = d->m_dockWidget->features().testFlag(
|
||||||
|
DockWidget::DockWidgetClosable);
|
||||||
|
bool activeTabHasCloseButton = d->testConfigFlag(DockManager::ActiveTabHasCloseButton);
|
||||||
|
bool allTabsHaveCloseButton = d->testConfigFlag(DockManager::AllTabsHaveCloseButton);
|
||||||
|
bool tabHasCloseButton = (activeTabHasCloseButton && active) | allTabsHaveCloseButton;
|
||||||
|
d->m_closeButton->setVisible(dockWidgetClosable && tabHasCloseButton);
|
||||||
|
if (d->m_isActiveTab == active) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
d->m_isActiveTab = active;
|
||||||
|
|
||||||
|
style()->unpolish(this);
|
||||||
|
style()->polish(this);
|
||||||
|
d->m_titleLabel->style()->unpolish(d->m_titleLabel);
|
||||||
|
d->m_titleLabel->style()->polish(d->m_titleLabel);
|
||||||
|
update();
|
||||||
|
updateGeometry();
|
||||||
|
|
||||||
|
emit activeTabChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
DockWidget *DockWidgetTab::dockWidget() const { return d->m_dockWidget; }
|
||||||
|
|
||||||
|
void DockWidgetTab::setDockAreaWidget(DockAreaWidget *dockArea) { d->m_dockArea = dockArea; }
|
||||||
|
|
||||||
|
DockAreaWidget *DockWidgetTab::dockAreaWidget() const { return d->m_dockArea; }
|
||||||
|
|
||||||
|
void DockWidgetTab::setIcon(const QIcon &icon)
|
||||||
|
{
|
||||||
|
QBoxLayout *boxLayout = qobject_cast<QBoxLayout *>(layout());
|
||||||
|
if (!d->m_iconLabel && icon.isNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!d->m_iconLabel) {
|
||||||
|
d->m_iconLabel = new QLabel();
|
||||||
|
d->m_iconLabel->setAlignment(Qt::AlignVCenter);
|
||||||
|
d->m_iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
|
||||||
|
internal::setToolTip(d->m_iconLabel, d->m_titleLabel->toolTip());
|
||||||
|
boxLayout->insertWidget(0, d->m_iconLabel, Qt::AlignVCenter);
|
||||||
|
boxLayout->insertSpacing(1, qRound(1.5 * boxLayout->contentsMargins().left() / 2.0));
|
||||||
|
} else if (icon.isNull()) {
|
||||||
|
// Remove icon label and spacer item
|
||||||
|
boxLayout->removeWidget(d->m_iconLabel);
|
||||||
|
boxLayout->removeItem(boxLayout->itemAt(0));
|
||||||
|
delete d->m_iconLabel;
|
||||||
|
d->m_iconLabel = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
d->m_icon = icon;
|
||||||
|
if (d->m_iconLabel) {
|
||||||
|
d->m_iconLabel->setPixmap(
|
||||||
|
icon.pixmap(style()->pixelMetric(QStyle::PM_SmallIconSize, nullptr, this)));
|
||||||
|
d->m_iconLabel->setVisible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const QIcon &DockWidgetTab::icon() const { return d->m_icon; }
|
||||||
|
|
||||||
|
QString DockWidgetTab::text() const { return d->m_titleLabel->text(); }
|
||||||
|
|
||||||
|
void DockWidgetTab::mouseDoubleClickEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
// If this is the last dock area in a dock container it does not make
|
||||||
|
// sense to move it to a new floating widget and leave this one empty
|
||||||
|
if ((!d->m_dockArea->dockContainer()->isFloating() || d->m_dockArea->dockWidgetsCount() > 1)
|
||||||
|
&& d->m_dockWidget->features().testFlag(DockWidget::DockWidgetFloatable)) {
|
||||||
|
d->saveDragStartMousePosition(event->globalPos());
|
||||||
|
d->startFloating(DraggingInactive);
|
||||||
|
}
|
||||||
|
|
||||||
|
Super::mouseDoubleClickEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockWidgetTab::setVisible(bool visible)
|
||||||
|
{
|
||||||
|
// Just here for debugging to insert debug output
|
||||||
|
Super::setVisible(visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockWidgetTab::setText(const QString &title) { d->m_titleLabel->setText(title); }
|
||||||
|
bool DockWidgetTab::isTitleElided() const { return d->m_titleLabel->isElided(); }
|
||||||
|
|
||||||
|
bool DockWidgetTab::isClosable() const
|
||||||
|
{
|
||||||
|
return d->m_dockWidget
|
||||||
|
&& d->m_dockWidget->features().testFlag(DockWidget::DockWidgetClosable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockWidgetTab::detachDockWidget()
|
||||||
|
{
|
||||||
|
if (!d->m_dockWidget->features().testFlag(DockWidget::DockWidgetFloatable)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
d->saveDragStartMousePosition(QCursor::pos());
|
||||||
|
d->startFloating(DraggingInactive);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DockWidgetTab::event(QEvent *event)
|
||||||
|
{
|
||||||
|
#ifndef QT_NO_TOOLTIP
|
||||||
|
if (event->type() == QEvent::ToolTipChange) {
|
||||||
|
const auto text = toolTip();
|
||||||
|
d->m_titleLabel->setToolTip(text);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return Super::event(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DockWidgetTab::onDockWidgetFeaturesChanged()
|
||||||
|
{
|
||||||
|
auto features = d->m_dockWidget->features();
|
||||||
|
auto sizePolicy = d->m_closeButton->sizePolicy();
|
||||||
|
sizePolicy.setRetainSizeWhenHidden(
|
||||||
|
features.testFlag(DockWidget::DockWidgetClosable)
|
||||||
|
&& d->testConfigFlag(DockManager::RetainTabSizeWhenCloseButtonHidden));
|
||||||
|
d->m_closeButton->setSizePolicy(sizePolicy);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
164
src/libs/advanceddockingsystem/dockwidgettab.h
Normal file
164
src/libs/advanceddockingsystem/dockwidgettab.h
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 Uwe Kindler
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ads_globals.h"
|
||||||
|
|
||||||
|
#include <QFrame>
|
||||||
|
|
||||||
|
namespace ADS {
|
||||||
|
|
||||||
|
class DockWidget;
|
||||||
|
class DockAreaWidget;
|
||||||
|
struct DockWidgetTabPrivate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A dock widget tab that shows a title and an icon.
|
||||||
|
* The dock widget tab is shown in the dock area title bar to switch between
|
||||||
|
* tabbed dock widgets
|
||||||
|
*/
|
||||||
|
class ADS_EXPORT DockWidgetTab : public QFrame
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(bool activeTab READ isActiveTab WRITE setActiveTab NOTIFY activeTabChanged)
|
||||||
|
|
||||||
|
private:
|
||||||
|
DockWidgetTabPrivate *d; ///< private data (pimpl)
|
||||||
|
friend struct DockWidgetTabPrivate;
|
||||||
|
friend class DockWidget;
|
||||||
|
void onDockWidgetFeaturesChanged();
|
||||||
|
void detachDockWidget();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void mousePressEvent(QMouseEvent *event) override;
|
||||||
|
virtual void mouseReleaseEvent(QMouseEvent *event) override;
|
||||||
|
virtual void mouseMoveEvent(QMouseEvent *event) override;
|
||||||
|
virtual void contextMenuEvent(QContextMenuEvent *event) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Double clicking the tab widget makes the assigned dock widget floating
|
||||||
|
*/
|
||||||
|
virtual void mouseDoubleClickEvent(QMouseEvent *event) override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using Super = QFrame;
|
||||||
|
/**
|
||||||
|
* Default Constructor
|
||||||
|
* param[in] DockWidget The dock widget this title bar belongs to
|
||||||
|
* param[in] parent The parent widget of this title bar
|
||||||
|
*/
|
||||||
|
DockWidgetTab(DockWidget *DockWidget, QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual Destructor
|
||||||
|
*/
|
||||||
|
virtual ~DockWidgetTab() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true, if this is the active tab
|
||||||
|
*/
|
||||||
|
bool isActiveTab() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set this true to make this tab the active tab
|
||||||
|
*/
|
||||||
|
void setActiveTab(bool active);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the dock widget this title widget belongs to
|
||||||
|
*/
|
||||||
|
DockWidget *dockWidget() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the dock area widget the dockWidget returned by dockWidget()
|
||||||
|
* function belongs to.
|
||||||
|
*/
|
||||||
|
void setDockAreaWidget(DockAreaWidget *dockArea);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the dock area widget this title bar belongs to.
|
||||||
|
* \return This function returns 0 if the dock widget that owns this title
|
||||||
|
* bar widget has not been added to any dock area yet.
|
||||||
|
*/
|
||||||
|
DockAreaWidget *dockAreaWidget() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the icon to show in title bar
|
||||||
|
*/
|
||||||
|
void setIcon(const QIcon &icon);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the icon
|
||||||
|
*/
|
||||||
|
const QIcon &icon() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the tab text
|
||||||
|
*/
|
||||||
|
QString text() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the tab text
|
||||||
|
*/
|
||||||
|
void setText(const QString &title);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if text is elided on the tab's title
|
||||||
|
*/
|
||||||
|
bool isTitleElided() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function returns true if the assigned dock widget is closable
|
||||||
|
*/
|
||||||
|
bool isClosable() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Track event ToolTipChange and set child ToolTip
|
||||||
|
*/
|
||||||
|
virtual bool event(QEvent *event) override;
|
||||||
|
|
||||||
|
virtual void setVisible(bool visible) override;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void activeTabChanged();
|
||||||
|
void clicked();
|
||||||
|
void closeRequested();
|
||||||
|
void closeOtherTabsRequested();
|
||||||
|
void moved(const QPoint &globalPosition);
|
||||||
|
void elidedChanged(bool elided);
|
||||||
|
}; // class DockWidgetTab
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
184
src/libs/advanceddockingsystem/elidinglabel.cpp
Normal file
184
src/libs/advanceddockingsystem/elidinglabel.cpp
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 Uwe Kindler
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "elidinglabel.h"
|
||||||
|
|
||||||
|
#include <QMouseEvent>
|
||||||
|
|
||||||
|
namespace ADS {
|
||||||
|
/**
|
||||||
|
* Private data of public ClickableLabel
|
||||||
|
*/
|
||||||
|
struct ElidingLabelPrivate
|
||||||
|
{
|
||||||
|
ElidingLabel *q;
|
||||||
|
Qt::TextElideMode m_elideMode = Qt::ElideNone;
|
||||||
|
QString m_text;
|
||||||
|
bool m_isElided = false;
|
||||||
|
|
||||||
|
ElidingLabelPrivate(ElidingLabel *parent)
|
||||||
|
: q(parent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void elideText(int width);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience function to check if the
|
||||||
|
*/
|
||||||
|
bool isModeElideNone() const { return Qt::ElideNone == m_elideMode; }
|
||||||
|
};
|
||||||
|
|
||||||
|
void ElidingLabelPrivate::elideText(int width)
|
||||||
|
{
|
||||||
|
if (isModeElideNone())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QFontMetrics fm = q->fontMetrics();
|
||||||
|
QString str = fm.elidedText(m_text, m_elideMode, width - q->margin() * 2 - q->indent());
|
||||||
|
if (str == QChar(0x2026))
|
||||||
|
str = m_text.at(0);
|
||||||
|
|
||||||
|
bool wasElided = m_isElided;
|
||||||
|
m_isElided = str != m_text;
|
||||||
|
if (m_isElided != wasElided)
|
||||||
|
emit q->elidedChanged(m_isElided);
|
||||||
|
|
||||||
|
q->QLabel::setText(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
ElidingLabel::ElidingLabel(QWidget *parent, Qt::WindowFlags flags)
|
||||||
|
: QLabel(parent, flags)
|
||||||
|
, d(new ElidingLabelPrivate(this))
|
||||||
|
{}
|
||||||
|
|
||||||
|
ElidingLabel::ElidingLabel(const QString &text, QWidget *parent, Qt::WindowFlags flags)
|
||||||
|
: QLabel(text, parent, flags)
|
||||||
|
, d(new ElidingLabelPrivate(this))
|
||||||
|
{
|
||||||
|
d->m_text = text;
|
||||||
|
internal::setToolTip(this, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
ElidingLabel::~ElidingLabel()
|
||||||
|
{
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::TextElideMode ElidingLabel::elideMode() const
|
||||||
|
{
|
||||||
|
return d->m_elideMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ElidingLabel::setElideMode(Qt::TextElideMode mode)
|
||||||
|
{
|
||||||
|
d->m_elideMode = mode;
|
||||||
|
d->elideText(size().width());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ElidingLabel::isElided() const
|
||||||
|
{
|
||||||
|
return d->m_isElided;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ElidingLabel::mouseReleaseEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
Super::mouseReleaseEvent(event);
|
||||||
|
if (event->button() != Qt::LeftButton) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit clicked();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ElidingLabel::mouseDoubleClickEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
Q_UNUSED(event)
|
||||||
|
emit doubleClicked();
|
||||||
|
Super::mouseDoubleClickEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ElidingLabel::resizeEvent(QResizeEvent *event)
|
||||||
|
{
|
||||||
|
if (!d->isModeElideNone()) {
|
||||||
|
d->elideText(event->size().width());
|
||||||
|
}
|
||||||
|
Super::resizeEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize ElidingLabel::minimumSizeHint() const
|
||||||
|
{
|
||||||
|
if (pixmap() != nullptr || d->isModeElideNone()) {
|
||||||
|
return QLabel::minimumSizeHint();
|
||||||
|
}
|
||||||
|
const QFontMetrics &fm = fontMetrics();
|
||||||
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
|
||||||
|
QSize size(fm.horizontalAdvance(d->m_text.left(2) + "…"), fm.height());
|
||||||
|
#else
|
||||||
|
QSize size(fm.width(d->m_text.left(2) + "…"), fm.height());
|
||||||
|
#endif
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize ElidingLabel::sizeHint() const
|
||||||
|
{
|
||||||
|
if (pixmap() != nullptr || d->isModeElideNone()) {
|
||||||
|
return QLabel::sizeHint();
|
||||||
|
}
|
||||||
|
const QFontMetrics &fm = fontMetrics();
|
||||||
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
|
||||||
|
QSize size(fm.horizontalAdvance(d->m_text), QLabel::sizeHint().height());
|
||||||
|
#else
|
||||||
|
QSize size(fm.width(d->m_text), QLabel::sizeHint().height());
|
||||||
|
#endif
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ElidingLabel::setText(const QString &text)
|
||||||
|
{
|
||||||
|
if (d->isModeElideNone()) {
|
||||||
|
Super::setText(text);
|
||||||
|
} else {
|
||||||
|
d->m_text = text;
|
||||||
|
internal::setToolTip(this, text);
|
||||||
|
d->elideText(this->size().width());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ElidingLabel::text() const
|
||||||
|
{
|
||||||
|
return d->m_text;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
111
src/libs/advanceddockingsystem/elidinglabel.h
Normal file
111
src/libs/advanceddockingsystem/elidinglabel.h
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 Uwe Kindler
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ads_globals.h"
|
||||||
|
|
||||||
|
#include <QLabel>
|
||||||
|
|
||||||
|
namespace ADS {
|
||||||
|
|
||||||
|
struct ElidingLabelPrivate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A QLabel that supports eliding text.
|
||||||
|
* Because the functions setText() and text() are no virtual functions setting
|
||||||
|
* and reading the text via a pointer to the base class QLabel does not work
|
||||||
|
* properly
|
||||||
|
*/
|
||||||
|
class ADS_EXPORT ElidingLabel : public QLabel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private:
|
||||||
|
ElidingLabelPrivate *d;
|
||||||
|
friend struct ElidingLabelPrivate;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void mouseReleaseEvent(QMouseEvent *event) override;
|
||||||
|
virtual void resizeEvent(QResizeEvent *event) override;
|
||||||
|
virtual void mouseDoubleClickEvent(QMouseEvent *ev) override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using Super = QLabel;
|
||||||
|
|
||||||
|
ElidingLabel(QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::Widget);
|
||||||
|
ElidingLabel(const QString &text, QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::Widget);
|
||||||
|
virtual ~ElidingLabel() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the text elide mode.
|
||||||
|
* The default mode is ElideNone
|
||||||
|
*/
|
||||||
|
Qt::TextElideMode elideMode() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the text elide mode
|
||||||
|
*/
|
||||||
|
void setElideMode(Qt::TextElideMode mode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function indicates whether the text on this label is currently elided
|
||||||
|
*/
|
||||||
|
bool isElided() const;
|
||||||
|
|
||||||
|
public: // reimplements QLabel
|
||||||
|
virtual QSize minimumSizeHint() const override;
|
||||||
|
virtual QSize sizeHint() const override;
|
||||||
|
void setText(const QString &text);
|
||||||
|
QString text() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
/**
|
||||||
|
* This signal is emitted if the user clicks on the label (i.e. pressed
|
||||||
|
* down then released while the mouse cursor is inside the label)
|
||||||
|
*/
|
||||||
|
void clicked();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This signal is emitted if the user does a double click on the label
|
||||||
|
*/
|
||||||
|
void doubleClicked();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This signal is emitted when isElided() state of this label is changed
|
||||||
|
*/
|
||||||
|
void elidedChanged(bool elided);
|
||||||
|
}; //class ElidingLabel
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
563
src/libs/advanceddockingsystem/floatingdockcontainer.cpp
Normal file
563
src/libs/advanceddockingsystem/floatingdockcontainer.cpp
Normal file
@@ -0,0 +1,563 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 Uwe Kindler
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "floatingdockcontainer.h"
|
||||||
|
|
||||||
|
#include "dockareawidget.h"
|
||||||
|
#include "dockcontainerwidget.h"
|
||||||
|
#include "dockmanager.h"
|
||||||
|
#include "dockoverlay.h"
|
||||||
|
#include "dockwidget.h"
|
||||||
|
#include "linux/floatingwidgettitlebar.h"
|
||||||
|
|
||||||
|
#include <QAbstractButton>
|
||||||
|
#include <QAction>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QBoxLayout>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QElapsedTimer>
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
#include <QMouseEvent>
|
||||||
|
#include <QPointer>
|
||||||
|
|
||||||
|
static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtDebugMsg)
|
||||||
|
|
||||||
|
namespace ADS
|
||||||
|
{
|
||||||
|
AbstractFloatingWidget::~AbstractFloatingWidget() = default;
|
||||||
|
|
||||||
|
static unsigned int zOrderCounter = 0;
|
||||||
|
/**
|
||||||
|
* Private data class of FloatingDockContainer class (pimpl)
|
||||||
|
*/
|
||||||
|
struct FloatingDockContainerPrivate
|
||||||
|
{
|
||||||
|
FloatingDockContainer *q;
|
||||||
|
DockContainerWidget *m_dockContainer;
|
||||||
|
unsigned int m_zOrderIndex = ++zOrderCounter;
|
||||||
|
QPointer<DockManager> m_dockManager;
|
||||||
|
eDragState m_draggingState = DraggingInactive;
|
||||||
|
QPoint m_dragStartMousePosition;
|
||||||
|
DockContainerWidget *m_dropContainer = nullptr;
|
||||||
|
DockAreaWidget *m_singleDockArea = nullptr;
|
||||||
|
QWidget *m_mouseEventHandler = nullptr;
|
||||||
|
FloatingWidgetTitleBar *m_titleBar = nullptr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private data constructor
|
||||||
|
*/
|
||||||
|
FloatingDockContainerPrivate(FloatingDockContainer *parent);
|
||||||
|
|
||||||
|
void titleMouseReleaseEvent();
|
||||||
|
void updateDropOverlays(const QPoint &globalPosition);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the given config flag is set
|
||||||
|
*/
|
||||||
|
static bool testConfigFlag(DockManager::eConfigFlag flag)
|
||||||
|
{
|
||||||
|
return DockManager::configFlags().testFlag(flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests is a certain state is active
|
||||||
|
*/
|
||||||
|
bool isState(eDragState stateId) const { return stateId == m_draggingState; }
|
||||||
|
|
||||||
|
void setState(eDragState stateId) { m_draggingState = stateId; }
|
||||||
|
|
||||||
|
void setWindowTitle(const QString &text)
|
||||||
|
{
|
||||||
|
if (Utils::HostOsInfo::isLinuxHost())
|
||||||
|
m_titleBar->setTitle(text);
|
||||||
|
else
|
||||||
|
q->setWindowTitle(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reflectCurrentWidget(DockWidget *currentWidget)
|
||||||
|
{
|
||||||
|
// reflect CurrentWidget's title if configured to do so, otherwise display application name as window title
|
||||||
|
if (testConfigFlag(DockManager::FloatingContainerHasWidgetTitle)) {
|
||||||
|
setWindowTitle(currentWidget->windowTitle());
|
||||||
|
} else {
|
||||||
|
setWindowTitle(qApp->applicationDisplayName());
|
||||||
|
}
|
||||||
|
|
||||||
|
// reflect CurrentWidget's icon if configured to do so, otherwise display application icon as window icon
|
||||||
|
QIcon CurrentWidgetIcon = currentWidget->icon();
|
||||||
|
if (testConfigFlag(DockManager::FloatingContainerHasWidgetIcon)
|
||||||
|
&& !CurrentWidgetIcon.isNull())
|
||||||
|
{
|
||||||
|
q->setWindowIcon(currentWidget->icon());
|
||||||
|
} else {
|
||||||
|
q->setWindowIcon(QApplication::windowIcon());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// struct FloatingDockContainerPrivate
|
||||||
|
|
||||||
|
FloatingDockContainerPrivate::FloatingDockContainerPrivate(FloatingDockContainer *parent)
|
||||||
|
: q(parent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void FloatingDockContainerPrivate::titleMouseReleaseEvent()
|
||||||
|
{
|
||||||
|
setState(DraggingInactive);
|
||||||
|
if (!m_dropContainer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_dockManager->dockAreaOverlay()->dropAreaUnderCursor() != InvalidDockWidgetArea
|
||||||
|
|| m_dockManager->containerOverlay()->dropAreaUnderCursor() != InvalidDockWidgetArea) {
|
||||||
|
// Resize the floating widget to the size of the highlighted drop area rectangle
|
||||||
|
DockOverlay *overlay = m_dockManager->containerOverlay();
|
||||||
|
if (!overlay->dropOverlayRect().isValid()) {
|
||||||
|
overlay = m_dockManager->dockAreaOverlay();
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect rect = overlay->dropOverlayRect();
|
||||||
|
int frameWidth = (q->frameSize().width() - q->rect().width()) / 2;
|
||||||
|
int titleBarHeight = q->frameSize().height() - q->rect().height() - frameWidth;
|
||||||
|
if (rect.isValid()) {
|
||||||
|
QPoint topLeft = overlay->mapToGlobal(rect.topLeft());
|
||||||
|
topLeft.ry() += titleBarHeight;
|
||||||
|
q->setGeometry(QRect(topLeft, QSize(rect.width(), rect.height() - titleBarHeight)));
|
||||||
|
QApplication::processEvents();
|
||||||
|
}
|
||||||
|
m_dropContainer->dropFloatingWidget(q, QCursor::pos());
|
||||||
|
}
|
||||||
|
|
||||||
|
m_dockManager->containerOverlay()->hideOverlay();
|
||||||
|
m_dockManager->dockAreaOverlay()->hideOverlay();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatingDockContainerPrivate::updateDropOverlays(const QPoint &globalPosition)
|
||||||
|
{
|
||||||
|
if (!q->isVisible() || !m_dockManager) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto containers = m_dockManager->dockContainers();
|
||||||
|
DockContainerWidget *topContainer = nullptr;
|
||||||
|
for (auto containerWidget : containers) {
|
||||||
|
if (!containerWidget->isVisible()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_dockContainer == containerWidget) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPoint mappedPos = containerWidget->mapFromGlobal(globalPosition);
|
||||||
|
if (containerWidget->rect().contains(mappedPos)) {
|
||||||
|
if (!topContainer || containerWidget->isInFrontOf(topContainer)) {
|
||||||
|
topContainer = containerWidget;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_dropContainer = topContainer;
|
||||||
|
auto containerOverlay = m_dockManager->containerOverlay();
|
||||||
|
auto dockAreaOverlay = m_dockManager->dockAreaOverlay();
|
||||||
|
|
||||||
|
if (!topContainer) {
|
||||||
|
containerOverlay->hideOverlay();
|
||||||
|
dockAreaOverlay->hideOverlay();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int visibleDockAreas = topContainer->visibleDockAreaCount();
|
||||||
|
containerOverlay->setAllowedAreas(visibleDockAreas > 1 ? OuterDockAreas : AllDockAreas);
|
||||||
|
DockWidgetArea containerArea = containerOverlay->showOverlay(topContainer);
|
||||||
|
containerOverlay->enableDropPreview(containerArea != InvalidDockWidgetArea);
|
||||||
|
auto dockArea = topContainer->dockAreaAt(globalPosition);
|
||||||
|
if (dockArea && dockArea->isVisible() && visibleDockAreas > 0) {
|
||||||
|
dockAreaOverlay->enableDropPreview(true);
|
||||||
|
dockAreaOverlay->setAllowedAreas((visibleDockAreas == 1) ? NoDockWidgetArea
|
||||||
|
: dockArea->allowedAreas());
|
||||||
|
DockWidgetArea area = dockAreaOverlay->showOverlay(dockArea);
|
||||||
|
|
||||||
|
// A CenterDockWidgetArea for the dockAreaOverlay() indicates that the mouse is in
|
||||||
|
// the title bar. If the ContainerArea is valid then we ignore the dock area of the
|
||||||
|
// dockAreaOverlay() and disable the drop preview
|
||||||
|
if ((area == CenterDockWidgetArea) && (containerArea != InvalidDockWidgetArea)) {
|
||||||
|
dockAreaOverlay->enableDropPreview(false);
|
||||||
|
containerOverlay->enableDropPreview(true);
|
||||||
|
} else {
|
||||||
|
containerOverlay->enableDropPreview(InvalidDockWidgetArea == area);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dockAreaOverlay->hideOverlay();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FloatingDockContainer::FloatingDockContainer(DockManager *dockManager)
|
||||||
|
: FloatingWidgetBaseType(dockManager)
|
||||||
|
, d(new FloatingDockContainerPrivate(this))
|
||||||
|
{
|
||||||
|
d->m_dockManager = dockManager;
|
||||||
|
d->m_dockContainer = new DockContainerWidget(dockManager, this);
|
||||||
|
connect(d->m_dockContainer,
|
||||||
|
&DockContainerWidget::dockAreasAdded,
|
||||||
|
this,
|
||||||
|
&FloatingDockContainer::onDockAreasAddedOrRemoved);
|
||||||
|
connect(d->m_dockContainer,
|
||||||
|
&DockContainerWidget::dockAreasRemoved,
|
||||||
|
this,
|
||||||
|
&FloatingDockContainer::onDockAreasAddedOrRemoved);
|
||||||
|
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
d->m_titleBar = new FloatingWidgetTitleBar(this);
|
||||||
|
setWindowFlags(windowFlags() | Qt::Tool);
|
||||||
|
QDockWidget::setWidget(d->m_dockContainer);
|
||||||
|
QDockWidget::setFloating(true);
|
||||||
|
QDockWidget::setFeatures(QDockWidget::AllDockWidgetFeatures);
|
||||||
|
setTitleBarWidget(d->m_titleBar);
|
||||||
|
connect(d->m_titleBar,
|
||||||
|
&FloatingWidgetTitleBar::closeRequested,
|
||||||
|
this,
|
||||||
|
&FloatingDockContainer::close);
|
||||||
|
#else
|
||||||
|
setWindowFlags(Qt::Window | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint);
|
||||||
|
QBoxLayout *boxLayout = new QBoxLayout(QBoxLayout::TopToBottom);
|
||||||
|
boxLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
boxLayout->setSpacing(0);
|
||||||
|
setLayout(boxLayout);
|
||||||
|
boxLayout->addWidget(d->m_dockContainer);
|
||||||
|
#endif
|
||||||
|
dockManager->registerFloatingWidget(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
FloatingDockContainer::FloatingDockContainer(DockAreaWidget *dockArea)
|
||||||
|
: FloatingDockContainer(dockArea->dockManager())
|
||||||
|
{
|
||||||
|
d->m_dockContainer->addDockArea(dockArea);
|
||||||
|
if (Utils::HostOsInfo::isLinuxHost())
|
||||||
|
d->m_titleBar->enableCloseButton(isClosable());
|
||||||
|
|
||||||
|
auto dw = topLevelDockWidget();
|
||||||
|
if (dw) {
|
||||||
|
dw->emitTopLevelChanged(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FloatingDockContainer::FloatingDockContainer(DockWidget *dockWidget)
|
||||||
|
: FloatingDockContainer(dockWidget->dockManager())
|
||||||
|
{
|
||||||
|
d->m_dockContainer->addDockWidget(CenterDockWidgetArea, dockWidget);
|
||||||
|
if (Utils::HostOsInfo::isLinuxHost())
|
||||||
|
d->m_titleBar->enableCloseButton(isClosable());
|
||||||
|
|
||||||
|
auto dw = topLevelDockWidget();
|
||||||
|
if (dw) {
|
||||||
|
dw->emitTopLevelChanged(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FloatingDockContainer::~FloatingDockContainer()
|
||||||
|
{
|
||||||
|
qCInfo(adsLog) << Q_FUNC_INFO;
|
||||||
|
if (d->m_dockManager) {
|
||||||
|
d->m_dockManager->removeFloatingWidget(this);
|
||||||
|
}
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
DockContainerWidget *FloatingDockContainer::dockContainer() const { return d->m_dockContainer; }
|
||||||
|
|
||||||
|
void FloatingDockContainer::changeEvent(QEvent *event)
|
||||||
|
{
|
||||||
|
QWidget::changeEvent(event);
|
||||||
|
if ((event->type() == QEvent::ActivationChange) && isActiveWindow()) {
|
||||||
|
qCInfo(adsLog) << Q_FUNC_INFO << "QEvent::ActivationChange";
|
||||||
|
d->m_zOrderIndex = ++zOrderCounter;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatingDockContainer::moveEvent(QMoveEvent *event)
|
||||||
|
{
|
||||||
|
QWidget::moveEvent(event);
|
||||||
|
switch (d->m_draggingState) {
|
||||||
|
case DraggingMousePressed:
|
||||||
|
d->setState(DraggingFloatingWidget);
|
||||||
|
d->updateDropOverlays(QCursor::pos());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DraggingFloatingWidget:
|
||||||
|
d->updateDropOverlays(QCursor::pos());
|
||||||
|
if (Utils::HostOsInfo::isMacHost()) {
|
||||||
|
// In macOS when hiding the DockAreaOverlay the application would set
|
||||||
|
// the main window as the active window for some reason. This fixes
|
||||||
|
// that by resetting the active window to the floating widget after
|
||||||
|
// updating the overlays.
|
||||||
|
QApplication::setActiveWindow(this);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatingDockContainer::closeEvent(QCloseEvent *event)
|
||||||
|
{
|
||||||
|
qCInfo(adsLog) << Q_FUNC_INFO;
|
||||||
|
d->setState(DraggingInactive);
|
||||||
|
event->ignore();
|
||||||
|
|
||||||
|
if (isClosable()) {
|
||||||
|
auto dw = topLevelDockWidget();
|
||||||
|
if (dw && dw->features().testFlag(DockWidget::DockWidgetDeleteOnClose)) {
|
||||||
|
if (!dw->closeDockWidgetInternal()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatingDockContainer::hideEvent(QHideEvent *event)
|
||||||
|
{
|
||||||
|
Super::hideEvent(event);
|
||||||
|
if (event->spontaneous()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent toogleView() events during restore state
|
||||||
|
if (d->m_dockManager->isRestoringState()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto dockArea : d->m_dockContainer->openedDockAreas()) {
|
||||||
|
for (auto dockWidget : dockArea->openedDockWidgets()) {
|
||||||
|
dockWidget->toggleView(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatingDockContainer::showEvent(QShowEvent *event) { Super::showEvent(event); }
|
||||||
|
|
||||||
|
bool FloatingDockContainer::event(QEvent *event)
|
||||||
|
{
|
||||||
|
switch (d->m_draggingState) {
|
||||||
|
case DraggingInactive: {
|
||||||
|
// Normally we would check here, if the left mouse button is pressed.
|
||||||
|
// But from QT version 5.12.2 on the mouse events from
|
||||||
|
// QEvent::NonClientAreaMouseButtonPress return the wrong mouse button
|
||||||
|
// The event always returns Qt::RightButton even if the left button is clicked.
|
||||||
|
// It is really great to work around the whole NonClientMouseArea bugs
|
||||||
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 2))
|
||||||
|
if (event->type()
|
||||||
|
== QEvent::
|
||||||
|
NonClientAreaMouseButtonPress /*&& QGuiApplication::mouseButtons().testFlag(Qt::LeftButton)*/) {
|
||||||
|
qCInfo(adsLog) << Q_FUNC_INFO << "QEvent::NonClientAreaMouseButtonPress"
|
||||||
|
<< event->type();
|
||||||
|
d->setState(DraggingMousePressed);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (e->type() == QEvent::NonClientAreaMouseButtonPress
|
||||||
|
&& QGuiApplication::mouseButtons().testFlag(Qt::LeftButton)) {
|
||||||
|
qCInfo(adsLog) << Q_FUNC_INFO << "QEvent::NonClientAreaMouseButtonPress"
|
||||||
|
<< e->type();
|
||||||
|
d->setState(DraggingMousePressed);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case DraggingMousePressed:
|
||||||
|
switch (event->type()) {
|
||||||
|
case QEvent::NonClientAreaMouseButtonDblClick:
|
||||||
|
qCInfo(adsLog) << Q_FUNC_INFO << "QEvent::NonClientAreaMouseButtonDblClick";
|
||||||
|
d->setState(DraggingInactive);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QEvent::Resize:
|
||||||
|
// If the first event after the mouse press is a resize event, then
|
||||||
|
// the user resizes the window instead of dragging it around.
|
||||||
|
// But there is one exception. If the window is maximized,
|
||||||
|
// then dragging the window via title bar will cause the widget to
|
||||||
|
// leave the maximized state. This in turn will trigger a resize event.
|
||||||
|
// To know, if the resize event was triggered by user via moving a
|
||||||
|
// corner of the window frame or if it was caused by a windows state
|
||||||
|
// change, we check, if we are not in maximized state.
|
||||||
|
if (!isMaximized()) {
|
||||||
|
d->setState(DraggingInactive);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DraggingFloatingWidget:
|
||||||
|
if (event->type() == QEvent::NonClientAreaMouseButtonRelease) {
|
||||||
|
qCInfo(adsLog) << Q_FUNC_INFO << "QEvent::NonClientAreaMouseButtonRelease";
|
||||||
|
d->titleMouseReleaseEvent();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if (ADS_DEBUG_LEVEL > 0)
|
||||||
|
qDebug() << "FloatingDockContainer::event " << event->type();
|
||||||
|
#endif
|
||||||
|
return QWidget::event(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatingDockContainer::startFloating(const QPoint &dragStartMousePos,
|
||||||
|
const QSize &size,
|
||||||
|
eDragState dragState,
|
||||||
|
QWidget *mouseEventHandler)
|
||||||
|
{
|
||||||
|
resize(size);
|
||||||
|
d->setState(dragState);
|
||||||
|
d->m_dragStartMousePosition = dragStartMousePos;
|
||||||
|
|
||||||
|
if (Utils::HostOsInfo::isLinuxHost()) {
|
||||||
|
if (DraggingFloatingWidget == dragState) {
|
||||||
|
setAttribute(Qt::WA_X11NetWmWindowTypeDock, true);
|
||||||
|
d->m_mouseEventHandler = mouseEventHandler;
|
||||||
|
if (d->m_mouseEventHandler) {
|
||||||
|
d->m_mouseEventHandler->grabMouse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
moveFloating();
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatingDockContainer::moveFloating()
|
||||||
|
{
|
||||||
|
int borderSize = (frameSize().width() - size().width()) / 2;
|
||||||
|
const QPoint moveToPos = QCursor::pos() - d->m_dragStartMousePosition
|
||||||
|
- QPoint(borderSize, 0);
|
||||||
|
move(moveToPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FloatingDockContainer::isClosable() const
|
||||||
|
{
|
||||||
|
return d->m_dockContainer->features().testFlag(DockWidget::DockWidgetClosable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatingDockContainer::onDockAreasAddedOrRemoved()
|
||||||
|
{
|
||||||
|
qCInfo(adsLog) << Q_FUNC_INFO;
|
||||||
|
auto topLevelDockArea = d->m_dockContainer->topLevelDockArea();
|
||||||
|
if (topLevelDockArea) {
|
||||||
|
d->m_singleDockArea = topLevelDockArea;
|
||||||
|
DockWidget *currentWidget = d->m_singleDockArea->currentDockWidget();
|
||||||
|
d->reflectCurrentWidget(currentWidget);
|
||||||
|
connect(d->m_singleDockArea,
|
||||||
|
&DockAreaWidget::currentChanged,
|
||||||
|
this,
|
||||||
|
&FloatingDockContainer::onDockAreaCurrentChanged);
|
||||||
|
} else {
|
||||||
|
if (d->m_singleDockArea) {
|
||||||
|
disconnect(d->m_singleDockArea,
|
||||||
|
&DockAreaWidget::currentChanged,
|
||||||
|
this,
|
||||||
|
&FloatingDockContainer::onDockAreaCurrentChanged);
|
||||||
|
d->m_singleDockArea = nullptr;
|
||||||
|
}
|
||||||
|
d->setWindowTitle(qApp->applicationDisplayName());
|
||||||
|
setWindowIcon(QApplication::windowIcon());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatingDockContainer::updateWindowTitle()
|
||||||
|
{
|
||||||
|
auto topLevelDockArea = d->m_dockContainer->topLevelDockArea();
|
||||||
|
if (topLevelDockArea) {
|
||||||
|
DockWidget *currentWidget = topLevelDockArea->currentDockWidget();
|
||||||
|
d->reflectCurrentWidget(currentWidget);
|
||||||
|
} else {
|
||||||
|
d->setWindowTitle(qApp->applicationDisplayName());
|
||||||
|
setWindowIcon(QApplication::windowIcon());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatingDockContainer::onDockAreaCurrentChanged(int index)
|
||||||
|
{
|
||||||
|
Q_UNUSED(index)
|
||||||
|
DockWidget *currentWidget = d->m_singleDockArea->currentDockWidget();
|
||||||
|
d->reflectCurrentWidget(currentWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FloatingDockContainer::restoreState(DockingStateReader &stream, bool testing)
|
||||||
|
{
|
||||||
|
if (!d->m_dockContainer->restoreState(stream, testing)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
onDockAreasAddedOrRemoved();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FloatingDockContainer::hasTopLevelDockWidget() const
|
||||||
|
{
|
||||||
|
return d->m_dockContainer->hasTopLevelDockWidget();
|
||||||
|
}
|
||||||
|
|
||||||
|
DockWidget *FloatingDockContainer::topLevelDockWidget() const
|
||||||
|
{
|
||||||
|
return d->m_dockContainer->topLevelDockWidget();
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<DockWidget *> FloatingDockContainer::dockWidgets() const
|
||||||
|
{
|
||||||
|
return d->m_dockContainer->dockWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatingDockContainer::finishDragging()
|
||||||
|
{
|
||||||
|
qCInfo(adsLog) << Q_FUNC_INFO;
|
||||||
|
|
||||||
|
if (Utils::HostOsInfo::isLinuxHost()) {
|
||||||
|
setAttribute(Qt::WA_X11NetWmWindowTypeDock, false);
|
||||||
|
setWindowOpacity(1);
|
||||||
|
activateWindow();
|
||||||
|
if (d->m_mouseEventHandler) {
|
||||||
|
d->m_mouseEventHandler->releaseMouse();
|
||||||
|
d->m_mouseEventHandler = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d->titleMouseReleaseEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
249
src/libs/advanceddockingsystem/floatingdockcontainer.h
Normal file
249
src/libs/advanceddockingsystem/floatingdockcontainer.h
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 Uwe Kindler
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ads_globals.h"
|
||||||
|
|
||||||
|
#include <QDockWidget>
|
||||||
|
#include <QRubberBand>
|
||||||
|
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
using FloatingWidgetBaseType = QDockWidget;
|
||||||
|
#else
|
||||||
|
using FloatingWidgetBaseType = QWidget;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace ADS {
|
||||||
|
|
||||||
|
struct FloatingDockContainerPrivate;
|
||||||
|
class DockManager;
|
||||||
|
struct DockManagerPrivate;
|
||||||
|
class DockAreaWidget;
|
||||||
|
class DockContainerWidget;
|
||||||
|
class DockWidget;
|
||||||
|
class DockManager;
|
||||||
|
class DockAreaTabBar;
|
||||||
|
class DockWidgetTab;
|
||||||
|
struct DockWidgetTabPrivate;
|
||||||
|
class DockAreaTitleBar;
|
||||||
|
struct DockAreaTitleBarPrivate;
|
||||||
|
class FloatingWidgetTitleBar;
|
||||||
|
class DockingStateReader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pure virtual interface for floating widgets.
|
||||||
|
* This interface is used for opaque and non-opaque undocking. If opaque
|
||||||
|
* undocking is used, the a real FloatingDockContainer widget will be created
|
||||||
|
*/
|
||||||
|
class AbstractFloatingWidget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~AbstractFloatingWidget() = 0;
|
||||||
|
/**
|
||||||
|
* Starts floating.
|
||||||
|
* This function should get called typically from a mouse press event
|
||||||
|
* handler
|
||||||
|
*/
|
||||||
|
virtual void startFloating(const QPoint &dragStartMousePos,
|
||||||
|
const QSize &size,
|
||||||
|
eDragState dragState,
|
||||||
|
QWidget *mouseEventHandler)
|
||||||
|
= 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves the widget to a new position relative to the position given when
|
||||||
|
* startFloating() was called.
|
||||||
|
* This function should be called from a mouse mouve event handler to
|
||||||
|
* move the floating widget on mouse move events.
|
||||||
|
*/
|
||||||
|
virtual void moveFloating() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells the widget that to finish dragging if the mouse is released.
|
||||||
|
* This function should be called from a mouse release event handler
|
||||||
|
* to finish the dragging
|
||||||
|
*/
|
||||||
|
virtual void finishDragging() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This implements a floating widget that is a dock container that accepts
|
||||||
|
* docking of dock widgets like the main window and that can be docked into
|
||||||
|
* another dock container.
|
||||||
|
* Every floating window of the docking system is a FloatingDockContainer.
|
||||||
|
*/
|
||||||
|
class ADS_EXPORT FloatingDockContainer : public FloatingWidgetBaseType,
|
||||||
|
public AbstractFloatingWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private:
|
||||||
|
FloatingDockContainerPrivate *d; ///< private data (pimpl)
|
||||||
|
friend struct FloatingDockContainerPrivate;
|
||||||
|
friend class DockManager;
|
||||||
|
friend struct DockManagerPrivate;
|
||||||
|
friend class DockAreaTabBar;
|
||||||
|
friend struct DockWidgetTabPrivate;
|
||||||
|
friend class DockWidgetTab;
|
||||||
|
friend class DockAreaTitleBar;
|
||||||
|
friend struct DockAreaTitleBarPrivate;
|
||||||
|
friend class DockWidget;
|
||||||
|
friend class DockAreaWidget;
|
||||||
|
friend class FloatingWidgetTitleBar;
|
||||||
|
|
||||||
|
void onDockAreasAddedOrRemoved();
|
||||||
|
void onDockAreaCurrentChanged(int Index);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* Starts floating at the given global position.
|
||||||
|
* Use moveToGlobalPos() to move the widget to a new position
|
||||||
|
* depending on the start position given in Pos parameter
|
||||||
|
*/
|
||||||
|
virtual void startFloating(const QPoint &dragStartMousePos,
|
||||||
|
const QSize &size,
|
||||||
|
eDragState dragState,
|
||||||
|
QWidget *mouseEventHandler) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call this function to start dragging the floating widget
|
||||||
|
*/
|
||||||
|
void startDragging(const QPoint &dragStartMousePos,
|
||||||
|
const QSize &size,
|
||||||
|
QWidget *mouseEventHandler)
|
||||||
|
{
|
||||||
|
startFloating(dragStartMousePos, size, DraggingFloatingWidget, mouseEventHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call this function if you explicitly want to signal that dragging has
|
||||||
|
* finished
|
||||||
|
*/
|
||||||
|
virtual void finishDragging() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call this function if you just want to initialize the position
|
||||||
|
* and size of the floating widget
|
||||||
|
*/
|
||||||
|
void initFloatingGeometry(const QPoint &dragStartMousePos, const QSize &size)
|
||||||
|
{
|
||||||
|
startFloating(dragStartMousePos, size, DraggingInactive, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves the widget to a new position relative to the position given when
|
||||||
|
* startFloating() was called
|
||||||
|
*/
|
||||||
|
void moveFloating() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restores the state from given stream.
|
||||||
|
* If Testing is true, the function only parses the data from the given
|
||||||
|
* stream but does not restore anything. You can use this check for
|
||||||
|
* faulty files before you start restoring the state
|
||||||
|
*/
|
||||||
|
bool restoreState(DockingStateReader &stream, bool testing);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call this function to update the window title
|
||||||
|
*/
|
||||||
|
void updateWindowTitle();
|
||||||
|
|
||||||
|
protected: // reimplements QWidget
|
||||||
|
virtual void changeEvent(QEvent *event) override;
|
||||||
|
virtual void moveEvent(QMoveEvent *event) override;
|
||||||
|
virtual bool event(QEvent *event) override;
|
||||||
|
virtual void closeEvent(QCloseEvent *event) override;
|
||||||
|
virtual void hideEvent(QHideEvent *event) override;
|
||||||
|
virtual void showEvent(QShowEvent *event) override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using Super = QWidget;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create empty floating widget - required for restore state
|
||||||
|
*/
|
||||||
|
FloatingDockContainer(DockManager *dockManager);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create floating widget with the given dock area
|
||||||
|
*/
|
||||||
|
FloatingDockContainer(DockAreaWidget *dockArea);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create floating widget with the given dock widget
|
||||||
|
*/
|
||||||
|
FloatingDockContainer(DockWidget *dockWidget);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual Destructor
|
||||||
|
*/
|
||||||
|
virtual ~FloatingDockContainer() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access function for the internal dock container
|
||||||
|
*/
|
||||||
|
DockContainerWidget *dockContainer() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function returns true, if it can be closed.
|
||||||
|
* It can be closed, if all dock widgets in all dock areas can be closed
|
||||||
|
*/
|
||||||
|
bool isClosable() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function returns true, if this floating widget has only one single
|
||||||
|
* visible dock widget in a single visible dock area.
|
||||||
|
* The single dock widget is a real top level floating widget because no
|
||||||
|
* other widgets are docked.
|
||||||
|
*/
|
||||||
|
bool hasTopLevelDockWidget() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function returns the first dock widget in the first dock area.
|
||||||
|
* If the function hasSingleDockWidget() returns true, then this function
|
||||||
|
* returns this single dock widget.
|
||||||
|
*/
|
||||||
|
DockWidget *topLevelDockWidget() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function returns a list of all dock widget in this floating widget.
|
||||||
|
* This is a simple convenience function that simply calls the dockWidgets()
|
||||||
|
* function of the internal container widget.
|
||||||
|
*/
|
||||||
|
QList<DockWidget *> dockWidgets() const;
|
||||||
|
}; // class FloatingDockContainer
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
355
src/libs/advanceddockingsystem/floatingdragpreview.cpp
Normal file
355
src/libs/advanceddockingsystem/floatingdragpreview.cpp
Normal file
@@ -0,0 +1,355 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 Uwe Kindler
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "floatingdragpreview.h"
|
||||||
|
|
||||||
|
#include "dockareawidget.h"
|
||||||
|
#include "dockcontainerwidget.h"
|
||||||
|
#include "dockmanager.h"
|
||||||
|
#include "dockoverlay.h"
|
||||||
|
#include "dockwidget.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QEvent>
|
||||||
|
#include <QKeyEvent>
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
#include <QPainter>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
static Q_LOGGING_CATEGORY(adsLog, "qtc.qmldesigner.advanceddockingsystem", QtDebugMsg)
|
||||||
|
|
||||||
|
namespace ADS
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Private data class (pimpl)
|
||||||
|
*/
|
||||||
|
struct FloatingDragPreviewPrivate
|
||||||
|
{
|
||||||
|
FloatingDragPreview *q;
|
||||||
|
QWidget *m_content;
|
||||||
|
DockAreaWidget *m_contentSourceArea = nullptr;
|
||||||
|
DockContainerWidget *m_contenSourceContainer = nullptr;
|
||||||
|
QPoint m_dragStartMousePosition;
|
||||||
|
DockManager *m_dockManager;
|
||||||
|
DockContainerWidget *m_dropContainer = nullptr;
|
||||||
|
qreal m_windowOpacity;
|
||||||
|
bool m_hidden = false;
|
||||||
|
QPixmap m_contentPreviewPixmap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private data constructor
|
||||||
|
*/
|
||||||
|
FloatingDragPreviewPrivate(FloatingDragPreview *parent);
|
||||||
|
void updateDropOverlays(const QPoint &globalPosition);
|
||||||
|
|
||||||
|
void setHidden(bool value)
|
||||||
|
{
|
||||||
|
m_hidden = value;
|
||||||
|
q->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel dragging and emit the draggingCanceled event
|
||||||
|
*/
|
||||||
|
void cancelDragging()
|
||||||
|
{
|
||||||
|
emit q->draggingCanceled();
|
||||||
|
m_dockManager->containerOverlay()->hideOverlay();
|
||||||
|
m_dockManager->dockAreaOverlay()->hideOverlay();
|
||||||
|
q->close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// struct FloatingDragPreviewPrivate
|
||||||
|
|
||||||
|
void FloatingDragPreviewPrivate::updateDropOverlays(const QPoint &globalPosition)
|
||||||
|
{
|
||||||
|
if (!q->isVisible() || !m_dockManager) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto containers = m_dockManager->dockContainers();
|
||||||
|
DockContainerWidget *topContainer = nullptr;
|
||||||
|
for (auto containerWidget : containers) {
|
||||||
|
if (!containerWidget->isVisible()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPoint mappedPosition = containerWidget->mapFromGlobal(globalPosition);
|
||||||
|
if (containerWidget->rect().contains(mappedPosition)) {
|
||||||
|
if (!topContainer || containerWidget->isInFrontOf(topContainer)) {
|
||||||
|
topContainer = containerWidget;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_dropContainer = topContainer;
|
||||||
|
auto containerOverlay = m_dockManager->containerOverlay();
|
||||||
|
auto dockAreaOverlay = m_dockManager->dockAreaOverlay();
|
||||||
|
auto dockDropArea = dockAreaOverlay->dropAreaUnderCursor();
|
||||||
|
auto containerDropArea = containerOverlay->dropAreaUnderCursor();
|
||||||
|
|
||||||
|
if (!topContainer) {
|
||||||
|
containerOverlay->hideOverlay();
|
||||||
|
dockAreaOverlay->hideOverlay();
|
||||||
|
if (DockManager::configFlags().testFlag(DockManager::DragPreviewIsDynamic)) {
|
||||||
|
setHidden(false);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int visibleDockAreas = topContainer->visibleDockAreaCount();
|
||||||
|
containerOverlay->setAllowedAreas(visibleDockAreas > 1 ? OuterDockAreas : AllDockAreas);
|
||||||
|
DockWidgetArea containerArea = containerOverlay->showOverlay(topContainer);
|
||||||
|
containerOverlay->enableDropPreview(containerArea != InvalidDockWidgetArea);
|
||||||
|
auto dockArea = topContainer->dockAreaAt(globalPosition);
|
||||||
|
if (dockArea && dockArea->isVisible() && visibleDockAreas > 0
|
||||||
|
&& dockArea != m_contentSourceArea) {
|
||||||
|
dockAreaOverlay->enableDropPreview(true);
|
||||||
|
dockAreaOverlay->setAllowedAreas((visibleDockAreas == 1) ? NoDockWidgetArea
|
||||||
|
: dockArea->allowedAreas());
|
||||||
|
DockWidgetArea area = dockAreaOverlay->showOverlay(dockArea);
|
||||||
|
|
||||||
|
// A CenterDockWidgetArea for the dockAreaOverlay() indicates that the mouse is in the
|
||||||
|
// title bar. If the ContainerArea is valid then we ignore the dock area of the
|
||||||
|
// dockAreaOverlay() and disable the drop preview
|
||||||
|
if ((area == CenterDockWidgetArea) && (containerArea != InvalidDockWidgetArea)) {
|
||||||
|
dockAreaOverlay->enableDropPreview(false);
|
||||||
|
containerOverlay->enableDropPreview(true);
|
||||||
|
} else {
|
||||||
|
containerOverlay->enableDropPreview(InvalidDockWidgetArea == area);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dockAreaOverlay->hideOverlay();
|
||||||
|
if (dockArea == m_contentSourceArea && InvalidDockWidgetArea == containerDropArea) {
|
||||||
|
m_dropContainer = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DockManager::configFlags().testFlag(DockManager::DragPreviewIsDynamic)) {
|
||||||
|
setHidden(dockDropArea != InvalidDockWidgetArea
|
||||||
|
|| containerDropArea != InvalidDockWidgetArea);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FloatingDragPreviewPrivate::FloatingDragPreviewPrivate(FloatingDragPreview *parent)
|
||||||
|
: q(parent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
FloatingDragPreview::FloatingDragPreview(QWidget *content, QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
, d(new FloatingDragPreviewPrivate(this))
|
||||||
|
{
|
||||||
|
d->m_content = content;
|
||||||
|
setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
if (DockManager::configFlags().testFlag(DockManager::DragPreviewHasWindowFrame)) {
|
||||||
|
setWindowFlags(Qt::Window | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint);
|
||||||
|
} else {
|
||||||
|
setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
|
||||||
|
setAttribute(Qt::WA_NoSystemBackground);
|
||||||
|
setAttribute(Qt::WA_TranslucentBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Utils::HostOsInfo::isLinuxHost()) {
|
||||||
|
auto flags = windowFlags();
|
||||||
|
flags |= Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint;
|
||||||
|
setWindowFlags(flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
setWindowOpacity(0.6);
|
||||||
|
|
||||||
|
// Create a static image of the widget that should get undocked
|
||||||
|
// This is like some kind preview image like it is uses in drag and drop operations
|
||||||
|
if (DockManager::configFlags().testFlag(DockManager::DragPreviewShowsContentPixmap)) {
|
||||||
|
d->m_contentPreviewPixmap = QPixmap(content->size());
|
||||||
|
content->render(&d->m_contentPreviewPixmap);
|
||||||
|
}
|
||||||
|
connect(qApp,
|
||||||
|
&QApplication::applicationStateChanged,
|
||||||
|
this,
|
||||||
|
&FloatingDragPreview::onApplicationStateChanged); // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
FloatingDragPreview::FloatingDragPreview(DockWidget *content)
|
||||||
|
: FloatingDragPreview(static_cast<QWidget *>(content),
|
||||||
|
content->dockManager()) // TODO static_cast?
|
||||||
|
{
|
||||||
|
d->m_dockManager = content->dockManager();
|
||||||
|
if (content->dockAreaWidget()->openDockWidgetsCount() == 1) {
|
||||||
|
d->m_contentSourceArea = content->dockAreaWidget();
|
||||||
|
d->m_contenSourceContainer = content->dockContainer();
|
||||||
|
}
|
||||||
|
setWindowTitle(content->windowTitle());
|
||||||
|
// We need to install an event filter for the given content
|
||||||
|
// widget to receive the escape key press
|
||||||
|
content->dockAreaWidget()->installEventFilter(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
FloatingDragPreview::FloatingDragPreview(DockAreaWidget *content)
|
||||||
|
: FloatingDragPreview(static_cast<QWidget *>(content),
|
||||||
|
content->dockManager()) // TODO static_cast?
|
||||||
|
{
|
||||||
|
d->m_dockManager = content->dockManager();
|
||||||
|
d->m_contentSourceArea = content;
|
||||||
|
d->m_contenSourceContainer = content->dockContainer();
|
||||||
|
setWindowTitle(content->currentDockWidget()->windowTitle());
|
||||||
|
|
||||||
|
// We need to install an event filter for the given Content
|
||||||
|
// widget to receive the escape key press
|
||||||
|
content->installEventFilter(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
FloatingDragPreview::~FloatingDragPreview() { delete d; }
|
||||||
|
|
||||||
|
void FloatingDragPreview::moveFloating()
|
||||||
|
{
|
||||||
|
int borderSize = (frameSize().width() - size().width()) / 2;
|
||||||
|
const QPoint moveToPos = QCursor::pos() - d->m_dragStartMousePosition
|
||||||
|
- QPoint(borderSize, 0);
|
||||||
|
move(moveToPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatingDragPreview::startFloating(const QPoint &dragStartMousePos,
|
||||||
|
const QSize &size,
|
||||||
|
eDragState dragState,
|
||||||
|
QWidget *mouseEventHandler)
|
||||||
|
{
|
||||||
|
Q_UNUSED(mouseEventHandler)
|
||||||
|
Q_UNUSED(dragState)
|
||||||
|
resize(size);
|
||||||
|
d->m_dragStartMousePosition = dragStartMousePos;
|
||||||
|
moveFloating();
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatingDragPreview::moveEvent(QMoveEvent *event)
|
||||||
|
{
|
||||||
|
QWidget::moveEvent(event);
|
||||||
|
d->updateDropOverlays(QCursor::pos());
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatingDragPreview::finishDragging()
|
||||||
|
{
|
||||||
|
qCInfo(adsLog) << Q_FUNC_INFO;
|
||||||
|
auto dockDropArea = d->m_dockManager->dockAreaOverlay()->dropAreaUnderCursor();
|
||||||
|
auto containerDropArea = d->m_dockManager->containerOverlay()->dropAreaUnderCursor();
|
||||||
|
bool dropPossible = (dockDropArea != InvalidDockWidgetArea)
|
||||||
|
|| (containerDropArea != InvalidDockWidgetArea);
|
||||||
|
if (d->m_dropContainer && dropPossible) {
|
||||||
|
d->m_dropContainer->dropWidget(d->m_content, QCursor::pos());
|
||||||
|
} else {
|
||||||
|
DockWidget *dockWidget = qobject_cast<DockWidget *>(d->m_content);
|
||||||
|
FloatingDockContainer *floatingWidget = nullptr;
|
||||||
|
|
||||||
|
if (dockWidget && dockWidget->features().testFlag(DockWidget::DockWidgetFloatable)) {
|
||||||
|
floatingWidget = new FloatingDockContainer(dockWidget);
|
||||||
|
} else {
|
||||||
|
DockAreaWidget *dockArea = qobject_cast<DockAreaWidget *>(d->m_content);
|
||||||
|
if (dockArea->features().testFlag(DockWidget::DockWidgetFloatable)) {
|
||||||
|
floatingWidget = new FloatingDockContainer(dockArea);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (floatingWidget) {
|
||||||
|
floatingWidget->setGeometry(this->geometry());
|
||||||
|
floatingWidget->show();
|
||||||
|
if (!DockManager::configFlags().testFlag(DockManager::DragPreviewHasWindowFrame)) {
|
||||||
|
QApplication::processEvents();
|
||||||
|
int frameHeight = floatingWidget->frameGeometry().height() - floatingWidget->geometry().height();
|
||||||
|
QRect fixedGeometry = this->geometry();
|
||||||
|
fixedGeometry.adjust(0, frameHeight, 0, 0);
|
||||||
|
floatingWidget->setGeometry(fixedGeometry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->close();
|
||||||
|
d->m_dockManager->containerOverlay()->hideOverlay();
|
||||||
|
d->m_dockManager->dockAreaOverlay()->hideOverlay();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatingDragPreview::paintEvent(QPaintEvent *event)
|
||||||
|
{
|
||||||
|
Q_UNUSED(event)
|
||||||
|
if (d->m_hidden) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPainter painter(this);
|
||||||
|
if (DockManager::configFlags().testFlag(DockManager::DragPreviewShowsContentPixmap)) {
|
||||||
|
painter.drawPixmap(QPoint(0, 0), d->m_contentPreviewPixmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we do not have a window frame then we paint a QRubberBand like frameless window
|
||||||
|
if (!DockManager::configFlags().testFlag(DockManager::DragPreviewHasWindowFrame)) {
|
||||||
|
QColor color = palette().color(QPalette::Active, QPalette::Highlight);
|
||||||
|
QPen pen = painter.pen();
|
||||||
|
pen.setColor(color.darker(120));
|
||||||
|
pen.setStyle(Qt::SolidLine);
|
||||||
|
pen.setWidth(1);
|
||||||
|
pen.setCosmetic(true);
|
||||||
|
painter.setPen(pen);
|
||||||
|
color = color.lighter(130);
|
||||||
|
color.setAlpha(64);
|
||||||
|
painter.setBrush(color);
|
||||||
|
painter.drawRect(rect().adjusted(0, 0, -1, -1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatingDragPreview::onApplicationStateChanged(Qt::ApplicationState state)
|
||||||
|
{
|
||||||
|
if (state != Qt::ApplicationActive) {
|
||||||
|
disconnect(qApp,
|
||||||
|
&QApplication::applicationStateChanged,
|
||||||
|
this,
|
||||||
|
&FloatingDragPreview::onApplicationStateChanged);
|
||||||
|
d->cancelDragging();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FloatingDragPreview::eventFilter(QObject *watched, QEvent *event)
|
||||||
|
{
|
||||||
|
if (event->type() == QEvent::KeyPress) {
|
||||||
|
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
|
||||||
|
if (keyEvent->key() == Qt::Key_Escape) {
|
||||||
|
watched->removeEventFilter(this);
|
||||||
|
d->cancelDragging();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
134
src/libs/advanceddockingsystem/floatingdragpreview.h
Normal file
134
src/libs/advanceddockingsystem/floatingdragpreview.h
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 Uwe Kindler
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "floatingdockcontainer.h"
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
namespace ADS {
|
||||||
|
|
||||||
|
class DockWidget;
|
||||||
|
class DockAreaWidget;
|
||||||
|
struct FloatingDragPreviewPrivate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A floating overlay is a temporary floating widget that is just used to
|
||||||
|
* indicate the floating widget movement.
|
||||||
|
* This widget is used as a placeholder for drag operations for non-opaque
|
||||||
|
* docking
|
||||||
|
*/
|
||||||
|
class FloatingDragPreview : public QWidget, public AbstractFloatingWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private:
|
||||||
|
FloatingDragPreviewPrivate *d;
|
||||||
|
friend struct FloatingDragPreviewPrivate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel non opaque undocking if application becomes inactive
|
||||||
|
*/
|
||||||
|
void onApplicationStateChanged(Qt::ApplicationState state);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* Updates the drop overlays
|
||||||
|
*/
|
||||||
|
virtual void moveEvent(QMoveEvent *event) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cares about painting the
|
||||||
|
*/
|
||||||
|
virtual void paintEvent(QPaintEvent *event) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The content is a DockArea or a DockWidget
|
||||||
|
*/
|
||||||
|
FloatingDragPreview(QWidget *content, QWidget *parent);
|
||||||
|
|
||||||
|
public:
|
||||||
|
using Super = QWidget;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance for undocking the DockWidget in Content parameter
|
||||||
|
*/
|
||||||
|
FloatingDragPreview(DockWidget *content);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance for undocking the DockArea given in Content
|
||||||
|
* parameters
|
||||||
|
*/
|
||||||
|
FloatingDragPreview(DockAreaWidget *content);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete private data
|
||||||
|
*/
|
||||||
|
~FloatingDragPreview() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We filter the events of the assigned content widget to receive
|
||||||
|
* escape key presses for canceling the drag operation
|
||||||
|
*/
|
||||||
|
virtual bool eventFilter(QObject *watched, QEvent *event) override;
|
||||||
|
|
||||||
|
public: // implements AbstractFloatingWidget
|
||||||
|
virtual void startFloating(const QPoint &dragStartMousePos,
|
||||||
|
const QSize &size,
|
||||||
|
eDragState dragState,
|
||||||
|
QWidget *mouseEventHandler) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves the widget to a new position relative to the position given when
|
||||||
|
* startFloating() was called
|
||||||
|
*/
|
||||||
|
virtual void moveFloating() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finishes dragging.
|
||||||
|
* Hides the dock overlays and executes the real undocking and docking
|
||||||
|
* of the assigned Content widget
|
||||||
|
*/
|
||||||
|
virtual void finishDragging() override;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
/**
|
||||||
|
* This signal is emitted, if dragging has been canceled by escape key
|
||||||
|
* or by active application switching via task manager
|
||||||
|
*/
|
||||||
|
void draggingCanceled();
|
||||||
|
}; // class FloatingDragPreview
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
81
src/libs/advanceddockingsystem/iconprovider.cpp
Normal file
81
src/libs/advanceddockingsystem/iconprovider.cpp
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 Uwe Kindler
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "iconprovider.h"
|
||||||
|
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
namespace ADS {
|
||||||
|
/**
|
||||||
|
* Private data class (pimpl)
|
||||||
|
*/
|
||||||
|
struct IconProviderPrivate
|
||||||
|
{
|
||||||
|
IconProvider *q;
|
||||||
|
QVector<QIcon> m_userIcons{IconCount, QIcon()};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private data constructor
|
||||||
|
*/
|
||||||
|
IconProviderPrivate(IconProvider *parent);
|
||||||
|
};
|
||||||
|
// struct LedArrayPanelPrivate
|
||||||
|
|
||||||
|
IconProviderPrivate::IconProviderPrivate(IconProvider *parent)
|
||||||
|
: q(parent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
IconProvider::IconProvider()
|
||||||
|
: d(new IconProviderPrivate(this))
|
||||||
|
{}
|
||||||
|
|
||||||
|
IconProvider::~IconProvider()
|
||||||
|
{
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
QIcon IconProvider::customIcon(eIcon iconId) const
|
||||||
|
{
|
||||||
|
Q_ASSERT(iconId < d->m_userIcons.size());
|
||||||
|
return d->m_userIcons[iconId];
|
||||||
|
}
|
||||||
|
|
||||||
|
void IconProvider::registerCustomIcon(eIcon iconId, const QIcon &icon)
|
||||||
|
{
|
||||||
|
Q_ASSERT(iconId < d->m_userIcons.size());
|
||||||
|
d->m_userIcons[iconId] = icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
81
src/libs/advanceddockingsystem/iconprovider.h
Normal file
81
src/libs/advanceddockingsystem/iconprovider.h
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 Uwe Kindler
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ads_globals.h"
|
||||||
|
|
||||||
|
#include <QIcon>
|
||||||
|
|
||||||
|
namespace ADS {
|
||||||
|
|
||||||
|
struct IconProviderPrivate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This object provides all icons that are required by the advanced docking
|
||||||
|
* system.
|
||||||
|
* The IconProvider enables the user to register custom icons in case using
|
||||||
|
* stylesheets is not an option.
|
||||||
|
*/
|
||||||
|
class ADS_EXPORT IconProvider
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
IconProviderPrivate *d; ///< private data (pimpl)
|
||||||
|
friend struct IconProviderPrivate;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Default Constructor
|
||||||
|
*/
|
||||||
|
IconProvider();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual Destructor
|
||||||
|
*/
|
||||||
|
virtual ~IconProvider();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The function returns a custom icon if one is registered and a null Icon
|
||||||
|
* if no custom icon is registered
|
||||||
|
*/
|
||||||
|
QIcon customIcon(eIcon iconId) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a custom icon for the given IconId
|
||||||
|
*/
|
||||||
|
void registerCustomIcon(eIcon iconId, const QIcon &icon);
|
||||||
|
}; // class IconProvider
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
122
src/libs/advanceddockingsystem/images/close-button-disabled.svg
Normal file
122
src/libs/advanceddockingsystem/images/close-button-disabled.svg
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
version="1.1"
|
||||||
|
id="Capa_1"
|
||||||
|
x="0px"
|
||||||
|
y="0px"
|
||||||
|
width="512"
|
||||||
|
height="512"
|
||||||
|
viewBox="0 0 512 512"
|
||||||
|
xml:space="preserve"
|
||||||
|
sodipodi:docname="close-button-disabled.svg"
|
||||||
|
inkscape:version="0.92.3 (2405546, 2018-03-11)"><metadata
|
||||||
|
id="metadata897"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs895" /><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1017"
|
||||||
|
id="namedview893"
|
||||||
|
showgrid="false"
|
||||||
|
fit-margin-top="0"
|
||||||
|
fit-margin-left="0"
|
||||||
|
fit-margin-right="0"
|
||||||
|
fit-margin-bottom="0"
|
||||||
|
inkscape:zoom="0.85862966"
|
||||||
|
inkscape:cx="345.29142"
|
||||||
|
inkscape:cy="32.731258"
|
||||||
|
inkscape:window-x="-8"
|
||||||
|
inkscape:window-y="-8"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="Capa_1" />
|
||||||
|
<g
|
||||||
|
id="g860"
|
||||||
|
transform="matrix(0.71708683,0,0,0.71708683,128,128)"
|
||||||
|
style="stroke:none;stroke-opacity:1;fill:#000000;fill-opacity:0.50196081">
|
||||||
|
<g
|
||||||
|
id="close"
|
||||||
|
style="stroke:none;stroke-opacity:1;fill:#000000;fill-opacity:0.50196081">
|
||||||
|
<polygon
|
||||||
|
points="357,321.3 214.2,178.5 357,35.7 321.3,0 178.5,142.8 35.7,0 0,35.7 142.8,178.5 0,321.3 35.7,357 178.5,214.2 321.3,357 "
|
||||||
|
id="polygon857"
|
||||||
|
style="stroke:none;stroke-opacity:1;fill:#000000;fill-opacity:0.50196081" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g862"
|
||||||
|
transform="translate(0,155)">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g864"
|
||||||
|
transform="translate(0,155)">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g866"
|
||||||
|
transform="translate(0,155)">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g868"
|
||||||
|
transform="translate(0,155)">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g870"
|
||||||
|
transform="translate(0,155)">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g872"
|
||||||
|
transform="translate(0,155)">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g874"
|
||||||
|
transform="translate(0,155)">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g876"
|
||||||
|
transform="translate(0,155)">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g878"
|
||||||
|
transform="translate(0,155)">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g880"
|
||||||
|
transform="translate(0,155)">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g882"
|
||||||
|
transform="translate(0,155)">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g884"
|
||||||
|
transform="translate(0,155)">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g886"
|
||||||
|
transform="translate(0,155)">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g888"
|
||||||
|
transform="translate(0,155)">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g890"
|
||||||
|
transform="translate(0,155)">
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.9 KiB |
119
src/libs/advanceddockingsystem/images/close-button.svg
Normal file
119
src/libs/advanceddockingsystem/images/close-button.svg
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
version="1.1"
|
||||||
|
id="Capa_1"
|
||||||
|
x="0px"
|
||||||
|
y="0px"
|
||||||
|
width="512"
|
||||||
|
height="512"
|
||||||
|
viewBox="0 0 512 512"
|
||||||
|
xml:space="preserve"
|
||||||
|
sodipodi:docname="close-button.svg"
|
||||||
|
inkscape:version="0.92.3 (2405546, 2018-03-11)"><metadata
|
||||||
|
id="metadata897"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs895" /><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1017"
|
||||||
|
id="namedview893"
|
||||||
|
showgrid="false"
|
||||||
|
fit-margin-top="0"
|
||||||
|
fit-margin-left="0"
|
||||||
|
fit-margin-right="0"
|
||||||
|
fit-margin-bottom="0"
|
||||||
|
inkscape:zoom="0.85862966"
|
||||||
|
inkscape:cx="345.29142"
|
||||||
|
inkscape:cy="32.731258"
|
||||||
|
inkscape:window-x="-8"
|
||||||
|
inkscape:window-y="-8"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="Capa_1" />
|
||||||
|
<g
|
||||||
|
id="g860"
|
||||||
|
transform="matrix(0.71708683,0,0,0.71708683,128,128)">
|
||||||
|
<g
|
||||||
|
id="close">
|
||||||
|
<polygon
|
||||||
|
points="357,321.3 214.2,178.5 357,35.7 321.3,0 178.5,142.8 35.7,0 0,35.7 142.8,178.5 0,321.3 35.7,357 178.5,214.2 321.3,357 "
|
||||||
|
id="polygon857" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g862"
|
||||||
|
transform="translate(0,155)">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g864"
|
||||||
|
transform="translate(0,155)">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g866"
|
||||||
|
transform="translate(0,155)">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g868"
|
||||||
|
transform="translate(0,155)">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g870"
|
||||||
|
transform="translate(0,155)">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g872"
|
||||||
|
transform="translate(0,155)">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g874"
|
||||||
|
transform="translate(0,155)">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g876"
|
||||||
|
transform="translate(0,155)">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g878"
|
||||||
|
transform="translate(0,155)">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g880"
|
||||||
|
transform="translate(0,155)">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g882"
|
||||||
|
transform="translate(0,155)">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g884"
|
||||||
|
transform="translate(0,155)">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g886"
|
||||||
|
transform="translate(0,155)">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g888"
|
||||||
|
transform="translate(0,155)">
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g890"
|
||||||
|
transform="translate(0,155)">
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.7 KiB |
168
src/libs/advanceddockingsystem/linux/floatingwidgettitlebar.cpp
Normal file
168
src/libs/advanceddockingsystem/linux/floatingwidgettitlebar.cpp
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 Uwe Kindler
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "floatingwidgettitlebar.h"
|
||||||
|
|
||||||
|
#include "ads_globals.h"
|
||||||
|
#include "elidinglabel.h"
|
||||||
|
#include "floatingdockcontainer.h"
|
||||||
|
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QMouseEvent>
|
||||||
|
#include <QPixmap>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QStyle>
|
||||||
|
#include <QToolButton>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace ADS {
|
||||||
|
|
||||||
|
using TabLabelType = ElidingLabel;
|
||||||
|
using tCloseButton = QPushButton;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Private data class of public interface CFloatingWidgetTitleBar
|
||||||
|
*/
|
||||||
|
struct FloatingWidgetTitleBarPrivate
|
||||||
|
{
|
||||||
|
FloatingWidgetTitleBar *q; ///< public interface class
|
||||||
|
QLabel *m_iconLabel = nullptr;
|
||||||
|
TabLabelType *m_titleLabel;
|
||||||
|
tCloseButton *m_closeButton = nullptr;
|
||||||
|
FloatingDockContainer *m_floatingWidget = nullptr;
|
||||||
|
eDragState m_dragState = DraggingInactive;
|
||||||
|
|
||||||
|
FloatingWidgetTitleBarPrivate(FloatingWidgetTitleBar *parent)
|
||||||
|
: q(parent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the complete layout including all controls
|
||||||
|
*/
|
||||||
|
void createLayout();
|
||||||
|
};
|
||||||
|
|
||||||
|
void FloatingWidgetTitleBarPrivate::createLayout()
|
||||||
|
{
|
||||||
|
m_titleLabel = new TabLabelType();
|
||||||
|
m_titleLabel->setElideMode(Qt::ElideRight);
|
||||||
|
m_titleLabel->setText("DockWidget->windowTitle()");
|
||||||
|
m_titleLabel->setObjectName("floatingTitleLabel");
|
||||||
|
m_titleLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||||
|
|
||||||
|
m_closeButton = new tCloseButton();
|
||||||
|
m_closeButton->setObjectName("floatingTitleCloseButton");
|
||||||
|
m_closeButton->setFlat(true);
|
||||||
|
|
||||||
|
// The standard icons do does not look good on high DPI screens
|
||||||
|
QIcon closeIcon;
|
||||||
|
QPixmap normalPixmap = q->style()->standardPixmap(QStyle::SP_TitleBarCloseButton,
|
||||||
|
nullptr,
|
||||||
|
m_closeButton);
|
||||||
|
closeIcon.addPixmap(normalPixmap, QIcon::Normal);
|
||||||
|
closeIcon.addPixmap(internal::createTransparentPixmap(normalPixmap, 0.25), QIcon::Disabled);
|
||||||
|
m_closeButton->setIcon(q->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
|
||||||
|
m_closeButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||||
|
m_closeButton->setVisible(true);
|
||||||
|
m_closeButton->setFocusPolicy(Qt::NoFocus);
|
||||||
|
q->connect(m_closeButton, &QPushButton::clicked, q, &FloatingWidgetTitleBar::closeRequested);
|
||||||
|
|
||||||
|
QFontMetrics fontMetrics(m_titleLabel->font());
|
||||||
|
int spacing = qRound(fontMetrics.height() / 4.0);
|
||||||
|
|
||||||
|
// Fill the layout
|
||||||
|
QBoxLayout *layout = new QBoxLayout(QBoxLayout::LeftToRight);
|
||||||
|
layout->setContentsMargins(6, 0, 0, 0);
|
||||||
|
layout->setSpacing(0);
|
||||||
|
q->setLayout(layout);
|
||||||
|
layout->addWidget(m_titleLabel, 1);
|
||||||
|
layout->addSpacing(spacing);
|
||||||
|
layout->addWidget(m_closeButton);
|
||||||
|
layout->setAlignment(Qt::AlignCenter);
|
||||||
|
|
||||||
|
m_titleLabel->setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
FloatingWidgetTitleBar::FloatingWidgetTitleBar(FloatingDockContainer *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
, d(new FloatingWidgetTitleBarPrivate(this))
|
||||||
|
{
|
||||||
|
d->m_floatingWidget = parent;
|
||||||
|
d->createLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
FloatingWidgetTitleBar::~FloatingWidgetTitleBar()
|
||||||
|
{
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatingWidgetTitleBar::mousePressEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
if (event->button() == Qt::LeftButton) {
|
||||||
|
d->m_dragState = DraggingFloatingWidget;
|
||||||
|
d->m_floatingWidget->startDragging(event->pos(), d->m_floatingWidget->size(), this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Super::mousePressEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatingWidgetTitleBar::mouseReleaseEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
d->m_dragState = DraggingInactive;
|
||||||
|
if (d->m_floatingWidget) {
|
||||||
|
d->m_floatingWidget->finishDragging();
|
||||||
|
}
|
||||||
|
Super::mouseReleaseEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatingWidgetTitleBar::mouseMoveEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
if (!(event->buttons() & Qt::LeftButton) || DraggingInactive == d->m_dragState) {
|
||||||
|
d->m_dragState = DraggingInactive;
|
||||||
|
Super::mouseMoveEvent(event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// move floating window
|
||||||
|
if (DraggingFloatingWidget == d->m_dragState) {
|
||||||
|
d->m_floatingWidget->moveFloating();
|
||||||
|
Super::mouseMoveEvent(event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Super::mouseMoveEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatingWidgetTitleBar::enableCloseButton(bool enable)
|
||||||
|
{
|
||||||
|
d->m_closeButton->setEnabled(enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatingWidgetTitleBar::setTitle(const QString &text)
|
||||||
|
{
|
||||||
|
d->m_titleLabel->setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 Uwe Kindler
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
namespace ADS {
|
||||||
|
|
||||||
|
class FloatingDockContainer;
|
||||||
|
struct FloatingWidgetTitleBarPrivate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Titlebar for floating widgets to capture non client are mouse events.
|
||||||
|
* Linux does not support NonClieantArea mouse events like
|
||||||
|
* QEvent::NonClientAreaMouseButtonPress. Because these events are required
|
||||||
|
* for the docking system to work properly, we use our own titlebar here to
|
||||||
|
* capture the required mouse events.
|
||||||
|
*/
|
||||||
|
class FloatingWidgetTitleBar : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private:
|
||||||
|
FloatingWidgetTitleBarPrivate *d; ///< private data (pimpl)
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void mousePressEvent(QMouseEvent *event) override;
|
||||||
|
virtual void mouseReleaseEvent(QMouseEvent *event) override;
|
||||||
|
virtual void mouseMoveEvent(QMouseEvent *event) override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using Super = QWidget;
|
||||||
|
explicit FloatingWidgetTitleBar(FloatingDockContainer *parent = nullptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual Destructor
|
||||||
|
*/
|
||||||
|
virtual ~FloatingWidgetTitleBar() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables / disables the window close button.
|
||||||
|
*/
|
||||||
|
void enableCloseButton(bool enable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the window title, that means, the text of the internal tile label.
|
||||||
|
*/
|
||||||
|
void setTitle(const QString &text);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
/**
|
||||||
|
* This signal is emitted, if the close button is clicked.
|
||||||
|
*/
|
||||||
|
void closeRequested();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
4
src/libs/advanceddockingsystem/linux/linux.pri
Normal file
4
src/libs/advanceddockingsystem/linux/linux.pri
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
VPATH += $$PWD
|
||||||
|
SOURCES += $$PWD/floatingwidgettitlebar.cpp
|
||||||
|
|
||||||
|
HEADERS += $$PWD/floatingwidgettitlebar.h
|
||||||
6
src/libs/advanceddockingsystem/resources.qrc
Normal file
6
src/libs/advanceddockingsystem/resources.qrc
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<RCC>
|
||||||
|
<qresource prefix="/ads">
|
||||||
|
<file>images/close-button.svg</file>
|
||||||
|
<file>images/close-button-disabled.svg</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
||||||
206
src/libs/advanceddockingsystem/workspacedialog.cpp
Normal file
206
src/libs/advanceddockingsystem/workspacedialog.cpp
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "workspacedialog.h"
|
||||||
|
|
||||||
|
#include "dockmanager.h"
|
||||||
|
|
||||||
|
#include <utils/algorithm.h>
|
||||||
|
|
||||||
|
#include <QInputDialog>
|
||||||
|
#include <QValidator>
|
||||||
|
|
||||||
|
namespace ADS {
|
||||||
|
|
||||||
|
class WorkspaceValidator : public QValidator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WorkspaceValidator(QObject *parent, const QStringList &workspaces);
|
||||||
|
void fixup(QString &input) const override;
|
||||||
|
QValidator::State validate(QString &input, int &pos) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QStringList m_workspaces;
|
||||||
|
};
|
||||||
|
|
||||||
|
WorkspaceValidator::WorkspaceValidator(QObject *parent, const QStringList &workspaces)
|
||||||
|
: QValidator(parent)
|
||||||
|
, m_workspaces(workspaces)
|
||||||
|
{}
|
||||||
|
|
||||||
|
QValidator::State WorkspaceValidator::validate(QString &input, int &pos) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(pos)
|
||||||
|
|
||||||
|
if (input.contains(QLatin1Char('/')) || input.contains(QLatin1Char(':'))
|
||||||
|
|| input.contains(QLatin1Char('\\')) || input.contains(QLatin1Char('?'))
|
||||||
|
|| input.contains(QLatin1Char('*')) || input.contains(QLatin1Char('_')))
|
||||||
|
return QValidator::Invalid;
|
||||||
|
|
||||||
|
if (m_workspaces.contains(input))
|
||||||
|
return QValidator::Intermediate;
|
||||||
|
else
|
||||||
|
return QValidator::Acceptable;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceValidator::fixup(QString &input) const
|
||||||
|
{
|
||||||
|
int i = 2;
|
||||||
|
QString copy;
|
||||||
|
do {
|
||||||
|
copy = input + QLatin1String(" (") + QString::number(i) + QLatin1Char(')');
|
||||||
|
++i;
|
||||||
|
} while (m_workspaces.contains(copy));
|
||||||
|
input = copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
WorkspaceNameInputDialog::WorkspaceNameInputDialog(DockManager *manager, QWidget *parent)
|
||||||
|
: QDialog(parent)
|
||||||
|
, m_manager(manager)
|
||||||
|
{
|
||||||
|
auto hlayout = new QVBoxLayout(this);
|
||||||
|
auto label = new QLabel(tr("Enter the name of the workspace:"), this);
|
||||||
|
hlayout->addWidget(label);
|
||||||
|
m_newWorkspaceLineEdit = new QLineEdit(this);
|
||||||
|
m_newWorkspaceLineEdit->setValidator(new WorkspaceValidator(this, m_manager->workspaces()));
|
||||||
|
hlayout->addWidget(m_newWorkspaceLineEdit);
|
||||||
|
auto buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel,
|
||||||
|
Qt::Horizontal,
|
||||||
|
this);
|
||||||
|
m_okButton = buttons->button(QDialogButtonBox::Ok);
|
||||||
|
m_switchToButton = new QPushButton;
|
||||||
|
buttons->addButton(m_switchToButton, QDialogButtonBox::AcceptRole);
|
||||||
|
connect(m_switchToButton, &QPushButton::clicked, [this]() { m_usedSwitchTo = true; });
|
||||||
|
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||||
|
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||||
|
hlayout->addWidget(buttons);
|
||||||
|
setLayout(hlayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceNameInputDialog::setActionText(const QString &actionText,
|
||||||
|
const QString &openActionText)
|
||||||
|
{
|
||||||
|
m_okButton->setText(actionText);
|
||||||
|
m_switchToButton->setText(openActionText);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceNameInputDialog::setValue(const QString &value)
|
||||||
|
{
|
||||||
|
m_newWorkspaceLineEdit->setText(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString WorkspaceNameInputDialog::value() const
|
||||||
|
{
|
||||||
|
return m_newWorkspaceLineEdit->text();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WorkspaceNameInputDialog::isSwitchToRequested() const
|
||||||
|
{
|
||||||
|
return m_usedSwitchTo;
|
||||||
|
}
|
||||||
|
|
||||||
|
WorkspaceDialog::WorkspaceDialog(DockManager *manager, QWidget *parent)
|
||||||
|
: QDialog(parent)
|
||||||
|
, m_manager(manager)
|
||||||
|
{
|
||||||
|
m_ui.setupUi(this);
|
||||||
|
m_ui.workspaceView->setActivationMode(Utils::DoubleClickActivation);
|
||||||
|
|
||||||
|
connect(m_ui.btCreateNew,
|
||||||
|
&QAbstractButton::clicked,
|
||||||
|
m_ui.workspaceView,
|
||||||
|
&WorkspaceView::createNewWorkspace);
|
||||||
|
connect(m_ui.btClone,
|
||||||
|
&QAbstractButton::clicked,
|
||||||
|
m_ui.workspaceView,
|
||||||
|
&WorkspaceView::cloneCurrentWorkspace);
|
||||||
|
connect(m_ui.btDelete,
|
||||||
|
&QAbstractButton::clicked,
|
||||||
|
m_ui.workspaceView,
|
||||||
|
&WorkspaceView::deleteSelectedWorkspaces);
|
||||||
|
connect(m_ui.btSwitch,
|
||||||
|
&QAbstractButton::clicked,
|
||||||
|
m_ui.workspaceView,
|
||||||
|
&WorkspaceView::switchToCurrentWorkspace);
|
||||||
|
connect(m_ui.btRename,
|
||||||
|
&QAbstractButton::clicked,
|
||||||
|
m_ui.workspaceView,
|
||||||
|
&WorkspaceView::renameCurrentWorkspace);
|
||||||
|
connect(m_ui.workspaceView,
|
||||||
|
&WorkspaceView::activated,
|
||||||
|
m_ui.workspaceView,
|
||||||
|
&WorkspaceView::switchToCurrentWorkspace);
|
||||||
|
|
||||||
|
connect(m_ui.workspaceView, &WorkspaceView::selected, this, &WorkspaceDialog::updateActions);
|
||||||
|
connect(m_ui.workspaceView, &WorkspaceView::workspaceSwitched, this, &QDialog::reject);
|
||||||
|
|
||||||
|
m_ui.whatsAWorkspaceLabel->setOpenExternalLinks(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceDialog::setAutoLoadWorkspace(bool check)
|
||||||
|
{
|
||||||
|
m_ui.autoLoadCheckBox->setChecked(check);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WorkspaceDialog::autoLoadWorkspace() const
|
||||||
|
{
|
||||||
|
return m_ui.autoLoadCheckBox->checkState() == Qt::Checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
DockManager *WorkspaceDialog::dockManager() const
|
||||||
|
{
|
||||||
|
return m_manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceDialog::updateActions(const QStringList &workspaces)
|
||||||
|
{
|
||||||
|
if (workspaces.isEmpty()) {
|
||||||
|
m_ui.btDelete->setEnabled(false);
|
||||||
|
m_ui.btRename->setEnabled(false);
|
||||||
|
m_ui.btClone->setEnabled(false);
|
||||||
|
m_ui.btSwitch->setEnabled(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const bool defaultIsSelected = workspaces.contains("default"); // TODO use const var
|
||||||
|
const bool activeIsSelected = Utils::anyOf(workspaces, [this](const QString &workspace) {
|
||||||
|
return workspace == m_manager->activeWorkspace();
|
||||||
|
});
|
||||||
|
m_ui.btDelete->setEnabled(!defaultIsSelected && !activeIsSelected);
|
||||||
|
m_ui.btRename->setEnabled(workspaces.size() == 1 && !defaultIsSelected);
|
||||||
|
m_ui.btClone->setEnabled(workspaces.size() == 1);
|
||||||
|
m_ui.btSwitch->setEnabled(workspaces.size() == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
93
src/libs/advanceddockingsystem/workspacedialog.h
Normal file
93
src/libs/advanceddockingsystem/workspacedialog.h
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ui_workspacedialog.h"
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QLineEdit;
|
||||||
|
class QPushButton;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
namespace ADS {
|
||||||
|
|
||||||
|
class DockManager;
|
||||||
|
|
||||||
|
class WorkspaceDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit WorkspaceDialog(DockManager *manager, QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
void setAutoLoadWorkspace(bool);
|
||||||
|
bool autoLoadWorkspace() const;
|
||||||
|
|
||||||
|
DockManager *dockManager() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateActions(const QStringList &workspaces);
|
||||||
|
|
||||||
|
Ui::WorkspaceDialog m_ui;
|
||||||
|
|
||||||
|
DockManager *m_manager = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class WorkspaceNameInputDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit WorkspaceNameInputDialog(DockManager *manager, QWidget *parent);
|
||||||
|
|
||||||
|
void setActionText(const QString &actionText, const QString &openActionText);
|
||||||
|
void setValue(const QString &value);
|
||||||
|
QString value() const;
|
||||||
|
bool isSwitchToRequested() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QLineEdit *m_newWorkspaceLineEdit = nullptr;
|
||||||
|
QPushButton *m_switchToButton = nullptr;
|
||||||
|
QPushButton *m_okButton = nullptr;
|
||||||
|
bool m_usedSwitchTo = false;
|
||||||
|
|
||||||
|
DockManager *m_manager;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
172
src/libs/advanceddockingsystem/workspacedialog.ui
Normal file
172
src/libs/advanceddockingsystem/workspacedialog.ui
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>ADS::WorkspaceDialog</class>
|
||||||
|
<widget class="QDialog" name="ADS::WorkspaceDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>373</width>
|
||||||
|
<height>282</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Workspace Manager</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="WorkspaceView" name="workspaceView">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
|
<horstretch>1</horstretch>
|
||||||
|
<verstretch>1</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1" rowspan="2">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btCreateNew">
|
||||||
|
<property name="text">
|
||||||
|
<string>&New</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btRename">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Rename</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btClone">
|
||||||
|
<property name="text">
|
||||||
|
<string>C&lone</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btDelete">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Delete</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btSwitch">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Switch To</string>
|
||||||
|
</property>
|
||||||
|
<property name="default">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>85</width>
|
||||||
|
<height>48</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QCheckBox" name="autoLoadCheckBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>Restore last workspace on startup</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0" colspan="2">
|
||||||
|
<widget class="Line" name="line">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="whatsAWorkspaceLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string><a href="qthelp://org.qt-project.qtcreator/doc/creator-project-managing-workspaces.html">What is a Workspace?</a></string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Close</set>
|
||||||
|
</property>
|
||||||
|
<property name="centerButtons">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>WorkspaceView</class>
|
||||||
|
<extends>QTreeView</extends>
|
||||||
|
<header>workspaceview.h</header>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>ADS::WorkspaceDialog</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>191</x>
|
||||||
|
<y>244</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>114</x>
|
||||||
|
<y>237</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>ADS::WorkspaceDialog</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>246</x>
|
||||||
|
<y>237</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>78</x>
|
||||||
|
<y>216</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
||||||
269
src/libs/advanceddockingsystem/workspacemodel.cpp
Normal file
269
src/libs/advanceddockingsystem/workspacemodel.cpp
Normal file
@@ -0,0 +1,269 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "workspacemodel.h"
|
||||||
|
|
||||||
|
#include "dockmanager.h"
|
||||||
|
#include "workspacedialog.h"
|
||||||
|
|
||||||
|
#include <utils/algorithm.h>
|
||||||
|
#include <utils/fileutils.h>
|
||||||
|
#include <utils/stringutils.h>
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFileInfo>
|
||||||
|
|
||||||
|
namespace ADS {
|
||||||
|
|
||||||
|
WorkspaceModel::WorkspaceModel(DockManager *manager, QObject *parent)
|
||||||
|
: QAbstractTableModel(parent)
|
||||||
|
, m_manager(manager)
|
||||||
|
{
|
||||||
|
m_sortedWorkspaces = m_manager->workspaces();
|
||||||
|
connect(m_manager, &DockManager::workspaceLoaded, this, &WorkspaceModel::resetWorkspaces);
|
||||||
|
}
|
||||||
|
|
||||||
|
int WorkspaceModel::indexOfWorkspace(const QString &workspace)
|
||||||
|
{
|
||||||
|
return m_sortedWorkspaces.indexOf(workspace);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString WorkspaceModel::workspaceAt(int row) const
|
||||||
|
{
|
||||||
|
return m_sortedWorkspaces.value(row, QString());
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant WorkspaceModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||||
|
{
|
||||||
|
QVariant result;
|
||||||
|
if (orientation == Qt::Horizontal) {
|
||||||
|
switch (role) {
|
||||||
|
case Qt::DisplayRole:
|
||||||
|
switch (section) {
|
||||||
|
case 0:
|
||||||
|
result = tr("Workspace");
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
result = tr("Last Modified");
|
||||||
|
break;
|
||||||
|
} // switch (section)
|
||||||
|
break;
|
||||||
|
} // switch (role)
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WorkspaceModel::columnCount(const QModelIndex &) const
|
||||||
|
{
|
||||||
|
static int sectionCount = 0;
|
||||||
|
if (sectionCount == 0) {
|
||||||
|
// headers sections defining possible columns
|
||||||
|
while (!headerData(sectionCount, Qt::Horizontal, Qt::DisplayRole).isNull())
|
||||||
|
sectionCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sectionCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WorkspaceModel::rowCount(const QModelIndex &) const
|
||||||
|
{
|
||||||
|
return m_sortedWorkspaces.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList pathsToBaseNames(const QStringList &paths)
|
||||||
|
{
|
||||||
|
return Utils::transform(paths,
|
||||||
|
[](const QString &path) { return QFileInfo(path).completeBaseName(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList pathsWithTildeHomePath(const QStringList &paths)
|
||||||
|
{
|
||||||
|
return Utils::transform(paths, [](const QString &path) {
|
||||||
|
return Utils::withTildeHomePath(QDir::toNativeSeparators(path));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant WorkspaceModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
QVariant result;
|
||||||
|
if (index.isValid()) {
|
||||||
|
QString workspaceName = m_sortedWorkspaces.at(index.row());
|
||||||
|
|
||||||
|
switch (role) {
|
||||||
|
case Qt::DisplayRole:
|
||||||
|
switch (index.column()) {
|
||||||
|
case 0:
|
||||||
|
result = workspaceName;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
result = m_manager->workspaceDateTime(workspaceName);
|
||||||
|
break;
|
||||||
|
} // switch (section)
|
||||||
|
break;
|
||||||
|
case Qt::FontRole: {
|
||||||
|
QFont font;
|
||||||
|
if (m_manager->isDefaultWorkspace(workspaceName))
|
||||||
|
font.setItalic(true);
|
||||||
|
else
|
||||||
|
font.setItalic(false);
|
||||||
|
if (m_manager->activeWorkspace() == workspaceName
|
||||||
|
&& !m_manager->isFactoryDefaultWorkspace(workspaceName))
|
||||||
|
font.setBold(true);
|
||||||
|
else
|
||||||
|
font.setBold(false);
|
||||||
|
result = font;
|
||||||
|
} break;
|
||||||
|
case DefaultWorkspaceRole:
|
||||||
|
result = m_manager->isDefaultWorkspace(workspaceName);
|
||||||
|
break;
|
||||||
|
case LastWorkspaceRole:
|
||||||
|
result = m_manager->lastWorkspace() == workspaceName;
|
||||||
|
break;
|
||||||
|
case ActiveWorkspaceRole:
|
||||||
|
result = m_manager->activeWorkspace() == workspaceName;
|
||||||
|
break;
|
||||||
|
} // switch (role)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<int, QByteArray> WorkspaceModel::roleNames() const
|
||||||
|
{
|
||||||
|
static QHash<int, QByteArray> extraRoles{{Qt::DisplayRole, "workspaceName"},
|
||||||
|
{DefaultWorkspaceRole, "defaultWorkspace"},
|
||||||
|
{LastWorkspaceRole, "activeWorkspace"},
|
||||||
|
{ActiveWorkspaceRole, "lastWorkspace"}};
|
||||||
|
return QAbstractTableModel::roleNames().unite(extraRoles);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceModel::sort(int column, Qt::SortOrder order)
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
const auto cmp = [this, column, order](const QString &s1, const QString &s2) {
|
||||||
|
bool isLess;
|
||||||
|
if (column == 0)
|
||||||
|
isLess = s1 < s2;
|
||||||
|
else
|
||||||
|
isLess = m_manager->workspaceDateTime(s1) < m_manager->workspaceDateTime(s2);
|
||||||
|
if (order == Qt::DescendingOrder)
|
||||||
|
isLess = !isLess;
|
||||||
|
return isLess;
|
||||||
|
};
|
||||||
|
Utils::sort(m_sortedWorkspaces, cmp);
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WorkspaceModel::isDefaultVirgin() const
|
||||||
|
{
|
||||||
|
return false; //m_manager->isFactoryDefaultWorkspace(); // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceModel::resetWorkspaces()
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
m_sortedWorkspaces = m_manager->workspaces();
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceModel::newWorkspace(QWidget *parent)
|
||||||
|
{
|
||||||
|
WorkspaceNameInputDialog workspaceInputDialog(m_manager, parent);
|
||||||
|
workspaceInputDialog.setWindowTitle(tr("New Workspace Name"));
|
||||||
|
workspaceInputDialog.setActionText(tr("&Create"), tr("Create and &Open"));
|
||||||
|
|
||||||
|
runWorkspaceNameInputDialog(&workspaceInputDialog, [this](const QString &newName) {
|
||||||
|
m_manager->createWorkspace(newName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceModel::cloneWorkspace(QWidget *parent, const QString &workspace)
|
||||||
|
{
|
||||||
|
WorkspaceNameInputDialog workspaceInputDialog(m_manager, parent);
|
||||||
|
workspaceInputDialog.setWindowTitle(tr("New Workspace Name"));
|
||||||
|
workspaceInputDialog.setActionText(tr("&Clone"), tr("Clone and &Open"));
|
||||||
|
workspaceInputDialog.setValue(workspace + " (2)");
|
||||||
|
|
||||||
|
runWorkspaceNameInputDialog(&workspaceInputDialog, [this, workspace](const QString &newName) {
|
||||||
|
m_manager->cloneWorkspace(workspace, newName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceModel::deleteWorkspaces(const QStringList &workspaces)
|
||||||
|
{
|
||||||
|
if (!m_manager->confirmWorkspaceDelete(workspaces))
|
||||||
|
return;
|
||||||
|
beginResetModel();
|
||||||
|
m_manager->deleteWorkspaces(workspaces);
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceModel::renameWorkspace(QWidget *parent, const QString &workspace)
|
||||||
|
{
|
||||||
|
WorkspaceNameInputDialog workspaceInputDialog(m_manager, parent);
|
||||||
|
workspaceInputDialog.setWindowTitle(tr("Rename Workspace"));
|
||||||
|
workspaceInputDialog.setActionText(tr("&Rename"), tr("Rename and &Open"));
|
||||||
|
workspaceInputDialog.setValue(workspace);
|
||||||
|
|
||||||
|
runWorkspaceNameInputDialog(&workspaceInputDialog, [this, workspace](const QString &newName) {
|
||||||
|
m_manager->renameWorkspace(workspace, newName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceModel::switchToWorkspace(const QString &workspace)
|
||||||
|
{
|
||||||
|
m_manager->openWorkspace(workspace);
|
||||||
|
emit workspaceSwitched();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceModel::runWorkspaceNameInputDialog(WorkspaceNameInputDialog *workspaceInputDialog,
|
||||||
|
std::function<void(const QString &)> createWorkspace)
|
||||||
|
{
|
||||||
|
if (workspaceInputDialog->exec() == QDialog::Accepted) {
|
||||||
|
QString newWorkspace = workspaceInputDialog->value();
|
||||||
|
if (newWorkspace.isEmpty() || m_manager->workspaces().contains(newWorkspace))
|
||||||
|
return;
|
||||||
|
beginResetModel();
|
||||||
|
createWorkspace(newWorkspace);
|
||||||
|
m_sortedWorkspaces = m_manager->workspaces();
|
||||||
|
endResetModel();
|
||||||
|
|
||||||
|
if (workspaceInputDialog->isSwitchToRequested())
|
||||||
|
switchToWorkspace(newWorkspace);
|
||||||
|
emit workspaceCreated(newWorkspace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
89
src/libs/advanceddockingsystem/workspacemodel.h
Normal file
89
src/libs/advanceddockingsystem/workspacemodel.h
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QAbstractTableModel>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace ADS {
|
||||||
|
|
||||||
|
class DockManager;
|
||||||
|
class WorkspaceNameInputDialog;
|
||||||
|
|
||||||
|
class WorkspaceModel : public QAbstractTableModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum { DefaultWorkspaceRole = Qt::UserRole + 1, LastWorkspaceRole, ActiveWorkspaceRole };
|
||||||
|
|
||||||
|
explicit WorkspaceModel(DockManager *manager, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
int indexOfWorkspace(const QString &workspace);
|
||||||
|
QString workspaceAt(int row) const;
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
|
||||||
|
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
||||||
|
QVariant data(const QModelIndex &index, int role) const override;
|
||||||
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override;
|
||||||
|
|
||||||
|
Q_SCRIPTABLE bool isDefaultVirgin() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void workspaceSwitched();
|
||||||
|
void workspaceCreated(const QString &workspaceName);
|
||||||
|
|
||||||
|
public:
|
||||||
|
void resetWorkspaces();
|
||||||
|
void newWorkspace(QWidget *parent);
|
||||||
|
void cloneWorkspace(QWidget *parent, const QString &workspace);
|
||||||
|
void deleteWorkspaces(const QStringList &workspaces);
|
||||||
|
void renameWorkspace(QWidget *parent, const QString &workspace);
|
||||||
|
void switchToWorkspace(const QString &workspace);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void runWorkspaceNameInputDialog(WorkspaceNameInputDialog *workspaceInputDialog,
|
||||||
|
std::function<void(const QString &)> createWorkspace);
|
||||||
|
|
||||||
|
QStringList m_sortedWorkspaces;
|
||||||
|
DockManager *m_manager;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
205
src/libs/advanceddockingsystem/workspaceview.cpp
Normal file
205
src/libs/advanceddockingsystem/workspaceview.cpp
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "workspaceview.h"
|
||||||
|
|
||||||
|
#include "dockmanager.h"
|
||||||
|
#include "workspacedialog.h"
|
||||||
|
|
||||||
|
#include <utils/algorithm.h>
|
||||||
|
|
||||||
|
#include <QHeaderView>
|
||||||
|
#include <QItemSelection>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QStyledItemDelegate>
|
||||||
|
|
||||||
|
namespace ADS {
|
||||||
|
|
||||||
|
// custom item delegate class
|
||||||
|
class RemoveItemFocusDelegate : public QStyledItemDelegate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RemoveItemFocusDelegate(QObject *parent = nullptr)
|
||||||
|
: QStyledItemDelegate(parent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paint(QPainter *painter,
|
||||||
|
const QStyleOptionViewItem &option,
|
||||||
|
const QModelIndex &index) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
void RemoveItemFocusDelegate::paint(QPainter *painter,
|
||||||
|
const QStyleOptionViewItem &option,
|
||||||
|
const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
QStyleOptionViewItem opt = option;
|
||||||
|
opt.state &= ~QStyle::State_HasFocus;
|
||||||
|
QStyledItemDelegate::paint(painter, opt, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
WorkspaceDialog *WorkspaceView::castToWorkspaceDialog(QWidget *widget)
|
||||||
|
{
|
||||||
|
auto dialog = qobject_cast<WorkspaceDialog *>(widget);
|
||||||
|
Q_ASSERT(dialog);
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
WorkspaceView::WorkspaceView(QWidget *parent)
|
||||||
|
: Utils::TreeView(parent)
|
||||||
|
, m_manager(WorkspaceView::castToWorkspaceDialog(parent)->dockManager())
|
||||||
|
, m_workspaceModel(m_manager)
|
||||||
|
{
|
||||||
|
setItemDelegate(new RemoveItemFocusDelegate(this));
|
||||||
|
setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||||
|
setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||||
|
setWordWrap(false);
|
||||||
|
setRootIsDecorated(false);
|
||||||
|
setSortingEnabled(true);
|
||||||
|
|
||||||
|
setModel(&m_workspaceModel);
|
||||||
|
sortByColumn(0, Qt::AscendingOrder);
|
||||||
|
|
||||||
|
// Ensure that the full workspace name is visible.
|
||||||
|
header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
|
||||||
|
|
||||||
|
QItemSelection firstRow(m_workspaceModel.index(0, 0),
|
||||||
|
m_workspaceModel.index(0, m_workspaceModel.columnCount() - 1));
|
||||||
|
selectionModel()->select(firstRow, QItemSelectionModel::QItemSelectionModel::SelectCurrent);
|
||||||
|
|
||||||
|
connect(this, &Utils::TreeView::activated, [this](const QModelIndex &index) {
|
||||||
|
emit activated(m_workspaceModel.workspaceAt(index.row()));
|
||||||
|
});
|
||||||
|
connect(selectionModel(), &QItemSelectionModel::selectionChanged, [this] {
|
||||||
|
emit selected(selectedWorkspaces());
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(&m_workspaceModel,
|
||||||
|
&WorkspaceModel::workspaceSwitched,
|
||||||
|
this,
|
||||||
|
&WorkspaceView::workspaceSwitched);
|
||||||
|
connect(&m_workspaceModel,
|
||||||
|
&WorkspaceModel::modelReset,
|
||||||
|
this,
|
||||||
|
&WorkspaceView::selectActiveWorkspace);
|
||||||
|
connect(&m_workspaceModel,
|
||||||
|
&WorkspaceModel::workspaceCreated,
|
||||||
|
this,
|
||||||
|
&WorkspaceView::selectWorkspace);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceView::createNewWorkspace()
|
||||||
|
{
|
||||||
|
m_workspaceModel.newWorkspace(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceView::deleteSelectedWorkspaces()
|
||||||
|
{
|
||||||
|
deleteWorkspaces(selectedWorkspaces());
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceView::deleteWorkspaces(const QStringList &workspaces)
|
||||||
|
{
|
||||||
|
m_workspaceModel.deleteWorkspaces(workspaces);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceView::cloneCurrentWorkspace()
|
||||||
|
{
|
||||||
|
m_workspaceModel.cloneWorkspace(this, currentWorkspace());
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceView::renameCurrentWorkspace()
|
||||||
|
{
|
||||||
|
m_workspaceModel.renameWorkspace(this, currentWorkspace());
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceView::switchToCurrentWorkspace()
|
||||||
|
{
|
||||||
|
m_workspaceModel.switchToWorkspace(currentWorkspace());
|
||||||
|
}
|
||||||
|
|
||||||
|
QString WorkspaceView::currentWorkspace()
|
||||||
|
{
|
||||||
|
return m_workspaceModel.workspaceAt(selectionModel()->currentIndex().row());
|
||||||
|
}
|
||||||
|
|
||||||
|
WorkspaceModel *WorkspaceView::workspaceModel()
|
||||||
|
{
|
||||||
|
return &m_workspaceModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceView::selectActiveWorkspace()
|
||||||
|
{
|
||||||
|
selectWorkspace(m_manager->activeWorkspace());
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceView::selectWorkspace(const QString &workspaceName)
|
||||||
|
{
|
||||||
|
int row = m_workspaceModel.indexOfWorkspace(workspaceName);
|
||||||
|
selectionModel()->setCurrentIndex(model()->index(row, 0),
|
||||||
|
QItemSelectionModel::ClearAndSelect
|
||||||
|
| QItemSelectionModel::Rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceView::showEvent(QShowEvent *event)
|
||||||
|
{
|
||||||
|
Utils::TreeView::showEvent(event);
|
||||||
|
selectActiveWorkspace();
|
||||||
|
setFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkspaceView::keyPressEvent(QKeyEvent *event)
|
||||||
|
{
|
||||||
|
if (event->key() != Qt::Key_Delete) {
|
||||||
|
TreeView::keyPressEvent(event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const QStringList workspaces = selectedWorkspaces();
|
||||||
|
if (!workspaces.contains("default")
|
||||||
|
&& !Utils::anyOf(workspaces, [this](const QString &workspace) {
|
||||||
|
return workspace == m_manager->activeWorkspace();
|
||||||
|
})) {
|
||||||
|
deleteWorkspaces(workspaces);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList WorkspaceView::selectedWorkspaces() const
|
||||||
|
{
|
||||||
|
return Utils::transform(selectionModel()->selectedRows(), [this](const QModelIndex &index) {
|
||||||
|
return m_workspaceModel.workspaceAt(index.row());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
85
src/libs/advanceddockingsystem/workspaceview.h
Normal file
85
src/libs/advanceddockingsystem/workspaceview.h
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or (at your option) any later version.
|
||||||
|
** The licenses are as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.LGPLv21 included in the packaging
|
||||||
|
** of this file. Please review the following information to ensure
|
||||||
|
** the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 or (at your option) any later version
|
||||||
|
** approved by the KDE Free Qt Foundation. The licenses are as published by
|
||||||
|
** the Free Software Foundation and appearing in the file LICENSE.GPL3
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "workspacemodel.h"
|
||||||
|
|
||||||
|
#include <utils/itemviews.h>
|
||||||
|
|
||||||
|
#include <QAbstractTableModel>
|
||||||
|
|
||||||
|
namespace ADS {
|
||||||
|
|
||||||
|
class DockManager;
|
||||||
|
class WorkspaceDialog;
|
||||||
|
|
||||||
|
class WorkspaceView : public Utils::TreeView
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit WorkspaceView(QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
void createNewWorkspace();
|
||||||
|
void deleteSelectedWorkspaces();
|
||||||
|
void cloneCurrentWorkspace();
|
||||||
|
void renameCurrentWorkspace();
|
||||||
|
void switchToCurrentWorkspace();
|
||||||
|
|
||||||
|
QString currentWorkspace();
|
||||||
|
WorkspaceModel *workspaceModel();
|
||||||
|
void selectActiveWorkspace();
|
||||||
|
void selectWorkspace(const QString &workspaceName);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void activated(const QString &workspace);
|
||||||
|
void selected(const QStringList &workspaces);
|
||||||
|
void workspaceSwitched();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void showEvent(QShowEvent *event) override;
|
||||||
|
void keyPressEvent(QKeyEvent *event) override;
|
||||||
|
|
||||||
|
void deleteWorkspaces(const QStringList &workspaces);
|
||||||
|
QStringList selectedWorkspaces() const;
|
||||||
|
|
||||||
|
static WorkspaceDialog *castToWorkspaceDialog(QWidget *widget);
|
||||||
|
|
||||||
|
DockManager *m_manager;
|
||||||
|
WorkspaceModel m_workspaceModel;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ADS
|
||||||
@@ -3,6 +3,7 @@ include(../../qtcreator.pri)
|
|||||||
TEMPLATE = subdirs
|
TEMPLATE = subdirs
|
||||||
|
|
||||||
SUBDIRS += \
|
SUBDIRS += \
|
||||||
|
advanceddockingsystem \
|
||||||
aggregation \
|
aggregation \
|
||||||
extensionsystem \
|
extensionsystem \
|
||||||
utils \
|
utils \
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import qbs
|
|||||||
Project {
|
Project {
|
||||||
name: "Libs"
|
name: "Libs"
|
||||||
references: [
|
references: [
|
||||||
|
"advanceddockingsystem/advanceddockingsystem.qbs",
|
||||||
"aggregation/aggregation.qbs",
|
"aggregation/aggregation.qbs",
|
||||||
"clangsupport/clangsupport.qbs",
|
"clangsupport/clangsupport.qbs",
|
||||||
"cplusplus/cplusplus.qbs",
|
"cplusplus/cplusplus.qbs",
|
||||||
|
|||||||
Reference in New Issue
Block a user