pax_global_header00006660000000000000000000000064144654227410014523gustar00rootroot0000000000000052 comment=71f1fe577b5b665bc19c7ab3736ffe8387fa52ee QGnomePlatform-0.9.2/000077500000000000000000000000001446542274100144265ustar00rootroot00000000000000QGnomePlatform-0.9.2/.clang-format000066400000000000000000000062631446542274100170100ustar00rootroot00000000000000# SPDX-License-Identifier: MIT # # Copyright (C) 2019 Christoph Cullmann # Copyright (C) 2019 Gernot Gebhard # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # Style for C++ Language: Cpp # base is WebKit coding style: https://webkit.org/code-style-guidelines/ # below are only things set that diverge from this style! BasedOnStyle: WebKit # enforce C++11 (e.g. for std::vector> Standard: Cpp11 # 4 spaces indent TabWidth: 4 # 2 * 80 wide lines ColumnLimit: 160 # sort includes inside line separated groups SortIncludes: true # break before braces on function, namespace and class definitions. BreakBeforeBraces: Linux # CrlInstruction *a; PointerAlignment: Right # horizontally aligns arguments after an open bracket. AlignAfterOpenBracket: Align # don't move all parameters to new line AllowAllParametersOfDeclarationOnNextLine: false # no single line functions AllowShortFunctionsOnASingleLine: None # always break before you encounter multi line strings AlwaysBreakBeforeMultilineStrings: true # don't move arguments to own lines if they are not all on the same BinPackArguments: false # don't move parameters to own lines if they are not all on the same BinPackParameters: false # In case we have an if statement with multiple lines the operator should be at the beginning of the line # but we do not want to break assignments BreakBeforeBinaryOperators: NonAssignment # format C++11 braced lists like function calls Cpp11BracedListStyle: true # do not put a space before C++11 braced lists SpaceBeforeCpp11BracedList: false # remove empty lines KeepEmptyLinesAtTheStartOfBlocks: false # no namespace indentation to keep indent level low NamespaceIndentation: None # we use template< without space. SpaceAfterTemplateKeyword: false # Always break after template declaration AlwaysBreakTemplateDeclarations: true # macros for which the opening brace stays attached. ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH, forever, Q_FOREVER, QBENCHMARK, QBENCHMARK_ONCE ] # keep lambda formatting multi-line if not empty AllowShortLambdasOnASingleLine: Empty # We do not want clang-format to put all arguments on a new line AllowAllArgumentsOnNextLine: false QGnomePlatform-0.9.2/.github/000077500000000000000000000000001446542274100157665ustar00rootroot00000000000000QGnomePlatform-0.9.2/.github/workflows/000077500000000000000000000000001446542274100200235ustar00rootroot00000000000000QGnomePlatform-0.9.2/.github/workflows/build.yaml000066400000000000000000000033601446542274100220100ustar00rootroot00000000000000name: Automatic build on: push: branches: - '**' pull_request: branches: - '**' release: types: [ created ] env: BUILD_TYPE: Release jobs: Linux_Qt5: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Install base dependencies run: | sudo apt update sudo apt install cmake make pkg-config libx11-dev xcb libx11-xcb-dev libxkbcommon-dev libgtk-3-dev sassc - name: Install Qt uses: jurplel/install-qt-action@v3 with: version: 5.15.2 - name: Install Adwaita-qt run: | git clone https://github.com/FedoraQt/adwaita-qt.git adwaita-qt cd adwaita-qt mkdir build cd build cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DUSE_QT6=OFF make -j2 sudo make install - name: Build run: | mkdir build cd build cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DUSE_QT6=OFF make -j2 Linux_Qt6: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Install base dependencies run: | sudo apt update sudo apt install cmake make pkg-config libx11-dev xcb libx11-xcb-dev libxkbcommon-dev libgtk-3-dev sassc - name: Install Qt uses: jurplel/install-qt-action@v3 with: version: 6.5.0 - name: Install Adwaita-qt run: | git clone https://github.com/FedoraQt/adwaita-qt.git adwaita-qt cd adwaita-qt mkdir build cd build cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DUSE_QT6=ON make -j2 sudo make install - name: Build run: | mkdir build cd build cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DUSE_QT6=ON make -j2 QGnomePlatform-0.9.2/.github/workflows/clang-format-check.yaml000066400000000000000000000005461446542274100243410ustar00rootroot00000000000000name: clang-format Check on: [pull_request] jobs: formatting-check: name: Formatting Check runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Run clang-format style check for C/C++/Protobuf programs. uses: jidicula/clang-format-action@v4.5.0 with: clang-format-version: '13' check-path: 'src' QGnomePlatform-0.9.2/.gitignore000066400000000000000000000004441446542274100164200ustar00rootroot00000000000000# C++ objects and libs *.slo *.lo *.o *.a *.la *.lai *.so *.dll *.dylib # Qt-es /.qmake.cache /.qmake.stash *.pro.user *.pro.user.* *.qbs.user *.qbs.user.* *.moc moc_*.cpp qrc_*.cpp ui_*.h Makefile* *-build-* # QtCreator *.autosave #QtCtreator Qml *.qmlproject.user *.qmlproject.user.* QGnomePlatform-0.9.2/CMakeLists.txt000066400000000000000000000064311446542274100171720ustar00rootroot00000000000000project(QGnomePlatform) cmake_minimum_required(VERSION 3.0) set(QGNOMEPLATFORM_VERSION "0.9.2") option(USE_QT6 "Use Qt6 instead of Qt5" OFF) if (USE_QT6) set(QT_MIN_VERSION "6.2.0") set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(LIBQGNOMEPLATFORM_SUFFIX "6") else() set(QT_MIN_VERSION "5.15.2") set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(LIBQGNOMEPLATFORM_SUFFIX "") endif() set(CMAKE_AUTOMOC ON) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules" ${CMAKE_MODULE_PATH}) include(GNUInstallDirs) include(FeatureSummary) if (USE_QT6) find_package(QT NAMES Qt6 COMPONENTS Core DBus Gui Widgets REQUIRED) else() find_package(QT NAMES Qt5 COMPONENTS Core DBus Gui Widgets REQUIRED) endif() find_package(Qt${QT_VERSION_MAJOR} ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Core DBus Gui Widgets QuickControls2 ) find_package(Qt${QT_VERSION_MAJOR}Gui ${QT_MIN_VERSION} CONFIG REQUIRED Private) if (NOT USE_QT6) find_package(Qt${QT_VERSION_MAJOR}ThemeSupport REQUIRED) endif() find_package(GSettingSchemas REQUIRED) if (USE_QT6) find_package(AdwaitaQt6 "1.4.2" REQUIRED) else() find_package(AdwaitaQt "1.4.2" REQUIRED) endif() if (NOT USE_QT6) find_package(KF5QQC2DesktopStyle) set_package_properties(KF5QQC2DesktopStyle PROPERTIES DESCRIPTION "Styling for QtQuick Controls 2 applications" TYPE RUNTIME) else() # TODO, the desktop style for Qt6 is not yet released endif() find_package(PkgConfig REQUIRED) pkg_check_modules(GTK+3 REQUIRED IMPORTED_TARGET gtk+-3.0) # NOTE: there is no reason to disable any of the following options, but # it is useful when building Flatpak extensions if (DISABLE_DECORATION_SUPPORT) message(STATUS "Disabling Qt Wayland decoration support") else() find_package(Qt${QT_VERSION_MAJOR}WaylandClient ${QT_MIN_VERSION} CONFIG REQUIRED) set_package_properties(Qt${QT_VERSION_MAJOR}WaylandClient PROPERTIES DESCRIPTION "Qt Wayland decoration support" PURPOSE "Required for QGnomePlatform decoration plugin" TYPE REQUIRED ) # NOTE: I don't know how to do this only in case of qt_config(xkbcommon). # We would miss an include in QWaylandDisplay header file. if (NOT USE_QT6) find_package(Qt${QT_VERSION_MAJOR}XkbCommonSupport ${QT_MIN_VERSION}) endif() if (DECORATION_SHADOWS_SUPPORT OR USE_QT6) message(STATUS "Enabling Qt Wayland decoration shadows support") message(STATUS "NOTE: This support requires changes in Qt Wayland from Qt 6.2") add_definitions(-DDECORATION_SHADOWS_SUPPORT) endif() endif() if (DISABLE_THEME_SUPPORT) message(STATUS "Disabling platform theme support") endif() if (NOT QT_PLUGINS_DIR) if (NOT USE_QT6) get_target_property(REAL_QMAKE_EXECUTABLE ${Qt5Core_QMAKE_EXECUTABLE} IMPORTED_LOCATION) execute_process(COMMAND "${REAL_QMAKE_EXECUTABLE}" -query QT_INSTALL_PLUGINS OUTPUT_VARIABLE QT_PLUGINS_DIR ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) else() set(QT_PLUGINS_DIR ${QT6_INSTALL_PLUGINS}) endif() endif() add_subdirectory(src) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) QGnomePlatform-0.9.2/LICENSE000066400000000000000000000635361446542274100154500ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. (This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.) Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. {description} Copyright (C) {year} {fullname} This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. {signature of Ty Coon}, 1 April 1990 Ty Coon, President of Vice That's all there is to it! QGnomePlatform-0.9.2/README.md000066400000000000000000000025771446542274100157200ustar00rootroot00000000000000QGnomePlatform ========== QGnomePlatform is a Qt Platform Theme designed to use as many of the GNOME settings as possible in unmodified Qt applications. It allows Qt applications to fit into the environment as well as possible. ## How to compile This library uses private Qt headers and will likely not be forward nor backward compatible. This library will have to be recompiled with every Qt update. ``` mkdir build cd build cmake [OPTIONS] [-DUSE_QT6=true] .. make && make install ``` ## Usage This library is used automatically in Gtk based desktops such as Gnome, Cinnamon or Xfce. This platform theme can also be used by setting the QT_QPA_PLATFORMTHEME environment variable to "gnome". For example, put the following command in `.bashrc`: ``` export QT_QPA_PLATFORMTHEME='gnome' ``` ## License Most code is under [LGPL 2.1](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html) with the "or any later version" clause. New code should be contributed under this license. This project also incorporates some code from the Qt Project. Because of that the so-called combined work is licensed under [LGPL 3.0-only](https://www.gnu.org/licenses/lgpl-3.0), [GPL 2.0](https://www.gnu.org/licenses/old-licenses/gpl-2.0), [GPL 3.0](https://www.gnu.org/licenses/gpl-3.0), or any later GPL version approved by the [KDE Free Qt Foundation](https://kde.org/community/whatiskde/kdefreeqtfoundation/). QGnomePlatform-0.9.2/cmake/000077500000000000000000000000001446542274100155065ustar00rootroot00000000000000QGnomePlatform-0.9.2/cmake/modules/000077500000000000000000000000001446542274100171565ustar00rootroot00000000000000QGnomePlatform-0.9.2/cmake/modules/FindGSettingSchemas.cmake000066400000000000000000000010401446542274100240040ustar00rootroot00000000000000find_package(PkgConfig) pkg_check_modules(PC_GLIB2 REQUIRED glib-2.0) find_path(GLIB_SCHEMAS_DIR org.gnome.desktop.interface.gschema.xml HINTS ${PC_GLIB2_PREFIX}/share PATH_SUFFIXES glib-2.0/schemas) if (GLIB_SCHEMAS_DIR) set(GSettingSchemas_FOUND true) else() set(GSettingSchemas_FOUND false) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GSettingSchemas FOUND_VAR GSettingSchemas_FOUND REQUIRED_VARS GSettingSchemas_FOUND ) mark_as_advanced(GSettingSchemas_FOUND) QGnomePlatform-0.9.2/src/000077500000000000000000000000001446542274100152155ustar00rootroot00000000000000QGnomePlatform-0.9.2/src/CMakeLists.txt000066400000000000000000000003131446542274100177520ustar00rootroot00000000000000add_subdirectory(color-schemes) add_subdirectory(common) if (NOT DISABLE_DECORATION_SUPPORT) add_subdirectory(decoration) endif() if (NOT DISABLE_THEME_SUPPORT) add_subdirectory(theme) endif() QGnomePlatform-0.9.2/src/color-schemes/000077500000000000000000000000001446542274100177605ustar00rootroot00000000000000QGnomePlatform-0.9.2/src/color-schemes/Adwaita.colors000066400000000000000000000053241446542274100225610ustar00rootroot00000000000000[ColorEffects:Disabled] Color=56,56,56 ColorAmount=0 ColorEffect=0 ContrastAmount=0.65 ContrastEffect=1 IntensityAmount=0.1 IntensityEffect=2 [ColorEffects:Inactive] ChangeSelectionColor=true Color=112,111,110 ColorAmount=0.025 ColorEffect=2 ContrastAmount=0.1 ContrastEffect=2 Enable=false IntensityAmount=0 IntensityEffect=0 [Colors:Button] BackgroundAlternate=241,239,238 BackgroundNormal=241,239,238 DecorationFocus=61,174,233 DecorationHover=147,206,233 ForegroundActive=61,174,233 ForegroundInactive=127,140,141 ForegroundLink=41,128,185 ForegroundNegative=218,68,83 ForegroundNeutral=198,92,0 ForegroundNormal=55,61,63 ForegroundPositive=39,174,96 ForegroundVisited=127,140,141 [Colors:Complementary] BackgroundAlternate=59,64,69 BackgroundNormal=49,54,59 DecorationFocus=30,146,255 DecorationHover=61,174,230 ForegroundActive=147,206,233 ForegroundInactive=175,176,179 ForegroundLink=61,174,230 ForegroundNegative=231,76,60 ForegroundNeutral=253,188,75 ForegroundNormal=239,240,241 ForegroundPositive=46,204,113 ForegroundVisited=61,174,230 [Colors:Selection] BackgroundAlternate=53,132,228 BackgroundNormal=53,132,228 DecorationFocus=61,174,233 DecorationHover=147,206,233 ForegroundActive=252,252,252 ForegroundInactive=239,240,241 ForegroundLink=253,188,75 ForegroundNegative=176,55,69 ForegroundNeutral=246,116,0 ForegroundNormal=252,253,255 ForegroundPositive=23,104,57 ForegroundVisited=189,195,199 [Colors:Tooltip] BackgroundAlternate=0,0,0 BackgroundNormal=0,0,0 DecorationFocus=61,174,233 DecorationHover=147,206,233 ForegroundActive=61,174,233 ForegroundInactive=189,195,199 ForegroundLink=41,128,185 ForegroundNegative=218,68,83 ForegroundNeutral=246,116,0 ForegroundNormal=255,255,255 ForegroundPositive=39,174,96 ForegroundVisited=127,140,141 [Colors:View] BackgroundAlternate=255,255,255 BackgroundNormal=255,255,255 DecorationFocus=61,174,233 DecorationHover=147,206,233 ForegroundActive=61,174,233 ForegroundInactive=25,25,25 ForegroundLink=41,128,185 ForegroundNegative=218,68,83 ForegroundNeutral=246,116,0 ForegroundNormal=25,25,25 ForegroundPositive=39,174,96 ForegroundVisited=127,140,141 [Colors:Window] BackgroundAlternate=246,245,244 BackgroundNormal=246,245,244 DecorationFocus=61,174,233 DecorationHover=147,206,233 ForegroundActive=61,174,233 ForegroundInactive=127,140,141 ForegroundLink=41,128,185 ForegroundNegative=218,68,83 ForegroundNeutral=246,116,0 ForegroundNormal=54,59,61 ForegroundPositive=39,174,96 ForegroundVisited=127,140,141 [General] ColorScheme=BreezeClassic Name=Adwaita TitlebarIsAccentColored=true shadeSortColumn=true [KDE] contrast=4 [WM] activeBackground=222,219,215 activeBlend=252,252,252 activeForeground=54,59,61 inactiveBackground=239,240,241 inactiveBlend=75,71,67 inactiveForeground=189,195,199 QGnomePlatform-0.9.2/src/color-schemes/AdwaitaDark.colors000066400000000000000000000065721446542274100233710ustar00rootroot00000000000000[ColorEffects:Disabled] Color=56,56,56 ColorAmount=0 ColorEffect=0 ContrastAmount=0.65 ContrastEffect=1 IntensityAmount=0.1 IntensityEffect=2 [ColorEffects:Inactive] ChangeSelectionColor=true Color=112,111,110 ColorAmount=0.025 ColorEffect=2 ContrastAmount=0.1 ContrastEffect=2 Enable=false IntensityAmount=0 IntensityEffect=0 [Colors:Button] BackgroundAlternate=52,52,52 BackgroundNormal=52,52,52 DecorationFocus=61,174,233 DecorationHover=61,174,233 ForegroundActive=61,174,233 ForegroundInactive=161,169,177 ForegroundLink=29,153,243 ForegroundNegative=218,68,83 ForegroundNeutral=246,116,0 ForegroundNormal=238,238,236 ForegroundPositive=39,174,96 ForegroundVisited=155,89,182 [Colors:Complementary] BackgroundAlternate=30,87,116 BackgroundNormal=42,46,50 DecorationFocus=61,174,233 DecorationHover=61,174,233 ForegroundActive=61,174,233 ForegroundInactive=161,169,177 ForegroundLink=29,153,243 ForegroundNegative=218,68,83 ForegroundNeutral=246,116,0 ForegroundNormal=252,252,252 ForegroundPositive=39,174,96 ForegroundVisited=155,89,182 [Colors:Header] BackgroundAlternate=49,54,59 BackgroundNormal=49,54,59 DecorationFocus=61,174,233 DecorationHover=61,174,233 ForegroundActive=61,174,233 ForegroundInactive=161,169,177 ForegroundLink=29,153,243 ForegroundNegative=218,68,83 ForegroundNeutral=246,116,0 ForegroundNormal=252,252,252 ForegroundPositive=39,174,96 ForegroundVisited=155,89,182 [Colors:Header][Inactive] BackgroundAlternate=49,54,59 BackgroundNormal=42,46,50 DecorationFocus=61,174,233 DecorationHover=61,174,233 ForegroundActive=61,174,233 ForegroundInactive=161,169,177 ForegroundLink=29,153,243 ForegroundNegative=218,68,83 ForegroundNeutral=246,116,0 ForegroundNormal=252,252,252 ForegroundPositive=39,174,96 ForegroundVisited=155,89,182 [Colors:Selection] BackgroundAlternate=21,83,158 BackgroundNormal=21,83,158 DecorationFocus=61,174,233 DecorationHover=61,174,233 ForegroundActive=252,252,252 ForegroundInactive=161,169,177 ForegroundLink=253,188,75 ForegroundNegative=176,55,69 ForegroundNeutral=198,92,0 ForegroundNormal=252,253,254 ForegroundPositive=23,104,57 ForegroundVisited=155,89,182 [Colors:Tooltip] BackgroundAlternate=255,255,255 BackgroundNormal=255,255,255 DecorationFocus=61,174,233 DecorationHover=61,174,233 ForegroundActive=61,174,233 ForegroundInactive=161,169,177 ForegroundLink=29,153,243 ForegroundNegative=218,68,83 ForegroundNeutral=246,116,0 ForegroundNormal=0,0,0 ForegroundPositive=39,174,96 ForegroundVisited=155,89,182 [Colors:View] BackgroundAlternate=45,45,45 BackgroundNormal=45,45,45 DecorationFocus=61,174,233 DecorationHover=61,174,233 ForegroundActive=61,174,233 ForegroundInactive=161,169,177 ForegroundLink=29,153,243 ForegroundNegative=218,68,83 ForegroundNeutral=246,116,0 ForegroundNormal=255,255,255 ForegroundPositive=39,174,96 ForegroundVisited=155,89,182 [Colors:Window] BackgroundAlternate=53,53,53 BackgroundNormal=53,53,53 DecorationFocus=61,174,233 DecorationHover=61,174,233 ForegroundActive=61,174,233 ForegroundInactive=161,169,177 ForegroundLink=29,153,243 ForegroundNegative=218,68,83 ForegroundNeutral=246,116,0 ForegroundNormal=225,225,223 ForegroundPositive=39,174,96 ForegroundVisited=155,89,182 [General] ColorScheme=BreezeDark Name=AdwaitaDark shadeSortColumn=true [KDE] contrast=4 [WM] activeBackground=41,41,41 activeBlend=252,252,252 activeForeground=225,225,223 inactiveBackground=42,46,50 inactiveBlend=161,169,177 inactiveForeground=161,169,177 QGnomePlatform-0.9.2/src/color-schemes/AdwaitaHighcontrast.colors000066400000000000000000000053351446542274100251410ustar00rootroot00000000000000[ColorEffects:Disabled] Color=56,56,56 ColorAmount=0 ColorEffect=0 ContrastAmount=0.65 ContrastEffect=1 IntensityAmount=0.1 IntensityEffect=2 [ColorEffects:Inactive] ChangeSelectionColor=true Color=112,111,110 ColorAmount=0.025 ColorEffect=2 ContrastAmount=0.1 ContrastEffect=2 Enable=false IntensityAmount=0 IntensityEffect=0 [Colors:Button] BackgroundAlternate=248,246,245 BackgroundNormal=248,246,245 DecorationFocus=61,174,233 DecorationHover=147,206,233 ForegroundActive=61,174,233 ForegroundInactive=127,140,141 ForegroundLink=41,128,185 ForegroundNegative=218,68,83 ForegroundNeutral=198,92,0 ForegroundNormal=47,52,54 ForegroundPositive=39,174,96 ForegroundVisited=127,140,141 [Colors:Complementary] BackgroundAlternate=59,64,69 BackgroundNormal=49,54,59 DecorationFocus=30,146,255 DecorationHover=61,174,230 ForegroundActive=147,206,233 ForegroundInactive=175,176,179 ForegroundLink=61,174,230 ForegroundNegative=231,76,60 ForegroundNeutral=253,188,75 ForegroundNormal=239,240,241 ForegroundPositive=46,204,113 ForegroundVisited=61,174,230 [Colors:Selection] BackgroundAlternate=53,132,228 BackgroundNormal=53,132,228 DecorationFocus=61,174,233 DecorationHover=147,206,233 ForegroundActive=252,252,252 ForegroundInactive=239,240,241 ForegroundLink=253,188,75 ForegroundNegative=176,55,69 ForegroundNeutral=246,116,0 ForegroundNormal=252,253,255 ForegroundPositive=23,104,57 ForegroundVisited=189,195,199 [Colors:Tooltip] BackgroundAlternate=0,0,0 BackgroundNormal=0,0,0 DecorationFocus=61,174,233 DecorationHover=147,206,233 ForegroundActive=61,174,233 ForegroundInactive=189,195,199 ForegroundLink=41,128,185 ForegroundNegative=218,68,83 ForegroundNeutral=246,116,0 ForegroundNormal=255,255,255 ForegroundPositive=39,174,96 ForegroundVisited=127,140,141 [Colors:View] BackgroundAlternate=255,255,255 BackgroundNormal=255,255,255 DecorationFocus=61,174,233 DecorationHover=147,206,233 ForegroundActive=61,174,233 ForegroundInactive=25,25,25 ForegroundLink=41,128,185 ForegroundNegative=218,68,83 ForegroundNeutral=246,116,0 ForegroundNormal=3,3,3 ForegroundPositive=39,174,96 ForegroundVisited=127,140,141 [Colors:Window] BackgroundAlternate=253,253,252 BackgroundNormal=253,253,252 DecorationFocus=61,174,233 DecorationHover=147,206,233 ForegroundActive=61,174,233 ForegroundInactive=127,140,141 ForegroundLink=41,128,185 ForegroundNegative=218,68,83 ForegroundNeutral=246,116,0 ForegroundNormal=42,47,49 ForegroundPositive=39,174,96 ForegroundVisited=127,140,141 [General] ColorScheme=BreezeClassic Name=AdwaitaHighcontrast TitlebarIsAccentColored=true shadeSortColumn=true [KDE] contrast=4 [WM] activeBackground=228,226,223 activeBlend=252,252,252 activeForeground=42,47,49 inactiveBackground=239,240,241 inactiveBlend=75,71,67 inactiveForeground=189,195,199 QGnomePlatform-0.9.2/src/color-schemes/AdwaitaHighcontrastInverse.colors000066400000000000000000000066111446542274100264730ustar00rootroot00000000000000[ColorEffects:Disabled] Color=56,56,56 ColorAmount=0 ColorEffect=0 ContrastAmount=0.65 ContrastEffect=1 IntensityAmount=0.1 IntensityEffect=2 [ColorEffects:Inactive] ChangeSelectionColor=true Color=112,111,110 ColorAmount=0.025 ColorEffect=2 ContrastAmount=0.1 ContrastEffect=2 Enable=false IntensityAmount=0 IntensityEffect=0 [Colors:Button] BackgroundAlternate=52,52,52 BackgroundNormal=52,52,52 DecorationFocus=61,174,233 DecorationHover=61,174,233 ForegroundActive=61,174,233 ForegroundInactive=161,169,177 ForegroundLink=29,153,243 ForegroundNegative=218,68,83 ForegroundNeutral=246,116,0 ForegroundNormal=238,238,236 ForegroundPositive=39,174,96 ForegroundVisited=155,89,182 [Colors:Complementary] BackgroundAlternate=30,87,116 BackgroundNormal=42,46,50 DecorationFocus=61,174,233 DecorationHover=61,174,233 ForegroundActive=61,174,233 ForegroundInactive=161,169,177 ForegroundLink=29,153,243 ForegroundNegative=218,68,83 ForegroundNeutral=246,116,0 ForegroundNormal=252,252,252 ForegroundPositive=39,174,96 ForegroundVisited=155,89,182 [Colors:Header] BackgroundAlternate=49,54,59 BackgroundNormal=49,54,59 DecorationFocus=61,174,233 DecorationHover=61,174,233 ForegroundActive=61,174,233 ForegroundInactive=161,169,177 ForegroundLink=29,153,243 ForegroundNegative=218,68,83 ForegroundNeutral=246,116,0 ForegroundNormal=252,252,252 ForegroundPositive=39,174,96 ForegroundVisited=155,89,182 [Colors:Header][Inactive] BackgroundAlternate=49,54,59 BackgroundNormal=42,46,50 DecorationFocus=61,174,233 DecorationHover=61,174,233 ForegroundActive=61,174,233 ForegroundInactive=161,169,177 ForegroundLink=29,153,243 ForegroundNegative=218,68,83 ForegroundNeutral=246,116,0 ForegroundNormal=252,252,252 ForegroundPositive=39,174,96 ForegroundVisited=155,89,182 [Colors:Selection] BackgroundAlternate=21,83,158 BackgroundNormal=21,83,158 DecorationFocus=61,174,233 DecorationHover=61,174,233 ForegroundActive=252,252,252 ForegroundInactive=161,169,177 ForegroundLink=253,188,75 ForegroundNegative=176,55,69 ForegroundNeutral=198,92,0 ForegroundNormal=252,253,254 ForegroundPositive=23,104,57 ForegroundVisited=155,89,182 [Colors:Tooltip] BackgroundAlternate=255,255,255 BackgroundNormal=255,255,255 DecorationFocus=61,174,233 DecorationHover=61,174,233 ForegroundActive=61,174,233 ForegroundInactive=161,169,177 ForegroundLink=29,153,243 ForegroundNegative=218,68,83 ForegroundNeutral=246,116,0 ForegroundNormal=0,0,0 ForegroundPositive=39,174,96 ForegroundVisited=155,89,182 [Colors:View] BackgroundAlternate=45,45,45 BackgroundNormal=45,45,45 DecorationFocus=61,174,233 DecorationHover=61,174,233 ForegroundActive=61,174,233 ForegroundInactive=161,169,177 ForegroundLink=29,153,243 ForegroundNegative=218,68,83 ForegroundNeutral=246,116,0 ForegroundNormal=255,255,255 ForegroundPositive=39,174,96 ForegroundVisited=155,89,182 [Colors:Window] BackgroundAlternate=53,53,53 BackgroundNormal=53,53,53 DecorationFocus=61,174,233 DecorationHover=61,174,233 ForegroundActive=61,174,233 ForegroundInactive=161,169,177 ForegroundLink=29,153,243 ForegroundNegative=218,68,83 ForegroundNeutral=246,116,0 ForegroundNormal=225,225,223 ForegroundPositive=39,174,96 ForegroundVisited=155,89,182 [General] ColorScheme=BreezeDark Name=AdwaitaHighcontrastInverse shadeSortColumn=true [KDE] contrast=4 [WM] activeBackground=41,41,41 activeBlend=252,252,252 activeForeground=225,225,223 inactiveBackground=42,46,50 inactiveBlend=161,169,177 inactiveForeground=161,169,177 QGnomePlatform-0.9.2/src/color-schemes/CMakeLists.txt000066400000000000000000000005531446542274100225230ustar00rootroot00000000000000install(FILES Adwaita.colors DESTINATION ${CMAKE_INSTALL_DATADIR}/color-schemes/) install(FILES AdwaitaDark.colors DESTINATION ${CMAKE_INSTALL_DATADIR}/color-schemes/) install(FILES AdwaitaHighcontrast.colors DESTINATION ${CMAKE_INSTALL_DATADIR}/color-schemes/) install(FILES AdwaitaHighcontrastInverse.colors DESTINATION ${CMAKE_INSTALL_DATADIR}/color-schemes/) QGnomePlatform-0.9.2/src/common/000077500000000000000000000000001446542274100165055ustar00rootroot00000000000000QGnomePlatform-0.9.2/src/common/CMakeLists.txt000066400000000000000000000011741446542274100212500ustar00rootroot00000000000000 set(common_SRCS gnomesettings.cpp gsettingshintprovider.cpp hintprovider.cpp portalhintprovider.cpp utils.cpp ) add_library(qgnomeplatform${LIBQGNOMEPLATFORM_SUFFIX} SHARED ${common_SRCS}) target_link_libraries(qgnomeplatform${LIBQGNOMEPLATFORM_SUFFIX} Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::DBus Qt${QT_VERSION_MAJOR}::GuiPrivate Qt${QT_VERSION_MAJOR}::Widgets ${ADWAITAQT_LIBRARIES} PkgConfig::GTK+3 ) install(TARGETS "qgnomeplatform${LIBQGNOMEPLATFORM_SUFFIX}" RUNTIME DESTINATION bin LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) QGnomePlatform-0.9.2/src/common/gnomesettings.cpp000066400000000000000000000436141446542274100221070ustar00rootroot00000000000000/* * Copyright (C) 2016-2022 Jan Grulich * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "gnomesettings.h" #include "gsettingshintprovider.h" #include "hintprovider.h" #include "portalhintprovider.h" #if QT_VERSION >= 0x060000 #include #else #include #endif // QtCore #include #include #include #include #include #include #include // QtDbus #include #include #include #include #include #include #include // QtGui #include #include #include #include #include #include #include #include #include Q_GLOBAL_STATIC(GnomeSettings, gnomeSettingsGlobal) Q_LOGGING_CATEGORY(QGnomePlatform, "qt.qpa.qgnomeplatform") static inline bool checkSandboxApplication() { return !QStandardPaths::locate(QStandardPaths::RuntimeLocation, QStringLiteral("flatpak-info")).isEmpty() || qEnvironmentVariableIsSet("SNAP"); } GnomeSettings &GnomeSettings::getInstance() { return *gnomeSettingsGlobal; } GnomeSettings::GnomeSettings(QObject *parent) : QObject(parent) , m_fallbackFont(new QFont(QLatin1String("Sans"), 10)) , m_isRunningInSandbox(checkSandboxApplication()) , m_canUseFileChooserPortal(!m_isRunningInSandbox) { gtk_init(nullptr, nullptr); if (m_isRunningInSandbox) { qCDebug(QGnomePlatform) << "Using xdg-desktop-portal backend"; m_hintProvider = std::make_unique(this); } else if (qgetenv("XDG_CURRENT_DESKTOP").toLower() == QStringLiteral("x-cinnamon")) { qCDebug(QGnomePlatform) << "Using GSettings backend"; m_hintProvider = std::make_unique(this); } else { // check if service already exists on QGnomePlatform initialization QDBusConnectionInterface *interface = QDBusConnection::sessionBus().interface(); const bool dbusServiceExists = interface && interface->isServiceRegistered(QString::fromLatin1("org.freedesktop.impl.portal.desktop.gnome")); if (dbusServiceExists) { qCDebug(QGnomePlatform) << "Using xdg-desktop-portal backend"; m_hintProvider = std::make_unique(this); } else { qCDebug(QGnomePlatform) << "Using GSettings backend"; m_hintProvider = std::make_unique(this); } // to switch between backends on runtime QDBusServiceWatcher *watcher = new QDBusServiceWatcher(this); watcher->setConnection(QDBusConnection::sessionBus()); watcher->setWatchMode(QDBusServiceWatcher::WatchForOwnerChange); watcher->addWatchedService(QString::fromLatin1("org.freedesktop.portal.Desktop")); connect(watcher, &QDBusServiceWatcher::serviceOwnerChanged, this, [=](const QString &service, const QString &oldOwner, const QString &newOwner) { Q_UNUSED(service) if (newOwner.isEmpty()) { qCDebug(QGnomePlatform) << "Portal service disappeared. Switching to GSettings backend"; m_hintProvider = std::make_unique(this); onHintProviderChanged(); } else if (oldOwner.isEmpty()) { qCDebug(QGnomePlatform) << "Portal service appeared. Switching xdg-desktop-portal backend"; PortalHintProvider *provider = new PortalHintProvider(this, true); connect(provider, &PortalHintProvider::settingsRecieved, this, [=]() { m_hintProvider.reset(provider); onHintProviderChanged(); }); } }); } initializeHintProvider(); // Initialize some cursor env variables needed by QtWayland onCursorSizeChanged(); onCursorThemeChanged(); loadPalette(); if (m_canUseFileChooserPortal) { QTimer::singleShot(0, this, [this]() { const QString filePath = QStringLiteral("/proc/%1/root").arg(QCoreApplication::applicationPid()); struct stat info; if (lstat(filePath.toStdString().c_str(), &info) == 0) { if (!static_cast(info.st_uid)) { m_canUseFileChooserPortal = false; } } else { // Do not use FileChooser portal if we fail to get information about the file m_canUseFileChooserPortal = false; } }); if (m_canUseFileChooserPortal) { // Get information about portal version QDBusMessage message = QDBusMessage::createMethodCall(QLatin1String("org.freedesktop.portal.Desktop"), QLatin1String("/org/freedesktop/portal/desktop"), QLatin1String("org.freedesktop.DBus.Properties"), QLatin1String("Get")); message << QLatin1String("org.freedesktop.portal.FileChooser") << QLatin1String("version"); QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall); QObject::connect(watcher, &QDBusPendingCallWatcher::finished, [this](QDBusPendingCallWatcher *watcher) { QDBusPendingReply reply = *watcher; if (reply.isValid()) { uint fileChooserPortalVersion = reply.value().toUInt(); if (fileChooserPortalVersion < 3) { m_canUseFileChooserPortal = false; } } else { m_canUseFileChooserPortal = false; } watcher->deleteLater(); }); } } } GnomeSettings::~GnomeSettings() { delete m_fallbackFont; delete m_palette; } void GnomeSettings::initializeHintProvider() const { connect(m_hintProvider.get(), &HintProvider::cursorBlinkTimeChanged, this, &GnomeSettings::onCursorBlinkTimeChanged); connect(m_hintProvider.get(), &HintProvider::cursorSizeChanged, this, &GnomeSettings::onCursorSizeChanged); connect(m_hintProvider.get(), &HintProvider::cursorThemeChanged, this, &GnomeSettings::onCursorThemeChanged); connect(m_hintProvider.get(), &HintProvider::fontChanged, this, &GnomeSettings::onFontChanged); connect(m_hintProvider.get(), &HintProvider::iconThemeChanged, this, &GnomeSettings::onIconThemeChanged); connect(m_hintProvider.get(), &HintProvider::titlebarChanged, this, &GnomeSettings::titlebarChanged); connect(m_hintProvider.get(), &HintProvider::themeChanged, this, &GnomeSettings::loadPalette); connect(m_hintProvider.get(), &HintProvider::themeChanged, this, &GnomeSettings::themeChanged); connect(m_hintProvider.get(), &HintProvider::themeChanged, this, &GnomeSettings::onThemeChanged); } QFont *GnomeSettings::font(QPlatformTheme::Font type) const { auto fonts = m_hintProvider->fonts(); if (fonts.contains(type)) { return fonts[type]; } else if (fonts.contains(QPlatformTheme::SystemFont)) { return fonts[QPlatformTheme::SystemFont]; } else { // GTK default font return m_fallbackFont; } } QPalette *GnomeSettings::palette() const { return m_palette; } bool GnomeSettings::canUseFileChooserPortal() const { return m_canUseFileChooserPortal; } bool GnomeSettings::useGtkThemeDarkVariant() const { QString theme = m_hintProvider->gtkTheme(); if (qEnvironmentVariableIsSet("QT_STYLE_OVERRIDE")) { /* If QT_STYLE_OVERRIDE we should rely on it */ theme = QString::fromLocal8Bit(qgetenv("QT_STYLE_OVERRIDE")); } else if (m_hintProvider->canRelyOnAppearance()) { return m_hintProvider->appearance() == PreferDark; } return theme.toLower().contains("-dark") || theme.toLower().endsWith("inverse") || m_hintProvider->appearance() == PreferDark; } bool GnomeSettings::useGtkThemeHighContrastVariant() const { const QString theme = m_hintProvider->gtkTheme(); return theme.toLower().startsWith("highcontrast"); } QString GnomeSettings::gtkTheme() const { return m_hintProvider->gtkTheme(); } QVariant GnomeSettings::hint(QPlatformTheme::ThemeHint hint) const { if (hint == QPlatformTheme::StyleNames) { return styleNames(); } else if (hint == QPlatformTheme::IconThemeSearchPaths) { return xdgIconThemePaths(); } return m_hintProvider->hints()[hint]; } GnomeSettings::TitlebarButtons GnomeSettings::titlebarButtons() const { return m_hintProvider->titlebarButtons(); } GnomeSettings::TitlebarButtonsPlacement GnomeSettings::titlebarButtonPlacement() const { return m_hintProvider->titlebarButtonPlacement(); } static QString colorScheme(bool useHighContrast, bool useDarkVariant) { QString colorScheme; if (useHighContrast) { colorScheme = useDarkVariant ? QStringLiteral("AdwaitaHighcontrastInverse") : QStringLiteral("AdwaitaHighcontrast"); } else { colorScheme = useDarkVariant ? QStringLiteral("AdwaitaDark") : QStringLiteral("Adwaita"); } return QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("color-schemes/") + colorScheme + QStringLiteral(".colors")); } void GnomeSettings::loadPalette() { if (useGtkThemeHighContrastVariant()) { m_palette = new QPalette(Adwaita::Colors::palette(useGtkThemeDarkVariant() ? Adwaita::ColorVariant::AdwaitaHighcontrastInverse : Adwaita::ColorVariant::AdwaitaHighcontrast)); } else { m_palette = new QPalette(Adwaita::Colors::palette(useGtkThemeDarkVariant() ? Adwaita::ColorVariant::AdwaitaDark : Adwaita::ColorVariant::Adwaita)); } const QString colorSchemePath = colorScheme(useGtkThemeHighContrastVariant(), useGtkThemeDarkVariant()); if (colorSchemePath.isEmpty()) { qCWarning(QGnomePlatform) << "Could not find color scheme " << colorSchemePath; return; } qApp->setProperty("KDE_COLOR_SCHEME_PATH", colorSchemePath); } void GnomeSettings::onCursorBlinkTimeChanged() { // If we are not a QApplication, means that we are a QGuiApplication, then we do nothing. if (!qobject_cast(QCoreApplication::instance())) { return; } QWidgetList widgets = QApplication::allWidgets(); for (QWidget *widget : widgets) { if (qobject_cast(widget) || qobject_cast(widget)) { QEvent event(QEvent::StyleChange); QApplication::sendEvent(widget, &event); } } } void GnomeSettings::onCursorSizeChanged() { if (QGuiApplication::platformName() != QStringLiteral("xcb")) { qputenv("XCURSOR_SIZE", QString::number(m_hintProvider->cursorSize()).toUtf8()); } } void GnomeSettings::onCursorThemeChanged() { if (QGuiApplication::platformName() != QStringLiteral("xcb")) { qputenv("XCURSOR_THEME", m_hintProvider->cursorTheme().toUtf8()); } } void GnomeSettings::onFontChanged() { if (qobject_cast(QCoreApplication::instance())) { QApplication::setFont(*m_hintProvider->fonts()[QPlatformTheme::SystemFont]); QWidgetList widgets = QApplication::allWidgets(); for (QWidget *widget : widgets) { widget->setFont(*m_hintProvider->fonts()[QPlatformTheme::SystemFont]); } } else { QGuiApplication::setFont(*m_hintProvider->fonts()[QPlatformTheme::SystemFont]); } } void GnomeSettings::onIconThemeChanged() { // If we are not a QApplication, means that we are a QGuiApplication, then we do nothing. if (!qobject_cast(QCoreApplication::instance())) { return; } QWidgetList widgets = QApplication::allWidgets(); for (QWidget *widget : widgets) { if (qobject_cast(widget) || qobject_cast(widget)) { QEvent event(QEvent::StyleChange); QApplication::sendEvent(widget, &event); } } } void GnomeSettings::onThemeChanged() { QApplication *app = qobject_cast(QCoreApplication::instance()); if (!app) { return; } app->setStyle(styleNames().first()); } void GnomeSettings::onHintProviderChanged() { initializeHintProvider(); // Reload some configuration as switching backends we might get different configuration loadPalette(); onThemeChanged(); // Also notify to update decorations Q_EMIT themeChanged(); } QStringList GnomeSettings::styleNames() const { QStringList styleNames; // QT_STYLE_OVERRIDE should always have highest priority QString styleOverride; if (qEnvironmentVariableIsSet("QT_STYLE_OVERRIDE")) { styleOverride = QString::fromLocal8Bit(qgetenv("QT_STYLE_OVERRIDE")); styleNames << styleOverride; } bool isDarkTheme = false; const bool preferDarkTheme = m_hintProvider->appearance() == Appearance::PreferDark; const QString gtkTheme = m_hintProvider->gtkTheme(); if (gtkTheme.toLower().contains("-dark") || gtkTheme.toLower().endsWith("inverse")) { isDarkTheme = true; } // 2) Use GTK theme if (!gtkTheme.isEmpty()) { const QStringList adwaitaStyles = {QStringLiteral("adwaita"), QStringLiteral("adwaita-dark"), QStringLiteral("highcontrast"), QStringLiteral("highcontrastinverse")}; if (adwaitaStyles.contains(gtkTheme.toLower())) { QString theme = gtkTheme; if (m_hintProvider->canRelyOnAppearance()) { if (gtkTheme.toLower().contains(QStringLiteral("adwaita"))) { theme = preferDarkTheme ? QStringLiteral("adwaita-dark") : QStringLiteral("adwaita"); } else if (gtkTheme.toLower().contains(QStringLiteral("highcontrast"))) { theme = preferDarkTheme ? QStringLiteral("highcontrastinverse") : QStringLiteral("highcontrast"); } } styleNames << theme; } } // 3) Use Kvantum // Detect if we have a Kvantum theme for this Gtk theme QString kvTheme = kvantumThemeForGtkTheme(); if (!kvTheme.isEmpty()) { // Found matching Kvantum theme, configure user's Kvantum setting to use this configureKvantum(kvTheme); if (isDarkTheme || preferDarkTheme) { styleNames << QStringLiteral("kvantum-dark"); } styleNames << QStringLiteral("kvantum"); } // 4) Use light/dark adwaita as fallback if (isDarkTheme || preferDarkTheme) { styleNames << QStringLiteral("adwaita-dark"); } else { styleNames << QStringLiteral("adwaita"); } // 5) Use other styles styleNames << QStringLiteral("fusion") << QStringLiteral("windows"); return styleNames; } QStringList GnomeSettings::xdgIconThemePaths() const { QStringList paths; const QFileInfo homeIconDir(QDir::homePath() + QStringLiteral("/.icons")); if (homeIconDir.isDir()) { paths << homeIconDir.absoluteFilePath(); } QString xdgDirString = QFile::decodeName(qgetenv("XDG_DATA_DIRS")); if (xdgDirString.isEmpty()) { xdgDirString = QStringLiteral("/usr/local/share:/usr/share"); } for (const QString &xdgDir : xdgDirString.split(QLatin1Char(':'))) { const QFileInfo xdgIconsDir(xdgDir + QStringLiteral("/icons")); if (xdgIconsDir.isDir()) { paths << xdgIconsDir.absoluteFilePath(); } } return paths; } QString GnomeSettings::kvantumThemeForGtkTheme() const { if (m_hintProvider->gtkTheme().isEmpty()) { // No Gtk theme? Then can't match to Kvantum! return QString(); } QString gtkName = m_hintProvider->gtkTheme(); QStringList dirs = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); // Look for a matching KVantum config file in the theme's folder for (const QString &dir : dirs) { if (QFile::exists(QStringLiteral("%1/themes/%2/Kvantum/%3.kvconfig").arg(dir).arg(gtkName).arg(gtkName))) { return gtkName; } } // No config found in theme folder, look for a Kv as shipped as part of Kvantum itself // (Kvantum ships KvAdapta, KvAmbiance, KvArc, etc. QStringList names{QStringLiteral("Kv") + gtkName}; // Convert Ark-Dark to ArcDark to look for KvArcDark if (gtkName.indexOf("-") != -1) { names.append("Kv" + gtkName.replace("-", "")); } for (const QString &name : names) { for (const QString &dir : dirs) { if (QFile::exists(QStringLiteral("%1/Kvantum/%2/%3.kvconfig").arg(dir).arg(name).arg(name))) { return name; } } } return QString(); } void GnomeSettings::configureKvantum(const QString &theme) const { QSettings config(QDir::homePath() + "/.config/Kvantum/kvantum.kvconfig", QSettings::NativeFormat); if (!config.contains("theme") || config.value("theme").toString() != theme) { config.setValue("theme", theme); } } QGnomePlatform-0.9.2/src/common/gnomesettings.h000066400000000000000000000053761446542274100215570ustar00rootroot00000000000000/* * Copyright (C) 2016-2022 Jan Grulich * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef GNOME_SETTINGS_H #define GNOME_SETTINGS_H #include #include #include #include #include class QFont; class QVariant; class QPalette; class HintProvider; class GnomeSettings : public QObject { Q_OBJECT public: enum Appearance { PreferDark = 1, PreferLight = 2 }; enum TitlebarButtonsPlacement { LeftPlacement = 0, RightPlacement = 1 }; enum TitlebarButton { CloseButton = 0x1, MinimizeButton = 0x02, MaximizeButton = 0x04 }; Q_DECLARE_FLAGS(TitlebarButtons, TitlebarButton); explicit GnomeSettings(QObject *parent = nullptr); virtual ~GnomeSettings(); static GnomeSettings &getInstance(); QFont *font(QPlatformTheme::Font type) const; QPalette *palette() const; QVariant hint(QPlatformTheme::ThemeHint hint) const; bool canUseFileChooserPortal() const; bool useGtkThemeDarkVariant() const; bool useGtkThemeHighContrastVariant() const; QString gtkTheme() const; TitlebarButtons titlebarButtons() const; TitlebarButtonsPlacement titlebarButtonPlacement() const; Q_SIGNALS: void themeChanged(); void titlebarChanged(); private Q_SLOTS: void loadPalette(); void onCursorBlinkTimeChanged(); void onCursorSizeChanged(); void onCursorThemeChanged(); void onFontChanged(); void onIconThemeChanged(); void onThemeChanged(); void onHintProviderChanged(); private: void configureKvantum(const QString &theme) const; void initializeHintProvider() const; QString kvantumThemeForGtkTheme() const; QStringList styleNames() const; QStringList xdgIconThemePaths() const; QFont *m_fallbackFont = nullptr; QPalette *m_palette = nullptr; std::unique_ptr m_hintProvider; bool m_relyOnAppearance = false; bool m_isRunningInSandbox; bool m_canUseFileChooserPortal = false; }; Q_DECLARE_OPERATORS_FOR_FLAGS(GnomeSettings::TitlebarButtons) #endif // GNOME_SETTINGS_H QGnomePlatform-0.9.2/src/common/gsettingshintprovider.cpp000066400000000000000000000233441446542274100236640ustar00rootroot00000000000000/* * Copyright (C) 2016-2022 Jan Grulich * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "gsettingshintprovider.h" #include "utils.h" #include #include #include Q_LOGGING_CATEGORY(QGnomePlatformGSettingsHintProvider, "qt.qpa.qgnomeplatform.gsettingshintprovider") static GSettings *loadGSettingsSchema(const QString &schema) { GSettingsSchemaSource *source = g_settings_schema_source_get_default(); GSettingsSchema *gschema = nullptr; gschema = g_settings_schema_source_lookup(source, schema.toLatin1(), TRUE); if (!gschema) { return nullptr; } GSettings *settings = g_settings_new(schema.toLatin1()); g_settings_schema_unref(gschema); return settings; } GSettingsHintProvider::GSettingsHintProvider(QObject *parent) : HintProvider(parent) , m_gnomeDesktopSettings(loadGSettingsSchema(QLatin1String("org.gnome.desktop.wm.preferences"))) , m_settings(loadGSettingsSchema(QLatin1String("org.gnome.desktop.interface"))) { // Check if this is a Cinnamon session to use additionally a different setting scheme if (qgetenv("XDG_CURRENT_DESKTOP").toLower() == QStringLiteral("x-cinnamon")) { m_cinnamonSettings = loadGSettingsSchema(QLatin1String("org.cinnamon.desktop.interface")); } // Do not continue on missing GSettings if (!m_settings && !m_cinnamonSettings) { return; } // Watch for changes QStringList watchListDesktopInterface = {"changed::gtk-theme", "changed::color-scheme", "changed::icon-theme", "changed::cursor-blink-time", "changed::font-name", "changed::monospace-font-name", "changed::cursor-size"}; for (const QString &watchedProperty : watchListDesktopInterface) { g_signal_connect(m_settings, watchedProperty.toStdString().c_str(), G_CALLBACK(gsettingPropertyChanged), this); // Additionally watch Cinnamon configuration if (m_cinnamonSettings) { g_signal_connect(m_cinnamonSettings, watchedProperty.toStdString().c_str(), G_CALLBACK(gsettingPropertyChanged), this); } } QStringList watchListWmPreferences = {"changed::titlebar-font", "changed::button-layout"}; for (const QString &watchedProperty : watchListWmPreferences) { g_signal_connect(m_gnomeDesktopSettings, watchedProperty.toStdString().c_str(), G_CALLBACK(gsettingPropertyChanged), this); } m_canRelyOnAppearance = true; loadCursorBlinkTime(); loadCursorSize(); loadCursorTheme(); loadFonts(); loadStaticHints(); loadTheme(); loadTitlebar(); loadIconTheme(); } GSettingsHintProvider::~GSettingsHintProvider() { if (m_cinnamonSettings) { g_object_unref(m_cinnamonSettings); } g_object_unref(m_gnomeDesktopSettings); g_object_unref(m_settings); } void GSettingsHintProvider::gsettingPropertyChanged(GSettings *settings, gchar *key, GSettingsHintProvider *hintProvider) { Q_UNUSED(settings) const QString changedProperty = key; qCDebug(QGnomePlatformGSettingsHintProvider) << "GSetting property change: " << key; if (changedProperty == QStringLiteral("gtk-theme") || changedProperty == QStringLiteral("color-scheme")) { hintProvider->loadTheme(); Q_EMIT hintProvider->themeChanged(); } else if (changedProperty == QStringLiteral("icon-theme")) { hintProvider->loadIconTheme(); Q_EMIT hintProvider->iconThemeChanged(); } else if (changedProperty == QStringLiteral("cursor-blink-time")) { hintProvider->loadCursorBlinkTime(); Q_EMIT hintProvider->cursorBlinkTimeChanged(); } else if (changedProperty == QStringLiteral("font-name") || changedProperty == QStringLiteral("monospace-font-name") || changedProperty == QStringLiteral("titlebar-font")) { hintProvider->loadFonts(); Q_EMIT hintProvider->fontChanged(); } else if (changedProperty == QStringLiteral("cursor-size")) { hintProvider->loadCursorSize(); ; Q_EMIT hintProvider->fontChanged(); } else if (changedProperty == QStringLiteral("cursor-theme")) { hintProvider->loadCursorTheme(); Q_EMIT hintProvider->cursorThemeChanged(); } else if (changedProperty == QStringLiteral("button-layout")) { hintProvider->loadTitlebar(); Q_EMIT hintProvider->titlebarChanged(); } } void GSettingsHintProvider::loadCursorBlinkTime() { const int cursorBlinkTime = getSettingsProperty(QStringLiteral("cursor-blink-time")); setCursorBlinkTime(cursorBlinkTime); } void GSettingsHintProvider::loadCursorSize() { const int cursorSize = getSettingsProperty(QStringLiteral("cursor-size")); setCursorSize(cursorSize); } void GSettingsHintProvider::loadCursorTheme() { const QString cursorTheme = getSettingsProperty(QStringLiteral("cursor-theme")); setCursorTheme(cursorTheme); } void GSettingsHintProvider::loadIconTheme() { const QString systemIconTheme = getSettingsProperty(QStringLiteral("icon-theme")); setIconTheme(systemIconTheme); } void GSettingsHintProvider::loadFonts() { const QString fontName = getSettingsProperty(QStringLiteral("font-name")); const QString monospaceFontName = getSettingsProperty(QStringLiteral("monospace-font-name")); const QString titlebarFontName = getSettingsProperty(QStringLiteral("titlebar-font")); setFonts(fontName, monospaceFontName, titlebarFontName); } void GSettingsHintProvider::loadTitlebar() { const QString buttonLayout = getSettingsProperty("button-layout"); setTitlebar(buttonLayout); } void GSettingsHintProvider::loadTheme() { const QString colorScheme = getSettingsProperty(QStringLiteral("color-scheme")); const QString theme = getSettingsProperty(QStringLiteral("gtk-theme")); const GnomeSettings::Appearance appearance = colorScheme == QStringLiteral("prefer-dark") ? GnomeSettings::PreferDark : GnomeSettings::PreferLight; setTheme(theme, appearance); } void GSettingsHintProvider::loadStaticHints() { gint doubleClickTime = 400; g_object_get(gtk_settings_get_default(), "gtk-double-click-time", &doubleClickTime, NULL); guint longPressTime = 500; g_object_get(gtk_settings_get_default(), "gtk-long-press-time", &longPressTime, NULL); gint doubleClickDistance = 5; g_object_get(gtk_settings_get_default(), "gtk-double-click-distance", &doubleClickDistance, NULL); gint startDragDistance = 8; g_object_get(gtk_settings_get_default(), "gtk-dnd-drag-threshold", &startDragDistance, NULL); guint passwordMaskDelay = 0; g_object_get(gtk_settings_get_default(), "gtk-entry-password-hint-timeout", &passwordMaskDelay, NULL); setStaticHints(doubleClickTime, longPressTime, doubleClickDistance, startDragDistance, passwordMaskDelay); } template T GSettingsHintProvider::getSettingsProperty(GSettings *settings, const QString &property, bool *ok) { Q_UNUSED(settings) Q_UNUSED(property) Q_UNUSED(ok) return {}; } template T GSettingsHintProvider::getSettingsProperty(const QString &property, bool *ok) { GSettings *settings = m_settings; // In case of Cinnamon session, we most probably want to return the value from here if possible if (m_cinnamonSettings) { GSettingsSchema *schema; g_object_get(G_OBJECT(m_cinnamonSettings), "settings-schema", &schema, NULL); if (schema) { if (g_settings_schema_has_key(schema, property.toStdString().c_str())) { settings = m_cinnamonSettings; } } } // Use org.gnome.desktop.wm.preferences if the property is there, otherwise it would bail on // non-existent property GSettingsSchema *schema; g_object_get(G_OBJECT(m_gnomeDesktopSettings), "settings-schema", &schema, NULL); if (schema) { if (g_settings_schema_has_key(schema, property.toStdString().c_str())) { settings = m_gnomeDesktopSettings; } } return getSettingsProperty(settings, property, ok); } template<> int GSettingsHintProvider::getSettingsProperty(GSettings *settings, const QString &property, bool *ok) { if (ok) { *ok = true; } return g_settings_get_int(settings, property.toStdString().c_str()); } template<> QString GSettingsHintProvider::getSettingsProperty(GSettings *settings, const QString &property, bool *ok) { // be exception and resources safe std::unique_ptr raw{g_settings_get_string(settings, property.toStdString().c_str()), g_free}; if (ok) { *ok = !!raw; } return QString{raw.get()}; } template<> qreal GSettingsHintProvider::getSettingsProperty(GSettings *settings, const QString &property, bool *ok) { if (ok) { *ok = true; } return g_settings_get_double(settings, property.toStdString().c_str()); } QGnomePlatform-0.9.2/src/common/gsettingshintprovider.h000066400000000000000000000037071446542274100233320ustar00rootroot00000000000000/* * Copyright (C) 2016-2022 Jan Grulich * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef GSETTINGS_HINT_PROVIDER_H #define GSETTINGS_HINT_PROVIDER_H #include "hintprovider.h" #undef signals #include #include #include #define signals Q_SIGNALS class QFont; class QString; class QVariant; class GSettingsHintProvider : public HintProvider { Q_OBJECT public: explicit GSettingsHintProvider(QObject *parent = nullptr); virtual ~GSettingsHintProvider(); protected: static void gsettingPropertyChanged(GSettings *settings, gchar *key, GSettingsHintProvider *hintProvider); private: template T getSettingsProperty(GSettings *settings, const QString &property, bool *ok = nullptr); template T getSettingsProperty(const QString &property, bool *ok = nullptr); void loadCursorBlinkTime(); void loadCursorSize(); void loadCursorTheme(); void loadIconTheme(); void loadFonts(); void loadTheme(); void loadTitlebar(); void loadStaticHints(); GSettings *m_cinnamonSettings = nullptr; GSettings *m_gnomeDesktopSettings = nullptr; GSettings *m_settings = nullptr; }; #endif // GSETTINGS_HINT_PROVIDER_H QGnomePlatform-0.9.2/src/common/hintprovider.cpp000066400000000000000000000136141446542274100217330ustar00rootroot00000000000000/* * Copyright (C) 2016-2022 Jan Grulich * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "hintprovider.h" #include "utils.h" #include #include #include #include #include Q_LOGGING_CATEGORY(QGnomePlatformHintProvider, "qt.qpa.qgnomeplatform.hintprovider") HintProvider::HintProvider(QObject *parent) : QObject(parent) { // Generic hints shared with all providers m_hints[QPlatformTheme::DialogButtonBoxLayout] = QDialogButtonBox::GnomeLayout; m_hints[QPlatformTheme::DialogButtonBoxButtonsHaveIcons] = true; m_hints[QPlatformTheme::KeyboardScheme] = QPlatformTheme::GnomeKeyboardScheme; m_hints[QPlatformTheme::IconPixmapSizes] = QVariant::fromValue(QList() << 512 << 256 << 128 << 64 << 32 << 22 << 16 << 8); m_hints[QPlatformTheme::PasswordMaskCharacter] = QVariant(QChar(0x2022)); } HintProvider::~HintProvider() { qDeleteAll(m_fonts); } void HintProvider::setCursorBlinkTime(int cursorBlinkTime) { if (cursorBlinkTime >= 100) { qCDebug(QGnomePlatformHintProvider) << "Cursor blink time: " << cursorBlinkTime; m_hints[QPlatformTheme::CursorFlashTime] = cursorBlinkTime; } else { m_hints[QPlatformTheme::CursorFlashTime] = 1200; } } void HintProvider::setCursorSize(int cursorSize) { m_cursorSize = cursorSize; #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) m_hints[QPlatformTheme::MouseCursorSize] = QSize(cursorSize, cursorSize); #endif } void HintProvider::setCursorTheme(const QString &cursorTheme) { m_cursorTheme = cursorTheme; #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) m_hints[QPlatformTheme::MouseCursorTheme] = cursorTheme; #endif } void HintProvider::setIconTheme(const QString &iconTheme) { bool useDarkTheme = false; if (m_canRelyOnAppearance) { useDarkTheme = m_appearance == GnomeSettings::PreferDark; } else { useDarkTheme = m_gtkTheme.toLower().contains("-dark") || m_gtkTheme.toLower().endsWith("inverse") || m_appearance == GnomeSettings::PreferDark; } const QString breezeTheme = useDarkTheme ? QStringLiteral("breeze-dark") : QStringLiteral("breeze"); const QString adwaitaTheme = QStringLiteral("Adwaita"); if (!iconTheme.isEmpty() && iconTheme != adwaitaTheme) { m_hints[QPlatformTheme::SystemIconThemeName] = iconTheme; m_hints[QPlatformTheme::SystemIconFallbackThemeName] = adwaitaTheme; } else { m_hints[QPlatformTheme::SystemIconThemeName] = adwaitaTheme; m_hints[QPlatformTheme::SystemIconFallbackThemeName] = breezeTheme; } qCDebug(QGnomePlatformHintProvider) << "Icon theme: " << m_hints[QPlatformTheme::SystemIconThemeName].toString(); qCDebug(QGnomePlatformHintProvider) << "Fallback icon theme: " << m_hints[QPlatformTheme::SystemIconFallbackThemeName].toString(); } void HintProvider::setFonts(const QString &systemFont, const QString &monospaceFont, const QString &titlebarFont) { qDeleteAll(m_fonts); m_fonts.clear(); QFont *font = Utils::qt_fontFromString(systemFont); m_fonts[QPlatformTheme::SystemFont] = font; qCDebug(QGnomePlatformHintProvider) << "Font name: " << font->family() << " (size " << font->pointSize() << ")"; QFont *fixedFont = Utils::qt_fontFromString(monospaceFont); m_fonts[QPlatformTheme::FixedFont] = fixedFont; qCDebug(QGnomePlatformHintProvider) << "Monospace font name: " << fixedFont->family() << " (size " << fixedFont->pointSize() << ")"; QFont *tbarFont = Utils::qt_fontFromString(titlebarFont); m_fonts[QPlatformTheme::TitleBarFont] = tbarFont; qCDebug(QGnomePlatformHintProvider) << "TitleBar font name: " << tbarFont->family() << " (size " << tbarFont->pointSize() << ")"; } void HintProvider::setTitlebar(const QString &buttonLayout) { m_titlebarButtonPlacement = Utils::titlebarButtonPlacementFromString(buttonLayout); m_titlebarButtons = Utils::titlebarButtonsFromString(buttonLayout); } void HintProvider::setTheme(const QString &theme, GnomeSettings::Appearance appearance) { m_gtkTheme = theme; qCDebug(QGnomePlatformHintProvider) << "GTK theme: " << m_gtkTheme; m_appearance = appearance; qCDebug(QGnomePlatformHintProvider) << "Prefer dark theme: " << (appearance == GnomeSettings::PreferDark ? "yes" : "no"); } void HintProvider::setStaticHints(int doubleClickTime, int longPressTime, int doubleClickDistance, int startDragDistance, int passwordMaskDelay) { qCDebug(QGnomePlatformHintProvider) << "Double click time: " << doubleClickTime; m_hints[QPlatformTheme::MouseDoubleClickInterval] = doubleClickTime; qCDebug(QGnomePlatformHintProvider) << "Long press time: " << longPressTime; m_hints[QPlatformTheme::MousePressAndHoldInterval] = longPressTime; qCDebug(QGnomePlatformHintProvider) << "Double click distance: " << doubleClickDistance; m_hints[QPlatformTheme::MouseDoubleClickDistance] = doubleClickDistance; qCDebug(QGnomePlatformHintProvider) << "Dnd drag threshold: " << startDragDistance; m_hints[QPlatformTheme::StartDragDistance] = startDragDistance; qCDebug(QGnomePlatformHintProvider) << "Password hint timeout: " << passwordMaskDelay; m_hints[QPlatformTheme::PasswordMaskDelay] = passwordMaskDelay; } QGnomePlatform-0.9.2/src/common/hintprovider.h000066400000000000000000000072571446542274100214060ustar00rootroot00000000000000/* * Copyright (C) 2016-2022 Jan Grulich * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef HINT_PROVIDER_H #define HINT_PROVIDER_H #include "gnomesettings.h" #include #include #include #include class QFont; class QString; class HintProvider : public QObject { Q_OBJECT public: explicit HintProvider(QObject *parent = nullptr); virtual ~HintProvider(); inline QHash hints() const { return m_hints; } inline QHash fonts() const { return m_fonts; } // Theme inline QString gtkTheme() const { return m_gtkTheme; } inline GnomeSettings::Appearance appearance() const { return m_appearance; } // Whether we can assume e.g. Adwaita-dark should be used when Appearance == PreferDark // even though the theme is set to Adwaita // Used only when color-scheme from org.freedesktop.Appearance interface is implemented // in the xdg-desktop-portal backend inline bool canRelyOnAppearance() const { return m_canRelyOnAppearance; } // Cursor inline int cursorSize() const { return m_cursorSize; } inline QString cursorTheme() const { return m_cursorTheme; } // Window decorations inline GnomeSettings::TitlebarButtons titlebarButtons() const { return m_titlebarButtons; } inline GnomeSettings::TitlebarButtonsPlacement titlebarButtonPlacement() const { return m_titlebarButtonPlacement; } Q_SIGNALS: void cursorBlinkTimeChanged(); void cursorSizeChanged(); void cursorThemeChanged(); void fontChanged(); void iconThemeChanged(); void titlebarChanged(); void themeChanged(); protected: void setCursorBlinkTime(int cursorBlinkTime); void setCursorSize(int cursorSize); void setCursorTheme(const QString &cursorTheme); void setIconTheme(const QString &iconTheme); void setFonts(const QString &systemFont, const QString &monospaceFont, const QString &titlebarFont); void setTheme(const QString &theme, GnomeSettings::Appearance appearance); void setTitlebar(const QString &buttonLayout); void setStaticHints(int doubleClickTime, int longPressTime, int doubleClickDistance, int startDragDistance, int passwordMaskDelay); // Theme QString m_gtkTheme; GnomeSettings::Appearance m_appearance = GnomeSettings::PreferLight; bool m_canRelyOnAppearance = false; // Cursor int m_cursorSize = 0; QString m_cursorTheme; // Window decorations GnomeSettings::TitlebarButtons m_titlebarButtons = GnomeSettings::TitlebarButton::CloseButton; GnomeSettings::TitlebarButtonsPlacement m_titlebarButtonPlacement = GnomeSettings::TitlebarButtonsPlacement::RightPlacement; QHash m_fonts; QHash m_hints; }; #endif // GNOME_SETTINGS_P_H QGnomePlatform-0.9.2/src/common/portalhintprovider.cpp000066400000000000000000000177011446542274100231560ustar00rootroot00000000000000/* * Copyright (C) 2016-2022 Jan Grulich * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "portalhintprovider.h" // QtDBus #include #include #include #include #include #include #include #include #include #include Q_LOGGING_CATEGORY(QGnomePlatformPortalHintProvider, "qt.qpa.qgnomeplatform.portalhintprovider") const QDBusArgument &operator>>(const QDBusArgument &argument, QMap &map) { argument.beginMap(); map.clear(); while (!argument.atEnd()) { QString key; QVariantMap value; argument.beginMapEntry(); argument >> key >> value; argument.endMapEntry(); map.insert(key, value); } argument.endMap(); return argument; } PortalHintProvider::PortalHintProvider(QObject *parent, bool asynchronous) : HintProvider(parent) { QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.portal.Desktop"), QStringLiteral("/org/freedesktop/portal/desktop"), QStringLiteral("org.freedesktop.portal.Settings"), QStringLiteral("ReadAll")); message << QStringList{{QStringLiteral("org.gnome.desktop.interface")}, {QStringLiteral("org.gnome.desktop.wm.preferences")}, {QStringLiteral("org.freedesktop.appearance")}}; qCDebug(QGnomePlatformPortalHintProvider) << "Reading settings from xdg-desktop-portal"; if (asynchronous) { qDBusRegisterMetaType>(); QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall); QObject::connect(watcher, &QDBusPendingCallWatcher::finished, [=](QDBusPendingCallWatcher *watcher) { QDBusPendingReply> reply = *watcher; if (reply.isValid()) { m_portalSettings = reply.value(); onSettingsReceived(); Q_EMIT settingsRecieved(); watcher->deleteLater(); } }); } else { QDBusMessage resultMessage = QDBusConnection::sessionBus().call(message); qCDebug(QGnomePlatformPortalHintProvider) << "Received settings from xdg-desktop-portal"; if (resultMessage.type() == QDBusMessage::ReplyMessage) { QDBusArgument dbusArgument = resultMessage.arguments().at(0).value(); dbusArgument >> m_portalSettings; onSettingsReceived(); } } QDBusConnection::sessionBus().connect(QString(), QStringLiteral("/org/freedesktop/portal/desktop"), QStringLiteral("org.freedesktop.portal.Settings"), QStringLiteral("SettingChanged"), this, SLOT(settingChanged(QString, QString, QDBusVariant))); } void PortalHintProvider::onSettingsReceived() { if (m_portalSettings.contains(QStringLiteral("org.freedesktop.appearance"))) { m_canRelyOnAppearance = true; } loadCursorBlinkTime(); loadCursorSize(); loadCursorTheme(); loadFonts(); loadStaticHints(); loadTheme(); loadTitlebar(); loadIconTheme(); } void PortalHintProvider::settingChanged(const QString &group, const QString &key, const QDBusVariant &value) { qCDebug(QGnomePlatformPortalHintProvider) << "Setting property change: " << group << " : " << key; m_portalSettings[group][key] = value.variant(); if (key == QStringLiteral("gtk-theme") || key == QStringLiteral("color-scheme")) { loadTheme(); Q_EMIT themeChanged(); } else if (key == QStringLiteral("icon-theme")) { loadIconTheme(); Q_EMIT iconThemeChanged(); } else if (key == QStringLiteral("cursor-blink-time")) { loadCursorBlinkTime(); Q_EMIT cursorBlinkTimeChanged(); } else if (key == QStringLiteral("font-name") || key == QStringLiteral("monospace-font-name") || key == QStringLiteral("titlebar-font")) { loadFonts(); Q_EMIT fontChanged(); } else if (key == QStringLiteral("cursor-size")) { loadCursorSize(); Q_EMIT fontChanged(); } else if (key == QStringLiteral("cursor-theme")) { loadCursorTheme(); Q_EMIT cursorThemeChanged(); } else if (key == QStringLiteral("button-layout")) { loadTitlebar(); Q_EMIT titlebarChanged(); } } void PortalHintProvider::loadCursorBlinkTime() { const int cursorBlinkTime = m_portalSettings.value(QStringLiteral("org.gnome.desktop.interface")).value(QStringLiteral("cursor-blink-time")).toInt(); setCursorBlinkTime(cursorBlinkTime); } void PortalHintProvider::loadCursorSize() { const int cursorSize = m_portalSettings.value(QStringLiteral("org.gnome.desktop.interface")).value(QStringLiteral("cursor-size")).toInt(); setCursorSize(cursorSize); } void PortalHintProvider::loadCursorTheme() { const QString cursorTheme = m_portalSettings.value(QStringLiteral("org.gnome.desktop.interface")).value(QStringLiteral("cursor-theme")).toString(); setCursorTheme(cursorTheme); } void PortalHintProvider::loadIconTheme() { const QString systemIconTheme = m_portalSettings.value(QStringLiteral("org.gnome.desktop.interface")).value(QStringLiteral("icon-theme")).toString(); setIconTheme(systemIconTheme); } void PortalHintProvider::loadFonts() { const QString fontName = m_portalSettings.value(QStringLiteral("org.gnome.desktop.interface")).value(QStringLiteral("font-name")).toString(); const QString monospaceFontName = m_portalSettings.value(QStringLiteral("org.gnome.desktop.interface")).value(QStringLiteral("monospace-font-name")).toString(); const QString titlebarFontName = m_portalSettings.value(QStringLiteral("org.gnome.desktop.wm.preferences")).value(QStringLiteral("titlebar-font")).toString(); setFonts(fontName, monospaceFontName, titlebarFontName); } void PortalHintProvider::loadTitlebar() { const QString buttonLayout = m_portalSettings.value(QStringLiteral("org.gnome.desktop.wm.preferences")).value(QStringLiteral("button-layout")).toString(); setTitlebar(buttonLayout); } void PortalHintProvider::loadTheme() { const QString theme = m_portalSettings.value(QStringLiteral("org.gnome.desktop.interface")).value(QStringLiteral("gtk-theme")).toString(); const GnomeSettings::Appearance appearance = static_cast( m_portalSettings.value(QStringLiteral("org.freedesktop.appearance")).value(QStringLiteral("color-scheme")).toUInt()); setTheme(theme, appearance); } void PortalHintProvider::loadStaticHints() { int doubleClickTime = 400; int longPressTime = 500; int doubleClickDistance = 5; int startDragDistance = 8; uint passwordMaskDelay = 0; setStaticHints(doubleClickTime, longPressTime, doubleClickDistance, startDragDistance, passwordMaskDelay); } QGnomePlatform-0.9.2/src/common/portalhintprovider.h000066400000000000000000000032011446542274100226110ustar00rootroot00000000000000/* * Copyright (C) 2016-2022 Jan Grulich * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef PORTAL_HINT_PROVIDER_H #define PORTAL_HINT_PROVIDER_H #include "hintprovider.h" class QDBusVariant; class QFont; class QString; class QVariant; class PortalHintProvider : public HintProvider { Q_OBJECT public: explicit PortalHintProvider(QObject *parent = nullptr, bool asynchronous = false); virtual ~PortalHintProvider() = default; Q_SIGNALS: void settingsRecieved(); private Q_SLOTS: void settingChanged(const QString &group, const QString &key, const QDBusVariant &value); private: void onSettingsReceived(); void loadCursorBlinkTime(); void loadCursorSize(); void loadCursorTheme(); void loadIconTheme(); void loadFonts(); void loadTheme(); void loadTitlebar(); void loadStaticHints(); QMap m_portalSettings; }; #endif // PORTAL_HINT_PROVIDER_H QGnomePlatform-0.9.2/src/common/utils.cpp000066400000000000000000000075401446542274100203570ustar00rootroot00000000000000/* * Copyright (C) 2016-2022 Jan Grulich * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "utils.h" #include #include namespace Utils { // FIXME: duplicate QFont *qt_fontFromString(const QString &name) { QFont *font = new QFont(QLatin1String("Sans"), 10); PangoFontDescription *desc = pango_font_description_from_string(name.toUtf8()); font->setPointSizeF(static_cast(pango_font_description_get_size(desc)) / PANGO_SCALE); QString family = QString::fromUtf8(pango_font_description_get_family(desc)); if (!family.isEmpty()) { font->setFamily(family); } const int weight = pango_font_description_get_weight(desc); if (weight >= PANGO_WEIGHT_HEAVY) { font->setWeight(QFont::Black); } else if (weight >= PANGO_WEIGHT_ULTRABOLD) { font->setWeight(QFont::ExtraBold); } else if (weight >= PANGO_WEIGHT_BOLD) { font->setWeight(QFont::Bold); } else if (weight >= PANGO_WEIGHT_SEMIBOLD) { font->setWeight(QFont::DemiBold); } else if (weight >= PANGO_WEIGHT_MEDIUM) { font->setWeight(QFont::Medium); } else if (weight >= PANGO_WEIGHT_NORMAL) { font->setWeight(QFont::Normal); } else if (weight >= PANGO_WEIGHT_LIGHT) { font->setWeight(QFont::Light); } else if (weight >= PANGO_WEIGHT_ULTRALIGHT) { font->setWeight(QFont::ExtraLight); } else { font->setWeight(QFont::Thin); } PangoStyle style = pango_font_description_get_style(desc); if (style == PANGO_STYLE_ITALIC) { font->setStyle(QFont::StyleItalic); } else if (style == PANGO_STYLE_OBLIQUE) { font->setStyle(QFont::StyleOblique); } else { font->setStyle(QFont::StyleNormal); } pango_font_description_free(desc); return font; } GnomeSettings::TitlebarButtons titlebarButtonsFromString(const QString &layout) { const QStringList btnList = layout.split(QLatin1Char(':')); if (btnList.count() == 2) { const QString &leftButtons = btnList.first(); const QString &rightButtons = btnList.last(); // TODO support button order GnomeSettings::TitlebarButtons buttons; if (leftButtons.contains(QStringLiteral("close")) || rightButtons.contains("close")) { buttons = buttons | GnomeSettings::CloseButton; } if (leftButtons.contains(QStringLiteral("maximize")) || rightButtons.contains("maximize")) { buttons = buttons | GnomeSettings::MaximizeButton; } if (leftButtons.contains(QStringLiteral("minimize")) || rightButtons.contains("minimize")) { buttons = buttons | GnomeSettings::MinimizeButton; } return buttons; } return GnomeSettings::CloseButton; } GnomeSettings::TitlebarButtonsPlacement titlebarButtonPlacementFromString(const QString &layout) { const QStringList btnList = layout.split(QLatin1Char(':')); if (btnList.count() == 2) { const QString &leftButtons = btnList.first(); return leftButtons.contains(QStringLiteral("close")) ? GnomeSettings::LeftPlacement : GnomeSettings::RightPlacement; } return GnomeSettings::RightPlacement; } } QGnomePlatform-0.9.2/src/common/utils.h000066400000000000000000000022231446542274100200150ustar00rootroot00000000000000/* * Copyright (C) 2016-2022 Jan Grulich * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef UTILS_H #define UTILS_H #include "gnomesettings.h" class QFont; class QString; namespace Utils { QFont *qt_fontFromString(const QString &name); GnomeSettings::TitlebarButtons titlebarButtonsFromString(const QString &layout); GnomeSettings::TitlebarButtonsPlacement titlebarButtonPlacementFromString(const QString &layout); } #endif // UTILS_H QGnomePlatform-0.9.2/src/decoration/000077500000000000000000000000001446542274100173445ustar00rootroot00000000000000QGnomePlatform-0.9.2/src/decoration/CMakeLists.txt000066400000000000000000000013601446542274100221040ustar00rootroot00000000000000 include_directories( ${CMAKE_SOURCE_DIR}/src/common ) set(decoration_SRCS decorationplugin.cpp qgnomeplatformdecoration.cpp ) add_library(qgnomeplatformdecoration MODULE ${decoration_SRCS}) target_link_libraries(qgnomeplatformdecoration qgnomeplatform${LIBQGNOMEPLATFORM_SUFFIX} Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::GuiPrivate Qt${QT_VERSION_MAJOR}::WaylandClientPrivate ${ADWAITAQT_LIBRARIES} PkgConfig::GTK+3 ) if (NOT USE_QT6) if (${Qt5XkbCommonSupport_FOUND}) target_link_libraries(qgnomeplatformdecoration Qt${QT_VERSION_MAJOR}::XkbCommonSupportPrivate ) endif() endif() install(TARGETS qgnomeplatformdecoration DESTINATION ${QT_PLUGINS_DIR}/wayland-decoration-client) QGnomePlatform-0.9.2/src/decoration/decorationplugin.cpp000066400000000000000000000021741446542274100234220ustar00rootroot00000000000000/* * Copyright (C) 2019-2022 Jan Grulich * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "decorationplugin.h" #include "qgnomeplatformdecoration.h" QWaylandAbstractDecoration *QGnomePlatformDecorationPlugin::create(const QString &key, const QStringList ¶mList) { Q_UNUSED(paramList) if (key == "gnome" || key == "qgnomeplatform") return new QGnomePlatformDecoration(); return nullptr; } QGnomePlatform-0.9.2/src/decoration/decorationplugin.h000066400000000000000000000024231446542274100230640ustar00rootroot00000000000000/* * Copyright (C) 2019-2022 Jan Grulich * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef DECORATIONPLUGIN_H #define DECORATIONPLUGIN_H #include using namespace QtWaylandClient; class QGnomePlatformDecorationPlugin : public QWaylandDecorationPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID QWaylandDecorationFactoryInterface_iid FILE "qgnomeplatformdecoration.json") public: QWaylandAbstractDecoration *create(const QString &key, const QStringList ¶mList) override; }; #endif // DECORATIONPLUGIN_H QGnomePlatform-0.9.2/src/decoration/qgnomeplatformdecoration.cpp000066400000000000000000001102031446542274100251500ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2019-2022 Jan Grulich ** Copyright (C) 2016 Robin Burchell ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qgnomeplatformdecoration.h" #include "gnomesettings.h" #include #include #include #include #include #include #include #include #include #include #include #include // Button sizing #define BUTTON_MARGINS 6 #define BUTTON_SPACING 8 #define BUTTON_WIDTH 28 // Decoration sizing #define SHADOWS_WIDTH 10 #define TITLEBAR_HEIGHT 37 #define WINDOW_BORDER_WIDTH 1 #define TITLEBAR_SEPARATOR_SIZE 0.5 Q_DECL_IMPORT void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0); QGnomePlatformDecoration::QGnomePlatformDecoration() : m_closeButtonHovered(false) , m_maximizeButtonHovered(false) , m_minimizeButtonHovered(false) { m_lastButtonClick = QDateTime::currentDateTime(); QTextOption option(Qt::AlignHCenter | Qt::AlignVCenter); option.setWrapMode(QTextOption::NoWrap); m_windowTitle.setTextOption(option); connect(&GnomeSettings::getInstance(), &GnomeSettings::themeChanged, this, [this]() { loadConfiguration(); forceRepaint(); }); connect(&GnomeSettings::getInstance(), &GnomeSettings::titlebarChanged, this, [this]() { loadConfiguration(); forceRepaint(); }); loadConfiguration(); } QRectF QGnomePlatformDecoration::closeButtonRect() const { if (GnomeSettings::getInstance().titlebarButtonPlacement() == GnomeSettings::getInstance().RightPlacement) { return QRectF(windowContentGeometry().width() - BUTTON_WIDTH - (BUTTON_SPACING * 0) - BUTTON_MARGINS - margins().right(), (margins().top() - BUTTON_WIDTH + margins().bottom()) / 2, BUTTON_WIDTH, BUTTON_WIDTH); } else { return QRectF(BUTTON_SPACING * 0 + BUTTON_MARGINS + margins().left(), (margins().top() - BUTTON_WIDTH + margins().bottom()) / 2, BUTTON_WIDTH, BUTTON_WIDTH); } } QRectF QGnomePlatformDecoration::maximizeButtonRect() const { if (GnomeSettings::getInstance().titlebarButtonPlacement() == GnomeSettings::getInstance().RightPlacement) { return QRectF(windowContentGeometry().width() - (BUTTON_WIDTH * 2) - (BUTTON_SPACING * 1) - BUTTON_MARGINS - margins().right(), (margins().top() - BUTTON_WIDTH + margins().bottom()) / 2, BUTTON_WIDTH, BUTTON_WIDTH); } else { return QRectF(BUTTON_WIDTH * 1 + (BUTTON_SPACING * 1) + BUTTON_MARGINS + margins().left(), (margins().top() - BUTTON_WIDTH + margins().bottom()) / 2, BUTTON_WIDTH, BUTTON_WIDTH); } } QRectF QGnomePlatformDecoration::minimizeButtonRect() const { const bool maximizeEnabled = GnomeSettings::getInstance().titlebarButtons().testFlag(GnomeSettings::getInstance().MaximizeButton); if (GnomeSettings::getInstance().titlebarButtonPlacement() == GnomeSettings::getInstance().RightPlacement) { return QRectF(windowContentGeometry().width() - BUTTON_WIDTH * (maximizeEnabled ? 3 : 2) - (BUTTON_SPACING * (maximizeEnabled ? 2 : 1)) - BUTTON_MARGINS - margins().right(), (margins().top() - BUTTON_WIDTH + margins().bottom()) / 2, BUTTON_WIDTH, BUTTON_WIDTH); } else { return QRectF(BUTTON_WIDTH * (maximizeEnabled ? 2 : 1) + (BUTTON_SPACING * (maximizeEnabled ? 2 : 1)) + BUTTON_MARGINS + margins().left(), (margins().top() - BUTTON_WIDTH + margins().bottom()) / 2, BUTTON_WIDTH, BUTTON_WIDTH); } } #ifdef DECORATION_SHADOWS_SUPPORT // Qt 6.2.0+ or patched QtWayland QMargins QGnomePlatformDecoration::margins(MarginsType marginsType) const { const bool maximized = waylandWindow()->windowStates() & Qt::WindowMaximized; const bool tiledLeft = waylandWindow()->toplevelWindowTilingStates() & QWaylandWindow::WindowTiledLeft; const bool tiledRight = waylandWindow()->toplevelWindowTilingStates() & QWaylandWindow::WindowTiledRight; const bool tiledTop = waylandWindow()->toplevelWindowTilingStates() & QWaylandWindow::WindowTiledTop; const bool tiledBottom = waylandWindow()->toplevelWindowTilingStates() & QWaylandWindow::WindowTiledBottom; if (marginsType == Full || marginsType == ShadowsExcluded) { // For maximized window, we only include window title, no borders and no shadows if (maximized) { return QMargins(0, TITLEBAR_HEIGHT, 0, 0); } // Specifically requsted margins with shadows excluded, only on non-tiled side if (marginsType == ShadowsExcluded) { return QMargins(tiledLeft ? 0 : WINDOW_BORDER_WIDTH, tiledTop ? TITLEBAR_HEIGHT : TITLEBAR_HEIGHT + WINDOW_BORDER_WIDTH, tiledRight ? 0 : WINDOW_BORDER_WIDTH, tiledBottom ? 0 : WINDOW_BORDER_WIDTH); } // Otherwise include borders and shadows only on non-tiled side return QMargins(tiledLeft ? 0 : WINDOW_BORDER_WIDTH + SHADOWS_WIDTH, tiledTop ? TITLEBAR_HEIGHT : TITLEBAR_HEIGHT + WINDOW_BORDER_WIDTH + SHADOWS_WIDTH, tiledRight ? 0 : WINDOW_BORDER_WIDTH + SHADOWS_WIDTH, tiledBottom ? 0 : WINDOW_BORDER_WIDTH + SHADOWS_WIDTH); } else { // (marginsType == ShadowsOnly) // For maximized window, we only include window title, no borders and no shadows if (maximized) { return QMargins(); } else { // For tiled window, we only include shadows on non-tiled side return QMargins(tiledLeft ? 0 : SHADOWS_WIDTH, tiledTop ? 0 : SHADOWS_WIDTH, tiledRight ? 0 : SHADOWS_WIDTH, tiledBottom ? 0 : SHADOWS_WIDTH); } } #else QMargins QGnomePlatformDecoration::margins() const { if ((window()->windowStates() & Qt::WindowMaximized)) { return QMargins(0, TITLEBAR_HEIGHT, 0, 0); } return QMargins(WINDOW_BORDER_WIDTH, // Left TITLEBAR_HEIGHT + WINDOW_BORDER_WIDTH, // Top WINDOW_BORDER_WIDTH, // Right WINDOW_BORDER_WIDTH); // Bottom #endif } void QGnomePlatformDecoration::paint(QPaintDevice *device) { #ifdef DECORATION_SHADOWS_SUPPORT // Qt 6.2.0+ or patched QtWayland const Qt::WindowStates windowStates = waylandWindow()->windowStates(); const bool active = windowStates & Qt::WindowActive; #else const Qt::WindowStates windowStates = window()->windowStates(); const bool active = window()->handle()->isActive(); #endif const QRect surfaceRect = windowContentGeometry(); const QColor borderColor = active ? m_borderColor : m_borderInactiveColor; QPainter p(device); p.setRenderHint(QPainter::Antialiasing); #ifdef DECORATION_SHADOWS_SUPPORT // Qt 6.2.0+ or patched QtWayland const bool maximized = windowStates & Qt::WindowMaximized; const bool tiledLeft = waylandWindow()->toplevelWindowTilingStates() & QWaylandWindow::WindowTiledLeft; const bool tiledRight = waylandWindow()->toplevelWindowTilingStates() & QWaylandWindow::WindowTiledRight; const bool tiledTop = waylandWindow()->toplevelWindowTilingStates() & QWaylandWindow::WindowTiledTop; const bool tiledBottom = waylandWindow()->toplevelWindowTilingStates() & QWaylandWindow::WindowTiledBottom; // Shadows // ******************************** // * * // * * // * * // * * // * * // * * // * * // ******************************** if (active && !(maximized || tiledBottom || tiledTop || tiledRight || tiledLeft)) { if (m_shadowPixmap.size() != surfaceRect.size()) { QPixmap source = QPixmap(surfaceRect.size()); source.fill(Qt::transparent); { QPainter tmpPainter(&source); tmpPainter.setBrush(borderColor); tmpPainter.drawRoundedRect(SHADOWS_WIDTH, // Do not paint over shadows SHADOWS_WIDTH, // Do not paint over shadows surfaceRect.width() - (2 * SHADOWS_WIDTH), // Full width - shadows surfaceRect.height() / 2, // Half of the full height 8, 8); tmpPainter.drawRect(SHADOWS_WIDTH, // Do not paint over shadows surfaceRect.height() / 2, // Start somewhere in the middle surfaceRect.width() - (2 * SHADOWS_WIDTH), // Full width - shadows (surfaceRect.height() / 2) - SHADOWS_WIDTH); // Half of the full height - shadows tmpPainter.end(); } QImage backgroundImage(surfaceRect.size(), QImage::Format_ARGB32_Premultiplied); backgroundImage.fill(0); QPainter backgroundPainter(&backgroundImage); backgroundPainter.drawPixmap(QPointF(), source); backgroundPainter.end(); QImage blurredImage(surfaceRect.size(), QImage::Format_ARGB32_Premultiplied); blurredImage.fill(0); { QPainter blurPainter(&blurredImage); qt_blurImage(&blurPainter, backgroundImage, 12, false, false); blurPainter.end(); } backgroundImage = blurredImage; backgroundPainter.begin(&backgroundImage); backgroundPainter.setCompositionMode(QPainter::CompositionMode_SourceIn); QRect rect = backgroundImage.rect().marginsRemoved(QMargins(8, 8, 8, 8)); backgroundPainter.fillRect(rect, QColor(0, 0, 0, 160)); backgroundPainter.end(); m_shadowPixmap = QPixmap::fromImage(backgroundImage); } QRect clips[] = { QRect(0, 0, surfaceRect.width(), margins().top()), QRect(0, margins().top(), margins().left(), surfaceRect.height() - margins().top() - margins().bottom()), QRect(0, surfaceRect.height() - margins().bottom(), surfaceRect.width(), margins().bottom()), QRect(surfaceRect.width() - margins().right(), margins().top(), margins().right(), surfaceRect.height() - margins().top() - margins().bottom())}; for (int i = 0; i < 4; ++i) { p.save(); p.setClipRect(clips[i]); p.drawPixmap(QPoint(), m_shadowPixmap); p.restore(); } } // Title bar (border) - painted only when the window is not maximized or tiled // ******************************** // *------------------------------* // *| |* // *------------------------------* // * * // * * // * * // * * // * * // * * // ******************************** QPainterPath borderRect; if (!(maximized || tiledLeft || tiledRight)) { borderRect.addRoundedRect(SHADOWS_WIDTH, SHADOWS_WIDTH, surfaceRect.width() - (2 * SHADOWS_WIDTH), margins().top() + 8, 10, 10); p.fillPath(borderRect.simplified(), borderColor); } // Title bar // ******************************** // *------------------------------* // *|############################|* // * * // * * // * * // * * // * * // * * // * * // ******************************** QPainterPath roundedRect; if (maximized || tiledRight || tiledLeft) { roundedRect.addRect(margins().left(), margins().bottom(), surfaceRect.width() - margins().left() - margins().right(), margins().top() + 8); } else { roundedRect.addRoundedRect(margins().left(), margins().bottom(), surfaceRect.width() - margins().left() - margins().right(), margins().top() + 8, 8, 8); } QLinearGradient gradient(margins().left(), margins().top() + 6, margins().left(), 1); gradient.setColorAt(0, active ? m_backgroundColorStart : m_backgroundInactiveColor); gradient.setColorAt(1, active ? m_backgroundColorEnd : m_backgroundInactiveColor); p.fillPath(roundedRect.simplified(), gradient); // Border around // ******************************** // *------------------------------* // *|############################|* // *| |* // *| |* // *| |* // *| |* // *| |* // *| |* // *------------------------------* // ******************************** if (!maximized) { QPainterPath borderPath; // Left if (!tiledLeft) { // Assume tiled-left also means it will be tiled-top and tiled bottom borderPath.addRect(SHADOWS_WIDTH, tiledTop || tiledBottom ? 0 : margins().top(), WINDOW_BORDER_WIDTH, tiledTop || tiledBottom ? surfaceRect.height() : surfaceRect.height() - margins().top() - SHADOWS_WIDTH - WINDOW_BORDER_WIDTH); } // Bottom if (!tiledBottom) { borderPath.addRect(SHADOWS_WIDTH, surfaceRect.height() - SHADOWS_WIDTH - WINDOW_BORDER_WIDTH, surfaceRect.width() - (2 * SHADOWS_WIDTH), WINDOW_BORDER_WIDTH); } // Right if (!tiledRight) { borderPath.addRect(surfaceRect.width() - margins().right(), tiledTop || tiledBottom ? 0 : margins().top(), WINDOW_BORDER_WIDTH, tiledTop || tiledBottom ? surfaceRect.height() : surfaceRect.height() - margins().top() - SHADOWS_WIDTH - WINDOW_BORDER_WIDTH); } p.fillPath(borderPath, borderColor); } #else // Title bar (border) // ******************************** // *------------------------------* // *| |* // *------------------------------* // * * // * * // * * // * * // * * // * * // ******************************** QPainterPath borderRect; if (!(windowStates & Qt::WindowMaximized)) { borderRect.addRoundedRect(0, 0, surfaceRect.width(), margins().top() + 8, 10, 10); p.fillPath(borderRect.simplified(), borderColor); } // Title bar // ******************************** // *------------------------------* // *|############################|* // * * // * * // * * // * * // * * // * * // * * // ******************************** QPainterPath roundedRect; if ((windowStates & Qt::WindowMaximized)) { roundedRect.addRect(0, 0, surfaceRect.width(), margins().top() + 8); } else { roundedRect .addRoundedRect(WINDOW_BORDER_WIDTH, WINDOW_BORDER_WIDTH, surfaceRect.width() - margins().left() - margins().right(), margins().top() + 8, 8, 8); } QLinearGradient gradient(margins().left(), margins().top() + 6, margins().left(), 1); gradient.setColorAt(0, active ? m_backgroundColorStart : m_backgroundInactiveColor); gradient.setColorAt(1, active ? m_backgroundColorEnd : m_backgroundInactiveColor); p.fillPath(roundedRect.simplified(), gradient); // Border around // ******************************** // *------------------------------* // *|############################|* // *| |* // *| |* // *| |* // *| |* // *| |* // *| |* // *------------------------------* // ******************************** if (!(windowStates & Qt::WindowMaximized)) { QPainterPath borderPath; // Left borderPath.addRect(0, margins().top(), margins().left(), surfaceRect.height() - margins().top() - WINDOW_BORDER_WIDTH); // Bottom borderPath.addRect(0, surfaceRect.height() - WINDOW_BORDER_WIDTH, surfaceRect.width(), WINDOW_BORDER_WIDTH); // Right borderPath.addRect(surfaceRect.width() - margins().right(), margins().top(), WINDOW_BORDER_WIDTH, surfaceRect.height() - margins().bottom() - margins().top()); p.fillPath(borderPath, borderColor); } #endif // Border between window and decorations // ******************************** // *------------------------------* // *|############################|* // *------------------------------* // *| |* // *| |* // *| |* // *| |* // *| |* // *------------------------------* // ******************************** p.save(); p.setPen(borderColor); p.drawLine(QLineF(margins().left(), margins().top() - TITLEBAR_SEPARATOR_SIZE, surfaceRect.width() - margins().right(), margins().top() - TITLEBAR_SEPARATOR_SIZE)); p.restore(); // Window title // ******************************** // *------------------------------* // *|########## FOO #############|* // *------------------------------* // *| |* // *| |* // *| |* // *| |* // *| |* // *------------------------------* // ******************************** const QRect top = QRect(margins().left(), margins().bottom(), surfaceRect.width(), margins().top() - margins().bottom()); const QString windowTitleText = window()->title(); if (!windowTitleText.isEmpty()) { if (m_windowTitle.text() != windowTitleText) { m_windowTitle.setText(windowTitleText); m_windowTitle.prepare(); } QRect titleBar = top; if (GnomeSettings::getInstance().titlebarButtonPlacement() == GnomeSettings::getInstance().RightPlacement) { titleBar.setLeft(margins().left()); titleBar.setRight(static_cast(minimizeButtonRect().left()) - 8); } else { titleBar.setLeft(static_cast(minimizeButtonRect().right()) + 8); titleBar.setRight(surfaceRect.width() - margins().right()); } p.save(); p.setClipRect(titleBar); p.setPen(active ? m_foregroundColor : m_foregroundInactiveColor); QSizeF size = m_windowTitle.size(); int dx = (static_cast(top.width()) - static_cast(size.width())) / 2; int dy = (static_cast(top.height()) - static_cast(size.height())) / 2; QFont font; const QFont *themeFont = GnomeSettings::getInstance().font(QPlatformTheme::TitleBarFont); font.setPointSizeF(themeFont->pointSizeF()); font.setFamily(themeFont->family()); font.setBold(themeFont->bold()); p.setFont(font); QPoint windowTitlePoint(top.topLeft().x() + dx, top.topLeft().y() + dy); p.drawStaticText(windowTitlePoint, m_windowTitle); p.restore(); } // Close button renderButton(&p, closeButtonRect(), Adwaita::ButtonType::ButtonClose, m_closeButtonHovered && active, m_clicking == Button::Close); // Maximize button if (GnomeSettings::getInstance().titlebarButtons().testFlag(GnomeSettings::getInstance().MaximizeButton)) { renderButton(&p, maximizeButtonRect(), (windowStates & Qt::WindowMaximized) ? Adwaita::ButtonType::ButtonRestore : Adwaita::ButtonType::ButtonMaximize, m_maximizeButtonHovered && active, m_clicking == Button::Maximize || m_clicking == Button::Restore); } // Minimize button if (GnomeSettings::getInstance().titlebarButtons().testFlag(GnomeSettings::getInstance().MinimizeButton)) { renderButton(&p, minimizeButtonRect(), Adwaita::ButtonType::ButtonMinimize, m_minimizeButtonHovered && active, m_clicking == Button::Minimize); } } bool QGnomePlatformDecoration::clickButton(Qt::MouseButtons b, Button btn) { if (isLeftClicked(b)) { m_clicking = btn; return false; } else if (isLeftReleased(b)) { if (m_clicking == btn) { m_clicking = None; return true; } else { m_clicking = None; } } return false; } bool QGnomePlatformDecoration::doubleClickButton(Qt::MouseButtons b, const QPointF &local, const QDateTime ¤tTime) { if (b & Qt::LeftButton) { const qint64 clickInterval = m_lastButtonClick.msecsTo(currentTime); m_lastButtonClick = currentTime; const int doubleClickDistance = GnomeSettings::getInstance().hint(QPlatformTheme::MouseDoubleClickDistance).toInt(); const QPointF posDiff = m_lastButtonClickPosition - local; if ((clickInterval <= GnomeSettings::getInstance().hint(QPlatformTheme::MouseDoubleClickInterval).toInt()) && ((posDiff.x() <= doubleClickDistance && posDiff.x() >= -doubleClickDistance) && ((posDiff.y() <= doubleClickDistance && posDiff.y() >= -doubleClickDistance)))) { return true; } m_lastButtonClickPosition = local; } return false; } bool QGnomePlatformDecoration::handleMouse(QWaylandInputDevice *inputDevice, const QPointF &local, const QPointF &global, Qt::MouseButtons b, Qt::KeyboardModifiers mods) { Q_UNUSED(global) if (local.y() > margins().top()) { updateButtonHoverState(Button::None); } // Figure out what area mouse is in QRect surfaceRect = windowContentGeometry(); if (local.y() <= surfaceRect.top() + margins().top()) { processMouseTop(inputDevice, local, b, mods); } else if (local.y() > surfaceRect.bottom() - margins().bottom()) { processMouseBottom(inputDevice, local, b, mods); } else if (local.x() <= surfaceRect.left() + margins().left()) { processMouseLeft(inputDevice, local, b, mods); } else if (local.x() > surfaceRect.right() - margins().right()) { processMouseRight(inputDevice, local, b, mods); } else { #if QT_CONFIG(cursor) waylandWindow()->restoreMouseCursor(inputDevice); #endif setMouseButtons(b); return false; } setMouseButtons(b); return true; } #if QT_VERSION >= 0x060000 bool QGnomePlatformDecoration::handleTouch(QWaylandInputDevice *inputDevice, const QPointF &local, const QPointF &global, QEventPoint::State state, Qt::KeyboardModifiers mods) #else bool QGnomePlatformDecoration::handleTouch(QWaylandInputDevice *inputDevice, const QPointF &local, const QPointF &global, Qt::TouchPointState state, Qt::KeyboardModifiers mods) #endif { Q_UNUSED(inputDevice) Q_UNUSED(global) Q_UNUSED(mods) #if QT_VERSION >= 0x060000 bool handled = state == QEventPoint::Pressed; #else bool handled = state == Qt::TouchPointPressed; #endif if (handled) { if (closeButtonRect().contains(local)) { QWindowSystemInterface::handleCloseEvent(window()); } else if (GnomeSettings::getInstance().titlebarButtons().testFlag(GnomeSettings::getInstance().MaximizeButton) && maximizeButtonRect().contains(local)) { window()->setWindowStates(window()->windowStates() ^ Qt::WindowMaximized); } else if (GnomeSettings::getInstance().titlebarButtons().testFlag(GnomeSettings::getInstance().MinimizeButton) && minimizeButtonRect().contains(local)) { window()->setWindowState(Qt::WindowMinimized); } else if (local.y() <= margins().top()) { waylandWindow()->shellSurface()->move(inputDevice); } else { handled = false; } } return handled; } QRect QGnomePlatformDecoration::windowContentGeometry() const { #ifdef DECORATION_SHADOWS_SUPPORT // Qt 6.2.0+ or patched QtWayland return waylandWindow()->windowContentGeometry() + margins(ShadowsOnly); #else return waylandWindow()->windowContentGeometry(); #endif } void QGnomePlatformDecoration::loadConfiguration() { // Colors // TODO: move colors used for decorations to Adwaita-qt const bool darkVariant = GnomeSettings::getInstance().useGtkThemeDarkVariant(); const bool highContrastVariant = GnomeSettings::getInstance().useGtkThemeHighContrastVariant(); m_adwaitaVariant = darkVariant ? highContrastVariant ? Adwaita::ColorVariant::AdwaitaHighcontrastInverse : Adwaita::ColorVariant::AdwaitaDark : highContrastVariant ? Adwaita::ColorVariant::AdwaitaHighcontrast : Adwaita::ColorVariant::Adwaita; const QPalette &palette(Adwaita::Colors::palette(m_adwaitaVariant)); m_foregroundColor = palette.color(QPalette::Active, QPalette::WindowText); m_foregroundInactiveColor = palette.color(QPalette::Inactive, QPalette::WindowText); m_backgroundColorStart = darkVariant ? QColor("#262626") : QColor("#dad6d2"); // Adwaita GtkHeaderBar color m_backgroundColorEnd = darkVariant ? QColor("#2b2b2b") : QColor("#e1dedb"); // Adwaita GtkHeaderBar color m_foregroundInactiveColor = darkVariant ? QColor("#919190") : QColor("#929595"); m_backgroundInactiveColor = darkVariant ? QColor("#353535") : QColor("#f6f5f4"); m_borderColor = darkVariant ? Adwaita::Colors::transparentize(QColor("#1b1b1b"), 0.1) : Adwaita::Colors::transparentize(QColor("black"), 0.77); m_borderInactiveColor = darkVariant ? Adwaita::Colors::transparentize(QColor("#1b1b1b"), 0.1) : Adwaita::Colors::transparentize(QColor("black"), 0.82); } void QGnomePlatformDecoration::forceRepaint() { // Set dirty flag waylandWindow()->decoration()->update(); // Force re-paint // NOTE: not sure it's correct, but it's the only way to make it work if (waylandWindow()->backingStore()) { waylandWindow()->backingStore()->flush(window(), QRegion(), QPoint()); } } void QGnomePlatformDecoration::processMouseTop(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b, Qt::KeyboardModifiers mods) { Q_UNUSED(mods) QDateTime currentDateTime = QDateTime::currentDateTime(); QRect surfaceRect = windowContentGeometry(); if (!closeButtonRect().contains(local) && !maximizeButtonRect().contains(local) && !minimizeButtonRect().contains(local)) { updateButtonHoverState(Button::None); } if (local.y() <= surfaceRect.top() + margins().bottom()) { if (local.x() <= margins().left()) { // top left bit #if QT_CONFIG(cursor) waylandWindow()->setMouseCursor(inputDevice, Qt::SizeFDiagCursor); #endif startResize(inputDevice, Qt::TopEdge | Qt::LeftEdge, b); } else if (local.x() > surfaceRect.right() - margins().left()) { // top right bit #if QT_CONFIG(cursor) waylandWindow()->setMouseCursor(inputDevice, Qt::SizeBDiagCursor); #endif startResize(inputDevice, Qt::TopEdge | Qt::RightEdge, b); } else { // top resize bit #if QT_CONFIG(cursor) waylandWindow()->setMouseCursor(inputDevice, Qt::SplitVCursor); #endif startResize(inputDevice, Qt::TopEdge, b); } } else if (local.x() <= surfaceRect.left() + margins().left()) { processMouseLeft(inputDevice, local, b, mods); } else if (local.x() > surfaceRect.right() - margins().right()) { processMouseRight(inputDevice, local, b, mods); } else if (closeButtonRect().contains(local)) { if (clickButton(b, Close)) { QWindowSystemInterface::handleCloseEvent(window()); m_closeButtonHovered = false; } updateButtonHoverState(Button::Close); } else if (GnomeSettings::getInstance().titlebarButtons().testFlag(GnomeSettings::getInstance().MaximizeButton) && maximizeButtonRect().contains(local)) { updateButtonHoverState(Button::Maximize); if (clickButton(b, Maximize)) { window()->setWindowStates(window()->windowStates() ^ Qt::WindowMaximized); m_maximizeButtonHovered = false; } } else if (GnomeSettings::getInstance().titlebarButtons().testFlag(GnomeSettings::getInstance().MinimizeButton) && minimizeButtonRect().contains(local)) { updateButtonHoverState(Button::Minimize); if (clickButton(b, Minimize)) { window()->setWindowState(Qt::WindowMinimized); m_minimizeButtonHovered = false; } } else if (doubleClickButton(b, local, currentDateTime)) { window()->setWindowStates(window()->windowStates() ^ Qt::WindowMaximized); } else { // Show window menu if (b == Qt::MouseButton::RightButton) { waylandWindow()->shellSurface()->showWindowMenu(inputDevice); } #if QT_CONFIG(cursor) waylandWindow()->restoreMouseCursor(inputDevice); #endif startMove(inputDevice, b); } } void QGnomePlatformDecoration::processMouseBottom(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b, Qt::KeyboardModifiers mods) { Q_UNUSED(mods) if (local.x() <= margins().left()) { // bottom left bit #if QT_CONFIG(cursor) waylandWindow()->setMouseCursor(inputDevice, Qt::SizeBDiagCursor); #endif startResize(inputDevice, Qt::BottomEdge | Qt::LeftEdge, b); } else if (local.x() > window()->width() + margins().right()) { // bottom right bit #if QT_CONFIG(cursor) waylandWindow()->setMouseCursor(inputDevice, Qt::SizeFDiagCursor); #endif startResize(inputDevice, Qt::BottomEdge | Qt::RightEdge, b); } else { // bottom bit #if QT_CONFIG(cursor) waylandWindow()->setMouseCursor(inputDevice, Qt::SplitVCursor); #endif startResize(inputDevice, Qt::BottomEdge, b); } } void QGnomePlatformDecoration::processMouseLeft(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b, Qt::KeyboardModifiers mods) { Q_UNUSED(local) Q_UNUSED(mods) #if QT_CONFIG(cursor) waylandWindow()->setMouseCursor(inputDevice, Qt::SplitHCursor); #endif startResize(inputDevice, Qt::LeftEdge, b); } void QGnomePlatformDecoration::processMouseRight(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b, Qt::KeyboardModifiers mods) { Q_UNUSED(local) Q_UNUSED(mods) #if QT_CONFIG(cursor) waylandWindow()->setMouseCursor(inputDevice, Qt::SplitHCursor); #endif startResize(inputDevice, Qt::RightEdge, b); } void QGnomePlatformDecoration::renderButton(QPainter *painter, const QRectF &rect, Adwaita::ButtonType button, bool renderFrame, bool sunken) { #ifdef DECORATION_SHADOWS_SUPPORT // Qt 6.2.0+ or patched QtWayland const Qt::WindowStates windowStates = waylandWindow()->windowStates(); const bool active = windowStates & Qt::WindowActive; #else const Qt::WindowStates windowStates = window()->windowStates(); const bool active = window()->handle()->isActive(); #endif Adwaita::StyleOptions decorationButtonStyle(painter, QRect()); decorationButtonStyle.setColor(active ? m_foregroundColor : m_foregroundInactiveColor); if (renderFrame) { QRect buttonRect(static_cast(rect.x()), static_cast(rect.y()), BUTTON_WIDTH, BUTTON_WIDTH); Adwaita::StyleOptions styleOptions(painter, buttonRect); styleOptions.setMouseOver(true); styleOptions.setSunken(sunken); styleOptions.setColorVariant(m_adwaitaVariant); styleOptions.setColor(Adwaita::Colors::buttonBackgroundColor(styleOptions)); styleOptions.setOutlineColor(Adwaita::Colors::buttonOutlineColor(styleOptions)); Adwaita::Renderer::renderFlatRoundedButtonFrame(styleOptions); } decorationButtonStyle.setRect( QRect(static_cast(rect.x()) + (BUTTON_WIDTH / 4), static_cast(rect.y()) + (BUTTON_WIDTH / 4), BUTTON_WIDTH / 2, BUTTON_WIDTH / 2)); Adwaita::Renderer::renderDecorationButton(decorationButtonStyle, button); } bool QGnomePlatformDecoration::updateButtonHoverState(Button hoveredButton) { bool currentCloseButtonState = m_closeButtonHovered; bool currentMaximizeButtonState = m_maximizeButtonHovered; bool currentMinimizeButtonState = m_minimizeButtonHovered; m_closeButtonHovered = hoveredButton == Button::Close; m_maximizeButtonHovered = hoveredButton == Button::Maximize; m_minimizeButtonHovered = hoveredButton == Button::Minimize; if (m_closeButtonHovered != currentCloseButtonState || m_maximizeButtonHovered != currentMaximizeButtonState || m_minimizeButtonHovered != currentMinimizeButtonState) { forceRepaint(); return true; } return false; } QGnomePlatform-0.9.2/src/decoration/qgnomeplatformdecoration.h000066400000000000000000000077011446542274100246250ustar00rootroot00000000000000/* * Copyright (C) 2019-2022 Jan Grulich * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef QGNOMEPLATFORMDECORATION_H #define QGNOMEPLATFORMDECORATION_H #include #if QT_VERSION >= 0x060000 #include #include #else #include #include #endif #include #include #include using namespace QtWaylandClient; enum Button { None, Close, Maximize, Minimize, Restore }; class QGnomePlatformDecoration : public QWaylandAbstractDecoration { public: QGnomePlatformDecoration(); virtual ~QGnomePlatformDecoration() override = default; protected: #ifdef DECORATION_SHADOWS_SUPPORT // Qt 6.2.0+ or patched QtWayland QMargins margins(MarginsType marginsType = Full) const override; #else QMargins margins() const override; #endif void paint(QPaintDevice *device) override; bool handleMouse(QWaylandInputDevice *inputDevice, const QPointF &local, const QPointF &global, Qt::MouseButtons b, Qt::KeyboardModifiers mods) override; #if QT_VERSION >= 0x060000 bool handleTouch(QWaylandInputDevice *inputDevice, const QPointF &local, const QPointF &global, QEventPoint::State state, Qt::KeyboardModifiers mods) override; #else bool handleTouch(QWaylandInputDevice *inputDevice, const QPointF &local, const QPointF &global, Qt::TouchPointState state, Qt::KeyboardModifiers mods) override; #endif private: QRect windowContentGeometry() const; void forceRepaint(); void loadConfiguration(); void processMouseTop(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b, Qt::KeyboardModifiers mods); void processMouseBottom(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b, Qt::KeyboardModifiers mods); void processMouseLeft(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b, Qt::KeyboardModifiers mods); void processMouseRight(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b, Qt::KeyboardModifiers mods); void renderButton(QPainter *painter, const QRectF &rect, Adwaita::ButtonType button, bool renderFrame, bool sunken); bool clickButton(Qt::MouseButtons b, Button btn); bool doubleClickButton(Qt::MouseButtons b, const QPointF &local, const QDateTime ¤tTime); bool updateButtonHoverState(Button hoveredButton); QRectF closeButtonRect() const; QRectF maximizeButtonRect() const; QRectF minimizeButtonRect() const; // Colors QColor m_backgroundColorStart; QColor m_backgroundColorEnd; QColor m_backgroundInactiveColor; QColor m_borderColor; QColor m_borderInactiveColor; QColor m_foregroundColor; QColor m_foregroundInactiveColor; // Buttons bool m_closeButtonHovered; bool m_maximizeButtonHovered; bool m_minimizeButtonHovered; // For double-click support QDateTime m_lastButtonClick; QPointF m_lastButtonClickPosition; Button m_doubleClicking = None; QStaticText m_windowTitle; Button m_clicking = None; // Shadows QPixmap m_shadowPixmap; Adwaita::ColorVariant m_adwaitaVariant; }; #endif // QGNOMEPLATFORMDECORATION_H QGnomePlatform-0.9.2/src/decoration/qgnomeplatformdecoration.json000066400000000000000000000000561446542274100253430ustar00rootroot00000000000000{ "Keys": [ "gnome", "qgnomeplatform" ] } QGnomePlatform-0.9.2/src/theme/000077500000000000000000000000001446542274100163175ustar00rootroot00000000000000QGnomePlatform-0.9.2/src/theme/CMakeLists.txt000066400000000000000000000013041446542274100210550ustar00rootroot00000000000000 include_directories( ${CMAKE_SOURCE_DIR}/src/common ) set(theme_SRCS platformplugin.cpp qgnomeplatformtheme.cpp qgtk3dialoghelpers.cpp qxdgdesktopportalfiledialog.cpp ) add_library(qgnomeplatformtheme MODULE ${theme_SRCS}) target_link_libraries(qgnomeplatformtheme qgnomeplatform${LIBQGNOMEPLATFORM_SUFFIX} Qt${QT_VERSION_MAJOR}::DBus Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::GuiPrivate Qt${QT_VERSION_MAJOR}::QuickControls2 ${ADWAITAQT_LIBRARIES} PkgConfig::GTK+3 ) if (NOT USE_QT6) target_link_libraries(qgnomeplatformtheme Qt5::ThemeSupportPrivate) endif() install(TARGETS qgnomeplatformtheme DESTINATION ${QT_PLUGINS_DIR}/platformthemes) QGnomePlatform-0.9.2/src/theme/platformplugin.cpp000066400000000000000000000022501446542274100220650ustar00rootroot00000000000000/* * Copyright (C) 2015 Martin Bříza * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "platformplugin.h" QGnomePlatformThemePlugin::QGnomePlatformThemePlugin(QObject *parent) : QPlatformThemePlugin(parent) { } QPlatformTheme *QGnomePlatformThemePlugin::create(const QString &key, const QStringList ¶mList) { Q_UNUSED(paramList) if (key == "gnome" || key == "qgnomeplatform") return new QGnomePlatformTheme(); return nullptr; } QGnomePlatform-0.9.2/src/theme/platformplugin.h000066400000000000000000000024751446542274100215430ustar00rootroot00000000000000/* * Copyright (C) 2015 Martin Bříza * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef PLATFORMPLUGIN_H #define PLATFORMPLUGIN_H #include #include #include "qgnomeplatformtheme.h" class QGnomePlatformThemePlugin : public QPlatformThemePlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QPA.QPlatformThemeFactoryInterface.5.1" FILE "qgnomeplatformtheme.json") public: QGnomePlatformThemePlugin(QObject *parent = 0); virtual QPlatformTheme *create(const QString &key, const QStringList ¶mList); }; #endif // PLATFORMPLUGIN_H QGnomePlatform-0.9.2/src/theme/qgnomeplatformtheme.cpp000066400000000000000000000156201446542274100231050ustar00rootroot00000000000000/* * Copyright (C) 2017-2022 Jan Grulich * Copyright (C) 2015 Martin Bříza * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "qgnomeplatformtheme.h" #include "gnomesettings.h" #include "qgtk3dialoghelpers.h" #include "qxdgdesktopportalfiledialog_p.h" #include #include #include #include #include #undef signals #include #define signals Q_SIGNALS #include #if QT_VERSION < 0x060000 #ifndef QT_NO_SYSTEMTRAYICON #include #endif #endif #if QT_VERSION > 0x060000 #include #endif Q_LOGGING_CATEGORY(QGnomePlatformThemeLog, "qt.qpa.qgnomeplatform.theme") void gtkMessageHandler(const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer unused_data) { /* Silence false-positive Gtk warnings (we are using Xlib to set * the WM_TRANSIENT_FOR hint). */ if (g_strcmp0(message, "GtkDialog mapped without a transient parent. " "This is discouraged.") != 0) { /* For other messages, call the default handler. */ g_log_default_handler(log_domain, log_level, message, unused_data); } } QGnomePlatformTheme::QGnomePlatformTheme() { if (QGuiApplication::platformName() != QStringLiteral("xcb")) { if (!qEnvironmentVariableIsSet("QT_WAYLAND_DECORATION")) { qputenv("QT_WAYLAND_DECORATION", "gnome"); } } // Ensure gtk uses the same windowing system, but let it // fallback in case GDK_BACKEND environment variable // filters the preferred one out if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"))) { gdk_set_allowed_backends("wayland,x11"); } else if (QGuiApplication::platformName() == QLatin1String("xcb")) { gdk_set_allowed_backends("x11,wayland"); } // Set log handler to suppress false GtkDialog warnings g_log_set_handler("Gtk", G_LOG_LEVEL_MESSAGE, gtkMessageHandler, nullptr); /* Initialize some types here so that Gtk+ does not crash when reading * the treemodel for GtkFontChooser. */ g_type_ensure(PANGO_TYPE_FONT_FAMILY); g_type_ensure(PANGO_TYPE_FONT_FACE); #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) // Load QGnomeTheme m_platformTheme = QGenericUnixTheme::createUnixTheme(QLatin1String("gnome")); #endif // Configure the Qt Quick Controls 2 style to the KDE desktop style, // Which passes the QtWidgets theme through to Qt Quick Controls. // From https://invent.kde.org/plasma/plasma-integration/-/blob/02fe12a55522a43de3efa6de2185a695ff2a576a/src/platformtheme/kdeplatformtheme.cpp#L582 // if the user has explicitly set something else, don't meddle // Also ignore the default Fusion style if (!QQuickStyle::name().isEmpty() && QQuickStyle::name() != QLatin1String("Fusion")) { return; } #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // Unfortunately we only have a way to check this on Qt5 if (!QQuickStyle::availableStyles().contains(QStringLiteral("org.kde.desktop"))) { qCWarning(QGnomePlatformThemeLog) << "The desktop style for QtQuick Controls 2 applications" << "is not available on the system (qqc2-desktop-style)." << "The application may look broken."; return; } QQuickStyle::setStyle(QStringLiteral("org.kde.desktop")); #endif } QGnomePlatformTheme::~QGnomePlatformTheme() { } QVariant QGnomePlatformTheme::themeHint(QPlatformTheme::ThemeHint hintType) const { QVariant hint = GnomeSettings::getInstance().hint(hintType); if (hint.isValid()) { return hint; } else { return QPlatformTheme::themeHint(hintType); } } const QFont *QGnomePlatformTheme::font(Font type) const { return GnomeSettings::getInstance().font(type); } const QPalette *QGnomePlatformTheme::palette(Palette type) const { Q_UNUSED(type) return GnomeSettings::getInstance().palette(); } bool QGnomePlatformTheme::usePlatformNativeDialog(QPlatformTheme::DialogType type) const { switch (type) { case QPlatformTheme::FileDialog: return true; case QPlatformTheme::FontDialog: return true; case QPlatformTheme::ColorDialog: return true; default: return false; } } QPlatformDialogHelper *QGnomePlatformTheme::createPlatformDialogHelper(QPlatformTheme::DialogType type) const { switch (type) { case QPlatformTheme::FileDialog: { if (GnomeSettings::getInstance().canUseFileChooserPortal()) { return new QXdgDesktopPortalFileDialog; } else { return new QGtk3FileDialogHelper; } } case QPlatformTheme::FontDialog: return new QGtk3FontDialogHelper(); case QPlatformTheme::ColorDialog: return new QGtk3ColorDialogHelper(); default: return nullptr; } } #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) #ifndef QT_NO_SYSTEMTRAYICON static bool isDBusTrayAvailable() { static bool dbusTrayAvailable = false; static bool dbusTrayAvailableKnown = false; if (!dbusTrayAvailableKnown) { QDBusMenuConnection conn; if (conn.isStatusNotifierHostRegistered()) { dbusTrayAvailable = true; } dbusTrayAvailableKnown = true; } return dbusTrayAvailable; } #endif #endif #ifndef QT_NO_SYSTEMTRAYICON QPlatformSystemTrayIcon *QGnomePlatformTheme::createPlatformSystemTrayIcon() const { #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) if (isDBusTrayAvailable()) { return new QDBusTrayIcon(); } #else if (m_platformTheme) { return m_platformTheme->createPlatformSystemTrayIcon(); } #endif return Q_NULLPTR; } #endif #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) Qt::ColorScheme QGnomePlatformTheme::colorScheme() const { return GnomeSettings::getInstance().useGtkThemeDarkVariant() ? Qt::ColorScheme::Dark : Qt::ColorScheme::Light; } #elif QT_VERSION >= QT_VERSION_CHECK(6, 3, 0) QPlatformTheme::Appearance QGnomePlatformTheme::appearance() const { return GnomeSettings::getInstance().useGtkThemeDarkVariant() ? Appearance::Dark : Appearance::Light; } #endif QGnomePlatform-0.9.2/src/theme/qgnomeplatformtheme.h000066400000000000000000000040721446542274100225510ustar00rootroot00000000000000/* * Copyright (C) 2017-2022 Jan Grulich * Copyright (C) 2015 Martin Bříza * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef QGNOME_PLATFORM_THEME_H #define QGNOME_PLATFORM_THEME_H #include #include #include #include class QGnomePlatformTheme : public QPlatformTheme { public: QGnomePlatformTheme(); ~QGnomePlatformTheme(); QVariant themeHint(ThemeHint hint) const Q_DECL_OVERRIDE; const QFont *font(Font type) const Q_DECL_OVERRIDE; const QPalette *palette(Palette type = SystemPalette) const Q_DECL_OVERRIDE; bool usePlatformNativeDialog(DialogType type) const Q_DECL_OVERRIDE; QPlatformDialogHelper *createPlatformDialogHelper(DialogType type) const Q_DECL_OVERRIDE; #ifndef QT_NO_SYSTEMTRAYICON QPlatformSystemTrayIcon *createPlatformSystemTrayIcon() const Q_DECL_OVERRIDE; #endif #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) Qt::ColorScheme colorScheme() const Q_DECL_OVERRIDE; #elif QT_VERSION >= QT_VERSION_CHECK(6, 3, 0) QPlatformTheme::Appearance appearance() const Q_DECL_OVERRIDE; #endif private: #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) // Used to load Qt's internall platform theme to get access to // non-public stuff, like QDBusTrayIcon QPlatformTheme *m_platformTheme = nullptr; #endif }; #endif // QGNOME_PLATFORM_THEME_H QGnomePlatform-0.9.2/src/theme/qgnomeplatformtheme.json000066400000000000000000000000561446542274100232710ustar00rootroot00000000000000{ "Keys": [ "gnome", "qgnomeplatform" ] } QGnomePlatform-0.9.2/src/theme/qgtk3dialoghelpers.cpp000066400000000000000000000542621446542274100226300ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qgtk3dialoghelpers.h" #include #include #include #include #include #include #include #include #undef signals #include #ifdef GDK_WINDOWING_X11 #include #endif #include #include QT_BEGIN_NAMESPACE // GTK file chooser image preview: thanks to Chromium // The size of the preview we display for selected image files. We set height // larger than width because generally there is more free space vertically // than horiztonally (setting the preview image will alway expand the width of // the dialog, but usually not the height). The image's aspect ratio will always // be preserved. #define PREVIEW_WIDTH 256 #define PREVIEW_HEIGHT 512 class QGtk3Dialog : public QWindow { Q_OBJECT public: QGtk3Dialog(GtkWidget *gtkWidget); ~QGtk3Dialog(); GtkDialog *gtkDialog() const; void exec(); bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent); void hide(); Q_SIGNALS: void accept(); void reject(); protected: static void onResponse(QGtk3Dialog *dialog, int response); private slots: void onParentWindowDestroyed(); private: GtkWidget *gtkWidget; }; QGtk3Dialog::QGtk3Dialog(GtkWidget *gtkWidget) : gtkWidget(gtkWidget) { g_signal_connect_swapped(G_OBJECT(gtkWidget), "response", G_CALLBACK(onResponse), this); g_signal_connect(G_OBJECT(gtkWidget), "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL); } QGtk3Dialog::~QGtk3Dialog() { gtk_clipboard_store(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD)); gtk_widget_destroy(gtkWidget); } GtkDialog *QGtk3Dialog::gtkDialog() const { return GTK_DIALOG(gtkWidget); } void QGtk3Dialog::exec() { if (modality() == Qt::ApplicationModal) { // block input to the whole app, including other GTK dialogs gtk_dialog_run(gtkDialog()); } else { // block input to the window, allow input to other GTK dialogs QEventLoop loop; connect(this, SIGNAL(accept()), &loop, SLOT(quit())); connect(this, SIGNAL(reject()), &loop, SLOT(quit())); loop.exec(); } } bool QGtk3Dialog::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) { if (parent) { connect(parent, &QWindow::destroyed, this, &QGtk3Dialog::onParentWindowDestroyed, Qt::UniqueConnection); } setParent(parent); setFlags(flags); setModality(modality); gtk_widget_realize(gtkWidget); // creates X window GdkWindow *gdkWindow = gtk_widget_get_window(gtkWidget); #ifdef GDK_WINDOWING_X11 if (parent) { GdkDisplay *gdkDisplay = gdk_window_get_display(gdkWindow); if (GDK_IS_X11_DISPLAY(gdkDisplay)) { XSetTransientForHint(gdk_x11_display_get_xdisplay(gdkDisplay), gdk_x11_window_get_xid(gdkWindow), parent->winId()); } } #endif if (modality != Qt::NonModal) { gdk_window_set_modal_hint(gdkWindow, true); QGuiApplicationPrivate::showModalWindow(this); } gtk_widget_show(gtkWidget); gdk_window_focus(gdkWindow, GDK_CURRENT_TIME); return true; } void QGtk3Dialog::hide() { QGuiApplicationPrivate::hideModalWindow(this); gtk_widget_hide(gtkWidget); } void QGtk3Dialog::onResponse(QGtk3Dialog *dialog, int response) { if (response == GTK_RESPONSE_OK) emit dialog->accept(); else emit dialog->reject(); } void QGtk3Dialog::onParentWindowDestroyed() { // The QGtk3*DialogHelper classes own this object. Make sure the parent doesn't delete it. setParent(0); } QGtk3ColorDialogHelper::QGtk3ColorDialogHelper() { d.reset(new QGtk3Dialog(gtk_color_chooser_dialog_new("", 0))); connect(d.data(), SIGNAL(accept()), this, SLOT(onAccepted())); connect(d.data(), SIGNAL(reject()), this, SIGNAL(reject())); g_signal_connect_swapped(d->gtkDialog(), "color-activated", G_CALLBACK(onColorChanged), this); } QGtk3ColorDialogHelper::~QGtk3ColorDialogHelper() { } bool QGtk3ColorDialogHelper::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) { applyOptions(); return d->show(flags, modality, parent); } void QGtk3ColorDialogHelper::exec() { d->exec(); } void QGtk3ColorDialogHelper::hide() { d->hide(); } void QGtk3ColorDialogHelper::setCurrentColor(const QColor &color) { GtkDialog *gtkDialog = d->gtkDialog(); if (color.alpha() < 255) gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(gtkDialog), true); GdkRGBA gdkColor; gdkColor.red = color.redF(); gdkColor.green = color.greenF(); gdkColor.blue = color.blueF(); gdkColor.alpha = color.alphaF(); gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(gtkDialog), &gdkColor); } QColor QGtk3ColorDialogHelper::currentColor() const { GtkDialog *gtkDialog = d->gtkDialog(); GdkRGBA gdkColor; gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(gtkDialog), &gdkColor); return QColor::fromRgbF(gdkColor.red, gdkColor.green, gdkColor.blue, gdkColor.alpha); } void QGtk3ColorDialogHelper::onAccepted() { const QColor color = currentColor(); emit currentColorChanged(color); emit accept(); } void QGtk3ColorDialogHelper::onColorChanged(QGtk3ColorDialogHelper *dialog) { emit dialog->currentColorChanged(dialog->currentColor()); } void QGtk3ColorDialogHelper::applyOptions() { GtkDialog *gtkDialog = d->gtkDialog(); gtk_window_set_title(GTK_WINDOW(gtkDialog), options()->windowTitle().toUtf8()); gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(gtkDialog), options()->testOption(QColorDialogOptions::ShowAlphaChannel)); } QGtk3FileDialogHelper::QGtk3FileDialogHelper() { d.reset(new QGtk3Dialog( gtk_file_chooser_dialog_new("", 0, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL))); connect(d.data(), SIGNAL(accept()), this, SLOT(onAccepted())); connect(d.data(), SIGNAL(reject()), this, SIGNAL(reject())); g_signal_connect(GTK_FILE_CHOOSER(d->gtkDialog()), "selection-changed", G_CALLBACK(onSelectionChanged), this); g_signal_connect_swapped(GTK_FILE_CHOOSER(d->gtkDialog()), "current-folder-changed", G_CALLBACK(onCurrentFolderChanged), this); previewWidget = gtk_image_new(); g_signal_connect(G_OBJECT(d->gtkDialog()), "update-preview", G_CALLBACK(onUpdatePreview), this); gtk_file_chooser_set_preview_widget(GTK_FILE_CHOOSER(d->gtkDialog()), previewWidget); } QGtk3FileDialogHelper::~QGtk3FileDialogHelper() { } GtkImage *QGtk3FileDialogHelper::previewImage() const { return GTK_IMAGE(previewWidget); } bool QGtk3FileDialogHelper::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) { _dir.clear(); _selection.clear(); applyOptions(); return d->show(flags, modality, parent); } void QGtk3FileDialogHelper::exec() { d->exec(); } void QGtk3FileDialogHelper::hide() { // After GtkFileChooserDialog has been hidden, gtk_file_chooser_get_current_folder() // & gtk_file_chooser_get_filenames() will return bogus values -> cache the actual // values before hiding the dialog _dir = directory(); _selection = selectedFiles(); d->hide(); } bool QGtk3FileDialogHelper::defaultNameFilterDisables() const { return false; } void QGtk3FileDialogHelper::setDirectory(const QUrl &directory) { GtkDialog *gtkDialog = d->gtkDialog(); gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(gtkDialog), directory.toLocalFile().toUtf8()); } QUrl QGtk3FileDialogHelper::directory() const { // While GtkFileChooserDialog is hidden, gtk_file_chooser_get_current_folder() // returns a bogus value -> return the cached value before hiding if (!_dir.isEmpty()) return _dir; QString ret; GtkDialog *gtkDialog = d->gtkDialog(); gchar *folder = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(gtkDialog)); if (folder) { ret = QString::fromUtf8(folder); g_free(folder); } return QUrl::fromLocalFile(ret); } void QGtk3FileDialogHelper::selectFile(const QUrl &filename) { GtkDialog *gtkDialog = d->gtkDialog(); if (options()->acceptMode() == QFileDialogOptions::AcceptSave) { QFileInfo fi(filename.toLocalFile()); gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(gtkDialog), fi.path().toUtf8()); gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(gtkDialog), fi.fileName().toUtf8()); } else { gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(gtkDialog), filename.toLocalFile().toUtf8()); } } QList QGtk3FileDialogHelper::selectedFiles() const { // While GtkFileChooserDialog is hidden, gtk_file_chooser_get_filenames() // returns a bogus value -> return the cached value before hiding if (!_selection.isEmpty()) return _selection; QList selection; GtkDialog *gtkDialog = d->gtkDialog(); GSList *filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(gtkDialog)); for (GSList *it = filenames; it; it = it->next) selection += QUrl::fromLocalFile(QString::fromUtf8((const char *)it->data)); g_slist_free(filenames); return selection; } void QGtk3FileDialogHelper::setFilter() { applyOptions(); } void QGtk3FileDialogHelper::selectNameFilter(const QString &filter) { GtkFileFilter *gtkFilter = _filters.value(filter); if (gtkFilter) { GtkDialog *gtkDialog = d->gtkDialog(); gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(gtkDialog), gtkFilter); } } QString QGtk3FileDialogHelper::selectedNameFilter() const { GtkDialog *gtkDialog = d->gtkDialog(); GtkFileFilter *gtkFilter = gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(gtkDialog)); return _filterNames.value(gtkFilter); } void QGtk3FileDialogHelper::onAccepted() { emit accept(); QString filter = selectedNameFilter(); if (filter.isEmpty()) emit filterSelected(filter); // These signals are emitted by QFileDialog::accept(), don't emit them twice /* QList files = selectedFiles(); emit filesSelected(files); if (files.count() == 1) emit fileSelected(files.first()); */ } void QGtk3FileDialogHelper::onSelectionChanged(GtkDialog *gtkDialog, QGtk3FileDialogHelper *helper) { QString selection; gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(gtkDialog)); if (filename) { selection = QString::fromUtf8(filename); g_free(filename); } emit helper->currentChanged(QUrl::fromLocalFile(selection)); } void QGtk3FileDialogHelper::onCurrentFolderChanged(QGtk3FileDialogHelper *dialog) { emit dialog->directoryEntered(dialog->directory()); } void QGtk3FileDialogHelper::onUpdatePreview(GtkDialog *gtkDialog, QGtk3FileDialogHelper *helper) { gchar *filename = gtk_file_chooser_get_preview_filename(GTK_FILE_CHOOSER(gtkDialog)); if (!filename) { gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(gtkDialog), false); return; } // Don't attempt to open anything which isn't a regular file. If a named pipe, // this may hang. See https://crbug.com/534754. QFileInfo fileinfo(filename); if (!fileinfo.exists() || !fileinfo.isFile()) { g_free(filename); gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(gtkDialog), false); return; } // This will preserve the image's aspect ratio. GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file_at_size(filename, PREVIEW_WIDTH, PREVIEW_HEIGHT, 0); g_free(filename); if (pixbuf) { gtk_image_set_from_pixbuf(helper->previewImage(), pixbuf); g_object_unref(pixbuf); } gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(gtkDialog), pixbuf ? true : false); } static GtkFileChooserAction gtkFileChooserAction(const QSharedPointer &options) { switch (options->fileMode()) { case QFileDialogOptions::AnyFile: case QFileDialogOptions::ExistingFile: case QFileDialogOptions::ExistingFiles: if (options->acceptMode() == QFileDialogOptions::AcceptOpen) return GTK_FILE_CHOOSER_ACTION_OPEN; else return GTK_FILE_CHOOSER_ACTION_SAVE; case QFileDialogOptions::Directory: case QFileDialogOptions::DirectoryOnly: default: if (options->acceptMode() == QFileDialogOptions::AcceptOpen) return GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER; else return GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER; } } void QGtk3FileDialogHelper::applyOptions() { GtkDialog *gtkDialog = d->gtkDialog(); const QSharedPointer &opts = options(); gtk_window_set_title(GTK_WINDOW(gtkDialog), opts->windowTitle().toUtf8()); gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(gtkDialog), true); const GtkFileChooserAction action = gtkFileChooserAction(opts); gtk_file_chooser_set_action(GTK_FILE_CHOOSER(gtkDialog), action); const bool selectMultiple = opts->fileMode() == QFileDialogOptions::ExistingFiles; gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(gtkDialog), selectMultiple); const bool confirmOverwrite = !opts->testOption(QFileDialogOptions::DontConfirmOverwrite); gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(gtkDialog), confirmOverwrite); const bool readOnly = opts->testOption(QFileDialogOptions::ReadOnly); gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER(gtkDialog), !readOnly); const QStringList nameFilters = opts->nameFilters(); if (!nameFilters.isEmpty()) setNameFilters(nameFilters); if (opts->initialDirectory().isLocalFile()) setDirectory(opts->initialDirectory()); foreach (const QUrl &filename, opts->initiallySelectedFiles()) selectFile(filename); const QString initialNameFilter = opts->initiallySelectedNameFilter(); if (!initialNameFilter.isEmpty()) selectNameFilter(initialNameFilter); GtkWidget *acceptButton = gtk_dialog_get_widget_for_response(gtkDialog, GTK_RESPONSE_OK); if (acceptButton) { if (opts->isLabelExplicitlySet(QFileDialogOptions::Accept)) gtk_button_set_label(GTK_BUTTON(acceptButton), opts->labelText(QFileDialogOptions::Accept).toUtf8()); else if (opts->acceptMode() == QFileDialogOptions::AcceptOpen) gtk_button_set_label(GTK_BUTTON(acceptButton), GTK_STOCK_OPEN); else gtk_button_set_label(GTK_BUTTON(acceptButton), GTK_STOCK_SAVE); } GtkWidget *rejectButton = gtk_dialog_get_widget_for_response(gtkDialog, GTK_RESPONSE_CANCEL); if (rejectButton) { if (opts->isLabelExplicitlySet(QFileDialogOptions::Reject)) gtk_button_set_label(GTK_BUTTON(rejectButton), opts->labelText(QFileDialogOptions::Reject).toUtf8()); else gtk_button_set_label(GTK_BUTTON(rejectButton), GTK_STOCK_CANCEL); } } void QGtk3FileDialogHelper::setNameFilters(const QStringList &filters) { GtkDialog *gtkDialog = d->gtkDialog(); foreach (GtkFileFilter *filter, _filters) gtk_file_chooser_remove_filter(GTK_FILE_CHOOSER(gtkDialog), filter); _filters.clear(); _filterNames.clear(); foreach (const QString &filter, filters) { GtkFileFilter *gtkFilter = gtk_file_filter_new(); const QString name = filter.left(filter.indexOf(QLatin1Char('('))); const QStringList extensions = cleanFilterList(filter); gtk_file_filter_set_name(gtkFilter, name.isEmpty() ? extensions.join(QStringLiteral(", ")).toUtf8() : name.toUtf8()); foreach (const QString &ext, extensions) gtk_file_filter_add_pattern(gtkFilter, ext.toUtf8()); gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(gtkDialog), gtkFilter); _filters.insert(filter, gtkFilter); _filterNames.insert(gtkFilter, filter); } } QGtk3FontDialogHelper::QGtk3FontDialogHelper() { d.reset(new QGtk3Dialog(gtk_font_chooser_dialog_new("", 0))); connect(d.data(), SIGNAL(accept()), this, SLOT(onAccepted())); connect(d.data(), SIGNAL(reject()), this, SIGNAL(reject())); } QGtk3FontDialogHelper::~QGtk3FontDialogHelper() { } bool QGtk3FontDialogHelper::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) { applyOptions(); return d->show(flags, modality, parent); } void QGtk3FontDialogHelper::exec() { d->exec(); } void QGtk3FontDialogHelper::hide() { d->hide(); } static QString qt_fontToString(const QFont &font) { PangoFontDescription *desc = pango_font_description_new(); pango_font_description_set_size(desc, (font.pointSizeF() > 0.0 ? font.pointSizeF() : QFontInfo(font).pointSizeF()) * PANGO_SCALE); pango_font_description_set_family(desc, QFontInfo(font).family().toUtf8()); int weight = font.weight(); if (weight >= QFont::Black) pango_font_description_set_weight(desc, PANGO_WEIGHT_HEAVY); else if (weight >= QFont::ExtraBold) pango_font_description_set_weight(desc, PANGO_WEIGHT_ULTRABOLD); else if (weight >= QFont::Bold) pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD); else if (weight >= QFont::DemiBold) pango_font_description_set_weight(desc, PANGO_WEIGHT_SEMIBOLD); else if (weight >= QFont::Medium) pango_font_description_set_weight(desc, PANGO_WEIGHT_MEDIUM); else if (weight >= QFont::Normal) pango_font_description_set_weight(desc, PANGO_WEIGHT_NORMAL); else if (weight >= QFont::Light) pango_font_description_set_weight(desc, PANGO_WEIGHT_LIGHT); else if (weight >= QFont::ExtraLight) pango_font_description_set_weight(desc, PANGO_WEIGHT_ULTRALIGHT); else pango_font_description_set_weight(desc, PANGO_WEIGHT_THIN); int style = font.style(); if (style == QFont::StyleItalic) pango_font_description_set_style(desc, PANGO_STYLE_ITALIC); else if (style == QFont::StyleOblique) pango_font_description_set_style(desc, PANGO_STYLE_OBLIQUE); else pango_font_description_set_style(desc, PANGO_STYLE_NORMAL); char *str = pango_font_description_to_string(desc); QString name = QString::fromUtf8(str); pango_font_description_free(desc); g_free(str); return name; } static QFont qt_fontFromString(const QString &name) { QFont font; PangoFontDescription *desc = pango_font_description_from_string(name.toUtf8()); font.setPointSizeF(static_cast(pango_font_description_get_size(desc)) / PANGO_SCALE); QString family = QString::fromUtf8(pango_font_description_get_family(desc)); if (!family.isEmpty()) font.setFamily(family); const int weight = pango_font_description_get_weight(desc); if (weight >= PANGO_WEIGHT_HEAVY) font.setWeight(QFont::Black); else if (weight >= PANGO_WEIGHT_ULTRABOLD) font.setWeight(QFont::ExtraBold); else if (weight >= PANGO_WEIGHT_BOLD) font.setWeight(QFont::Bold); else if (weight >= PANGO_WEIGHT_SEMIBOLD) font.setWeight(QFont::DemiBold); else if (weight >= PANGO_WEIGHT_MEDIUM) font.setWeight(QFont::Medium); else if (weight >= PANGO_WEIGHT_NORMAL) font.setWeight(QFont::Normal); else if (weight >= PANGO_WEIGHT_LIGHT) font.setWeight(QFont::Light); else if (weight >= PANGO_WEIGHT_ULTRALIGHT) font.setWeight(QFont::ExtraLight); else font.setWeight(QFont::Thin); PangoStyle style = pango_font_description_get_style(desc); if (style == PANGO_STYLE_ITALIC) font.setStyle(QFont::StyleItalic); else if (style == PANGO_STYLE_OBLIQUE) font.setStyle(QFont::StyleOblique); else font.setStyle(QFont::StyleNormal); pango_font_description_free(desc); return font; } void QGtk3FontDialogHelper::setCurrentFont(const QFont &font) { GtkFontChooser *gtkDialog = GTK_FONT_CHOOSER(d->gtkDialog()); gtk_font_chooser_set_font(gtkDialog, qt_fontToString(font).toUtf8()); } QFont QGtk3FontDialogHelper::currentFont() const { GtkFontChooser *gtkDialog = GTK_FONT_CHOOSER(d->gtkDialog()); gchar *name = gtk_font_chooser_get_font(gtkDialog); QFont font = qt_fontFromString(QString::fromUtf8(name)); g_free(name); return font; } void QGtk3FontDialogHelper::onAccepted() { const QFont font = currentFont(); emit currentFontChanged(font); emit accept(); } void QGtk3FontDialogHelper::applyOptions() { GtkDialog *gtkDialog = d->gtkDialog(); const QSharedPointer &opts = options(); gtk_window_set_title(GTK_WINDOW(gtkDialog), opts->windowTitle().toUtf8()); } QT_END_NAMESPACE #include "qgtk3dialoghelpers.moc" QGnomePlatform-0.9.2/src/theme/qgtk3dialoghelpers.h000066400000000000000000000115001446542274100222610ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QGTK3DIALOGHELPERS_H #define QGTK3DIALOGHELPERS_H #include #include #include #include #include #include typedef struct _GtkWidget GtkWidget; typedef struct _GtkImage GtkImage; typedef struct _GtkDialog GtkDialog; typedef struct _GtkFileFilter GtkFileFilter; QT_BEGIN_NAMESPACE class QGtk3Dialog; class QColor; class QGtk3ColorDialogHelper : public QPlatformColorDialogHelper { Q_OBJECT public: QGtk3ColorDialogHelper(); ~QGtk3ColorDialogHelper(); bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) Q_DECL_OVERRIDE; void exec() Q_DECL_OVERRIDE; void hide() Q_DECL_OVERRIDE; void setCurrentColor(const QColor &color) Q_DECL_OVERRIDE; QColor currentColor() const Q_DECL_OVERRIDE; private Q_SLOTS: void onAccepted(); private: static void onColorChanged(QGtk3ColorDialogHelper *helper); void applyOptions(); QScopedPointer d; }; class QGtk3FileDialogHelper : public QPlatformFileDialogHelper { Q_OBJECT public: QGtk3FileDialogHelper(); ~QGtk3FileDialogHelper(); GtkImage *previewImage() const; bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) Q_DECL_OVERRIDE; void exec() Q_DECL_OVERRIDE; void hide() Q_DECL_OVERRIDE; bool defaultNameFilterDisables() const Q_DECL_OVERRIDE; void setDirectory(const QUrl &directory) Q_DECL_OVERRIDE; QUrl directory() const Q_DECL_OVERRIDE; void selectFile(const QUrl &filename) Q_DECL_OVERRIDE; QList selectedFiles() const Q_DECL_OVERRIDE; void setFilter() Q_DECL_OVERRIDE; void selectNameFilter(const QString &filter) Q_DECL_OVERRIDE; QString selectedNameFilter() const Q_DECL_OVERRIDE; private Q_SLOTS: void onAccepted(); private: static void onSelectionChanged(GtkDialog *dialog, QGtk3FileDialogHelper *helper); static void onCurrentFolderChanged(QGtk3FileDialogHelper *helper); static void onUpdatePreview(GtkDialog *dialog, QGtk3FileDialogHelper *helper); void applyOptions(); void setNameFilters(const QStringList &filters); QUrl _dir; QList _selection; QHash _filters; QHash _filterNames; QScopedPointer d; GtkWidget *previewWidget; }; class QGtk3FontDialogHelper : public QPlatformFontDialogHelper { Q_OBJECT public: QGtk3FontDialogHelper(); ~QGtk3FontDialogHelper(); bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) Q_DECL_OVERRIDE; void exec() Q_DECL_OVERRIDE; void hide() Q_DECL_OVERRIDE; void setCurrentFont(const QFont &font) Q_DECL_OVERRIDE; QFont currentFont() const Q_DECL_OVERRIDE; private Q_SLOTS: void onAccepted(); private: void applyOptions(); QScopedPointer d; }; QT_END_NAMESPACE #endif // QGTK3DIALOGHELPERS_H QGnomePlatform-0.9.2/src/theme/qxdgdesktopportalfiledialog.cpp000066400000000000000000000370661446542274100246360ustar00rootroot00000000000000/* * Copyright (C) 2017-2021 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "qxdgdesktopportalfiledialog_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE QDBusArgument &operator<<(QDBusArgument &arg, const QXdgDesktopPortalFileDialog::FilterCondition &filterCondition) { arg.beginStructure(); arg << filterCondition.type << filterCondition.pattern; arg.endStructure(); return arg; } const QDBusArgument &operator>>(const QDBusArgument &arg, QXdgDesktopPortalFileDialog::FilterCondition &filterCondition) { uint type; QString filterPattern; arg.beginStructure(); arg >> type >> filterPattern; filterCondition.type = (QXdgDesktopPortalFileDialog::ConditionType)type; filterCondition.pattern = filterPattern; arg.endStructure(); return arg; } QDBusArgument &operator<<(QDBusArgument &arg, const QXdgDesktopPortalFileDialog::Filter filter) { arg.beginStructure(); arg << filter.name << filter.filterConditions; arg.endStructure(); return arg; } const QDBusArgument &operator>>(const QDBusArgument &arg, QXdgDesktopPortalFileDialog::Filter &filter) { QString name; QXdgDesktopPortalFileDialog::FilterConditionList filterConditions; arg.beginStructure(); arg >> name >> filterConditions; filter.name = name; filter.filterConditions = filterConditions; arg.endStructure(); return arg; } class QXdgDesktopPortalFileDialogPrivate { public: QXdgDesktopPortalFileDialogPrivate(QPlatformFileDialogHelper *nativeFileDialog) : nativeFileDialog(nativeFileDialog) { } WId winId = 0; bool directoryMode = false; bool modal = false; bool multipleFiles = false; bool saveFile = false; QString acceptLabel; QString directory; QString title; QStringList nameFilters; QStringList mimeTypesFilters; // maps user-visible name for portal to full name filter QMap userVisibleToNameFilter; QString selectedMimeTypeFilter; QString selectedNameFilter; QStringList selectedFiles; std::unique_ptr nativeFileDialog; }; QXdgDesktopPortalFileDialog::QXdgDesktopPortalFileDialog(QPlatformFileDialogHelper *nativeFileDialog) : QPlatformFileDialogHelper() , d_ptr(new QXdgDesktopPortalFileDialogPrivate(nativeFileDialog)) { Q_D(QXdgDesktopPortalFileDialog); if (d->nativeFileDialog) { connect(d->nativeFileDialog.get(), SIGNAL(accept()), this, SIGNAL(accept())); connect(d->nativeFileDialog.get(), SIGNAL(reject()), this, SIGNAL(reject())); } } QXdgDesktopPortalFileDialog::~QXdgDesktopPortalFileDialog() { } void QXdgDesktopPortalFileDialog::initializeDialog() { Q_D(QXdgDesktopPortalFileDialog); if (d->nativeFileDialog) d->nativeFileDialog->setOptions(options()); if (options()->fileMode() == QFileDialogOptions::ExistingFiles) d->multipleFiles = true; if (options()->fileMode() == QFileDialogOptions::Directory || options()->fileMode() == QFileDialogOptions::DirectoryOnly) d->directoryMode = true; if (options()->isLabelExplicitlySet(QFileDialogOptions::Accept)) d->acceptLabel = options()->labelText(QFileDialogOptions::Accept); if (!options()->windowTitle().isEmpty()) d->title = options()->windowTitle(); if (options()->acceptMode() == QFileDialogOptions::AcceptSave) d->saveFile = true; if (!options()->nameFilters().isEmpty()) d->nameFilters = options()->nameFilters(); if (!options()->mimeTypeFilters().isEmpty()) d->mimeTypesFilters = options()->mimeTypeFilters(); if (!options()->initiallySelectedMimeTypeFilter().isEmpty()) d->selectedMimeTypeFilter = options()->initiallySelectedMimeTypeFilter(); if (!options()->initiallySelectedNameFilter().isEmpty()) d->selectedNameFilter = options()->initiallySelectedNameFilter(); setDirectory(options()->initialDirectory()); } void QXdgDesktopPortalFileDialog::openPortal() { Q_D(QXdgDesktopPortalFileDialog); QDBusMessage message = QDBusMessage::createMethodCall(QLatin1String("org.freedesktop.portal.Desktop"), QLatin1String("/org/freedesktop/portal/desktop"), QLatin1String("org.freedesktop.portal.FileChooser"), d->saveFile ? QLatin1String("SaveFile") : QLatin1String("OpenFile")); QVariantMap options; if (!d->acceptLabel.isEmpty()) options.insert(QLatin1String("accept_label"), d->acceptLabel); options.insert(QLatin1String("modal"), d->modal); options.insert(QLatin1String("multiple"), d->multipleFiles); options.insert(QLatin1String("directory"), d->directoryMode); if (d->saveFile) { if (!d->directory.isEmpty()) options.insert(QLatin1String("current_folder"), QFile::encodeName(d->directory).append('\0')); if (!d->selectedFiles.isEmpty()) { // current_file for the file to be pre-selected, current_name for the file name to be pre-filled // current_file accepts absolute path while current_name accepts just file name options.insert(QLatin1String("current_file"), QFile::encodeName(d->selectedFiles.first()).append('\0')); options.insert(QLatin1String("current_name"), QFileInfo(d->selectedFiles.first()).fileName()); } } // Insert filters qDBusRegisterMetaType(); qDBusRegisterMetaType(); qDBusRegisterMetaType(); qDBusRegisterMetaType(); FilterList filterList; auto selectedFilterIndex = filterList.size() - 1; d->userVisibleToNameFilter.clear(); if (!d->mimeTypesFilters.isEmpty()) { for (const QString &mimeTypefilter : d->mimeTypesFilters) { QMimeDatabase mimeDatabase; QMimeType mimeType = mimeDatabase.mimeTypeForName(mimeTypefilter); // Creates e.g. (1, "image/png") FilterCondition filterCondition; filterCondition.type = MimeType; filterCondition.pattern = mimeTypefilter; // Creates e.g. [((1, "image/png"))] FilterConditionList filterConditions; filterConditions << filterCondition; // Creates e.g. [("Images", [((1, "image/png"))])] Filter filter; filter.name = mimeType.comment(); filter.filterConditions = filterConditions; filterList << filter; if (!d->selectedMimeTypeFilter.isEmpty() && d->selectedMimeTypeFilter == mimeTypefilter) selectedFilterIndex = filterList.size() - 1; } } else if (!d->nameFilters.isEmpty()) { for (const QString &nameFilter : d->nameFilters) { // Do parsing: // Supported format is ("Images (*.png *.jpg)") QRegularExpression regexp(QPlatformFileDialogHelper::filterRegExp); QRegularExpressionMatch match = regexp.match(nameFilter); if (match.hasMatch()) { QString userVisibleName = match.captured(1); #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) QStringList filterStrings = match.captured(2).split(QLatin1Char(' '), Qt::SkipEmptyParts); #else QStringList filterStrings = match.captured(2).split(QLatin1Char(' '), QString::SkipEmptyParts); #endif if (filterStrings.isEmpty()) { qWarning() << "Filter " << userVisibleName << " is empty and will be ignored."; continue; } FilterConditionList filterConditions; for (const QString &filterString : filterStrings) { FilterCondition filterCondition; filterCondition.type = GlobalPattern; filterCondition.pattern = filterString; filterConditions << filterCondition; } Filter filter; filter.name = userVisibleName; filter.filterConditions = filterConditions; filterList << filter; d->userVisibleToNameFilter.insert(userVisibleName, nameFilter); if (!d->selectedNameFilter.isEmpty() && d->selectedNameFilter == nameFilter) selectedFilterIndex = filterList.size() - 1; } } } if (!filterList.isEmpty()) options.insert(QLatin1String("filters"), QVariant::fromValue(filterList)); if (selectedFilterIndex != -1) options.insert(QLatin1String("current_filter"), QVariant::fromValue(filterList[selectedFilterIndex])); options.insert(QLatin1String("handle_token"), QStringLiteral("qt%1").arg(QRandomGenerator::global()->generate())); // TODO choices a(ssa(ss)s) // List of serialized combo boxes to add to the file chooser. QString parentWindowId; if (QGuiApplication::platformName() == QStringLiteral("xcb")) { parentWindowId = QLatin1String("x11:") + QString::number(d->winId, 16); } message << parentWindowId << d->title << options; QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall); connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) { QDBusPendingReply reply = *watcher; if (reply.isError()) { Q_EMIT reject(); } else { QDBusConnection::sessionBus().connect(nullptr, reply.value().path(), QLatin1String("org.freedesktop.portal.Request"), QLatin1String("Response"), this, SLOT(gotResponse(uint, QVariantMap))); } watcher->deleteLater(); }); } bool QXdgDesktopPortalFileDialog::defaultNameFilterDisables() const { return false; } void QXdgDesktopPortalFileDialog::setDirectory(const QUrl &directory) { Q_D(QXdgDesktopPortalFileDialog); if (d->nativeFileDialog) { d->nativeFileDialog->setOptions(options()); d->nativeFileDialog->setDirectory(directory); } d->directory = directory.path(); } QUrl QXdgDesktopPortalFileDialog::directory() const { Q_D(const QXdgDesktopPortalFileDialog); if (d->nativeFileDialog && useNativeFileDialog()) return d->nativeFileDialog->directory(); return d->directory; } void QXdgDesktopPortalFileDialog::selectFile(const QUrl &filename) { Q_D(QXdgDesktopPortalFileDialog); if (d->nativeFileDialog) { d->nativeFileDialog->setOptions(options()); d->nativeFileDialog->selectFile(filename); } d->selectedFiles << filename.path(); } QList QXdgDesktopPortalFileDialog::selectedFiles() const { Q_D(const QXdgDesktopPortalFileDialog); if (d->nativeFileDialog && useNativeFileDialog()) return d->nativeFileDialog->selectedFiles(); QList files; for (const QString &file : d->selectedFiles) { files << QUrl(file); } return files; } void QXdgDesktopPortalFileDialog::setFilter() { Q_D(QXdgDesktopPortalFileDialog); if (d->nativeFileDialog) { d->nativeFileDialog->setOptions(options()); d->nativeFileDialog->setFilter(); } } void QXdgDesktopPortalFileDialog::selectMimeTypeFilter(const QString &filter) { Q_D(QXdgDesktopPortalFileDialog); if (d->nativeFileDialog) { d->nativeFileDialog->setOptions(options()); d->nativeFileDialog->selectMimeTypeFilter(filter); } } QString QXdgDesktopPortalFileDialog::selectedMimeTypeFilter() const { Q_D(const QXdgDesktopPortalFileDialog); return d->selectedMimeTypeFilter; } void QXdgDesktopPortalFileDialog::selectNameFilter(const QString &filter) { Q_D(QXdgDesktopPortalFileDialog); if (d->nativeFileDialog) { d->nativeFileDialog->setOptions(options()); d->nativeFileDialog->selectNameFilter(filter); } } QString QXdgDesktopPortalFileDialog::selectedNameFilter() const { Q_D(const QXdgDesktopPortalFileDialog); return d->selectedNameFilter; } void QXdgDesktopPortalFileDialog::exec() { Q_D(QXdgDesktopPortalFileDialog); if (d->nativeFileDialog && useNativeFileDialog()) { d->nativeFileDialog->exec(); return; } // HACK we have to avoid returning until we emit that the dialog was accepted or rejected QEventLoop loop; loop.connect(this, SIGNAL(accept()), SLOT(quit())); loop.connect(this, SIGNAL(reject()), SLOT(quit())); loop.exec(); } void QXdgDesktopPortalFileDialog::hide() { Q_D(QXdgDesktopPortalFileDialog); if (d->nativeFileDialog) d->nativeFileDialog->hide(); } bool QXdgDesktopPortalFileDialog::show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) { Q_D(QXdgDesktopPortalFileDialog); initializeDialog(); d->modal = windowModality != Qt::NonModal; d->winId = parent ? parent->winId() : 0; if (d->nativeFileDialog && useNativeFileDialog()) return d->nativeFileDialog->show(windowFlags, windowModality, parent); openPortal(); return true; } void QXdgDesktopPortalFileDialog::gotResponse(uint response, const QVariantMap &results) { Q_D(QXdgDesktopPortalFileDialog); if (!response) { if (results.contains(QLatin1String("uris"))) d->selectedFiles = results.value(QLatin1String("uris")).toStringList(); if (results.contains(QLatin1String("current_filter"))) { const Filter selectedFilter = qdbus_cast(results.value(QStringLiteral("current_filter"))); if (!selectedFilter.filterConditions.empty() && selectedFilter.filterConditions[0].type == MimeType) { // s.a. QXdgDesktopPortalFileDialog::openPortal which basically does the inverse d->selectedMimeTypeFilter = selectedFilter.filterConditions[0].pattern; d->selectedNameFilter.clear(); } else { d->selectedNameFilter = d->userVisibleToNameFilter.value(selectedFilter.name); d->selectedMimeTypeFilter.clear(); } } Q_EMIT accept(); } else { Q_EMIT reject(); } } bool QXdgDesktopPortalFileDialog::useNativeFileDialog() const { if (options()->fileMode() == QFileDialogOptions::Directory) return true; else if (options()->fileMode() == QFileDialogOptions::DirectoryOnly) return true; return false; } QT_END_NAMESPACE QGnomePlatform-0.9.2/src/theme/qxdgdesktopportalfiledialog_p.h000066400000000000000000000060071446542274100246110ustar00rootroot00000000000000/* * Copyright (C) 2017-2021 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef QXDGDESKTOPPORTALFILEDIALOG_P_H #define QXDGDESKTOPPORTALFILEDIALOG_P_H #include #include QT_BEGIN_NAMESPACE class QXdgDesktopPortalFileDialogPrivate; class QXdgDesktopPortalFileDialog : public QPlatformFileDialogHelper { Q_OBJECT Q_DECLARE_PRIVATE(QXdgDesktopPortalFileDialog) public: enum ConditionType : uint { GlobalPattern = 0, MimeType = 1 }; // Filters a(sa(us)) // Example: [('Images', [(0, '*.ico'), (1, 'image/png')]), ('Text', [(0, '*.txt')])] struct FilterCondition { ConditionType type; QString pattern; // E.g. '*ico' or 'image/png' }; typedef QVector FilterConditionList; struct Filter { QString name; // E.g. 'Images' or 'Text FilterConditionList filterConditions; ; // E.g. [(0, '*.ico'), (1, 'image/png')] or [(0, '*.txt')] }; typedef QVector FilterList; QXdgDesktopPortalFileDialog(QPlatformFileDialogHelper *nativeFileDialog = nullptr); ~QXdgDesktopPortalFileDialog(); bool defaultNameFilterDisables() const override; QUrl directory() const override; void setDirectory(const QUrl &directory) override; void selectFile(const QUrl &filename) override; QList selectedFiles() const override; void setFilter() override; void selectNameFilter(const QString &filter) override; QString selectedNameFilter() const override; void selectMimeTypeFilter(const QString &filter) override; QString selectedMimeTypeFilter() const override; void exec() override; bool show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) override; void hide() override; private Q_SLOTS: void gotResponse(uint response, const QVariantMap &results); private: void initializeDialog(); void openPortal(); bool useNativeFileDialog() const; QScopedPointer d_ptr; }; QT_END_NAMESPACE Q_DECLARE_METATYPE(QXdgDesktopPortalFileDialog::FilterCondition); Q_DECLARE_METATYPE(QXdgDesktopPortalFileDialog::FilterConditionList); Q_DECLARE_METATYPE(QXdgDesktopPortalFileDialog::Filter); Q_DECLARE_METATYPE(QXdgDesktopPortalFileDialog::FilterList); #endif // QXDGDESKTOPPORTALFILEDIALOG_P_H