├── .github
└── workflows
│ └── build.yml
├── .gitignore
├── CMakeLists.txt
├── LICENSE
├── README.md
├── cmake
├── FindQt5PlatformSupport.cmake
└── FindQt5ThemeSupport.cmake
├── debian
├── changelog
├── compat
├── control
├── copyright
├── rules
└── source
│ └── format
├── platformtheme
├── CMakeLists.txt
├── cutefish-platformtheme.json
├── hintsettings.cpp
├── hintsettings.h
├── main.cpp
├── org.kde.StatusNotifierWatcher.xml
├── platformtheme.cpp
├── platformtheme.h
├── qdbusmenubar.cpp
├── qdbusmenubar_p.h
├── statusnotifier
│ ├── dbustypes.cpp
│ ├── dbustypes.h
│ ├── org.kde.StatusNotifierItem.xml
│ ├── statusnotifieritem.cpp
│ └── statusnotifieritem.h
├── systemtrayicon.cpp
├── systemtrayicon.h
├── x11integration.cpp
└── x11integration.h
└── widgetstyle
├── CMakeLists.txt
├── basestyle.cpp
├── basestyle.h
├── blurhelper.cpp
├── blurhelper.h
├── boxshadowrenderer.cpp
├── boxshadowrenderer.h
├── cutefishstyle.json
├── phantomcolor.cpp
├── phantomcolor.h
├── pstyleplugin.cpp
├── pstyleplugin.h
├── shadowhelper.cpp
├── shadowhelper.h
├── tileset.cpp
└── tileset.h
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | branches: [ main ]
8 |
9 | jobs:
10 | debian:
11 | name: Debian
12 | runs-on: ubuntu-latest
13 | container: docker.io/library/debian:sid
14 | steps:
15 | - name: Checkout Source
16 | uses: actions/checkout@v2
17 | - name: Update repository
18 | run: apt-get update -y
19 | - name: Install the basic dev packages
20 | run: apt-get install -y equivs curl git devscripts lintian build-essential automake autotools-dev cmake g++
21 | - name: Install build dependencies
22 | run: mk-build-deps -i -t "apt-get --yes" -r
23 | - name: Build Package
24 | run: dpkg-buildpackage -b -uc -us -j$(nproc)
25 |
26 | ubuntu:
27 | name: Ubuntu
28 | runs-on: ubuntu-latest
29 | steps:
30 | - name: Checkout Source
31 | uses: actions/checkout@v2
32 | - name: Update repository
33 | run: sudo apt-get update -y
34 | - name: Install the basic dev packages
35 | run: sudo apt-get install -y equivs curl git devscripts lintian build-essential automake autotools-dev cmake g++
36 | - name: Install build dependencies
37 | run: sudo mk-build-deps -i -t "apt-get --yes" -r
38 | - name: Build Package
39 | run: dpkg-buildpackage -b -uc -us -j$(nproc)
40 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # C++ objects and libs
2 | *.slo
3 | *.lo
4 | *.o
5 | *.a
6 | *.la
7 | *.lai
8 | *.so
9 | *.so.*
10 | *.dll
11 | *.dylib
12 |
13 | # Qt-es
14 | object_script.*.Release
15 | object_script.*.Debug
16 | *_plugin_import.cpp
17 | /.qmake.cache
18 | /.qmake.stash
19 | *.pro.user
20 | *.pro.user.*
21 | *.qbs.user
22 | *.qbs.user.*
23 | *.moc
24 | moc_*.cpp
25 | moc_*.h
26 | qrc_*.cpp
27 | ui_*.h
28 | *.qmlc
29 | *.jsc
30 | Makefile*
31 | *build-*
32 | *.qm
33 | *.prl
34 |
35 | # Qt unit tests
36 | target_wrapper.*
37 |
38 | # QtCreator
39 | *.autosave
40 |
41 | # QtCreator Qml
42 | *.qmlproject.user
43 | *.qmlproject.user.*
44 |
45 | # QtCreator CMake
46 | CMakeLists.txt.user*
47 |
48 | # QtCreator 4.8< compilation database
49 | compile_commands.json
50 |
51 | # QtCreator local machine specific files for imported projects
52 | *creator.user*
53 |
54 | build/*
55 | .vscode/*
56 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.0)
2 |
3 | project(cutefish-qt-plugins)
4 |
5 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
6 |
7 | add_subdirectory(platformtheme)
8 | add_subdirectory(widgetstyle)
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Qt Plugins
2 |
3 | Unify Qt application style of CutefishOS.
4 |
5 | ## Dependencies
6 |
7 | `sudo pacman -S gcc extra-cmake-modules qt5-base qt5-tools qt5-x11extras libqtxdg libdbusmenu-qt5 libxcb`
8 |
9 | ## Build
10 |
11 | ```shell
12 | mkdir build
13 | cd build
14 | cmake ..
15 | make
16 | sudo make install
17 | ```
18 |
19 | ## License
20 |
21 | cutefish-qt-plugins is licensed under GPLv3.
22 |
--------------------------------------------------------------------------------
/cmake/FindQt5PlatformSupport.cmake:
--------------------------------------------------------------------------------
1 | #.rst:
2 | # FindQt5PlatformSupport
3 | # -------
4 | #
5 | # Try to find Qt5PlatformSupport on a Unix system.
6 | #
7 | # This will define the following variables:
8 | #
9 | # ``Qt5PlatformSupport_FOUND``
10 | # True if (the requested version of) Qt5PlatformSupport is available
11 | # ``Qt5PlatformSupport_VERSION``
12 | # The version of Qt5PlatformSupport
13 | # ``Qt5PlatformSupport_LIBRARIES``
14 | # This can be passed to target_link_libraries() instead of the ``Qt5PlatformSupport::Qt5PlatformSupport``
15 | # target
16 | # ``Qt5PlatformSupport_INCLUDE_DIRS``
17 | # This should be passed to target_include_directories() if the target is not
18 | # used for linking
19 | # ``Qt5PlatformSupport_DEFINITIONS``
20 | # This should be passed to target_compile_options() if the target is not
21 | # used for linking
22 | #
23 | # If ``Qt5PlatformSupport_FOUND`` is TRUE, it will also define the following imported target:
24 | #
25 | # ``Qt5PlatformSupport::Qt5PlatformSupport``
26 | # The Qt5PlatformSupport library
27 | #
28 | # In general we recommend using the imported target, as it is easier to use.
29 | # Bear in mind, however, that if the target is in the link interface of an
30 | # exported library, it must be made available by the package config file.
31 |
32 | #=============================================================================
33 | # Copyright 2014 Alex Merry
34 | # Copyright 2014 Martin Gräßlin
35 | #
36 | # Redistribution and use in source and binary forms, with or without
37 | # modification, are permitted provided that the following conditions
38 | # are met:
39 | #
40 | # 1. Redistributions of source code must retain the copyright
41 | # notice, this list of conditions and the following disclaimer.
42 | # 2. Redistributions in binary form must reproduce the copyright
43 | # notice, this list of conditions and the following disclaimer in the
44 | # documentation and/or other materials provided with the distribution.
45 | # 3. The name of the author may not be used to endorse or promote products
46 | # derived from this software without specific prior written permission.
47 | #
48 | # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
49 | # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
50 | # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
51 | # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
52 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
53 | # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
54 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
55 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
56 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
57 | # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
58 | #=============================================================================
59 |
60 | if(CMAKE_VERSION VERSION_LESS 2.8.12)
61 | message(FATAL_ERROR "CMake 2.8.12 is required by FindQt5PlatformSupport.cmake")
62 | endif()
63 | if(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.12)
64 | message(AUTHOR_WARNING "Your project should require at least CMake 2.8.12 to use FindQt5PlatformSupport.cmake")
65 | endif()
66 |
67 | # Use pkg-config to get the directories and then use these values
68 | # in the FIND_PATH() and FIND_LIBRARY() calls
69 | find_package(PkgConfig)
70 | pkg_check_modules(PKG_Qt5PlatformSupport QUIET Qt5Gui)
71 |
72 | set(Qt5PlatformSupport_DEFINITIONS ${PKG_Qt5PlatformSupport_CFLAGS_OTHER})
73 | set(Qt5PlatformSupport_VERSION ${PKG_Qt5PlatformSupport_VERSION})
74 |
75 | find_path(Qt5PlatformSupport_INCLUDE_DIR
76 | NAMES
77 | QtPlatformSupport/private/qfontconfigdatabase_p.h
78 | HINTS
79 | ${PKG_Qt5PlatformSupport_INCLUDEDIR}/QtPlatformSupport/${PKG_Qt5PlatformSupport_VERSION}/
80 | )
81 | find_library(Qt5PlatformSupport_LIBRARY
82 | NAMES
83 | Qt5PlatformSupport
84 | HINTS
85 | ${PKG_Qt5PlatformSupport_LIBRARY_DIRS}
86 | )
87 |
88 | include(FindPackageHandleStandardArgs)
89 | find_package_handle_standard_args(Qt5PlatformSupport
90 | FOUND_VAR
91 | Qt5PlatformSupport_FOUND
92 | REQUIRED_VARS
93 | Qt5PlatformSupport_LIBRARY
94 | Qt5PlatformSupport_INCLUDE_DIR
95 | VERSION_VAR
96 | Qt5PlatformSupport_VERSION
97 | )
98 |
99 | if(Qt5PlatformSupport_FOUND AND NOT TARGET Qt5PlatformSupport::Qt5PlatformSupport)
100 | add_library(Qt5PlatformSupport::Qt5PlatformSupport UNKNOWN IMPORTED)
101 | set_target_properties(Qt5PlatformSupport::Qt5PlatformSupport PROPERTIES
102 | IMPORTED_LOCATION "${Qt5PlatformSupport_LIBRARY}"
103 | INTERFACE_COMPILE_OPTIONS "${Qt5PlatformSupport_DEFINITIONS}"
104 | INTERFACE_INCLUDE_DIRECTORIES "${Qt5PlatformSupport_INCLUDE_DIR}"
105 | )
106 | endif()
107 |
108 | mark_as_advanced(Qt5PlatformSupport_LIBRARY Qt5PlatformSupport_INCLUDE_DIR)
109 |
110 | # compatibility variables
111 | set(Qt5PlatformSupport_LIBRARIES ${Qt5PlatformSupport_LIBRARY})
112 | set(Qt5PlatformSupport_INCLUDE_DIRS ${Qt5PlatformSupport_INCLUDE_DIR})
113 | set(Qt5PlatformSupport_VERSION_STRING ${Qt5PlatformSupport_VERSION})
114 |
115 |
116 | include(FeatureSummary)
117 | set_package_properties(Qt5PlatformSupport PROPERTIES
118 | URL "http://www.qt.io"
119 | DESCRIPTION "Qt PlatformSupport module."
120 | )
121 |
122 |
--------------------------------------------------------------------------------
/cmake/FindQt5ThemeSupport.cmake:
--------------------------------------------------------------------------------
1 | #.rst:
2 | # FindQt5ThemeSupport
3 | # -------
4 | #
5 | # Try to find Qt5ThemeSupport on a Unix system.
6 | #
7 | # This will define the following variables:
8 | #
9 | # ``Qt5ThemeSupport_FOUND``
10 | # True if (the requested version of) Qt5ThemeSupport is available
11 | # ``Qt5ThemeSupport_VERSION``
12 | # The version of Qt5ThemeSupport
13 | # ``Qt5ThemeSupport_LIBRARIES``
14 | # This can be passed to target_link_libraries() instead of the ``Qt5ThemeSupport::Qt5ThemeSupport``
15 | # target
16 | # ``Qt5ThemeSupport_INCLUDE_DIRS``
17 | # This should be passed to target_include_directories() if the target is not
18 | # used for linking
19 | # ``Qt5ThemeSupport_DEFINITIONS``
20 | # This should be passed to target_compile_options() if the target is not
21 | # used for linking
22 | #
23 | # If ``Qt5ThemeSupport_FOUND`` is TRUE, it will also define the following imported target:
24 | #
25 | # ``Qt5ThemeSupport::Qt5ThemeSupport``
26 | # The Qt5ThemeSupport library
27 | #
28 | # In general we recommend using the imported target, as it is easier to use.
29 | # Bear in mind, however, that if the target is in the link interface of an
30 | # exported library, it must be made available by the package config file.
31 |
32 | #=============================================================================
33 | # Copyright 2014 Alex Merry
34 | # Copyright 2014 Martin Gräßlin
35 | # Copyright 2016 Takahiro Hashimoto
36 | #
37 | # Redistribution and use in source and binary forms, with or without
38 | # modification, are permitted provided that the following conditions
39 | # are met:
40 | #
41 | # 1. Redistributions of source code must retain the copyright
42 | # notice, this list of conditions and the following disclaimer.
43 | # 2. Redistributions in binary form must reproduce the copyright
44 | # notice, this list of conditions and the following disclaimer in the
45 | # documentation and/or other materials provided with the distribution.
46 | # 3. The name of the author may not be used to endorse or promote products
47 | # derived from this software without specific prior written permission.
48 | #
49 | # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
50 | # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
51 | # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
52 | # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
53 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
54 | # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
55 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
56 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
57 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
58 | # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
59 | #=============================================================================
60 |
61 | if(CMAKE_VERSION VERSION_LESS 2.8.12)
62 | message(FATAL_ERROR "CMake 2.8.12 is required by FindQt5ThemeSupport.cmake")
63 | endif()
64 | if(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.12)
65 | message(AUTHOR_WARNING "Your project should require at least CMake 2.8.12 to use FindQt5ThemeSupport.cmake")
66 | endif()
67 |
68 | # Use pkg-config to get the directories and then use these values
69 | # in the FIND_PATH() and FIND_LIBRARY() calls
70 | find_package(PkgConfig)
71 | pkg_check_modules(PKG_Qt5ThemeSupport QUIET Qt5Gui)
72 |
73 | set(Qt5ThemeSupport_DEFINITIONS ${PKG_Qt5ThemeSupport_CFLAGS_OTHER})
74 | set(Qt5ThemeSupport_VERSION ${PKG_Qt5ThemeSupport_VERSION})
75 |
76 | find_path(Qt5ThemeSupport_INCLUDE_DIR
77 | NAMES
78 | QtThemeSupport/private/qgenericunixthemes_p.h
79 | HINTS
80 | ${PKG_Qt5ThemeSupport_INCLUDEDIR}/QtThemeSupport/${PKG_Qt5ThemeSupport_VERSION}/
81 | )
82 | find_library(Qt5ThemeSupport_LIBRARY
83 | NAMES
84 | Qt5ThemeSupport
85 | HINTS
86 | ${PKG_Qt5ThemeSupport_LIBRARY_DIRS}
87 | )
88 |
89 | include(FindPackageHandleStandardArgs)
90 | find_package_handle_standard_args(Qt5ThemeSupport
91 | FOUND_VAR
92 | Qt5ThemeSupport_FOUND
93 | REQUIRED_VARS
94 | Qt5ThemeSupport_LIBRARY
95 | Qt5ThemeSupport_INCLUDE_DIR
96 | VERSION_VAR
97 | Qt5ThemeSupport_VERSION
98 | )
99 |
100 | if(Qt5ThemeSupport_FOUND AND NOT TARGET Qt5ThemeSupport::Qt5ThemeSupport)
101 | add_library(Qt5ThemeSupport::Qt5ThemeSupport UNKNOWN IMPORTED)
102 | set_target_properties(Qt5ThemeSupport::Qt5ThemeSupport PROPERTIES
103 | IMPORTED_LOCATION "${Qt5ThemeSupport_LIBRARY}"
104 | INTERFACE_COMPILE_OPTIONS "${Qt5ThemeSupport_DEFINITIONS}"
105 | INTERFACE_INCLUDE_DIRECTORIES "${Qt5ThemeSupport_INCLUDE_DIR}"
106 | )
107 | endif()
108 |
109 | mark_as_advanced(Qt5ThemeSupport_LIBRARY Qt5ThemeSupport_INCLUDE_DIR)
110 |
111 | # compatibility variables
112 | set(Qt5ThemeSupport_LIBRARIES ${Qt5ThemeSupport_LIBRARY})
113 | set(Qt5ThemeSupport_INCLUDE_DIRS ${Qt5ThemeSupport_INCLUDE_DIR})
114 | set(Qt5ThemeSupport_VERSION_STRING ${Qt5ThemeSupport_VERSION})
115 |
116 |
117 | include(FeatureSummary)
118 | set_package_properties(Qt5ThemeSupport PROPERTIES
119 | URL "http://www.qt.io"
120 | DESCRIPTION "Qt ThemeSupport module."
121 | )
122 |
123 |
--------------------------------------------------------------------------------
/debian/changelog:
--------------------------------------------------------------------------------
1 | cutefish-qt-plugins (0.6) UNRELEASED; urgency=high
2 |
3 | * Initial release (CutefishOS)
4 |
5 | -- CutefishOS Packaging Team Sun, 21 Nov 2021 13:52:29 +0800
6 |
--------------------------------------------------------------------------------
/debian/compat:
--------------------------------------------------------------------------------
1 | 9
2 |
--------------------------------------------------------------------------------
/debian/control:
--------------------------------------------------------------------------------
1 | Source: cutefish-qt-plugins
2 | Section: devel
3 | Priority: optional
4 | Maintainer: CutefishOS
5 | Build-Depends: cmake,
6 | debhelper (>= 9),
7 | extra-cmake-modules,
8 | libqt5x11extras5-dev,
9 | libkf5windowsystem-dev,
10 | libqt5xdg-dev,
11 | libdbusmenu-qt5-dev,
12 | libxcb-shape0-dev,
13 | libxcb-icccm4-dev,
14 | libxcb1-dev,
15 | libxcb-ewmh-dev,
16 | qtbase5-dev,
17 | qtbase5-private-dev,
18 | qtdeclarative5-dev,
19 | qtquickcontrols2-5-dev,
20 | qttools5-dev,
21 | qttools5-dev-tools
22 | Standards-Version: 4.5.0
23 | Homepage: https://cutefishos.com
24 |
25 | Package: cutefish-qt-plugins
26 | Architecture: any
27 | Depends: ${misc:Depends},
28 | ${shlibs:Depends}
29 | Description: CutefishOS Qt Plugins
30 |
--------------------------------------------------------------------------------
/debian/copyright:
--------------------------------------------------------------------------------
1 | Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
2 | Upstream-Name: cutefish-qt-plugins
3 | Source: cutefishos.com
4 |
--------------------------------------------------------------------------------
/debian/rules:
--------------------------------------------------------------------------------
1 | #!/usr/bin/make -f
2 |
3 | export QT_SELECT=5
4 |
5 | %:
6 | dh $@
7 |
8 | override_dh_auto_configure:
9 | dh_auto_configure -- -DEMBED_TRANSLATIONS=ON -DBUILD_TESTING=ON
10 |
--------------------------------------------------------------------------------
/debian/source/format:
--------------------------------------------------------------------------------
1 | 3.0 (quilt)
2 |
--------------------------------------------------------------------------------
/platformtheme/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | project(platformthemeplugin)
2 | include(GNUInstallDirs)
3 |
4 | set(CMAKE_AUTOMOC ON)
5 | set(CMAKE_INCLUDE_CURRENT_DIR ON)
6 | set(CMAKE_POSITION_INDEPENDENT_CODE ON)
7 |
8 | find_package(Qt5Core REQUIRED)
9 | find_package(Qt5Widgets REQUIRED)
10 | find_package(Qt5QuickControls2 REQUIRED)
11 | find_package(Qt5DBus REQUIRED)
12 | find_package(Qt5X11Extras REQUIRED)
13 | find_package(Qt5Gui CONFIG REQUIRED Private)
14 | find_package(Qt5XdgIconLoader REQUIRED)
15 | find_package(dbusmenu-qt5 REQUIRED)
16 | find_package(KF5WindowSystem REQUIRED)
17 |
18 | # dependencies for QPA plugin
19 | find_package(Qt5ThemeSupport REQUIRED)
20 | set(QT5PLATFORMSUPPORT_LIBS Qt5ThemeSupport::Qt5ThemeSupport)
21 |
22 | # qdbusmenubar uses them
23 | remove_definitions(-DQT_NO_SIGNALS_SLOTS_KEYWORDS)
24 |
25 | pkg_check_modules(XCB_EWMH REQUIRED xcb xcb-ewmh x11)
26 |
27 | set (SRCS
28 | main.cpp
29 | platformtheme.h
30 | platformtheme.cpp
31 | hintsettings.h
32 | hintsettings.cpp
33 | systemtrayicon.h
34 | systemtrayicon.cpp
35 | qdbusmenubar_p.h
36 | qdbusmenubar.cpp
37 | x11integration.h
38 | x11integration.cpp
39 | statusnotifier/dbustypes.h
40 | statusnotifier/dbustypes.cpp
41 | statusnotifier/statusnotifieritem.h
42 | statusnotifier/statusnotifieritem.cpp
43 | )
44 |
45 | qt5_add_dbus_interface(SRCS org.kde.StatusNotifierWatcher.xml statusnotifierwatcher_interface)
46 |
47 | qt5_add_dbus_adaptor(SRCS
48 | statusnotifier/org.kde.StatusNotifierItem.xml
49 | statusnotifier/statusnotifieritem.h
50 | StatusNotifierItem
51 | )
52 |
53 | add_library(cutefishplatformtheme MODULE ${SRCS})
54 |
55 | target_compile_definitions(cutefishplatformtheme
56 | PRIVATE
57 | "QT_NO_FOREACH"
58 | "LIB_FM_QT_SONAME=\"${LIB_FM_QT_SONAME}\""
59 | )
60 |
61 | target_link_libraries(cutefishplatformtheme PRIVATE
62 | Qt5::GuiPrivate
63 | Qt5::X11Extras
64 | Qt5::Widgets
65 | Qt5::QuickControls2
66 | Qt5::Core
67 | Qt5::DBus
68 | dbusmenu-qt5
69 |
70 | Qt5XdgIconLoader
71 |
72 | KF5::WindowSystem
73 | ${XCB_LIBRARIES}
74 | ${QT5PLATFORMSUPPORT_LIBS}
75 | )
76 |
77 | get_target_property(QT_QMAKE_EXECUTABLE ${Qt5Core_QMAKE_EXECUTABLE} IMPORTED_LOCATION)
78 | if(NOT QT_QMAKE_EXECUTABLE)
79 | message(FATAL_ERROR "qmake is not found.")
80 | endif()
81 |
82 | # execute the command "qmake -query QT_INSTALL_PLUGINS" to get the path of plugins dir.
83 | execute_process(COMMAND ${QT_QMAKE_EXECUTABLE} -query QT_INSTALL_PLUGINS
84 | OUTPUT_VARIABLE QT_PLUGINS_DIR
85 | OUTPUT_STRIP_TRAILING_WHITESPACE
86 | )
87 | if(QT_PLUGINS_DIR)
88 | message(STATUS "Qt5 plugin directory:" "${QT_PLUGINS_DIR}")
89 | else()
90 | message(FATAL_ERROR "Qt5 plugin directory cannot be detected.")
91 | endif()
92 |
93 | install(TARGETS cutefishplatformtheme LIBRARY DESTINATION "${QT_PLUGINS_DIR}/platformthemes")
94 |
--------------------------------------------------------------------------------
/platformtheme/cutefish-platformtheme.json:
--------------------------------------------------------------------------------
1 | {
2 | "Keys": [ "cutefish" ]
3 | }
4 |
--------------------------------------------------------------------------------
/platformtheme/hintsettings.cpp:
--------------------------------------------------------------------------------
1 | #include "hintsettings.h"
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 |
19 | #include
20 | #include
21 | #include
22 |
23 | static const QByteArray s_systemFontName = QByteArrayLiteral("Font");
24 | static const QByteArray s_systemFixedFontName = QByteArrayLiteral("FixedFont");
25 | static const QByteArray s_systemPointFontSize = QByteArrayLiteral("FontSize");
26 | static const QByteArray s_darkModeName = QByteArrayLiteral("DarkMode");
27 | static const QByteArray s_lightIconName = QByteArrayLiteral("Crule");
28 | static const QByteArray s_darkIconName = QByteArrayLiteral("Crule-dark");
29 |
30 | HintsSettings::HintsSettings(QObject *parent)
31 | : QObject(parent),
32 | m_settings(new QSettings(QSettings::UserScope, "cutefishos", "theme"))
33 | {
34 | m_iconTheme = m_settings->value("IconTheme", "Crule").toString();
35 |
36 | if (m_iconTheme.contains("Crule"))
37 | m_hints[QPlatformTheme::SystemIconThemeName] = darkMode() ? s_darkIconName : s_lightIconName;
38 | else
39 | m_hints[QPlatformTheme::SystemIconThemeName] = m_iconTheme;
40 |
41 | m_hints[QPlatformTheme::SystemIconFallbackThemeName] = s_lightIconName;
42 | m_hints[QPlatformTheme::StyleNames] = "cutefish";
43 | m_hints[QPlatformTheme::SystemIconFallbackThemeName] = QStringLiteral("hicolor");
44 | m_hints[QPlatformTheme::IconThemeSearchPaths] = xdgIconThemePaths();
45 | m_hints[QPlatformTheme::UseFullScreenForPopupMenu] = false;
46 | m_hints[QPlatformTheme::DialogButtonBoxLayout] = QPlatformDialogHelper::MacLayout;
47 |
48 | m_settingsFile = m_settings->fileName();
49 |
50 | QMetaObject::invokeMethod(this, "lazyInit", Qt::QueuedConnection);
51 | }
52 |
53 | HintsSettings::~HintsSettings()
54 | {
55 | }
56 |
57 | void HintsSettings::lazyInit()
58 | {
59 | m_fileWatcher = new QFileSystemWatcher();
60 | m_fileWatcher->addPath(m_settingsFile);
61 | connect(m_fileWatcher, &QFileSystemWatcher::fileChanged, this, &HintsSettings::onFileChanged);
62 | }
63 |
64 | QStringList HintsSettings::xdgIconThemePaths() const
65 | {
66 | QStringList paths;
67 |
68 | // make sure we have ~/.local/share/icons in paths if it exists
69 | paths << QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("icons"), QStandardPaths::LocateDirectory);
70 |
71 | const QFileInfo homeIconDir(QDir::homePath() + QStringLiteral("/.icons"));
72 | if (homeIconDir.isDir()) {
73 | paths << homeIconDir.absoluteFilePath();
74 | }
75 |
76 | return paths;
77 | }
78 |
79 | QString HintsSettings::systemFont() const
80 | {
81 | return m_settings->value(s_systemFontName, "Noto Sans").toString();
82 | }
83 |
84 | QString HintsSettings::systemFixedFont() const
85 | {
86 | return m_settings->value(s_systemFixedFontName, "Monospace").toString();
87 | }
88 |
89 | qreal HintsSettings::systemFontPointSize() const
90 | {
91 | return m_settings->value(s_systemPointFontSize, 9).toDouble();
92 | }
93 |
94 | bool HintsSettings::darkMode()
95 | {
96 | return m_settings->value(s_darkModeName, false).toBool();
97 | }
98 |
99 | void HintsSettings::onFileChanged(const QString &path)
100 | {
101 | Q_UNUSED(path);
102 |
103 | QVariantMap map;
104 | for (const QString &value : m_settings->allKeys()) {
105 | map[value] = m_settings->value(value);
106 | }
107 |
108 | m_settings->sync();
109 |
110 | for (const QString &value : m_settings->allKeys()) {
111 | const QVariant &oldValue = map.value(value);
112 | const QVariant &newValue = m_settings->value(value);
113 |
114 | if (oldValue != newValue) {
115 | if (value == s_systemFontName)
116 | emit systemFontChanged(newValue.toString());
117 | else if (value == s_systemFixedFontName)
118 | emit systemFixedFontChanged(newValue.toString());
119 | else if (value == s_systemPointFontSize)
120 | emit systemFontPointSizeChanged(newValue.toDouble());
121 | else if (value == s_darkModeName) {
122 | emit darkModeChanged(newValue.toBool());
123 | // Need to update the icon to dark
124 |
125 | if (m_iconTheme.contains("Crule"))
126 | m_hints[QPlatformTheme::SystemIconThemeName] = darkMode() ? s_darkIconName : s_lightIconName;
127 |
128 | emit iconThemeChanged();
129 | } else if (value == "IconTheme") {
130 | if (!m_iconTheme.contains("Crule")) {
131 | m_iconTheme = newValue.toString();
132 | m_hints[QPlatformTheme::SystemIconThemeName] = m_iconTheme;
133 | }
134 |
135 | emit iconThemeChanged();
136 | }
137 | }
138 | }
139 |
140 | bool fileDeleted = !m_fileWatcher->files().contains(m_settingsFile);
141 | if (fileDeleted)
142 | m_fileWatcher->addPath(m_settingsFile);
143 | }
144 |
--------------------------------------------------------------------------------
/platformtheme/hintsettings.h:
--------------------------------------------------------------------------------
1 | #ifndef HINTSSETTINGS_H
2 | #define HINTSSETTINGS_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | #include
11 |
12 | class QPalette;
13 | class HintsSettings : public QObject
14 | {
15 | Q_OBJECT
16 |
17 | public:
18 | explicit HintsSettings(QObject *parent = nullptr);
19 | ~HintsSettings() override;
20 |
21 | QStringList xdgIconThemePaths() const;
22 |
23 | inline QVariant hint(QPlatformTheme::ThemeHint hint) const {
24 | return m_hints[hint];
25 | }
26 |
27 | QString systemFont() const;
28 | QString systemFixedFont() const;
29 | qreal systemFontPointSize() const;
30 |
31 | bool darkMode();
32 |
33 | public Q_SLOTS:
34 | void lazyInit();
35 |
36 | Q_SIGNALS:
37 | void systemFontChanged(QString font);
38 | void systemFixedFontChanged(QString fixedFont);
39 | void systemFontPointSizeChanged(qreal systemFontPointSize);
40 | void iconThemeChanged();
41 | void darkModeChanged(bool darkMode);
42 |
43 | private:
44 | void onFileChanged(const QString &path);
45 |
46 | private:
47 | QHash m_hints;
48 | QSettings *m_settings;
49 | QString m_settingsFile;
50 | QFileSystemWatcher *m_fileWatcher;
51 |
52 | QString m_systemFont;
53 | QString m_systemFixedFont;
54 | qreal m_systemFontPointSize;
55 | QString m_iconTheme;
56 | };
57 |
58 | #endif //HINTSSETTINGS_H
59 |
--------------------------------------------------------------------------------
/platformtheme/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include "platformtheme.h"
3 |
4 | #include
5 |
6 | QT_BEGIN_NAMESPACE
7 |
8 | void updateXdgIconSystemTheme()
9 | {
10 | XdgIconLoader::instance()->updateSystemTheme();
11 | }
12 |
13 | class PlatformThemePlugin : public QPlatformThemePlugin
14 | {
15 | Q_OBJECT
16 | Q_PLUGIN_METADATA(IID QPlatformThemeFactoryInterface_iid FILE "cutefish-platformtheme.json")
17 |
18 | public:
19 | PlatformThemePlugin(QObject *parent = nullptr)
20 | : QPlatformThemePlugin(parent) {}
21 |
22 | QPlatformTheme *create(const QString &key, const QStringList ¶mList) override
23 | {
24 | Q_UNUSED(key)
25 | Q_UNUSED(paramList)
26 | return new PlatformTheme;
27 | }
28 | };
29 |
30 | QT_END_NAMESPACE
31 |
32 | #include "main.moc"
33 |
--------------------------------------------------------------------------------
/platformtheme/org.kde.StatusNotifierWatcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/platformtheme/platformtheme.cpp:
--------------------------------------------------------------------------------
1 | #include "platformtheme.h"
2 | #include "x11integration.h"
3 | #include "qdbusmenubar_p.h"
4 |
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 |
15 | // Qt Private
16 | #include
17 | #include
18 | #include
19 | #include
20 |
21 | // Qt DBus
22 | #include
23 | #include
24 |
25 | #include
26 |
27 | static const QByteArray s_x11AppMenuServiceNamePropertyName = QByteArrayLiteral("_KDE_NET_WM_APPMENU_SERVICE_NAME");
28 | static const QByteArray s_x11AppMenuObjectPathPropertyName = QByteArrayLiteral("_KDE_NET_WM_APPMENU_OBJECT_PATH");
29 |
30 | static bool checkDBusGlobalMenuAvailable()
31 | {
32 | QDBusConnection connection = QDBusConnection::sessionBus();
33 | QString registrarService = QStringLiteral("com.canonical.AppMenu.Registrar");
34 | return connection.interface()->isServiceRegistered(registrarService);
35 | }
36 |
37 | static bool isDBusGlobalMenuAvailable()
38 | {
39 | static bool dbusGlobalMenuAvailable = checkDBusGlobalMenuAvailable();
40 | return dbusGlobalMenuAvailable;
41 | }
42 |
43 | extern void updateXdgIconSystemTheme();
44 |
45 | void onDarkModeChanged()
46 | {
47 | if (qApp->applicationName() == "systemsettings"
48 | || qApp->applicationName().contains("plasma")
49 | || qApp->applicationName().contains("QtCreator")) {
50 | return;
51 | }
52 |
53 | QStyle *style = QStyleFactory::create("cutefish");
54 | if (style) {
55 | qApp->setStyle(style);
56 | }
57 | }
58 |
59 | PlatformTheme::PlatformTheme()
60 | : m_hints(new HintsSettings)
61 | {
62 | // qApp->setProperty("_hints_settings_object", (quintptr)m_hints);
63 |
64 | if (KWindowSystem::isPlatformX11()) {
65 | m_x11Integration.reset(new X11Integration());
66 | m_x11Integration->init();
67 | }
68 |
69 | connect(m_hints, &HintsSettings::systemFontChanged, this, &PlatformTheme::onFontChanged);
70 | connect(m_hints, &HintsSettings::systemFontPointSizeChanged, this, &PlatformTheme::onFontChanged);
71 | connect(m_hints, &HintsSettings::iconThemeChanged, this, &PlatformTheme::onIconThemeChanged);
72 | connect(m_hints, &HintsSettings::darkModeChanged, &onDarkModeChanged);
73 |
74 | QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, false);
75 | setQtQuickControlsTheme();
76 | }
77 |
78 | PlatformTheme::~PlatformTheme()
79 | {
80 | }
81 |
82 | QVariant PlatformTheme::themeHint(QPlatformTheme::ThemeHint hintType) const
83 | {
84 | QVariant hint = m_hints->hint(hintType);
85 | if (hint.isValid()) {
86 | return hint;
87 | } else {
88 | return QPlatformTheme::themeHint(hintType);
89 | }
90 | }
91 |
92 | const QFont* PlatformTheme::font(Font type) const
93 | {
94 | switch (type) {
95 | case SystemFont:
96 | case MessageBoxFont:
97 | case LabelFont:
98 | case TipLabelFont:
99 | case StatusBarFont:
100 | case PushButtonFont:
101 | case ItemViewFont:
102 | case ListViewFont:
103 | case HeaderViewFont:
104 | case ListBoxFont:
105 | case ComboMenuItemFont:
106 | case ComboLineEditFont: {
107 | const QString &fontName = m_hints->systemFont();
108 | qreal fontSize = m_hints->systemFontPointSize();
109 | static QFont font = QFont(QString());
110 | font.setFamily(fontName);
111 | font.setPointSizeF(fontSize);
112 | return &font;
113 | }
114 | case FixedFont: {
115 | const QString &fontName = m_hints->systemFixedFont();
116 | qreal fontSize = m_hints->systemFontPointSize();
117 | static QFont font = QFont(QString());
118 | font.setFamily(fontName);
119 | font.setPointSizeF(fontSize);
120 | return &font;
121 | }
122 | default: {
123 | const QString &fontName = m_hints->systemFont();
124 | qreal fontSize = m_hints->systemFontPointSize();
125 | static QFont font = QFont(QString());
126 | font.setFamily(fontName);
127 | font.setPointSizeF(fontSize);
128 | return &font;
129 | }
130 | }
131 |
132 | return QPlatformTheme::font(type);
133 | }
134 |
135 | QPlatformMenuBar *PlatformTheme::createPlatformMenuBar() const
136 | {
137 | if (isDBusGlobalMenuAvailable()) {
138 | auto *menu = new QDBusMenuBar();
139 |
140 | QObject::connect(menu, &QDBusMenuBar::windowChanged, menu, [this, menu](QWindow *newWindow, QWindow *oldWindow) {
141 | const QString &serviceName = QDBusConnection::sessionBus().baseService();
142 | const QString &objectPath = menu->objectPath();
143 |
144 | if (m_x11Integration) {
145 | if (oldWindow) {
146 | m_x11Integration->setWindowProperty(oldWindow, s_x11AppMenuServiceNamePropertyName, {});
147 | m_x11Integration->setWindowProperty(oldWindow, s_x11AppMenuObjectPathPropertyName, {});
148 | }
149 |
150 | if (newWindow) {
151 | m_x11Integration->setWindowProperty(newWindow, s_x11AppMenuServiceNamePropertyName, serviceName.toUtf8());
152 | m_x11Integration->setWindowProperty(newWindow, s_x11AppMenuObjectPathPropertyName, objectPath.toUtf8());
153 | }
154 | }
155 |
156 | // if (m_kwaylandIntegration) {
157 | // if (oldWindow) {
158 | // m_kwaylandIntegration->setAppMenu(oldWindow, QString(), QString());
159 | // }
160 | //
161 | // if (newWindow) {
162 | // m_kwaylandIntegration->setAppMenu(newWindow, serviceName, objectPath);
163 | // }
164 | // }
165 | });
166 |
167 | return menu;
168 | }
169 |
170 | return nullptr;
171 | }
172 |
173 | void PlatformTheme::onFontChanged()
174 | {
175 | QFont font;
176 | font.setFamily(m_hints->systemFont());
177 | font.setPointSizeF(m_hints->systemFontPointSize());
178 |
179 | // Change font
180 | if (qobject_cast(QCoreApplication::instance()))
181 | QApplication::setFont(font);
182 | else if (qobject_cast(QCoreApplication::instance()))
183 | QGuiApplication::setFont(font);
184 | }
185 |
186 | void PlatformTheme::onIconThemeChanged()
187 | {
188 | QIconLoader::instance()->updateSystemTheme();
189 | updateXdgIconSystemTheme();
190 |
191 | QEvent update(QEvent::UpdateRequest);
192 | for (QWindow *window : qGuiApp->allWindows()) {
193 | if (window->type() == Qt::Desktop)
194 | continue;
195 |
196 | qApp->sendEvent(window, &update);
197 | }
198 | }
199 |
200 | void PlatformTheme::setQtQuickControlsTheme()
201 | {
202 | //if the user has explicitly set something else, don't meddle
203 | if (!QQuickStyle::name().isEmpty()) {
204 | return;
205 | }
206 |
207 | if (qApp->applicationName() == "systemsettings"
208 | || qApp->applicationName().contains("plasma")) {
209 | QQuickStyle::setStyle("Plasma");
210 | QStyle *style = QStyleFactory::create("Breeze");
211 | qApp->setStyle(style);
212 | return;
213 | }
214 |
215 | QQuickStyle::setStyle(QLatin1String("fish-style"));
216 | }
217 |
--------------------------------------------------------------------------------
/platformtheme/platformtheme.h:
--------------------------------------------------------------------------------
1 | #ifndef PLATFORMTHEME_H
2 | #define PLATFORMTHEME_H
3 |
4 | #include
5 | #include "hintsettings.h"
6 | #include "systemtrayicon.h"
7 |
8 | #include
9 | #include
10 |
11 | class QIconEngine;
12 | class QWindow;
13 | class X11Integration;
14 |
15 | class PlatformTheme : public QObject, public QPlatformTheme
16 | {
17 | Q_OBJECT
18 |
19 | public:
20 | PlatformTheme();
21 | ~PlatformTheme() override;
22 |
23 | QVariant themeHint(ThemeHint hint) const override;
24 | const QFont *font(Font type) const override;
25 |
26 | QPlatformMenuBar *createPlatformMenuBar() const override;
27 |
28 | QPlatformSystemTrayIcon *createPlatformSystemTrayIcon() const override {
29 | auto trayIcon = new SystemTrayIcon;
30 | if (trayIcon->isSystemTrayAvailable())
31 | return trayIcon;
32 | else {
33 | delete trayIcon;
34 | return nullptr;
35 | }
36 | }
37 |
38 | private:
39 | void onFontChanged();
40 | void onIconThemeChanged();
41 | void setQtQuickControlsTheme();
42 |
43 | private:
44 | HintsSettings *m_hints;
45 | QScopedPointer m_x11Integration;
46 | };
47 |
48 | #endif // PLATFORMTHEME_H
49 |
--------------------------------------------------------------------------------
/platformtheme/qdbusmenubar.cpp:
--------------------------------------------------------------------------------
1 | #include "qdbusmenubar_p.h"
2 |
3 |
4 | QT_BEGIN_NAMESPACE
5 |
6 | /* note: do not change these to QStringLiteral;
7 | we are unloaded before QtDBus is done using the strings.
8 | */
9 | #define REGISTRAR_SERVICE QLatin1String("com.canonical.AppMenu.Registrar")
10 | #define REGISTRAR_PATH QLatin1String("/com/canonical/AppMenu/Registrar")
11 |
12 | QDBusMenuBar::QDBusMenuBar()
13 | : QPlatformMenuBar()
14 | , m_menu(new QDBusPlatformMenu())
15 | , m_menuAdaptor(new QDBusMenuAdaptor(m_menu))
16 | {
17 | QDBusMenuItem::registerDBusTypes();
18 | connect(m_menu, &QDBusPlatformMenu::propertiesUpdated,
19 | m_menuAdaptor, &QDBusMenuAdaptor::ItemsPropertiesUpdated);
20 | connect(m_menu, &QDBusPlatformMenu::updated,
21 | m_menuAdaptor, &QDBusMenuAdaptor::LayoutUpdated);
22 |
23 | // This signal is new in Qt 5.8 but distros might have backported it, hence a runtime look-up
24 | if (m_menu->metaObject()->indexOfSignal("popupRequested(int,uint)") != -1) {
25 | connect(m_menu, SIGNAL(popupRequested(int,uint)), m_menuAdaptor, SIGNAL(ItemActivationRequested(int,uint)));
26 | }
27 | }
28 |
29 | QDBusMenuBar::~QDBusMenuBar()
30 | {
31 | unregisterMenuBar();
32 | delete m_menuAdaptor;
33 | delete m_menu;
34 | qDeleteAll(m_menuItems);
35 | }
36 |
37 | QDBusPlatformMenuItem *QDBusMenuBar::menuItemForMenu(QPlatformMenu *menu)
38 | {
39 | if (!menu)
40 | return nullptr;
41 | quintptr tag = menu->tag();
42 | const auto it = m_menuItems.constFind(tag);
43 | if (it != m_menuItems.cend()) {
44 | return *it;
45 | } else {
46 | QDBusPlatformMenuItem *item = new QDBusPlatformMenuItem;
47 | updateMenuItem(item, menu);
48 | m_menuItems.insert(tag, item);
49 | return item;
50 | }
51 | }
52 |
53 | void QDBusMenuBar::updateMenuItem(QDBusPlatformMenuItem *item, QPlatformMenu *menu)
54 | {
55 | const QDBusPlatformMenu *ourMenu = qobject_cast(menu);
56 | item->setText(ourMenu->text());
57 | item->setIcon(ourMenu->icon());
58 | item->setEnabled(ourMenu->isEnabled());
59 | item->setVisible(ourMenu->isVisible());
60 | item->setMenu(menu);
61 | }
62 |
63 | void QDBusMenuBar::insertMenu(QPlatformMenu *menu, QPlatformMenu *before)
64 | {
65 | QDBusPlatformMenuItem *menuItem = menuItemForMenu(menu);
66 | QDBusPlatformMenuItem *beforeItem = menuItemForMenu(before);
67 | m_menu->insertMenuItem(menuItem, beforeItem);
68 | m_menu->emitUpdated();
69 | }
70 |
71 | void QDBusMenuBar::removeMenu(QPlatformMenu *menu)
72 | {
73 | QDBusPlatformMenuItem *menuItem = menuItemForMenu(menu);
74 | m_menu->removeMenuItem(menuItem);
75 | m_menu->emitUpdated();
76 | }
77 |
78 | void QDBusMenuBar::syncMenu(QPlatformMenu *menu)
79 | {
80 | QDBusPlatformMenuItem *menuItem = menuItemForMenu(menu);
81 | updateMenuItem(menuItem, menu);
82 | }
83 |
84 | void QDBusMenuBar::handleReparent(QWindow *newParentWindow)
85 | {
86 | if (newParentWindow == m_window) {
87 | return;
88 | }
89 |
90 | QWindow *oldWindow = m_window;
91 |
92 | unregisterMenuBar();
93 | m_window = newParentWindow;
94 |
95 | if (newParentWindow) {
96 | registerMenuBar();
97 | }
98 |
99 | emit windowChanged(newParentWindow, oldWindow);
100 | }
101 |
102 | QPlatformMenu *QDBusMenuBar::menuForTag(quintptr tag) const
103 | {
104 | QDBusPlatformMenuItem *menuItem = m_menuItems.value(tag);
105 | if (menuItem)
106 | return const_cast(menuItem->menu());
107 | return nullptr;
108 | }
109 |
110 | QPlatformMenu *QDBusMenuBar::createMenu() const
111 | {
112 | return new QDBusPlatformMenu;
113 | }
114 |
115 | void QDBusMenuBar::registerMenuBar()
116 | {
117 | static uint menuBarId = 0;
118 |
119 | if (!m_window) {
120 | qWarning("Cannot register window menu without window");
121 | return;
122 | }
123 |
124 | QDBusConnection connection = QDBusConnection::sessionBus();
125 | m_objectPath = QStringLiteral("/MenuBar/%1").arg(++menuBarId);
126 | if (!connection.registerObject(m_objectPath, m_menu))
127 | return;
128 |
129 | QDBusMenuRegistrarInterface registrar(REGISTRAR_SERVICE, REGISTRAR_PATH, connection, this);
130 | QDBusPendingReply<> r = registrar.RegisterWindow(static_cast(window()->winId()), QDBusObjectPath(m_objectPath));
131 | r.waitForFinished();
132 | if (r.isError()) {
133 | qWarning("Failed to register window menu, reason: %s (\"%s\")",
134 | qUtf8Printable(r.error().name()), qUtf8Printable(r.error().message()));
135 | connection.unregisterObject(m_objectPath);
136 | }
137 | }
138 |
139 | void QDBusMenuBar::unregisterMenuBar()
140 | {
141 | QDBusConnection connection = QDBusConnection::sessionBus();
142 |
143 | if (m_window) {
144 | QDBusMenuRegistrarInterface registrar(REGISTRAR_SERVICE, REGISTRAR_PATH, connection, this);
145 | QDBusPendingReply<> r = registrar.UnregisterWindow(static_cast(window()->winId()));
146 | r.waitForFinished();
147 | if (r.isError())
148 | qWarning("Failed to unregister window menu, reason: %s (\"%s\")",
149 | qUtf8Printable(r.error().name()), qUtf8Printable(r.error().message()));
150 | }
151 |
152 | if (!m_objectPath.isEmpty())
153 | connection.unregisterObject(m_objectPath);
154 | }
155 |
156 | QT_END_NAMESPACE
157 |
--------------------------------------------------------------------------------
/platformtheme/qdbusmenubar_p.h:
--------------------------------------------------------------------------------
1 | /****************************************************************************
2 | **
3 | ** Copyright (C) 2016 Dmitry Shachnev
4 | ** Contact: https://www.qt.io/licensing/
5 | **
6 | ** This file is part of the QtGui module of the Qt Toolkit.
7 | **
8 | ** $QT_BEGIN_LICENSE:LGPL$
9 | ** Commercial License Usage
10 | ** Licensees holding valid commercial Qt licenses may use this file in
11 | ** accordance with the commercial license agreement provided with the
12 | ** Software or, alternatively, in accordance with the terms contained in
13 | ** a written agreement between you and The Qt Company. For licensing terms
14 | ** and conditions see https://www.qt.io/terms-conditions. For further
15 | ** information use the contact form at https://www.qt.io/contact-us.
16 | **
17 | ** GNU Lesser General Public License Usage
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser
19 | ** General Public License version 3 as published by the Free Software
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 | ** packaging of this file. Please review the following information to
22 | ** ensure the GNU Lesser General Public License version 3 requirements
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 | **
25 | ** GNU General Public License Usage
26 | ** Alternatively, this file may be used under the terms of the GNU
27 | ** General Public License version 2.0 or (at your option) the GNU General
28 | ** Public license version 3 or any later version approved by the KDE Free
29 | ** Qt Foundation. The licenses are as published by the Free Software
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 | ** included in the packaging of this file. Please review the following
32 | ** information to ensure the GNU General Public License requirements will
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 | ** https://www.gnu.org/licenses/gpl-3.0.html.
35 | **
36 | ** $QT_END_LICENSE$
37 | **
38 | ****************************************************************************/
39 |
40 | #ifndef QDBUSMENUBAR_P_H
41 | #define QDBUSMENUBAR_P_H
42 |
43 | //
44 | // W A R N I N G
45 | // -------------
46 | //
47 | // This file is not part of the Qt API. It exists purely as an
48 | // implementation detail. This header file may change from version to
49 | // version without notice, or even be removed.
50 | //
51 | // We mean it.
52 | //
53 |
54 | #include
55 | #include
56 | #include
57 |
58 | #include
59 | #include
60 | #include
61 | #include
62 |
63 | QT_BEGIN_NAMESPACE
64 |
65 | class QDBusMenuBar : public QPlatformMenuBar
66 | {
67 | Q_OBJECT
68 |
69 | public:
70 | QDBusMenuBar();
71 | ~QDBusMenuBar() override;
72 |
73 | void insertMenu(QPlatformMenu *menu, QPlatformMenu *before) override;
74 | void removeMenu(QPlatformMenu *menu) override;
75 | void syncMenu(QPlatformMenu *menu) override;
76 | void handleReparent(QWindow *newParentWindow) override;
77 | QPlatformMenu *menuForTag(quintptr tag) const override;
78 | QPlatformMenu *createMenu() const override;
79 |
80 | QWindow *window() const { return m_window; }
81 | QString objectPath() const { return m_objectPath; }
82 |
83 | Q_SIGNALS:
84 | void windowChanged(QWindow *newWindow, QWindow *oldWindow);
85 |
86 | private:
87 | QDBusPlatformMenu *m_menu;
88 | QDBusMenuAdaptor *m_menuAdaptor;
89 | QHash m_menuItems;
90 | QPointer m_window;
91 | QString m_objectPath;
92 |
93 | QDBusPlatformMenuItem *menuItemForMenu(QPlatformMenu *menu);
94 | static void updateMenuItem(QDBusPlatformMenuItem *item, QPlatformMenu *menu);
95 | void registerMenuBar();
96 | void unregisterMenuBar();
97 | };
98 |
99 | QT_END_NAMESPACE
100 |
101 | #endif // QDBUSMENUBAR_P_H
102 |
--------------------------------------------------------------------------------
/platformtheme/statusnotifier/dbustypes.cpp:
--------------------------------------------------------------------------------
1 | #include "dbustypes.h"
2 |
3 | // Marshall the IconPixmap data into a D-Bus argument
4 | QDBusArgument &operator<<(QDBusArgument &argument, const IconPixmap &icon)
5 | {
6 | argument.beginStructure();
7 | argument << icon.width;
8 | argument << icon.height;
9 | argument << icon.bytes;
10 | argument.endStructure();
11 | return argument;
12 | }
13 |
14 | // Retrieve the ImageStruct data from the D-Bus argument
15 | const QDBusArgument &operator>>(const QDBusArgument &argument, IconPixmap &icon)
16 | {
17 | argument.beginStructure();
18 | argument >> icon.width;
19 | argument >> icon.height;
20 | argument >> icon.bytes;
21 | argument.endStructure();
22 | return argument;
23 | }
24 |
25 | // Marshall the ToolTip data into a D-Bus argument
26 | QDBusArgument &operator<<(QDBusArgument &argument, const ToolTip &toolTip)
27 | {
28 | argument.beginStructure();
29 | argument << toolTip.iconName;
30 | argument << toolTip.iconPixmap;
31 | argument << toolTip.title;
32 | argument << toolTip.description;
33 | argument.endStructure();
34 | return argument;
35 | }
36 |
37 | // Retrieve the ToolTip data from the D-Bus argument
38 | const QDBusArgument &operator>>(const QDBusArgument &argument, ToolTip &toolTip)
39 | {
40 | argument.beginStructure();
41 | argument >> toolTip.iconName;
42 | argument >> toolTip.iconPixmap;
43 | argument >> toolTip.title;
44 | argument >> toolTip.description;
45 | argument.endStructure();
46 | return argument;
47 | }
--------------------------------------------------------------------------------
/platformtheme/statusnotifier/dbustypes.h:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #ifndef DBUSTYPES_H
4 | #define DBUSTYPES_H
5 |
6 | struct IconPixmap {
7 | int width;
8 | int height;
9 | QByteArray bytes;
10 | };
11 |
12 | typedef QList IconPixmapList;
13 |
14 | Q_DECLARE_METATYPE(IconPixmap)
15 | Q_DECLARE_METATYPE(IconPixmapList)
16 |
17 | struct ToolTip {
18 | QString iconName;
19 | QList iconPixmap;
20 | QString title;
21 | QString description;
22 | };
23 |
24 | Q_DECLARE_METATYPE(ToolTip)
25 |
26 | QDBusArgument &operator<<(QDBusArgument &argument, const IconPixmap &icon);
27 | const QDBusArgument &operator>>(const QDBusArgument &argument, IconPixmap &icon);
28 |
29 | QDBusArgument &operator<<(QDBusArgument &argument, const ToolTip &toolTip);
30 | const QDBusArgument &operator>>(const QDBusArgument &argument, ToolTip &toolTip);
31 |
32 | #endif // DBUSTYPES_H
--------------------------------------------------------------------------------
/platformtheme/statusnotifier/org.kde.StatusNotifierItem.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/platformtheme/statusnotifier/statusnotifieritem.cpp:
--------------------------------------------------------------------------------
1 | #include "statusnotifieritem.h"
2 | #include "statusnotifieritemadaptor.h"
3 | #include
4 | #include
5 | #include
6 |
7 | int StatusNotifierItem::mServiceCounter = 0;
8 |
9 | StatusNotifierItem::StatusNotifierItem(QString id, QObject *parent)
10 | : QObject(parent),
11 | mAdaptor(new StatusNotifierItemAdaptor(this)),
12 | mService(QString::fromLatin1("org.freedesktop.StatusNotifierItem-%1-%2")
13 | .arg(QCoreApplication::applicationPid())
14 | .arg(++mServiceCounter)),
15 | mId(id),
16 | mTitle(QLatin1String("Test")),
17 | mStatus(QLatin1String("Active")),
18 | mCategory(QLatin1String("ApplicationStatus")),
19 | mMenu(nullptr),
20 | mMenuPath(QLatin1String("/NO_DBUSMENU")),
21 | mMenuExporter(nullptr),
22 | mSessionBus(QDBusConnection::connectToBus(QDBusConnection::SessionBus, mService))
23 | {
24 | // Separate DBus connection to the session bus is created, because QDbus does not provide
25 | // a way to register different objects for different services with the same paths.
26 | // For status notifiers we need different /StatusNotifierItem for each service.
27 |
28 | // register service
29 |
30 | mSessionBus.registerObject(QLatin1String("/StatusNotifierItem"), this);
31 |
32 | registerToHost();
33 |
34 | // monitor the watcher service in case the host restarts
35 | QDBusServiceWatcher *watcher = new QDBusServiceWatcher(QLatin1String("org.kde.StatusNotifierWatcher"),
36 | mSessionBus,
37 | QDBusServiceWatcher::WatchForOwnerChange,
38 | this);
39 | connect(watcher, &QDBusServiceWatcher::serviceOwnerChanged,
40 | this, &StatusNotifierItem::onServiceOwnerChanged);
41 | }
42 |
43 | StatusNotifierItem::~StatusNotifierItem()
44 | {
45 | mSessionBus.unregisterObject(QLatin1String("/StatusNotifierItem"));
46 | QDBusConnection::disconnectFromBus(mService);
47 | }
48 |
49 | void StatusNotifierItem::registerToHost()
50 | {
51 | QDBusInterface interface(QLatin1String("org.kde.StatusNotifierWatcher"),
52 | QLatin1String("/StatusNotifierWatcher"),
53 | QLatin1String("org.kde.StatusNotifierWatcher"),
54 | mSessionBus);
55 | interface.asyncCall(QLatin1String("RegisterStatusNotifierItem"), mSessionBus.baseService());
56 | }
57 |
58 | void StatusNotifierItem::onServiceOwnerChanged(const QString& service, const QString& oldOwner,
59 | const QString& newOwner)
60 | {
61 | Q_UNUSED(service);
62 | Q_UNUSED(oldOwner);
63 |
64 | if (!newOwner.isEmpty())
65 | registerToHost();
66 | }
67 |
68 | void StatusNotifierItem::onMenuDestroyed()
69 | {
70 | mMenu = nullptr;
71 | setMenuPath(QLatin1String("/NO_DBUSMENU"));
72 | mMenuExporter = nullptr; //mMenu is a QObject parent of the mMenuExporter
73 | }
74 |
75 | void StatusNotifierItem::setTitle(const QString &title)
76 | {
77 | if (mTitle == title)
78 | return;
79 |
80 | mTitle = title;
81 | Q_EMIT mAdaptor->NewTitle();
82 | }
83 |
84 | void StatusNotifierItem::setStatus(const QString &status)
85 | {
86 | if (mStatus == status)
87 | return;
88 |
89 | mStatus = status;
90 | Q_EMIT mAdaptor->NewStatus(mStatus);
91 | }
92 |
93 | void StatusNotifierItem::setCategory(const QString &category)
94 | {
95 | if (mCategory == category)
96 | return;
97 |
98 | mCategory = category;
99 | }
100 |
101 | void StatusNotifierItem::setMenuPath(const QString& path)
102 | {
103 | mMenuPath.setPath(path);
104 | }
105 |
106 | void StatusNotifierItem::setIconByName(const QString &name)
107 | {
108 | if (mIconName == name)
109 | return;
110 |
111 | mIconName = name;
112 | Q_EMIT mAdaptor->NewIcon();
113 | }
114 |
115 | void StatusNotifierItem::setIconByPixmap(const QIcon &icon)
116 | {
117 | if (mIconCacheKey == icon.cacheKey())
118 | return;
119 |
120 | mIconCacheKey = icon.cacheKey();
121 | mIcon = iconToPixmapList(icon);
122 | mIconName.clear();
123 | Q_EMIT mAdaptor->NewIcon();
124 | }
125 |
126 | void StatusNotifierItem::setOverlayIconByName(const QString &name)
127 | {
128 | if (mOverlayIconName == name)
129 | return;
130 |
131 | mOverlayIconName = name;
132 | Q_EMIT mAdaptor->NewOverlayIcon();
133 | }
134 |
135 | void StatusNotifierItem::setOverlayIconByPixmap(const QIcon &icon)
136 | {
137 | if (mOverlayIconCacheKey == icon.cacheKey())
138 | return;
139 |
140 | mOverlayIconCacheKey = icon.cacheKey();
141 | mOverlayIcon = iconToPixmapList(icon);
142 | mOverlayIconName.clear();
143 | Q_EMIT mAdaptor->NewOverlayIcon();
144 | }
145 |
146 | void StatusNotifierItem::setAttentionIconByName(const QString &name)
147 | {
148 | if (mAttentionIconName == name)
149 | return;
150 |
151 | mAttentionIconName = name;
152 | Q_EMIT mAdaptor->NewAttentionIcon();
153 | }
154 |
155 | void StatusNotifierItem::setAttentionIconByPixmap(const QIcon &icon)
156 | {
157 | if (mAttentionIconCacheKey == icon.cacheKey())
158 | return;
159 |
160 | mAttentionIconCacheKey = icon.cacheKey();
161 | mAttentionIcon = iconToPixmapList(icon);
162 | mAttentionIconName.clear();
163 | Q_EMIT mAdaptor->NewAttentionIcon();
164 | }
165 |
166 | void StatusNotifierItem::setToolTipTitle(const QString &title)
167 | {
168 | if (mTooltipTitle == title)
169 | return;
170 |
171 | mTooltipTitle = title;
172 | Q_EMIT mAdaptor->NewToolTip();
173 | }
174 |
175 | void StatusNotifierItem::setToolTipSubTitle(const QString &subTitle)
176 | {
177 | if (mTooltipSubtitle == subTitle)
178 | return;
179 |
180 | mTooltipSubtitle = subTitle;
181 | Q_EMIT mAdaptor->NewToolTip();
182 | }
183 |
184 | void StatusNotifierItem::setToolTipIconByName(const QString &name)
185 | {
186 | if (mTooltipIconName == name)
187 | return;
188 |
189 | mTooltipIconName = name;
190 | Q_EMIT mAdaptor->NewToolTip();
191 | }
192 |
193 | void StatusNotifierItem::setToolTipIconByPixmap(const QIcon &icon)
194 | {
195 | if (mTooltipIconCacheKey == icon.cacheKey())
196 | return;
197 |
198 | mTooltipIconCacheKey = icon.cacheKey();
199 | mTooltipIcon = iconToPixmapList(icon);
200 | mTooltipIconName.clear();
201 | Q_EMIT mAdaptor->NewToolTip();
202 | }
203 |
204 | void StatusNotifierItem::setContextMenu(QMenu* menu)
205 | {
206 | if (mMenu == menu)
207 | return;
208 |
209 | if (nullptr != mMenu)
210 | {
211 | disconnect(mMenu, &QObject::destroyed, this, &StatusNotifierItem::onMenuDestroyed);
212 | }
213 | mMenu = menu;
214 |
215 | if (nullptr != mMenu)
216 | setMenuPath(QLatin1String("/MenuBar"));
217 | else
218 | setMenuPath(QLatin1String("/NO_DBUSMENU"));
219 |
220 | //Note: we need to destroy menu exporter before creating new one -> to free the DBus object path for new menu
221 | delete mMenuExporter;
222 | if (nullptr != mMenu)
223 | {
224 | connect(mMenu, &QObject::destroyed, this, &StatusNotifierItem::onMenuDestroyed);
225 | mMenuExporter = new DBusMenuExporter{this->menu().path(), mMenu, mSessionBus};
226 | }
227 | }
228 |
229 | void StatusNotifierItem::Activate(int x, int y)
230 | {
231 | if (mStatus == QLatin1String("NeedsAttention"))
232 | mStatus = QLatin1String("Active");
233 |
234 | Q_EMIT activateRequested(QPoint(x, y));
235 | }
236 |
237 | void StatusNotifierItem::SecondaryActivate(int x, int y)
238 | {
239 | if (mStatus == QLatin1String("NeedsAttention"))
240 | mStatus = QLatin1String("Active");
241 |
242 | Q_EMIT secondaryActivateRequested(QPoint(x, y));
243 | }
244 |
245 | void StatusNotifierItem::ContextMenu(int x, int y)
246 | {
247 | if (mMenu)
248 | {
249 | if (mMenu->isVisible())
250 | mMenu->popup(QPoint(x, y));
251 | else
252 | mMenu->hide();
253 | }
254 | }
255 |
256 | void StatusNotifierItem::Scroll(int delta, const QString &orientation)
257 | {
258 | Qt::Orientation orient = Qt::Vertical;
259 | if (orientation.toLower() == QLatin1String("horizontal"))
260 | orient = Qt::Horizontal;
261 |
262 | Q_EMIT scrollRequested(delta, orient);
263 | }
264 |
265 | void StatusNotifierItem::showMessage(const QString& title, const QString& msg,
266 | const QString& iconName, int secs)
267 | {
268 | QDBusInterface interface(QLatin1String("org.freedesktop.Notifications"), QLatin1String("/org/freedesktop/Notifications"),
269 | QLatin1String("org.freedesktop.Notifications"), mSessionBus);
270 | interface.call(QLatin1String("Notify"), mTitle, (uint) 0, iconName, title,
271 | msg, QStringList(), QVariantMap(), secs);
272 | }
273 |
274 | IconPixmapList StatusNotifierItem::iconToPixmapList(const QIcon& icon)
275 | {
276 | IconPixmapList pixmapList;
277 |
278 | // long live KDE!
279 | const QList sizes = icon.availableSizes();
280 | for (const QSize &size : sizes)
281 | {
282 | QImage image = icon.pixmap(size).toImage();
283 |
284 | IconPixmap pix;
285 | pix.height = image.height();
286 | pix.width = image.width();
287 |
288 | if (image.format() != QImage::Format_ARGB32)
289 | image = image.convertToFormat(QImage::Format_ARGB32);
290 |
291 | pix.bytes = QByteArray((char *) image.bits(),
292 | #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
293 | image.byteCount());
294 | #else
295 | image.sizeInBytes());
296 | #endif
297 |
298 | // swap to network byte order if we are little endian
299 | if (QSysInfo::ByteOrder == QSysInfo::LittleEndian)
300 | {
301 | quint32 *uintBuf = (quint32 *) pix.bytes.data();
302 | for (uint i = 0; i < pix.bytes.size() / sizeof(quint32); ++i)
303 | {
304 | *uintBuf = qToBigEndian(*uintBuf);
305 | ++uintBuf;
306 | }
307 | }
308 |
309 | pixmapList.append(pix);
310 | }
311 |
312 | return pixmapList;
313 | }
--------------------------------------------------------------------------------
/platformtheme/statusnotifier/statusnotifieritem.h:
--------------------------------------------------------------------------------
1 | #ifndef STATUS_NOTIFIER_ITEM_H
2 | #define STATUS_NOTIFIER_ITEM_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | #include "dbustypes.h"
10 |
11 | class StatusNotifierItemAdaptor;
12 | class DBusMenuExporter;
13 |
14 | class StatusNotifierItem : public QObject
15 | {
16 | Q_OBJECT
17 |
18 | Q_PROPERTY(QString Category READ category)
19 | Q_PROPERTY(QString Title READ title)
20 | Q_PROPERTY(QString Id READ id)
21 | Q_PROPERTY(QString Status READ status)
22 | Q_PROPERTY(QDBusObjectPath Menu READ menu)
23 |
24 | Q_PROPERTY(QString IconName READ iconName)
25 | Q_PROPERTY(IconPixmapList IconPixmap READ iconPixmap)
26 |
27 | Q_PROPERTY(QString OverlayIconName READ overlayIconName)
28 | Q_PROPERTY(IconPixmapList OverlayIconPixmap READ overlayIconPixmap)
29 |
30 | Q_PROPERTY(QString AttentionIconName READ attentionIconName)
31 | Q_PROPERTY(IconPixmapList AttentionIconPixmap READ attentionIconPixmap)
32 |
33 | Q_PROPERTY(ToolTip ToolTip READ toolTip)
34 |
35 | public:
36 | StatusNotifierItem(QString id, QObject *parent = nullptr);
37 | ~StatusNotifierItem() override;
38 |
39 | QString id() const
40 | { return mId; }
41 |
42 | QString title() const
43 | { return mTitle; }
44 | void setTitle(const QString &title);
45 |
46 | QString status() const
47 | { return mStatus; }
48 | void setStatus(const QString &status);
49 |
50 | QString category() const
51 | { return mCategory; }
52 | void setCategory(const QString &category);
53 |
54 | QDBusObjectPath menu() const
55 | { return mMenuPath; }
56 | void setMenuPath(const QString &path);
57 |
58 | QString iconName() const
59 | { return mIconName; }
60 | void setIconByName(const QString &name);
61 |
62 | IconPixmapList iconPixmap() const
63 | { return mIcon; }
64 | void setIconByPixmap(const QIcon &icon);
65 |
66 | QString overlayIconName() const
67 | { return mOverlayIconName; }
68 | void setOverlayIconByName(const QString &name);
69 |
70 | IconPixmapList overlayIconPixmap() const
71 | { return mOverlayIcon; }
72 | void setOverlayIconByPixmap(const QIcon &icon);
73 |
74 | QString attentionIconName() const
75 | { return mAttentionIconName; }
76 | void setAttentionIconByName(const QString &name);
77 |
78 | IconPixmapList attentionIconPixmap() const
79 | { return mAttentionIcon; }
80 | void setAttentionIconByPixmap(const QIcon &icon);
81 |
82 | QString toolTipTitle() const
83 | { return mTooltipTitle; }
84 | void setToolTipTitle(const QString &title);
85 |
86 | QString toolTipSubTitle() const
87 | { return mTooltipSubtitle; }
88 | void setToolTipSubTitle(const QString &subTitle);
89 |
90 | QString toolTipIconName() const
91 | { return mTooltipIconName; }
92 | void setToolTipIconByName(const QString &name);
93 |
94 | IconPixmapList toolTipIconPixmap() const
95 | { return mTooltipIcon; }
96 | void setToolTipIconByPixmap(const QIcon &icon);
97 |
98 | ToolTip toolTip() const
99 | {
100 | ToolTip tt;
101 | tt.title = mTooltipTitle;
102 | tt.description = mTooltipSubtitle;
103 | tt.iconName = mTooltipIconName;
104 | tt.iconPixmap = mTooltipIcon;
105 | return tt;
106 | }
107 |
108 | /*!
109 | * \Note: we don't take ownership for the \param menu
110 | */
111 | void setContextMenu(QMenu *menu);
112 |
113 | public Q_SLOTS:
114 | void Activate(int x, int y);
115 | void SecondaryActivate(int x, int y);
116 | void ContextMenu(int x, int y);
117 | void Scroll(int delta, const QString &orientation);
118 |
119 | void showMessage(const QString &title, const QString &msg, const QString &iconName, int secs);
120 |
121 | private:
122 | void registerToHost();
123 | IconPixmapList iconToPixmapList(const QIcon &icon);
124 |
125 | private Q_SLOTS:
126 | void onServiceOwnerChanged(const QString &service, const QString &oldOwner,
127 | const QString &newOwner);
128 | void onMenuDestroyed();
129 |
130 | Q_SIGNALS:
131 | void activateRequested(const QPoint &pos);
132 | void secondaryActivateRequested(const QPoint &pos);
133 | void scrollRequested(int delta, Qt::Orientation orientation);
134 |
135 | private:
136 | StatusNotifierItemAdaptor *mAdaptor;
137 |
138 | QString mService;
139 | QString mId;
140 | QString mTitle;
141 | QString mStatus;
142 | QString mCategory;
143 |
144 | // icons
145 | QString mIconName, mOverlayIconName, mAttentionIconName;
146 | IconPixmapList mIcon, mOverlayIcon, mAttentionIcon;
147 | qint64 mIconCacheKey, mOverlayIconCacheKey, mAttentionIconCacheKey;
148 |
149 | // tooltip
150 | QString mTooltipTitle, mTooltipSubtitle, mTooltipIconName;
151 | IconPixmapList mTooltipIcon;
152 | qint64 mTooltipIconCacheKey;
153 |
154 | // menu
155 | QMenu *mMenu;
156 | QDBusObjectPath mMenuPath;
157 | DBusMenuExporter *mMenuExporter;
158 | QDBusConnection mSessionBus;
159 |
160 | static int mServiceCounter;
161 | };
162 |
163 | #endif
--------------------------------------------------------------------------------
/platformtheme/systemtrayicon.cpp:
--------------------------------------------------------------------------------
1 | #include "systemtrayicon.h"
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | SystemTrayMenu::SystemTrayMenu()
11 | : QPlatformMenu(),
12 | m_tag(0),
13 | m_menu(new QMenu())
14 | {
15 | connect(m_menu.data(), &QMenu::aboutToShow, this, &QPlatformMenu::aboutToShow);
16 | connect(m_menu.data(), &QMenu::aboutToHide, this, &QPlatformMenu::aboutToHide);
17 | }
18 |
19 | SystemTrayMenu::~SystemTrayMenu()
20 | {
21 | if (m_menu)
22 | m_menu->deleteLater();
23 | }
24 |
25 | QPlatformMenuItem *SystemTrayMenu::createMenuItem() const
26 | {
27 | return new SystemTrayMenuItem();
28 | }
29 |
30 | void SystemTrayMenu::insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before)
31 | {
32 | if (SystemTrayMenuItem *ours = qobject_cast(menuItem))
33 | {
34 | bool inserted = false;
35 |
36 | if (SystemTrayMenuItem *oursBefore = qobject_cast(before))
37 | {
38 | for (auto it = m_items.begin(); it != m_items.end(); ++it)
39 | {
40 | if (*it == oursBefore)
41 | {
42 | m_items.insert(it, ours);
43 | if (m_menu)
44 | m_menu->insertAction(oursBefore->action(), ours->action());
45 |
46 | inserted = true;
47 | break;
48 | }
49 | }
50 | }
51 |
52 | if (!inserted)
53 | {
54 | m_items.append(ours);
55 | if (m_menu)
56 | m_menu->addAction(ours->action());
57 | }
58 | }
59 | }
60 |
61 | QPlatformMenuItem *SystemTrayMenu::menuItemAt(int position) const
62 | {
63 | if (position < m_items.size())
64 | return m_items.at(position);
65 |
66 | return nullptr;
67 | }
68 |
69 | QPlatformMenuItem *SystemTrayMenu::menuItemForTag(quintptr tag) const
70 | {
71 | auto it = std::find_if(m_items.constBegin(), m_items.constEnd(), [tag] (SystemTrayMenuItem *item)
72 | {
73 | return item->tag() == tag;
74 | });
75 |
76 | if (it != m_items.constEnd())
77 | return *it;
78 |
79 | return nullptr;
80 | }
81 |
82 | void SystemTrayMenu::removeMenuItem(QPlatformMenuItem *menuItem)
83 | {
84 | if (SystemTrayMenuItem *ours = qobject_cast(menuItem))
85 | {
86 | m_items.removeOne(ours);
87 | if (ours->action() && m_menu)
88 | m_menu->removeAction(ours->action());
89 | }
90 | }
91 |
92 | void SystemTrayMenu::setEnabled(bool enabled)
93 | {
94 | if (!m_menu)
95 | return;
96 |
97 | m_menu->setEnabled(enabled);
98 | }
99 |
100 | void SystemTrayMenu::setIcon(const QIcon &icon)
101 | {
102 | if (!m_menu)
103 | return;
104 |
105 | m_menu->setIcon(icon);
106 | }
107 |
108 | void SystemTrayMenu::setTag(quintptr tag)
109 | {
110 | m_tag = tag;
111 | }
112 |
113 | void SystemTrayMenu::setText(const QString &text)
114 | {
115 | if (!m_menu)
116 | return;
117 |
118 | m_menu->setTitle(text);
119 | }
120 |
121 | void SystemTrayMenu::setVisible(bool visible)
122 | {
123 | if (!m_menu)
124 | return;
125 |
126 | m_menu->setVisible(visible);
127 | }
128 |
129 | void SystemTrayMenu::syncMenuItem(QPlatformMenuItem *)
130 | {
131 | // Nothing to do
132 | }
133 |
134 | void SystemTrayMenu::syncSeparatorsCollapsible(bool enable)
135 | {
136 | if (!m_menu)
137 | return;
138 |
139 | m_menu->setSeparatorsCollapsible(enable);
140 | }
141 |
142 | quintptr SystemTrayMenu::tag() const
143 | {
144 | return m_tag;
145 | }
146 |
147 | QMenu *SystemTrayMenu::menu() const
148 | {
149 | return m_menu.data();
150 | }
151 |
152 | SystemTrayMenuItem::SystemTrayMenuItem()
153 | : QPlatformMenuItem(),
154 | m_tag(0),
155 | m_action(new QAction(this))
156 | {
157 | connect(m_action, &QAction::triggered, this, &QPlatformMenuItem::activated);
158 | connect(m_action, &QAction::hovered, this, &QPlatformMenuItem::hovered);
159 | }
160 |
161 | SystemTrayMenuItem::~SystemTrayMenuItem()
162 | {
163 | }
164 |
165 | void SystemTrayMenuItem::setCheckable(bool checkable)
166 | {
167 | m_action->setCheckable(checkable);
168 | }
169 |
170 | void SystemTrayMenuItem::setChecked(bool isChecked)
171 | {
172 | m_action->setChecked(isChecked);
173 | }
174 |
175 | void SystemTrayMenuItem::setEnabled(bool enabled)
176 | {
177 | m_action->setEnabled(enabled);
178 | }
179 |
180 | void SystemTrayMenuItem::setFont(const QFont &font)
181 | {
182 | m_action->setFont(font);
183 | }
184 |
185 | void SystemTrayMenuItem::setIcon(const QIcon &icon)
186 | {
187 | m_action->setIcon(icon);
188 | }
189 |
190 | void SystemTrayMenuItem::setIsSeparator(bool isSeparator)
191 | {
192 | m_action->setSeparator(isSeparator);
193 | }
194 |
195 | void SystemTrayMenuItem::setMenu(QPlatformMenu *menu)
196 | {
197 | if (SystemTrayMenu *ourMenu = qobject_cast(menu))
198 | m_action->setMenu(ourMenu->menu());
199 | }
200 |
201 | void SystemTrayMenuItem::setRole(QPlatformMenuItem::MenuRole)
202 | {
203 | }
204 |
205 | void SystemTrayMenuItem::setShortcut(const QKeySequence &shortcut)
206 | {
207 | m_action->setShortcut(shortcut);
208 | }
209 |
210 | void SystemTrayMenuItem::setTag(quintptr tag)
211 | {
212 | m_tag = tag;
213 | }
214 |
215 | void SystemTrayMenuItem::setText(const QString &text)
216 | {
217 | m_action->setText(text);
218 | }
219 |
220 | void SystemTrayMenuItem::setVisible(bool isVisible)
221 | {
222 | m_action->setVisible(isVisible);
223 | }
224 |
225 | void SystemTrayMenuItem::setIconSize(int)
226 | {
227 | }
228 |
229 | quintptr SystemTrayMenuItem::tag() const
230 | {
231 | return m_tag;
232 | }
233 |
234 | QAction *SystemTrayMenuItem::action() const
235 | {
236 | return m_action;
237 | }
238 |
239 | SystemTrayIcon::SystemTrayIcon()
240 | : QPlatformSystemTrayIcon(),
241 | mSni(nullptr)
242 | {
243 | // register types
244 | qDBusRegisterMetaType();
245 | qDBusRegisterMetaType();
246 | qDBusRegisterMetaType();
247 | }
248 |
249 | SystemTrayIcon::~SystemTrayIcon()
250 | {
251 | }
252 |
253 | void SystemTrayIcon::init()
254 | {
255 | if (!mSni)
256 | {
257 | mSni = new StatusNotifierItem(QString::number(QCoreApplication::applicationPid()), this);
258 | mSni->setTitle(QApplication::applicationDisplayName());
259 |
260 | // default menu
261 | QPlatformMenu *menu = createMenu();
262 | menu->setParent(mSni);
263 | QPlatformMenuItem *menuItem = menu->createMenuItem();
264 | menuItem->setParent(menu);
265 | menuItem->setText(tr("Quit"));
266 | menuItem->setIcon(QIcon::fromTheme(QLatin1String("application-exit")));
267 | connect(menuItem, &QPlatformMenuItem::activated, qApp, &QApplication::quit);
268 | menu->insertMenuItem(menuItem, nullptr);
269 | updateMenu(menu);
270 |
271 | connect(mSni, &StatusNotifierItem::activateRequested, [this](const QPoint &)
272 | {
273 | Q_EMIT activated(QPlatformSystemTrayIcon::Trigger);
274 | });
275 |
276 | connect(mSni, &StatusNotifierItem::secondaryActivateRequested, [this](const QPoint &)
277 | {
278 | Q_EMIT activated(QPlatformSystemTrayIcon::MiddleClick);
279 | });
280 | }
281 | }
282 |
283 | void SystemTrayIcon::cleanup()
284 | {
285 | delete mSni;
286 | mSni = nullptr;
287 | }
288 |
289 | void SystemTrayIcon::updateIcon(const QIcon &icon)
290 | {
291 | if (!mSni)
292 | return;
293 |
294 | if (icon.name().isEmpty())
295 | {
296 | mSni->setIconByPixmap(icon);
297 | mSni->setToolTipIconByPixmap(icon);
298 | }
299 | else
300 | {
301 | mSni->setIconByName(icon.name());
302 | mSni->setToolTipIconByName(icon.name());
303 | }
304 | }
305 |
306 | void SystemTrayIcon::updateToolTip(const QString &tooltip)
307 | {
308 | if (!mSni)
309 | return;
310 |
311 | mSni->setToolTipTitle(tooltip);
312 | }
313 |
314 | void SystemTrayIcon::updateMenu(QPlatformMenu *menu)
315 | {
316 | if (!mSni)
317 | return;
318 |
319 | if (SystemTrayMenu *ourMenu = qobject_cast(menu))
320 | mSni->setContextMenu(ourMenu->menu());
321 | }
322 |
323 | QPlatformMenu *SystemTrayIcon::createMenu() const
324 | {
325 | return new SystemTrayMenu();
326 | }
327 |
328 | QRect SystemTrayIcon::geometry() const
329 | {
330 | // StatusNotifierItem doesn't provide the geometry
331 | return {};
332 | }
333 |
334 | void SystemTrayIcon::showMessage(const QString &title, const QString &msg,
335 | const QIcon &icon, MessageIcon, int secs)
336 | {
337 | if (!mSni)
338 | return;
339 |
340 | mSni->showMessage(title, msg, icon.name(), secs);
341 | }
342 |
343 | bool SystemTrayIcon::isSystemTrayAvailable() const
344 | {
345 | QDBusInterface systrayHost(QLatin1String("org.kde.StatusNotifierWatcher"),
346 | QLatin1String("/StatusNotifierWatcher"),
347 | QLatin1String("org.kde.StatusNotifierWatcher"));
348 |
349 | return systrayHost.isValid() && systrayHost.property("IsStatusNotifierHostRegistered").toBool();
350 | }
351 |
352 | bool SystemTrayIcon::supportsMessages() const
353 | {
354 | return true;
355 | }
--------------------------------------------------------------------------------
/platformtheme/systemtrayicon.h:
--------------------------------------------------------------------------------
1 | /* BEGIN_COMMON_COPYRIGHT_HEADER
2 | * (c)LGPL2+
3 | *
4 | * LXQt - a lightweight, Qt based, desktop toolset
5 | * https://lxqt.org/
6 | *
7 | * Copyright: 2015 LXQt team
8 | * Authors:
9 | * Paulo Lieuthier
10 | *
11 | * This program or library is free software; you can redistribute it
12 | * and/or modify it under the terms of the GNU Lesser General Public
13 | * License as published by the Free Software Foundation; either
14 | * version 2.1 of the License, or (at your option) any later version.
15 | *
16 | * This library is distributed in the hope that it will be useful,
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 | * Lesser General Public License for more details.
20 | *
21 | * You should have received a copy of the GNU Lesser General
22 | * Public License along with this library; if not, write to the
23 | * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24 | * Boston, MA 02110-1301 USA
25 | *
26 | * END_COMMON_COPYRIGHT_HEADER */
27 |
28 | #ifndef SYSTEMTRAYICON_H
29 | #define SYSTEMTRAYICON_H
30 |
31 | #include
32 | #include
33 |
34 | #include "statusnotifier/statusnotifieritem.h"
35 |
36 | class SystemTrayMenuItem;
37 | class QAction;
38 | class QMenu;
39 |
40 | class SystemTrayMenu : public QPlatformMenu
41 | {
42 | Q_OBJECT
43 | public:
44 | SystemTrayMenu();
45 | ~SystemTrayMenu() Q_DECL_OVERRIDE;
46 | void insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before) Q_DECL_OVERRIDE;
47 | QPlatformMenuItem *menuItemAt(int position) const Q_DECL_OVERRIDE;
48 | QPlatformMenuItem *menuItemForTag(quintptr tag) const Q_DECL_OVERRIDE;
49 | void removeMenuItem(QPlatformMenuItem *menuItem) Q_DECL_OVERRIDE;
50 | void setEnabled(bool enabled) Q_DECL_OVERRIDE;
51 | void setIcon(const QIcon &icon) Q_DECL_OVERRIDE;
52 | void setTag(quintptr tag) Q_DECL_OVERRIDE;
53 | void setText(const QString &text) Q_DECL_OVERRIDE;
54 | void setVisible(bool visible) Q_DECL_OVERRIDE;
55 | void syncMenuItem(QPlatformMenuItem *menuItem) Q_DECL_OVERRIDE;
56 | void syncSeparatorsCollapsible(bool enable) Q_DECL_OVERRIDE;
57 | quintptr tag() const Q_DECL_OVERRIDE;
58 | QPlatformMenuItem *createMenuItem() const Q_DECL_OVERRIDE;
59 |
60 | QMenu *menu() const;
61 |
62 | private:
63 | quintptr m_tag;
64 | QPointer m_menu;
65 | QList m_items;
66 | };
67 |
68 | class SystemTrayMenuItem : public QPlatformMenuItem
69 | {
70 | Q_OBJECT
71 | public:
72 | SystemTrayMenuItem();
73 | ~SystemTrayMenuItem() Q_DECL_OVERRIDE;
74 | void setCheckable(bool checkable) Q_DECL_OVERRIDE;
75 | void setChecked(bool isChecked) Q_DECL_OVERRIDE;
76 | void setEnabled(bool enabled) Q_DECL_OVERRIDE;
77 | void setFont(const QFont &font) Q_DECL_OVERRIDE;
78 | void setIcon(const QIcon &icon) Q_DECL_OVERRIDE;
79 | void setIsSeparator(bool isSeparator) Q_DECL_OVERRIDE;
80 | void setMenu(QPlatformMenu *menu) Q_DECL_OVERRIDE;
81 | void setRole(MenuRole role) Q_DECL_OVERRIDE;
82 | void setShortcut(const QKeySequence &shortcut) Q_DECL_OVERRIDE;
83 | void setTag(quintptr tag) Q_DECL_OVERRIDE;
84 | void setText(const QString &text) Q_DECL_OVERRIDE;
85 | void setVisible(bool isVisible) Q_DECL_OVERRIDE;
86 | quintptr tag() const Q_DECL_OVERRIDE;
87 | void setIconSize(int size)
88 | #if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
89 | Q_DECL_OVERRIDE
90 | #endif
91 | ;
92 |
93 | QAction *action() const;
94 |
95 | private:
96 | quintptr m_tag;
97 | QAction *m_action;
98 | };
99 |
100 | class SystemTrayIcon : public QPlatformSystemTrayIcon
101 | {
102 | public:
103 | SystemTrayIcon();
104 | ~SystemTrayIcon() Q_DECL_OVERRIDE;
105 |
106 | void init() Q_DECL_OVERRIDE;
107 | void cleanup() Q_DECL_OVERRIDE;
108 | void updateIcon(const QIcon &icon) Q_DECL_OVERRIDE;
109 | void updateToolTip(const QString &tooltip) Q_DECL_OVERRIDE;
110 | void updateMenu(QPlatformMenu *menu) Q_DECL_OVERRIDE;
111 | QRect geometry() const Q_DECL_OVERRIDE;
112 | void showMessage(const QString &title, const QString &msg,
113 | const QIcon &icon, MessageIcon iconType, int secs) Q_DECL_OVERRIDE;
114 |
115 | bool isSystemTrayAvailable() const Q_DECL_OVERRIDE;
116 | bool supportsMessages() const Q_DECL_OVERRIDE;
117 |
118 | QPlatformMenu *createMenu() const Q_DECL_OVERRIDE;
119 |
120 | private:
121 | StatusNotifierItem *mSni;
122 | };
123 |
124 | #endif
--------------------------------------------------------------------------------
/platformtheme/x11integration.cpp:
--------------------------------------------------------------------------------
1 | /* This file is part of the KDE libraries
2 | * Copyright 2015 Martin Gräßlin
3 | * Copyright 2016 Marco Martin
4 | *
5 | * This library is free software; you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation; either version 2 of the License or ( at
8 | * your option ) version 3 or, at the discretion of KDE e.V. ( which shall
9 | * act as a proxy as in section 14 of the GPLv3 ), any later version.
10 | *
11 | * This library is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Library General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this library; see the file COPYING.LIB. If not, write to
18 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 | * Boston, MA 02110-1301, USA.
20 | */
21 | #include "x11integration.h"
22 |
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 |
33 | #include
34 | #include
35 |
36 | #include
37 |
38 | static const char s_schemePropertyName[] = "KDE_COLOR_SCHEME_PATH";
39 | static const QByteArray s_blurBehindPropertyName = QByteArrayLiteral("ENABLE_BLUR_BEHIND_HINT");
40 | static const QByteArray s_blurRegionPropertyName = QByteArrayLiteral("BLUR_REGION");
41 |
42 | X11Integration::X11Integration()
43 | : QObject()
44 | {
45 | }
46 |
47 | X11Integration::~X11Integration() = default;
48 |
49 | void X11Integration::init()
50 | {
51 | QCoreApplication::instance()->installEventFilter(this);
52 | }
53 |
54 | bool X11Integration::eventFilter(QObject *watched, QEvent *event)
55 | {
56 | //the drag and drop window should NOT be a tooltip
57 | //https://bugreports.qt.io/browse/QTBUG-52560
58 | if (event->type() == QEvent::Show && watched->inherits("QShapedPixmapWindow")) {
59 | //static cast should be safe there
60 | QWindow *w = static_cast(watched);
61 | NETWinInfo info(QX11Info::connection(), w->winId(), QX11Info::appRootWindow(), NET::WMWindowType, NET::Properties2());
62 | info.setWindowType(NET::DNDIcon);
63 | // TODO: does this flash the xcb connection?
64 | }
65 |
66 | // if (event->type() == QEvent::PlatformSurface) {
67 | // if (QWindow *w = qobject_cast(watched)) {
68 | // QPlatformSurfaceEvent *pe = static_cast(event);
69 | // if (!w->flags().testFlag(Qt::ForeignWindow)) {
70 | // if (pe->surfaceEventType() == QPlatformSurfaceEvent::SurfaceCreated) {
71 | // const auto blurBehindProperty = w->property(s_blurBehindPropertyName.constData());
72 | // if (blurBehindProperty.isValid()) {
73 | // KWindowEffects::enableBlurBehind(w->winId(), blurBehindProperty.toBool());
74 | // }
75 | // installDesktopFileName(w);
76 | // }
77 | // }
78 | // }
79 | // }
80 |
81 | // if (event->type() == QEvent::ApplicationPaletteChange) {
82 | // const auto topLevelWindows = QGuiApplication::topLevelWindows();
83 | // for (QWindow *w : topLevelWindows) {
84 | // installColorScheme(w);
85 | // }
86 | // }
87 |
88 | return false;
89 | }
90 |
91 | void X11Integration::installDesktopFileName(QWindow *w)
92 | {
93 | if (!w->isTopLevel()) {
94 | return;
95 | }
96 |
97 | QString desktopFileName = QGuiApplication::desktopFileName();
98 | if (desktopFileName.isEmpty()) {
99 | return;
100 | }
101 | // handle apps which set the desktopFileName property with filename suffix,
102 | // due to unclear API dox (https://bugreports.qt.io/browse/QTBUG-75521)
103 | if (desktopFileName.endsWith(QLatin1String(".desktop"))) {
104 | desktopFileName.chop(8);
105 | }
106 | NETWinInfo info(QX11Info::connection(), w->winId(), QX11Info::appRootWindow(), NET::Properties(), NET::Properties2());
107 | info.setDesktopFileName(desktopFileName.toUtf8().constData());
108 | }
109 |
110 | void X11Integration::setWindowProperty(QWindow *window, const QByteArray &name, const QByteArray &value)
111 | {
112 | auto *c = QX11Info::connection();
113 |
114 | xcb_atom_t atom;
115 | auto it = m_atoms.find(name);
116 | if (it == m_atoms.end()) {
117 | const xcb_intern_atom_cookie_t cookie = xcb_intern_atom(c, false, name.length(), name.constData());
118 | QScopedPointer reply(xcb_intern_atom_reply(c, cookie, nullptr));
119 | if (!reply.isNull()) {
120 | atom = reply->atom;
121 | m_atoms[name] = atom;
122 | } else {
123 | return;
124 | }
125 | } else {
126 | atom = *it;
127 | }
128 |
129 | if (value.isEmpty()) {
130 | xcb_delete_property(c, window->winId(), atom);
131 | } else {
132 | xcb_change_property(c, XCB_PROP_MODE_REPLACE, window->winId(), atom, XCB_ATOM_STRING,
133 | 8, value.length(), value.constData());
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/platformtheme/x11integration.h:
--------------------------------------------------------------------------------
1 | /* This file is part of the KDE libraries
2 | * Copyright 2015 Martin Gräßlin
3 | * Copyright 2016 Marco Martin
4 | *
5 | * This library is free software; you can redistribute it and/or modify
6 | * it under the terms of the GNU Lesser General Public License as published by
7 | * the Free Software Foundation; either version 2 of the License or ( at
8 | * your option ) version 3 or, at the discretion of KDE e.V. ( which shall
9 | * act as a proxy as in section 14 of the GPLv3 ), any later version.
10 | *
11 | * This library is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Library General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this library; see the file COPYING.LIB. If not, write to
18 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 | * Boston, MA 02110-1301, USA.
20 | */
21 | #ifndef X11INTEGRATION_H
22 | #define X11INTEGRATION_H
23 |
24 | #include
25 | #include
26 | #include
27 |
28 | class QWindow;
29 |
30 | class X11Integration : public QObject
31 | {
32 | Q_OBJECT
33 | public:
34 | explicit X11Integration();
35 | ~X11Integration() override;
36 | void init();
37 |
38 | void setWindowProperty(QWindow *window, const QByteArray &name, const QByteArray &value);
39 |
40 | bool eventFilter(QObject *watched, QEvent *event) override;
41 |
42 | private:
43 | void installDesktopFileName(QWindow *w);
44 | QHash m_atoms;
45 | };
46 |
47 | #endif
48 |
--------------------------------------------------------------------------------
/widgetstyle/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.5)
2 | project(cutefishstyle)
3 |
4 | set(TARGET cutefishstyle)
5 | set(CMAKE_CXX_STANDARD 17)
6 | set(CMAKE_AUTOMOC ON)
7 | set(CMAKE_INCLUDE_CURRENT_DIR ON)
8 |
9 | find_package(ECM REQUIRED NO_MODULE)
10 | set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
11 |
12 | set(QT Core Gui Widgets DBus X11Extras)
13 | find_package(Qt5 REQUIRED ${QT})
14 | find_package(PkgConfig REQUIRED)
15 | find_package(KF5 REQUIRED WindowSystem)
16 |
17 | include(ECMQueryQmake)
18 |
19 | set (SRCS
20 | blurhelper.cpp
21 | blurhelper.h
22 | pstyleplugin.cpp
23 | pstyleplugin.h
24 | basestyle.h
25 | basestyle.cpp
26 | phantomcolor.h
27 | phantomcolor.cpp
28 | shadowhelper.h
29 | shadowhelper.cpp
30 | tileset.h
31 | tileset.cpp
32 | boxshadowrenderer.h
33 | boxshadowrenderer.cpp
34 | )
35 |
36 | add_library(${TARGET} MODULE ${SRCS})
37 | target_link_libraries(${TARGET}
38 | Qt5::GuiPrivate
39 | Qt5::Core
40 | Qt5::Gui
41 | Qt5::Widgets
42 | Qt5::DBus
43 | Qt5::X11Extras
44 | KF5::WindowSystem
45 | )
46 |
47 | query_qmake(CMAKE_INSTALL_QTPLUGINDIR QT_INSTALL_PLUGINS)
48 |
49 | install(TARGETS ${TARGET} DESTINATION ${CMAKE_INSTALL_QTPLUGINDIR}/styles/)
50 |
--------------------------------------------------------------------------------
/widgetstyle/basestyle.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2020 Reven Martin
3 | * Copyright (C) 2020 KeePassXC Team
4 | * Copyright (C) 2019 Andrew Richards
5 | *
6 | * Derived from Phantomstyle and relicensed under the GPLv2 or v3.
7 | * https://github.com/randrew/phantomstyle
8 | *
9 | * This program is free software: you can redistribute it and/or modify
10 | * it under the terms of the GNU General Public License as published by
11 | * the Free Software Foundation, either version 2 or (at your option)
12 | * version 3 of the License.
13 | *
14 | * This program is distributed in the hope that it will be useful,
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 | * GNU General Public License for more details.
18 | *
19 | * You should have received a copy of the GNU General Public License
20 | * along with this program. If not, see .
21 | */
22 |
23 | #ifndef BASESTYLE_H
24 | #define BASESTYLE_H
25 |
26 | #include
27 |
28 | class BaseStylePrivate;
29 | class ShadowHelper;
30 | class BlurHelper;
31 | class BaseStyle : public QCommonStyle
32 | {
33 | Q_OBJECT
34 |
35 | public:
36 | BaseStyle();
37 | ~BaseStyle() override;
38 |
39 | enum PhantomPrimitiveElement
40 | {
41 | Phantom_PE_IndicatorTabNew = PE_CustomBase + 1,
42 | Phantom_PE_ScrollBarSliderVertical,
43 | Phantom_PE_WindowFrameColor,
44 | };
45 |
46 | static QPalette lightModePalette();
47 | static QPalette darkModePalette();
48 | QPalette standardPalette() const override;
49 | void drawPrimitive(PrimitiveElement elem,
50 | const QStyleOption* option,
51 | QPainter* painter,
52 | const QWidget* widget = nullptr) const override;
53 | void
54 | drawControl(ControlElement ce, const QStyleOption* option, QPainter* painter, const QWidget* widget) const override;
55 | int pixelMetric(PixelMetric metric,
56 | const QStyleOption* option = nullptr,
57 | const QWidget* widget = nullptr) const override;
58 | void drawComplexControl(ComplexControl control,
59 | const QStyleOptionComplex* option,
60 | QPainter* painter,
61 | const QWidget* widget) const override;
62 | QRect subElementRect(SubElement r, const QStyleOption* opt, const QWidget* widget = nullptr) const override;
63 | QSize sizeFromContents(ContentsType type,
64 | const QStyleOption* option,
65 | const QSize& size,
66 | const QWidget* widget) const override;
67 | SubControl hitTestComplexControl(ComplexControl cc,
68 | const QStyleOptionComplex* opt,
69 | const QPoint& pt,
70 | const QWidget* w = nullptr) const override;
71 | QRect subControlRect(ComplexControl cc,
72 | const QStyleOptionComplex* opt,
73 | SubControl sc,
74 | const QWidget* widget) const override;
75 | QPixmap generatedIconPixmap(QIcon::Mode iconMode, const QPixmap& pixmap, const QStyleOption* opt) const override;
76 | int styleHint(StyleHint hint,
77 | const QStyleOption* option = nullptr,
78 | const QWidget* widget = nullptr,
79 | QStyleHintReturn* returnData = nullptr) const override;
80 | QRect itemPixmapRect(const QRect& r, int flags, const QPixmap& pixmap) const override;
81 | void drawItemPixmap(QPainter* painter, const QRect& rect, int alignment, const QPixmap& pixmap) const override;
82 | void drawItemText(QPainter* painter,
83 | const QRect& rect,
84 | int flags,
85 | const QPalette& pal,
86 | bool enabled,
87 | const QString& text,
88 | QPalette::ColorRole textRole = QPalette::NoRole) const override;
89 |
90 | using QCommonStyle::polish;
91 | void polish(QApplication* app) override;
92 | void unpolish(QApplication* app) override;
93 | void polish(QWidget *widget) override;
94 | void unpolish(QWidget *widget) override;
95 |
96 | bool isDarkMode() const;
97 |
98 | protected:
99 | /**
100 | * @return Paths to application stylesheets
101 | */
102 | virtual QString getAppStyleSheet() const
103 | {
104 | return {};
105 | }
106 |
107 | BaseStylePrivate* d;
108 |
109 | private:
110 | ShadowHelper *m_shadowHelper;
111 | BlurHelper *m_blurHelper;
112 | };
113 |
114 | #endif
115 |
--------------------------------------------------------------------------------
/widgetstyle/blurhelper.cpp:
--------------------------------------------------------------------------------
1 | //////////////////////////////////////////////////////////////////////////////
2 | // breezeblurhelper.cpp
3 | // handle regions passed to kwin for blurring
4 | // -------------------
5 | //
6 | // Copyright (C) 2021 Reion Wong
7 | // Copyright (C) 2018 Alex Nemeth
8 | //
9 | // Largely rewritten from Oxygen widget style
10 | // Copyright (C) 2007 Thomas Luebking
11 | // Copyright (c) 2010 Hugo Pereira Da Costa
12 | //
13 | // Permission is hereby granted, free of charge, to any person obtaining a copy
14 | // of this software and associated documentation files (the "Software"), to
15 | // deal in the Software without restriction, including without limitation the
16 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
17 | // sell copies of the Software, and to permit persons to whom the Software is
18 | // furnished to do so, subject to the following conditions:
19 | //
20 | // The above copyright notice and this permission notice shall be included in
21 | // all copies or substantial portions of the Software.
22 | //
23 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
29 | // IN THE SOFTWARE.
30 | //////////////////////////////////////////////////////////////////////////////
31 |
32 | #include "blurhelper.h"
33 |
34 | // KF5
35 | #include
36 |
37 | // Qt
38 | #include
39 | #include
40 | #include
41 | #include
42 | #include
43 | #include
44 | #include
45 | #include
46 |
47 | // XCB
48 | #include
49 | #include
50 | #include
51 |
52 | BlurHelper::BlurHelper(QObject *parent)
53 | : QObject(parent)
54 | {
55 | }
56 |
57 | void BlurHelper::registerWidget(QWidget *widget)
58 | {
59 | // install event filter
60 | addEventFilter(widget);
61 |
62 | // schedule shadow area repaint
63 | update(widget);
64 | }
65 |
66 | void BlurHelper::unregisterWidget(QWidget *widget)
67 | {
68 | // remove event filter
69 | widget->removeEventFilter(this);
70 | }
71 |
72 | bool BlurHelper::eventFilter(QObject *object, QEvent *event)
73 | {
74 | switch (event->type()) {
75 | case QEvent::Hide:
76 | case QEvent::Show:
77 | case QEvent::Resize: {
78 | // cast to widget and check
79 | QWidget *widget(qobject_cast(object));
80 |
81 | if (!widget)
82 | break;
83 |
84 | update(widget);
85 | break;
86 | }
87 |
88 | default: break;
89 | }
90 |
91 | // never eat events
92 | return false;
93 | }
94 |
95 |
96 | void BlurHelper::update(QWidget *widget) const
97 | {
98 | /*
99 | directly from bespin code. Supposedly prevent playing with some 'pseudo-widgets'
100 | that have winId matching some other -random- window
101 | */
102 | if (!(widget->testAttribute(Qt::WA_WState_Created) || widget->internalWinId()))
103 | return;
104 |
105 | if (widget->mask().isEmpty()) {
106 | KWindowEffects::enableBlurBehind(widget->winId(), true);
107 | } else {
108 | KWindowEffects::enableBlurBehind(widget->winId(), true, widget->mask());
109 | }
110 |
111 | // force update
112 | if (widget->isVisible()) {
113 | widget->update();
114 | }
115 | }
116 |
117 | void BlurHelper::enableBlurBehind(QWidget *widget, bool enable, qreal windowRadius)
118 | {
119 | if (!widget)
120 | return;
121 |
122 | xcb_connection_t *c = QX11Info::connection();
123 | if (!c)
124 | return;
125 |
126 | const QByteArray effectName = QByteArrayLiteral("_KDE_NET_WM_BLUR_BEHIND_REGION");
127 | xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(c, false, effectName.length(), effectName.constData());
128 | QScopedPointer atom(xcb_intern_atom_reply(c, atomCookie, nullptr));
129 | if (!atom)
130 | return;
131 |
132 | if (enable) {
133 | qreal devicePixelRatio = qApp->devicePixelRatio();
134 | QPainterPath path;
135 | path.addRoundedRect(QRectF(QPoint(0, 0), widget->size() * devicePixelRatio),
136 | windowRadius * devicePixelRatio,
137 | windowRadius * devicePixelRatio);
138 | QVector data;
139 | for (const QPolygonF &polygon : path.toFillPolygons()) {
140 | QRegion region = polygon.toPolygon();
141 | for (auto i = region.begin(); i != region.end(); ++i) {
142 | data << i->x() << i->y() << i->width() << i->height();
143 | }
144 | }
145 |
146 | xcb_change_property(c, XCB_PROP_MODE_REPLACE, widget->winId(),
147 | atom->atom, XCB_ATOM_CARDINAL,
148 | 32, data.size(), data.constData());
149 |
150 | } else {
151 | xcb_delete_property(c, widget->winId(), atom->atom);
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/widgetstyle/blurhelper.h:
--------------------------------------------------------------------------------
1 | //////////////////////////////////////////////////////////////////////////////
2 | // breezeblurhelper.h
3 | // handle regions passed to kwin for blurring
4 | // -------------------
5 | //
6 | // Copyright (C) 2018 Alex Nemeth
7 | //
8 | // Largely rewritten from Oxygen widget style
9 | // Copyright (C) 2007 Thomas Luebking
10 | // Copyright (c) 2010 Hugo Pereira Da Costa
11 | //
12 | // Permission is hereby granted, free of charge, to any person obtaining a copy
13 | // of this software and associated documentation files (the "Software"), to
14 | // deal in the Software without restriction, including without limitation the
15 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
16 | // sell copies of the Software, and to permit persons to whom the Software is
17 | // furnished to do so, subject to the following conditions:
18 | //
19 | // The above copyright notice and this permission notice shall be included in
20 | // all copies or substantial portions of the Software.
21 | //
22 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
28 | // IN THE SOFTWARE.
29 | //////////////////////////////////////////////////////////////////////////////
30 |
31 | #ifndef BLURHELPER_H
32 | #define BLURHELPER_H
33 |
34 | #include
35 |
36 | class BlurHelper : public QObject
37 | {
38 | Q_OBJECT
39 |
40 | public:
41 | explicit BlurHelper(QObject *parent = nullptr);
42 |
43 | void registerWidget(QWidget *);
44 | void unregisterWidget(QWidget *);
45 | bool eventFilter(QObject *, QEvent *) override;
46 |
47 | void update(QWidget *) const;
48 |
49 | void enableBlurBehind(QWidget *widget, bool enable = true, qreal windowRadius = 0.0);
50 |
51 | protected:
52 | void addEventFilter(QObject *object) {
53 | object->removeEventFilter(this);
54 | object->installEventFilter(this);
55 | }
56 |
57 | };
58 |
59 | #endif // BLURHELPER_H
60 |
--------------------------------------------------------------------------------
/widgetstyle/boxshadowrenderer.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Vlad Zahorodnii
3 | *
4 | * The box blur implementation is based on AlphaBoxBlur from Firefox.
5 | *
6 | * This program is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation; either version 2 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this program; if not, write to the Free Software
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 | */
20 |
21 | // own
22 | #include "boxshadowrenderer.h"
23 |
24 | // Qt
25 | #include
26 | #include
27 |
28 | static inline int calculateBlurRadius(qreal stdDev)
29 | {
30 | // See https://www.w3.org/TR/SVG11/filters.html#feGaussianBlurElement
31 | const qreal gaussianScaleFactor = (3.0 * qSqrt(2.0 * M_PI) / 4.0) * 1.5;
32 | return qMax(2, qFloor(stdDev * gaussianScaleFactor + 0.5));
33 | }
34 |
35 | static inline qreal calculateBlurStdDev(int radius)
36 | {
37 | // See https://www.w3.org/TR/css-backgrounds-3/#shadow-blur
38 | return radius * 0.5;
39 | }
40 |
41 | static inline QSize calculateBlurExtent(int radius)
42 | {
43 | const int blurRadius = calculateBlurRadius(calculateBlurStdDev(radius));
44 | return QSize(blurRadius, blurRadius);
45 | }
46 |
47 | struct BoxLobes
48 | {
49 | int left; ///< how many pixels sample to the left
50 | int right; ///< how many pixels sample to the right
51 | };
52 |
53 | /**
54 | * Compute box filter parameters.
55 | *
56 | * @param radius The blur radius.
57 | * @returns Parameters for three box filters.
58 | **/
59 | static QVector computeLobes(int radius)
60 | {
61 | const int blurRadius = calculateBlurRadius(calculateBlurStdDev(radius));
62 | const int z = blurRadius / 3;
63 |
64 | int major;
65 | int minor;
66 | int final;
67 |
68 | switch (blurRadius % 3) {
69 | case 0:
70 | major = z;
71 | minor = z;
72 | final = z;
73 | break;
74 |
75 | case 1:
76 | major = z + 1;
77 | minor = z;
78 | final = z;
79 | break;
80 |
81 | case 2:
82 | major = z + 1;
83 | minor = z;
84 | final = z + 1;
85 | break;
86 |
87 | default:
88 | Q_UNREACHABLE();
89 | }
90 |
91 | Q_ASSERT(major + minor + final == blurRadius);
92 |
93 | return {
94 | {major, minor},
95 | {minor, major},
96 | {final, final}
97 | };
98 | }
99 |
100 | /**
101 | * Process a row with a box filter.
102 | *
103 | * @param src The start of the row.
104 | * @param dst The destination.
105 | * @param width The width of the row, in pixels.
106 | * @param horizontalStride The number of bytes from one alpha value to the
107 | * next alpha value.
108 | * @param verticalStride The number of bytes from one row to the next row.
109 | * @param lobes Params of the box filter.
110 | * @param transposeInput Whether the input is transposed.
111 | * @param transposeOutput Whether the output should be transposed.
112 | **/
113 | static inline void boxBlurRowAlpha(const uint8_t *src, uint8_t *dst, int width, int horizontalStride,
114 | int verticalStride, const BoxLobes &lobes, bool transposeInput,
115 | bool transposeOutput)
116 | {
117 | const int inputStep = transposeInput ? verticalStride : horizontalStride;
118 | const int outputStep = transposeOutput ? verticalStride : horizontalStride;
119 |
120 | const int boxSize = lobes.left + 1 + lobes.right;
121 | const int reciprocal = (1 << 24) / boxSize;
122 |
123 | uint32_t alphaSum = (boxSize + 1) / 2;
124 |
125 | const uint8_t *left = src;
126 | const uint8_t *right = src;
127 | uint8_t *out = dst;
128 |
129 | const uint8_t firstValue = src[0];
130 | const uint8_t lastValue = src[(width - 1) * inputStep];
131 |
132 | alphaSum += firstValue * lobes.left;
133 |
134 | const uint8_t *initEnd = src + (boxSize - lobes.left) * inputStep;
135 | while (right < initEnd) {
136 | alphaSum += *right;
137 | right += inputStep;
138 | }
139 |
140 | const uint8_t *leftEnd = src + boxSize * inputStep;
141 | while (right < leftEnd) {
142 | *out = (alphaSum * reciprocal) >> 24;
143 | alphaSum += *right - firstValue;
144 | right += inputStep;
145 | out += outputStep;
146 | }
147 |
148 | const uint8_t *centerEnd = src + width * inputStep;
149 | while (right < centerEnd) {
150 | *out = (alphaSum * reciprocal) >> 24;
151 | alphaSum += *right - *left;
152 | left += inputStep;
153 | right += inputStep;
154 | out += outputStep;
155 | }
156 |
157 | const uint8_t *rightEnd = dst + width * outputStep;
158 | while (out < rightEnd) {
159 | *out = (alphaSum * reciprocal) >> 24;
160 | alphaSum += lastValue - *left;
161 | left += inputStep;
162 | out += outputStep;
163 | }
164 | }
165 |
166 | /**
167 | * Blur the alpha channel of a given image.
168 | *
169 | * @param image The input image.
170 | * @param radius The blur radius.
171 | * @param rect Specifies what part of the image to blur. If nothing is provided, then
172 | * the whole alpha channel of the input image will be blurred.
173 | **/
174 | static inline void boxBlurAlpha(QImage &image, int radius, const QRect &rect = {})
175 | {
176 | if (radius < 2) {
177 | return;
178 | }
179 |
180 | const QVector lobes = computeLobes(radius);
181 |
182 | const QRect blurRect = rect.isNull() ? image.rect() : rect;
183 |
184 | const int alphaOffset = QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3;
185 | const int width = blurRect.width();
186 | const int height = blurRect.height();
187 | const int rowStride = image.bytesPerLine();
188 | const int pixelStride = image.depth() >> 3;
189 |
190 | const int bufferStride = qMax(width, height) * pixelStride;
191 | QScopedPointer > buf(new uint8_t[2 * bufferStride]);
192 | uint8_t *buf1 = buf.data();
193 | uint8_t *buf2 = buf1 + bufferStride;
194 |
195 | // Blur the image in horizontal direction.
196 | for (int i = 0; i < height; ++i) {
197 | uint8_t *row = image.scanLine(blurRect.y() + i) + blurRect.x() * pixelStride + alphaOffset;
198 | boxBlurRowAlpha(row, buf1, width, pixelStride, rowStride, lobes[0], false, false);
199 | boxBlurRowAlpha(buf1, buf2, width, pixelStride, rowStride, lobes[1], false, false);
200 | boxBlurRowAlpha(buf2, row, width, pixelStride, rowStride, lobes[2], false, false);
201 | }
202 |
203 | // Blur the image in vertical direction.
204 | for (int i = 0; i < width; ++i) {
205 | uint8_t *column = image.scanLine(blurRect.y()) + (blurRect.x() + i) * pixelStride + alphaOffset;
206 | boxBlurRowAlpha(column, buf1, height, pixelStride, rowStride, lobes[0], true, false);
207 | boxBlurRowAlpha(buf1, buf2, height, pixelStride, rowStride, lobes[1], false, false);
208 | boxBlurRowAlpha(buf2, column, height, pixelStride, rowStride, lobes[2], false, true);
209 | }
210 | }
211 |
212 | static inline void mirrorTopLeftQuadrant(QImage &image)
213 | {
214 | const int width = image.width();
215 | const int height = image.height();
216 |
217 | const int centerX = qCeil(width * 0.5);
218 | const int centerY = qCeil(height * 0.5);
219 |
220 | const int alphaOffset = QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3;
221 | const int stride = image.depth() >> 3;
222 |
223 | for (int y = 0; y < centerY; ++y) {
224 | uint8_t *in = image.scanLine(y) + alphaOffset;
225 | uint8_t *out = in + (width - 1) * stride;
226 |
227 | for (int x = 0; x < centerX; ++x, in += stride, out -= stride) {
228 | *out = *in;
229 | }
230 | }
231 |
232 | for (int y = 0; y < centerY; ++y) {
233 | const uint8_t *in = image.scanLine(y) + alphaOffset;
234 | uint8_t *out = image.scanLine(width - y - 1) + alphaOffset;
235 |
236 | for (int x = 0; x < width; ++x, in += stride, out += stride) {
237 | *out = *in;
238 | }
239 | }
240 | }
241 |
242 | static void renderShadow(QPainter *painter, const QRect &rect, qreal borderRadius, const QPoint &offset, int radius, const QColor &color)
243 | {
244 | const QSize inflation = calculateBlurExtent(radius);
245 | const QSize size = rect.size() + 2 * inflation;
246 |
247 | const qreal dpr = painter->device()->devicePixelRatioF();
248 |
249 | QImage shadow(size * dpr, QImage::Format_ARGB32_Premultiplied);
250 | shadow.setDevicePixelRatio(dpr);
251 | shadow.fill(Qt::transparent);
252 |
253 | QRect boxRect(QPoint(0, 0), rect.size());
254 | boxRect.moveCenter(QRect(QPoint(0, 0), size).center());
255 |
256 | const qreal xRadius = 2.0 * borderRadius / boxRect.width();
257 | const qreal yRadius = 2.0 * borderRadius / boxRect.height();
258 |
259 | QPainter shadowPainter;
260 | shadowPainter.begin(&shadow);
261 | shadowPainter.setRenderHint(QPainter::Antialiasing);
262 | shadowPainter.setPen(Qt::NoPen);
263 | shadowPainter.setBrush(Qt::black);
264 | shadowPainter.drawRoundedRect(boxRect, xRadius, yRadius);
265 | shadowPainter.end();
266 |
267 | // Because the shadow texture is symmetrical, that's enough to blur
268 | // only the top-left quadrant and then mirror it.
269 | const QRect blurRect(0, 0, qCeil(shadow.width() * 0.5), qCeil(shadow.height() * 0.5));
270 | const int scaledRadius = qRound(radius * dpr);
271 | boxBlurAlpha(shadow, scaledRadius, blurRect);
272 | mirrorTopLeftQuadrant(shadow);
273 |
274 | // Give the shadow a tint of the desired color.
275 | shadowPainter.begin(&shadow);
276 | shadowPainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
277 | shadowPainter.fillRect(shadow.rect(), color);
278 | shadowPainter.end();
279 |
280 | // Actually, present the shadow.
281 | QRect shadowRect = shadow.rect();
282 | shadowRect.setSize(shadowRect.size() / dpr);
283 | shadowRect.moveCenter(rect.center() + offset);
284 | painter->drawImage(shadowRect, shadow);
285 | }
286 |
287 | void BoxShadowRenderer::setBoxSize(const QSize &size)
288 | {
289 | m_boxSize = size;
290 | }
291 |
292 | void BoxShadowRenderer::setBorderRadius(qreal radius)
293 | {
294 | m_borderRadius = radius;
295 | }
296 |
297 | void BoxShadowRenderer::setDevicePixelRatio(qreal dpr)
298 | {
299 | m_dpr = dpr;
300 | }
301 |
302 | void BoxShadowRenderer::addShadow(const QPoint &offset, int radius, const QColor &color)
303 | {
304 | Shadow shadow = {};
305 | shadow.offset = offset;
306 | shadow.radius = radius;
307 | shadow.color = color;
308 | m_shadows.append(shadow);
309 | }
310 |
311 | QImage BoxShadowRenderer::render() const
312 | {
313 | if (m_shadows.isEmpty()) {
314 | return {};
315 | }
316 |
317 | QSize canvasSize;
318 | for (const Shadow &shadow : qAsConst(m_shadows)) {
319 | canvasSize = canvasSize.expandedTo(
320 | calculateMinimumShadowTextureSize(m_boxSize, shadow.radius, shadow.offset));
321 | }
322 |
323 | QImage canvas(canvasSize * m_dpr, QImage::Format_ARGB32_Premultiplied);
324 | canvas.setDevicePixelRatio(m_dpr);
325 | canvas.fill(Qt::transparent);
326 |
327 | QRect boxRect(QPoint(0, 0), m_boxSize);
328 | boxRect.moveCenter(QRect(QPoint(0, 0), canvasSize).center());
329 |
330 | QPainter painter(&canvas);
331 | for (const Shadow &shadow : qAsConst(m_shadows)) {
332 | renderShadow(&painter, boxRect, m_borderRadius, shadow.offset, shadow.radius, shadow.color);
333 | }
334 | painter.end();
335 |
336 | return canvas;
337 | }
338 |
339 | QSize BoxShadowRenderer::calculateMinimumBoxSize(int radius)
340 | {
341 | const QSize blurExtent = calculateBlurExtent(radius);
342 | return 2 * blurExtent + QSize(1, 1);
343 | }
344 |
345 | QSize BoxShadowRenderer::calculateMinimumShadowTextureSize(const QSize &boxSize, int radius, const QPoint &offset)
346 | {
347 | return boxSize + 2 * calculateBlurExtent(radius) + QSize(qAbs(offset.x()), qAbs(offset.y()));
348 | }
--------------------------------------------------------------------------------
/widgetstyle/boxshadowrenderer.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Vlad Zahorodnii
3 | *
4 | * This program is free software; you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation; either version 2 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program; if not, write to the Free Software
16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 | */
18 |
19 | #pragma once
20 |
21 | // Qt
22 | #include
23 | #include
24 | #include
25 | #include
26 |
27 | class BoxShadowRenderer
28 | {
29 | public:
30 | // Compiler generated constructors & destructor are fine.
31 |
32 | /**
33 | * Set the size of the box.
34 | * @param size The size of the box.
35 | **/
36 | void setBoxSize(const QSize &size);
37 |
38 | /**
39 | * Set the radius of box' corners.
40 | * @param radius The border radius, in pixels.
41 | **/
42 | void setBorderRadius(qreal radius);
43 |
44 | /**
45 | * Set the device pixel ratio of the resulting shadow texture.
46 | * @param dpr The device pixel ratio.
47 | **/
48 | void setDevicePixelRatio(qreal dpr);
49 |
50 | /**
51 | * Add a shadow.
52 | * @param offset The offset of the shadow.
53 | * @param radius The blur radius.
54 | * @param color The color of the shadow.
55 | **/
56 | void addShadow(const QPoint &offset, int radius, const QColor &color);
57 |
58 | /**
59 | * Render the shadow.
60 | **/
61 | QImage render() const;
62 |
63 | /**
64 | * Calculate the minimum size of the box.
65 | *
66 | * This helper computes the minimum size of the box so the shadow behind it has
67 | * full its strength.
68 | *
69 | * @param radius The blur radius of the shadow.
70 | **/
71 | static QSize calculateMinimumBoxSize(int radius);
72 |
73 | /**
74 | * Calculate the minimum size of the shadow texture.
75 | *
76 | * This helper computes the minimum size of the resulting texture so the shadow
77 | * is not clipped.
78 | *
79 | * @param boxSize The size of the box.
80 | * @param radius The blur radius.
81 | * @param offset The offset of the shadow.
82 | **/
83 | static QSize calculateMinimumShadowTextureSize(const QSize &boxSize, int radius, const QPoint &offset);
84 |
85 | private:
86 | QSize m_boxSize;
87 | qreal m_borderRadius = 0.0;
88 | qreal m_dpr = 1.0;
89 |
90 | struct Shadow {
91 | QPoint offset;
92 | int radius;
93 | QColor color;
94 | };
95 |
96 | QVector m_shadows;
97 | };
98 |
--------------------------------------------------------------------------------
/widgetstyle/cutefishstyle.json:
--------------------------------------------------------------------------------
1 |
2 | {
3 | "Keys": ["cutefish"]
4 | }
5 |
--------------------------------------------------------------------------------
/widgetstyle/phantomcolor.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * HSLuv-C: Human-friendly HSL
3 | *
4 | *
5 | *
6 | * Copyright (c) 2015 Alexei Boronine (original idea, JavaScript implementation)
7 | * Copyright (c) 2015 Roger Tallada (Obj-C implementation)
8 | * Copyright (c) 2017 Martin Mitas (C implementation, based on Obj-C implementation)
9 | *
10 | * Permission is hereby granted, free of charge, to any person obtaining a
11 | * copy of this software and associated documentation files (the "Software"),
12 | * to deal in the Software without restriction, including without limitation
13 | * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 | * and/or sell copies of the Software, and to permit persons to whom the
15 | * Software is furnished to do so, subject to the following conditions:
16 | *
17 | * The above copyright notice and this permission notice shall be included in
18 | * all copies or substantial portions of the Software.
19 | *
20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
26 | * IN THE SOFTWARE.
27 | */
28 |
29 | #include "phantomcolor.h"
30 | #include
31 | #include
32 |
33 | namespace Phantom
34 | {
35 | namespace
36 | {
37 |
38 | // Th`ese declarations originate from hsluv.h, from the hsluv-c library. The
39 | // hpluv functions have been removed, as they are unnecessary for Phantom.
40 | /**
41 | * Convert HSLuv to RGB.
42 | *
43 | * @param h Hue. Between 0.0 and 360.0.
44 | * @param s Saturation. Between 0.0 and 100.0.
45 | * @param l Lightness. Between 0.0 and 100.0.
46 | * @param[out] pr Red component. Between 0.0 and 1.0.
47 | * @param[out] pr Green component. Between 0.0 and 1.0.
48 | * @param[out] pr Blue component. Between 0.0 and 1.0.
49 | */
50 | void hsluv2rgb(double h, double s, double l, double* pr, double* pg, double* pb);
51 |
52 | /**
53 | * Convert RGB to HSLuv.
54 | *
55 | * @param r Red component. Between 0.0 and 1.0.
56 | * @param g Green component. Between 0.0 and 1.0.
57 | * @param b Blue component. Between 0.0 and 1.0.
58 | * @param[out] ph Hue. Between 0.0 and 360.0.
59 | * @param[out] ps Saturation. Between 0.0 and 100.0.
60 | * @param[out] pl Lightness. Between 0.0 and 100.0.
61 | */
62 | void rgb2hsluv(double r, double g, double b, double* ph, double* ps, double* pl);
63 |
64 | // Contents below originate from hsluv.c from the hsluv-c library. They have
65 | // been wrapped in a C++ namespace to avoid collisions and to reduce the
66 | // translation unit count, and hsluv's own sRGB conversion code has been
67 | // stripped out (sRGB conversion is now performed in the Phantom color code
68 | // when going to/from the Rgb type.)
69 | //
70 | // If you need to update the hsluv-c code, be mindful of the removed sRGB
71 | // conversions -- you will need to make similar modifications to the upstream
72 | // hsluv-c code. Also note that that the hpluv (pastel) functions have been
73 | // removed, as they are not used in Phantom.
74 | typedef struct Triplet_tag Triplet;
75 | struct Triplet_tag
76 | {
77 | double a;
78 | double b;
79 | double c;
80 | };
81 |
82 | /* for RGB */
83 | const Triplet m[3] = {{3.24096994190452134377, -1.53738317757009345794, -0.49861076029300328366},
84 | {-0.96924363628087982613, 1.87596750150772066772, 0.04155505740717561247},
85 | {0.05563007969699360846, -0.20397695888897656435, 1.05697151424287856072}};
86 |
87 | /* for XYZ */
88 | const Triplet m_inv[3] = {{0.41239079926595948129, 0.35758433938387796373, 0.18048078840183428751},
89 | {0.21263900587151035754, 0.71516867876775592746, 0.07219231536073371500},
90 | {0.01933081871559185069, 0.11919477979462598791, 0.95053215224966058086}};
91 |
92 | const double ref_u = 0.19783000664283680764;
93 | const double ref_v = 0.46831999493879100370;
94 |
95 | const double kappa = 903.29629629629629629630;
96 | const double epsilon = 0.00885645167903563082;
97 |
98 | typedef struct Bounds_tag Bounds;
99 | struct Bounds_tag
100 | {
101 | double a;
102 | double b;
103 | };
104 |
105 | void get_bounds(double l, Bounds bounds[6])
106 | {
107 | double tl = l + 16.0;
108 | double sub1 = (tl * tl * tl) / 1560896.0;
109 | double sub2 = (sub1 > epsilon ? sub1 : (l / kappa));
110 | int channel;
111 | int t;
112 |
113 | for (channel = 0; channel < 3; channel++) {
114 | double m1 = m[channel].a;
115 | double m2 = m[channel].b;
116 | double m3 = m[channel].c;
117 |
118 | for (t = 0; t < 2; t++) {
119 | double top1 = (284517.0 * m1 - 94839.0 * m3) * sub2;
120 | double top2 = (838422.0 * m3 + 769860.0 * m2 + 731718.0 * m1) * l * sub2 - 769860.0 * t * l;
121 | double bottom = (632260.0 * m3 - 126452.0 * m2) * sub2 + 126452.0 * t;
122 |
123 | bounds[channel * 2 + t].a = top1 / bottom;
124 | bounds[channel * 2 + t].b = top2 / bottom;
125 | }
126 | }
127 | }
128 |
129 | double ray_length_until_intersect(double theta, const Bounds* line)
130 | {
131 | return line->b / (sin(theta) - line->a * cos(theta));
132 | }
133 |
134 | double max_chroma_for_lh(double l, double h)
135 | {
136 | double min_len = DBL_MAX;
137 | double hrad = h * 0.01745329251994329577; /* (2 * pi / 360) */
138 | Bounds bounds[6];
139 | int i;
140 |
141 | get_bounds(l, bounds);
142 | for (i = 0; i < 6; i++) {
143 | double len = ray_length_until_intersect(hrad, &bounds[i]);
144 |
145 | if (len >= 0 && len < min_len)
146 | min_len = len;
147 | }
148 | return min_len;
149 | }
150 |
151 | double dot_product(const Triplet* t1, const Triplet* t2)
152 | {
153 | return (t1->a * t2->a + t1->b * t2->b + t1->c * t2->c);
154 | }
155 |
156 | void xyz2rgb(Triplet* in_out)
157 | {
158 | double r = dot_product(&m[0], in_out);
159 | double g = dot_product(&m[1], in_out);
160 | double b = dot_product(&m[2], in_out);
161 | in_out->a = r;
162 | in_out->b = g;
163 | in_out->c = b;
164 | }
165 |
166 | void rgb2xyz(Triplet* in_out)
167 | {
168 | Triplet rgbl = {in_out->a, in_out->b, in_out->c};
169 | double x = dot_product(&m_inv[0], &rgbl);
170 | double y = dot_product(&m_inv[1], &rgbl);
171 | double z = dot_product(&m_inv[2], &rgbl);
172 | in_out->a = x;
173 | in_out->b = y;
174 | in_out->c = z;
175 | }
176 |
177 | /* http://en.wikipedia.org/wiki/CIELUV
178 | * In these formulas, Yn refers to the reference white point. We are using
179 | * illuminant D65, so Yn (see refY in Maxima file) equals 1. The formula is
180 | * simplified accordingly.
181 | */
182 | double y2l(double y)
183 | {
184 | if (y <= epsilon) {
185 | return y * kappa;
186 | } else {
187 | return 116.0 * cbrt(y) - 16.0;
188 | }
189 | }
190 |
191 | double l2y(double l)
192 | {
193 | if (l <= 8.0) {
194 | return l / kappa;
195 | } else {
196 | double x = (l + 16.0) / 116.0;
197 | return (x * x * x);
198 | }
199 | }
200 |
201 | void xyz2luv(Triplet* in_out)
202 | {
203 | double divisor = in_out->a + (15.0 * in_out->b) + (3.0 * in_out->c);
204 | if (divisor <= 0.00000001) {
205 | in_out->a = 0.0;
206 | in_out->b = 0.0;
207 | in_out->c = 0.0;
208 | return;
209 | }
210 |
211 | double var_u = (4.0 * in_out->a) / divisor;
212 | double var_v = (9.0 * in_out->b) / divisor;
213 | double l = y2l(in_out->b);
214 | double u = 13.0 * l * (var_u - ref_u);
215 | double v = 13.0 * l * (var_v - ref_v);
216 |
217 | in_out->a = l;
218 | if (l < 0.00000001) {
219 | in_out->b = 0.0;
220 | in_out->c = 0.0;
221 | } else {
222 | in_out->b = u;
223 | in_out->c = v;
224 | }
225 | }
226 |
227 | void luv2xyz(Triplet* in_out)
228 | {
229 | if (in_out->a <= 0.00000001) {
230 | /* Black will create a divide-by-zero error. */
231 | in_out->a = 0.0;
232 | in_out->b = 0.0;
233 | in_out->c = 0.0;
234 | return;
235 | }
236 |
237 | double var_u = in_out->b / (13.0 * in_out->a) + ref_u;
238 | double var_v = in_out->c / (13.0 * in_out->a) + ref_v;
239 | double y = l2y(in_out->a);
240 | double x = -(9.0 * y * var_u) / ((var_u - 4.0) * var_v - var_u * var_v);
241 | double z = (9.0 * y - (15.0 * var_v * y) - (var_v * x)) / (3.0 * var_v);
242 | in_out->a = x;
243 | in_out->b = y;
244 | in_out->c = z;
245 | }
246 |
247 | void luv2lch(Triplet* in_out)
248 | {
249 | double l = in_out->a;
250 | double u = in_out->b;
251 | double v = in_out->c;
252 | double h;
253 | double c = sqrt(u * u + v * v);
254 |
255 | /* Grays: disambiguate hue */
256 | if (c < 0.00000001) {
257 | h = 0;
258 | } else {
259 | h = atan2(v, u) * 57.29577951308232087680; /* (180 / pi) */
260 | if (h < 0.0)
261 | h += 360.0;
262 | }
263 |
264 | in_out->a = l;
265 | in_out->b = c;
266 | in_out->c = h;
267 | }
268 |
269 | void lch2luv(Triplet* in_out)
270 | {
271 | double hrad = in_out->c * 0.01745329251994329577; /* (pi / 180.0) */
272 | double u = cos(hrad) * in_out->b;
273 | double v = sin(hrad) * in_out->b;
274 |
275 | in_out->b = u;
276 | in_out->c = v;
277 | }
278 |
279 | void hsluv2lch(Triplet* in_out)
280 | {
281 | double h = in_out->a;
282 | double s = in_out->b;
283 | double l = in_out->c;
284 | double c;
285 |
286 | /* White and black: disambiguate chroma */
287 | if (l > 99.9999999 || l < 0.00000001) {
288 | c = 0.0;
289 | } else {
290 | c = max_chroma_for_lh(l, h) / 100.0 * s;
291 | }
292 |
293 | /* Grays: disambiguate hue */
294 | if (s < 0.00000001)
295 | h = 0.0;
296 |
297 | in_out->a = l;
298 | in_out->b = c;
299 | in_out->c = h;
300 | }
301 |
302 | void lch2hsluv(Triplet* in_out)
303 | {
304 | double l = in_out->a;
305 | double c = in_out->b;
306 | double h = in_out->c;
307 | double s;
308 |
309 | /* White and black: disambiguate saturation */
310 | if (l > 99.9999999 || l < 0.00000001) {
311 | s = 0.0;
312 | } else {
313 | s = c / max_chroma_for_lh(l, h) * 100.0;
314 | }
315 |
316 | /* Grays: disambiguate hue */
317 | if (c < 0.00000001)
318 | h = 0.0;
319 |
320 | in_out->a = h;
321 | in_out->b = s;
322 | in_out->c = l;
323 | }
324 |
325 | void hsluv2rgb(double h, double s, double l, double* pr, double* pg, double* pb)
326 | {
327 | Triplet tmp = {h, s, l};
328 |
329 | hsluv2lch(&tmp);
330 | lch2luv(&tmp);
331 | luv2xyz(&tmp);
332 | xyz2rgb(&tmp);
333 |
334 | *pr = tmp.a;
335 | *pg = tmp.b;
336 | *pb = tmp.c;
337 | }
338 |
339 | void rgb2hsluv(double r, double g, double b, double* ph, double* ps, double* pl)
340 | {
341 | Triplet tmp = {r, g, b};
342 |
343 | rgb2xyz(&tmp);
344 | xyz2luv(&tmp);
345 | luv2lch(&tmp);
346 | lch2hsluv(&tmp);
347 |
348 | *ph = tmp.a;
349 | *ps = tmp.b;
350 | *pl = tmp.c;
351 | }
352 |
353 | } // namespace
354 | } // namespace Phantom
355 |
356 | // The code below is for Phantom, and is used for the Rgb/Hsl-based interface
357 | // for color operations.
358 | namespace Phantom
359 | {
360 | namespace
361 | {
362 | // Note: these constants might be out of range when qreal is defined as float
363 | // instead of double.
364 | inline qreal linear_of_srgb(qreal x)
365 | {
366 | return x < 0.0404482362771082 ? x / 12.92 : std::pow((x + 0.055) / 1.055, 2.4f);
367 | }
368 | inline qreal srgb_of_linear(qreal x)
369 | {
370 | return x < 0.00313066844250063 ? x * 12.92 : std::pow(x, 1.0 / 2.4) * 1.055 - 0.055;
371 | }
372 | } // namespace
373 |
374 | Rgb rgb_of_qcolor(const QColor& color)
375 | {
376 | Rgb a;
377 | a.r = linear_of_srgb(color.red() / 255.0);
378 | a.g = linear_of_srgb(color.green() / 255.0);
379 | a.b = linear_of_srgb(color.blue() / 255.0);
380 | return a;
381 | }
382 |
383 | Hsl hsl_of_rgb(qreal r, qreal g, qreal b)
384 | {
385 | double h, s, l;
386 | rgb2hsluv(r, g, b, &h, &s, &l);
387 | s /= 100.0;
388 | l /= 100.0;
389 | return {h, s, l};
390 | }
391 |
392 | Rgb rgb_of_hsl(qreal h, qreal s, qreal l)
393 | {
394 | double r, g, b;
395 | hsluv2rgb(h, s * 100.0, l * 100.0, &r, &g, &b);
396 | return {r, g, b};
397 | }
398 |
399 | QColor qcolor_of_rgb(qreal r, qreal g, qreal b)
400 | {
401 | int r_ = static_cast(std::lround(srgb_of_linear(r) * 255.0));
402 | int g_ = static_cast(std::lround(srgb_of_linear(g) * 255.0));
403 | int b_ = static_cast(std::lround(srgb_of_linear(b) * 255.0));
404 | return {r_, g_, b_};
405 | }
406 |
407 | QColor lerpQColor(const QColor& x, const QColor& y, qreal a)
408 | {
409 | Rgb x_ = rgb_of_qcolor(x);
410 | Rgb y_ = rgb_of_qcolor(y);
411 | Rgb z = Rgb::lerp(x_, y_, a);
412 | return qcolor_of_rgb(z.r, z.g, z.b);
413 | }
414 |
415 | Rgb Rgb::lerp(const Rgb& x, const Rgb& y, qreal a)
416 | {
417 | Rgb z;
418 | z.r = (1.0 - a) * x.r + a * y.r;
419 | z.g = (1.0 - a) * x.g + a * y.g;
420 | z.b = (1.0 - a) * x.b + a * y.b;
421 | return z;
422 | }
423 | } // namespace Phantom
424 |
--------------------------------------------------------------------------------
/widgetstyle/phantomcolor.h:
--------------------------------------------------------------------------------
1 | /*
2 | * HSLuv-C: Human-friendly HSL
3 | *
4 | *
5 | *
6 | * Copyright (c) 2015 Alexei Boronine (original idea, JavaScript implementation)
7 | * Copyright (c) 2015 Roger Tallada (Obj-C implementation)
8 | * Copyright (c) 2017 Martin Mitas (C implementation, based on Obj-C implementation)
9 | *
10 | * Permission is hereby granted, free of charge, to any person obtaining a
11 | * copy of this software and associated documentation files (the "Software"),
12 | * to deal in the Software without restriction, including without limitation
13 | * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 | * and/or sell copies of the Software, and to permit persons to whom the
15 | * Software is furnished to do so, subject to the following conditions:
16 | *
17 | * The above copyright notice and this permission notice shall be included in
18 | * all copies or substantial portions of the Software.
19 | *
20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
26 | * IN THE SOFTWARE.
27 | */
28 |
29 | #ifndef PHANTOMCOLOR_H
30 | #define PHANTOMCOLOR_H
31 |
32 | #include
33 |
34 | namespace Phantom
35 | {
36 | struct Rgb;
37 | struct Hsl;
38 |
39 | // A color presumed to be in linear space, represented as RGB. Values are in
40 | // the range 0.0 - 1.0. Conversions to and from QColor will assume the QColor
41 | // is in sRGB space, and sRGB conversion will be performed.
42 | struct Rgb
43 | {
44 | qreal r, g, b;
45 | Rgb()
46 | {
47 | }
48 | Rgb(qreal r, qreal g, qreal b)
49 | : r(r)
50 | , g(g)
51 | , b(b)
52 | {
53 | }
54 |
55 | inline Hsl toHsl() const;
56 | inline QColor toQColor() const;
57 | static inline Rgb ofHsl(const Hsl&);
58 | static inline Rgb ofQColor(const QColor&);
59 |
60 | static Rgb lerp(const Rgb& x, const Rgb& y, qreal a);
61 | };
62 |
63 | // A color represented as pseudo-CIE hue, saturation, and lightness. Hue is in
64 | // the range 0.0 - 360.0 (degrees). Lightness and saturation are in the range
65 | // 0.0 - 1.0. Using this and making adjustments to the L value will produce
66 | // more consistent and predictable results than QColor's .darker()/.lighter().
67 | // Note that this is not strictly CIE -- some of the colorspace is distorted so
68 | // that it can represented as a continuous coordinate space. Therefore not all
69 | // adjustments to the parameters will produce perfectly linear results with
70 | // regards to saturation and lightness. But it's still useful, and better than
71 | // QColor's .darker()/.lighter(). Additionally, the L value is more useful for
72 | // performing comparisons between two colors to measure relative and absolute
73 | // brightness.
74 | //
75 | // See the documentation for the hsluv library for more information. (Note that
76 | // for consistency we treat the S and L values in the range 0.0 - 1.0 instead
77 | // of 0.0 - 100.0 like hsluv-c on its own does.)
78 | struct Hsl
79 | {
80 | qreal h, s, l;
81 | Hsl()
82 | {
83 | }
84 | Hsl(qreal h, qreal s, qreal l)
85 | : h(h)
86 | , s(s)
87 | , l(l)
88 | {
89 | }
90 |
91 | inline Rgb toRgb() const;
92 | inline QColor toQColor() const;
93 | static inline Hsl ofRgb(const Rgb&);
94 | static inline Hsl ofQColor(const QColor&);
95 | };
96 | Rgb rgb_of_qcolor(const QColor& color);
97 | QColor qcolor_of_rgb(qreal r, qreal g, qreal b);
98 | Hsl hsl_of_rgb(qreal r, qreal g, qreal b);
99 | Rgb rgb_of_hsl(qreal h, qreal s, qreal l);
100 |
101 | // Clip a floating point value to the range 0.0 - 1.0.
102 | inline qreal saturate(qreal x)
103 | {
104 | if (x < 0.0)
105 | return 0.0;
106 | if (x > 1.0)
107 | return 1.0;
108 | return x;
109 | }
110 |
111 | inline qreal lerp(qreal x, qreal y, qreal a)
112 | {
113 | return (1.0 - a) * x + a * y;
114 | }
115 |
116 | // Linearly interpolate two QColors after trasnforming them to linear color
117 | // space, treating the QColor values as if they were in sRGB space. The
118 | // returned QColor is converted back to sRGB space.
119 | QColor lerpQColor(const QColor& x, const QColor& y, qreal a);
120 |
121 | Hsl Rgb::toHsl() const
122 | {
123 | return hsl_of_rgb(r, g, b);
124 | }
125 |
126 | QColor Rgb::toQColor() const
127 | {
128 | return qcolor_of_rgb(r, g, b);
129 | }
130 |
131 | Rgb Rgb::ofHsl(const Hsl& hsl)
132 | {
133 | return rgb_of_hsl(hsl.h, hsl.s, hsl.l);
134 | }
135 |
136 | Rgb Rgb::ofQColor(const QColor& color)
137 | {
138 | return rgb_of_qcolor(color);
139 | }
140 |
141 | Rgb Hsl::toRgb() const
142 | {
143 | return rgb_of_hsl(h, s, l);
144 | }
145 |
146 | QColor Hsl::toQColor() const
147 | {
148 | Rgb rgb = rgb_of_hsl(h, s, l);
149 | return qcolor_of_rgb(rgb.r, rgb.g, rgb.b);
150 | }
151 |
152 | Hsl Hsl::ofRgb(const Rgb& rgb)
153 | {
154 | return hsl_of_rgb(rgb.r, rgb.g, rgb.b);
155 | }
156 |
157 | Hsl Hsl::ofQColor(const QColor& color)
158 | {
159 | Rgb rgb = rgb_of_qcolor(color);
160 | return hsl_of_rgb(rgb.r, rgb.g, rgb.b);
161 | }
162 |
163 | } // namespace Phantom
164 |
165 | #endif
166 |
--------------------------------------------------------------------------------
/widgetstyle/pstyleplugin.cpp:
--------------------------------------------------------------------------------
1 | #include "pstyleplugin.h"
2 | #include "basestyle.h"
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | QStringList ProxyStylePlugin::keys() const
9 | {
10 | return {"cutefish"};
11 | }
12 |
13 | QStyle *ProxyStylePlugin::create(const QString &key)
14 | {
15 | if (key != QStringLiteral("cutefish")) {
16 | return nullptr;
17 | }
18 |
19 | return new BaseStyle;
20 | }
21 |
--------------------------------------------------------------------------------
/widgetstyle/pstyleplugin.h:
--------------------------------------------------------------------------------
1 | #ifndef PSTYLEPLUGIN_H
2 | #define PSTYLEPLUGIN_H
3 |
4 | #include
5 |
6 | class ProxyStylePlugin : public QStylePlugin
7 | {
8 | Q_OBJECT
9 | Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QStyleFactoryInterface" FILE "cutefishstyle.json")
10 |
11 | public:
12 | ProxyStylePlugin() = default;
13 |
14 | QStringList keys() const;
15 | QStyle *create(const QString &key) override;
16 | };
17 |
18 | #endif // PSTYLEPLUGIN_H
19 |
--------------------------------------------------------------------------------
/widgetstyle/shadowhelper.cpp:
--------------------------------------------------------------------------------
1 | /*************************************************************************
2 | * Copyright (C) 2014 by Hugo Pereira Da Costa *
3 | * Copyright (C) 2018, 2020 by Vlad Zahorodnii *
4 | * Copyright (C) 2020, 2020 by Reven Martin *
5 | * *
6 | * This program is free software; you can redistribute it and/or modify *
7 | * it under the terms of the GNU General Public License as published by *
8 | * the Free Software Foundation; either version 2 of the License, or *
9 | * (at your option) any later version. *
10 | * *
11 | * This program is distributed in the hope that it will be useful, *
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 | * GNU General Public License for more details. *
15 | * *
16 | * You should have received a copy of the GNU General Public License *
17 | * along with this program; if not, write to the *
18 | * Free Software Foundation, Inc., *
19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
20 | *************************************************************************/
21 |
22 | #include "shadowhelper.h"
23 | #include "boxshadowrenderer.h"
24 |
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 |
35 | #include
36 |
37 | const char netWMSkipShadow[] = "_CUTEFISH_NET_WM_SKIP_SHADOW";
38 | const char netWMForceShadow[] = "_CUTEFISH_NET_WM_FORCE_SHADOW";
39 | const char netWMFrameRadius[] = "_CUTEFISH_NET_WM_FRAME_RADIUS";
40 |
41 | enum {
42 | ShadowNone,
43 | ShadowSmall,
44 | ShadowMedium,
45 | ShadowLarge,
46 | ShadowVeryLarge
47 | };
48 |
49 | const CompositeShadowParams s_shadowParams[] = {
50 | // None
51 | CompositeShadowParams(),
52 | // Small
53 | CompositeShadowParams(
54 | QPoint(0, 3),
55 | ShadowParams(QPoint(0, 0), 16, 0.26),
56 | ShadowParams(QPoint(0, -2), 8, 0.16)),
57 | // Medium
58 | CompositeShadowParams(
59 | QPoint(0, 4),
60 | ShadowParams(QPoint(0, 0), 20, 0.24),
61 | ShadowParams(QPoint(0, -2), 10, 0.14)),
62 | // Large
63 | CompositeShadowParams(
64 | QPoint(0, 5),
65 | ShadowParams(QPoint(0, 0), 24, 0.22),
66 | ShadowParams(QPoint(0, -3), 12, 0.12)),
67 | // Very Large
68 | CompositeShadowParams(
69 | QPoint(0, 6),
70 | ShadowParams(QPoint(0, 0), 32, 0.2),
71 | ShadowParams(QPoint(0, -3), 16, 0.1))
72 | };
73 |
74 |
75 | ShadowHelper::ShadowHelper(QObject * parent)
76 | : QObject(parent),
77 | m_frameRadius(12)
78 | {
79 | }
80 |
81 | ShadowHelper::~ShadowHelper()
82 | {
83 | }
84 |
85 | CompositeShadowParams ShadowHelper::lookupShadowParams(int shadowSizeEnum)
86 | {
87 | switch (shadowSizeEnum) {
88 | case ShadowNone:
89 | return s_shadowParams[0];
90 | case ShadowSmall:
91 | return s_shadowParams[1];
92 | case ShadowMedium:
93 | return s_shadowParams[2];
94 | case ShadowLarge:
95 | return s_shadowParams[3];
96 | case ShadowVeryLarge:
97 | return s_shadowParams[4];
98 | default:
99 | // Fallback to the Large size.
100 | return s_shadowParams[3];
101 | }
102 | }
103 |
104 | bool ShadowHelper::registerWidget(QWidget *widget, bool force)
105 | {
106 | // make sure widget is not already registered
107 | if (m_widgets.contains(widget))
108 | return false;
109 |
110 | // check if widget qualifies
111 | if (!(force || acceptWidget(widget)))
112 | return false;
113 |
114 | qreal frameRadius = m_frameRadius;
115 | const auto frameRadiusProperty = widget->property(netWMFrameRadius);
116 | if (frameRadiusProperty.isValid())
117 | frameRadius = frameRadiusProperty.toReal();
118 |
119 | installShadows(widget, shadowTiles(frameRadius));
120 | m_widgets.insert(widget);
121 |
122 | // install event filter
123 | widget->removeEventFilter(this);
124 | widget->installEventFilter(this);
125 |
126 | // connect destroy signal
127 | connect(widget, &QObject::destroyed, this, &ShadowHelper::objectDeleted);
128 |
129 | return true;
130 | }
131 |
132 | void ShadowHelper::unregisterWidget(QWidget *widget)
133 | {
134 | if (m_widgets.remove(widget)) {
135 | // uninstall the event filter
136 | widget->removeEventFilter(this);
137 |
138 | // disconnect all signals
139 | disconnect(widget, nullptr, this, nullptr);
140 |
141 | // uninstall the shadow
142 | uninstallShadows(widget);
143 | }
144 | }
145 |
146 | bool ShadowHelper::eventFilter(QObject *object, QEvent *event)
147 | {
148 | if (KWindowSystem::isPlatformX11()) {
149 | // check event type
150 | if (event->type() == QEvent::WinIdChange) {
151 | QWidget *widget = static_cast(object);
152 |
153 | qreal frameRadius = m_frameRadius;
154 | const auto frameRadiusProperty = widget->property(netWMFrameRadius);
155 | if (frameRadiusProperty.isValid())
156 | frameRadius = frameRadiusProperty.toReal();
157 | TileSet shadowTileSet = shadowTiles(frameRadius);
158 | installShadows(widget, shadowTileSet);
159 | }
160 | } else {
161 | if (event->type() != QEvent::PlatformSurface)
162 | return false;
163 |
164 | // QWidget *widget(static_cast(object));
165 | QPlatformSurfaceEvent* surfaceEvent(static_cast(event));
166 |
167 | switch (surfaceEvent->surfaceEventType()) {
168 | case QPlatformSurfaceEvent::SurfaceCreated:
169 | //installShadows(widget);
170 | break;
171 | case QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed:
172 | // Don't care.
173 | break;
174 | }
175 | }
176 |
177 | return false;
178 | }
179 |
180 | TileSet ShadowHelper::shadowTiles(const qreal frameRadius)
181 | {
182 | const CompositeShadowParams params = lookupShadowParams(ShadowVeryLarge);
183 |
184 | if (params.isNone())
185 | return TileSet();
186 | // } else if (_shadowTiles.isValid()) {
187 | // return _shadowTiles;
188 | // }
189 |
190 | auto withOpacity = [](const QColor &color, qreal opacity) -> QColor {
191 | QColor c(color);
192 | c.setAlphaF(opacity);
193 | return c;
194 | };
195 |
196 | const QColor color = Qt::black;
197 | // const qreal strength = static_cast(255) / 255.0;
198 | const qreal strength = 1.5;
199 |
200 | const QSize boxSize = BoxShadowRenderer::calculateMinimumBoxSize(params.shadow1.radius)
201 | .expandedTo(BoxShadowRenderer::calculateMinimumBoxSize(params.shadow2.radius));
202 |
203 | const qreal dpr = qApp->devicePixelRatio();
204 |
205 | BoxShadowRenderer shadowRenderer;
206 | shadowRenderer.setBorderRadius(frameRadius);
207 | shadowRenderer.setBoxSize(boxSize);
208 | shadowRenderer.setDevicePixelRatio(dpr);
209 |
210 | shadowRenderer.addShadow(params.shadow1.offset, params.shadow1.radius,
211 | withOpacity(color, params.shadow1.opacity * strength));
212 | shadowRenderer.addShadow(params.shadow2.offset, params.shadow2.radius,
213 | withOpacity(color, params.shadow2.opacity * strength));
214 |
215 | QImage shadowTexture = shadowRenderer.render();
216 |
217 | const QRect outerRect(QPoint(0, 0), shadowTexture.size() / dpr);
218 |
219 | QRect boxRect(QPoint(0, 0), boxSize);
220 | boxRect.moveCenter(outerRect.center());
221 |
222 | // Mask out inner rect.
223 | QPainter painter(&shadowTexture);
224 | painter.setRenderHint(QPainter::Antialiasing);
225 |
226 | int Shadow_Overlap = 3;
227 | const QMargins margins = QMargins(
228 | boxRect.left() - outerRect.left() - Shadow_Overlap - params.offset.x(),
229 | boxRect.top() - outerRect.top() - Shadow_Overlap - params.offset.y(),
230 | outerRect.right() - boxRect.right() - Shadow_Overlap + params.offset.x(),
231 | outerRect.bottom() - boxRect.bottom() - Shadow_Overlap + params.offset.y());
232 |
233 | painter.setPen(Qt::NoPen);
234 | painter.setBrush(Qt::black);
235 | painter.setCompositionMode(QPainter::CompositionMode_DestinationOut);
236 | painter.drawRoundedRect(
237 | outerRect - margins,
238 | frameRadius,
239 | frameRadius);
240 |
241 | // We're done.
242 | painter.end();
243 |
244 | const QPoint innerRectTopLeft = outerRect.center();
245 | TileSet tiles = TileSet(
246 | QPixmap::fromImage(shadowTexture),
247 | innerRectTopLeft.x(),
248 | innerRectTopLeft.y(),
249 | 1, 1);
250 |
251 | return tiles;
252 | }
253 |
254 | void ShadowHelper::objectDeleted(QObject *object)
255 | {
256 | QWidget *widget(static_cast(object));
257 | m_widgets.remove(widget);
258 | m_shadows.remove(widget);
259 | }
260 |
261 | bool ShadowHelper::isMenu(QWidget *widget) const
262 | {
263 | return qobject_cast(widget);
264 | }
265 |
266 | bool ShadowHelper::isToolTip(QWidget *widget) const
267 | {
268 | return widget->inherits("QTipLabel") || (widget->windowFlags() & Qt::WindowType_Mask) == Qt::ToolTip;
269 | }
270 |
271 | bool ShadowHelper::isDockWidget(QWidget *widget) const
272 | {
273 | return qobject_cast(widget);
274 | }
275 |
276 | bool ShadowHelper::isToolBar(QWidget *widget) const
277 | {
278 | return qobject_cast(widget);
279 | }
280 |
281 | bool ShadowHelper::acceptWidget(QWidget *widget) const
282 | {
283 | // flags
284 | if (widget->property(netWMSkipShadow).toBool())
285 | return false;
286 | if (widget->property(netWMForceShadow).toBool())
287 | return true;
288 |
289 | // menus
290 | if (isMenu(widget))
291 | return true;
292 |
293 | // combobox dropdown lists
294 | if (widget->inherits("QComboBoxPrivateContainer"))
295 | return true;
296 |
297 | // tooltips
298 | if (isToolTip(widget) && !widget->inherits("Plasma::ToolTip"))
299 | return true;
300 |
301 | // detached widgets
302 | if (isDockWidget(widget) || isToolBar(widget))
303 | return true;
304 |
305 | // reject
306 | return false;
307 | }
308 |
309 | KWindowShadowTile::Ptr ShadowHelper::createTile(const QPixmap& source)
310 | {
311 | KWindowShadowTile::Ptr tile = KWindowShadowTile::Ptr::create();
312 | tile->setImage(source.toImage());
313 | return tile;
314 | }
315 |
316 | void ShadowHelper::installShadows(QWidget *widget, TileSet shadowTiles)
317 | {
318 | if (!widget)
319 | return;
320 |
321 | // only toplevel widgets can cast drop-shadows
322 | if (!widget->isWindow())
323 | return;
324 |
325 | // widget must have valid native window
326 | if (!widget->testAttribute(Qt::WA_WState_Created))
327 | return;
328 |
329 | // create platform shadow tiles
330 | QVector tiles = {
331 | createTile(shadowTiles.pixmap(1)),
332 | createTile(shadowTiles.pixmap(2)),
333 | createTile(shadowTiles.pixmap(5)),
334 | createTile(shadowTiles.pixmap(8)),
335 | createTile(shadowTiles.pixmap(7)),
336 | createTile(shadowTiles.pixmap(6)),
337 | createTile(shadowTiles.pixmap(3)),
338 | createTile(shadowTiles.pixmap(0))
339 | };
340 | if (tiles.count() != numTiles)
341 | return;
342 |
343 | // find a shadow associated with the widget
344 | KWindowShadow*& shadow = m_shadows[ widget ];
345 |
346 | if (!shadow)
347 | shadow = new KWindowShadow(widget);
348 |
349 | if (shadow->isCreated())
350 | shadow->destroy();
351 |
352 | shadow->setTopTile(tiles[ 0 ]);
353 | shadow->setTopRightTile(tiles[ 1 ]);
354 | shadow->setRightTile(tiles[ 2 ]);
355 | shadow->setBottomRightTile(tiles[ 3 ]);
356 | shadow->setBottomTile(tiles[ 4 ]);
357 | shadow->setBottomLeftTile(tiles[ 5 ]);
358 | shadow->setLeftTile(tiles[ 6 ]);
359 | shadow->setTopLeftTile(tiles[ 7 ]);
360 | shadow->setPadding(shadowMargins(widget, shadowTiles));
361 | shadow->setWindow(widget->windowHandle());
362 | shadow->create();
363 | }
364 |
365 | QMargins ShadowHelper::shadowMargins(QWidget *widget, TileSet shadowTiles) const
366 | {
367 | const CompositeShadowParams params = lookupShadowParams(ShadowVeryLarge);
368 | if (params.isNone())
369 | return QMargins();
370 |
371 | const QSize boxSize = BoxShadowRenderer::calculateMinimumBoxSize(params.shadow1.radius)
372 | .expandedTo(BoxShadowRenderer::calculateMinimumBoxSize(params.shadow2.radius));
373 |
374 | const QSize shadowSize = BoxShadowRenderer::calculateMinimumShadowTextureSize(boxSize, params.shadow1.radius, params.shadow1.offset)
375 | .expandedTo(BoxShadowRenderer::calculateMinimumShadowTextureSize(boxSize, params.shadow2.radius, params.shadow2.offset));
376 |
377 | const QRect shadowRect(QPoint(0, 0), shadowSize);
378 |
379 | QRect boxRect(QPoint(0, 0), boxSize);
380 | boxRect.moveCenter(shadowRect.center());
381 |
382 | int Shadow_Overlap = 4;
383 | QMargins margins(
384 | boxRect.left() - shadowRect.left() - Shadow_Overlap - params.offset.x(),
385 | boxRect.top() - shadowRect.top() - Shadow_Overlap - params.offset.y(),
386 | shadowRect.right() - boxRect.right() - Shadow_Overlap + params.offset.x(),
387 | shadowRect.bottom() - boxRect.bottom() - Shadow_Overlap + params.offset.y());
388 |
389 | if (widget->inherits("QBalloonTip")) {
390 | // Balloon tip needs special margins to deal with the arrow.
391 | int top = widget->contentsMargins().top();
392 | int bottom = widget->contentsMargins().bottom();
393 |
394 | // Need to decrement default size further due to extra hard coded round corner.
395 | margins -= 1;
396 |
397 | // Arrow can be either to the top or the bottom. Adjust margins accordingly.
398 | const int diff = qAbs(top - bottom);
399 | if (top > bottom) {
400 | margins.setTop(margins.top() - diff);
401 | } else {
402 | margins.setBottom(margins.bottom() - diff);
403 | }
404 | }
405 |
406 | margins *= shadowTiles.pixmap(0).devicePixelRatio();
407 |
408 | return margins;
409 | }
410 |
411 | void ShadowHelper::uninstallShadows(QWidget *widget)
412 | {
413 | delete m_shadows.take(widget);
414 | }
415 |
--------------------------------------------------------------------------------
/widgetstyle/shadowhelper.h:
--------------------------------------------------------------------------------
1 | /*************************************************************************
2 | * Copyright (C) 2014 by Hugo Pereira Da Costa *
3 | * Copyright (C) 2020 by Vlad Zahorodnii *
4 | * Copyright (C) 2020, 2020 by Reven Martin *
5 | * *
6 | * This program is free software; you can redistribute it and/or modify *
7 | * it under the terms of the GNU General Public License as published by *
8 | * the Free Software Foundation; either version 2 of the License, or *
9 | * (at your option) any later version. *
10 | * *
11 | * This program is distributed in the hope that it will be useful, *
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 | * GNU General Public License for more details. *
15 | * *
16 | * You should have received a copy of the GNU General Public License *
17 | * along with this program; if not, write to the *
18 | * Free Software Foundation, Inc., *
19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
20 | *************************************************************************/
21 |
22 | #ifndef SHADOWHELPER_H
23 | #define SHADOWHELPER_H
24 |
25 | #include "tileset.h"
26 | #include
27 |
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 |
34 | struct ShadowParams
35 | {
36 | ShadowParams() = default;
37 |
38 | ShadowParams(const QPoint &offset, int radius, qreal opacity):
39 | offset(offset),
40 | radius(radius),
41 | opacity(opacity)
42 | {}
43 |
44 | QPoint offset;
45 | int radius = 0;
46 | qreal opacity = 0;
47 | };
48 |
49 | struct CompositeShadowParams
50 | {
51 | CompositeShadowParams() = default;
52 |
53 | CompositeShadowParams(
54 | const QPoint &offset,
55 | const ShadowParams &shadow1,
56 | const ShadowParams &shadow2)
57 | : offset(offset)
58 | , shadow1(shadow1)
59 | , shadow2(shadow2) {}
60 |
61 | bool isNone() const
62 | { return qMax(shadow1.radius, shadow2.radius) == 0; }
63 |
64 | QPoint offset;
65 | ShadowParams shadow1;
66 | ShadowParams shadow2;
67 | };
68 |
69 | //* handle shadow pixmaps passed to window manager via X property
70 | class ShadowHelper: public QObject
71 | {
72 | Q_OBJECT
73 |
74 | public:
75 | //* constructor
76 | ShadowHelper(QObject *);
77 |
78 | //* destructor
79 | ~ShadowHelper() override;
80 |
81 | //* shadow params from size enum
82 | static CompositeShadowParams lookupShadowParams(int shadowSizeEnum);
83 |
84 | //* register widget
85 | bool registerWidget(QWidget *, bool force = false);
86 |
87 | //* unregister widget
88 | void unregisterWidget(QWidget *);
89 |
90 | //* event filter
91 | bool eventFilter(QObject *, QEvent *) override;
92 |
93 | void setFrameRadius(qreal radius) { m_frameRadius = radius; }
94 |
95 | //* shadow tiles
96 | /** is public because it is also needed for mdi windows */
97 | // TileSet shadowTiles();
98 |
99 | TileSet shadowTiles(const qreal frameRadius);
100 |
101 | protected Q_SLOTS:
102 | //* unregister widget
103 | void objectDeleted(QObject *);
104 |
105 | protected:
106 | //* true if widget is a menu
107 | bool isMenu(QWidget *) const;
108 |
109 | //* true if widget is a tooltip
110 | bool isToolTip(QWidget *) const;
111 |
112 | //* dock widget
113 | bool isDockWidget(QWidget *) const;
114 |
115 | //* toolbar
116 | bool isToolBar(QWidget *) const;
117 |
118 | //* accept widget
119 | bool acceptWidget(QWidget *) const;
120 |
121 | // create shadow tile from pixmap
122 | KWindowShadowTile::Ptr createTile(const QPixmap &);
123 |
124 | //* installs shadow on given widget in a platform independent way
125 | // void installShadows( QWidget * );
126 |
127 | void installShadows(QWidget *widget, TileSet shadowTiles);
128 |
129 | //* uninstalls shadow on given widget in a platform independent way
130 | void uninstallShadows(QWidget *);
131 |
132 | //* gets the shadow margins for the given widget
133 | QMargins shadowMargins(QWidget*, TileSet) const;
134 |
135 | private:
136 | //* registered widgets
137 | QSet m_widgets;
138 |
139 | //* managed shadows
140 | QMap m_shadows;
141 |
142 | qreal m_frameRadius;
143 |
144 | //* number of tiles
145 | enum { numTiles = 8 };
146 | };
147 |
148 | #endif
149 |
--------------------------------------------------------------------------------
/widgetstyle/tileset.cpp:
--------------------------------------------------------------------------------
1 | /*************************************************************************
2 | * Copyright (C) 2014 by Hugo Pereira Da Costa *
3 | * *
4 | * This program is free software; you can redistribute it and/or modify *
5 | * it under the terms of the GNU General Public License as published by *
6 | * the Free Software Foundation; either version 2 of the License, or *
7 | * (at your option) any later version. *
8 | * *
9 | * This program is distributed in the hope that it will be useful, *
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 | * GNU General Public License for more details. *
13 | * *
14 | * You should have received a copy of the GNU General Public License *
15 | * along with this program; if not, write to the *
16 | * Free Software Foundation, Inc., *
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
18 | *************************************************************************/
19 |
20 | #include "tileset.h"
21 |
22 | #include
23 |
24 | //___________________________________________________________
25 | inline bool bits(TileSet::Tiles flags, TileSet::Tiles testFlags)
26 | { return (flags & testFlags) == testFlags; }
27 |
28 | //______________________________________________________________________________________
29 | inline qreal devicePixelRatio( const QPixmap& pixmap )
30 | {
31 | return pixmap.devicePixelRatio();
32 | }
33 |
34 | //______________________________________________________________________________________
35 | inline void setDevicePixelRatio( QPixmap& pixmap, qreal value )
36 | {
37 | return pixmap.setDevicePixelRatio( value );
38 | }
39 |
40 | //______________________________________________________________
41 | void TileSet::initPixmap( PixmapList& pixmaps, const QPixmap &source, int width, int height, const QRect &rect)
42 | {
43 | QSize size( width, height );
44 | if( !( size.isValid() && rect.isValid() ) )
45 | {
46 | pixmaps.append( QPixmap() );
47 |
48 | } else if( size != rect.size() ) {
49 |
50 | const qreal dpiRatio( devicePixelRatio( source ) );
51 | const QRect scaledRect( rect.topLeft()*dpiRatio, rect.size()*dpiRatio );
52 | const QSize scaledSize( size*dpiRatio );
53 | const QPixmap tile( source.copy(scaledRect) );
54 | QPixmap pixmap( scaledSize );
55 |
56 | pixmap.fill(Qt::transparent);
57 | QPainter painter(&pixmap);
58 | painter.drawTiledPixmap(0, 0, scaledSize.width(), scaledSize.height(), tile);
59 | setDevicePixelRatio( pixmap, dpiRatio );
60 | pixmaps.append( pixmap );
61 |
62 | } else {
63 |
64 | const qreal dpiRatio( devicePixelRatio( source ) );
65 | const QRect scaledRect( rect.topLeft()*dpiRatio, rect.size()*dpiRatio );
66 | QPixmap pixmap( source.copy( scaledRect ) );
67 | setDevicePixelRatio( pixmap, dpiRatio );
68 | pixmaps.append( pixmap );
69 |
70 | }
71 |
72 | }
73 |
74 | //______________________________________________________________
75 | TileSet::TileSet():
76 | _w1(0),
77 | _h1(0),
78 | _w3(0),
79 | _h3(0)
80 | { _pixmaps.reserve(9); }
81 |
82 | //______________________________________________________________
83 | TileSet::TileSet(const QPixmap &source, int w1, int h1, int w2, int h2 ):
84 | _w1(w1),
85 | _h1(h1),
86 | _w3(0),
87 | _h3(0)
88 | {
89 | _pixmaps.reserve(9);
90 | if( source.isNull() ) return;
91 |
92 | _w3 = source.width()/devicePixelRatio( source ) - (w1 + w2);
93 | _h3 = source.height()/devicePixelRatio( source ) - (h1 + h2);
94 | int w = w2;
95 | int h = h2;
96 |
97 | // initialise pixmap array
98 | initPixmap( _pixmaps, source, _w1, _h1, QRect(0, 0, _w1, _h1) );
99 | initPixmap( _pixmaps, source, w, _h1, QRect(_w1, 0, w2, _h1) );
100 | initPixmap( _pixmaps, source, _w3, _h1, QRect(_w1+w2, 0, _w3, _h1) );
101 | initPixmap( _pixmaps, source, _w1, h, QRect(0, _h1, _w1, h2) );
102 | initPixmap( _pixmaps, source, w, h, QRect(_w1, _h1, w2, h2) );
103 | initPixmap( _pixmaps, source, _w3, h, QRect(_w1+w2, _h1, _w3, h2) );
104 | initPixmap( _pixmaps, source, _w1, _h3, QRect(0, _h1+h2, _w1, _h3) );
105 | initPixmap( _pixmaps, source, w, _h3, QRect(_w1, _h1+h2, w2, _h3) );
106 | initPixmap( _pixmaps, source, _w3, _h3, QRect(_w1+w2, _h1+h2, _w3, _h3) );
107 | }
108 |
109 | //___________________________________________________________
110 | void TileSet::render(const QRect &constRect, QPainter *painter, Tiles tiles) const
111 | {
112 |
113 | const bool oldHint( painter->testRenderHint( QPainter::SmoothPixmapTransform ) );
114 | painter->setRenderHint( QPainter::SmoothPixmapTransform, true );
115 |
116 | // check initialization
117 | if( _pixmaps.size() < 9 ) return;
118 |
119 | // copy source rect
120 | QRect rect( constRect );
121 |
122 | // get rect dimensions
123 | int x0, y0, w, h;
124 | rect.getRect(&x0, &y0, &w, &h);
125 |
126 | // calculate pixmaps widths
127 | int wLeft(0);
128 | int wRight(0);
129 | if( _w1+_w3 > 0 )
130 | {
131 | qreal wRatio( qreal( _w1 )/qreal( _w1 + _w3 ) );
132 | wLeft = (tiles&Right) ? qMin( _w1, int(w*wRatio) ):_w1;
133 | wRight = (tiles&Left) ? qMin( _w3, int(w*(1.0-wRatio)) ):_w3;
134 | }
135 |
136 | // calculate pixmap heights
137 | int hTop(0);
138 | int hBottom(0);
139 | if( _h1+_h3 > 0 )
140 | {
141 | qreal hRatio( qreal( _h1 )/qreal( _h1 + _h3 ) );
142 | hTop = (tiles&Bottom) ? qMin( _h1, int(h*hRatio) ):_h1;
143 | hBottom = (tiles&Top) ? qMin( _h3, int(h*(1.0-hRatio)) ):_h3;
144 | }
145 |
146 | // calculate corner locations
147 | w -= wLeft + wRight;
148 | h -= hTop + hBottom;
149 | const int x1 = x0 + wLeft;
150 | const int x2 = x1 + w;
151 | const int y1 = y0 + hTop;
152 | const int y2 = y1 + h;
153 |
154 | const int w2 = _pixmaps.at(7).width()/devicePixelRatio( _pixmaps.at(7) );
155 | const int h2 = _pixmaps.at(5).height()/devicePixelRatio( _pixmaps.at(5) );
156 |
157 | // corner
158 | if( bits( tiles, Top|Left) ) painter->drawPixmap(x0, y0, _pixmaps.at(0), 0, 0, wLeft*devicePixelRatio( _pixmaps.at(0) ), hTop*devicePixelRatio( _pixmaps.at(0) ));
159 | if( bits( tiles, Top|Right) ) painter->drawPixmap(x2, y0, _pixmaps.at(2), (_w3-wRight)*devicePixelRatio( _pixmaps.at(2) ), 0, wRight*devicePixelRatio( _pixmaps.at(2) ), hTop*devicePixelRatio( _pixmaps.at(2) ) );
160 | if( bits( tiles, Bottom|Left) ) painter->drawPixmap(x0, y2, _pixmaps.at(6), 0, (_h3-hBottom)*devicePixelRatio( _pixmaps.at(6) ), wLeft*devicePixelRatio( _pixmaps.at(6) ), hBottom*devicePixelRatio( _pixmaps.at(6) ));
161 | if( bits( tiles, Bottom|Right) ) painter->drawPixmap(x2, y2, _pixmaps.at(8), (_w3-wRight)*devicePixelRatio( _pixmaps.at(8) ), (_h3-hBottom)*devicePixelRatio( _pixmaps.at(8) ), wRight*devicePixelRatio( _pixmaps.at(8) ), hBottom*devicePixelRatio( _pixmaps.at(8) ) );
162 |
163 | // top and bottom
164 | if( w > 0 )
165 | {
166 | if( tiles&Top ) painter->drawPixmap(x1, y0, w, hTop, _pixmaps.at(1), 0, 0, w2*devicePixelRatio( _pixmaps.at(1) ), hTop*devicePixelRatio( _pixmaps.at(1) ) );
167 | if( tiles&Bottom ) painter->drawPixmap(x1, y2, w, hBottom, _pixmaps.at(7), 0, (_h3-hBottom)*devicePixelRatio( _pixmaps.at(7) ), w2*devicePixelRatio( _pixmaps.at(7) ), hBottom*devicePixelRatio( _pixmaps.at(7) ) );
168 | }
169 |
170 | // left and right
171 | if( h > 0 )
172 | {
173 | if( tiles&Left ) painter->drawPixmap(x0, y1, wLeft, h, _pixmaps.at(3), 0, 0, wLeft*devicePixelRatio( _pixmaps.at(3) ), h2*devicePixelRatio( _pixmaps.at(3) ) );
174 | if( tiles&Right ) painter->drawPixmap(x2, y1, wRight, h, _pixmaps.at(5), (_w3-wRight)*devicePixelRatio( _pixmaps.at(5) ), 0, wRight*devicePixelRatio( _pixmaps.at(5) ), h2*devicePixelRatio( _pixmaps.at(5) ) );
175 | }
176 |
177 | // center
178 | if( (tiles&Center) && h > 0 && w > 0 ) painter->drawPixmap(x1, y1, w, h, _pixmaps.at(4));
179 |
180 | // restore
181 | painter->setRenderHint( QPainter::SmoothPixmapTransform, oldHint );
182 |
183 | }
184 |
--------------------------------------------------------------------------------
/widgetstyle/tileset.h:
--------------------------------------------------------------------------------
1 | #ifndef TILESET_H
2 | #define TILESET_H
3 |
4 | /*************************************************************************
5 | * Copyright (C) 2014 by Hugo Pereira Da Costa *
6 | * *
7 | * This program is free software; you can redistribute it and/or modify *
8 | * it under the terms of the GNU General Public License as published by *
9 | * the Free Software Foundation; either version 2 of the License, or *
10 | * (at your option) any later version. *
11 | * *
12 | * This program is distributed in the hope that it will be useful, *
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 | * GNU General Public License for more details. *
16 | * *
17 | * You should have received a copy of the GNU General Public License *
18 | * along with this program; if not, write to the *
19 | * Free Software Foundation, Inc., *
20 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
21 | *************************************************************************/
22 |
23 |
24 | #include
25 | #include
26 | #include
27 |
28 | //* handles proper scaling of pixmap to match widget rect.
29 | /**
30 | tilesets are collections of stretchable pixmaps corresponding to a given widget corners, sides, and center.
31 | corner pixmaps are never stretched. center pixmaps are
32 | */
33 | class TileSet
34 | {
35 | public:
36 | /**
37 | Create a TileSet from a pixmap. The size of the bottom/right chunks is
38 | whatever is left over from the other chunks, whose size is specified
39 | in the required parameters.
40 |
41 | @param w1 width of the left chunks
42 | @param h1 height of the top chunks
43 | @param w2 width of the not-left-or-right chunks
44 | @param h2 height of the not-top-or-bottom chunks
45 | */
46 | TileSet(const QPixmap&, int w1, int h1, int w2, int h2 );
47 |
48 | //* empty constructor
49 | TileSet();
50 |
51 | //* destructor
52 | virtual ~TileSet()
53 | {}
54 |
55 | /**
56 | Flags specifying what sides to draw in ::render. Corners are drawn when
57 | the sides forming that corner are drawn, e.g. Top|Left draws the
58 | top-center, center-left, and top-left chunks. The center-center chunk is
59 | only drawn when Center is requested.
60 | */
61 | enum Tile {
62 | Top = 0x1,
63 | Left = 0x2,
64 | Bottom = 0x4,
65 | Right = 0x8,
66 | Center = 0x10,
67 | TopLeft = Top|Left,
68 | TopRight = Top|Right,
69 | BottomLeft = Bottom|Left,
70 | BottomRight = Bottom|Right,
71 | Ring = Top|Left|Bottom|Right,
72 | Horizontal = Left|Right|Center,
73 | Vertical = Top|Bottom|Center,
74 | Full = Ring|Center
75 | };
76 | Q_DECLARE_FLAGS(Tiles, Tile)
77 |
78 | /**
79 | Fills the specified rect with tiled chunks. Corners are never tiled,
80 | edges are tiled in one direction, and the center chunk is tiled in both
81 | directions. Partial tiles are used as needed so that the entire rect is
82 | perfectly filled. Filling is performed as if all chunks are being drawn.
83 | */
84 | void render(const QRect&, QPainter*, Tiles = Ring) const;
85 |
86 | //* return size associated to this tileset
87 | QSize size() const
88 | { return QSize( _w1 + _w3, _h1 + _h3 ); }
89 |
90 | //* is valid
91 | bool isValid() const
92 | { return _pixmaps.size() == 9; }
93 |
94 | //* returns pixmap for given index
95 | QPixmap pixmap( int index ) const
96 | { return _pixmaps[index]; }
97 |
98 | protected:
99 |
100 | //* shortcut to pixmap list
101 | using PixmapList = QVector;
102 |
103 | //* initialize pixmap
104 | void initPixmap( PixmapList&, const QPixmap&, int w, int h, const QRect& );
105 |
106 | private:
107 |
108 | //* pixmap arry
109 | PixmapList _pixmaps;
110 |
111 | // dimensions
112 | int _w1;
113 | int _h1;
114 | int _w3;
115 | int _h3;
116 |
117 | };
118 |
119 | Q_DECLARE_OPERATORS_FOR_FLAGS(TileSet::Tiles)
120 |
121 | #endif //TILESET_H
122 |
--------------------------------------------------------------------------------