├── AUTHORS ├── COPYING ├── ChangeLog ├── README.md ├── qt6gtk2.pri ├── qt6gtk2.pro └── src ├── qt6gtk2-qtplugin ├── main.cpp ├── qt6gtk2-qtplugin.pro ├── qt6gtk2.json ├── qt6gtk2dialoghelpers.cpp ├── qt6gtk2dialoghelpers.h ├── qt6gtk2theme.cpp └── qt6gtk2theme.h └── qt6gtk2-style ├── plugin.cpp ├── qgtk2painter.cpp ├── qgtk2painter_p.h ├── qgtkglobal_p.h ├── qgtkpainter.cpp ├── qgtkpainter_p.h ├── qgtkstyle.cpp ├── qgtkstyle_p.cpp ├── qgtkstyle_p.h ├── qgtkstyle_p_p.h ├── qstylehelper.cpp ├── qstylehelper_p.h ├── qt6gtk2-style.pro └── qt6gtk2.json /AUTHORS: -------------------------------------------------------------------------------- 1 | The Qt Company Ltd. 2 | Ilya Kotov , , 3 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | Changelog of Qt6Gtk2 2 | 3 | ---------- 4 | Version 0.1 5 | * inital release 6 | 7 | Version 0.2 8 | * Added Qt version checking 9 | * Added preview support to the file dialog (Ilya Fedin) 10 | * Added "gtk2" platform theme key (lvlgl) 11 | 12 | Version 0.3 13 | * Added Qt 6.8 support 14 | * Fixed dock widget buttons size for Qt 6.7 or higher 15 | * Improved GTK settings support (patch by Alexei Sorokin) 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Qt6Gtk2 - GTK+2.0 integration plugins for Qt6 2 | 3 | Official home page: https://github.com/trialuser02/qt6gtk2 4 | 5 | Requirements: 6 | 7 | - GNU Linux or FreeBSD 8 | - qtbase >= 6.0.0 (with private headers) 9 | - GTK+ 2.0 10 | - libX11 11 | 12 | Installation: 13 | 14 | - Arch AUR 15 | https://aur.archlinux.org/packages/qt6gtk2/ 16 | 17 | - Source Code 18 | ``` 19 | qmake PREFIX= 20 | make 21 | sudo make install 22 | ``` 23 | 24 | To change default installation root you should run the following 25 | command: 26 | 27 | `make install INSTALL_ROOT="custom root"` 28 | 29 | Add line `export QT_QPA_PLATFORMTHEME=gtk2` to `~/.profile` and re-login. 30 | Alternatively, create the file `/etc/X11/Xsession.d/100-qt6gtk2` with 31 | the following line: 32 | 33 | `export QT_QPA_PLATFORMTHEME=gtk2` 34 | 35 | (`qt5gtk2` for compatibility with Qt5Gtk2, `qt6gtk2` is also a valid value) 36 | 37 | Now restart X11 server to take the changes effect. 38 | 39 | Files and directories: 40 | 41 | `libqt6gtk2.so` - GTK+2.0 platform plugin 42 | `libqt6gtk2-style.so` - GTK+2.0 style plugin 43 | 44 | Attention! 45 | Environment variable `QT_STYLE_OVERRIDE` should be removed before usage. 46 | -------------------------------------------------------------------------------- /qt6gtk2.pri: -------------------------------------------------------------------------------- 1 | #Some conf to redirect intermediate stuff in separate dirs 2 | UI_DIR=./.build/ui/ 3 | MOC_DIR=./.build/moc/ 4 | OBJECTS_DIR=./.build/obj 5 | RCC_DIR=./.build/rcc 6 | 7 | CONFIG += hide_symbols c++17 8 | DEFINES += QT_DEPRECATED_WARNINGS QT_DISABLE_DEPRECATED_BEFORE=0x060000 9 | DEFINES += QT_NO_CAST_FROM_BYTEARRAY QT_STRICT_ITERATORS QT_NO_FOREACH QT_DEPRECATED_WARNINGS 10 | QMAKE_DISTCLEAN += -r .build 11 | 12 | #*-g++ { 13 | # QMAKE_CXXFLAGS += -Werror=suggest-override 14 | # QMAKE_CXXFLAGS += -Wzero-as-null-pointer-constant 15 | #} 16 | 17 | !isEqual (QT_MAJOR_VERSION, 6) { 18 | error("Use Qt 6.0.0 or higher.") 19 | } 20 | 21 | #Install paths 22 | unix { 23 | isEmpty(PLUGINDIR):PLUGINDIR = $$[QT_INSTALL_PLUGINS] 24 | } 25 | -------------------------------------------------------------------------------- /qt6gtk2.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = subdirs 2 | 3 | SUBDIRS += src/qt6gtk2-qtplugin src/qt6gtk2-style 4 | 5 | include(qt6gtk2.pri) 6 | 7 | message (PLUGINDIR=$$PLUGINDIR) 8 | -------------------------------------------------------------------------------- /src/qt6gtk2-qtplugin/main.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2015 The Qt Company Ltd. * 3 | * Copyright (C) 2016-2024 Ilya Kotov, forkotov02@ya.ru * 4 | * * 5 | * This program is free software; you can redistribute it and/or modify * 6 | * it under the terms of the GNU General Public License as published by * 7 | * the Free Software Foundation; either version 2 of the License, or * 8 | * (at your option) any later version. * 9 | * * 10 | * This program is distributed in the hope that it will be useful, * 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 13 | * GNU General Public License for more details. * 14 | * * 15 | * You should have received a copy of the GNU General Public License * 16 | * along with this program; if not, write to the * 17 | * Free Software Foundation, Inc., * 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * 19 | ***************************************************************************/ 20 | 21 | #include 22 | #include "qt6gtk2theme.h" 23 | 24 | QT_BEGIN_NAMESPACE 25 | 26 | class Qt6Gtk2ThemePlugin : public QPlatformThemePlugin 27 | { 28 | Q_OBJECT 29 | Q_PLUGIN_METADATA(IID QPlatformThemeFactoryInterface_iid FILE "qt6gtk2.json") 30 | 31 | public: 32 | QPlatformTheme *create(const QString &key, const QStringList ¶ms) override; 33 | }; 34 | 35 | QPlatformTheme *Qt6Gtk2ThemePlugin::create(const QString &key, const QStringList ¶ms) 36 | { 37 | Q_UNUSED(params); 38 | if (key.toLower() == QLatin1String("gtk2") || key.toLower() == QLatin1String("qt6gtk2") || key.toLower() == QLatin1String("qt5gtk2")) 39 | return new Qt6Gtk2Theme; 40 | 41 | return nullptr; 42 | } 43 | 44 | QT_END_NAMESPACE 45 | 46 | #include "main.moc" 47 | -------------------------------------------------------------------------------- /src/qt6gtk2-qtplugin/qt6gtk2-qtplugin.pro: -------------------------------------------------------------------------------- 1 | include(../../qt6gtk2.pri) 2 | 3 | TARGET = qt6gtk2 4 | 5 | QT += core-private gui-private 6 | 7 | HEADERS += \ 8 | qt6gtk2theme.h \ 9 | qt6gtk2dialoghelpers.h 10 | 11 | SOURCES += \ 12 | main.cpp \ 13 | qt6gtk2theme.cpp \ 14 | qt6gtk2dialoghelpers.cpp 15 | 16 | TEMPLATE = lib 17 | TARGET = qt6gtk2 18 | CONFIG += plugin \ 19 | link_pkgconfig \ 20 | 21 | PKGCONFIG += gtk+-2.0 x11 22 | 23 | target.path = $$PLUGINDIR/platformthemes 24 | INSTALLS += target 25 | -------------------------------------------------------------------------------- /src/qt6gtk2-qtplugin/qt6gtk2.json: -------------------------------------------------------------------------------- 1 | { 2 | "Keys": [ "gtk2", "qt6gtk2", "qt5gtk2" ] 3 | } 4 | -------------------------------------------------------------------------------- /src/qt6gtk2-qtplugin/qt6gtk2dialoghelpers.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2015 The Qt Company Ltd. * 3 | * Copyright (C) 2016-2024 Ilya Kotov, forkotov02@ya.ru * 4 | * * 5 | * This program is free software; you can redistribute it and/or modify * 6 | * it under the terms of the GNU General Public License as published by * 7 | * the Free Software Foundation; either version 2 of the License, or * 8 | * (at your option) any later version. * 9 | * * 10 | * This program is distributed in the hope that it will be useful, * 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 13 | * GNU General Public License for more details. * 14 | * * 15 | * You should have received a copy of the GNU General Public License * 16 | * along with this program; if not, write to the * 17 | * Free Software Foundation, Inc., * 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * 19 | ***************************************************************************/ 20 | 21 | #include "qt6gtk2dialoghelpers.h" 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | 33 | #undef signals 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | // The size of the preview we display for selected image files. We set height 40 | // larger than width because generally there is more free space vertically 41 | // than horiztonally (setting the preview image will alway expand the width of 42 | // the dialog, but usually not the height). The image's aspect ratio will always 43 | // be preserved. 44 | #define PREVIEW_WIDTH 256 45 | #define PREVIEW_HEIGHT 512 46 | 47 | QT_BEGIN_NAMESPACE 48 | 49 | class QGtk2Dialog : public QWindow 50 | { 51 | Q_OBJECT 52 | 53 | public: 54 | QGtk2Dialog(GtkWidget *gtkWidget); 55 | ~QGtk2Dialog(); 56 | 57 | GtkDialog *gtkDialog() const; 58 | 59 | void exec(); 60 | bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent); 61 | void hide(); 62 | 63 | Q_SIGNALS: 64 | void accept(); 65 | void reject(); 66 | 67 | protected: 68 | static void onResponse(QGtk2Dialog *dialog, int response); 69 | 70 | private slots: 71 | void onParentWindowDestroyed(); 72 | 73 | private: 74 | GtkWidget *gtkWidget; 75 | }; 76 | 77 | QGtk2Dialog::QGtk2Dialog(GtkWidget *gtkWidget) : gtkWidget(gtkWidget) 78 | { 79 | g_signal_connect_swapped(G_OBJECT(gtkWidget), "response", G_CALLBACK(onResponse), this); 80 | g_signal_connect(G_OBJECT(gtkWidget), "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), nullptr); 81 | } 82 | 83 | QGtk2Dialog::~QGtk2Dialog() 84 | { 85 | gtk_clipboard_store(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD)); 86 | gtk_widget_destroy(gtkWidget); 87 | } 88 | 89 | GtkDialog *QGtk2Dialog::gtkDialog() const 90 | { 91 | return GTK_DIALOG(gtkWidget); 92 | } 93 | 94 | void QGtk2Dialog::exec() 95 | { 96 | if (modality() == Qt::ApplicationModal) { 97 | // block input to the whole app, including other GTK dialogs 98 | gtk_dialog_run(gtkDialog()); 99 | } else { 100 | // block input to the window, allow input to other GTK dialogs 101 | QEventLoop loop; 102 | connect(this, SIGNAL(accept()), &loop, SLOT(quit())); 103 | connect(this, SIGNAL(reject()), &loop, SLOT(quit())); 104 | loop.exec(); 105 | } 106 | } 107 | 108 | bool QGtk2Dialog::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) 109 | { 110 | connect(parent, &QWindow::destroyed, this, &QGtk2Dialog::onParentWindowDestroyed, 111 | Qt::UniqueConnection); 112 | setParent(parent); 113 | setFlags(flags); 114 | setModality(modality); 115 | 116 | gtk_widget_realize(gtkWidget); // creates X window 117 | 118 | if (parent) { 119 | XSetTransientForHint(gdk_x11_drawable_get_xdisplay(gtkWidget->window), 120 | gdk_x11_drawable_get_xid(gtkWidget->window), 121 | parent->winId()); 122 | } 123 | 124 | if (modality != Qt::NonModal) { 125 | gdk_window_set_modal_hint(gtkWidget->window, true); 126 | QGuiApplicationPrivate::showModalWindow(this); 127 | } 128 | 129 | gtk_widget_show(gtkWidget); 130 | gdk_window_focus(gtkWidget->window, 0); 131 | return true; 132 | } 133 | 134 | void QGtk2Dialog::hide() 135 | { 136 | QGuiApplicationPrivate::hideModalWindow(this); 137 | gtk_widget_hide(gtkWidget); 138 | } 139 | 140 | void QGtk2Dialog::onResponse(QGtk2Dialog *dialog, int response) 141 | { 142 | if (response == GTK_RESPONSE_OK) 143 | emit dialog->accept(); 144 | else 145 | emit dialog->reject(); 146 | } 147 | 148 | void QGtk2Dialog::onParentWindowDestroyed() 149 | { 150 | // The QGtk2*DialogHelper classes own this object. Make sure the parent doesn't delete it. 151 | setParent(nullptr); 152 | } 153 | 154 | Qt6Gtk2ColorDialogHelper::Qt6Gtk2ColorDialogHelper() 155 | { 156 | d.reset(new QGtk2Dialog(gtk_color_selection_dialog_new(""))); 157 | connect(d.data(), SIGNAL(accept()), this, SLOT(onAccepted())); 158 | connect(d.data(), SIGNAL(reject()), this, SIGNAL(reject())); 159 | 160 | GtkWidget *gtkColorSelection = gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(d->gtkDialog())); 161 | g_signal_connect_swapped(gtkColorSelection, "color-changed", G_CALLBACK(onColorChanged), this); 162 | } 163 | 164 | Qt6Gtk2ColorDialogHelper::~Qt6Gtk2ColorDialogHelper() 165 | { 166 | } 167 | 168 | bool Qt6Gtk2ColorDialogHelper::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) 169 | { 170 | applyOptions(); 171 | return d->show(flags, modality, parent); 172 | } 173 | 174 | void Qt6Gtk2ColorDialogHelper::exec() 175 | { 176 | d->exec(); 177 | } 178 | 179 | void Qt6Gtk2ColorDialogHelper::hide() 180 | { 181 | d->hide(); 182 | } 183 | 184 | void Qt6Gtk2ColorDialogHelper::setCurrentColor(const QColor &color) 185 | { 186 | GtkDialog *gtkDialog = d->gtkDialog(); 187 | GtkWidget *gtkColorSelection = gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(gtkDialog)); 188 | GdkColor gdkColor; 189 | gdkColor.red = color.red() << 8; 190 | gdkColor.green = color.green() << 8; 191 | gdkColor.blue = color.blue() << 8; 192 | gtk_color_selection_set_current_color(GTK_COLOR_SELECTION(gtkColorSelection), &gdkColor); 193 | if (color.alpha() < 255) { 194 | gtk_color_selection_set_has_opacity_control(GTK_COLOR_SELECTION(gtkColorSelection), true); 195 | gtk_color_selection_set_current_alpha(GTK_COLOR_SELECTION(gtkColorSelection), color.alpha() << 8); 196 | } 197 | } 198 | 199 | QColor Qt6Gtk2ColorDialogHelper::currentColor() const 200 | { 201 | GtkDialog *gtkDialog = d->gtkDialog(); 202 | GtkWidget *gtkColorSelection = gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(gtkDialog)); 203 | GdkColor gdkColor; 204 | gtk_color_selection_get_current_color(GTK_COLOR_SELECTION(gtkColorSelection), &gdkColor); 205 | guint16 alpha = gtk_color_selection_get_current_alpha(GTK_COLOR_SELECTION(gtkColorSelection)); 206 | return QColor(gdkColor.red >> 8, gdkColor.green >> 8, gdkColor.blue >> 8, alpha >> 8); 207 | } 208 | 209 | void Qt6Gtk2ColorDialogHelper::onAccepted() 210 | { 211 | emit accept(); 212 | emit colorSelected(currentColor()); 213 | } 214 | 215 | void Qt6Gtk2ColorDialogHelper::onColorChanged(Qt6Gtk2ColorDialogHelper *dialog) 216 | { 217 | emit dialog->currentColorChanged(dialog->currentColor()); 218 | } 219 | 220 | void Qt6Gtk2ColorDialogHelper::applyOptions() 221 | { 222 | GtkDialog *gtkDialog = d->gtkDialog(); 223 | gtk_window_set_title(GTK_WINDOW(gtkDialog), options()->windowTitle().toUtf8().constData()); 224 | 225 | GtkWidget *gtkColorSelection = gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(gtkDialog)); 226 | gtk_color_selection_set_has_opacity_control(GTK_COLOR_SELECTION(gtkColorSelection), options()->testOption(QColorDialogOptions::ShowAlphaChannel)); 227 | 228 | GtkWidget *okButton = nullptr; 229 | GtkWidget *cancelButton = nullptr; 230 | GtkWidget *helpButton = nullptr; 231 | g_object_get(G_OBJECT(gtkDialog), "ok-button", &okButton, "cancel-button", &cancelButton, "help-button", &helpButton, nullptr); 232 | if (okButton) 233 | g_object_set(G_OBJECT(okButton), "visible", !options()->testOption(QColorDialogOptions::NoButtons), nullptr); 234 | if (cancelButton) 235 | g_object_set(G_OBJECT(cancelButton), "visible", !options()->testOption(QColorDialogOptions::NoButtons), nullptr); 236 | if (helpButton) 237 | gtk_widget_hide(helpButton); 238 | } 239 | 240 | Qt6Gtk2FileDialogHelper::Qt6Gtk2FileDialogHelper() 241 | { 242 | d.reset(new QGtk2Dialog(gtk_file_chooser_dialog_new("", nullptr, 243 | GTK_FILE_CHOOSER_ACTION_OPEN, 244 | GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, 245 | GTK_STOCK_OK, GTK_RESPONSE_OK, nullptr))); 246 | connect(d.data(), SIGNAL(accept()), this, SLOT(onAccepted())); 247 | connect(d.data(), SIGNAL(reject()), this, SIGNAL(reject())); 248 | 249 | g_signal_connect(GTK_FILE_CHOOSER(d->gtkDialog()), "selection-changed", G_CALLBACK(onSelectionChanged), this); 250 | g_signal_connect_swapped(GTK_FILE_CHOOSER(d->gtkDialog()), "current-folder-changed", G_CALLBACK(onCurrentFolderChanged), this); 251 | 252 | previewWidget = gtk_image_new(); 253 | g_signal_connect(G_OBJECT(d->gtkDialog()), "update-preview", G_CALLBACK(onUpdatePreview), this); 254 | gtk_file_chooser_set_preview_widget(GTK_FILE_CHOOSER(d->gtkDialog()), previewWidget); 255 | } 256 | 257 | Qt6Gtk2FileDialogHelper::~Qt6Gtk2FileDialogHelper() 258 | { 259 | } 260 | 261 | bool Qt6Gtk2FileDialogHelper::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) 262 | { 263 | _dir.clear(); 264 | _selection.clear(); 265 | 266 | applyOptions(); 267 | return d->show(flags, modality, parent); 268 | } 269 | 270 | void Qt6Gtk2FileDialogHelper::exec() 271 | { 272 | d->exec(); 273 | } 274 | 275 | void Qt6Gtk2FileDialogHelper::hide() 276 | { 277 | // After GtkFileChooserDialog has been hidden, gtk_file_chooser_get_current_folder() 278 | // & gtk_file_chooser_get_filenames() will return bogus values -> cache the actual 279 | // values before hiding the dialog 280 | _dir = directory(); 281 | _selection = selectedFiles(); 282 | 283 | d->hide(); 284 | } 285 | 286 | bool Qt6Gtk2FileDialogHelper::defaultNameFilterDisables() const 287 | { 288 | return false; 289 | } 290 | 291 | void Qt6Gtk2FileDialogHelper::setDirectory(const QUrl &directory) 292 | { 293 | GtkDialog *gtkDialog = d->gtkDialog(); 294 | gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(gtkDialog), directory.toLocalFile().toUtf8().constData()); 295 | } 296 | 297 | QUrl Qt6Gtk2FileDialogHelper::directory() const 298 | { 299 | // While GtkFileChooserDialog is hidden, gtk_file_chooser_get_current_folder() 300 | // returns a bogus value -> return the cached value before hiding 301 | if (!_dir.isEmpty()) 302 | return _dir; 303 | 304 | QString ret; 305 | GtkDialog *gtkDialog = d->gtkDialog(); 306 | gchar *folder = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(gtkDialog)); 307 | if (folder) { 308 | ret = QString::fromUtf8(folder); 309 | g_free(folder); 310 | } 311 | return QUrl::fromLocalFile(ret); 312 | } 313 | 314 | void Qt6Gtk2FileDialogHelper::selectFile(const QUrl &filename) 315 | { 316 | GtkDialog *gtkDialog = d->gtkDialog(); 317 | if (options()->acceptMode() == QFileDialogOptions::AcceptSave) { 318 | QFileInfo fi(filename.toLocalFile()); 319 | gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(gtkDialog), fi.path().toUtf8().constData()); 320 | gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(gtkDialog), fi.fileName().toUtf8().constData()); 321 | } else { 322 | gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(gtkDialog), filename.toLocalFile().toUtf8().constData()); 323 | } 324 | } 325 | 326 | QList Qt6Gtk2FileDialogHelper::selectedFiles() const 327 | { 328 | // While GtkFileChooserDialog is hidden, gtk_file_chooser_get_filenames() 329 | // returns a bogus value -> return the cached value before hiding 330 | if (!_selection.isEmpty()) 331 | return _selection; 332 | 333 | QList selection; 334 | GtkDialog *gtkDialog = d->gtkDialog(); 335 | GSList *filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(gtkDialog)); 336 | for (GSList *it = filenames; it; it = it->next) 337 | selection += QUrl::fromLocalFile(QString::fromUtf8((const char*)it->data)); 338 | g_slist_free(filenames); 339 | return selection; 340 | } 341 | 342 | void Qt6Gtk2FileDialogHelper::setFilter() 343 | { 344 | applyOptions(); 345 | } 346 | 347 | void Qt6Gtk2FileDialogHelper::selectNameFilter(const QString &filter) 348 | { 349 | GtkFileFilter *gtkFilter = _filters.value(filter); 350 | if (gtkFilter) { 351 | GtkDialog *gtkDialog = d->gtkDialog(); 352 | gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(gtkDialog), gtkFilter); 353 | } 354 | } 355 | 356 | QString Qt6Gtk2FileDialogHelper::selectedNameFilter() const 357 | { 358 | GtkDialog *gtkDialog = d->gtkDialog(); 359 | GtkFileFilter *gtkFilter = gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(gtkDialog)); 360 | return _filterNames.value(gtkFilter); 361 | } 362 | 363 | void Qt6Gtk2FileDialogHelper::onAccepted() 364 | { 365 | emit accept(); 366 | 367 | QString filter = selectedNameFilter(); 368 | if (filter.isEmpty()) 369 | emit filterSelected(filter); 370 | 371 | QList files = selectedFiles(); 372 | emit filesSelected(files); 373 | if (files.count() == 1) 374 | emit fileSelected(files.first()); 375 | } 376 | 377 | void Qt6Gtk2FileDialogHelper::onSelectionChanged(GtkDialog *gtkDialog, Qt6Gtk2FileDialogHelper *helper) 378 | { 379 | QString selection; 380 | gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(gtkDialog)); 381 | if (filename) { 382 | selection = QString::fromUtf8(filename); 383 | g_free(filename); 384 | } 385 | emit helper->currentChanged(QUrl::fromLocalFile(selection)); 386 | } 387 | 388 | void Qt6Gtk2FileDialogHelper::onCurrentFolderChanged(Qt6Gtk2FileDialogHelper *dialog) 389 | { 390 | emit dialog->directoryEntered(dialog->directory()); 391 | } 392 | 393 | void Qt6Gtk2FileDialogHelper::onUpdatePreview(GtkDialog *gtkDialog, Qt6Gtk2FileDialogHelper *helper) 394 | { 395 | gchar *filename = gtk_file_chooser_get_preview_filename(GTK_FILE_CHOOSER(gtkDialog)); 396 | if (!filename) { 397 | gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(gtkDialog), false); 398 | return; 399 | } 400 | 401 | // Don't attempt to open anything which isn't a regular file. If a named pipe, 402 | // this may hang. 403 | QFileInfo fileinfo(filename); 404 | if (!fileinfo.exists() || !fileinfo.isFile()) { 405 | g_free(filename); 406 | gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(gtkDialog), false); 407 | return; 408 | } 409 | 410 | // This will preserve the image's aspect ratio. 411 | GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file_at_size(filename, PREVIEW_WIDTH, PREVIEW_HEIGHT, 0); 412 | g_free(filename); 413 | if (pixbuf) { 414 | gtk_image_set_from_pixbuf(GTK_IMAGE(helper->previewWidget), pixbuf); 415 | g_object_unref(pixbuf); 416 | } 417 | gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(gtkDialog), pixbuf ? true : false); 418 | } 419 | 420 | static GtkFileChooserAction gtkFileChooserAction(const QSharedPointer &options) 421 | { 422 | switch (options->fileMode()) { 423 | case QFileDialogOptions::AnyFile: 424 | case QFileDialogOptions::ExistingFile: 425 | case QFileDialogOptions::ExistingFiles: 426 | if (options->acceptMode() == QFileDialogOptions::AcceptOpen) 427 | return GTK_FILE_CHOOSER_ACTION_OPEN; 428 | else 429 | return GTK_FILE_CHOOSER_ACTION_SAVE; 430 | case QFileDialogOptions::Directory: 431 | case QFileDialogOptions::DirectoryOnly: 432 | default: 433 | if (options->acceptMode() == QFileDialogOptions::AcceptOpen) 434 | return GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER; 435 | else 436 | return GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER; 437 | } 438 | } 439 | 440 | void Qt6Gtk2FileDialogHelper::applyOptions() 441 | { 442 | GtkDialog *gtkDialog = d->gtkDialog(); 443 | const QSharedPointer &opts = options(); 444 | 445 | gtk_window_set_title(GTK_WINDOW(gtkDialog), opts->windowTitle().toUtf8().constData()); 446 | gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(gtkDialog), true); 447 | 448 | const GtkFileChooserAction action = gtkFileChooserAction(opts); 449 | gtk_file_chooser_set_action(GTK_FILE_CHOOSER(gtkDialog), action); 450 | 451 | const bool selectMultiple = opts->fileMode() == QFileDialogOptions::ExistingFiles; 452 | gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(gtkDialog), selectMultiple); 453 | 454 | const bool confirmOverwrite = !opts->testOption(QFileDialogOptions::DontConfirmOverwrite); 455 | gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(gtkDialog), confirmOverwrite); 456 | 457 | const QStringList nameFilters = opts->nameFilters(); 458 | if (!nameFilters.isEmpty()) 459 | setNameFilters(nameFilters); 460 | 461 | if (opts->initialDirectory().isLocalFile()) 462 | setDirectory(opts->initialDirectory()); 463 | 464 | for (const QUrl &filename : opts->initiallySelectedFiles()) 465 | selectFile(filename); 466 | 467 | const QString initialNameFilter = opts->initiallySelectedNameFilter(); 468 | if (!initialNameFilter.isEmpty()) 469 | selectNameFilter(initialNameFilter); 470 | 471 | #if GTK_CHECK_VERSION(2, 20, 0) 472 | GtkWidget *acceptButton = gtk_dialog_get_widget_for_response(gtkDialog, GTK_RESPONSE_OK); 473 | if (acceptButton) { 474 | if (opts->isLabelExplicitlySet(QFileDialogOptions::Accept)) 475 | gtk_button_set_label(GTK_BUTTON(acceptButton), opts->labelText(QFileDialogOptions::Accept).toUtf8().constData()); 476 | else if (opts->acceptMode() == QFileDialogOptions::AcceptOpen) 477 | gtk_button_set_label(GTK_BUTTON(acceptButton), GTK_STOCK_OPEN); 478 | else 479 | gtk_button_set_label(GTK_BUTTON(acceptButton), GTK_STOCK_SAVE); 480 | } 481 | 482 | GtkWidget *rejectButton = gtk_dialog_get_widget_for_response(gtkDialog, GTK_RESPONSE_CANCEL); 483 | if (rejectButton) { 484 | if (opts->isLabelExplicitlySet(QFileDialogOptions::Reject)) 485 | gtk_button_set_label(GTK_BUTTON(rejectButton), opts->labelText(QFileDialogOptions::Reject).toUtf8().constData()); 486 | else 487 | gtk_button_set_label(GTK_BUTTON(rejectButton), GTK_STOCK_CANCEL); 488 | } 489 | #endif 490 | } 491 | 492 | void Qt6Gtk2FileDialogHelper::setNameFilters(const QStringList &filters) 493 | { 494 | GtkDialog *gtkDialog = d->gtkDialog(); 495 | for (GtkFileFilter *filter : std::as_const(_filters)) 496 | gtk_file_chooser_remove_filter(GTK_FILE_CHOOSER(gtkDialog), filter); 497 | 498 | _filters.clear(); 499 | _filterNames.clear(); 500 | 501 | for (const QString &filter : std::as_const(filters)) { 502 | GtkFileFilter *gtkFilter = gtk_file_filter_new(); 503 | const QString name = filter.left(filter.indexOf(QLatin1Char('('))); 504 | const QStringList extensions = cleanFilterList(filter); 505 | 506 | gtk_file_filter_set_name(gtkFilter, name.isEmpty() ? extensions.join(QStringLiteral(", ")).toUtf8().constData() : 507 | name.toUtf8().constData()); 508 | for (const QString &ext : std::as_const(extensions)) 509 | gtk_file_filter_add_pattern(gtkFilter, ext.toUtf8().constData()); 510 | 511 | gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(gtkDialog), gtkFilter); 512 | 513 | _filters.insert(filter, gtkFilter); 514 | _filterNames.insert(gtkFilter, filter); 515 | } 516 | } 517 | 518 | Qt6Gtk2FontDialogHelper::Qt6Gtk2FontDialogHelper() 519 | { 520 | d.reset(new QGtk2Dialog(gtk_font_selection_dialog_new(""))); 521 | connect(d.data(), SIGNAL(accept()), this, SLOT(onAccepted())); 522 | connect(d.data(), SIGNAL(reject()), this, SIGNAL(reject())); 523 | } 524 | 525 | Qt6Gtk2FontDialogHelper::~Qt6Gtk2FontDialogHelper() 526 | { 527 | } 528 | 529 | bool Qt6Gtk2FontDialogHelper::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) 530 | { 531 | applyOptions(); 532 | return d->show(flags, modality, parent); 533 | } 534 | 535 | void Qt6Gtk2FontDialogHelper::exec() 536 | { 537 | d->exec(); 538 | } 539 | 540 | void Qt6Gtk2FontDialogHelper::hide() 541 | { 542 | d->hide(); 543 | } 544 | 545 | static QString qt_fontToString(const QFont &font) 546 | { 547 | PangoFontDescription *desc = pango_font_description_new(); 548 | pango_font_description_set_size(desc, (font.pointSizeF() > 0.0 ? font.pointSizeF() : QFontInfo(font).pointSizeF()) * PANGO_SCALE); 549 | pango_font_description_set_family(desc, QFontInfo(font).family().toUtf8().constData()); 550 | 551 | int weight = font.weight(); 552 | if (weight >= QFont::Black) 553 | pango_font_description_set_weight(desc, PANGO_WEIGHT_HEAVY); 554 | else if (weight >= QFont::ExtraBold) 555 | pango_font_description_set_weight(desc, PANGO_WEIGHT_ULTRABOLD); 556 | else if (weight >= QFont::Bold) 557 | pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD); 558 | else if (weight >= QFont::DemiBold) 559 | pango_font_description_set_weight(desc, PANGO_WEIGHT_SEMIBOLD); 560 | else if (weight >= QFont::Medium) 561 | pango_font_description_set_weight(desc, PANGO_WEIGHT_MEDIUM); 562 | else if (weight >= QFont::Normal) 563 | pango_font_description_set_weight(desc, PANGO_WEIGHT_NORMAL); 564 | else if (weight >= QFont::Light) 565 | pango_font_description_set_weight(desc, PANGO_WEIGHT_LIGHT); 566 | else if (weight >= QFont::ExtraLight) 567 | pango_font_description_set_weight(desc, PANGO_WEIGHT_ULTRALIGHT); 568 | else 569 | pango_font_description_set_weight(desc, PANGO_WEIGHT_THIN); 570 | 571 | int style = font.style(); 572 | if (style == QFont::StyleItalic) 573 | pango_font_description_set_style(desc, PANGO_STYLE_ITALIC); 574 | else if (style == QFont::StyleOblique) 575 | pango_font_description_set_style(desc, PANGO_STYLE_OBLIQUE); 576 | else 577 | pango_font_description_set_style(desc, PANGO_STYLE_NORMAL); 578 | 579 | char *str = pango_font_description_to_string(desc); 580 | QString name = QString::fromUtf8(str); 581 | pango_font_description_free(desc); 582 | g_free(str); 583 | return name; 584 | } 585 | 586 | static QFont qt_fontFromString(const QString &name) 587 | { 588 | QFont font; 589 | PangoFontDescription *desc = pango_font_description_from_string(name.toUtf8().constData()); 590 | font.setPointSizeF(static_cast(pango_font_description_get_size(desc)) / PANGO_SCALE); 591 | 592 | QString family = QString::fromUtf8(pango_font_description_get_family(desc)); 593 | if (!family.isEmpty()) 594 | font.setFamily(family); 595 | 596 | const int weight = pango_font_description_get_weight(desc); 597 | font.setWeight(QFont::Weight(weight)); 598 | 599 | PangoStyle style = pango_font_description_get_style(desc); 600 | if (style == PANGO_STYLE_ITALIC) 601 | font.setStyle(QFont::StyleItalic); 602 | else if (style == PANGO_STYLE_OBLIQUE) 603 | font.setStyle(QFont::StyleOblique); 604 | else 605 | font.setStyle(QFont::StyleNormal); 606 | 607 | pango_font_description_free(desc); 608 | return font; 609 | } 610 | 611 | void Qt6Gtk2FontDialogHelper::setCurrentFont(const QFont &font) 612 | { 613 | GtkFontSelectionDialog *gtkDialog = GTK_FONT_SELECTION_DIALOG(d->gtkDialog()); 614 | gtk_font_selection_dialog_set_font_name(gtkDialog, qt_fontToString(font).toUtf8().constData()); 615 | } 616 | 617 | QFont Qt6Gtk2FontDialogHelper::currentFont() const 618 | { 619 | GtkFontSelectionDialog *gtkDialog = GTK_FONT_SELECTION_DIALOG(d->gtkDialog()); 620 | gchar *name = gtk_font_selection_dialog_get_font_name(gtkDialog); 621 | QFont font = qt_fontFromString(QString::fromUtf8(name)); 622 | g_free(name); 623 | return font; 624 | } 625 | 626 | void Qt6Gtk2FontDialogHelper::onAccepted() 627 | { 628 | emit currentFontChanged(currentFont()); 629 | emit accept(); 630 | emit fontSelected(currentFont()); 631 | } 632 | 633 | void Qt6Gtk2FontDialogHelper::applyOptions() 634 | { 635 | GtkDialog *gtkDialog = d->gtkDialog(); 636 | const QSharedPointer &opts = options(); 637 | 638 | gtk_window_set_title(GTK_WINDOW(gtkDialog), opts->windowTitle().toUtf8().constData()); 639 | 640 | GtkWidget *okButton = gtk_font_selection_dialog_get_ok_button(GTK_FONT_SELECTION_DIALOG(gtkDialog)); 641 | GtkWidget *cancelButton = gtk_font_selection_dialog_get_cancel_button(GTK_FONT_SELECTION_DIALOG(gtkDialog)); 642 | if (okButton) 643 | gtk_widget_set_visible(okButton, !options()->testOption(QFontDialogOptions::NoButtons)); 644 | if (cancelButton) 645 | gtk_widget_set_visible(cancelButton, !options()->testOption(QFontDialogOptions::NoButtons)); 646 | } 647 | 648 | QT_END_NAMESPACE 649 | 650 | #include "qt6gtk2dialoghelpers.moc" 651 | -------------------------------------------------------------------------------- /src/qt6gtk2-qtplugin/qt6gtk2dialoghelpers.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2015 The Qt Company Ltd. * 3 | * Copyright (C) 2016-2024 Ilya Kotov, forkotov02@ya.ru * 4 | * * 5 | * This program is free software; you can redistribute it and/or modify * 6 | * it under the terms of the GNU General Public License as published by * 7 | * the Free Software Foundation; either version 2 of the License, or * 8 | * (at your option) any later version. * 9 | * * 10 | * This program is distributed in the hope that it will be useful, * 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 13 | * GNU General Public License for more details. * 14 | * * 15 | * You should have received a copy of the GNU General Public License * 16 | * along with this program; if not, write to the * 17 | * Free Software Foundation, Inc., * 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * 19 | ***************************************************************************/ 20 | 21 | #ifndef QGTK2DIALOGHELPERS_P_H 22 | #define QGTK2DIALOGHELPERS_P_H 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | typedef struct _GtkWidget GtkWidget; 32 | typedef struct _GtkDialog GtkDialog; 33 | typedef struct _GtkFileFilter GtkFileFilter; 34 | 35 | QT_BEGIN_NAMESPACE 36 | 37 | class QGtk2Dialog; 38 | class QColor; 39 | 40 | class Qt6Gtk2ColorDialogHelper : public QPlatformColorDialogHelper 41 | { 42 | Q_OBJECT 43 | 44 | public: 45 | Qt6Gtk2ColorDialogHelper(); 46 | ~Qt6Gtk2ColorDialogHelper(); 47 | 48 | bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) override; 49 | void exec() override; 50 | void hide() override; 51 | 52 | void setCurrentColor(const QColor &color) override; 53 | QColor currentColor() const override; 54 | 55 | private Q_SLOTS: 56 | void onAccepted(); 57 | 58 | private: 59 | static void onColorChanged(Qt6Gtk2ColorDialogHelper *helper); 60 | void applyOptions(); 61 | 62 | QScopedPointer d; 63 | }; 64 | 65 | class Qt6Gtk2FileDialogHelper : public QPlatformFileDialogHelper 66 | { 67 | Q_OBJECT 68 | 69 | public: 70 | Qt6Gtk2FileDialogHelper(); 71 | ~Qt6Gtk2FileDialogHelper(); 72 | 73 | bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) override; 74 | void exec() override; 75 | void hide() override; 76 | 77 | bool defaultNameFilterDisables() const override; 78 | void setDirectory(const QUrl &directory) override; 79 | QUrl directory() const override; 80 | void selectFile(const QUrl &filename) override; 81 | QList selectedFiles() const override; 82 | void setFilter() override; 83 | void selectNameFilter(const QString &filter) override; 84 | QString selectedNameFilter() const override; 85 | 86 | private Q_SLOTS: 87 | void onAccepted(); 88 | 89 | private: 90 | static void onSelectionChanged(GtkDialog *dialog, Qt6Gtk2FileDialogHelper *helper); 91 | static void onCurrentFolderChanged(Qt6Gtk2FileDialogHelper *helper); 92 | static void onUpdatePreview(GtkDialog *dialog, Qt6Gtk2FileDialogHelper *helper); 93 | void applyOptions(); 94 | void setNameFilters(const QStringList &filters); 95 | 96 | QUrl _dir; 97 | QList _selection; 98 | QHash _filters; 99 | QHash _filterNames; 100 | QScopedPointer d; 101 | GtkWidget *previewWidget; 102 | }; 103 | 104 | class Qt6Gtk2FontDialogHelper : public QPlatformFontDialogHelper 105 | { 106 | Q_OBJECT 107 | 108 | public: 109 | Qt6Gtk2FontDialogHelper(); 110 | ~Qt6Gtk2FontDialogHelper(); 111 | 112 | bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) override; 113 | void exec() override; 114 | void hide() override; 115 | 116 | void setCurrentFont(const QFont &font) override; 117 | QFont currentFont() const override; 118 | 119 | private Q_SLOTS: 120 | void onAccepted(); 121 | 122 | private: 123 | void applyOptions(); 124 | 125 | QScopedPointer d; 126 | }; 127 | 128 | QT_END_NAMESPACE 129 | 130 | #endif // QT5GTK2DIALOGHELPERS_P_H 131 | -------------------------------------------------------------------------------- /src/qt6gtk2-qtplugin/qt6gtk2theme.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2015 The Qt Company Ltd. * 3 | * Copyright (C) 2016-2024 Ilya Kotov, forkotov02@ya.ru * 4 | * * 5 | * This program is free software; you can redistribute it and/or modify * 6 | * it under the terms of the GNU General Public License as published by * 7 | * the Free Software Foundation; either version 2 of the License, or * 8 | * (at your option) any later version. * 9 | * * 10 | * This program is distributed in the hope that it will be useful, * 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 13 | * GNU General Public License for more details. * 14 | * * 15 | * You should have received a copy of the GNU General Public License * 16 | * along with this program; if not, write to the * 17 | * Free Software Foundation, Inc., * 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * 19 | ***************************************************************************/ 20 | 21 | #include "qt6gtk2theme.h" 22 | #include "qt6gtk2dialoghelpers.h" 23 | #include 24 | 25 | #undef signals 26 | #include 27 | 28 | #include 29 | 30 | QT_BEGIN_NAMESPACE 31 | 32 | static QVariant gtkSetting(const gchar *propertyName) 33 | { 34 | GtkSettings *settings = gtk_settings_get_default(); 35 | GValue value = G_VALUE_INIT; 36 | QVariant ret; 37 | 38 | g_object_get_property(G_OBJECT(settings), propertyName, &value); 39 | if (G_VALUE_HOLDS_INT(&value)) { 40 | ret = QVariant(g_value_get_int(&value)); 41 | } else if (G_VALUE_HOLDS_UINT(&value)) { 42 | ret = QVariant(g_value_get_uint(&value)); 43 | } else if (G_VALUE_HOLDS_FLOAT(&value)) { 44 | ret = QVariant(g_value_get_float(&value)); 45 | } else if (G_VALUE_HOLDS_STRING(&value)) { 46 | ret = QVariant(QString::fromUtf8(g_value_get_string(&value))); 47 | } else if (G_VALUE_HOLDS_BOOLEAN(&value)) { 48 | ret = QVariant(g_value_get_boolean(&value)); 49 | } else { 50 | ret = QVariant(); 51 | } 52 | g_value_unset(&value); 53 | return ret; 54 | } 55 | 56 | Qt6Gtk2Theme::Qt6Gtk2Theme() 57 | { 58 | // gtk_init will reset the Xlib error handler, and that causes 59 | // Qt applications to quit on X errors. Therefore, we need to manually restore it. 60 | int (*oldErrorHandler)(Display *, XErrorEvent *) = XSetErrorHandler(nullptr); 61 | 62 | gtk_init(nullptr, nullptr); 63 | 64 | XSetErrorHandler(oldErrorHandler); 65 | } 66 | 67 | QVariant Qt6Gtk2Theme::themeHint(QPlatformTheme::ThemeHint hint) const 68 | { 69 | switch (hint) { 70 | case QPlatformTheme::CursorFlashTime: 71 | // As close to GTK as possible. 72 | if (gtkSetting("gtk-cursor-blink").toBool() && gtkSetting("gtk-cursor-blink-timeout").toInt() != 0) { 73 | return gtkSetting("gtk-cursor-blink-time"); 74 | } else { 75 | return QVariant((int) 0); 76 | } 77 | case QPlatformTheme::PasswordMaskDelay: 78 | return gtkSetting("gtk-entry-password-hint-timeout"); 79 | case QPlatformTheme::DialogButtonBoxButtonsHaveIcons: 80 | return gtkSetting("gtk-button-images"); 81 | case QPlatformTheme::ShowShortcutsInContextMenus: 82 | return gtkSetting("gtk-enable-accels"); 83 | case QPlatformTheme::SystemIconThemeName: 84 | return gtkSetting("gtk-icon-theme-name"); 85 | case QPlatformTheme::SystemIconFallbackThemeName: 86 | return gtkSetting("gtk-fallback-icon-theme"); 87 | case QPlatformTheme::StyleNames: 88 | { 89 | QStringList styleNames; 90 | styleNames << "qt6gtk2"; 91 | //styleNames << QGnomeTheme::themeHint(hint).toStringList(); 92 | return styleNames; 93 | } 94 | default: 95 | return QGnomeTheme::themeHint(hint); 96 | } 97 | } 98 | 99 | QString Qt6Gtk2Theme::gtkFontName() const 100 | { 101 | QString cfgFontName = gtkSetting("gtk-font-name").toString(); 102 | if (!cfgFontName.isEmpty()) 103 | return cfgFontName; 104 | return QGnomeTheme::gtkFontName(); 105 | } 106 | 107 | bool Qt6Gtk2Theme::usePlatformNativeDialog(DialogType type) const 108 | { 109 | switch (type) { 110 | case ColorDialog: 111 | return true; 112 | case FileDialog: 113 | return true; 114 | case FontDialog: 115 | return true; 116 | default: 117 | return false; 118 | } 119 | } 120 | 121 | QPlatformDialogHelper *Qt6Gtk2Theme::createPlatformDialogHelper(DialogType type) const 122 | { 123 | switch (type) { 124 | case ColorDialog: 125 | return new Qt6Gtk2ColorDialogHelper; 126 | case FileDialog: 127 | return new Qt6Gtk2FileDialogHelper; 128 | case FontDialog: 129 | return new Qt6Gtk2FontDialogHelper; 130 | default: 131 | return nullptr; 132 | } 133 | } 134 | 135 | QT_END_NAMESPACE 136 | -------------------------------------------------------------------------------- /src/qt6gtk2-qtplugin/qt6gtk2theme.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2015 The Qt Company Ltd. * 3 | * Copyright (C) 2016-2024 Ilya Kotov, forkotov02@ya.ru * 4 | * * 5 | * This program is free software; you can redistribute it and/or modify * 6 | * it under the terms of the GNU General Public License as published by * 7 | * the Free Software Foundation; either version 2 of the License, or * 8 | * (at your option) any later version. * 9 | * * 10 | * This program is distributed in the hope that it will be useful, * 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 13 | * GNU General Public License for more details. * 14 | * * 15 | * You should have received a copy of the GNU General Public License * 16 | * along with this program; if not, write to the * 17 | * Free Software Foundation, Inc., * 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * 19 | ***************************************************************************/ 20 | 21 | #ifndef QT6GTK2THEME_H 22 | #define QT6GTK2THEME_H 23 | 24 | #include 25 | 26 | QT_BEGIN_NAMESPACE 27 | 28 | class Qt6Gtk2Theme : public QGnomeTheme 29 | { 30 | public: 31 | Qt6Gtk2Theme(); 32 | 33 | virtual QVariant themeHint(ThemeHint hint) const override; 34 | virtual QString gtkFontName() const override; 35 | 36 | bool usePlatformNativeDialog(DialogType type) const override; 37 | QPlatformDialogHelper *createPlatformDialogHelper(DialogType type) const override; 38 | }; 39 | 40 | QT_END_NAMESPACE 41 | 42 | #endif // QT6GTK2THEME_H 43 | -------------------------------------------------------------------------------- /src/qt6gtk2-style/plugin.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2015 The Qt Company Ltd. * 3 | * Copyright (C) 2016-2024 Ilya Kotov, forkotov02@ya.ru * 4 | * * 5 | * This program is free software; you can redistribute it and/or modify * 6 | * it under the terms of the GNU General Public License as published by * 7 | * the Free Software Foundation; either version 2 of the License, or * 8 | * (at your option) any later version. * 9 | * * 10 | * This program is distributed in the hope that it will be useful, * 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 13 | * GNU General Public License for more details. * 14 | * * 15 | * You should have received a copy of the GNU General Public License * 16 | * along with this program; if not, write to the * 17 | * Free Software Foundation, Inc., * 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * 19 | ***************************************************************************/ 20 | 21 | #include 22 | #include 23 | #include "qgtkstyle_p.h" 24 | 25 | QT_BEGIN_NAMESPACE 26 | 27 | class Qt6Gtk2StylePlugin : public QStylePlugin 28 | { 29 | Q_OBJECT 30 | Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QStyleFactoryInterface" FILE "qt6gtk2.json") 31 | 32 | public: 33 | QStyle *create(const QString &key) override; 34 | }; 35 | 36 | QStyle *Qt6Gtk2StylePlugin::create(const QString &key) 37 | { 38 | QVersionNumber v = QLibraryInfo::version(); 39 | if(v.majorVersion() != QT_VERSION_MAJOR || v.minorVersion() != QT_VERSION_MINOR) 40 | { 41 | qCritical("qt6gtk2 is compiled against incompatible Qt version (" QT_VERSION_STR ")."); 42 | return nullptr; 43 | } 44 | 45 | if (key == "gtk2" || key == "qt6gtk2" || key == "qt5gtk2") 46 | return new QGtkStyle; 47 | return nullptr; 48 | } 49 | 50 | QT_END_NAMESPACE 51 | 52 | #include "plugin.moc" 53 | -------------------------------------------------------------------------------- /src/qt6gtk2-style/qgtk2painter.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2015 The Qt Company Ltd. * 3 | * Copyright (C) 2016-2024 Ilya Kotov, forkotov02@ya.ru * 4 | * * 5 | * This program is free software; you can redistribute it and/or modify * 6 | * it under the terms of the GNU General Public License as published by * 7 | * the Free Software Foundation; either version 2 of the License, or * 8 | * (at your option) any later version. * 9 | * * 10 | * This program is distributed in the hope that it will be useful, * 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 13 | * GNU General Public License for more details. * 14 | * * 15 | * You should have received a copy of the GNU General Public License * 16 | * along with this program; if not, write to the * 17 | * Free Software Foundation, Inc., * 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * 19 | ***************************************************************************/ 20 | 21 | #include "qgtk2painter_p.h" 22 | 23 | #include 24 | #if !defined(QT_NO_STYLE_GTK) 25 | 26 | // This class is primarily a wrapper around the gtk painter functions 27 | // and takes care of converting all such calls into cached Qt pixmaps. 28 | 29 | #include "qgtkstyle_p_p.h" 30 | #include 31 | #include 32 | #include 33 | 34 | QT_BEGIN_NAMESPACE 35 | 36 | // To recover alpha we apply the gtk painting function two times to 37 | // white, and black window backgrounds. This can be used to 38 | // recover the premultiplied alpha channel 39 | QPixmap QGtk2Painter::renderTheme(uchar *bdata, uchar *wdata, const QRect &rect) const 40 | { 41 | const int bytecount = rect.width() * rect.height() * 4; 42 | for (int index = 0; index < bytecount ; index += 4) { 43 | uchar val = bdata[index + GTK_BLUE]; 44 | if (m_alpha) { 45 | int alphaval = qMax(bdata[index + GTK_BLUE] - wdata[index + GTK_BLUE], 46 | bdata[index + GTK_GREEN] - wdata[index + GTK_GREEN]); 47 | alphaval = qMax(alphaval, bdata[index + GTK_RED] - wdata[index + GTK_RED]) + 255; 48 | bdata[index + QT_ALPHA] = alphaval; 49 | } 50 | bdata[index + QT_RED] = bdata[index + GTK_RED]; 51 | bdata[index + QT_GREEN] = bdata[index + GTK_GREEN]; 52 | bdata[index + QT_BLUE] = val; 53 | } 54 | QImage converted((const uchar*)bdata, rect.width(), rect.height(), m_alpha ? 55 | QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32); 56 | 57 | if (m_hflipped || m_vflipped) { 58 | return QPixmap::fromImage(converted.mirrored(m_hflipped, m_vflipped)); 59 | } 60 | // on raster graphicssystem we need to do a copy here, because 61 | // we intend to deallocate the qimage bits shortly after... 62 | return QPixmap::fromImage(converted.copy()); 63 | } 64 | 65 | // This macro is responsible for painting any GtkStyle painting function onto a QPixmap 66 | #define DRAW_TO_CACHE(draw_func) \ 67 | if (rect.width() > QWIDGETSIZE_MAX || rect.height() > QWIDGETSIZE_MAX) \ 68 | return; \ 69 | QRect pixmapRect(0, 0, rect.width(), rect.height()); \ 70 | { \ 71 | GdkPixmap *pixmap = gdk_pixmap_new((GdkDrawable*)(m_window->window), \ 72 | rect.width(), rect.height(), -1); \ 73 | if (!pixmap) \ 74 | return; \ 75 | style = gtk_style_attach (style, m_window->window); \ 76 | gdk_draw_rectangle(pixmap, m_alpha ? style->black_gc : *style->bg_gc, \ 77 | true, 0, 0, rect.width(), rect.height()); \ 78 | draw_func; \ 79 | GdkPixbuf *imgb = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, \ 80 | rect.width(), rect.height()); \ 81 | if (!imgb) \ 82 | return; \ 83 | imgb = gdk_pixbuf_get_from_drawable(imgb, pixmap, nullptr, 0, 0, 0, 0, \ 84 | rect.width(), rect.height()); \ 85 | uchar* bdata = (uchar*)gdk_pixbuf_get_pixels(imgb); \ 86 | if (m_alpha) { \ 87 | gdk_draw_rectangle(pixmap, style->white_gc, true, 0, 0, \ 88 | rect.width(), rect.height()); \ 89 | draw_func; \ 90 | GdkPixbuf *imgw = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, \ 91 | rect.width(), rect.height()); \ 92 | if (!imgw) \ 93 | return; \ 94 | imgw = gdk_pixbuf_get_from_drawable(imgw, pixmap, nullptr, 0, 0, 0, 0,\ 95 | rect.width(), rect.height()); \ 96 | uchar* wdata = (uchar*)gdk_pixbuf_get_pixels(imgw); \ 97 | cache = renderTheme(bdata, wdata, rect); \ 98 | g_object_unref(imgw); \ 99 | } else { \ 100 | cache = renderTheme(bdata, nullptr, rect); \ 101 | } \ 102 | gdk_drawable_unref(pixmap); \ 103 | g_object_unref(imgb); \ 104 | } 105 | 106 | QGtk2Painter::QGtk2Painter() : QGtkPainter(), m_window(QGtkStylePrivate::gtkWidget("GtkWindow")) 107 | {} 108 | 109 | // Note currently painted without alpha for performance reasons 110 | void QGtk2Painter::paintBoxGap(GtkWidget *gtkWidget, const gchar* part, 111 | const QRect &paintRect, GtkStateType state, 112 | GtkShadowType shadow, GtkPositionType gap_side, 113 | gint x, gint width, 114 | GtkStyle *style) 115 | { 116 | if (!paintRect.isValid()) 117 | return; 118 | 119 | QPixmap cache; 120 | QRect rect = paintRect; 121 | 122 | // To avoid exhausting cache on large tabframes we cheat a bit by 123 | // tiling the center part. 124 | 125 | const int maxHeight = 256; 126 | const int border = 16; 127 | if (rect.height() > maxHeight && (gap_side == GTK_POS_TOP || gap_side == GTK_POS_BOTTOM)) 128 | rect.setHeight(2 * border + 1); 129 | 130 | QString pixmapName = uniqueName(QLS(part), state, shadow, rect.size(), gtkWidget) 131 | % HexString(gap_side) 132 | % HexString(width) 133 | % HexString(x); 134 | 135 | if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, &cache)) { 136 | DRAW_TO_CACHE(gtk_paint_box_gap (style, 137 | pixmap, 138 | state, 139 | shadow, 140 | nullptr, 141 | gtkWidget, 142 | (const gchar*)part, 143 | 0, 0, 144 | rect.width(), 145 | rect.height(), 146 | gap_side, 147 | x, 148 | width)); 149 | if (m_usePixmapCache) 150 | QPixmapCache::insert(pixmapName, cache); 151 | } 152 | if (rect.size() != paintRect.size()) { 153 | // We assume we can stretch the middle tab part 154 | // Note: the side effect of this is that pinstripe patterns will get fuzzy 155 | const QSize size = cache.size(); 156 | // top part 157 | m_painter->drawPixmap(QRect(paintRect.left(), paintRect.top(), 158 | paintRect.width(), border), cache, 159 | QRect(0, 0, size.width(), border)); 160 | 161 | // tiled center part 162 | QPixmap tilePart(cache.width(), 1); 163 | QPainter scanLinePainter(&tilePart); 164 | scanLinePainter.drawPixmap(QRect(0, 0, tilePart.width(), tilePart.height()), cache, QRect(0, border, size.width(), 1)); 165 | scanLinePainter.end(); 166 | m_painter->drawTiledPixmap(QRect(paintRect.left(), paintRect.top() + border, 167 | paintRect.width(), paintRect.height() - 2*border), tilePart); 168 | 169 | // bottom part 170 | m_painter->drawPixmap(QRect(paintRect.left(), paintRect.top() + paintRect.height() - border, 171 | paintRect.width(), border), cache, 172 | QRect(0, size.height() - border, size.width(), border)); 173 | } else 174 | m_painter->drawPixmap(paintRect.topLeft(), cache); 175 | } 176 | 177 | void QGtk2Painter::paintBox(GtkWidget *gtkWidget, const gchar* part, 178 | const QRect &paintRect, GtkStateType state, 179 | GtkShadowType shadow, GtkStyle *style, 180 | const QString &pmKey) 181 | { 182 | if (!paintRect.isValid()) 183 | return; 184 | 185 | QPixmap cache; 186 | QRect rect = paintRect; 187 | 188 | // To avoid exhausting cache on large tabframes we cheat a bit by 189 | // tiling the center part. 190 | 191 | const int maxHeight = 256; 192 | const int maxArea = 256*512; 193 | const int border = 32; 194 | if (rect.height() > maxHeight && (rect.width()*rect.height() > maxArea)) 195 | rect.setHeight(2 * border + 1); 196 | 197 | QString pixmapName = uniqueName(QLS(part), state, shadow, 198 | rect.size(), gtkWidget) % pmKey; 199 | 200 | if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, &cache)) { 201 | DRAW_TO_CACHE(gtk_paint_box (style, 202 | pixmap, 203 | state, 204 | shadow, 205 | nullptr, 206 | gtkWidget, 207 | part, 208 | 0, 0, 209 | rect.width(), 210 | rect.height())); 211 | if (m_usePixmapCache) 212 | QPixmapCache::insert(pixmapName, cache); 213 | } 214 | if (rect.size() != paintRect.size()) { 215 | // We assume we can stretch the middle tab part 216 | // Note: the side effect of this is that pinstripe patterns will get fuzzy 217 | const QSize size = cache.size(); 218 | // top part 219 | m_painter->drawPixmap(QRect(paintRect.left(), paintRect.top(), 220 | paintRect.width(), border), cache, 221 | QRect(0, 0, size.width(), border)); 222 | 223 | // tiled center part 224 | QPixmap tilePart(cache.width(), 1); 225 | QPainter scanLinePainter(&tilePart); 226 | scanLinePainter.drawPixmap(QRect(0, 0, tilePart.width(), tilePart.height()), cache, QRect(0, border, size.width(), 1)); 227 | scanLinePainter.end(); 228 | m_painter->drawTiledPixmap(QRect(paintRect.left(), paintRect.top() + border, 229 | paintRect.width(), paintRect.height() - 2*border), tilePart); 230 | 231 | // bottom part 232 | m_painter->drawPixmap(QRect(paintRect.left(), paintRect.top() + paintRect.height() - border, 233 | paintRect.width(), border), cache, 234 | QRect(0, size.height() - border, size.width(), border)); 235 | } else 236 | m_painter->drawPixmap(paintRect.topLeft(), cache); 237 | } 238 | 239 | void QGtk2Painter::paintHline(GtkWidget *gtkWidget, const gchar* part, 240 | const QRect &rect, GtkStateType state, 241 | GtkStyle *style, int x1, int x2, int y, 242 | const QString &pmKey) 243 | { 244 | if (!rect.isValid()) 245 | return; 246 | 247 | QPixmap cache; 248 | QString pixmapName = uniqueName(QLS(part), state, GTK_SHADOW_NONE, rect.size(), gtkWidget) 249 | % HexString(x1) 250 | % HexString(x2) 251 | % HexString(y) 252 | % pmKey; 253 | if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, &cache)) { 254 | DRAW_TO_CACHE(gtk_paint_hline (style, 255 | pixmap, 256 | state, 257 | nullptr, 258 | gtkWidget, 259 | part, 260 | x1, x2, y)); 261 | if (m_usePixmapCache) 262 | QPixmapCache::insert(pixmapName, cache); 263 | } 264 | 265 | m_painter->drawPixmap(rect.topLeft(), cache); 266 | } 267 | 268 | void QGtk2Painter::paintVline(GtkWidget *gtkWidget, const gchar* part, 269 | const QRect &rect, GtkStateType state, 270 | GtkStyle *style, int y1, int y2, int x, 271 | const QString &pmKey) 272 | { 273 | if (!rect.isValid()) 274 | return; 275 | 276 | QPixmap cache; 277 | QString pixmapName = uniqueName(QLS(part), state, GTK_SHADOW_NONE, rect.size(), gtkWidget) 278 | % HexString(y1) 279 | % HexString(y2) 280 | % HexString(x) 281 | % pmKey; 282 | 283 | if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, &cache)) { 284 | DRAW_TO_CACHE(gtk_paint_vline (style, 285 | pixmap, 286 | state, 287 | nullptr, 288 | gtkWidget, 289 | part, 290 | y1, y2, 291 | x)); 292 | if (m_usePixmapCache) 293 | QPixmapCache::insert(pixmapName, cache); 294 | } 295 | m_painter->drawPixmap(rect.topLeft(), cache); 296 | } 297 | 298 | 299 | void QGtk2Painter::paintExpander(GtkWidget *gtkWidget, 300 | const gchar* part, const QRect &rect, 301 | GtkStateType state, GtkExpanderStyle expander_state, 302 | GtkStyle *style, const QString &pmKey) 303 | { 304 | if (!rect.isValid()) 305 | return; 306 | 307 | QPixmap cache; 308 | QString pixmapName = uniqueName(QLS(part), state, GTK_SHADOW_NONE, rect.size(), gtkWidget) 309 | % HexString(expander_state) 310 | % pmKey; 311 | 312 | if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, &cache)) { 313 | DRAW_TO_CACHE(gtk_paint_expander (style, pixmap, 314 | state, nullptr, 315 | gtkWidget, part, 316 | rect.width()/2, 317 | rect.height()/2, 318 | expander_state)); 319 | if (m_usePixmapCache) 320 | QPixmapCache::insert(pixmapName, cache); 321 | } 322 | 323 | m_painter->drawPixmap(rect.topLeft(), cache); 324 | } 325 | 326 | void QGtk2Painter::paintFocus(GtkWidget *gtkWidget, const gchar* part, 327 | const QRect &rect, GtkStateType state, 328 | GtkStyle *style, const QString &pmKey) 329 | { 330 | if (!rect.isValid()) 331 | return; 332 | 333 | QPixmap cache; 334 | QString pixmapName = uniqueName(QLS(part), state, GTK_SHADOW_NONE, rect.size(), gtkWidget) % pmKey; 335 | if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, &cache)) { 336 | DRAW_TO_CACHE(gtk_paint_focus (style, pixmap, state, nullptr, 337 | gtkWidget, 338 | part, 339 | 0, 0, 340 | rect.width(), 341 | rect.height())); 342 | if (m_usePixmapCache) 343 | QPixmapCache::insert(pixmapName, cache); 344 | } 345 | 346 | m_painter->drawPixmap(rect.topLeft(), cache); 347 | } 348 | 349 | 350 | void QGtk2Painter::paintResizeGrip(GtkWidget *gtkWidget, const gchar* part, 351 | const QRect &rect, GtkStateType state, 352 | GtkShadowType shadow, GdkWindowEdge edge, 353 | GtkStyle *style, const QString &pmKey) 354 | { 355 | if (!rect.isValid()) 356 | return; 357 | 358 | QPixmap cache; 359 | QString pixmapName = uniqueName(QLS(part), state, shadow, rect.size(), gtkWidget) % pmKey; 360 | if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, &cache)) { 361 | DRAW_TO_CACHE(gtk_paint_resize_grip (style, pixmap, state, 362 | nullptr, gtkWidget, 363 | part, edge, 0, 0, 364 | rect.width(), 365 | rect.height())); 366 | if (m_usePixmapCache) 367 | QPixmapCache::insert(pixmapName, cache); 368 | } 369 | 370 | m_painter->drawPixmap(rect.topLeft(), cache); 371 | } 372 | 373 | 374 | void QGtk2Painter::paintArrow(GtkWidget *gtkWidget, const gchar* part, 375 | const QRect &arrowrect, GtkArrowType arrow_type, 376 | GtkStateType state, GtkShadowType shadow, 377 | gboolean fill, GtkStyle *style, const QString &pmKey) 378 | { 379 | QRect rect = m_cliprect.isValid() ? m_cliprect : arrowrect; 380 | if (!rect.isValid()) 381 | return; 382 | 383 | QPixmap cache; 384 | QString pixmapName = uniqueName(QLS(part), state, shadow, rect.size()) 385 | % HexString(arrow_type) 386 | % pmKey; 387 | 388 | GdkRectangle gtkCliprect = {0, 0, rect.width(), rect.height()}; 389 | int xOffset = m_cliprect.isValid() ? arrowrect.x() - m_cliprect.x() : 0; 390 | int yOffset = m_cliprect.isValid() ? arrowrect.y() - m_cliprect.y() : 0; 391 | if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, &cache)) { 392 | DRAW_TO_CACHE(gtk_paint_arrow (style, pixmap, state, shadow, 393 | >kCliprect, 394 | gtkWidget, 395 | part, 396 | arrow_type, fill, 397 | xOffset, yOffset, 398 | arrowrect.width(), 399 | arrowrect.height())) 400 | if (m_usePixmapCache) 401 | QPixmapCache::insert(pixmapName, cache); 402 | } 403 | 404 | m_painter->drawPixmap(rect.topLeft(), cache); 405 | } 406 | 407 | 408 | void QGtk2Painter::paintHandle(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, 409 | GtkStateType state, GtkShadowType shadow, 410 | GtkOrientation orientation, GtkStyle *style) 411 | { 412 | if (!rect.isValid()) 413 | return; 414 | 415 | QPixmap cache; 416 | QString pixmapName = uniqueName(QLS(part), state, shadow, rect.size()) 417 | % HexString(orientation); 418 | 419 | if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, &cache)) { 420 | DRAW_TO_CACHE(gtk_paint_handle (style, 421 | pixmap, 422 | state, 423 | shadow, 424 | nullptr, 425 | gtkWidget, 426 | part, 0, 0, 427 | rect.width(), 428 | rect.height(), 429 | orientation)); 430 | if (m_usePixmapCache) 431 | QPixmapCache::insert(pixmapName, cache); 432 | } 433 | m_painter->drawPixmap(rect.topLeft(), cache); 434 | } 435 | 436 | 437 | void QGtk2Painter::paintSlider(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, 438 | GtkStateType state, GtkShadowType shadow, 439 | GtkStyle *style, GtkOrientation orientation, 440 | const QString &pmKey) 441 | { 442 | if (!rect.isValid()) 443 | return; 444 | 445 | QPixmap cache; 446 | QString pixmapName = uniqueName(QLS(part), state, shadow, rect.size(), gtkWidget) % pmKey; 447 | if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, &cache)) { 448 | DRAW_TO_CACHE(gtk_paint_slider (style, 449 | pixmap, 450 | state, 451 | shadow, 452 | nullptr, 453 | gtkWidget, 454 | part, 455 | 0, 0, 456 | rect.width(), 457 | rect.height(), 458 | orientation)); 459 | if (m_usePixmapCache) 460 | QPixmapCache::insert(pixmapName, cache); 461 | } 462 | m_painter->drawPixmap(rect.topLeft(), cache); 463 | } 464 | 465 | 466 | void QGtk2Painter::paintShadow(GtkWidget *gtkWidget, const gchar* part, 467 | const QRect &rect, GtkStateType state, 468 | GtkShadowType shadow, GtkStyle *style, 469 | const QString &pmKey) 470 | 471 | { 472 | if (!rect.isValid()) 473 | return; 474 | 475 | QPixmap cache; 476 | QString pixmapName = uniqueName(QLS(part), state, shadow, rect.size()) % pmKey; 477 | if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, &cache)) { 478 | DRAW_TO_CACHE(gtk_paint_shadow(style, pixmap, state, shadow, nullptr, 479 | gtkWidget, part, 0, 0, rect.width(), rect.height())); 480 | if (m_usePixmapCache) 481 | QPixmapCache::insert(pixmapName, cache); 482 | } 483 | m_painter->drawPixmap(rect.topLeft(), cache); 484 | } 485 | 486 | void QGtk2Painter::paintFlatBox(GtkWidget *gtkWidget, const gchar* part, 487 | const QRect &rect, GtkStateType state, 488 | GtkShadowType shadow, GtkStyle *style, 489 | const QString &pmKey) 490 | { 491 | if (!rect.isValid()) 492 | return; 493 | QPixmap cache; 494 | QString pixmapName = uniqueName(QLS(part), state, shadow, rect.size()) % pmKey; 495 | if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, &cache)) { 496 | DRAW_TO_CACHE(gtk_paint_flat_box (style, 497 | pixmap, 498 | state, 499 | shadow, 500 | nullptr, 501 | gtkWidget, 502 | part, 0, 0, 503 | rect.width(), 504 | rect.height())); 505 | if (m_usePixmapCache) 506 | QPixmapCache::insert(pixmapName, cache); 507 | } 508 | m_painter->drawPixmap(rect.topLeft(), cache); 509 | } 510 | 511 | void QGtk2Painter::paintExtention(GtkWidget *gtkWidget, 512 | const gchar *part, const QRect &rect, 513 | GtkStateType state, GtkShadowType shadow, 514 | GtkPositionType gap_pos, GtkStyle *style) 515 | { 516 | if (!rect.isValid()) 517 | return; 518 | 519 | QPixmap cache; 520 | QString pixmapName = uniqueName(QLS(part), state, shadow, rect.size(), gtkWidget) 521 | % HexString(gap_pos); 522 | 523 | if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, &cache)) { 524 | DRAW_TO_CACHE(gtk_paint_extension (style, pixmap, state, shadow, 525 | nullptr, gtkWidget, 526 | (const gchar*)part, 0, 0, 527 | rect.width(), 528 | rect.height(), 529 | gap_pos)); 530 | if (m_usePixmapCache) 531 | QPixmapCache::insert(pixmapName, cache); 532 | } 533 | 534 | m_painter->drawPixmap(rect.topLeft(), cache); 535 | } 536 | 537 | void QGtk2Painter::paintOption(GtkWidget *gtkWidget, const QRect &radiorect, 538 | GtkStateType state, GtkShadowType shadow, 539 | GtkStyle *style, const QString &detail) 540 | 541 | { 542 | QRect rect = m_cliprect.isValid() ? m_cliprect : radiorect; 543 | if (!rect.isValid()) 544 | return; 545 | 546 | QPixmap cache; 547 | QString pixmapName = uniqueName(detail, state, shadow, rect.size()); 548 | GdkRectangle gtkCliprect = {0, 0, rect.width(), rect.height()}; 549 | int xOffset = m_cliprect.isValid() ? radiorect.x() - m_cliprect.x() : 0; 550 | int yOffset = m_cliprect.isValid() ? radiorect.y() - m_cliprect.y() : 0; 551 | if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, &cache)) { 552 | DRAW_TO_CACHE(gtk_paint_option(style, pixmap, 553 | state, shadow, 554 | >kCliprect, 555 | gtkWidget, 556 | detail.toLatin1().constData(), 557 | xOffset, yOffset, 558 | radiorect.width(), 559 | radiorect.height())); 560 | 561 | if (m_usePixmapCache) 562 | QPixmapCache::insert(pixmapName, cache); 563 | } 564 | 565 | m_painter->drawPixmap(rect.topLeft(), cache); 566 | } 567 | 568 | void QGtk2Painter::paintCheckbox(GtkWidget *gtkWidget, const QRect &checkrect, 569 | GtkStateType state, GtkShadowType shadow, 570 | GtkStyle *style, const QString &detail) 571 | 572 | { 573 | QRect rect = m_cliprect.isValid() ? m_cliprect : checkrect; 574 | if (!rect.isValid()) 575 | return; 576 | 577 | QPixmap cache; 578 | QString pixmapName = uniqueName(detail, state, shadow, rect.size()); 579 | GdkRectangle gtkCliprect = {0, 0, rect.width(), rect.height()}; 580 | int xOffset = m_cliprect.isValid() ? checkrect.x() - m_cliprect.x() : 0; 581 | int yOffset = m_cliprect.isValid() ? checkrect.y() - m_cliprect.y() : 0; 582 | if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, &cache)) { 583 | DRAW_TO_CACHE(gtk_paint_check (style, 584 | pixmap, 585 | state, 586 | shadow, 587 | >kCliprect, 588 | gtkWidget, 589 | detail.toLatin1().constData(), 590 | xOffset, yOffset, 591 | checkrect.width(), 592 | checkrect.height())); 593 | if (m_usePixmapCache) 594 | QPixmapCache::insert(pixmapName, cache); 595 | } 596 | 597 | m_painter->drawPixmap(rect.topLeft(), cache); 598 | } 599 | 600 | QT_END_NAMESPACE 601 | 602 | #endif //!defined(QT_NO_STYLE_GTK) 603 | -------------------------------------------------------------------------------- /src/qt6gtk2-style/qgtk2painter_p.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2015 The Qt Company Ltd. * 3 | * Copyright (C) 2016-2024 Ilya Kotov, forkotov02@ya.ru * 4 | * * 5 | * This program is free software; you can redistribute it and/or modify * 6 | * it under the terms of the GNU General Public License as published by * 7 | * the Free Software Foundation; either version 2 of the License, or * 8 | * (at your option) any later version. * 9 | * * 10 | * This program is distributed in the hope that it will be useful, * 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 13 | * GNU General Public License for more details. * 14 | * * 15 | * You should have received a copy of the GNU General Public License * 16 | * along with this program; if not, write to the * 17 | * Free Software Foundation, Inc., * 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * 19 | ***************************************************************************/ 20 | 21 | #ifndef QGTK2PAINTER_P_H 22 | #define QGTK2PAINTER_P_H 23 | 24 | // 25 | // W A R N I N G 26 | // ------------- 27 | // 28 | // This file is not part of the Qt API. It exists purely as an 29 | // implementation detail. This header file may change from version to 30 | // version without notice, or even be removed. 31 | // 32 | // We mean it. 33 | // 34 | 35 | #include 36 | #if !defined(QT_NO_STYLE_GTK) 37 | 38 | #include "qgtkpainter_p.h" 39 | 40 | QT_BEGIN_NAMESPACE 41 | 42 | class QGtk2Painter : public QGtkPainter 43 | { 44 | public: 45 | QGtk2Painter(); 46 | 47 | void paintBoxGap(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, 48 | GtkStateType state, GtkShadowType shadow, GtkPositionType gap_side, gint x, 49 | gint width, GtkStyle *style) override; 50 | void paintBox(GtkWidget *gtkWidget, const gchar* part, 51 | const QRect &rect, GtkStateType state, GtkShadowType shadow, GtkStyle *style, 52 | const QString &pmKey = QString()) override; 53 | void paintHline(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkStyle *style, 54 | int x1, int x2, int y, const QString &pmKey = QString()) override; 55 | void paintVline(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkStyle *style, 56 | int y1, int y2, int x, const QString &pmKey = QString()) override; 57 | void paintExpander(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, 58 | GtkExpanderStyle expander_state, GtkStyle *style, const QString &pmKey = QString()) override; 59 | void paintFocus(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkStyle *style, 60 | const QString &pmKey = QString()) override; 61 | void paintResizeGrip(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkShadowType shadow, 62 | GdkWindowEdge edge, GtkStyle *style, const QString &pmKey = QString()) override; 63 | void paintArrow(GtkWidget *gtkWidget, const gchar* part, const QRect &arrowrect, GtkArrowType arrow_type, GtkStateType state, GtkShadowType shadow, 64 | gboolean fill, GtkStyle *style, const QString &pmKey = QString()) override; 65 | void paintHandle(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, 66 | GtkStateType state, GtkShadowType shadow, GtkOrientation orientation, GtkStyle *style) override; 67 | void paintSlider(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkShadowType shadow, 68 | GtkStyle *style, GtkOrientation orientation, const QString &pmKey = QString()) override; 69 | void paintShadow(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkShadowType shadow, 70 | GtkStyle *style, const QString &pmKey = QString()) override; 71 | void paintFlatBox(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkShadowType shadow, GtkStyle *style, const QString & = QString()) override; 72 | void paintExtention(GtkWidget *gtkWidget, const gchar *part, const QRect &rect, GtkStateType state, GtkShadowType shadow, 73 | GtkPositionType gap_pos, GtkStyle *style) override; 74 | void paintOption(GtkWidget *gtkWidget, const QRect &rect, GtkStateType state, GtkShadowType shadow, GtkStyle *style, const QString &detail) override; 75 | void paintCheckbox(GtkWidget *gtkWidget, const QRect &rect, GtkStateType state, GtkShadowType shadow, GtkStyle *style, const QString &detail) override; 76 | 77 | private: 78 | QPixmap renderTheme(uchar *bdata, uchar *wdata, const QRect &rect) const; 79 | 80 | GtkWidget *m_window; 81 | }; 82 | 83 | QT_END_NAMESPACE 84 | 85 | #endif //!defined(QT_NO_STYLE_QGTK) 86 | 87 | #endif // QGTK2PAINTER_P_H 88 | -------------------------------------------------------------------------------- /src/qt6gtk2-style/qgtkglobal_p.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2015 The Qt Company Ltd. * 3 | * Copyright (C) 2016-2024 Ilya Kotov, forkotov02@ya.ru * 4 | * * 5 | * This program is free software; you can redistribute it and/or modify * 6 | * it under the terms of the GNU General Public License as published by * 7 | * the Free Software Foundation; either version 2 of the License, or * 8 | * (at your option) any later version. * 9 | * * 10 | * This program is distributed in the hope that it will be useful, * 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 13 | * GNU General Public License for more details. * 14 | * * 15 | * You should have received a copy of the GNU General Public License * 16 | * along with this program; if not, write to the * 17 | * Free Software Foundation, Inc., * 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * 19 | ***************************************************************************/ 20 | 21 | #ifndef QGTKGLOBAL_P_H 22 | #define QGTKGLOBAL_P_H 23 | 24 | // 25 | // W A R N I N G 26 | // ------------- 27 | // 28 | // This file is not part of the Qt API. It exists purely as an 29 | // implementation detail. This header file may change from version to 30 | // version without notice, or even be removed. 31 | // 32 | // We mean it. 33 | // 34 | 35 | #include 36 | #if !defined(QT_NO_STYLE_GTK) 37 | 38 | #undef signals // Collides with GTK symbols 39 | #include 40 | 41 | typedef unsigned long XID; 42 | 43 | #undef GTK_OBJECT_FLAGS 44 | #define GTK_OBJECT_FLAGS(obj)(((GtkObject*)(obj))->flags) 45 | 46 | #define QLS(x) QLatin1String(x) 47 | 48 | QT_BEGIN_NAMESPACE 49 | 50 | #if Q_BYTE_ORDER == Q_BIG_ENDIAN 51 | # define QT_RED 3 52 | # define QT_GREEN 2 53 | # define QT_BLUE 1 54 | # define QT_ALPHA 0 55 | #else 56 | # define QT_RED 0 57 | # define QT_GREEN 1 58 | # define QT_BLUE 2 59 | # define QT_ALPHA 3 60 | #endif 61 | # define GTK_RED 2 62 | # define GTK_GREEN 1 63 | # define GTK_BLUE 0 64 | # define GTK_ALPHA 3 65 | 66 | QT_END_NAMESPACE 67 | 68 | #endif // !QT_NO_STYLE_GTK 69 | #endif // QGTKGLOBAL_P_H 70 | -------------------------------------------------------------------------------- /src/qt6gtk2-style/qgtkpainter.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2015 The Qt Company Ltd. * 3 | * Copyright (C) 2016-2024 Ilya Kotov, forkotov02@ya.ru * 4 | * * 5 | * This program is free software; you can redistribute it and/or modify * 6 | * it under the terms of the GNU General Public License as published by * 7 | * the Free Software Foundation; either version 2 of the License, or * 8 | * (at your option) any later version. * 9 | * * 10 | * This program is distributed in the hope that it will be useful, * 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 13 | * GNU General Public License for more details. * 14 | * * 15 | * You should have received a copy of the GNU General Public License * 16 | * along with this program; if not, write to the * 17 | * Free Software Foundation, Inc., * 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * 19 | ***************************************************************************/ 20 | 21 | #include "qgtkpainter_p.h" 22 | 23 | #if !defined(QT_NO_STYLE_GTK) 24 | 25 | #include 26 | 27 | QT_BEGIN_NAMESPACE 28 | 29 | QGtkPainter::QGtkPainter() 30 | { 31 | reset(nullptr); 32 | } 33 | 34 | QGtkPainter::~QGtkPainter() 35 | { 36 | } 37 | 38 | void QGtkPainter::reset(QPainter *painter) 39 | { 40 | m_painter = painter; 41 | m_alpha = true; 42 | m_hflipped = false; 43 | m_vflipped = false; 44 | m_usePixmapCache = true; 45 | m_cliprect = QRect(); 46 | } 47 | 48 | QString QGtkPainter::uniqueName(const QString &key, GtkStateType state, GtkShadowType shadow, 49 | const QSize &size, GtkWidget *widget) 50 | { 51 | // Note the widget arg should ideally use the widget path, though would compromise performance 52 | QString tmp = key 53 | % HexString(state) 54 | % HexString(shadow) 55 | % HexString(size.width()) 56 | % HexString(size.height()) 57 | % HexString(quint64(widget)); 58 | return tmp; 59 | } 60 | 61 | QT_END_NAMESPACE 62 | 63 | #endif //!defined(QT_NO_STYLE_GTK) 64 | -------------------------------------------------------------------------------- /src/qt6gtk2-style/qgtkpainter_p.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2015 The Qt Company Ltd. * 3 | * Copyright (C) 2016-2024 Ilya Kotov, forkotov02@ya.ru * 4 | * * 5 | * This program is free software; you can redistribute it and/or modify * 6 | * it under the terms of the GNU General Public License as published by * 7 | * the Free Software Foundation; either version 2 of the License, or * 8 | * (at your option) any later version. * 9 | * * 10 | * This program is distributed in the hope that it will be useful, * 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 13 | * GNU General Public License for more details. * 14 | * * 15 | * You should have received a copy of the GNU General Public License * 16 | * along with this program; if not, write to the * 17 | * Free Software Foundation, Inc., * 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * 19 | ***************************************************************************/ 20 | 21 | #ifndef QGTKPAINTER_H 22 | #define QGTKPAINTER_H 23 | 24 | // 25 | // W A R N I N G 26 | // ------------- 27 | // 28 | // This file is not part of the Qt API. It exists purely as an 29 | // implementation detail. This header file may change from version to 30 | // version without notice, or even be removed. 31 | // 32 | // We mean it. 33 | // 34 | 35 | #include 36 | #if !defined(QT_NO_STYLE_GTK) 37 | 38 | #include "qgtkglobal_p.h" 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | QT_BEGIN_NAMESPACE 46 | 47 | class QGtkPainter 48 | { 49 | public: 50 | QGtkPainter(); 51 | virtual ~QGtkPainter(); 52 | 53 | void reset(QPainter *painter = nullptr); 54 | 55 | void setAlphaSupport(bool value) { m_alpha = value; } 56 | void setClipRect(const QRect &rect) { m_cliprect = rect; } 57 | void setFlipHorizontal(bool value) { m_hflipped = value; } 58 | void setFlipVertical(bool value) { m_vflipped = value; } 59 | void setUsePixmapCache(bool value) { m_usePixmapCache = value; } 60 | 61 | virtual void paintBoxGap(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, 62 | GtkStateType state, GtkShadowType shadow, GtkPositionType gap_side, gint x, 63 | gint width, GtkStyle *style) = 0; 64 | virtual void paintBox(GtkWidget *gtkWidget, const gchar* part, 65 | const QRect &rect, GtkStateType state, GtkShadowType shadow, GtkStyle *style, 66 | const QString &pmKey = QString()) = 0; 67 | virtual void paintHline(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkStyle *style, 68 | int x1, int x2, int y, const QString &pmKey = QString()) = 0; 69 | virtual void paintVline(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkStyle *style, 70 | int y1, int y2, int x, const QString &pmKey = QString()) = 0; 71 | virtual void paintExpander(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, 72 | GtkExpanderStyle expander_state, GtkStyle *style, const QString &pmKey = QString()) = 0; 73 | virtual void paintFocus(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkStyle *style, 74 | const QString &pmKey = QString()) = 0; 75 | virtual void paintResizeGrip(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkShadowType shadow, 76 | GdkWindowEdge edge, GtkStyle *style, const QString &pmKey = QString()) = 0; 77 | virtual void paintArrow(GtkWidget *gtkWidget, const gchar* part, const QRect &arrowrect, GtkArrowType arrow_type, GtkStateType state, GtkShadowType shadow, 78 | gboolean fill, GtkStyle *style, const QString &pmKey = QString()) = 0; 79 | virtual void paintHandle(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, 80 | GtkStateType state, GtkShadowType shadow, GtkOrientation orientation, GtkStyle *style) = 0; 81 | virtual void paintSlider(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkShadowType shadow, 82 | GtkStyle *style, GtkOrientation orientation, const QString &pmKey = QString()) = 0; 83 | virtual void paintShadow(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkShadowType shadow, 84 | GtkStyle *style, const QString &pmKey = QString()) = 0; 85 | virtual void paintFlatBox(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkShadowType shadow, GtkStyle *style, const QString & = QString()) = 0; 86 | virtual void paintExtention(GtkWidget *gtkWidget, const gchar *part, const QRect &rect, GtkStateType state, GtkShadowType shadow, 87 | GtkPositionType gap_pos, GtkStyle *style) = 0; 88 | virtual void paintOption(GtkWidget *gtkWidget, const QRect &rect, GtkStateType state, GtkShadowType shadow, GtkStyle *style, const QString &detail) = 0; 89 | virtual void paintCheckbox(GtkWidget *gtkWidget, const QRect &rect, GtkStateType state, GtkShadowType shadow, GtkStyle *style, const QString &detail) = 0; 90 | 91 | protected: 92 | static QString uniqueName(const QString &key, GtkStateType state, GtkShadowType shadow, const QSize &size, GtkWidget *widget = nullptr); 93 | 94 | QPainter *m_painter; 95 | bool m_alpha; 96 | bool m_hflipped; 97 | bool m_vflipped; 98 | bool m_usePixmapCache; 99 | QRect m_cliprect; 100 | }; 101 | 102 | QT_END_NAMESPACE 103 | 104 | #endif //!defined(QT_NO_STYLE_QGTK) 105 | 106 | #endif // QGTKPAINTER_H 107 | -------------------------------------------------------------------------------- /src/qt6gtk2-style/qgtkstyle_p.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2015 The Qt Company Ltd. * 3 | * Copyright (C) 2016-2024 Ilya Kotov, forkotov02@ya.ru * 4 | * * 5 | * This program is free software; you can redistribute it and/or modify * 6 | * it under the terms of the GNU General Public License as published by * 7 | * the Free Software Foundation; either version 2 of the License, or * 8 | * (at your option) any later version. * 9 | * * 10 | * This program is distributed in the hope that it will be useful, * 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 13 | * GNU General Public License for more details. * 14 | * * 15 | * You should have received a copy of the GNU General Public License * 16 | * along with this program; if not, write to the * 17 | * Free Software Foundation, Inc., * 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * 19 | ***************************************************************************/ 20 | 21 | #include "qgtkstyle_p_p.h" 22 | 23 | // This file is responsible for resolving all GTK functions we use 24 | // dynamically. This is done to avoid link-time dependancy on GTK 25 | // as well as crashes occurring due to usage of the GTK_QT engines 26 | // 27 | // Additionally we create a map of common GTK widgets that we can pass 28 | // to the GTK theme engine as many engines resort to querying the 29 | // actual widget pointers for details that are not covered by the 30 | // state flags 31 | 32 | #include 33 | #if !defined(QT_NO_STYLE_GTK) 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | #include "qgtk2painter_p.h" 44 | #include 45 | #include 46 | #include 47 | 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | 57 | #ifndef Q_OS_MAC 58 | // X11 Includes: 59 | 60 | // the following is necessary to work around breakage in many versions 61 | // of XFree86's Xlib.h still in use 62 | // ### which versions? 63 | #if defined(_XLIB_H_) // crude hack, but... 64 | #error "cannot include before this file" 65 | #endif 66 | #define XRegisterIMInstantiateCallback qt_XRegisterIMInstantiateCallback 67 | #define XUnregisterIMInstantiateCallback qt_XUnregisterIMInstantiateCallback 68 | #define XSetIMValues qt_XSetIMValues 69 | #include 70 | #undef XRegisterIMInstantiateCallback 71 | #undef XUnregisterIMInstantiateCallback 72 | #undef XSetIMValues 73 | #endif 74 | 75 | QT_BEGIN_NAMESPACE 76 | 77 | Q_GLOBAL_STATIC(QGtkStyleUpdateScheduler, styleScheduler) 78 | 79 | #ifndef Q_OS_MAC 80 | typedef int (*x11ErrorHandler)(Display*, XErrorEvent*); 81 | #endif 82 | 83 | QT_END_NAMESPACE 84 | 85 | Q_DECLARE_METATYPE(QGtkStylePrivate*) 86 | 87 | QT_BEGIN_NAMESPACE 88 | 89 | static void gtkStyleSetCallback(GtkWidget*) 90 | { 91 | qRegisterMetaType(); 92 | 93 | // We have to let this function return and complete the event 94 | // loop to ensure that all gtk widgets have been styled before 95 | // updating 96 | QMetaObject::invokeMethod(styleScheduler(), "updateTheme", Qt::QueuedConnection); 97 | } 98 | 99 | static void update_toolbar_style(GtkWidget *gtkToolBar, GParamSpec *, gpointer) 100 | { 101 | GtkToolbarStyle toolbar_style = GTK_TOOLBAR_ICONS; 102 | g_object_get(gtkToolBar, "toolbar-style", &toolbar_style, nullptr); 103 | QWidgetList widgets = QApplication::allWidgets(); 104 | for (int i = 0; i < widgets.size(); ++i) { 105 | QWidget *widget = widgets.at(i); 106 | if (qobject_cast(widget)) { 107 | QEvent event(QEvent::StyleChange); 108 | QApplication::sendEvent(widget, &event); 109 | } 110 | } 111 | } 112 | 113 | static QHashableLatin1Literal classPath(GtkWidget *widget) 114 | { 115 | char *class_path; 116 | gtk_widget_path (widget, nullptr, &class_path, nullptr); 117 | 118 | char *copy = class_path; 119 | if (strncmp(copy, "GtkWindow.", 10) == 0) 120 | copy += 10; 121 | if (strncmp(copy, "GtkFixed.", 9) == 0) 122 | copy += 9; 123 | 124 | copy = strdup(copy); 125 | 126 | g_free(class_path); 127 | 128 | return QHashableLatin1Literal::fromData(copy); 129 | } 130 | 131 | 132 | 133 | bool QGtkStyleFilter::eventFilter(QObject *obj, QEvent *e) 134 | { 135 | if (e->type() == QEvent::ApplicationPaletteChange) { 136 | // Only do this the first time since this will also 137 | // generate applicationPaletteChange events 138 | //if (!qt_app_palettes_hash() || qt_app_palettes_hash()->isEmpty()) { 139 | // stylePrivate->applyCustomPaletteHash(); 140 | //} 141 | } 142 | return QObject::eventFilter(obj, e); 143 | } 144 | 145 | QList QGtkStylePrivate::instances; 146 | QGtkStylePrivate::WidgetMap *QGtkStylePrivate::widgetMap = nullptr; 147 | 148 | QGtkStylePrivate::QGtkStylePrivate() 149 | : QCommonStylePrivate() 150 | , filter(this) 151 | { 152 | instances.append(this); 153 | animationFps = 60; 154 | } 155 | 156 | QGtkStylePrivate::~QGtkStylePrivate() 157 | { 158 | instances.removeOne(this); 159 | } 160 | 161 | void QGtkStylePrivate::init() 162 | { 163 | initGtkWidgets(); 164 | } 165 | 166 | QGtkPainter* QGtkStylePrivate::gtkPainter(QPainter *painter) 167 | { 168 | // TODO: choose between gtk2 and gtk3 169 | static QGtk2Painter instance; 170 | instance.reset(painter); 171 | return &instance; 172 | } 173 | 174 | GtkWidget* QGtkStylePrivate::gtkWidget(const QHashableLatin1Literal &path) 175 | { 176 | GtkWidget *widget = gtkWidgetMap()->value(path); 177 | if (!widget) { 178 | // Theme might have rearranged widget internals 179 | widget = gtkWidgetMap()->value(path); 180 | } 181 | return widget; 182 | } 183 | 184 | GtkStyle* QGtkStylePrivate::gtkStyle(const QHashableLatin1Literal &path) 185 | { 186 | if (GtkWidget *w = gtkWidgetMap()->value(path)) 187 | return gtk_widget_get_style(w); 188 | return nullptr; 189 | } 190 | 191 | void QGtkStylePrivate::gtkWidgetSetFocus(GtkWidget *widget, bool focus) 192 | { 193 | GdkEvent *event = gdk_event_new(GDK_FOCUS_CHANGE); 194 | event->focus_change.type = GDK_FOCUS_CHANGE; 195 | event->focus_change.in = focus; 196 | gtk_widget_send_focus_change(widget, event); 197 | gdk_event_free(event); 198 | } 199 | 200 | /* \internal 201 | * Initializes a number of gtk menu widgets. 202 | * The widgets are cached. 203 | */ 204 | void QGtkStylePrivate::initGtkMenu() const 205 | { 206 | // Create menubar 207 | GtkWidget *gtkMenuBar = gtk_menu_bar_new(); 208 | setupGtkWidget(gtkMenuBar); 209 | 210 | GtkWidget *gtkMenuBarItem = gtk_menu_item_new_with_label("X"); 211 | gtk_menu_shell_append((GtkMenuShell*)(gtkMenuBar), gtkMenuBarItem); 212 | gtk_widget_realize(gtkMenuBarItem); 213 | 214 | // Create menu 215 | GtkWidget *gtkMenu = gtk_menu_new(); 216 | gtk_menu_item_set_submenu((GtkMenuItem*)(gtkMenuBarItem), gtkMenu); 217 | gtk_widget_realize(gtkMenu); 218 | 219 | GtkWidget *gtkMenuItem = gtk_menu_item_new_with_label("X"); 220 | gtk_menu_shell_append((GtkMenuShell*)gtkMenu, gtkMenuItem); 221 | gtk_widget_realize(gtkMenuItem); 222 | 223 | GtkWidget *gtkCheckMenuItem = gtk_check_menu_item_new_with_label("X"); 224 | gtk_menu_shell_append((GtkMenuShell*)gtkMenu, gtkCheckMenuItem); 225 | gtk_widget_realize(gtkCheckMenuItem); 226 | 227 | GtkWidget *gtkMenuSeparator = gtk_separator_menu_item_new(); 228 | gtk_menu_shell_append((GtkMenuShell*)gtkMenu, gtkMenuSeparator); 229 | 230 | addAllSubWidgets(gtkMenuBar); 231 | addAllSubWidgets(gtkMenu); 232 | } 233 | 234 | 235 | void QGtkStylePrivate::initGtkTreeview() const 236 | { 237 | GtkWidget *gtkTreeView = gtk_tree_view_new(); 238 | gtk_tree_view_append_column((GtkTreeView*)gtkTreeView, gtk_tree_view_column_new()); 239 | gtk_tree_view_append_column((GtkTreeView*)gtkTreeView, gtk_tree_view_column_new()); 240 | gtk_tree_view_append_column((GtkTreeView*)gtkTreeView, gtk_tree_view_column_new()); 241 | addWidget(gtkTreeView); 242 | } 243 | 244 | 245 | /* \internal 246 | * Initializes a number of gtk widgets that we can later on use to determine some of our styles. 247 | * The widgets are cached. 248 | */ 249 | void QGtkStylePrivate::initGtkWidgets() const 250 | { 251 | // From gtkmain.c 252 | uid_t ruid = getuid (); 253 | uid_t rgid = getgid (); 254 | uid_t euid = geteuid (); 255 | uid_t egid = getegid (); 256 | if (ruid != euid || rgid != egid) { 257 | qWarning("\nThis process is currently running setuid or setgid.\nGTK+ does not allow this " 258 | "therefore Qt cannot use the GTK+ integration.\nTry launching your app using \'gksudo\', " 259 | "\'kdesudo\' or a similar tool.\n\n" 260 | "See http://www.gtk.org/setuid.html for more information.\n"); 261 | return; 262 | } 263 | 264 | 265 | #ifndef Q_OS_MAC 266 | // Gtk will set the Qt error handler so we have to reset it afterwards 267 | x11ErrorHandler qt_x_errhandler = XSetErrorHandler(nullptr); 268 | #endif 269 | gtk_init (nullptr, nullptr); 270 | #ifndef Q_OS_MAC 271 | XSetErrorHandler(qt_x_errhandler); 272 | #endif 273 | 274 | // make a window 275 | GtkWidget* gtkWindow = gtk_window_new(GTK_WINDOW_POPUP); 276 | gtk_widget_realize(gtkWindow); 277 | QHashableLatin1Literal widgetPath = QHashableLatin1Literal::fromData(strdup("GtkWindow")); 278 | removeWidgetFromMap(widgetPath); 279 | gtkWidgetMap()->insert(widgetPath, gtkWindow); 280 | 281 | 282 | // Make all other widgets. respect the text direction 283 | if (qApp->layoutDirection() == Qt::RightToLeft) 284 | gtk_widget_set_default_direction(GTK_TEXT_DIR_RTL); 285 | 286 | if (!gtkWidgetMap()->contains("GtkButton")) { 287 | GtkWidget *gtkButton = gtk_button_new(); 288 | addWidget(gtkButton); 289 | g_signal_connect(gtkButton, "style-set", G_CALLBACK(gtkStyleSetCallback), 0); 290 | addWidget((GtkWidget*)gtk_tool_button_new(nullptr, "Qt")); 291 | addWidget(gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE)); 292 | addWidget(gtk_hbutton_box_new()); 293 | addWidget(gtk_check_button_new()); 294 | addWidget(gtk_radio_button_new(nullptr)); 295 | addWidget(gtk_combo_box_new()); 296 | addWidget(gtk_combo_box_entry_new()); 297 | GtkWidget *entry = gtk_entry_new(); 298 | // gtk-im-context-none is supported in gtk+ since 2.19.5 299 | // and also exists in gtk3 300 | // http://git.gnome.org/browse/gtk+/tree/gtk/gtkimmulticontext.c?id=2.19.5#n33 301 | // reason that we don't use gtk-im-context-simple here is, 302 | // gtk-im-context-none has less overhead, and 2.19.5 is 303 | // relatively old. and even for older gtk+, it will fallback 304 | // to gtk-im-context-simple if gtk-im-context-none doesn't 305 | // exists. 306 | g_object_set(entry, "im-module", "gtk-im-context-none", nullptr); 307 | addWidget(entry); 308 | addWidget(gtk_frame_new(nullptr)); 309 | addWidget(gtk_expander_new("")); 310 | addWidget(gtk_statusbar_new()); 311 | addWidget(gtk_hscale_new((GtkAdjustment*)gtk_adjustment_new(1, 0, 1, 0, 0, 0))); 312 | addWidget(gtk_hscrollbar_new(nullptr)); 313 | addWidget(gtk_scrolled_window_new(nullptr, nullptr)); 314 | 315 | initGtkMenu(); 316 | addWidget(gtk_notebook_new()); 317 | addWidget(gtk_progress_bar_new()); 318 | addWidget(gtk_spin_button_new((GtkAdjustment*)gtk_adjustment_new(1, 0, 1, 0, 0, 0), 0.1, 3)); 319 | GtkWidget *toolbar = gtk_toolbar_new(); 320 | g_signal_connect (toolbar, "notify::toolbar-style", G_CALLBACK (update_toolbar_style), toolbar); 321 | gtk_toolbar_insert((GtkToolbar*)toolbar, gtk_separator_tool_item_new(), -1); 322 | addWidget(toolbar); 323 | initGtkTreeview(); 324 | addWidget(gtk_vscale_new((GtkAdjustment*)gtk_adjustment_new(1, 0, 1, 0, 0, 0))); 325 | addWidget(gtk_vscrollbar_new(nullptr)); 326 | } 327 | else // Rebuild map 328 | { 329 | // When styles change subwidgets can get rearranged 330 | // as with the combo box. We need to update the widget map 331 | // to reflect this; 332 | QHash oldMap = *gtkWidgetMap(); 333 | gtkWidgetMap()->clear(); 334 | QHashIterator it(oldMap); 335 | while (it.hasNext()) { 336 | it.next(); 337 | if (!strchr(it.key().data(), '.')) { 338 | addAllSubWidgets(it.value()); 339 | } 340 | free(const_cast(it.key().data())); 341 | } 342 | } 343 | } 344 | 345 | /*! \internal 346 | * destroys all previously buffered widgets. 347 | */ 348 | void QGtkStylePrivate::cleanupGtkWidgets() 349 | { 350 | if (!widgetMap) 351 | return; 352 | if (widgetMap->contains("GtkWindow")) // Gtk will destroy all children 353 | gtk_widget_destroy(widgetMap->value("GtkWindow")); 354 | for (QHash::const_iterator it = widgetMap->constBegin(); 355 | it != widgetMap->constEnd(); ++it) 356 | free(const_cast(it.key().data())); 357 | } 358 | 359 | QString QGtkStylePrivate::getThemeName() 360 | { 361 | QString themeName; 362 | // Read the theme name from GtkSettings 363 | GtkSettings *settings = gtk_settings_get_default(); 364 | gchararray value; 365 | g_object_get(settings, "gtk-theme-name", &value, nullptr); 366 | themeName = QString::fromUtf8(value); 367 | g_free(value); 368 | return themeName; 369 | } 370 | 371 | // Get size of the arrow controls in a GtkSpinButton 372 | int QGtkStylePrivate::getSpinboxArrowSize() const 373 | { 374 | const int MIN_ARROW_WIDTH = 6; 375 | GtkWidget *spinButton = gtkWidget("GtkSpinButton"); 376 | GtkStyle *style = gtk_widget_get_style(spinButton); 377 | gint size = pango_font_description_get_size (style->font_desc); 378 | gint arrow_size; 379 | arrow_size = qMax(PANGO_PIXELS (size), MIN_ARROW_WIDTH) + style->xthickness; 380 | arrow_size += arrow_size%2 + 1; 381 | return arrow_size; 382 | } 383 | 384 | 385 | bool QGtkStylePrivate::isKDE4Session() 386 | { 387 | static int version = -1; 388 | if (version == -1) 389 | version = qgetenv("KDE_SESSION_VERSION").toInt(); 390 | return (version == 4); 391 | } 392 | 393 | void QGtkStylePrivate::applyCustomPaletteHash() 394 | { 395 | QPalette menuPal = gtkWidgetPalette("GtkMenu"); 396 | GdkColor gdkBg = gtk_widget_get_style(gtkWidget("GtkMenu"))->bg[GTK_STATE_NORMAL]; 397 | QColor bgColor(gdkBg.red>>8, gdkBg.green>>8, gdkBg.blue>>8); 398 | menuPal.setBrush(QPalette::Base, bgColor); 399 | menuPal.setBrush(QPalette::Window, bgColor); 400 | qApp->setPalette(menuPal, "QMenu"); 401 | 402 | QPalette toolbarPal = gtkWidgetPalette("GtkToolbar"); 403 | qApp->setPalette(toolbarPal, "QToolBar"); 404 | 405 | QPalette menuBarPal = gtkWidgetPalette("GtkMenuBar"); 406 | qApp->setPalette(menuBarPal, "QMenuBar"); 407 | } 408 | 409 | /*! \internal 410 | * Returns the gtk Widget that should be used to determine text foreground and background colors. 411 | */ 412 | GtkWidget* QGtkStylePrivate::getTextColorWidget() const 413 | { 414 | return gtkWidget("GtkEntry"); 415 | } 416 | 417 | void QGtkStylePrivate::setupGtkWidget(GtkWidget* widget) 418 | { 419 | if (Q_GTK_IS_WIDGET(widget)) { 420 | GtkWidget *protoLayout = gtkWidgetMap()->value("GtkContainer"); 421 | if (!protoLayout) { 422 | protoLayout = gtk_fixed_new(); 423 | gtk_container_add((GtkContainer*)(gtkWidgetMap()->value("GtkWindow")), protoLayout); 424 | QHashableLatin1Literal widgetPath = QHashableLatin1Literal::fromData(strdup("GtkContainer")); 425 | gtkWidgetMap()->insert(widgetPath, protoLayout); 426 | } 427 | Q_ASSERT(protoLayout); 428 | 429 | if (!gtk_widget_get_parent(widget) && !gtk_widget_is_toplevel(widget)) 430 | gtk_container_add((GtkContainer*)(protoLayout), widget); 431 | gtk_widget_realize(widget); 432 | } 433 | } 434 | 435 | void QGtkStylePrivate::removeWidgetFromMap(const QHashableLatin1Literal &path) 436 | { 437 | WidgetMap *map = gtkWidgetMap(); 438 | WidgetMap::iterator it = map->find(path); 439 | if (it != map->end()) { 440 | char* keyData = const_cast(it.key().data()); 441 | map->erase(it); 442 | free(keyData); 443 | } 444 | } 445 | 446 | void QGtkStylePrivate::addWidgetToMap(GtkWidget *widget) 447 | { 448 | if (Q_GTK_IS_WIDGET(widget)) { 449 | gtk_widget_realize(widget); 450 | QHashableLatin1Literal widgetPath = classPath(widget); 451 | 452 | removeWidgetFromMap(widgetPath); 453 | gtkWidgetMap()->insert(widgetPath, widget); 454 | #ifdef DUMP_GTK_WIDGET_TREE 455 | qWarning("Inserted Gtk Widget: %s", widgetPath.data()); 456 | #endif 457 | } 458 | } 459 | 460 | void QGtkStylePrivate::addAllSubWidgets(GtkWidget *widget, gpointer v) 461 | { 462 | Q_UNUSED(v); 463 | addWidgetToMap(widget); 464 | if (G_TYPE_CHECK_INSTANCE_TYPE ((widget), gtk_container_get_type())) 465 | gtk_container_forall((GtkContainer*)widget, addAllSubWidgets, nullptr); 466 | } 467 | 468 | // Updates window/windowtext palette based on the indicated gtk widget 469 | QPalette QGtkStylePrivate::gtkWidgetPalette(const QHashableLatin1Literal >kWidgetName) const 470 | { 471 | GtkWidget *gtkWidget = QGtkStylePrivate::gtkWidget(gtkWidgetName); 472 | Q_ASSERT(gtkWidget); 473 | QPalette pal = QApplication::palette(); 474 | GdkColor gdkBg = gtk_widget_get_style(gtkWidget)->bg[GTK_STATE_NORMAL]; 475 | GdkColor gdkText = gtk_widget_get_style(gtkWidget)->fg[GTK_STATE_NORMAL]; 476 | GdkColor gdkDisabledText = gtk_widget_get_style(gtkWidget)->fg[GTK_STATE_INSENSITIVE]; 477 | QColor bgColor(gdkBg.red>>8, gdkBg.green>>8, gdkBg.blue>>8); 478 | QColor textColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8); 479 | QColor disabledTextColor(gdkDisabledText.red>>8, gdkDisabledText.green>>8, gdkDisabledText.blue>>8); 480 | pal.setBrush(QPalette::Window, bgColor); 481 | pal.setBrush(QPalette::Button, bgColor); 482 | pal.setBrush(QPalette::All, QPalette::WindowText, textColor); 483 | pal.setBrush(QPalette::Disabled, QPalette::WindowText, disabledTextColor); 484 | pal.setBrush(QPalette::All, QPalette::ButtonText, textColor); 485 | pal.setBrush(QPalette::Disabled, QPalette::ButtonText, disabledTextColor); 486 | return pal; 487 | } 488 | 489 | 490 | void QGtkStyleUpdateScheduler::updateTheme() 491 | { 492 | static QString oldTheme(QLS("qt_not_set")); 493 | QPixmapCache::clear(); 494 | 495 | QFont font = QGtkStylePrivate::getThemeFont(); 496 | if (QApplication::font() != font) 497 | qApp->setFont(font); 498 | 499 | if (oldTheme != QGtkStylePrivate::getThemeName()) { 500 | oldTheme = QGtkStylePrivate::getThemeName(); 501 | QPalette newPalette = qApp->style()->standardPalette(); 502 | QApplication::setPalette(newPalette); 503 | if (!QGtkStylePrivate::instances.isEmpty()) { 504 | QGtkStylePrivate::instances.last()->initGtkWidgets(); 505 | QGtkStylePrivate::instances.last()->applyCustomPaletteHash(); 506 | } 507 | QList widgets = QApplication::allWidgets(); 508 | // Notify all widgets that size metrics might have changed 509 | for (QWidget *widget : std::as_const(widgets)) { 510 | QEvent e(QEvent::StyleChange); 511 | QApplication::sendEvent(widget, &e); 512 | } 513 | } 514 | QIconLoader::instance()->updateSystemTheme(); 515 | } 516 | 517 | void QGtkStylePrivate::addWidget(GtkWidget *widget) 518 | { 519 | if (widget) { 520 | setupGtkWidget(widget); 521 | addAllSubWidgets(widget); 522 | } 523 | } 524 | 525 | 526 | // Fetch the application font from the pango font description 527 | // contained in the theme. 528 | QFont QGtkStylePrivate::getThemeFont() 529 | { 530 | QFont font; 531 | GtkStyle *style = gtkStyle(); 532 | if (style && qApp->desktopSettingsAware()) 533 | { 534 | PangoFontDescription *gtk_font = style->font_desc; 535 | font.setPointSizeF((float)(pango_font_description_get_size(gtk_font))/PANGO_SCALE); 536 | 537 | QString family = QString::fromLatin1(pango_font_description_get_family(gtk_font)); 538 | if (!family.isEmpty()) 539 | font.setFamily(family); 540 | 541 | const int weight = pango_font_description_get_weight(gtk_font); 542 | font.setWeight(QFont::Weight(weight)); 543 | 544 | PangoStyle fontstyle = pango_font_description_get_style(gtk_font); 545 | if (fontstyle == PANGO_STYLE_ITALIC) 546 | font.setStyle(QFont::StyleItalic); 547 | else if (fontstyle == PANGO_STYLE_OBLIQUE) 548 | font.setStyle(QFont::StyleOblique); 549 | else 550 | font.setStyle(QFont::StyleNormal); 551 | } 552 | return font; 553 | } 554 | 555 | bool operator==(const QHashableLatin1Literal &l1, const QHashableLatin1Literal &l2) 556 | { 557 | return l1.size() == l2.size() && !qstrcmp(l1.data(), l2.data()); 558 | } 559 | 560 | // copied from qHash.cpp 561 | uint qHash(const QHashableLatin1Literal &key) 562 | { 563 | int n = key.size(); 564 | const uchar *p = reinterpret_cast(key.data()); 565 | uint h = 0; 566 | uint g; 567 | 568 | while (n--) { 569 | h = (h << 4) + *p++; 570 | if ((g = (h & 0xf0000000)) != 0) 571 | h ^= g >> 23; 572 | h &= ~g; 573 | } 574 | return h; 575 | } 576 | 577 | QT_END_NAMESPACE 578 | 579 | #include "moc_qgtkstyle_p.cpp" 580 | #include "moc_qgtkstyle_p_p.cpp" 581 | 582 | #endif // !defined(QT_NO_STYLE_GTK) 583 | -------------------------------------------------------------------------------- /src/qt6gtk2-style/qgtkstyle_p.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2015 The Qt Company Ltd. * 3 | * Copyright (C) 2016-2024 Ilya Kotov, forkotov02@ya.ru * 4 | * * 5 | * This program is free software; you can redistribute it and/or modify * 6 | * it under the terms of the GNU General Public License as published by * 7 | * the Free Software Foundation; either version 2 of the License, or * 8 | * (at your option) any later version. * 9 | * * 10 | * This program is distributed in the hope that it will be useful, * 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 13 | * GNU General Public License for more details. * 14 | * * 15 | * You should have received a copy of the GNU General Public License * 16 | * along with this program; if not, write to the * 17 | * Free Software Foundation, Inc., * 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * 19 | ***************************************************************************/ 20 | 21 | #ifndef QGTKSTYLE_P_H 22 | #define QGTKSTYLE_P_H 23 | 24 | // 25 | // W A R N I N G 26 | // ------------- 27 | // 28 | // This file is not part of the Qt API. It exists purely as an 29 | // implementation detail. This header file may change from version to 30 | // version without notice, or even be removed. 31 | // 32 | // We mean it. 33 | // 34 | 35 | //#include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | QT_BEGIN_NAMESPACE 42 | 43 | 44 | #if !defined(QT_NO_STYLE_GTK) 45 | 46 | class QPainterPath; 47 | class QGtkStylePrivate; 48 | 49 | class QGtkStyle : public QCommonStyle 50 | { 51 | Q_OBJECT 52 | Q_DECLARE_PRIVATE(QGtkStyle) 53 | 54 | public: 55 | QGtkStyle(); 56 | QGtkStyle(QGtkStylePrivate &dd); 57 | 58 | ~QGtkStyle(); 59 | 60 | QPalette standardPalette() const override; 61 | 62 | void drawPrimitive(PrimitiveElement element, const QStyleOption *option, 63 | QPainter *painter, const QWidget *widget) const override; 64 | void drawControl(ControlElement control, const QStyleOption *option, 65 | QPainter *painter, const QWidget *widget) const override; 66 | void drawComplexControl(ComplexControl control, const QStyleOptionComplex *option, 67 | QPainter *painter, const QWidget *widget) const override; 68 | void drawItemPixmap(QPainter *painter, const QRect &rect, int alignment, 69 | const QPixmap &pixmap) const override; 70 | void drawItemText(QPainter *painter, const QRect &rect, int alignment, const QPalette &pal, 71 | bool enabled, const QString& text, QPalette::ColorRole textRole) const override; 72 | 73 | int pixelMetric(PixelMetric metric, const QStyleOption *option = nullptr, 74 | const QWidget *widget = nullptr) const override; 75 | int styleHint(StyleHint hint, const QStyleOption *option, 76 | const QWidget *widget, QStyleHintReturn *returnData) const override; 77 | 78 | QStyle::SubControl hitTestComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, 79 | const QPoint &pt, const QWidget *w) const override; 80 | 81 | QRect subControlRect(ComplexControl control, const QStyleOptionComplex *option, 82 | SubControl subControl, const QWidget *widget) const override; 83 | QRect subElementRect(SubElement sr, const QStyleOption *opt, const QWidget *w) const override; 84 | QRect itemPixmapRect(const QRect &r, int flags, const QPixmap &pixmap) const override; 85 | 86 | 87 | QSize sizeFromContents(ContentsType type, const QStyleOption *option, 88 | const QSize &size, const QWidget *widget) const override; 89 | QIcon standardIcon(StandardPixmap standardIcon, const QStyleOption *option = nullptr, 90 | const QWidget *widget = nullptr) const override; 91 | QPixmap standardPixmap(StandardPixmap sp, const QStyleOption *option, 92 | const QWidget *widget) const override; 93 | QPixmap generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap, 94 | const QStyleOption *opt) const override; 95 | 96 | void polish(QWidget *widget) override; 97 | void polish(QApplication *app) override; 98 | void polish(QPalette &palette) override; 99 | 100 | void unpolish(QWidget *widget) override; 101 | void unpolish(QApplication *app) override; 102 | }; 103 | 104 | #endif //!defined(QT_NO_STYLE_QGTK) 105 | 106 | QT_END_NAMESPACE 107 | 108 | #endif //QGTKSTYLE_P_H 109 | -------------------------------------------------------------------------------- /src/qt6gtk2-style/qgtkstyle_p_p.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2015 The Qt Company Ltd. * 3 | * Copyright (C) 2016-2024 Ilya Kotov, forkotov02@ya.ru * 4 | * * 5 | * This program is free software; you can redistribute it and/or modify * 6 | * it under the terms of the GNU General Public License as published by * 7 | * the Free Software Foundation; either version 2 of the License, or * 8 | * (at your option) any later version. * 9 | * * 10 | * This program is distributed in the hope that it will be useful, * 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 13 | * GNU General Public License for more details. * 14 | * * 15 | * You should have received a copy of the GNU General Public License * 16 | * along with this program; if not, write to the * 17 | * Free Software Foundation, Inc., * 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * 19 | ***************************************************************************/ 20 | 21 | #ifndef QGTKSTYLE_P_P_H 22 | #define QGTKSTYLE_P_P_H 23 | 24 | // 25 | // W A R N I N G 26 | // ------------- 27 | // 28 | // This file is not part of the Qt API. It exists purely as an 29 | // implementation detail. This header file may change from version to 30 | // version without notice, or even be removed. 31 | // 32 | // We mean it. 33 | // 34 | 35 | #include 36 | #if !defined(QT_NO_STYLE_GTK) 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | #include 45 | #include "qgtkstyle_p.h" 46 | #include "qgtkglobal_p.h" 47 | 48 | #define Q_GTK_IS_WIDGET(widget) widget && G_TYPE_CHECK_INSTANCE_TYPE ((widget), gtk_widget_get_type()) 49 | 50 | QT_BEGIN_NAMESPACE 51 | 52 | class QHashableLatin1Literal 53 | { 54 | public: 55 | int size() const { return m_size; } 56 | const char *data() const { return m_data; } 57 | 58 | #ifdef __SUNPRO_CC 59 | QHashableLatin1Literal(const char* str) 60 | : m_size(strlen(str)), m_data(str) {} 61 | #else 62 | template 63 | QHashableLatin1Literal(const char (&str)[N]) 64 | : m_size(N - 1), m_data(str) {} 65 | #endif 66 | 67 | QHashableLatin1Literal(const QHashableLatin1Literal &other) 68 | : m_size(other.m_size), m_data(other.m_data) 69 | {} 70 | 71 | QHashableLatin1Literal &operator=(const QHashableLatin1Literal &other) 72 | { 73 | if (this == &other) 74 | return *this; 75 | *const_cast(&m_size) = other.m_size; 76 | *const_cast(&m_data) = const_cast(other.m_data); 77 | return *this; 78 | } 79 | 80 | QString toString() const { return QString::fromLatin1(m_data, m_size); } 81 | 82 | static QHashableLatin1Literal fromData(const char *str) 83 | { 84 | return QHashableLatin1Literal(str, qstrlen(str)); 85 | } 86 | 87 | private: 88 | QHashableLatin1Literal(const char *str, int length) 89 | : m_size(length), m_data(str) 90 | {} 91 | 92 | const int m_size; 93 | const char *m_data; 94 | }; 95 | 96 | bool operator==(const QHashableLatin1Literal &l1, const QHashableLatin1Literal &l2); 97 | inline bool operator!=(const QHashableLatin1Literal &l1, const QHashableLatin1Literal &l2) { return !operator==(l1, l2); } 98 | uint qHash(const QHashableLatin1Literal &key); 99 | 100 | QT_END_NAMESPACE 101 | 102 | typedef struct _XDisplay Display; 103 | 104 | QT_BEGIN_NAMESPACE 105 | 106 | class QGtkPainter; 107 | class QGtkStylePrivate; 108 | 109 | class QGtkStyleFilter : public QObject 110 | { 111 | public: 112 | QGtkStyleFilter(QGtkStylePrivate* sp) 113 | : stylePrivate(sp) 114 | {} 115 | private: 116 | QGtkStylePrivate* stylePrivate; 117 | bool eventFilter(QObject *obj, QEvent *e) override; 118 | }; 119 | 120 | class QGtkStylePrivate : public QCommonStylePrivate 121 | { 122 | Q_DECLARE_PUBLIC(QGtkStyle) 123 | public: 124 | QGtkStylePrivate(); 125 | ~QGtkStylePrivate(); 126 | 127 | QGtkStyleFilter filter; 128 | 129 | static QGtkPainter* gtkPainter(QPainter *painter = nullptr); 130 | static GtkWidget* gtkWidget(const QHashableLatin1Literal &path); 131 | static GtkStyle* gtkStyle(const QHashableLatin1Literal &path = QHashableLatin1Literal("GtkWindow")); 132 | static void gtkWidgetSetFocus(GtkWidget *widget, bool focus); 133 | 134 | virtual void initGtkMenu() const; 135 | virtual void initGtkTreeview() const; 136 | virtual void initGtkWidgets() const; 137 | 138 | static void cleanupGtkWidgets(); 139 | 140 | static bool isKDE4Session(); 141 | void applyCustomPaletteHash(); 142 | static QFont getThemeFont(); 143 | static bool isThemeAvailable() { return gtkStyle() != nullptr; } 144 | 145 | static QString getThemeName(); 146 | virtual int getSpinboxArrowSize() const; 147 | 148 | virtual QPalette gtkWidgetPalette(const QHashableLatin1Literal >kWidgetName) const; 149 | 150 | protected: 151 | typedef QHash WidgetMap; 152 | 153 | static inline void destroyWidgetMap() 154 | { 155 | cleanupGtkWidgets(); 156 | delete widgetMap; 157 | widgetMap = nullptr; 158 | } 159 | 160 | static inline WidgetMap *gtkWidgetMap() 161 | { 162 | if (!widgetMap) { 163 | widgetMap = new WidgetMap(); 164 | qAddPostRoutine(destroyWidgetMap); 165 | } 166 | return widgetMap; 167 | } 168 | 169 | static QStringList extract_filter(const QString &rawFilter); 170 | 171 | virtual GtkWidget* getTextColorWidget() const; 172 | static void setupGtkWidget(GtkWidget* widget); 173 | static void addWidgetToMap(GtkWidget* widget); 174 | static void addAllSubWidgets(GtkWidget *widget, gpointer v = nullptr); 175 | static void addWidget(GtkWidget *widget); 176 | static void removeWidgetFromMap(const QHashableLatin1Literal &path); 177 | 178 | virtual void init(); 179 | 180 | enum { 181 | menuItemFrame = 2, // menu item frame width 182 | menuItemHMargin = 3, // menu item hor text margin 183 | menuArrowHMargin = 6, // menu arrow horizontal margin 184 | menuItemVMargin = 2, // menu item ver text margin 185 | menuRightBorder = 15, // right border on menus 186 | menuCheckMarkWidth = 12 // checkmarks width on menus 187 | }; 188 | 189 | private: 190 | static QList instances; 191 | static WidgetMap *widgetMap; 192 | friend class QGtkStyleUpdateScheduler; 193 | }; 194 | 195 | // Helper to ensure that we have polished all our gtk widgets 196 | // before updating our own palettes 197 | class QGtkStyleUpdateScheduler : public QObject 198 | { 199 | Q_OBJECT 200 | public slots: 201 | void updateTheme(); 202 | }; 203 | 204 | QT_END_NAMESPACE 205 | 206 | #endif // !QT_NO_STYLE_GTK 207 | #endif // QGTKSTYLE_P_P_H 208 | -------------------------------------------------------------------------------- /src/qt6gtk2-style/qstylehelper.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2015 The Qt Company Ltd. * 3 | * Copyright (C) 2016-2024 Ilya Kotov, forkotov02@ya.ru * 4 | * * 5 | * This program is free software; you can redistribute it and/or modify * 6 | * it under the terms of the GNU General Public License as published by * 7 | * the Free Software Foundation; either version 2 of the License, or * 8 | * (at your option) any later version. * 9 | * * 10 | * This program is distributed in the hope that it will be useful, * 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 13 | * GNU General Public License for more details. * 14 | * * 15 | * You should have received a copy of the GNU General Public License * 16 | * along with this program; if not, write to the * 17 | * Free Software Foundation, Inc., * 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * 19 | ***************************************************************************/ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "qstylehelper_p.h" 32 | #include 33 | 34 | QT_BEGIN_NAMESPACE 35 | 36 | namespace QStyleHelper { 37 | 38 | #if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)) 39 | QString uniqueName(const QString &key, const QStyleOption *option, const QSize &size, int dpr) 40 | #else 41 | QString uniqueName(const QString &key, const QStyleOption *option, const QSize &size) 42 | #endif 43 | { 44 | const QStyleOptionComplex *complexOption = qstyleoption_cast(option); 45 | QString tmp = key % HexString(option->state) 46 | % HexString(option->direction) 47 | % HexString(complexOption ? uint(complexOption->activeSubControls) : 0u) 48 | % HexString(option->palette.cacheKey()) 49 | % HexString(size.width()) 50 | % HexString(size.height()) 51 | #if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)) 52 | % HexString(dpr) 53 | #endif 54 | ; 55 | 56 | #ifndef QT_NO_SPINBOX 57 | if (const QStyleOptionSpinBox *spinBox = qstyleoption_cast(option)) { 58 | tmp = tmp % HexString(spinBox->buttonSymbols) 59 | % HexString(spinBox->stepEnabled) 60 | % QLatin1Char(spinBox->frame ? '1' : '0'); 61 | } 62 | #endif // QT_NO_SPINBOX 63 | return tmp; 64 | } 65 | 66 | #ifndef QT_NO_ACCESSIBILITY 67 | bool isInstanceOf(QObject *obj, QAccessible::Role role) 68 | { 69 | bool match = false; 70 | QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(obj); 71 | match = iface && iface->role() == role; 72 | return match; 73 | } 74 | 75 | // Searches for an ancestor of a particular accessible role 76 | bool hasAncestor(QObject *obj, QAccessible::Role role) 77 | { 78 | bool found = false; 79 | QObject *parent = obj ? obj->parent() : nullptr; 80 | while (parent && !found) { 81 | if (isInstanceOf(parent, role)) 82 | found = true; 83 | parent = parent->parent(); 84 | } 85 | return found; 86 | } 87 | #endif // QT_NO_ACCESSIBILITY 88 | 89 | 90 | #ifndef QT_NO_DIAL 91 | 92 | int calcBigLineSize(int radius) 93 | { 94 | int bigLineSize = radius / 6; 95 | if (bigLineSize < 4) 96 | bigLineSize = 4; 97 | if (bigLineSize > radius / 2) 98 | bigLineSize = radius / 2; 99 | return bigLineSize; 100 | } 101 | 102 | static QPointF calcRadialPos(const QStyleOptionSlider *dial, qreal offset) 103 | { 104 | const int width = dial->rect.width(); 105 | const int height = dial->rect.height(); 106 | const int r = qMin(width, height) / 2; 107 | const int currentSliderPosition = dial->upsideDown ? dial->sliderPosition : (dial->maximum - dial->sliderPosition); 108 | qreal a = 0; 109 | if (dial->maximum == dial->minimum) 110 | a = Q_PI / 2; 111 | else if (dial->dialWrapping) 112 | a = Q_PI * 3 / 2 - (currentSliderPosition - dial->minimum) * 2 * Q_PI 113 | / (dial->maximum - dial->minimum); 114 | else 115 | a = (Q_PI * 8 - (currentSliderPosition - dial->minimum) * 10 * Q_PI 116 | / (dial->maximum - dial->minimum)) / 6; 117 | qreal xc = width / 2.0; 118 | qreal yc = height / 2.0; 119 | qreal len = r - QStyleHelper::calcBigLineSize(r) - 3; 120 | qreal back = offset * len; 121 | QPointF pos(QPointF(xc + back * qCos(a), yc - back * qSin(a))); 122 | return pos; 123 | } 124 | 125 | qreal angle(const QPointF &p1, const QPointF &p2) 126 | { 127 | static const qreal rad_factor = 180 / Q_PI; 128 | qreal _angle = 0; 129 | 130 | if (p1.x() == p2.x()) { 131 | if (p1.y() < p2.y()) 132 | _angle = 270; 133 | else 134 | _angle = 90; 135 | } else { 136 | qreal x1, x2, y1, y2; 137 | 138 | if (p1.x() <= p2.x()) { 139 | x1 = p1.x(); y1 = p1.y(); 140 | x2 = p2.x(); y2 = p2.y(); 141 | } else { 142 | x2 = p1.x(); y2 = p1.y(); 143 | x1 = p2.x(); y1 = p2.y(); 144 | } 145 | 146 | qreal m = -(y2 - y1) / (x2 - x1); 147 | _angle = qAtan(m) * rad_factor; 148 | 149 | if (p1.x() < p2.x()) 150 | _angle = 180 - _angle; 151 | else 152 | _angle = -_angle; 153 | } 154 | return _angle; 155 | } 156 | 157 | QPolygonF calcLines(const QStyleOptionSlider *dial) 158 | { 159 | QPolygonF poly; 160 | int width = dial->rect.width(); 161 | int height = dial->rect.height(); 162 | qreal r = qMin(width, height) / 2; 163 | int bigLineSize = calcBigLineSize(int(r)); 164 | 165 | qreal xc = width / 2 + 0.5; 166 | qreal yc = height / 2 + 0.5; 167 | const int ns = dial->tickInterval; 168 | if (!ns) // Invalid values may be set by Qt Designer. 169 | return poly; 170 | int notches = (dial->maximum + ns - 1 - dial->minimum) / ns; 171 | if (notches <= 0) 172 | return poly; 173 | if (dial->maximum < dial->minimum || dial->maximum - dial->minimum > 1000) { 174 | int maximum = dial->minimum + 1000; 175 | notches = (maximum + ns - 1 - dial->minimum) / ns; 176 | } 177 | 178 | poly.resize(2 + 2 * notches); 179 | int smallLineSize = bigLineSize / 2; 180 | for (int i = 0; i <= notches; ++i) { 181 | qreal angle = dial->dialWrapping ? Q_PI * 3 / 2 - i * 2 * Q_PI / notches 182 | : (Q_PI * 8 - i * 10 * Q_PI / notches) / 6; 183 | qreal s = qSin(angle); 184 | qreal c = qCos(angle); 185 | if (i == 0 || (((ns * i) % (dial->pageStep ? dial->pageStep : 1)) == 0)) { 186 | poly[2 * i] = QPointF(xc + (r - bigLineSize) * c, 187 | yc - (r - bigLineSize) * s); 188 | poly[2 * i + 1] = QPointF(xc + r * c, yc - r * s); 189 | } else { 190 | poly[2 * i] = QPointF(xc + (r - 1 - smallLineSize) * c, 191 | yc - (r - 1 - smallLineSize) * s); 192 | poly[2 * i + 1] = QPointF(xc + (r - 1) * c, yc -(r - 1) * s); 193 | } 194 | } 195 | return poly; 196 | } 197 | 198 | // This will draw a nice and shiny QDial for us. We don't want 199 | // all the shinyness in QWindowsStyle, hence we place it here 200 | 201 | void drawDial(const QStyleOptionSlider *option, QPainter *painter) 202 | { 203 | QPalette pal = option->palette; 204 | QColor buttonColor = pal.button().color(); 205 | const int width = option->rect.width(); 206 | const int height = option->rect.height(); 207 | const bool enabled = option->state & QStyle::State_Enabled; 208 | qreal r = qMin(width, height) / 2; 209 | r -= r/50; 210 | const qreal penSize = r/20.0; 211 | 212 | painter->save(); 213 | painter->setRenderHint(QPainter::Antialiasing); 214 | 215 | // Draw notches 216 | if (option->subControls & QStyle::SC_DialTickmarks) { 217 | painter->setPen(option->palette.dark().color().darker(120)); 218 | painter->drawLines(QStyleHelper::calcLines(option)); 219 | } 220 | 221 | // Cache dial background 222 | BEGIN_STYLE_PIXMAPCACHE(QString::fromLatin1("qdial")); 223 | p->setRenderHint(QPainter::Antialiasing); 224 | 225 | const qreal d_ = r / 6; 226 | const qreal dx = option->rect.x() + d_ + (width - 2 * r) / 2 + 1; 227 | const qreal dy = option->rect.y() + d_ + (height - 2 * r) / 2 + 1; 228 | 229 | QRectF br = QRectF(dx + 0.5, dy + 0.5, 230 | int(r * 2 - 2 * d_ - 2), 231 | int(r * 2 - 2 * d_ - 2)); 232 | buttonColor.setHsv(buttonColor .hue(), 233 | qMin(140, buttonColor .saturation()), 234 | qMax(180, buttonColor.value())); 235 | 236 | if (enabled) { 237 | // Drop shadow 238 | qreal shadowSize = qMax(1.0, penSize/2.0); 239 | QRectF shadowRect= br.adjusted(-2*shadowSize, -2*shadowSize, 240 | 2*shadowSize, 2*shadowSize); 241 | QRadialGradient shadowGradient(shadowRect.center().x(), 242 | shadowRect.center().y(), shadowRect.width()/2.0, 243 | shadowRect.center().x(), shadowRect.center().y()); 244 | shadowGradient.setColorAt(qreal(0.91), QColor(0, 0, 0, 40)); 245 | shadowGradient.setColorAt(qreal(1.0), Qt::transparent); 246 | p->setBrush(shadowGradient); 247 | p->setPen(Qt::NoPen); 248 | p->translate(shadowSize, shadowSize); 249 | p->drawEllipse(shadowRect); 250 | p->translate(-shadowSize, -shadowSize); 251 | 252 | // Main gradient 253 | QRadialGradient gradient(br.center().x() - br.width()/3, dy, 254 | br.width()*1.3, br.center().x(), 255 | br.center().y() - br.height()/2); 256 | gradient.setColorAt(0, buttonColor.lighter(110)); 257 | gradient.setColorAt(qreal(0.5), buttonColor); 258 | gradient.setColorAt(qreal(0.501), buttonColor.darker(102)); 259 | gradient.setColorAt(1, buttonColor.darker(115)); 260 | p->setBrush(gradient); 261 | } else { 262 | p->setBrush(Qt::NoBrush); 263 | } 264 | 265 | p->setPen(QPen(buttonColor.darker(280))); 266 | p->drawEllipse(br); 267 | p->setBrush(Qt::NoBrush); 268 | p->setPen(buttonColor.lighter(110)); 269 | p->drawEllipse(br.adjusted(1, 1, -1, -1)); 270 | 271 | if (option->state & QStyle::State_HasFocus) { 272 | QColor highlight = pal.highlight().color(); 273 | highlight.setHsv(highlight.hue(), 274 | qMin(160, highlight.saturation()), 275 | qMax(230, highlight.value())); 276 | highlight.setAlpha(127); 277 | p->setPen(QPen(highlight, 2.0)); 278 | p->setBrush(Qt::NoBrush); 279 | p->drawEllipse(br.adjusted(-1, -1, 1, 1)); 280 | } 281 | 282 | END_STYLE_PIXMAPCACHE 283 | 284 | QPointF dp = calcRadialPos(option, qreal(0.70)); 285 | buttonColor = buttonColor.lighter(104); 286 | buttonColor.setAlphaF(qreal(0.8)); 287 | const qreal ds = r/qreal(7.0); 288 | QRectF dialRect(dp.x() - ds, dp.y() - ds, 2*ds, 2*ds); 289 | QRadialGradient dialGradient(dialRect.center().x() + dialRect.width()/2, 290 | dialRect.center().y() + dialRect.width(), 291 | dialRect.width()*2, 292 | dialRect.center().x(), dialRect.center().y()); 293 | dialGradient.setColorAt(1, buttonColor.darker(140)); 294 | dialGradient.setColorAt(qreal(0.4), buttonColor.darker(120)); 295 | dialGradient.setColorAt(0, buttonColor.darker(110)); 296 | if (penSize > 3.0) { 297 | painter->setPen(QPen(QColor(0, 0, 0, 25), penSize)); 298 | painter->drawLine(calcRadialPos(option, qreal(0.90)), calcRadialPos(option, qreal(0.96))); 299 | } 300 | 301 | painter->setBrush(dialGradient); 302 | painter->setPen(QColor(255, 255, 255, 150)); 303 | painter->drawEllipse(dialRect.adjusted(-1, -1, 1, 1)); 304 | painter->setPen(QColor(0, 0, 0, 80)); 305 | painter->drawEllipse(dialRect); 306 | painter->restore(); 307 | } 308 | #endif //QT_NO_DIAL 309 | 310 | } 311 | QT_END_NAMESPACE 312 | -------------------------------------------------------------------------------- /src/qt6gtk2-style/qstylehelper_p.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | * Copyright (C) 2015 The Qt Company Ltd. * 3 | * Copyright (C) 2016-2024 Ilya Kotov, forkotov02@ya.ru * 4 | * * 5 | * This program is free software; you can redistribute it and/or modify * 6 | * it under the terms of the GNU General Public License as published by * 7 | * the Free Software Foundation; either version 2 of the License, or * 8 | * (at your option) any later version. * 9 | * * 10 | * This program is distributed in the hope that it will be useful, * 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 13 | * GNU General Public License for more details. * 14 | * * 15 | * You should have received a copy of the GNU General Public License * 16 | * along with this program; if not, write to the * 17 | * Free Software Foundation, Inc., * 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * 19 | ***************************************************************************/ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #ifndef QSTYLEHELPER_P_H 29 | #define QSTYLEHELPER_P_H 30 | 31 | // 32 | // W A R N I N G 33 | // ------------- 34 | // 35 | // This file is not part of the Qt API. It exists purely as an 36 | // implementation detail. This header file may change from version to 37 | // version without notice, or even be removed. 38 | // 39 | // We mean it. 40 | // 41 | 42 | #include 43 | 44 | QT_BEGIN_NAMESPACE 45 | 46 | class QPainter; 47 | class QPixmap; 48 | class QStyleOptionSlider; 49 | class QStyleOption; 50 | class QWindow; 51 | 52 | namespace QStyleHelper 53 | { 54 | #if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)) 55 | QString uniqueName(const QString &key, const QStyleOption *option, const QSize &size, int dpr); 56 | #else 57 | QString uniqueName(const QString &key, const QStyleOption *option, const QSize &size); 58 | #endif 59 | #ifndef QT_NO_DIAL 60 | qreal angle(const QPointF &p1, const QPointF &p2); 61 | QPolygonF calcLines(const QStyleOptionSlider *dial); 62 | int calcBigLineSize(int radius); 63 | void drawDial(const QStyleOptionSlider *dial, QPainter *painter); 64 | #endif //QT_NO_DIAL 65 | void drawBorderPixmap(const QPixmap &pixmap, QPainter *painter, const QRect &rect, 66 | int left = 0, int top = 0, int right = 0, 67 | int bottom = 0); 68 | #ifndef QT_NO_ACCESSIBILITY 69 | bool isInstanceOf(QObject *obj, QAccessible::Role role); 70 | bool hasAncestor(QObject *obj, QAccessible::Role role); 71 | #endif 72 | } 73 | 74 | 75 | QT_END_NAMESPACE 76 | 77 | #endif // QSTYLEHELPER_P_H 78 | -------------------------------------------------------------------------------- /src/qt6gtk2-style/qt6gtk2-style.pro: -------------------------------------------------------------------------------- 1 | include(../../qt6gtk2.pri) 2 | 3 | TEMPLATE = lib 4 | TARGET = qt6gtk2-style 5 | QT += core-private gui-private widgets-private 6 | 7 | DEFINES += QT_NO_ANIMATION 8 | 9 | # Input 10 | HEADERS += qgtk2painter_p.h \ 11 | qgtkglobal_p.h \ 12 | qgtkpainter_p.h \ 13 | qgtkstyle_p.h \ 14 | qgtkstyle_p_p.h \ 15 | qstylehelper_p.h 16 | SOURCES += qgtk2painter.cpp qgtkpainter.cpp qgtkstyle.cpp qgtkstyle_p.cpp \ 17 | plugin.cpp \ 18 | qstylehelper.cpp 19 | 20 | CONFIG += plugin \ 21 | link_pkgconfig \ 22 | 23 | PKGCONFIG += gtk+-2.0 x11 24 | 25 | target.path = $$PLUGINDIR/styles 26 | INSTALLS += target 27 | -------------------------------------------------------------------------------- /src/qt6gtk2-style/qt6gtk2.json: -------------------------------------------------------------------------------- 1 | { 2 | "Keys": [ "gtk2", "qt6gtk2", "qt5gtk2" ] 3 | } 4 | --------------------------------------------------------------------------------