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(advanceddockingsystem)
|
||||
add_subdirectory(aggregation)
|
||||
add_subdirectory(extensionsystem)
|
||||
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
|
||||
|
||||
SUBDIRS += \
|
||||
advanceddockingsystem \
|
||||
aggregation \
|
||||
extensionsystem \
|
||||
utils \
|
||||
|
||||
@@ -3,6 +3,7 @@ import qbs
|
||||
Project {
|
||||
name: "Libs"
|
||||
references: [
|
||||
"advanceddockingsystem/advanceddockingsystem.qbs",
|
||||
"aggregation/aggregation.qbs",
|
||||
"clangsupport/clangsupport.qbs",
|
||||
"cplusplus/cplusplus.qbs",
|
||||
|
||||
Reference in New Issue
Block a user