diff --git a/LICENSES/LGPL-2.0-or-later.txt b/LICENSES/LGPL-2.0-or-later.txt new file mode 100644 index 00000000..5c96471a --- /dev/null +++ b/LICENSES/LGPL-2.0-or-later.txt @@ -0,0 +1,446 @@ +GNU LIBRARY GENERAL PUBLIC LICENSE + +Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc. + +51 Franklin St, 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 library GPL. It is numbered 2 because +it goes with version 2 of the ordinary GPL.] + +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 Library General Public License, applies to some specially +designated Free Software Foundation software, and to any other libraries whose +authors decide to use it. You can use it for your libraries, too. + +When we speak of free software, we are referring to freedom, 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 or use pieces of it in new free programs; and that +you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to +deny you these rights or to ask you to surrender the 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 +a program 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. + +Our method of protecting your rights has two steps: (1) copyright the library, +and (2) offer you this license which gives you legal permission to copy, distribute +and/or modify the library. + +Also, for each distributor's protection, we want to make certain that everyone +understands that there is no warranty for this free library. If the library +is modified by someone else and passed on, we want its recipients to know +that what they have is not the original version, so that any problems introduced +by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We +wish to avoid the danger that companies distributing free software will individually +obtain patent licenses, thus in effect transforming the program into proprietary +software. To prevent this, we have made it clear that any patent must be licensed +for everyone's free use or not licensed at all. + +Most GNU software, including some libraries, is covered by the ordinary GNU +General Public License, which was designed for utility programs. This license, +the GNU Library General Public License, applies to certain designated libraries. +This license is quite different from the ordinary one; be sure to read it +in full, and don't assume that anything in it is the same as in the ordinary +license. + +The reason we have a separate public license for some libraries is that they +blur the distinction we usually make between modifying or adding to a program +and simply using it. Linking a program with a library, without changing the +library, is in some sense simply using the library, and is analogous to running +a utility program or application program. However, in a textual and legal +sense, the linked executable is a combined work, a derivative of the original +library, and the ordinary General Public License treats it as such. + +Because of this blurred distinction, using the ordinary General Public License +for libraries did not effectively promote software sharing, because most developers +did not use the libraries. We concluded that weaker conditions might promote +sharing better. + +However, unrestricted linking of non-free programs would deprive the users +of those programs of all benefit from the free status of the libraries themselves. +This Library General Public License is intended to permit developers of non-free +programs to use free libraries, while preserving your freedom as a user of +such programs to change the free libraries that are incorporated in them. +(We have not seen how to achieve this as regards changes in header files, +but we have achieved it as regards changes in the actual functions of the +Library.) The hope is that this will lead to faster development of free libraries. + +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, while the latter only works together with the library. + +Note that it is possible for a library to be covered by the ordinary General +Public License rather than by this special one. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License Agreement applies to any software library which contains a +notice placed by the copyright holder or other authorized party saying it +may be distributed under the terms of this Library 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 compile 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) 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. + +c) 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. + +d) 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 source code 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 to 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 Library 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 an 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 Library General Public License as published by the Free +Software Foundation; either version 2 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 Library General Public License for more +details. + +You should have received a copy of the GNU Library General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin St, 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! diff --git a/Mainpage.dox b/Mainpage.dox index ec673b26..db97bae6 100644 --- a/Mainpage.dox +++ b/Mainpage.dox @@ -1,212 +1,199 @@ /* - This file is part of Kirigami - Copyright (C) 2016 Marco Martin - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2, or - (at your option) any later version. - - This program 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 Library General Public License for more details - - You should have received a copy of the GNU Library General Public - License along with this program; if not, write to the - Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ + * This file is part of Kirigami + * SPDX-FileCopyrightText: 2016 Marco Martin + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ /** \mainpage kirigami \section overview Introduction Kirigami is a set of QtQuick components for building adaptable UIs based on Qt Quick Controls 2. Its goal is to enable creation of applications that look and feel great on mobile as well as desktop devices and follow the KDE Human Interface Guidelines. The target of those components is anybody that wants to do an application using QtQuick as its main UI, especially if targeting a mobile platform, without adding many dependencies. They work on a variety of platforms, such as Plasma Mobile, Desktop Linux, Android, iOS and Windows. It is a Tier-1 KDE Framework starting with KDE Frameworks 5.37. \section components Main Components - \link org::kde::kirigami::ApplicationWindow ApplicationWindow \endlink - \link org::kde::kirigami::Action Action \endlink - \link org::kde::kirigami::GlobalDrawer GlobalDrawer \endlink - \link org::kde::kirigami::ContextDrawer ContextDrawer \endlink - \link org::kde::kirigami::OverlayDrawer OverlayDrawer \endlink - \link org::kde::kirigami::Page Page \endlink - \link org::kde::kirigami::ScrollablePage ScrollablePage \endlink - \link org::kde::kirigami::OverlaySheet OverlaySheet \endlink - \link org::kde::kirigami::Theme Theme \endlink - \link org::kde::kirigami::Units Units \endlink - \link org::kde::kirigami::Icon Icon \endlink - \link org::kde::kirigami::AbstractApplicationHeader AbstractApplicationHeader \endlink - \link org::kde::kirigami::AbstractApplicationWindow AbstractApplicationWindow \endlink - \link org::kde::kirigami::AbstractListItem AbstractListItem \endlink - \link org::kde::kirigami::ApplicationHeader ApplicationHeader \endlink - \link org::kde::kirigami::BasicListItem BasicListItem \endlink - \link org::kde::kirigami::ListSectionHeader ListSectionHeader \endlink - \link org::kde::kirigami::SwipeListItem SwipeListItem \endlink - \link org::kde::kirigami::Heading Heading \endlink - \link org::kde::kirigami::Label Label \endlink \section example Minimal Example @code import QtQuick 2.1 import QtQuick.Controls 2.0 as QQC2 import org.kde.kirigami 2.4 as Kirigami Kirigami.ApplicationWindow { id: root header: Kirigami.ApplicationHeader {} globalDrawer: Kirigami.GlobalDrawer { title: "Hello App" titleIcon: "applications-graphics" actions: [ Kirigami.Action { text: "View" iconName: "view-list-icons" Kirigami.Action { text: "action 1" } Kirigami.Action { text: "action 2" } Kirigami.Action { text: "action 3" } }, Kirigami.Action { text: "action 3" }, Kirigami.Action { text: "action 4" } ] } contextDrawer: Kirigami.ContextDrawer { id: contextDrawer } pageStack.initialPage: mainPageComponent Component { id: mainPageComponent Kirigami.ScrollablePage { title: "Hello" actions { main: Kirigami.Action { iconName: sheet.sheetOpen ? "dialog-cancel" : "document-edit" onTriggered: { print("Action button in buttons page clicked"); sheet.sheetOpen = !sheet.sheetOpen } } left: Kirigami.Action { iconName: "go-previous" onTriggered: { print("Left action triggered") } } right: Kirigami.Action { iconName: "go-next" onTriggered: { print("Right action triggered") } } contextualActions: [ Kirigami.Action { text:"Action for buttons" iconName: "bookmarks" onTriggered: print("Action 1 clicked") }, Kirigami.Action { text:"Action 2" iconName: "folder" enabled: false }, Kirigami.Action { text: "Action for Sheet" visible: sheet.sheetOpen } ] } Kirigami.OverlaySheet { id: sheet onSheetOpenChanged: page.actions.main.checked = sheetOpen QQC2.Label { wrapMode: Text.WordWrap text: "Lorem ipsum dolor sit amet" } } //Page contents... } } } @endcode \section deployment Deployment CMake is recomended for both building Kirigami and the project using it, QMake is supported as well, so we can have several configurations, depending what is the host build system and how the deployment needs to be done. Kirigami can be built in two ways: both as a module or statically linked in the application, leading to four combinations: * Kirigami built as a module with CMake * Kirigami statically built with CMake (needed to link statically from applications built with CMake) * Kirigami built as a module with QMake * Kirigami statically built with QMake (needed to link statically from applications built with QMake) The simplest and recomended way to use Kirigami is to just use the module provided by the Linux distribution, or build it as a module and deploy it together the main application. For example when building an application on Android with CMake, if Kirigami for Android is built and installed in the same temporary directory before the application, the create-apk- step of the application will include the Kirigami files as well in the APK. If QMake needs to be used, it's recomended to follow the schema of the example app present in the folder examples/minimalqmake of the Kirigami source code. It will use Kirigami statically linked only on Android, while on desktop systems it will use the version provided by the distribution. What platforms use the static version and what the dynamic one can be freely adjusted. The application needs to have a folder called "3rdparty" containing clones of two KDE repositories: kirigami and breeze-icons (available at git://anongit.kde.org/kirigami.git and git://anongit.kde.org/breeze-icons.git). The relevant part in the .pro file is: @code android: { include(3rdparty/kirigami/kirigami.pri) } @endcode While the main.cpp file will have something like: @code #include #include #ifdef Q_OS_ANDROID #include "./3rdparty/kirigami/src/kirigamiplugin.h" #endif int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); QQmlApplicationEngine engine; #ifdef Q_OS_ANDROID KirigamiPlugin::getInstance().registerTypes(); #endif .... } @endcode @authors Marco Martin \
Sebastian Kuegler \
Aleix Pol Gonzalez \
Dirk Hohndel \
@maintainers Marco Martin \ @licenses @lgpl */ // DOXYGEN_SET_RECURSIVE = YES // DOXYGEN_SET_EXCLUDE_PATTERNS += *_p.h */private/* */examples/* // DOXYGEN_SET_PROJECT_NAME = Kirigami // vim:ts=4:sw=4:expandtab:filetype=doxygen diff --git a/autotests/tst_keynavigation.qml b/autotests/tst_keynavigation.qml index 3d2af32d..6fc539fe 100644 --- a/autotests/tst_keynavigation.qml +++ b/autotests/tst_keynavigation.qml @@ -1,74 +1,61 @@ /* - * Copyright 2016 Aleix Pol Gonzalez + * SPDX-FileCopyrightText: 2016 Aleix Pol Gonzalez * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.7 import QtQuick.Controls 2.0 import QtQuick.Window 2.1 import org.kde.kirigami 2.4 as Kirigami import QtTest 1.0 import "../tests" TestCase { id: testCase width: 400 height: 400 name: "KeyboardNavigation" KeyboardTest { id: mainWindow width: 480 height: 360 } SignalSpy { id: spyActive target: mainWindow signalName: "activeChanged" } SignalSpy { id: spyLastKey target: mainWindow.pageStack.currentItem signalName: "lastKeyChanged" } function initTestCase() { mainWindow.show() } function cleanupTestCase() { mainWindow.close() } function test_press() { compare(mainWindow.pageStack.depth, 2) compare(mainWindow.pageStack.currentIndex, 1) if (!mainWindow.active) spyActive.wait(5000) verify(mainWindow.active) keyClick("A") spyLastKey.wait() compare(mainWindow.pageStack.currentItem.lastKey, "A") keyClick(Qt.Key_Left, Qt.AltModifier) compare(mainWindow.pageStack.currentIndex, 0) compare(mainWindow.pageStack.currentItem.lastKey, "") keyClick("B") spyLastKey.wait() compare(mainWindow.pageStack.currentItem.lastKey, "B") } } diff --git a/autotests/tst_listskeynavigation.qml b/autotests/tst_listskeynavigation.qml index 27872334..b9645053 100644 --- a/autotests/tst_listskeynavigation.qml +++ b/autotests/tst_listskeynavigation.qml @@ -1,70 +1,57 @@ /* - * Copyright 2016 Aleix Pol Gonzalez - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Aleix Pol Gonzalez + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.7 import QtQuick.Controls 2.0 import QtQuick.Window 2.1 import org.kde.kirigami 2.4 as Kirigami import QtTest 1.0 import "../tests" TestCase { id: testCase width: 400 height: 400 name: "KeyboardListsNavigation" KeyboardListTest { id: mainWindow width: 480 height: 360 } SignalSpy { id: spyActive target: mainWindow signalName: "activeChanged" } SignalSpy { id: spyCurrentIndex target: mainWindow.pageStack.currentItem.flickable signalName: "currentIndexChanged" } function initTestCase() { mainWindow.show() } function cleanupTestCase() { mainWindow.close() } function test_press() { compare(mainWindow.pageStack.depth, 1) compare(mainWindow.pageStack.currentIndex, 0) if (!mainWindow.active) spyActive.wait(5000) verify(mainWindow.active) compare(mainWindow.pageStack.currentItem.flickable.currentIndex, 0) keyClick(Qt.Key_Down) spyCurrentIndex.wait() compare(mainWindow.pageStack.currentItem.flickable.currentIndex, 1) } } diff --git a/autotests/tst_pagerow.qml b/autotests/tst_pagerow.qml index 3aff4708..b3a972f9 100644 --- a/autotests/tst_pagerow.qml +++ b/autotests/tst_pagerow.qml @@ -1,144 +1,131 @@ /* - * Copyright 2016 Aleix Pol Gonzalez + * SPDX-FileCopyrightText: 2016 Aleix Pol Gonzalez * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.7 import QtQuick.Controls 2.0 import QtQuick.Window 2.1 import org.kde.kirigami 2.4 as Kirigami import QtTest 1.0 TestCase { id: testCase width: 400 height: 400 name: "GoBack" function applicationWindow() { return mainWindow; } Kirigami.ApplicationWindow { id: mainWindow width: 480 height: 360 pageStack.initialPage: Kirigami.Page { Rectangle { anchors.fill: parent color: "green" } } } Component { id: randomPage Kirigami.Page { Rectangle { anchors.fill: parent color: "red" } } } SignalSpy { id: spyCurrentIndex target: mainWindow.pageStack signalName: "currentIndexChanged" } SignalSpy { id: spyActive target: mainWindow signalName: "activeChanged" } function initTestCase() { mainWindow.show() } function cleanupTestCase() { mainWindow.close() } function init() { mainWindow.pageStack.clear() spyActive.clear() spyCurrentIndex.clear() } function test_pop() { compare(mainWindow.pageStack.depth, 0) mainWindow.pageStack.push(randomPage) compare(mainWindow.pageStack.depth, 1) mainWindow.pageStack.pop() compare(mainWindow.pageStack.depth, 0) } function test_goBack() { compare(mainWindow.pageStack.depth, 0) mainWindow.pageStack.push(randomPage) mainWindow.pageStack.push(randomPage) compare(mainWindow.pageStack.depth, 2) compare(mainWindow.pageStack.currentIndex, 1) compare(spyCurrentIndex.count, 3) spyActive.clear() mainWindow.requestActivate() spyCurrentIndex.clear() if (!mainWindow.active) spyActive.wait() verify(mainWindow.active) keyClick(Qt.Key_Left, Qt.AltModifier) spyCurrentIndex.wait() compare(mainWindow.pageStack.depth, 2) compare(mainWindow.pageStack.currentIndex, 0) compare(spyCurrentIndex.count, 1) mainWindow.pageStack.pop() compare(mainWindow.pageStack.depth, 1) } property int destructions: 0 Component { id: destroyedPage Kirigami.Page { id: page Rectangle { anchors.fill: parent color: "blue" Component.onDestruction: { testCase.destructions++ } } } } SignalSpy { id: spyDestructions target: testCase signalName: "destructionsChanged" } function test_clearPages() { mainWindow.pageStack.push(destroyedPage) mainWindow.pageStack.push(destroyedPage) mainWindow.pageStack.push(destroyedPage) compare(mainWindow.pageStack.depth, 3) mainWindow.pageStack.clear() compare(mainWindow.pageStack.depth, 0) spyDestructions.wait() compare(testCase.destructions, 2) } } diff --git a/examples/applicationitemapp/main.cpp b/examples/applicationitemapp/main.cpp index a695c8a6..994b6fce 100644 --- a/examples/applicationitemapp/main.cpp +++ b/examples/applicationitemapp/main.cpp @@ -1,38 +1,25 @@ /* - * Copyright 2017 Marco Martin + * SPDX-FileCopyrightText: 2017 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ #include #include #include #include Q_DECL_EXPORT int main(int argc, char *argv[]) { QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QApplication app(argc, argv); QQuickView view; view.setResizeMode(QQuickView::SizeRootObjectToView); view.setSource(QUrl(QStringLiteral("qrc:///main.qml"))); view.show(); - + return app.exec(); } diff --git a/examples/applicationitemapp/main.qml b/examples/applicationitemapp/main.qml index 9d020163..85b0d093 100644 --- a/examples/applicationitemapp/main.qml +++ b/examples/applicationitemapp/main.qml @@ -1,81 +1,68 @@ /* - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.1 import org.kde.kirigami 2.4 as Kirigami Kirigami.ApplicationItem { id: root globalDrawer: Kirigami.GlobalDrawer { title: "Hello App" titleIcon: "applications-graphics" actions: [ Kirigami.Action { text: "View" iconName: "view-list-icons" Kirigami.Action { text: "action 1" } Kirigami.Action { text: "action 2" } Kirigami.Action { text: "action 3" } }, Kirigami.Action { text: "action 3" }, Kirigami.Action { text: "action 4" } ] handleVisible: true } contextDrawer: Kirigami.ContextDrawer { id: contextDrawer } pageStack.initialPage: mainPageComponent Component { id: mainPageComponent Kirigami.Page { title: "Hello" actions { contextualActions: [ Kirigami.Action { text: "action 1" }, Kirigami.Action { text: "action 2" } ] } Rectangle { color: "red" anchors.fill: parent } } } } diff --git a/examples/minimalqmake/Page1.qml b/examples/minimalqmake/Page1.qml index 9dec2827..31396734 100644 --- a/examples/minimalqmake/Page1.qml +++ b/examples/minimalqmake/Page1.qml @@ -1,26 +1,13 @@ /* - * Copyright 2017 Marco Martin + * SPDX-FileCopyrightText: 2017 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.7 Page1Form { button1.onClicked: { console.log("Button Pressed. Entered text: " + textField1.text); } } diff --git a/examples/minimalqmake/Page1Form.ui.qml b/examples/minimalqmake/Page1Form.ui.qml index 4f879151..b779c6a3 100644 --- a/examples/minimalqmake/Page1Form.ui.qml +++ b/examples/minimalqmake/Page1Form.ui.qml @@ -1,45 +1,32 @@ /* - * Copyright 2017 Marco Martin + * SPDX-FileCopyrightText: 2017 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.7 import QtQuick.Controls 2.0 import QtQuick.Layouts 1.3 import org.kde.kirigami 2.4 as Kirigami Kirigami.Page { title: qsTr("Page 1") property alias textField1: textField1 property alias button1: button1 RowLayout { anchors.horizontalCenter: parent.horizontalCenter anchors.topMargin: 20 anchors.top: parent.top TextField { id: textField1 placeholderText: qsTr("Text Field") } Button { id: button1 text: qsTr("Press Me") } } } diff --git a/examples/minimalqmake/main.cpp b/examples/minimalqmake/main.cpp index 887d4ced..6be593b0 100644 --- a/examples/minimalqmake/main.cpp +++ b/examples/minimalqmake/main.cpp @@ -1,42 +1,29 @@ /* - * Copyright 2017 Marco Martin + * SPDX-FileCopyrightText: 2017 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ #include #include #ifdef Q_OS_ANDROID #include "./3rdparty/kirigami/src/kirigamiplugin.h" #endif int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); QQmlApplicationEngine engine; #ifdef Q_OS_ANDROID KirigamiPlugin::getInstance().registerTypes(); #endif engine.load(QUrl(QLatin1String("qrc:/main.qml"))); if (engine.rootObjects().isEmpty()) return -1; return app.exec(); } diff --git a/examples/minimalqmake/main.qml b/examples/minimalqmake/main.qml index 7ee2d4ef..0ab4748e 100644 --- a/examples/minimalqmake/main.qml +++ b/examples/minimalqmake/main.qml @@ -1,57 +1,44 @@ /* - * Copyright 2017 Marco Martin + * SPDX-FileCopyrightText: 2017 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.7 import QtQuick.Controls 2.0 import QtQuick.Layouts 1.3 import org.kde.kirigami 2.4 as Kirigami Kirigami.ApplicationWindow { visible: true title: qsTr("Hello World") pageStack.initialPage: Page1 {} globalDrawer: Kirigami.GlobalDrawer { title: "Hello App" titleIcon: "applications-graphics" actions: [ Kirigami.Action { text: "View" iconName: "view-list-icons" Kirigami.Action { text: "action 1" } Kirigami.Action { text: "action 2" } Kirigami.Action { text: "action 3" } }, Kirigami.Action { text: "action 3" }, Kirigami.Action { text: "action 4" } ] } } diff --git a/examples/multiplatformnotesapp/NotesGeneral.qml b/examples/multiplatformnotesapp/NotesGeneral.qml index abeac770..472d10b9 100644 --- a/examples/multiplatformnotesapp/NotesGeneral.qml +++ b/examples/multiplatformnotesapp/NotesGeneral.qml @@ -1,213 +1,200 @@ /* - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.1 import QtQuick.Layouts 1.2 import QtQuick.Controls 2.0 as QQC2 import org.kde.kirigami 2.4 as Kirigami Kirigami.ApplicationWindow { id: root property string currentFile pageStack.initialPage: iconView Kirigami.ScrollablePage { id: iconView title: "Notes" actions { contextualActions: [ Kirigami.Action { id: sortAction iconName: "view-sort-ascending-symbolic" tooltip: "Sort Ascending" } ] } background: Rectangle { color: Kirigami.Thmeme.backgroundColor } GridView { id: view model: 100 cellWidth: Kirigami.Units.gridUnit * 9 cellHeight: cellWidth currentIndex: -1 highlightMoveDuration: 0 highlight: Rectangle { color: Kirigami.Theme.highlightColor } delegate: MouseArea { width: view.cellWidth height: view.cellHeight Kirigami.Icon { source: "text-plain" anchors { fill: parent margins: Kirigami.Units.gridUnit } QQC2.Label { anchors { top: parent.bottom horizontalCenter: parent.horizontalCenter } text: "File " + modelData } } onClicked: { view.currentIndex = index; root.currentFile = "File " + modelData; if (root.pageStack.depth < 2) { root.pageStack.push(editorComponent); } root.pageStack.currentIndex = 1 } } } } Component { id: editorComponent Kirigami.ScrollablePage { id: editor title: root.currentFile actions { main: Kirigami.Action { id: shareAction iconName: "document-share" text: "Share..." tooltip: "Share this document with your device" checkable: true onCheckedChanged: sheet.sheetOpen = checked; } contextualActions: [ Kirigami.Action { iconName: "format-text-bold-symbolic" tooltip: "Bold" }, Kirigami.Action { iconName: "format-text-underline-symbolic" tooltip: "Underline" }, Kirigami.Action { iconName: "format-text-italic-symbolic" tooltip: "Italic" } ] } background: Rectangle { color: Kirigami.Theme.backgroundColor Rectangle { anchors.fill: parent color: "yellow" opacity: 0.2 } } Kirigami.OverlaySheet { id: sheet onSheetOpenChanged: shareAction.checked = sheetOpen ListView { implicitWidth: Kirigami.Units.gridUnit * 30 model: ListModel { ListElement { title: "Share with phone \"Nokia 3310\"" description: "You selected this phone 12 times before. It's currently connected via bluetooth" buttonText: "Push Sync" } ListElement { title: "Share with phone \"My other Nexus5\"" description: "You selected this phone 0 times before. It's currently connected to your laptop via Wifi" buttonText: "push sync" } ListElement { title: "Share with NextCloud" description: "You currently do not have a server set up for sharing and storing notes from Katie. If you want to set one up click here" buttonText: "Setup..." } ListElement { title: "Send document via email" description: "This will send the document as an attached file to your own email for later sync" buttonText: "Send As Email" } } header: Kirigami.AbstractListItem { height: Kirigami.Units.gridUnit * 6 hoverEnabled: false RowLayout { Kirigami.Icon { source: "documentinfo" width: Kirigami.Units.iconSizes.large height: width } QQC2.Label { Layout.fillWidth: true Layout.minimumWidth: 0 wrapMode: Text.WordWrap text: "This document has already automatically synced with your phone \"Dancepartymeister 12\". If you want to sync with another device or do further actions you can do that here" } } } delegate: Kirigami.AbstractListItem { height: Kirigami.Units.gridUnit * 6 hoverEnabled: false //TODO: bug in overlaysheet rightPadding: Kirigami.Units.gridUnit * 1.5 RowLayout { ColumnLayout { Layout.fillWidth: true Layout.minimumWidth: 0 QQC2.Label { wrapMode: Text.WordWrap text: model.title } QQC2.Label { Layout.fillWidth: true Layout.minimumWidth: 0 wrapMode: Text.WordWrap text: model.description } } QQC2.Button { text: model.buttonText onClicked: sheet.close() } } } } } QQC2.TextArea { background: Item {} - wrapMode: TextEdit.WordWrap + wrapMode: TextEdit.WordWrap selectByMouse: true text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec sollicitudin, lorem at semper pretium, tortor nisl pellentesque risus, eget eleifend odio ipsum ac mi. Donec justo ex, elementum vitae gravida vel, pretium ac lacus. Duis non metus ac enim viverra auctor in non nunc. Sed sit amet luctus nisi. Proin justo nulla, vehicula eget porta sit amet, aliquet vitae dolor. Mauris sed odio auctor, tempus ipsum ac, placerat enim. Ut in dolor vel ante dictum auctor. Praesent blandit rhoncus augue. Phasellus consequat luctus pulvinar. Pellentesque rutrum laoreet dolor, sit amet pellentesque tellus mattis sed. Sed accumsan cursus tortor. Morbi et risus dolor. Nullam facilisis ipsum justo, nec sollicitudin mi pulvinar ac. Nulla facilisi. Donec maximus turpis eget mollis laoreet. Phasellus vel mauris et est mattis auctor eget sit amet turpis. Aliquam dignissim euismod purus, eu efficitur neque fermentum eu. Suspendisse potenti. Praesent mattis ex vitae neque rutrum tincidunt. Etiam placerat leo viverra pulvinar tincidunt. Proin vel rutrum massa. Proin volutpat aliquet dapibus. Maecenas aliquet elit eu venenatis venenatis. Ut elementum, lacus vel auctor auctor, velit massa elementum ligula, quis elementum ex nisi aliquam mauris. Nulla facilisi. Pellentesque aliquet egestas venenatis. Donec iaculis ultrices laoreet. Vestibulum cursus rhoncus sollicitudin. Proin quam libero, bibendum eget sodales id, gravida quis enim. Duis fermentum libero vitae sapien hendrerit, in tincidunt tortor semper. Nullam quam nisi, feugiat sed rutrum vitae, dignissim quis risus. Ut ultricies pellentesque est, ut gravida massa convallis sed. Ut placerat dui non felis interdum, id malesuada nulla ornare. Phasellus volutpat purus placerat velit porta tristique. Donec molestie leo in turpis bibendum pharetra. Fusce fermentum diam vitae neque laoreet, sed aliquam leo sollicitudin. Ut facilisis massa arcu, eu suscipit ante varius sed. Morbi augue leo, mattis eu tempor vitae, condimentum sed urna. Curabitur ac blandit orci. Vestibulum quis consequat nunc. Proin imperdiet commodo imperdiet. Aenean mattis augue et imperdiet ultricies. Ut id feugiat nulla, et sollicitudin dui. Etiam scelerisque ligula ac euismod hendrerit. Integer in quam nibh. Pellentesque risus massa, porttitor quis fermentum eu, dictum varius magna. Morbi euismod bibendum lacus efficitur pretium. Phasellus elementum porttitor enim nec dictum. Morbi et augue laoreet, convallis quam quis, egestas quam." } } } } diff --git a/examples/multiplatformnotesapp/notesDesktop.qml b/examples/multiplatformnotesapp/notesDesktop.qml index 5be22fe6..fb85732a 100644 --- a/examples/multiplatformnotesapp/notesDesktop.qml +++ b/examples/multiplatformnotesapp/notesDesktop.qml @@ -1,27 +1,14 @@ /* - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.1 import QtQuick.Layouts 1.2 import QtQuick.Controls 2.0 as QQC2 import org.kde.kirigami 2.4 as Kirigami NotesGeneral { id: root } diff --git a/examples/multiplatformnotesapp/notesMobile.qml b/examples/multiplatformnotesapp/notesMobile.qml index cb27f3c7..553b8e9b 100644 --- a/examples/multiplatformnotesapp/notesMobile.qml +++ b/examples/multiplatformnotesapp/notesMobile.qml @@ -1,29 +1,16 @@ /* - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.1 import QtQuick.Layouts 1.2 import QtQuick.Controls 2.0 as QQC2 import org.kde.kirigami 2.4 as Kirigami NotesGeneral { id: root contextDrawer: Kirigami.ContextDrawer {} } diff --git a/examples/qmakewithstaticplugin/src/Page1.qml b/examples/qmakewithstaticplugin/src/Page1.qml index 9dec2827..31396734 100644 --- a/examples/qmakewithstaticplugin/src/Page1.qml +++ b/examples/qmakewithstaticplugin/src/Page1.qml @@ -1,26 +1,13 @@ /* - * Copyright 2017 Marco Martin + * SPDX-FileCopyrightText: 2017 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.7 Page1Form { button1.onClicked: { console.log("Button Pressed. Entered text: " + textField1.text); } } diff --git a/examples/qmakewithstaticplugin/src/Page1Form.ui.qml b/examples/qmakewithstaticplugin/src/Page1Form.ui.qml index 4f879151..b779c6a3 100644 --- a/examples/qmakewithstaticplugin/src/Page1Form.ui.qml +++ b/examples/qmakewithstaticplugin/src/Page1Form.ui.qml @@ -1,45 +1,32 @@ /* - * Copyright 2017 Marco Martin + * SPDX-FileCopyrightText: 2017 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.7 import QtQuick.Controls 2.0 import QtQuick.Layouts 1.3 import org.kde.kirigami 2.4 as Kirigami Kirigami.Page { title: qsTr("Page 1") property alias textField1: textField1 property alias button1: button1 RowLayout { anchors.horizontalCenter: parent.horizontalCenter anchors.topMargin: 20 anchors.top: parent.top TextField { id: textField1 placeholderText: qsTr("Text Field") } Button { id: button1 text: qsTr("Press Me") } } } diff --git a/examples/qmakewithstaticplugin/src/main.cpp b/examples/qmakewithstaticplugin/src/main.cpp index b8d79cf2..ce1cf22c 100644 --- a/examples/qmakewithstaticplugin/src/main.cpp +++ b/examples/qmakewithstaticplugin/src/main.cpp @@ -1,40 +1,27 @@ /* - * Copyright 2017 Marco Martin + * SPDX-FileCopyrightText: 2017 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ #include #include #include Q_IMPORT_PLUGIN(KirigamiPlugin) int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); QQmlApplicationEngine engine; engine.load(QUrl(QLatin1String("qrc:/main.qml"))); if (engine.rootObjects().isEmpty()) return -1; return app.exec(); } diff --git a/examples/qmakewithstaticplugin/src/main.qml b/examples/qmakewithstaticplugin/src/main.qml index 7ee2d4ef..0ab4748e 100644 --- a/examples/qmakewithstaticplugin/src/main.qml +++ b/examples/qmakewithstaticplugin/src/main.qml @@ -1,57 +1,44 @@ /* - * Copyright 2017 Marco Martin + * SPDX-FileCopyrightText: 2017 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.7 import QtQuick.Controls 2.0 import QtQuick.Layouts 1.3 import org.kde.kirigami 2.4 as Kirigami Kirigami.ApplicationWindow { visible: true title: qsTr("Hello World") pageStack.initialPage: Page1 {} globalDrawer: Kirigami.GlobalDrawer { title: "Hello App" titleIcon: "applications-graphics" actions: [ Kirigami.Action { text: "View" iconName: "view-list-icons" Kirigami.Action { text: "action 1" } Kirigami.Action { text: "action 2" } Kirigami.Action { text: "action 3" } }, Kirigami.Action { text: "action 3" }, Kirigami.Action { text: "action 4" } ] } } diff --git a/examples/simpleexamples/AbstractApplicationWindow.qml b/examples/simpleexamples/AbstractApplicationWindow.qml index 6058d627..789af359 100644 --- a/examples/simpleexamples/AbstractApplicationWindow.qml +++ b/examples/simpleexamples/AbstractApplicationWindow.qml @@ -1,164 +1,151 @@ /* - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.1 import QtQuick.Layouts 1.2 import QtQuick.Controls 2.0 as Controls import org.kde.kirigami 2.4 as Kirigami Kirigami.AbstractApplicationWindow { id: root width: 500 height: 800 visible: true globalDrawer: Kirigami.GlobalDrawer { title: "Widget gallery" titleIcon: "applications-graphics" actions: [ Kirigami.Action { text: "View" iconName: "view-list-icons" Kirigami.Action { text: "action 1" } Kirigami.Action { text: "action 2" } Kirigami.Action { text: "action 3" } }, Kirigami.Action { text: "Sync" iconName: "folder-sync" Kirigami.Action { text: "action 4" } Kirigami.Action { text: "action 5" } }, Kirigami.Action { text: "Checkable" iconName: "view-list-details" checkable: true checked: false onTriggered: { print("Action checked:" + checked) } }, Kirigami.Action { text: "Settings" iconName: "configure" checkable: true //Need to do this, otherwise it breaks the bindings property bool current: pageStack.currentItem ? pageStack.currentItem.objectName == "settingsPage" : false onCurrentChanged: { checked = current; } onTriggered: { pageStack.push(settingsComponent); } } ] Controls.CheckBox { checked: true text: "Option 1" } Controls.CheckBox { text: "Option 2" } Controls.CheckBox { text: "Option 3" } Controls.Slider { Layout.fillWidth: true value: 0.5 } } contextDrawer: Kirigami.ContextDrawer { id: contextDrawer } pageStack: Controls.StackView { anchors.fill: parent property int currentIndex: 0 focus: true onCurrentIndexChanged: { if (depth > currentIndex+1) { pop(get(currentIndex)); } } onDepthChanged: { currentIndex = depth-1; } initialItem: mainPageComponent Keys.onReleased: { if (event.key == Qt.Key_Back || (event.key === Qt.Key_Left && (event.modifiers & Qt.AltModifier))) { event.accepted = true; if (root.contextDrawer && root.contextDrawer.drawerOpen) { root.contextDrawer.close(); } else if (root.globalDrawer && root.globalDrawer.drawerOpen) { root.globalDrawer.close(); } else { var backEvent = {accepted: false} if (root.pageStack.currentIndex >= 1) { root.pageStack.currentItem.backRequested(backEvent); if (!backEvent.accepted) { if (root.pageStack.depth > 1) { root.pageStack.currentIndex = Math.max(0, root.pageStack.currentIndex - 1); backEvent.accepted = true; } else { Qt.quit(); } } } if (!backEvent.accepted) { Qt.quit(); } } } } } Component { id: settingsComponent Kirigami.Page { title: "Settings" objectName: "settingsPage" Rectangle { anchors.fill: parent } } } //Main app content Component { id: mainPageComponent MultipleColumnsGallery {} } } diff --git a/examples/simpleexamples/FixedSidebar.qml b/examples/simpleexamples/FixedSidebar.qml index 632daa97..a82941eb 100644 --- a/examples/simpleexamples/FixedSidebar.qml +++ b/examples/simpleexamples/FixedSidebar.qml @@ -1,53 +1,40 @@ /* - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.1 import QtQuick.Layouts 1.2 import org.kde.kirigami 2.4 as Kirigami import QtQuick.Controls 2.0 as Controls Kirigami.ApplicationWindow { id: root width: Kirigami.Units.gridUnit * 60 height: Kirigami.Units.gridUnit * 40 pageStack.initialPage: mainPageComponent globalDrawer: Kirigami.OverlayDrawer { drawerOpen: true modal: false contentItem: Item { implicitWidth: Kirigami.Units.gridUnit * 10 Controls.Label { text: "This is a sidebar" width: parent.width - Kirigami.Units.smallSpacing * 2 wrapMode: Text.WordWrap anchors.horizontalCenter: parent.horizontalCenter } } } //Main app content Component { id: mainPageComponent MultipleColumnsGallery {} } } diff --git a/examples/simpleexamples/MultipleColumnsGallery.qml b/examples/simpleexamples/MultipleColumnsGallery.qml index 9b091675..9d8acfd7 100644 --- a/examples/simpleexamples/MultipleColumnsGallery.qml +++ b/examples/simpleexamples/MultipleColumnsGallery.qml @@ -1,96 +1,83 @@ /* - * Copyright 2015 Marco Martin + * SPDX-FileCopyrightText: 2015 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.0 import QtQuick.Controls 2.0 as Controls import QtQuick.Layouts 1.2 import org.kde.kirigami 2.8 ScrollablePage { id: page Layout.fillWidth: true implicitWidth: Units.gridUnit * (Math.floor(Math.random() * 35) + 8) title: "Multiple Columns" actions { contextualActions: [ Action { text:"Action for buttons" iconName: "bookmarks" onTriggered: print("Action 1 clicked") }, Action { text:"Action 2" iconName: "folder" enabled: false } ] } ColumnLayout { width: page.width spacing: Units.smallSpacing Controls.Label { Layout.fillWidth: true wrapMode: Text.WordWrap text: "This page is used to test multiple columns: you can push and pop an arbitrary number of pages, each new page will have a random implicit width between 8 and 35 grid units.\nIf you enlarge the window enough, you can test how the application behaves with multiple columns." } Item { Layout.minimumWidth: Units.gridUnit *2 Layout.minimumHeight: Layout.minimumWidth } Controls.Label { Layout.alignment: Qt.AlignHCenter text: "Page implicitWidth: " + page.implicitWidth } Controls.Button { text: "Push Another Page" Layout.alignment: Qt.AlignHCenter onClicked: pageStack.push(Qt.resolvedUrl("MultipleColumnsGallery.qml")); } Controls.Button { text: "Pop A Page" Layout.alignment: Qt.AlignHCenter onClicked: pageStack.pop(); } RowLayout { Layout.alignment: Qt.AlignHCenter Controls.TextField { id: edit text: page.title } Controls.Button { text: "Rename Page" onClicked: page.title = edit.text; } } SearchField { Layout.alignment: Qt.AlignHCenter id: searchField onAccepted: console.log("Search text is " + searchField.text); } PasswordField { Layout.alignment: Qt.AlignHCenter id: passwordField onAccepted: console.log("Password") } } } diff --git a/examples/simpleexamples/Sidebar.qml b/examples/simpleexamples/Sidebar.qml index e6dcd69c..8400f714 100644 --- a/examples/simpleexamples/Sidebar.qml +++ b/examples/simpleexamples/Sidebar.qml @@ -1,181 +1,168 @@ /* - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.1 import QtQuick.Controls 2.3 as Controls import QtQuick.Layouts 1.2 import org.kde.kirigami 2.7 as Kirigami Kirigami.ApplicationWindow { id: root width: Kirigami.Units.gridUnit * 60 height: Kirigami.Units.gridUnit * 40 pageStack.initialPage: mainPageComponent globalDrawer: Kirigami.OverlayDrawer { id: drawer drawerOpen: true modal: false //leftPadding: Kirigami.Units.largeSpacing rightPadding: Kirigami.Units.largeSpacing contentItem: ColumnLayout { Layout.preferredWidth: Kirigami.Units.gridUnit * 20 Controls.Label { Layout.alignment: Qt.AlignHCenter text: "This is a sidebar" Layout.fillWidth: true width: parent.width - Kirigami.Units.smallSpacing * 2 wrapMode: Text.WordWrap } Controls.Button { Layout.alignment: Qt.AlignHCenter text: "Modal" checkable: true Layout.fillWidth: true checked: false onCheckedChanged: drawer.modal = checked } Item { Layout.fillHeight: true } } } contextDrawer: Kirigami.OverlayDrawer { id: contextDrawer drawerOpen: true edge: Qt.application.layoutDirection == Qt.RightToLeft ? Qt.LeftEdge : Qt.RightEdge modal: false leftPadding: Kirigami.Units.largeSpacing rightPadding: Kirigami.Units.largeSpacing contentItem: ColumnLayout { Layout.preferredWidth: Kirigami.Units.gridUnit * 10 Controls.Label { Layout.alignment: Qt.AlignHCenter text: "This is a sidebar" Layout.fillWidth: true width: parent.width - Kirigami.Units.smallSpacing * 2 wrapMode: Text.WordWrap } Controls.Button { Layout.alignment: Qt.AlignHCenter text: "Modal" checkable: true Layout.fillWidth: true checked: false onCheckedChanged: contextDrawer.modal = checked } Item { Layout.fillHeight: true } } } menuBar: Controls.MenuBar { Controls.Menu { title: qsTr("&File") Controls.Action { text: qsTr("&New...") } Controls.Action { text: qsTr("&Open...") } Controls.Action { text: qsTr("&Save") } Controls.Action { text: qsTr("Save &As...") } Controls.MenuSeparator { } Controls.Action { text: qsTr("&Quit") } } Controls.Menu { title: qsTr("&Edit") Controls.Action { text: qsTr("Cu&t") } Controls.Action { text: qsTr("&Copy") } Controls.Action { text: qsTr("&Paste") } } Controls.Menu { title: qsTr("&Help") Controls.Action { text: qsTr("&About") } } } header: Controls.ToolBar { contentItem: RowLayout { Controls.ToolButton { text: "Global ToolBar" } Item { Layout.fillWidth: true } Kirigami.ActionTextField { id: searchField placeholderText: "Search..." focusSequence: "Ctrl+F" leftActions: [ Kirigami.Action { iconName: "edit-clear" visible: searchField.text != "" onTriggered: { searchField.text = "" searchField.accepted() } }, Kirigami.Action { iconName: "edit-clear" visible: searchField.text != "" onTriggered: { searchField.text = "" searchField.accepted() } } ] rightActions: [ Kirigami.Action { iconName: "edit-clear" visible: searchField.text != "" onTriggered: { searchField.text = "" searchField.accepted() } }, Kirigami.Action { iconName: "anchor" visible: searchField.text != "" onTriggered: { searchField.text = "" searchField.accepted() } } ] onAccepted: console.log("Search text is " + searchField.text) } } } //Main app content Component { id: mainPageComponent MultipleColumnsGallery {} } footer: Controls.ToolBar { position: Controls.ToolBar.Footer Controls.Label { anchors.fill: parent verticalAlignment: Qt.AlignVCenter text: "Global Footer" } } } diff --git a/examples/simpleexamples/SimplePage.qml b/examples/simpleexamples/SimplePage.qml index 11bfab54..3a0b3b35 100644 --- a/examples/simpleexamples/SimplePage.qml +++ b/examples/simpleexamples/SimplePage.qml @@ -1,57 +1,44 @@ /* - * Copyright 2015 Marco Martin + * SPDX-FileCopyrightText: 2015 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.0 import QtQuick.Controls 2.0 as Controls import QtQuick.Layouts 1.2 import org.kde.kirigami 2.8 ScrollablePage { id: page Layout.fillWidth: true implicitWidth: Units.gridUnit * (Math.floor(Math.random() * 35) + 8) title: i18n("Simple Scrollable Page") actions { contextualActions: [ Action { text:"Action for buttons" iconName: "bookmarks" onTriggered: print("Action 1 clicked") }, Action { text:"Action 2" iconName: "folder" enabled: false } ] } ColumnLayout { width: page.width spacing: Units.smallSpacing Controls.Label { Layout.fillWidth: true wrapMode: Text.WordWrap text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed a sem venenatis, dictum odio vitae, tincidunt sapien. Proin a suscipit ligula, id interdum leo. Donec sed dolor sed lacus dignissim tempor a a lorem. In ullamcorper varius vestibulum. Sed nec arcu semper, varius velit ut, pharetra est. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Integer odio nibh, tincidunt quis condimentum quis, consequat id lacus. Nulla quis mauris erat. Suspendisse rhoncus suscipit massa, at suscipit lorem rhoncus et. " } } } diff --git a/examples/simpleexamples/TabBarHeader.qml b/examples/simpleexamples/TabBarHeader.qml index c28f0e7f..ab1bac0d 100644 --- a/examples/simpleexamples/TabBarHeader.qml +++ b/examples/simpleexamples/TabBarHeader.qml @@ -1,47 +1,34 @@ /* - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.1 import QtQuick.Layouts 1.2 import org.kde.kirigami 2.4 as Kirigami Kirigami.ApplicationWindow { id: root width: 500 height: 800 visible: true pageStack.initialPage: mainPageComponent pageStack.globalToolBar.style: Kirigami.ApplicationHeaderStyle.TabBar Component.onCompleted: { pageStack.push(mainPageComponent); pageStack.push(mainPageComponent); pageStack.currentIndex = 0; } //Main app content Component { id: mainPageComponent MultipleColumnsGallery {} } } diff --git a/examples/simpleexamples/customdrawer.qml b/examples/simpleexamples/customdrawer.qml index d9d973df..632a7e4e 100644 --- a/examples/simpleexamples/customdrawer.qml +++ b/examples/simpleexamples/customdrawer.qml @@ -1,50 +1,37 @@ /* - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.1 import org.kde.kirigami 2.4 as Kirigami Kirigami.ApplicationWindow { id: root globalDrawer: Kirigami.OverlayDrawer { contentItem: Rectangle { implicitWidth: Kirigami.Units.gridUnit * 10 color: "red" anchors.fill: parent } } contextDrawer: Kirigami.ContextDrawer { id: contextDrawer } pageStack.initialPage: mainPageComponent Component { id: mainPageComponent Kirigami.ScrollablePage { title: "Hello" Rectangle { anchors.fill: parent } } } } diff --git a/examples/simpleexamples/dragPageWidth.qml b/examples/simpleexamples/dragPageWidth.qml index 81b16f11..f3a12592 100644 --- a/examples/simpleexamples/dragPageWidth.qml +++ b/examples/simpleexamples/dragPageWidth.qml @@ -1,87 +1,74 @@ /* - * Copyright 2017 Eike Hein + * SPDX-FileCopyrightText: 2017 Eike Hein * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.1 import org.kde.kirigami 2.4 as Kirigami Kirigami.ApplicationWindow { id: root property int defaultColumnWidth: Kirigami.Units.gridUnit * 13 property int columnWidth: defaultColumnWidth pageStack.defaultColumnWidth: columnWidth pageStack.initialPage: [firstPageComponent, secondPageComponent] MouseArea { id: dragHandle visible: pageStack.wideMode anchors.top: parent.top anchors.bottom: parent.bottom x: columnWidth - (width / 2) width: Kirigami.Units.devicePixelRatio * 2 property int dragRange: (Kirigami.Units.gridUnit * 5) property int _lastX: -1 cursorShape: Qt.SplitHCursor onPressed: _lastX = mouseX onPositionChanged: { if (mouse.x > _lastX) { columnWidth = Math.min((defaultColumnWidth + dragRange), columnWidth + (mouse.x - _lastX)); } else if (mouse.x < _lastX) { columnWidth = Math.max((defaultColumnWidth - dragRange), columnWidth - (_lastX - mouse.x)); } } Rectangle { anchors.fill: parent color: "blue" } } Component { id: firstPageComponent Kirigami.Page { id: firstPage background: Rectangle { color: "red" } } } Component { id: secondPageComponent Kirigami.Page { id: secondPage background: Rectangle { color: "green" } } } } diff --git a/examples/simpleexamples/footer.qml b/examples/simpleexamples/footer.qml index 6176da11..6e015232 100644 --- a/examples/simpleexamples/footer.qml +++ b/examples/simpleexamples/footer.qml @@ -1,79 +1,66 @@ /* - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.1 import QtQuick.Layouts 1.2 import QtQuick.Controls 2.0 as Controls import org.kde.kirigami 2.4 as Kirigami Kirigami.ApplicationWindow { id: root footer: Controls.ToolBar { //height: Kirigami.Units.gridUnit * 3 RowLayout { Controls.ToolButton { text: "text" } } } globalDrawer: Kirigami.GlobalDrawer { title: "Hello App" titleIcon: "applications-graphics" actions: [ Kirigami.Action { text: "View" iconName: "view-list-icons" Kirigami.Action { text: "action 1" } Kirigami.Action { text: "action 2" } Kirigami.Action { text: "action 3" } }, Kirigami.Action { text: "action 3" }, Kirigami.Action { text: "action 4" } ] } contextDrawer: Kirigami.ContextDrawer { id: contextDrawer } pageStack.initialPage: mainPageComponent Component { id: mainPageComponent Kirigami.ScrollablePage { title: "Hello" Rectangle { anchors.fill: parent } } } } diff --git a/examples/simpleexamples/minimal.qml b/examples/simpleexamples/minimal.qml index 029a9565..7443ecfb 100644 --- a/examples/simpleexamples/minimal.qml +++ b/examples/simpleexamples/minimal.qml @@ -1,69 +1,56 @@ /* - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.1 import org.kde.kirigami 2.4 as Kirigami Kirigami.ApplicationWindow { id: root globalDrawer: Kirigami.GlobalDrawer { title: "Hello App" titleIcon: "applications-graphics" actions: [ Kirigami.Action { text: "View" iconName: "view-list-icons" Kirigami.Action { text: "action 1" } Kirigami.Action { text: "action 2" } Kirigami.Action { text: "action 3" } }, Kirigami.Action { text: "action 3" }, Kirigami.Action { text: "action 4" } ] } contextDrawer: Kirigami.ContextDrawer { id: contextDrawer } pageStack.initialPage: mainPageComponent Component { id: mainPageComponent Kirigami.ScrollablePage { title: "Hello" Rectangle { anchors.fill: parent } } } } diff --git a/examples/simpleexamples/pagePoolDrawer.qml b/examples/simpleexamples/pagePoolDrawer.qml index c90750a9..7eefbce0 100644 --- a/examples/simpleexamples/pagePoolDrawer.qml +++ b/examples/simpleexamples/pagePoolDrawer.qml @@ -1,57 +1,44 @@ /* - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.1 import org.kde.kirigami 2.11 as Kirigami Kirigami.ApplicationWindow { id: root Kirigami.PagePool { id: mainPagePool } globalDrawer: Kirigami.GlobalDrawer { title: "Hello App" titleIcon: "applications-graphics" modal: !root.wideScreen width: Kirigami.Units.gridUnit * 10 actions: [ Kirigami.PagePoolAction { text: i18n("Page1") icon.name: "speedometer" pagePool: mainPagePool page: "SimplePage.qml" }, Kirigami.PagePoolAction { text: i18n("Page2") icon.name: "window-duplicate" pagePool: mainPagePool page: "MultipleColumnsGallery.qml" } ] } contextDrawer: Kirigami.ContextDrawer { id: contextDrawer } pageStack.initialPage: mainPagePool.loadPage("SimplePage.qml") } diff --git a/examples/simpleexamples/pagePoolFirstColumn.qml b/examples/simpleexamples/pagePoolFirstColumn.qml index c1f96a15..858bd7e0 100644 --- a/examples/simpleexamples/pagePoolFirstColumn.qml +++ b/examples/simpleexamples/pagePoolFirstColumn.qml @@ -1,72 +1,59 @@ /* - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.11 import org.kde.kirigami 2.11 as Kirigami Kirigami.ApplicationWindow { id: root Kirigami.PagePool { id: mainPagePool } globalDrawer: Kirigami.GlobalDrawer { title: "Hello App" titleIcon: "applications-graphics" } contextDrawer: Kirigami.ContextDrawer { id: contextDrawer } pageStack.initialPage: wideScreen ? [firstPage, mainPagePool.loadPage("SimplePage.qml")] : [firstPage] Component { id: firstPage Kirigami.ScrollablePage { id: root title: i18n("Sidebar") property list pageActions: [ Kirigami.PagePoolAction { text: i18n("Page1") icon.name: "speedometer" pagePool: mainPagePool basePage: root page: "SimplePage.qml" }, Kirigami.PagePoolAction { text: i18n("Page2") icon.name: "window-duplicate" pagePool: mainPagePool basePage: root page: "MultipleColumnsGallery.qml" } ] ListView { model: pageActions keyNavigationEnabled: true activeFocusOnTab: true delegate: Kirigami.BasicListItem { id: delegate action: modelData } } } } } diff --git a/examples/simpleexamples/pushpopclear.qml b/examples/simpleexamples/pushpopclear.qml index f07014e4..ae7a9e62 100644 --- a/examples/simpleexamples/pushpopclear.qml +++ b/examples/simpleexamples/pushpopclear.qml @@ -1,79 +1,66 @@ /* - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.1 import org.kde.kirigami 2.4 as Kirigami Kirigami.ApplicationWindow { id: root globalDrawer: Kirigami.GlobalDrawer { title: "Hello App" titleIcon: "applications-graphics" actions: [ Kirigami.Action { text: "push" onTriggered: pageStack.push(secondPageComponent) }, Kirigami.Action { text: "pop" onTriggered: pageStack.pop() }, Kirigami.Action { text: "clear" onTriggered: pageStack.clear() }, Kirigami.Action { text: "replace" onTriggered: pageStack.replace(secondPageComponent) } ] } pageStack.initialPage: mainPageComponent Component { id: mainPageComponent Kirigami.Page { title: "First Page" Rectangle { anchors.fill: parent Kirigami.Label { text: "First Page" } } } } Component { id: secondPageComponent Kirigami.Page { title: "Second Page" Rectangle { color: "red" anchors.fill: parent Kirigami.Label { text: "Second Page" } } } } } diff --git a/examples/simpleexamples/simpleChatApp.qml b/examples/simpleexamples/simpleChatApp.qml index a08176c4..0fe69b3e 100644 --- a/examples/simpleexamples/simpleChatApp.qml +++ b/examples/simpleexamples/simpleChatApp.qml @@ -1,337 +1,324 @@ /* - * Copyright 2017 Marco Martin + * SPDX-FileCopyrightText: 2017 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.6 import QtQuick.Layouts 1.2 import QtQuick.Controls 2.2 as QQC2 import org.kde.kirigami 2.11 as Kirigami Kirigami.ApplicationWindow { id: root //header: Kirigami.ToolBarApplicationHeader {} //FIXME: perhaps the default logic for going widescreen should be refined upstream wideScreen: width > columnWidth * 3 property int columnWidth: Kirigami.Units.gridUnit * 13 property int footerHeight: Math.round(Kirigami.Units.gridUnit * 2.5) globalDrawer: Kirigami.GlobalDrawer { contentItem.implicitWidth: columnWidth title: "Chat App" titleIcon: "konversation" modal: true drawerOpen: false isMenu: true actions: [ Kirigami.Action { text: "Rooms" iconName: "view-list-icons" }, Kirigami.Action { text: "Contacts" iconName: "tag-people" }, Kirigami.Action { text: "Search" iconName: "search" } ] } contextDrawer: Kirigami.OverlayDrawer { id: contextDrawer //they can depend on the page like that or be defined directly here edge: Qt.application.layoutDirection == Qt.RightToLeft ? Qt.LeftEdge : Qt.RightEdge modal: !root.wideScreen onModalChanged: drawerOpen = !modal handleVisible: applicationWindow == undefined ? false : applicationWindow().controlsVisible //here padding 0 as listitems look better without as opposed to any other control topPadding: 0 bottomPadding: 0 leftPadding: 0 rightPadding: 0 contentItem: ColumnLayout { readonly property int implicitWidth: root.columnWidth spacing: 0 QQC2.Control { Layout.fillWidth: true background: Rectangle { anchors.fill: parent color: Kirigami.Theme.highlightColor opacity: 0.8 } padding: Kirigami.Units.gridUnit contentItem: ColumnLayout { id: titleLayout spacing: Kirigami.Units.gridUnit - + RowLayout { spacing: Kirigami.Units.gridUnit Rectangle { color: Kirigami.Theme.highlightedTextColor radius: width implicitWidth: Kirigami.Units.iconSizes.medium implicitHeight: implicitWidth } ColumnLayout { QQC2.Label { Layout.fillWidth: true color: Kirigami.Theme.highlightedTextColor text: "KDE" } QQC2.Label { Layout.fillWidth: true color: Kirigami.Theme.highlightedTextColor font.pointSize: Kirigami.Units.fontMetrics.font.pointSize * 0.8 text: "#kde: kde.org" } } } QQC2.Label { Layout.fillWidth: true color: Kirigami.Theme.highlightedTextColor text: "Main room for KDE community, other rooms are listed at kde.org/rooms" wrapMode: Text.WordWrap } } } Kirigami.Separator { Layout.fillWidth: true } QQC2.ScrollView { Layout.fillWidth: true Layout.fillHeight: true ListView { model: 50 delegate: Kirigami.BasicListItem { label: "Person " + modelData separatorVisible: false reserveSpaceForIcon: false } } } Kirigami.Separator { Layout.fillWidth: true Layout.maximumHeight: 1//implicitHeight } Kirigami.BasicListItem { label: "Group call" icon: "call-start" separatorVisible: false } Kirigami.BasicListItem { label: "Send Attachment" icon: "mail-attachment" separatorVisible: false } } } pageStack.defaultColumnWidth: columnWidth pageStack.initialPage: [channelsComponent, chatComponent] Component { id: channelsComponent Kirigami.ScrollablePage { title: "Channels" actions.main: Kirigami.Action { icon.name: "search" text: "Search" } background: Rectangle { anchors.fill: parent color: Kirigami.Theme.backgroundColor } footer: QQC2.ToolBar { height: root.footerHeight padding: Kirigami.Units.smallSpacing RowLayout { anchors.fill: parent spacing: Kirigami.Units.smallSpacing //NOTE: icon support in tool button in Qt 5.11 QQC2.ToolButton { Layout.fillHeight: true //make it square implicitWidth: height Kirigami.Icon { anchors.centerIn: parent width: Kirigami.Units.iconSizes.smallMedium height: width source: "configure" } onClicked: root.pageStack.layers.push(secondLayerComponent); } QQC2.ComboBox { Layout.fillWidth: true Layout.fillHeight: true model: ["First", "Second", "Third"] } } } ListView { id: channelsList currentIndex: 2 model: 30 delegate: Kirigami.BasicListItem { label: "#Channel " + modelData checkable: true checked: channelsList.currentIndex == index separatorVisible: false reserveSpaceForIcon: false } } } } Component { id: chatComponent Kirigami.ScrollablePage { title: "#KDE" actions { left: Kirigami.Action { icon.name: "documentinfo" text: "Channel info" } main: Kirigami.Action { icon.name: "search" text: "Search" } } actions.contextualActions: [ Kirigami.Action { text: "Room Settings" iconName: "configure" Kirigami.Action { text: "Setting 1" } Kirigami.Action { text: "Setting 2" } }, Kirigami.Action { text: "Shared Media" iconName: "document-share" Kirigami.Action { text: "Media 1" } Kirigami.Action { text: "Media 2" } Kirigami.Action { text: "Media 3" } } ] background: Rectangle { anchors.fill: parent color: Kirigami.Theme.backgroundColor } footer: QQC2.Control { height: footerHeight padding: Kirigami.Units.smallSpacing background: Rectangle { color: Kirigami.Theme.backgroundColor Kirigami.Separator { Rectangle { anchors.fill: parent color: Kirigami.Theme.focusColor visible: chatTextInput.activeFocus } anchors { left: parent.left right: parent.right top: parent.top } } } contentItem: RowLayout { QQC2.TextField { Layout.fillWidth: true id: chatTextInput background: Item {} } //NOTE: icon support in tool button in Qt 5.11 QQC2.ToolButton { Layout.fillHeight: true //make it square implicitWidth: height Kirigami.Icon { anchors.centerIn: parent width: Kirigami.Units.iconSizes.smallMedium height: width source: "go-next" } } } } ListView { id: channelsList verticalLayoutDirection: ListView.BottomToTop currentIndex: 2 model: 30 delegate: Item { height: Kirigami.Units.gridUnit * 4 ColumnLayout { x: Kirigami.Units.gridUnit anchors.verticalCenter: parent.verticalCenter QQC2.Label { text: modelData % 2 ? "John Doe" : "John Applebaum" } QQC2.Label { text: "Message " + modelData } } } } } } Component { id: secondLayerComponent Kirigami.Page { title: "Settings" background: Rectangle { color: Kirigami.Theme.backgroundColor } footer: QQC2.ToolBar { height: root.footerHeight QQC2.ToolButton { Layout.fillHeight: true //make it square implicitWidth: height Kirigami.Icon { anchors.centerIn: parent width: Kirigami.Units.iconSizes.smallMedium height: width source: "configure" } onClicked: root.pageStack.layers.pop(); } } } } } diff --git a/examples/staticcmake/src/Page1.qml b/examples/staticcmake/src/Page1.qml index 9dec2827..31396734 100644 --- a/examples/staticcmake/src/Page1.qml +++ b/examples/staticcmake/src/Page1.qml @@ -1,26 +1,13 @@ /* - * Copyright 2017 Marco Martin + * SPDX-FileCopyrightText: 2017 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.7 Page1Form { button1.onClicked: { console.log("Button Pressed. Entered text: " + textField1.text); } } diff --git a/examples/staticcmake/src/Page1Form.ui.qml b/examples/staticcmake/src/Page1Form.ui.qml index 6a127cea..9b98a32d 100644 --- a/examples/staticcmake/src/Page1Form.ui.qml +++ b/examples/staticcmake/src/Page1Form.ui.qml @@ -1,52 +1,39 @@ /* - * Copyright 2017 Marco Martin + * SPDX-FileCopyrightText: 2017 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.7 import QtQuick.Controls 2.0 import QtQuick.Layouts 1.3 import org.kde.kirigami 2.4 as Kirigami Kirigami.Page { title: qsTr("Page 1") property alias textField1: textField1 property alias button1: button1 actions { main: Kirigami.Action { text: "Sync" icon.name: "folder-sync" onTriggered: showPassiveNotification("Action clicked") } } RowLayout { anchors.horizontalCenter: parent.horizontalCenter anchors.topMargin: 20 anchors.top: parent.top TextField { id: textField1 placeholderText: qsTr("Text Field") } Button { id: button1 text: qsTr("Press Me") } } } diff --git a/examples/staticcmake/src/main.cpp b/examples/staticcmake/src/main.cpp index 57ac9d90..12dd87f9 100644 --- a/examples/staticcmake/src/main.cpp +++ b/examples/staticcmake/src/main.cpp @@ -1,78 +1,65 @@ /* - * Copyright 2017 Marco Martin + * SPDX-FileCopyrightText: 2017 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ #ifdef Q_OS_ANDROID #include #else #include #endif #include #include #include #include #ifdef Q_OS_ANDROID #include // WindowManager.LayoutParams #define FLAG_TRANSLUCENT_STATUS 0x04000000 #define FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS 0x80000000 // View #define SYSTEM_UI_FLAG_LIGHT_STATUS_BAR 0x00002000 #endif Q_IMPORT_PLUGIN(KirigamiPlugin) Q_DECL_EXPORT int main(int argc, char *argv[]) { QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); //The desktop QQC2 style needs it to be a QApplication #ifdef Q_OS_ANDROID QGuiApplication app(argc, argv); #else QApplication app(argc, argv); #endif //qputenv("QML_IMPORT_TRACE", "1"); QQmlApplicationEngine engine; - + engine.load(QUrl(QStringLiteral("qrc:///main.qml"))); if (engine.rootObjects().isEmpty()) { return -1; } - //HACK to color the system bar on Android, use qtandroidextras and call the appropriate Java methods + //HACK to color the system bar on Android, use qtandroidextras and call the appropriate Java methods #ifdef Q_OS_ANDROID QtAndroid::runOnAndroidThread([=]() { QAndroidJniObject window = QtAndroid::androidActivity().callObjectMethod("getWindow", "()Landroid/view/Window;"); window.callMethod("addFlags", "(I)V", FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.callMethod("clearFlags", "(I)V", FLAG_TRANSLUCENT_STATUS); window.callMethod("setStatusBarColor", "(I)V", QColor("#2196f3").rgba()); window.callMethod("setNavigationBarColor", "(I)V", QColor("#2196f3").rgba()); }); #endif return app.exec(); } diff --git a/examples/staticcmake/src/main.qml b/examples/staticcmake/src/main.qml index 7ee2d4ef..0ab4748e 100644 --- a/examples/staticcmake/src/main.qml +++ b/examples/staticcmake/src/main.qml @@ -1,57 +1,44 @@ /* - * Copyright 2017 Marco Martin + * SPDX-FileCopyrightText: 2017 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.7 import QtQuick.Controls 2.0 import QtQuick.Layouts 1.3 import org.kde.kirigami 2.4 as Kirigami Kirigami.ApplicationWindow { visible: true title: qsTr("Hello World") pageStack.initialPage: Page1 {} globalDrawer: Kirigami.GlobalDrawer { title: "Hello App" titleIcon: "applications-graphics" actions: [ Kirigami.Action { text: "View" iconName: "view-list-icons" Kirigami.Action { text: "action 1" } Kirigami.Action { text: "action 2" } Kirigami.Action { text: "action 3" } }, Kirigami.Action { text: "action 3" }, Kirigami.Action { text: "action 4" } ] } } diff --git a/src/columnview.cpp b/src/columnview.cpp index 052572fb..67965bea 100644 --- a/src/columnview.cpp +++ b/src/columnview.cpp @@ -1,1595 +1,1582 @@ /* - * Copyright 2019 Marco Martin + * SPDX-FileCopyrightText: 2019 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ #include "columnview.h" #include "columnview_p.h" #include #include #include #include #include #include #include #include QHash ColumnView::m_attachedObjects = QHash(); class QmlComponentsPoolSingleton { public: QmlComponentsPoolSingleton() {} static QmlComponentsPool *instance(QQmlEngine *engine); private: QHash m_instances; }; Q_GLOBAL_STATIC(QmlComponentsPoolSingleton, privateQmlComponentsPoolSelf) QmlComponentsPool *QmlComponentsPoolSingleton::instance(QQmlEngine *engine) { Q_ASSERT(engine); auto componentPool = privateQmlComponentsPoolSelf->m_instances.value(engine); if (componentPool) { return componentPool; } componentPool = new QmlComponentsPool(engine); QObject::connect(componentPool, &QObject::destroyed, [engine]() { if (privateQmlComponentsPoolSelf) { privateQmlComponentsPoolSelf->m_instances.remove(engine); } }); privateQmlComponentsPoolSelf->m_instances[engine] = componentPool; return componentPool; } QmlComponentsPool::QmlComponentsPool(QQmlEngine *engine) : QObject(engine) { QQmlComponent *component = new QQmlComponent(engine, this); component->setData(QByteArrayLiteral("import QtQuick 2.7\n" "import org.kde.kirigami 2.7 as Kirigami\n" "QtObject {\n" "id: root\n" "readonly property Kirigami.Units units: Kirigami.Units\n" "readonly property Component separator: Kirigami.Separator {" "property Item column\n" "visible: column.Kirigami.ColumnView.view && column.Kirigami.ColumnView.view.contentX < column.x;" "anchors.top: column.top;" "anchors.bottom: column.bottom;" "}\n" "readonly property Component rightSeparator: Kirigami.Separator {" "property Item column\n" "anchors.top: column.top;" "anchors.right: column.right;" "anchors.bottom: column.bottom;" "}" "}"), QUrl(QStringLiteral("columnview.cpp"))); m_instance = component->create(); //qWarning()<errors(); Q_ASSERT(m_instance); m_separatorComponent = m_instance->property("separator").value(); Q_ASSERT(m_separatorComponent); m_rightSeparatorComponent = m_instance->property("rightSeparator").value(); Q_ASSERT(m_rightSeparatorComponent); m_units = m_instance->property("units").value(); Q_ASSERT(m_units); connect(m_units, SIGNAL(gridUnitChanged()), this, SIGNAL(gridUnitChanged())); connect(m_units, SIGNAL(longDurationChanged()), this, SIGNAL(longDurationChanged())); } QmlComponentsPool::~QmlComponentsPool() {} ///////// ColumnViewAttached::ColumnViewAttached(QObject *parent) : QObject(parent) {} ColumnViewAttached::~ColumnViewAttached() {} void ColumnViewAttached::setIndex(int index) { if (!m_customFillWidth && m_view) { const bool oldFillWidth = m_fillWidth; m_fillWidth = index == m_view->count() - 1; if (oldFillWidth != m_fillWidth) { emit fillWidthChanged(); } } if (index == m_index) { return; } m_index = index; emit indexChanged(); } int ColumnViewAttached::index() const { return m_index; } void ColumnViewAttached::setFillWidth(bool fill) { if (m_view) { disconnect(m_view.data(), &ColumnView::countChanged, this, nullptr); } m_customFillWidth = true; if (fill == m_fillWidth) { return; } m_fillWidth = fill; emit fillWidthChanged(); if (m_view) { m_view->polish(); } } bool ColumnViewAttached::fillWidth() const { return m_fillWidth; } qreal ColumnViewAttached::reservedSpace() const { return m_reservedSpace; } void ColumnViewAttached::setReservedSpace(qreal space) { if (m_view) { disconnect(m_view.data(), &ColumnView::columnWidthChanged, this, nullptr); } m_customReservedSpace = true; if (qFuzzyCompare(space, m_reservedSpace)) { return; } m_reservedSpace = space; emit reservedSpaceChanged(); if (m_view) { m_view->polish(); } } ColumnView *ColumnViewAttached::view() { return m_view; } void ColumnViewAttached::setView(ColumnView *view) { if (view == m_view) { return; } if (m_view) { disconnect(m_view.data(), nullptr, this, nullptr); } m_view = view; if (!m_customFillWidth && m_view) { m_fillWidth = m_index == m_view->count() - 1; connect(m_view.data(), &ColumnView::countChanged, this, [this]() { m_fillWidth = m_index == m_view->count() - 1; emit fillWidthChanged(); }); } if (!m_customReservedSpace && m_view) { m_reservedSpace = m_view->columnWidth(); connect(m_view.data(), &ColumnView::columnWidthChanged, this, [this]() { m_reservedSpace = m_view->columnWidth(); emit reservedSpaceChanged(); }); } emit viewChanged(); } QQuickItem *ColumnViewAttached::originalParent() const { return m_originalParent; } void ColumnViewAttached::setOriginalParent(QQuickItem *parent) { m_originalParent = parent; } bool ColumnViewAttached::shouldDeleteOnRemove() const { return m_shouldDeleteOnRemove; } void ColumnViewAttached::setShouldDeleteOnRemove(bool del) { m_shouldDeleteOnRemove = del; } bool ColumnViewAttached::preventStealing() const { return m_preventStealing; } void ColumnViewAttached::setPreventStealing(bool prevent) { if (prevent == m_preventStealing) { return; } m_preventStealing = prevent; emit preventStealingChanged(); } bool ColumnViewAttached::isPinned() const { return m_pinned; } void ColumnViewAttached::setPinned(bool pinned) { if (pinned == m_pinned) { return; } m_pinned = pinned; emit pinnedChanged(); if (m_view) { m_view->polish(); } } ///////// ContentItem::ContentItem(ColumnView *parent) : QQuickItem(parent), m_view(parent) { setFlags(flags() | ItemIsFocusScope); m_slideAnim = new QPropertyAnimation(this); m_slideAnim->setTargetObject(this); m_slideAnim->setPropertyName("x"); //NOTE: the duration will be taked from kirigami units upon classBegin m_slideAnim->setDuration(0); m_slideAnim->setEasingCurve(QEasingCurve(QEasingCurve::InOutQuad)); connect(m_slideAnim, &QPropertyAnimation::finished, this, [this] () { if (!m_view->currentItem()) { m_view->setCurrentIndex(m_items.indexOf(m_viewAnchorItem)); } else { QRectF mapped = m_view->currentItem()->mapRectToItem(m_view, QRectF(QPointF(0, 0), m_view->currentItem()->size())); if (!QRectF(QPointF(0, 0), m_view->size()).intersects(mapped)) { m_view->setCurrentIndex(m_items.indexOf(m_viewAnchorItem)); } } }); connect(this, &QQuickItem::xChanged, this, &ContentItem::layoutPinnedItems); } ContentItem::~ContentItem() {} void ContentItem::setBoundedX(qreal x) { if (!parentItem()) { return; } m_slideAnim->stop(); setX(qRound(qBound(qMin(0.0, -width()+parentItem()->width()), x, 0.0))); } void ContentItem::animateX(qreal newX) { if (!parentItem()) { return; } const qreal to = qRound(qBound(qMin(0.0, -width()+parentItem()->width()), newX, 0.0)); m_slideAnim->stop(); m_slideAnim->setStartValue(x()); m_slideAnim->setEndValue(to); m_slideAnim->start(); } void ContentItem::snapToItem() { QQuickItem *firstItem = childAt(viewportLeft(), 0); if (!firstItem) { return; } QQuickItem *nextItem = childAt(firstItem->x() + firstItem->width() + 1, 0); //need to make the last item visible? if (nextItem && ((m_view->dragging() && m_lastDragDelta < 0) || (!m_view->dragging() && width() - (viewportRight()) < viewportLeft() - firstItem->x()))) { m_viewAnchorItem = nextItem; animateX(-nextItem->x() + m_leftPinnedSpace); //The first one found? } else if ((m_view->dragging() && m_lastDragDelta >= 0) || (!m_view->dragging() && viewportLeft() <= firstItem->x() + firstItem->width()/2) || !nextItem) { m_viewAnchorItem = firstItem; animateX(-firstItem->x() + m_leftPinnedSpace); //the second? } else { m_viewAnchorItem = nextItem; animateX(-nextItem->x() + m_leftPinnedSpace); } } qreal ContentItem::viewportLeft() const { return -x() + m_leftPinnedSpace; } qreal ContentItem::viewportRight() const { return -x() + m_view->width() - m_rightPinnedSpace; } qreal ContentItem::childWidth(QQuickItem *child) { if (!parentItem()) { return 0.0; } ColumnViewAttached *attached = qobject_cast(qmlAttachedPropertiesObject(child, true)); if (m_columnResizeMode == ColumnView::SingleColumn) { return qRound(parentItem()->width()); } else if (attached->fillWidth()) { return qRound(qBound(m_columnWidth, (parentItem()->width() - attached->reservedSpace()), parentItem()->width())); } else if (m_columnResizeMode == ColumnView::FixedColumns) { return qRound(qMin(parentItem()->width(), m_columnWidth)); // DynamicColumns } else { //TODO:look for Layout size hints qreal width = child->implicitWidth(); if (width < 1.0) { width = m_columnWidth; } return qRound(qMin(m_view->width(), width)); } } void ContentItem::layoutItems() { setY(m_view->topPadding()); setHeight(m_view->height() - m_view->topPadding() - m_view->bottomPadding()); qreal implicitWidth = 0; qreal implicitHeight = 0; qreal partialWidth = 0; int i = 0; m_leftPinnedSpace = 0; m_rightPinnedSpace = 0; for (QQuickItem *child : m_items) { ColumnViewAttached *attached = qobject_cast(qmlAttachedPropertiesObject(child, true)); - + if (child->isVisible()) { if (attached->isPinned() && m_view->columnResizeMode() != ColumnView::SingleColumn) { QQuickItem *sep = nullptr; int sepWidth = 0; if (m_view->separatorVisible()) { sep = ensureRightSeparator(child); sepWidth = (sep ? sep->width() : 0); } const qreal width = childWidth(child); child->setSize(QSizeF(width + sepWidth, height())); child->setPosition(QPointF(qMin(qMax(-x(), partialWidth), -x() + m_view->width() - child->width() + sepWidth), 0.0)); child->setZ(1); if (partialWidth <= -x()) { m_leftPinnedSpace = qMax(m_leftPinnedSpace, width); } else if (partialWidth > -x() + m_view->width() - child->width() + sepWidth) { m_rightPinnedSpace = qMax(m_rightPinnedSpace, child->width()); } partialWidth += width; } else { child->setSize(QSizeF(childWidth(child), height())); auto it = m_rightSeparators.find(child); if (it != m_rightSeparators.end()) { it.value()->deleteLater(); m_rightSeparators.erase(it); } child->setPosition(QPointF(partialWidth, 0.0)); child->setZ(0); partialWidth += child->width(); } } attached->setIndex(i++); implicitWidth += child->implicitWidth(); implicitHeight = qMax(implicitHeight, child->implicitHeight()); } setWidth(partialWidth); setImplicitWidth(implicitWidth); setImplicitHeight(implicitHeight); m_view->setImplicitWidth(implicitWidth); m_view->setImplicitHeight(implicitHeight + m_view->topPadding() + m_view->bottomPadding()); const qreal newContentX = m_viewAnchorItem ? -m_viewAnchorItem->x() : 0.0; if (m_shouldAnimate) { animateX(newContentX); } else { setBoundedX(newContentX); } updateVisibleItems(); } void ContentItem::layoutPinnedItems() { if (m_view->columnResizeMode() == ColumnView::SingleColumn) { return; } qreal partialWidth = 0; m_leftPinnedSpace = 0; m_rightPinnedSpace = 0; for (QQuickItem *child : m_items) { ColumnViewAttached *attached = qobject_cast(qmlAttachedPropertiesObject(child, true)); - + if (child->isVisible()) { if (attached->isPinned()) { QQuickItem *sep = nullptr; int sepWidth = 0; if (m_view->separatorVisible()) { sep = ensureRightSeparator(child); sepWidth = (sep ? sep->width() : 0); } child->setPosition(QPointF(qMin(qMax(-x(), partialWidth), -x() + m_view->width() - child->width() + sepWidth), 0.0)); if (partialWidth <= -x()) { m_leftPinnedSpace = qMax(m_leftPinnedSpace, child->width() - sepWidth); } else if (partialWidth > -x() + m_view->width() - child->width() + sepWidth) { m_rightPinnedSpace = qMax(m_rightPinnedSpace, child->width()); } } partialWidth += child->width(); } } } void ContentItem::updateVisibleItems() { QList newItems; for (auto *item : m_items) { if (item->isVisible() && item->x() + x() < width() && item->x() + item->width() + x() > 0) { newItems << item; connect(item, &QObject::destroyed, this, [this, item] { m_visibleItems.removeAll(item); }); } } for (auto *item : m_visibleItems) { disconnect(item, &QObject::destroyed, this, nullptr); } const QQuickItem *oldFirstVisibleItem = m_visibleItems.isEmpty() ? nullptr : qobject_cast(m_visibleItems.first()); const QQuickItem *oldLastVisibleItem = m_visibleItems.isEmpty() ? nullptr : qobject_cast(m_visibleItems.last()); if (newItems != m_visibleItems) { m_visibleItems = newItems; emit m_view->visibleItemsChanged(); if (!newItems.isEmpty() && m_visibleItems.first() != oldFirstVisibleItem) { emit m_view->firstVisibleItemChanged(); } if (!newItems.isEmpty() && m_visibleItems.last() != oldLastVisibleItem) { emit m_view->lastVisibleItemChanged(); } } } void ContentItem::forgetItem(QQuickItem *item) { if (!m_items.contains(item)) { return; } ColumnViewAttached *attached = qobject_cast(qmlAttachedPropertiesObject(item, true)); attached->setView(nullptr); attached->setIndex(-1); disconnect(attached, nullptr, this, nullptr); disconnect(item, nullptr, this, nullptr); disconnect(item, nullptr, m_view, nullptr); QQuickItem *separatorItem = m_separators.take(item); if (separatorItem) { separatorItem->deleteLater(); } separatorItem = m_rightSeparators.take(item); if (separatorItem) { separatorItem->deleteLater(); } const int index = m_items.indexOf(item); m_items.removeAll(item); disconnect(item, &QObject::destroyed, this, nullptr); updateVisibleItems(); m_shouldAnimate = true; m_view->polish(); item->setVisible(false); if (index <= m_view->currentIndex()) { m_view->setCurrentIndex(qBound(0, index - 1, m_items.count() - 1)); } emit m_view->countChanged(); } QQuickItem *ContentItem::ensureSeparator(QQuickItem *item) { QQuickItem *separatorItem = m_separators.value(item); - + if (!separatorItem) { separatorItem = qobject_cast(privateQmlComponentsPoolSelf->instance(qmlEngine(item))->m_separatorComponent->beginCreate(QQmlEngine::contextForObject(item))); if (separatorItem) { separatorItem->setParentItem(item); separatorItem->setZ(9999); separatorItem->setProperty("column", QVariant::fromValue(item)); QmlComponentsPoolSingleton::instance(qmlEngine(item))->m_separatorComponent->completeCreate(); m_separators[item] = separatorItem; } } return separatorItem; } QQuickItem *ContentItem::ensureRightSeparator(QQuickItem *item) { QQuickItem *separatorItem = m_rightSeparators.value(item); - + if (!separatorItem) { separatorItem = qobject_cast(QmlComponentsPoolSingleton::instance(qmlEngine(item))->m_rightSeparatorComponent->beginCreate(QQmlEngine::contextForObject(item))); if (separatorItem) { separatorItem->setParentItem(item); separatorItem->setZ(9999); separatorItem->setProperty("column", QVariant::fromValue(item)); QmlComponentsPoolSingleton::instance(qmlEngine(item))->m_rightSeparatorComponent->completeCreate(); m_rightSeparators[item] = separatorItem; } } return separatorItem; } void ContentItem::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value) { switch (change) { case QQuickItem::ItemChildAddedChange: { ColumnViewAttached *attached = qobject_cast(qmlAttachedPropertiesObject(value.item, true)); attached->setView(m_view); //connect(attached, &ColumnViewAttached::fillWidthChanged, m_view, &ColumnView::polish); connect(attached, &ColumnViewAttached::fillWidthChanged, this, [this, attached](){ m_view->polish(); - + }); connect(attached, &ColumnViewAttached::reservedSpaceChanged, m_view, &ColumnView::polish); value.item->setVisible(true); if (!m_items.contains(value.item)) { connect(value.item, &QQuickItem::widthChanged, m_view, &ColumnView::polish); QQuickItem *item = value.item; m_items << item; connect(item, &QObject::destroyed, this, [this, item]() { m_view->removeItem(item); }); } if (m_view->separatorVisible()) { ensureSeparator(value.item); } m_shouldAnimate = true; m_view->polish(); emit m_view->countChanged(); break; } case QQuickItem::ItemChildRemovedChange: { forgetItem(value.item); break; } case QQuickItem::ItemVisibleHasChanged: updateVisibleItems(); if (value.boolValue) { m_view->polish(); } break; default: break; } QQuickItem::itemChange(change, value); } void ContentItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { updateVisibleItems(); QQuickItem::geometryChanged(newGeometry, oldGeometry); } void ContentItem::syncItemsOrder() { if (m_items == childItems()) { return; } m_items = childItems(); //NOTE: polish() here sometimes gets indefinitely delayed and items chaging order isn't seen layoutItems(); } void ContentItem::updateRepeaterModel() { if (!sender()) { return; } QObject *modelObj = sender()->property("model").value(); if (!modelObj) { m_models.remove(sender()); return; } if (m_models[sender()]) { disconnect(m_models[sender()], nullptr, this, nullptr); } m_models[sender()] = modelObj; QAbstractItemModel *qaim = qobject_cast(modelObj); if (qaim) { connect(qaim, &QAbstractItemModel::rowsMoved, this, &ContentItem::syncItemsOrder); } else { connect(modelObj, SIGNAL(childrenChanged()), this, SLOT(syncItemsOrder())); } } ColumnView::ColumnView(QQuickItem *parent) : QQuickItem(parent), m_contentItem(nullptr) { //NOTE: this is to *not* trigger itemChange m_contentItem = new ContentItem(this); setAcceptedMouseButtons(Qt::LeftButton | Qt::BackButton | Qt::ForwardButton); setFiltersChildMouseEvents(true); connect(m_contentItem->m_slideAnim, &QPropertyAnimation::finished, this, [this] () { m_moving = false; emit movingChanged(); }); connect(m_contentItem, &ContentItem::widthChanged, this, &ColumnView::contentWidthChanged); connect(m_contentItem, &ContentItem::xChanged, this, &ColumnView::contentXChanged); connect(this, &ColumnView::activeFocusChanged, this, [this]() { if (hasActiveFocus() && m_currentItem) { m_currentItem->forceActiveFocus(); } }); ColumnViewAttached *attached = qobject_cast(qmlAttachedPropertiesObject(this, true)); attached->setView(this); attached = qobject_cast(qmlAttachedPropertiesObject(m_contentItem, true)); attached->setView(this); } ColumnView::~ColumnView() { } ColumnView::ColumnResizeMode ColumnView::columnResizeMode() const { return m_contentItem->m_columnResizeMode; } void ColumnView::setColumnResizeMode(ColumnResizeMode mode) { if (m_contentItem->m_columnResizeMode == mode) { return; } m_contentItem->m_columnResizeMode = mode; if (mode == SingleColumn && m_currentItem) { m_contentItem->m_viewAnchorItem = m_currentItem; } m_contentItem->m_shouldAnimate = false; polish(); emit columnResizeModeChanged(); } qreal ColumnView::columnWidth() const { return m_contentItem->m_columnWidth; } void ColumnView::setColumnWidth(qreal width) { // Always forget the internal binding when the user sets anything, even the same value disconnect(QmlComponentsPoolSingleton::instance(qmlEngine(this)), &QmlComponentsPool::gridUnitChanged, this, nullptr); if (m_contentItem->m_columnWidth == width) { return; } m_contentItem->m_columnWidth = width; m_contentItem->m_shouldAnimate = false; polish(); emit columnWidthChanged(); } int ColumnView::currentIndex() const { return m_currentIndex; } void ColumnView::setCurrentIndex(int index) { if (m_currentIndex == index || index < -1 || index >= m_contentItem->m_items.count()) { return; } m_currentIndex = index; if (index == -1) { m_currentItem.clear(); } else { m_currentItem = m_contentItem->m_items[index]; Q_ASSERT(m_currentItem); m_currentItem->forceActiveFocus(); // If the current item is not on view, scroll QRectF mappedCurrent = m_currentItem->mapRectToItem(this, QRectF(QPointF(0, 0), m_currentItem->size())); if (m_contentItem->m_slideAnim->state() == QAbstractAnimation::Running) { mappedCurrent.moveLeft(mappedCurrent.left() + m_contentItem->x() + m_contentItem->m_slideAnim->endValue().toInt()); } //m_contentItem->m_slideAnim->stop(); QRectF contentsRect(m_contentItem->m_leftPinnedSpace, 0, width() - m_contentItem->m_rightPinnedSpace - m_contentItem->m_leftPinnedSpace, height()); m_contentItem->m_shouldAnimate = true; if (!m_mouseDown) { if (!contentsRect.contains(mappedCurrent)) { m_contentItem->m_viewAnchorItem = m_currentItem; m_contentItem->animateX(-m_currentItem->x() + m_contentItem->m_leftPinnedSpace); } else { m_contentItem->snapToItem(); } } } emit currentIndexChanged(); emit currentItemChanged(); } QQuickItem *ColumnView::currentItem() { return m_currentItem; } QListColumnView::visibleItems() const { return m_contentItem->m_visibleItems; } QQuickItem *ColumnView::firstVisibleItem() const { if (m_contentItem->m_visibleItems.isEmpty()) { return nullptr; } return qobject_cast(m_contentItem->m_visibleItems.first()); } QQuickItem *ColumnView::lastVisibleItem() const { if (m_contentItem->m_visibleItems.isEmpty()) { return nullptr; } return qobject_cast(m_contentItem->m_visibleItems.last()); } int ColumnView::count() const { return m_contentItem->m_items.count(); } qreal ColumnView::topPadding() const { return m_topPadding; } void ColumnView::setTopPadding(qreal padding) { if (padding == m_topPadding) { return; } m_topPadding = padding; polish(); emit topPaddingChanged(); } qreal ColumnView::bottomPadding() const { return m_bottomPadding; } void ColumnView::setBottomPadding(qreal padding) { if (padding == m_bottomPadding) { return; } m_bottomPadding = padding; polish(); emit bottomPaddingChanged(); } QQuickItem *ColumnView::contentItem() const { return m_contentItem; } int ColumnView::scrollDuration() const { return m_contentItem->m_slideAnim->duration(); } void ColumnView::setScrollDuration(int duration) { disconnect(QmlComponentsPoolSingleton::instance(qmlEngine(this)), &QmlComponentsPool::longDurationChanged, this, nullptr); if (m_contentItem->m_slideAnim->duration() == duration) { return; } m_contentItem->m_slideAnim->setDuration(duration); emit scrollDurationChanged(); } bool ColumnView::separatorVisible() const { return m_separatorVisible; } void ColumnView::setSeparatorVisible(bool visible) { if (visible == m_separatorVisible) { return; } m_separatorVisible = visible; if (visible) { for (QQuickItem *item : m_contentItem->m_items) { QQuickItem *sep = m_contentItem->ensureSeparator(item); if (sep) { sep->setVisible(true); } ColumnViewAttached *attached = qobject_cast(qmlAttachedPropertiesObject(item, true)); if (attached->isPinned()) { QQuickItem *sep = m_contentItem->ensureRightSeparator(item); if (sep) { sep->setVisible(true); } } } } else { for (QQuickItem *sep : m_contentItem->m_separators.values()) { sep->setVisible(false); } for (QQuickItem *sep : m_contentItem->m_rightSeparators.values()) { sep->setVisible(false); } } emit separatorVisibleChanged(); } bool ColumnView::dragging() const { return m_dragging; } bool ColumnView::moving() const { return m_moving; } qreal ColumnView::contentWidth() const { return m_contentItem->width(); } qreal ColumnView::contentX() const { return -m_contentItem->x(); } void ColumnView::setContentX(qreal x) const { m_contentItem->setX(qRound(-x)); } bool ColumnView::interactive() const { return m_interactive; } void ColumnView::setInteractive(bool interactive) { if (m_interactive == interactive) { return; } m_interactive = interactive; if (!m_interactive) { if (m_dragging) { m_dragging = false; emit draggingChanged(); } m_contentItem->snapToItem(); setKeepMouseGrab(false); } emit interactiveChanged(); } void ColumnView::addItem(QQuickItem *item) { insertItem(m_contentItem->m_items.length(), item); } void ColumnView::insertItem(int pos, QQuickItem *item) { if (!item || m_contentItem->m_items.contains(item)) { return; } m_contentItem->m_items.insert(qBound(0, pos, m_contentItem->m_items.length()), item); connect(item, &QObject::destroyed, m_contentItem, [this, item]() { removeItem(item); }); ColumnViewAttached *attached = qobject_cast(qmlAttachedPropertiesObject(item, true)); attached->setOriginalParent(item->parentItem()); attached->setShouldDeleteOnRemove(item->parentItem() == nullptr && QQmlEngine::objectOwnership(item) == QQmlEngine::JavaScriptOwnership); item->setParentItem(m_contentItem); item->forceActiveFocus(); // We layout immediately to be sure all geometries are final after the return of this call m_contentItem->m_shouldAnimate = false; m_contentItem->layoutItems(); emit contentChildrenChanged(); // In order to keep the same current item we need to increase the current index if displaced // NOTE: just updating m_currentIndex does *not* update currentItem (which is what we need atm) while setCurrentIndex will update also currentItem if (m_currentIndex >= pos) { ++m_currentIndex; emit currentIndexChanged(); } emit itemInserted(pos, item); } void ColumnView::moveItem(int from, int to) { if (m_contentItem->m_items.isEmpty() || from < 0 || from >= m_contentItem->m_items.length() || to < 0 || to >= m_contentItem->m_items.length()) { return; } m_contentItem->m_items.move(from, to); m_contentItem->m_shouldAnimate = true; if (from == m_currentIndex) { m_currentIndex = to; emit currentIndexChanged(); } else if (from < m_currentIndex && to > m_currentIndex) { --m_currentIndex; emit currentIndexChanged(); } else if (from > m_currentIndex && to <= m_currentIndex) { ++m_currentIndex; emit currentIndexChanged(); } polish(); } QQuickItem *ColumnView::removeItem(const QVariant &item) { if (item.canConvert()) { return removeItem(item.value()); } else if (item.canConvert()) { return removeItem(item.toInt()); } else { return nullptr; } } QQuickItem *ColumnView::removeItem(QQuickItem *item) { if (m_contentItem->m_items.isEmpty() || !m_contentItem->m_items.contains(item)) { return nullptr; } const int index = m_contentItem->m_items.indexOf(item); // In order to keep the same current item we need to increase the current index if displaced if (m_currentIndex >= index) { setCurrentIndex(m_currentIndex - 1); } m_contentItem->forgetItem(item); ColumnViewAttached *attached = qobject_cast(qmlAttachedPropertiesObject(item, false)); if (attached && attached->shouldDeleteOnRemove()) { item->deleteLater(); } else { item->setParentItem(attached ? attached->originalParent() : nullptr); } emit itemRemoved(item); return item; } QQuickItem *ColumnView::removeItem(int pos) { if (m_contentItem->m_items.isEmpty() || pos < 0 || pos >= m_contentItem->m_items.length()) { return nullptr; } - return removeItem(m_contentItem->m_items[pos]); + return removeItem(m_contentItem->m_items[pos]); } QQuickItem *ColumnView::pop(QQuickItem *item) { QQuickItem *removed = nullptr; while (!m_contentItem->m_items.isEmpty() && m_contentItem->m_items.last() != item) { removed = removeItem(m_contentItem->m_items.last()); // if no item has been passed, just pop one if (!item) { break; } } return removed; } void ColumnView::clear() { for (QQuickItem *item : m_contentItem->m_items) { removeItem(item); } m_contentItem->m_items.clear(); emit contentChildrenChanged(); } bool ColumnView::containsItem(QQuickItem *item) { return m_contentItem->m_items.contains(item); } QQuickItem *ColumnView::itemAt(qreal x, qreal y) { return m_contentItem->childAt(x, y); } ColumnViewAttached *ColumnView::qmlAttachedProperties(QObject *object) { return new ColumnViewAttached(object); } void ColumnView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { m_contentItem->setY(m_topPadding); m_contentItem->setHeight(newGeometry.height() - m_topPadding - m_bottomPadding); m_contentItem->m_shouldAnimate = false; polish(); m_contentItem->updateVisibleItems(); QQuickItem::geometryChanged(newGeometry, oldGeometry); } bool ColumnView::childMouseEventFilter(QQuickItem *item, QEvent *event) { if (!m_interactive || item == m_contentItem) { return QQuickItem::childMouseEventFilter(item, event); } switch (event->type()) { case QEvent::MouseButtonPress: { QMouseEvent *me = static_cast(event); if (me->button() != Qt::LeftButton) { return false; } m_contentItem->m_slideAnim->stop(); if (item->property("preventStealing").toBool()) { m_contentItem->snapToItem(); return false; } m_oldMouseX = m_startMouseX = mapFromItem(item, me->localPos()).x(); m_mouseDown = true; me->setAccepted(false); setKeepMouseGrab(false); - // On press, we set the current index of the view to the root item + // On press, we set the current index of the view to the root item QQuickItem *candidateItem = item; while (candidateItem->parentItem() && candidateItem->parentItem() != m_contentItem) { candidateItem = candidateItem->parentItem(); } if (candidateItem->parentItem() == m_contentItem) { setCurrentIndex(m_contentItem->m_items.indexOf(candidateItem)); } break; } case QEvent::MouseMove: { QMouseEvent *me = static_cast(event); if (!(me->buttons() & Qt::LeftButton)) { return false; } if ((!keepMouseGrab() && item->keepMouseGrab()) || item->property("preventStealing").toBool()) { m_contentItem->snapToItem(); return false; } QQuickItem *candidateItem = item; while (candidateItem->parentItem() && candidateItem->parentItem() != m_contentItem) { candidateItem = candidateItem->parentItem(); } if (candidateItem->parentItem() == m_contentItem) { ColumnViewAttached *attached = qobject_cast(qmlAttachedPropertiesObject(candidateItem, true)); if (attached->preventStealing()) { return false; } } const QPointF pos = mapFromItem(item, me->localPos()); const bool wasDragging = m_dragging; // If a drag happened, start to steal all events, use startDragDistance * 2 to give time to widgets to take the mouse grab by themselves m_dragging = keepMouseGrab() || qAbs(mapFromItem(item, me->localPos()).x() - m_startMouseX) > qApp->styleHints()->startDragDistance() * 3; if (m_dragging != wasDragging) { m_moving = true; emit movingChanged(); emit draggingChanged(); } if (m_dragging) { m_contentItem->setBoundedX(m_contentItem->x() + pos.x() - m_oldMouseX); } m_contentItem->m_lastDragDelta = pos.x() - m_oldMouseX; m_oldMouseX = pos.x(); setKeepMouseGrab(m_dragging); me->setAccepted(m_dragging); return m_dragging; break; } case QEvent::MouseButtonRelease: { QMouseEvent *me = static_cast(event); if (me->button() == Qt::BackButton && m_currentIndex > 0) { setCurrentIndex(m_currentIndex - 1); me->accept(); return true; } else if (me->button() == Qt::ForwardButton) { setCurrentIndex(m_currentIndex + 1); me->accept(); return true; } if (me->button() != Qt::LeftButton) { return false; } m_mouseDown = false; m_contentItem->snapToItem(); m_contentItem->m_lastDragDelta = 0; if (m_dragging) { m_dragging = false; emit draggingChanged(); } if (item->property("preventStealing").toBool()) { return false; } event->accept(); //if a drag happened, don't pass the event const bool block = keepMouseGrab(); setKeepMouseGrab(false); me->setAccepted(block); return block; break; } default: break; } return QQuickItem::childMouseEventFilter(item, event); } void ColumnView::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::BackButton || event->button() == Qt::ForwardButton) { event->accept(); return; } if (!m_interactive) { return; } m_contentItem->snapToItem(); m_oldMouseX = event->localPos().x(); m_startMouseX = event->localPos().x(); m_mouseDown = true; setKeepMouseGrab(false); event->accept(); } void ColumnView::mouseMoveEvent(QMouseEvent *event) { if (event->buttons() & Qt::BackButton || event->buttons() & Qt::ForwardButton) { event->accept(); return; } if (!m_interactive) { return; } const bool wasDragging = m_dragging; // Same startDragDistance * 2 as the event filter m_dragging = keepMouseGrab() || qAbs(event->localPos().x() - m_startMouseX) > qApp->styleHints()->startDragDistance() * 2; if (m_dragging != wasDragging) { m_moving = true; emit movingChanged(); emit draggingChanged(); } setKeepMouseGrab(m_dragging); if (m_dragging) { m_contentItem->setBoundedX(m_contentItem->x() + event->pos().x() - m_oldMouseX); } m_contentItem->m_lastDragDelta = event->pos().x() - m_oldMouseX; m_oldMouseX = event->pos().x(); event->accept(); } void ColumnView::mouseReleaseEvent(QMouseEvent *event) { if (event->button() == Qt::BackButton && m_currentIndex > 0) { setCurrentIndex(m_currentIndex - 1); event->accept(); return; } else if (event->button() == Qt::ForwardButton) { setCurrentIndex(m_currentIndex + 1); event->accept(); return; } m_mouseDown = false; if (!m_interactive) { return; } m_contentItem->snapToItem(); m_contentItem->m_lastDragDelta = 0; if (m_dragging) { m_dragging = false; emit draggingChanged(); } setKeepMouseGrab(false); event->accept(); } void ColumnView::mouseUngrabEvent() { m_mouseDown = false; if (m_contentItem->m_slideAnim->state() != QAbstractAnimation::Running) { m_contentItem->snapToItem(); } m_contentItem->m_lastDragDelta = 0; if (m_dragging) { m_dragging = false; emit draggingChanged(); } setKeepMouseGrab(false); } void ColumnView::classBegin() { auto syncColumnWidth = [this]() { m_contentItem->m_columnWidth = privateQmlComponentsPoolSelf->instance(qmlEngine(this))->m_units->property("gridUnit").toInt() * 20; emit columnWidthChanged(); }; connect(QmlComponentsPoolSingleton::instance(qmlEngine(this)), &QmlComponentsPool::gridUnitChanged, this, syncColumnWidth); syncColumnWidth(); auto syncDuration = [this]() { m_contentItem->m_slideAnim->setDuration(QmlComponentsPoolSingleton::instance(qmlEngine(this))->m_units->property("longDuration").toInt()); emit scrollDurationChanged(); }; connect(QmlComponentsPoolSingleton::instance(qmlEngine(this)), &QmlComponentsPool::longDurationChanged, this, syncDuration); syncDuration(); QQuickItem::classBegin(); } void ColumnView::componentComplete() { m_complete = true; QQuickItem::componentComplete(); } void ColumnView::updatePolish() { m_contentItem->layoutItems(); } void ColumnView::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value) { switch (change) { case QQuickItem::ItemChildAddedChange: if (m_contentItem && value.item != m_contentItem && !value.item->inherits("QQuickRepeater")) { addItem(value.item); } break; default: break; } QQuickItem::itemChange(change, value); } void ColumnView::contentChildren_append(QQmlListProperty *prop, QQuickItem *item) { // This can only be called from QML ColumnView *view = static_cast(prop->object); if (!view) { return; } view->m_contentItem->m_items.append(item); connect(item, &QObject::destroyed, view->m_contentItem, [view, item]() { view->removeItem(item); }); ColumnViewAttached *attached = qobject_cast(qmlAttachedPropertiesObject(item, true)); attached->setOriginalParent(item->parentItem()); attached->setShouldDeleteOnRemove(item->parentItem() == nullptr && QQmlEngine::objectOwnership(item) == QQmlEngine::JavaScriptOwnership); item->setParentItem(view->m_contentItem); } int ColumnView::contentChildren_count(QQmlListProperty *prop) { ColumnView *view = static_cast(prop->object); if (!view) { return 0; } return view->m_contentItem->m_items.count(); } QQuickItem *ColumnView::contentChildren_at(QQmlListProperty *prop, int index) { ColumnView *view = static_cast(prop->object); if (!view) { return nullptr; } if (index < 0 || index >= view->m_contentItem->m_items.count()) { return nullptr; } return view->m_contentItem->m_items.value(index); } void ColumnView::contentChildren_clear(QQmlListProperty *prop) { ColumnView *view = static_cast(prop->object); if (!view) { return; } return view->m_contentItem->m_items.clear(); } QQmlListProperty ColumnView::contentChildren() { return QQmlListProperty(this, nullptr, contentChildren_append, contentChildren_count, contentChildren_at, contentChildren_clear); } void ColumnView::contentData_append(QQmlListProperty *prop, QObject *object) { ColumnView *view = static_cast(prop->object); if (!view) { return; } view->m_contentData.append(object); QQuickItem *item = qobject_cast(object); //exclude repeaters from layout if (item && item->inherits("QQuickRepeater")) { item->setParentItem(view); connect(item, SIGNAL(modelChanged()), view->m_contentItem, SLOT(updateRepeaterModel())); } else if (item) { view->m_contentItem->m_items.append(item); connect(item, &QObject::destroyed, view->m_contentItem, [view, item]() { view->removeItem(item); }); ColumnViewAttached *attached = qobject_cast(qmlAttachedPropertiesObject(item, true)); attached->setOriginalParent(item->parentItem()); attached->setShouldDeleteOnRemove(view->m_complete && !item->parentItem() && QQmlEngine::objectOwnership(item) == QQmlEngine::JavaScriptOwnership); item->setParentItem(view->m_contentItem); } else { object->setParent(view); } } int ColumnView::contentData_count(QQmlListProperty *prop) { ColumnView *view = static_cast(prop->object); if (!view) { return 0; } return view->m_contentData.count(); } QObject *ColumnView::contentData_at(QQmlListProperty *prop, int index) { ColumnView *view = static_cast(prop->object); if (!view) { return nullptr; } if (index < 0 || index >= view->m_contentData.count()) { return nullptr; } return view->m_contentData.value(index); } void ColumnView::contentData_clear(QQmlListProperty *prop) { ColumnView *view = static_cast(prop->object); if (!view) { return; } return view->m_contentData.clear(); } QQmlListProperty ColumnView::contentData() { return QQmlListProperty(this, nullptr, contentData_append, contentData_count, contentData_at, contentData_clear); } #include "moc_columnview.cpp" diff --git a/src/columnview.h b/src/columnview.h index 763578da..e637627e 100644 --- a/src/columnview.h +++ b/src/columnview.h @@ -1,446 +1,433 @@ /* - * Copyright 2019 Marco Martin + * SPDX-FileCopyrightText: 2019 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ #pragma once #include #include #include class ContentItem; class ColumnView; /** * This is an attached property to every item that is inserted in the ColumnView, * used to access the view and page information such as the position and informations for layouting, such as fillWidth * @since 2.7 */ class ColumnViewAttached : public QObject { Q_OBJECT /** * The index position of the column in the view, starting from 0 */ Q_PROPERTY(int index READ index WRITE setIndex NOTIFY indexChanged) /** * If true, the column will expand to take the whole viewport space minus reservedSpace */ Q_PROPERTY(bool fillWidth READ fillWidth WRITE setFillWidth NOTIFY fillWidthChanged) /** * When a column is fillWidth, it will keep reservedSpace amount of pixels from going to fill the full viewport width */ Q_PROPERTY(qreal reservedSpace READ reservedSpace WRITE setReservedSpace NOTIFY reservedSpaceChanged) /** - * Like the same property of MouseArea, when this is true, the column view won't + * Like the same property of MouseArea, when this is true, the column view won't * try to manage events by itself when filtering from a child, not * disturbing user interaction */ Q_PROPERTY(bool preventStealing READ preventStealing WRITE setPreventStealing NOTIFY preventStealingChanged) /** * If true the page will never go out of view, but will stay either * at the right or left side of the Columnview */ Q_PROPERTY(bool pinned READ isPinned WRITE setPinned NOTIFY pinnedChanged) /** * The view this column belongs to */ Q_PROPERTY(ColumnView *view READ view NOTIFY viewChanged) public: ColumnViewAttached(QObject *parent = nullptr); ~ColumnViewAttached(); void setIndex(int index); int index() const; void setFillWidth(bool fill); bool fillWidth() const; qreal reservedSpace() const; void setReservedSpace(qreal space); ColumnView *view(); void setView(ColumnView *view); //Private API, not for QML use QQuickItem *originalParent() const; void setOriginalParent(QQuickItem *parent); bool shouldDeleteOnRemove() const; void setShouldDeleteOnRemove(bool del); bool preventStealing() const; void setPreventStealing(bool prevent); bool isPinned() const; void setPinned(bool pinned); Q_SIGNALS: void indexChanged(); void fillWidthChanged(); void reservedSpaceChanged(); void viewChanged(); void preventStealingChanged(); void pinnedChanged(); private: int m_index = -1; bool m_fillWidth = false; qreal m_reservedSpace = 0; QPointer m_view; QPointer m_originalParent; bool m_customFillWidth = false; bool m_customReservedSpace = false; bool m_shouldDeleteOnRemove = true; bool m_preventStealing = false; bool m_pinned = false; }; /** * ColumnView is a container that lays out items horizontally in a row, * when not all items fit in the ColumnView, it will behave ike a Flickable and will be a scrollable view which shows only a determined number of columns. * The columns can either all have the same fixed size (recommended), * size themselves with implicitWidth, or automatically expand to take all the available width: by default the last column will always be the expanding one. * Items inside the Columnview can access info of the view and set layouting hints via the Columnview attached property. * * This is the base for the implementation of PageRow * @since 2.7 */ class ColumnView : public QQuickItem { Q_OBJECT /** * The strategy to follow while automatically resizing the columns, * the enum can have the following values: * * FixedColumns: every column is fixed at the same width of the columnWidth property * * DynamicColumns: columns take their width from their implicitWidth * * SingleColumn: only one column at a time is shown, as wide as the viewport, eventual reservedSpace on the column's atttached property is ignored */ Q_PROPERTY(ColumnResizeMode columnResizeMode READ columnResizeMode WRITE setColumnResizeMode NOTIFY columnResizeModeChanged) /** * The width of all columns when columnResizeMode is FixedColumns */ Q_PROPERTY(qreal columnWidth READ columnWidth WRITE setColumnWidth NOTIFY columnWidthChanged) /** * How many columns this view containsItem*/ Q_PROPERTY(int count READ count NOTIFY countChanged) /** * The position of the currently active column. The current column will also have keyboard focus */ Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) /** * The currently active column. The current column will also have keyboard focus */ Q_PROPERTY(QQuickItem *currentItem READ currentItem NOTIFY currentItemChanged) /** * The main content item of this view: it's the parent of the column items */ Q_PROPERTY(QQuickItem *contentItem READ contentItem CONSTANT) /** * The value of the horizontal scroll of the view, in pixels */ Q_PROPERTY(qreal contentX READ contentX WRITE setContentX NOTIFY contentXChanged) /** * The compound width of all columns in the view */ Q_PROPERTY(qreal contentWidth READ contentWidth NOTIFY contentWidthChanged) /** * The padding this will have at the top */ Q_PROPERTY(qreal topPadding READ topPadding WRITE setTopPadding NOTIFY topPaddingChanged) /** * The padding this will have at the bottom */ Q_PROPERTY(qreal bottomPadding READ bottomPadding WRITE setBottomPadding NOTIFY bottomPaddingChanged) /** * The duration for scrolling animations */ Q_PROPERTY(int scrollDuration READ scrollDuration WRITE setScrollDuration NOTIFY scrollDurationChanged) /** * True if columns should be visually separed by a separator line */ Q_PROPERTY(bool separatorVisible READ separatorVisible WRITE setSeparatorVisible NOTIFY separatorVisibleChanged) /** * The list of all visible column items that are at least partially in the viewport at any given moment */ Q_PROPERTY(QList visibleItems READ visibleItems NOTIFY visibleItemsChanged) /** * The first of visibleItems provided from convenience */ Q_PROPERTY(QQuickItem *firstVisibleItem READ firstVisibleItem NOTIFY firstVisibleItemChanged) /** * The last of visibleItems provided from convenience */ Q_PROPERTY(QQuickItem *lastVisibleItem READ lastVisibleItem NOTIFY lastVisibleItemChanged) // Properties to make it similar to Flickable /** * True when the user is dragging around with touch gestures the view contents */ Q_PROPERTY(bool dragging READ dragging NOTIFY draggingChanged) /** * True both when the user is dragging around with touch gestures the view contents or the view is animating */ Q_PROPERTY(bool moving READ moving NOTIFY movingChanged) /** * True if it supports moving the contents by dragging */ Q_PROPERTY(bool interactive READ interactive WRITE setInteractive NOTIFY interactiveChanged) // Default properties /** * Every column item the view contains */ Q_PROPERTY(QQmlListProperty contentChildren READ contentChildren NOTIFY contentChildrenChanged FINAL) /** * every item declared inside the view, both visual and non-visual items */ Q_PROPERTY(QQmlListProperty contentData READ contentData FINAL) Q_CLASSINFO("DefaultProperty", "contentData") Q_ENUMS(ColumnResizeMode) public: enum ColumnResizeMode { FixedColumns = 0, DynamicColumns, SingleColumn }; ColumnView(QQuickItem *parent = nullptr); ~ColumnView(); // QML property accessors ColumnResizeMode columnResizeMode() const; void setColumnResizeMode(ColumnResizeMode mode); qreal columnWidth() const; void setColumnWidth(qreal width); int currentIndex() const; void setCurrentIndex(int index); int scrollDuration() const; void setScrollDuration(int duration); bool separatorVisible() const; void setSeparatorVisible(bool visible); int count() const; qreal topPadding() const; void setTopPadding(qreal padding); qreal bottomPadding() const; void setBottomPadding(qreal padding); QQuickItem *currentItem(); //NOTE: It's a QList as QML can't corectly build an Array out of QList QList visibleItems() const; QQuickItem *firstVisibleItem() const; QQuickItem *lastVisibleItem() const; QQuickItem *contentItem() const; QQmlListProperty contentChildren(); QQmlListProperty contentData(); bool dragging() const; bool moving() const; qreal contentWidth() const; qreal contentX() const; void setContentX(qreal x) const; bool interactive() const; void setInteractive(bool interactive); // Api not intended for QML use //can't do overloads in QML QQuickItem *removeItem(QQuickItem *item); QQuickItem *removeItem(int item); // QML attached property static ColumnViewAttached *qmlAttachedProperties(QObject *object); public Q_SLOTS: /** * Pushes a new item at the end of the view * @param item the new item which will be reparented and managed */ void addItem(QQuickItem *item); /** * Inserts a new item in the view at a given position. * The current Item will not be changed, currentIndex will be adjusted * accordingly if needed to keep the same current item. * @param pos the position we want the new item to be inserted in * @param item the new item which will be reparented and managed */ void insertItem(int pos, QQuickItem *item); /** * Move an item inside the view. * The currentIndex property may be changed in order to keep currentItem the same. * @param from the old position * @param to the new position */ void moveItem(int from, int to); /** * Removes an item from the view. * Items will be reparented to their old parent. * If they have JavaScript ownership and they didn't have an old parent, they will be destroyed. * CurrentIndex may be changed in order to keep the same currentItem * @param item it can either be a pointer of an item or an integer specifying the position to remove * @returns the item that has just been removed */ QQuickItem *removeItem(const QVariant &item); /** * Removes all the items after item. Starting from the last column, every column will be removed until item is found, which will be left in place. * Items will be reparented to their old parent. * If they have JavaScript ownership and they didn't have an old parent, they will be destroyed * @param item the item which will be the new last one of the row. * @returns the last item that has been removed */ QQuickItem *pop(QQuickItem *item); /** * Removes every item in the view. * Items will be reparented to their old parent. * If they have JavaScript ownership and they didn't have an old parent, they will be destroyed */ void clear(); /** * @returns true if the view contains the given item */ bool containsItem(QQuickItem *item); /** * Returns the visible item containing the point x, y in content coordinates. * If there is no item at the point specified, or the item is not visible null is returned. */ QQuickItem *itemAt(qreal x, qreal y); protected: void classBegin() override; void componentComplete() override; void updatePolish() override; void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value) override; void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; bool childMouseEventFilter(QQuickItem *item, QEvent *event) override; void mousePressEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; void mouseUngrabEvent() override; Q_SIGNALS: /** * A new item has been inserted * @param position where the page has been inserted * @param item a pointer to the new item */ void itemInserted(int position, QQuickItem *item); /** * An item has just been removed from the view * @param item a pointer to the item that has just been removed */ void itemRemoved(QQuickItem *item); // Property notifiers void contentChildrenChanged(); void columnResizeModeChanged(); void columnWidthChanged(); void currentIndexChanged(); void currentItemChanged(); void visibleItemsChanged(); void countChanged(); void draggingChanged(); void movingChanged(); void contentXChanged(); void contentWidthChanged(); void interactiveChanged(); void scrollDurationChanged(); void separatorVisibleChanged(); void firstVisibleItemChanged(); void lastVisibleItemChanged(); void topPaddingChanged(); void bottomPaddingChanged(); private: static void contentChildren_append(QQmlListProperty *prop, QQuickItem *object); static int contentChildren_count(QQmlListProperty *prop); static QQuickItem *contentChildren_at(QQmlListProperty *prop, int index); static void contentChildren_clear(QQmlListProperty *prop); static void contentData_append(QQmlListProperty *prop, QObject *object); static int contentData_count(QQmlListProperty *prop); static QObject *contentData_at(QQmlListProperty *prop, int index); static void contentData_clear(QQmlListProperty *prop); QList m_contentData; ContentItem *m_contentItem; QPointer m_currentItem; static QHash m_attachedObjects; qreal m_oldMouseX = -1.0; qreal m_startMouseX = -1.0; int m_currentIndex = -1; qreal m_topPadding = 0; qreal m_bottomPadding = 0; bool m_mouseDown = false; bool m_interactive = true; bool m_dragging = false; bool m_moving = false; bool m_separatorVisible = true; bool m_complete = false; }; QML_DECLARE_TYPEINFO(ColumnView, QML_HAS_ATTACHED_PROPERTIES) diff --git a/src/columnview_p.h b/src/columnview_p.h index 886b96b8..a103d8fb 100644 --- a/src/columnview_p.h +++ b/src/columnview_p.h @@ -1,100 +1,87 @@ /* - * Copyright 2019 Marco Martin + * SPDX-FileCopyrightText: 2019 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ #pragma once #include "columnview.h" #include #include class QPropertyAnimation; class QQmlComponent; class QmlComponentsPool: public QObject { Q_OBJECT public: QmlComponentsPool(QQmlEngine *engine); ~QmlComponentsPool(); QQmlComponent *m_separatorComponent = nullptr; QQmlComponent *m_rightSeparatorComponent = nullptr; QObject *m_units = nullptr; Q_SIGNALS: void gridUnitChanged(); void longDurationChanged(); private: QObject *m_instance = nullptr; }; class ContentItem : public QQuickItem { Q_OBJECT public: ContentItem(ColumnView *parent = nullptr); ~ContentItem(); void layoutItems(); void layoutPinnedItems(); qreal childWidth(QQuickItem *child); void updateVisibleItems(); void forgetItem(QQuickItem *item); QQuickItem *ensureSeparator(QQuickItem *item); QQuickItem *ensureRightSeparator(QQuickItem *item); void setBoundedX(qreal x); void animateX(qreal x); void snapToItem(); inline qreal viewportLeft() const; inline qreal viewportRight() const; protected: void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value) override; void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; private Q_SLOTS: void syncItemsOrder(); void updateRepeaterModel(); private: ColumnView *m_view; QPropertyAnimation *m_slideAnim; QList m_items; QList m_visibleItems; QPointer m_viewAnchorItem; QHash m_separators; QHash m_rightSeparators; QHash m_models; qreal m_leftPinnedSpace = 361; qreal m_rightPinnedSpace = 0; qreal m_columnWidth = 0; qreal m_lastDragDelta = 0; ColumnView::ColumnResizeMode m_columnResizeMode = ColumnView::FixedColumns; bool m_shouldAnimate = false; friend class ColumnView; }; diff --git a/src/controls/AboutPage.qml b/src/controls/AboutPage.qml index 3db43d9f..94a71afd 100644 --- a/src/controls/AboutPage.qml +++ b/src/controls/AboutPage.qml @@ -1,234 +1,221 @@ /* - * Copyright (C) 2018 Aleix Pol Gonzalez + * SPDX-FileCopyrightText: 2018 Aleix Pol Gonzalez * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library/Lesser General Public License - * version 2, or (at your option) any later version, as published by the - * Free Software Foundation - * - * This program 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 General Public License for more details - * - * You should have received a copy of the GNU Library/Lesser General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.1 import QtQuick.Controls 2.4 as QQC2 import QtQuick.Layouts 1.3 import org.kde.kirigami 2.5 /** * An about page that is ready to integrate in a kirigami app * * Allows to have a page that will show the copyright notice of the application * together with the contributors and some information of which platform it's * running on. * * @since 5.52 * @since org.kde.kirigami 2.6 */ ScrollablePage { id: page /** * An object with the same shape of KAboutData * * For example: * @code * aboutData: { "displayName" : "KirigamiApp", "productName" : "kirigami/app", "componentName" : "kirigamiapp", "shortDescription" : "A Kirigami example", "homepage" : "", "bugAddress" : "submit@bugs.kde.org", "version" : "5.14.80", "otherText" : "", "authors" : [ { "name" : "...", "task" : "", "emailAddress" : "somebody@kde.org", "webAddress" : "", "ocsUsername" : "" } ], "credits" : [], "translators" : [], "licenses" : [ { "name" : "GPL v2", "text" : "long, boring, license text", "spdx" : "GPL-2.0" } ], "copyrightStatement" : "© 2010-2018 Plasma Development Team", "desktopFileName" : "org.kde.kirigamiapp" } @endcode * * @see KAboutData */ property var aboutData title: qsTr("About") Component { id: licencePage ScrollablePage { property alias text: content.text QQC2.TextArea { id: content readOnly: true } } } Component { id: personDelegate RowLayout { height: implicitHeight + (Units.smallSpacing * 2) spacing: Units.smallSpacing * 2 Icon { width: Units.iconSizes.smallMedium height: width source: "user" } QQC2.Label { text: modelData.emailAddress ? qsTr("%1 <%2>").arg(modelData.name).arg(modelData.emailAddress) : modelData.name } } } FormLayout { id: form GridLayout { columns: 2 Layout.fillWidth: true Icon { Layout.rowSpan: 2 Layout.preferredHeight: Units.iconSizes.huge Layout.preferredWidth: height Layout.maximumWidth: page.width / 3; Layout.rightMargin: Units.largeSpacing source: Settings.applicationWindowIcon || page.aboutData.programLogo || page.aboutData.programIconName || page.aboutData.componentName } Heading { Layout.fillWidth: true text: page.aboutData.displayName + " " + page.aboutData.version } Heading { Layout.fillWidth: true level: 2 wrapMode: Text.WordWrap text: page.aboutData.shortDescription } } Separator { Layout.fillWidth: true } Heading { FormData.isSection: true text: qsTr("Copyright") } QQC2.Label { Layout.leftMargin: Units.gridUnit text: aboutData.otherText visible: text.length > 0 } QQC2.Label { Layout.leftMargin: Units.gridUnit text: aboutData.copyrightStatement visible: text.length > 0 } UrlButton { Layout.leftMargin: Units.gridUnit url: aboutData.homepage visible: url.length > 0 } Component { id: licenseLinkButton RowLayout { Layout.leftMargin: Units.smallSpacing QQC2.Label { text: qsTr("License:") } LinkButton { text: modelData.name onClicked: applicationWindow().pageStack.push(licencePage, { text: modelData.text, title: modelData.name } ) } } } Component { id: licenseTextItem RowLayout { Layout.leftMargin: Units.smallSpacing QQC2.Label { text: qsTr("License: %1").arg(modelData.name) } } } Repeater { model: aboutData.licenses delegate: applicationWindow().pageStack ? licenseLinkButton : licenseTextItem } Heading { FormData.isSection: visible text: qsTr("Libraries in use") visible: Settings.information } Repeater { model: Settings.information delegate: QQC2.Label { Layout.leftMargin: Units.gridUnit id: libraries text: modelData } } Heading { Layout.fillWidth: true FormData.isSection: visible text: qsTr("Authors") visible: aboutData.authors.length > 0 } Repeater { model: aboutData.authors delegate: personDelegate } Heading { height: visible ? implicitHeight : 0 FormData.isSection: visible text: qsTr("Credits") visible: repCredits.count > 0 } Repeater { id: repCredits model: aboutData.credits delegate: personDelegate } Heading { height: visible ? implicitHeight : 0 FormData.isSection: visible text: qsTr("Translators") visible: repTranslators.count > 0 } Repeater { id: repTranslators model: aboutData.translators delegate: personDelegate } } } diff --git a/src/controls/AbstractApplicationHeader.qml b/src/controls/AbstractApplicationHeader.qml index 2611b82f..90435efb 100644 --- a/src/controls/AbstractApplicationHeader.qml +++ b/src/controls/AbstractApplicationHeader.qml @@ -1,73 +1,60 @@ /* - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.5 import org.kde.kirigami 2.5 import "private" import "templates" as T /** * An item that can be used as a title for the application. * Scrolling the main page will make it taller or shorter (trough the point of going away) * It's a behavior similar to the typical mobile web browser addressbar * the minimum, preferred and maximum heights of the item can be controlled with * * minimumHeight: default is 0, i.e. hidden * * preferredHeight: default is Units.gridUnit * 1.6 * * maximumHeight: default is Units.gridUnit * 3 * * To achieve a titlebar that stays completely fixed just set the 3 sizes as the same */ T.AbstractApplicationHeader { id: root Theme.inherit: false Theme.textColor: root.parent.Theme.highlightedTextColor Theme.backgroundColor: root.parent.Theme.highlightColor Theme.highlightColor: root.parent.Theme.backgroundColor background: Rectangle { color: Theme.backgroundColor EdgeShadow { id: shadow visible: root.separatorVisible anchors { right: parent.right left: parent.left top: parent.bottom } edge: Qt.TopEdge opacity: (!root.page.header || root.page.header.toString().indexOf("ToolBar") === -1) Behavior on opacity { OpacityAnimator { duration: Units.longDuration easing.type: Easing.InOutQuad } } } Behavior on opacity { OpacityAnimator { duration: Units.longDuration easing.type: Easing.InOutQuad } } } } diff --git a/src/controls/AbstractApplicationItem.qml b/src/controls/AbstractApplicationItem.qml index f1eb0a08..3330ee02 100644 --- a/src/controls/AbstractApplicationItem.qml +++ b/src/controls/AbstractApplicationItem.qml @@ -1,369 +1,356 @@ /* - * Copyright 2017 Marco Martin + * SPDX-FileCopyrightText: 2017 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.5 import QtQuick.Templates 2.0 as T2 import QtQuick.Window 2.2 import "templates/private" import org.kde.kirigami 2.4 import QtGraphicalEffects 1.0 /** * A window that provides some basic features needed for all apps * Use this class only if you need a custom content for your application, * different from the Page Row behavior recommended by the HIG and provided * by ApplicationItem. * It is recommended to use ApplicationItem instead * @see ApplicationItem * * It's usually used as a root QML component for the application. * It provides support for a central page stack, side drawers and * a top ApplicationHeader, as well as basic support for the * Android back button * * Example usage: * @code * import org.kde.kirigami 2.4 as Kirigami * * Kirigami.ApplicationItem { * [...] * globalDrawer: Kirigami.GlobalDrawer { * actions: [ * Kirigami.Action { * text: "View" * iconName: "view-list-icons" * Kirigami.Action { * text: "action 1" * } * Kirigami.Action { * text: "action 2" * } * Kirigami.Action { * text: "action 3" * } * }, * Kirigami.Action { * text: "Sync" * iconName: "folder-sync" * } * ] * } * * contextDrawer: Kirigami.ContextDrawer { * id: contextDrawer * } - * + * * pageStack: PageStack { * ... * } * [...] * } * @endcode * * @inherit QtQuick.Item */ Item { id: root /** * pageStack: StackView * Readonly. * The stack used to allocate the pages and to manage the transitions * between them. * Put a container here, such as QQuickControls PageStack */ property Item pageStack - LayoutMirroring.enabled: Qt.application.layoutDirection == Qt.RightToLeft + LayoutMirroring.enabled: Qt.application.layoutDirection == Qt.RightToLeft LayoutMirroring.childrenInherit: true property alias overlay: overlayRoot Item { anchors.fill: parent parent: root.parent || root z: 999999 Rectangle { z: -1 anchors.fill: parent color: "black" visible: contextDrawer && contextDrawer.modal parent: contextDrawer ? contextDrawer.background.parent.parent : overlayRoot opacity: contextDrawer ? contextDrawer.position * 0.6 : 0 } Rectangle { z: -1 anchors.fill: parent color: "black" visible: globalDrawer && globalDrawer.modal parent: contextDrawer ? globalDrawer.background.parent.parent : overlayRoot opacity: contextDrawer ? globalDrawer.position * 0.6 : 0 } Item { id: overlayRoot z: -1 anchors.fill: parent } Window.onWindowChanged: { if (globalDrawer) { globalDrawer.visible = globalDrawer.drawerOpen; } if (contextDrawer) { contextDrawer.visible = contextDrawer.drawerOpen; } } } /** * copatibility with Applicationwindow */ readonly property Item activeFocusItem: Window.activeFocusItem /** * Shows a little passive notification at the bottom of the app window * lasting for few seconds, with an optional action button. * * @param message The text message to be shown to the user. * @param timeout How long to show the message: * possible values: "short", "long" or the number of milliseconds * @param actionText Text in the action button, if any. * @param callBack A JavaScript function that will be executed when the * user clicks the button. */ function showPassiveNotification(message, timeout, actionText, callBack) { if (!internal.__passiveNotification) { var component = Qt.createComponent("templates/private/PassiveNotification.qml"); internal.__passiveNotification = component.createObject(overlay.parent); } internal.__passiveNotification.showNotification(message, timeout, actionText, callBack); } /** * Hide the passive notification, if any is shown */ function hidePassiveNotification() { if(internal.__passiveNotification) { internal.__passiveNotification.hideNotification(); } } /** * @returns a pointer to this application window * can be used anywhere in the application. */ function applicationWindow() { return root; } /** * header: ApplicationHeader * An item that can be used as a title for the application. * Scrolling the main page will make it taller or shorter (trough the point of going away) * It's a behavior similar to the typical mobile web browser addressbar * the minimum, preferred and maximum heights of the item can be controlled with * * Layout.minimumHeight: default is 0, i.e. hidden * * Layout.preferredHeight: default is Units.gridUnit * 1.6 * * Layout.maximumHeight: default is Units.gridUnit * 3 * * To achieve a titlebar that stays completely fixed just set the 3 sizes as the same */ property Item header onHeaderChanged: header.parent = contentItemRoot /** * footer: ApplicationHeader * An item that can be used as a footer for the application. */ property Item footer onFooterChanged: footer.parent = contentItemRoot /** * controlsVisible: bool * This property controls whether the standard chrome of the app, such * as the Action button, the drawer handles and the application * header should be visible or not. */ property bool controlsVisible: true /** * globalDrawer: OverlayDrawer * The drawer for global actions, that will be opened by sliding from the * left screen edge or by dragging the ActionButton to the right. * It is recommended to use the GlobalDrawer class here */ property OverlayDrawer globalDrawer /** * wideScreen: bool * If true the application is considered to be in "widescreen" mode, such as on desktops or horizontal tablets. * Different styles can have an own logic for deciding this */ property bool wideScreen: width >= Units.gridUnit * 60 /** * contextDrawer: OverlayDrawer * The drawer for context-dependent actions, that will be opened by sliding from the * right screen edge or by dragging the ActionButton to the left. * It is recommended to use the ContextDrawer class here. * The contents of the context drawer should depend from what page is * loaded in the main pageStack * * Example usage: * @code * import org.kde.kirigami 2.4 as Kirigami * * Kirigami.ApplicationItem { * [...] * contextDrawer: Kirigami.ContextDrawer { * id: contextDrawer * } * [...] * } * @endcode * * @code * import org.kde.kirigami 2.4 as Kirigami * * Kirigami.Page { * [...] * contextualActions: [ * Kirigami.Action { * iconName: "edit" * text: "Action text" * onTriggered: { * // do stuff * } * }, * Kirigami.Action { * iconName: "edit" * text: "Action text" * onTriggered: { * // do stuff * } * } * ] * [...] * } * @endcode * * When this page will be the current one, the context drawer will visualize * contextualActions defined as property in that page. */ property OverlayDrawer contextDrawer /** * reachableMode: bool * When true the application is in reachable mode for single hand use. * the whole content of the application is moved down the screen to be * reachable with the thumb. if wideScreen is true, or reachableModeEnabled is false, * tis property has no effect. */ property bool reachableMode: false /** * When true the application will go into reachable mode on pull down */ property bool reachableModeEnabled: true - + MouseArea { parent: contentItem.parent z: -1 anchors.fill: parent onClicked: root.reachableMode = false; visible: root.reachableMode && root.reachableModeEnabled Rectangle { anchors.fill: parent color: Qt.rgba(0, 0, 0, 0.3) opacity: 0.15 Icon { anchors.horizontalCenter: parent.horizontalCenter y: x width: Units.iconSizes.large height: width source: "go-up" } } } /** * contentItem: Item * This property holds the Item of the main part of the Application UI */ default property alias __data: contentItemRoot.data readonly property Item contentItem: Item { id: contentItemRoot parent: root anchors.fill: parent anchors { left: contentItem.parent.left right: contentItem.parent.right topMargin: header && controlsVisible ? header.height : 0 leftMargin: root.globalDrawer && (root.globalDrawer.modal === false) ? root.globalDrawer.contentItem.width * root.globalDrawer.position : 0 rightMargin: root.contextDrawer && root.contextDrawer.modal === false ? root.contextDrawer.contentItem.width * root.contextDrawer.position : 0 } - + transform: Translate { Behavior on y { NumberAnimation { duration: Units.longDuration easing.type: Easing.InOutQuad } } y: root.reachableMode && root.reachableModeEnabled && !root.wideScreen ? root.height/2 : 0 x: root.globalDrawer && root.globalDrawer.modal === true && root.globalDrawer.toString().indexOf("SplitDrawer") === 0 ? root.globalDrawer.contentItem.width * root.globalDrawer.position : 0 } Binding { when: root.header target: root.header property: "y" value: root.header ? -root.header.height : 0 } } //Don't want overscroll in landscape mode onWidthChanged: { if (width > height) { root.reachableMode = false; } } Binding { when: globalDrawer !== undefined && root.visible target: globalDrawer property: "parent" value: overlay } Binding { when: contextDrawer !== undefined && root.visible target: contextDrawer property: "parent" value: overlay } onPageStackChanged: pageStack.parent = contentItem; implicitWidth: Units.gridUnit * 30 implicitHeight: Units.gridUnit * 45 visible: true QtObject { id: internal property Item __passiveNotification } } diff --git a/src/controls/AbstractApplicationWindow.qml b/src/controls/AbstractApplicationWindow.qml index 322fcb97..73e288b3 100644 --- a/src/controls/AbstractApplicationWindow.qml +++ b/src/controls/AbstractApplicationWindow.qml @@ -1,326 +1,313 @@ /* - * Copyright 2015 Marco Martin + * SPDX-FileCopyrightText: 2015 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.5 import QtQuick.Controls 2.0 as QQC2 import "templates/private" import org.kde.kirigami 2.4 import QtGraphicalEffects 1.0 /** * A window that provides some basic features needed for all apps * Use this class only if you need a custom content for your application, * different from the Page Row behavior recommended by the HIG and provided * by ApplicationWindow. * It is recommended to use ApplicationWindow instead * @see ApplicationWindow * * It's usually used as a root QML component for the application. * It provides support for a central page stack, side drawers and * a top ApplicationHeader, as well as basic support for the * Android back button * * Example usage: * @code * import org.kde.kirigami 2.4 as Kirigami * * Kirigami.ApplicationWindow { * [...] * globalDrawer: Kirigami.GlobalDrawer { * actions: [ * Kirigami.Action { * text: "View" * iconName: "view-list-icons" * Kirigami.Action { * text: "action 1" * } * Kirigami.Action { * text: "action 2" * } * Kirigami.Action { * text: "action 3" * } * }, * Kirigami.Action { * text: "Sync" * iconName: "folder-sync" * } * ] * } * * contextDrawer: Kirigami.ContextDrawer { * id: contextDrawer * } - * + * * pageStack: PageStack { * ... * } * [...] * } * @endcode * * @inherit QtQuick.Controls.ApplicationWindow */ QQC2.ApplicationWindow { id: root /** * pageStack: StackView * Readonly. * The stack used to allocate the pages and to manage the transitions * between them. * Put a container here, such as QQuickControls PageStack */ property Item pageStack - LayoutMirroring.enabled: Qt.application.layoutDirection == Qt.RightToLeft + LayoutMirroring.enabled: Qt.application.layoutDirection == Qt.RightToLeft LayoutMirroring.childrenInherit: true /** * Shows a little passive notification at the bottom of the app window * lasting for few seconds, with an optional action button. * * @param message The text message to be shown to the user. * @param timeout How long to show the message: * possible values: "short", "long" or the number of milliseconds * @param actionText Text in the action button, if any. * @param callBack A JavaScript function that will be executed when the * user clicks the button. */ function showPassiveNotification(message, timeout, actionText, callBack) { if (!internal.__passiveNotification) { var component = Qt.createComponent("templates/private/PassiveNotification.qml"); internal.__passiveNotification = component.createObject(overlay.parent); } internal.__passiveNotification.showNotification(message, timeout, actionText, callBack); } /** * Hide the passive notification, if any is shown */ function hidePassiveNotification() { if(internal.__passiveNotification) { internal.__passiveNotification.hideNotification(); } } /** * @returns a pointer to this application window * can be used anywhere in the application. */ function applicationWindow() { return root; } /** * header: ApplicationHeader * An item that can be used as a title for the application. * Scrolling the main page will make it taller or shorter (trough the point of going away) * It's a behavior similar to the typical mobile web browser addressbar * the minimum, preferred and maximum heights of the item can be controlled with * * Layout.minimumHeight: default is 0, i.e. hidden * * Layout.preferredHeight: default is Units.gridUnit * 1.6 * * Layout.maximumHeight: default is Units.gridUnit * 3 * * To achieve a titlebar that stays completely fixed just set the 3 sizes as the same * //FIXME: this should become an actual ApplicationHeader */ //header: undefined /** * controlsVisible: bool * This property controls whether the standard chrome of the app, such * as the Action button, the drawer handles and the application * header should be visible or not. */ property bool controlsVisible: true /** * globalDrawer: OverlayDrawer * The drawer for global actions, that will be opened by sliding from the * left screen edge or by dragging the ActionButton to the right. * It is recommended to use the GlobalDrawer class here */ property OverlayDrawer globalDrawer /** * wideScreen: bool * If true the application is considered to be in "widescreen" mode, such as on desktops or horizontal tablets. * Different styles can have an own logic for deciding this */ property bool wideScreen: width >= Units.gridUnit * 60 /** * contextDrawer: OverlayDrawer * The drawer for context-dependent actions, that will be opened by sliding from the * right screen edge or by dragging the ActionButton to the left. * It is recommended to use the ContextDrawer class here. * The contents of the context drawer should depend from what page is * loaded in the main pageStack * * Example usage: * @code * import org.kde.kirigami 2.4 as Kirigami * * Kirigami.ApplicationWindow { * [...] * contextDrawer: Kirigami.ContextDrawer { * id: contextDrawer * } * [...] * } * @endcode * * @code * import org.kde.kirigami 2.4 as Kirigami * * Kirigami.Page { * [...] * contextualActions: [ * Kirigami.Action { * iconName: "edit" * text: "Action text" * onTriggered: { * // do stuff * } * }, * Kirigami.Action { * iconName: "edit" * text: "Action text" * onTriggered: { * // do stuff * } * } * ] * [...] * } * @endcode * * When this page will be the current one, the context drawer will visualize * contextualActions defined as property in that page. */ property OverlayDrawer contextDrawer /** * reachableMode: bool * When true the application is in reachable mode for single hand use. * the whole content of the application is moved down the screen to be * reachable with the thumb. if wideScreen is true, or reachableModeEnabled is false, * tis property has no effect. */ property bool reachableMode: false /** * When true the application will go into reachable mode on pull down */ property bool reachableModeEnabled: true color: Theme.backgroundColor MouseArea { parent: contentItem.parent z: -1 anchors.fill: parent onClicked: root.reachableMode = false; visible: root.reachableMode && root.reachableModeEnabled Rectangle { anchors.fill: parent color: Qt.rgba(0, 0, 0, 0.3) opacity: 0.15 Icon { anchors.horizontalCenter: parent.horizontalCenter y: x width: Units.iconSizes.large height: width source: "go-up" } } } contentItem.anchors.left: contentItem.parent.left contentItem.anchors.right: contentItem.parent.right contentItem.anchors.topMargin: root.wideScreen && header && controlsVisible ? header.height : 0 contentItem.anchors.leftMargin: root.globalDrawer && (root.globalDrawer.modal === false) ? root.globalDrawer.width * root.globalDrawer.position : 0 contentItem.anchors.rightMargin: root.contextDrawer && root.contextDrawer.modal === false ? root.contextDrawer.width * root.contextDrawer.position : 0 Binding { when: menuBar !== undefined target: menuBar property: "x" value: -contentItem.x } Binding { when: header !== undefined target: header property: "x" value: -contentItem.x } Binding { when: footer !== undefined target: footer property: "x" value: -contentItem.x } contentItem.transform: Translate { Behavior on y { NumberAnimation { duration: Units.longDuration easing.type: Easing.InOutQuad } } y: root.reachableMode && root.reachableModeEnabled && !root.wideScreen ? root.height/2 : 0 x: root.globalDrawer && root.globalDrawer.modal === true && root.globalDrawer.toString().indexOf("SplitDrawer") === 0 ? root.globalDrawer.contentItem.width * root.globalDrawer.position : 0 } //Don't want overscroll in landscape mode onWidthChanged: { if (width > height) { root.reachableMode = false; } } Binding { when: globalDrawer !== undefined && root.visible target: globalDrawer property: "parent" value: overlay } Binding { when: contextDrawer !== undefined && root.visible target: contextDrawer property: "parent" value: overlay } onPageStackChanged: pageStack.parent = contentItem; width: Units.gridUnit * 30 height: Units.gridUnit * 45 visible: true QtObject { id: internal property Item __passiveNotification } Shortcut { sequence: StandardKey.Quit onActivated: root.close() } } diff --git a/src/controls/AbstractCard.qml b/src/controls/AbstractCard.qml index d9d331d7..7f35422f 100644 --- a/src/controls/AbstractCard.qml +++ b/src/controls/AbstractCard.qml @@ -1,61 +1,48 @@ /* - * Copyright 2018 Marco Martin + * SPDX-FileCopyrightText: 2018 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.6 import QtGraphicalEffects 1.0 import org.kde.kirigami 2.4 as Kirigami import "templates" as T import "private" /** * A AbstractCard is the base for cards. A Card is a visual object that serves * as an entry point for more detailed information. An abstractCard is empty, * providing just the look and the base properties and signals for an ItemDelegate. * It can be filled with any custom layout of items, its content is organized * in 3 properties: header, contentItem and footer. * Use this only when you need particular custom contents, for a standard layout * for cards, use the Card component. * * @see Card * @inherits T.AbstractCard * @since 2.4 */ T.AbstractCard { id: root background: DefaultCardBackground { Rectangle { anchors.fill: parent color: Kirigami.Theme.highlightColor opacity: { if (root.showClickFeedback || highlighted) { return (root.highlighted || root.down) ? 0.3 : (root.hovered ? 0.1 : 0); } else { return 0; } } Behavior on opacity { OpacityAnimator { duration: Kirigami.Units.longDuration easing.type: Easing.InOutQuad } } } } } diff --git a/src/controls/AbstractItemViewHeader.qml b/src/controls/AbstractItemViewHeader.qml index 0ba4f52e..86da18c0 100644 --- a/src/controls/AbstractItemViewHeader.qml +++ b/src/controls/AbstractItemViewHeader.qml @@ -1,49 +1,36 @@ /* - * Copyright 2017 Marco Martin + * SPDX-FileCopyrightText: 2017 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.5 import QtQuick.Templates 2.0 as T2 import org.kde.kirigami 2.4 as Kirigami /** * An item that can be used as an header for a ListView. * It will play nice with the margin policies of ScrollablePage and can * automatically shrink when the list is scrolled, like the behavior * of list headers in many mobile applications. * @since 2.1 * @inherit QtQuick.Controls.Control */ T2.Control { property int minimumHeight: Kirigami.Units.gridUnit * 2 + Kirigami.Units.smallSpacing * 2 property int maximumHeight: Kirigami.Units.gridUnit * 6 property ListView view: ListView.view width: view.width implicitHeight: topPadding + bottomPadding + (view.headerPositioning === ListView.InlineHeader ? maximumHeight : Math.min(maximumHeight, Math.max(minimumHeight, maximumHeight - Math.max(0, view.contentY)))) z: 9 topPadding: applicationWindow() && !applicationWindow().wideScreen && applicationWindow().header ? applicationWindow().header.paintedHeight : 0 rightPadding: Kirigami.Units.gridUnit } diff --git a/src/controls/AbstractListItem.qml b/src/controls/AbstractListItem.qml index 8f907d1d..f5b87dd4 100644 --- a/src/controls/AbstractListItem.qml +++ b/src/controls/AbstractListItem.qml @@ -1,35 +1,22 @@ /* - * Copyright 2015 Marco Martin + * SPDX-FileCopyrightText: 2015 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.1 import "private" import "templates" as T /** * An item delegate for the primitive ListView component. * * It's intended to make all listviews look coherent. * * @inherit QtQuick.Item */ T.AbstractListItem { id: listItem background: DefaultListItemBackground {} } diff --git a/src/controls/Action.qml b/src/controls/Action.qml index 66adebff..965a1a97 100644 --- a/src/controls/Action.qml +++ b/src/controls/Action.qml @@ -1,185 +1,172 @@ /* - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.7 import QtQuick.Controls 2.4 as Controls import "private" /** * An item that represents an abstract Action * * @inherit QtQuick.Controls.Action */ Controls.Action { id: root /** * Hints for implementations using Actions indicating preferences about how to display the action. */ enum DisplayHint { /** * Indicates there is no specific preference. */ NoPreference = 0, /** * Only display an icon for this Action. */ IconOnly = 1, /** * Try to keep the action visible even when space constrained. * Mutually exclusive with AlwaysHide, KeepVisible has priority. */ KeepVisible = 2, /** * If possible, hide the action in an overflow menu or similar location. * Mutually exclusive with KeepVisible, KeepVisible has priority. */ AlwaysHide = 4, /** * When this action has children, do not display any indicator (like a * menu arrow) for this action. */ HideChildIndicator = 8 } /** * visible: bool * True (default) when the graphic representation of the action * is supposed to be visible. * It's up to the action representation to honor this property. */ property bool visible: true /** * iconName: string * Sets the icon name for the action. This will pick the icon with the given name from the current theme. */ property alias iconName: root.icon.name /** * iconSource: string * Sets the icon file or resource url for the action. Defaults to the empty URL. Use this if you want a specific file rather than an icon from the theme */ property alias iconSource: root.icon.source /** * A tooltip text to be shown when hovering the control bound to this action. Not all controls support tooltips on all platforms */ property string tooltip /** * children: list * A list of children actions. * Useful for tree-like menus * @code * Action { * text: "Tools" * Action { * text: "Action1" * } * Action { * text: "Action2" * } * } * @endcode */ /** * separator: bool * Whether the action is is a separator action; defaults to false. */ property bool separator: false /** * expandible: bool * When true, actions in globalDrawers and contextDrawers will become titles displaying te child actions as sub items * @since 2.6 */ property bool expandible: false property Controls.Action parent /** * displayHint: int * * A combination of values from the Action.DisplayHint enum. These are provided to implementations to indicate * a preference for certain display styles. The default is DisplayHint.NoPreference. * * Note that these are only preferences, implementations may choose to disregard them. * * @since 2.12 */ property int displayHint: Action.DisplayHint.NoPreference /** * Helper function to check if a certain display hint has been set. * * This function is mostly convenience to enforce the mutual exclusivity of KeepVisible and AlwaysHide. * * @param hint The display hint to check if it is set. * * @return true if the hint was set for this action, false if not. * * @since 2.12 */ function displayHintSet(hint) { if (hint === Action.DisplayHint.AlwaysHide && (displayHint & Action.DisplayHint.KeepVisible)) { return false; } return (displayHint & hint) } /** * A Component that should be preferred when displaying this Action. * * @since 5.65 * @since 2.12 */ property Component displayComponent: null default property alias children: root.__children property list __children onChildrenChanged: { var child; for (var i in children) { child = children[i]; if (child.hasOwnProperty("parent")) { child.parent = root } } } /** * visibleChildren: list * All child actions that are visible */ readonly property var visibleChildren: { var visible = []; var child; for (var i in children) { child = children[i]; if (!child.hasOwnProperty("visible") || child.visible) { visible.push(child) } } return visible; } } diff --git a/src/controls/ActionTextField.qml b/src/controls/ActionTextField.qml index a60787e0..67b2f70d 100644 --- a/src/controls/ActionTextField.qml +++ b/src/controls/ActionTextField.qml @@ -1,154 +1,141 @@ /* - * Copyright (C) 2019 Carl-Lucien Schwan * + * SPDX-FileCopyrightText: 2019 Carl-Lucien Schwan * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library/Lesser General Public License - * version 2, or (at your option) any later version, as published by the - * Free Software Foundation - * - * This program 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 General Public License for more details - * - * You should have received a copy of the GNU Library/Lesser General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.6 import QtQuick.Controls 2.1 as Controls import org.kde.kirigami 2.7 as Kirigami /** * This is advanced textfield. It is recommanded to us this class when there * is a need to create a create a textfield with action buttons(e.g a clear * action). * * Example usage for a search field: * @code * import org.kde.kirigami 2.7 as Kirigami * * Kirigami.ActionTextField { * id: searchField * * placeholderText: i18n("Search...") * * focusSequence: "Ctrl+F" * * rightActions: [ * Kirigami.Action { * iconName: "edit-clear" * visible: searchField.text !== "" * onTriggered: { * searchField.text = "" * searchField.accepted() * } * } * ] * * onAccepted: console.log("Search text is " + searchField.text) * } * @endcode * * @inherit QtQuick.Controls.TextField */ Controls.TextField { id: root /** * focusSequence: keysequence * This property hold the a list of shortcut sequence that put the text * field into focus. */ property string focusSequence /** * leftActions: list * This propery hold the action that is left in the field. By default this * field is null. */ property list leftActions /** * rightActions: list * This propery hold the action that is right in the field. By default this * field is null. */ property list rightActions hoverEnabled: true leftPadding: leftActionsRow.width rightPadding: rightActionsRow.width Shortcut { id: focusShortcut enabled: root.focusSequence sequence: root.focusSequence onActivated: { root.forceActiveFocus() root.selectAll() } } Controls.ToolTip { visible: root.focusSequence && root.text.length === 0 && !rightActionsRow.hovered && !leftActionsRow.hovered && hovered text: root.focusSequence ? root.focusSequence : "" } Row { id: leftActionsRow padding: Kirigami.Units.smallSpacing anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter height: root.implicitHeight - 2 * Kirigami.Units.smallSpacing Repeater { model: root.leftActions Kirigami.Icon { height: parent.height width: parent.height anchors.verticalCenter: parent.verticalCenter source: modelData.icon.name.length > 0 ? modelData.icon.name : modelData.icon.source visible: modelData.visible MouseArea { anchors.fill: parent onClicked: modelData.trigger() cursorShape: Qt.PointingHandCursor } } } } Row { id: rightActionsRow padding: Kirigami.Units.smallSpacing layoutDirection: Qt.RightToLeft anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter height: root.implicitHeight - 2 * Kirigami.Units.smallSpacing Repeater { model: root.rightActions Kirigami.Icon { height: parent.height width: parent.height anchors.verticalCenter: parent.verticalCenter source: modelData.icon.name.length > 0 ? modelData.icon.name : modelData.icon.source active: actionArea.containsPress visible: modelData.visible MouseArea { id: actionArea anchors.fill: parent onClicked: modelData.trigger() cursorShape: Qt.PointingHandCursor } } } } } diff --git a/src/controls/ActionToolBar.qml b/src/controls/ActionToolBar.qml index 52816fb4..1bb572c5 100644 --- a/src/controls/ActionToolBar.qml +++ b/src/controls/ActionToolBar.qml @@ -1,228 +1,215 @@ /* - * Copyright 2018 Marco Martin + * SPDX-FileCopyrightText: 2018 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.7 import QtQuick.Layouts 1.2 import QtQuick.Controls 2.4 as Controls import org.kde.kirigami 2.5 as Kirigami import "private" /** * This is a simple toolbar built out of a list of actions * each action is represented by a ToolButton, those that won't fit * the size will go in a menu under a button with the overflow ... icon * * @inherits Item * @since 2.5 */ Item { id: root /** * actions: list * if the card should provide clickable actions, put them in this property, * they will be put in the footer as a list of ToolButtons plus an optional * overflow menu, when not all of them will fit in the available Card width. */ property list actions /** * actions: hiddenActions * This list of actions is for those you always want in the menu, even if there * is enough space. * @since 2.6 */ property list hiddenActions /** * flat: bool * Wether we want our buttons to have a flat appearance. Default: true */ property bool flat: true /** * display: enum * This controls the label position regarding the icon, is the same value to control individual Button components, * permitted values are: * * Button.IconOnly * * Button.TextOnly * * Button.TextBesideIcon * * Button.TextUnderIcon */ property int display: Controls.Button.TextBesideIcon property int alignment: Qt.AlignLeft /** * position enum * This property holds the position of the toolbar. * if this ActionToolBar is the contentItem of a QQC2 Toolbar, the position is binded to the ToolBar's position * * permitted values are: * *ToolBar.Header: The toolbar is at the top, as a window or page header. * *ToolBar.Footer: The toolbar is at the bottom, as a window or page footer. */ property int position: parent && parent.hasOwnProperty("position") ? parent.position : Controls.ToolBar.Header /** * The maximum width of the contents of this ToolBar. If the toolbar's width is larger than this value, empty space will * be added on the sides, according to the Alignment property. * * The value of this property is derived from the ToolBar's actions and their properties. */ readonly property alias maximumContentWidth: details.maximumWidth /** * The name of the icon to use for the overflow menu button. * * @since 5.65 * @since 2.12 */ property string overflowIconName: "overflow-menu" implicitHeight: actionsLayout.implicitHeight implicitWidth: actionsLayout.implicitWidth Layout.minimumWidth: moreButton.visible ? moreButton.implicitWidth : 0 RowLayout { id: actionsLayout anchors.fill: parent spacing: 0 Item { Layout.fillWidth: root.alignment == Qt.AlignRight || root.alignment == Qt.AlignHCenter || root.alignment == Qt.AlignCenter; Layout.fillHeight: true } Repeater { model: root.actions delegate: Loader { id: delegate Layout.alignment: Qt.AlignVCenter // Use leftMargin instead of spacing on the layout to prevent spacer items // from creating useless spacing, only for items that are actually next to // other items. Layout.leftMargin: index > 0 ? Kirigami.Units.smallSpacing : 0 Layout.fillWidth: item ? item.Layout.fillWidth : false Layout.minimumWidth: item ? item.Layout.minimumWidth : implicitWidth Layout.preferredWidth: item ? item.Layout.preferredWidth : implicitWidth Layout.maximumWidth: item ? item.Layout.maximumWidth : -1 property var kirigamiAction: modelData sourceComponent: { if (modelData.displayComponent && !modelData.displayHintSet(Action.DisplayHint.IconOnly)) { return modelData.displayComponent } return toolButtonDelegate } visible: details.visibleActions.indexOf(modelData) != -1 && (modelData.visible === undefined || modelData.visible) onLoaded: { if (sourceComponent == toolButtonDelegate) { item.kirigamiAction = modelData } } } } Item { Layout.fillWidth: root.alignment == Qt.AlignLeft || root.alignment == Qt.AlignHCenter || root.alignment == Qt.AlignCenter; Layout.fillHeight: true } PrivateActionToolButton { id: moreButton Layout.alignment: Qt.AlignRight visible: { // Only show the overflow button when we actually have visible actions in the menu, // otherwise we end up showing an overflow button that shows nothing. var visibleCount = Array.prototype.reduce.call(kirigamiAction.children, function (total, current) { return (details.visibleActions.indexOf(current) == -1 && (current.visible === undefined || current.visible)) ? total + 1 : total }, 0); return visibleCount > 0 } kirigamiAction: Kirigami.Action { icon.name: root.overflowIconName displayHint: Kirigami.Action.DisplayHint.IconOnly | Kirigami.Action.DisplayHint.HideChildIndicator children: Array.prototype.map.call(root.actions, function (i) { return i }).concat(Array.prototype.map.call(hiddenActions, function (i) { return i })) } menu.submenuComponent: ActionsMenu { Binding { target: parentItem property: "visible" value: details.visibleActions.indexOf(parentAction) == -1 && (parentAction.visible === undefined || parentAction.visible) } } menu.itemDelegate: ActionMenuItem { visible: details.visibleActions.indexOf(action) == -1 && (action.visible === undefined || action.visible) } menu.loaderDelegate: Loader { property var kirigamiAction height: visible ? implicitHeight : 0 visible: details.visibleActions.indexOf(kirigamiAction) == -1 && (kirigamiAction.visible === undefined || kirigamiAction.visible) } } } ActionToolBarLayoutDetails { id: details anchors.fill: parent actions: root.actions rightPadding: moreButton.width + Kirigami.Units.smallSpacing flat: root.flat display: root.display } Component { id: toolButtonDelegate PrivateActionToolButton { id: button flat: root.flat && !kirigamiAction.icon.color.a display: details.iconOnlyActions.indexOf(kirigamiAction) != -1 ? Controls.Button.IconOnly : root.display menu.actions: { if (kirigamiAction.displayComponent && kirigamiAction.displayHintSet(Kirigami.Action.DisplayHint.IconOnly)) { kirigamiAction.displayHint |= Kirigami.Action.DisplayHint.HideChildIndicator return [kirigamiAction] } if (kirigamiAction.children) { return kirigamiAction.children } return [] } } } } diff --git a/src/controls/ApplicationHeader.qml b/src/controls/ApplicationHeader.qml index 914ef756..85dc00fb 100644 --- a/src/controls/ApplicationHeader.qml +++ b/src/controls/ApplicationHeader.qml @@ -1,37 +1,24 @@ /* - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.5 import "templates" as T /** * An item that can be used as a title for the application. * Scrolling the main page will make it taller or shorter (trough the point of going away) * It's a behavior similar to the typical mobile web browser addressbar * the minimum, preferred and maximum heights of the item can be controlled with * * minimumHeight: default is 0, i.e. hidden * * preferredHeight: default is Units.gridUnit * 1.6 * * maximumHeight: default is Units.gridUnit * 3 * * To achieve a titlebar that stays completely fixed just set the 3 sizes as the same */ T.ApplicationHeader { id: header } diff --git a/src/controls/ApplicationItem.qml b/src/controls/ApplicationItem.qml index 3def421a..af47882a 100644 --- a/src/controls/ApplicationItem.qml +++ b/src/controls/ApplicationItem.qml @@ -1,180 +1,167 @@ /* - * Copyright 2017 Marco Martin + * SPDX-FileCopyrightText: 2017 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.5 import "templates/private" import org.kde.kirigami 2.4 as Kirigami import QtGraphicalEffects 1.0 /** * A window that provides some basic features needed for all apps * This version is an Item as opposed to a window, it's intended * for use into a QQuickView * * It's usually used as a root QML component for the application. * It's based around the PageRow component, the application will be * about pages adding and removal. * For most of the usages, this class should be used instead * of AbstractApplicationWindow * @see AbstractApplicationWindow * * Example usage: * @code * import org.kde.kirigami 2.4 as Kirigami * * Kirigami.ApplicationItem { * [...] * globalDrawer: Kirigami.GlobalDrawer { * actions: [ * Kirigami.Action { * text: "View" * iconName: "view-list-icons" * Kirigami.Action { * text: "action 1" * } * Kirigami.Action { * text: "action 2" * } * Kirigami.Action { * text: "action 3" * } * }, * Kirigami.Action { * text: "Sync" * iconName: "folder-sync" * } * ] * } * * contextDrawer: Kirigami.ContextDrawer { * id: contextDrawer * } - * + * * pageStack.initialPage: Kirigami.Page { * mainAction: Kirigami.Action { * iconName: "edit" * onTriggered: { * // do stuff * } * } * contextualActions: [ * Kirigami.Action { * iconName: "edit" * text: "Action text" * onTriggered: { * // do stuff * } * }, * Kirigami.Action { * iconName: "edit" * text: "Action text" * onTriggered: { * // do stuff * } * } * ] * [...] * } * [...] * } * @endcode * */ AbstractApplicationItem { id: root /** * pageStack: StackView * Readonly. * The stack used to allocate the pages and to manage the transitions * between them. * It's using a PageRow, while having the same API as PageStack, * it positions the pages as adjacent columns, with as many columns * as can fit in the screen. An handheld device would usually have a single * fullscreen column, a tablet device would have many tiled columns. */ property alias pageStack: __pageStack //redefines here as here we can know a pointer to PageRow wideScreen: width >= applicationWindow().pageStack.defaultColumnWidth*2 Component.onCompleted: { if (pageStack.currentItem) { pageStack.currentItem.forceActiveFocus() } } PageRow { id: __pageStack anchors { fill: parent //HACK: workaround a bug in android iOS keyboard management bottomMargin: ((Qt.platform.os == "android" || Qt.platform.os == "ios") || !Qt.inputMethod.visible) ? 0 : Qt.inputMethod.keyboardRectangle.height onBottomMarginChanged: { if (bottomMargin > 0) { root.reachableMode = false; } } } //FIXME onCurrentIndexChanged: root.reachableMode = false; function goBack() { //NOTE: drawers are handling the back button by themselves var backEvent = {accepted: false} if (root.pageStack.currentIndex >= 1) { root.pageStack.currentItem.backRequested(backEvent); if (!backEvent.accepted) { root.pageStack.flickBack(); backEvent.accepted = true; } } if (Kirigami.Settings.isMobile && !backEvent.accepted && Qt.platform.os !== "ios") { Qt.quit(); } } function goForward() { root.pageStack.currentIndex = Math.min(root.pageStack.depth-1, root.pageStack.currentIndex + 1); } Keys.onBackPressed: { goBack(); event.accepted = true } Shortcut { sequence: "Forward" onActivated: __pageStack.goForward(); } Shortcut { sequence: StandardKey.Forward onActivated: __pageStack.goForward(); } Shortcut { sequence: StandardKey.Back onActivated: __pageStack.goBack(); } Rectangle { z: -1 anchors.fill: parent color: Kirigami.Theme.backgroundColor } focus: true } } diff --git a/src/controls/ApplicationWindow.qml b/src/controls/ApplicationWindow.qml index d6164974..c04229d0 100644 --- a/src/controls/ApplicationWindow.qml +++ b/src/controls/ApplicationWindow.qml @@ -1,182 +1,169 @@ /* - * Copyright 2015 Marco Martin + * SPDX-FileCopyrightText: 2015 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.5 import "templates/private" import org.kde.kirigami 2.4 as Kirigami import QtGraphicalEffects 1.0 /** * A window that provides some basic features needed for all apps * * It's usually used as a root QML component for the application. * It's based around the PageRow component, the application will be * about pages adding and removal. * For most of the usages, this class should be used instead * of AbstractApplicationWidnow * @see AbstractApplicationWidnow * * Example usage: * @code * import org.kde.kirigami 2.4 as Kirigami * * Kirigami.ApplicationWindow { * [...] * globalDrawer: Kirigami.GlobalDrawer { * actions: [ * Kirigami.Action { * text: "View" * iconName: "view-list-icons" * Kirigami.Action { * text: "action 1" * } * Kirigami.Action { * text: "action 2" * } * Kirigami.Action { * text: "action 3" * } * }, * Kirigami.Action { * text: "Sync" * iconName: "folder-sync" * } * ] * } * * contextDrawer: Kirigami.ContextDrawer { * id: contextDrawer * } - * + * * pageStack.initialPage: Kirigami.Page { * mainAction: Kirigami.Action { * iconName: "edit" * onTriggered: { * // do stuff * } * } * contextualActions: [ * Kirigami.Action { * iconName: "edit" * text: "Action text" * onTriggered: { * // do stuff * } * }, * Kirigami.Action { * iconName: "edit" * text: "Action text" * onTriggered: { * // do stuff * } * } * ] * [...] * } * [...] * } * @endcode * */ AbstractApplicationWindow { id: root /** * pageStack: StackView * Readonly. * The stack used to allocate the pages and to manage the transitions * between them. * It's using a PageRow, while having the same API as PageStack, * it positions the pages as adjacent columns, with as many columns * as can fit in the screen. An handheld device would usually have a single * fullscreen column, a tablet device would have many tiled columns. */ property alias pageStack: __pageStack //redefines here as here we can know a pointer to PageRow wideScreen: width >= applicationWindow().pageStack.defaultColumnWidth * 1.5 Component.onCompleted: { if (pageStack.currentItem) { pageStack.currentItem.forceActiveFocus() } } PageRow { id: __pageStack globalToolBar.style: Kirigami.ApplicationHeaderStyle.Auto anchors { fill: parent //HACK: workaround a bug in android iOS keyboard management bottomMargin: ((Qt.platform.os == "android" || Qt.platform.os == "ios") || !Qt.inputMethod.visible) ? 0 : Qt.inputMethod.keyboardRectangle.height onBottomMarginChanged: { if (__pageStack.anchors.bottomMargin > 0) { root.reachableMode = false; } } } //FIXME onCurrentIndexChanged: root.reachableMode = false; function goBack() { //NOTE: drawers are handling the back button by themselves var backEvent = {accepted: false} if (root.pageStack.layers.depth > 1) { root.pageStack.layers.currentItem.backRequested(backEvent); if (!backEvent.accepted) { root.pageStack.layers.pop(); backEvent.accepted = true; } } else { root.pageStack.currentItem.backRequested(backEvent); if (root.pageStack.currentIndex >= 1) { if (!backEvent.accepted) { root.pageStack.flickBack(); backEvent.accepted = true; } } } if (Kirigami.Settings.isMobile && !backEvent.accepted && Qt.platform.os !== "ios") { Qt.quit(); } } function goForward() { root.pageStack.currentIndex = Math.min(root.pageStack.depth-1, root.pageStack.currentIndex + 1); } Keys.onBackPressed: { goBack(); event.accepted = true } Shortcut { sequence: "Forward" onActivated: __pageStack.goForward(); } Shortcut { sequence: StandardKey.Forward onActivated: __pageStack.goForward(); } Shortcut { sequence: StandardKey.Back onActivated: __pageStack.goBack(); } focus: true } } diff --git a/src/controls/BasicListItem.qml b/src/controls/BasicListItem.qml index e7142f5e..67f95eb7 100644 --- a/src/controls/BasicListItem.qml +++ b/src/controls/BasicListItem.qml @@ -1,115 +1,102 @@ /* - * Copyright 2010 Marco Martin + * SPDX-FileCopyrightText: 2010 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.1 import QtQuick.Layouts 1.2 import QtQuick.Controls 2.0 as QQC2 import org.kde.kirigami 2.4 /** * An item delegate for the primitive ListView component. * * It's intended to make all listviews look coherent. * It has a default icon and a label * */ AbstractListItem { id: listItem /** * string: bool * A single text label the list item will contain */ property alias label: listItem.text /** * icon: var * A single icon that will be displayed in the list item. * The icon can be a grouped property with name,size,color etc, as QtQuickControls2 icons are defined. * The icon can also be either a QIcon, a string name of a fdo compatible name, * or any url accepted by the Image element. */ property var icon /** * iconSize: int * The preferred size for the icon * @since 2.5 */ property alias iconSize: iconItem.size /** * iconColor: color * The color the icon should be colorized to. * By default it will be the text color. * If the icon shouldn't be colorized in any way, set it to "transparent" * @since 2.7 */ property alias iconColor: iconItem.color /** * reserveSpaceForIcon: bool * If true, even when there is no icon the space will be reserved for it * It's useful in layouts where only some entries have an icon, * having the text all horizontally aligned */ property alias reserveSpaceForIcon: iconItem.visible /** * reserveSpaceForLabel: bool * If false, the label will not try to be as wide as possible * It's useful in layouts containing entries without text */ property alias reserveSpaceForLabel: labelItem.visible default property alias _basicDefault: layout.data icon: action ? action.icon.name || action.icon.source : undefined contentItem: RowLayout { id: layout spacing: LayoutMirroring.enabled ? listItem.rightPadding : listItem.leftPadding Icon { id: iconItem source: { if (listItem.icon && listItem.icon.hasOwnProperty) { if (listItem.icon.hasOwnProperty("name") && listItem.icon.name !== "") return listItem.icon.name; if (listItem.icon.hasOwnProperty("source")) return listItem.icon.source; } return listItem.icon; } property int size: Units.iconSizes.smallMedium Layout.minimumHeight: size Layout.maximumHeight: size Layout.minimumWidth: size selected: (listItem.highlighted || listItem.checked || (listItem.pressed && listItem.supportsMouseEvents)) opacity: 1 } QQC2.Label { id: labelItem text: listItem.text Layout.fillWidth: true color: (listItem.highlighted || listItem.checked || (listItem.pressed && listItem.supportsMouseEvents)) ? listItem.activeTextColor : listItem.textColor elide: Text.ElideRight font: listItem.font opacity: 1 } } } diff --git a/src/controls/Card.qml b/src/controls/Card.qml index a3aea39b..d511269b 100644 --- a/src/controls/Card.qml +++ b/src/controls/Card.qml @@ -1,109 +1,96 @@ /* - * Copyright 2018 Marco Martin + * SPDX-FileCopyrightText: 2018 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.6 import QtQuick.Layouts 1.2 import QtQuick.Controls 2.0 as Controls import org.kde.kirigami 2.5 as Kirigami import "private" /** * This is the standard layout of a Card. * It is recommended to use this class when the concept of Cards is needed * in the application. * This Card has default items as header and footer. The header is an * image that can contain an optional title and icon, accessible via the * banner grouped property. * The footer will show a series of toolbuttons (and eventual overflow menu) * representing the actions list accessible with the list property actions. * It is possible even tough is discouraged to override the footer: * in this case the actions property shouldn't be used. * * @inherits AbstractCard * @since 2.4 */ Kirigami.AbstractCard { id: root /** * actions: list * if the card should provide clickable actions, put them in this property, * they will be put in the footer as a list of ToolButtons plus an optional * overflow menu, when not all of them will fit in the available Card width. */ property list actions /** * actions: hiddenActions * This list of actions is for those you always want in the menu, even if there * is enough space. * @since 2.6 */ property list hiddenActions /** * banner: Image * Gropuped property to control the banner image present in the header, it * has the following sub properties: * * url source: the source for the image, it understands any url * valid for an Image component * * string title: the title for the banner, shown as contrasting * text over the image * * Qt.Alignment titleAlignment: the alignment of the title inside the image, * a combination of flags is supported * (default: Qt.AlignTop | Qt.AlignLeft) * * string titleIcon: the optional icon to put in the banner: * it can be either a freedesktop-compatible icon name (recommended) * or any url supported by Image * * titleLevel: The Kirigami Heading level for the title, it controls the font size, default 1 * * wrapMode: if the header should be able to do wrapping * * It also has the full set of properties a QML Image has, such as sourceSize and fillMode */ readonly property alias banner: bannerImage header: BannerImage { id: bannerImage anchors.leftMargin: -root.leftPadding anchors.topMargin: -root.topPadding anchors.rightMargin: root.headerOrientation == Qt.Vertical ? -root.rightPadding : 0 anchors.bottomMargin: root.headerOrientation == Qt.Horizontal ? -root.bottomPadding : 0 //height: Layout.preferredHeight implicitWidth: root.headerOrientation == Qt.Horizontal ? sourceSize.width : Layout.preferredWidth } onHeaderChanged: { if (!header) { return; } header.anchors.leftMargin = Qt.binding(function() {return -root.leftPadding}); header.anchors.topMargin = Qt.binding(function() {return -root.topPadding}); header.anchors.rightMargin = Qt.binding(function() {return root.headerOrientation == Qt.Vertical ? -root.rightPadding : 0}); header.anchors.bottomMargin = Qt.binding(function() {return root.headerOrientation == Qt.Horizontal ? -root.bottomPadding : 0}); } footer: Kirigami.ActionToolBar { id: actionsToolBar actions: root.actions position: Controls.ToolBar.Footer hiddenActions: root.hiddenActions visible: root.footer == actionsToolBar } } diff --git a/src/controls/CardsGridView.qml b/src/controls/CardsGridView.qml index 43ccdc15..125038ce 100644 --- a/src/controls/CardsGridView.qml +++ b/src/controls/CardsGridView.qml @@ -1,88 +1,75 @@ /* - * Copyright 2018 Marco Martin + * SPDX-FileCopyrightText: 2018 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.10 import QtQuick.Controls 2.0 as Controls import QtQuick.Layouts 1.2 import org.kde.kirigami 2.4 as Kirigami import "private" /** * CardsGridView is used to display a grid of Cards generated from any model. * The behavior is same as CardsLayout, and it allows cards to be put in one or two * columns depending from the available width. * GridView has the limitation that every Card must have the same exact height, * so cellHeight must be manually set to a value in which the content fits * for every item. * If possible use cards only when you don't need to instantiate a lot * and use CardsLayout intead. * @inherits GridView * @see CardsLayout * @since 2.4 */ CardsGridViewPrivate { id: root /** * columns: int * how many columns the gridview has * @since 2.5 */ readonly property int columns: Math.max(1, Math.min(maximumColumns > 0 ? maximumColumns : Infinity, Math.floor(width/minimumColumnWidth), Math.ceil(width/maximumColumnWidth)) ); /** * maximumColumns: int * Maximum value for columns if the user wants to limit it * @since 2.5 */ property int maximumColumns: Infinity /** * maximumColumnWidth: int * The maximum width the columns may have. the cards will never * get wider than this size, when the GridView is wider than * maximumColumnWidth, it will switch from one to two columns. * If the default needs to be overridden for some reason, * it is advised to express this unit as a multiple * of Kirigami.Units.gridUnit */ property int maximumColumnWidth: Kirigami.Units.gridUnit * 20 /** * minimumColumnWidth: int * The minimum width the columns may have. the cards will never * get smaller than this size. * If the default needs to be overridden for some reason, * it is advised to express this unit as a multiple * of Kirigami.Units.gridUnit * @since 2.5 */ property int minimumColumnWidth: Kirigami.Units.gridUnit * 12 cellWidth: Math.floor(width/columns) cellHeight: Math.max(Kirigami.Units.gridUnit * 15, Math.min(cellWidth, maximumColumnWidth) / 1.2) default property alias delegate: root._delegateComponent topMargin: Kirigami.Units.largeSpacing * 2 } diff --git a/src/controls/CardsLayout.qml b/src/controls/CardsLayout.qml index 80337141..d3a31ebf 100644 --- a/src/controls/CardsLayout.qml +++ b/src/controls/CardsLayout.qml @@ -1,91 +1,78 @@ /* - * Copyright 2018 Marco Martin + * SPDX-FileCopyrightText: 2018 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.6 import QtQuick.Controls 2.0 as Controls import QtQuick.Layouts 1.2 import org.kde.kirigami 2.4 as Kirigami /** * A GridLayout optimized for showing one or two columns of cards, * depending on the available space. * It Should be used when the cards are not instantiated by a model or by a * model which has always very few items (In the case of a big model * CardsListView or CardsGridview should be used instead). * They are presented as a grid of two columns which will remain * centered if the application is really wide, or become a single * column if there is not enough space for two columns, * such as a mobile phone screen. * A CardsLayout should always be contained within a ColumnLayout. * @inherits GridLayout * @since 2.4 */ GridLayout { /** * maximumColumns: int * The layout will never lay out the items in more columns than maximumColumns * Default: 2 * @since 2.5 */ property int maximumColumns: 2 /** * maximumColumnWidth: int * The maximum width the columns may have. the cards will never * get wider than this size, when the GridLayout is wider than * maximumColumnWidth, it will switch from one to two columns. * If the default needs to be overridden for some reason, * it is advised to express this unit as a multiple * of Kirigami.Units.gridUnit */ property int maximumColumnWidth: Kirigami.Units.gridUnit * 20 /** * minimumColumnWidth: int * The minimumWidth the columns may have. The layout will try to dispose items * in a number of columns that will respect this size constraint. * @since 2.5 */ property int minimumColumnWidth: Kirigami.Units.gridUnit * 12 columns: Math.max(1, Math.min(maximumColumns > 0 ? maximumColumns : Infinity, Math.floor(width/minimumColumnWidth), Math.ceil(width/maximumColumnWidth))); rowSpacing: Kirigami.Units.largeSpacing * columns columnSpacing: Kirigami.Units.largeSpacing * columns //NOTE: this default width which defaults to 2 columns is just to remove a binding loop on columns width: maximumColumnWidth*2 + Kirigami.Units.largeSpacing //same computation of columns, but on the parent size Layout.preferredWidth: maximumColumnWidth * Math.max(1, Math.min(maximumColumns > 0 ? maximumColumns : Infinity, Math.floor(parent.width/minimumColumnWidth), Math.ceil(parent.width/maximumColumnWidth))) + Kirigami.Units.largeSpacing * (columns - 1) Layout.maximumWidth: Layout.preferredWidth Layout.alignment: Qt.AlignHCenter Component.onCompleted: childrenChanged() onChildrenChanged: { for (var i = 0; i < children.length; ++i) { children[i].Layout.fillHeight = true; } } } diff --git a/src/controls/CardsListView.qml b/src/controls/CardsListView.qml index d992c39e..73fb3d37 100644 --- a/src/controls/CardsListView.qml +++ b/src/controls/CardsListView.qml @@ -1,47 +1,34 @@ /* - * Copyright 2018 Marco Martin + * SPDX-FileCopyrightText: 2018 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.10 import QtQuick.Controls 2.0 as Controls import QtQuick.Layouts 1.2 import org.kde.kirigami 2.4 as Kirigami import "private" /** * CardsListView is a ListView which can have AbstractCard as its delegate: it will * automatically assign the proper spacings and margins around the cards adhering * to the design guidelines. * CardsListView should be used only with cards which can look good at any - * horizontal size, so It is recommended to use directly AbstractCard with an + * horizontal size, so It is recommended to use directly AbstractCard with an * appropriate layout inside, because they are stretching for the whole list width. * Therefore is discouraged to use it with the Card type, unless it has * Horizontal as headerOrientation. * The choice between using this view with AbstractCard or a normal ListView * with AbstractListItem/BasicListItem is purely a choice based on aesthetics alone. * It is discouraged to tweak the properties of this ListView. * @inherits ListView * @since 2.4 */ CardsListViewPrivate { id: root spacing: Kirigami.Units.largeSpacing * 2 topMargin: headerPositioning != ListView.InlineHeader ? spacing : 0 property alias delegate: root._delegateComponent headerPositioning: ListView.OverlayHeader } diff --git a/src/controls/ContextDrawer.qml b/src/controls/ContextDrawer.qml index a0079ee2..bf62d70b 100644 --- a/src/controls/ContextDrawer.qml +++ b/src/controls/ContextDrawer.qml @@ -1,198 +1,185 @@ /* - * Copyright 2015 Marco Martin + * SPDX-FileCopyrightText: 2015 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.1 import QtQuick.Layouts 1.2 import org.kde.kirigami 2.4 import "private" import "templates/private" /** * A drawer specialization that will show a list of actions that are * specific of the current page shown by the application * * Example usage: * @code * import org.kde.kirigami 2.4 as Kirigami * * Kirigami.ApplicationWindow { * [...] * contextDrawer: Kirigami.ContextDrawer { * id: contextDrawer * } * [...] * } * @endcode * * @code * import org.kde.kirigami 2.4 as Kirigami * * Kirigami.Page { * [...] * contextualActions: [ * Kirigami.Action { * iconName: "edit" * text: "Action text" * onTriggered: { * // do stuff * } * }, * Kirigami.Action { * iconName: "edit" * text: "Action text" * onTriggered: { * // do stuff * } * } * ] * [...] * } * @endcode * * @inherit AbstractDrawer */ OverlayDrawer { id: root handleClosedIcon.source: null handleOpenIcon.source: null /** * title: string * A title for the action list that will be shown to the user when opens the drawer */ property string title: qsTr("Actions") /** * actions: list - * This can be any type of object that a ListView can accept as model. + * This can be any type of object that a ListView can accept as model. * It expects items compatible with either QAction or Kirigami Action */ property var actions: page ? page.contextualActions : [] property Page page: { if (applicationWindow().pageStack.layers && applicationWindow().pageStack.layers.depth > 1 && applicationWindow().pageStack.layers.currentItem.hasOwnProperty("contextualActions")) { return applicationWindow().pageStack.layers.currentItem; } else if (applicationWindow().pageStack.currentItem.hasOwnProperty("contextualActions")) { return applicationWindow().pageStack.currentItem; } else { return applicationWindow().pageStack.lastVisibleItem; } } // Disable for empty menus or when we have a global toolbar enabled: menu.count > 0 && (typeof applicationWindow() === "undefined" || !applicationWindow().pageStack.globalToolBar || (applicationWindow().pageStack.lastVisibleItem && applicationWindow().pageStack.lastVisibleItem.globalToolBarStyle !== ApplicationHeaderStyle.ToolBar)) edge: Qt.application.layoutDirection == Qt.RightToLeft ? Qt.LeftEdge : Qt.RightEdge drawerOpen: false /** * header: Component * Arbitrary content that will go on top of the list (by default is the title) * @since 2.7 */ property alias header: menu.header /** * footer: Component * Arbitrary content that will go on top of the list (by default is empty) * @since 2.7 */ property alias footer: menu.footer //list items go to edges, have their own padding leftPadding: 0 rightPadding: 0 bottomPadding: 0 handleVisible: applicationWindow == undefined ? false : applicationWindow().controlsVisible onPeekingChanged: { if (page) { page.contextualActionsAboutToShow(); } } contentItem: ScrollView { //this just to create the attached property Theme.inherit: true implicitWidth: Units.gridUnit * 20 ListView { id: menu interactive: contentHeight > height model: { if (typeof root.actions == "undefined") { return null; } if (root.actions.length === 0) { return null; } else { // Check if at least one action is visible var somethingVisible = false; for (var i=0; i 0 ? menu.height - menu.contentHeight : 0 header: Item { height: heading.height width: menu.width Heading { id: heading anchors { left: parent.left right: parent.right margins: Units.largeSpacing } elide: Text.ElideRight level: 2 text: root.title } } delegate: Column { width: parent.width ContextDrawerActionItem { width: parent.width } Repeater { model: modelData.hasOwnProperty("expandible") && modelData.expandible ? modelData.children : null delegate: ContextDrawerActionItem { width: parent.width leftPadding: Units.largeSpacing * 2 opacity: !root.collapsed } } } } } } diff --git a/src/controls/FormLayout.qml b/src/controls/FormLayout.qml index 61fbaf95..e0c7cc9c 100644 --- a/src/controls/FormLayout.qml +++ b/src/controls/FormLayout.qml @@ -1,392 +1,379 @@ /* - * Copyright 2017 Marco Martin + * SPDX-FileCopyrightText: 2017 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.6 import QtQuick.Layouts 1.2 import QtQuick.Controls 2.2 import org.kde.kirigami 2.4 as Kirigami /** * This is the base class for Form layouts conforming to the * Kirigami Human interface guidelines. The layout will * be divided in two columns: on the right there will be a column * of fields, on the left their labels specified in the FormData attached * property. * * Example: * @code * import org.kde.kirigami 2.3 as Kirigami * Kirigami.FormLayout { * TextField { * Kirigami.FormData.label: "Label:" * } * Kirigami.Separator { * Kirigami.FormData.label: "Section Title" * Kirigami.FormData.isSection: true * } * TextField { * Kirigami.FormData.label: "Label:" * } * TextField { * } * } * @endcode * @inherits QtQuick.Item * @since 2.3 */ Item { id: root /** * wideMode: bool * If true the layout will be optimized for a wide screen, such as * a desktop machine (the labels will be on a left column, * the fields on a right column beside it), if false (such as on a phone) * everything is laid out in a single column. * by default this will be based on whether the application is * wide enough for the layout of being in such mode. * It can be overridden by reassigning the property */ property bool wideMode: width >= lay.wideImplicitWidth implicitWidth: lay.wideImplicitWidth implicitHeight: lay.implicitHeight Layout.preferredHeight: lay.implicitHeight Component.onCompleted: { relayoutTimer.triggered() } /** * twinFormLayouts: list * If for some implementation reason multiple FormLayouts has to appear * on the same page, they can have each other in twinFormLayouts, * so they will vertically align each other perfectly * @since 5.53 */ //should be list but we can't have a recursive declaration property list twinFormLayouts Layout.fillWidth: true GridLayout { id: lay property int wideImplicitWidth columns: root.wideMode ? 2 : 1 rowSpacing: Kirigami.Units.smallSpacing columnSpacing: Kirigami.Units.smallSpacing property var knownItems: [] property var buddies: [] property int knownItemsImplicitWidth: { var hint = 0; for (var i in knownItems) { hint = Math.max(hint, knownItems[i].Layout.preferredWidth > 0 ? knownItems[i].Layout.preferredWidth : knownItems[i].implicitWidth); } return hint; } property int buddiesImplicitWidth: { var hint = 0; for (var i in buddies) { if (buddies[i].visible) { hint = Math.max(hint, buddies[i].implicitWidth); } } return hint; } states: [ State { when: root.wideMode AnchorChanges { target: lay anchors { left: undefined right: undefined horizontalCenter: root.horizontalCenter } } PropertyChanges { target: lay implicitWidth: undefined width: undefined } }, State { when: !root.wideMode AnchorChanges { target: lay anchors { left: parent.left right: parent.right horizontalCenter: undefined } } PropertyChanges { target: lay implicitWidth: root.width width: Math.min(implicitWidth, parent.width) } - + } ] width: Math.min(implicitWidth, parent.width) Timer { id: hintCompression onTriggered: { if (root.wideMode) { lay.wideImplicitWidth = lay.implicitWidth; } } } onImplicitWidthChanged: hintCompression.restart(); //This invisible row is used to sync alignment between multiple layouts Item { Layout.preferredWidth: { var hint = 1; for (var i in root.twinFormLayouts) { hint = Math.max(hint, root.twinFormLayouts[i].children[0].buddiesImplicitWidth); } return hint; } } Item { Layout.preferredWidth: { var hint = 1; for (var i in root.twinFormLayouts) { hint = Math.max(hint, root.twinFormLayouts[i].children[0].knownItemsImplicitWidth); } return hint; } } } Item { id: temp } Timer { id: relayoutTimer interval: 0 onTriggered: { var __items = children; //exclude the layout and temp for (var i = 2; i < __items.length; ++i) { var item = __items[i]; //skip items that are already there if (lay.knownItems.indexOf(item) != -1 || //exclude Repeaters //NOTE: this is an heuristic but there are't better ways (item.hasOwnProperty("model") && item.model !== undefined && item.children.length === 0)) { continue; } lay.knownItems.push(item); var itemContainer = itemComponent.createObject(temp, {"item": item}) //if section, label goes after the separator if (item.Kirigami.FormData.isSection) { //put an extra spacer var placeHolder = placeHolderComponent.createObject(lay, {"item": item}); itemContainer.parent = lay; } var buddy; if (item.Kirigami.FormData.checkable) { buddy = checkableBuddyComponent.createObject(lay, {"item": item}) } else { buddy = buddyComponent.createObject(lay, {"item": item}) } itemContainer.parent = lay; lay.buddies.push(buddy); } lay.knownItemsChanged(); lay.buddiesChanged(); hintCompression.triggered(); } } onChildrenChanged: relayoutTimer.restart(); Component { id: itemComponent Item { id: container property var item enabled: item.enabled visible: item.visible //NOTE: work around a GridLayout quirk which doesn't lay out items with null size hints causing things to be laid out incorrectly in some cases implicitWidth: Math.max(item.implicitWidth, 1) implicitHeight: Math.max(item.implicitHeight, 1) Layout.preferredWidth: Math.max(1, item.Layout.preferredWidth > 0 ? item.Layout.preferredWidth : item.implicitWidth) Layout.preferredHeight: Math.max(1, item.Layout.preferredHeight > 0 ? item.Layout.preferredHeight : item.implicitHeight) Layout.minimumWidth: item.Layout.minimumWidth Layout.minimumHeight: item.Layout.minimumHeight Layout.maximumWidth: item.Layout.maximumWidth Layout.maximumHeight: item.Layout.maximumHeight Layout.leftMargin: root.wideMode ? 0 : Kirigami.Units.largeSpacing Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter Layout.fillWidth: item.Layout.fillWidth || item.Kirigami.FormData.isSection Layout.columnSpan: item.Kirigami.FormData.isSection ? lay.columns : 1 onItemChanged: { if (!item) { container.destroy(); } } onXChanged: item.x = x + lay.x; //Assume lay.y is always 0 onYChanged: item.y = y + lay.y; onWidthChanged: item.width = width; Component.onCompleted: item.x = x + lay.x; Connections { target: lay onXChanged: item.x = x + lay.x; } } } Component { id: placeHolderComponent Item { property var item enabled: item.enabled visible: item.visible width: Kirigami.Units.smallSpacing height: Kirigami.Units.smallSpacing - Layout.topMargin: item.height > 0 ? Kirigami.Units.smallSpacing : 0 + Layout.topMargin: item.height > 0 ? Kirigami.Units.smallSpacing : 0 onItemChanged: { if (!item) { labelItem.destroy(); } } } } Component { id: buddyComponent Kirigami.Heading { id: labelItem property var item enabled: item.enabled visible: item.visible Kirigami.MnemonicData.enabled: item.Kirigami.FormData.buddyFor && item.Kirigami.FormData.buddyFor.activeFocusOnTab Kirigami.MnemonicData.controlType: Kirigami.MnemonicData.FormLabel Kirigami.MnemonicData.label: item.Kirigami.FormData.label text: Kirigami.MnemonicData.richTextLabel level: item.Kirigami.FormData.isSection ? 3 : 5 Layout.columnSpan: item.Kirigami.FormData.isSection ? lay.columns : 1 Layout.preferredHeight: { if (item.Kirigami.FormData.label.length > 0) { if (root.wideMode) { return Math.max(implicitHeight, item.Kirigami.FormData.buddyFor.height) } else { return implicitHeight } } else { Kirigami.Units.smallSpacing } } Layout.alignment: item.Kirigami.FormData.isSection ? Qt.AlignLeft : (root.wideMode ? (Qt.AlignRight | Qt.AlignTop) : (Qt.AlignLeft | Qt.AlignBottom)) verticalAlignment: root.wideMode ? Text.AlignVCenter : Text.AlignBottom //Layout.topMargin: item.Kirigami.FormData.buddyFor.y onItemChanged: { if (!item) { labelItem.destroy(); } } Shortcut { sequence: labelItem.Kirigami.MnemonicData.sequence onActivated: item.Kirigami.FormData.buddyFor.forceActiveFocus() } } } Component { id: checkableBuddyComponent CheckBox { id: labelItem property var item visible: item.visible Kirigami.MnemonicData.enabled: item.Kirigami.FormData.buddyFor && item.Kirigami.FormData.buddyFor.activeFocusOnTab Kirigami.MnemonicData.controlType: Kirigami.MnemonicData.FormLabel Kirigami.MnemonicData.label: item.Kirigami.FormData.label Layout.columnSpan: item.Kirigami.FormData.isSection ? lay.columns : 1 Layout.preferredHeight: item.Kirigami.FormData.label.length > 0 ? implicitHeight : Kirigami.Units.smallSpacing Layout.alignment: item.Kirigami.FormData.isSection ? Qt.AlignLeft : (root.wideMode ? (Qt.AlignRight | (item.Kirigami.FormData.buddyFor.height > height * 2 ? Qt.AlignTop : Qt.AlignVCenter)) : (Qt.AlignLeft | Qt.AlignBottom)) Layout.topMargin: item.Kirigami.FormData.buddyFor.height > implicitHeight * 2 ? Kirigami.Units.smallSpacing/2 : 0 activeFocusOnTab: indicator.visible && indicator.enabled text: labelItem.Kirigami.MnemonicData.richTextLabel enabled: labelItem.item.Kirigami.FormData.enabled checked: labelItem.item.Kirigami.FormData.checked onItemChanged: { if (!item) { labelItem.destroy(); } } Shortcut { sequence: labelItem.Kirigami.MnemonicData.sequence onActivated: { checked = !checked item.Kirigami.FormData.buddyFor.forceActiveFocus() } } onCheckedChanged: { item.Kirigami.FormData.checked = checked } contentItem: Kirigami.Heading { id: labelItemHeading level: labelItem.item.Kirigami.FormData.isSection ? 3 : 5 text: labelItem.text verticalAlignment: root.wideMode ? Text.AlignVCenter : Text.AlignBottom enabled: labelItem.item.Kirigami.FormData.enabled leftPadding: parent.indicator.width } Rectangle { enabled: labelItem.indicator.enabled anchors.left: labelItemHeading.left anchors.right: labelItemHeading.right anchors.top: labelItemHeading.bottom anchors.leftMargin: labelItemHeading.leftPadding height: 1 * Kirigami.Units.devicePixelRatio color: Kirigami.Theme.highlightColor visible: labelItem.activeFocus && labelItem.indicator.visible } } } } diff --git a/src/controls/GlobalDrawer.qml b/src/controls/GlobalDrawer.qml index 9166ad7a..6855c139 100644 --- a/src/controls/GlobalDrawer.qml +++ b/src/controls/GlobalDrawer.qml @@ -1,520 +1,507 @@ /* - * Copyright 2015 Marco Martin + * SPDX-FileCopyrightText: 2015 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.12 import QtQuick.Templates 2.0 as T2 import QtQuick.Controls 2.2 as QQC2 import QtQuick.Layouts 1.2 import QtGraphicalEffects 1.0 import org.kde.kirigami 2.4 import "private" import "templates/private" /** * A drawer specialization intended for the global actions of the application * valid regardless of the application state (think about the menubar * of a desktop application). * * Example usage: * @code * import org.kde.kirigami 2.4 as Kirigami * * Kirigami.ApplicationWindow { * [...] * globalDrawer: Kirigami.GlobalDrawer { * actions: [ * Kirigami.Action { * text: "View" * iconName: "view-list-icons" * Kirigami.Action { * text: "action 1" * } * Kirigami.Action { * text: "action 2" * } * Kirigami.Action { * text: "action 3" * } * }, * Kirigami.Action { * text: "Sync" * iconName: "folder-sync" * } * ] * } * [...] * } * @endcode * */ OverlayDrawer { id: root edge: Qt.application.layoutDirection == Qt.RightToLeft ? Qt.RightEdge : Qt.LeftEdge handleClosedIcon.source: null handleOpenIcon.source: null handleVisible: (modal || !drawerOpen) && (typeof(applicationWindow)===typeof(Function) && applicationWindow() ? applicationWindow().controlsVisible : true) && (!isMenu || Settings.isMobile) - + enabled: !isMenu || Settings.isMobile /** * title: string * A title to be displayed on top of the drawer */ property alias title: bannerImage.title /** * icon: var * An icon to be displayed alongside the title. * It can be a QIcon, a fdo-compatible icon name, or any url understood by Image */ property alias titleIcon: bannerImage.titleIcon /** * bannerImageSource: string * An image to be used as background for the title and icon for * a decorative purpose. * It accepts any url format supported by Image */ property alias bannerImageSource: bannerImage.source /** * actions: list * The list of actions can be nested having a tree structure. * A tree depth bigger than 2 is discouraged. * * Example usage: * @code * import org.kde.kirigami 2.4 as Kirigami * * Kirigami.ApplicationWindow { * [...] * globalDrawer: Kirigami.GlobalDrawer { * actions: [ * Kirigami.Action { * text: "View" * iconName: "view-list-icons" * Kirigami.Action { * text: "action 1" * } * Kirigami.Action { * text: "action 2" * } * Kirigami.Action { * text: "action 3" * } * }, * Kirigami.Action { * text: "Sync" * iconName: "folder-sync" * } * ] * } * [...] * } * @endcode */ property list actions /** * header: Item * an item that will stay on top of the drawer, * and if the drawer contents can be scrolled, * this item will stay still and won't scroll. * Indended mainly for toolbars * @since 2.12 */ property Item header /** * bannerVisible: bool * if true the banner area, which can contain an image, * an icon and a title will be visible. * By default the banner will be visible only on mobile platforms * @since 2.12 */ property bool bannerVisible: Settings.isMobile /** * content: list default property * Any random Item can be instantiated inside the drawer and * will be displayed underneath the actions list. * * Example usage: * @code * import org.kde.kirigami 2.4 as Kirigami * * Kirigami.ApplicationWindow { * [...] * globalDrawer: Kirigami.GlobalDrawer { * actions: [...] * Button { * text: "Button" * onClicked: //do stuff * } * } * [...] * } * @endcode */ default property alias content: mainContent.data /** * topContent: list default property * Items that will be instantiated inside the drawer and * will be displayed on top of the actions list. * * Example usage: * @code * import org.kde.kirigami 2.4 as Kirigami * * Kirigami.ApplicationWindow { * [...] * globalDrawer: Kirigami.GlobalDrawer { * actions: [...] * topContent: [Button { * text: "Button" * onClicked: //do stuff * }] * } * [...] * } * @endcode */ property alias topContent: topContent.data /** * showContentWhenCollapsed: bool * If true, when the drawer is collapsed as a sidebar, the content items * at the bottom will be hidden (default false). - * If you want to keep some items visible and some invisible, set this to + * If you want to keep some items visible and some invisible, set this to * false and control the visibility/opacity of individual items, * binded to the collapsed property * @since 2.5 */ property bool showContentWhenCollapsed: false /** * showTopContentWhenCollapsed: bool * If true, when the drawer is collapsed as a sidebar, the top content items * at the top will be hidden (default false). - * If you want to keep some items visible and some invisible, set this to + * If you want to keep some items visible and some invisible, set this to * false and control the visibility/opacity of individual items, * binded to the collapsed property * @since 2.5 */ property bool showTopContentWhenCollapsed: false //TODO property bool showHeaderWhenCollapsed: false /** * resetMenuOnTriggered: bool * * On the actions menu, whenever a leaf action is triggered, the menu * will reset to its parent. */ property bool resetMenuOnTriggered: true /** * currentSubMenu: Action * * Points to the action acting as a submenu */ readonly property Action currentSubMenu: stackView.currentItem ? stackView.currentItem.current: null /** * isMenu: bool * When true the global drawer becomes a menu on the desktop. Defauls to false. * @since 2.11 */ property bool isMenu: false /** * Notifies that the banner has been clicked */ signal bannerClicked() /** * When the sidebar is collapsible, this controls the visibility of * the collapse button * @since 2.12 */ property bool collapseButtonVisible: true /** * Reverts the menu back to its initial state */ function resetMenu() { stackView.pop(stackView.get(0, T2.StackView.DontLoad)); if (root.modal) { root.drawerOpen = false; } } //rightPadding: !Settings.isMobile && mainFlickable.contentHeight > mainFlickable.height ? Units.gridUnit : Units.smallSpacing onHeaderChanged: { if (header) { header.parent = headerContainer header.Layout.fillWidth = true; } } contentItem: QQC2.ScrollView { id: scrollView //ensure the attached property exists Theme.inherit: true anchors.fill: parent implicitWidth: Math.min (Units.gridUnit * 20, root.parent.width * 0.8) QQC2.ScrollBar.horizontal.policy: QQC2.ScrollBar.AlwaysOff QQC2.ScrollBar.vertical.anchors { top: scrollView.top bottom: scrollView.bottom topMargin: headerParent.height + headerParent.y } Flickable { id: mainFlickable contentWidth: width contentHeight: mainColumn.Layout.minimumHeight topMargin: headerParent.height ColumnLayout { id: headerParent parent: mainFlickable anchors { left: parent.left right: parent.right rightMargin: Math.min(0, -scrollView.width + mainFlickable.width) } spacing: 0 y: bannerImage.visible ? Math.max(headerContainer.height, -mainFlickable.contentY) - height : 0 Layout.fillWidth: true //visible: !bannerImage.empty || root.collapsible BannerImage { id: bannerImage visible: !bannerImage.empty && opacity > 0 && root.bannerVisible opacity: !root.collapsed fillMode: Image.PreserveAspectCrop Behavior on opacity { OpacityAnimator { duration: Units.longDuration easing.type: Easing.InOutQuad } } //leftPadding: root.collapsible ? collapseButton.width + Units.smallSpacing*2 : topPadding MouseArea { anchors.fill: parent onClicked: root.bannerClicked() } EdgeShadow { edge: Qt.BottomEdge visible: bannerImageSource != "" anchors { left: parent.left right: parent.right bottom: parent.top } } } RowLayout { id: headerContainer Theme.inherit: false Theme.colorSet: Theme.Window Layout.fillWidth: true visible: contentItem && opacity > 0 Layout.preferredHeight: implicitHeight * opacity opacity: !root.collapsed || showHeaderWhenCollapsed Behavior on opacity { //not an animator as is binded NumberAnimation { duration: Units.longDuration easing.type: Easing.InOutQuad } } } } ColumnLayout { id: mainColumn width: mainFlickable.width spacing: 0 height: Math.max(root.height - headerParent.height, Layout.minimumHeight) ColumnLayout { id: topContent spacing: 0 Layout.alignment: Qt.AlignHCenter Layout.leftMargin: root.leftPadding Layout.rightMargin: root.rightPadding Layout.bottomMargin: Units.smallSpacing Layout.topMargin: root.topPadding Layout.fillWidth: true Layout.fillHeight: true Layout.preferredHeight: implicitHeight * opacity //NOTE: why this? just Layout.fillWidth: true doesn't seem sufficient //as items are added only after this column creation Layout.minimumWidth: parent.width - root.leftPadding - root.rightPadding visible: children.length > 0 && childrenRect.height > 0 && opacity > 0 opacity: !root.collapsed || showTopContentWhenCollapsed Behavior on opacity { //not an animator as is binded NumberAnimation { duration: Units.longDuration easing.type: Easing.InOutQuad } } } T2.StackView { id: stackView Layout.fillWidth: true Layout.minimumHeight: currentItem ? currentItem.implicitHeight : 0 Layout.maximumHeight: Layout.minimumHeight property ActionsMenu openSubMenu initialItem: menuComponent //NOTE: it's important those are NumberAnimation and not XAnimators // as while the animation is running the drawer may close, and //the animator would stop when not drawing see BUG 381576 popEnter: Transition { NumberAnimation { property: "x"; from: (stackView.mirrored ? -1 : 1) * -stackView.width; to: 0; duration: 400; easing.type: Easing.OutCubic } } popExit: Transition { NumberAnimation { property: "x"; from: 0; to: (stackView.mirrored ? -1 : 1) * stackView.width; duration: 400; easing.type: Easing.OutCubic } } pushEnter: Transition { NumberAnimation { property: "x"; from: (stackView.mirrored ? -1 : 1) * stackView.width; to: 0; duration: 400; easing.type: Easing.OutCubic } } pushExit: Transition { NumberAnimation { property: "x"; from: 0; to: (stackView.mirrored ? -1 : 1) * -stackView.width; duration: 400; easing.type: Easing.OutCubic } } replaceEnter: Transition { NumberAnimation { property: "x"; from: (stackView.mirrored ? -1 : 1) * stackView.width; to: 0; duration: 400; easing.type: Easing.OutCubic } } replaceExit: Transition { NumberAnimation { property: "x"; from: 0; to: (stackView.mirrored ? -1 : 1) * -stackView.width; duration: 400; easing.type: Easing.OutCubic } } } Item { Layout.fillWidth: true Layout.fillHeight: root.actions.length>0 Layout.minimumHeight: Units.smallSpacing } ColumnLayout { id: mainContent Layout.alignment: Qt.AlignHCenter Layout.leftMargin: root.leftPadding Layout.rightMargin: root.rightPadding Layout.fillWidth: true Layout.fillHeight: true //NOTE: why this? just Layout.fillWidth: true doesn't seem sufficient //as items are added only after this column creation Layout.minimumWidth: parent.width - root.leftPadding - root.rightPadding visible: children.length > 0 && (opacity > 0 || mainContentAnimator.running) opacity: !root.collapsed || showContentWhenCollapsed Behavior on opacity { OpacityAnimator { id: mainContentAnimator duration: Units.longDuration easing.type: Easing.InOutQuad } } } Item { Layout.minimumWidth: Units.smallSpacing Layout.minimumHeight: root.bottomPadding } Component { id: menuComponent Column { spacing: 0 property alias model: actionsRepeater.model property Action current property int level: 0 Layout.maximumHeight: Layout.minimumHeight BasicListItem { id: backItem visible: level > 0 supportsMouseEvents: true icon: (LayoutMirroring.enabled ? "go-previous-symbolic-rtl" : "go-previous-symbolic") label: MnemonicData.richTextLabel MnemonicData.enabled: backItem.enabled && backItem.visible MnemonicData.controlType: MnemonicData.MenuItem MnemonicData.label: qsTr("Back") separatorVisible: false onClicked: stackView.pop() } Shortcut { sequence: backItem.MnemonicData.sequence onActivated: backItem.clicked() } Repeater { id: actionsRepeater model: root.actions delegate: Column { width: parent.width GlobalDrawerActionItem { id: drawerItem width: parent.width } Repeater { model: drawerItem.visible && modelData.hasOwnProperty("expandible") && modelData.expandible ? modelData.children : null delegate: GlobalDrawerActionItem { width: parent.width leftPadding: Units.largeSpacing * 2 opacity: !root.collapsed } } } } } } - + QQC2.ToolButton { icon.name: root.collapsed ? "view-right-new" : "view-right-close" Layout.fillWidth: root.collapsed onClicked: root.collapsed = !root.collapsed visible: root.collapsible && root.collapseButtonVisible text: root.collapsed ? "" : qsTr("Close Sidebar") } } } } } diff --git a/src/controls/Heading.qml b/src/controls/Heading.qml index 0b6000c0..40cad22a 100644 --- a/src/controls/Heading.qml +++ b/src/controls/Heading.qml @@ -1,85 +1,72 @@ /* -* Copyright 2012 by Sebastian Kügler -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU Library General Public License as -* published by the Free Software Foundation; either version 2, or -* (at your option) any later version. -* -* This program 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 Library General Public License for more details -* -* You should have received a copy of the GNU Library General Public -* License along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. -*/ + * SPDX-FileCopyrightText: 2012 by Sebastian Kügler + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ import QtQuick 2.0 import QtQuick.Controls 2.0 as QQC2 import org.kde.kirigami 2.4 /** * A heading label used for subsections of texts. * * The characteristics of the text will be automatically set according to the * plasma Theme. Use this components for section titles or headings in your UI, * for example page or section titles. * * Example usage: * * @code * import org.kde.kirigami 2.4 as Kirigami * [...] * Column { * Kirigami.Heading { * text: "Apples in the sunlight" * level: 2 * } * [...] * } * @endcode * * The most important property is "text", which applies to the text property of * Label. See PlasmaComponents Label and primitive QML Text element API for * additional properties, methods and signals. * @inherits QtQuick.Controls.Label */ QQC2.Label { id: heading /** * level: int * The level determines how big the section header is display, values * between 1 (big) and 5 (small) are accepted */ property int level: 1 /** * step: int * adjust the point size in between a level and another. * DEPRECATED */ property int step: 0 font.pointSize: headerPointSize(level) function headerPointSize(l) { var n = Theme.defaultFont.pointSize; var s; switch (l) { case 1: return Math.round(n * 1.50) + step; case 2: return Math.round(n * 1.30) + step; case 3: return Math.round(n * 1.20) + step; case 4: return Math.round(n * 1.10) + step; default: return n + step; } } } diff --git a/src/controls/InlineMessage.qml b/src/controls/InlineMessage.qml index 2b3afe66..63cfeb5f 100644 --- a/src/controls/InlineMessage.qml +++ b/src/controls/InlineMessage.qml @@ -1,118 +1,105 @@ /* - * Copyright 2018 Eike Hein - * Copyright 2018 Marco Martin - * Copyright 2018 Kai Uwe Broulik + * SPDX-FileCopyrightText: 2018 Eike Hein + * SPDX-FileCopyrightText: 2018 Marco Martin + * SPDX-FileCopyrightText: 2018 Kai Uwe Broulik * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.7 import org.kde.kirigami 2.5 as Kirigami import "private" import "templates" as T /** * An inline message item with support for informational, positive, * warning and error types, and with support for associated actions. * * InlineMessage can be used to give information to the user or * interact with the user, without requiring the use of a dialog. * * The InlineMessage item is hidden by default. It also manages its * height (and implicitHeight) during an animated reveal when shown. * You should avoid setting height on an InlineMessage unless it is * already visible. * * Optionally an icon can be set, defaulting to an icon appropriate * to the message type otherwise. * * Optionally a close button can be shown. * * Actions are added from left to right. If more actions are set than * can fit, an overflow menu is provided. * * Example: * @code * InlineMessage { * type: Kirigami.MessageType.Error * * text: "My error message" * * actions: [ * Kirigami.Action { * iconName: "edit" * text: "Action text" * onTriggered: { * // do stuff * } * }, * Kirigami.Action { * iconName: "edit" * text: "Action text" * onTriggered: { * // do stuff * } * } * ] * } * @endcode * * @since 5.45 */ T.InlineMessage { id: root background: Rectangle { id: bgBorderRect color: { if (root.type == Kirigami.MessageType.Positive) { return Kirigami.Theme.positiveTextColor; } else if (root.type == Kirigami.MessageType.Warning) { return Kirigami.Theme.neutralTextColor; } else if (root.type == Kirigami.MessageType.Error) { return Kirigami.Theme.negativeTextColor; } return Kirigami.Theme.activeTextColor; } radius: Kirigami.Units.smallSpacing / 2 Rectangle { id: bgFillRect anchors.fill: parent anchors.margins: Kirigami.Units.devicePixelRatio color: Kirigami.Theme.backgroundColor radius: bgBorderRect.radius * 0.60 } Rectangle { anchors.fill: bgFillRect color: bgBorderRect.color opacity: 0.20 radius: bgFillRect.radius } } } diff --git a/src/controls/ItemViewHeader.qml b/src/controls/ItemViewHeader.qml index fdbd22f2..f574bdca 100644 --- a/src/controls/ItemViewHeader.qml +++ b/src/controls/ItemViewHeader.qml @@ -1,115 +1,102 @@ /* - * Copyright 2017 Marco Martin + * SPDX-FileCopyrightText: 2017 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.5 import QtQuick.Templates 2.0 as T2 import QtGraphicalEffects 1.0 import org.kde.kirigami 2.4 as Kirigami import "private" /** * An item that can be used as an header for a ListView. * It will play nice with the margin policies of ScrollablePage and can * automatically shrink when the list is scrolled, like the behavior * of list headers in many mobile applications. * It provides some default content: a title and an optional background image * @since 2.1 */ Kirigami.AbstractItemViewHeader { id: root property alias title: heading.text property alias color: heading.color property alias backgroundImage: image maximumHeight: (backgroundImage.hasImage ? 10 : 6) * Kirigami.Units.gridUnit - (applicationWindow().header ? applicationWindow().header.height : 0) - bottomPadding bottomPadding: Kirigami.Units.smallSpacing leftPadding: Kirigami.Units.smallSpacing background: Rectangle { id: backgroundItem color: Kirigami.Theme.backgroundColor Image { id: image anchors.fill: parent readonly property bool hasImage: backgroundImage.status === Image.Ready || backgroundImage.status === Image.Loading fillMode: Image.PreserveAspectCrop asynchronous: true } EdgeShadow { edge: root.view.headerPositioning == ListView.InlineHeader ? Qt.BottomEdge : Qt.TopEdge anchors { right: parent.right left: parent.left top: root.view.headerPositioning == ListView.InlineHeader ? undefined : parent.bottom bottom: root.view.headerPositioning == ListView.InlineHeader ? parent.top : undefined } } readonly property Page page: { var obj = root.view; while(obj && !obj.hasOwnProperty("title") && !obj.hasOwnProperty("isCurrentPage")) { obj = obj.parent } return obj; } Rectangle { id: rect color: backgroundItem.page && backgroundItem.page.isCurrentPage ? Kirigami.Theme.highlightColor : Kirigami.Theme.disabledTextColor height: root.bottomPadding anchors { left: parent.left right: parent.right bottom: parent.bottom } } } contentItem: Item { Kirigami.Heading { id: heading anchors { fill: parent margins: Kirigami.Units.smallSpacing } height: undefined text: page.title fontSizeMode: Text.Fit minimumPointSize: 10 font.pointSize: 30 horizontalAlignment: Text.AlignRight verticalAlignment: Text.AlignBottom //with an image it needs to be white regardless of system palette color: root.backgroundImage.hasImage ? "white" : Kirigami.Theme.highlightColor opacity: 1 elide: Text.ElideRight layer.enabled: root.backgroundImage.hasImage layer.effect: DropShadow { horizontalOffset: 0 verticalOffset: 2 radius: Kirigami.Units.smallSpacing*2 samples: 32 color: Qt.rgba(0, 0, 0, 0.7) } } } } diff --git a/src/controls/Label.qml b/src/controls/Label.qml index 6a10b90c..6ac5aeaa 100644 --- a/src/controls/Label.qml +++ b/src/controls/Label.qml @@ -1,47 +1,34 @@ /* -* Copyright (C) 2011 by Marco Martin -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU Library General Public License as -* published by the Free Software Foundation; either version 2, or -* (at your option) any later version. -* -* This program 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 Library General Public License for more details -* -* You should have received a copy of the GNU Library General Public -* License along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. -*/ + * SPDX-FileCopyrightText: 2011 by Marco Martin + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ import QtQuick 2.1 import QtQuick.Window 2.2 import org.kde.kirigami 2.4 import QtQuick.Controls 2.0 as Controls /** * This is a label which uses the current Theme. * * The characteristics of the text will be automatically set according to the * current Theme. If you need a more customized text item use the Text component * from QtQuick. * * You can use all elements of the QML Text component, in particular the "text" * property to define the label text. * * @inherit QtQuick.Templates.Label * @deprecated use QtQuick.Templates.Label directly, it will be styled appropriately */ Controls.Label { height: Math.round(Math.max(paintedHeight, Units.gridUnit * 1.6)) verticalAlignment: lineCount > 1 ? Text.AlignTop : Text.AlignVCenter activeFocusOnTab: false Component.onCompleted: { console.warn("Kirigami.Label is deprecated. Use QtQuickControls2.Label instead") } } diff --git a/src/controls/LinkButton.qml b/src/controls/LinkButton.qml index 4d739d0f..f3849ff4 100644 --- a/src/controls/LinkButton.qml +++ b/src/controls/LinkButton.qml @@ -1,65 +1,52 @@ /* - * Copyright (C) 2018 Aleix Pol Gonzalez + * SPDX-FileCopyrightText: 2018 Aleix Pol Gonzalez * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library/Lesser General Public License - * version 2, or (at your option) any later version, as published by the - * Free Software Foundation - * - * This program 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 General Public License for more details - * - * You should have received a copy of the GNU Library/Lesser General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.2 import org.kde.kirigami 2.0 import QtQuick.Controls 2.1 as QQC2 /** * Shows a Button that looks like a link * * Uses the link color settings and allows to trigger an action when clicked. * * Maps to the Command Link in the HIG: * https://hig.kde.org/components/navigation/commandlink.html * * @since 5.52 * @since org.kde.kirigami 2.6 */ QQC2.Label { id: control property Action action: null property alias acceptedButtons: area.acceptedButtons Accessible.role: Accessible.Button Accessible.name: text Accessible.onPressAction: control.clicked(null) text: action ? action.text : "" enabled: !action || action.enabled onClicked: if (action) action.trigger() font.underline: control.enabled && area.containsMouse color: enabled ? Theme.linkColor : Theme.textColor horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter elide: Text.ElideRight signal pressed(QtObject mouse) signal clicked(QtObject mouse) MouseArea { id: area anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: control.clicked(mouse) onPressed: control.pressed(mouse) } } diff --git a/src/controls/ListItemDragHandle.qml b/src/controls/ListItemDragHandle.qml index ab609e68..a2c4a66a 100644 --- a/src/controls/ListItemDragHandle.qml +++ b/src/controls/ListItemDragHandle.qml @@ -1,207 +1,194 @@ /* -* Copyright (C) 2018 by Marco Martin -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU Library General Public License as -* published by the Free Software Foundation; either version 2, or -* (at your option) any later version. -* -* This program 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 Library General Public License for more details -* -* You should have received a copy of the GNU Library General Public -* License along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. -*/ + * SPDX-FileCopyrightText: 2018 by Marco Martin + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ import QtQuick 2.6 import QtQuick.Layouts 1.2 import org.kde.kirigami 2.4 as Kirigami /** * Implements a drag handle supposed to be in items in ListViews to reorder items * The ListView must visualize a model which supports item reordering, * such as ListModel.move() or QAbstractItemModel instances with moveRows() correctly implemented. * In order for ListItemDragHandle to work correctly, the listItem that is being dragged * should not directly be the delegate of the ListView, but a child of it. * * It is recommended to use DelagateRecycler as base delegate like the following code: * @code * ... * Component { * id: delegateComponent * Kirigami.AbstractListItem { * id: listItem * contentItem: RowLayout { * Kirigami.ListItemDragHandle { * listItem: listItem * listView: mainList * onMoveRequested: listModel.move(oldIndex, newIndex, 1) * } * Controls.Label { * text: model.label * } * } * } * } * ListView { * id: mainList * * model: ListModel { * id: listModel * ListItem { * lablel: "Item 1" * } * ListItem { * lablel: "Item 2" * } * ListItem { * lablel: "Item 3" - * } + * } * } * //this is optional to make list items animated when reordered * moveDisplaced: Transition { * YAnimator { * duration: Kirigami.Units.longDuration * easing.type: Easing.InOutQuad * } * } * delegate: Kirigami.DelegateRecycler { * width: mainList.width * sourceComponent: delegateComponent * } * } * ... * @endcode * * @inherits Item * @since 2.5 */ Item { id: root /** * listItem: Item * The id of the delegate that we want to drag around, which *must* * be a child of the actual ListView's delegate */ property Item listItem /** * listView: Listview * The id of the ListView the delegates belong to. */ property ListView listView /** * Emitted when the drag handle wants to move the item in the model * The following example does the move in the case a ListModel is used * @code * onMoveRequested: listModel.move(oldIndex, newIndex, 1) * @endcode * @param oldIndex the index the item is currently at * @param newIndex the index we want to move the item to */ signal moveRequested(int oldIndex, int newIndex) /** * Emitted when the drag operation is complete and the item has been * dropped in the new final position */ signal dropped() implicitWidth: Kirigami.Units.iconSizes.smallMedium implicitHeight: implicitWidth MouseArea { id: mouseArea anchors.fill: parent drag { target: listItem axis: Drag.YAxis minimumY: 0 maximumY: listView.height - listItem.height } Kirigami.Icon { id: internal source: "handle-sort" property int startY property int mouseDownY property Item originalParent property int autoScrollThreshold: listItem.height * 3 opacity: mouseArea.pressed || (!Kirigami.Settings.tabletMode && listItem.hovered) ? 1 : 0.6 function arrangeItem() { var newIndex = listView.indexAt(1, listView.contentItem.mapFromItem(listItem, 0, 0).y + internal.mouseDownY); if (Math.abs(listItem.y - internal.startY) > height && newIndex > -1 && newIndex !== index) { root.moveRequested(index, newIndex); } } anchors.fill: parent } preventStealing: true - + onPressed: { internal.originalParent = listItem.parent; listItem.parent = listView; listItem.y = internal.originalParent.mapToItem(listItem.parent, listItem.x, listItem.y).y; internal.originalParent.z = 99; internal.startY = listItem.y; internal.mouseDownY = mouse.y; } onPositionChanged: { if (!pressed) { return; } internal.arrangeItem(); scrollTimer.interval = 500 * Math.max(0.1, (1-Math.max(internal.autoScrollThreshold - listItem.y, listItem.y - listView.height + internal.autoScrollThreshold + listItem.height) / internal.autoScrollThreshold)); scrollTimer.running = (listItem.y < internal.autoScrollThreshold || listItem.y > listView.height - internal.autoScrollThreshold); } onReleased: { listItem.y = internal.originalParent.mapFromItem(listItem, 0, 0).y; listItem.parent = internal.originalParent; dropAnimation.running = true; scrollTimer.running = false; root.dropped(); } onCanceled: released() SequentialAnimation { id: dropAnimation YAnimator { target: listItem - from: listItem.y + from: listItem.y to: 0 duration: Kirigami.Units.longDuration easing.type: Easing.InOutQuad } PropertyAction { target: listItem.parent property: "z" value: 0 } } Timer { id: scrollTimer interval: 500 repeat: true onTriggered: { if (listItem.y < internal.autoScrollThreshold) { listView.contentY = Math.max(0, listView.contentY - Kirigami.Units.gridUnit) } else { listView.contentY = Math.min(listView.contentHeight - listView.height, listView.contentY + Kirigami.Units.gridUnit) } internal.arrangeItem(); } } } } diff --git a/src/controls/ListSectionHeader.qml b/src/controls/ListSectionHeader.qml index c2667e4c..ee319b17 100644 --- a/src/controls/ListSectionHeader.qml +++ b/src/controls/ListSectionHeader.qml @@ -1,83 +1,70 @@ /* - * Copyright 2019 Björn Feber + * SPDX-FileCopyrightText: 2019 Björn Feber * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.5 import QtQuick.Layouts 1.2 import QtQuick.Controls 2.4 as QQC2 import org.kde.kirigami 2.10 /** * A section delegate for the primitive ListView component. * * It's intended to make all listviews look coherent. * * Example usage: * @code * import QtQuick 2.5 * import QtQuick.Controls 2.5 as QQC2 * * import org.kde.kirigami 2.10 as Kirigami * * ListView { * [...] * section.delegate: Kirigami.ListSectionHeader { * label: section * * QQC2.Button { * text: "Button 1" * } * QQC2.Button { * text: "Button 2" * } * } * [...] * } * @endcode * */ AbstractListItem { id: listSection /** * string: bool * A single text label the list section header will contain */ property alias label: listSection.text default property alias _contents: rowLayout.data backgroundColor: Theme.backgroundColor Theme.inherit: false Theme.colorSet: Theme.Window separatorVisible: false sectionDelegate: true hoverEnabled: false supportsMouseEvents: false contentItem: RowLayout { id: rowLayout Heading { level: 3 text: listSection.text Layout.fillWidth: rowLayout.children.length === 1 } } } diff --git a/src/controls/OverlayDrawer.qml b/src/controls/OverlayDrawer.qml index 1bc43fc1..ac8dadb3 100644 --- a/src/controls/OverlayDrawer.qml +++ b/src/controls/OverlayDrawer.qml @@ -1,162 +1,149 @@ /* - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.1 import QtGraphicalEffects 1.0 import QtQuick.Templates 2.0 as T2 import org.kde.kirigami 2.5 import "private" import "templates" as T /** * Overlay Drawers are used to expose additional UI elements needed for * small secondary tasks for which the main UI elements are not needed. * For example in Okular Mobile, an Overlay Drawer is used to display * thumbnails of all pages within a document along with a search field. * This is used for the distinct task of navigating to another page. */ T.OverlayDrawer { id: root //BEGIN Properties focus: false modal: true drawerOpen: !modal closePolicy: modal ? T2.Popup.CloseOnEscape | T2.Popup.CloseOnReleaseOutside : T2.Popup.NoAutoClose handleVisible: (modal || !drawerOpen) && (typeof(applicationWindow)===typeof(Function) && applicationWindow() ? applicationWindow().controlsVisible : true) onPositionChanged: { if (!modal && !root.peeking && !root.animating) { position = 1; } } background: Rectangle { color: Theme.backgroundColor Item { parent: root.handle anchors.fill: parent DropShadow { anchors.fill: handleGraphics visible: !parent.parent.handleAnchor || !parent.parent.handleAnchor.visible || root.handle.pressed || (root.modal && root.position > 0) horizontalOffset: 0 verticalOffset: Units.devicePixelRatio radius: Units.gridUnit /2 samples: 16 color: Qt.rgba(0, 0, 0, root.handle.pressed ? 0.6 : 0.4) source: handleGraphics } Rectangle { id: handleGraphics anchors.centerIn: parent Theme.colorSet: parent.parent.handleAnchor && parent.parent.handleAnchor.visible ? parent.parent.handleAnchor.Theme.colorSet : Theme.Button Theme.backgroundColor: parent.parent.handleAnchor && parent.parent.handleAnchor.visible ? parent.parent.handleAnchor.Theme.backgroundColor : undefined Theme.textColor: parent.parent.handleAnchor && parent.parent.handleAnchor.visible ? parent.parent.handleAnchor.Theme.textColor : undefined Theme.inherit: false color: root.handle.pressed ? Theme.highlightColor : Theme.backgroundColor visible: !parent.parent.handleAnchor || !parent.parent.handleAnchor.visible width: Units.iconSizes.smallMedium + Units.smallSpacing * 2 height: width radius: Units.devicePixelRatio * 2 Behavior on color { ColorAnimation { duration: Units.longDuration easing.type: Easing.InOutQuad } } } Loader { anchors.centerIn: handleGraphics width: height height: Units.iconSizes.smallMedium Theme.colorSet: handleGraphics.Theme.colorSet Theme.backgroundColor: handleGraphics.Theme.backgroundColor Theme.textColor: handleGraphics.Theme.textColor source: { var edge = root.edge; if (Qt.application.layoutDirection == Qt.RightToLeft) { if (edge === Qt.LeftEdge) { edge = Qt.RightEdge; } else { edge = Qt.LeftEdge; } } if (root.handleClosedIcon.source && root.handleOpenIcon.source) { return Qt.resolvedUrl("templates/private/GenericDrawerIcon.qml"); } else if (edge == Qt.LeftEdge ) { return Qt.resolvedUrl("templates/private/MenuIcon.qml"); } else if(edge == Qt.RightEdge && root.hasOwnProperty("actions")) { return Qt.resolvedUrl("templates/private/ContextIcon.qml"); }else { return ""; } } onItemChanged: { if(item) { item.drawer = Qt.binding(function(){return root}); item.color = Qt.binding(function(){return root.handle.pressed ? Theme.highlightedTextColor : Theme.textColor}); } } } } Separator { anchors { right: root.edge == Qt.RightEdge ? parent.left : (root.edge == Qt.LeftEdge ? undefined : parent.right) left: root.edge == Qt.LeftEdge ? parent.right : (root.edge == Qt.RightEdge ? undefined : parent.left) top: root.edge == Qt.TopEdge ? parent.bottom : (root.edge == Qt.BottomEdge ? undefined : parent.top) bottom: root.edge == Qt.BottomEdge ? parent.top : (root.edge == Qt.TopEdge ? undefined : parent.bottom) } visible: !root.modal } EdgeShadow { z: -2 visible: root.modal edge: root.edge anchors { right: root.edge == Qt.RightEdge ? parent.left : (root.edge == Qt.LeftEdge ? undefined : parent.right) left: root.edge == Qt.LeftEdge ? parent.right : (root.edge == Qt.RightEdge ? undefined : parent.left) top: root.edge == Qt.TopEdge ? parent.bottom : (root.edge == Qt.BottomEdge ? undefined : parent.top) bottom: root.edge == Qt.BottomEdge ? parent.top : (root.edge == Qt.TopEdge ? undefined : parent.bottom) } opacity: root.position == 0 ? 0 : 1 Behavior on opacity { NumberAnimation { duration: Units.longDuration easing.type: Easing.InOutQuad } } } } } diff --git a/src/controls/OverlaySheet.qml b/src/controls/OverlaySheet.qml index 1b3a660b..51a3b171 100644 --- a/src/controls/OverlaySheet.qml +++ b/src/controls/OverlaySheet.qml @@ -1,39 +1,26 @@ /* -* Copyright (C) 2016 by Marco Martin -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU Library General Public License as -* published by the Free Software Foundation; either version 2, or -* (at your option) any later version. -* -* This program 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 Library General Public License for more details -* -* You should have received a copy of the GNU Library General Public -* License along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. -*/ + * SPDX-FileCopyrightText: 2016 by Marco Martin + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ import QtQuick 2.5 import org.kde.kirigami 2.4 import "private" import "templates" as T /** * An overlay sheet that covers the current Page content. * Its contents can be scrolled up or down, scrolling all the way up or * all the way down, dismisses it. * Use this for big, modal dialogs or information display, that can't be * logically done as a new separate Page, even if potentially * are taller than the screen space. */ T.OverlaySheet { id: root background: DefaultCardBackground { anchors.fill: parent } } diff --git a/src/controls/Page.qml b/src/controls/Page.qml index 42591213..962bdfe9 100644 --- a/src/controls/Page.qml +++ b/src/controls/Page.qml @@ -1,414 +1,401 @@ /* - * Copyright 2015 Marco Martin + * SPDX-FileCopyrightText: 2015 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.5 import QtQuick.Layouts 1.2 import org.kde.kirigami 2.10 as Kirigami import "private" import QtQuick.Templates 2.1 as T2 import QtQuick.Controls 2.1 as QQC2 /** * Page is a container for all the app pages: everything pushed to the * ApplicationWindow stackView should be a Page instance (or a subclass, * such as ScrollablePage) * @see ScrollablePage * @inherit QtQuick.Templates.Page */ QQC2.Page { id: root /** * leftPadding: int * default contents padding at left */ leftPadding: Kirigami.Units.gridUnit /** * topPadding: int * default contents padding at top */ topPadding: Kirigami.Units.gridUnit /** * rightPadding: int * default contents padding at right */ rightPadding: Kirigami.Units.gridUnit /** * bottomPadding: int * default contents padding at bottom */ bottomPadding: actionButtons.item ? actionButtons.height : Kirigami.Units.gridUnit /** * flickable: Flickable * if the central element of the page is a Flickable * (ListView and Gridview as well) you can set it there. * normally, you wouldn't need to do that, but just use the * ScrollablePage element instead * @see ScrollablePage * Use this if your flickable has some non standard properties, such as not covering the whole Page */ property Flickable flickable /** * actions.contextualActions: list * Defines the contextual actions for the page: * an easy way to assign actions in the right sliding panel * * Example usage: * @code * import org.kde.kirigami 2.4 as Kirigami * * Kirigami.ApplicationWindow { * [...] * contextDrawer: Kirigami.ContextDrawer { * id: contextDrawer * } * [...] * } * @endcode * * @code * import org.kde.kirigami 2.4 as Kirigami * * Kirigami.Page { * [...] * actions.contextualActions: [ * Kirigami.Action { * iconName: "edit" * text: "Action text" * onTriggered: { * // do stuff * } * }, * Kirigami.Action { * iconName: "edit" * text: "Action text" * onTriggered: { * // do stuff * } * } * ] * [...] * } * @endcode */ //TODO: remove property alias contextualActions: actionsGroup.contextualActions /** * actions.main: Action * An optional single action for the action button. * it can be a Kirigami.Action or a QAction * * Example usage: * * @code * import org.kde.kirigami 2.4 as Kirigami * Kirigami.Page { * actions.main: Kirigami.Action { * iconName: "edit" * onTriggered: { * // do stuff * } * } * } * @endcode */ //TODO: remove property alias mainAction: actionsGroup.main /** * actions.left: Action * An optional extra action at the left of the main action button. * it can be a Kirigami.Action or a QAction * * Example usage: * * @code * import org.kde.kirigami 2.4 as Kirigami * Kirigami.Page { * actions.left: Kirigami.Action { * iconName: "edit" * onTriggered: { * // do stuff * } * } * } * @endcode */ //TODO: remove property alias leftAction: actionsGroup.left /** * actions.right: Action * An optional extra action at the right of the main action button. * it can be a Kirigami.Action or a QAction * * Example usage: * * @code * import org.kde.kirigami 2.4 as Kirigami * Kirigami.Page { * actions.right: Kirigami.Action { * iconName: "edit" * onTriggered: { * // do stuff * } * } * } * @endcode */ //TODO: remove property alias rightAction: actionsGroup.right /** * Actions properties are grouped. * * @code * import org.kde.kirigami 2.4 as Kirigami * Kirigami.Page { * actions { * main: Kirigami.Action {...} * left: Kirigami.Action {...} * right: Kirigami.Action {...} * contextualActions: [ * Kirigami.Action {...}, * Kirigami.Action {...} * ] * } * } * @endcode */ readonly property alias actions: actionsGroup /** * contextualActionsAboutToShow: signal * Emitted when a visualization for the actions is about to be shown, * such as the toolbar menu or the contextDrawer * @since 2.7 */ signal contextualActionsAboutToShow /** * isCurrentPage: bool * * Specifies if it's the currently selected page in the window's pages row. * * @since 2.1 */ readonly property bool isCurrentPage: Kirigami.ColumnView.view ? Kirigami.ColumnView.index == Kirigami.ColumnView.view.currentIndex : true /** * overlay: Item * an item which stays on top of every other item in the page, * if you want to make sure some elements are completely in a * layer on top of the whole content, parent items to this one. * It's a "local" version of ApplicationWindow's overlay * @since 2.5 */ readonly property alias overlay: overlayItem /** * titleDelegate: Component * The delegate which will be used to draw the page title. It can be customized to put any kind of Item in there. * @since 2.7 */ property Component titleDelegate: Kirigami.Heading { id: title level: 1 Layout.fillWidth: true Layout.maximumWidth: implicitWidth + 1 // The +1 is to make sure we do not trigger eliding at max width Layout.minimumWidth: 0 opacity: root.isCurrentPage ? 1 : 0.4 maximumLineCount: 1 elide: Text.ElideRight text: root.title } /** * emitted When the application requests a Back action * For instance a global "back" shortcut or the Android * Back button has been pressed. * The page can manage the back event by itself, * and if it set event.accepted = true, it will stop the main * application to manage the back event. */ signal backRequested(var event); // Look for sheets and cose them //FIXME: port Sheets to Popup? onBackRequested: { for(var i in root.resources) { var item = root.resources[i]; if (item.hasOwnProperty("close") && item.hasOwnProperty("sheetOpen") && item.sheetOpen) { item.close() event.accepted = true; return; } } } /** * globalToolBarItem: Item * The item used as global toolbar for the page * present only if we are in a PageRow as a page or as a layer, * and the style is either Titles or ToolBar * @since 2.5 */ readonly property Item globalToolBarItem: globalToolBar.item /** * The style for the automatically generated global toolbar: by default the Page toolbar is the one set globally in the PageRow in its globalToolBar.style property. * A single page can override the application toolbar style for itself. - * It is discouraged to use this, except very specific exceptions, like a chat + * It is discouraged to use this, except very specific exceptions, like a chat * application which can't have controls on the bottom except the text field. */ property int globalToolBarStyle: { if (globalToolBar.row && !globalToolBar.stack) { return globalToolBar.row.globalToolBar.actualStyle; } else if (globalToolBar.stack) { return Kirigami.Settings.isMobile ? Kirigami.ApplicationHeaderStyle.Titles : Kirigami.ApplicationHeaderStyle.ToolBar; } else { return Kirigami.ApplicationHeaderStyle.None; } } //NOTE: contentItem will be created if not existing (and contentChildren of Page would become its children) This with anchors enforces the geometry we want, where globalToolBar is a super-header, on top of header contentItem: Item { anchors { top: root.header ? root.header.bottom : (globalToolBar.visible ? globalToolBar.bottom : parent.top) topMargin: root.topPadding + root.spacing bottom: root.footer ? root.footer.top : parent.bottom bottomMargin: root.bottomPadding + root.spacing } } background: Rectangle { color: Kirigami.Theme.backgroundColor } implicitHeight: (header ? header.implicitHeight : 0) + (footer ? footer.implicitHeight : 0) + contentItem.implicitHeight + topPadding + bottomPadding implicitWidth: contentItem.implicitWidth + leftPadding + rightPadding //FIXME: on material the shadow would bleed over clip: root.header != null; onHeaderChanged: { if (header) { header.anchors.top = Qt.binding(function() {return globalToolBar.visible ? globalToolBar.bottom : root.top}); } } Component.onCompleted: { headerChanged(); parentChanged(root.parent); } onParentChanged: { if (!parent) { return; } globalToolBar.stack = null; globalToolBar.row = null; if (root.Kirigami.ColumnView.view) { globalToolBar.row = root.Kirigami.ColumnView.view.__pageRow; } if (root.T2.StackView.view) { globalToolBar.stack = root.T2.StackView.view; globalToolBar.row = root.T2.StackView.view ? root.T2.StackView.view.parent : null; } if (globalToolBar.row) { root.globalToolBarStyleChanged.connect(globalToolBar.syncSource); globalToolBar.syncSource(); } } //in data in order for them to not be considered for contentItem, contentChildren, contentData data: [ PageActionPropertyGroup { id: actionsGroup }, Item { id: overlayItem parent: root z: 9997 anchors { fill: parent topMargin: globalToolBar.height } }, //global top toolbar if we are in a PageRow (in the row or as a layer) Loader { id: globalToolBar z: 9999 height: item ? item.implicitHeight : 0 anchors { left: parent.left right: parent.right top: parent.top } property Kirigami.PageRow row property T2.StackView stack visible: active active: (row || stack) && (root.globalToolBarStyle === Kirigami.ApplicationHeaderStyle.ToolBar || root.globalToolBarStyle == Kirigami.ApplicationHeaderStyle.Titles) function syncSource() { if (row && active) { setSource(Qt.resolvedUrl(root.globalToolBarStyle === Kirigami.ApplicationHeaderStyle.ToolBar ? "private/globaltoolbar/ToolBarPageHeader.qml" : "private/globaltoolbar/TitlesPageHeader.qml"), //TODO: find container reliably, remove assumption {"pageRow": Qt.binding(function() {return row}), "page": root, "current": Qt.binding(function() {return stack || row.currentIndex === root.Kirigami.ColumnView.level})}); } } }, //bottom action buttons Loader { id: actionButtons z: 9999 parent: root anchors { left: parent.left right: parent.right bottom: parent.bottom } //It should be T2.Page, Qt 5.7 doesn't like it property Item page: root height: item ? item.implicitHeight : 0 active: typeof applicationWindow !== "undefined" && (!globalToolBar.row || root.globalToolBarStyle !== Kirigami.ApplicationHeaderStyle.ToolBar) && root.actions && (root.actions.main || root.actions.left || root.actions.right || root.actions.contextualActions.length) && //Legacy (typeof applicationWindow === "undefined" || (!applicationWindow().header || applicationWindow().header.toString().indexOf("ToolBarApplicationHeader") === -1) && (!applicationWindow().footer || applicationWindow().footer.toString().indexOf("ToolBarApplicationHeader") === -1)) source: Qt.resolvedUrl("./private/ActionButton.qml") } ] Layout.fillWidth: true } diff --git a/src/controls/PagePoolAction.qml b/src/controls/PagePoolAction.qml index b1205a3f..fe4d5f9b 100644 --- a/src/controls/PagePoolAction.qml +++ b/src/controls/PagePoolAction.qml @@ -1,97 +1,84 @@ /* - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.7 import QtQuick.Controls 2.5 as Controls import org.kde.kirigami 2.11 as Kirigami /** * An action used to load Pages coming from a common PagePool * in a PageRow or QtQuickControls2 StackView * * @inherit Action */ Kirigami.Action { id: root /** * page: string * Url or filename of the page this action will load */ property string page /** * pagePool: Kirigami.PagePool * The PagePool used by this PagePoolAction. * PagePool will make sure only one instance of the page identified by the page url will be created and reused. - *PagePool's lastLoaderUrl property will be used to control the mutual + *PagePool's lastLoaderUrl property will be used to control the mutual * exclusivity of the checked state of the PagePoolAction instances * sharing the same PagePool */ property Kirigami.PagePool pagePool /** * pageStack: Kirigami.PageRow or QtQuickControls2 StackView * The component that will instantiate the pages, which has to work with a stack logic. * Kirigami.PageRow is recommended, but will work with QtQuicControls2 StackView as well. * By default this property is binded to ApplicationWindow's global * pageStack, which is a PageRow by default. */ property Item pageStack: typeof applicationWindow != undefined ? applicationWindow().pageStack : null /** * basePage: Kirigami.Page * The page of pageStack new pages will be pushed after. * All pages present after the given basePage will be removed from the pageStack */ property Controls.Page basePage checked: pagePool && pagePool.resolvedUrl(page) == pagePool.lastLoadedUrl onTriggered: { if (page.length == 0 || !pagePool || !pageStack) { return; } if (pagePool.resolvedUrl(page) == pagePool.lastLoadedUrl) { return; } if (!pageStack.hasOwnProperty("pop") || typeof pageStack.pop !== "function" || !pageStack.hasOwnProperty("push") || typeof pageStack.push !== "function") { return; } if (pagePool.isLocalUrl(page)) { if (basePage) { pageStack.pop(basePage); } else { pageStack.clear(); } pageStack.push(pagePool.loadPage(page)); } else { pagePool.loadPage(page, function(item) { if (basePage) { pageStack.pop(basePage); } else { pageStack.clear(); } pageStack.push(item); }); } } } diff --git a/src/controls/PageRow.qml b/src/controls/PageRow.qml index 1c91a262..e96dc993 100644 --- a/src/controls/PageRow.qml +++ b/src/controls/PageRow.qml @@ -1,626 +1,613 @@ /* - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.5 import QtQuick.Layouts 1.2 import QtQml.Models 2.2 import QtQuick.Templates 2.0 as T import QtQuick.Controls 2.0 as QQC2 import org.kde.kirigami 2.7 import "private/globaltoolbar" as GlobalToolBar import "templates" as KT /** * PageRow implements a row-based navigation model, which can be used * with a set of interlinked information pages. Items are pushed in the * back of the row and the view scrolls until that row is visualized. * A PageRowcan show a single page or a multiple set of columns, depending * on the window width: on a phone a single column should be fullscreen, * while on a tablet or a desktop more than one column should be visible. * @inherit QtQuick.Templates.Control */ T.Control { id: root //BEGIN PROPERTIES /** * This property holds the number of items currently pushed onto the view */ property alias depth: columnView.count /** * The last Page in the Row */ readonly property Item lastItem: columnView.contentChildren.length > 0 ? columnView.contentChildren[columnView.contentChildren.length - 1] : null /** * The currently visible Item */ property alias currentItem: columnView.currentItem /** * the index of the currently visible Item */ property alias currentIndex: columnView.currentIndex /** * The initial item when this PageRow is created */ property variant initialPage /** * The main ColumnView of this Row */ contentItem: columnView /** * items: list * All the items that are present in the PageRow * @since 2.6 */ property alias items: columnView.contentChildren; /** * visibleItems: list * All pages which are visible in the PageRow, excluding those which are scrolled away * @since 2.6 */ property alias visibleItems: columnView.visibleItems /** * firstVisibleItem: Item * The first at least partially visible page in the PageRow, pages before that one will be out of the viewport * @since 2.6 */ property alias firstVisibleItem: columnView.firstVisibleItem /** * lastVisibleItem: Item * The last at least partially visible page in the PageRow, pages after that one will be out of the viewport * @since 2.6 */ property alias lastVisibleItem: columnView.lastVisibleItem /** * The default width for a column * default is wide enough for 30 grid units. * Pages can override it with their Layout.fillWidth, * implicitWidth Layout.minimumWidth etc. */ property int defaultColumnWidth: Units.gridUnit * 20 /** * interactive: bool * If true it will be possible to go back/forward by dragging the * content themselves with a gesture. * Otherwise the only way to go back will be programmatically * default: true */ property alias interactive: columnView.interactive /** * wideMode: bool * If true, the PageRow is wide enough that willshow more than one column at once * @since 5.37 */ readonly property bool wideMode: root.width >= root.defaultColumnWidth*2 && depth >= 2 /** * separatorVisible: bool * True if the separator between pages should be visible * default: true * @since 5.38 */ property alias separatorVisible: columnView.separatorVisible /** * globalToolBar: grouped property * Controls the appearance of an optional global toolbar for the whole PageRow. * It's a grouped property comprised of the following properties: * * style (Kirigami.ApplicationHeaderStyle): can have the following values: * * Auto: depending on application formfactor, it can behave automatically like other values, such as a Breadcrumb on mobile and ToolBar on desktop * * Breadcrumb: it will show a breadcrumb of all the page titles in the stack, for easy navigation * * Titles: each page will only have its own tile on top * * TabBar: the global toolbar will look like a TabBar to select the pages * * ToolBar: each page will have the title on top together buttons and menus to represent all of the page actions: not available on Mobile systems. * * None: no global toolbar will be shown * * * actualStyle: this will represent the actual style of the toolbar: it can be different from style in the case style is Auto * * showNavigationButtons: OR flags combination of ApplicationHeaderStyle.ShowBackButton and ApplicationHeaderStyle.ShowForwardButton * * toolbarActionAlignment: How to horizontally align the actions when using the ToolBar style. Note that anything but Qt.AlignRight will cause the title to be hidden (default: Qt.AlignRight) * * minimumHeight (int): minimum height of the header, which will be resized when scrolling, only in Mobile mode (default: preferredHeight, sliding but no scaling) * * preferredHeight (int): the height the toolbar will usually have * * maximumHeight (int): the height the toolbar will have in mobile mode when the app is in reachable mode (default: preferredHeight * 1.5) * * leftReservedSpace (int, readonly): how many pixels are reserved at the left of the page toolbar (for navigation buttons or drawer handle) * * rightReservedSpace (int, readonly): how many pixels are reserved at the right of the page toolbar (drawer handle) * * @since 5.48 */ readonly property alias globalToolBar: globalToolBar implicitWidth: contentItem.implicitWidth + leftPadding + rightPadding implicitHeight: contentItem.implicitHeight + topPadding + bottomPadding //END PROPERTIES //BEGIN FUNCTIONS /** * Pushes a page on the stack. * The page can be defined as a component, item or string. * If an item is used then the page will get re-parented. - * If a string is used then it is interpreted as a url that is used to load a page + * If a string is used then it is interpreted as a url that is used to load a page * component. * The last pushed page will become the current item. * * @param page The page can also be given as an array of pages. * In this case all those pages will * be pushed onto the stack. The items in the stack can be components, items or * strings just like for single pages. * Additionally an object can be used, which specifies a page and an optional * properties property. * This can be used to push multiple pages while still giving each of * them properties. * When an array is used the transition animation will only be to the last page. * * @param properties The properties argument is optional and allows defining a * map of properties to set on the page. If page is actually an array of pages, properties should also be an array of key/value maps * @return The new created page (or the last one if it was an array) */ function push(page, properties) { var item = insertPage(depth, page, properties); currentIndex = depth - 1; return item; } /** * Inserts a new page or a list of new at an arbitrary position * The page can be defined as a component, item or string. * If an item is used then the page will get re-parented. - * If a string is used then it is interpreted as a url that is used to load a page + * If a string is used then it is interpreted as a url that is used to load a page * component. * The current Page will not be changed, currentIndex will be adjusted * accordingly if needed to keep the same current page. * * @param page The page can also be given as an array of pages. * In this case all those pages will * be pushed onto the stack. The items in the stack can be components, items or * strings just like for single pages. * Additionally an object can be used, which specifies a page and an optional * properties property. * This can be used to push multiple pages while still giving each of * them properties. * When an array is used the transition animation will only be to the last page. * * @param properties The properties argument is optional and allows defining a * map of properties to set on the page. If page is actually an array of pages, properties should also be an array of key/value maps * @return The new created page (or the last one if it was an array) * @since 2.7 */ function insertPage(position, page, properties) { if (!page) { return null } //don't push again things already there if (page.createObject === undefined && typeof page != "string" && columnView.containsItem(page)) { print("The item " + page + " is already in the PageRow"); return null; } position = Math.max(0, Math.min(depth, position)); columnView.pop(columnView.currentItem); // figure out if more than one page is being pushed var pages; var propsArray = []; if (page instanceof Array) { pages = page; page = pages.pop(); //compatibility with pre-qqc1 api, can probably be removed if (page.createObject === undefined && page.parent === undefined && typeof page != "string") { properties = properties || page.properties; page = page.page; } } if (properties instanceof Array) { propsArray = properties; properties = propsArray.pop(); } else { propsArray = [properties]; } // push any extra defined pages onto the stack if (pages) { var i; for (i = 0; i < pages.length; i++) { var tPage = pages[i]; var tProps = propsArray[i]; //compatibility with pre-qqc1 api, can probably be removed if (tPage.createObject === undefined && tPage.parent === undefined && typeof tPage != "string") { if (columnView.containsItem(tPage)) { print("The item " + page + " is already in the PageRow"); continue; } tProps = tPage.properties; tPage = tPage.page; } var pageItem = pagesLogic.initAndInsertPage(position, tPage, tProps); ++position; } } // initialize the page var pageItem = pagesLogic.initAndInsertPage(position, page, properties); pagePushed(pageItem); return pageItem; } /** * Move the page at position fromPos to the new position toPos * If needed, currentIndex will be adjusted * in order to keep the same current page. * @since 2.7 */ function movePage(fromPos, toPos) { columnView.moveItem(fromPos, toPos); } /** - * Remove the given page + * Remove the given page * @param page The page can be given both as integer position or by reference * @return The page that has just been removed * @since 2.7 */ function removePage(page) { if (depth == 0) { return null; } return columnView.removeItem(page); } /** * Pops a page off the stack. * @param page If page is specified then the stack is unwound to that page, * to unwind to the first page specify * page as null. * @return The page instance that was popped off the stack. */ function pop(page) { if (depth == 0) { return null; } return columnView.pop(page); } /** * Emitted when a page has been inserted anywhere * @param position where the page has been inserted * @param page the new page * @since 2.7 */ signal pageInserted(int position, Item page) /** * Emitted when a page has been pushed to the bottom * @param page the new page * @since 2.5 */ signal pagePushed(Item page) /** * Emitted when a page has been removed from the row. * @param page the page that has been removed: at this point it's still valid, * but may be auto deleted soon. * @since 2.5 */ signal pageRemoved(Item page) /** * Replaces a page on the stack. * @param page The page can also be given as an array of pages. * In this case all those pages will * be pushed onto the stack. The items in the stack can be components, items or * strings just like for single pages. * the current page and all pagest after it in the stack will be removed. * Additionally an object can be used, which specifies a page and an optional * properties property. * This can be used to push multiple pages while still giving each of * them properties. * When an array is used the transition animation will only be to the last page. * @param properties The properties argument is optional and allows defining a * map of properties to set on the page. * @see push() for details. */ function replace(page, properties) { if (currentIndex >= 1) { pop(columnView.contentChildren[currentIndex-1]); } else if (currentIndex == 0) { pop(); } else { console.warn("There's no page to replace"); } return push(page, properties); } /** * Clears the page stack. * Destroy (or reparent) all the pages contained. */ function clear() { return columnView.clear(); } /** * @return the page at idx * @param idx the depth of the page we want */ function get(idx) { return columnView.contentChildren[idx]; } /** * go back to the previous index and scroll to the left to show one more column */ function flickBack() { if (depth > 1) { currentIndex = Math.max(0, currentIndex - 1); } } /** * layers: QtQuick.Controls.PageStack * Access to the modal layers. * Sometimes an application needs a modal page that always covers all the rows. * For instance the full screen image of an image viewer or a settings page. * @since 5.38 */ property alias layers: layersStack //END FUNCTIONS onInitialPageChanged: { if (initialPage) { clear(); push(initialPage, null) } } /* onActiveFocusChanged: { if (activeFocus) { layersStack.currentItem.forceActiveFocus() if (columnView.activeFocus) { print("SSS"+columnView.currentItem) columnView.currentItem.forceActiveFocus(); } } } */ Keys.forwardTo: [currentItem] GlobalToolBar.PageRowGlobalToolBarStyleGroup { id: globalToolBar readonly property int leftReservedSpace: globalToolBarUI.item ? globalToolBarUI.item.leftReservedSpace : 0 readonly property int rightReservedSpace: globalToolBarUI.item ? globalToolBarUI.item.rightReservedSpace : 0 readonly property int height: globalToolBarUI.height readonly property Item leftHandleAnchor: globalToolBarUI.item ? globalToolBarUI.item.leftHandleAnchor : null readonly property Item rightHandleAnchor: globalToolBarUI.item ? globalToolBarUI.item.rightHandleAnchor : null } QQC2.StackView { id: layersStack z: 99 anchors { fill: parent } //placeholder as initial item initialItem: columnView function clear () { //don't let it kill the main page row var d = root.depth; for (var i = 1; i < d; ++i) { pop(); - } + } } popEnter: Transition { OpacityAnimator { from: 0 to: 1 duration: Units.longDuration easing.type: Easing.InOutCubic } } popExit: Transition { ParallelAnimation { OpacityAnimator { from: 1 to: 0 duration: Units.longDuration easing.type: Easing.InOutCubic } YAnimator { from: 0 to: height/2 duration: Units.longDuration easing.type: Easing.InCubic } } } pushEnter: Transition { ParallelAnimation { //NOTE: It's a PropertyAnimation instead of an Animator because with an animator the item will be visible for an instant before starting to fade PropertyAnimation { property: "opacity" from: 0 to: 1 duration: Units.longDuration easing.type: Easing.InOutCubic } YAnimator { from: height/2 to: 0 duration: Units.longDuration - easing.type: Easing.OutCubic + easing.type: Easing.OutCubic } } } pushExit: Transition { OpacityAnimator { from: 1 to: 0 duration: Units.longDuration easing.type: Easing.InOutCubic } } replaceEnter: Transition { ParallelAnimation { OpacityAnimator { from: 0 to: 1 duration: Units.longDuration easing.type: Easing.InOutCubic } YAnimator { from: height/2 to: 0 duration: Units.longDuration easing.type: Easing.OutCubic } } } replaceExit: Transition { ParallelAnimation { OpacityAnimator { from: 1 to: 0 duration: Units.longDuration easing.type: Easing.InCubic } YAnimator { from: 0 to: -height/2 duration: Units.longDuration easing.type: Easing.InOutCubic } } } } Loader { id: globalToolBarUI anchors { left: parent.left top: parent.top right: parent.right } z: 100 active: globalToolBar.actualStyle != ApplicationHeaderStyle.None || (firstVisibleItem && firstVisibleItem.globalToolBarStyle == ApplicationHeaderStyle.ToolBar) visible: active height: active ? implicitHeight : 0 source: Qt.resolvedUrl("private/globaltoolbar/PageRowGlobalToolBarUI.qml"); } QtObject { id: pagesLogic readonly property var componentCache: new Array() function initAndInsertPage(position, page, properties) { var pageComp; if (page.createObject) { // page defined as component pageComp = page; } else if (typeof page == "string") { // page defined as string (a url) pageComp = pagesLogic.componentCache[page]; if (!pageComp) { pageComp = pagesLogic.componentCache[page] = Qt.createComponent(page); } } if (pageComp) { // instantiate page from component // FIXME: parent directly to columnView or root? page = pageComp.createObject(null, properties || {}); columnView.insertItem(position, page); if (pageComp.status === Component.Error) { throw new Error("Error while loading page: " + pageComp.errorString()); - } + } } else { // copy properties to the page for (var prop in properties) { if (properties.hasOwnProperty(prop)) { page[prop] = properties[prop]; } } columnView.insertItem(position, page); } return page; } } ColumnView { id: columnView topPadding: globalToolBarUI.item && globalToolBarUI.item.breadcrumbVisible ? globalToolBarUI.height : 0 // Internal hidden api for Page readonly property Item __pageRow: root columnResizeMode: root.wideMode ? ColumnView.FixedColumns : ColumnView.SingleColumn columnWidth: root.defaultColumnWidth onItemInserted: root.pageInserted(position, item); onItemRemoved: root.pageRemoved(item); } Rectangle { anchors.bottom: parent.bottom height: Units.smallSpacing x: (columnView.width - width) * (columnView.contentX / (columnView.contentWidth - columnView.width)) width: columnView.width * (columnView.width/columnView.contentWidth) color: Theme.textColor opacity: 0 onXChanged: { opacity = 0.3 scrollIndicatorTimer.restart(); } Behavior on opacity { OpacityAnimator { duration: Units.longDuration easing.type: Easing.InOutQuad } } Timer { id: scrollIndicatorTimer interval: Units.longDuration * 4 onTriggered: parent.opacity = 0; } } } diff --git a/src/controls/PasswordField.qml b/src/controls/PasswordField.qml index c8c3a962..a55b896f 100644 --- a/src/controls/PasswordField.qml +++ b/src/controls/PasswordField.qml @@ -1,52 +1,39 @@ /* - * Copyright (C) 2019 Carl-Lucien Schwan * + * SPDX-FileCopyrightText: 2019 Carl-Lucien Schwan * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library/Lesser General Public License - * version 2, or (at your option) any later version, as published by the - * Free Software Foundation - * - * This program 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 General Public License for more details - * - * You should have received a copy of the GNU Library/Lesser General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.6 import QtQuick.Controls 2.1 as Controls import org.kde.kirigami 2.7 as Kirigami /** * This is a standard password text field. * * Example usage for the password field component: * @code * import org.kde.kirigami 2.8 as Kirigami * * Kirigami.PasswordField { * id: passwordField * onAccepted: // check if passwordField.text is valid * } * @endcode * * @inherit org.kde.kirgami.ActionTextField */ Kirigami.ActionTextField { id: root property bool showPassword: false echoMode: root.showPassword ? TextInput.Normal : TextInput.Password placeholderText: qsTr("Password") rightActions: [ Kirigami.Action { iconName: root.showPassword ? "password-show-off" : "password-show-on" onTriggered: root.showPassword = !root.showPassword } ] } diff --git a/src/controls/ScrollablePage.qml b/src/controls/ScrollablePage.qml index 1c43ff83..0fce6282 100644 --- a/src/controls/ScrollablePage.qml +++ b/src/controls/ScrollablePage.qml @@ -1,192 +1,179 @@ /* - * Copyright 2015 Marco Martin + * SPDX-FileCopyrightText: 2015 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.1 import QtQuick.Controls 2.1 import QtQuick.Layouts 1.2 import org.kde.kirigami 2.4 import "private" /** * ScrollablePage is a container for all the app pages: everything pushed to the * ApplicationWindow stackView should be a Page or ScrollablePage instance. * This Page subclass is for content that has to be scrolled around, such as * bigger content than the screen that would normally go in a Flickable * or a ListView. * Scrolling and scrolling indicators will be automatically managed * * * @code * ScrollablePage { * id: root * //The rectangle will automatically bescrollable * Rectangle { * width: root.width * height: 99999 * } * } * @endcode * * Another behavior added by this class is a "scroll down to refresh" behavior * It also can give the contents of the flickable to have more top margins in order * to make possible to scroll down the list to reach it with the thumb while using the * phone with a single hand. * * Implementations should handle the refresh themselves as follows * * @code * Kirigami.ScrollablePage { * id: view * supportsRefreshing: true * onRefreshingChanged: { * if (refreshing) { * myModel.refresh(); * } * } * ListView { * //NOTE: MyModel doesn't come from the components, * //it's purely an example on how it can be used together * //some application logic that can update the list model * //and signals when it's done. * model: MyModel { * onRefreshDone: view.refreshing = false; * } * delegate: BasicListItem {} * } * } * [...] * @endcode * */ Page { id: root /** * refreshing: bool * If true the list is asking for refresh and will show a loading spinner. * it will automatically be set to true when the user pulls down enough the list. * This signals the application logic to start its refresh procedure. * The application itself will have to set back this property to false when done. */ property alias refreshing: scrollView.refreshing /** * supportsRefreshing: bool * If true the list supports the "pull down to refresh" behavior. * default is false. */ property alias supportsRefreshing: scrollView.supportsRefreshing /** * flickable: Flickable * The main Flickable item of this page */ property alias flickable: scrollView.flickableItem /** * verticalScrollBarPolicy: Qt.ScrollBarPolicy * The vertical scrollbar policy */ property alias verticalScrollBarPolicy: scrollView.verticalScrollBarPolicy /** * horizontalScrollBarPolicy: Qt.ScrollBarPolicy * The horizontal scrollbar policy */ property alias horizontalScrollBarPolicy: scrollView.horizontalScrollBarPolicy /** * The main content Item of this page. * In the case of a ListView or GridView, both contentItem and flickable * will be a pointer to the ListView (or GridView) * NOTE: can't be contentItem as Page's contentItem is final */ default property QtObject mainItem /** * keyboardNavigationEnabled: bool * If true, and if flickable is an item view, like a ListView or * a GridView, it will be possible to navigate the list current item * to next and previous items with keyboard up/down arrow buttons. * Also, any key event will be forwarded to the current list item. * default is true. */ property bool keyboardNavigationEnabled: true contentHeight: root.flickable.contentHeight implicitHeight: (header ? header.implicitHeight : 0) + (footer ? footer.implicitHeight : 0) + contentHeight + topPadding + bottomPadding implicitWidth: root.flickable.contentItem ? root.flickable.contentItem.implicitWidth : contentItem.implicitWidth + leftPadding + rightPadding Theme.colorSet: flickable && flickable.hasOwnProperty("model") ? Theme.View : Theme.Window clip: true contentItem: RefreshableScrollView { id: scrollView //NOTE: here to not expose it to public api property QtObject oldMainItem page: root clip: true topPadding: contentItem == flickableItem ? 0 : root.topPadding leftPadding: root.leftPadding rightPadding: root.rightPadding + (root.flickable.ScrollBar.vertical && root.flickable.ScrollBar.vertical.visible ? root.flickable.ScrollBar.vertical.width : 0) bottomPadding: contentItem == flickableItem ? 0 : root.bottomPadding anchors { top: root.header ? root.header.bottom //FIXME: for nowassuming globalToolBarItem is in a Loader, which needs to be got rid of : (globalToolBarItem && globalToolBarItem.parent && globalToolBarItem.visible ? globalToolBarItem.parent.bottom : parent.top) bottom: root.footer ? root.footer.top : parent.bottom left: parent.left right: parent.right } } anchors.topMargin: 0 Keys.forwardTo: root.keyboardNavigationEnabled && root.flickable - ? (("currentItem" in root.flickable) && root.flickable.currentItem ? + ? (("currentItem" in root.flickable) && root.flickable.currentItem ? [ root.flickable.currentItem, root.flickable ] : [ root.flickable ]) : [] //HACK to get the mainItem as the last one, all the other eventual items as an overlay //no idea if is the way the user expects onMainItemChanged: { //Duck type for Item if (mainItem.hasOwnProperty("anchors") && mainItem.hasOwnProperty("antialiasing")) { scrollView.contentItem = mainItem mainItem.focus = true //don't try to reparent drawers } else if (mainItem.hasOwnProperty("dragMargin")) { return; //reparent sheets } else if (mainItem.hasOwnProperty("sheetOpen")) { if (mainItem.parent === root || mainItem.parent === null) { mainItem.parent = root; } root.data.push(mainItem); return; } if (scrollView.oldMainItem && scrollView.oldMainItem instanceof Item && (typeof applicationWindow == 'undefined' || scrollView.oldMainItem.parent !== applicationWindow().overlay)) { scrollView.oldMainItem.parent = overlay } scrollView.oldMainItem = mainItem } } diff --git a/src/controls/SearchField.qml b/src/controls/SearchField.qml index c168de9b..26e74022 100644 --- a/src/controls/SearchField.qml +++ b/src/controls/SearchField.qml @@ -1,56 +1,43 @@ /* - * Copyright (C) 2019 Carl-Lucien Schwan * + * SPDX-FileCopyrightText: 2019 Carl-Lucien Schwan * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library/Lesser General Public License - * version 2, or (at your option) any later version, as published by the - * Free Software Foundation - * - * This program 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 General Public License for more details - * - * You should have received a copy of the GNU Library/Lesser General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.6 import QtQuick.Controls 2.1 as Controls import org.kde.kirigami 2.7 as Kirigami /** * This is a standard textfield following KDE HIG. Using Ctrl+F as focus * sequence and "Search..." as placeholder text. * * Example usage for the search field component: * @code * import org.kde.kirigami 2.8 as Kirigami * * Kirigami.SearchField { * id: searchField * onAccepted: console.log("Search text is " + searchField.text) * } * @endcode * * @inherit org.kde.kirgami.ActionTextField */ Kirigami.ActionTextField { id: root placeholderText: qsTr("Search...") focusSequence: "Ctrl+F" rightActions: [ Kirigami.Action { icon.name: root.LayoutMirroring.enabled ? "edit-clear-locationbar-ltr" : "edit-clear-locationbar-rtl" visible: root.text.length > 0 onTriggered: { root.text = "" root.accepted() } } ] } diff --git a/src/controls/Separator.qml b/src/controls/Separator.qml index 18609a3b..ddf0b853 100644 --- a/src/controls/Separator.qml +++ b/src/controls/Separator.qml @@ -1,39 +1,26 @@ /* - * Copyright 2012 Marco Martin - * Copyright 2016 Aleix Pol Gonzalez + * SPDX-FileCopyrightText: 2012 Marco Martin + * SPDX-FileCopyrightText: 2016 Aleix Pol Gonzalez * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.1 import QtQuick.Layouts 1.2 import org.kde.kirigami 2.4 /** * A visual separator * * Useful for splitting one set of items from another. * * @inherit QtQuick.Rectangle */ Rectangle { height: Math.floor(Units.devicePixelRatio) width: Math.floor(Units.devicePixelRatio) Layout.preferredWidth: Math.floor(Units.devicePixelRatio) Layout.preferredHeight: Math.floor(Units.devicePixelRatio) color: Qt.tint(Theme.textColor, Qt.rgba(Theme.backgroundColor.r, Theme.backgroundColor.g, Theme.backgroundColor.b, 0.8)) } diff --git a/src/controls/SwipeListItem.qml b/src/controls/SwipeListItem.qml index da34e9f7..c9d93518 100644 --- a/src/controls/SwipeListItem.qml +++ b/src/controls/SwipeListItem.qml @@ -1,56 +1,43 @@ /* - * Copyright 2010 Marco Martin + * SPDX-FileCopyrightText: 2010 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.5 import "private" import "templates" as T /** * An item delegate Intended to support extra actions obtainable * by uncovering them by dragging away the item with the handle * This acts as a container for normal list items. * Any subclass of AbstractListItem can be assigned as the contentItem property. * @code * ListView { * model: myModel * delegate: SwipeListItem { * QQC2.Label { * text: model.text * } * actions: [ * Action { * icon.name: "document-decrypt" * onTriggered: print("Action 1 clicked") * }, * Action { * icon.name: model.action2Icon * onTriggered: //do something * } * ] * } - * + * * } * @endcode * */ T.SwipeListItem { id: listItem background: DefaultListItemBackground {} } diff --git a/src/controls/Theme.qml b/src/controls/Theme.qml index 8212e7af..b78e9776 100644 --- a/src/controls/Theme.qml +++ b/src/controls/Theme.qml @@ -1,116 +1,103 @@ /* - * Copyright 2015 Marco Martin + * SPDX-FileCopyrightText: 2015 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.4 pragma Singleton /** * A set of named colors for the application - * - * Since frameworks 5.38 Kirigami.Theme has been made an attached + * + * Since frameworks 5.38 Kirigami.Theme has been made an attached * property - the recommended way to interact with the Kirigami.Theme - * object is documented in its replacement found at + * object is documented in its replacement found at * Kirigami::PlatformTheme. * - * Attempting to access the depreciated colors declared here will - * generate warnings. Use the attached properties color sets instead. + * Attempting to access the depreciated colors declared here will + * generate warnings. Use the attached properties color sets instead. * For example, instead of using: - * + * * @code * color: Kirigami.Theme.buttonTextColor * @endcode - * + * * Use the button color set instead: - * + * * @code * Kirigami.Theme.colorSet: Kirigami.Theme.Button * color: Kirigami.Theme.textColor * @endcode - * + * * For more color sets, see Kirigami::PlatformTheme::ColorSet. * @inherit QtQuick.QtObject */ QtObject { id: theme property color textColor: "#31363b" property color disabledTextColor: "#9931363b" property color highlightColor: "#2196F3" property color highlightedTextColor: "#eff0fa" property color backgroundColor: "#eff0f1" property color alternateBackgroundColor: "#bdc3c7" - + property color activeTextColor: "#0176D3" property color activeBackgroundColor: "#0176D3" property color linkColor: "#2196F3" property color linkBackgroundColor: "#2196F3" property color visitedLinkColor: "#2196F3" property color visitedLinkBackgroundColor: "#2196F3" property color negativeTextColor: "#DA4453" property color negativeBackgroundColor: "#DA4453" property color neutralTextColor: "#F67400" property color neutralBackgroundColor: "#F67400" property color positiveTextColor: "#27AE60" property color positiveBackgroundColor: "#27AE60" property color buttonTextColor: "#31363b" property color buttonBackgroundColor: "#eff0f1" property color buttonAlternateBackgroundColor: "#bdc3c7" property color buttonHoverColor: "#2196F3" property color buttonFocusColor: "#2196F3" property color viewTextColor: "#31363b" property color viewBackgroundColor: "#fcfcfc" property color viewAlternateBackgroundColor: "#eff0f1" property color viewHoverColor: "#2196F3" property color viewFocusColor: "#2196F3" property color selectionTextColor: "#eff0fa" property color selectionBackgroundColor: "#2196F3" property color selectionAlternateBackgroundColor: "#1d99f3" property color selectionHoverColor: "#2196F3" property color selectionFocusColor: "#2196F3" property color tooltipTextColor: "#eff0f1" property color tooltipBackgroundColor: "#31363b" property color tooltipAlternateBackgroundColor: "#4d4d4d" property color tooltipHoverColor: "#2196F3" property color tooltipFocusColor: "#2196F3" property color complementaryTextColor: "#eff0f1" property color complementaryBackgroundColor: "#31363b" property color complementaryAlternateBackgroundColor: "#3b4045" property color complementaryHoverColor: "#2196F3" property color complementaryFocusColor: "#2196F3" property font defaultFont: fontMetrics.font property list children: [ TextMetrics { id: fontMetrics } ] function __propagateColorSet(object, context) {} function __propagateTextColor(object, color) {} function __propagateBackgroundColor(object, color) {} function __propagatePrimaryColor(object, color) {} function __propagateAccentColor(object, color) {} } diff --git a/src/controls/ToolBarApplicationHeader.qml b/src/controls/ToolBarApplicationHeader.qml index 13ca4a96..8a5f8799 100644 --- a/src/controls/ToolBarApplicationHeader.qml +++ b/src/controls/ToolBarApplicationHeader.qml @@ -1,134 +1,121 @@ /* - * Copyright 2015 Marco Martin + * SPDX-FileCopyrightText: 2015 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.5 import QtQuick.Controls 2.0 as Controls import QtQuick.Layouts 1.2 import "private" import org.kde.kirigami 2.4 /** * This Application header represents a toolbar that * will display the actions of the current page. * Both Contextual actions and the main, left and right actions */ ApplicationHeader { id: header preferredHeight: 42 maximumHeight: preferredHeight headerStyle: ApplicationHeaderStyle.Titles //FIXME: needs a property definition to have its own type in qml property string _internal: "" Component.onCompleted: print("Warning: ToolbarApplicationHeader is deprecated, remove and use the automatic internal toolbar instead.") pageDelegate: Item { id: delegateItem readonly property bool current: __appWindow.pageStack.currentIndex === index implicitWidth: titleTextMetrics.width/2 + buttonTextMetrics.collapsedButtonsWidth RowLayout { id: titleLayout anchors { verticalCenter: parent.verticalCenter left: parent.left right: actionsLayout.left } Separator { id: separator Layout.preferredHeight: parent.height * 0.6 } Heading { id: title Layout.fillWidth: true Layout.preferredWidth: implicitWidth Layout.minimumWidth: Math.min(titleTextMetrics.width, delegateItem.width - buttonTextMetrics.requiredWidth) leftPadding: Units.largeSpacing opacity: delegateItem.current ? 1 : 0.4 maximumLineCount: 1 color: Theme.textColor elide: Text.ElideRight text: page ? page.title : "" } } TextMetrics { id: titleTextMetrics text: page ? page.title : "" font: title.font } TextMetrics { id: buttonTextMetrics text: (page.actions.left ? page.actions.left.text : "") + (page.actions.main ? page.actions.main.text : "") + (page.actions.right ? page.actions.right.text : "") readonly property int collapsedButtonsWidth: ctxActionsButton.width + (page.actions.left ? ctxActionsButton.width + Units.gridUnit : 0) + (page.actions.main ? ctxActionsButton.width + Units.gridUnit : 0) + (page.actions.right ? ctxActionsButton.width + Units.gridUnit : 0) readonly property int requiredWidth: width + collapsedButtonsWidth } RowLayout { id: actionsLayout anchors { verticalCenter: parent.verticalCenter right: ctxActionsButton.visible ? ctxActionsButton.left : parent.right } readonly property bool toobig: delegateItem.width - titleTextMetrics.width - Units.gridUnit < buttonTextMetrics.requiredWidth PrivateActionToolButton { Layout.alignment: Qt.AlignVCenter kirigamiAction: page && page.actions ? page.actions.left : null showText: !parent.toobig } PrivateActionToolButton { Layout.alignment: Qt.AlignVCenter Layout.rightMargin: Units.smallSpacing kirigamiAction: page && page.actions ? page.actions.main : null showText: !parent.toobig flat: false } PrivateActionToolButton { Layout.alignment: Qt.AlignVCenter kirigamiAction: page && page.actions ? page.actions.right : null showText: !parent.toobig } } PrivateActionToolButton { id: ctxActionsButton showMenuArrow: page.actions.contextualActions.length === 1 anchors { right: parent.right verticalCenter: parent.verticalCenter rightMargin: Units.smallSpacing } Action { id: overflowAction icon.name: "overflow-menu" tooltip: qsTr("More Actions") visible: children.length > 0 children: page && page.actions.contextualActions ? page.actions.contextualActions : null } kirigamiAction: page && page.actions.contextualActions.length === 1 ? page.actions.contextualActions[0] : overflowAction } } } diff --git a/src/controls/Units.qml b/src/controls/Units.qml index ae833309..5639192f 100644 --- a/src/controls/Units.qml +++ b/src/controls/Units.qml @@ -1,136 +1,123 @@ /* - * Copyright 2015 Marco Martin + * SPDX-FileCopyrightText: 2015 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.4 import QtQuick.Window 2.2 import org.kde.kirigami 2.4 pragma Singleton /** * A set of values to define semantically sizes and durations * @inherit QtQuick.QtObject */ QtObject { id: units /** * The fundamental unit of space that should be used for sizes, expressed in pixels. * Given the screen has an accurate DPI settings, it corresponds to a width of * the capital letter M */ property int gridUnit: fontMetrics.height /** * units.iconSizes provides access to platform-dependent icon sizing * * The icon sizes provided are normalized for different DPI, so icons * will scale depending on the DPI. * * Icon sizes from KIconLoader, adjusted to devicePixelRatio: * * small * * smallMedium * * medium * * large * * huge * * enormous * * Not devicePixelRation-adjusted:: * * desktop */ property QtObject iconSizes: QtObject { property int small: Math.floor(fontMetrics.roundedIconSize(16 * devicePixelRatio) * (Settings.isMobile ? 1.5 : 1)) property int smallMedium: Math.floor(fontMetrics.roundedIconSize(22 * devicePixelRatio) * (Settings.isMobile ? 1.5 : 1)) property int medium: Math.floor(fontMetrics.roundedIconSize(32 * devicePixelRatio) * (Settings.isMobile ? 1.5 : 1)) property int large: Math.floor(fontMetrics.roundedIconSize(48 * devicePixelRatio) * (Settings.isMobile ? 1.5 : 1)) property int huge: Math.floor(fontMetrics.roundedIconSize(64 * devicePixelRatio) * (Settings.isMobile ? 1.5 : 1)) property int enormous: Math.floor(128 * devicePixelRatio * (Settings.isMobile ? 1.5 : 1)) } /** * units.smallSpacing is the amount of spacing that should be used around smaller UI elements, * for example as spacing in Columns. Internally, this size depends on the size of * the default font as rendered on the screen, so it takes user-configured font size and DPI * into account. */ property int smallSpacing: Math.floor(gridUnit/4) /** * units.largeSpacing is the amount of spacing that should be used inside bigger UI elements, * for example between an icon and the corresponding text. Internally, this size depends on * the size of the default font as rendered on the screen, so it takes user-configured font * size and DPI into account. */ property int largeSpacing: smallSpacing*2 /** * The ratio between physical and device-independent pixels. This value does not depend on the \ * size of the configured font. If you want to take font sizes into account when scaling elements, * use theme.mSize(theme.defaultFont), units.smallSpacing and units.largeSpacing. * The devicePixelRatio follows the definition of "device independent pixel" by Microsoft. */ property real devicePixelRatio: Math.max(1, ((fontMetrics.font.pixelSize*0.75) / fontMetrics.font.pointSize)) /** * units.longDuration should be used for longer, screen-covering animations, for opening and * closing of dialogs and other "not too small" animations */ property int longDuration: 250 /** * units.shortDuration should be used for short animations, such as accentuating a UI event, * hover events, etc.. */ property int shortDuration: 150 /** * time in ms by which the display of tooltips will be delayed. * * @sa ToolTip.delay property */ property int toolTipDelay: 700 /** * How much the mouse scroll wheel scrolls, expressed in lines of text. * Note: this is strictly for classical mouse wheels, touchpads 2 figer scrolling won't be affected */ readonly property int wheelScrollLines: 3 /** * metrics used by the default font */ property variant fontMetrics: TextMetrics { text: "M" function roundedIconSize(size) { if (size < 16) { return size; } else if (size < 22) { return 16; } else if (size < 32) { return 22; } else if (size < 48) { return 32; } else if (size < 64) { return 48; } else { return size; } } } } diff --git a/src/controls/UrlButton.qml b/src/controls/UrlButton.qml index 2714651f..d3432237 100644 --- a/src/controls/UrlButton.qml +++ b/src/controls/UrlButton.qml @@ -1,55 +1,42 @@ /* - * Copyright (C) 2018 Aleix Pol Gonzalez + * SPDX-FileCopyrightText: 2018 Aleix Pol Gonzalez * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library/Lesser General Public License - * version 2, or (at your option) any later version, as published by the - * Free Software Foundation - * - * This program 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 General Public License for more details - * - * You should have received a copy of the GNU Library/Lesser General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.2 import org.kde.kirigami.private 2.6 as KirigamiPrivate import QtQuick.Controls 2.1 as QQC2 /** * A link button that contains a URL * * It will open the url by default, allow to copy it if triggered with the * secondary mouse button. * * @since 5.63 * @since org.kde.kirigami 2.6 */ LinkButton { id: button property string url text: url visible: text.length > 0 acceptedButtons: Qt.LeftButton | Qt.RightButton onPressed: if (mouse.button === Qt.RightButton) { menu.popup() } onClicked: if (mouse.button !== Qt.RightButton) { Qt.openUrlExternally(url) } QQC2.Menu { id: menu QQC2.MenuItem { text: qsTr("Copy link address") onClicked: KirigamiPrivate.CopyHelperPrivate.copyTextToClipboard(button.url) } } } diff --git a/src/controls/private/ActionButton.qml b/src/controls/private/ActionButton.qml index 0ecdb229..69cfae18 100644 --- a/src/controls/private/ActionButton.qml +++ b/src/controls/private/ActionButton.qml @@ -1,502 +1,489 @@ /* - * Copyright 2015 Marco Martin + * SPDX-FileCopyrightText: 2015 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.1 import QtQuick.Layouts 1.2 import QtQuick.Controls 2.0 as Controls import QtGraphicalEffects 1.0 import org.kde.kirigami 2.7 import "../templates/private" Item { id: root Theme.colorSet: Theme.Button Theme.inherit: false anchors { left: parent.left right: parent.right bottom: parent.bottom bottomMargin: root.page.footer ? root.page.footer.height : 0 } //smallSpacing for the shadow implicitHeight: button.height + Units.smallSpacing clip: true readonly property Page page: root.parent.page //either Action or QAction should work here function isActionAvailable(action) { return action && (action.hasOwnProperty("visible") ? action.visible === undefined || action.visible : !action.hasOwnProperty("visible")); } readonly property QtObject action: root.page && isActionAvailable(root.page.mainAction) ? root.page.mainAction : null readonly property QtObject leftAction: root.page && isActionAvailable(root.page.leftAction) ? root.page.leftAction : null readonly property QtObject rightAction: root.page && isActionAvailable(root.page.rightAction) ? root.page.rightAction : null readonly property bool hasApplicationWindow: typeof applicationWindow !== "undefined" && applicationWindow readonly property bool hasGlobalDrawer: typeof globalDrawer !== "undefined" && globalDrawer readonly property bool hasContextDrawer: typeof contextDrawer !== "undefined" && contextDrawer transform: Translate { id: translateTransform y: mouseArea.internalVisibility ? 0 : button.height Behavior on y { NumberAnimation { duration: Units.longDuration easing.type: mouseArea.internalVisibility == true ? Easing.InQuad : Easing.OutQuad } } } onWidthChanged: button.x = root.width/2 - button.width/2 Item { id: button x: root.width/2 - button.width/2 anchors.bottom: edgeMouseArea.bottom implicitWidth: implicitHeight + Units.iconSizes.smallMedium*2 + Units.gridUnit implicitHeight: Units.iconSizes.medium + Units.largeSpacing * 2 onXChanged: { if (mouseArea.pressed || edgeMouseArea.pressed || fakeContextMenuButton.pressed) { if (root.hasGlobalDrawer && globalDrawer.enabled && globalDrawer.modal) { globalDrawer.peeking = true; globalDrawer.visible = true; globalDrawer.position = Math.min(1, Math.max(0, (x - root.width/2 + button.width/2)/globalDrawer.contentItem.width + mouseArea.drawerShowAdjust)); } if (root.hasContextDrawer && contextDrawer.enabled && contextDrawer.modal) { contextDrawer.peeking = true; contextDrawer.visible = true; contextDrawer.position = Math.min(1, Math.max(0, (root.width/2 - button.width/2 - x)/contextDrawer.contentItem.width + mouseArea.drawerShowAdjust)); } } } MouseArea { id: mouseArea anchors.fill: parent visible: action != null || leftAction != null || rightAction != null property bool internalVisibility: (!root.hasApplicationWindow || (applicationWindow().controlsVisible && applicationWindow().height > root.height*2)) && (root.action === null || root.action.visible === undefined || root.action.visible) preventStealing: true drag { target: button //filterChildren: true axis: Drag.XAxis minimumX: root.hasContextDrawer && contextDrawer.enabled && contextDrawer.modal ? 0 : root.width/2 - button.width/2 maximumX: root.hasGlobalDrawer && globalDrawer.enabled && globalDrawer.modal ? root.width : root.width/2 - button.width/2 } property var downTimestamp; property int startX property int startMouseY property real drawerShowAdjust readonly property int currentThird: (3*mouseX)/width readonly property QtObject actionUnderMouse: { switch(currentThird) { case 0: return leftAction; case 1: return action; case 2: return rightAction; default: return null } } hoverEnabled: true Controls.ToolTip.visible: containsMouse && !Settings.tabletMode && actionUnderMouse Controls.ToolTip.text: actionUnderMouse ? actionUnderMouse.text : "" Controls.ToolTip.delay: Units.toolTipDelay onPressed: { //search if we have a page to set to current if (root.hasApplicationWindow && applicationWindow().pageStack.currentIndex !== undefined && root.page.ColumnView.level !== undefined) { //search the button parent's parent, that is the page parent //this will make the context drawer open for the proper page applicationWindow().pageStack.currentIndex = root.page.ColumnView.level; } downTimestamp = (new Date()).getTime(); startX = button.x + button.width/2; startMouseY = mouse.y; drawerShowAdjust = 0; } onReleased: { if (root.hasGlobalDrawer) globalDrawer.peeking = false; if (root.hasContextDrawer) contextDrawer.peeking = false; //pixel/second var x = button.x + button.width/2; var speed = ((x - startX) / ((new Date()).getTime() - downTimestamp) * 1000); drawerShowAdjust = 0; //project where it would be a full second in the future if (root.hasContextDrawer && root.hasGlobalDrawer && globalDrawer.modal && x + speed > Math.min(root.width/4*3, root.width/2 + globalDrawer.contentItem.width/2)) { globalDrawer.open(); contextDrawer.close(); } else if (root.hasContextDrawer && x + speed < Math.max(root.width/4, root.width/2 - contextDrawer.contentItem.width/2)) { if (root.hasContextDrawer && contextDrawer.modal) { contextDrawer.open(); } if (root.hasGlobalDrawer && globalDrawer.modal) { globalDrawer.close(); } } else { if (root.hasGlobalDrawer && globalDrawer.modal) { globalDrawer.close(); } if (root.hasContextDrawer && contextDrawer.modal) { contextDrawer.close(); } } //Don't rely on native onClicked, but fake it here: //Qt.startDragDistance is not adapted to devices dpi in case //of Android, so consider the button "clicked" when: //*the button has been dragged less than a gridunit //*the finger is still on the button if (Math.abs((button.x + button.width/2) - startX) < Units.gridUnit && mouse.y > 0) { if (!actionUnderMouse) { return; } //if an action has been assigned, trigger it if (actionUnderMouse && actionUnderMouse.trigger) { actionUnderMouse.trigger(); } - + if (actionUnderMouse.hasOwnProperty("children") && actionUnderMouse.children.length > 0) { var subMenuUnderMouse; switch (actionUnderMouse) { case leftAction: subMenuUnderMouse = leftActionSubMenu; break; case mainAction: subMenuUnderMouse = mainActionSubMenu; break case rightAction: subMenuUnderMouse = rightActionSubMenu; break; } if (subMenuUnderMouse && !subMenuUnderMouse.visible) { subMenuUnderMouse.visible = true; } } } } onPositionChanged: { drawerShowAdjust = Math.min(0.3, Math.max(0, (startMouseY - mouse.y)/(Units.gridUnit*15))); button.xChanged(); } onPressAndHold: { if (!actionUnderMouse) { return; } //if an action has been assigned, show a message like a tooltip if (actionUnderMouse && actionUnderMouse.text && Settings.tabletMode) { Controls.ToolTip.show(actionUnderMouse.text, 3000) } } Connections { target: root.hasGlobalDrawer ? globalDrawer : null onPositionChanged: { if ( globalDrawer && globalDrawer.modal && !mouseArea.pressed && !edgeMouseArea.pressed && !fakeContextMenuButton.pressed) { button.x = globalDrawer.contentItem.width * globalDrawer.position + root.width/2 - button.width/2; } } } Connections { target: root.hasContextDrawer ? contextDrawer : null onPositionChanged: { if (contextDrawer && contextDrawer.modal && !mouseArea.pressed && !edgeMouseArea.pressed && !fakeContextMenuButton.pressed) { button.x = root.width/2 - button.width/2 - contextDrawer.contentItem.width * contextDrawer.position; } } } Item { id: background anchors { fill: parent } Rectangle { id: buttonGraphics radius: width/2 anchors.centerIn: parent height: parent.height - Units.smallSpacing*2 width: height enabled: root.action && root.action.enabled visible: root.action readonly property bool pressed: root.action && root.action.enabled && ((root.action == mouseArea.actionUnderMouse && mouseArea.pressed) || root.action.checked) property color baseColor: root.action && root.action.icon && root.action.icon.color && root.action.icon.color != undefined && root.action.icon.color.a > 0 ? root.action.icon.color : Theme.highlightColor color: pressed ? Qt.darker(baseColor, 1.3) : baseColor ActionsMenu { id: mainActionSubMenu y: -height x: -width/2 + parent.width/2 actions: root.action && root.action.hasOwnProperty("children") ? root.action.children : "" submenuComponent: Component { ActionsMenu {} } } Icon { id: icon anchors.centerIn: parent width: Units.iconSizes.smallMedium height: width source: root.action && root.action.icon.name ? root.action.icon.name : "" selected: true color: root.action && root.action.color && root.action.color.a > 0 ? root.action.color : (selected ? Theme.highlightedTextColor : Theme.textColor) } Behavior on color { ColorAnimation { duration: Units.longDuration easing.type: Easing.InOutQuad } } Behavior on x { NumberAnimation { duration: Units.longDuration easing.type: Easing.InOutQuad } } } //left button Rectangle { id: leftButtonGraphics z: -1 anchors { left: parent.left bottom: parent.bottom bottomMargin: Units.smallSpacing } enabled: root.leftAction && root.leftAction.enabled radius: Units.devicePixelRatio*2 height: Units.iconSizes.smallMedium + Units.smallSpacing * 2 width: height + (root.action ? Units.gridUnit*2 : 0) visible: root.leftAction readonly property bool pressed: root.leftAction && root.leftAction.enabled && ((mouseArea.actionUnderMouse == root.leftAction && mouseArea.pressed) || root.leftAction.checked) property color baseColor: root.leftAction && root.leftAction.icon && root.leftAction.icon.color && root.leftAction.icon.color != undefined && root.leftAction.icon.color.a > 0 ? root.leftAction.icon.color : Theme.highlightColor color: pressed ? baseColor : Theme.backgroundColor Behavior on color { ColorAnimation { duration: Units.longDuration easing.type: Easing.InOutQuad } } ActionsMenu { id: leftActionSubMenu y: -height x: -width/2 + parent.width/2 actions: root.leftAction && root.leftAction.hasOwnProperty("children") ? root.leftAction.children : "" submenuComponent: Component { ActionsMenu {} } } Icon { source: root.leftAction && root.leftAction.icon.name ? root.leftAction.icon.name : "" width: Units.iconSizes.smallMedium height: width selected: leftButtonGraphics.pressed color: root.action && root.action.color && root.action.color.a > 0 ? root.action.color : (selected ? Theme.highlightedTextColor : Theme.textColor) anchors { left: parent.left verticalCenter: parent.verticalCenter margins: Units.smallSpacing * 2 } } } //right button Rectangle { id: rightButtonGraphics z: -1 anchors { right: parent.right //verticalCenter: parent.verticalCenter bottom: parent.bottom bottomMargin: Units.smallSpacing } enabled: root.rightAction && root.rightAction.enabled radius: Units.devicePixelRatio*2 height: Units.iconSizes.smallMedium + Units.smallSpacing * 2 width: height + (root.action ? Units.gridUnit*2 : 0) visible: root.rightAction readonly property bool pressed: root.rightAction && root.rightAction.enabled && ((mouseArea.actionUnderMouse == root.rightAction && mouseArea.pressed) || root.rightAction.checked) property color baseColor: root.rightAction && root.rightAction.icon && root.rightAction.icon.color && root.rightAction.icon.color != undefined && root.rightAction.icon.color.a > 0 ? root.rightAction.icon.color : Theme.highlightColor color: pressed ? baseColor : Theme.backgroundColor Behavior on color { ColorAnimation { duration: Units.longDuration easing.type: Easing.InOutQuad } } ActionsMenu { id: rightActionSubMenu y: -height x: -width/2 + parent.width/2 actions: root.rightAction && root.rightAction.hasOwnProperty("children") ? root.rightAction.children : "" submenuComponent: Component { ActionsMenu {} } } Icon { source: root.rightAction && root.rightAction.icon.name ? root.rightAction.icon.name : "" width: Units.iconSizes.smallMedium height: width selected: rightButtonGraphics.pressed color: root.action && root.action.color && root.action.color.a > 0 ? root.action.color : (selected ? Theme.highlightedTextColor : Theme.textColor) anchors { right: parent.right verticalCenter: parent.verticalCenter margins: Units.smallSpacing * 2 } } } } DropShadow { anchors.fill: background horizontalOffset: 0 verticalOffset: Units.devicePixelRatio radius: Units.gridUnit /2 samples: 16 color: Qt.rgba(0, 0, 0, mouseArea.pressed ? 0.6 : 0.4) source: background } } } MouseArea { id: fakeContextMenuButton anchors { right: edgeMouseArea.right bottom: parent.bottom margins: Units.smallSpacing } drag { target: button axis: Drag.XAxis minimumX: root.hasContextDrawer && contextDrawer.enabled && contextDrawer.modal ? 0 : root.width/2 - button.width/2 maximumX: root.hasGlobalDrawer && globalDrawer.enabled && globalDrawer.modal ? root.width : root.width/2 - button.width/2 } visible: root.page.actions && root.page.actions.contextualActions.length > 0 && (applicationWindow === undefined || applicationWindow().wideScreen) //using internal pagerow api && (root.page && root.page.parent ? root.page.ColumnView.level < applicationWindow().pageStack.depth-1 : false) width: Units.iconSizes.smallMedium + Units.smallSpacing*2 height: width DropShadow { anchors.fill: handleGraphics horizontalOffset: 0 verticalOffset: Units.devicePixelRatio radius: Units.gridUnit /2 samples: 16 color: Qt.rgba(0, 0, 0, fakeContextMenuButton.pressed ? 0.6 : 0.4) source: handleGraphics } Rectangle { id: handleGraphics anchors.fill: parent color: fakeContextMenuButton.pressed ? Theme.highlightColor : Theme.backgroundColor radius: Units.devicePixelRatio Icon { anchors.centerIn: parent width: Units.iconSizes.smallMedium selected: fakeContextMenuButton.pressed height: width source: "overflow-menu" } Behavior on color { ColorAnimation { duration: Units.longDuration easing.type: Easing.InOutQuad } } } onPressed: { mouseArea.onPressed(mouse) } onReleased: { if (globalDrawer) { globalDrawer.peeking = false; } if (contextDrawer) { contextDrawer.peeking = false; } var pos = root.mapFromItem(fakeContextMenuButton, mouse.x, mouse.y); if (contextDrawer) { if (pos.x < root.width/2) { contextDrawer.open(); } else if (contextDrawer.drawerOpen && mouse.x > 0 && mouse.x < width) { contextDrawer.close(); } } if (globalDrawer) { if (globalDrawer.position > 0.5) { globalDrawer.open(); } else { globalDrawer.close(); } } if (containsMouse && (!globalDrawer || !globalDrawer.drawerOpen || !globalDrawer.modal) && (!contextDrawer || !contextDrawer.drawerOpen || !contextDrawer.modal)) { contextMenu.visible = !contextMenu.visible; } } ActionsMenu { id: contextMenu x: parent.width - width y: -height actions: root.page.actions.contextualActions submenuComponent: Component { ActionsMenu {} } } } MouseArea { id: edgeMouseArea z:99 anchors { left: parent.left right: parent.right bottom: parent.bottom } drag { target: button //filterChildren: true axis: Drag.XAxis minimumX: root.hasContextDrawer && contextDrawer.enabled && contextDrawer.modal ? 0 : root.width/2 - button.width/2 maximumX: root.hasGlobalDrawer && globalDrawer.enabled && globalDrawer.modal ? root.width : root.width/2 - button.width/2 } height: Units.smallSpacing * 3 onPressed: mouseArea.onPressed(mouse) onPositionChanged: mouseArea.positionChanged(mouse) onReleased: mouseArea.released(mouse) } } diff --git a/src/controls/private/ActionIconGroup.qml b/src/controls/private/ActionIconGroup.qml index 19a5ea19..c64d75ef 100644 --- a/src/controls/private/ActionIconGroup.qml +++ b/src/controls/private/ActionIconGroup.qml @@ -1,29 +1,16 @@ /* - * Copyright 2017 Marco Martin + * SPDX-FileCopyrightText: 2017 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQml 2.1 QtObject { property string name property string source property int width property int height property color color: Qt.rgba(0, 0, 0, 0) } diff --git a/src/controls/private/ActionMenuItem.qml b/src/controls/private/ActionMenuItem.qml index 75466953..3654210f 100644 --- a/src/controls/private/ActionMenuItem.qml +++ b/src/controls/private/ActionMenuItem.qml @@ -1,34 +1,21 @@ /* - * Copyright 2018 Aleix Pol Gonzalez + * SPDX-FileCopyrightText: 2018 Aleix Pol Gonzalez * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.3 import QtQuick.Controls 2.3 as Controls import org.kde.kirigami 2.4 as Kirigami Controls.MenuItem { id: menuItem visible: action.visible === undefined || action.visible height: visible ? implicitHeight : 0 autoExclusive: action.Controls.ActionGroup && action.Controls.ActionGroup.group && action.Controls.ActionGroup.group.exclusive Controls.ToolTip.text: action.tooltip || "" Controls.ToolTip.visible: menuItem.hovered && Controls.ToolTip.text.length>0 Controls.ToolTip.delay: Kirigami.Units.toolTipDelay } diff --git a/src/controls/private/ActionToolBarLayoutDetails.qml b/src/controls/private/ActionToolBarLayoutDetails.qml index 655d03cd..99b9e64d 100644 --- a/src/controls/private/ActionToolBarLayoutDetails.qml +++ b/src/controls/private/ActionToolBarLayoutDetails.qml @@ -1,173 +1,160 @@ /* - * Copyright 2018 Marco Martin - * Copyright 2019 Arjen Hiemstra + * SPDX-FileCopyrightText: 2018 Marco Martin + * SPDX-FileCopyrightText: 2019 Arjen Hiemstra * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.7 import QtQuick.Layouts 1.2 import QtQuick.Controls 2.4 as Controls import org.kde.kirigami 2.5 as Kirigami /** * This fairly complex thing determines the layout of ActionToolBar, that is, * which actions should be displayed in full width toolbutton form, which should * be displayed in an icon-only reduced size and which should be placed in the * overflow menu. * * It makes use of two fairly static layouts, one contains all actions in expanded * full-size form, the other in reduced icon-only form. The items in these layouts * determine if they should be visible based on their relative position and size * and some properties of the action. The update function then goes through these * items, adding the actions to the relevant lists, so they can be used by the * ActionToolBar to determine which action should be visible in what state. * * The reason for using two separate layouts from ActionToolBar's main layout is * so that we can use the actual geometry of the buttons to calculate the * visibility, completely disregarding any other layouting quirks. We can then * use that information so only the relevant things in the ActionToolBar are * visible. This allows the main ActionToolBar to use normal layout features for * things like the positioning of the visible actions. */ Item { id: details property var actions property var visibleActions: [] property var hiddenActions: [] property var iconOnlyActions: [] property bool flat: false property int display: Controls.Button.TextBesideIcon property real spacing: Kirigami.Units.smallSpacing property real leftPadding: 0 property real rightPadding: 0 property real iconOnlyWidth: 0 readonly property real iconLayoutWidth: width - rightPadding readonly property real fullLayoutWidth: iconLayoutWidth - iconOnlyWidth readonly property real maximumWidth: fullSizePlaceholderLayout.width + leftPadding + rightPadding enabled: false opacity: 0 // Cannot use visible: false because then relayout doesn't happen correctly function update() { var visible = [] var hidden = [] var iconOnly = [] var iconsWidth = 0 for (var i = 0; i < root.actions.length; ++i) { var item = fullSizePlaceholderRepeater.itemAt(i) var iconOnlyItem = iconOnlyPlaceholderRepeater.itemAt(i) if (item.actionVisible) { visible.push(item.kirigamiAction) } else if (iconOnlyItem.actionVisible) { visible.push(item.kirigamiAction) iconOnly.push(item.kirigamiAction) iconsWidth += iconOnlyItem.width + details.spacing } else { hidden.push(item.kirigamiAction) } } visibleActions = visible hiddenActions = hidden iconOnlyActions = iconOnly iconOnlyWidth = iconsWidth } onWidthChanged: Qt.callLater(update) Component.onCompleted: Qt.callLater(update) RowLayout { id: fullSizePlaceholderLayout spacing: details.spacing // This binding is here to take care of things like visibility changes onWidthChanged: Qt.callLater(details.update) Repeater { id: fullSizePlaceholderRepeater model: details.actions Loader { property var kirigamiAction: modelData sourceComponent: { if (modelData.displayComponent && !modelData.displayHintSet(Kirigami.Action.DisplayHint.IconOnly)) { return modelData.displayComponent } return toolButtonDelegate } visible: (modelData.visible === undefined || modelData.visible) && (modelData.displayHint !== undefined && !modelData.displayHintSet(Kirigami.Action.DisplayHint.AlwaysHide)) property bool actionVisible: visible && (x + width < details.fullLayoutWidth) onLoaded: { if (sourceComponent == toolButtonDelegate) { item.kirigamiAction = modelData } } } } } Component { id: toolButtonDelegate PrivateActionToolButton { flat: details.flat && !kirigamiAction.icon.color.a display: details.display menu.actions: { if (kirigamiAction.displayComponent && kirigamiAction.displayHintSet(Kirigami.Action.DisplayHint.IconOnly)) { return [kirigamiAction] } if (kirigamiAction.children) { return kirigamiAction.children } return [] } } } RowLayout { id: iconOnlyPlaceholderLayout spacing: details.spacing Repeater { id: iconOnlyPlaceholderRepeater model: details.actions PrivateActionToolButton { flat: details.flat && !modelData.icon.color.a display: Controls.Button.IconOnly visible: (modelData.visible === undefined || modelData.visible) && (modelData.displayHint !== undefined && modelData.displayHintSet(Kirigami.Action.DisplayHint.KeepVisible)) kirigamiAction: modelData property bool actionVisible: visible && (iconOnlyPlaceholderRepeater.count === 1 || (x + width < details.iconLayoutWidth)) } } } } diff --git a/src/controls/private/ActionsMenu.qml b/src/controls/private/ActionsMenu.qml index 670d26b5..65417c89 100644 --- a/src/controls/private/ActionsMenu.qml +++ b/src/controls/private/ActionsMenu.qml @@ -1,82 +1,69 @@ /* - * Copyright 2018 Aleix Pol Gonzalez + * SPDX-FileCopyrightText: 2018 Aleix Pol Gonzalez * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.3 import QtQuick.Controls 2.4 as Controls import org.kde.kirigami 2.4 as Kirigami Controls.Menu { id: theMenu z: 999999999 property alias actions: actionsInstantiator.model property Component submenuComponent //renamed to work on both Qt 5.9 and 5.10 property Component itemDelegate: ActionMenuItem {} property Component separatorDelegate: Controls.MenuSeparator { } property Component loaderDelegate: Loader { property var kirigamiAction } property Controls.Action parentAction property Controls.MenuItem parentItem Item { id: invisibleItems visible: false } Instantiator { id: actionsInstantiator active: theMenu.visible delegate: QtObject { readonly property Controls.Action action: modelData property QtObject item: null function create() { if (!action.hasOwnProperty("children") && !action.children || action.children.length === 0) { if (action.hasOwnProperty("separator") && action.separator) { item = theMenu.separatorDelegate.createObject(null, {}); } else if (action.displayComponent) { item = theMenu.loaderDelegate.createObject(null, { kirigamiAction: action, sourceComponent: action.displayComponent }); } else { item = theMenu.itemDelegate.createObject(null, { action: action }); } theMenu.addItem(item) } else if (theMenu.submenuComponent) { item = theMenu.submenuComponent.createObject(null, { parentAction: action, title: action.text, actions: action.children }); theMenu.insertMenu(theMenu.count, item) item.parentItem = theMenu.contentData[theMenu.contentData.length-1] item.parentItem.icon = action.icon } } function remove() { if (!action.hasOwnProperty("children") && !action.children || action.children.length === 0) { theMenu.removeItem(item) } else if (theMenu.submenuComponent) { theMenu.removeMenu(item) } } } onObjectAdded: object.create() onObjectRemoved: object.remove() } } diff --git a/src/controls/private/BannerImage.qml b/src/controls/private/BannerImage.qml index 7ce46c4f..877afae9 100644 --- a/src/controls/private/BannerImage.qml +++ b/src/controls/private/BannerImage.qml @@ -1,152 +1,139 @@ /* - * Copyright 2018 Marco Martin + * SPDX-FileCopyrightText: 2018 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.6 import QtQuick.Layouts 1.2 import QtGraphicalEffects 1.0 import org.kde.kirigami 2.4 as Kirigami /** * This Component is used as the header of GlobalDrawer and as the header * of Card, It can be accessed there as a grouped property but can never * be instantiated directly */ Image { id: root /* * FIXME: compatibility */ property alias imageSource: root.source property alias iconSource: root.titleIcon - + /** * title: string * A title to be displayed on top of the image */ property alias title: heading.text /** * icon: var * An icon to be displayed alongside the title. * It can be a QIcon, a fdo-compatible icon name, or any url understood by Image */ property alias titleIcon: headingIcon.source /** * titleAlignment: Qt.Alignment */ property int titleAlignment: Qt.AlignTop | Qt.AlignLeft /** * titleLevel: a Kirigami Heading level, default 1 */ property alias titleLevel: heading.level /** * wrapMode: if the header should be able to do wrapping */ property alias titleWrapMode: heading.wrapMode property int leftPadding: headingIcon.valid ? Kirigami.Units.smallSpacing * 2 : Kirigami.Units.largeSpacing property int topPadding: headingIcon.valid ? Kirigami.Units.smallSpacing * 2 : Kirigami.Units.largeSpacing property int rightPadding: headingIcon.valid ? Kirigami.Units.smallSpacing * 2 : Kirigami.Units.largeSpacing property int bottomPadding: headingIcon.valid ? Kirigami.Units.smallSpacing * 2 : Kirigami.Units.largeSpacing Layout.fillWidth: true Layout.preferredWidth: titleLayout.implicitWidth || sourceSize.width Layout.preferredHeight: titleLayout.completed && source != "" ? width/(sourceSize.width / sourceSize.height) : Layout.minimumHeight Layout.minimumHeight: titleLayout.implicitHeight > 0 ? titleLayout.implicitHeight + Kirigami.Units.smallSpacing * 2 : 0 property int implicitWidth: Layout.preferredWidth readonly property bool empty: bannerImage.title !== undefined && bannerImage.title.length === 0 && bannerImage.source !== undefined && bannerImage.source.length === 0 && bannerImage.titleIcon !== undefined &&bannerImage.titleIcon.length === 0 fillMode: Image.PreserveAspectCrop asynchronous: true Component.onCompleted: { titleLayout.completed = true; } LinearGradient { anchors { left: parent.left right: parent.right - top: root.status != + top: root.status != Image.Ready || (root.titleAlignment & Qt.AlignTop) ? parent.top : undefined - bottom: root.status != + bottom: root.status != Image.Ready || (root.titleAlignment & Qt.AlignBottom) ? parent.bottom : undefined } visible: root.source != "" && root.title != "" && ((root.titleAlignment & Qt.AlignTop) || (root.titleAlignment & Qt.AlignBottom)) height: Math.min(parent.height, titleLayout.height * 2) start: Qt.point(0, 0) end: Qt.point(0, height) gradient: Gradient { GradientStop { position: (root.titleAlignment & Qt.AlignTop) ? 0.0 : 1.0 color: Qt.rgba(0, 0, 0, 0.8) } GradientStop { position: (root.titleAlignment & Qt.AlignTop) ? 1.0 : 0.0 - color: Qt.rgba(0, 0, 0, root.status == + color: Qt.rgba(0, 0, 0, root.status == Image.Ready ? 0 : 0.3) } } } RowLayout { id: titleLayout property bool completed: false anchors { left: root.titleAlignment & Qt.AlignLeft ? parent.left : undefined top: root.titleAlignment & Qt.AlignTop ? parent.top : undefined right: root.titleAlignment & Qt.AlignRight ? parent.right : undefined bottom: root.titleAlignment & Qt.AlignBottom ? parent.bottom : undefined horizontalCenter: root.titleAlignment & Qt.AlignHCenter ? parent.horizontalCenter : undefined verticalCenter: root.titleAlignment & Qt.AlignVCenter ? parent.verticalCenter : undefined leftMargin: root.leftPadding topMargin: root.topPadding rightMargin: root.rightPadding bottomMargin: root.bottomPadding } width: Math.min(implicitWidth, parent.width -root.leftPadding - root.rightPadding) height: Math.min(implicitHeight, parent.height - root.topPadding - root.bottomPadding) Kirigami.Icon { id: headingIcon Layout.minimumWidth: Kirigami.Units.iconSizes.large Layout.minimumHeight: width visible: valid isMask: false } Kirigami.Heading { id: heading Layout.fillWidth: true Layout.fillHeight: true visible: text.length > 0 level: 1 color: source != "" ? "white" : Kirigami.Theme.textColor wrapMode: Text.NoWrap elide: Text.ElideRight } } } diff --git a/src/controls/private/CardsGridViewPrivate.qml b/src/controls/private/CardsGridViewPrivate.qml index 1b82e85d..cc3fe2fe 100644 --- a/src/controls/private/CardsGridViewPrivate.qml +++ b/src/controls/private/CardsGridViewPrivate.qml @@ -1,39 +1,26 @@ /* - * Copyright 2018 Marco Martin + * SPDX-FileCopyrightText: 2018 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.10 import QtQuick.Controls 2.0 as Controls import QtQuick.Layouts 1.2 import org.kde.kirigami 2.4 as Kirigami GridView { id: root property Component _delegateComponent delegate: Kirigami.DelegateRecycler { width: Math.min(root.cellWidth, root.maximumColumnWidth) - Kirigami.Units.largeSpacing * 2 //in grid views align the cells in the middle anchors.left: parent.left anchors.leftMargin: (width + Kirigami.Units.largeSpacing*2) * (index % root.columns ) + root.width/2 - (root.columns*(width + Kirigami.Units.largeSpacing*2))/2 sourceComponent: root._delegateComponent } } diff --git a/src/controls/private/CardsListViewPrivate.qml b/src/controls/private/CardsListViewPrivate.qml index c76d641a..9e72188b 100644 --- a/src/controls/private/CardsListViewPrivate.qml +++ b/src/controls/private/CardsListViewPrivate.qml @@ -1,36 +1,23 @@ /* - * Copyright 2018 Marco Martin + * SPDX-FileCopyrightText: 2018 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.10 import QtQuick.Controls 2.0 as Controls import org.kde.kirigami 2.4 as Kirigami ListView { id: root rightMargin: Kirigami.Units.largeSpacing * 2 leftMargin: Kirigami.Units.largeSpacing * 2 property Component _delegateComponent delegate: Kirigami.DelegateRecycler { width: root.width - root.leftMargin - root.rightMargin sourceComponent: root._delegateComponent } } diff --git a/src/controls/private/ContextDrawerActionItem.qml b/src/controls/private/ContextDrawerActionItem.qml index f33094c1..208d2906 100644 --- a/src/controls/private/ContextDrawerActionItem.qml +++ b/src/controls/private/ContextDrawerActionItem.qml @@ -1,93 +1,80 @@ /* - * Copyright 2019 Marco Martin + * SPDX-FileCopyrightText: 2019 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.6 import QtQuick.Controls 2.0 as QQC2 import QtQuick.Layouts 1.2 import org.kde.kirigami 2.5 BasicListItem { id: listItem readonly property bool isSeparator: modelData.hasOwnProperty("separator") && modelData.separator readonly property bool isExpandible: modelData && modelData.hasOwnProperty("expandible") && modelData.expandible checked: modelData.checked icon: modelData.icon separatorVisible: false reserveSpaceForIcon: !isSeparator reserveSpaceForLabel: !isSeparator label: model ? (model.tooltip ? model.tooltip : model.text) : (modelData.tooltip ? modelData.tooltip : modelData.text) hoverEnabled: (!isExpandible || root.collapsed) && !Settings.tabletMode sectionDelegate: isExpandible font.pointSize: isExpandible ? Theme.defaultFont.pointSize * 1.30 : Theme.defaultFont.pointSize enabled: !isExpandible && !isSeparator && (model ? model.enabled : modelData.enabled) visible: model ? model.visible : modelData.visible opacity: enabled || isExpandible ? 1.0 : 0.6 Separator { id: separatorAction visible: listItem.isSeparator Layout.fillWidth: true - + ActionsMenu { id: actionsMenu y: Settings.isMobile ? -height : listItem.height z: 9999 actions: modelData.children submenuComponent: Component { ActionsMenu {} } } } Icon { isMask: true Layout.alignment: Qt.AlignVCenter Layout.preferredHeight: Units.iconSizes.small/2 selected: listItem.checked || listItem.pressed Layout.preferredWidth: Layout.preferredHeight source: "go-up-symbolic" visible: !isExpandible && !listItem.isSeparator && modelData.children!== undefined && modelData.children.length > 0 } onPressed: { if (modelData.children.length > 0) { actionsMenu.open(); } } onClicked: { if (modelData.children.length === 0) { root.drawerOpen = false; } if (modelData && modelData.trigger !== undefined) { modelData.trigger(); // assume the model is a list of QAction or Action } else if (menu.model.length > index) { menu.model[index].trigger(); } else { console.warning("Don't know how to trigger the action") } } } diff --git a/src/controls/private/CornerShadow.qml b/src/controls/private/CornerShadow.qml index ac9591ac..2f18d8e4 100644 --- a/src/controls/private/CornerShadow.qml +++ b/src/controls/private/CornerShadow.qml @@ -1,86 +1,73 @@ /* - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.1 import QtGraphicalEffects 1.0 import org.kde.kirigami 2.4 RadialGradient { id: shadow /** * corner: enumeration * This property holds the corner of the shadow that will determine * the direction of the gradient. * The acceptable values are: * Qt.TopLeftCorner, TopRightCorner, BottomLeftCorner, BottomRightCorner */ property int corner: Qt.TopRightCorner readonly property real margin: -Math.floor(radius/3) property int radius: Units.gridUnit width: radius - margin height: radius - margin horizontalRadius: width verticalRadius: height horizontalOffset: { switch (corner) { case Qt.TopLeftCorner: case Qt.BottomLeftCorner: - return -width/2; + return -width/2; default: return width/2; } } verticalOffset: { switch (corner) { case Qt.TopLeftCorner: case Qt.TopRightCorner: - return -width/2; + return -width/2; default: return width/2; } } gradient: Gradient { GradientStop { position: 0.0 color: Qt.rgba(0, 0, 0, 0.25) } GradientStop { position: 1 - radius/(radius - margin) color: Qt.rgba(0, 0, 0, 0.25) } GradientStop { position: 1 - radius/(radius - margin) + radius/(radius - margin) * 0.2 color: Qt.rgba(0, 0, 0, 0.1) } GradientStop { position: 1 - radius/(radius - margin) + radius/(radius - margin) * 0.35 color: Qt.rgba(0, 0, 0, 0.02) } GradientStop { position: 1.0 color: "transparent" } } } diff --git a/src/controls/private/DefaultCardBackground.qml b/src/controls/private/DefaultCardBackground.qml index 65dc9f8a..70cba43a 100644 --- a/src/controls/private/DefaultCardBackground.qml +++ b/src/controls/private/DefaultCardBackground.qml @@ -1,106 +1,93 @@ /* - * Copyright 2019 Marco Martin + * SPDX-FileCopyrightText: 2019 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.6 import org.kde.kirigami 2.11 as Kirigami Rectangle { color: Kirigami.Theme.backgroundColor CornerShadow { id: topLeft corner: Qt.BottomRightCorner z: -1 anchors { right: parent.left bottom: parent.top margins: margin bottomMargin: margin - 1 } } CornerShadow { id: topRight corner: Qt.BottomLeftCorner z: -1 anchors { left: parent.right bottom: parent.top margins: margin bottomMargin: margin - 1 } } CornerShadow { id: bottomLeft corner: Qt.TopRightCorner z: -1 anchors { right: parent.left top: parent.bottom margins: margin } } CornerShadow { id: bottomRight corner: Qt.TopLeftCorner z: -1 anchors { left: parent.right top: parent.bottom margins: margin } } EdgeShadow { edge: Qt.BottomEdge z: -1 anchors { left: bottomLeft.right right: bottomRight.left bottom: parent.top bottomMargin: - 1 } } EdgeShadow { edge: Qt.TopEdge z: -1 anchors { left: topLeft.right right: topRight.left top: parent.bottom } } EdgeShadow { edge: Qt.LeftEdge z: -1 anchors { top: topRight.bottom bottom: bottomRight.top left: parent.right } } EdgeShadow { edge: Qt.RightEdge z: -1 anchors { top: topLeft.bottom bottom: bottomLeft.top right: parent.left } } } diff --git a/src/controls/private/DefaultListItemBackground.qml b/src/controls/private/DefaultListItemBackground.qml index f7c0c4c6..697ffabe 100644 --- a/src/controls/private/DefaultListItemBackground.qml +++ b/src/controls/private/DefaultListItemBackground.qml @@ -1,60 +1,47 @@ /* - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.1 import org.kde.kirigami 2.4 Rectangle { id: background color: listItem.checked || listItem.highlighted || (listItem.supportsMouseEvents && listItem.pressed && !listItem.checked && !listItem.sectionDelegate) ? listItem.activeBackgroundColor : (listItem.alternatingBackground && index%2 ? listItem.alternateBackgroundColor : listItem.backgroundColor) visible: listItem.ListView.view ? listItem.ListView.view.highlight === null : true Rectangle { id: internal property bool indicateActiveFocus: listItem.pressed || Settings.tabletMode || listItem.activeFocus || (listItem.ListView.view ? listItem.ListView.view.activeFocus : false) anchors.fill: parent visible: !Settings.tabletMode && listItem.supportsMouseEvents color: listItem.activeBackgroundColor opacity: (listItem.hovered || listItem.highlighted || listItem.activeFocus) && !listItem.pressed ? 0.5 : 0 Behavior on opacity { NumberAnimation { duration: Units.longDuration } } } readonly property bool __separatorVisible: listItem.separatorVisible Separator { anchors { left: parent.left right: parent.right bottom: parent.top } visible: background.__separatorVisible } Separator { anchors { left: parent.left right: parent.right bottom: parent.bottom } visible: background.__separatorVisible } } diff --git a/src/controls/private/EdgeShadow.qml b/src/controls/private/EdgeShadow.qml index 5e36e3ae..e9009ce7 100644 --- a/src/controls/private/EdgeShadow.qml +++ b/src/controls/private/EdgeShadow.qml @@ -1,63 +1,50 @@ /* - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.1 import QtGraphicalEffects 1.0 import org.kde.kirigami 2.4 LinearGradient { id: shadow /** * edge: enumeration * This property holds the edge of the shadow that will determine * the direction of the gradient. * The acceptable values are: * Qt.TopEdge: The top edge of the content item. * Qt.LeftEdge: The left edge of the content item (default). * Qt.RightEdge: The right edge of the content item. * Qt.BottomEdge: The bottom edge of the content item. */ property int edge: Qt.LeftEdge property int radius: Units.gridUnit implicitWidth: radius implicitHeight: radius start: Qt.point((edge !== Qt.RightEdge ? 0 : width), (edge !== Qt.BottomEdge ? 0 : height)) end: Qt.point((edge !== Qt.LeftEdge ? 0 : width), (edge !== Qt.TopEdge ? 0 : height)) gradient: Gradient { GradientStop { position: 0.0 color: Qt.rgba(0, 0, 0, 0.25) } GradientStop { position: 0.20 color: Qt.rgba(0, 0, 0, 0.1) } GradientStop { position: 0.35 color: Qt.rgba(0, 0, 0, 0.02) } GradientStop { position: 1.0 color: "transparent" } } } diff --git a/src/controls/private/GlobalDrawerActionItem.qml b/src/controls/private/GlobalDrawerActionItem.qml index 4a540b9d..29f98054 100644 --- a/src/controls/private/GlobalDrawerActionItem.qml +++ b/src/controls/private/GlobalDrawerActionItem.qml @@ -1,152 +1,139 @@ /* - * Copyright 2015 Marco Martin + * SPDX-FileCopyrightText: 2015 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.6 import QtQuick.Controls 2.0 as QQC2 import QtQuick.Layouts 1.2 import org.kde.kirigami 2.5 BasicListItem { id: listItem supportsMouseEvents: (!isExpandible || root.collapsed) readonly property bool wideMode: width > height * 2 readonly property bool isSeparator: modelData.hasOwnProperty("separator") && modelData.separator readonly property bool isExpandible: modelData && modelData.hasOwnProperty("expandible") && modelData.expandible reserveSpaceForIcon: !isSeparator reserveSpaceForLabel: !isSeparator checked: modelData.checked || (actionsMenu && actionsMenu.visible) width: parent.width icon: modelData.icon.name || modelData.icon.source iconColor: modelData.icon.color label: width > height * 2 ? MnemonicData.richTextLabel : "" MnemonicData.enabled: listItem.enabled && listItem.visible MnemonicData.controlType: MnemonicData.MenuItem MnemonicData.label: modelData.text property ActionsMenu actionsMenu: ActionsMenu { x: Qt.application.layoutDirection === Qt.RightToLeft ? -width : listItem.width actions: modelData.hasOwnProperty("children") ? modelData.children : null submenuComponent: Component { ActionsMenu {} } onVisibleChanged: { if (visible) { stackView.openSubMenu = listItem.actionsMenu; } else if (stackView.openSubMenu === listItem.actionsMenu) { stackView.openSubMenu = null; } } } separatorVisible: false //TODO: animate the hide by collapse visible: (model ? model.visible || model.visible===undefined : modelData.visible) && opacity > 0 opacity: !root.collapsed || icon.length > 0 Behavior on opacity { NumberAnimation { duration: Units.longDuration/2 easing.type: Easing.InOutQuad } } enabled: !isSeparator && ( (model && model.enabled !== undefined) ? model.enabled : modelData.enabled) hoverEnabled: (!isExpandible || root.collapsed) && !Settings.tabletMode sectionDelegate: isExpandible font.pointSize: isExpandible ? Theme.defaultFont.pointSize * 1.30 : Theme.defaultFont.pointSize opacity: !root.collapsed height: implicitHeight * opacity Separator { id: separatorAction visible: listItem.isSeparator Layout.fillWidth: true } Icon { Shortcut { sequence: listItem.MnemonicData.sequence onActivated: listItem.clicked() } isMask: true Layout.alignment: Qt.AlignVCenter Layout.leftMargin: !root.collapsed ? 0 : parent.width - listItem.width Layout.preferredHeight: !root.collapsed ? Units.iconSizes.smallMedium : Units.iconSizes.small/2 selected: listItem.checked || listItem.pressed Layout.preferredWidth: Layout.preferredHeight source: (LayoutMirroring.enabled ? "go-next-symbolic-rtl" : "go-next-symbolic") visible: (!isExpandible || root.collapsed) && !listItem.isSeparator && modelData.hasOwnProperty("children") && modelData.children!==undefined && modelData.children.length > 0 } data: [ QQC2.ToolTip { visible: !listItem.isSeparator && (modelData.hasOwnProperty("tooltip") && modelData.tooltip.length || root.collapsed) && (!actionsMenu || !actionsMenu.visible) && listItem.hovered && text.length > 0 text: modelData.hasOwnProperty("tooltip") && modelData.tooltip.length ? modelData.tooltip : modelData.text delay: Units.toolTipDelay timeout: 5000 y: listItem.height/2 - height/2 x: Qt.application.layoutDirection === Qt.RightToLeft ? -width : listItem.width } ] onHoveredChanged: { if (!hovered) { return; } if (stackView.openSubMenu) { stackView.openSubMenu.visible = false; if (!listItem.actionsMenu.hasOwnProperty("count") || listItem.actionsMenu.count>0) { if (listItem.actionsMenu.hasOwnProperty("popup")) { listItem.actionsMenu.popup(listItem, listItem.width, 0) } else { listItem.actionsMenu.visible = true; } } } } onClicked: { if (!supportsMouseEvents) { return; } modelData.trigger(); if (modelData.hasOwnProperty("children") && modelData.children!==undefined && modelData.children.length > 0) { if (root.collapsed) { //fallbacks needed for Qt 5.9 if ((!listItem.actionsMenu.hasOwnProperty("count") || listItem.actionsMenu.count>0) && !listItem.actionsMenu.visible) { stackView.openSubMenu = listItem.actionsMenu; if (listItem.actionsMenu.hasOwnProperty("popup")) { listItem.actionsMenu.popup(listItem, listItem.width, 0) } else { listItem.actionsMenu.visible = true; } } } else { stackView.push(menuComponent, {model: modelData.children, level: level + 1, current: modelData }); } } else if (root.resetMenuOnTriggered) { root.resetMenu(); } checked = Qt.binding(function() { return modelData.checked || (actionsMenu && actionsMenu.visible) }); } } diff --git a/src/controls/private/PageActionPropertyGroup.qml b/src/controls/private/PageActionPropertyGroup.qml index a999f370..bf7567b4 100644 --- a/src/controls/private/PageActionPropertyGroup.qml +++ b/src/controls/private/PageActionPropertyGroup.qml @@ -1,28 +1,15 @@ /* - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQml 2.1 QtObject { property QtObject main property QtObject left property QtObject right property list contextualActions } diff --git a/src/controls/private/PrivateActionToolButton.qml b/src/controls/private/PrivateActionToolButton.qml index 0623b82b..eaf1c8f4 100644 --- a/src/controls/private/PrivateActionToolButton.qml +++ b/src/controls/private/PrivateActionToolButton.qml @@ -1,144 +1,131 @@ /* - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.7 import QtQuick.Layouts 1.2 import QtQuick.Controls 2.4 as Controls import org.kde.kirigami 2.4 Controls.ToolButton { id: control signal menuAboutToShow implicitWidth: menuArrow.visible || (showText && ( kirigamiAction ? kirigamiAction.text.length > 0 : text.length > 0)) ? Math.max(layout.implicitWidth + Units.largeSpacing*2, background.implicitWidth) : implicitHeight Theme.colorSet: Theme.Button Theme.inherit: kirigamiAction && kirigamiAction.icon.color.a === 0 Theme.backgroundColor: kirigamiAction && kirigamiAction.icon.color.a ? kirigamiAction.icon.color : undefined Theme.textColor: kirigamiAction && !flat && kirigamiAction.icon.color.a ? Theme.highlightedTextColor : undefined hoverEnabled: true flat: !control.kirigamiAction || !control.kirigamiAction.icon.color.a //TODO: replace with upstream action when we depend on Qt 5.10 //TODO: upstream action makes the style to re-draw the content, it'd be ideal except for the custom dropDown icon needed for actionsMenu property Controls.Action kirigamiAction property bool showText: !(kirigamiAction && kirigamiAction.displayHint !== undefined && kirigamiAction.displayHintSet(Action.DisplayHint.IconOnly)) property bool showMenuArrow: !(kirigamiAction && kirigamiAction.displayHint !== undefined && kirigamiAction.displayHintSet(Action.DisplayHint.HideChildIndicator)) property alias menu: menu //we need our own text delegate text: "" checkable: (kirigamiAction && kirigamiAction.checkable) || (menu.actions && menu.actions.length > 0) checked: (kirigamiAction && kirigamiAction.checked) enabled: kirigamiAction && kirigamiAction.enabled opacity: enabled ? 1 : 0.4 visible: kirigamiAction && kirigamiAction.visible onClicked: { if (kirigamiAction) { kirigamiAction.trigger(); } } onToggled: { if (menu.actions.length > 0) { if (checked) { control.menuAboutToShow(); menu.popup(control, 0, control.height) } else { menu.dismiss() } } } ActionsMenu { id: menu y: control.height actions: control.kirigamiAction && kirigamiAction.hasOwnProperty("children") ? control.kirigamiAction.children : null // Important: We handle the press on parent in the parent, so ignore it here. closePolicy: Controls.Popup.CloseOnEscape | Controls.Popup.CloseOnPressOutsideParent submenuComponent: Component { ActionsMenu {} } onClosed: { control.checked = false } } contentItem: MouseArea { hoverEnabled: true onPressed: mouse.accepted = false Theme.colorSet: checked && (control.kirigamiAction && control.kirigamiAction.icon.color.a) ? Theme.Selection : control.Theme.colorSet Theme.inherit: control.kirigamiAction && Theme.colorSet != Theme.Selection && control.kirigamiAction.icon.color.a === 0 implicitWidth: layout.implicitWidth implicitHeight: layout.implicitHeight GridLayout { id: layout columns: control.display == Controls.ToolButton.TextUnderIcon ? 1 : 2 + (menuArrow.visible ? 1 : 0) rows: control.display == Controls.ToolButton.TextUnderIcon ? 2 : 1 anchors.centerIn: parent Icon { id: mainIcon Layout.alignment: Qt.AlignCenter Layout.minimumWidth: Units.iconSizes.smallMedium Layout.minimumHeight: Units.iconSizes.smallMedium source: control.kirigamiAction ? (control.kirigamiAction.icon ? control.kirigamiAction.icon.name : control.kirigamiAction.iconName) : "" visible: control.kirigamiAction && control.kirigamiAction.iconName != "" && control.display != Controls.ToolButton.TextOnly color: control.flat && control.kirigamiAction && control.kirigamiAction.icon && control.kirigamiAction.icon.color.a > 0 ? control.kirigamiAction.icon.color : label.color } Controls.Label { id: label MnemonicData.enabled: control.enabled MnemonicData.controlType: MnemonicData.ActionElement MnemonicData.label: control.kirigamiAction ? control.kirigamiAction.text : "" text: MnemonicData.richTextLabel visible: control.showText && text.length > 0 && control.display != Controls.ToolButton.IconOnly Shortcut { sequence: label.MnemonicData.sequence onActivated: control.clicked() } } Icon { id: menuArrow Layout.minimumWidth: Units.iconSizes.small Layout.minimumHeight: Units.iconSizes.small color: mainIcon.color source: "arrow-down" visible: showMenuArrow && menu.actions && menu.actions.length > 0 } } } Controls.ToolTip { visible: control.hovered && text.length > 0 && !menu.visible && !control.pressed text: kirigamiAction ? (kirigamiAction.tooltip.length ? kirigamiAction.tooltip : kirigamiAction.text) : "" delay: Units.toolTipDelay timeout: 5000 y: control.height } } diff --git a/src/controls/private/RefreshableScrollView.qml b/src/controls/private/RefreshableScrollView.qml index 1c1f6cb1..afcd8b47 100644 --- a/src/controls/private/RefreshableScrollView.qml +++ b/src/controls/private/RefreshableScrollView.qml @@ -1,216 +1,203 @@ /* - * Copyright 2015 Marco Martin + * SPDX-FileCopyrightText: 2015 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.7 import QtQuick.Window 2.2 import QtQuick.Controls 2.0 as QQC2 import QtGraphicalEffects 1.0 import QtQuick.Layouts 1.2 import org.kde.kirigami 2.4 import "../templates/private" as P P.ScrollView { id: root /** * type: bool * If true the list is asking for refresh and will show a loading spinner. * it will automatically be set to true when the user pulls down enough the list. * This signals the application logic to start its refresh procedure. * The application itself will have to set back this property to false when done. */ property bool refreshing: false /** * type: bool * If true the list supports the "pull down to refresh" behavior. */ property bool supportsRefreshing: false /** * leftPadding: int * default contents padding at left */ property int leftPadding: Units.gridUnit /** * topPadding: int * default contents padding at top */ property int topPadding: Units.gridUnit /** * rightPadding: int * default contents padding at right */ property int rightPadding: Units.gridUnit /** * bottomPadding: int * default contents padding at bottom */ property int bottomPadding: Units.gridUnit /** * Set when this scrollview manages a whole page */ property Page page property Item _swipeFilter onRefreshingChanged: flickableItem.topMargin = topPadding + (refreshing ? busyIndicatorFrame.height : 0); children: [ Item { id: busyIndicatorFrame z: 99 y: root.flickableItem.verticalLayoutDirection === ListView.BottomToTop ? -root.flickableItem.contentY+height : -root.flickableItem.contentY-height width: root.flickableItem.width height: busyIndicator.height + Units.gridUnit * 2 QQC2.BusyIndicator { id: busyIndicator anchors.centerIn: parent running: root.refreshing visible: root.refreshing //Android busywidget QQC seems to be broken at custom sizes } property int headerItemHeight: (root.flickableItem.headerItem ? (root.flickableItem.headerItem.maximumHeight ? root.flickableItem.headerItem.maximumHeight : root.flickableItem.headerItem.height) : 0) Rectangle { id: spinnerProgress anchors { fill: busyIndicator margins: Math.ceil(Units.smallSpacing) } radius: width visible: supportsRefreshing && !refreshing && progress > 0 color: "transparent" opacity: 0.8 border.color: Theme.backgroundColor border.width: Math.ceil(Units.smallSpacing) //also take into account the listview header height if present property real progress: supportsRefreshing && !refreshing ? ((parent.y - busyIndicatorFrame.headerItemHeight)/busyIndicatorFrame.height) : 0 } ConicalGradient { source: spinnerProgress visible: spinnerProgress.visible anchors.fill: spinnerProgress gradient: Gradient { GradientStop { position: 0.00; color: Theme.highlightColor } GradientStop { position: spinnerProgress.progress; color: Theme.highlightColor } GradientStop { position: spinnerProgress.progress + 0.01; color: "transparent" } GradientStop { position: 1.00; color: "transparent" } } } onYChanged: { //it's overshooting enough and not reachable: start countdown for reachability if (y - busyIndicatorFrame.headerItemHeight > root.topPadding + Units.gridUnit && (typeof(applicationWindow) == "undefined" || !applicationWindow().reachableMode)) { overshootResetTimer.running = true; //not reachable and not overshooting enough, stop reachability countdown } else if (typeof(applicationWindow) == "undefined" || !applicationWindow().reachableMode) { //it's important it doesn't restart overshootResetTimer.running = false; } if (!supportsRefreshing) { return; } //also take into account the listview header height if present if (!root.refreshing && y - busyIndicatorFrame.headerItemHeight > busyIndicatorFrame.height/2 + topPadding) { refreshTriggerTimer.running = true; } else { refreshTriggerTimer.running = false; } } Timer { id: refreshTriggerTimer interval: 500 onTriggered: { //also take into account the listview header height if present if (!root.refreshing && parent.y - busyIndicatorFrame.headerItemHeight > busyIndicatorFrame.height/2 + topPadding) { root.refreshing = true; } } } Connections { - enabled: typeof applicationWindow !== "undefined" + enabled: typeof applicationWindow !== "undefined" target: typeof applicationWindow !== "undefined" ? applicationWindow() : null onReachableModeChanged: { overshootResetTimer.running = applicationWindow().reachableMode; } } Timer { id: overshootResetTimer interval: (typeof applicationWindow !== "undefined" && applicationWindow().reachableMode) ? 8000 : 2000 onTriggered: { //put it there because widescreen may have changed since timer start if (!Settings.isMobile || (typeof applicationWindow !== "undefined" && applicationWindow().wideScreen) || root.flickableItem.verticalLayoutDirection === ListView.BottomToTop) { return; } applicationWindow().reachableMode = !applicationWindow().reachableMode; } } Binding { target: root.flickableItem property: "flickableDirection" value: Flickable.VerticalFlick } Binding { target: root.flickableItem property: "bottomMargin" value: root.page.bottomPadding } Binding { target: root.contentItem property: "width" value: root.flickableItem.width } } ] Component.onCompleted: leftPaddingChanged() onRightPaddingChanged: leftPaddingChanged() onLeftPaddingChanged: { //for gridviews do apply margins if (root.contentItem == root.flickableItem) { if (typeof root.flickableItem.cellWidth != "undefined") { flickableItem.anchors.leftMargin = leftPadding; flickableItem.anchors.rightMargin = rightPadding; } else { flickableItem.anchors.leftMargin = 0; flickableItem.anchors.rightMargin = 0; } flickableItem.anchors.topMargin = 0; flickableItem.anchors.bottomMargin = 0; } else { flickableItem.anchors.leftMargin = leftPadding; flickableItem.anchors.topMargin = topPadding; flickableItem.anchors.rightMargin = rightPadding; flickableItem.anchors.bottomMargin = bottomPadding; } } } diff --git a/src/controls/private/SwipeItemEventFilter.qml b/src/controls/private/SwipeItemEventFilter.qml index db9ff3f9..3eeb0d7b 100644 --- a/src/controls/private/SwipeItemEventFilter.qml +++ b/src/controls/private/SwipeItemEventFilter.qml @@ -1,47 +1,34 @@ /* - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.5 import org.kde.kirigami 2.4 MouseArea { id: swipeFilter anchors { right: parent.right top: parent.top bottom: parent.bottom } z: 99999 property Item currentItem property real peek preventStealing: true width: Units.gridUnit onPressed: { var mapped = mapToItem(parent.flickableItem.contentItem, mouse.x, mouse.y); currentItem = parent.flickableItem.itemAt(mapped.x, mapped.y); } onPositionChanged: { var mapped = mapToItem(parent.flickableItem.contentItem, mouse.x, mouse.y); currentItem = parent.flickableItem.itemAt(mapped.x, mapped.y); peek = 1 - mapped.x / parent.flickableItem.contentItem.width; } } diff --git a/src/controls/private/globaltoolbar/AbstractPageHeader.qml b/src/controls/private/globaltoolbar/AbstractPageHeader.qml index 779a6f08..4f62a215 100644 --- a/src/controls/private/globaltoolbar/AbstractPageHeader.qml +++ b/src/controls/private/globaltoolbar/AbstractPageHeader.qml @@ -1,45 +1,32 @@ /* - * Copyright 2018 Marco Martin + * SPDX-FileCopyrightText: 2018 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.5 import QtQuick.Controls 2.0 as Controls import QtQuick.Layouts 1.2 import org.kde.kirigami 2.5 AbstractApplicationHeader { id: root // anchors.fill: parent property Item container property bool current minimumHeight: pageRow.globalToolBar.minimumHeight maximumHeight: pageRow.globalToolBar.maximumHeight preferredHeight: pageRow.globalToolBar.preferredHeight separatorVisible: pageRow.globalToolBar.separatorVisible leftPadding: Math.min(Qt.application.layoutDirection == Qt.LeftToRight ? Math.max(0, pageRow.ScenePosition.x - page.ScenePosition.x + pageRow.globalToolBar.leftReservedSpace) : Math.max(0, -pageRow.width + pageRow.ScenePosition.x + page.ScenePosition.x + page.width + pageRow.globalToolBar.leftReservedSpace), root.width/2) rightPadding: Qt.application.layoutDirection == Qt.LeftToRight ? Math.max(0, -pageRow.width - pageRow.ScenePosition.x + page.ScenePosition.x + page.width + pageRow.globalToolBar.rightReservedSpace) : Math.max(0, pageRow.ScenePosition.x - page.ScenePosition.x + pageRow.globalToolBar.rightReservedSpace) } diff --git a/src/controls/private/globaltoolbar/BreadcrumbControl.qml b/src/controls/private/globaltoolbar/BreadcrumbControl.qml index 3d478247..9fe7e721 100644 --- a/src/controls/private/globaltoolbar/BreadcrumbControl.qml +++ b/src/controls/private/globaltoolbar/BreadcrumbControl.qml @@ -1,108 +1,95 @@ /* - * Copyright 2018 Marco Martin + * SPDX-FileCopyrightText: 2018 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.5 import QtQuick.Controls 2.0 as Controls import QtQuick.Layouts 1.2 import org.kde.kirigami 2.4 as Kirigami Flickable { id: root property Kirigami.PageRow pageRow: parent.pageRow contentHeight: height contentWidth: mainLayout.width clip: true boundsBehavior: Flickable.StopAtBounds Connections { target: pageRow onCurrentIndexChanged: { var currentItem = mainLayout.children[pageRow.currentIndex]; listScrollAnim.running = false listScrollAnim.from = root.contentX; listScrollAnim.to = Math.max( 0, Math.min(currentItem.x + currentItem.width/2 - root.width/2, root.contentWidth - root.width)) listScrollAnim.running = true; } } RowLayout { id: mainLayout height: parent.height spacing: 0 Repeater { id: mainRepeater model: pageRow.depth delegate: MouseArea { Layout.preferredWidth: delegateLayout.implicitWidth Layout.fillHeight: true onClicked: pageRow.currentIndex = modelData; hoverEnabled: !Kirigami.Settings.tabletMode Rectangle { color: Kirigami.Theme.highlightColor anchors.fill: parent radius: 3 opacity: mainRepeater.count > 1 && parent.containsMouse ? 0.1 : 0 } RowLayout { id: delegateLayout anchors.fill: parent readonly property Kirigami.Page page: pageRow.get(modelData) spacing: 0 Kirigami.Icon { visible: modelData > 0 Layout.alignment: Qt.AlignVCenter Layout.preferredHeight: Kirigami.Units.iconSizes.small Layout.preferredWidth: Layout.preferredHeight isMask: true color: Kirigami.Theme.textColor source: LayoutMirroring.enabled ? "go-next-symbolic-rtl" : "go-next-symbolic" } Kirigami.Heading { Layout.leftMargin: Kirigami.Units.largeSpacing font.pointSize: -1 font.pixelSize: Math.max(1, mainLayout.height * 0.6) color: Kirigami.Theme.textColor verticalAlignment: Text.AlignVCenter wrapMode: Text.NoWrap text: delegateLayout.page ? delegateLayout.page.title : "" opacity: modelData == pageRow.currentIndex ? 1 : 0.4 rightPadding: Kirigami.Units.largeSpacing } } } } } NumberAnimation { id: listScrollAnim target: root property: "contentX" duration: Kirigami.Units.longDuration easing.type: Easing.InOutQuad } } diff --git a/src/controls/private/globaltoolbar/PageRowGlobalToolBarStyleGroup.qml b/src/controls/private/globaltoolbar/PageRowGlobalToolBarStyleGroup.qml index d464042f..19925403 100644 --- a/src/controls/private/globaltoolbar/PageRowGlobalToolBarStyleGroup.qml +++ b/src/controls/private/globaltoolbar/PageRowGlobalToolBarStyleGroup.qml @@ -1,53 +1,40 @@ /* - * Copyright 2018 Marco Martin + * SPDX-FileCopyrightText: 2018 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.1 import org.kde.kirigami 2.4 as Kirigami QtObject { id: globalToolBar property int style: Kirigami.ApplicationHeaderStyle.None readonly property int actualStyle: { if (style == Kirigami.ApplicationHeaderStyle.Auto) { //Legacy: if ApplicationHeader or ToolbarApplicationHeader are in the header or footer, disable the toolbar here if (typeof applicationWindow !== "undefined" && applicationWindow().header && applicationWindow().header.toString().indexOf("ApplicationHeader") !== -1) { return Kirigami.ApplicationHeaderStyle.None } //non legacy logic return (Kirigami.Settings.isMobile ? (root.wideMode ? Kirigami.ApplicationHeaderStyle.Titles : Kirigami.ApplicationHeaderStyle.Breadcrumb) : Kirigami.ApplicationHeaderStyle.ToolBar) } else { //forbid ToolBar on mobile systems return style; } } property var showNavigationButtons: (style != Kirigami.ApplicationHeaderStyle.TabBar && (!Kirigami.Settings.isMobile || Qt.platform.os == "ios")) ? (Kirigami.ApplicationHeaderStyle.ShowBackButton | Kirigami.ApplicationHeaderStyle.ShowForwardButton) : Kirigami.ApplicationHeaderStyle.NoNavigationButtons property bool separatorVisible: true property int toolbarActionAlignment: Qt.AlignRight property int minimumHeight: 0 // FIXME: Figure out the exact standard size of a Toolbar property int preferredHeight: (actualStyle == Kirigami.ApplicationHeaderStyle.ToolBar ? Kirigami.Units.iconSizes.medium : Kirigami.Units.gridUnit * 1.8) + Kirigami.Units.smallSpacing * 2 property int maximumHeight: preferredHeight } diff --git a/src/controls/private/globaltoolbar/PageRowGlobalToolBarUI.qml b/src/controls/private/globaltoolbar/PageRowGlobalToolBarUI.qml index 4f28aebe..a6b59aee 100644 --- a/src/controls/private/globaltoolbar/PageRowGlobalToolBarUI.qml +++ b/src/controls/private/globaltoolbar/PageRowGlobalToolBarUI.qml @@ -1,133 +1,120 @@ /* - * Copyright 2018 Marco Martin + * SPDX-FileCopyrightText: 2018 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.6 import QtQuick.Layouts 1.2 import org.kde.kirigami 2.4 as Kirigami import "../../templates/private" as TemplatesPrivate import "../" as Private - + Kirigami.AbstractApplicationHeader { id: header readonly property int leftReservedSpace: (buttonsLayout.visible && buttonsLayout.visibleChildren.length > 0 ? buttonsLayout.width : 0) + (leftHandleAnchor.visible ? leftHandleAnchor.width : 0) + (menuButton.visible ? menuButton.width : 0) readonly property int rightReservedSpace: rightHandleAnchor.visible ? backButton.background.implicitHeight : 0 readonly property alias leftHandleAnchor: leftHandleAnchor readonly property alias rightHandleAnchor: rightHandleAnchor readonly property bool breadcrumbVisible: layerIsMainRow && breadcrumbLoader.active readonly property bool layerIsMainRow: root.layers.currentItem == root.contentItem readonly property Item currentItem: layerIsMainRow ? root.currentItem : root.layers.currentItem height: visible ? implicitHeight : 0 minimumHeight: globalToolBar.minimumHeight preferredHeight: globalToolBar.preferredHeight maximumHeight: globalToolBar.maximumHeight separatorVisible: globalToolBar.separatorVisible RowLayout { anchors.fill: parent spacing: 0 Item { id: leftHandleAnchor visible: (typeof applicationWindow() !== "undefined" && applicationWindow().globalDrawer && applicationWindow().globalDrawer.enabled && applicationWindow().globalDrawer.handleVisible && (applicationWindow().globalDrawer.handle.handleAnchor == (Qt.application.layoutDirection == Qt.LeftToRight ? leftHandleAnchor : rightHandleAnchor))) && breadcrumbLoader.pageRow.firstVisibleItem && breadcrumbLoader.pageRow.firstVisibleItem.globalToolBarStyle == Kirigami.ApplicationHeaderStyle.ToolBar Layout.preferredHeight: Math.min(backButton.implicitHeight, parent.height) Layout.preferredWidth: height } Private.PrivateActionToolButton { id: menuButton visible: !Kirigami.Settings.isMobile && applicationWindow().globalDrawer && applicationWindow().globalDrawer.isMenu !== "undefined" && applicationWindow().globalDrawer.isMenu icon.name: "application-menu" showMenuArrow: false Layout.preferredHeight: Math.min(backButton.implicitHeight, parent.height) Layout.preferredWidth: height Layout.leftMargin: Kirigami.Units.smallSpacing kirigamiAction: Kirigami.Action { children: applicationWindow().globalDrawer ? applicationWindow().globalDrawer.actions : [] } } RowLayout { id: buttonsLayout Layout.fillHeight: true Layout.leftMargin: leftHandleAnchor.visible ? Kirigami.Units.smallSpacing : 0 visible: (globalToolBar.showNavigationButtons != Kirigami.ApplicationHeaderStyle.NoNavigationButtons || root.layers.depth > 1) && (globalToolBar.actualStyle != Kirigami.ApplicationHeaderStyle.None) Layout.maximumWidth: visibleChildren.length > 0 ? implicitWidth : 0 TemplatesPrivate.BackButton { id: backButton Layout.leftMargin: leftHandleAnchor.visible ? 0 : Kirigami.Units.smallSpacing Layout.preferredWidth: height } TemplatesPrivate.ForwardButton { Layout.preferredWidth: height } } Loader { id: breadcrumbLoader Layout.fillWidth: true Layout.fillHeight: true Layout.minimumHeight: -1 Layout.preferredHeight: -1 property Kirigami.PageRow pageRow: root opacity: layerIsMainRow && active enabled: opacity > 0 active: (globalToolBar.actualStyle == Kirigami.ApplicationHeaderStyle.TabBar || globalToolBar.actualStyle == Kirigami.ApplicationHeaderStyle.Breadcrumb) && currentItem && currentItem.globalToolBarStyle != Kirigami.ApplicationHeaderStyle.None //TODO: different implementation? source: globalToolBar.actualStyle == Kirigami.ApplicationHeaderStyle.TabBar ? Qt.resolvedUrl("TabBarControl.qml") : Qt.resolvedUrl("BreadcrumbControl.qml") Behavior on opacity { OpacityAnimator { duration: Kirigami.Units.longDuration easing.type: Easing.InOutQuad } } } Item { id: rightHandleAnchor visible: (typeof applicationWindow() !== "undefined" && applicationWindow().contextDrawer && applicationWindow().contextDrawer.enabled && applicationWindow().contextDrawer.handleVisible && applicationWindow().contextDrawer.handle.handleAnchor == (Qt.application.layoutDirection == Qt.LeftToRight ? rightHandleAnchor : leftHandleAnchor) && breadcrumbLoader.pageRow && breadcrumbLoader.pageRow.lastVisibleItem.globalToolBarStyle == Kirigami.ApplicationHeaderStyle.ToolBar) Layout.fillHeight: true Layout.preferredWidth: height } } background.opacity: breadcrumbLoader.opacity } diff --git a/src/controls/private/globaltoolbar/TabBarControl.qml b/src/controls/private/globaltoolbar/TabBarControl.qml index 52d2a53f..64045a66 100644 --- a/src/controls/private/globaltoolbar/TabBarControl.qml +++ b/src/controls/private/globaltoolbar/TabBarControl.qml @@ -1,46 +1,33 @@ /* - * Copyright 2018 Marco Martin + * SPDX-FileCopyrightText: 2018 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.5 import QtQuick.Controls 2.0 as Controls import QtQuick.Layouts 1.2 import org.kde.kirigami 2.4 as Kirigami Controls.TabBar { id: root property Kirigami.PageRow pageRow: parent.pageRow Repeater { id: mainRepeater model: pageRow.depth delegate: Controls.TabButton { anchors { top:parent.top bottom:parent.bottom } width: mainRepeater.count == 1 ? implicitWidth : Math.max(implicitWidth, Math.round(root.width/mainRepeater.count)) height: root.height readonly property Kirigami.Page page: pageRow.get(modelData) text: page ? page.title : "" checked: modelData == pageRow.currentIndex onClicked: pageRow.currentIndex = modelData; } } } diff --git a/src/controls/private/globaltoolbar/TitlesPageHeader.qml b/src/controls/private/globaltoolbar/TitlesPageHeader.qml index a64d1f03..854db3c4 100644 --- a/src/controls/private/globaltoolbar/TitlesPageHeader.qml +++ b/src/controls/private/globaltoolbar/TitlesPageHeader.qml @@ -1,46 +1,33 @@ /* - * Copyright 2018 Marco Martin + * SPDX-FileCopyrightText: 2018 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.5 import QtQuick.Controls 2.0 as Controls import QtQuick.Layouts 1.2 import org.kde.kirigami 2.4 AbstractPageHeader { id: root Loader { id: titleLoader anchors { verticalCenter: parent.verticalCenter left: parent.left right: parent.right leftMargin: Units.gridUnit } height: Math.min(root.height, item ? (item.Layout.preferredHeight > 0 ? item.Layout.preferredHeight : item.implicitHeight) : 0) sourceComponent: page ? page.titleDelegate : null } } diff --git a/src/controls/private/globaltoolbar/ToolBarPageHeader.qml b/src/controls/private/globaltoolbar/ToolBarPageHeader.qml index 747e3e4e..5d0bc04e 100644 --- a/src/controls/private/globaltoolbar/ToolBarPageHeader.qml +++ b/src/controls/private/globaltoolbar/ToolBarPageHeader.qml @@ -1,92 +1,79 @@ /* - * Copyright 2018 Marco Martin + * SPDX-FileCopyrightText: 2018 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.5 import QtQuick.Controls 2.0 as Controls import QtQuick.Layouts 1.2 import org.kde.kirigami 2.5 import "../" as Private AbstractPageHeader { id: root implicitWidth: layout.implicitWidth + Units.smallSpacing * 2 Layout.preferredHeight: Math.max(titleLoader.implicitHeight, toolBar.implicitHeight) + Units.smallSpacing * 2 MouseArea { anchors.fill: parent onClicked: page.forceActiveFocus() } RowLayout { id: layout anchors.fill: parent anchors.leftMargin: Units.gridUnit anchors.rightMargin: Units.smallSpacing spacing: Units.smallSpacing Loader { id: titleLoader Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter Layout.fillWidth: item ? item.Layout.fillWidth : undefined Layout.minimumWidth: item ? item.Layout.minimumWidth : undefined Layout.preferredWidth: item ? item.Layout.preferredWidth : undefined Layout.maximumWidth: item ? item.Layout.maximumWidth : undefined sourceComponent: page ? page.titleDelegate : null } ActionToolBar { id: toolBar Layout.alignment: Qt.AlignVCenter Layout.fillWidth: true visible: actions.length > 0 alignment: pageRow.globalToolBar.toolbarActionAlignment actions: { var result = [] if (page) { if (page.actions.main) { page.actions.main.displayHint |= Action.DisplayHint.KeepVisible result.push(page.actions.main) } if (page.actions.left) { page.actions.left.displayHint |= Action.DisplayHint.KeepVisible result.push(page.actions.left) } if (page.actions.right) { page.actions.right.displayHint |= Action.DisplayHint.KeepVisible result.push(page.actions.right) } if (page.actions.contextualActions.length > 0) { result = result.concat(Array.prototype.map.call(page.actions.contextualActions, function(item) { return item })) } } return result } } } } diff --git a/src/controls/templates/AbstractApplicationHeader.qml b/src/controls/templates/AbstractApplicationHeader.qml index fc429c76..00899318 100644 --- a/src/controls/templates/AbstractApplicationHeader.qml +++ b/src/controls/templates/AbstractApplicationHeader.qml @@ -1,200 +1,187 @@ /* - * Copyright 2015 Marco Martin + * SPDX-FileCopyrightText: 2015 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.7 import QtQuick.Layouts 1.2 import "private" import org.kde.kirigami 2.4 /** * An item that can be used as a title for the application. * Scrolling the main page will make it taller or shorter (trough the point of going away) * It's a behavior similar to the typical mobile web browser addressbar * the minimum, preferred and maximum heights of the item can be controlled with * * minimumHeight: default is 0, i.e. hidden * * preferredHeight: default is Units.gridUnit * 1.6 * * preferredHeight: default is Units.gridUnit * 3 * * To achieve a titlebar that stays completely fixed just set the 3 sizes as the same * @inherit QtQuick.Item */ Item { id: root z: 90 property int minimumHeight: 0 property int preferredHeight: Units.iconSizes.medium + Units.smallSpacing * 2 property int maximumHeight: Units.gridUnit * 3 property PageRow pageRow: __appWindow ? __appWindow.pageStack: null property Page page: pageRow.currentItem default property alias contentItem: mainItem.data readonly property int paintedHeight: headerItem.y + headerItem.height - 1 property int leftPadding: 0 property int topPadding: 0 property int rightPadding: 0 property int bottomPadding: 0 property bool separatorVisible: true - LayoutMirroring.enabled: Qt.application.layoutDirection == Qt.RightToLeft + LayoutMirroring.enabled: Qt.application.layoutDirection == Qt.RightToLeft LayoutMirroring.childrenInherit: true //FIXME: remove property QtObject __appWindow: typeof applicationWindow !== "undefined" ? applicationWindow() : null; implicitHeight: preferredHeight /** * background: Item * This property holds the background item. * Note: the background will be automatically sized as the whole control */ property Item background onBackgroundChanged: { background.z = -1; background.parent = headerItem; background.anchors.fill = headerItem; } onMinimumHeightChanged: implicitHeight = preferredHeight; onPreferredHeightChanged: implicitHeight = preferredHeight; opacity: height > 0 ? 1 : 0 NumberAnimation { id: heightAnim target: root property: "implicitHeight" duration: Units.longDuration easing.type: Easing.InOutQuad } Connections { target: __appWindow onControlsVisibleChanged: { heightAnim.from = root.implicitHeight heightAnim.to = __appWindow.controlsVisible ? root.preferredHeight : 0; heightAnim.restart(); } } Item { id: headerItem property real computedRootHeight: root.preferredHeight anchors { left: parent.left right: parent.right bottom: parent.bottom } height: __appWindow && __appWindow.reachableMode && __appWindow.reachableModeEnabled ? root.maximumHeight : (root.minimumHeight > 0 ? Math.max(root.height, root.minimumHeight) : root.preferredHeight) //FIXME: see FIXME below Connections { target: root.page ? root.page.globalToolBarItem : null enabled: headerSlideConnection.passive && target onImplicitHeightChanged: root.implicitHeight = root.page.globalToolBarItem.implicitHeight } Connections { id: headerSlideConnection target: root.page ? root.page.flickable : null enabled: !passive property int oldContentY property bool updatingContentY: false //FIXME HACK: if we are in global mode, meaning if we are the toolbar showing the global breadcrumb (but the pages are showing their own toolbar), not to try to mess with page contentY. //A better solution is needed readonly property bool passive: root.pageRow && parent.parent == root.pageRow && root.pageRow.globalToolBar.actualStyle !== ApplicationHeaderStyle.TabBar && root.pageRow.globalToolBar.actualStyle != ApplicationHeaderStyle.Breadcrumb onContentYChanged: { if (updatingContentY || !Settings.isMobile || (__appWindow && !__appWindow.controlsVisible) || !root.page) { oldContentY = root.page.flickable.contentY; return; //TODO: merge //if moves but not dragging, just update oldContentY } else if (!root.page.flickable.dragging) { oldContentY = root.page.flickable.contentY; return; } if ((root.pageRow ? root.pageRow.wideMode : (__appWindow && __appWindow.wideScreen)) || !Settings.isMobile) { root.implicitHeight = root.preferredHeight; } else { var oldHeight = root.implicitHeight; root.implicitHeight = Math.max(root.minimumHeight, Math.min(root.preferredHeight, root.implicitHeight + oldContentY - root.page.flickable.contentY)); //if the implicitHeight is changed, use that to simulate scroll if (oldHeight !== implicitHeight) { updatingContentY = true; root.page.flickable.contentY -= (oldHeight - root.implicitHeight); updatingContentY = false; } else { oldContentY = root.page.flickable.contentY; } } } onMovementEnded: { if ((root.pageRow ? root.pageRow.wideMode : (__appWindow && __appWindow.wideScreen)) || !Settings.isMobile) { return; } if (root.height > root.minimumHeight + (root.preferredHeight - root.minimumHeight)/2 ) { root.implicitHeight = root.preferredHeight; } else { root.implicitHeight = root.minimumHeight; } } } Connections { target: pageRow onCurrentItemChanged: { if (!root.page) { return; } if (root.page.flickable) { headerSlideConnection.oldContentY = root.page.flickable.contentY; } else { headerSlideConnection.oldContentY = 0; } root.implicitHeight = root.preferredHeight; } } Item { id: mainItem clip: childrenRect.width > width anchors { fill: parent leftMargin: root.leftPadding topMargin: root.topPadding rightMargin: root.rightPadding bottomMargin: root.bottomPadding } } } } diff --git a/src/controls/templates/AbstractCard.qml b/src/controls/templates/AbstractCard.qml index 1fc2569e..deaa301f 100644 --- a/src/controls/templates/AbstractCard.qml +++ b/src/controls/templates/AbstractCard.qml @@ -1,172 +1,159 @@ /* - * Copyright 2018 Marco Martin + * SPDX-FileCopyrightText: 2018 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.6 import QtQuick.Layouts 1.2 import QtQuick.Templates 2.0 as T import org.kde.kirigami 2.4 as Kirigami /** * A AbstractCard is the base for cards. A Card is a visual object that serves * as an entry point for more detailed information. An abstractCard is empty, * providing just the look and the base properties and signals for an ItemDelegate. * It can be filled with any custom layout of items, its content is organized * in 3 properties: header, contentItem and footer. * Use this only when you need particular custom contents, for a standard layout * for cards, use the Card component. * * @see Card * @inherits QtQuick.Templates.ItemDelegate * @since 2.4 */ T.ItemDelegate { id: root /** * header: Item * This item serves as header, it will be put either on top if headerOrientation * is Qt.Vertical(default) or on the left if it's Qt.Horizontal */ property Item header /** * headerOrientation: Qt.Orientation * If Qt.Vertical the header will be positioned on top(default), * if Qt.Horizontal will be positioned on the left (or right if an RTL layout is used) */ property int headerOrientation: Qt.Vertical /** * footer: Item * This item serves as footer, and it will be positioned at the bottom of the card. */ property Item footer /** * showClickFeedback: bool * if true, when clicking or tapping on the card area, the card will be colored * to show a visual click feedback. * Use this if you want to do an action in the onClicked signal handler of the card. */ property bool showClickFeedback: false Layout.fillWidth: true implicitWidth: Math.max(background.implicitWidth, mainLayout.implicitWidth) + leftPadding + rightPadding - + implicitHeight: mainLayout.implicitHeight + topPadding + bottomPadding hoverEnabled: !Kirigami.Settings.tabletMode && showClickFeedback //if it's in a CardLayout, try to expand horizontal cards to both columns Layout.columnSpan: headerOrientation == Qt.Horizontal && parent.hasOwnProperty("columns") ? parent.columns : 1 Kirigami.Theme.inherit: false Kirigami.Theme.colorSet: Kirigami.Theme.View topPadding: Kirigami.Units.largeSpacing leftPadding: Kirigami.Units.largeSpacing bottomPadding: Kirigami.Units.largeSpacing rightPadding: Kirigami.Units.largeSpacing GridLayout { id: mainLayout rowSpacing: root.topPadding columnSpacing: root.leftPadding anchors { top: parent.top left: parent.left right: parent.right leftMargin: root.leftPadding topMargin: root.topPadding rightMargin: root.rightPadding bottom:parent.bottom bottomMargin: root.bottomPadding } columns: headerOrientation == Qt.Vertical ? 1 : 2 function preferredHeight(item) { if (!item) { return 0; } if (item.Layout.preferredHeight > 0) { return item.Layout.preferredHeight; } return item.implicitHeight } Item { id: headerParent Layout.fillWidth: true Layout.fillHeight: root.headerOrientation == Qt.Horizontal Layout.rowSpan: root.headerOrientation == Qt.Vertical ? 1 : 2 Layout.preferredWidth: header ? header.implicitWidth : 0 Layout.preferredHeight: root.headerOrientation == Qt.Vertical ? mainLayout.preferredHeight(header) : -1 visible: children.length > 0 } Item { id: contentItemParent Layout.fillWidth: true Layout.fillHeight: true Layout.topMargin: root.topPadding Layout.bottomMargin: root.bottomPadding Layout.preferredWidth: contentItem ? contentItem.implicitWidth : 0 Layout.preferredHeight: mainLayout.preferredHeight(contentItem) visible: children.length > 0 } Item { id: footerParent Layout.fillWidth: true Layout.preferredWidth: footer ? footer.implicitWidth : 0 Layout.preferredHeight: mainLayout.preferredHeight(footer) visible: children.length > 0 } } //BEGIN signal handlers onContentItemChanged: { if (!contentItem) { return; } contentItem.parent = contentItemParent; contentItem.anchors.fill = contentItemParent; } onHeaderChanged: { if (!header) { return; } header.parent = headerParent; header.anchors.fill = headerParent; } onFooterChanged: { if (!footer) { return; } //make the footer always looking it's at the bottom of the card footer.parent = footerParent; footer.anchors.left = footerParent.left; footer.anchors.top = footerParent.top; footer.anchors.right = footerParent.right; footer.anchors.topMargin = Qt.binding(function() {return (root.height - root.bottomPadding - root.topPadding) - (footerParent.y + footerParent.height)}); } Component.onCompleted: { contentItemChanged(); } //END signal handlers } diff --git a/src/controls/templates/AbstractListItem.qml b/src/controls/templates/AbstractListItem.qml index c9a93ee2..3be93a71 100644 --- a/src/controls/templates/AbstractListItem.qml +++ b/src/controls/templates/AbstractListItem.qml @@ -1,171 +1,158 @@ /* - * Copyright 2010 Marco Martin + * SPDX-FileCopyrightText: 2010 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.1 import QtQuick.Layouts 1.0 import org.kde.kirigami 2.4 //NOTE: This must stay at 2.0 until KF6 due to retrocompatibility of the "icon" property import QtQuick.Templates 2.0 as T2 import QtQuick.Templates 2.4 as QQC2 /** * An item delegate for the primitive ListView component. * * It's intended to make all listviews look coherent. * * @inherit QtQuick.Templates.ItemDelegate */ T2.ItemDelegate { id: listItem - + /** * supportsMouseEvents: bool * Holds if the item emits signals related to mouse interaction. *TODO: remove * The default value is false. */ property bool supportsMouseEvents: hoverEnabled /** * containsMouse: bool * True when the user hovers the mouse over the list item * NOTE: on mobile touch devices this will be true only when pressed is also true * TODO: remove? */ property alias containsMouse: listItem.hovered /** * alternatingBackground: bool * If true the background of the list items will be alternating between two * colors, helping readability with multiple column views. * Use it only when implementing a view which shows data visually in multiple columns - * @ since 2.7 + * @ since 2.7 */ property bool alternatingBackground: false /** * sectionDelegate: bool * If true the item will be a delegate for a section, so will look like a * "title" for the items under it. */ property bool sectionDelegate: false /** * separatorVisible: bool * True if the separator between items is visible * default: true */ property bool separatorVisible: true /** * textColor: color * Color for the text in the item * It is advised to leave the default value (Theme.textColor) * * Note: if custom text elements are inserted in an AbstractListItem, * their color property will have to be manually bound with this property */ property color textColor: Theme.textColor /** * backgroundColor: color * Color for the background of the item * It is advised to leave the default value (Theme.viewBackgroundColor) */ property color backgroundColor: "transparent" /** * alternateBackgroundColor: color * The background color to use if alternatingBackground is true. * It is advised to leave the default. * @since 2.7 */ property color alternateBackgroundColor: Theme.alternateBackgroundColor /** * activeTextColor: color * Color for the text in the item when pressed or selected * It is advised to leave the default value (Theme.highlightedTextColor) * * Note: if custom text elements are inserted in an AbstractListItem, * their color property will have to be manually bound with this property */ property color activeTextColor: Theme.highlightedTextColor /** * activeBackgroundColor: color * Color for the background of the item when pressed or selected * It is advised to leave the default value (Theme.highlightColor) */ property color activeBackgroundColor: Theme.highlightColor default property alias _default: listItem.contentItem // Overrides action property of newer import versions which we can't use property QQC2.Action action activeFocusOnTab: ListView.view ? false : true text: action ? action.text : undefined checked: action ? action.checked : false checkable: action ? action.checkable : false onClicked: { if (ListView.view && typeof index !== "undefined") { ListView.view.currentIndex = index; } if (!action) { return; } action.trigger(); checked = Qt.binding(function() { return action.checked }); } //Theme.inherit: false //Theme.colorSet: Theme.View padding: Settings.tabletMode ? Units.largeSpacing : Units.smallSpacing leftPadding: padding*2 topPadding: padding rightPadding: padding*2 bottomPadding: padding implicitWidth: contentItem ? contentItem.implicitWidth + leftPadding + rightPadding : Units.gridUnit * 12 implicitHeight: contentItem.implicitHeight + topPadding + bottomPadding width: parent && parent.width > 0 ? parent.width : implicitWidth Layout.fillWidth: true opacity: enabled ? 1 : 0.6 height: visible ? implicitHeight : 0 hoverEnabled: true QtObject { id: internal property Flickable view: listItem.ListView.view || (listItem.parent ? listItem.parent.ListView.view : null) property bool indicateActiveFocus: listItem.pressed || Settings.tabletMode || listItem.activeFocus || (view ? view.activeFocus : false) } Accessible.role: Accessible.ListItem highlighted: focus && ListView.isCurrentItem && ListView.view && ListView.view.keyNavigationEnabled } diff --git a/src/controls/templates/ApplicationHeader.qml b/src/controls/templates/ApplicationHeader.qml index 2f990562..58786e55 100644 --- a/src/controls/templates/ApplicationHeader.qml +++ b/src/controls/templates/ApplicationHeader.qml @@ -1,392 +1,379 @@ /* - * Copyright 2015 Marco Martin + * SPDX-FileCopyrightText: 2015 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.5 import QtQuick.Controls 2.0 as QQC2 import QtQuick.Layouts 1.2 import "private" import org.kde.kirigami 2.4 /** * An item that can be used as a title for the application. * Scrolling the main page will make it taller or shorter (trough the point of going away) * It's a behavior similar to the typical mobile web browser addressbar * the minimum, preferred and maximum heights of the item can be controlled with * * minimumHeight: default is 0, i.e. hidden * * preferredHeight: default is Units.gridUnit * 1.6 * * maximumHeight: default is Units.gridUnit * 3 * * To achieve a titlebar that stays completely fixed just set the 3 sizes as the same */ AbstractApplicationHeader { id: header /** * headerStyle: int * The way the separator between pages should be drawn in the header. * Allowed values are: * * Breadcrumb: the pages are hierarchical and the separator will look like a > * * TabBar: the pages are intended to behave like tabbar pages * and the separator will look limke a dot. * * When the header is in wide screen mode, no separator will be drawn. */ property int headerStyle: ApplicationHeaderStyle.Auto /** * backButtonEnabled: bool * if true, there will be a back button present that will make the pagerow scroll back when clicked */ property bool backButtonEnabled: (!titleList.isTabBar && (!Settings.isMobile || Qt.platform.os == "ios")) onBackButtonEnabledChanged: { if (backButtonEnabled && !titleList.backButton) { var component = Qt.createComponent(Qt.resolvedUrl("private/BackButton.qml")); titleList.backButton = component.createObject(navButtons); component = Qt.createComponent(Qt.resolvedUrl("private/ForwardButton.qml")); titleList.forwardButton = component.createObject(navButtons, {"headerFlickable": titleList}); } else if (titleList.backButton) { titleList.backButton.destroy(); titleList.forwardButton.destroy(); } } property Component pageDelegate: Component { Row { height: parent.height spacing: Units.smallSpacing x: Units.smallSpacing Icon { //in tabbar mode this is just a spacer visible: !titleList.wideMode && ((typeof(modelData) != "undefined" && modelData > 0) || titleList.internalHeaderStyle == ApplicationHeaderStyle.TabBar) anchors.verticalCenter: parent.verticalCenter height: Units.iconSizes.small width: height selected: header.background && header.background.color && header.background.color === Theme.highlightColor source: titleList.isTabBar ? "" : (LayoutMirroring.enabled ? "go-next-symbolic-rtl" : "go-next-symbolic") } Heading { id: title width: Math.min(parent.width, Math.min(titleList.width, implicitWidth)) + Units.smallSpacing anchors.verticalCenter: parent.verticalCenter opacity: current ? 1 : 0.4 //Scaling animate NativeRendering is too slow renderType: Text.QtRendering color: header.background && header.background.color && header.background.color === Theme.highlightColor ? Theme.highlightedTextColor : Theme.textColor elide: Text.ElideRight text: page ? page.title : "" font.pointSize: -1 font.pixelSize: Math.max(1, titleList.height * 0.7) verticalAlignment: Text.AlignVCenter wrapMode: Text.NoWrap Rectangle { anchors { bottom: parent.bottom left: parent.left right: parent.right } height: Units.smallSpacing color: title.color opacity: 0.6 visible: titleList.isTabBar && current } } } } Component.onCompleted: print("Warning: ApplicationHeader is deprecated, remove and use the automatic internal toolbar instead.") Rectangle { anchors { right: titleList.left verticalCenter: parent.verticalCenter } visible: titleList.x > 0 && !titleList.atXBeginning height: parent.height * 0.7 color: Theme.highlightedTextColor width: Math.ceil(Units.smallSpacing / 6) opacity: 0.4 } QQC2.StackView { id: stack anchors { fill: parent leftMargin: navButtons.width rightMargin: __appWindow.contextDrawer && __appWindow.contextDrawer.handleVisible && __appWindow.contextDrawer.handle && __appWindow.contextDrawer.handle.y == 0 ? __appWindow.contextDrawer.handle.width : 0 } initialItem: titleList popEnter: Transition { YAnimator { from: -height to: 0 duration: Units.longDuration easing.type: Easing.OutCubic } } popExit: Transition { YAnimator { from: 0 to: height duration: Units.longDuration easing.type: Easing.OutCubic } } pushEnter: Transition { YAnimator { from: height to: 0 duration: Units.longDuration - easing.type: Easing.OutCubic + easing.type: Easing.OutCubic } } pushExit: Transition { YAnimator { from: 0 to: -height duration: Units.longDuration - easing.type: Easing.OutCubic + easing.type: Easing.OutCubic } } replaceEnter: Transition { YAnimator { from: height to: 0 duration: Units.longDuration easing.type: Easing.OutCubic } } replaceExit: Transition { YAnimator { from: 0 to: -height duration: Units.longDuration easing.type: Easing.OutCubic } } } Separator { id: separator height: parent.height * 0.6 visible: navButtons.width > 0 anchors { verticalCenter: parent.verticalCenter left: navButtons.right } } Separator { height: parent.height * 0.6 visible: stack.anchors.rightMargin > 0 anchors { verticalCenter: parent.verticalCenter right: parent.right rightMargin: stack.anchors.rightMargin } } Repeater { model: pageRow.layers.depth -1 delegate: Loader { sourceComponent: header.pageDelegate readonly property Page page: pageRow.layers.get(modelData+1) readonly property bool current: true; Component.onCompleted: stack.push(this) Component.onDestruction: stack.pop() } } Row { id: navButtons anchors { left: parent.left top: parent.top bottom: parent.bottom topMargin: Units.smallSpacing bottomMargin: Units.smallSpacing } Item { height: parent.height width: (applicationWindow().header && applicationWindow().header.toString().indexOf("ToolBarApplicationHeader") === 0) && __appWindow.globalDrawer && __appWindow.globalDrawer.handleVisible && __appWindow.globalDrawer.handle && __appWindow.globalDrawer.handle.y === 0 ? __appWindow.globalDrawer.handle.width : 0 } } Flickable { id: titleList readonly property bool wideMode: pageRow.hasOwnProperty("wideMode") ? pageRow.wideMode : __appWindow.wideScreen property int internalHeaderStyle: header.headerStyle == ApplicationHeaderStyle.Auto ? (titleList.wideMode ? ApplicationHeaderStyle.Titles : ApplicationHeaderStyle.Breadcrumb) : header.headerStyle //if scrolling the titlebar should scroll also the pages and vice versa property bool scrollingLocked: (header.headerStyle == ApplicationHeaderStyle.Titles || titleList.wideMode) //uses this to have less strings comparisons property bool scrollMutex property bool isTabBar: header.headerStyle == ApplicationHeaderStyle.TabBar property Item backButton property Item forwardButton clip: true boundsBehavior: Flickable.StopAtBounds readonly property alias model: mainRepeater.model contentWidth: contentItem.width contentHeight: height readonly property int currentIndex: pageRow && pageRow.currentIndex !== undefined ? pageRow.currentIndex : 0 readonly property int count: mainRepeater.count function gotoIndex(idx) { //don't actually scroll in widescreen mode if (titleList.wideMode || contentItem.children.length < 2) { return; } listScrollAnim.running = false var pos = titleList.contentX; var destPos; titleList.contentX = Math.max((contentItem.children[idx].x + contentItem.children[idx].width) - titleList.width, Math.min(titleList.contentX, contentItem.children[idx].x)); destPos = titleList.contentX; listScrollAnim.from = pos; listScrollAnim.to = destPos; listScrollAnim.running = true; } NumberAnimation { id: listScrollAnim target: titleList property: "contentX" duration: Units.longDuration easing.type: Easing.InOutQuad } Timer { id: contentXSyncTimer interval: 0 onTriggered: { titleList.contentX = pageRow.contentItem.contentX - pageRow.contentItem.originX + titleList.originX; } } onCountChanged: contentXSyncTimer.restart(); onCurrentIndexChanged: gotoIndex(currentIndex); onModelChanged: gotoIndex(currentIndex); onContentWidthChanged: gotoIndex(currentIndex); onContentXChanged: { if (movingHorizontally && !titleList.scrollMutex && titleList.scrollingLocked && !pageRow.contentItem.moving) { titleList.scrollMutex = true; pageRow.contentItem.contentX = titleList.contentX - titleList.originX + pageRow.contentItem.originX; titleList.scrollMutex = false; } } onHeightChanged: { titleList.returnToBounds() } onMovementEnded: { if (titleList.scrollingLocked) { //this will trigger snap as well pageRow.contentItem.flick(0,0); } } onFlickEnded: movementEnded(); NumberAnimation { id: scrollTopAnimation target: pageRow.currentItem && pageRow.currentItem.flickable ? pageRow.currentItem.flickable : null property: "contentY" to: 0 duration: Units.longDuration easing.type: Easing.InOutQuad } Row { id: contentItem spacing: 0 Repeater { id: mainRepeater model: pageRow.depth delegate: MouseArea { id: delegate readonly property int currentIndex: index readonly property var currentModelData: modelData clip: true width: { //more columns shown? if (titleList.scrollingLocked && delegateLoader.page) { return delegateLoader.page.width - (index == 0 ? navButtons.width : 0) - (index == pageRow.depth-1 ? stack.anchors.rightMargin : 0); } else { return Math.min(titleList.width, delegateLoader.implicitWidth + Units.smallSpacing); } } height: titleList.height onClicked: { if (pageRow.currentIndex === modelData) { //scroll up if current otherwise make current if (!pageRow.currentItem.flickable) { return; } if (pageRow.currentItem.flickable.contentY > -__appWindow.header.height) { scrollTopAnimation.to = -pageRow.currentItem.flickable.topMargin; scrollTopAnimation.running = true; } } else { pageRow.currentIndex = modelData; } } Loader { id: delegateLoader height: parent.height x: titleList.wideMode || headerStyle == ApplicationHeaderStyle.Titles ? (Math.min(delegate.width - implicitWidth, Math.max(0, titleList.contentX - delegate.x))) : 0 width: parent.width - x Connections { target: delegateLoader.page Component.onDestruction: delegateLoader.sourceComponent = null } sourceComponent: header.pageDelegate readonly property Page page: pageRow.get(modelData) //NOTE: why not use ListViewCurrentIndex? because listview itself resets //currentIndex in some situations (since here we are using an int as a model, //even more often) so the property binding gets broken readonly property bool current: pageRow.currentIndex === index readonly property int index: parent.currentIndex readonly property var modelData: parent.currentModelData } } } } Connections { target: titleList.scrollingLocked ? pageRow.contentItem : null onContentXChanged: { if (!titleList.dragging && !titleList.movingHorizontally && !titleList.scrollMutex) { titleList.contentX = pageRow.contentItem.contentX - pageRow.contentItem.originX + titleList.originX; } } } } } diff --git a/src/controls/templates/InlineMessage.qml b/src/controls/templates/InlineMessage.qml index bd048447..d87ce1b0 100644 --- a/src/controls/templates/InlineMessage.qml +++ b/src/controls/templates/InlineMessage.qml @@ -1,306 +1,293 @@ /* - * Copyright 2018 Eike Hein + * SPDX-FileCopyrightText: 2018 Eike Hein * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.7 import QtQuick.Templates 2.0 as T2 import QtQuick.Controls 2.0 as Controls import QtQuick.Layouts 1.0 import org.kde.kirigami 2.5 as Kirigami import "private" /** * An inline message item with support for informational, positive, * warning and error types, and with support for associated actions. * * InlineMessage can be used to give information to the user or * interact with the user, without requiring the use of a dialog. * * The InlineMessage item is hidden by default. It also manages its * height (and implicitHeight) during an animated reveal when shown. * You should avoid setting height on an InlineMessage unless it is * already visible. * * Optionally an icon can be set, defaulting to an icon appropriate * to the message type otherwise. * * Optionally a close button can be shown. * * Actions are added from left to right. If more actions are set than * can fit, an overflow menu is provided. * * Example: * @code * InlineMessage { * type: Kirigami.MessageType.Error * * text: "My error message" * * actions: [ * Kirigami.Action { * iconName: "edit" * text: "Action text" * onTriggered: { * // do stuff * } * }, * Kirigami.Action { * iconName: "edit" * text: "Action text" * onTriggered: { * // do stuff * } * } * ] * } * @endcode * * @since 5.45 */ T2.Control { id: root visible: false /** * Emitted when a link is hovered in the message text. * @param The hovered link. */ signal linkHovered(string link) /** * Emitted when a link is clicked or tapped in the message text. * @param The clicked or tapped link. */ signal linkActivated(string link) /** * type: int * The message type. One of Information, Positive, Warning or Error. * * The default is Kirigami.MessageType.Information. */ property int type: Kirigami.MessageType.Information /** * A grouped property describing an optional icon. * * source: The source of the icon, a freedesktop-compatible icon name is recommended. * * color: An optional tint color for the icon. * * If no custom icon is set, an icon appropriate to the message type * is shown. */ property IconPropertiesGroup icon: IconPropertiesGroup {} /** * text: string * The message text. */ property string text /** * showCloseButton: bool * When enabled, a close button is shown. * The default is false. */ property bool showCloseButton: false /** * actions: list * The list of actions to show. Actions are added from left to * right. If more actions are set than can fit, an overflow menu is * provided. */ property list actions /** * animating: bool * True while the message item is animating. */ readonly property bool animating: root.hasOwnProperty("_animating") && _animating implicitHeight: visible ? contentLayout.implicitHeight + (2 * (background.border.width + Kirigami.Units.smallSpacing)) : 0 property bool _animating: false leftPadding: background.border.width + Kirigami.Units.smallSpacing topPadding: background.border.width + Kirigami.Units.smallSpacing rightPadding: background.border.width + Kirigami.Units.smallSpacing bottomPadding: background.border.width + Kirigami.Units.smallSpacing Behavior on implicitHeight { enabled: !root.visible SequentialAnimation { PropertyAction { targets: root; property: "_animating"; value: true } NumberAnimation { duration: Kirigami.Units.longDuration } } } onVisibleChanged: { if (!visible) { contentLayout.opacity = 0.0; } } opacity: visible ? 1.0 : 0.0 Behavior on opacity { enabled: !root.visible NumberAnimation { duration: Kirigami.Units.shortDuration } } onOpacityChanged: { if (opacity == 0.0) { contentLayout.opacity = 0.0; } else if (opacity == 1.0) { contentLayout.opacity = 1.0; } } onImplicitHeightChanged: { height = implicitHeight; } contentItem: GridLayout { id: contentLayout // Used to defer opacity animation until we know if InlineMessage was // initialized visible. property bool complete: false Behavior on opacity { enabled: root.visible && contentLayout.complete SequentialAnimation { NumberAnimation { duration: Kirigami.Units.shortDuration * 2 } PropertyAction { targets: root; property: "_animating"; value: false } } } rowSpacing: Kirigami.Units.largeSpacing columnSpacing: Kirigami.Units.smallSpacing Kirigami.Icon { id: icon width: Kirigami.Units.iconSizes.smallMedium height: width Layout.alignment: text.lineCount > 1 ? Qt.AlignTop : Qt.AlignVCenter Layout.minimumWidth: width Layout.minimumHeight: height source: { if (root.icon.source) { return root.icon.source; } if (root.type == Kirigami.MessageType.Positive) { return "dialog-positive"; } else if (root.type == Kirigami.MessageType.Warning) { return "dialog-warning"; } else if (root.type == Kirigami.MessageType.Error) { return "dialog-error"; } return "dialog-information"; } color: root.icon.color } MouseArea { implicitHeight: text.implicitHeight Layout.fillWidth: true Layout.alignment: text.lineCount > 1 ? Qt.AlignTop : Qt.AlignVCenter Layout.row: 0 Layout.column: 1 cursorShape: text.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor Controls.Label { id: text width: parent.width color: Kirigami.Theme.textColor wrapMode: Text.WordWrap elide: Text.ElideRight text: root.text onLinkHovered: root.linkHovered(link) onLinkActivated: root.linkActivated(link) } //this must be child of an item which doesn't try to resize it TextMetrics { id: messageTextMetrics font: text.font text: text.text } } Kirigami.ActionToolBar { id: actionsLayout flat: false actions: root.actions visible: root.actions.length alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight Layout.maximumWidth: maximumContentWidth Layout.fillWidth: true Layout.row: { var width = contentLayout.width - icon.width - actionsLayout.maximumContentWidth - (closeButton.visible ? closeButton.width : 0) - 3 * contentLayout.columnSpacing if (messageTextMetrics.width + Kirigami.Units.smallSpacing > width) { return 1; } return 0; } Layout.column: Layout.row ? 0 : 2 Layout.columnSpan: Layout.row ? (closeButton.visible ? 3 : 2) : 1 } Controls.ToolButton { id: closeButton visible: root.showCloseButton Layout.alignment: text.lineCount > 1 || actionsLayout.Layout.row ? Qt.AlignTop : Qt.AlignVCenter Layout.row: 0 Layout.column: actionsLayout.Layout.row ? 2 : 3 icon.name: "dialog-close" onClicked: root.visible = false } Component.onCompleted: complete = true } } diff --git a/src/controls/templates/OverlayDrawer.qml b/src/controls/templates/OverlayDrawer.qml index 691841df..a3e44941 100644 --- a/src/controls/templates/OverlayDrawer.qml +++ b/src/controls/templates/OverlayDrawer.qml @@ -1,500 +1,487 @@ /* - * Copyright 2012 Marco Martin + * SPDX-FileCopyrightText: 2012 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.1 import QtQuick.Templates 2.2 as T2 import org.kde.kirigami 2.11 import "private" /** * Overlay Drawers are used to expose additional UI elements needed for * small secondary tasks for which the main UI elements are not needed. * For example in Okular Active, an Overlay Drawer is used to display * thumbnails of all pages within a document along with a search field. * This is used for the distinct task of navigating to another page. * @inherits: QtQuick.Templates.Drawer */ T2.Drawer { id: root z: modal ? (Math.round((position * 10000000)) ): 100 //BEGIN Properties /** * drawerOpen: bool * true when the drawer is open and visible */ property bool drawerOpen: false /** * enabled: bool * This property holds whether the item receives mouse and keyboard events. By default this is true. */ property bool enabled: true /** * peeking: true * When true the drawer is in a state between open and closed. the drawer is visible but not completely open. - * This is usually the case when the user is dragging the drawer from a screen + * This is usually the case when the user is dragging the drawer from a screen * edge, so the user is "peeking" what's in the drawer */ property bool peeking: false /** * animating: Bool * True during an animation of a drawer either opening or closing */ readonly property bool animating : enterAnimation.animating || exitAnimation.animating || positionResetAnim.running /** * collapsible: Bool * When true, the drawer can be collapsed to a very thin, usually icon only sidebar. * Only modal drawers are collapsible. * Collapsible is not supported in Mobile mode * @since 2.5 */ property bool collapsible: false /** * collapsed: bool * When true, the drawer will be collapsed to a very thin sidebar, * usually icon only. * Only collapsible drawers can be collapsed */ property bool collapsed: false /** * collapsedSize: int * When collapsed, the drawer will be resized to this size * (which may be width for vertical drawers or height for * horizontal drawers). * By default it's just enough to accommodate medium sized icons */ property int collapsedSize: Units.iconSizes.medium /** * A grouped property describing an optional icon. * * source: The source of the icon, a freedesktop-compatible icon name is recommended. * * color: An optional tint color for the icon. * * If no custom icon is set, a menu icon is shown for the application globalDrawer * and an overflow menu icon is shown for the contextDrawer. * That's the default for the GlobalDrawer and ContextDrawer components respectively. - * + * * For OverlayDrawer the default is view-right-close or view-left-close depending on the drawer location * @since 2.5 */ readonly property IconPropertiesGroup handleOpenIcon: IconPropertiesGroup { source: root.edge === Qt.RightEdge ? "view-right-close" : "view-left-close" } /** * A grouped property describing an optional icon. * * source: The source of the icon, a freedesktop-compatible icon name is recommended. * * color: An optional tint color for the icon. * * If no custom icon is set, an X icon is shown, * which will morph into the Menu or overflow icons - * + * * For OverlayDrawer the default is view-right-new or view-left-new depending on the drawer location * @since 2.5 */ property IconPropertiesGroup handleClosedIcon: IconPropertiesGroup { id: handleClosedIconGroup source: root.edge === Qt.RightEdge ? "view-right-new" : "view-left-new" } /** * handleVisible: bool * If true, a little handle will be visible to make opening the drawer easier * Currently supported only on left and right drawers */ property bool handleVisible: typeof(applicationWindow)===typeof(Function) && applicationWindow() ? applicationWindow().controlsVisible : true /** * handle: Item * Readonly property that points to the item that will act as a physical * handle for the Drawer **/ readonly property Item handle: MouseArea { id: drawerHandle z: root.modal ? applicationWindow().overlay.z + (root.position > 0 ? +1 : -1) : root.background.parent.z + 1 preventStealing: true hoverEnabled: handleAnchor && handleAnchor.visible parent: applicationWindow().overlay.parent property Item handleAnchor: (applicationWindow().pageStack && applicationWindow().pageStack.globalToolBar) ? (root.edge === Qt.LeftEdge ? applicationWindow().pageStack.globalToolBar.leftHandleAnchor : applicationWindow().pageStack.globalToolBar.rightHandleAnchor) : (applicationWindow().header && applicationWindow().header.toString().indexOf("ToolBarApplicationHeader") !== -1 ? applicationWindow().header : null) property int startX property int mappedStartX enabled: root.handleVisible onPressed: { root.peeking = true; startX = mouse.x; mappedStartX = mapToItem(parent, startX, 0).x } onPositionChanged: { if (!pressed) { return; } var pos = mapToItem(parent, mouse.x - startX, mouse.y); switch(root.edge) { case Qt.LeftEdge: root.position = pos.x/root.contentItem.width; break; case Qt.RightEdge: root.position = (root.parent.width - pos.x - width)/root.contentItem.width; break; default: } } onReleased: { root.peeking = false; if (Math.abs(mapToItem(parent, mouse.x, 0).x - mappedStartX) < Qt.styleHints.startDragDistance) { if (!root.drawerOpen) { root.close(); } root.drawerOpen = !root.drawerOpen; } } onCanceled: { root.peeking = false } x: { switch(root.edge) { case Qt.LeftEdge: return root.background.width * root.position + Units.smallSpacing; case Qt.RightEdge: return drawerHandle.parent.width - (root.background.width * root.position) - width - Units.smallSpacing; default: return 0; } } y: handleAnchor && anchors.bottom ? handleAnchor.ScenePosition.y : 0 anchors { bottom: drawerHandle.handleAnchor && drawerHandle.handleAnchor.visible ? undefined : parent.bottom bottomMargin: { if (typeof applicationWindow === "undefined") { return; } var margin = Units.smallSpacing; if (applicationWindow().footer) { margin = applicationWindow().footer.height + Units.smallSpacing; } - + if(root.height < root.parent.height) { margin = root.parent.height - root.height - root.y + Units.smallSpacing; } if (!applicationWindow() || !applicationWindow().pageStack || !applicationWindow().pageStack.contentItem || !applicationWindow().pageStack.contentItem.itemAt) { return margin; } var item; if (applicationWindow().pageStack.layers.depth > 1) { item = applicationWindow().pageStack.layers.currentItem; } else { item = applicationWindow().pageStack.contentItem.itemAt(applicationWindow().pageStack.contentItem.contentX + drawerHandle.x, 0); } //try to take the last item if (!item) { item = applicationWindow().pageStack.lastItem; } var pageFooter = item && item.page ? item.page.footer : (item ? item.footer : undefined); if (pageFooter) { margin = root.height < root.parent.height ? margin : margin + pageFooter.height } return margin; } Behavior on bottomMargin { NumberAnimation { duration: Units.shortDuration easing.type: Easing.InOutQuad } } } visible: root.enabled && (root.edge === Qt.LeftEdge || root.edge === Qt.RightEdge) width: handleAnchor && handleAnchor.visible ? handleAnchor.width : Units.iconSizes.smallMedium + Units.smallSpacing*2 height: handleAnchor && handleAnchor.visible ? handleAnchor.height : width opacity: root.handleVisible ? 1 : 0 Behavior on opacity { NumberAnimation { duration: Units.longDuration easing.type: Easing.InOutQuad } } transform: Translate { id: translateTransform x: root.handleVisible ? 0 : (root.edge === Qt.LeftEdge ? -drawerHandle.width : drawerHandle.width) Behavior on x { NumberAnimation { duration: Units.longDuration easing.type: !root.handleVisible ? Easing.OutQuad : Easing.InQuad } } } } interactive: modal Theme.colorSet: modal ? Theme.View : Theme.Window Theme.onColorSetChanged: { contentItem.Theme.colorSet = Theme.colorSet background.Theme.colorSet = Theme.colorSet } //END Properties //BEGIN reassign properties //default paddings leftPadding: Units.smallSpacing topPadding: Units.smallSpacing rightPadding: Units.smallSpacing bottomPadding: Units.smallSpacing y: modal ? 0 : ((T2.ApplicationWindow.menuBar ? T2.ApplicationWindow.menuBar.height : 0) + (T2.ApplicationWindow.header ? T2.ApplicationWindow.header.height : 0)) height: root.edge == Qt.LeftEdge || root.edge == Qt.RightEdge ? (modal ? parent.height : (parent.height - y - (T2.ApplicationWindow.footer ? T2.ApplicationWindow.footer.height : 0))) : implicitHeight parent: modal || edge === Qt.LeftEdge || edge === Qt.RightEdge ? T2.ApplicationWindow.overlay : T2.ApplicationWindow.contentItem edge: Qt.LeftEdge modal: true dragMargin: enabled && (edge === Qt.LeftEdge || edge === Qt.RightEdge) ? Math.min(Units.gridUnit, Qt.styleHints.startDragDistance) : 0 contentWidth: contentItem.implicitWidth || (contentChildren.length === 1 ? contentChildren[0].implicitWidth : 0) contentHeight: contentItem.implicitHeight || (contentChildren.length === 1 ? contentChildren[0].implicitHeight : 0) implicitWidth: contentWidth + leftPadding + rightPadding implicitHeight: contentHeight + topPadding + bottomPadding //this is a workaround for the height not being propagated automatically only sometimes // see https://bugs.kde.org/show_bug.cgi?id=398163 //NOTE: this is NOT a binding, otherwise it causes a binding loop in implicitHeight Connections { target: parent onWidthChanged: { if (edge === Qt.TopEdge || edge === Qt.BottomEdge) { width = parent.width; } } onHeightChanged: { if (edge === Qt.LeftEdge || edge === Qt.RightEdge) { height = parent.height; } } } enter: Transition { SequentialAnimation { id: enterAnimation /*NOTE: why this? the running status of the enter transition is not relaible and * the SmoothedAnimation is always marked as non running, * so the only way to get to a reliable animating status is with this */ property bool animating ScriptAction { script: { enterAnimation.animating = true //on non modal dialog we don't want drawers in the overlay if (!root.modal) { root.background.parent.parent = applicationWindow().overlay.parent } } } SmoothedAnimation { velocity: 5 } ScriptAction { script: enterAnimation.animating = false } } } exit: Transition { SequentialAnimation { id: exitAnimation property bool animating ScriptAction { script: exitAnimation.animating = true } SmoothedAnimation { - velocity: 5 + velocity: 5 } ScriptAction { script: exitAnimation.animating = false } } } //END reassign properties //BEGIN signal handlers onCollapsedChanged: { if (Settings.isMobile) { collapsed = false; } if (!__internal.completed) { return; } if ((!collapsible || modal) && collapsed) { collapsed = true; } } onCollapsibleChanged: { if (Settings.isMobile) { collapsible = false; } if (!__internal.completed) { return; } if (!collapsible) { collapsed = false; } else if (modal) { collapsible = false; } } onModalChanged: { if (!__internal.completed) { return; } if (modal) { collapsible = false; } } onPositionChanged: { if (peeking) { visible = true } } onVisibleChanged: { if (peeking) { visible = true } else { drawerOpen = visible; } } onPeekingChanged: { if (peeking) { root.enter.enabled = false; root.exit.enabled = false; } else { drawerOpen = position > 0.5 ? 1 : 0; positionResetAnim.running = true root.enter.enabled = true; root.exit.enabled = true; } } onDrawerOpenChanged: { //sync this property only when the component is properly loaded if (!__internal.completed) { return; } positionResetAnim.running = false; if (drawerOpen) { open(); } else { close(); } } Component.onCompleted: { //if defined as drawerOpen by default in QML, don't animate if (root.drawerOpen) { root.enter.enabled = false; root.visible = true; root.position = 1; root.enter.enabled = true; } __internal.completed = true; contentItem.Theme.colorSet = Theme.colorSet; background.Theme.colorSet = Theme.colorSet; } //END signal handlers //this is as hidden as it can get here property QtObject __internal: QtObject { //here in order to not be accessible from outside property bool completed: false property SequentialAnimation positionResetAnim: SequentialAnimation { id: positionResetAnim property alias to: internalAnim.to NumberAnimation { id: internalAnim target: root to: drawerOpen ? 1 : 0 property: "position" duration: (root.position)*Units.longDuration } ScriptAction { script: { root.drawerOpen = internalAnim.to != 0; } } } readonly property Item statesItem: Item { states: [ State { when: root.collapsed PropertyChanges { target: root implicitWidth: edge === Qt.TopEdge || edge === Qt.BottomEdge ? applicationWindow().width : Math.min(collapsedSize + leftPadding + rightPadding, Math.round(applicationWindow().width*0.8)) implicitHeight: edge === Qt.LeftEdge || edge === Qt.RightEdge ? applicationWindow().height : Math.min(collapsedSize + topPadding + bottomPadding, Math.round(applicationWindow().height*0.8)) } }, State { when: !root.collapsed PropertyChanges { target: root implicitWidth: edge === Qt.TopEdge || edge === Qt.BottomEdge ? applicationWindow().width : Math.min(contentItem.implicitWidth, Math.round(applicationWindow().width*0.8)) implicitHeight: edge === Qt.LeftEdge || edge === Qt.RightEdge ? applicationWindow().height : Math.min(contentHeight + topPadding + bottomPadding, Math.round(applicationWindow().height*0.4)) contentWidth: contentItem.implicitWidth || (contentChildren.length === 1 ? contentChildren[0].implicitWidth : 0) contentHeight: contentItem.implicitHeight || (contentChildren.length === 1 ? contentChildren[0].implicitHeight : 0) } } ] transitions: Transition { reversible: true NumberAnimation { properties: root.edge === Qt.TopEdge || root.edge === Qt.BottomEdge ? "implicitHeight" : "implicitWidth" duration: Units.longDuration easing.type: Easing.InOutQuad } } } } } diff --git a/src/controls/templates/OverlaySheet.qml b/src/controls/templates/OverlaySheet.qml index f449faa8..c5f92006 100644 --- a/src/controls/templates/OverlaySheet.qml +++ b/src/controls/templates/OverlaySheet.qml @@ -1,564 +1,551 @@ /* -* Copyright (C) 2016 by Marco Martin -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU Library General Public License as -* published by the Free Software Foundation; either version 2, or -* (at your option) any later version. -* -* This program 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 Library General Public License for more details -* -* You should have received a copy of the GNU Library General Public -* License along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. -*/ + * SPDX-FileCopyrightText: 2016 by Marco Martin + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ import QtQuick 2.11 import QtQuick.Layouts 1.2 import QtQuick.Window 2.2 import org.kde.kirigami 2.7 import QtGraphicalEffects 1.0 import QtQuick.Templates 2.0 as T2 import "private" import "../private" /** * An overlay sheet that covers the current Page content. * Its contents can be scrolled up or down, scrolling all the way up or * all the way down, dismisses it. * Use this for big, modal dialogs or information display, that can't be * logically done as a new separate Page, even if potentially * are taller than the screen space. * @inherits: QtQuick.QtObject */ QtObject { id: root Theme.colorSet: Theme.View Theme.inherit: false /** * contentItem: Item * This property holds the visual content item. * * Note: The content item is automatically resized inside the * padding of the control. * Conversely, the Sheet will be sized based on the size hints * of the contentItem, so if you need a custom size sheet, * redefine contentWidth and contentHeight of your contentItem */ default property Item contentItem /** * sheetOpen: bool * If true the sheet is open showing the contents of the OverlaySheet * component. */ property bool sheetOpen /** * leftPadding: int * default contents padding at left */ property int leftPadding: Units.gridUnit /** * topPadding: int * default contents padding at top */ property int topPadding: Units.gridUnit /** * rightPadding: int * default contents padding at right */ property int rightPadding: Units.gridUnit /** * bottomPadding: int * default contents padding at bottom */ property int bottomPadding: Units.gridUnit /** * header: Item * an optional item which will be used as the sheet's header, * always kept on screen * @since 5.43 */ property Item header /** * header: Item * an optional item which will be used as the sheet's footer, * always kept on screen * @since 5.43 */ property Item footer /** * background: Item * This property holds the background item. * * Note: If the background item has no explicit size specified, * it automatically follows the control's size. * In most cases, there is no need to specify width or * height for a background item. */ property Item background /** * showCloseButton: bool * whether to show the close button in the top-right corner * @since 5.44 */ property alias showCloseButton: closeIcon.visible property Item parent function open() { openAnimation.from = -mainItem.height; openAnimation.to = openAnimation.topOpenPosition; openAnimation.running = true; root.sheetOpen = true; mainItem.visible = true; } function close() { if (scrollView.flickableItem.contentY < 0) { closeAnimation.to = -height; } else { closeAnimation.to = scrollView.flickableItem.contentHeight; } closeAnimation.running = true; } onBackgroundChanged: { background.parent = flickableContents; background.z = -1; } onContentItemChanged: { if (contentItem.hasOwnProperty("contentY") && // Check if flickable contentItem.hasOwnProperty("contentHeight")) { contentItem.parent = scrollView; scrollView.contentItem = contentItem; } else { contentItem.parent = contentItemParent; scrollView.contentItem = flickableContents; contentItem.anchors.left = contentItemParent.left; contentItem.anchors.right = contentItemParent.right; } scrollView.flickableItem.flickableDirection = Flickable.VerticalFlick; } onSheetOpenChanged: { if (sheetOpen) { open(); } else { close(); Qt.inputMethod.hide(); } } onHeaderChanged: { header.parent = headerParent; header.anchors.fill = headerParent; //TODO: special case for actual ListViews } onFooterChanged: { footer.parent = footerParent; footer.anchors.fill = footerParent; } Component.onCompleted: { if (!root.parent && typeof applicationWindow !== "undefined") { root.parent = applicationWindow().overlay } } readonly property Item rootItem: MouseArea { id: mainItem Theme.colorSet: root.Theme.colorSet Theme.inherit: root.Theme.inherit //we want to be over any possible OverlayDrawers, including handles parent: { if (root.parent && root.parent.ColumnView.view && (root.parent.ColumnView.view == root.parent || root.parent.ColumnView.view == root.parent.parent)) { return root.parent.ColumnView.view.parent; } else if (root.parent && root.parent.overlay) { root.parent.overlay; } else { return root.parent; } } anchors.fill: parent z: 9998 visible: false drag.filterChildren: true hoverEnabled: true clip: true onClicked: { var pos = mapToItem(flickableContents, mouse.x, mouse.y); if (!flickableContents.contains(pos)) { root.close(); } } readonly property int contentItemPreferredWidth: root.contentItem.Layout.preferredWidth > 0 ? root.contentItem.Layout.preferredWidth : root.contentItem.implicitWidth readonly property int contentItemMaximumWidth: width > Units.gridUnit * 30 ? width * 0.95 : width property bool ownSizeUpdate: false function updateContentWidth() { if (!contentItem.contentItem) { return; } var newWidth = Math.min(contentItemMaximumWidth, Math.max(mainItem.width/2, Math.min(mainItem.width, mainItem.contentItemPreferredWidth))); if (scrollView.verticalScrollBar && scrollView.verticalScrollBar.interactive) { newWidth -= scrollView.verticalScrollBar.width; } ownSizeUpdate = true; contentItem.contentItem.x = (mainItem.width - newWidth)/2 contentItem.contentItem.width = newWidth; ownSizeUpdate = false; } onContentItemMaximumWidthChanged: updateContentWidth() onWidthChanged: updateContentWidth() Connections { target: typeof contentItem.contentItem === "undefined" ? null : contentItem.contentItem onWidthChanged: { if (!mainItem.ownSizeUpdate) { mainItem.updateContentWidth(); } } } onHeightChanged: { var focusItem; focusItem = Window.activeFocusItem; if (!focusItem) { return; } //NOTE: there is no function to know if an item is descended from another, //so we have to walk the parent hierarchy by hand var isDescendent = false; var candidate = focusItem.parent; while (candidate) { if (candidate === root) { isDescendent = true; break; } candidate = candidate.parent; } if (!isDescendent) { return; } var cursorY = 0; if (focusItem.cursorPosition !== undefined) { cursorY = focusItem.positionToRectangle(focusItem.cursorPosition).y; } - + var pos = focusItem.mapToItem(flickableContents, 0, cursorY - Units.gridUnit*3); //focused item already visible? add some margin for the space of the action buttons if (pos.y >= scrollView.flickableItem.contentY && pos.y <= scrollView.flickableItem.contentY + scrollView.flickableItem.height - Units.gridUnit * 8) { return; } scrollView.flickableItem.contentY = pos.y; } ParallelAnimation { - id: openAnimation + id: openAnimation property int margins: Units.gridUnit * 5 property int topOpenPosition: Math.min(-mainItem.height*0.15, scrollView.flickableItem.contentHeight - mainItem.height + margins) property alias from: openAnimationInternal.from property alias to: openAnimationInternal.to NumberAnimation { id: openAnimationInternal target: scrollView.flickableItem properties: "contentY" from: -mainItem.height to: openAnimation.topOpenPosition duration: Units.longDuration easing.type: Easing.OutQuad } OpacityAnimator { target: mainItem from: 0 to: 1 duration: Units.longDuration easing.type: Easing.InQuad } } SequentialAnimation { id: closeAnimation property int to: -mainItem.height ParallelAnimation { NumberAnimation { target: scrollView.flickableItem properties: "contentY" to: closeAnimation.to duration: Units.longDuration easing.type: Easing.InQuad } OpacityAnimator { target: mainItem from: 1 to: 0 duration: Units.longDuration easing.type: Easing.InQuad } } ScriptAction { script: { scrollView.flickableItem.contentY = -mainItem.height; mainItem.visible = root.sheetOpen = false; } } } Rectangle { anchors.fill: parent color: "black" opacity: 0.6 * Math.min( (Math.min(scrollView.flickableItem.contentY + scrollView.flickableItem.height, scrollView.flickableItem.height) / scrollView.flickableItem.height), (2 + (scrollView.flickableItem.contentHeight - scrollView.flickableItem.contentY - scrollView.flickableItem.topMargin - scrollView.flickableItem.bottomMargin)/scrollView.flickableItem.height)) } Icon { id: closeIcon anchors { right: headerItem.right margins: Units.smallSpacing top: headerItem.top } z: 3 visible: !Settings.isMobile width: Units.iconSizes.smallMedium height: width source: closeMouseArea.containsMouse ? "window-close" : "window-close-symbolic" active: closeMouseArea.containsMouse MouseArea { id: closeMouseArea hoverEnabled: true anchors.fill: parent onClicked: root.close(); } } Rectangle { id: headerItem width: flickableContents.width x: flickableContents.x visible: root.header height: Math.max(headerParent.implicitHeight, closeIcon.height) + Units.smallSpacing * 2 color: Theme.backgroundColor //different y depending if we're a listview or a normal item y: Math.max(0, -scrollView.flickableItem.contentY - (scrollView.contentItem != flickableContents ? height : 0)) z: 2 Item { id: headerParent implicitHeight: header ? header.implicitHeight : 0 anchors { fill: parent margins: Units.smallSpacing rightMargin: closeIcon.width + Units.smallSpacing } } - + EdgeShadow { z: -2 edge: Qt.TopEdge anchors { right: parent.right left: parent.left top: parent.bottom } opacity: parent.y == 0 ? 1 : 0 Behavior on opacity { NumberAnimation { duration: Units.longDuration easing.type: Easing.InOutQuad } } } } Rectangle { id: footerItem width: flickableContents.width x: flickableContents.x visible: root.footer height: footerParent.implicitHeight + Units.smallSpacing * 2 + extraMargin color: Theme.backgroundColor y: mainItem.mapFromItem(flickableContents, 0, flickableContents.height).y - height onHeightChanged: y = Math.min(mainItem.height, mainItem.mapFromItem(flickableContents, 0, flickableContents.height).y) - footerItem.height; //Show an extra margin when: //* the application is in mobile mode (no toolbarapplicationheader) //* the bottom screen controls are visible //* the sheet is displayed *under* the controls property int extraMargin: (!root.parent || typeof applicationWindow === "undefined" || (root.parent === applicationWindow().overlay) || !applicationWindow().controlsVisible || (applicationWindow().pageStack && applicationWindow().pageStack.globalToolBar && applicationWindow().pageStack.globalToolBar.actualStyle === ApplicationHeaderStyle.ToolBar) || (applicationWindow().header && applicationWindow().header.toString().indexOf("ToolBarApplicationHeader") === 0)) ? 0 : Units.gridUnit * 3 Connections { target: scrollView.flickableItem onContentYChanged: footerItem.y = Math.min(mainItem.height, mainItem.mapFromItem(flickableContents, 0, flickableContents.height).y) - footerItem.height; onHeightChanged: scrollView.flickableItem.contentYChanged() } z: 2 Item { id: footerParent implicitHeight: footer ? footer.implicitHeight : 0 anchors { top: parent.top left: parent.left right: parent.right margins: Units.smallSpacing } } EdgeShadow { z: -2 edge: Qt.BottomEdge anchors { right: parent.right left: parent.left bottom: parent.top } opacity: parent.y + parent.height < mainItem.height ? 0 : 1 Behavior on opacity { NumberAnimation { duration: Units.longDuration easing.type: Easing.InOutQuad } } } } FocusScope { id: flickableContents //anchors.horizontalCenter: parent.horizontalCenter x: (mainItem.width - width) / 2 readonly property real listHeaderHeight: scrollView.flickableItem && root.contentItem.headerItem ? root.contentItem.headerItem.height : 0 y: (scrollView.contentItem != flickableContents ? -scrollView.flickableItem.contentY - listHeaderHeight - (headerItem.visible ? headerItem.height : 0): 0) width: mainItem.contentItemPreferredWidth <= 0 ? mainItem.width : Math.max(mainItem.width/2, Math.min(mainItem.contentItemMaximumWidth, mainItem.contentItemPreferredWidth)) height: (scrollView.contentItem != flickableContents ? scrollView.flickableItem.contentHeight + listHeaderHeight : (root.contentItem.height + topPadding + bottomPadding)) + (headerItem.visible ? headerItem.height : 0) + (footerItem.visible ? footerItem.height : 0) Connections { target: enabled ? flickableContents.Window.activeFocusItem : null enabled: flickableContents.focus && flickableContents.Window.activeFocusItem && flickableContents.Window.activeFocusItem.hasOwnProperty("text") onTextChanged: { if (Qt.inputMethod.cursorRectangle.y + Qt.inputMethod.cursorRectangle.height > mainItem.Window.height) { scrollView.flickableItem.contentY += (Qt.inputMethod.cursorRectangle.y + Qt.inputMethod.cursorRectangle.height) - mainItem.Window.height } } } Item { id: contentItemParent anchors { fill: parent leftMargin: leftPadding topMargin: topPadding + (headerItem.visible ? headerItem.height : 0) rightMargin: rightPadding + (scrollView.verticalScrollBar && scrollView.verticalScrollBar.interactive ? scrollView.verticalScrollBar.width : 0) bottomMargin: bottomPadding + (footerItem.visible ? footerItem.height : 0) } } } Binding { when: scrollView.flickableItem != null target: scrollView.flickableItem property: "topMargin" //hack needed for smoother open anim value: openAnimation.running ? -scrollView.flickableItem.contentY : -openAnimation.topOpenPosition } Binding { when: scrollView.flickableItem != null target: scrollView.flickableItem property: "bottomMargin" value: openAnimation.margins } Binding { target: scrollView.verticalScrollBar ? scrollView.verticalScrollBar.anchors : null property: "topMargin" value: headerItem.y + headerItem.height } Binding { target: scrollView.verticalScrollBar property: "height" value: mainItem.height - (scrollView.verticalScrollBar ? scrollView.verticalScrollBar.anchors.topMargin : 0) - (mainItem.height - footerItem.y) } Binding { target: scrollView.verticalScrollBar ? scrollView.verticalScrollBar.anchors : null property: "rightMargin" value: mainItem.width - flickableContents.width - flickableContents.x } Connections { target: scrollView.flickableItem onContentHeightChanged: { if (openAnimation.running) { openAnimation.running = false; open(); } } onDraggingChanged: { if (scrollView.flickableItem.dragging) { return; } //close if ((mainItem.height + scrollView.flickableItem.contentY) < mainItem.height/2) { closeAnimation.to = -mainItem.height; closeAnimation.running = true; } else if ((mainItem.height*0.6 + scrollView.flickableItem.contentY) > scrollView.flickableItem.contentHeight) { closeAnimation.to = scrollView.flickableItem.contentHeight; closeAnimation.running = true; } } } Binding { target: scrollView.verticalScrollBar property: "visible" value: scrollView.flickableItem.contentHeight > mainItem.height*0.8 } Connections { target: scrollView.verticalScrollBar onActiveChanged: { if (!scrollView.verticalScrollBar.active) { scrollView.flickableItem.movementEnded(); } } } ScrollView { id: scrollView anchors.fill: parent horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff rightPadding: 0 } } } diff --git a/src/controls/templates/SwipeListItem.qml b/src/controls/templates/SwipeListItem.qml index a5b667fa..66f3bd9f 100644 --- a/src/controls/templates/SwipeListItem.qml +++ b/src/controls/templates/SwipeListItem.qml @@ -1,491 +1,478 @@ /* - * Copyright 2019 Marco Martin + * SPDX-FileCopyrightText: 2019 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.6 import QtQuick.Layouts 1.4 import QtQuick.Controls 2.4 as Controls import QtQuick.Templates 2.4 as T2 import org.kde.kirigami 2.11 as Kirigami import "../private" /** * An item delegate Intended to support extra actions obtainable * by uncovering them by dragging away the item with the handle * This acts as a container for normal list items. * Any subclass of AbstractListItem can be assigned as the contentItem property. * @code * ListView { * model: myModel * delegate: SwipeListItem { * QQC2.Label { * text: model.text * } * actions: [ * Action { * icon.name: "document-decrypt" * onTriggered: print("Action 1 clicked") * }, * Action { * icon.name: model.action2Icon * onTriggered: //do something * } * ] * } - * + * * } * @endcode * */ T2.SwipeDelegate { id: listItem /** * supportsMouseEvents: bool * Holds if the item emits signals related to mouse interaction. *TODO: remove * The default value is false. */ property alias supportsMouseEvents: listItem.hoverEnabled /** * containsMouse: bool * True when the user hover the mouse over the list item * NOTE: on mobile touch devices this will be true only when pressed is also true * KF6: remove */ property alias containsMouse: listItem.hovered /** * alternatingBackground: bool * If true the background of the list items will be alternating between two * colors, helping readability with multiple column views. * Use it only when implementing a view which shows data visually in multiple columns - * @ since 2.7 + * @ since 2.7 */ property bool alternatingBackground: false /** * sectionDelegate: bool * If true the item will be a delegate for a section, so will look like a * "title" for the items under it. */ property bool sectionDelegate: false /** * separatorVisible: bool * True if the separator between items is visible * default: true */ property bool separatorVisible: true /** * actionsVisible: bool * True if it's possible to see and access the item actions. * Actions should go completely out of the way for instance during * the editing of an item. * @since 2.5 */ readonly property bool actionsVisible: actionsLayout.hasVisibleActions /** * actions: list * Defines the actions for the list item: at most 4 buttons will * contain the actions for the item, that can be revealed by * sliding away the list item. */ property list actions /** * textColor: color * Color for the text in the item * * Note: if custom text elements are inserted in an AbstractListItem, * their color property will have to be manually bound with this property */ property color textColor: Kirigami.Theme.textColor /** * backgroundColor: color * Color for the background of the item */ property color backgroundColor: Kirigami.Theme.backgroundColor /** * alternateBackgroundColor: color * The background color to use if alternatingBackground is true. * It is advised to leave the default. * @since 2.7 */ property color alternateBackgroundColor: Kirigami.Theme.alternateBackgroundColor /** * activeTextColor: color * Color for the text in the item when pressed or selected * It is advised to leave the default value (Theme.highlightedTextColor) * * Note: if custom text elements are inserted in an AbstractListItem, * their color property will have to be manually bound with this property */ property color activeTextColor: Kirigami.Theme.highlightedTextColor /** * activeBackgroundColor: color * Color for the background of the item when pressed or selected * It is advised to leave the default value (Theme.highlightColor) */ property color activeBackgroundColor: Kirigami.Theme.highlightColor //TODO KF6 remove this super wrong thing default property alias _default: listItem.contentItem LayoutMirroring.childrenInherit: true hoverEnabled: true implicitWidth: contentItem ? contentItem.implicitWidth : Kirigami.Units.gridUnit * 12 width: parent ? parent.width : implicitWidth implicitHeight: Math.max(Kirigami.Units.gridUnit * 2, contentItem.implicitHeight) + topPadding + bottomPadding padding: Kirigami.Settings.tabletMode ? Kirigami.Units.largeSpacing : Kirigami.Units.smallSpacing leftPadding: padding * 2 rightPadding: padding * 2 + (overlayLoader.visible ? overlayLoader.width : 0) + Kirigami.Units.smallSpacing - + topPadding: padding bottomPadding: padding contentItem: Item {} QtObject { id: internal property Flickable view: listItem.ListView.view || (listItem.parent ? (listItem.parent.ListView.view || listItem.parent) : null) readonly property QtObject swipeFilterItem: (view && view.parent && view.parent.parent && view.parent.parent._swipeFilter) ? view.parent.parent._swipeFilter : null readonly property bool edgeEnabled: swipeFilterItem ? swipeFilterItem.currentItem === listItem || swipeFilterItem.currentItem === listItem.parent : false property bool indicateActiveFocus: listItem.pressed || Kirigami.Settings.tabletMode || listItem.activeFocus || (view ? view.activeFocus : false) // Search for scrollbar of the view or of the ScrollView property T2.ScrollBar verticalScrollBar: { if (!view) { return null; } return view.T2.ScrollBar.vertical || view.parent.T2.ScrollBar.vertical; } //install the SwipeItemEventFilter onViewChanged: { if (!Kirigami.Settings.tabletMode) { return; } if (internal.view && Kirigami.Settings.tabletMode && !internal.view.parent.parent._swipeFilter) { var component = Qt.createComponent(Qt.resolvedUrl("../private/SwipeItemEventFilter.qml")); internal.view.parent.parent._swipeFilter = component.createObject(internal.view.parent.parent); } } } Connections { target: Kirigami.Settings onTabletModeChanged: { if (Kirigami.Settings.tabletMode) { if (!internal.swipeFilterItem) { var component = Qt.createComponent(Qt.resolvedUrl("../private/SwipeItemEventFilter.qml")); listItem.ListView.view.parent.parent._swipeFilter = component.createObject(listItem.ListView.view.parent.parent); } } else { if (listItem.ListView.view.parent.parent._swipeFilter) { listItem.ListView.view.parent.parent._swipeFilter.destroy(); slideAnim.to = 0; slideAnim.restart(); } } } } //BEGIN Items Loader { id: overlayLoader anchors { right: contentItem ? contentItem.right : undefined top: parent.top bottom: parent.bottom rightMargin: -listItem.rightPadding + Kirigami.Units.smallSpacing } parent: listItem z: contentItem ? contentItem.z + 1 : 0 width: item ? item.implicitWidth : actionsLayout.implicitWidth active: Kirigami.Settings.tabletMode visible: listItem.actionsVisible && opacity > 0 sourceComponent: handleComponent opacity: Kirigami.Settings.tabletMode || listItem.hovered || !listItem.supportsMouseEvents ? 1 : 0 Behavior on opacity { OpacityAnimator { id: opacityAnim duration: Kirigami.Units.longDuration easing.type: Easing.InOutQuad } } } Component { id: handleComponent MouseArea { id: dragButton anchors { right: parent.right } implicitWidth: Kirigami.Units.iconSizes.smallMedium preventStealing: true readonly property real openPosition: (listItem.width - width - listItem.leftPadding * 2)/listItem.width property real startX: 0 property real lastPosition: 0 property bool openIntention onPressed: startX = mapToItem(listItem, 0, 0).x; onClicked: { if (Math.abs(mapToItem(listItem, 0, 0).x - startX) > Qt.styleHints.startDragDistance) { return; } if (listItem.LayoutMirroring.enabled) { if (listItem.swipe.position < 0.5) { slideAnim.to = openPosition } else { slideAnim.to = 0 } } else { if (listItem.swipe.position > -0.5) { slideAnim.to = -openPosition } else { slideAnim.to = 0 } } slideAnim.restart(); } onPositionChanged: { var pos = mapToItem(listItem, mouse.x, mouse.y); - + if (listItem.LayoutMirroring.enabled) { listItem.swipe.position = Math.max(0, Math.min(openPosition, (pos.x / listItem.width))); openIntention = listItem.swipe.position > lastPosition; } else { listItem.swipe.position = Math.min(0, Math.max(-openPosition, (pos.x / (listItem.width -listItem.rightPadding) - 1))); openIntention = listItem.swipe.position < lastPosition; } lastPosition = listItem.swipe.position; } onReleased: { if (listItem.LayoutMirroring.enabled) { if (openIntention) { slideAnim.to = openPosition } else { slideAnim.to = 0 } } else { if (openIntention) { slideAnim.to = -openPosition } else { slideAnim.to = 0 } } slideAnim.restart(); } Kirigami.Icon { id: handleIcon anchors.fill: parent selected: listItem.checked || (listItem.pressed && !listItem.checked && !listItem.sectionDelegate) source: (LayoutMirroring.enabled ? (listItem.background.x < listItem.background.width/2 ? "overflow-menu-right" : "overflow-menu-left") : (listItem.background.x < -listItem.background.width/2 ? "overflow-menu-right" : "overflow-menu-left")) } Connections { id: swipeFilterConnection target: internal.edgeEnabled ? internal.swipeFilterItem : null onPeekChanged: { if (!listItem.actionsVisible) { return; } if (listItem.LayoutMirroring.enabled) { listItem.swipe.position = Math.max(0, Math.min(dragButton.openPosition, internal.swipeFilterItem.peek)); dragButton.openIntention = listItem.swipe.position > dragButton.lastPosition; } else { listItem.swipe.position = Math.min(0, Math.max(-dragButton.openPosition, -internal.swipeFilterItem.peek)); dragButton.openIntention = listItem.swipe.position < dragButton.lastPosition; } dragButton.lastPosition = listItem.swipe.position; } onPressed: { if (internal.edgeEnabled) { dragButton.onPressed(mouse); } } onClicked: { if (Math.abs(listItem.background.x) < Units.gridUnit && internal.edgeEnabled) { dragButton.clicked(mouse); } } onReleased: { if (internal.edgeEnabled) { dragButton.released(mouse); } } onCurrentItemChanged: { if (!internal.edgeEnabled) { slideAnim.to = 0; slideAnim.restart(); } } } } } //TODO: expose in API? Component { id: actionsBackgroundDelegate MouseArea { anchors.fill: parent // Controls.SwipeDelegate.onPressedChanged is broken with touch onClicked: { slideAnim.to = 0; slideAnim.restart(); } Rectangle { anchors.fill: parent color: parent.pressed ? Qt.darker(Kirigami.Theme.backgroundColor, 1.1) : Qt.darker(Kirigami.Theme.backgroundColor, 1.05) } visible: listItem.swipe.position != 0 EdgeShadow { edge: Qt.TopEdge visible: background.x != 0 anchors { right: parent.right left: parent.left top: parent.top } } EdgeShadow { edge: LayoutMirroring.enabled ? Qt.RightEdge : Qt.LeftEdge x: LayoutMirroring.enabled ? listItem.background.x - width : (listItem.background.x + listItem.background.width) visible: background.x != 0 anchors { top: parent.top bottom: parent.bottom } } } } - + RowLayout { id: actionsLayout anchors { right: parent.right top: parent.top bottom: parent.bottom rightMargin: Kirigami.Units.smallSpacing } visible: parent != listItem parent: Kirigami.Settings.tabletMode - ? listItem.swipe.leftItem || listItem.swipe.rightItem || listItem + ? listItem.swipe.leftItem || listItem.swipe.rightItem || listItem : overlayLoader property bool hasVisibleActions: false function updateVisibleActions(definitelyVisible = false) { if (definitelyVisible) { hasVisibleActions = true; } else { var actionCount = listItem.actions.length; for (var i = 0; i < actionCount; i++) { // Assuming that visible is only false if it is explicitly false, and not just falsy if (listItem.actions[i].visible === false) { continue; } hasVisibleActions = true; break; } } } Repeater { model: { if (listItem.actions.length === 0) { return null; } else { return listItem.actions[0].text !== undefined && listItem.actions[0].trigger !== undefined ? listItem.actions : listItem.actions[0]; } } delegate: Controls.ToolButton { icon.name: modelData.iconName !== "" ? modelData.iconName : "" icon.source: modelData.iconSource !== "" ? modelData.iconSource : "" enabled: (modelData && modelData.enabled !== undefined) ? modelData.enabled : true; visible: (modelData && modelData.visible !== undefined) ? modelData.visible : true; onVisibleChanged: actionsLayout.updateVisibleActions(visible); Component.onCompleted: actionsLayout.updateVisibleActions(visible); Component.onDestruction: actionsLayout.updateVisibleActions(visible); Controls.ToolTip.delay: Kirigami.Units.toolTipDelay Controls.ToolTip.timeout: 5000 Controls.ToolTip.visible: listItem.visible && (Kirigami.Settings.tabletMode ? pressed : hovered) && Controls.ToolTip.text.length > 0 Controls.ToolTip.text: modelData.tooltip || modelData.text onClicked: { if (modelData && modelData.trigger !== undefined) { modelData.trigger(); } slideAnim.to = 0; slideAnim.restart(); } } } } - + background: DefaultListItemBackground {} swipe { enabled: false right: listItem.LayoutMirroring.enabled || !Kirigami.Settings.tabletMode ? null : actionsBackgroundDelegate left: listItem.LayoutMirroring.enabled && Kirigami.Settings.tabletMode ? actionsBackgroundDelegate : null } NumberAnimation { id: slideAnim duration: Kirigami.Units.longDuration easing.type: Easing.InOutQuad target: listItem.swipe property: "position" from: listItem.swipe.position } //END Items } diff --git a/src/controls/templates/private/BackButton.qml b/src/controls/templates/private/BackButton.qml index 45fc2f74..b0eab875 100644 --- a/src/controls/templates/private/BackButton.qml +++ b/src/controls/templates/private/BackButton.qml @@ -1,52 +1,39 @@ /* - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.1 import QtQuick.Layouts 1.2 import QtQuick.Controls 2.0 as Controls import org.kde.kirigami 2.4 Controls.ToolButton { id: button icon.name: (LayoutMirroring.enabled ? "go-previous-symbolic-rtl" : "go-previous-symbolic") enabled: applicationWindow().pageStack.layers.depth > 1 || (applicationWindow().pageStack.depth > 1 && (applicationWindow().pageStack.currentIndex > 0 || applicationWindow().pageStack.contentItem.contentX > 0)) visible: applicationWindow().pageStack.layers.depth > 1 || (applicationWindow().pageStack.contentItem.contentWidth > applicationWindow().pageStack.width && (globalToolBar.showNavigationButtons === true || (globalToolBar.showNavigationButtons & ApplicationHeaderStyle.ShowBackButton))) width: height height: parent.height onClicked: { if (applicationWindow().pageStack.layers && applicationWindow().pageStack.layers.depth > 1) { applicationWindow().pageStack.layers.pop(); } else { applicationWindow().pageStack.goBack(); } } Controls.ToolTip { visible: button.hovered text: qsTr("Navigate Back") delay: Units.toolTipDelay timeout: 5000 y: button.height } } diff --git a/src/controls/templates/private/ContextIcon.qml b/src/controls/templates/private/ContextIcon.qml index 7e8edd80..0e5e66f2 100644 --- a/src/controls/templates/private/ContextIcon.qml +++ b/src/controls/templates/private/ContextIcon.qml @@ -1,80 +1,67 @@ /* - * Copyright 2015 Marco Martin + * SPDX-FileCopyrightText: 2015 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.1 import QtQuick.Layouts 1.2 import QtGraphicalEffects 1.0 import org.kde.kirigami 2.4 Item { id: canvas width: height height: Units.iconSizes.smallMedium property OverlayDrawer drawer property color color: Theme.textColor opacity: 0.8 layer.enabled: true Item { id: iconRoot anchors { fill: parent margins: Units.smallSpacing } property int thickness: Math.floor(Units.devicePixelRatio)*2 Rectangle { anchors { horizontalCenter: parent.horizontalCenter top: parent.top //horizontalCenterOffset: -parent.width/2 topMargin: (parent.height/2 - iconRoot.thickness/2) * drawer.position } antialiasing: drawer.position != 0 transformOrigin: Item.Center width: (1 - drawer.position) * height + drawer.position * (Math.sqrt(2*(parent.width*parent.width))) height: iconRoot.thickness color: canvas.color rotation: 45 * drawer.position } Rectangle { anchors.centerIn: parent width: height height: iconRoot.thickness color: canvas.color } Rectangle { anchors { horizontalCenter: parent.horizontalCenter bottom: parent.bottom // topMargin: -iconRoot.thickness/2 * drawer.position bottomMargin: (parent.height/2 - iconRoot.thickness/2) * drawer.position } antialiasing: drawer.position != 0 transformOrigin: Item.Center width: (1 - drawer.position) * height + drawer.position * (Math.sqrt(2*(parent.width*parent.width))) height: iconRoot.thickness color: canvas.color rotation: -45 * drawer.position } } } diff --git a/src/controls/templates/private/ForwardButton.qml b/src/controls/templates/private/ForwardButton.qml index 7335a866..783bbed3 100644 --- a/src/controls/templates/private/ForwardButton.qml +++ b/src/controls/templates/private/ForwardButton.qml @@ -1,48 +1,35 @@ /* - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.1 import QtQuick.Layouts 1.2 import QtQuick.Controls 2.0 as Controls import org.kde.kirigami 2.4 Controls.ToolButton { id: button icon.name: (LayoutMirroring.enabled ? "go-next-symbolic-rtl" : "go-next-symbolic") enabled: applicationWindow().pageStack.currentIndex < applicationWindow().pageStack.depth-1 visible: applicationWindow().pageStack.layers.depth == 1 && applicationWindow().pageStack.contentItem.contentWidth > applicationWindow().pageStack.width && (globalToolBar.showNavigationButtons === true || (globalToolBar.showNavigationButtons & ApplicationHeaderStyle.ShowForwardButton)) width: height height: parent.height onClicked: applicationWindow().pageStack.goForward(); Controls.ToolTip { visible: button.hovered text: qsTr("Navigate Forward") delay: Units.toolTipDelay timeout: 5000 y: button.height } } diff --git a/src/controls/templates/private/GenericDrawerIcon.qml b/src/controls/templates/private/GenericDrawerIcon.qml index 94e2a0cb..055820fc 100644 --- a/src/controls/templates/private/GenericDrawerIcon.qml +++ b/src/controls/templates/private/GenericDrawerIcon.qml @@ -1,48 +1,35 @@ /* - * Copyright 2015 Marco Martin + * SPDX-FileCopyrightText: 2015 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.1 import QtQuick.Layouts 1.2 import QtGraphicalEffects 1.0 import org.kde.kirigami 2.4 as Kirigami Item { width: height height: Kirigami.Units.iconSizes.smallMedium property Kirigami.OverlayDrawer drawer property color color: Theme.textColor opacity: 0.8 layer.enabled: true Kirigami.Icon { selected: drawer.handle.pressed opacity: 1 - drawer.position anchors.fill: parent source: drawer.handleClosedIcon.source color: drawer.handleClosedIcon.color } Kirigami.Icon { selected: drawer.handle.pressed opacity: drawer.position anchors.fill: parent source: drawer.handleOpenIcon.source color: drawer.handleOpenIcon.color } } diff --git a/src/controls/templates/private/IconPropertiesGroup.qml b/src/controls/templates/private/IconPropertiesGroup.qml index 6e7656aa..7ce15b95 100644 --- a/src/controls/templates/private/IconPropertiesGroup.qml +++ b/src/controls/templates/private/IconPropertiesGroup.qml @@ -1,27 +1,14 @@ /* - * Copyright 2017 Marco Martin + * SPDX-FileCopyrightText: 2017 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQml 2.1 //this is a subset of those used in QQC2, Kirigami.Action still needs the full one as needs 100% api compatibility QtObject { property var source property color color: Qt.rgba(0, 0, 0, 0) } diff --git a/src/controls/templates/private/MenuIcon.qml b/src/controls/templates/private/MenuIcon.qml index 6d751ef4..6f64f443 100644 --- a/src/controls/templates/private/MenuIcon.qml +++ b/src/controls/templates/private/MenuIcon.qml @@ -1,79 +1,66 @@ /* - * Copyright 2015 Marco Martin + * SPDX-FileCopyrightText: 2015 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.1 import QtQuick.Layouts 1.2 import QtGraphicalEffects 1.0 import org.kde.kirigami 2.4 Item { id: canvas width: height height: Units.iconSizes.smallMedium property OverlayDrawer drawer property color color: Theme.textColor opacity: 0.8 layer.enabled: true Item { id: iconRoot anchors { fill: parent margins: Units.smallSpacing } readonly property int thickness: Math.floor(Units.devicePixelRatio)*2 readonly property real drawerPosition: drawer ? drawer.position : 0 Rectangle { anchors { right: parent.right top: parent.top topMargin: -iconRoot.thickness/2 * iconRoot.drawerPosition } antialiasing: iconRoot.drawerPosition != 0 transformOrigin: Item.Right width: (1 - iconRoot.drawerPosition) * parent.width + iconRoot.drawerPosition * (Math.sqrt(2*(parent.width*parent.width))) height: iconRoot.thickness color: canvas.color rotation: -45 * iconRoot.drawerPosition } Rectangle { anchors.centerIn: parent width: parent.width - parent.width * iconRoot.drawerPosition height: iconRoot.thickness color: canvas.color } Rectangle { anchors { right: parent.right bottom: parent.bottom bottomMargin: -iconRoot.thickness/2 * iconRoot.drawerPosition } antialiasing: iconRoot.drawerPosition != 0 transformOrigin: Item.Right width: (1 - iconRoot.drawerPosition) * parent.width + iconRoot.drawerPosition * (Math.sqrt(2*(parent.width*parent.width))) height: iconRoot.thickness color: canvas.color rotation: 45 * iconRoot.drawerPosition } } } diff --git a/src/controls/templates/private/PassiveNotification.qml b/src/controls/templates/private/PassiveNotification.qml index f23f348a..3f6daa33 100644 --- a/src/controls/templates/private/PassiveNotification.qml +++ b/src/controls/templates/private/PassiveNotification.qml @@ -1,161 +1,148 @@ /* - * Copyright 2015 Marco Martin + * SPDX-FileCopyrightText: 2015 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.5 import QtQuick.Controls 2.0 as QQC2 import QtQuick.Layouts 1.2 import QtQuick.Window 2.2 import QtGraphicalEffects 1.0 import org.kde.kirigami 2.4 MouseArea { id: root z: 9999999 width: background.width height: background.height opacity: 0 enabled: appearAnimation.appear anchors { horizontalCenter: parent.horizontalCenter bottom: parent.bottom bottomMargin: Units.gridUnit * 4 } function showNotification(message, timeout, actionText, callBack) { if (!message) { return; } appearAnimation.running = false; appearAnimation.appear = true; appearAnimation.running = true; if (timeout == "short") { timer.interval = 4000; } else if (timeout == "long") { timer.interval = 12000; } else if (timeout > 0) { timer.interval = timeout; } else { timer.interval = 7000; } messageLabel.text = message ? message : ""; actionButton.text = actionText ? actionText : ""; actionButton.callBack = callBack ? callBack : ""; timer.stop(); // stop first to ensure it always starts anew // Only start the timer when the window has focus, to ensure that // messages don't get missed on the desktop where it's common to //be working with multiple windows at once timer.running = Qt.binding(function() { return root.Window.active; }); } function hideNotification() { appearAnimation.running = false; appearAnimation.appear = false; appearAnimation.running = true; } onClicked: { appearAnimation.appear = false; appearAnimation.running = true; } transform: Translate { id: transform y: root.height } Timer { id: timer interval: 4000 onTriggered: { appearAnimation.appear = false; appearAnimation.running = true; } } ParallelAnimation { id: appearAnimation property bool appear: true NumberAnimation { target: root properties: "opacity" to: appearAnimation.appear ? 1 : 0 duration: Units.longDuration easing.type: Easing.InOutQuad } NumberAnimation { target: transform properties: "y" to: appearAnimation.appear ? 0 : background.height duration: Units.longDuration easing.type: appearAnimation.appear ? Easing.OutQuad : Easing.InQuad } } Item { id: background width: backgroundRect.width + Units.gridUnit height: backgroundRect.height + Units.gridUnit Rectangle { id: backgroundRect anchors.centerIn: parent radius: Units.smallSpacing color: Theme.textColor opacity: 0.6 width: mainLayout.width + Math.round((height - mainLayout.height)) height: Math.max(mainLayout.height + Units.smallSpacing*2, Units.gridUnit*2) } RowLayout { id: mainLayout anchors.centerIn: parent QQC2.Label { id: messageLabel Layout.maximumWidth: Math.min(root.parent.width - Units.largeSpacing*2, implicitWidth) elide: Text.ElideRight wrapMode: Text.WordWrap maximumLineCount: 4 color: Theme.backgroundColor } QQC2.Button { id: actionButton property var callBack visible: text != "" onClicked: { appearAnimation.appear = false; appearAnimation.running = true; if (callBack) { callBack(); } } } } layer.enabled: true layer.effect: DropShadow { horizontalOffset: 0 verticalOffset: 0 radius: Units.gridUnit samples: 32 color: Qt.rgba(0, 0, 0, 0.5) } } } diff --git a/src/controls/templates/private/ScrollView.qml b/src/controls/templates/private/ScrollView.qml index f207511f..93731bd4 100644 --- a/src/controls/templates/private/ScrollView.qml +++ b/src/controls/templates/private/ScrollView.qml @@ -1,151 +1,138 @@ /* - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.7 import QtQuick.Controls 2.0 import org.kde.kirigami 2.9 as Kirigami MouseArea { id: root default property Item contentItem property Flickable flickableItem clip: true //TODO: horizontalScrollBarPolicy is completely noop just for compatibility right now property int horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff property int verticalScrollBarPolicy: Qt.ScrollBarAsNeeded property int topPadding: 0 property int leftPadding: 0 property int rightPadding: !Kirigami.Settings.hasTransientTouchInput && flickableItem.ScrollBar.vertical && flickableItem.ScrollBar.vertical.visible ? flickableItem.ScrollBar.vertical.width : 0 property int bottomPadding: 0 readonly property Item verticalScrollBar: flickableItem.ScrollBar.vertical ? flickableItem.ScrollBar.vertical : null onVerticalScrollBarPolicyChanged: { if (flickableItem.ScrollBar.vertical) { flickableItem.ScrollBar.vertical.visible = verticalScrollBarPolicy != Qt.ScrollBarAlwaysOff; } scrollBarCreationTimer.restart(); } onHorizontalScrollBarPolicyChanged: { if (flickableItem.ScrollBar.horizontal) { flickableItem.ScrollBar.horizontal.visible = horizontalScrollBarPolicy != Qt.ScrollBarAlwaysOff; } scrollBarCreationTimer.restart(); } onContentItemChanged: { if (contentItem.hasOwnProperty("contentY")) { flickableItem = contentItem; if (typeof(flickableItem.keyNavigationEnabled) != "undefined") { flickableItem.keyNavigationEnabled = true; flickableItem.keyNavigationWraps = false; } contentItem.parent = flickableParent; } else { flickableItem = flickableComponent.createObject(flickableParent); contentItem.parent = flickableItem.contentItem; } flickableItem.interactive = true; flickableItem.anchors.fill = flickableParent; scrollBarCreationTimer.restart(); } Timer { id: scrollBarCreationTimer interval: 0 onTriggered: { //create or destroy the vertical scrollbar - if ((!flickableItem.ScrollBar.vertical) && + if ((!flickableItem.ScrollBar.vertical) && verticalScrollBarPolicy != Qt.ScrollBarAlwaysOff) { flickableItem.ScrollBar.vertical = verticalScrollComponent.createObject(root); } else if (flickableItem.ScrollBar.vertical && verticalScrollBarPolicy == Qt.ScrollBarAlwaysOff) { flickableItem.ScrollBar.vertical.destroy(); } //create or destroy the horizontal scrollbar - if ((!flickableItem.ScrollBar.horizontal) && + if ((!flickableItem.ScrollBar.horizontal) && horizontalScrollBarPolicy != Qt.ScrollBarAlwaysOff) { flickableItem.ScrollBar.horizontal = horizontalScrollComponent.createObject(root); } else if (flickableItem.ScrollBar.horizontal && horizontalScrollBarPolicy == Qt.ScrollBarAlwaysOff) { flickableItem.ScrollBar.horizontal.destroy(); } } } Kirigami.WheelHandler { id: wheelHandler target: root.flickableItem } Item { id: flickableParent clip: true anchors { fill: parent leftMargin: root.leftPadding topMargin: root.topPadding rightMargin: root.rightPadding bottomMargin: root.bottomPadding } } Component { id: flickableComponent Flickable { anchors { fill: parent } contentWidth: root.contentItem ? root.contentItem.width : 0 contentHeight: root.contentItem ? root.contentItem.height : 0 } } Component { id: verticalScrollComponent ScrollBar { z: flickableParent.z + 1 visible: root.contentItem.visible && size < 1 interactive: !Kirigami.Settings.hasTransientTouchInput //NOTE: use this instead of anchors as crashes on some Qt 5.8 checkouts height: parent.height anchors { right: parent.right top: parent.top } } } Component { id: horizontalScrollComponent ScrollBar { z: flickableParent.z + 1 visible: root.contentItem.visible && size < 1 interactive: !Kirigami.Settings.hasTransientTouchInput //NOTE: use this instead of anchors as crashes on some Qt 5.8 checkouts height: parent.height - anchors.topMargin anchors { left: parent.left right: parent.right bottom: parent.bottom } } } } diff --git a/src/delegaterecycler.cpp b/src/delegaterecycler.cpp index 79a43c1b..d132f5a0 100644 --- a/src/delegaterecycler.cpp +++ b/src/delegaterecycler.cpp @@ -1,412 +1,399 @@ /* - * Copyright 2011 Marco Martin - * Copyright 2014 Aleix Pol Gonzalez + * SPDX-FileCopyrightText: 2011 Marco Martin + * SPDX-FileCopyrightText: 2014 Aleix Pol Gonzalez * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ #include "delegaterecycler.h" #include #include #include #include DelegateRecyclerAttached::DelegateRecyclerAttached(QObject *parent) : QObject(parent) { } DelegateRecyclerAttached::~DelegateRecyclerAttached() {} /* void setRecycler(DelegateRecycler *recycler) { m_recycler = recycler; } DelegateRecycler *recycler() const { return m_recycler; } */ class DelegateCache { public: DelegateCache(); ~DelegateCache(); void ref(QQmlComponent *); void deref(QQmlComponent *); void insert(QQmlComponent *, QQuickItem *); QQuickItem *take(QQmlComponent *); private: static const int s_cacheSize = 40; QHash m_refs; QHash > m_unusedItems; }; Q_GLOBAL_STATIC(DelegateCache, s_delegateCache) DelegateCache::DelegateCache() { } DelegateCache::~DelegateCache() { for (auto& item : qAsConst(m_unusedItems)) { qDeleteAll(item); } } void DelegateCache::ref(QQmlComponent *component) { m_refs[component]++; } void DelegateCache::deref(QQmlComponent *component) { auto itRef = m_refs.find(component); if (itRef == m_refs.end()) { return; } (*itRef)--; if (*itRef <= 0) { m_refs.erase(itRef); qDeleteAll(m_unusedItems.take(component)); } } void DelegateCache::insert(QQmlComponent *component, QQuickItem *item) { auto& items = m_unusedItems[component]; if (items.length() >= s_cacheSize) { item->deleteLater(); return; } DelegateRecyclerAttached *attached = qobject_cast(qmlAttachedPropertiesObject(item, false)); if (attached) { emit attached->pooled(); } - + item->setParentItem(nullptr); items.append(item); } QQuickItem *DelegateCache::take(QQmlComponent *component) { auto it = m_unusedItems.find(component); if (it != m_unusedItems.end() && !it->isEmpty()) { return it->takeFirst(); } return nullptr; } DelegateRecycler::DelegateRecycler(QQuickItem *parent) : QQuickItem(parent) { setFlags(QQuickItem::ItemIsFocusScope); } DelegateRecycler::~DelegateRecycler() { if (m_sourceComponent) { s_delegateCache->insert(m_sourceComponent, m_item); s_delegateCache->deref(m_sourceComponent); } } void DelegateRecycler::syncIndex() { const QVariant newIndex = m_propertiesTracker->property("trackedIndex"); if (!newIndex.isValid()) { return; } QQmlContext *ctx = QQmlEngine::contextForObject(m_item)->parentContext(); ctx->setContextProperty(QStringLiteral("index"), newIndex); } void DelegateRecycler::syncModel() { const QVariant newModel = m_propertiesTracker->property("trackedModel"); if (!newModel.isValid()) { return; } QQmlContext *ctx = QQmlEngine::contextForObject(m_item)->parentContext(); ctx->setContextProperty(QStringLiteral("model"), newModel); //try to bind all properties QObject *modelObj = newModel.value(); if (modelObj) { const QMetaObject *metaObj = modelObj->metaObject(); for (int i = metaObj->propertyOffset(); i < metaObj->propertyCount(); ++i) { ctx->setContextProperty(QString::fromUtf8(metaObj->property(i).name()), metaObj->property(i).read(modelObj)); } } } void DelegateRecycler::syncModelProperties() { const QVariant model = m_propertiesTracker->property("trackedModel"); if (!model.isValid()) { return; } QQmlContext *ctx = QQmlEngine::contextForObject(m_item)->parentContext(); //try to bind all properties QObject *modelObj = model.value(); if (modelObj) { const QMetaObject *metaObj = modelObj->metaObject(); for (int i = metaObj->propertyOffset(); i < metaObj->propertyCount(); ++i) { ctx->setContextProperty(QString::fromUtf8(metaObj->property(i).name()), metaObj->property(i).read(modelObj)); } } } void DelegateRecycler::syncModelData() { const QVariant newModelData = m_propertiesTracker->property("trackedModelData"); if (!newModelData.isValid()) { return; } QQmlContext *ctx = QQmlEngine::contextForObject(m_item)->parentContext(); ctx->setContextProperty(QStringLiteral("modelData"), newModelData); } QQmlComponent *DelegateRecycler::sourceComponent() const { return m_sourceComponent; } void DelegateRecycler::setSourceComponent(QQmlComponent *component) { if (component && component->parent() == this) { qWarning() << "Error: source components cannot be declared inside DelegateRecycler"; return; } if (m_sourceComponent == component) { return; } if (!m_propertiesTracker) { static QMap propertiesTrackerComponent; auto engine = qmlEngine(this); auto it = propertiesTrackerComponent.find(engine); if (it == propertiesTrackerComponent.end()) { connect(engine, &QObject::destroyed, engine, [engine] { propertiesTrackerComponent.remove(engine); }); it = propertiesTrackerComponent.insert(engine, new QQmlComponent(engine, engine)); (*it)->setData(QByteArrayLiteral("import QtQuick 2.3\nQtObject{property int trackedIndex: index; property var trackedModel: typeof model != 'undefined' ? model : null; property var trackedModelData: typeof modelData != 'undefined' ? modelData : null}"), QUrl(QStringLiteral("delegaterecycler.cpp"))); } m_propertiesTracker = (*it)->create(QQmlEngine::contextForObject(this)); connect(m_propertiesTracker, SIGNAL(trackedIndexChanged()), this, SLOT(syncIndex())); connect(m_propertiesTracker, SIGNAL(trackedModelChanged()), this, SLOT(syncModel())); connect(m_propertiesTracker, SIGNAL(trackedModelDataChanged()), this, SLOT(syncModelData())); } if (m_sourceComponent) { if (m_item) { disconnect(m_item.data(), &QQuickItem::implicitWidthChanged, this, &DelegateRecycler::updateHints); disconnect(m_item.data(), &QQuickItem::implicitHeightChanged, this, &DelegateRecycler::updateHints); s_delegateCache->insert(component, m_item); } s_delegateCache->deref(component); } m_sourceComponent = component; s_delegateCache->ref(component); m_item = s_delegateCache->take(component); if (!m_item) { QQuickItem *candidate = parentItem(); QQmlContext *ctx = nullptr; while (candidate) { QQmlContext *parentCtx = QQmlEngine::contextForObject(candidate); if (parentCtx) { ctx = new QQmlContext(parentCtx, candidate); break; } else { candidate = candidate->parentItem(); } } Q_ASSERT(ctx); QObject *contextObjectToSet = nullptr; { // Find the first parent that has a context object with a valid translationDomain property, i.e. is a KLocalizedContext QQmlContext *auxCtx = ctx; while (auxCtx != nullptr) { QObject *auxCtxObj = auxCtx->contextObject(); if (auxCtxObj && auxCtxObj->property("translationDomain").isValid()) { contextObjectToSet = auxCtxObj; break; } auxCtx = auxCtx->parentContext(); } } if (contextObjectToSet) { ctx->setContextObject(contextObjectToSet); } QObject *modelObj = m_propertiesTracker->property("trackedModel").value(); if (modelObj) { const QMetaObject *metaObj = modelObj->metaObject(); for (int i = metaObj->propertyOffset(); i < metaObj->propertyCount(); ++i) { QMetaProperty prop = metaObj->property(i); ctx->setContextProperty(QString::fromUtf8(prop.name()), prop.read(modelObj)); if (prop.hasNotifySignal()) { QMetaMethod updateSlot = metaObject()->method( metaObject()->indexOfSlot("syncModelProperties()")); connect(modelObj, prop.notifySignal(), this, updateSlot); } } } ctx->setContextProperty(QStringLiteral("model"), m_propertiesTracker->property("trackedModel")); ctx->setContextProperty(QStringLiteral("modelData"), m_propertiesTracker->property("trackedModelData")); ctx->setContextProperty(QStringLiteral("index"), m_propertiesTracker->property("trackedIndex")); ctx->setContextProperty(QStringLiteral("delegateRecycler"), this); QObject * obj = component->create(ctx); m_item = qobject_cast(obj); if (!m_item) { obj->deleteLater(); } else { connect(m_item.data(), &QObject::destroyed, ctx, &QObject::deleteLater); //if the user binded an explicit width, consider it, otherwise base upon implicit m_widthFromItem = m_item->width() > 0 && m_item->width() != m_item->implicitWidth(); m_heightFromItem = m_item->height() > 0 && m_item->height() != m_item->implicitHeight(); if (m_widthFromItem && m_heightFromItem) { connect(m_item.data(), &QQuickItem::heightChanged, this, [this]() { updateSize(false); }); } } } else { syncModel(); QQmlContext *ctx = QQmlEngine::contextForObject(m_item)->parentContext(); ctx->setContextProperties({ QQmlContext::PropertyPair{ QStringLiteral("modelData"), m_propertiesTracker->property("trackedModelData") }, QQmlContext::PropertyPair{ QStringLiteral("index"), m_propertiesTracker->property("trackedIndex")}, QQmlContext::PropertyPair{ QStringLiteral("delegateRecycler"), QVariant::fromValue(this) } }); DelegateRecyclerAttached *attached = qobject_cast(qmlAttachedPropertiesObject(m_item, false)); if (attached) { emit attached->reused(); } } if (m_item) { m_item->setParentItem(this); connect(m_item.data(), &QQuickItem::implicitWidthChanged, this, &DelegateRecycler::updateHints); connect(m_item.data(), &QQuickItem::implicitHeightChanged, this, &DelegateRecycler::updateHints); updateSize(true); } emit sourceComponentChanged(); } void DelegateRecycler::resetSourceComponent() { s_delegateCache->deref(m_sourceComponent); m_sourceComponent = nullptr; } DelegateRecyclerAttached *DelegateRecycler::qmlAttachedProperties(QObject *object) { return new DelegateRecyclerAttached(object); } void DelegateRecycler::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { if (m_item && newGeometry.size() != oldGeometry.size()) { updateSize(true); } QQuickItem::geometryChanged(newGeometry, oldGeometry); } void DelegateRecycler::focusInEvent(QFocusEvent *event) { QQuickItem::focusInEvent(event); if (!m_item) { return; } m_item->setFocus(event->reason()); } void DelegateRecycler::updateHints() { updateSize(false); } void DelegateRecycler::updateSize(bool parentResized) { if (!m_item) { return; } const bool needToUpdateWidth = !m_widthFromItem && parentResized && widthValid(); const bool needToUpdateHeight = !m_heightFromItem && parentResized && heightValid(); if (parentResized) { m_item->setPosition(QPoint(0,0)); } if (needToUpdateWidth && needToUpdateHeight) { m_item->setSize(QSizeF(width(), height())); } else if (needToUpdateWidth) { m_item->setWidth(width()); } else if (needToUpdateHeight) { m_item->setHeight(height()); } if (m_updatingSize) { return; } m_updatingSize = true; if (m_heightFromItem) { setHeight(m_item->height()); } if (m_widthFromItem) { setWidth(m_item->width()); } setImplicitSize(m_item->implicitWidth() >= 0 ? m_item->implicitWidth() : m_item->width(), m_item->implicitHeight() >= 0 ? m_item->implicitHeight() : m_item->height()); m_updatingSize = false; } diff --git a/src/delegaterecycler.h b/src/delegaterecycler.h index 05b1913f..14947de1 100644 --- a/src/delegaterecycler.h +++ b/src/delegaterecycler.h @@ -1,112 +1,99 @@ /* - * Copyright 2018 Marco Martin + * SPDX-FileCopyrightText: 2018 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef DELEGATERECYCLER_H #define DELEGATERECYCLER_H #include #include #include class DelegateCache; class DelegateRecyclerAttached : public QObject { Q_OBJECT public: DelegateRecyclerAttached(QObject *parent = nullptr); ~DelegateRecyclerAttached(); Q_SIGNALS: void pooled(); void reused(); }; /** * This class may be used as a delegate of a ListView or a GridView in the case * the intended delegate is a bit heavy, with many objects inside. * This will ensure the delegate instances will be put back in a common pool after * destruction, so when scrolling a big list, the delegates from old delete items will * be taken from the pool and reused, minimizing the need of instantiating new objects * and deleting old ones. It ensures scrolling of lists with heavy delegates is * smoother and helps with memory fragmentations as well. - * - * NOTE: CardListView and CardGridView are already using this recycler, so do NOT use it + * + * NOTE: CardListView and CardGridView are already using this recycler, so do NOT use it * as a delegate for those 2 views. * Also, do NOT use this with a Repeater. * @since 2.4 */ class DelegateRecycler : public QQuickItem { Q_OBJECT /** * The Component the actual delegates will be built from. * Note: the component may not be a child of this object, therefore it can't be * declared inside the DelegateRecycler declaration. * The DelegateRecycler will not take ownership of the delegate Component, so it's up * to the caller to delete it (usually with the normal child/parent relationship) */ Q_PROPERTY(QQmlComponent *sourceComponent READ sourceComponent WRITE setSourceComponent RESET resetSourceComponent NOTIFY sourceComponentChanged) public: DelegateRecycler(QQuickItem *parent = nullptr); ~DelegateRecycler(); QQmlComponent *sourceComponent() const; void setSourceComponent(QQmlComponent *component); void resetSourceComponent(); static DelegateRecyclerAttached *qmlAttachedProperties(QObject *object); protected: void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; void focusInEvent(QFocusEvent *event) override; void updateHints(); void updateSize(bool parentResized); Q_SIGNALS: void sourceComponentChanged(); private Q_SLOTS: void syncIndex(); void syncModel(); void syncModelProperties(); void syncModelData(); private: QPointer m_sourceComponent; QPointer m_item; QObject *m_propertiesTracker = nullptr; bool m_updatingSize = false; bool m_widthFromItem = false; bool m_heightFromItem = false; }; QML_DECLARE_TYPEINFO(DelegateRecycler, QML_HAS_ATTACHED_PROPERTIES) #endif diff --git a/src/enums.cpp b/src/enums.cpp index 5c101628..73cfc721 100644 --- a/src/enums.cpp +++ b/src/enums.cpp @@ -1,22 +1,9 @@ /* -* Copyright (C) 2016 by Marco Martin -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU Library General Public License as -* published by the Free Software Foundation; either version 2, or -* (at your option) any later version. -* -* This program 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 Library General Public License for more details -* -* You should have received a copy of the GNU Library General Public -* License along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ + * SPDX-FileCopyrightText: 2016 Marco Martin + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ #include "enums.h" #include "moc_enums.cpp" diff --git a/src/enums.h b/src/enums.h index 2415de39..f41db3fb 100644 --- a/src/enums.h +++ b/src/enums.h @@ -1,63 +1,50 @@ /* -* Copyright (C) 2016 by Marco Martin -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU Library General Public License as -* published by the Free Software Foundation; either version 2, or -* (at your option) any later version. -* -* This program 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 Library General Public License for more details -* -* You should have received a copy of the GNU Library General Public -* License along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ + * SPDX-FileCopyrightText: 2016 Marco Martin + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ #ifndef ENUMS_H #define ENUMS_H #include class ApplicationHeaderStyle : public QObject { Q_OBJECT public: enum Status { Auto = 0, Breadcrumb, Titles, TabBar, ToolBar, ///@since 5.48 None ///@since 5.48 }; Q_ENUM(Status) enum NavigationButton { NoNavigationButtons = 0, ShowBackButton = 0x1, ShowForwardButton = 0x2 }; Q_ENUM(NavigationButton) Q_DECLARE_FLAGS(NavigationButtons, NavigationButton) }; class MessageType : public QObject { Q_OBJECT Q_ENUMS(Type) public: enum Type { Information = 0, Positive, Warning, Error }; }; #endif // ENUMS_H diff --git a/src/formlayoutattached.cpp b/src/formlayoutattached.cpp index 13754a48..acd9f2e9 100644 --- a/src/formlayoutattached.cpp +++ b/src/formlayoutattached.cpp @@ -1,129 +1,116 @@ /* -* Copyright (C) 2017 by Marco Martin -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU Library General Public License as -* published by the Free Software Foundation; either version 2, or -* (at your option) any later version. -* -* This program 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 Library General Public License for more details -* -* You should have received a copy of the GNU Library General Public -* License along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ + * SPDX-FileCopyrightText: 2017 Marco Martin + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ #include "formlayoutattached.h" #include #include FormLayoutAttached::FormLayoutAttached(QObject *parent) : QObject(parent) { m_buddyFor = qobject_cast(parent); } FormLayoutAttached::~FormLayoutAttached() { } void FormLayoutAttached::setLabel(const QString &text) { if (m_label == text) { return; } m_label = text; emit labelChanged(); } QString FormLayoutAttached::label() const { return m_label; } void FormLayoutAttached::setIsSection(bool section) { if (m_isSection == section) { return; } m_isSection = section; emit isSectionChanged(); } bool FormLayoutAttached::isSection() const { return m_isSection; } void FormLayoutAttached::setCheckable(bool checkable) { if (checkable == m_checkable) { return; } - + m_checkable = checkable; emit checkableChanged(); } bool FormLayoutAttached::checkable() const { return m_checkable; } void FormLayoutAttached::setChecked(bool checked) { if (checked == m_checked) { return; } - + m_checked = checked; emit checkedChanged(); } bool FormLayoutAttached::checked() const { return m_checked; } void FormLayoutAttached::setEnabled(bool enabled) { if (enabled == m_enabled) { return; } - + m_enabled = enabled; emit enabledChanged(); } bool FormLayoutAttached::enabled() const { return m_enabled; } QQuickItem *FormLayoutAttached::buddyFor() const { return m_buddyFor; } void FormLayoutAttached::setBuddyFor(QQuickItem *buddyfor) { if (m_buddyFor == buddyfor || !m_buddyFor->isAncestorOf(buddyfor)) { return; } m_buddyFor = buddyfor; emit buddyForChanged(); } FormLayoutAttached *FormLayoutAttached::qmlAttachedProperties(QObject *object) { return new FormLayoutAttached(object); } #include "moc_formlayoutattached.cpp" diff --git a/src/formlayoutattached.h b/src/formlayoutattached.h index 8061d242..fa221c6f 100644 --- a/src/formlayoutattached.h +++ b/src/formlayoutattached.h @@ -1,169 +1,156 @@ /* -* Copyright (C) 2017 by Marco Martin -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU Library General Public License as -* published by the Free Software Foundation; either version 2, or -* (at your option) any later version. -* -* This program 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 Library General Public License for more details -* -* You should have received a copy of the GNU Library General Public -* License along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ + * SPDX-FileCopyrightText: 2017 Marco Martin + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ #ifndef FORMLAYOUTATTACHED_H #define FORMLAYOUTATTACHED_H #include #include class QQuickItem; /** * This attached property contains the information for decorating a FormLayout: * It contains the text labels of fields and information about sections. * @code * import org.kde.kirigami 2.3 as Kirigami * Kirigami.FormLayout { * TextField { * Kirigami.FormData.label: "Label:" * } * TextField { * Kirigami.FormData.label: "Label:" * } * } * @endcode * @since 2.3 */ class FormLayoutAttached : public QObject { Q_OBJECT /** * The label for a form layout field */ Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY labelChanged) /** * If true the FormLayout item is a section separator, a section separator * may have different looks: * * To make it just a space between two fields, just put an empty item with isSection: * @code * TextField { * Kirigami.FormData.label: "Label:" * } * Item { * Kirigami.FormData.isSection: true * } * TextField { * Kirigami.FormData.label: "Label:" * } * @endcode * * * To make it a space with a section title: * @code * TextField { * Kirigami.FormData.label: "Label:" * } * Item { * Kirigami.FormData.label: "Section Title" * Kirigami.FormData.isSection: true * } * TextField { * Kirigami.FormData.label: "Label:" * } * @endcode * * * To make it a space with a section title and a separator line: * @code * TextField { * Kirigami.FormData.label: "Label:" * } * Kirigami.Separator { * Kirigami.FormData.label: "Section Title" * Kirigami.FormData.isSection: true * } * TextField { * Kirigami.FormData.label: "Label:" * } * @endcode */ Q_PROPERTY(bool isSection READ isSection WRITE setIsSection NOTIFY isSectionChanged) /** * If true a checkbox is prepended to the FormLayout item. */ Q_PROPERTY(bool checkable READ checkable WRITE setCheckable NOTIFY checkableChanged) - + /** * This property is true when the checkbox of the FormLayout item is checked, @see checkable. */ Q_PROPERTY(bool checked READ checked WRITE setChecked NOTIFY checkedChanged) - + /** * This property holds whether the label and the checkbox of the FormLayout item receive mouse and keyboard events. */ Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) /** * The Item the label will be considered a "Buddy" for, * which will be the parent item the attached property is in. * A buddy item is useful for instance when the label has a keyboard accelerator, * which on triggered will be given active keyboard focus to. */ Q_PROPERTY(QQuickItem *buddyFor READ buddyFor WRITE setBuddyFor NOTIFY buddyForChanged) public: explicit FormLayoutAttached(QObject *parent = nullptr); ~FormLayoutAttached() override; void setLabel(const QString &text); QString label() const; QString decoratedLabel() const; void setIsSection(bool section); bool isSection() const; - + void setCheckable(bool checkable); bool checkable() const; - + void setChecked(bool checked); bool checked() const; - + void setEnabled(bool enabled); bool enabled() const; QQuickItem *buddyFor() const; void setBuddyFor(QQuickItem *buddyfor); //QML attached property static FormLayoutAttached *qmlAttachedProperties(QObject *object); Q_SIGNALS: void labelChanged(); void isSectionChanged(); void checkableChanged(); void checkedChanged(); void enabledChanged(); void buddyForChanged(); private: QString m_label; QString m_actualDecoratedLabel; QString m_decoratedLabel; QPointer m_buddyFor; bool m_isSection = false; bool m_checkable = false; bool m_checked = false; bool m_enabled = true; }; QML_DECLARE_TYPEINFO(FormLayoutAttached, QML_HAS_ATTACHED_PROPERTIES) #endif // FORMLAYOUTATTACHED_H diff --git a/src/icon.cpp b/src/icon.cpp index d26c472a..784ee40f 100644 --- a/src/icon.cpp +++ b/src/icon.cpp @@ -1,606 +1,593 @@ /* - * Copyright 2011 Marco Martin - * Copyright 2014 Aleix Pol Gonzalez + * SPDX-FileCopyrightText: 2011 Marco Martin + * SPDX-FileCopyrightText: 2014 Aleix Pol Gonzalez * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ #include "icon.h" #include "libkirigami/platformtheme.h" #include #include #include #include #include #include #include #include #include #include #include #include #include class ManagedTextureNode : public QSGSimpleTextureNode { Q_DISABLE_COPY(ManagedTextureNode) public: ManagedTextureNode(); void setTexture(QSharedPointer texture); private: QSharedPointer m_texture; }; ManagedTextureNode::ManagedTextureNode() {} void ManagedTextureNode::setTexture(QSharedPointer texture) { m_texture = texture; QSGSimpleTextureNode::setTexture(texture.data()); } typedef QHash > > TexturesCache; struct ImageTexturesCachePrivate { TexturesCache cache; }; class ImageTexturesCache { public: ImageTexturesCache(); ~ImageTexturesCache(); /** * @returns the texture for a given @p window and @p image. * * If an @p image id is the same as one already provided before, we won't create * a new texture and return a shared pointer to the existing texture. */ QSharedPointer loadTexture(QQuickWindow *window, const QImage &image, QQuickWindow::CreateTextureOptions options); QSharedPointer loadTexture(QQuickWindow *window, const QImage &image); private: QScopedPointer d; }; ImageTexturesCache::ImageTexturesCache() : d(new ImageTexturesCachePrivate) { } ImageTexturesCache::~ImageTexturesCache() { } QSharedPointer ImageTexturesCache::loadTexture(QQuickWindow *window, const QImage &image, QQuickWindow::CreateTextureOptions options) { qint64 id = image.cacheKey(); QSharedPointer texture = d->cache.value(id).value(window).toStrongRef(); if (!texture) { auto cleanAndDelete = [this, window, id](QSGTexture* texture) { QHash >& textures = (d->cache)[id]; textures.remove(window); if (textures.isEmpty()) d->cache.remove(id); delete texture; }; texture = QSharedPointer(window->createTextureFromImage(image, options), cleanAndDelete); (d->cache)[id][window] = texture.toWeakRef(); } //if we have a cache in an atlas but our request cannot use an atlassed texture //create a new texture and use that //don't use removedFromAtlas() as that requires keeping a reference to the non atlased version if (!(options & QQuickWindow::TextureCanUseAtlas) && texture->isAtlasTexture()) { texture = QSharedPointer(window->createTextureFromImage(image, options)); } return texture; } QSharedPointer ImageTexturesCache::loadTexture(QQuickWindow *window, const QImage &image) { return loadTexture(window, image, {}); } Q_GLOBAL_STATIC(ImageTexturesCache, s_iconImageCache) Icon::Icon(QQuickItem *parent) : QQuickItem(parent), m_smooth(false), m_changed(false), m_active(false), m_selected(false), m_isMask(false) { setFlag(ItemHasContents, true); //FIXME: not necessary anymore connect(qApp, &QGuiApplication::paletteChanged, this, &QQuickItem::polish); connect(this, &QQuickItem::enabledChanged, this, &QQuickItem::polish); } Icon::~Icon() { } void Icon::setSource(const QVariant &icon) { if (m_source == icon) { return; } m_source = icon; m_monochromeHeuristics.clear(); if (!m_theme) { m_theme = static_cast(qmlAttachedPropertiesObject(this, true)); Q_ASSERT(m_theme); connect(m_theme, &Kirigami::PlatformTheme::colorsChanged, this, &QQuickItem::polish); } if (icon.type() == QVariant::String) { const QString iconSource = icon.toString(); m_isMaskHeuristic = (iconSource.endsWith(QLatin1String("-symbolic")) || iconSource.endsWith(QLatin1String("-symbolic-rtl")) || iconSource.endsWith(QLatin1String("-symbolic-ltr"))); emit isMaskChanged(); } if (m_networkReply) { //if there was a network query going on, interrupt it m_networkReply->close(); } m_loadedImage = QImage(); polish(); emit sourceChanged(); emit validChanged(); } QVariant Icon::source() const { return m_source; } void Icon::setActive(const bool active) { if (active == m_active) { return; } m_active = active; polish(); emit activeChanged(); } bool Icon::active() const { return m_active; } bool Icon::valid() const { return !m_source.isNull(); } void Icon::setSelected(const bool selected) { if (selected == m_selected) { return; } m_selected = selected; polish(); emit selectedChanged(); } bool Icon::selected() const { return m_selected; } void Icon::setIsMask(bool mask) { if (m_isMask == mask) { return; } m_isMask = mask; m_isMaskHeuristic = mask; polish(); emit isMaskChanged(); } bool Icon::isMask() const { return m_isMask || m_isMaskHeuristic; } void Icon::setColor(const QColor &color) { if (m_color == color) { return; } m_color = color; polish(); emit colorChanged(); } QColor Icon::color() const { return m_color; } int Icon::implicitWidth() const { return 32; } int Icon::implicitHeight() const { return 32; } void Icon::setSmooth(const bool smooth) { if (smooth == m_smooth) { return; } m_smooth = smooth; polish(); emit smoothChanged(); } bool Icon::smooth() const { return m_smooth; } QSGNode* Icon::updatePaintNode(QSGNode* node, QQuickItem::UpdatePaintNodeData* /*data*/) { if (m_source.isNull() || qFuzzyIsNull(width()) || qFuzzyIsNull(height())) { delete node; return Q_NULLPTR; } if (m_changed || node == nullptr) { const QSize itemSize(width(), height()); QRect nodeRect(QPoint(0,0), itemSize); ManagedTextureNode* mNode = dynamic_cast(node); if (!mNode) { delete node; mNode = new ManagedTextureNode; } if (itemSize.width() != 0 && itemSize.height() != 0) { const auto multiplier = QCoreApplication::instance()->testAttribute(Qt::AA_UseHighDpiPixmaps) ? 1 : (window() ? window()->devicePixelRatio() : qGuiApp->devicePixelRatio()); const QSize size = itemSize * multiplier; mNode->setTexture(s_iconImageCache->loadTexture(window(), m_icon)); if (m_icon.size() != size) { // At this point, the image will already be scaled, but we need to output it in // the correct aspect ratio, painted centered in the viewport. So: QRect destination(QPoint(0, 0), m_icon.size().scaled(itemSize, Qt::KeepAspectRatio)); destination.moveCenter(nodeRect.center()); nodeRect = destination; } } mNode->setRect(nodeRect); node = mNode; if (m_smooth) { mNode->setFiltering(QSGTexture::Linear); } m_changed = false; } return node; } void Icon::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { QQuickItem::geometryChanged(newGeometry, oldGeometry); if (newGeometry.size() != oldGeometry.size()) { polish(); } } void Icon::handleRedirect(QNetworkReply* reply) { QNetworkAccessManager* qnam = reply->manager(); #if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) if (reply->error() != QNetworkReply::NoError) { #else if (reply->networkError() != QNetworkReply::NoError) { #endif return; } const QUrl possibleRedirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); if (!possibleRedirectUrl.isEmpty()) { const QUrl redirectUrl = reply->url().resolved(possibleRedirectUrl); if (redirectUrl == reply->url()) { // no infinite redirections thank you very much reply->deleteLater(); return; } reply->deleteLater(); QNetworkRequest request(possibleRedirectUrl); request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); m_networkReply = qnam->get(request); connect(m_networkReply.data(), &QNetworkReply::finished, this, [this](){handleFinished(m_networkReply); }); } } void Icon::handleFinished(QNetworkReply* reply) { reply->deleteLater(); if (!reply->attribute(QNetworkRequest::RedirectionTargetAttribute).isNull()) { handleRedirect(reply); return; } m_loadedImage = QImage(); const QString filename = reply->url().fileName(); if (!m_loadedImage.load(reply, filename.mid(filename.indexOf(QLatin1Char('.'))).toLatin1().constData())) { qWarning() << "received broken image" << reply->url(); // broken image from data, inform the user of this with some useful broken-image thing... const QSize size = QSize(width(), height()) * (window() ? window()->devicePixelRatio() : qApp->devicePixelRatio()); m_loadedImage = QIcon::fromTheme(m_fallback).pixmap(size, iconMode(), QIcon::On).toImage(); } polish(); } void Icon::updatePolish() { QQuickItem::updatePolish(); if (m_source.isNull()) { return; } const QSize itemSize(width(), height()); if (itemSize.width() != 0 && itemSize.height() != 0) { const auto multiplier = QCoreApplication::instance()->testAttribute(Qt::AA_UseHighDpiPixmaps) ? 1 : (window() ? window()->devicePixelRatio() : qGuiApp->devicePixelRatio()); const QSize size = itemSize * multiplier; switch(m_source.type()){ case QVariant::Pixmap: m_icon = m_source.value().toImage(); break; case QVariant::Image: m_icon = m_source.value(); break; case QVariant::Bitmap: m_icon = m_source.value().toImage(); break; case QVariant::Icon: m_icon = m_source.value().pixmap(size, iconMode(), QIcon::On).toImage(); break; case QVariant::Url: case QVariant::String: m_icon = findIcon(size); break; case QVariant::Brush: //todo: fill here too? case QVariant::Color: m_icon = QImage(size, QImage::Format_Alpha8); m_icon.fill(m_source.value()); break; default: break; } if (m_icon.isNull()){ m_icon = QImage(size, QImage::Format_Alpha8); m_icon.fill(Qt::transparent); } const QColor tintColor = !m_color.isValid() || m_color == Qt::transparent ? (m_selected ? m_theme->highlightedTextColor() : m_theme->textColor()) : m_color; //TODO: initialize m_isMask with icon.isMask() if (tintColor.alpha() > 0 && (isMask() || guessMonochrome(m_icon))) { QPainter p(&m_icon); p.setCompositionMode(QPainter::CompositionMode_SourceIn); p.fillRect(m_icon.rect(), tintColor); p.end(); } } m_changed = true; update(); } QImage Icon::findIcon(const QSize &size) { QImage img; QString iconSource = m_source.toString(); if (iconSource.startsWith(QLatin1String("image://"))) { const auto multiplier = QCoreApplication::instance()->testAttribute(Qt::AA_UseHighDpiPixmaps) ? (window() ? window()->devicePixelRatio() : qGuiApp->devicePixelRatio()) : 1; QUrl iconUrl(iconSource); QString iconProviderId = iconUrl.host(); QString iconId = iconUrl.path(); // QRC paths are not correctly handled by .path() if (iconId.size() >=2 && iconId.startsWith(QLatin1String("/:"))) { iconId.remove(0, 1); } QSize actualSize; QQuickImageProvider* imageProvider = dynamic_cast( qmlEngine(this)->imageProvider(iconProviderId)); if (!imageProvider) return img; switch(imageProvider->imageType()){ case QQmlImageProviderBase::Image: img = imageProvider->requestImage(iconId, &actualSize, size * multiplier); break; case QQmlImageProviderBase::Pixmap: img = imageProvider->requestPixmap(iconId, &actualSize, size * multiplier).toImage(); break; case QQmlImageProviderBase::Texture: case QQmlImageProviderBase::Invalid: case QQmlImageProviderBase::ImageResponse: //will have to investigate this more break; } } else if(iconSource.startsWith(QLatin1String("http://")) || iconSource.startsWith(QLatin1String("https://"))) { if(!m_loadedImage.isNull()) { return m_loadedImage.scaled(size, Qt::KeepAspectRatio, m_smooth ? Qt::SmoothTransformation : Qt::FastTransformation ); } const auto url = m_source.toUrl(); QQmlEngine* engine = qmlEngine(this); QNetworkAccessManager* qnam; if (engine && (qnam = engine->networkAccessManager()) && (!m_networkReply || m_networkReply->url() != url)) { QNetworkRequest request(url); request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); m_networkReply = qnam->get(request); connect(m_networkReply.data(), &QNetworkReply::finished, this, [this](){ handleFinished(m_networkReply); }); } // Temporary icon while we wait for the real image to load... img = QIcon::fromTheme(QStringLiteral("image-x-icon")).pixmap(size, iconMode(), QIcon::On).toImage(); } else { if (iconSource.startsWith(QLatin1String("qrc:/"))) { iconSource = iconSource.mid(3); } else if (iconSource.startsWith(QLatin1String("file:/"))) { iconSource = QUrl(iconSource).path(); } QIcon icon; const bool isPath = iconSource.contains(QLatin1String("/")); if (isPath) { icon = QIcon(iconSource); } else { if (icon.isNull()) { icon = m_theme->iconFromTheme(iconSource, m_color); } } if (!icon.isNull()) { img = icon.pixmap(size, iconMode(), QIcon::On).toImage(); /*const QColor tintColor = !m_color.isValid() || m_color == Qt::transparent ? (m_selected ? m_theme->highlightedTextColor() : m_theme->textColor()) : m_color; if (m_isMask || icon.isMask() || iconSource.endsWith(QLatin1String("-symbolic")) || iconSource.endsWith(QLatin1String("-symbolic-rtl")) || iconSource.endsWith(QLatin1String("-symbolic-ltr")) || guessMonochrome(img)) { QPainter p(&img); p.setCompositionMode(QPainter::CompositionMode_SourceIn); p.fillRect(img.rect(), tintColor); p.end(); }*/ } } if (!iconSource.isEmpty() && img.isNull()) { img = QIcon::fromTheme(m_fallback).pixmap(size, iconMode(), QIcon::On).toImage(); } return img; } QIcon::Mode Icon::iconMode() const { if (!isEnabled()) { return QIcon::Disabled; } else if (m_selected) { return QIcon::Selected; } else if (m_active) { return QIcon::Active; } return QIcon::Normal; } bool Icon::guessMonochrome(const QImage &img) { //don't try for too big images if (img.width() >= 256 || m_theme->supportsIconColoring()) { return false; } // round size to a standard size. hardcode as we can't use KIconLoader int stdSize; if (img.width() <= 16) { stdSize = 16; } else if (img.width() <= 22) { stdSize = 22; } else if (img.width() <= 24) { stdSize = 24; } else if (img.width() <= 32) { stdSize = 32; } else if (img.width() <= 48) { stdSize = 48; } else if (img.width() <= 64) { stdSize = 64; } else { stdSize = 128; } auto findIt = m_monochromeHeuristics.constFind(stdSize); if (findIt != m_monochromeHeuristics.constEnd()) { return findIt.value(); } QHash dist; int transparentPixels = 0; int saturatedPixels = 0; for(int x=0; x < img.width(); x++) { for(int y=0; y < img.height(); y++) { QColor color = QColor::fromRgba(qUnpremultiply(img.pixel(x, y))); if (color.alpha() < 100) { ++transparentPixels; continue; } else if (color.saturation() > 84) { ++saturatedPixels; } dist[qGray(color.rgb())]++; } } QMultiMap reverseDist; auto it = dist.constBegin(); qreal entropy = 0; while (it != dist.constEnd()) { reverseDist.insert(it.value(), it.key()); qreal probability = qreal(it.value()) / qreal(img.size().width() * img.size().height() - transparentPixels); entropy -= probability * log(probability) / log(255); ++it; } // Arbitrarly low values of entropy and colored pixels m_monochromeHeuristics[stdSize] = saturatedPixels <= (img.size().width()*img.size().height() - transparentPixels) * 0.3 && entropy <= 0.3; return m_monochromeHeuristics[stdSize]; } QString Icon::fallback() const { return m_fallback; } void Icon::setFallback(const QString& fallback) { if (m_fallback != fallback) { m_fallback = fallback; Q_EMIT fallbackChanged(fallback); } } #include "moc_icon.cpp" diff --git a/src/icon.h b/src/icon.h index a1e474c8..d43c326c 100644 --- a/src/icon.h +++ b/src/icon.h @@ -1,119 +1,106 @@ /* - * Copyright 2011 Marco Martin - * Copyright 2014 Aleix Pol Gonzalez + * SPDX-FileCopyrightText: 2011 Marco Martin + * SPDX-FileCopyrightText: 2014 Aleix Pol Gonzalez * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ #pragma once #include #include #include #include class QNetworkAccessManager; class QNetworkReply; namespace Kirigami { class PlatformTheme; } class Icon : public QQuickItem { Q_OBJECT Q_PROPERTY(QVariant source READ source WRITE setSource NOTIFY sourceChanged) Q_PROPERTY(bool smooth READ smooth WRITE setSmooth NOTIFY smoothChanged) Q_PROPERTY(int implicitWidth READ implicitWidth CONSTANT) Q_PROPERTY(int implicitHeight READ implicitHeight CONSTANT) Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged) Q_PROPERTY(bool valid READ valid NOTIFY validChanged) Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectedChanged) Q_PROPERTY(bool isMask READ isMask WRITE setIsMask NOTIFY isMaskChanged) Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) Q_PROPERTY(QString fallback READ fallback WRITE setFallback NOTIFY fallbackChanged) public: Icon(QQuickItem *parent = nullptr); ~Icon(); void setSource(const QVariant &source); QVariant source() const; int implicitWidth() const; int implicitHeight() const; void setSmooth(const bool smooth); bool smooth() const; void setActive(bool active = true); bool active() const; bool valid() const; void setSelected(bool selected = true); bool selected() const; void setIsMask(bool mask); bool isMask() const; void setColor(const QColor &color); QColor color() const; QString fallback() const; void setFallback(const QString &fallback); QSGNode* updatePaintNode(QSGNode* node, UpdatePaintNodeData* data) override; Q_SIGNALS: void sourceChanged(); void smoothChanged(); void enabledChanged(); void activeChanged(); void validChanged(); void selectedChanged(); void isMaskChanged(); void colorChanged(); void fallbackChanged(const QString &fallback); protected: void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; QImage findIcon(const QSize& size); void handleFinished(QNetworkReply* reply); void handleRedirect(QNetworkReply* reply); QIcon::Mode iconMode() const; bool guessMonochrome(const QImage &img); void updatePolish() override; private: Kirigami::PlatformTheme *m_theme = nullptr; QPointer m_networkReply; QHash m_monochromeHeuristics; QVariant m_source; bool m_smooth; bool m_changed; bool m_active; bool m_selected; bool m_isMask; bool m_isMaskHeuristic = false; QImage m_loadedImage; QColor m_color = Qt::transparent; QString m_fallback = QStringLiteral("unknown"); QImage m_icon; }; diff --git a/src/kirigamiplugin.cpp b/src/kirigamiplugin.cpp index 6657b3c3..a418ffee 100644 --- a/src/kirigamiplugin.cpp +++ b/src/kirigamiplugin.cpp @@ -1,260 +1,247 @@ /* - * Copyright 2009 by Alan Alpert - * Copyright 2010 by Ménard Alexis - * Copyright 2010 by Marco Martin - - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. + * SPDX-FileCopyrightText: 2009 Alan Alpert + * SPDX-FileCopyrightText: 2010 Ménard Alexis + * SPDX-FileCopyrightText: 2010 Marco Martin * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ #include "kirigamiplugin.h" #include "columnview.h" #include "enums.h" #include "icon.h" #include "settings.h" #include "formlayoutattached.h" #include "mnemonicattached.h" #include "delegaterecycler.h" #include "pagepool.h" #include "scenepositionattached.h" #include "wheelhandler.h" #include #include #include #include #include #include #include "libkirigami/platformtheme.h" static QString s_selectedStyle; //Q_INIT_RESOURCE(kirigami); #ifdef KIRIGAMI_BUILD_TYPE_STATIC #include #endif class CopyHelperPrivate : public QObject { Q_OBJECT public: Q_INVOKABLE static void copyTextToClipboard(const QString& text) { qGuiApp->clipboard()->setText(text); } }; // we can't do this in the plugin object directly, as that can live in a different thread // and event filters are only allowed in the same thread as the filtered object class LanguageChangeEventFilter : public QObject { Q_OBJECT public: bool eventFilter(QObject *receiver, QEvent *event) override { if (event->type() == QEvent::LanguageChange && receiver == QCoreApplication::instance()) { emit languageChangeEvent(); } return QObject::eventFilter(receiver, event); } Q_SIGNALS: void languageChangeEvent(); }; KirigamiPlugin::KirigamiPlugin(QObject *parent) : QQmlExtensionPlugin(parent) { auto filter = new LanguageChangeEventFilter; filter->moveToThread(QCoreApplication::instance()->thread()); QCoreApplication::instance()->installEventFilter(filter); connect(filter, &LanguageChangeEventFilter::languageChangeEvent, this, &KirigamiPlugin::languageChangeEvent); } QUrl KirigamiPlugin::componentUrl(const QString &fileName) const { for (const QString &style : qAsConst(m_stylesFallbackChain)) { const QString candidate = QStringLiteral("styles/") + style + QLatin1Char('/') + fileName; if (QFile::exists(resolveFilePath(candidate))) { #ifdef KIRIGAMI_BUILD_TYPE_STATIC return QUrl(QStringLiteral("qrc:/org/kde/kirigami/styles/") + style + QLatin1Char('/') + fileName); #else return QUrl(resolveFileUrl(candidate)); #endif } } #ifdef KIRIGAMI_BUILD_TYPE_STATIC return QUrl(QStringLiteral("qrc:/org/kde/kirigami/") + fileName); #else return QUrl(resolveFileUrl(fileName)); #endif } void KirigamiPlugin::registerTypes(const char *uri) { #if defined(Q_OS_ANDROID) && QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) QResource::registerResource(QStringLiteral("assets:/android_rcc_bundle.rcc")); #endif Q_ASSERT(QLatin1String(uri) == QLatin1String("org.kde.kirigami")); const QString style = QQuickStyle::name(); if (QIcon::themeName().isEmpty() && !qEnvironmentVariableIsSet("XDG_CURRENT_DESKTOP")) { QIcon::setThemeSearchPaths({resolveFilePath(QStringLiteral(".")), QStringLiteral(":/icons")}); QIcon::setThemeName(QStringLiteral("breeze-internal")); } #if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) //org.kde.desktop.plasma is a couple of files that fall back to desktop by purpose if ((style.isEmpty() || style == QStringLiteral("org.kde.desktop.plasma")) && QFile::exists(resolveFilePath(QStringLiteral("/styles/org.kde.desktop")))) { m_stylesFallbackChain.prepend(QStringLiteral("org.kde.desktop")); } #elif defined(Q_OS_ANDROID) if (!m_stylesFallbackChain.contains(QLatin1String("Material"))) { m_stylesFallbackChain.prepend(QStringLiteral("Material")); } #else // do we have an iOS specific style? if (!m_stylesFallbackChain.contains(QLatin1String("Material"))) { m_stylesFallbackChain.prepend(QStringLiteral("Material")); } #endif if (!style.isEmpty() && QFile::exists(resolveFilePath(QStringLiteral("/styles/") + style)) && !m_stylesFallbackChain.contains(style)) { m_stylesFallbackChain.prepend(style); //if we have plasma deps installed, use them for extra integration if (style == QStringLiteral("org.kde.desktop") && QFile::exists(resolveFilePath(QStringLiteral("/styles/org.kde.desktop.plasma")))) { m_stylesFallbackChain.prepend(QStringLiteral("org.kde.desktop.plasma")); } } else { #if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) m_stylesFallbackChain.prepend(QStringLiteral("org.kde.desktop")); #endif } //At this point the fallback chain will be selected->org.kde.desktop->Fallback s_selectedStyle = m_stylesFallbackChain.first(); qmlRegisterSingletonType(uri, 2, 0, "Settings", [](QQmlEngine *e, QJSEngine*) -> QObject* { Settings *settings = Settings::self(); //singleton managed internally, qml should never delete it e->setObjectOwnership(settings, QQmlEngine::CppOwnership); settings->setStyle(s_selectedStyle); return settings; } ); qmlRegisterUncreatableType(uri, 2, 0, "ApplicationHeaderStyle", QStringLiteral("Cannot create objects of type ApplicationHeaderStyle")); //old legacy retrocompatible Theme qmlRegisterSingletonType(componentUrl(QStringLiteral("Theme.qml")), uri, 2, 0, "Theme"); qmlRegisterSingletonType(componentUrl(QStringLiteral("Units.qml")), uri, 2, 0, "Units"); qmlRegisterType(componentUrl(QStringLiteral("Action.qml")), uri, 2, 0, "Action"); qmlRegisterType(componentUrl(QStringLiteral("AbstractApplicationHeader.qml")), uri, 2, 0, "AbstractApplicationHeader"); qmlRegisterType(componentUrl(QStringLiteral("AbstractApplicationWindow.qml")), uri, 2, 0, "AbstractApplicationWindow"); qmlRegisterType(componentUrl(QStringLiteral("AbstractListItem.qml")), uri, 2, 0, "AbstractListItem"); qmlRegisterType(componentUrl(QStringLiteral("ApplicationHeader.qml")), uri, 2, 0, "ApplicationHeader"); qmlRegisterType(componentUrl(QStringLiteral("ToolBarApplicationHeader.qml")), uri, 2, 0, "ToolBarApplicationHeader"); qmlRegisterType(componentUrl(QStringLiteral("ApplicationWindow.qml")), uri, 2, 0, "ApplicationWindow"); qmlRegisterType(componentUrl(QStringLiteral("BasicListItem.qml")), uri, 2, 0, "BasicListItem"); qmlRegisterType(componentUrl(QStringLiteral("OverlayDrawer.qml")), uri, 2, 0, "OverlayDrawer"); qmlRegisterType(componentUrl(QStringLiteral("ContextDrawer.qml")), uri, 2, 0, "ContextDrawer"); qmlRegisterType(componentUrl(QStringLiteral("GlobalDrawer.qml")), uri, 2, 0, "GlobalDrawer"); qmlRegisterType(componentUrl(QStringLiteral("Heading.qml")), uri, 2, 0, "Heading"); qmlRegisterType(componentUrl(QStringLiteral("Separator.qml")), uri, 2, 0, "Separator"); qmlRegisterType(componentUrl(QStringLiteral("PageRow.qml")), uri, 2, 0, "PageRow"); qmlRegisterType(uri, 2, 0, "Icon"); qmlRegisterType(componentUrl(QStringLiteral("Label.qml")), uri, 2, 0, "Label"); //TODO: uncomment for 2.3 release //qmlRegisterTypeNotAvailable(uri, 2, 3, "Label", "Label type not supported anymore, use QtQuick.Controls.Label 2.0 instead"); qmlRegisterType(componentUrl(QStringLiteral("OverlaySheet.qml")), uri, 2, 0, "OverlaySheet"); qmlRegisterType(componentUrl(QStringLiteral("Page.qml")), uri, 2, 0, "Page"); qmlRegisterType(componentUrl(QStringLiteral("ScrollablePage.qml")), uri, 2, 0, "ScrollablePage"); qmlRegisterType(componentUrl(QStringLiteral("SplitDrawer.qml")), uri, 2, 0, "SplitDrawer"); qmlRegisterType(componentUrl(QStringLiteral("SwipeListItem.qml")), uri, 2, 0, "SwipeListItem"); //2.1 qmlRegisterType(componentUrl(QStringLiteral("AbstractItemViewHeader.qml")), uri, 2, 1, "AbstractItemViewHeader"); qmlRegisterType(componentUrl(QStringLiteral("ItemViewHeader.qml")), uri, 2, 1, "ItemViewHeader"); qmlRegisterType(componentUrl(QStringLiteral("AbstractApplicationItem.qml")), uri, 2, 1, "AbstractApplicationItem"); qmlRegisterType(componentUrl(QStringLiteral("ApplicationItem.qml")), uri, 2, 1, "ApplicationItem"); //2.2 //Theme changed from a singleton to an attached property qmlRegisterUncreatableType(uri, 2, 2, "Theme", QStringLiteral("Cannot create objects of type Theme, use it as an attached property")); //2.3 qmlRegisterType(componentUrl(QStringLiteral("FormLayout.qml")), uri, 2, 3, "FormLayout"); qmlRegisterUncreatableType(uri, 2, 3, "FormData", QStringLiteral("Cannot create objects of type FormData, use it as an attached property")); qmlRegisterUncreatableType(uri, 2, 3, "MnemonicData", QStringLiteral("Cannot create objects of type MnemonicData, use it as an attached property")); //2.4 qmlRegisterType(componentUrl(QStringLiteral("AbstractCard.qml")), uri, 2, 4, "AbstractCard"); qmlRegisterType(componentUrl(QStringLiteral("Card.qml")), uri, 2, 4, "Card"); qmlRegisterType(componentUrl(QStringLiteral("CardsListView.qml")), uri, 2, 4, "CardsListView"); qmlRegisterType(componentUrl(QStringLiteral("CardsGridView.qml")), uri, 2, 4, "CardsGridView"); qmlRegisterType(componentUrl(QStringLiteral("CardsLayout.qml")), uri, 2, 4, "CardsLayout"); qmlRegisterType(componentUrl(QStringLiteral("InlineMessage.qml")), uri, 2, 4, "InlineMessage"); qmlRegisterUncreatableType(uri, 2, 4, "MessageType", QStringLiteral("Cannot create objects of type MessageType")); qmlRegisterType(uri, 2, 4, "DelegateRecycler"); //2.5 qmlRegisterType(componentUrl(QStringLiteral("ListItemDragHandle.qml")), uri, 2, 5, "ListItemDragHandle"); qmlRegisterType(componentUrl(QStringLiteral("ActionToolBar.qml")), uri, 2, 5, "ActionToolBar"); qmlRegisterUncreatableType(uri, 2, 5, "ScenePosition", QStringLiteral("Cannot create objects of type ScenePosition, use it as an attached property")); //2.6 qmlRegisterType(componentUrl(QStringLiteral("AboutPage.qml")), uri, 2, 6, "AboutPage"); qmlRegisterType(componentUrl(QStringLiteral("LinkButton.qml")), uri, 2, 6, "LinkButton"); qmlRegisterType(componentUrl(QStringLiteral("UrlButton.qml")), uri, 2, 6, "UrlButton"); qmlRegisterSingletonType("org.kde.kirigami.private", 2, 6, "CopyHelperPrivate", [] (QQmlEngine*, QJSEngine*) -> QObject* { return new CopyHelperPrivate; }); //2.7 qmlRegisterType(uri, 2, 7, "ColumnView"); qmlRegisterType(componentUrl(QStringLiteral("ActionTextField.qml")), uri, 2, 7, "ActionTextField"); //2.8 qmlRegisterType(componentUrl(QStringLiteral("SearchField.qml")), uri, 2, 8, "SearchField"); qmlRegisterType(componentUrl(QStringLiteral("PasswordField.qml")), uri, 2, 8, "PasswordField"); //2.9 qmlRegisterType(uri, 2, 9, "WheelHandler"); qmlRegisterUncreatableType(uri, 2, 9, "WheelEvent", QStringLiteral("Cannot create objects of type WheelEvent.")); //2.10 qmlRegisterType(componentUrl(QStringLiteral("ListSectionHeader.qml")), uri, 2, 10, "ListSectionHeader"); // 2.11 qmlRegisterType(uri, 2, 11, "PagePool"); qmlRegisterType(componentUrl(QStringLiteral("PagePoolAction.qml")), uri, 2, 11, "PagePoolAction"); //TODO: remove qmlRegisterType(componentUrl(QStringLiteral("SwipeListItem2.qml")), uri, 2, 11, "SwipeListItem2"); qmlProtectModule(uri, 2); } void KirigamiPlugin::initializeEngine(QQmlEngine *engine, const char *uri) { Q_UNUSED(uri); connect(this, &KirigamiPlugin::languageChangeEvent, engine, &QQmlEngine::retranslate); } #include "kirigamiplugin.moc" diff --git a/src/kirigamiplugin.h b/src/kirigamiplugin.h index 69b35b22..aa20ba03 100644 --- a/src/kirigamiplugin.h +++ b/src/kirigamiplugin.h @@ -1,82 +1,69 @@ /* - * Copyright 2009 by Alan Alpert - * Copyright 2010 by Ménard Alexis - * Copyright 2010 by Marco Martin - - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details + * SPDX-FileCopyrightText: 2009 Alan Alpert + * SPDX-FileCopyrightText: 2010 Ménard Alexis + * SPDX-FileCopyrightText: 2010 Marco Martin * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef KIRIGAMIPLUGIN_H #define KIRIGAMIPLUGIN_H #include #include #include class KirigamiPlugin : public QQmlExtensionPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface") public: KirigamiPlugin(QObject *parent = nullptr); void registerTypes(const char *uri) override; void initializeEngine(QQmlEngine *engine, const char *uri) override; #ifdef KIRIGAMI_BUILD_TYPE_STATIC static KirigamiPlugin& getInstance() { static KirigamiPlugin instance; return instance; } static void registerTypes() { static KirigamiPlugin instance; instance.registerTypes("org.kde.kirigami"); } #endif Q_SIGNALS: void languageChangeEvent(); private: QUrl componentUrl(const QString &fileName) const; QString resolveFilePath(const QString &path) const { #if defined(Q_OS_ANDROID) && QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) return QStringLiteral(":/android_rcc_bundle/qml/org/kde/kirigami.2/") + path; #elif defined(KIRIGAMI_BUILD_TYPE_STATIC) return QStringLiteral(":/org/kde/kirigami/") + path; #else return baseUrl().toLocalFile() + QLatin1Char('/') + path; #endif } QString resolveFileUrl(const QString &filePath) const { #if defined(Q_OS_ANDROID) && QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) return QStringLiteral("qrc:/android_rcc_bundle/qml/org/kde/kirigami.2/") + filePath; #elif defined(KIRIGAMI_BUILD_TYPE_STATIC) return filePath; #else return baseUrl().toString() + QLatin1Char('/') + filePath; #endif } QStringList m_stylesFallbackChain; }; #endif diff --git a/src/libkirigami/basictheme.cpp b/src/libkirigami/basictheme.cpp index 34d0d6b1..cf8a33b0 100644 --- a/src/libkirigami/basictheme.cpp +++ b/src/libkirigami/basictheme.cpp @@ -1,348 +1,335 @@ /* -* Copyright (C) 2017 by Marco Martin -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU Library General Public License as -* published by the Free Software Foundation; either version 2, or -* (at your option) any later version. -* -* This program 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 Library General Public License for more details -* -* You should have received a copy of the GNU Library General Public -* License along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ + * SPDX-FileCopyrightText: 2017 by Marco Martin + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ #include "basictheme_p.h" #include #include #include #include #include #include #include namespace Kirigami { class BasicThemeDeclarativeSingleton { public: BasicThemeDeclarativeSingleton() {} BasicThemeDeclarative self; }; Q_GLOBAL_STATIC(BasicThemeDeclarativeSingleton, privateBasicThemeDeclarativeSelf) BasicThemeDeclarative::BasicThemeDeclarative() { m_colorSyncTimer = new QTimer; m_colorSyncTimer->setInterval(0); m_colorSyncTimer->setSingleShot(true); } BasicThemeDeclarative::~BasicThemeDeclarative() { delete m_colorSyncTimer; } QObject *BasicThemeDeclarative::instance(const BasicTheme *theme) { if (m_declarativeBasicTheme) { return m_declarativeBasicTheme; } QQmlEngine *engine = qmlEngine(theme->parent()); Q_ASSERT(engine); QQmlComponent c(engine); //NOTE: for now is important this import stays at 2.0 c.setData("import QtQuick 2.6\n\ import org.kde.kirigami 2.0 as Kirigami\n\ QtObject {\n\ property QtObject theme: Kirigami.Theme\n\ }", QUrl(QStringLiteral("basictheme.cpp"))); QObject *obj = c.create(); m_declarativeBasicTheme = obj->property("theme").value(); return m_declarativeBasicTheme; } BasicTheme::BasicTheme(QObject *parent) : PlatformTheme(parent) { //TODO: correct? connect(qApp, &QGuiApplication::fontDatabaseChanged, this, [this]() {setDefaultFont(qApp->font());}); //connect all the declarative object signals to the timer start to compress, use the old syntax as they are all signals defined in QML connect(basicThemeDeclarative()->instance(this), SIGNAL(textColorChanged()), basicThemeDeclarative()->m_colorSyncTimer, SLOT(start())); connect(basicThemeDeclarative()->instance(this), SIGNAL(disabledTextColorChanged()), basicThemeDeclarative()->m_colorSyncTimer, SLOT(start())); connect(basicThemeDeclarative()->instance(this), SIGNAL(highlightColorChanged()), basicThemeDeclarative()->m_colorSyncTimer, SLOT(start())); connect(basicThemeDeclarative()->instance(this), SIGNAL(highlightedTextColorChanged()), basicThemeDeclarative()->m_colorSyncTimer, SLOT(start())); connect(basicThemeDeclarative()->instance(this), SIGNAL(backgroundColorChanged()), basicThemeDeclarative()->m_colorSyncTimer, SLOT(start())); connect(basicThemeDeclarative()->instance(this), SIGNAL(alternateBackgroundColorChanged()), basicThemeDeclarative()->m_colorSyncTimer, SLOT(start())); connect(basicThemeDeclarative()->instance(this), SIGNAL(linkColorChanged()), basicThemeDeclarative()->m_colorSyncTimer, SLOT(start())); connect(basicThemeDeclarative()->instance(this), SIGNAL(visitedLinkColorChanged()), basicThemeDeclarative()->m_colorSyncTimer, SLOT(start())); connect(basicThemeDeclarative()->instance(this), SIGNAL(buttonTextColorChanged()), basicThemeDeclarative()->m_colorSyncTimer, SLOT(start())); connect(basicThemeDeclarative()->instance(this), SIGNAL(buttonBackgroundColorChanged()), basicThemeDeclarative()->m_colorSyncTimer, SLOT(start())); connect(basicThemeDeclarative()->instance(this), SIGNAL(buttonAlternateBackgroundColorChanged()), basicThemeDeclarative()->m_colorSyncTimer, SLOT(start())); connect(basicThemeDeclarative()->instance(this), SIGNAL(buttonHoverColorChanged()), basicThemeDeclarative()->m_colorSyncTimer, SLOT(start())); connect(basicThemeDeclarative()->instance(this), SIGNAL(buttonFocusColorChanged()), basicThemeDeclarative()->m_colorSyncTimer, SLOT(start())); connect(basicThemeDeclarative()->instance(this), SIGNAL(viewTextColorChanged()), basicThemeDeclarative()->m_colorSyncTimer, SLOT(start())); - connect(basicThemeDeclarative()->instance(this), SIGNAL(viewBackgroundColorChanged()), + connect(basicThemeDeclarative()->instance(this), SIGNAL(viewBackgroundColorChanged()), basicThemeDeclarative()->m_colorSyncTimer, SLOT(start())); - connect(basicThemeDeclarative()->instance(this), SIGNAL(viewAlternateBackgroundColorChanged()), + connect(basicThemeDeclarative()->instance(this), SIGNAL(viewAlternateBackgroundColorChanged()), basicThemeDeclarative()->m_colorSyncTimer, SLOT(start())); connect(basicThemeDeclarative()->instance(this), SIGNAL(viewHoverColorChanged()), basicThemeDeclarative()->m_colorSyncTimer, SLOT(start())); connect(basicThemeDeclarative()->instance(this), SIGNAL(viewFocusColorChanged()), basicThemeDeclarative()->m_colorSyncTimer, SLOT(start())); connect(basicThemeDeclarative()->instance(this), SIGNAL(complementaryTextColorChanged()), basicThemeDeclarative()->m_colorSyncTimer, SLOT(start())); connect(basicThemeDeclarative()->instance(this), SIGNAL(complementaryBackgroundColorChanged()), basicThemeDeclarative()->m_colorSyncTimer, SLOT(start())); connect(basicThemeDeclarative()->instance(this), SIGNAL(complementaryAlternateBackgroundColorChanged()), basicThemeDeclarative()->m_colorSyncTimer, SLOT(start())); connect(basicThemeDeclarative()->instance(this), SIGNAL(complementaryHoverColorChanged()), basicThemeDeclarative()->m_colorSyncTimer, SLOT(start())); connect(basicThemeDeclarative()->instance(this), SIGNAL(complementaryFocusColorChanged()), basicThemeDeclarative()->m_colorSyncTimer, SLOT(start())); //finally connect the timer to the sync connect(basicThemeDeclarative()->m_colorSyncTimer, &QTimer::timeout, this, &BasicTheme::syncColors); connect(this, &BasicTheme::colorSetChanged, this, &BasicTheme::syncColors); connect(this, &BasicTheme::colorGroupChanged, this, &BasicTheme::syncColors); connect(this, &PlatformTheme::colorSetChanged, this, &BasicTheme::syncCustomColorsToQML); connect(this, &PlatformTheme::colorsChanged, this, &BasicTheme::syncCustomColorsToQML); syncColors(); } BasicTheme::~BasicTheme() { } static inline QColor colorGroupTint(const QColor &color, PlatformTheme::ColorGroup group) { switch (group) { case PlatformTheme::Inactive: return QColor::fromHsvF(color.hueF(), color.saturationF() * 0.5, color.valueF()); case PlatformTheme::Disabled: return QColor::fromHsvF(color.hueF(), color.saturationF() * 0.5, color.valueF()*0.8); default: return color; } } //TODO: tint for which we need to chain to m_parentBasicTheme's color #define RESOLVECOLOR(colorName, upperCaseColor) \ QColor color;\ switch (colorSet()) {\ case BasicTheme::Button:\ color = basicThemeDeclarative()->instance(this)->property("button"#upperCaseColor).value();\ break;\ case BasicTheme::View:\ color = basicThemeDeclarative()->instance(this)->property("view"#upperCaseColor).value();\ break;\ case BasicTheme::Selection:\ color = basicThemeDeclarative()->instance(this)->property("selection"#upperCaseColor).value();\ break;\ case BasicTheme::Tooltip:\ color = basicThemeDeclarative()->instance(this)->property("tooltip"#upperCaseColor).value();\ break;\ case BasicTheme::Complementary:\ color = basicThemeDeclarative()->instance(this)->property("complementary"#upperCaseColor).value();\ break;\ case BasicTheme::Window:\ default:\ color = basicThemeDeclarative()->instance(this)->property(#colorName).value();\ }\ color = colorGroupTint(color, colorGroup()); #define PROXYCOLOR(colorName, upperCaseColor) \ colorGroupTint(basicThemeDeclarative()->instance(this)->property(#colorName).value(), colorGroup()) void BasicTheme::syncColors() { { RESOLVECOLOR(textColor, TextColor); setTextColor(color); }{ setDisabledTextColor(PROXYCOLOR(disabledTextColor, DisabledTextColor)); }{ RESOLVECOLOR(backgroundColor, BackgroundColor) setBackgroundColor(color); }{ RESOLVECOLOR(alternateBackgroundColor, AlternateBackgroundColor) setAlternateBackgroundColor(color); }{ setHighlightColor(PROXYCOLOR(highlightColor, HighlightColor)); }{ setHighlightedTextColor(PROXYCOLOR(highlightedTextColor, HighlightedTextColor)); }{ setActiveTextColor(PROXYCOLOR(activeTextColor, ActiveTextColor)); }{ setActiveBackgroundColor(PROXYCOLOR(activeBackgroundColor, ActiveBackgroundColor)); }{ setLinkColor(PROXYCOLOR(linkColor, LinkColor)); }{ setLinkBackgroundColor(PROXYCOLOR(linkBackgroundColor, LinkBackgroundColor)); }{ setVisitedLinkColor(PROXYCOLOR(visitedLinkColor, VisitedLinkColor)); }{ setVisitedLinkBackgroundColor(PROXYCOLOR(visitedLinkBackgroundColor, VisitedLinkBackgroundColor)); }{ setNegativeTextColor(PROXYCOLOR(negativeTextColor, NegativeTextColor)); }{ setNegativeBackgroundColor(PROXYCOLOR(negativeBackgroundColor, NegativeBackgroundColor)); }{ setNeutralTextColor(PROXYCOLOR(neutralTextColor, NeutralTextColor)); }{ setNeutralBackgroundColor(PROXYCOLOR(neutralBackgroundColor, NeutralBackgroundColor)); }{ setPositiveTextColor(PROXYCOLOR(positiveTextColor, PositiveTextColor)); }{ setPositiveBackgroundColor(PROXYCOLOR(positiveBackgroundColor, PositiveBackgroundColor)); }{ RESOLVECOLOR(hoverColor, HoverColor); setHoverColor(color); }{ RESOLVECOLOR(focusColor, FocusColor); setFocusColor(color); } //legacy { m_buttonTextColor = PROXYCOLOR(buttonTextColor, ButtonTextColor); m_buttonBackgroundColor = PROXYCOLOR(buttonBackgroundColor, ButtonBackgroundColor); m_buttonHoverColor = PROXYCOLOR(buttonHoverColor, ButtonHoverColor); m_buttonFocusColor = PROXYCOLOR(buttonFocusColor, ButtonFocusColor); m_viewTextColor = PROXYCOLOR(viewTextColor, ViewTextColor); m_viewBackgroundColor = PROXYCOLOR(viewBackgroundColor, ViewBackgroundColor); m_viewHoverColor = PROXYCOLOR(viewHoverColor, ViewHoverColor); m_viewFocusColor = PROXYCOLOR(viewFocusColor, ViewFocusColor); } QPalette pal = qApp->palette(); pal.setColor(QPalette::WindowText, textColor()); pal.setColor(QPalette::Button, m_buttonBackgroundColor); pal.setColor(QPalette::Light, m_buttonBackgroundColor.lighter(120)); pal.setColor(QPalette::Dark, m_buttonBackgroundColor.darker(120)); pal.setColor(QPalette::Mid, m_buttonBackgroundColor.darker(110)); pal.setColor(QPalette::Base, m_viewBackgroundColor); pal.setColor(QPalette::HighlightedText, highlightedTextColor()); pal.setColor(QPalette::Text, m_viewTextColor); pal.setColor(QPalette::Window, backgroundColor()); setPalette(pal); if (this->parent()) { //this will work on Qt 5.10+ but is a safe noop on older releases this->parent()->setProperty("palette", QVariant::fromValue(pal)); if (basicThemeDeclarative()->instance(this)) { QMetaObject::invokeMethod(basicThemeDeclarative()->instance(this), "__propagateColorSet", Q_ARG(QVariant, QVariant::fromValue(this->parent())), Q_ARG(QVariant, colorSet())); } } emit colorsChanged(); } void BasicTheme::syncCustomColorsToQML() { if (basicThemeDeclarative()->instance(this)) { QMetaObject::invokeMethod(basicThemeDeclarative()->instance(this), "__propagateTextColor", Q_ARG(QVariant, QVariant::fromValue(this->parent())), Q_ARG(QVariant, textColor())); QMetaObject::invokeMethod(basicThemeDeclarative()->instance(this), "__propagateBackgroundColor", Q_ARG(QVariant, QVariant::fromValue(this->parent())), Q_ARG(QVariant, backgroundColor())); QMetaObject::invokeMethod(basicThemeDeclarative()->instance(this), "__propagatePrimaryColor", Q_ARG(QVariant, QVariant::fromValue(this->parent())), Q_ARG(QVariant, highlightColor())); QMetaObject::invokeMethod(basicThemeDeclarative()->instance(this), "__propagateAccentColor", Q_ARG(QVariant, QVariant::fromValue(this->parent())), Q_ARG(QVariant, highlightColor())); } } QColor BasicTheme::buttonTextColor() const { qWarning()<<"WARNING: buttonTextColor is deprecated, use textColor with colorSet: Theme.Button instead"; return m_buttonTextColor; } QColor BasicTheme::buttonBackgroundColor() const { qWarning()<<"WARNING: buttonBackgroundColor is deprecated, use backgroundColor with colorSet: Theme.Button instead"; return m_buttonBackgroundColor; } QColor BasicTheme::buttonHoverColor() const { qWarning()<<"WARNING: buttonHoverColor is deprecated, use backgroundColor with colorSet: Theme.Button instead"; return m_buttonHoverColor; } QColor BasicTheme::buttonFocusColor() const { qWarning()<<"WARNING: buttonFocusColor is deprecated, use backgroundColor with colorSet: Theme.Button instead"; return m_buttonFocusColor; } QColor BasicTheme::viewTextColor() const { qWarning()<<"WARNING: viewTextColor is deprecated, use backgroundColor with colorSet: Theme.View instead"; return m_viewTextColor; } QColor BasicTheme::viewBackgroundColor() const { qWarning()<<"WARNING: viewBackgroundColor is deprecated, use backgroundColor with colorSet: Theme.View instead"; return m_viewBackgroundColor; } QColor BasicTheme::viewHoverColor() const { qWarning()<<"WARNING: viewHoverColor is deprecated, use backgroundColor with colorSet: Theme.View instead"; return m_viewHoverColor; } QColor BasicTheme::viewFocusColor() const { qWarning()<<"WARNING: viewFocusColor is deprecated, use backgroundColor with colorSet: Theme.View instead"; return m_viewFocusColor; } BasicThemeDeclarative *BasicTheme::basicThemeDeclarative() { return &privateBasicThemeDeclarativeSelf->self; } } #include "moc_basictheme_p.cpp" diff --git a/src/libkirigami/basictheme_p.h b/src/libkirigami/basictheme_p.h index c565cb8b..eed8f73b 100644 --- a/src/libkirigami/basictheme_p.h +++ b/src/libkirigami/basictheme_p.h @@ -1,100 +1,87 @@ /* -* Copyright (C) 2017 by Marco Martin -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU Library General Public License as -* published by the Free Software Foundation; either version 2, or -* (at your option) any later version. -* -* This program 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 Library General Public License for more details -* -* You should have received a copy of the GNU Library General Public -* License along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ + * SPDX-FileCopyrightText: 2017 by Marco Martin + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ #ifndef BASICTHEME_H #define BASICTHEME_H #include "platformtheme.h" #include #include #include #include namespace Kirigami { class BasicTheme; class BasicThemeDeclarative { public: BasicThemeDeclarative(); virtual ~BasicThemeDeclarative(); QObject *instance(const BasicTheme *theme); QTimer *m_colorSyncTimer; private: QUrl m_qmlPath; //The instance can die when and if the engine dies QPointer m_declarativeBasicTheme; }; class BasicTheme : public PlatformTheme { Q_OBJECT // colors Q_PROPERTY(QColor buttonTextColor READ buttonTextColor NOTIFY colorsChanged) Q_PROPERTY(QColor buttonBackgroundColor READ buttonBackgroundColor NOTIFY colorsChanged) Q_PROPERTY(QColor buttonHoverColor READ buttonHoverColor NOTIFY colorsChanged) Q_PROPERTY(QColor buttonFocusColor READ buttonFocusColor NOTIFY colorsChanged) Q_PROPERTY(QColor viewTextColor READ viewTextColor NOTIFY colorsChanged) Q_PROPERTY(QColor viewBackgroundColor READ viewBackgroundColor NOTIFY colorsChanged) Q_PROPERTY(QColor viewHoverColor READ viewHoverColor NOTIFY colorsChanged) Q_PROPERTY(QColor viewFocusColor READ viewFocusColor NOTIFY colorsChanged) public: explicit BasicTheme(QObject *parent = nullptr); ~BasicTheme(); void syncColors(); QColor buttonTextColor() const; QColor buttonBackgroundColor() const; QColor buttonHoverColor() const; QColor buttonFocusColor() const; QColor viewTextColor() const; QColor viewBackgroundColor() const; QColor viewHoverColor() const; QColor viewFocusColor() const; static BasicThemeDeclarative *basicThemeDeclarative(); Q_SIGNALS: void colorsChanged(); private: inline void syncCustomColorsToQML(); //legacy colors QColor m_buttonTextColor; QColor m_buttonBackgroundColor; QColor m_buttonHoverColor; QColor m_buttonFocusColor; QColor m_viewTextColor; QColor m_viewBackgroundColor; QColor m_viewHoverColor; QColor m_viewFocusColor; }; } #endif // BASICTHEME_H diff --git a/src/libkirigami/kirigamipluginfactory.cpp b/src/libkirigami/kirigamipluginfactory.cpp index ae492760..26c0eefa 100644 --- a/src/libkirigami/kirigamipluginfactory.cpp +++ b/src/libkirigami/kirigamipluginfactory.cpp @@ -1,39 +1,26 @@ /* -* Copyright (C) 2017 by Marco Martin -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU Library General Public License as -* published by the Free Software Foundation; either version 2, or -* (at your option) any later version. -* -* This program 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 Library General Public License for more details -* -* You should have received a copy of the GNU Library General Public -* License along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ + * SPDX-FileCopyrightText: 2017 by Marco Martin + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ #include "kirigamipluginfactory.h" #include "platformtheme.h" #include namespace Kirigami { KirigamiPluginFactory::KirigamiPluginFactory(QObject *parent) : QObject(parent) { } KirigamiPluginFactory::~KirigamiPluginFactory() { } } #include "moc_kirigamipluginfactory.cpp" diff --git a/src/libkirigami/kirigamipluginfactory.h b/src/libkirigami/kirigamipluginfactory.h index 93754800..9bc0e2dd 100644 --- a/src/libkirigami/kirigamipluginfactory.h +++ b/src/libkirigami/kirigamipluginfactory.h @@ -1,65 +1,52 @@ /* -* Copyright (C) 2017 by Marco Martin -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU Library General Public License as -* published by the Free Software Foundation; either version 2, or -* (at your option) any later version. -* -* This program 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 Library General Public License for more details -* -* You should have received a copy of the GNU Library General Public -* License along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ + * SPDX-FileCopyrightText: 2017 by Marco Martin + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ #ifndef KIRIGAMIPLUGINFACTORY_H #define KIRIGAMIPLUGINFACTORY_H #include "platformtheme.h" #include #ifndef KIRIGAMI_BUILD_TYPE_STATIC #include #endif namespace Kirigami { /** * @class KirigamiPluginFactory kirigamipluginfactory.h KirigamiPluginFactory * * This class is reimpleented by plugins to provide different implementations * of PlatformTheme */ #ifdef KIRIGAMI_BUILD_TYPE_STATIC class KirigamiPluginFactory : public QObject #else class KIRIGAMI2_EXPORT KirigamiPluginFactory : public QObject #endif { Q_OBJECT public: explicit KirigamiPluginFactory(QObject *parent = nullptr); ~KirigamiPluginFactory(); /** * Creates an instance of PlatformTheme which can come out from * an implementation provided by a plugin * @param parent the parent object of the created PlatformTheme */ virtual PlatformTheme *createPlatformTheme(QObject *parent) = 0; }; } QT_BEGIN_NAMESPACE #define KirigamiPluginFactory_iid "org.kde.kirigami.KirigamiPluginFactory" Q_DECLARE_INTERFACE(Kirigami::KirigamiPluginFactory, KirigamiPluginFactory_iid) QT_END_NAMESPACE #endif //KIRIGAMIPLUGINFACTORY_H diff --git a/src/libkirigami/platformtheme.cpp b/src/libkirigami/platformtheme.cpp index 3c50327a..c167f719 100644 --- a/src/libkirigami/platformtheme.cpp +++ b/src/libkirigami/platformtheme.cpp @@ -1,938 +1,925 @@ /* -* Copyright (C) 2017 by Marco Martin -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU Library General Public License as -* published by the Free Software Foundation; either version 2, or -* (at your option) any later version. -* -* This program 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 Library General Public License for more details -* -* You should have received a copy of the GNU Library General Public -* License along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ + * SPDX-FileCopyrightText: 2017 by Marco Martin + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ #include "platformtheme.h" #include "kirigamipluginfactory.h" #include "basictheme_p.h" #include #include #include #include #include #include #include #include #include namespace Kirigami { class PlatformThemePrivate { public: PlatformThemePrivate(PlatformTheme *q); ~PlatformThemePrivate(); inline void syncCustomPalette(); void emitCompressedColorChanged(); void findParentStyle(); static QColor tint(const QColor &c1, const QColor &c2, qreal ratio); PlatformTheme *q; PlatformTheme::ColorSet m_colorSet = PlatformTheme::Window; PlatformTheme::ColorGroup m_colorGroup = PlatformTheme::Active; QSet m_childThemes; QPointer m_parentTheme; //ordinary colors QColor textColor; QColor disabledTextColor; QColor highlightedTextColor; QColor activeTextColor; QColor linkColor; QColor visitedLinkColor; QColor negativeTextColor; QColor neutralTextColor; QColor positiveTextColor; QColor backgroundColor; QColor alternateBackgroundColor; QColor highlightColor; QColor activeBackgroundColor; QColor linkBackgroundColor; QColor visitedLinkBackgroundColor; QColor negativeBackgroundColor; QColor neutralBackgroundColor; QColor positiveBackgroundColor; QColor focusColor; QColor hoverColor; QPalette palette; //custom colors QColor customTextColor; QColor customDisabledTextColor; QColor customHighlightedTextColor; QColor customActiveTextColor; QColor customLinkColor; QColor customVisitedLinkColor; QColor customNegativeTextColor; QColor customNeutralTextColor; QColor customPositiveTextColor; QColor customBackgroundColor; QColor customAlternateBackgroundColor; QColor customHighlightColor; QColor customActiveBackgroundColor; QColor customLinkBackgroundColor; QColor customVisitedLinkBackgroundColor; QColor customNegativeBackgroundColor; QColor customNeutralBackgroundColor; QColor customPositiveBackgroundColor; QColor customFocusColor; QColor customHoverColor; QPalette customPalette; QFont font; bool m_inherit = true; bool m_init = true; bool m_supportsIconColoring = false; bool m_pendingColorChange = false; static KirigamiPluginFactory *s_pluginFactory; }; KirigamiPluginFactory *PlatformThemePrivate::s_pluginFactory = nullptr; PlatformThemePrivate::PlatformThemePrivate(PlatformTheme *q) : q(q) { } PlatformThemePrivate::~PlatformThemePrivate() {} void setPaletteColor(QPalette& customPalette, QPalette::ColorGroup cg, QPalette::ColorRole cr, const QColor &color) { if (customPalette.color(cg, cr) != color) { customPalette.setColor(cg, cr, color); } } void PlatformThemePrivate::syncCustomPalette() { for (auto state : { QPalette::Active, QPalette::Inactive, QPalette::Disabled }) { setPaletteColor(customPalette, state, QPalette::WindowText, q->textColor()); setPaletteColor(customPalette, state, QPalette::Window, q->backgroundColor()); setPaletteColor(customPalette, state, QPalette::Base, q->backgroundColor()); setPaletteColor(customPalette, state, QPalette::Text, q->textColor()); setPaletteColor(customPalette, state, QPalette::Button, q->backgroundColor()); setPaletteColor(customPalette, state, QPalette::ButtonText, q->textColor()); setPaletteColor(customPalette, state, QPalette::Highlight, q->highlightColor()); setPaletteColor(customPalette, state, QPalette::HighlightedText, q->highlightedTextColor()); setPaletteColor(customPalette, state, QPalette::ToolTipBase, q->backgroundColor()); setPaletteColor(customPalette, state, QPalette::ToolTipText, q->textColor()); setPaletteColor(customPalette, state, QPalette::Link, q->linkColor()); setPaletteColor(customPalette, state, QPalette::LinkVisited, q->visitedLinkColor()); } emit q->paletteChanged(customPalette); } void PlatformThemePrivate::findParentStyle() { if (m_parentTheme) { m_parentTheme->d->m_childThemes.remove(q); } QQuickItem *candidate = qobject_cast(q->parent()); while (candidate) { candidate = candidate->parentItem(); PlatformTheme *t = static_cast(qmlAttachedPropertiesObject(candidate, false)); if (t) { t->d->m_childThemes.insert(q); m_parentTheme = t; if (m_inherit) { q->setColorSet(t->colorSet()); q->setCustomTextColor(t->d->customTextColor); q->setCustomDisabledTextColor(t->d->customDisabledTextColor); q->setCustomHighlightedTextColor(t->d->customHighlightedTextColor); q->setCustomActiveTextColor(t->d->customActiveTextColor); q->setCustomLinkColor(t->d->customLinkColor); q->setCustomVisitedLinkColor(t->d->customVisitedLinkColor); q->setCustomNegativeTextColor(t->d->customNegativeTextColor); q->setCustomNeutralTextColor(t->d->customNeutralTextColor); q->setCustomPositiveTextColor(t->d->customPositiveTextColor); q->setCustomBackgroundColor(t->d->customBackgroundColor); q->setCustomAlternateBackgroundColor(t->d->customAlternateBackgroundColor); q->setCustomHighlightColor(t->d->customHighlightColor); q->setCustomActiveBackgroundColor(t->d->customActiveBackgroundColor); q->setCustomLinkBackgroundColor(t->d->customLinkBackgroundColor); q->setCustomVisitedLinkBackgroundColor(t->d->customVisitedLinkBackgroundColor); q->setCustomNegativeBackgroundColor(t->d->customNegativeBackgroundColor); q->setCustomNeutralBackgroundColor(t->d->customNeutralBackgroundColor); q->setCustomPositiveBackgroundColor(t->d->customPositiveBackgroundColor); q->setCustomFocusColor(t->d->customFocusColor); q->setCustomHoverColor(t->d->customHoverColor); } break; } - + } } QColor PlatformThemePrivate::tint(const QColor &c1, const QColor &c2, qreal ratio) { qreal r = c1.redF() + (c2.redF() - c1.redF()) * ratio; qreal g = c1.greenF() + (c2.greenF() - c1.greenF()) * ratio; qreal b = c1.blueF() + (c2.blueF() - c1.blueF()) * ratio; - + return QColor::fromRgbF(r, g, b, 1); } void PlatformThemePrivate::emitCompressedColorChanged() { if (m_pendingColorChange) { return; } m_pendingColorChange = true; QMetaObject::invokeMethod(q, [this]() { syncCustomPalette(); emit q->colorsChanged(); m_pendingColorChange = false; }, Qt::QueuedConnection); } PlatformTheme::PlatformTheme(QObject *parent) : QObject(parent), d(new PlatformThemePrivate(this)) { d->findParentStyle(); if (QQuickItem *item = qobject_cast(parent)) { connect(item, &QQuickItem::windowChanged, this, [this]() { d->findParentStyle(); }); connect(item, &QQuickItem::parentChanged, this, [this]() { d->findParentStyle(); }); } d->m_init = false; //TODO: needs https://codereview.qt-project.org/#/c/206889/ for font changes } PlatformTheme::~PlatformTheme() { if (d->m_parentTheme) { d->m_parentTheme->d->m_childThemes.remove(this); } delete d; } void PlatformTheme::setColorSet(PlatformTheme::ColorSet colorSet) { if (d->m_colorSet == colorSet) { return; } d->m_colorSet = colorSet; for (PlatformTheme *t : qAsConst(d->m_childThemes)) { if (t->inherit()) { t->setColorSet(colorSet); } } if (!d->m_init) { emit colorSetChanged(colorSet); d->emitCompressedColorChanged(); } } PlatformTheme::ColorSet PlatformTheme::colorSet() const { return d->m_colorSet; } void PlatformTheme::setColorGroup(PlatformTheme::ColorGroup colorGroup) { if (d->m_colorGroup == colorGroup) { return; } d->m_colorGroup = colorGroup; for (PlatformTheme *t : qAsConst(d->m_childThemes)) { if (t->inherit()) { t->setColorGroup(colorGroup); } } if (!d->m_init) { emit colorGroupChanged(colorGroup); d->emitCompressedColorChanged(); } } PlatformTheme::ColorGroup PlatformTheme::colorGroup() const { return d->m_colorGroup; } bool PlatformTheme::inherit() const { return d->m_inherit; } void PlatformTheme::setInherit(bool inherit) { if (d->m_inherit == inherit) { return; } d->m_inherit = inherit; if (inherit && d->m_parentTheme) { setColorSet(d->m_parentTheme->colorSet()); } emit inheritChanged(inherit); } QColor PlatformTheme::textColor() const { return d->customTextColor.isValid() ? d->customTextColor : d->textColor; } QColor PlatformTheme::disabledTextColor() const { return d->customDisabledTextColor.isValid() ? d->customDisabledTextColor : d->disabledTextColor; } QColor PlatformTheme::highlightColor() const { return d->customHighlightColor.isValid() ? d->customHighlightColor : d->highlightColor; } QColor PlatformTheme::highlightedTextColor() const { return d->customHighlightedTextColor.isValid() ? d->customHighlightedTextColor : d->highlightedTextColor; } QColor PlatformTheme::backgroundColor() const { return d->customBackgroundColor.isValid() ? d->customBackgroundColor : d->backgroundColor; } QColor PlatformTheme::alternateBackgroundColor() const { return d->customAlternateBackgroundColor.isValid() ? d->customAlternateBackgroundColor : d->alternateBackgroundColor; } QColor PlatformTheme::activeTextColor() const { return d->customActiveTextColor.isValid() ? d->customActiveTextColor : d->activeTextColor; } QColor PlatformTheme::activeBackgroundColor() const { return d->customActiveBackgroundColor.isValid() ? d->customActiveBackgroundColor : d->activeBackgroundColor; } QColor PlatformTheme::linkColor() const { return d->customLinkColor.isValid() ? d->customLinkColor : d->linkColor; } QColor PlatformTheme::linkBackgroundColor() const { return d->customLinkBackgroundColor.isValid() ? d->customLinkBackgroundColor : d->linkBackgroundColor; } QColor PlatformTheme::visitedLinkColor() const { return d->customVisitedLinkColor.isValid() ? d->customVisitedLinkColor : d->visitedLinkColor; } QColor PlatformTheme::visitedLinkBackgroundColor() const { return d->customVisitedLinkBackgroundColor.isValid() ? d->customVisitedLinkBackgroundColor : d->visitedLinkBackgroundColor; } QColor PlatformTheme::negativeTextColor() const { return d->customNegativeTextColor.isValid() ? d->customNegativeTextColor : d->negativeTextColor; } QColor PlatformTheme::negativeBackgroundColor() const { return d->customNegativeBackgroundColor.isValid() ? d->customNegativeBackgroundColor : d->negativeBackgroundColor; } QColor PlatformTheme::neutralTextColor() const { return d->customNeutralTextColor.isValid() ? d->customNeutralTextColor : d->neutralTextColor; } QColor PlatformTheme::neutralBackgroundColor() const { return d->customNeutralBackgroundColor.isValid() ? d->customNeutralBackgroundColor : d->neutralBackgroundColor; } QColor PlatformTheme::positiveTextColor() const { return d->customPositiveTextColor.isValid() ? d->customPositiveTextColor : d->positiveTextColor; } QColor PlatformTheme::positiveBackgroundColor() const { return d->customPositiveBackgroundColor.isValid() ? d->customPositiveBackgroundColor : d->positiveBackgroundColor; } QColor PlatformTheme::focusColor() const { return d->customFocusColor.isValid() ? d->customFocusColor : d->focusColor; } QColor PlatformTheme::hoverColor() const { return d->customHoverColor.isValid() ? d->customHoverColor : d->hoverColor; } //setters for theme implementations void PlatformTheme::setTextColor(const QColor &color) { if (d->textColor == color) { return; } d->textColor = color; d->emitCompressedColorChanged(); } void PlatformTheme::setDisabledTextColor(const QColor &color) { if (d->disabledTextColor == color) { return; } d->disabledTextColor = color; d->emitCompressedColorChanged(); } void PlatformTheme::setBackgroundColor(const QColor &color) { if (d->backgroundColor == color) { return; } d->backgroundColor = color; d->emitCompressedColorChanged(); } void PlatformTheme::setAlternateBackgroundColor(const QColor &color) { if (d->alternateBackgroundColor == color) { return; } d->alternateBackgroundColor = color; d->emitCompressedColorChanged(); } void PlatformTheme::setHighlightColor(const QColor &color) { if (d->highlightColor == color) { return; } d->highlightColor = color; d->emitCompressedColorChanged(); } void PlatformTheme::setHighlightedTextColor(const QColor &color) { if (d->highlightedTextColor == color) { return; } d->highlightedTextColor = color; d->emitCompressedColorChanged(); } void PlatformTheme::setActiveTextColor(const QColor &color) { if (d->activeTextColor == color) { return; } d->activeTextColor = color; d->emitCompressedColorChanged(); } void PlatformTheme::setActiveBackgroundColor(const QColor &color) { if (d->activeBackgroundColor == color) { return; } d->activeBackgroundColor = color; d->emitCompressedColorChanged(); } void PlatformTheme::setLinkColor(const QColor &color) { if (d->linkColor == color) { return; } d->linkColor = color; d->emitCompressedColorChanged(); } void PlatformTheme::setLinkBackgroundColor(const QColor &color) { if (d->linkBackgroundColor == color) { return; } d->linkBackgroundColor = color; d->emitCompressedColorChanged(); } void PlatformTheme::setVisitedLinkColor(const QColor &color) { if (d->visitedLinkColor == color) { return; } d->visitedLinkColor = color; d->emitCompressedColorChanged(); } void PlatformTheme::setVisitedLinkBackgroundColor(const QColor &color) { if (d->visitedLinkBackgroundColor == color) { return; } d->visitedLinkBackgroundColor = color; d->emitCompressedColorChanged(); } void PlatformTheme::setNegativeTextColor(const QColor &color) { if (d->negativeTextColor == color) { return; } d->negativeTextColor = color; d->emitCompressedColorChanged(); } void PlatformTheme::setNegativeBackgroundColor(const QColor &color) { if (d->negativeBackgroundColor == color) { return; } d->negativeBackgroundColor = color; d->emitCompressedColorChanged(); } void PlatformTheme::setNeutralTextColor(const QColor &color) { if (d->neutralTextColor == color) { return; } d->neutralTextColor = color; d->emitCompressedColorChanged(); } void PlatformTheme::setNeutralBackgroundColor(const QColor &color) { if (d->neutralBackgroundColor == color) { return; } d->neutralBackgroundColor = color; d->emitCompressedColorChanged(); } void PlatformTheme::setPositiveTextColor(const QColor &color) { if (d->positiveTextColor == color) { return; } d->positiveTextColor = color; d->emitCompressedColorChanged(); } void PlatformTheme::setPositiveBackgroundColor(const QColor &color) { if (d->positiveBackgroundColor == color) { return; } d->positiveBackgroundColor = color; d->emitCompressedColorChanged(); } void PlatformTheme::setHoverColor(const QColor &color) { if (d->hoverColor == color) { return; } d->hoverColor = color; d->emitCompressedColorChanged(); } void PlatformTheme::setFocusColor(const QColor &color) { if (d->focusColor == color) { return; } d->focusColor = color; d->emitCompressedColorChanged(); } QFont PlatformTheme::defaultFont() const { return d->font; } void PlatformTheme::setDefaultFont(const QFont &font) { if (d->font == font) { return; } d->font = font; emit defaultFontChanged(font); } #define PROPAGATECUSTOMCOLOR(colorName, color)\ for (PlatformTheme *t : qAsConst(d->m_childThemes)) {\ if (t->inherit()) {\ t->set##colorName(color);\ }\ } //setters for QML clients void PlatformTheme::setCustomTextColor(const QColor &color) { if (d->customTextColor == color) { return; } d->customTextColor = color; PROPAGATECUSTOMCOLOR(CustomTextColor, color) d->emitCompressedColorChanged(); } void PlatformTheme::setCustomDisabledTextColor(const QColor &color) { if (d->customDisabledTextColor == color) { return; } d->customDisabledTextColor = color; PROPAGATECUSTOMCOLOR(CustomDisabledTextColor, color) d->emitCompressedColorChanged(); } void PlatformTheme::setCustomBackgroundColor(const QColor &color) { if (d->customBackgroundColor == color) { return; } d->customBackgroundColor = color; PROPAGATECUSTOMCOLOR(CustomBackgroundColor, color) d->emitCompressedColorChanged(); } void PlatformTheme::setCustomAlternateBackgroundColor(const QColor &color) { if (d->customAlternateBackgroundColor == color) { return; } d->customAlternateBackgroundColor = color; PROPAGATECUSTOMCOLOR(CustomAlternateBackgroundColor, color) d->emitCompressedColorChanged(); } void PlatformTheme::setCustomHighlightColor(const QColor &color) { if (d->customHighlightColor == color) { return; } d->customHighlightColor = color; PROPAGATECUSTOMCOLOR(CustomHighlightColor, color) d->emitCompressedColorChanged(); } void PlatformTheme::setCustomHighlightedTextColor(const QColor &color) { if (d->customHighlightedTextColor == color) { return; } d->customHighlightedTextColor = color; PROPAGATECUSTOMCOLOR(CustomHighlightedTextColor, color) d->emitCompressedColorChanged(); } void PlatformTheme::setCustomActiveTextColor(const QColor &color) { if (d->customActiveTextColor == color) { return; } d->customActiveTextColor = color; PROPAGATECUSTOMCOLOR(CustomActiveTextColor, color) d->emitCompressedColorChanged(); } void PlatformTheme::setCustomActiveBackgroundColor(const QColor &color) { if (d->customActiveBackgroundColor == color) { return; } d->customActiveBackgroundColor = color; PROPAGATECUSTOMCOLOR(CustomActiveBackgroundColor, color) d->emitCompressedColorChanged(); } void PlatformTheme::setCustomLinkColor(const QColor &color) { if (d->customLinkColor == color) { return; } d->customLinkColor = color; PROPAGATECUSTOMCOLOR(CustomLinkColor, color) d->emitCompressedColorChanged(); } void PlatformTheme::setCustomLinkBackgroundColor(const QColor &color) { if (d->customLinkBackgroundColor == color) { return; } d->customLinkBackgroundColor = color; PROPAGATECUSTOMCOLOR(CustomLinkBackgroundColor, color) d->emitCompressedColorChanged(); } void PlatformTheme::setCustomVisitedLinkColor(const QColor &color) { if (d->customVisitedLinkColor == color) { return; } d->customVisitedLinkColor = color; PROPAGATECUSTOMCOLOR(CustomVisitedLinkColor, color) d->emitCompressedColorChanged(); } void PlatformTheme::setCustomVisitedLinkBackgroundColor(const QColor &color) { if (d->customVisitedLinkBackgroundColor == color) { return; } d->customVisitedLinkBackgroundColor = color; PROPAGATECUSTOMCOLOR(CustomVisitedLinkBackgroundColor, color) d->emitCompressedColorChanged(); } void PlatformTheme::setCustomNegativeTextColor(const QColor &color) { if (d->customNegativeTextColor == color) { return; } d->customNegativeTextColor = color; PROPAGATECUSTOMCOLOR(CustomNegativeTextColor, color) d->emitCompressedColorChanged(); } void PlatformTheme::setCustomNegativeBackgroundColor(const QColor &color) { if (d->customNegativeBackgroundColor == color) { return; } d->customNegativeBackgroundColor = color; PROPAGATECUSTOMCOLOR(CustomNegativeBackgroundColor, color) d->emitCompressedColorChanged(); } void PlatformTheme::setCustomNeutralTextColor(const QColor &color) { if (d->customNeutralTextColor == color) { return; } d->customNeutralTextColor = color; PROPAGATECUSTOMCOLOR(CustomNeutralTextColor, color) d->emitCompressedColorChanged(); } void PlatformTheme::setCustomNeutralBackgroundColor(const QColor &color) { if (d->customNeutralBackgroundColor == color) { return; } d->customNeutralBackgroundColor = color; PROPAGATECUSTOMCOLOR(CustomNeutralBackgroundColor, color) d->emitCompressedColorChanged(); } void PlatformTheme::setCustomPositiveTextColor(const QColor &color) { if (d->customPositiveTextColor == color) { return; } d->customPositiveTextColor = color; PROPAGATECUSTOMCOLOR(CustomPositiveTextColor, color) d->emitCompressedColorChanged(); } void PlatformTheme::setCustomPositiveBackgroundColor(const QColor &color) { if (d->customPositiveBackgroundColor == color) { return; } d->customPositiveBackgroundColor = color; PROPAGATECUSTOMCOLOR(CustomPositiveBackgroundColor, color) d->emitCompressedColorChanged(); } void PlatformTheme::setCustomHoverColor(const QColor &color) { if (d->customHoverColor == color) { return; } d->customHoverColor = color; PROPAGATECUSTOMCOLOR(CustomHoverColor, color) d->emitCompressedColorChanged(); } void PlatformTheme::setCustomFocusColor(const QColor &color) { if (d->customFocusColor == color) { return; } d->customFocusColor = color; PROPAGATECUSTOMCOLOR(CustomFocusColor, color) d->emitCompressedColorChanged(); } QPalette PlatformTheme::palette() const { //check the most important custom colors to decide to return a custom palette return d->customTextColor.isValid() || d->customBackgroundColor.isValid() || d->customHighlightColor.isValid() ? d->customPalette : d->palette; } void PlatformTheme::setPalette(const QPalette &palette) { if (d->palette == palette) { return; } d->palette = palette; PROPAGATECUSTOMCOLOR(Palette, palette) emit paletteChanged(palette); } QIcon PlatformTheme::iconFromTheme(const QString &name, const QColor &customColor) { Q_UNUSED(customColor); QIcon icon = QIcon::fromTheme(name); return icon; } bool PlatformTheme::supportsIconColoring() const { return d->m_supportsIconColoring; } void PlatformTheme::setSupportsIconColoring(bool support) { d->m_supportsIconColoring = support; } PlatformTheme *PlatformTheme::qmlAttachedProperties(QObject *object) { static bool s_factoryChecked = false; //check for the plugin only once: it's an heavy operation if (PlatformThemePrivate::s_pluginFactory) { return PlatformThemePrivate::s_pluginFactory->createPlatformTheme(object); } else if (!s_factoryChecked) { s_factoryChecked = true; #ifdef KIRIGAMI_BUILD_TYPE_STATIC for (QObject* staticPlugin : QPluginLoader::staticInstances()) { KirigamiPluginFactory *factory = qobject_cast(staticPlugin); if (factory) { PlatformThemePrivate::s_pluginFactory = factory; return factory->createPlatformTheme(object); } } #else const auto libraryPaths = QCoreApplication::libraryPaths(); for (const QString &path : libraryPaths) { QDir dir(path + QStringLiteral("/kf5/kirigami")); const auto fileNames = dir.entryList(QDir::Files); for (const QString &fileName : fileNames) { //TODO: env variable? if (!QQuickStyle::name().isEmpty() && fileName.startsWith(QQuickStyle::name())) { QPluginLoader loader(dir.absoluteFilePath(fileName)); QObject *plugin = loader.instance(); //TODO: load actually a factory as plugin KirigamiPluginFactory *factory = qobject_cast(plugin); if (factory) { PlatformThemePrivate::s_pluginFactory = factory; return factory->createPlatformTheme(object); } } } } #endif } return new BasicTheme(object); } } #include "moc_platformtheme.cpp" diff --git a/src/libkirigami/platformtheme.h b/src/libkirigami/platformtheme.h index ce41f896..b9d6ef6b 100644 --- a/src/libkirigami/platformtheme.h +++ b/src/libkirigami/platformtheme.h @@ -1,332 +1,319 @@ /* -* Copyright (C) 2017 by Marco Martin -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU Library General Public License as -* published by the Free Software Foundation; either version 2, or -* (at your option) any later version. -* -* This program 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 Library General Public License for more details -* -* You should have received a copy of the GNU Library General Public -* License along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ + * SPDX-FileCopyrightText: 2017 by Marco Martin + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ #ifndef PLATFORMTHEME_H #define PLATFORMTHEME_H #include #include #include #include #include #ifndef KIRIGAMI_BUILD_TYPE_STATIC #include #endif namespace Kirigami { class PlatformThemePrivate; /** * @class PlatformTheme platformtheme.h PlatformTheme * * This class is the base for color management in Kirigami, * different platforms can reimplement this class to integrate with * system platform colors of a given platform */ #ifdef KIRIGAMI_BUILD_TYPE_STATIC class PlatformTheme : public QObject #else class KIRIGAMI2_EXPORT PlatformTheme : public QObject #endif { Q_OBJECT /** * This enumeration describes the color set for which a color is being selected. * * Color sets define a color "environment", suitable for drawing all parts of a * given region. Colors from different sets should not be combined. */ Q_PROPERTY(ColorSet colorSet READ colorSet WRITE setColorSet NOTIFY colorSetChanged) /** * This enumeration describes the color group used to generate the colors. * The enum value is based upon QPalette::CpolorGroup and has the same values. * It's redefined here in order to make it work with QML * @since 4.43 */ Q_PROPERTY(ColorGroup colorGroup READ colorGroup WRITE setColorGroup NOTIFY colorGroupChanged) /** * If true, the colorSet will be inherited from the colorset of a theme of one * of the ancestor items * default: true */ Q_PROPERTY(bool inherit READ inherit WRITE setInherit NOTIFY inheritChanged) // foreground colors /** * Color for normal foregrounds, usually text, but not limited to it, * anything that should be painted with a clear contrast should use this color */ Q_PROPERTY(QColor textColor READ textColor WRITE setCustomTextColor RESET setCustomTextColor NOTIFY colorsChanged) /** * Foreground color for disabled areas, usually a mid-gray */ Q_PROPERTY(QColor disabledTextColor READ disabledTextColor WRITE setCustomDisabledTextColor RESET setCustomDisabledTextColor NOTIFY colorsChanged) /** * Color for text that has been highlighted, often is a light color while normal text is dark */ Q_PROPERTY(QColor highlightedTextColor READ highlightedTextColor WRITE setCustomHighlightedTextColor RESET setCustomHighlightedTextColor NOTIFY colorsChanged) /** * Foreground for areas that are active or requesting attention */ Q_PROPERTY(QColor activeTextColor READ activeTextColor WRITE setCustomActiveTextColor RESET setCustomActiveTextColor NOTIFY colorsChanged) /** * Color for links */ Q_PROPERTY(QColor linkColor READ linkColor WRITE setCustomLinkColor RESET setCustomLinkColor NOTIFY colorsChanged) /** * Color for visited links, usually a bit darker than linkColor */ Q_PROPERTY(QColor visitedLinkColor READ visitedLinkColor WRITE setCustomVisitedLinkColor RESET setCustomVisitedLinkColor NOTIFY colorsChanged) /** * Foreground color for negative areas, such as critical error text */ Q_PROPERTY(QColor negativeTextColor READ negativeTextColor WRITE setCustomNegativeTextColor RESET setCustomNegativeTextColor NOTIFY colorsChanged) /** * Foreground color for neutral areas, such as warning texts (but not critical) */ Q_PROPERTY(QColor neutralTextColor READ neutralTextColor WRITE setCustomNeutralTextColor RESET setCustomNeutralTextColor NOTIFY colorsChanged) /** * Success messages, trusted content */ Q_PROPERTY(QColor positiveTextColor READ positiveTextColor WRITE setCustomPositiveTextColor RESET setCustomPositiveTextColor NOTIFY colorsChanged) //background colors /** * The generic background color */ Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setCustomBackgroundColor RESET setCustomBackgroundColor NOTIFY colorsChanged) /** * The generic background color * Alternate background; for example, for use in lists. * This color may be the same as BackgroundNormal, - * especially in sets other than View and Window. + * especially in sets other than View and Window. */ Q_PROPERTY(QColor alternateBackgroundColor READ alternateBackgroundColor WRITE setCustomAlternateBackgroundColor RESET setCustomAlternateBackgroundColor NOTIFY colorsChanged) /** * The background color for selected areas */ Q_PROPERTY(QColor highlightColor READ highlightColor WRITE setCustomHighlightColor RESET setCustomHighlightColor NOTIFY colorsChanged) /** * Background for areas that are active or requesting attention */ Q_PROPERTY(QColor activeBackgroundColor READ activeBackgroundColor WRITE setCustomActiveBackgroundColor RESET setCustomActiveBackgroundColor NOTIFY colorsChanged) /** * Background color for links */ Q_PROPERTY(QColor linkBackgroundColor READ linkBackgroundColor WRITE setCustomLinkBackgroundColor RESET setCustomLinkBackgroundColor NOTIFY colorsChanged) /** * Background color for visited links, usually a bit darker than linkBackgroundColor */ Q_PROPERTY(QColor visitedLinkBackgroundColor READ visitedLinkBackgroundColor WRITE setCustomVisitedLinkBackgroundColor RESET setCustomVisitedLinkBackgroundColor NOTIFY colorsChanged) /** * Background color for negative areas, such as critical errors and destructive actions */ Q_PROPERTY(QColor negativeBackgroundColor READ negativeBackgroundColor WRITE setCustomNegativeBackgroundColor RESET setCustomNegativeBackgroundColor NOTIFY colorsChanged) /** * Background color for neutral areas, such as warnings (but not critical) */ Q_PROPERTY(QColor neutralBackgroundColor READ neutralBackgroundColor WRITE setCustomNeutralBackgroundColor RESET setCustomNeutralBackgroundColor NOTIFY colorsChanged) /** * Background color for positive areas, such as success messages and trusted content */ Q_PROPERTY(QColor positiveBackgroundColor READ positiveBackgroundColor WRITE setCustomPositiveBackgroundColor RESET setCustomPositiveBackgroundColor NOTIFY colorsChanged) - + //decoration colors /** * A decoration color that indicates active focus */ Q_PROPERTY(QColor focusColor READ focusColor WRITE setCustomFocusColor RESET setCustomFocusColor NOTIFY colorsChanged) /** * A decoration color that indicates mouse hovering */ Q_PROPERTY(QColor hoverColor READ hoverColor WRITE setCustomHoverColor RESET setCustomHoverColor NOTIFY colorsChanged) // font and palette Q_PROPERTY(QFont defaultFont READ defaultFont NOTIFY defaultFontChanged) //Active palette Q_PROPERTY(QPalette palette READ palette NOTIFY paletteChanged) public: enum ColorSet { View = 0, /** Color set for item views, usually the lightest of all */ Window, /** Default Color set for windows and "chrome" areas */ Button, /** Color set used by buttons */ Selection, /** Color set used by selectged areas */ Tooltip, /** Color set used by tooltips */ Complementary /** Color set meant to be complementary to Window: usually is a dark theme for light themes */ }; Q_ENUM(ColorSet) enum ColorGroup { Disabled = QPalette::Disabled, Active = QPalette::Active, Inactive = QPalette::Inactive, Normal = QPalette::Normal }; Q_ENUM(ColorGroup) explicit PlatformTheme(QObject *parent = nullptr); ~PlatformTheme(); void setColorSet(PlatformTheme::ColorSet); PlatformTheme::ColorSet colorSet() const; void setColorGroup(PlatformTheme::ColorGroup); PlatformTheme::ColorGroup colorGroup() const; bool inherit() const; void setInherit(bool inherit); //foreground colors QColor textColor() const; QColor disabledTextColor() const; QColor highlightedTextColor() const; QColor activeTextColor() const; QColor linkColor() const; QColor visitedLinkColor() const; QColor negativeTextColor() const; QColor neutralTextColor() const; QColor positiveTextColor() const; //background colors QColor backgroundColor() const; QColor alternateBackgroundColor() const; QColor highlightColor() const; QColor activeBackgroundColor() const; QColor linkBackgroundColor() const; QColor visitedLinkBackgroundColor() const; QColor negativeBackgroundColor() const; QColor neutralBackgroundColor() const; QColor positiveBackgroundColor() const; //decoration colors QColor focusColor() const; QColor hoverColor() const; QFont defaultFont() const; //this may is used by the desktop QQC2 to set the styleoption palettes QPalette palette() const; //this will be used by desktopicon to fetch icons with KIconLoader virtual Q_INVOKABLE QIcon iconFromTheme(const QString &name, const QColor &customColor = Qt::transparent); bool supportsIconColoring() const; //foreground colors void setCustomTextColor(const QColor &color = QColor()); void setCustomDisabledTextColor(const QColor &color = QColor()); void setCustomHighlightedTextColor(const QColor &color = QColor()); void setCustomActiveTextColor(const QColor &color = QColor()); void setCustomLinkColor(const QColor &color = QColor()); void setCustomVisitedLinkColor(const QColor &color = QColor()); void setCustomNegativeTextColor(const QColor &color = QColor()); void setCustomNeutralTextColor(const QColor &color = QColor()); void setCustomPositiveTextColor(const QColor &color = QColor()); //background colors void setCustomBackgroundColor(const QColor &color = QColor()); void setCustomAlternateBackgroundColor(const QColor &color = QColor()); void setCustomHighlightColor(const QColor &color = QColor()); void setCustomActiveBackgroundColor(const QColor &color = QColor()); void setCustomLinkBackgroundColor(const QColor &color = QColor()); void setCustomVisitedLinkBackgroundColor(const QColor &color = QColor()); void setCustomNegativeBackgroundColor(const QColor &color = QColor()); void setCustomNeutralBackgroundColor(const QColor &color = QColor()); void setCustomPositiveBackgroundColor(const QColor &color = QColor()); //decoration colors void setCustomFocusColor(const QColor &color = QColor()); void setCustomHoverColor(const QColor &color = QColor()); //QML attached property static PlatformTheme *qmlAttachedProperties(QObject *object); Q_SIGNALS: //TODO: parameters to signals as this is also a c++ api void colorsChanged(); void defaultFontChanged(const QFont &font); void colorSetChanged(Kirigami::PlatformTheme::ColorSet colorSet); void colorGroupChanged(Kirigami::PlatformTheme::ColorGroup colorGroup); void paletteChanged(const QPalette &pal); void inheritChanged(bool inherit); protected: //Setters, not accessible from QML but from implementations void setSupportsIconColoring(bool support); //foreground colors void setTextColor(const QColor &color); void setDisabledTextColor(const QColor &color); void setHighlightedTextColor(const QColor &color); void setActiveTextColor(const QColor &color); void setLinkColor(const QColor &color); void setVisitedLinkColor(const QColor &color); void setNegativeTextColor(const QColor &color); void setNeutralTextColor(const QColor &color); void setPositiveTextColor(const QColor &color); //background colors void setBackgroundColor(const QColor &color); void setAlternateBackgroundColor(const QColor &color); void setHighlightColor(const QColor &color); void setActiveBackgroundColor(const QColor &color); void setLinkBackgroundColor(const QColor &color); void setVisitedLinkBackgroundColor(const QColor &color); void setNegativeBackgroundColor(const QColor &color); void setNeutralBackgroundColor(const QColor &color); void setPositiveBackgroundColor(const QColor &color); //decoration colors void setFocusColor(const QColor &color); void setHoverColor(const QColor &color); void setDefaultFont(const QFont &defaultFont); void setPalette(const QPalette &palette); private: PlatformThemePrivate *d; friend class PlatformThemePrivate; }; } QML_DECLARE_TYPEINFO(Kirigami::PlatformTheme, QML_HAS_ATTACHED_PROPERTIES) #endif // PLATFORMTHEME_H diff --git a/src/libkirigami/tabletmodewatcher.cpp b/src/libkirigami/tabletmodewatcher.cpp index e0ca2cf0..b362c0ba 100644 --- a/src/libkirigami/tabletmodewatcher.cpp +++ b/src/libkirigami/tabletmodewatcher.cpp @@ -1,138 +1,125 @@ /* -* Copyright 2018 Marco Martin -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU Library General Public License as -* published by the Free Software Foundation; either version 2, or -* (at your option) any later version. -* -* This program 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 Library General Public License for more details -* -* You should have received a copy of the GNU Library General Public -* License along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ + * SPDX-FileCopyrightText: 2018 Marco Martin + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ #include "tabletmodewatcher.h" #if defined(KIRIGAMI_ENABLE_DBUS) #include "tabletmodemanager_interface.h" #include #endif //TODO: All the dbus stuff should be conditional, optional win32 support namespace Kirigami { class TabletModeWatcherSingleton { public: TabletModeWatcher self; }; Q_GLOBAL_STATIC(TabletModeWatcherSingleton, privateTabletModeWatcherSelf) class TabletModeWatcherPrivate { public: TabletModeWatcherPrivate(TabletModeWatcher *watcher) : q(watcher) { #if !defined(KIRIGAMI_ENABLE_DBUS) && (defined(Q_OS_ANDROID) || defined(Q_OS_IOS)) isTabletModeAvailable = true; isTabletMode = true; #elif defined(KIRIGAMI_ENABLE_DBUS) //Mostly for debug purposes and for platforms which are always mobile, //such as Plasma Mobile if (qEnvironmentVariableIsSet("QT_QUICK_CONTROLS_MOBILE") || qEnvironmentVariableIsSet("KDE_KIRIGAMI_TABLET_MODE")) { isTabletMode = (QString::fromLatin1(qgetenv("QT_QUICK_CONTROLS_MOBILE")) == QStringLiteral("1") || QString::fromLatin1(qgetenv("QT_QUICK_CONTROLS_MOBILE")) == QStringLiteral("true")) || (QString::fromLatin1(qgetenv("KDE_KIRIGAMI_TABLET_MODE")) == QStringLiteral("1") || QString::fromLatin1(qgetenv("KDE_KIRIGAMI_TABLET_MODE")) == QStringLiteral("true")); isTabletModeAvailable = isTabletMode; } else { m_interface = new OrgKdeKWinTabletModeManagerInterface(QStringLiteral("org.kde.KWin"), QStringLiteral("/org/kde/KWin"), QDBusConnection::sessionBus(), q); if (m_interface->isValid()) { //NOTE: the initial call is actually sync, because is better a tiny freeze than having the ui always recalculated and changed at the start isTabletModeAvailable = m_interface->tabletModeAvailable(); isTabletMode = m_interface->tabletMode(); QObject::connect(m_interface, &OrgKdeKWinTabletModeManagerInterface::tabletModeChanged, q, [this](bool tabletMode) { setIsTablet(tabletMode); }); QObject::connect(m_interface, &OrgKdeKWinTabletModeManagerInterface::tabletModeAvailableChanged, q, [this](bool avail) { isTabletModeAvailable = avail; emit q->tabletModeAvailableChanged(avail); }); } else { isTabletModeAvailable = false; isTabletMode = false; } } //TODO: case for Windows #else isTabletModeAvailable = false; isTabletMode = false; #endif } ~TabletModeWatcherPrivate() {}; void setIsTablet(bool tablet); TabletModeWatcher *q; #if defined(KIRIGAMI_ENABLE_DBUS) OrgKdeKWinTabletModeManagerInterface *m_interface = nullptr; #endif bool isTabletModeAvailable = false; bool isTabletMode = false; }; void TabletModeWatcherPrivate::setIsTablet(bool tablet) { if (isTabletMode == tablet) { return; } isTabletMode = tablet; emit q->tabletModeChanged(tablet); } TabletModeWatcher::TabletModeWatcher(QObject *parent) : QObject(parent), d(new TabletModeWatcherPrivate(this)) { } TabletModeWatcher::~TabletModeWatcher() { delete d; } TabletModeWatcher *TabletModeWatcher::self() { return &privateTabletModeWatcherSelf()->self; } bool TabletModeWatcher::isTabletModeAvailable() const { return d->isTabletModeAvailable; } bool TabletModeWatcher::isTabletMode() const { return d->isTabletMode; } } #include "moc_tabletmodewatcher.cpp" diff --git a/src/libkirigami/tabletmodewatcher.h b/src/libkirigami/tabletmodewatcher.h index 0f9ce54a..73ab5a66 100644 --- a/src/libkirigami/tabletmodewatcher.h +++ b/src/libkirigami/tabletmodewatcher.h @@ -1,86 +1,73 @@ /* -* Copyright 2018 Marco Martin -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU Library General Public License as -* published by the Free Software Foundation; either version 2, or -* (at your option) any later version. -* -* This program 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 Library General Public License for more details -* -* You should have received a copy of the GNU Library General Public -* License along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ + * SPDX-FileCopyrightText: 2018 Marco Martin + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ #ifndef KIRIGAMI_TABLETMODEWATCHER #define KIRIGAMI_TABLETMODEWATCHER #include #ifndef KIRIGAMI_BUILD_TYPE_STATIC #include #endif namespace Kirigami { class TabletModeWatcherPrivate; /** * This class reports on the status of certain transformable * devices which can be both tablets and laptops at the same time, * with a detachable keyboard. * It reports whether the device supports a tablet mode and if * the device is currently in such mode or not, emitting a signal * when the user switches. */ #ifdef KIRIGAMI_BUILD_TYPE_STATIC class TabletModeWatcher : public QObject #else class KIRIGAMI2_EXPORT TabletModeWatcher : public QObject #endif { Q_OBJECT Q_PROPERTY(bool tabletModeAvailable READ isTabletModeAvailable NOTIFY tabletModeAvailableChanged) Q_PROPERTY(bool tabletMode READ isTabletMode NOTIFY tabletModeChanged) public: ~TabletModeWatcher(); static TabletModeWatcher *self(); /** * @returns true if the device supports a tablet mode and has a switch * to report when the device has been transformed. * For debug purposes, if either the environment variable QT_QUICK_CONTROLS_MOBILE * or KDE_KIRIGAMI_TABLET_MODE are set to true, isTabletModeAvailable will be true */ bool isTabletModeAvailable() const; /** * @returns true if the machine is now in tablet mode, such as the * laptop keyboard flipped away or detached. * Note that this doesn't mean exactly a tablet form factor, but * that the preferred input mode for the device is the touch screen * and that pointer and keyboard are either secondary or not available. * * For debug purposes, if either the environment variable QT_QUICK_CONTROLS_MOBILE * or KDE_KIRIGAMI_TABLET_MODE are set to true, isTabletMode will be true */ bool isTabletMode() const; Q_SIGNALS: void tabletModeAvailableChanged(bool tabletModeAvailable); void tabletModeChanged(bool tabletMode); private: TabletModeWatcher(QObject *parent = nullptr); TabletModeWatcherPrivate *d; friend class TabletModeWatcherSingleton; }; } #endif // KIRIGAMI_TABLETMODEWATCHER diff --git a/src/mnemonicattached.cpp b/src/mnemonicattached.cpp index 5da5bfab..f289a873 100644 --- a/src/mnemonicattached.cpp +++ b/src/mnemonicattached.cpp @@ -1,325 +1,312 @@ /* -* Copyright (C) 2017 by Marco Martin -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU Library General Public License as -* published by the Free Software Foundation; either version 2, or -* (at your option) any later version. -* -* This program 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 Library General Public License for more details -* -* You should have received a copy of the GNU Library General Public -* License along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ + * SPDX-FileCopyrightText: 2017 Marco Martin + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ #include "mnemonicattached.h" #include #include #include #include QHash MnemonicAttached::s_sequenceToObject = QHash(); MnemonicAttached::MnemonicAttached(QObject *parent) : QObject(parent) { QQuickItem *parentItem = qobject_cast(parent); if (parentItem) { if (parentItem->window()) { m_window = parentItem->window(); m_window->installEventFilter(this); } connect(parentItem, &QQuickItem::windowChanged, this, [this](QQuickWindow *window) { if (m_window) { QWindow *renderWindow = QQuickRenderControl::renderWindowFor(m_window); if (renderWindow) { renderWindow->removeEventFilter(this); } else { m_window->removeEventFilter(this); } } m_window = window; if (m_window) { QWindow *renderWindow = QQuickRenderControl::renderWindowFor(m_window); //renderWindow means the widget is rendering somewhere else, like a QQuickWidget if (renderWindow && renderWindow != m_window) { renderWindow->installEventFilter(this); } else { m_window->installEventFilter(this); } } }); } } MnemonicAttached::~MnemonicAttached() { s_sequenceToObject.remove(m_sequence); } bool MnemonicAttached::eventFilter(QObject *watched, QEvent *e) { Q_UNUSED(watched) if (m_richTextLabel.isEmpty()) { return false; } if (e->type() == QEvent::KeyPress) { QKeyEvent *ke = static_cast(e); if (ke->key() == Qt::Key_Alt) { m_actualRichTextLabel = m_richTextLabel; emit richTextLabelChanged(); } } else if (e->type() == QEvent::KeyRelease) { QKeyEvent *ke = static_cast(e); if (ke->key() == Qt::Key_Alt) { m_actualRichTextLabel = m_label; m_actualRichTextLabel.replace(QRegularExpression(QStringLiteral("\\&([^\\&])")), QStringLiteral("\\1")); emit richTextLabelChanged(); } } return false; } //Algorythm adapted from KAccelString void MnemonicAttached::calculateWeights() { m_weights.clear(); int pos = 0; bool start_character = true; bool wanted_character = false; while (pos < m_label.length()) { QChar c = m_label[pos]; // skip non typeable characters if (!c.isLetterOrNumber()) { start_character = true; ++pos; continue; } int weight = 1; // add special weight to first character if (pos == 0) { weight += FIRST_CHARACTER_EXTRA_WEIGHT; } // add weight to word beginnings if (start_character) { weight += WORD_BEGINNING_EXTRA_WEIGHT; start_character = false; } // add weight to word beginnings if (wanted_character) { weight += WANTED_ACCEL_EXTRA_WEIGHT; wanted_character = false; } // add decreasing weight to left characters if (pos < 50) { weight += (50 - pos); } // try to preserve the wanted accelerators if (c == QLatin1Char('&') && (pos == m_label.length() - 1 || m_label[pos+1] != QLatin1Char('&'))) { wanted_character = true; ++pos; continue; } while (m_weights.contains(weight)) { ++weight; } m_weights[weight] = c; ++pos; } //update our maximum weight if (m_weights.isEmpty()) { m_weight = m_baseWeight; } else { m_weight = m_baseWeight + m_weights.keys().last(); } } void MnemonicAttached::updateSequence() { if (!m_sequence.isEmpty()) { s_sequenceToObject.remove(m_sequence); m_sequence = {}; } calculateWeights(); const QString text = label(); if (!m_enabled) { m_actualRichTextLabel = text; m_actualRichTextLabel.replace(QRegularExpression(QStringLiteral("\\&([^\\&])")), QStringLiteral("\\1")); //was the label already completely plain text? try to limit signal emission if (m_mnemonicLabel != m_actualRichTextLabel) { m_mnemonicLabel = m_actualRichTextLabel; emit mnemonicLabelChanged(); emit richTextLabelChanged(); } return; } if (!m_weights.isEmpty()) { QMap::const_iterator i = m_weights.constEnd(); do { --i; QChar c = i.value(); QKeySequence ks(QStringLiteral("Alt+") % c); MnemonicAttached *otherMa = s_sequenceToObject.value(ks); Q_ASSERT(otherMa != this); if (!otherMa || otherMa->m_weight < m_weight) { //the old shortcut is less valuable than the current: remove it if (otherMa) { s_sequenceToObject.remove(otherMa->sequence()); otherMa->m_sequence = {}; } s_sequenceToObject[ks] = this; m_sequence = ks; m_richTextLabel = text; m_richTextLabel.replace(QRegularExpression(QLatin1String("\\&([^\\&])")), QStringLiteral("\\1")); m_actualRichTextLabel = m_richTextLabel; m_mnemonicLabel = m_richTextLabel; const int mnemonicPos = m_mnemonicLabel.indexOf(c); if (mnemonicPos > -1) { m_mnemonicLabel.replace(mnemonicPos, 1, c); } const int richTextPos = m_richTextLabel.indexOf(c); if (richTextPos > -1) { m_richTextLabel.replace(richTextPos, 1, QLatin1String("") % c % QLatin1String("")); } //remap the sequence of the previous shortcut if (otherMa) { otherMa->updateSequence(); } break; } } while (i != m_weights.constBegin()); } if (!m_sequence.isEmpty()) { emit sequenceChanged(); } else { m_actualRichTextLabel = text; m_actualRichTextLabel.replace(QRegularExpression(QStringLiteral("\\&([^\\&])")), QStringLiteral("\\1")); m_mnemonicLabel = m_actualRichTextLabel; } emit richTextLabelChanged(); emit mnemonicLabelChanged(); } void MnemonicAttached::setLabel(const QString &text) { if (m_label == text) { return; } m_label = text; updateSequence(); emit labelChanged(); } QString MnemonicAttached::richTextLabel() const { return !m_actualRichTextLabel.isEmpty() ? m_actualRichTextLabel : m_label; } QString MnemonicAttached::mnemonicLabel() const { return m_mnemonicLabel; } QString MnemonicAttached::label() const { return m_label; } void MnemonicAttached::setEnabled(bool enabled) { if (m_enabled == enabled) { return; } m_enabled = enabled; updateSequence(); emit enabledChanged(); } bool MnemonicAttached::enabled() const { return m_enabled; } void MnemonicAttached::setControlType(MnemonicAttached::ControlType controlType) { if (m_controlType == controlType) { return; } m_controlType = controlType; switch (controlType) { case ActionElement: m_baseWeight = ACTION_ELEMENT_WEIGHT; break; case DialogButton: m_baseWeight = DIALOG_BUTTON_EXTRA_WEIGHT; break; case MenuItem: m_baseWeight = MENU_ITEM_WEIGHT; break; case FormLabel: m_baseWeight = FORM_LABEL_WEIGHT; break; default: m_baseWeight = SECONDARY_CONTROL_WEIGHT; break; } //update our maximum weight if (m_weights.isEmpty()) { m_weight = m_baseWeight; } else { m_weight = m_baseWeight + (m_weights.constEnd() - 1).key(); } emit controlTypeChanged(); } MnemonicAttached::ControlType MnemonicAttached::controlType() const { return m_controlType; } QKeySequence MnemonicAttached::sequence() { return m_sequence; } MnemonicAttached *MnemonicAttached::qmlAttachedProperties(QObject *object) { return new MnemonicAttached(object); } #include "moc_mnemonicattached.cpp" diff --git a/src/mnemonicattached.h b/src/mnemonicattached.h index 5b9c0e24..692e313c 100644 --- a/src/mnemonicattached.h +++ b/src/mnemonicattached.h @@ -1,169 +1,156 @@ /* -* Copyright (C) 2017 by Marco Martin -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU Library General Public License as -* published by the Free Software Foundation; either version 2, or -* (at your option) any later version. -* -* This program 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 Library General Public License for more details -* -* You should have received a copy of the GNU Library General Public -* License along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ + * SPDX-FileCopyrightText: 2017 Marco Martin + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ #ifndef MNEMONICATTACHED_H #define MNEMONICATTACHED_H #include #include #include class QQuickItem; /** * This Attached property is used to calculate automated keyboard sequences * to trigger actions based upon their text: if an "&" mnemonic is * used (ie "&Ok"), the system will attempt to assign the desired letter giving * it priority, otherwise a letter among the ones in the label will be used if * possible and not conflicting. * Different kinds of controls will have different priorities in assigning the * shortcut: for instance the "Ok/Cancel" buttons in a dialog will have priority * over fields of a FormLayout. * @see ControlType * * Usually the developer shouldn't use this directly as base components * already use this, but only when implementing a custom graphical Control. * @since 2.3 */ class MnemonicAttached : public QObject { Q_OBJECT /** * The label of the control we want to compute a mnemonic for, instance * "Label:" or "&Ok" */ Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY labelChanged) /** * The user-visible final label, which will have the shortcut letter underlined, * such as "<u>O</u>k" */ Q_PROPERTY(QString richTextLabel READ richTextLabel NOTIFY richTextLabelChanged) /** * The label with an "&" mnemonic in the place which will have the shortcut * assigned, regardless the & wasassigned by the user or automatically generated. */ Q_PROPERTY(QString mnemonicLabel READ mnemonicLabel NOTIFY mnemonicLabelChanged) /** * Only if true this mnemonic will be considered for the global assignment * default: true */ Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) /** * the type of control this mnemonic is attached: different types of controls have different importance and priority for shortcut assignment. * @see ControlType */ Q_PROPERTY(MnemonicAttached::ControlType controlType READ controlType WRITE setControlType NOTIFY controlTypeChanged) /** * The final key sequence assigned, if any: it will be Alt+alphanumeric char */ Q_PROPERTY(QKeySequence sequence READ sequence NOTIFY sequenceChanged) public: enum ControlType { ActionElement, /** pushbuttons, checkboxes etc */ DialogButton, /** buttons for dialogs */ MenuItem, /** Menu items */ FormLabel, /** Buddy label in a FormLayout*/ SecondaryControl /** Other controls that are considered not much important and low priority for shortcuts */ }; Q_ENUM(ControlType) explicit MnemonicAttached(QObject *parent = nullptr); ~MnemonicAttached(); void setLabel(const QString &text); QString label() const; QString richTextLabel() const; QString mnemonicLabel() const; void setEnabled(bool enabled); bool enabled() const; void setControlType(MnemonicAttached::ControlType controlType); ControlType controlType() const; QKeySequence sequence(); //QML attached property static MnemonicAttached *qmlAttachedProperties(QObject *object); protected: bool eventFilter(QObject *watched, QEvent *e) override; void updateSequence(); Q_SIGNALS: void labelChanged(); void enabledChanged(); void sequenceChanged(); void richTextLabelChanged(); void mnemonicLabelChanged(); void controlTypeChanged(); private: void calculateWeights(); //TODO: to have support for DIALOG_BUTTON_EXTRA_WEIGHT etc, a type enum should be exported enum { // Additional weight for first character in string FIRST_CHARACTER_EXTRA_WEIGHT = 50, // Additional weight for the beginning of a word WORD_BEGINNING_EXTRA_WEIGHT = 50, // Additional weight for a 'wanted' accelerator ie string with '&' WANTED_ACCEL_EXTRA_WEIGHT = 150, // Default weight for an 'action' widget (ie, pushbuttons) ACTION_ELEMENT_WEIGHT = 50, // Additional weight for the dialog buttons (large, we basically never want these reassigned) DIALOG_BUTTON_EXTRA_WEIGHT = 300, // Weight for FormLayout labels (low) FORM_LABEL_WEIGHT = 20, // Weight for Secondary controls which are considered less important (low) SECONDARY_CONTROL_WEIGHT = 10, // Default weight for menu items MENU_ITEM_WEIGHT = 250 }; //order word letters by weight int m_weight = 0; int m_baseWeight = 0; ControlType m_controlType = SecondaryControl; QMap m_weights; QString m_label; QString m_actualRichTextLabel; QString m_richTextLabel; QString m_mnemonicLabel; QKeySequence m_sequence; bool m_enabled = true; QPointer m_window; //global mapping of mnemonics //TODO: map by QWindow static QHash s_sequenceToObject; }; QML_DECLARE_TYPEINFO(MnemonicAttached, QML_HAS_ATTACHED_PROPERTIES) #endif // MnemonicATTACHED_H diff --git a/src/pagepool.cpp b/src/pagepool.cpp index a3d18170..8340ffe1 100644 --- a/src/pagepool.cpp +++ b/src/pagepool.cpp @@ -1,266 +1,253 @@ /* - * Copyright 2019 Marco Martin + * SPDX-FileCopyrightText: 2019 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ #include "pagepool.h" #include #include #include #include PagePool::PagePool(QObject *parent) : QObject(parent) { } PagePool::~PagePool() { } QUrl PagePool::lastLoadedUrl() const { return m_lastLoadedUrl; } void PagePool::setCachePages(bool cache) { if (cache == m_cachePages) { return; } if (cache) { for (auto *c : m_componentForUrl.values()) { c->deleteLater(); } m_componentForUrl.clear(); for (auto *i : m_itemForUrl.values()) { // items that had been deparented are safe to delete if (!i->parentItem()) { i->deleteLater(); } QQmlEngine::setObjectOwnership(i, QQmlEngine::JavaScriptOwnership); } m_itemForUrl.clear(); } m_cachePages = cache; emit cachePagesChanged(); } bool PagePool::cachePages() const { return m_cachePages; } QQuickItem *PagePool::loadPage(const QString &url, QJSValue callback) { Q_ASSERT(qmlEngine(this)); QQmlContext *ctx = QQmlEngine::contextForObject(this); Q_ASSERT(ctx); const QUrl actualUrl = resolvedUrl(url); QQuickItem *foundItem = nullptr; if (actualUrl == m_lastLoadedUrl && m_lastLoadedItem) { foundItem = m_lastLoadedItem; } else if (m_itemForUrl.contains(actualUrl)) { foundItem = m_itemForUrl[actualUrl]; } if (foundItem) { if (callback.isCallable()) { QJSValueList args = {qmlEngine(this)->newQObject(foundItem)}; callback.call(args); m_lastLoadedUrl = actualUrl; emit lastLoadedUrlChanged(); // We could return the item, but for api coherence return null return nullptr; } else { m_lastLoadedUrl = actualUrl; emit lastLoadedUrlChanged(); return foundItem; } } QQmlComponent *component = m_componentForUrl.value(actualUrl); if (!component) { component = new QQmlComponent(qmlEngine(this), actualUrl, QQmlComponent::PreferSynchronous); } if (component->status() == QQmlComponent::Loading) { if (!callback.isCallable()) { component->deleteLater(); m_componentForUrl.remove(actualUrl); return nullptr; } connect(component, &QQmlComponent::statusChanged, this, [this, component, callback] (QQmlComponent::Status status) mutable { if (status != QQmlComponent::Ready) { qWarning() << component->errors(); m_componentForUrl.remove(component->url()); component->deleteLater(); return; } QQuickItem *item = createFromComponent(component); if (item) { QJSValueList args = {qmlEngine(this)->newQObject(item)}; callback.call(args); } if (m_cachePages) { component->deleteLater(); } else { m_componentForUrl[component->url()] = component; } }); return nullptr; } else if (component->status() != QQmlComponent::Ready) { qWarning() << component->errors(); return nullptr; } QQuickItem *item = createFromComponent(component); if (m_cachePages) { component->deleteLater(); } else { m_componentForUrl[component->url()] = component; } if (callback.isCallable()) { QJSValueList args = {qmlEngine(this)->newQObject(item)}; callback.call(args); m_lastLoadedUrl = actualUrl; emit lastLoadedUrlChanged(); // We could return the item, but for api coherence return null return nullptr; } else { m_lastLoadedUrl = actualUrl; emit lastLoadedUrlChanged(); return item; } } QQuickItem *PagePool::createFromComponent(QQmlComponent *component) { QQmlContext *ctx = QQmlEngine::contextForObject(this); Q_ASSERT(ctx); QObject *obj = component->create(ctx); // Error? if (!obj) { return nullptr; } QQuickItem *item = qobject_cast(obj); if (!item) { obj->deleteLater(); return nullptr; } // Always cache just the last one m_lastLoadedItem = item; if (m_cachePages) { QQmlEngine::setObjectOwnership(item, QQmlEngine::CppOwnership); m_itemForUrl[component->url()] = item; } else { QQmlEngine::setObjectOwnership(item, QQmlEngine::JavaScriptOwnership); } return item; } QUrl PagePool::resolvedUrl(const QString &stringUrl) const { Q_ASSERT(qmlEngine(this)); QQmlContext *ctx = QQmlEngine::contextForObject(this); Q_ASSERT(ctx); QUrl actualUrl(stringUrl); if (actualUrl.scheme().isEmpty()) { actualUrl = ctx->resolvedUrl(actualUrl); } return actualUrl; } bool PagePool::isLocalUrl(const QUrl &url) { return url.isLocalFile() || url.scheme().isEmpty() || url.scheme() == QStringLiteral("qrc"); } QUrl PagePool::urlForPage(QQuickItem *item) const { return m_urlForItem.value(item); } bool PagePool::contains(const QVariant &page) const { if (page.canConvert()) { return m_urlForItem.contains(page.value()); } else if (page.canConvert()) { const QUrl actualUrl = resolvedUrl(page.value()); return m_itemForUrl.contains(actualUrl); } else { return false; } } void PagePool::deletePage(const QVariant &page) { if (!contains(page)) { return; } QQuickItem *item; if (page.canConvert()) { item = page.value(); } else if (page.canConvert()) { QString url = page.value(); if (url.isEmpty()) { return; } const QUrl actualUrl = resolvedUrl(page.value()); item = m_itemForUrl.value(actualUrl); } else { return; } if (!item) { return; } const QUrl url = m_urlForItem.value(item); if (url.isEmpty()) { return; } m_itemForUrl.remove(url); m_urlForItem.remove(item); item->deleteLater(); } #include "moc_pagepool.cpp" diff --git a/src/pagepool.h b/src/pagepool.h index 298345c3..27f719a5 100644 --- a/src/pagepool.h +++ b/src/pagepool.h @@ -1,115 +1,102 @@ /* - * Copyright 2019 Marco Martin + * SPDX-FileCopyrightText: 2019 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ #pragma once #include #include #include /** * A Pool of Page items, pages will be unique per url and the items * will be kept around unless explicitly deleted. * Instaces are C++ owned and can be deleted only manually using deletePage() * Instance are unique per url: if you need 2 different instance for a page * url, you should instantiate them in the traditional way * or use a different PagePool instance. */ class PagePool : public QObject { Q_OBJECT /** * The last url that was loaded with @loadPage. Useful if you need * to have a "checked" state to buttons or list items that * load the page when clicked. */ Q_PROPERTY(QUrl lastLoadedUrl READ lastLoadedUrl NOTIFY lastLoadedUrlChanged) /** * If true (default) the pages will be kept around, will have C++ ownership and only one instance per page will be created. * If false the pages will have Javascript ownership (thus deleted on pop by the page stacks) and each call to loadPage will create a new page instance. When cachePages is false, Components gets cached never the less */ Q_PROPERTY(bool cachePages READ cachePages WRITE setCachePages NOTIFY cachePagesChanged) public: PagePool(QObject *parent = nullptr); ~PagePool(); QUrl lastLoadedUrl() const; void setCachePages(bool cache); bool cachePages() const; /** * Returns the instance of the item defined in the QML file identified * by url, only one instance will be made per url if cachePAges is true. If the url is remote (i.e. http) don't rely on the return value but us the async callback instead * @param url full url of the item: it can be a well formed Url, * an absolute path * or a relative one to the path of the qml file the PagePool is instantiated from * @param callback If we are loading a remote url, we can't have the item immediately but will be passed as a parameter to the provided callback. * Normally, don't set a callback, use it only in case of remote urls. - * @returns the page instance that will have been created if necessary. + * @returns the page instance that will have been created if necessary. * If the url is remote it will return null, * as well will return null if the callback has been provided */ Q_INVOKABLE QQuickItem *loadPage(const QString &url, QJSValue callback = QJSValue()); /** * @returns The url of the page for the given instance, empty if there is no correspondence */ Q_INVOKABLE QUrl urlForPage(QQuickItem *item) const; /** * @returns true if the is managed by the PagePool * @param the page can be either a QQuickItem or an url */ Q_INVOKABLE bool contains(const QVariant &page) const; /** * Deletes the page (only if is managed by the pool. * @param page either the url or the instance of the page */ Q_INVOKABLE void deletePage(const QVariant &page); /** * @returns full url from an absolute or relative path */ Q_INVOKABLE QUrl resolvedUrl(const QString &file) const; /** * @returns true if the url identifies a local resource (local file or a file inside Qt's resource system). * False if the url points to a network location */ Q_INVOKABLE bool isLocalUrl(const QUrl &url); Q_SIGNALS: void lastLoadedUrlChanged(); void cachePagesChanged(); private: QQuickItem *createFromComponent(QQmlComponent *component); QUrl m_lastLoadedUrl; QPointer m_lastLoadedItem; QHash m_itemForUrl; QHash m_componentForUrl; QHash m_urlForItem; bool m_cachePages = true; }; diff --git a/src/scenepositionattached.cpp b/src/scenepositionattached.cpp index 4c2774d8..40707bea 100644 --- a/src/scenepositionattached.cpp +++ b/src/scenepositionattached.cpp @@ -1,95 +1,82 @@ /* -* Copyright (C) 2017 by Marco Martin -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU Library General Public License as -* published by the Free Software Foundation; either version 2, or -* (at your option) any later version. -* -* This program 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 Library General Public License for more details -* -* You should have received a copy of the GNU Library General Public -* License along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ + * SPDX-FileCopyrightText: 2017 Marco Martin + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ #include "scenepositionattached.h" #include #include ScenePositionAttached::ScenePositionAttached(QObject *parent) : QObject(parent) { m_item = qobject_cast(parent); connectAncestors(m_item); } ScenePositionAttached::~ScenePositionAttached() { } int ScenePositionAttached::x() const { qreal x = 0; QQuickItem *item = m_item; while (item) { x += item->x(); item = item->parentItem(); } return x; } int ScenePositionAttached::y() const { qreal y = 0; QQuickItem *item = m_item; while (item) { y += item->y(); item = item->parentItem(); } return y; } void ScenePositionAttached::connectAncestors(QQuickItem *item) { if (!item) { return; } QQuickItem *ancestor = item; while (ancestor) { m_ancestors << ancestor; connect(ancestor, &QQuickItem::xChanged, this, &ScenePositionAttached::xChanged); connect(ancestor, &QQuickItem::yChanged, this, &ScenePositionAttached::yChanged); - connect(ancestor, &QQuickItem::parentChanged, this, + connect(ancestor, &QQuickItem::parentChanged, this, [this, ancestor]() { do { disconnect(ancestor, nullptr, this, nullptr); m_ancestors.pop_back(); } while (!m_ancestors.isEmpty() && m_ancestors.last() != ancestor); connectAncestors(ancestor); emit xChanged(); emit yChanged(); } ); ancestor = ancestor->parentItem(); } } ScenePositionAttached *ScenePositionAttached::qmlAttachedProperties(QObject *object) { return new ScenePositionAttached(object); } #include "moc_scenepositionattached.cpp" diff --git a/src/scenepositionattached.h b/src/scenepositionattached.h index d96ed003..7745f5bd 100644 --- a/src/scenepositionattached.h +++ b/src/scenepositionattached.h @@ -1,76 +1,63 @@ /* -* Copyright (C) 2018 by Marco Martin -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU Library General Public License as -* published by the Free Software Foundation; either version 2, or -* (at your option) any later version. -* -* This program 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 Library General Public License for more details -* -* You should have received a copy of the GNU Library General Public -* License along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ + * SPDX-FileCopyrightText: 2018 Marco Martin + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ #ifndef SCENEPOSITIONATTACHED_H #define SCENEPOSITIONATTACHED_H #include #include class QQuickItem; /** * This attached property contains the information about the scene position of the item: * Its global x and y coordinates will update automatically and can be binded * @code * import org.kde.kirigami 2.5 as Kirigami * Text { * text: ScenePosition.x * } * @endcode * @since 2.3 */ class ScenePositionAttached : public QObject { Q_OBJECT /** * The global scene X position */ Q_PROPERTY(int x READ x NOTIFY xChanged) /** * The global scene Y position */ Q_PROPERTY(int y READ y NOTIFY yChanged) public: explicit ScenePositionAttached(QObject *parent = nullptr); ~ScenePositionAttached() override; int x() const; int y() const; //QML attached property static ScenePositionAttached *qmlAttachedProperties(QObject *object); Q_SIGNALS: void xChanged(); void yChanged(); private: void connectAncestors(QQuickItem *item); QQuickItem *m_item = nullptr; QList m_ancestors; }; QML_DECLARE_TYPEINFO(ScenePositionAttached, QML_HAS_ATTACHED_PROPERTIES) #endif // SCENEPOSITIONATTACHED_H diff --git a/src/settings.cpp b/src/settings.cpp index cb43d1dc..5bc864ac 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1,223 +1,210 @@ /* - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ #include "settings.h" #include #include #include #include #include #include #include #include #include "libkirigami/tabletmodewatcher.h" #ifndef KIRIGAMI_BUILD_TYPE_STATIC #include "../kirigami_version.h" #endif class SettingsSingleton { public: Settings self; }; Q_GLOBAL_STATIC(SettingsSingleton, privateSettingsSelf) Settings::Settings(QObject *parent) : QObject(parent), m_hasTouchScreen(false), m_hasTransientTouchInput(false) { m_tabletModeAvailable = Kirigami::TabletModeWatcher::self()->isTabletModeAvailable(); connect(Kirigami::TabletModeWatcher::self(), &Kirigami::TabletModeWatcher::tabletModeAvailableChanged, this, [this](bool tabletModeAvailable) { setTabletModeAvailable(tabletModeAvailable); }); m_tabletMode = Kirigami::TabletModeWatcher::self()->isTabletMode(); connect(Kirigami::TabletModeWatcher::self(), &Kirigami::TabletModeWatcher::tabletModeChanged, this, [this](bool tabletMode) { setTabletMode(tabletMode); }); #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) m_mobile = true; m_hasTouchScreen = true; #else //Mostly for debug purposes and for platforms which are always mobile, //such as Plasma Mobile if (qEnvironmentVariableIsSet("QT_QUICK_CONTROLS_MOBILE")) { m_mobile = QByteArrayList{"1", "true"}.contains(qgetenv("QT_QUICK_CONTROLS_MOBILE")); } else { m_mobile = false; } for (const auto &device : QTouchDevice::devices()) { if (device->type() == QTouchDevice::TouchScreen) { m_hasTouchScreen = true; break; } } if (m_hasTouchScreen) { connect(qApp, &QGuiApplication::focusWindowChanged, this, [this](QWindow *win) { if (win) { win->installEventFilter(this); } }); } #endif const QString configPath = QStandardPaths::locate(QStandardPaths::ConfigLocation, QStringLiteral("kdeglobals")); if (QFile::exists(configPath)) { QSettings globals(configPath, QSettings::IniFormat); globals.beginGroup(QStringLiteral("KDE")); m_scrollLines = qMax(1, globals.value(QStringLiteral("WheelScrollLines"), 3).toInt()); } else { m_scrollLines = 3; } } Settings::~Settings() { } Settings *Settings::self() { return &privateSettingsSelf()->self; } bool Settings::eventFilter(QObject *watched, QEvent *event) { switch (event->type()) { case QEvent::TouchBegin: setTransientTouchInput(true); break; case QEvent::MouseButtonPress: case QEvent::MouseMove: case QEvent::Wheel: setTransientTouchInput(false); default: break; } return false; } void Settings::setTabletModeAvailable(bool mobileAvailable) { if (mobileAvailable == m_tabletModeAvailable) { return; } m_tabletModeAvailable = mobileAvailable; emit tabletModeAvailableChanged(); } bool Settings::isTabletModeAvailable() const { return m_tabletModeAvailable; } void Settings::setIsMobile(bool mobile) { if (mobile == m_mobile) { return; } m_mobile = mobile; emit isMobileChanged(); } bool Settings::isMobile() const { return m_mobile; } void Settings::setTabletMode(bool tablet) { if (tablet == m_tabletMode) { return; } m_tabletMode = tablet; emit tabletModeChanged(); } bool Settings::tabletMode() const { return m_tabletMode; } void Settings::setTransientTouchInput(bool touch) { if (touch == m_hasTransientTouchInput) { return; } m_hasTransientTouchInput = touch; if (!m_tabletMode) { emit hasTransientTouchInputChanged(); } } bool Settings::hasTransientTouchInput() const { return m_hasTransientTouchInput || m_tabletMode; } QString Settings::style() const { return m_style; } void Settings::setStyle(const QString &style) { m_style = style; } int Settings::mouseWheelScrollLines() const { return m_scrollLines; } QStringList Settings::information() const { return { #ifndef KIRIGAMI_BUILD_TYPE_STATIC tr("KDE Frameworks %1").arg(QStringLiteral(KIRIGAMI2_VERSION_STRING)), #endif tr("The %1 windowing system").arg(QGuiApplication::platformName()), tr("Qt %2 (built against %3)").arg(QString::fromLocal8Bit(qVersion()), QStringLiteral(QT_VERSION_STR)) }; } QVariant Settings::applicationWindowIcon() const { const QIcon& windowIcon = qApp->windowIcon(); if (windowIcon.isNull()) { return QVariant(); } return windowIcon; } diff --git a/src/settings.h b/src/settings.h index 37c06a37..e63866ff 100644 --- a/src/settings.h +++ b/src/settings.h @@ -1,134 +1,121 @@ /* - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef SETTINGS_H #define SETTINGS_H #include #include /** * This class contains global kirigami settings about the current device setup * It is exposed to QML as the singleton "Settings" */ class Settings : public QObject { Q_OBJECT /** * True if the system can dynamically enter in tablet mode * (or the device is actually a tablet). * such as transformable laptops that support keyboard detachment */ Q_PROPERTY(bool tabletModeAvailable READ isTabletModeAvailable NOTIFY tabletModeAvailableChanged) /** * True if we are running on a small mobile device such as a mobile phone * This is used when we want to do specific adaptations to our UI for * small screen form factors, such as having bigger touch areas. */ Q_PROPERTY(bool isMobile READ isMobile NOTIFY isMobileChanged) /** * True if the device we are running on is behaving like a tablet: * Note that this doesn't mean exactly a tablet form factor, but * that the preferred input mode for the device is the touch screen * and that pointer and keyboard are either secondary or not available. */ Q_PROPERTY(bool tabletMode READ tabletMode NOTIFY tabletModeChanged) /** * True if the user in this moment is interacting with the app with the touch screen */ Q_PROPERTY(bool hasTransientTouchInput READ hasTransientTouchInput NOTIFY hasTransientTouchInputChanged) /** * name of the QtQuickControls2 style we are using, * for instance org.kde.desktop, Plasma, Material, Universal etc */ Q_PROPERTY(QString style READ style CONSTANT) //TODO: make this adapt without file watchers? /** * How many lines of text the mouse wheel should scroll */ Q_PROPERTY(int mouseWheelScrollLines READ mouseWheelScrollLines CONSTANT) /** * @returns runtime information about the libraries in use * * @since 5.52 * @since org.kde.kirigami 2.6 */ Q_PROPERTY(QStringList information READ information CONSTANT) - + /** * @returns application window icon, basically \QApplication::windowIcon() * * @since 5.62 * @since org.kde.kirigami 2.10 */ Q_PROPERTY(QVariant applicationWindowIcon READ applicationWindowIcon CONSTANT) public: Settings(QObject *parent = nullptr); ~Settings(); void setTabletModeAvailable(bool mobile); bool isTabletModeAvailable() const; void setIsMobile(bool mobile); bool isMobile() const; void setTabletMode(bool tablet); bool tabletMode() const; void setTransientTouchInput(bool touch); bool hasTransientTouchInput() const; QString style() const; void setStyle(const QString &style); int mouseWheelScrollLines() const; QStringList information() const; - + QVariant applicationWindowIcon() const; static Settings *self(); protected: bool eventFilter(QObject *watched, QEvent *event) override; Q_SIGNALS: void tabletModeAvailableChanged(); void tabletModeChanged(); void isMobileChanged(); void hasTransientTouchInputChanged(); private: QString m_style; int m_scrollLines = 0; bool m_tabletModeAvailable : 1; bool m_mobile : 1; bool m_tabletMode : 1; bool m_hasTouchScreen : 1; bool m_hasTransientTouchInput : 1; }; #endif diff --git a/src/styles/Material/AbstractListItem.qml b/src/styles/Material/AbstractListItem.qml index aeb8937d..0ea7d858 100644 --- a/src/styles/Material/AbstractListItem.qml +++ b/src/styles/Material/AbstractListItem.qml @@ -1,43 +1,30 @@ /* - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.7 import org.kde.kirigami 2.4 import QtQuick.Controls.Material 2.1 as Mat import QtQuick.Controls.Material.impl 2.1 as MatImp import "../../private" import "../../templates" as T T.AbstractListItem { id: listItem background: DefaultListItemBackground { MatImp.Ripple { anchors.fill: parent clip: visible visible: listItem.supportsMouseEvents pressed: listItem.pressed anchor: listItem - active: listItem.down || listItem.visualFocus + active: listItem.down || listItem.visualFocus color: Qt.rgba(0,0,0,0.2) } } implicitHeight: contentItem.implicitHeight + Units.smallSpacing * 6 } diff --git a/src/styles/Material/InlineMessage.qml b/src/styles/Material/InlineMessage.qml index f2e52e73..de45aa31 100644 --- a/src/styles/Material/InlineMessage.qml +++ b/src/styles/Material/InlineMessage.qml @@ -1,128 +1,115 @@ /* - * Copyright 2018 Eike Hein - * Copyright 2018 Marco Martin - * Copyright 2018 Kai Uwe Broulik + * SPDX-FileCopyrightText: 2018 Eike Hein + * SPDX-FileCopyrightText: 2018 Marco Martin + * SPDX-FileCopyrightText: 2018 Kai Uwe Broulik * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.7 import QtGraphicalEffects 1.0 import org.kde.kirigami 2.5 as Kirigami import "../../private" import "../../templates" as T /** * An inline message item with support for informational, positive, * warning and error types, and with support for associated actions. * * InlineMessage can be used to give information to the user or * interact with the user, without requiring the use of a dialog. * * The InlineMessage item is hidden by default. It also manages its * height (and implicitHeight) during an animated reveal when shown. * You should avoid setting height on an InlineMessage unless it is * already visible. * * Optionally an icon can be set, defaulting to an icon appropriate * to the message type otherwise. * * Optionally a close button can be shown. * * Actions are added from left to right. If more actions are set than * can fit, an overflow menu is provided. * * Example: * @code * InlineMessage { * type: Kirigami.MessageType.Error * * text: "My error message" * * actions: [ * Kirigami.Action { * iconName: "edit" * text: "Action text" * onTriggered: { * // do stuff * } * }, * Kirigami.Action { * iconName: "edit" * text: "Action text" * onTriggered: { * // do stuff * } * } * ] * } * @endcode * * @since 5.45 */ T.InlineMessage { id: root background: Rectangle { id: bgBorderRect color: { if (root.type == Kirigami.MessageType.Positive) { return Kirigami.Theme.positiveTextColor; } else if (root.type == Kirigami.MessageType.Warning) { return Kirigami.Theme.neutralTextColor; } else if (root.type == Kirigami.MessageType.Error) { return Kirigami.Theme.negativeTextColor; } return Kirigami.Theme.activeTextColor; } radius: Kirigami.Units.smallSpacing / 2 Rectangle { id: bgFillRect anchors.fill: parent anchors.margins: Kirigami.Units.devicePixelRatio color: Kirigami.Theme.backgroundColor radius: bgBorderRect.radius * 0.60 } Rectangle { anchors.fill: bgFillRect color: bgBorderRect.color opacity: 0.20 radius: bgFillRect.radius } layer.enabled: true layer.effect: DropShadow { horizontalOffset: 0 verticalOffset: 1 radius: 12 samples: 32 color: Qt.rgba(0, 0, 0, 0.5) } } } diff --git a/src/styles/Material/Label.qml b/src/styles/Material/Label.qml index ae137b07..24290e6d 100644 --- a/src/styles/Material/Label.qml +++ b/src/styles/Material/Label.qml @@ -1,46 +1,33 @@ /* -* Copyright (C) 2011 by Marco Martin -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU Library General Public License as -* published by the Free Software Foundation; either version 2, or -* (at your option) any later version. -* -* This program 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 Library General Public License for more details -* -* You should have received a copy of the GNU Library General Public -* License along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. -*/ + * SPDX-FileCopyrightText: 2011 by Marco Martin + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ import QtQuick 2.1 import QtQuick.Window 2.2 import org.kde.kirigami 2.4 import QtQuick.Controls 2.0 as Controls /** * This is a label which uses the current Theme. * * The characteristics of the text will be automatically set according to the * current Theme. If you need a more customized text item use the Text component * from QtQuick. * * You can use all elements of the QML Text component, in particular the "text" * property to define the label text. * * @inherit QtQuick.Templates.Label * @deprecated use QtQuick.Templates.Label directly, it will be styled appropriately */ Controls.Label { verticalAlignment: lineCount > 1 ? Text.AlignTop : Text.AlignVCenter activeFocusOnTab: false Component.onCompleted: { console.warn("Kirigami.Label is deprecated. Use QtQuickControls2.Label instead") } } diff --git a/src/styles/Material/SwipeListItem.qml b/src/styles/Material/SwipeListItem.qml index 5cbe6264..01b67d90 100644 --- a/src/styles/Material/SwipeListItem.qml +++ b/src/styles/Material/SwipeListItem.qml @@ -1,70 +1,57 @@ /* - * Copyright 2010 Marco Martin + * SPDX-FileCopyrightText: 2010 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.5 import org.kde.kirigami 2.4 import QtQuick.Controls.Material 2.1 as Mat import QtQuick.Controls.Material.impl 2.1 as MatImp import "../../private" import "../../templates" as T /** * An item delegate Intended to support extra actions obtainable * by uncovering them by dragging away the item with the handle * This acts as a container for normal list items. * Any subclass of AbstractListItem can be assigned as the contentItem property. * @code * ListView { * model: myModel * delegate: SwipeListItem { * Label { * text: model.text * } * actions: [ * Action { * iconName: "document-decrypt" * onTriggered: print("Action 1 clicked") * }, * Action { * iconName: model.action2Icon * onTriggered: //do something * } * ] * } - * + * * } * @endcode * * @inherit QtQuick.Item */ T.SwipeListItem { id: root background: DefaultListItemBackground { MatImp.Ripple { anchors.fill: parent clip: visible pressed: listItem.pressed anchor: listItem - active: listItem.down || listItem.visualFocus + active: listItem.down || listItem.visualFocus color: Qt.rgba(0,0,0,0.2) } } implicitHeight: contentItem.implicitHeight + Units.smallSpacing * 6 } diff --git a/src/styles/Material/Theme.qml b/src/styles/Material/Theme.qml index b5293544..4cd8a08d 100644 --- a/src/styles/Material/Theme.qml +++ b/src/styles/Material/Theme.qml @@ -1,117 +1,104 @@ /* - * Copyright 2015 Marco Martin + * SPDX-FileCopyrightText: 2015 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.7 import QtQuick.Controls.Material 2.0 import org.kde.kirigami 2.4 as Kirigami pragma Singleton QtObject { id: theme //NOTE: this is useless per se, but it forces the Material attached property to be created Material.elevation:2 property color textColor: theme.Material.foreground onTextColorChanged: theme.Material.foreground = textColor property color disabledTextColor: "#9931363b" property color highlightColor: theme.Material.accent onHighlightColorChanged: theme.Material.accent = highlightColor //FIXME: something better? property color highlightedTextColor: theme.Material.background property color backgroundColor: theme.Material.background property color alternateBackgroundColor: Qt.darker(theme.Material.background, 1.05) property color activeTextColor: theme.Material.primary property color activeBackgroundColor: theme.Material.primary property color linkColor: "#2980B9" property color linkBackgroundColor: "#2980B9" property color visitedLinkColor: "#7F8C8D" property color visitedLinkBackgroundColor: "#7F8C8D" property color hoverColor: theme.Material.highlightedButtonColor property color focusColor: theme.Material.highlightedButtonColor property color negativeTextColor: "#DA4453" property color negativeBackgroundColor: "#DA4453" property color neutralTextColor: "#F67400" property color neutralBackgroundColor: "#F67400" property color positiveTextColor: "#27AE60" property color positiveBackgroundColor: "#27AE60" property color buttonTextColor: theme.Material.foreground property color buttonBackgroundColor: theme.Material.buttonColor property color buttonAlternateBackgroundColor: Qt.darker(theme.Material.buttonColor, 1.05) property color buttonHoverColor: theme.Material.highlightedButtonColor property color buttonFocusColor: theme.Material.highlightedButtonColor property color viewTextColor: theme.Material.foreground property color viewBackgroundColor: theme.Material.dialogColor property color viewAlternateBackgroundColor: Qt.darker(theme.Material.dialogColor, 1.05) property color viewHoverColor: theme.Material.listHighlightColor property color viewFocusColor: theme.Material.listHighlightColor property color selectionTextColor: theme.Material.primaryHighlightedTextColor property color selectionBackgroundColor: theme.Material.textSelectionColor property color selectionAlternateBackgroundColor: Qt.darker(theme.Material.textSelectionColor, 1.05) property color selectionHoverColor: theme.Material.highlightedButtonColor property color selectionFocusColor: theme.Material.highlightedButtonColor property color tooltipTextColor: fontMetrics.Material.foreground property color tooltipBackgroundColor: fontMetrics.Material.tooltipColor property color tooltipAlternateBackgroundColor: Qt.darker(theme.Material.tooltipColor, 1.05) property color tooltipHoverColor: fontMetrics.Material.highlightedButtonColor property color tooltipFocusColor: fontMetrics.Material.highlightedButtonColor property color complementaryTextColor: fontMetrics.Material.foreground property color complementaryBackgroundColor: fontMetrics.Material.background property color complementaryAlternateBackgroundColor: Qt.lighter(fontMetrics.Material.background, 1.05) property color complementaryHoverColor: theme.Material.highlightedButtonColor property color complementaryFocusColor: theme.Material.highlightedButtonColor property font defaultFont: fontMetrics.font property list children: [ TextMetrics { id: fontMetrics //this is to get a source of dark colors Material.theme: Material.Dark } ] //for internal use function __propagateColorSet(object, context) { //TODO: actually check if it's a dark or light color if (context === Kirigami.Theme.Complementary) { object.Material.theme = Material.Dark; } else { object.Material.theme = Material.Light; } } function __propagateTextColor(object, color) { object.Material.foreground = color; } function __propagateBackgroundColor(object, color) { object.Material.background = color; } function __propagatePrimaryColor(object, color) { object.Material.primary = color; } function __propagateAccentColor(object, color) { object.Material.accent = color; } } diff --git a/src/styles/org.kde.desktop/AbstractApplicationHeader.qml b/src/styles/org.kde.desktop/AbstractApplicationHeader.qml index 91771d4e..53a6d6d9 100644 --- a/src/styles/org.kde.desktop/AbstractApplicationHeader.qml +++ b/src/styles/org.kde.desktop/AbstractApplicationHeader.qml @@ -1,55 +1,42 @@ /* - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.5 import org.kde.kirigami 2.4 import "../../templates" as T /** * An item that can be used as a title for the application. * Scrolling the main page will make it taller or shorter (trough the point of going away) * It's a behavior similar to the typical mobile web browser addressbar * the minimum, preferred and maximum heights of the item can be controlled with * * minimumHeight: default is 0, i.e. hidden * * preferredHeight: default is Units.gridUnit * 1.6 * * maximumHeight: default is Units.gridUnit * 3 * * To achieve a titlebar that stays completely fixed just set the 3 sizes as the same */ T.AbstractApplicationHeader { id: root Theme.inherit: false background: Rectangle { color: Theme.backgroundColor Separator { visible: root.separatorVisible && (!root.page || !root.page.header || !root.page.header.visible || root.page.header.toString().indexOf("ToolBar") === -1) anchors { left: parent.left right: parent.right bottom: root.y <= 0 ? parent.bottom : undefined - top: root.y <= 0 ? undefined : parent.top + top: root.y <= 0 ? undefined : parent.top } } } } diff --git a/src/styles/org.kde.desktop/AbstractListItem.qml b/src/styles/org.kde.desktop/AbstractListItem.qml index 56d1e5b3..a32d03ef 100644 --- a/src/styles/org.kde.desktop/AbstractListItem.qml +++ b/src/styles/org.kde.desktop/AbstractListItem.qml @@ -1,29 +1,16 @@ /* - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.1 import org.kde.kirigami 2.4 import "../../private" import "../../templates" as T T.AbstractListItem { id: listItem background: DefaultListItemBackground {} } diff --git a/src/styles/org.kde.desktop/ApplicationWindow.qml b/src/styles/org.kde.desktop/ApplicationWindow.qml index 10056ff4..8ebde115 100644 --- a/src/styles/org.kde.desktop/ApplicationWindow.qml +++ b/src/styles/org.kde.desktop/ApplicationWindow.qml @@ -1,29 +1,16 @@ /* - * Copyright 2015 Marco Martin + * SPDX-FileCopyrightText: 2015 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.1 import org.kde.kirigami 2.4 import "../../" as Base Base.ApplicationWindow { id: root width: Units.gridUnit * 55 height: Units.gridUnit * 40 } diff --git a/src/styles/org.kde.desktop/SwipeListItem.qml b/src/styles/org.kde.desktop/SwipeListItem.qml index 5df6f5e3..15f41759 100644 --- a/src/styles/org.kde.desktop/SwipeListItem.qml +++ b/src/styles/org.kde.desktop/SwipeListItem.qml @@ -1,29 +1,16 @@ /* - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.5 import org.kde.kirigami 2.4 import "../../private" import "../../templates" as T T.SwipeListItem { id: listItem background: DefaultListItemBackground {} } diff --git a/src/styles/org.kde.desktop/Theme.qml b/src/styles/org.kde.desktop/Theme.qml index 3eb54fdf..3fa78ab2 100644 --- a/src/styles/org.kde.desktop/Theme.qml +++ b/src/styles/org.kde.desktop/Theme.qml @@ -1,102 +1,89 @@ /* - * Copyright 2015 Marco Martin + * SPDX-FileCopyrightText: 2015 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.4 pragma Singleton QtObject { id: theme property color textColor: palette.windowText property color disabledTextColor: disabledPalette.windowText property color highlightColor: palette.highlight property color highlightedTextColor: palette.highlightedText property color backgroundColor: palette.window property color alternateBackgroundColor: Qt.darker(palette.window, 1.05) property color activeTextColor: palette.highlight property color activeBackgroundColor: palette.highlight property color linkColor: "#2980B9" property color linkBackgroundColor: "#2980B9" property color visitedLinkColor: "#7F8C8D" property color visitedLinkBackgroundColor: "#7F8C8D" property color hoverColor: palette.highlight property color focusColor: palette.highlight property color negativeTextColor: "#DA4453" property color negativeBackgroundColor: "#DA4453" property color neutralTextColor: "#F67400" property color neutralBackgroundColor: "#F67400" property color positiveTextColor: "#27AE60" property color positiveBackgroundColor: "#27AE60" property color buttonTextColor: palette.buttonText property color buttonBackgroundColor: palette.button property color buttonAlternateBackgroundColor: Qt.darker(palette.button, 1.05) property color buttonHoverColor: palette.highlight property color buttonFocusColor: palette.highlight property color viewTextColor: palette.text property color viewBackgroundColor: palette.base property color viewAlternateBackgroundColor: palette.alternateBase property color viewHoverColor: palette.highlight property color viewFocusColor: palette.highlight property color selectionTextColor: palette.highlightedText property color selectionBackgroundColor: palette.highlight property color selectionAlternateBackgroundColor: Qt.darker(palette.highlight, 1.05) property color selectionHoverColor: palette.highlight property color selectionFocusColor: palette.highlight property color tooltipTextColor: palette.base property color tooltipBackgroundColor: palette.text property color tooltipAlternateBackgroundColor: Qt.darker(palette.text, 1.05) property color tooltipHoverColor: palette.highlight property color tooltipFocusColor: palette.highlight property color complementaryTextColor: palette.base property color complementaryBackgroundColor: palette.text property color complementaryAlternateBackgroundColor: Qt.darker(palette.text, 1.05) property color complementaryHoverColor: palette.highlight property color complementaryFocusColor: palette.highlight property font defaultFont: fontMetrics.font property list children: [ TextMetrics { id: fontMetrics }, SystemPalette { id: palette colorGroup: SystemPalette.Active }, SystemPalette { id: disabledPalette colorGroup: SystemPalette.Disabled } ] function __propagateColorSet(object, context) {} - + function __propagateTextColor(object, color) {} function __propagateBackgroundColor(object, color) {} function __propagatePrimaryColor(object, color) {} function __propagateAccentColor(object, color) {} } diff --git a/src/styles/org.kde.desktop/Units.qml b/src/styles/org.kde.desktop/Units.qml index a667b036..42286221 100644 --- a/src/styles/org.kde.desktop/Units.qml +++ b/src/styles/org.kde.desktop/Units.qml @@ -1,126 +1,113 @@ /* - * Copyright 2015 Marco Martin + * SPDX-FileCopyrightText: 2015 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.4 import QtQuick.Window 2.2 pragma Singleton QtObject { id: units /** * The fundamental unit of space that should be used for sizes, expressed in pixels. * Given the screen has an accurate DPI settings, it corresponds to a width of * the capital letter M */ property int gridUnit: fontMetrics.height /** * units.iconSizes provides access to platform-dependent icon sizing * * The icon sizes provided are normalized for different DPI, so icons * will scale depending on the DPI. * * Icon sizes from KIconLoader, adjusted to devicePixelRatio: * * small * * smallMedium * * medium * * large * * huge * * enormous * * Not devicePixelRation-adjusted:: * * desktop */ property QtObject iconSizes: QtObject { property int small: fontMetrics.roundedIconSize(16 * devicePixelRatio) property int smallMedium: fontMetrics.roundedIconSize(22 * devicePixelRatio) property int medium: fontMetrics.roundedIconSize(32 * devicePixelRatio) property int large: fontMetrics.roundedIconSize(48 * devicePixelRatio) property int huge: fontMetrics.roundedIconSize(64 * devicePixelRatio) property int enormous: 128 * devicePixelRatio } /** * units.smallSpacing is the amount of spacing that should be used around smaller UI elements, * for example as spacing in Columns. Internally, this size depends on the size of * the default font as rendered on the screen, so it takes user-configured font size and DPI * into account. */ property int smallSpacing: Math.floor(gridUnit/4) /** * units.largeSpacing is the amount of spacing that should be used inside bigger UI elements, * for example between an icon and the corresponding text. Internally, this size depends on * the size of the default font as rendered on the screen, so it takes user-configured font * size and DPI into account. */ property int largeSpacing: smallSpacing * 2 /** * The ratio between physical and device-independent pixels. This value does not depend on the \ * size of the configured font. If you want to take font sizes into account when scaling elements, * use theme.mSize(theme.defaultFont), units.smallSpacing and units.largeSpacing. * The devicePixelRatio follows the definition of "device independent pixel" by Microsoft. */ property real devicePixelRatio: Math.max(1, (fontMetrics.font.pixelSize / fontMetrics.font.pointSize)) /** * units.longDuration should be used for longer, screen-covering animations, for opening and * closing of dialogs and other "not too small" animations */ property int longDuration: 250 /** * units.shortDuration should be used for short animations, such as accentuating a UI event, * hover events, etc.. */ property int shortDuration: 150 property int toolTipDelay: 700 //readonly property QtObject __styleItem: QtQuickControlsPrivate.StyleItem {elementType: "frame" } /** * How much the mouse scroll wheel scrolls, expressed in lines of text. * Note: this is strictly for classical mouse wheels, touchpads 2 figer scrolling won't be affected */ readonly property int wheelScrollLines: 3//__styleItem.styleHint("wheelScrollLines") property variant fontMetrics: TextMetrics { text: "M" function roundedIconSize(size) { if (size < 16) { return size; } else if (size < 22) { return 16; } else if (size < 32) { return 22; } else if (size < 48) { return 32; } else if (size < 64) { return 48; } else { return size; } } } } diff --git a/src/wheelhandler.cpp b/src/wheelhandler.cpp index f0d24ffc..6b1ce345 100644 --- a/src/wheelhandler.cpp +++ b/src/wheelhandler.cpp @@ -1,303 +1,290 @@ /* -* Copyright (C) 2019 by Marco Martin -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU Library General Public License as -* published by the Free Software Foundation; either version 2, or -* (at your option) any later version. -* -* This program 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 Library General Public License for more details -* -* You should have received a copy of the GNU Library General Public -* License along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ + * SPDX-FileCopyrightText: 2019 Marco Martin + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ #include "wheelhandler.h" #include "settings.h" #include #include #include class GlobalWheelFilterSingleton { public: GlobalWheelFilter self; }; Q_GLOBAL_STATIC(GlobalWheelFilterSingleton, privateGlobalWheelFilterSelf) GlobalWheelFilter::GlobalWheelFilter(QObject *parent) : QObject(parent) { } GlobalWheelFilter::~GlobalWheelFilter() {} GlobalWheelFilter *GlobalWheelFilter::self() { return &privateGlobalWheelFilterSelf()->self; } void GlobalWheelFilter::setItemHandlerAssociation(QQuickItem *item, WheelHandler *handler) { if (!m_handlersForItem.contains(handler->target())) { handler->target()->installEventFilter(this); } m_handlersForItem.insert(item, handler); connect(item, &QObject::destroyed, this, [this](QObject *obj) { QQuickItem *item = static_cast(obj); m_handlersForItem.remove(item); }); connect(handler, &QObject::destroyed, this, [this](QObject *obj) { WheelHandler *handler = static_cast(obj); removeItemHandlerAssociation(handler->target(), handler); }); } void GlobalWheelFilter::removeItemHandlerAssociation(QQuickItem *item, WheelHandler *handler) { if (!item || !handler) { return; } m_handlersForItem.remove(item, handler); if (!m_handlersForItem.contains(item)) { item->removeEventFilter(this); } } -bool GlobalWheelFilter::eventFilter(QObject *watched, QEvent *event) +bool GlobalWheelFilter::eventFilter(QObject *watched, QEvent *event) { if (event->type() == QEvent::Wheel) { QQuickItem *item = qobject_cast(watched); if (!item || !item->isEnabled()) { return QObject::eventFilter(watched, event); } QWheelEvent *we = static_cast(event); m_wheelEvent.initializeFromEvent(we); bool shouldBlock = false; bool shouldScrollFlickable = false; for (auto *handler : m_handlersForItem.values(item)) { if (handler->m_blockTargetWheel) { shouldBlock = true; } if (handler->m_scrollFlickableTarget) { shouldScrollFlickable = true; } emit handler->wheel(&m_wheelEvent); } if (shouldScrollFlickable && !m_wheelEvent.isAccepted()) { manageWheel(item, we); } if (shouldBlock) { return true; } } return QObject::eventFilter(watched, event); } void GlobalWheelFilter::manageWheel(QQuickItem *target, QWheelEvent *event) { // Duck typing: accept everyhint that has all the properties we need if (target->metaObject()->indexOfProperty("contentX") == -1 || target->metaObject()->indexOfProperty("contentY") == -1 || target->metaObject()->indexOfProperty("contentWidth") == -1 || target->metaObject()->indexOfProperty("contentHeight") == -1 || target->metaObject()->indexOfProperty("topMargin") == -1 || target->metaObject()->indexOfProperty("bottomMargin") == -1 || target->metaObject()->indexOfProperty("leftMargin") == -1 || target->metaObject()->indexOfProperty("rightMargin") == -1 || target->metaObject()->indexOfProperty("originX") == -1 || target->metaObject()->indexOfProperty("originY") == -1) { return; } qreal contentWidth = target->property("contentWidth").toReal(); qreal contentHeight = target->property("contentHeight").toReal(); qreal contentX = target->property("contentX").toReal(); qreal contentY = target->property("contentY").toReal(); qreal topMargin = target->property("topMargin").toReal(); qreal bottomMargin = target->property("bottomMargin").toReal(); qreal leftMargin = target->property("leftMaring").toReal(); qreal rightMargin = target->property("rightMargin").toReal(); qreal originX = target->property("originX").toReal(); qreal originY = target->property("originY").toReal(); // Scroll Y if (contentHeight > target->height()) { int y = event->pixelDelta().y() != 0 ? event->pixelDelta().y() : event->angleDelta().y() / 8; //if we don't have a pixeldelta, apply the configured mouse wheel lines if (!event->pixelDelta().y()) { y *= Settings::self()->mouseWheelScrollLines(); } // Scroll one page regardless of delta: if ((event->modifiers() & Qt::ControlModifier) || (event->modifiers() & Qt::ShiftModifier)) { if (y > 0) { y = target->height(); } else if (y < 0) { y = -target->height(); } } qreal minYExtent = topMargin - originY; qreal maxYExtent = target->height() - (contentHeight + bottomMargin + originY); target->setProperty("contentY", qMin(-maxYExtent, qMax(-minYExtent, contentY - y))); } - + //Scroll X if (contentWidth > target->width()) { int x = event->pixelDelta().x() != 0 ? event->pixelDelta().x() : event->angleDelta().x() / 8; // Special case: when can't scroll vertically, scroll horizontally with vertical wheel as well if (x == 0 && contentHeight <= target->height()) { x = event->pixelDelta().y() != 0 ? event->pixelDelta().y() : event->angleDelta().y() / 8; } //if we don't have a pixeldelta, apply the configured mouse wheel lines if (!event->pixelDelta().x()) { x *= Settings::self()->mouseWheelScrollLines(); } // Scroll one page regardless of delta: if ((event->modifiers() & Qt::ControlModifier) || (event->modifiers() & Qt::ShiftModifier)) { if (x > 0) { x = target->width(); } else if (x < 0) { x = -target->width(); } } qreal minXExtent = leftMargin - originX; qreal maxXExtent = target->width() - (contentWidth + rightMargin + originX); target->setProperty("contentX", qMin(-maxXExtent, qMax(-minXExtent, contentX - x))); } - //this is just for making the scrollbar + //this is just for making the scrollbar target->metaObject()->invokeMethod(target, "flick", Q_ARG(double, 0), Q_ARG(double, 1)); target->metaObject()->invokeMethod(target, "cancelFlick"); } //////////////////////////// KirigamiWheelEvent::KirigamiWheelEvent(QObject *parent) : QObject(parent) {} KirigamiWheelEvent::~KirigamiWheelEvent() {} void KirigamiWheelEvent::initializeFromEvent(QWheelEvent *event) { #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) m_x = event->position().x(); m_y = event->position().y(); #else m_x = event->x(); m_y = event->y(); #endif m_angleDelta = event->angleDelta(); m_pixelDelta = event->pixelDelta(); m_buttons = event->buttons(); m_modifiers = event->modifiers(); m_accepted = false; m_inverted = event->inverted(); } qreal KirigamiWheelEvent::x() const { return m_x; } qreal KirigamiWheelEvent::y() const { return m_y; } QPointF KirigamiWheelEvent::angleDelta() const { return m_angleDelta; } QPointF KirigamiWheelEvent::pixelDelta() const { return m_pixelDelta; } int KirigamiWheelEvent::buttons() const { return m_buttons; } int KirigamiWheelEvent::modifiers() const { return m_modifiers; } bool KirigamiWheelEvent::inverted() const { return m_inverted; } bool KirigamiWheelEvent::isAccepted() { return m_accepted; } void KirigamiWheelEvent::setAccepted(bool accepted) { m_accepted = accepted; } /////////////////////////////// WheelHandler::WheelHandler(QObject *parent) : QObject(parent) { } WheelHandler::~WheelHandler() { } QQuickItem *WheelHandler::target() const { return m_target; } void WheelHandler::setTarget(QQuickItem *target) { if (m_target == target) { return; } if (m_target) { GlobalWheelFilter::self()->removeItemHandlerAssociation(m_target, this); } m_target = target; GlobalWheelFilter::self()->setItemHandlerAssociation(target, this); emit targetChanged(); } #include "moc_wheelhandler.cpp" diff --git a/src/wheelhandler.h b/src/wheelhandler.h index b811784e..8e81f51e 100644 --- a/src/wheelhandler.h +++ b/src/wheelhandler.h @@ -1,226 +1,213 @@ /* -* Copyright (C) 2019 by Marco Martin -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU Library General Public License as -* published by the Free Software Foundation; either version 2, or -* (at your option) any later version. -* -* This program 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 Library General Public License for more details -* -* You should have received a copy of the GNU Library General Public -* License along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ + * SPDX-FileCopyrightText: 2019 Marco Martin + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ #pragma once #include #include #include #include class QWheelEvent; class WheelHandler; /** * Describes the mouse wheel event */ class KirigamiWheelEvent : public QObject { Q_OBJECT /** * x: real * * X coordinate of the mouse pointer */ Q_PROPERTY(qreal x READ x CONSTANT) /** * y: real * * Y coordinate of the mouse pointer */ Q_PROPERTY(qreal y READ y CONSTANT) /** * angleDelta: point * * The distance the wheel is rotated in degrees. * The x and y coordinates indicate the horizontal and vertical wheels respectively. * A positive value indicates it was rotated up/right, negative, bottom/left * This value is more likely to be set in traditional mice. */ Q_PROPERTY(QPointF angleDelta READ angleDelta CONSTANT) /** * pixelDelta: point * * provides the delta in screen pixels available on high resolution trackpads */ Q_PROPERTY(QPointF pixelDelta READ pixelDelta CONSTANT) /** * buttons: int * * it contains an OR combination of the buttons that were pressed during the wheel, they can be: * Qt.LeftButton, Qt.MiddleButton, Qt.RightButton */ Q_PROPERTY(int buttons READ buttons CONSTANT) /** * modifiers: int * * Keyboard mobifiers that were pressed during the wheel event, such as: * Qt.NoModifier (default, no modifiers) * Qt.ControlModifier * Qt.ShiftModifier * ... */ Q_PROPERTY(int modifiers READ modifiers CONSTANT) /** * inverted: bool * * Whether the delta values are inverted * On some platformsthe returned delta are inverted, so positive values would mean bottom/left */ Q_PROPERTY(bool inverted READ inverted CONSTANT) /** * accepted: bool * * If set, the event shouldn't be managed anymore, * for instance it can be used to block the handler to manage the scroll of a view on some scenarions * @code * // This handler handles automatically the scroll of - * // flickableItem, unless Ctrl is pressed, in this case the + * // flickableItem, unless Ctrl is pressed, in this case the * // app has custom code to handle Ctrl+wheel zooming * Kirigami.WheelHandler { * target: flickableItem * blockTargetWheel: true * scrollFlickableTarget: true * onWheel: { * if (wheel.modifiers & Qt.ControlModifier) { * wheel.accepted = true; * // Handle scaling of the view * } * } * } * @endcode - * + * */ Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted) public: KirigamiWheelEvent(QObject *parent = nullptr); ~KirigamiWheelEvent(); void initializeFromEvent(QWheelEvent *event); qreal x() const; qreal y() const; QPointF angleDelta() const; QPointF pixelDelta() const; int buttons() const; int modifiers() const; bool inverted() const; bool isAccepted(); void setAccepted(bool accepted); private: qreal m_x = 0; qreal m_y = 0; QPointF m_angleDelta; QPointF m_pixelDelta; Qt::MouseButtons m_buttons = Qt::NoButton; Qt::KeyboardModifiers m_modifiers = Qt::NoModifier; bool m_inverted = false; bool m_accepted = false; }; class GlobalWheelFilter : public QObject { Q_OBJECT public: GlobalWheelFilter(QObject *parent = nullptr); ~GlobalWheelFilter(); static GlobalWheelFilter *self(); void setItemHandlerAssociation(QQuickItem *item, WheelHandler *handler); void removeItemHandlerAssociation(QQuickItem *item, WheelHandler *handler); protected: bool eventFilter(QObject *watched, QEvent *event) override; private: void manageWheel(QQuickItem *target, QWheelEvent *wheel); QMultiHash m_handlersForItem; KirigamiWheelEvent m_wheelEvent; }; /** * This class intercepts the mouse wheel events of its target, and gives them to the user code as a signal, which can be used for custom mouse wheel management code. * The handler can block completely the wheel events from its target, and if it's a Flickable, it can automatically handle scrolling on it */ class WheelHandler : public QObject { Q_OBJECT /** * target: Item * * The target we want to manage wheel events. * We will receive wheel() signals every time the user moves * the mouse wheel (or scrolls with the touchpad) on top * of that item. */ Q_PROPERTY(QQuickItem *target READ target WRITE setTarget NOTIFY targetChanged) /** * blockTargetWheel: bool * * If true, the target won't receive any wheel event at all (default true) */ Q_PROPERTY(bool blockTargetWheel MEMBER m_blockTargetWheel NOTIFY blockTargetWheelChanged) /** * scrollFlickableTarget: bool * If this property is true and the target is a Flickable, wheel events will cause the Flickable to scroll (default true) */ Q_PROPERTY(bool scrollFlickableTarget MEMBER m_scrollFlickableTarget NOTIFY scrollFlickableTargetChanged) public: explicit WheelHandler(QObject *parent = nullptr); ~WheelHandler() override; QQuickItem *target() const; void setTarget(QQuickItem *target); Q_SIGNALS: void targetChanged(); void blockTargetWheelChanged(); void scrollFlickableTargetChanged(); void wheel(KirigamiWheelEvent *wheel); private: QPointer m_target; bool m_blockTargetWheel = true; bool m_scrollFlickableTarget = true; KirigamiWheelEvent m_wheelEvent; friend class GlobalWheelFilter; }; diff --git a/tests/KeyboardListTest.qml b/tests/KeyboardListTest.qml index b3732c96..4ddad42f 100644 --- a/tests/KeyboardListTest.qml +++ b/tests/KeyboardListTest.qml @@ -1,45 +1,32 @@ /* - * Copyright 2016 Aleix Pol Gonzalez - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Aleix Pol Gonzalez + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.7 import QtQuick.Controls 2.0 import org.kde.kirigami 2.4 as Kirigami Kirigami.ApplicationWindow { id: main Component { id: keyPage Kirigami.ScrollablePage { ListView { model: 10 delegate: Rectangle { width: 100 height: 30 color: ListView.isCurrentItem ? "red" : "white" } } } } Component.onCompleted: { main.pageStack.push(keyPage) } } diff --git a/tests/KeyboardTest.qml b/tests/KeyboardTest.qml index 558d6aa3..434528c8 100644 --- a/tests/KeyboardTest.qml +++ b/tests/KeyboardTest.qml @@ -1,56 +1,43 @@ /* - * Copyright 2016 Aleix Pol Gonzalez + * SPDX-FileCopyrightText: 2016 Aleix Pol Gonzalez * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.7 import QtQuick.Controls 2.0 import org.kde.kirigami 2.4 as Kirigami Kirigami.ApplicationWindow { id: main Component { id: keyPage Kirigami.Page { readonly property alias lastKey: see.text Text { id: see anchors.centerIn: parent color: parent.focus ? "black" : "red" } Keys.onPressed: { if (event.text) see.text = event.text else see.text = event.key } Keys.onEnterPressed: main.showPassiveNotification("page!") } } header: Text { text: "focus:" + activeFocusItem + " current: " + main.pageStack.currentIndex } Component.onCompleted: { main.pageStack.push(keyPage) main.pageStack.push(keyPage) } } diff --git a/tests/WheelHandler.qml b/tests/WheelHandler.qml index efa513bf..b2b8b204 100644 --- a/tests/WheelHandler.qml +++ b/tests/WheelHandler.qml @@ -1,72 +1,59 @@ /* - * Copyright 2019 Marco Martin + * SPDX-FileCopyrightText: 2019 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.7 import QtQuick.Controls 2.3 as Controls import org.kde.kirigami 2.9 as Kirigami Controls.ScrollView { id: root width: Kirigami.Units.gridUnit * 30 height: Kirigami.Units.gridUnit * 40 readonly property Flickable flickable: contentItem Kirigami.WheelHandler { target: flickable onWheel: { if (wheel.modifiers & Qt.ControlModifier) { wheel.accepted = true; var factor = 1.2; // Shrink if (wheel.angleDelta.y < 0 || wheel.pixelDelta.y < 0) { factor = 0.83 } contents.zoom = Math.max(contents.zoom * factor, 1); flickable.resizeContent(contents.implicitWidth , contents.implicitHeight, contents.mapFromItem(flickable, wheel.x, wheel.y)); flickable.contentWidth = contents.implicitWidth; flickable.contentHeight = contents.implicitHeight; - + flickable.returnToBounds(); } } } Item { id: contents property real zoom: 1 implicitWidth: root.width * zoom implicitHeight: Kirigami.Units.gridUnit * 60 * zoom Rectangle { anchors { fill: parent margins: Kirigami.Units.gridUnit * 2 } color: "red" Controls.Label { anchors.centerIn: parent text: contents.width+"x"+contents.height } } } } diff --git a/tests/actionsMenu.qml b/tests/actionsMenu.qml index 962a4623..7c10404b 100644 --- a/tests/actionsMenu.qml +++ b/tests/actionsMenu.qml @@ -1,104 +1,91 @@ /* - * Copyright 2016 Aleix Pol Gonzalez - * Copyright 2016 Marco Martin + * SPDX-FileCopyrightText: 2016 Aleix Pol Gonzalez + * SPDX-FileCopyrightText: 2016 Marco Martin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.7 import QtQuick.Controls 2.3 as QQC2 import org.kde.kirigami 2.4 as Kirigami Kirigami.ApplicationWindow { id: main header: Kirigami.ToolBarApplicationHeader {} pageStack.initialPage: Kirigami.Page { QQC2.Button { text: "button" onClicked: menu.popup() QQC2.Menu { id: menu QQC2.MenuItem { text: "xxx" } QQC2.MenuItem { text: "xxx" } QQC2.Menu { title: "yyy" QQC2.MenuItem { text: "yyy" } QQC2.MenuItem { text: "yyy" } } } } title: "aaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaa" actions { main: Kirigami.Action { icon.name: "kate"; text: "BonDia" } left : Kirigami.Action { icon.name: "kate"; text: "BonDia" } right: Kirigami.Action { icon.name: "kate"; text: "BonDia" } } QQC2.ActionGroup { id: group } contextualActions: [ Kirigami.Action { text: "submenus" icon.name: "kalgebra" Kirigami.Action { text: "xxx"; onTriggered: console.log("xxx") } Kirigami.Action { text: "xxx"; onTriggered: console.log("xxx") } Kirigami.Action { text: "xxx"; onTriggered: console.log("xxx") } Kirigami.Action { text: "yyy" Kirigami.Action { text: "yyy" } Kirigami.Action { text: "yyy" } Kirigami.Action { text: "yyy" } Kirigami.Action { text: "yyy" } } }, Kirigami.Action { id: optionsAction text: "Options" icon.name: "kate" Kirigami.Action { QQC2.ActionGroup.group: group text: "A" checkable: true checked: true } Kirigami.Action { QQC2.ActionGroup.group: group text: "B" checkable: true } Kirigami.Action { QQC2.ActionGroup.group: group text: "C" checkable: true } }, Kirigami.Action { text: "stuffing..." }, Kirigami.Action { text: "stuffing..." }, Kirigami.Action { text: "stuffing..." }, Kirigami.Action { text: "stuffing..." }, Kirigami.Action { text: "stuffing..." } ] } } diff --git a/tests/cardsList.qml b/tests/cardsList.qml index 188ee589..0836a02f 100644 --- a/tests/cardsList.qml +++ b/tests/cardsList.qml @@ -1,44 +1,31 @@ /* - * Copyright 2018 Aleix Pol Gonzalez + * SPDX-FileCopyrightText: 2018 Aleix Pol Gonzalez * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.7 import QtQuick.Controls 2.0 import org.kde.kirigami 2.5 as Kirigami Kirigami.ApplicationWindow { Component { id: delegateComponent Kirigami.Card { contentItem: Label { text: ourlist.prefix + index } } } pageStack.initialPage: Kirigami.ScrollablePage { Kirigami.CardsListView { id: ourlist property string prefix: "ciao " delegate: delegateComponent model: 100 } } } diff --git a/tests/swipeListItemTest.qml b/tests/swipeListItemTest.qml index f9b4681c..a653108f 100644 --- a/tests/swipeListItemTest.qml +++ b/tests/swipeListItemTest.qml @@ -1,51 +1,38 @@ /* - * Copyright 2016 Aleix Pol Gonzalez + * SPDX-FileCopyrightText: 2016 Aleix Pol Gonzalez * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2, or - * (at your option) any later version. - * - * This program 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * SPDX-License-Identifier: LGPL-2.0-or-later */ import QtQuick 2.7 import QtQuick.Controls 2.0 import org.kde.kirigami 2.4 as Kirigami Kirigami.ApplicationWindow { id: main Component { id: keyPage Kirigami.ScrollablePage { ListView { model: 25 delegate: Kirigami.SwipeListItem { supportsMouseEvents: false actions: [ Kirigami.Action { iconName: "go-up" } ] Label { elide: Text.ElideRight text: "big banana big banana big banana big banana big banana big banana big banana big banana big banana big banana big banana big banana big banana big banana big banana big banana big banana big banana big banana big banana " } } } } } Component.onCompleted: { main.pageStack.push(keyPage) } }