diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..ad1404ec
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+release/
+iconinstaller.tar.gz
diff --git a/CMakeLists.txt b/CMakeLists.txt
deleted file mode 100644
index ee76b30e..00000000
--- a/CMakeLists.txt
+++ /dev/null
@@ -1,41 +0,0 @@
-add_definitions(-DTRANSLATION_DOMAIN=\"plasma_applet_org.kde.plasma.taskmanager\")
-
-plasma_install_package(package org.kde.plasma.taskmanager)
-
-set(taskmanagerplugin_SRCS
- plugin/backend.cpp
- plugin/taskmanagerplugin.cpp
-
- plugin/smartlaunchers/smartlauncherbackend.cpp
- plugin/smartlaunchers/smartlauncheritem.cpp
-)
-
-ecm_qt_declare_logging_category(taskmanagerplugin_SRCS
- HEADER log_settings.h
- IDENTIFIER TASKMANAGER_DEBUG
- CATEGORY_NAME org.kde.plasma.taskmanager)
-
-install(FILES plugin/qmldir DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/plasma/private/taskmanager)
-
-add_library(taskmanagerplugin SHARED ${taskmanagerplugin_SRCS})
-
-# FIXME Cleanup no longer used libs.
-target_link_libraries(taskmanagerplugin
- Qt::Core
- Qt::DBus
- Qt::Qml
- Qt::Quick
- Plasma::Activities
- Plasma::ActivitiesStats
- KF6::I18n
- KF6::KIOCore
- KF6::KIOGui
- KF6::KIOFileWidgets # KFilePlacesModel
- KF6::Notifications # KNotificationJobUiDelegate
- Plasma::Plasma
- KSysGuard::ProcessCore
- KF6::Service
- KF6::WindowSystem
- PW::LibNotificationManager)
-
-install(TARGETS taskmanagerplugin DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/plasma/private/taskmanager)
diff --git a/FUNDING.yml b/FUNDING.yml
new file mode 100644
index 00000000..d5380694
--- /dev/null
+++ b/FUNDING.yml
@@ -0,0 +1 @@
+ko_fi: alexankitty
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..f288702d
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/Messages.sh b/Messages.sh
deleted file mode 100644
index c86f8d75..00000000
--- a/Messages.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#! /usr/bin/env bash
-$XGETTEXT `find . -name \*.js -o -name \*.qml -o -name \*.cpp` -o $podir/plasma_applet_org.kde.plasma.taskmanager.pot
diff --git a/README.MD b/README.MD
new file mode 100644
index 00000000..46a62c07
--- /dev/null
+++ b/README.MD
@@ -0,0 +1,101 @@
+# FancyTasks+
+
+
+
+This is a fork of [FancyTasks](https://github.com//alexankitty/FancyTasks) and [Luis Bocanegra's plasma6,hover-zoom branch](https://github.com/luisbocanegra/FancyTasks) with additional features:
+- Configurable icon zoom on hover
+- With better padding on hover previews
+- With a option in configuration which allows you to choose apps to pin and rearrange them
+
+# Contact
+You can reach me at ideas@alexankitty.com, by putting an issue on this github, by commenting on the [AUR package](https://aur.archlinux.org/packages/fancytasks), by commenting on the kde-look page, or on the [FancyTasks KDE Reddit community](https://www.reddit.com/r/fancytaskskde/).
+
+## Installing
+
+If your distro does not include kirigami2, install it now. It's required for the settings, and used for the dominant icon color effect. Python3 is required for installing.
+
+**Note for Plasma 6:** This plasmoid requires `Qt5Compat.GraphicalEffects` (often packaged as `qt6-5compat` or similar) to be installed on your system.
+
+1. Clone this repository with the following command `git clone https://github.com/alexankitty/FancyTasks`
+2. CD into the folder and run the script `install.sh`, you may need to chmod +x or prepend your shell before the script in order to run it
+3. Right click your panel, click add widgets and search for fancy tasks.
+4. Right click the new task panel and click Configure Fancy Tasks... - everything you will want to configure to enable the new features will be in there. Default features are based on the KDE's team existing vision.
+
+(Alternatively you should be able to double click the shell script to run it)
+
+(This package is also on the AUR and can be installed from there)
+
+## Updating
+
+1. Run the update.sh script, it will automatically stop and restart plasma to make sure the new changes get loaded in.
+2. It is recommended you log out and back into your session. plasmashell seems to exhibit some odd behaviors if the plasmoid is updated without clearing the session.
+
+## Features
+
+Features improvements tweaks and options to the original task manager plasmoid made by Eike Hein, licensed under GPL V2.0: [link](https://github.com/KDE/plasma-desktop/tree/master/applets/taskmanager).
+
+* Implemented adjustable task width.
+* Implemented switching between taskbar types.
+* Indicators based on dash to panel styles from gnome, using [latte-indicator-dashtopanel](https://github.com/psifidotos/latte-indicator-dashtopanel) as the reference implementation.
+* Customizable indicator locations and colorizing of plasma. Note: not every plasma theme will color in a pleasant way as it depends on how the SVGs are made. General rule of thumb is bright white does not colorize well.
+* Animations (Thanks ziomek64!)
+* Growing/Shrinking and desaturation of indicators on minimize (Thanks ziomek64!)
+* Customization of number of indicators displayed - limit 10.
+
+
+## Known Issues
+* Does not support LTS currently! (fix me pls)
+
+## To Do
+* Fix LTS support
+* Correct Metro to have *no* spacing (the multi app running indicator doesn't have a spacing in win10 and I apparently missed this)
+* Allow customization of the space between indicators (may have dangerous implications, required for presets)
+* Allow changing of animation duration, and possibly animating of button color and frame transitions.
+* Change styles (Metro, Ciliora, Dashes) to presets, switch preset to custom when modifying settings. Add in Dots as a preset.
+* Fix naming conventions to be more clear to users.
+
+## Translation Help Wanted!
+Information regarding submitting translations is available [here](https://github.com/alexankitty/FancyTasks/blob/main/package/translate/ReadMe.md).
+Important note: Any time an update to strings is made, the translations need to be merged and a new po template needs to be created.
+Finally, the metadata.json is done separately.
+
+## Pictures
+Colorful Tasks
+
+
+
+Metro
+
+
+
+Ciliora
+
+
+
+Dashes
+
+
+
+Dots
+
+
+
+Desaturate minimized tasks
+
+
+
+Minimized task shrinking
+
+
+
+## Extra Notes
+I'm a solo developer working on this project in my free time so it may take me a bit of time to work on issues. You can help me out by either doing a PR with fixes and I'll validate that they work in the code, or put in an issue for things that need to be changed.
+Especially helpful will be settings names to make them more clear. I have a code bias unfortunately so it does make it exceedingly difficult to convey what everything does.
+Lastly any feedback is appreciated, and I'll try to implement it as soon as I can.
+
+## Supporters
+catraxx
+Kloud
+
+If you want to support the project and have your name added to the readme, you may do so below.
+[](https://ko-fi.com/Q5Q4IFNAO)
diff --git a/Screenshot_20250324_211131.png b/Screenshot_20250324_211131.png
new file mode 100644
index 00000000..6df056f4
Binary files /dev/null and b/Screenshot_20250324_211131.png differ
diff --git a/build.sh b/build.sh
new file mode 100755
index 00000000..a3c122ba
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+SCRIPT_DIR=$(dirname $(readlink -f "$0"))
+
+cd "$SCRIPT_DIR/package/translate/"
+bash ./merge
+bash ./build
+cd "$SCRIPT_DIR"
+
+rm -rf "$SCRIPT_DIR/release"
+mkdir -p "$SCRIPT_DIR/build"
+mkdir -p "$SCRIPT_DIR/release"
+
+cp -r "$SCRIPT_DIR/package/contents" "$SCRIPT_DIR/build"
+cp "$SCRIPT_DIR/package/metadata.json" "$SCRIPT_DIR/build"
+cp "$SCRIPT_DIR/package/FancyTasks.png" "$SCRIPT_DIR/build"
+
+cd "$SCRIPT_DIR/build"
+tar cf "$SCRIPT_DIR/release/FancyTasks.tar.gz" .
+cd "$SCRIPT_DIR"
+
+rm -rf "$SCRIPT_DIR/build"
+echo "Build complete: $SCRIPT_DIR/release/FancyTasks.tar.gz"
diff --git a/docs/ciliora.png b/docs/ciliora.png
new file mode 100644
index 00000000..89650699
Binary files /dev/null and b/docs/ciliora.png differ
diff --git a/docs/colorfulicons crop.png b/docs/colorfulicons crop.png
new file mode 100644
index 00000000..299f7106
Binary files /dev/null and b/docs/colorfulicons crop.png differ
diff --git a/docs/dashes.png b/docs/dashes.png
new file mode 100644
index 00000000..cff914be
Binary files /dev/null and b/docs/dashes.png differ
diff --git a/docs/desaturate crop.png b/docs/desaturate crop.png
new file mode 100644
index 00000000..7fad965a
Binary files /dev/null and b/docs/desaturate crop.png differ
diff --git a/docs/dots.png b/docs/dots.png
new file mode 100644
index 00000000..a73791ae
Binary files /dev/null and b/docs/dots.png differ
diff --git a/docs/metro.png b/docs/metro.png
new file mode 100644
index 00000000..edd6507b
Binary files /dev/null and b/docs/metro.png differ
diff --git a/docs/shrink crop.png b/docs/shrink crop.png
new file mode 100644
index 00000000..6e898245
Binary files /dev/null and b/docs/shrink crop.png differ
diff --git a/iconinstall.sh b/iconinstall.sh
new file mode 100755
index 00000000..f66ab480
--- /dev/null
+++ b/iconinstall.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+SCRIPT_DIR=$(dirname $(readlink -f "$0"))
+mkdir -p ~/.local/share/icons/hicolor/256x256/apps/
+cp "$SCRIPT_DIR/package/FancyTasks.png" ~/.local/share/icons/hicolor/256x256/apps/FancyTasks.png
+echo "Icon installed."
diff --git a/install.sh b/install.sh
new file mode 100755
index 00000000..12ac26dc
--- /dev/null
+++ b/install.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+SCRIPT_DIR=$(dirname $(readlink -f "$0"))
+
+echo "Compiling translations..."
+bash "$SCRIPT_DIR/package/translate/build"
+
+echo "Installing plasmoid ..."
+kpackagetool6 -t Plasma/Applet --install "$SCRIPT_DIR/package"
+
+bash "$SCRIPT_DIR/iconinstall.sh"
+echo "Install complete."
diff --git a/package/FancyTasks.png b/package/FancyTasks.png
new file mode 100644
index 00000000..0de6fd73
Binary files /dev/null and b/package/FancyTasks.png differ
diff --git a/package/contents/config/config.qml b/package/contents/config/config.qml
index 9ed9787b..0a4a85b0 100644
--- a/package/contents/config/config.qml
+++ b/package/contents/config/config.qml
@@ -10,13 +10,23 @@ import org.kde.plasma.configuration
ConfigModel {
ConfigCategory {
- name: i18n("Appearance")
- icon: "preferences-desktop-color"
- source: "ConfigAppearance.qml"
+ name: i18n("Appearance")
+ icon: "preferences-desktop-color"
+ source: "ConfigAppearance.qml"
}
ConfigCategory {
- name: i18n("Behavior")
- icon: "preferences-desktop"
- source: "ConfigBehavior.qml"
+ name: i18n("Behavior")
+ icon: "preferences-desktop"
+ source: "ConfigBehavior.qml"
+ }
+ ConfigCategory {
+ name: i18n("Indicators")
+ icon: "preferences-desktop-navigation"
+ source: "ConfigIndicators.qml"
+ }
+ ConfigCategory {
+ name: i18n("Pinned Applications")
+ icon: "window-pin"
+ source: "ConfigPinnedApps.qml"
}
}
diff --git a/package/contents/config/main.xml b/package/contents/config/main.xml
index 9c1404fe..dcb8c83e 100644
--- a/package/contents/config/main.xml
+++ b/package/contents/config/main.xml
@@ -1,21 +1,26 @@
-
-
-
+
+
+
-
+
false
-
+
true
-
+
true
@@ -27,48 +32,69 @@
true
-
+
+ 1
+
+
+
1
-
+
0
-
+
true
-
+
true
-
-
+
+
-
-
+
+
-
+
1
-
+
true
-
+
true
-
+
1
1
+
+
+ 200
+ 1
+
-
+
false
@@ -78,69 +104,236 @@
-
-
-
+
+
+
1
-
+
true
-
+
true
-
+
true
-
- applications:systemsettings.desktop,applications:org.kde.discover.desktop,preferred://filemanager,preferred://browser
+
+ applications:systemsettings.desktop,applications:org.kde.discover.desktop,preferred://filemanager,preferred://browser
-
-
-
-
-
-
+
+
+
+
+
+
2
-
+
true
+
+
+ 100
+
+
+
+ 32
+
+
+
+ false
+
true
-
+
true
-
+
0
-
+
true
-
+
false
-
+
+ 1
+
+
+
+ 0
+
+
+
+ false
+
+
+
+ #00FF00
+
+
+
+ false
+
+
+
+ true
+
+
+
1
+
+
+ 0
+
+
+
+ 0
+
+
+
+ 0
+
+
+
+ 4
+
+
+
+ false
+
+
+
+ false
+
+
+
+ 100
+
+
+
+ 0
+
+
+
+ 4
+
+
+
+ 8
+
+
+
+ 0
+
+
+
+ 4
+
+
+
+ false
+
+
+
+ true
+
+
+
+ white
+
+
+
+ True
+
+
+
+ 0
+
+
+
+ false
+
+
+
+ false
+
+
+
+ true
+
+
+
+ #FFFFFF
+
+
+
+ false
+
+
+
+ false
+
+
+
+ false
+
+
+
+ 0
+
+
+
+ false
+
+
+
+ false
+
+
+ 20
+
+ How much the icon should grow when hovered
+
+
+ 250
+
+ Duration of the zoom animation in milliseconds
+
-
diff --git a/package/contents/locale/nl/LC_MESSAGES/plasma_applet_alexankitty.fancytasks.mo b/package/contents/locale/nl/LC_MESSAGES/plasma_applet_alexankitty.fancytasks.mo
new file mode 100644
index 00000000..083ee067
Binary files /dev/null and b/package/contents/locale/nl/LC_MESSAGES/plasma_applet_alexankitty.fancytasks.mo differ
diff --git a/package/contents/locale/pt_BR/LC_MESSAGES/plasma_applet_alexankitty.fancytasks.mo b/package/contents/locale/pt_BR/LC_MESSAGES/plasma_applet_alexankitty.fancytasks.mo
new file mode 100644
index 00000000..002de4e6
Binary files /dev/null and b/package/contents/locale/pt_BR/LC_MESSAGES/plasma_applet_alexankitty.fancytasks.mo differ
diff --git a/package/contents/locale/ru_RU/LC_MESSAGES/plasma_applet_alexankitty.fancytasks.mo b/package/contents/locale/ru_RU/LC_MESSAGES/plasma_applet_alexankitty.fancytasks.mo
new file mode 100644
index 00000000..0276148c
Binary files /dev/null and b/package/contents/locale/ru_RU/LC_MESSAGES/plasma_applet_alexankitty.fancytasks.mo differ
diff --git a/package/contents/locale/zh_CN/LC_MESSAGES/plasma_applet_alexankitty.fancytasks.mo b/package/contents/locale/zh_CN/LC_MESSAGES/plasma_applet_alexankitty.fancytasks.mo
new file mode 100644
index 00000000..af193e54
Binary files /dev/null and b/package/contents/locale/zh_CN/LC_MESSAGES/plasma_applet_alexankitty.fancytasks.mo differ
diff --git a/package/contents/ui/ConfigAppearance.qml b/package/contents/ui/ConfigAppearance.qml
index 1aeadb8d..9cb9f9ce 100644
--- a/package/contents/ui/ConfigAppearance.qml
+++ b/package/contents/ui/ConfigAppearance.qml
@@ -5,33 +5,170 @@
*/
import QtQuick
-import QtQuick.Controls as QQC2
+import QtQuick.Controls
import QtQuick.Layouts
-import org.kde.kcmutils as KCMUtils
import org.kde.kirigami as Kirigami
import org.kde.plasma.core as PlasmaCore
import org.kde.plasma.plasmoid
+import org.kde.kquickcontrols as KQuickAddons
-KCMUtils.SimpleKCM {
+Kirigami.ScrollablePage {
readonly property bool plasmaPaAvailable: Qt.createComponent("PulseAudio.qml").status === Component.Ready
readonly property bool plasmoidVertical: Plasmoid.formFactor === PlasmaCore.Types.Vertical
- readonly property bool iconOnly: Plasmoid.pluginName === "org.kde.plasma.icontasks"
+ readonly property bool iconOnly: plasmoid.configuration.iconOnly
+ property alias cfg_iconZoomFactor: iconZoomFactor.value
+ property alias cfg_iconZoomDuration: iconZoomDuration.value
property alias cfg_showToolTips: showToolTips.checked
property alias cfg_highlightWindows: highlightWindows.checked
property bool cfg_indicateAudioStreams
- property alias cfg_fill: fill.checked
- property alias cfg_maxStripes: maxStripes.value
+ property alias cfg_iconScale: iconScale.value
+ property alias cfg_iconSizePx: iconSizePx.value
+ property alias cfg_iconSizeOverride: iconSizeOverride.checked
property alias cfg_forceStripes: forceStripes.checked
+ property alias cfg_maxStripes: maxStripes.value
property alias cfg_taskMaxWidth: taskMaxWidth.currentIndex
+ property alias cfg_maxButtonLength: maxButtonLength.value
property int cfg_iconSpacing: 0
+ property alias cfg_fill: fill.checked
+
+ property alias cfg_useBorders: useBorders.checked
+ property alias cfg_taskSpacingSize: taskSpacingSize.value
+
+ property alias cfg_buttonColorize: buttonColorize.checked
+ property alias cfg_buttonColorizeInactive: buttonColorizeInactive.checked
+ property alias cfg_buttonColorizeDominant: buttonColorizeDominant.checked
+ property alias cfg_buttonColorizeCustom: buttonColorizeCustom.color
+
+ property alias cfg_disableButtonSvg: disableButtonSvg.checked
+ property alias cfg_disableButtonInactiveSvg: disableButtonInactiveSvg.checked
+ property alias cfg_overridePlasmaButtonDirection: overridePlasmaButtonDirection.checked
+ property alias cfg_plasmaButtonDirection: plasmaButtonDirection.currentIndex
+
+ // --- Properties to silence KCM errors ---
+ // Defaults for existing aliases
+ property var cfg_iconZoomFactorDefault
+ property var cfg_iconZoomDurationDefault
+ property var cfg_showToolTipsDefault
+ property var cfg_highlightWindowsDefault
+ property var cfg_indicateAudioStreamsDefault
+ property var cfg_iconScaleDefault
+ property var cfg_iconSizePxDefault
+ property var cfg_iconSizeOverrideDefault
+ property var cfg_forceStripesDefault
+ property var cfg_maxStripesDefault
+ property var cfg_taskMaxWidthDefault
+ property var cfg_maxButtonLengthDefault
+ property var cfg_iconSpacingDefault
+ property var cfg_fillDefault
+ property var cfg_useBordersDefault
+ property var cfg_taskSpacingSizeDefault
+ property var cfg_buttonColorizeDefault
+ property var cfg_buttonColorizeInactiveDefault
+ property var cfg_buttonColorizeDominantDefault
+ property var cfg_buttonColorizeCustomDefault
+ property var cfg_disableButtonSvgDefault
+ property var cfg_disableButtonInactiveSvgDefault
+ property var cfg_overridePlasmaButtonDirectionDefault
+ property var cfg_plasmaButtonDirectionDefault
+
+ // Missing properties from main.xml not used in this tab
+ property var cfg_showOnlyCurrentScreen
+ property var cfg_showOnlyCurrentScreenDefault
+ property var cfg_showOnlyCurrentDesktop
+ property var cfg_showOnlyCurrentDesktopDefault
+ property var cfg_showOnlyCurrentActivity
+ property var cfg_showOnlyCurrentActivityDefault
+ property var cfg_showOnlyMinimized
+ property var cfg_showOnlyMinimizedDefault
+ property var cfg_unhideOnAttention
+ property var cfg_unhideOnAttentionDefault
+ property var cfg_groupingStrategy
+ property var cfg_groupingStrategyDefault
+ property var cfg_iconOnly // Read via plasmoid.configuration
+ property var cfg_iconOnlyDefault
+ property var cfg_groupedTaskVisualization
+ property var cfg_groupedTaskVisualizationDefault
+ property var cfg_groupPopups
+ property var cfg_groupPopupsDefault
+ property var cfg_onlyGroupWhenFull
+ property var cfg_onlyGroupWhenFullDefault
+ property var cfg_groupingAppIdBlacklist
+ property var cfg_groupingAppIdBlacklistDefault
+ property var cfg_groupingLauncherUrlBlacklist
+ property var cfg_groupingLauncherUrlBlacklistDefault
+ property var cfg_sortingStrategy
+ property var cfg_sortingStrategyDefault
+ property var cfg_separateLaunchers
+ property var cfg_separateLaunchersDefault
+ property var cfg_hideLauncherOnStart
+ property var cfg_hideLauncherOnStartDefault
+ property var cfg_wheelEnabled
+ property var cfg_wheelEnabledDefault
+ property var cfg_wheelSkipMinimized
+ property var cfg_wheelSkipMinimizedDefault
+ property var cfg_launchers
+ property var cfg_launchersDefault
+ property var cfg_middleClickAction
+ property var cfg_middleClickActionDefault
+ property var cfg_taskHoverEffect
+ property var cfg_taskHoverEffectDefault
+ property var cfg_maxTextLines
+ property var cfg_maxTextLinesDefault
+ property var cfg_minimizeActiveTaskOnClick
+ property var cfg_minimizeActiveTaskOnClickDefault
+ property var cfg_reverseMode
+ property var cfg_reverseModeDefault
+ property var cfg_indicatorsEnabled
+ property var cfg_indicatorsEnabledDefault
+ property var cfg_indicatorProgress
+ property var cfg_indicatorProgressDefault
+ property var cfg_indicatorProgressColor
+ property var cfg_indicatorProgressColorDefault
+ property var cfg_disableInactiveIndicators
+ property var cfg_disableInactiveIndicatorsDefault
+ property var cfg_indicatorsAnimated
+ property var cfg_indicatorsAnimatedDefault
+ property var cfg_groupIconEnabled
+ property var cfg_groupIconEnabledDefault
+ property var cfg_indicatorLocation
+ property var cfg_indicatorLocationDefault
+ property var cfg_indicatorStyle
+ property var cfg_indicatorStyleDefault
+ property var cfg_indicatorMinLimit
+ property var cfg_indicatorMinLimitDefault
+ property var cfg_indicatorMaxLimit
+ property var cfg_indicatorMaxLimitDefault
+ property var cfg_indicatorDesaturate
+ property var cfg_indicatorDesaturateDefault
+ property var cfg_indicatorGrow
+ property var cfg_indicatorGrowDefault
+ property var cfg_indicatorGrowFactor
+ property var cfg_indicatorGrowFactorDefault
+ property var cfg_indicatorEdgeOffset
+ property var cfg_indicatorEdgeOffsetDefault
+ property var cfg_indicatorSize
+ property var cfg_indicatorSizeDefault
+ property var cfg_indicatorLength
+ property var cfg_indicatorLengthDefault
+ property var cfg_indicatorRadius
+ property var cfg_indicatorRadiusDefault
+ property var cfg_indicatorShrink
+ property var cfg_indicatorShrinkDefault
+ property var cfg_indicatorDominantColor
+ property var cfg_indicatorDominantColorDefault
+ property var cfg_indicatorAccentColor
+ property var cfg_indicatorAccentColorDefault
+ property var cfg_indicatorCustomColor
+ property var cfg_indicatorCustomColorDefault
+ property var cfg_indicatorReverse
+ property var cfg_indicatorReverseDefault
+ property var cfg_indicatorOverride
+ property var cfg_indicatorOverrideDefault
+ // -------------------------------------------------------------------
Component.onCompleted: {
- /* Don't rely on bindings for checking the radiobuttons
- When checking forceStripes, the condition for the checked value for the allow stripes button
- became true and that one got checked instead, stealing the checked state for the just clicked checkbox
- */
if (maxStripes.value === 1) {
forbidStripes.checked = true;
} else if (!Plasmoid.configuration.forceStripes && maxStripes.value > 1) {
@@ -40,29 +177,189 @@ KCMUtils.SimpleKCM {
forceStripes.checked = true;
}
}
+
Kirigami.FormLayout {
- QQC2.CheckBox {
+ CheckBox {
+ id: useBorders
+ text: i18n("Use plasma borders")
+ }
+
+ Item {
+ Kirigami.FormData.isSection: true
+ }
+
+ Slider {
+ id: iconScale
+ Layout.fillWidth: true
+ from: 0
+ to: 300
+ stepSize: 1.0
+ Kirigami.FormData.label: i18n("Icon Scale") + " " + iconScale.valueAt(iconScale.position) + "%"
+ visible: !iconSizeOverride.checked
+ }
+
+ Item {
+ Kirigami.FormData.isSection: true
+ Kirigami.FormData.label: i18n("Icon Hover Effects")
+ }
+
+ SpinBox {
+ id: iconZoomFactor
+ Kirigami.FormData.label: i18n("Icon zoom factor (px):")
+ from: 0
+ to: 50
+ stepSize: 1
+ value: plasmoid.configuration.iconZoomFactor
+
+ ToolTip.delay: 1000
+ ToolTip.visible: hovered
+ ToolTip.text: i18n("How much the icon should grow when hovered (in pixels)")
+ }
+
+ SpinBox {
+ id: iconZoomDuration
+ Kirigami.FormData.label: i18n("Zoom animation duration (ms):")
+ from: 0
+ to: 1000
+ stepSize: 50
+ value: plasmoid.configuration.iconZoomDuration
+
+ ToolTip.delay: 1000
+ ToolTip.visible: hovered
+ ToolTip.text: i18n("Duration of the zoom animation in milliseconds")
+ }
+
+ SpinBox {
+ id: iconSizePx
+ Kirigami.FormData.label: i18n("Icon Size (px):")
+ from: 0
+ to: 999
+ visible: iconSizeOverride.checked
+ }
+
+ CheckBox {
+ id: iconSizeOverride
+ text: i18n("Set icon size instead of scaling")
+ }
+
+ Item {
+ Kirigami.FormData.isSection: true
+ }
+
+ ButtonGroup {
+ id: colorizeButtonGroup
+ }
+
+ RadioButton {
+ Kirigami.FormData.label: i18n("Button Colors:")
+ checked: !buttonColorize.checked
+ text: i18n("Using Plasma Style/Accent")
+ ButtonGroup.group: colorizeButtonGroup
+ }
+
+ RadioButton {
+ id: buttonColorize
+ checked: plasmoid.configuration.buttonColorize === true
+ text: i18n("Using Color Overlay")
+ ButtonGroup.group: colorizeButtonGroup
+ }
+
+ CheckBox {
+ id: buttonColorizeDominant
+ enabled: buttonColorize.checked
+ text: i18n("Use dominant icon color")
+ visible: buttonColorize.checked
+ }
+
+ KQuickAddons.ColorButton {
+ id: buttonColorizeCustom
+ Layout.leftMargin: Kirigami.Units.gridUnit
+ enabled: buttonColorize.checked & !buttonColorizeDominant.checked
+ Kirigami.FormData.label: i18n("Custom Color:")
+ showAlphaChannel: true
+ visible: buttonColorize.checked && !buttonColorizeDominant.checked
+ }
+
+ CheckBox {
+ id: buttonColorizeInactive
+ text: i18n("Colorize inactive buttons")
+ visible: buttonColorize.checked
+ enabled: !disableButtonInactiveSvg.checked
+ }
+
+ Item {
+ Kirigami.FormData.isSection: true
+ }
+
+ CheckBox {
+ id: disableButtonSvg
+ Kirigami.FormData.label: i18n("Plasma Button Decorations:")
+ text: i18n("Disable All")
+ }
+ CheckBox {
+ id: disableButtonInactiveSvg
+ text: i18n("Disable Inactive Buttons")
+ enabled: !disableButtonSvg.checked
+ }
+
+ CheckBox {
+ id: overridePlasmaButtonDirection
+ Kirigami.FormData.label: i18n("Plasma Button Direction:")
+ text: i18n("Override")
+ }
+
+ Label {
+ text: i18n("Be sure to use this when using as a floating widget")
+ font: Kirigami.Theme.smallFont
+ }
+
+ ComboBox {
+ id: plasmaButtonDirection
+ visible: overridePlasmaButtonDirection.checked
+ model: [i18n("North"), i18n("South"), i18n("West"), i18n("East")]
+ }
+
+ Item {
+ Kirigami.FormData.isSection: true
+ }
+
+ SpinBox {
+ id: maxButtonLength
+ visible: !plasmoidVertical && !iconOnly
+ Kirigami.FormData.label: i18n("Maximum button length (px):")
+ from: 1
+ to: 9999
+ }
+
+ SpinBox {
+ id: taskSpacingSize
+ Kirigami.FormData.label: i18n("Space between taskbar items (px):")
+ from: 0
+ to: 99
+ }
+
+ CheckBox {
id: showToolTips
- Kirigami.FormData.label: i18nc("@label for several checkboxes", "General:")
- text: i18nc("@option:check section General", "Show small window previews when hovering over tasks")
+ Kirigami.FormData.label: i18n("General:")
+ text: i18n("Show small window previews when hovering over tasks")
}
- QQC2.CheckBox {
+ CheckBox {
id: highlightWindows
- text: i18nc("@option:check section General", "Hide other windows when hovering over previews")
+ text: i18n("Hide other windows when hovering over previews")
}
- QQC2.CheckBox {
+ CheckBox {
id: indicateAudioStreams
- text: i18nc("@option:check section General", "Mark applications that play audio")
+ text: i18n("Mark applications that play audio")
checked: cfg_indicateAudioStreams && plasmaPaAvailable
onToggled: cfg_indicateAudioStreams = checked
enabled: plasmaPaAvailable
}
- QQC2.CheckBox {
+ CheckBox {
id: fill
- text: i18nc("@option:check section General", "Fill free space on panel")
+ text: i18n("Fill free space on panel")
}
Item {
@@ -70,62 +367,54 @@ KCMUtils.SimpleKCM {
visible: !iconOnly
}
- QQC2.ComboBox {
+ ComboBox {
id: taskMaxWidth
visible: !iconOnly && !plasmoidVertical
- Kirigami.FormData.label: i18nc("@label:listbox", "Maximum task width:")
+ Kirigami.FormData.label: i18n("Maximum task width:")
- model: [
- i18nc("@item:inlistbox how wide a task item should be", "Narrow"),
- i18nc("@item:inlistbox how wide a task item should be", "Medium"),
- i18nc("@item:inlistbox how wide a task item should be", "Wide")
- ]
+ model: [i18n("Narrow"), i18n("Medium"), i18n("Wide")]
}
Item {
Kirigami.FormData.isSection: true
}
- QQC2.RadioButton {
+ RadioButton {
id: forbidStripes
- Kirigami.FormData.label: plasmoidVertical
- ? i18nc("@label for radio button group, completes sentence: … when panel is low on space etc.", "Use multi-column view:")
- : i18nc("@label for radio button group, completes sentence: … when panel is low on space etc.", "Use multi-row view:")
+ Kirigami.FormData.label: plasmoidVertical ? i18n("Use multi-column view:") : i18n("Use multi-row view:")
onToggled: {
if (checked) {
- maxStripes.value = 1
+ maxStripes.value = 1;
}
}
- text: i18nc("@option:radio Never use multi-column view for Task Manager", "Never")
+ text: i18n("Never")
}
- QQC2.RadioButton {
+ RadioButton {
id: allowStripes
onToggled: {
if (checked) {
- maxStripes.value = Math.max(2, maxStripes.value)
+ maxStripes.value = Math.max(2, maxStripes.value);
}
}
- text: i18nc("@option:radio completes sentence: Use multi-column/row view", "When panel is low on space and thick enough")
+ text: i18n("When panel is low on space and thick enough")
}
- QQC2.RadioButton {
+ RadioButton {
id: forceStripes
onToggled: {
if (checked) {
- maxStripes.value = Math.max(2, maxStripes.value)
+ maxStripes.value = Math.max(2, maxStripes.value);
}
}
- text: i18nc("@option:radio completes sentence: Use multi-column/row view", "Always when panel is thick enough")
+ text: i18n("Always when panel is thick enough")
}
- QQC2.SpinBox {
+ SpinBox {
id: maxStripes
enabled: maxStripes.value > 1
- Kirigami.FormData.label: plasmoidVertical
- ? i18nc("@label:spinbox maximum number of columns for tasks", "Maximum columns:")
- : i18nc("@label:spinbox maximum number of rows for tasks", "Maximum rows:")
+ Kirigami.FormData.label: plasmoidVertical ? i18n("Maximum columns:") : i18n("Maximum rows:")
from: 1
}
@@ -133,21 +422,21 @@ KCMUtils.SimpleKCM {
Kirigami.FormData.isSection: true
}
- QQC2.ComboBox {
+ ComboBox {
visible: iconOnly
- Kirigami.FormData.label: i18nc("@label:listbox", "Spacing between icons:")
+ Kirigami.FormData.label: i18n("Spacing between icons:")
model: [
{
- "label": i18nc("@item:inlistbox Icon spacing", "Small"),
+ "label": i18n("Small"),
"spacing": 0
},
{
- "label": i18nc("@item:inlistbox Icon spacing", "Normal"),
+ "label": i18n("Normal"),
"spacing": 1
},
{
- "label": i18nc("@item:inlistbox Icon spacing", "Large"),
+ "label": i18n("Large"),
"spacing": 3
},
]
@@ -161,9 +450,12 @@ KCMUtils.SimpleKCM {
}
switch (cfg_iconSpacing) {
- case 0: return 0; // Small
- case 1: return 1; // Normal
- case 3: return 2; // Large
+ case 0:
+ return 0; // Small
+ case 1:
+ return 1; // Normal
+ case 3:
+ return 2; // Large
}
}
onActivated: index => {
@@ -171,9 +463,9 @@ KCMUtils.SimpleKCM {
}
}
- QQC2.Label {
+ Label {
visible: Kirigami.Settings.tabletMode
- text: i18nc("@info:usagetip under a set of radio buttons when Touch Mode is on", "Automatically set to Large when in Touch mode")
+ text: i18n("Automatically set to Large when in Touch mode")
font: Kirigami.Theme.smallFont
}
}
diff --git a/package/contents/ui/ConfigBehavior.qml b/package/contents/ui/ConfigBehavior.qml
index ac03936e..9db8e817 100644
--- a/package/contents/ui/ConfigBehavior.qml
+++ b/package/contents/ui/ConfigBehavior.qml
@@ -5,17 +5,16 @@
*/
import QtQuick
-import QtQuick.Controls as QQC2
+import QtQuick.Controls
import QtQuick.Layouts
-import org.kde.kcmutils as KCMUtils
import org.kde.kirigami as Kirigami
import org.kde.plasma.core as PlasmaCore
import org.kde.plasma.plasmoid
import org.kde.plasma.workspace.dbus as DBus
-KCMUtils.SimpleKCM {
+Kirigami.ScrollablePage {
property alias cfg_groupingStrategy: groupingStrategy.currentIndex
property alias cfg_groupedTaskVisualization: groupedTaskVisualization.currentIndex
property alias cfg_groupPopups: groupPopups.checked
@@ -33,6 +32,134 @@ KCMUtils.SimpleKCM {
property alias cfg_minimizeActiveTaskOnClick: minimizeActive.checked
property alias cfg_unhideOnAttention: unhideOnAttention.checked
property alias cfg_reverseMode: reverseMode.checked
+ property alias cfg_iconOnly: iconOnly.currentIndex
+
+ // --- Properties to silence KCM errors ---
+ property var cfg_groupingStrategyDefault
+ property var cfg_groupedTaskVisualizationDefault
+ property var cfg_groupPopupsDefault
+ property var cfg_onlyGroupWhenFullDefault
+ property var cfg_sortingStrategyDefault
+ property var cfg_separateLaunchersDefault
+ property var cfg_hideLauncherOnStartDefault
+ property var cfg_middleClickActionDefault
+ property var cfg_wheelEnabledDefault
+ property var cfg_wheelSkipMinimizedDefault
+ property var cfg_showOnlyCurrentScreenDefault
+ property var cfg_showOnlyCurrentDesktopDefault
+ property var cfg_showOnlyCurrentActivityDefault
+ property var cfg_showOnlyMinimizedDefault
+ property var cfg_minimizeActiveTaskOnClickDefault
+ property var cfg_unhideOnAttentionDefault
+ property var cfg_reverseModeDefault
+ property var cfg_iconOnlyDefault
+
+ // Missing properties from main.xml not used in this tab
+ property var cfg_groupingAppIdBlacklist
+ property var cfg_groupingAppIdBlacklistDefault
+ property var cfg_groupingLauncherUrlBlacklist
+ property var cfg_groupingLauncherUrlBlacklistDefault
+ property var cfg_maxStripes
+ property var cfg_maxStripesDefault
+ property var cfg_maxButtonLength
+ property var cfg_maxButtonLengthDefault
+ property var cfg_forceStripes
+ property var cfg_forceStripesDefault
+ property var cfg_showToolTips
+ property var cfg_showToolTipsDefault
+ property var cfg_taskMaxWidth
+ property var cfg_taskMaxWidthDefault
+ property var cfg_highlightWindows
+ property var cfg_highlightWindowsDefault
+ property var cfg_launchers
+ property var cfg_launchersDefault
+ property var cfg_indicateAudioStreams
+ property var cfg_indicateAudioStreamsDefault
+ property var cfg_iconScale
+ property var cfg_iconScaleDefault
+ property var cfg_iconSizePx
+ property var cfg_iconSizePxDefault
+ property var cfg_iconSizeOverride
+ property var cfg_iconSizeOverrideDefault
+ property var cfg_fill
+ property var cfg_fillDefault
+ property var cfg_taskHoverEffect
+ property var cfg_taskHoverEffectDefault
+ property var cfg_maxTextLines
+ property var cfg_maxTextLinesDefault
+ property var cfg_iconSpacing
+ property var cfg_iconSpacingDefault
+ property var cfg_indicatorsEnabled
+ property var cfg_indicatorsEnabledDefault
+ property var cfg_indicatorProgress
+ property var cfg_indicatorProgressDefault
+ property var cfg_indicatorProgressColor
+ property var cfg_indicatorProgressColorDefault
+ property var cfg_disableInactiveIndicators
+ property var cfg_disableInactiveIndicatorsDefault
+ property var cfg_indicatorsAnimated
+ property var cfg_indicatorsAnimatedDefault
+ property var cfg_groupIconEnabled
+ property var cfg_groupIconEnabledDefault
+ property var cfg_indicatorLocation
+ property var cfg_indicatorLocationDefault
+ property var cfg_indicatorStyle
+ property var cfg_indicatorStyleDefault
+ property var cfg_indicatorMinLimit
+ property var cfg_indicatorMinLimitDefault
+ property var cfg_indicatorMaxLimit
+ property var cfg_indicatorMaxLimitDefault
+ property var cfg_indicatorDesaturate
+ property var cfg_indicatorDesaturateDefault
+ property var cfg_indicatorGrow
+ property var cfg_indicatorGrowDefault
+ property var cfg_indicatorGrowFactor
+ property var cfg_indicatorGrowFactorDefault
+ property var cfg_indicatorEdgeOffset
+ property var cfg_indicatorEdgeOffsetDefault
+ property var cfg_indicatorSize
+ property var cfg_indicatorSizeDefault
+ property var cfg_indicatorLength
+ property var cfg_indicatorLengthDefault
+ property var cfg_indicatorRadius
+ property var cfg_indicatorRadiusDefault
+ property var cfg_indicatorShrink
+ property var cfg_indicatorShrinkDefault
+ property var cfg_indicatorDominantColor
+ property var cfg_indicatorDominantColorDefault
+ property var cfg_indicatorAccentColor
+ property var cfg_indicatorAccentColorDefault
+ property var cfg_indicatorCustomColor
+ property var cfg_indicatorCustomColorDefault
+ property var cfg_useBorders
+ property var cfg_useBordersDefault
+ property var cfg_taskSpacingSize
+ property var cfg_taskSpacingSizeDefault
+ property var cfg_buttonColorize
+ property var cfg_buttonColorizeDefault
+ property var cfg_buttonColorizeInactive
+ property var cfg_buttonColorizeInactiveDefault
+ property var cfg_buttonColorizeDominant
+ property var cfg_buttonColorizeDominantDefault
+ property var cfg_buttonColorizeCustom
+ property var cfg_buttonColorizeCustomDefault
+ property var cfg_disableButtonSvg
+ property var cfg_disableButtonSvgDefault
+ property var cfg_disableButtonInactiveSvg
+ property var cfg_disableButtonInactiveSvgDefault
+ property var cfg_overridePlasmaButtonDirection
+ property var cfg_overridePlasmaButtonDirectionDefault
+ property var cfg_plasmaButtonDirection
+ property var cfg_plasmaButtonDirectionDefault
+ property var cfg_indicatorReverse
+ property var cfg_indicatorReverseDefault
+ property var cfg_indicatorOverride
+ property var cfg_indicatorOverrideDefault
+ property var cfg_iconZoomFactor
+ property var cfg_iconZoomFactorDefault
+ property var cfg_iconZoomDuration
+ property var cfg_iconZoomDurationDefault
+ // -----------------------------------------------------------
DBus.DBusServiceWatcher {
id: effectWatcher
@@ -41,33 +168,38 @@ KCMUtils.SimpleKCM {
}
Kirigami.FormLayout {
- anchors.left: parent.left
- anchors.right: parent.right
+ ComboBox {
+ id: iconOnly
+ Kirigami.FormData.label: i18n("Display:")
+ Layout.fillWidth: true
+ Layout.minimumWidth: Kirigami.Units.gridUnit * 14
+ model: [i18n("Show task names"), i18n("Show icons only")]
+ }
- QQC2.ComboBox {
+ ComboBox {
id: groupingStrategy
- Kirigami.FormData.label: i18nc("@label:listbox how to group tasks", "Group:")
+ Kirigami.FormData.label: i18n("Group:")
Layout.fillWidth: true
Layout.minimumWidth: Kirigami.Units.gridUnit * 14
model: [
- i18nc("@item:inlistbox how to group tasks", "Do not group"),
- i18nc("@item:inlistbox how to group tasks", "By program name")
+ i18n("Do not group"),
+ i18n("By program name")
]
}
- QQC2.ComboBox {
+ ComboBox {
id: groupedTaskVisualization
- Kirigami.FormData.label: i18nc("@label:listbox completes sentence like: … cycles through tasks", "Clicking grouped task:")
+ Kirigami.FormData.label: i18n("Clicking grouped task:")
Layout.fillWidth: true
Layout.minimumWidth: Kirigami.Units.gridUnit * 14
enabled: groupingStrategy.currentIndex !== 0
model: [
- i18nc("@item:inlistbox Completes the sentence 'Clicking grouped task cycles through tasks' ", "Cycles through tasks"),
- i18nc("@item:inlistbox Completes the sentence 'Clicking grouped task shows small window previews' ", "Shows small window previews"),
- i18nc("@item:inlistbox Completes the sentence 'Clicking grouped task shows large window previews' ", "Shows large window previews"),
- i18nc("@item:inlistbox Completes the sentence 'Clicking grouped task shows textual list' ", "Shows textual list"),
+ i18n("Cycles through tasks"),
+ i18n("Shows small window previews"),
+ i18n("Shows large window previews"),
+ i18n("Shows textual list"),
]
Accessible.name: currentText
@@ -78,24 +210,24 @@ KCMUtils.SimpleKCM {
Layout.fillWidth: true
visible: groupedTaskVisualization.currentIndex === 2 && !effectWatcher.registered
type: Kirigami.MessageType.Warning
- text: i18nc("@info displayed as InlineMessage", "The compositor does not support displaying windows side by side, so a textual list will be displayed instead.")
+ text: i18n("The compositor does not support displaying windows side by side, so a textual list will be displayed instead.")
}
Item {
Kirigami.FormData.isSection: true
}
- QQC2.CheckBox {
+ CheckBox {
id: groupPopups
- visible: (Plasmoid.pluginName !== "org.kde.plasma.icontasks")
- text: i18nc("@option:check grouped task", "Combine into single button")
+ visible: (!plasmoid.configuration.iconOnly)
+ text: i18n("Combine into single button")
enabled: groupingStrategy.currentIndex > 0
}
- QQC2.CheckBox {
+ CheckBox {
id: onlyGroupWhenFull
- visible: (Plasmoid.pluginName !== "org.kde.plasma.icontasks")
- text: i18nc("@option:check grouped task","Group only when the Task Manager is full")
+ visible: (!plasmoid.configuration.iconOnly)
+ text: i18n("Group only when the Task Manager is full")
enabled: groupingStrategy.currentIndex > 0 && groupPopups.checked
Accessible.onPressAction: toggle()
}
@@ -105,56 +237,56 @@ KCMUtils.SimpleKCM {
visible: (Plasmoid.pluginName !== "org.kde.plasma.icontasks")
}
- QQC2.ComboBox {
+ ComboBox {
id: sortingStrategy
- Kirigami.FormData.label: i18nc("@label:listbox sort tasks in grouped task", "Sort:")
+ Kirigami.FormData.label: i18n("Sort:")
Layout.fillWidth: true
Layout.minimumWidth: Kirigami.Units.gridUnit * 14
model: [
- i18nc("@item:inlistbox sort tasks in grouped task", "Do not sort"),
- i18nc("@item:inlistbox sort tasks in grouped task", "Manually"),
- i18nc("@item:inlistbox sort tasks in grouped task", "Alphabetically"),
- i18nc("@item:inlistbox sort tasks in grouped task", "By desktop"),
- i18nc("@item:inlistbox sort tasks in grouped task", "By activity")
+ i18n("Do not sort"),
+ i18n("Manually"),
+ i18n("Alphabetically"),
+ i18n("By desktop"),
+ i18n("By activity")
]
}
- QQC2.CheckBox {
+ CheckBox {
id: separateLaunchers
- visible: (Plasmoid.pluginName !== "org.kde.plasma.icontasks")
- text: i18nc("@option:check configure task sorting", "Keep launchers separate")
+ visible: (!plasmoid.configuration.iconOnly)
+ text: i18n("Keep launchers separate")
enabled: sortingStrategy.currentIndex === 1
}
- QQC2.CheckBox {
+ CheckBox {
id: hideLauncherOnStart
- visible: (Plasmoid.pluginName !== "org.kde.plasma.icontasks")
- text: i18nc("@option:check for icons-and-text task manager", "Hide launchers after application startup")
+ visible: (!plasmoid.configuration.iconOnly)
+ text: i18n("Hide launchers after application startup")
}
Item {
Kirigami.FormData.isSection: true
- visible: (Plasmoid.pluginName !== "org.kde.plasma.icontasks")
+ visible: (!plasmoid.configuration.iconOnly)
}
- QQC2.CheckBox {
+ CheckBox {
id: minimizeActive
- Kirigami.FormData.label: i18nc("@label for checkbox Part of a sentence: 'Clicking active task minimizes the task'", "Clicking active task:")
- text: i18nc("@option:check Part of a sentence: 'Clicking active task minimizes the task'", "Minimizes the task")
+ Kirigami.FormData.label: i18n("Clicking active task:")
+ text: i18n("Minimizes the task")
}
- QQC2.ComboBox {
+ ComboBox {
id: middleClickAction
- Kirigami.FormData.label: i18nc("@label:listbox completes sentence like: … does nothing", "Middle-clicking any task:")
+ Kirigami.FormData.label: i18n("Middle-clicking any task:")
Layout.fillWidth: true
Layout.minimumWidth: Kirigami.Units.gridUnit * 14
model: [
- i18nc("@item:inlistbox Part of a sentence: 'Middle-clicking any task does nothing'", "Does nothing"),
- i18nc("@item:inlistbox Part of a sentence: 'Middle-clicking any task closes window or group'", "Closes window or group"),
- i18nc("@item:inlistbox Part of a sentence: 'Middle-clicking any task opens a new window'", "Opens a new window"),
- i18nc("@item:inlistbox Part of a sentence: 'Middle-clicking any task minimizes/restores window or group'", "Minimizes/Restores window or group"),
- i18nc("@item:inlistbox Part of a sentence: 'Middle-clicking any task toggles grouping'", "Toggles grouping"),
- i18nc("@item:inlistbox Part of a sentence: 'Middle-clicking any task brings it to the current virtual desktop'", "Brings it to the current virtual desktop")
+ i18n("Does nothing"),
+ i18n("Closes window or group"),
+ i18n("Opens a new window"),
+ i18n("Minimizes/Restores window or group"),
+ i18n("Toggles grouping"),
+ i18n("Brings it to the current virtual desktop")
]
}
@@ -162,19 +294,19 @@ KCMUtils.SimpleKCM {
Kirigami.FormData.isSection: true
}
- QQC2.CheckBox {
+ CheckBox {
id: wheelEnabled
- Kirigami.FormData.label: i18nc("@label for checkbox Part of a sentence: 'Mouse wheel cycles through tasks'", "Mouse wheel:")
- text: i18nc("@option:check Part of a sentence: 'Mouse wheel cycles through tasks'", "Cycles through tasks")
+ Kirigami.FormData.label: i18n("Mouse wheel:")
+ text: i18n("Cycles through tasks")
}
RowLayout {
// HACK: Workaround for Kirigami bug 434625
- // due to which a simple Layout.leftMargin on QQC2.CheckBox doesn't work
+ // due to which a simple Layout.leftMargin on CheckBox doesn't work
Item { implicitWidth: Kirigami.Units.gridUnit }
- QQC2.CheckBox {
+ CheckBox {
id: wheelSkipMinimized
- text: i18nc("@option:check mouse wheel task cycling", "Skip minimized tasks")
+ text: i18n("Skip minimized tasks")
enabled: wheelEnabled.checked
}
}
@@ -183,77 +315,77 @@ KCMUtils.SimpleKCM {
Kirigami.FormData.isSection: true
}
- QQC2.CheckBox {
+ CheckBox {
id: showOnlyCurrentScreen
- Kirigami.FormData.label: i18nc("@label for checkbox group, completes sentence like: … from current screen", "Show only tasks:")
- text: i18nc("@option:check completes sentence: show only tasks", "From current screen")
+ Kirigami.FormData.label: i18n("Show only tasks:")
+ text: i18n("From current screen")
}
- QQC2.CheckBox {
+ CheckBox {
id: showOnlyCurrentDesktop
- text: i18nc("@option:check completes sentence: show only tasks", "From current desktop")
+ text: i18n("From current desktop")
}
- QQC2.CheckBox {
+ CheckBox {
id: showOnlyCurrentActivity
- text: i18nc("@option:check completes sentence: show only tasks", "From current activity")
+ text: i18n("From current activity")
}
- QQC2.CheckBox {
+ CheckBox {
id: showOnlyMinimized
- text: i18nc("@option:check completes sentence: show only tasks", "That are minimized")
+ text: i18n("That are minimized")
}
Item {
Kirigami.FormData.isSection: true
}
- QQC2.CheckBox {
+ CheckBox {
id: unhideOnAttention
- Kirigami.FormData.label: i18nc("@label for checkbox, completes sentence: … unhide if window wants attention", "When panel is hidden:")
- text: i18nc("@option:check completes sentence: When panel is hidden", "Unhide when a window wants attention")
+ Kirigami.FormData.label: i18n("When panel is hidden:")
+ text: i18n("Unhide when a window wants attention")
}
Item {
Kirigami.FormData.isSection: true
}
- QQC2.ButtonGroup {
+ ButtonGroup {
id: reverseModeRadioButtonGroup
}
- QQC2.RadioButton {
- Kirigami.FormData.label: i18nc("@label for radiobutton group completes sentence like: … on the bottom", "New tasks appear:")
+ RadioButton {
+ Kirigami.FormData.label: i18n("New tasks appear:")
checked: !reverseMode.checked
text: {
if (Plasmoid.formFactor === PlasmaCore.Types.Vertical) {
- return i18nc("@option:check completes sentence: New tasks appear", "On the bottom")
+ return i18n("On the bottom")
}
// horizontal
if (Qt.application.layoutDirection === Qt.LeftToRight) {
- return i18nc("@option:check completes sentence: New tasks appear", "To the right");
+ return i18n("To the right");
} else {
- return i18nc("@option:check completes sentence: New tasks appear", "To the left")
+ return i18n("To the left")
}
}
- QQC2.ButtonGroup.group: reverseModeRadioButtonGroup
+ ButtonGroup.group: reverseModeRadioButtonGroup
}
- QQC2.RadioButton {
+ RadioButton {
id: reverseMode
checked: Plasmoid.configuration.reverseMode === true
text: {
if (Plasmoid.formFactor === PlasmaCore.Types.Vertical) {
- return i18nc("@option:check completes sentence: New tasks appear", "On the top")
+ return i18n("On the top")
}
// horizontal
if (Qt.application.layoutDirection === Qt.LeftToRight) {
- return i18nc("@option:check completes sentence: New tasks appear", "To the left");
+ return i18n("To the left");
} else {
- return i18nc("@option:check completes sentence: New tasks appear", "To the right");
+ return i18n("To the right");
}
}
- QQC2.ButtonGroup.group: reverseModeRadioButtonGroup
+ ButtonGroup.group: reverseModeRadioButtonGroup
}
}
}
diff --git a/package/contents/ui/ConfigIndicators.qml b/package/contents/ui/ConfigIndicators.qml
new file mode 100644
index 00000000..1066a43c
--- /dev/null
+++ b/package/contents/ui/ConfigIndicators.qml
@@ -0,0 +1,388 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+import org.kde.kirigami as Kirigami
+import org.kde.plasma.core as PlasmaCore
+import org.kde.kquickcontrols as KQControls
+
+Kirigami.ScrollablePage {
+ property alias cfg_indicatorsEnabled: indicatorsEnabled.currentIndex
+ property alias cfg_groupIconEnabled: groupIconEnabled.currentIndex
+ property alias cfg_indicatorProgress: indicatorProgress.checked
+ property alias cfg_indicatorProgressColor: indicatorProgressColor.color
+ property alias cfg_disableInactiveIndicators: disableInactiveIndicators.checked
+ property alias cfg_indicatorsAnimated: indicatorsAnimated.checked
+ property alias cfg_indicatorLocation: indicatorLocation.currentIndex
+ property alias cfg_indicatorReverse: indicatorReverse.checked
+ property alias cfg_indicatorOverride: indicatorOverride.checked
+ property alias cfg_indicatorEdgeOffset: indicatorEdgeOffset.value
+ property alias cfg_indicatorStyle: indicatorStyle.currentIndex
+ property alias cfg_indicatorMinLimit: indicatorMinLimit.value
+ property alias cfg_indicatorMaxLimit: indicatorMaxLimit.value
+ property alias cfg_indicatorDesaturate: indicatorDesaturate.checked
+ property alias cfg_indicatorGrow: indicatorGrow.checked
+ property alias cfg_indicatorGrowFactor: indicatorGrowFactor.value
+ property alias cfg_indicatorSize: indicatorSize.value
+ property alias cfg_indicatorLength: indicatorLength.value
+ property alias cfg_indicatorRadius: indicatorRadius.value
+ property alias cfg_indicatorShrink: indicatorShrink.value
+ property alias cfg_indicatorDominantColor: indicatorDominantColor.checked
+ property alias cfg_indicatorAccentColor: indicatorAccentColor.checked
+ property alias cfg_indicatorCustomColor: indicatorCustomColor.color
+
+ // --- Properties to silence KCM errors ---
+ // Defaults for existing aliases
+ property var cfg_indicatorsEnabledDefault
+ property var cfg_groupIconEnabledDefault
+ property var cfg_indicatorProgressDefault
+ property var cfg_indicatorProgressColorDefault
+ property var cfg_disableInactiveIndicatorsDefault
+ property var cfg_indicatorsAnimatedDefault
+ property var cfg_indicatorLocationDefault
+ property var cfg_indicatorReverseDefault
+ property var cfg_indicatorOverrideDefault
+ property var cfg_indicatorEdgeOffsetDefault
+ property var cfg_indicatorStyleDefault
+ property var cfg_indicatorMinLimitDefault
+ property var cfg_indicatorMaxLimitDefault
+ property var cfg_indicatorDesaturateDefault
+ property var cfg_indicatorGrowDefault
+ property var cfg_indicatorGrowFactorDefault
+ property var cfg_indicatorSizeDefault
+ property var cfg_indicatorLengthDefault
+ property var cfg_indicatorRadiusDefault
+ property var cfg_indicatorShrinkDefault
+ property var cfg_indicatorDominantColorDefault
+ property var cfg_indicatorAccentColorDefault
+ property var cfg_indicatorCustomColorDefault
+
+ // Missing properties from main.xml not used in this tab
+ property var cfg_showOnlyCurrentScreen
+ property var cfg_showOnlyCurrentScreenDefault
+ property var cfg_showOnlyCurrentDesktop
+ property var cfg_showOnlyCurrentDesktopDefault
+ property var cfg_showOnlyCurrentActivity
+ property var cfg_showOnlyCurrentActivityDefault
+ property var cfg_showOnlyMinimized
+ property var cfg_showOnlyMinimizedDefault
+ property var cfg_unhideOnAttention
+ property var cfg_unhideOnAttentionDefault
+ property var cfg_groupingStrategy
+ property var cfg_groupingStrategyDefault
+ property var cfg_iconOnly
+ property var cfg_iconOnlyDefault
+ property var cfg_groupedTaskVisualization
+ property var cfg_groupedTaskVisualizationDefault
+ property var cfg_groupPopups
+ property var cfg_groupPopupsDefault
+ property var cfg_onlyGroupWhenFull
+ property var cfg_onlyGroupWhenFullDefault
+ property var cfg_groupingAppIdBlacklist
+ property var cfg_groupingAppIdBlacklistDefault
+ property var cfg_groupingLauncherUrlBlacklist
+ property var cfg_groupingLauncherUrlBlacklistDefault
+ property var cfg_sortingStrategy
+ property var cfg_sortingStrategyDefault
+ property var cfg_separateLaunchers
+ property var cfg_separateLaunchersDefault
+ property var cfg_hideLauncherOnStart
+ property var cfg_hideLauncherOnStartDefault
+ property var cfg_maxStripes
+ property var cfg_maxStripesDefault
+ property var cfg_maxButtonLength
+ property var cfg_maxButtonLengthDefault
+ property var cfg_forceStripes
+ property var cfg_forceStripesDefault
+ property var cfg_showToolTips
+ property var cfg_showToolTipsDefault
+ property var cfg_taskMaxWidth
+ property var cfg_taskMaxWidthDefault
+ property var cfg_wheelEnabled
+ property var cfg_wheelEnabledDefault
+ property var cfg_wheelSkipMinimized
+ property var cfg_wheelSkipMinimizedDefault
+ property var cfg_highlightWindows
+ property var cfg_highlightWindowsDefault
+ property var cfg_launchers
+ property var cfg_launchersDefault
+ property var cfg_middleClickAction
+ property var cfg_middleClickActionDefault
+ property var cfg_indicateAudioStreams
+ property var cfg_indicateAudioStreamsDefault
+ property var cfg_iconScale
+ property var cfg_iconScaleDefault
+ property var cfg_iconSizePx
+ property var cfg_iconSizePxDefault
+ property var cfg_iconSizeOverride
+ property var cfg_iconSizeOverrideDefault
+ property var cfg_fill
+ property var cfg_fillDefault
+ property var cfg_taskHoverEffect
+ property var cfg_taskHoverEffectDefault
+ property var cfg_maxTextLines
+ property var cfg_maxTextLinesDefault
+ property var cfg_minimizeActiveTaskOnClick
+ property var cfg_minimizeActiveTaskOnClickDefault
+ property var cfg_reverseMode
+ property var cfg_reverseModeDefault
+ property var cfg_iconSpacing
+ property var cfg_iconSpacingDefault
+ property var cfg_useBorders
+ property var cfg_useBordersDefault
+ property var cfg_taskSpacingSize
+ property var cfg_taskSpacingSizeDefault
+ property var cfg_buttonColorize
+ property var cfg_buttonColorizeDefault
+ property var cfg_buttonColorizeInactive
+ property var cfg_buttonColorizeInactiveDefault
+ property var cfg_buttonColorizeDominant
+ property var cfg_buttonColorizeDominantDefault
+ property var cfg_buttonColorizeCustom
+ property var cfg_buttonColorizeCustomDefault
+ property var cfg_disableButtonSvg
+ property var cfg_disableButtonSvgDefault
+ property var cfg_disableButtonInactiveSvg
+ property var cfg_disableButtonInactiveSvgDefault
+ property var cfg_overridePlasmaButtonDirection
+ property var cfg_overridePlasmaButtonDirectionDefault
+ property var cfg_plasmaButtonDirection
+ property var cfg_plasmaButtonDirectionDefault
+ property var cfg_iconZoomFactor
+ property var cfg_iconZoomFactorDefault
+ property var cfg_iconZoomDuration
+ property var cfg_iconZoomDurationDefault
+ // -----------------------------------------------------------------
+
+ Kirigami.FormLayout {
+
+ ComboBox {
+ id: indicatorsEnabled
+ Kirigami.FormData.label: i18n("Indicators:")
+ model: [i18n("Disabled"), i18n("Enabled")]
+ }
+
+ CheckBox {
+ id: indicatorProgress
+ enabled: indicatorsEnabled.currentIndex
+ visible: indicatorsEnabled.currentIndex
+ text: i18n("Display Progress on Indicator")
+ }
+
+ KQControls.ColorButton {
+ enabled: indicatorsEnabled.currentIndex
+ visible: indicatorProgress.checked
+ id: indicatorProgressColor
+ Kirigami.FormData.label: i18n("Progress Color:")
+ showAlphaChannel: true
+ }
+
+ CheckBox {
+ enabled: indicatorsEnabled.currentIndex
+ visible: indicatorsEnabled.currentIndex
+ id: disableInactiveIndicators
+ text: i18n("Disable for Inactive Windows")
+ }
+
+ ComboBox {
+ id: groupIconEnabled
+ Kirigami.FormData.label: i18n("Group Overlay:")
+ model: [i18n("Disabled"), i18n("Enabled")]
+ }
+ Label {
+ text: i18n("Takes effect on next time plasma groups tasks.")
+ font: Kirigami.Theme.smallFont
+ }
+
+ Item {
+ Kirigami.FormData.isSection: true
+ }
+
+ CheckBox {
+ enabled: indicatorsEnabled.currentIndex
+ id: indicatorsAnimated
+ Kirigami.FormData.label: i18n("Animate Indicators:")
+ text: i18n("Enabled")
+ }
+
+
+ Item {
+ Kirigami.FormData.isSection: true
+ }
+
+ CheckBox {
+ enabled: indicatorsEnabled.currentIndex && !indicatorOverride.checked
+ id: indicatorReverse
+ Kirigami.FormData.label: i18n("Indicator Location:")
+ text: i18n("Reverse shown side")
+ }
+
+ CheckBox {
+ enabled: indicatorsEnabled.currentIndex
+ id: indicatorOverride
+ text: i18n("Override location")
+ }
+
+ ComboBox {
+ enabled: indicatorsEnabled.currentIndex
+ visible: indicatorOverride.checked
+ id: indicatorLocation
+ model: [
+ i18n("Bottom"),
+ i18n("Left"),
+ i18n("Right"),
+ i18n("Top")
+ ]
+ }
+
+ Label {
+ text: i18n("Be sure to use this when using as a floating widget")
+ font: Kirigami.Theme.smallFont
+ }
+
+ SpinBox {
+ enabled: indicatorsEnabled.currentIndex
+ id: indicatorEdgeOffset
+ Kirigami.FormData.label: i18n("Indicator Edge Offset (px):")
+ from: 0
+ to: 999
+ }
+
+ Item {
+ Kirigami.FormData.isSection: true
+ }
+
+ ComboBox {
+ enabled: indicatorsEnabled.currentIndex
+ id: indicatorStyle
+ Kirigami.FormData.label: i18n("Indicator Style:")
+ Layout.fillWidth: true
+ Layout.minimumWidth: Kirigami.Units.gridUnit * 14
+ model: [
+ i18n("Metro"),
+ i18n("Ciliora"),
+ i18n("Dashes")
+ ]
+ }
+
+ SpinBox {
+ enabled: indicatorsEnabled.currentIndex
+ id: indicatorMinLimit
+ Kirigami.FormData.label: i18n("Indicator Min Limit:")
+ from: 0
+ to: 10
+ }
+
+ SpinBox {
+ enabled: indicatorsEnabled.currentIndex
+ id: indicatorMaxLimit
+ Kirigami.FormData.label: i18n("Indicator Max Limit:")
+ from: 1
+ to: 10
+ }
+
+ CheckBox {
+ enabled: indicatorsEnabled.currentIndex
+ id: indicatorDesaturate
+ Kirigami.FormData.label: i18n("Minimize Options:")
+ text: i18n("Desaturate")
+ }
+
+ CheckBox {
+ enabled: indicatorsEnabled.currentIndex
+ id: indicatorGrow
+ text: i18n("Shrink when minimized")
+ }
+
+ SpinBox {
+ id: indicatorGrowFactor
+ enabled: indicatorsEnabled.currentIndex
+ visible: indicatorGrow.checked
+ from: 100
+ to: 10 * 100
+ stepSize: 25
+ Kirigami.FormData.label: i18n("Growth/Shrink factor:")
+
+ property int decimals: 2
+ property real realValue: value / 100
+
+ validator: DoubleValidator {
+ bottom: Math.min(indicatorGrowFactor.from, indicatorGrowFactor.to)
+ top: Math.max(indicatorGrowFactor.from, indicatorGrowFactor.to)
+ }
+
+ textFromValue: function(value, locale) {
+ return Number(value / 100).toLocaleString(locale, 'f', indicatorGrowFactor.decimals)
+ }
+
+ valueFromText: function(text, locale) {
+ return Number.fromLocaleString(locale, text) * 100
+ }
+ }
+
+ Item {
+ Kirigami.FormData.isSection: true
+ }
+
+ SpinBox {
+ enabled: indicatorsEnabled.currentIndex
+ id: indicatorSize
+ Kirigami.FormData.label: i18n("Indicator size (px):")
+ from: 1
+ to: 999
+ }
+
+ SpinBox {
+ enabled: indicatorsEnabled.currentIndex
+ id: indicatorLength
+ Kirigami.FormData.label: i18n("Indicator length (px):")
+ from: 1
+ to: 999
+ }
+
+ SpinBox {
+ enabled: indicatorsEnabled.currentIndex
+ id: indicatorRadius
+ Kirigami.FormData.label: i18n("Indicator Radius (%):")
+ from: 0
+ to: 100
+ }
+
+ SpinBox {
+ enabled: indicatorsEnabled.currentIndex
+ id: indicatorShrink
+ Kirigami.FormData.label: i18n("Indicator margin (px):")
+ from: 0
+ to: 999
+ }
+
+
+ Item {
+ Kirigami.FormData.isSection: true
+ }
+
+ CheckBox {
+ enabled: indicatorsEnabled.currentIndex & !indicatorAccentColor.checked
+ id: indicatorDominantColor
+ Kirigami.FormData.label: i18n("Indicator Color:")
+ text: i18n("Use dominant icon color")
+ }
+
+ CheckBox {
+ enabled: indicatorsEnabled.currentIndex & !indicatorDominantColor.checked
+ id: indicatorAccentColor
+ text: i18n("Use plasma accent color")
+ }
+
+ KQControls.ColorButton {
+ enabled: indicatorsEnabled.currentIndex & !indicatorDominantColor.checked & !indicatorAccentColor.checked
+ id: indicatorCustomColor
+ Kirigami.FormData.label: i18n("Custom Color:")
+ showAlphaChannel: true
+ }
+
+ Item {
+ Kirigami.FormData.isSection: true
+ }
+ }
+}
diff --git a/package/contents/ui/ConfigPinnedApps.qml b/package/contents/ui/ConfigPinnedApps.qml
new file mode 100644
index 00000000..e3ec8957
--- /dev/null
+++ b/package/contents/ui/ConfigPinnedApps.qml
@@ -0,0 +1,853 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+import org.kde.kirigami as Kirigami
+import org.kde.plasma.core as PlasmaCore
+import org.kde.plasma.components as PlasmaComponents
+import org.kde.plasma.extras as PlasmaExtras
+import org.kde.plasma.plasmoid
+import org.kde.plasma.plasma5support as P5Support
+
+Kirigami.ScrollablePage {
+ id: pinnedAppsDialog
+
+ property var pinnedLaunchers: plasmoid.configuration.launchers
+ property bool appsLoaded: false
+ property bool initialLoadDone: false
+ property bool isLoadingApps: false
+ property int appsBatchSize: 15
+
+ // --- Properties to silence KCM errors ---
+ // Defaults for existing aliases
+ property var cfg_launchersDefault
+
+ // Missing properties from main.xml not used in this tab
+ property var cfg_showOnlyCurrentScreen
+ property var cfg_showOnlyCurrentScreenDefault
+ property var cfg_showOnlyCurrentDesktop
+ property var cfg_showOnlyCurrentDesktopDefault
+ property var cfg_showOnlyCurrentActivity
+ property var cfg_showOnlyCurrentActivityDefault
+ property var cfg_showOnlyMinimized
+ property var cfg_showOnlyMinimizedDefault
+ property var cfg_unhideOnAttention
+ property var cfg_unhideOnAttentionDefault
+ property var cfg_groupingStrategy
+ property var cfg_groupingStrategyDefault
+ property var cfg_iconOnly
+ property var cfg_iconOnlyDefault
+ property var cfg_groupedTaskVisualization
+ property var cfg_groupedTaskVisualizationDefault
+ property var cfg_groupPopups
+ property var cfg_groupPopupsDefault
+ property var cfg_onlyGroupWhenFull
+ property var cfg_onlyGroupWhenFullDefault
+ property var cfg_groupingAppIdBlacklist
+ property var cfg_groupingAppIdBlacklistDefault
+ property var cfg_groupingLauncherUrlBlacklist
+ property var cfg_groupingLauncherUrlBlacklistDefault
+ property var cfg_sortingStrategy
+ property var cfg_sortingStrategyDefault
+ property var cfg_separateLaunchers
+ property var cfg_separateLaunchersDefault
+ property var cfg_hideLauncherOnStart
+ property var cfg_hideLauncherOnStartDefault
+ property var cfg_maxStripes
+ property var cfg_maxStripesDefault
+ property var cfg_maxButtonLength
+ property var cfg_maxButtonLengthDefault
+ property var cfg_forceStripes
+ property var cfg_forceStripesDefault
+ property var cfg_showToolTips
+ property var cfg_showToolTipsDefault
+ property var cfg_taskMaxWidth
+ property var cfg_taskMaxWidthDefault
+ property var cfg_wheelEnabled
+ property var cfg_wheelEnabledDefault
+ property var cfg_wheelSkipMinimized
+ property var cfg_wheelSkipMinimizedDefault
+ property var cfg_highlightWindows
+ property var cfg_highlightWindowsDefault
+ property var cfg_launchers // Used manually via plasmoid.configuration
+ property var cfg_middleClickAction
+ property var cfg_middleClickActionDefault
+ property var cfg_indicateAudioStreams
+ property var cfg_indicateAudioStreamsDefault
+ property var cfg_iconScale
+ property var cfg_iconScaleDefault
+ property var cfg_iconSizePx
+ property var cfg_iconSizePxDefault
+ property var cfg_iconSizeOverride
+ property var cfg_iconSizeOverrideDefault
+ property var cfg_fill
+ property var cfg_fillDefault
+ property var cfg_taskHoverEffect
+ property var cfg_taskHoverEffectDefault
+ property var cfg_maxTextLines
+ property var cfg_maxTextLinesDefault
+ property var cfg_minimizeActiveTaskOnClick
+ property var cfg_minimizeActiveTaskOnClickDefault
+ property var cfg_reverseMode
+ property var cfg_reverseModeDefault
+ property var cfg_iconSpacing
+ property var cfg_iconSpacingDefault
+ property var cfg_indicatorsEnabled
+ property var cfg_indicatorsEnabledDefault
+ property var cfg_indicatorProgress
+ property var cfg_indicatorProgressDefault
+ property var cfg_indicatorProgressColor
+ property var cfg_indicatorProgressColorDefault
+ property var cfg_disableInactiveIndicators
+ property var cfg_disableInactiveIndicatorsDefault
+ property var cfg_indicatorsAnimated
+ property var cfg_indicatorsAnimatedDefault
+ property var cfg_groupIconEnabled
+ property var cfg_groupIconEnabledDefault
+ property var cfg_indicatorLocation
+ property var cfg_indicatorLocationDefault
+ property var cfg_indicatorStyle
+ property var cfg_indicatorStyleDefault
+ property var cfg_indicatorMinLimit
+ property var cfg_indicatorMinLimitDefault
+ property var cfg_indicatorMaxLimit
+ property var cfg_indicatorMaxLimitDefault
+ property var cfg_indicatorDesaturate
+ property var cfg_indicatorDesaturateDefault
+ property var cfg_indicatorGrow
+ property var cfg_indicatorGrowDefault
+ property var cfg_indicatorGrowFactor
+ property var cfg_indicatorGrowFactorDefault
+ property var cfg_indicatorEdgeOffset
+ property var cfg_indicatorEdgeOffsetDefault
+ property var cfg_indicatorSize
+ property var cfg_indicatorSizeDefault
+ property var cfg_indicatorLength
+ property var cfg_indicatorLengthDefault
+ property var cfg_indicatorRadius
+ property var cfg_indicatorRadiusDefault
+ property var cfg_indicatorShrink
+ property var cfg_indicatorShrinkDefault
+ property var cfg_indicatorDominantColor
+ property var cfg_indicatorDominantColorDefault
+ property var cfg_indicatorAccentColor
+ property var cfg_indicatorAccentColorDefault
+ property var cfg_indicatorCustomColor
+ property var cfg_indicatorCustomColorDefault
+ property var cfg_useBorders
+ property var cfg_useBordersDefault
+ property var cfg_taskSpacingSize
+ property var cfg_taskSpacingSizeDefault
+ property var cfg_buttonColorize
+ property var cfg_buttonColorizeDefault
+ property var cfg_buttonColorizeInactive
+ property var cfg_buttonColorizeInactiveDefault
+ property var cfg_buttonColorizeDominant
+ property var cfg_buttonColorizeDominantDefault
+ property var cfg_buttonColorizeCustom
+ property var cfg_buttonColorizeCustomDefault
+ property var cfg_disableButtonSvg
+ property var cfg_disableButtonSvgDefault
+ property var cfg_disableButtonInactiveSvg
+ property var cfg_disableButtonInactiveSvgDefault
+ property var cfg_overridePlasmaButtonDirection
+ property var cfg_overridePlasmaButtonDirectionDefault
+ property var cfg_plasmaButtonDirection
+ property var cfg_plasmaButtonDirectionDefault
+ property var cfg_indicatorReverse
+ property var cfg_indicatorReverseDefault
+ property var cfg_indicatorOverride
+ property var cfg_indicatorOverrideDefault
+ property var cfg_iconZoomFactor
+ property var cfg_iconZoomFactorDefault
+ property var cfg_iconZoomDuration
+ property var cfg_iconZoomDurationDefault
+ // ---------------------------------------
+
+ // Initial app loading timer
+ Timer {
+ id: initialLoadTimer
+ interval: 200
+ repeat: false
+ onTriggered: {
+ isLoadingApps = true;
+ loadInstalledApps();
+ }
+ }
+
+ // App loading data source
+ P5Support.DataSource {
+ id: appsSource
+ engine: "apps"
+ connectedSources: ["apps:"]
+ interval: 0
+
+ property var requestedSources: ({})
+
+ onSourceConnected: (source) => {
+ processAppSource(source);
+ }
+ }
+
+ ListModel {
+ id: installedAppsModel
+ }
+
+ ListModel {
+ id: filteredAppsModel
+ }
+
+ function loadInstalledApps() {
+ // Add default special launchers
+ if (installedAppsModel.count === 0) {
+ installedAppsModel.append({
+ name: i18n("Default Web Browser"),
+ icon: "internet-web-browser",
+ url: "preferred://browser",
+ keywords: "web browser internet"
+ });
+ installedAppsModel.append({
+ name: i18n("Default File Manager"),
+ icon: "system-file-manager",
+ url: "preferred://filemanager",
+ keywords: "files folder"
+ });
+ installedAppsModel.append({
+ name: i18n("Default Mail Client"),
+ icon: "internet-mail",
+ url: "preferred://mail",
+ keywords: "email mail"
+ });
+ }
+
+ // Start batch processing of app sources
+ startBatchProcessing();
+ }
+
+ function startBatchProcessing() {
+ // Create a prioritized list of desktop files
+ const desktopFiles = [];
+ const commonPrefixes = ["firefox", "chromium", "chrome", "brave", "dolphin", "konsole", "kate", "gnome-terminal", "org.kde"];
+ // Pre-sort desktop files with common ones first
+ for (let i = 0; i < appsSource.sources.length; i++) {
+ const source = appsSource.sources[i];
+ if (source !== "apps:" && source.endsWith(".desktop")) {
+ // Check if it's a commonly used app
+ let priority = 1000;
+ const sourceLower = source.toLowerCase();
+
+ for (let j = 0; j < commonPrefixes.length; j++) {
+ if (sourceLower.includes(commonPrefixes[j])) {
+ priority = j;
+ break;
+ }
+ }
+
+ desktopFiles.push({
+ source: source,
+ priority: priority
+ });
+ }
+ }
+
+ // Sort by priority
+ desktopFiles.sort((a, b) => a.priority - b.priority);
+ // Process in batches
+ let processedCount = 0;
+ function processBatch() {
+ const batchSize = 15;
+ const end = Math.min(processedCount + batchSize, desktopFiles.length);
+
+ for (let i = processedCount; i < end; i++) {
+ const source = desktopFiles[i].source;
+ if (!appsSource.requestedSources[source]) {
+ appsSource.connectSource(source);
+ appsSource.requestedSources[source] = true;
+ }
+ }
+
+ processedCount = end;
+ if (processedCount < desktopFiles.length) {
+ // Schedule next batch
+ Qt.callLater(processBatch);
+ } else {
+ // All batches scheduled
+ isLoadingApps = false;
+ appsLoaded = true;
+ }
+ }
+
+ // Start processing
+ processBatch();
+ }
+
+ function processAppSource(source) {
+ if (source === "apps:" || !source.endsWith(".desktop")) {
+ return;
+ }
+
+ try {
+ // Extract app name
+ let appName = source.replace(".desktop", "");
+ const lastSlash = appName.lastIndexOf("/");
+ if (lastSlash !== -1) {
+ appName = appName.substring(lastSlash + 1);
+ }
+ appName = appName.split(/[-_.]/).map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
+ // Check for duplicates
+ for (let j = 0; j < installedAppsModel.count; j++) {
+ if (installedAppsModel.get(j).url === "applications:" + source) {
+ return;
+ }
+ }
+
+ installedAppsModel.append({
+ name: appName,
+ icon: appName.toLowerCase().replace(/\s+/g, "-") || "application-x-executable",
+ url: "applications:" + source,
+ keywords: ""
+ });
+ // Update filtered model if needed
+ if (searchField.text.length === 0 && filteredAppsModel.count < 100) {
+ filteredAppsModel.append({
+ name: appName,
+ icon: appName.toLowerCase().replace(/\s+/g, "-") || "application-x-executable",
+ url: "applications:" + source,
+ keywords: ""
+ });
+ }
+ } catch (e) {
+ console.log("Error processing app:", source, e);
+ }
+ }
+
+ function filterAppsModel() {
+ const searchText = searchField.text.toLowerCase();
+ // Optimization for empty search with already filled model
+ if (searchText.length === 0 && filteredAppsModel.count > 0 && filteredAppsModel.count === Math.min(installedAppsModel.count, 100)) {
+ return;
+ }
+
+ filteredAppsModel.clear();
+
+ for (let i = 0; i < installedAppsModel.count; i++) {
+ const app = installedAppsModel.get(i);
+ // Apply search filter
+ if (searchText && searchText.length > 0) {
+ if (app.name.toLowerCase().includes(searchText) || app.url.toLowerCase().includes(searchText) || (app.keywords && app.keywords.toLowerCase().includes(searchText))) {
+ filteredAppsModel.append(app);
+ }
+ } else {
+ filteredAppsModel.append(app);
+ // Limit initial load for better performance
+ if (filteredAppsModel.count >= 100) {
+ break;
+ }
+ }
+ }
+ }
+
+ function loadMoreApps() {
+ if (isLoadingApps)
+ return;
+ if (filteredAppsModel.count < installedAppsModel.count) {
+ const startIndex = filteredAppsModel.count;
+ const endIndex = Math.min(startIndex + 30, installedAppsModel.count);
+
+ for (let i = startIndex; i < endIndex; i++) {
+ filteredAppsModel.append(installedAppsModel.get(i));
+ }
+ }
+ }
+
+ Component.onCompleted: {
+ refreshPinnedAppsModel();
+ }
+
+ function refreshPinnedAppsModel() {
+ pinnedAppsModel.clear();
+ for (let i = 0; i < pinnedLaunchers.length; i++) {
+ let launcher = pinnedLaunchers[i];
+ let name = getNameForUrl(launcher);
+ let icon = getIconForUrl(launcher);
+ pinnedAppsModel.append({
+ "name": name,
+ "icon": icon,
+ "url": launcher
+ });
+ }
+ }
+
+ function getNameForUrl(url) {
+ let appName = url;
+ if (appName.indexOf("preferred://") === 0) {
+ if (appName === "preferred://browser")
+ return i18n("Default Web Browser");
+ if (appName === "preferred://filemanager")
+ return i18n("Default File Manager");
+ if (appName === "preferred://mail")
+ return i18n("Default Mail Client");
+ return i18n("Default Application");
+ }
+
+ if (appName.indexOf("applications:") === 0) {
+ // Check installed apps model first
+ for (let i = 0; i < installedAppsModel.count; i++) {
+ const app = installedAppsModel.get(i);
+ if (app.url === appName) {
+ return app.name;
+ }
+ }
+
+ // Fallback formatting
+ appName = appName.substring(13);
+ if (appName.endsWith(".desktop")) {
+ appName = appName.substring(0, appName.length - 8);
+ }
+ appName = appName.split(/[-_.]/g).map(word => {
+ return word.charAt(0).toUpperCase() + word.slice(1);
+ }).join(" ");
+ }
+
+ return appName;
+ }
+
+ function getIconForUrl(url) {
+ let appId = url;
+ // Check installed apps model first
+ for (let i = 0; i < installedAppsModel.count; i++) {
+ const app = installedAppsModel.get(i);
+ if (app.url === appId) {
+ return app.icon;
+ }
+ }
+
+ if (appId.indexOf("preferred://") === 0) {
+ if (appId === "preferred://browser")
+ return "internet-web-browser";
+ if (appId === "preferred://filemanager")
+ return "system-file-manager";
+ if (appId === "preferred://mail")
+ return "internet-mail";
+ return "preferences-desktop-default-applications";
+ }
+
+ if (appId.indexOf("applications:") === 0) {
+ appId = appId.substring(13);
+ if (appId.endsWith(".desktop")) {
+ return appId.substring(0, appId.length - 8).toLowerCase();
+ }
+ return appId.toLowerCase();
+ }
+
+ return "application-x-executable";
+ }
+
+ ListModel {
+ id: pinnedAppsModel
+ }
+
+ // For better drag and drop reordering
+ property int dragItemIndex: -1
+ property int dropItemIndex: -1
+ property bool isDragging: false
+
+ ColumnLayout {
+ anchors.fill: parent
+ spacing: Kirigami.Units.largeSpacing
+
+ resources: [
+ // App picker dialog moved here
+ Dialog {
+ id: appPickerDialog
+ title: i18n("Add Application")
+ modal: true
+ standardButtons: Dialog.Close
+
+ width: Math.min(800, parent.width - Kirigami.Units.gridUnit * 4)
+ height: Math.min(600, parent.height - Kirigami.Units.gridUnit * 4)
+
+ anchors.centerIn: parent
+
+ onOpened: {
+ if (!initialLoadDone) {
+ initialLoadDone = true;
+ initialLoadTimer.start();
+ }
+ }
+
+ ColumnLayout {
+ anchors.fill: parent
+ spacing: Kirigami.Units.smallSpacing
+
+ PlasmaExtras.SearchField {
+ id: searchField
+ Layout.fillWidth: true
+ placeholderText: i18n("Search applications...")
+ onTextChanged: searchTimer.restart()
+ }
+
+ Timer {
+ id: searchTimer
+ interval: 300
+ onTriggered: filterAppsModel()
+ }
+
+ ScrollView {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ ListView {
+ id: appsListView
+ model: filteredAppsModel
+
+ onContentYChanged: {
+ if (contentY + height >= contentHeight - 200 && !isLoadingApps) {
+ loadMoreApps();
+ }
+ }
+
+ delegate: ItemDelegate {
+ width: appsListView.width
+
+ contentItem: RowLayout {
+ spacing: Kirigami.Units.smallSpacing
+
+ Kirigami.Icon {
+ source: model.icon || "application-x-executable"
+ Layout.preferredWidth: Kirigami.Units.iconSizes.medium
+ Layout.preferredHeight: Kirigami.Units.iconSizes.medium
+ }
+
+ Label {
+ text: model.name || model.url
+ elide: Text.ElideRight
+ Layout.fillWidth: true
+ font.bold: true
+ }
+
+ Button {
+ icon.name: "list-add"
+ text: i18n("Add")
+ onClicked: {
+ addLauncher(model.url);
+ appPickerDialog.close();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Loading indicator
+ Item {
+ visible: isLoadingApps && filteredAppsModel.count === 0
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ BusyIndicator {
+ anchors.centerIn: parent
+ running: isLoadingApps
+ }
+ }
+ }
+ }
+ ]
+
+ Kirigami.InlineMessage {
+ Layout.fillWidth: true
+ type: Kirigami.MessageType.Information
+ text: i18n("Add applications to pin to the taskbar. Drag items to reorder them.")
+ visible: true
+ }
+
+ ListView {
+ id: pinnedAppsList
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ model: pinnedAppsModel
+
+ // Disable animations during drag
+ interactive: !isDragging
+
+ // Add scrollbar when needed
+ ScrollBar.vertical: ScrollBar {}
+
+ // Add drop area to handle autoscroll
+ DropArea {
+ id: dropArea
+ anchors.fill: parent
+
+ onEntered: {
+ if (isDragging) {
+ // Calculate new index based on drop position
+ var newIndex = Math.floor((drag.y + pinnedAppsList.contentY) / 48);
+ // Approximate item height
+ if (newIndex >= 0 && newIndex < pinnedAppsModel.count) {
+ dropItemIndex = newIndex;
+ }
+ }
+ }
+
+ onPositionChanged: {
+ if (isDragging) {
+ // Handle auto-scrolling
+ var localY = drag.y;
+ if (localY < 50) {
+ // Auto-scroll up
+ autoScrollTimer.direction = -1;
+ autoScrollTimer.running = true;
+ } else if (localY > pinnedAppsList.height - 50) {
+ // Auto-scroll down
+ autoScrollTimer.direction = 1;
+ autoScrollTimer.running = true;
+ } else {
+ // Stop auto-scrolling
+ autoScrollTimer.running = false;
+ }
+
+ // Calculate drop index
+ var newIndex = Math.floor((localY + pinnedAppsList.contentY) / 48);
+ if (newIndex >= 0 && newIndex < pinnedAppsModel.count) {
+ dropItemIndex = newIndex;
+ }
+ }
+ }
+
+ onDropped: {
+ autoScrollTimer.running = false;
+ }
+
+ onExited: {
+ autoScrollTimer.running = false;
+ }
+ }
+
+ // Timer for auto-scrolling during drag
+ Timer {
+ id: autoScrollTimer
+ interval: 50
+ repeat: true
+ property int direction: 0 // -1 for up, 1 for down
+ property int scrollStep: 10
+
+ onTriggered: {
+ pinnedAppsList.contentY = Math.max(0, Math.min(pinnedAppsList.contentY + direction * scrollStep, pinnedAppsList.contentHeight - pinnedAppsList.height));
+ }
+ }
+
+ delegate: Item {
+ id: pinnedAppDelegate
+ width: ListView.view.width
+ height: 48
+
+ // Properties for drag operation
+ property bool beingDragged: index === dragItemIndex
+
+ Drag.active: mouseArea.drag.active
+ Drag.source: pinnedAppDelegate
+ Drag.hotSpot.x: width / 2
+ Drag.hotSpot.y: height / 2
+
+ // Visual representation during drag
+ states: [
+ State {
+ when: beingDragged
+ ParentChange {
+ target: pinnedAppDelegate
+ parent: pinnedAppsDialog
+ }
+ PropertyChanges {
+ target: pinnedAppDelegate
+ z: 100
+ opacity: 0.8
+ }
+ }
+ ]
+
+ Rectangle {
+ id: itemHighlight
+ anchors.fill: parent
+ color: dropItemIndex === index && isDragging ?
+Kirigami.Theme.highlightColor : mouseArea.containsMouse ? Kirigami.Theme.highlightColor.lighter(0.7) : "transparent"
+ opacity: 0.3
+ radius: 3
+
+ Behavior on color {
+ ColorAnimation {
+ duration: 100
+ }
+ }
+ }
+
+ RowLayout {
+ anchors.fill: parent
+ anchors.margins: Kirigami.Units.smallSpacing
+ spacing: Kirigami.Units.smallSpacing
+
+ // Drag handle
+ Item {
+ Layout.preferredWidth: Kirigami.Units.iconSizes.small
+ Layout.preferredHeight: parent.height
+
+ Rectangle {
+ width: Kirigami.Units.smallSpacing
+ height: Kirigami.Units.iconSizes.small
+ radius: width / 2
+ anchors.centerIn: parent
+ color: Kirigami.Theme.textColor
+ opacity: 0.5
+ }
+ }
+
+ Kirigami.Icon {
+ source: model.icon
+ Layout.preferredWidth: Kirigami.Units.iconSizes.medium
+ Layout.preferredHeight: Kirigami.Units.iconSizes.medium
+ }
+
+ Label {
+ text: model.name
+ elide: Text.ElideRight
+ Layout.fillWidth: true
+ }
+
+ Label {
+ text: model.url
+ opacity: 0.6
+ font.pointSize: Kirigami.Theme.smallFont.pointSize
+ elide: Text.ElideMiddle
+ Layout.maximumWidth: parent.width / 3
+ horizontalAlignment: Text.AlignRight
+ visible: Kirigami.Settings.isMobile ? false : true
+ }
+
+ Button {
+ icon.name: "list-remove"
+ onClicked: {
+ let currentLaunchers = pinnedLaunchers;
+ currentLaunchers.splice(index, 1);
+ plasmoid.configuration.launchers = currentLaunchers;
+ pinnedLaunchers = currentLaunchers;
+ refreshPinnedAppsModel();
+ }
+ ToolTip.text: i18n("Remove")
+ ToolTip.visible: hovered
+ ToolTip.delay: 1000
+ }
+ }
+
+ MouseArea {
+ id: mouseArea
+ anchors.fill: parent
+ hoverEnabled: true
+ cursorShape: pressed ? Qt.ClosedHandCursor : Qt.PointingHandCursor
+
+ drag.target: pinnedAppDelegate
+ drag.axis: Drag.YAxis
+
+ onPressed: {
+ // Start dragging
+ dragItemIndex = index;
+ isDragging = true;
+ }
+
+ onReleased: {
+ // End dragging
+ isDragging = false;
+ if (dropItemIndex !== -1 && dragItemIndex !== -1 && dropItemIndex !== dragItemIndex) {
+ // Move the item
+ moveItem(dragItemIndex, dropItemIndex);
+ }
+
+ // Reset state
+ dragItemIndex = -1;
+ dropItemIndex = -1;
+ }
+ }
+ }
+ }
+
+ RowLayout {
+ Layout.fillWidth: true
+
+ Button {
+ id: addAppButton
+ icon.name: "list-add"
+ text: i18n("Add Application...")
+ onClicked: appPickerDialog.open()
+ Layout.fillWidth: true
+ }
+
+ Button {
+ id: addSpecialButton
+ icon.name: "preferences-desktop-default-applications"
+ text: i18n("Add Special Launcher...")
+ onClicked: specialLauncherMenu.popup()
+
+ Menu {
+ id: specialLauncherMenu
+
+ MenuItem {
+ text: i18n("Default Web Browser")
+ icon.name: "internet-web-browser"
+ onTriggered: {
+ addLauncher("preferred://browser");
+ }
+ }
+
+ MenuItem {
+ text: i18n("Default File Manager")
+ icon.name: "system-file-manager"
+ onTriggered: {
+ addLauncher("preferred://filemanager");
+ }
+ }
+
+ MenuItem {
+ text: i18n("Default Mail Client")
+ icon.name: "internet-mail"
+ onTriggered: {
+ addLauncher("preferred://mail");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ function addLauncher(url) {
+ let currentLaunchers = pinnedLaunchers;
+ // Don't add if already in the list
+ if (currentLaunchers.indexOf(url) !== -1) {
+ return;
+ }
+
+ currentLaunchers.push(url);
+ plasmoid.configuration.launchers = currentLaunchers;
+ pinnedLaunchers = currentLaunchers;
+ refreshPinnedAppsModel();
+ }
+
+ // Move item in the model and update configuration
+ function moveItem(fromIndex, toIndex) {
+ // First create a copy of the current launchers
+ let currentLaunchers = [];
+ for (let i = 0; i < pinnedLaunchers.length; i++) {
+ currentLaunchers.push(pinnedLaunchers[i]);
+ }
+
+ // Move the item in the array
+ const item = currentLaunchers.splice(fromIndex, 1)[0];
+ currentLaunchers.splice(toIndex, 0, item);
+
+ console.log("Moving item from", fromIndex, "to", toIndex);
+ console.log("New order:", currentLaunchers.join(", "));
+ // Update the configuration
+ plasmoid.configuration.launchers = currentLaunchers;
+ pinnedLaunchers = currentLaunchers;
+ // Refresh model to ensure view is updated
+ refreshPinnedAppsModel();
+ }
+}
diff --git a/package/contents/ui/GroupDialog.qml b/package/contents/ui/GroupDialog.qml
index 3188df30..faee5d03 100644
--- a/package/contents/ui/GroupDialog.qml
+++ b/package/contents/ui/GroupDialog.qml
@@ -102,6 +102,7 @@ PlasmaCore.Dialog {
visible: true
inPopup: true
tasksRoot: tasks
+ readonly property bool isSubTask: true // TODO: check if is needed
ListView.onRemove: Qt.callLater(groupFilter.updateMaxTextWidth)
Connections {
diff --git a/package/contents/ui/Indicators.qml b/package/contents/ui/Indicators.qml
new file mode 100644
index 00000000..ea361c03
--- /dev/null
+++ b/package/contents/ui/Indicators.qml
@@ -0,0 +1,240 @@
+import QtQuick
+import QtQuick.Controls
+
+import org.kde.kirigami as Kirigami
+import org.kde.plasma.core as PlasmaCore
+import org.kde.plasma.plasmoid
+
+Flow {
+ anchors.margins: 4
+ spacing: 10
+ property int taskCount: 1
+ Repeater {
+ model: {
+ if(!plasmoid.configuration.indicatorsEnabled)
+ return 0;
+ if(taskCount < plasmoid.configuration.indicatorMinLimit)
+ return 0;
+ if(task.isSubTask)//Target only the main task items.
+ return 0;
+ if(task.state === 'launcher') {
+ return 0;
+ }
+ return Math.min((taskCount === 0) ? 1 : taskCount, maxStates);
+ }
+ readonly property int maxStates: plasmoid.configuration.indicatorMaxLimit
+
+ Rectangle{
+ id: stateRect
+ Behavior on height { PropertyAnimation {duration: plasmoid.configuration.indicatorsAnimated ? 250 : 0} }
+ Behavior on width { PropertyAnimation {duration: plasmoid.configuration.indicatorsAnimated ? 250 : 0} }
+ Behavior on color { PropertyAnimation {duration: plasmoid.configuration.indicatorsAnimated ? 250 : 0} }
+ Behavior on radius { PropertyAnimation {duration: plasmoid.configuration.indicatorsAnimated ? 250 : 0} }
+ readonly property color decoColor: frame.indicatorColor
+ readonly property int maxStates: plasmoid.configuration.indicatorMaxLimit
+ readonly property bool isFirst: index === 0
+ readonly property int adjust: plasmoid.configuration.indicatorShrink
+ readonly property int indicatorLength: plasmoid.configuration.indicatorLength
+ readonly property int spacing: Kirigami.Units.smallSpacing
+ readonly property bool isVertical: {
+ if(plasmoid.formFactor === PlasmaCore.Types.Vertical && !plasmoid.configuration.indicatorOverride)
+ return true;
+ if(plasmoid.formFactor == PlasmaCore.Types.Floating && plasmoid.configuration.indicatorOverride && (plasmoid.configuration.indicatorLocation === 1 || plasmoid.configuration.indicatorLocation === 2))
+ return true;
+ if(plasmoid.configuration.indicatorOverride && (plasmoid.configuration.indicatorLocation === 1 || plasmoid.configuration.indicatorLocation === 2))
+ return true;
+ else{
+ return false;
+ }
+ }
+ readonly property var computedVar: {
+ var height;
+ var width;
+ var colorCalc;
+ var colorEval = '#FFFFFF';
+ var parentSize = !isVertical ? frame.width : frame.height;
+ var indicatorComputedSize;
+ var adjustment = isFirst ? adjust : 0
+ var parentSpacingAdjust = taskCount >= 1 && maxStates >= 2 ? (spacing * 2.5) : 0 //Spacing fix for multiple items
+ if(plasmoid.configuration.indicatorDominantColor){
+ colorEval = decoColor
+ }
+ if(plasmoid.configuration.indicatorAccentColor){
+ colorEval = Kirigami.Theme.highlightColor
+ }
+ else if(!plasmoid.configuration.indicatorDominantColor && !plasmoid.configuration.indicatorAccentColor){
+ colorEval = plasmoid.configuration.indicatorCustomColor
+ }
+ if(isFirst){//compute the size
+
+ var growFactor = plasmoid.configuration.indicatorGrowFactor / 100
+ if(plasmoid.configuration.indicatorGrow && task.state === "minimized") {
+ var mainSize = indicatorLength * growFactor;
+ }
+ else{
+ var mainSize = (parentSize + parentSpacingAdjust);
+ }
+ switch(plasmoid.configuration.indicatorStyle){
+ case 0:
+ indicatorComputedSize = mainSize - (Math.min(taskCount, maxStates === 1 ? 0 : maxStates) * (spacing + indicatorLength)) - adjust
+ break
+ case 1:
+ indicatorComputedSize = mainSize - (Math.min(taskCount, maxStates === 1 ? 0 : maxStates) * (spacing + indicatorLength)) - adjust
+ break
+ case 2:
+ indicatorComputedSize = plasmoid.configuration.indicatorGrow && task.state !== "minimized" ? indicatorLength * growFactor : indicatorLength
+ break
+ default:
+ break
+ }
+
+ }
+ else {
+ indicatorComputedSize = indicatorLength
+ }
+ if(!isVertical){
+ width = indicatorComputedSize;
+ height = plasmoid.configuration.indicatorSize
+ }
+ else{
+ width = plasmoid.configuration.indicatorSize
+ height = indicatorComputedSize
+
+ }
+ if(plasmoid.configuration.indicatorDesaturate && task.state === "minimized") {
+ var colorHSL = hexToHSL(colorEval)
+ colorCalc = Qt.hsla(colorHSL.h, colorHSL.s*0.5, colorHSL.l*.8, 1)
+ }
+ else if(!isFirst && plasmoid.configuration.indicatorStyle === 0 && task.state !== "minimized") {//Metro specific handling
+ colorCalc = Qt.darker(colorEval, 1.2)
+ }
+ else {
+ colorCalc = colorEval
+ }
+ return {height: height, width: width, colorCalc: colorCalc}
+ }
+ width: computedVar.width
+ height: computedVar.height
+ color: computedVar.colorCalc
+ radius: Math.min(width, height) * (plasmoid.configuration.indicatorRadius / 200)
+
+ Rectangle{
+ Behavior on height { PropertyAnimation {duration: plasmoid.configuration.indicatorsAnimated ? 250 : 0} }
+ Behavior on width { PropertyAnimation {duration: plasmoid.configuration.indicatorsAnimated ? 250 : 0} }
+ Behavior on color { PropertyAnimation {duration: plasmoid.configuration.indicatorsAnimated ? 250 : 0} }
+ Behavior on radius { PropertyAnimation {duration: plasmoid.configuration.indicatorsAnimated ? 250 : 0} }
+ visible: task.isWindow && task.smartLauncherItem && task.smartLauncherItem.progressVisible && isFirst && plasmoid.configuration.indicatorProgress
+ anchors{
+ top: isVertical ? undefined : parent.top
+ bottom: isVertical ? undefined : parent.bottom
+ left: isVertical ? parent.left : undefined
+ right: isVertical ? parent.right : undefined
+ }
+ readonly property var progress: {
+ if(task.smartLauncherItem && task.smartLauncherItem.progressVisible && task.smartLauncherItem.progress){
+ return task.smartLauncherItem.progress / 100
+ }
+ return 0
+ }
+ width: isVertical ? parent.width : parent.width * progress
+ height: isVertical ? parent.height * progress : parent.height
+ radius: parent.radius
+ color: plasmoid.configuration.indicatorProgressColor
+ }
+ }
+ }
+
+ states:[
+ State {
+ name: "bottom"
+ when: (plasmoid.configuration.indicatorOverride && plasmoid.configuration.indicatorLocation === 0)
+ || (!plasmoid.configuration.indicatorOverride && plasmoid.location === PlasmaCore.Types.BottomEdge && !plasmoid.configuration.indicatorReverse)
+ || (!plasmoid.configuration.indicatorOverride && plasmoid.location === PlasmaCore.Types.TopEdge && plasmoid.configuration.indicatorReverse)
+ || (plasmoid.location === PlasmaCore.Types.Floating && plasmoid.configuration.indicatorLocation === 0)
+ || (plasmoid.location === PlasmaCore.Types.Floating && !plasmoid.configuration.indicatorOverride && !plasmoid.configuration.indicatorReverse)
+
+ AnchorChanges {
+ target: indicator
+ anchors{ top:undefined; bottom:parent.bottom; left:undefined; right:undefined;
+ horizontalCenter:parent.horizontalCenter; verticalCenter:undefined}
+ }
+ PropertyChanges {
+ target: indicator
+ width: undefined
+ height: plasmoid.configuration.indicatorSize
+
+ anchors.topMargin: 0;
+ anchors.bottomMargin: plasmoid.configuration.indicatorEdgeOffset;
+ anchors.leftMargin: 0;
+ anchors.rightMargin: 0;
+ }
+ },
+ State {
+ name: "left"
+ when: (plasmoid.configuration.indicatorOverride && plasmoid.configuration.indicatorLocation === 1)
+ || (!plasmoid.configuration.indicatorOverride && plasmoid.location === PlasmaCore.Types.LeftEdge && !plasmoid.configuration.indicatorReverse)
+ || (!plasmoid.configuration.indicatorOverride && plasmoid.location === PlasmaCore.Types.RightEdge && plasmoid.configuration.indicatorReverse)
+ || (plasmoid.location === PlasmaCore.Types.Floating && plasmoid.configuration.indicatorLocation === 1 && plasmoid.configuration.indicatorOverride)
+
+ AnchorChanges {
+ target: indicator
+ anchors{ top:undefined; bottom:undefined; left:parent.left; right:undefined;
+ horizontalCenter:undefined; verticalCenter:parent.verticalCenter}
+ }
+ PropertyChanges {
+ target: indicator
+ height: undefined
+ width: plasmoid.configuration.indicatorSize
+ anchors.topMargin: 0;
+ anchors.bottomMargin: 0;
+ anchors.leftMargin: plasmoid.configuration.indicatorEdgeOffset;
+ anchors.rightMargin: 0;
+ }
+ },
+ State {
+ name: "right"
+ when: (plasmoid.configuration.indicatorOverride && plasmoid.configuration.indicatorLocation === 2)
+ || (!plasmoid.configuration.indicatorOverride && plasmoid.location === PlasmaCore.Types.RightEdge && !plasmoid.configuration.indicatorReverse)
+ || (!plasmoid.configuration.indicatorOverride && plasmoid.location === PlasmaCore.Types.LeftEdge && plasmoid.configuration.indicatorReverse)
+ || (plasmoid.location === PlasmaCore.Types.Floating && plasmoid.configuration.indicatorLocation === 2 && plasmoid.configuration.indicatorOverride)
+
+ AnchorChanges {
+ target: indicator
+ anchors{ top:undefined; bottom:undefined; left:undefined; right:parent.right;
+ horizontalCenter:undefined; verticalCenter:parent.verticalCenter}
+ }
+ PropertyChanges {
+ target: indicator
+ height: undefined
+ width: plasmoid.configuration.indicatorSize
+ anchors.topMargin: 0;
+ anchors.bottomMargin: 0;
+ anchors.leftMargin: 0;
+ anchors.rightMargin: plasmoid.configuration.indicatorEdgeOffset;
+ }
+ },
+ State {
+ name: "top"
+ when: (plasmoid.configuration.indicatorOverride && plasmoid.configuration.indicatorLocation === 3)
+ || (!plasmoid.configuration.indicatorOverride && plasmoid.location === PlasmaCore.Types.TopEdge && !plasmoid.configuration.indicatorReverse)
+ || (!plasmoid.configuration.indicatorOverride && plasmoid.location === PlasmaCore.Types.BottomEdge && plasmoid.configuration.indicatorReverse)
+ || (plasmoid.location === PlasmaCore.Types.Floating && plasmoid.configuration.indicatorLocation === 3 && plasmoid.configuration.indicatorOverride)
+ || (plasmoid.location === PlasmaCore.Types.Floating && plasmoid.configuration.indicatorReverse && !plasmoid.configuration.indicatorOverride)
+
+ AnchorChanges {
+ target: indicator
+ anchors{ top:parent.top; bottom:undefined; left:undefined; right:undefined;
+ horizontalCenter:parent.horizontalCenter; verticalCenter:undefined}
+ }
+ PropertyChanges {
+ target: indicator
+ width: undefined
+ height: plasmoid.configuration.indicatorSize
+ anchors.topMargin: plasmoid.configuration.indicatorEdgeOffset;
+ anchors.bottomMargin: 0;
+ anchors.leftMargin: 0;
+ anchors.rightMargin: 0;
+ }
+ }
+ ]
+}
diff --git a/package/contents/ui/PipeWireThumbnail.qml b/package/contents/ui/PipeWireThumbnail.qml
index 9209ab58..d719a440 100644
--- a/package/contents/ui/PipeWireThumbnail.qml
+++ b/package/contents/ui/PipeWireThumbnail.qml
@@ -13,7 +13,22 @@ PipeWire.PipeWireSourceItem {
readonly property alias hasThumbnail: pipeWireSourceItem.ready
- anchors.fill: parent
+ // Center it in the parent
+ anchors.centerIn: parent
+
+ // Maintain aspect ratio with smarter dimension calculation
+ width: parent.width
+ height: {
+ // If we have valid source dimensions, calculate proper height
+ if (implicitWidth > 0 && implicitHeight > 0) {
+ return Math.min(parent.height, parent.width * (implicitHeight / implicitWidth));
+ } else {
+ // Fallback to a reasonable default
+ return Math.min(parent.height, parent.width * 0.75);
+ // 4:3 aspect ratio as fallback
+ }
+ }
+
nodeId: waylandItem.nodeId
TaskManager.ScreencastingRequest {
diff --git a/package/contents/ui/Task.qml b/package/contents/ui/Task.qml
index 0f3564b6..6cd522db 100644
--- a/package/contents/ui/Task.qml
+++ b/package/contents/ui/Task.qml
@@ -16,6 +16,7 @@ import org.kde.plasma.components as PlasmaComponents3
import org.kde.kirigami as Kirigami
import org.kde.plasma.private.taskmanager as TaskManagerApplet
import org.kde.plasma.plasmoid
+import Qt5Compat.GraphicalEffects
import "code/layoutmetrics.js" as LayoutMetrics
import "code/tools.js" as TaskTools
@@ -25,33 +26,37 @@ PlasmaCore.ToolTipArea {
activeFocusOnTab: true
- // To achieve a bottom to top layout, the task manager is rotated by 180 degrees(see main.qml).
- // This makes the tasks mirrored, so we mirror them again to fix that.
- rotation: Plasmoid.configuration.reverseMode && Plasmoid.formFactor === PlasmaCore.Types.Vertical ? 180 : 0
+ readonly property bool isMetro: plasmoid.configuration.indicatorStyle === 0
+ readonly property bool isCiliora: plasmoid.configuration.indicatorStyle === 1
+ readonly property bool isDashes: plasmoid.configuration.indicatorStyle === 2
- implicitHeight: inPopup
- ? LayoutMetrics.preferredHeightInPopup()
- : Math.max(tasksRoot.height / tasksRoot.plasmoid.configuration.maxStripes,
- LayoutMetrics.preferredMinHeight())
- implicitWidth: tasksRoot.vertical
- ? Math.max(LayoutMetrics.preferredMinWidth(), Math.min(LayoutMetrics.preferredMaxWidth(), tasksRoot.width / tasksRoot.plasmoid.configuration.maxStripes))
- : 0
+ property string tintColor: Kirigami.ColorUtils.brightnessForColor(Kirigami.Theme.backgroundColor) === Kirigami.ColorUtils.Dark ?
+ "#ffffff" : "#000000"
+
+ rotation: Plasmoid.configuration.reverseMode && Plasmoid.formFactor === PlasmaCore.Types.Vertical ?
+ 180 : 0
+
+ implicitHeight: inPopup ?
+ LayoutMetrics.preferredHeightInPopup() : Math.max(tasksRoot.height / tasksRoot.plasmoid.configuration.maxStripes, LayoutMetrics.preferredMinHeight())
+ implicitWidth: tasksRoot.vertical ?
+ Math.max(LayoutMetrics.preferredMinWidth(), Math.min(LayoutMetrics.preferredMaxWidth(), tasksRoot.width / tasksRoot.plasmoid.configuration.maxStripes)) : 0
Layout.fillWidth: true
Layout.fillHeight: !inPopup
- Layout.maximumWidth: tasksRoot.vertical
- ? -1
- : ((model.IsLauncher && !tasks.iconsOnly) ? tasksRoot.height / taskList.rows : LayoutMetrics.preferredMaxWidth())
- Layout.maximumHeight: tasksRoot.vertical ? LayoutMetrics.preferredMaxHeight() : -1
+ Layout.maximumWidth: tasksRoot.vertical ?
+ -1 : ((model.IsLauncher && !tasks.iconsOnly) ? tasksRoot.height / taskList.rows : LayoutMetrics.preferredMaxWidth())
+ Layout.maximumHeight: tasksRoot.vertical ?
+ LayoutMetrics.preferredMaxHeight() : -1
required property var model
required property int index
- required property /*main.qml*/ Item tasksRoot
+ required property /*main.qml*/ Item tasksRoot
readonly property int pid: model.AppPid
readonly property string appName: model.AppName
readonly property string appId: model.AppId.replace(/\.desktop/, '')
- readonly property bool isIcon: tasksRoot.iconsOnly || model.IsLauncher
+ readonly property bool isIcon: tasksRoot.iconsOnly ||
+ model.IsLauncher
property bool toolTipOpen: false
property bool inPopup: false
property bool isWindow: model.IsWindow
@@ -66,19 +71,280 @@ PlasmaCore.ToolTipArea {
property var audioStreams: []
property bool delayAudioStreamIndicator: false
property bool completed: false
- readonly property bool audioIndicatorsEnabled: Plasmoid.configuration.indicateAudioStreams
+ readonly property
+ bool audioIndicatorsEnabled: Plasmoid.configuration.indicateAudioStreams
readonly property bool hasAudioStream: audioStreams.length > 0
readonly property bool playingAudio: hasAudioStream && audioStreams.some(item => !item.corked)
readonly property bool muted: hasAudioStream && audioStreams.every(item => item.muted)
- readonly property bool highlighted: (inPopup && activeFocus) || (!inPopup && containsMouse)
- || (task.contextMenu && task.contextMenu.status === PlasmaExtras.Menu.Open)
- || (!!tasksRoot.groupDialog && tasksRoot.groupDialog.visualParent === task)
+ readonly property bool highlighted: (inPopup && activeFocus) ||
+ (!inPopup && containsMouse) || (task.contextMenu && task.contextMenu.status === PlasmaExtras.Menu.Open) ||
+ (!!tasksRoot.groupDialog && tasksRoot.groupDialog.visualParent === task)
+
+ property int itemIndex: index // fancytasks
+ // --- MODIFICATION START ---
+
+ // Disable standard tooltip (mainItem: null) but keep active for events
active: (Plasmoid.configuration.showToolTips || tasksRoot.toolTipOpenedByClick === task) && !inPopup && !tasksRoot.groupDialog
- interactive: model.IsWindow || mainItem.playerData
+ mainItem: null
location: Plasmoid.location
- mainItem: model.IsWindow ? openWindowToolTipDelegate : pinnedAppToolTipDelegate
+
+ property alias tooltipContent: delegateLoader.item
+
+ // Check interaction capability (windows always interactive, others check playerData)
+ interactive: model.IsWindow ||
+ (tooltipContent && tooltipContent.playerData)
+
+ // Custom Tooltip Dialog
+ PlasmaCore.Dialog {
+ id: tooltipDialog
+ visualParent: task
+ location: Plasmoid.location
+ type: PlasmaCore.Dialog.Tooltip
+
+ // We draw our own background to handle the gap
+ backgroundHints: PlasmaCore.Types.NoBackground
+ flags: Qt.ToolTip |
+ Qt.FramelessWindowHint | Qt.WA_TranslucentBackground
+
+ visible: false
+
+ mainItem: Item {
+ id: contentWrapper
+
+ // Interaction handler for the whole area (including the empty gap)
+ HoverHandler { id: wrapperHover }
+
+ // Gap configuration
+ readonly property int gapSize: {
+ const standardGap = Kirigami.Units.smallSpacing;
+
+ const zoom = plasmoid.configuration.iconZoomFactor;
+ const sizeOverride = plasmoid.configuration.iconSizeOverride;
+ const fixedSize = plasmoid.configuration.iconSizePx;
+ const iconScale = plasmoid.configuration.iconScale / 100;
+
+ const baseIconHeight = sizeOverride ? fixedSize : (iconBox.height * iconScale);
+ const zoomedIconHeight = baseIconHeight + zoom;
+
+ const padding = (iconBox.height - baseIconHeight) / 2;
+ const overflow = zoom - padding;
+
+ if (overflow > 0 && zoomedIconHeight > (iconBox.height + standardGap)) {
+ return overflow + 1;
+ }
+
+ return standardGap;
+ }
+
+ readonly property int loc: Plasmoid.location
+
+ // Calculate size: Background Frame + Gap
+ implicitWidth: (loc === PlasmaCore.Types.LeftEdge || loc === PlasmaCore.Types.RightEdge) ?
+ (bgFrame.width + gapSize) : bgFrame.width
+ implicitHeight: (loc === PlasmaCore.Types.TopEdge || loc === PlasmaCore.Types.BottomEdge) ?
+ (bgFrame.height + gapSize) : bgFrame.height
+
+ // The visual bubble
+ KSvg.FrameSvgItem {
+ id: bgFrame
+ imagePath: "widgets/tooltip"
+
+ readonly property int thumbBaseWidth: Kirigami.Units.gridUnit * 14
+ readonly property int thumbBaseHeight: thumbBaseWidth / (Screen.width / Screen.height)
+
+ width: Math.max(delegateLoader.item ? delegateLoader.item.implicitWidth : 0, model.IsWindow ? thumbBaseWidth : Kirigami.Units.gridUnit * 2) + margins.left + margins.right
+ height: Math.max(delegateLoader.item ? delegateLoader.item.implicitHeight : 0, model.IsWindow ? thumbBaseHeight : Kirigami.Units.gridUnit) + margins.top + margins.bottom
+
+ // Position logic: push away from the panel
+ anchors.top: (contentWrapper.loc === PlasmaCore.Types.BottomEdge) ?
+ parent.top : undefined
+ anchors.bottom: (contentWrapper.loc === PlasmaCore.Types.TopEdge) ?
+ parent.bottom : undefined
+ anchors.left: (contentWrapper.loc === PlasmaCore.Types.RightEdge) ?
+ parent.left : undefined
+ anchors.right: (contentWrapper.loc === PlasmaCore.Types.LeftEdge) ?
+ parent.right : undefined
+
+ // Center on the other axis
+ anchors.horizontalCenter: (contentWrapper.loc === PlasmaCore.Types.TopEdge || contentWrapper.loc === PlasmaCore.Types.BottomEdge) ?
+ parent.horizontalCenter : undefined
+ anchors.verticalCenter: (contentWrapper.loc === PlasmaCore.Types.LeftEdge || contentWrapper.loc === PlasmaCore.Types.RightEdge) ?
+ parent.verticalCenter : undefined
+
+ // Loader switches between complex window delegate and simple text delegate
+ Loader {
+ id: delegateLoader
+
+ anchors.fill: parent
+ anchors.leftMargin: bgFrame.margins.left
+ anchors.rightMargin: bgFrame.margins.right
+ anchors.topMargin: bgFrame.margins.top
+ anchors.bottomMargin: bgFrame.margins.bottom
+
+ sourceComponent: model.IsWindow ?
+ windowDelegate : pinnedAppDelegate
+ asynchronous: false
+
+ onLoaded: {
+ if (task.toolTipOpen) {
+ task.updateMainItemBindings()
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Component for Windows (complex delegate with previews)
+ Component {
+ id: windowDelegate
+ // We load the external file to avoid cyclic dependency issues directly in Task.qml
+ Loader {
+ source: "ToolTipDelegate.qml"
+
+ // Re-expose properties required by updateMainItemBindings
+ property var parentTask
+ property var rootIndex
+ property string appName
+ property int pidParent
+ property var windows
+ property bool isGroup
+ property var icon
+ property url launcherUrl
+ property bool isLauncher
+ property bool isMinimized
+ property string display
+ property string genericName
+ property var virtualDesktops
+ property bool isOnAllVirtualDesktops
+ property list activities
+ property bool smartLauncherCountVisible
+ property int smartLauncherCount
+ property bool blockingUpdates
+
+ // Expose playerData from inner item up to here, so Task can see it for 'interactive' check
+ readonly property var playerData: item ?
+ item.playerData : null
+
+ // Forward these properties to the inner ToolTipDelegate when it loads
+ onLoaded: {
+ item.parentTask = Qt.binding(() => parentTask)
+ item.rootIndex = Qt.binding(() => rootIndex)
+ item.appName = Qt.binding(() => appName)
+ item.pidParent = Qt.binding(() => pidParent)
+ item.windows = Qt.binding(() => windows)
+ item.isGroup = Qt.binding(() => isGroup)
+ item.icon = Qt.binding(() => icon)
+ item.launcherUrl = Qt.binding(() => launcherUrl)
+ item.isLauncher = Qt.binding(() => isLauncher)
+ item.isMinimized = Qt.binding(() => isMinimized)
+ item.display = Qt.binding(() => display)
+ item.genericName = Qt.binding(() => genericName)
+ item.virtualDesktops = Qt.binding(() => virtualDesktops)
+ item.isOnAllVirtualDesktops = Qt.binding(() => isOnAllVirtualDesktops)
+ item.activities = Qt.binding(() => activities)
+ item.smartLauncherCountVisible = Qt.binding(() => smartLauncherCountVisible)
+ item.smartLauncherCount = Qt.binding(() => smartLauncherCount)
+ item.blockingUpdates = Qt.binding(() => blockingUpdates)
+ }
+ }
+ }
+
+ // Component for Pinned Apps (Simple text)
+ Component {
+ id: pinnedAppDelegate
+ Item {
+ // These properties are set by updateMainItemBindings
+ property var parentTask
+ property var rootIndex
+ property string appName
+ property int pidParent
+ property var windows
+ property bool isGroup
+ property var icon
+ property url launcherUrl
+ property bool isLauncher
+ property bool isMinimized
+ property string display
+ property string genericName
+ property var virtualDesktops
+ property bool isOnAllVirtualDesktops
+ property list activities
+ property bool smartLauncherCountVisible
+ property int smartLauncherCount
+ property bool blockingUpdates
+
+ // Dummy playerData for interactive check (always null for pinned)
+ property var playerData: null
+
+ implicitWidth: label.implicitWidth
+ implicitHeight: label.implicitHeight
+
+ PlasmaComponents3.Label {
+ id: label
+ text: parent.display
+ anchors.centerIn: parent
+ }
+ }
+ }
+
+ // Timers for interaction
+ Timer {
+ id: closeTimer
+ interval: 250 // Time to cross the gap
+ onTriggered: {
+ if (!task.containsMouse && !wrapperHover.hovered) {
+ tooltipDialog.visible = false;
+ task.toolTipOpen = false;
+ tasksRoot.toolTipOpenedByClick = null;
+ }
+ }
+ }
+
+ Timer {
+ id: openTimer
+ interval: 500
+ onTriggered: {
+ if (task.containsMouse) {
+ tooltipDialog.visible = true;
+ task.toolTipOpen = true;
+ task.updateMainItemBindings();
+ tasksRoot.toolTipAreaItem = task;
+ }
+ }
+ }
+
+ Connections {
+ target: wrapperHover
+ function onHoveredChanged() {
+ if (wrapperHover.hovered) {
+ closeTimer.stop();
+ } else {
+ if (!task.containsMouse) closeTimer.start();
+ }
+ }
+ }
+
+ onContainsMouseChanged: {
+ if (containsMouse) {
+ task.forceActiveFocus(Qt.MouseFocusReason);
+ closeTimer.stop();
+
+ if (tooltipDialog.visible) {
+ task.updateMainItemBindings();
+ task.toolTipOpen = true;
+ tasksRoot.toolTipAreaItem = task;
+ } else {
+ openTimer.restart();
+ }
+ } else {
+ openTimer.stop();
+ closeTimer.start();
+ }
+ }
+ // --- MODIFICATION END ---
onXChanged: {
if (!completed) {
@@ -150,7 +416,7 @@ PlasmaCore.ToolTipArea {
}
if (model.IsLauncher) {
- return i18nc("@info:usagetip %1 application name", "Launch %1", model.display)
+ return i18nc("@info:usagetip %1 application name", "Launch %1", model.display);
}
let smartLauncherDescription = "";
@@ -161,59 +427,54 @@ PlasmaCore.ToolTipArea {
if (model.IsGroupParent) {
switch (Plasmoid.configuration.groupedTaskVisualization) {
case 0:
- break; // Use the default description
- case 1: {
- if (Plasmoid.configuration.showToolTips) {
- return `${i18nc("@info:usagetip %1 task name", "Show Task tooltip for %1", model.display)}; ${smartLauncherDescription}`;
+ break;
+ // Use the default description
+ case 1:
+ {
+ if (Plasmoid.configuration.showToolTips) {
+ return `${i18nc("@info:usagetip %1 task name", "Show Task tooltip for %1", model.display)};
+${smartLauncherDescription}`;
+ }
+ // fallthrough
}
- // fallthrough
- }
- case 2: {
- if (effectWatcher.registered) {
- return `${i18nc("@info:usagetip %1 task name", "Show windows side by side for %1", model.display)}; ${smartLauncherDescription}`;
+ case 2:
+ {
+ if (effectWatcher.registered) {
+ return `${i18nc("@info:usagetip %1 task name", "Show windows side by side for %1", model.display)};
+${smartLauncherDescription}`;
+ }
+ // fallthrough
}
- // fallthrough
- }
default:
- return `${i18nc("@info:usagetip %1 task name", "Open textual list of windows for %1", model.display)}; ${smartLauncherDescription}`;
+ return `${i18nc("@info:usagetip %1 task name", "Open textual list of windows for %1", model.display)};
+${smartLauncherDescription}`;
}
}
- return `${i18n("Activate %1", model.display)}; ${smartLauncherDescription}`;
+ return `${i18n("Activate %1", model.display)};
+${smartLauncherDescription}`;
}
Accessible.role: Accessible.Button
Accessible.onPressAction: leftTapHandler.leftClick()
- onToolTipVisibleChanged: toolTipVisible => {
- task.toolTipOpen = toolTipVisible;
- if (!toolTipVisible) {
- tasksRoot.toolTipOpenedByClick = null;
- } else {
- tasksRoot.toolTipAreaItem = task;
- }
- }
-
- onContainsMouseChanged: {
- if (containsMouse) {
- task.forceActiveFocus(Qt.MouseFocusReason);
- task.updateMainItemBindings();
- } else {
- tasksRoot.toolTipOpenedByClick = null;
- }
- }
-
onHighlightedChanged: {
// ensure it doesn't get stuck with a window highlighted
- backend.cancelHighlightWindows();
+ tasksRoot.cancelHighlightWindows();
}
- onPidChanged: updateAudioStreams({delay: false})
- onAppNameChanged: updateAudioStreams({delay: false})
+ onPidChanged: updateAudioStreams({
+ delay: false
+ })
+ onAppNameChanged: updateAudioStreams({
+ delay: false
+ })
onIsWindowChanged: {
if (model.IsWindow) {
taskInitComponent.createObject(task);
- updateAudioStreams({delay: false});
+ updateAudioStreams({
+ delay: false
+ });
}
}
@@ -226,10 +487,8 @@ PlasmaCore.ToolTipArea {
}
onIndexChanged: {
- hideToolTip();
-
- if (!inPopup && !tasksRoot.vertical
- && !Plasmoid.configuration.separateLaunchers) {
+ tooltipDialog.visible = false;
+ if (!inPopup && !tasksRoot.vertical && !Plasmoid.configuration.separateLaunchers) {
tasksRoot.requestLayout();
}
}
@@ -265,8 +524,8 @@ PlasmaCore.ToolTipArea {
Keys.onMenuPressed: event => contextMenuTimer.start()
Keys.onReturnPressed: event => TaskTools.activateTask(modelIndex(), model, event.modifiers, task, Plasmoid, tasksRoot, effectWatcher.registered)
- Keys.onEnterPressed: event => Keys.returnPressed(event);
- Keys.onSpacePressed: event => Keys.returnPressed(event);
+ Keys.onEnterPressed: event => Keys.returnPressed(event)
+ Keys.onSpacePressed: event => Keys.returnPressed(event)
Keys.onUpPressed: event => Keys.leftPressed(event)
Keys.onDownPressed: event => Keys.rightPressed(event)
Keys.onLeftPressed: event => {
@@ -285,13 +544,21 @@ PlasmaCore.ToolTipArea {
}
function modelIndex(): /*QModelIndex*/ var {
- return inPopup
- ? tasksModel.makeModelIndex(groupDialog.visualParent.index, index)
- : tasksModel.makeModelIndex(index);
+ return inPopup ?
+ tasksModel.makeModelIndex(groupDialog.visualParent.index, index) : tasksModel.makeModelIndex(index);
+ }
+
+ function closeTooltip(): void {
+ tooltipDialog.visible = false;
+ task.toolTipOpen = false;
+ tasksRoot.toolTipOpenedByClick = null;
+ if (typeof task.hideImmediately === "function") {
+ task.hideImmediately();
+ }
}
function showContextMenu(args: var): void {
- task.hideImmediately();
+ task.closeTooltip();
contextMenu = tasksRoot.createContextMenu(task, modelIndex(), args);
contextMenu.show();
}
@@ -299,7 +566,8 @@ PlasmaCore.ToolTipArea {
function updateAudioStreams(args: var): void {
if (args) {
// When the task just appeared (e.g. virtual desktop switch), show the audio indicator
- // right away. Only when audio streams change during the lifetime of this task, delay
+ // right away.
+ // Only when audio streams change during the lifetime of this task, delay
// showing that to avoid distraction.
delayAudioStreamIndicator = !!args.delay;
}
@@ -319,7 +587,8 @@ PlasmaCore.ToolTipArea {
pa.registerPidMatch(model.AppName);
} else {
// We only want to fall back to appName matching if we never managed to map
- // a PID to an audio stream window. Otherwise if you have two instances of
+ // a PID to an audio stream window.
+ // Otherwise if you have two instances of
// an application, one playing and the other not, it will look up appName
// for the non-playing instance and erroneously show an indicator on both.
if (!pa.hasPidMatch(model.AppName)) {
@@ -339,37 +608,37 @@ PlasmaCore.ToolTipArea {
}
}
- // Will also be called in activateTaskAtIndex(index)
+ // UPDATED: Bindings for delegateLoader.item
function updateMainItemBindings(): void {
- if ((mainItem.parentTask === this && mainItem.rootIndex.row === index)
- || (tasksRoot.toolTipOpenedByClick === null && !active)
- || (tasksRoot.toolTipOpenedByClick !== null && tasksRoot.toolTipOpenedByClick !== this)) {
+ var item = delegateLoader.item;
+ if (!item) return;
+
+ if ((item.parentTask === this && item.rootIndex.row === index) || (tasksRoot.toolTipOpenedByClick === null && !active) || (tasksRoot.toolTipOpenedByClick !== null && tasksRoot.toolTipOpenedByClick !== this)) {
return;
}
- mainItem.blockingUpdates = (mainItem.isGroup !== model.IsGroupParent); // BUG 464597 Force unload the previous component
-
- mainItem.parentTask = this;
- mainItem.rootIndex = tasksModel.makeModelIndex(index, -1);
-
- mainItem.appName = Qt.binding(() => model.AppName);
- mainItem.pidParent = Qt.binding(() => model.AppPid);
- mainItem.windows = Qt.binding(() => model.WinIdList);
- mainItem.isGroup = Qt.binding(() => model.IsGroupParent);
- mainItem.icon = Qt.binding(() => model.decoration);
- mainItem.launcherUrl = Qt.binding(() => model.LauncherUrlWithoutIcon);
- mainItem.isLauncher = Qt.binding(() => model.IsLauncher);
- mainItem.isMinimized = Qt.binding(() => model.IsMinimized);
- mainItem.display = Qt.binding(() => model.display);
- mainItem.genericName = Qt.binding(() => model.GenericName);
- mainItem.virtualDesktops = Qt.binding(() => model.VirtualDesktops);
- mainItem.isOnAllVirtualDesktops = Qt.binding(() => model.IsOnAllVirtualDesktops);
- mainItem.activities = Qt.binding(() => model.Activities);
-
- mainItem.smartLauncherCountVisible = Qt.binding(() => smartLauncherItem?.countVisible ?? false);
- mainItem.smartLauncherCount = Qt.binding(() => mainItem.smartLauncherCountVisible ? smartLauncherItem.count : 0);
-
- mainItem.blockingUpdates = false;
+ item.blockingUpdates = (item.isGroup !== model.IsGroupParent);
+
+ item.parentTask = this;
+ item.rootIndex = tasksModel.makeModelIndex(index, -1);
+ item.appName = Qt.binding(() => model.AppName);
+ item.pidParent = Qt.binding(() => model.AppPid);
+ item.windows = Qt.binding(() => model.WinIdList);
+ item.isGroup = Qt.binding(() => model.IsGroupParent);
+ item.icon = Qt.binding(() => model.decoration);
+ item.launcherUrl = Qt.binding(() => model.LauncherUrlWithoutIcon);
+ item.isLauncher = Qt.binding(() => model.IsLauncher);
+ item.isMinimized = Qt.binding(() => model.IsMinimized);
+ item.display = Qt.binding(() => model.display);
+ item.genericName = Qt.binding(() => model.GenericName);
+ item.virtualDesktops = Qt.binding(() => model.VirtualDesktops);
+ item.isOnAllVirtualDesktops = Qt.binding(() => model.IsOnAllVirtualDesktops);
+ item.activities = Qt.binding(() => model.Activities);
+
+ item.smartLauncherCountVisible = Qt.binding(() => smartLauncherItem?.countVisible ?? false);
+ item.smartLauncherCount = Qt.binding(() => item.smartLauncherCountVisible ? smartLauncherItem.count : 0);
+
+ item.blockingUpdates = false;
tasksRoot.toolTipAreaItem = this;
}
@@ -377,20 +646,78 @@ PlasmaCore.ToolTipArea {
target: pulseAudio.item
ignoreUnknownSignals: true // Plasma-PA might not be available
function onStreamsChanged(): void {
- task.updateAudioStreams({delay: true})
+ task.updateAudioStreams({
+ delay: true
+ });
}
}
+ function hexToHSL(hex) {
+ var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
+ let r = parseInt(result[1], 16);
+ let g = parseInt(result[2], 16);
+ let b = parseInt(result[3], 16);
+ r /= 255, g /= 255, b /= 255;
+ var max = Math.max(r, g, b), min = Math.min(r, g, b);
+ var h, s, l = (max + min) / 2;
+ if (max == min) {
+ h = s = 0; // achromatic
+ } else {
+ var d = max - min;
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
+ switch (max) {
+ case r:
+ h = (g - b) / d + (g < b ? 6 : 0);
+ break;
+ case g:
+ h = (b - r) / d + 2;
+ break;
+ case b:
+ h = (r - g) / d + 4;
+ break;
+ }
+ h /= 6;
+ }
+ var HSL = new Object();
+ HSL['h'] = h;
+ HSL['s'] = s;
+ HSL['l'] = l;
+ return HSL;
+ }
+
+ ColorOverlay {
+ id: colorOverride
+ anchors.fill: frame
+ source: frame
+ color: plasmoid.configuration.buttonColorizeDominant ?
+ frame.indicatorColor : plasmoid.configuration.buttonColorizeCustom
+ visible: plasmoid.configuration.buttonColorize ?
+ true : false
+ }
+
+ Indicators {
+ id: indicator
+ taskCount: task.childCount
+ visible: plasmoid.configuration.indicatorsEnabled ?
+ true : false
+ flow: Flow.LeftToRight
+ spacing: Kirigami.Units.smallSpacing
+ clip: true
+ }
+
TapHandler {
id: menuTapHandler
acceptedButtons: Qt.LeftButton
- acceptedDevices: PointerDevice.TouchScreen | PointerDevice.Stylus
+ acceptedDevices: PointerDevice.TouchScreen |
+ PointerDevice.Stylus
gesturePolicy: TapHandler.ReleaseWithinBounds
onLongPressed: {
// When we're a launcher, there's no window controls, so we can show all
// places without the menu getting super huge.
if (model.IsLauncher) {
- showContextMenu({showAllPlaces: true})
+ showContextMenu({
+ showAllPlaces: true
+ });
} else {
showContextMenu();
}
@@ -399,9 +726,11 @@ PlasmaCore.ToolTipArea {
TapHandler {
acceptedButtons: Qt.RightButton
- acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad | PointerDevice.Stylus
+ acceptedDevices: PointerDevice.Mouse |
+ PointerDevice.TouchPad | PointerDevice.Stylus
gesturePolicy: TapHandler.WithinBounds // Release grab when menu appears
- onPressedChanged: if (pressed) contextMenuTimer.start()
+ onPressedChanged: if (pressed)
+ contextMenuTimer.start()
}
Timer {
@@ -417,20 +746,21 @@ PlasmaCore.ToolTipArea {
function leftClick(): void {
if (Plasmoid.configuration.showToolTips && task.active) {
- hideToolTip();
+ tooltipDialog.visible = false;
}
TaskTools.activateTask(modelIndex(), model, point.modifiers, task, Plasmoid, tasksRoot, effectWatcher.registered);
}
}
TapHandler {
- acceptedButtons: Qt.MiddleButton | Qt.BackButton | Qt.ForwardButton
+ acceptedButtons: Qt.MiddleButton |
+ Qt.BackButton | Qt.ForwardButton
onTapped: (eventPoint, button) => {
if (button === Qt.MiddleButton) {
if (Plasmoid.configuration.middleClickAction === TaskManagerApplet.Backend.NewInstance) {
tasksModel.requestNewInstance(modelIndex());
} else if (Plasmoid.configuration.middleClickAction === TaskManagerApplet.Backend.Close) {
- tasksRoot.taskClosedWithMouseMiddleButton = model.WinIdList.slice()
+ tasksRoot.taskClosedWithMouseMiddleButton = model.WinIdList.slice();
tasksModel.requestClose(modelIndex());
} else if (Plasmoid.configuration.middleClickAction === TaskManagerApplet.Backend.ToggleMinimized) {
tasksModel.requestToggleMinimized(modelIndex());
@@ -459,19 +789,34 @@ PlasmaCore.ToolTipArea {
KSvg.FrameSvgItem {
id: frame
+ Kirigami.ImageColors {
+ id: imageColors
+ source: model.decoration
+ }
+ property color dominantColor: imageColors.dominant
+ property color indicatorColor: Kirigami.ColorUtils.tintWithAlpha(frame.dominantColor, tintColor, .38)
+
anchors {
fill: parent
- topMargin: (!tasksRoot.vertical && taskList.rows > 1) ? LayoutMetrics.iconMargin : 0
- bottomMargin: (!tasksRoot.vertical && taskList.rows > 1) ? LayoutMetrics.iconMargin : 0
- leftMargin: ((inPopup || tasksRoot.vertical) && taskList.columns > 1) ? LayoutMetrics.iconMargin : 0
- rightMargin: ((inPopup || tasksRoot.vertical) && taskList.columns > 1) ? LayoutMetrics.iconMargin : 0
+ topMargin: (!tasksRoot.vertical && taskList.rows > 1) ?
+ LayoutMetrics.iconMargin : 0
+ bottomMargin: (!tasksRoot.vertical && taskList.rows > 1) ?
+ LayoutMetrics.iconMargin : 0
+ leftMargin: ((inPopup || tasksRoot.vertical) && taskList.columns > 1) ?
+ LayoutMetrics.iconMargin : 0
+ rightMargin: ((inPopup || tasksRoot.vertical) && taskList.columns > 1) ?
+ LayoutMetrics.iconMargin : 0
}
- imagePath: "widgets/tasks"
+ imagePath: plasmoid.configuration.disableButtonSvg ?
+ "" : "widgets/tasks"
+ enabledBorders: plasmoid.configuration.useBorders ? 1 | 2 | 4 |
+ 8 : 0
property bool isHovered: task.highlighted && Plasmoid.configuration.taskHoverEffect
property string basePrefix: "normal"
- prefix: isHovered ? TaskTools.taskPrefixHovered(basePrefix, Plasmoid.location) : TaskTools.taskPrefix(basePrefix, Plasmoid.location)
+ prefix: isHovered ?
+ TaskTools.taskPrefixHovered(basePrefix, Plasmoid.location) : TaskTools.taskPrefix(basePrefix, Plasmoid.location)
// Avoid repositioning delegate item after dragFinished
DragHandler {
@@ -486,7 +831,7 @@ PlasmaCore.ToolTipArea {
while (item.parent) {
item = item.parent;
if (item.appletRequestsInhibitDnD !== undefined) {
- item.appletRequestsInhibitDnD = value
+ item.appletRequestsInhibitDnD = value;
}
}
}
@@ -504,7 +849,7 @@ PlasmaCore.ToolTipArea {
dragHelper.Drag.mimeData = {
"text/x-orgkdeplasmataskmanager_taskurl": backend.tryDecodeApplicationsUrl(model.LauncherUrlWithoutIcon).toString(),
[model.MimeType]: model.MimeData,
- "application/x-orgkdeplasmataskmanager_taskbuttonitem": model.MimeData,
+ "application/x-orgkdeplasmataskmanager_taskbuttonitem": model.MimeData
};
dragHelper.Drag.active = dragHandler.active;
});
@@ -537,13 +882,13 @@ PlasmaCore.ToolTipArea {
topMargin: adjustMargin(false, parent.height, taskFrame.margins.top)
}
- width: task.inPopup ? Math.max(Kirigami.Units.iconSizes.sizeForLabels, Kirigami.Units.iconSizes.medium) : Math.min(task.parent?.minimumWidth ?? 0, task.height)
- height: task.inPopup ? width : (parent.height - adjustMargin(false, parent.height, taskFrame.margins.top)
- - adjustMargin(false, parent.height, taskFrame.margins.bottom))
+ width: task.inPopup ?
+ Math.max(Kirigami.Units.iconSizes.sizeForLabels, Kirigami.Units.iconSizes.medium) : Math.min(task.parent?.minimumWidth ?? 0, task.height)
+ height: task.inPopup ?
+ width : (parent.height - adjustMargin(false, parent.height, taskFrame.margins.top) - adjustMargin(false, parent.height, taskFrame.margins.bottom))
asynchronous: true
- active: height >= Kirigami.Units.iconSizes.small
- && task.smartLauncherItem && task.smartLauncherItem.countVisible
+ active: height >= Kirigami.Units.iconSizes.small && task.smartLauncherItem && task.smartLauncherItem.countVisible
source: "TaskBadgeOverlay.qml"
function adjustMargin(isVertical: bool, size: real, margin: real): real {
@@ -552,7 +897,6 @@ PlasmaCore.ToolTipArea {
}
var margins = isVertical ? LayoutMetrics.horizontalMargins() : LayoutMetrics.verticalMargins();
-
if ((size - margins) < Kirigami.Units.iconSizes.small) {
return Math.ceil((margin * (Kirigami.Units.iconSizes.small / size)) / 2);
}
@@ -562,10 +906,28 @@ PlasmaCore.ToolTipArea {
Kirigami.Icon {
id: icon
+ property int growSize: active ?
+ plasmoid.configuration.iconZoomFactor : 0
- anchors.fill: parent
+ property bool sizeOverride: plasmoid.configuration.iconSizeOverride
+ property int fixedSize: plasmoid.configuration.iconSizePx
+ property real iconScale: plasmoid.configuration.iconScale / 100
- active: task.highlighted
+ width: (sizeOverride ? fixedSize : (parent.width * iconScale)) + growSize
+ height: (sizeOverride ? fixedSize : (parent.height * iconScale)) + growSize
+
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: (parent.height - (sizeOverride ? fixedSize : (parent.height * iconScale))) / 2
+
+ Behavior on growSize {
+ NumberAnimation {
+ duration: plasmoid.configuration.iconZoomDuration
+ easing.type: Easing.InOutQuad
+ }
+ }
+ roundToIconSize: growSize === 0
+ active: task.highlighted || wrapperHover.hovered
enabled: true
source: model.decoration
@@ -587,9 +949,7 @@ PlasmaCore.ToolTipArea {
PropertyChanges {
target: iconBox
anchors.leftMargin: 0
- width: Math.min(task.parent.minimumWidth, tasks.height)
- - adjustMargin(true, task.width, taskFrame.margins.left)
- - adjustMargin(true, task.width, taskFrame.margins.right)
+ width: Math.min(task.parent.minimumWidth, tasks.height) - adjustMargin(true, task.width, taskFrame.margins.left) - adjustMargin(true, task.width, taskFrame.margins.right)
}
}
]
@@ -606,22 +966,24 @@ PlasmaCore.ToolTipArea {
PlasmaComponents3.Label {
id: label
- visible: (inPopup || !iconsOnly && !model.IsLauncher
- && (parent.width - iconBox.height - Kirigami.Units.smallSpacing) >= LayoutMetrics.spaceRequiredToShowText())
+ visible: (inPopup || !iconsOnly && !model.IsLauncher && (parent.width - iconBox.height - Kirigami.Units.smallSpacing) >= LayoutMetrics.spaceRequiredToShowText())
anchors {
fill: parent
leftMargin: taskFrame.margins.left + iconBox.width + LayoutMetrics.labelMargin
topMargin: taskFrame.margins.top
- rightMargin: taskFrame.margins.right + (audioStreamIcon !== null && audioStreamIcon.visible ? (audioStreamIcon.width + LayoutMetrics.labelMargin) : 0)
+ rightMargin: taskFrame.margins.right + (audioStreamIcon !== null && audioStreamIcon.visible ?
+ (audioStreamIcon.width + LayoutMetrics.labelMargin) : 0)
bottomMargin: taskFrame.margins.bottom
}
- wrapMode: (maximumLineCount === 1) ? Text.NoWrap : Text.Wrap
+ wrapMode: (maximumLineCount === 1) ?
+ Text.NoWrap : Text.Wrap
elide: Text.ElideRight
textFormat: Text.PlainText
verticalAlignment: Text.AlignVCenter
- maximumLineCount: Plasmoid.configuration.maxTextLines || undefined
+ maximumLineCount: Plasmoid.configuration.maxTextLines ||
+ undefined
Accessible.ignored: true
@@ -640,48 +1002,163 @@ PlasmaCore.ToolTipArea {
states: [
State {
name: "launcher"
- when: model.IsLauncher
+ when: model.IsLauncher === true
PropertyChanges {
target: frame
basePrefix: ""
}
+ PropertyChanges {
+ target: colorOverride
+ visible: false
+ }
},
State {
name: "attention"
- when: model.IsDemandingAttention || (task.smartLauncherItem && task.smartLauncherItem.urgent)
+ when: model.IsDemandingAttention === true ||
+ (task.smartLauncherItem && task.smartLauncherItem.urgent)
PropertyChanges {
target: frame
basePrefix: "attention"
+ visible: (plasmoid.configuration.buttonColorize && !frame.isHovered) ||
+ !plasmoid.configuration.buttonColorize
+ }
+ PropertyChanges {
+ target: colorOverride
+ visible: (plasmoid.configuration.buttonColorize && frame.isHovered)
}
},
State {
name: "minimized"
- when: model.IsMinimized
+ when: model.IsMinimized === true && !frame.isHovered && !plasmoid.configuration.disableButtonInactiveSvg
PropertyChanges {
target: frame
basePrefix: "minimized"
+ visible: (plasmoid.configuration.buttonColorize && plasmoid.configuration.buttonColorizeInactive) ?
+ false : true
+ }
+ PropertyChanges {
+ target: colorOverride
+ visible: (plasmoid.configuration.buttonColorize && plasmoid.configuration.buttonColorizeInactive) ?
+ true : false
+ }
+ PropertyChanges {
+ target: indicator
+ visible: plasmoid.configuration.disableInactiveIndicators ?
+ false : true
+ }
+ },
+ State {
+ name: "minimizedNodecoration"
+ when: (model.IsMinimized === true && !frame.isHovered) && plasmoid.configuration.disableButtonInactiveSvg
+
+ PropertyChanges {
+ target: frame
+ basePrefix: "minimized"
+ visible: plasmoid.configuration.disableButtonInactiveSvg ?
+ false : true
+ }
+ PropertyChanges {
+ target: colorOverride
+ visible: plasmoid.configuration.disableButtonInactiveSvg ?
+ false : true
+ }
+ PropertyChanges {
+ target: indicator
+ visible: plasmoid.configuration.disableInactiveIndicators ?
+ false : true
}
},
State {
name: "active"
- when: model.IsActive
+ when: model.IsActive === true
PropertyChanges {
target: frame
basePrefix: "focus"
}
+ PropertyChanges {
+ target: colorOverride
+ visible: plasmoid.configuration.buttonColorize ?
+ true : false
+ }
+ PropertyChanges {
+ target: indicator
+ visible: plasmoid.configuration.indicatorsEnabled ?
+ true : false
+ }
+ },
+ State {
+ name: "inactive"
+ when: model.IsActive === false && !frame.isHovered && !plasmoid.configuration.disableButtonInactiveSvg
+ PropertyChanges {
+ target: colorOverride
+ visible: plasmoid.configuration.buttonColorize && plasmoid.configuration.buttonColorizeInactive ?
+ true : false
+ }
+ PropertyChanges {
+ target: frame
+ visible: plasmoid.configuration.buttonColorize && plasmoid.configuration.buttonColorizeInactive ?
+ false : true
+ }
+ PropertyChanges {
+ target: indicator
+ visible: plasmoid.configuration.disableInactiveIndicators ?
+ false : true
+ }
+ },
+ State {
+ name: "inactiveNoDecoration"
+ when: (model.IsActive === false && !frame.isHovered) && plasmoid.configuration.disableButtonInactiveSvg
+ PropertyChanges {
+ target: colorOverride
+ visible: plasmoid.configuration.disableButtonInactiveSvg ?
+ false : true
+ }
+ PropertyChanges {
+ target: frame
+ visible: plasmoid.configuration.disableButtonInactiveSvg ?
+ false : true
+ }
+ PropertyChanges {
+ target: indicator
+ visible: plasmoid.configuration.disableInactiveIndicators ?
+ false : true
+ }
+ },
+ State {
+ name: "hover"
+ when: frame.isHovered
+ PropertyChanges {
+ target: colorOverride
+ visible: plasmoid.configuration.buttonColorize ?
+ true : false
+ }
+ PropertyChanges {
+ target: frame
+ visible: plasmoid.configuration.buttonColorize ?
+ false : true
+ }
+ PropertyChanges {
+ target: indicator
+ visible: plasmoid.configuration.disableInactiveIndicators ?
+ false : true
+ }
}
]
Component.onCompleted: {
if (!inPopup && model.IsWindow) {
- const component = Qt.createComponent("GroupExpanderOverlay.qml");
- component.createObject(task);
- component.destroy();
- updateAudioStreams({delay: false});
+ if (plasmoid.configuration.groupIconEnabled) {
+ const component = Qt.createComponent("GroupExpanderOverlay.qml");
+ component.createObject(task);
+ component.destroy();
+ }
+ updateAudioStreams({
+ delay: false
+ });
}
if (!inPopup && !model.IsWindow) {
diff --git a/package/contents/ui/TaskBadgeOverlay.qml b/package/contents/ui/TaskBadgeOverlay.qml
index d3ec176a..68749c97 100644
--- a/package/contents/ui/TaskBadgeOverlay.qml
+++ b/package/contents/ui/TaskBadgeOverlay.qml
@@ -12,6 +12,9 @@ import org.kde.plasma.plasmoid
Item {
id: root
+ // Fix: Anchor directly to the icon to sync scaling and position automatically
+ anchors.fill: icon
+
readonly property int iconWidthDelta: (icon.width - icon.paintedWidth) / 2
readonly property bool shiftBadgeDown: (Plasmoid.pluginName === "org.kde.plasma.icontasks") && task.audioStreamIcon !== null
@@ -24,7 +27,8 @@ Item {
anchors.right: parent.right
anchors.rightMargin: -offset
- y: root.shiftBadgeDown ? (icon.height / 2) : 0
+ y: root.shiftBadgeDown ?
+ (icon.height / 2) : 0
Behavior on y {
NumberAnimation { duration: Kirigami.Units.longDuration }
diff --git a/package/contents/ui/TaskProgressOverlay.qml b/package/contents/ui/TaskProgressOverlay.qml
index acfedcf8..3d3cbdca 100644
--- a/package/contents/ui/TaskProgressOverlay.qml
+++ b/package/contents/ui/TaskProgressOverlay.qml
@@ -32,7 +32,7 @@ T.ProgressBar {
KSvg.FrameSvgItem {
id: progressFrame
-
+ enabledBorders: plasmoid.configuration.useBorders ? 1 | 2 | 4 | 8 : 0
anchors.left: parent.left
width: parent.width * control.position
height: parent.height
diff --git a/package/contents/ui/ToolTipDelegate.qml b/package/contents/ui/ToolTipDelegate.qml
index 894a310c..10626240 100644
--- a/package/contents/ui/ToolTipDelegate.qml
+++ b/package/contents/ui/ToolTipDelegate.qml
@@ -24,16 +24,16 @@ Loader {
id: toolTipDelegate
property Task parentTask
- property /*QModelIndex*/var rootIndex
+ property /*QModelIndex*/ var rootIndex
property string appName
property int pidParent
property bool isGroup
- property /*list where WId = int|string*/ var windows: []
+ property /*list where WId = int|string*/ var windows: []
readonly property bool isWin: windows.length > 0
- property /*QIcon*/ var icon
+ property /*QIcon*/ var icon
property url launcherUrl
property bool isLauncher
property bool isMinimized
@@ -41,7 +41,7 @@ Loader {
// Needed for generateSubtext()
property string display
property string genericName
- property /*list*/ var virtualDesktops: [] // Can't use list because of QTBUG-127600
+ property /*list*/ var virtualDesktops: [] // Can't use list because of QTBUG-127600
property bool isOnAllVirtualDesktops
property list activities: []
@@ -52,7 +52,7 @@ Loader {
readonly property bool isVerticalPanel: Plasmoid.formFactor === PlasmaCore.Types.Vertical
// This number controls the overall size of the window tooltips
- readonly property int tooltipInstanceMaximumWidth: Kirigami.Units.gridUnit * 16
+ readonly property int tooltipInstanceMaximumWidth: Kirigami.Units.gridUnit * 14
// These properties are required to make tooltip interactive when there is a player but no window is present.
readonly property Mpris.PlayerContainer playerData: mpris2Source.playerForLauncherUrl(launcherUrl, pidParent)
@@ -86,14 +86,15 @@ Loader {
PlasmaComponents3.ScrollView {
// 2 * Kirigami.Units.smallSpacing is for the margin of tooltipDialog
implicitWidth: leftPadding + rightPadding + Math.min(Screen.desktopAvailableWidth - 2 * Kirigami.Units.smallSpacing, Math.max(delegateModel.estimatedWidth, contentItem.contentItem.childrenRect.width))
- implicitHeight: bottomPadding + Math.min(Screen.desktopAvailableHeight - 2 * Kirigami.Units.smallSpacing, Math.max(delegateModel.estimatedHeight, contentItem.contentItem.childrenRect.height))
+ implicitHeight: topPadding + bottomPadding + Math.min(Screen.desktopAvailableHeight - 2 * Kirigami.Units.smallSpacing, Math.max(delegateModel.estimatedHeight, contentItem.contentItem.childrenRect.height))
ListView {
id: groupToolTipListView
model: delegateModel
- orientation: isVerticalPanel ? ListView.Vertical : ListView.Horizontal
+ orientation: isVerticalPanel ?
+ ListView.Vertical : ListView.Horizontal
reuseItems: true
spacing: Kirigami.Units.gridUnit
}
diff --git a/package/contents/ui/ToolTipInstance.qml b/package/contents/ui/ToolTipInstance.qml
index 8fcb23d5..d63c8cb8 100644
--- a/package/contents/ui/ToolTipInstance.qml
+++ b/package/contents/ui/ToolTipInstance.qml
@@ -24,19 +24,46 @@ import org.kde.kwindowsystem
ColumnLayout {
id: root
+ // ADDED: Tracks hover over the entire ColumnLayout (including headers, gaps)
+ HoverHandler {
+ id: rootHover
+ }
+
required property int index
- required property /*QModelIndex*/ var submodelIndex
+ required property var submodelIndex
required property int appPid
required property string display
required property bool isMinimized
required property bool isOnAllVirtualDesktops
- required property /*list*/ var virtualDesktops // Can't use list because of QTBUG-127600
+ required property var virtualDesktops
required property list activities
// HACK: Avoid blank space in the tooltip after closing a window
ListView.onPooled: width = height = 0
ListView.onReused: width = height = undefined
+ readonly property string calculatedAppName: {
+ if (toolTipDelegate.appName && toolTipDelegate.appName.length > 0) {
+ return toolTipDelegate.appName;
+ }
+
+ const text = display;
+
+ const versionRegex = /\s+(?:—|-|–)\s+([^\s(—|-|–)]+)\s+(?:—|-|–)\s+v?\d+(?:\.\d+)+.*$/i;
+ const matchVersion = text.match(versionRegex);
+ if (matchVersion && matchVersion[1]) {
+ return matchVersion[1];
+ }
+
+ const lastSepRegex = /.*(?:—|-|–)\s+(.*)$/;
+ const matchLast = text.match(lastSepRegex);
+ if (matchLast && matchLast[1]) {
+ return matchLast[1];
+ }
+
+ return "";
+ }
+
readonly property string title: {
if (!toolTipDelegate.isWin) {
return toolTipDelegate.genericName;
@@ -47,24 +74,39 @@ ColumnLayout {
return "";
}
- // Normally the window title will always have " — [app name]" at the end of
- // the window-provided title. But if it doesn't, this is intentional 100%
- // of the time because the developer or user has deliberately removed that
- // part, so just display it with no more fancy processing.
- if (!text.match(/\s+(—|-|–)/)) {
- return text;
+ let counter = "";
+ const counterMatch = text.match(/\s*<\d+>$/);
+ if (counterMatch) {
+ counter = counterMatch[0];
+ text = text.replace(/\s*<\d+>$/, "");
}
- // KWin appends increasing integers in between pointy brackets to otherwise equal window titles.
- // In this case save <#number> as counter and delete it at the end of text.
- text = `${(text.match(/.*(?=\s+(—|-|–))/) || [""])[0]}${(text.match(/<\d+>/) || [""]).pop()}`;
+ const appName = root.calculatedAppName;
+
+ if (appName && appName.length > 0) {
+ const escapedAppName = appName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+ const cleanupRegex = new RegExp(`\\s+(?:—|-|–)\\s+${escapedAppName}.*$`, "i");
+
+ if (text.match(cleanupRegex)) {
+ text = text.replace(cleanupRegex, "");
+ } else {
+ const greedyMatch = text.match(/.*(?=\s+(—|-|–))/);
+ if (greedyMatch) {
+ text = greedyMatch[0];
+ }
+ }
+ } else {
+ const greedyMatch = text.match(/.*(?=\s+(—|-|–))/);
+ if (greedyMatch) {
+ text = greedyMatch[0];
+ }
+ }
- // In case the window title had only redundant information (i.e. appName), text is now empty.
- // Add a hyphen to indicate that and avoid empty space.
if (text === "") {
text = "—";
}
- return text;
+
+ return text + counter;
}
readonly property bool titleIncludesTrack: toolTipDelegate.playerData !== null && title.includes(toolTipDelegate.playerData.track)
@@ -74,28 +116,34 @@ ColumnLayout {
// text labels + close button
RowLayout {
id: header
- // match spacing of DefaultToolTip.qml in plasma-framework
spacing: toolTipDelegate.isWin ? Kirigami.Units.smallSpacing : Kirigami.Units.gridUnit
- // This number controls the overall size of the window tooltips
Layout.maximumWidth: toolTipDelegate.tooltipInstanceMaximumWidth
- Layout.minimumWidth: toolTipDelegate.isWin ? Layout.maximumWidth : 0
+ Layout.minimumWidth: 0
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
- // match margins of DefaultToolTip.qml in plasma-framework
- Layout.margins: toolTipDelegate.isWin ? 0 : Kirigami.Units.gridUnit / 2
+ Layout.margins: toolTipDelegate.isWin ? Kirigami.Units.smallSpacing : Kirigami.Units.gridUnit / 2
+ Layout.fillWidth: true
// all textlabels
ColumnLayout {
spacing: 0
+
+ Layout.fillWidth: true
+ Layout.preferredWidth: 0
+ Layout.minimumWidth: 0
+
// app name
Kirigami.Heading {
id: appNameHeading
level: 3
maximumLineCount: 1
lineHeight: toolTipDelegate.isWin ? 1 : appNameHeading.lineHeight
+
Layout.fillWidth: true
+ Layout.minimumWidth: 0
elide: Text.ElideRight
- text: toolTipDelegate.appName
+
+ text: root.calculatedAppName
opacity: root.index === 0 ? 1 : 0
visible: text.length !== 0
textFormat: Text.PlainText
@@ -104,8 +152,11 @@ ColumnLayout {
PlasmaComponents3.Label {
id: winTitle
maximumLineCount: 1
+
Layout.fillWidth: true
+ Layout.minimumWidth: 0
elide: Text.ElideRight
+
text: root.titleIncludesTrack ? "" : root.title
opacity: 0.75
visible: root.title.length !== 0 && root.title !== appNameHeading.text
@@ -115,8 +166,11 @@ ColumnLayout {
PlasmaComponents3.Label {
id: subtext
maximumLineCount: 2
+
Layout.fillWidth: true
+ Layout.minimumWidth: 0
elide: Text.ElideRight
+
text: toolTipDelegate.isWin ? root.generateSubText() : ""
opacity: 0.6
visible: text.length !== 0 && text !== appNameHeading.text
@@ -125,7 +179,6 @@ ColumnLayout {
}
// Count badge.
- // The badge itself is inside an item to better center the text in the bubble
Item {
Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.preferredHeight: closeButton.height
@@ -146,7 +199,9 @@ ColumnLayout {
visible: toolTipDelegate.isWin
icon.name: "window-close"
onClicked: {
- backend.cancelHighlightWindows();
+ if (toolTipDelegate.parentTask && toolTipDelegate.parentTask.tasksRoot) {
+ toolTipDelegate.parentTask.tasksRoot.cancelHighlightWindows();
+ }
tasksModel.requestClose(root.submodelIndex);
}
}
@@ -156,64 +211,64 @@ ColumnLayout {
Item {
id: thumbnailSourceItem
- Layout.fillWidth: true
- Layout.preferredHeight: Kirigami.Units.gridUnit * 8
+ readonly property int targetWidth: Kirigami.Units.gridUnit * 14
+ readonly property int targetHeight: targetWidth / (Screen.width / Screen.height)
+ Layout.preferredWidth: targetWidth
+ Layout.preferredHeight: targetHeight
+
+ Layout.alignment: Qt.AlignCenter
clip: true
visible: toolTipDelegate.isWin
- readonly property /*undefined|WId where WId = int|string*/ var winId:
- toolTipDelegate.isWin ? toolTipDelegate.windows[root.index] : undefined
+ readonly property var winId: toolTipDelegate.isWin ?
+ toolTipDelegate.windows[root.index] : undefined
- // There's no PlasmaComponents3 version
PlasmaExtras.Highlight {
- anchors.fill: hoverHandler
- visible: (hoverHandler.item as MouseArea)?.containsMouse ?? false
+ anchors.fill: hoverHandler // Anchor to the interaction area
+
+ // FIX: Highlight is visible when hovering ANYWHERE on the tooltip (rootHover)
+ visible: rootHover.hovered
+
pressed: (hoverHandler.item as MouseArea)?.containsPress ?? false
hovered: true
}
Loader {
id: thumbnailLoader
- active: !toolTipDelegate.isLauncher
- && !albumArtImage.visible
- && (Number.isInteger(thumbnailSourceItem.winId) || pipeWireLoader.item && !pipeWireLoader.item.hasThumbnail)
- && root.index !== -1 // Avoid loading when the instance is going to be destroyed
+ active: !toolTipDelegate.isLauncher && !albumArtImage.visible && (Number.isInteger(thumbnailSourceItem.winId) || pipeWireLoader.item && !pipeWireLoader.item.hasThumbnail) && root.index !== -1
asynchronous: true
visible: active
+
anchors.fill: hoverHandler
- // Indent a little bit so that neither the thumbnail nor the drop
- // shadow can cover up the highlight
- anchors.margins: Kirigami.Units.smallSpacing * 2
+ anchors.margins: Kirigami.Units.smallSpacing
- sourceComponent: root.isMinimized || pipeWireLoader.active ? iconItem : x11Thumbnail
+ sourceComponent: root.isMinimized ||
+ pipeWireLoader.active ? iconItem : x11Thumbnail
Component {
id: x11Thumbnail
-
PlasmaCore.WindowThumbnail {
winId: thumbnailSourceItem.winId
}
}
- // when minimized, we don't have a preview on X11, so show the icon
Component {
id: iconItem
-
Kirigami.Icon {
id: realIconItem
source: toolTipDelegate.icon
animated: false
visible: valid
- opacity: pipeWireLoader.active ? 0 : 1
+ opacity: pipeWireLoader.active ?
+ 0 : 1
+
+ anchors.fill: parent
+ anchors.margins: Kirigami.Units.gridUnit
SequentialAnimation {
running: true
-
- PauseAnimation {
- duration: Kirigami.Units.humanMoment
- }
-
+ PauseAnimation { duration: Kirigami.Units.humanMoment }
NumberAnimation {
id: showAnimation
duration: Kirigami.Units.longDuration
@@ -223,7 +278,6 @@ ColumnLayout {
to: 1
}
}
-
}
}
}
@@ -231,8 +285,6 @@ ColumnLayout {
Loader {
id: pipeWireLoader
anchors.fill: hoverHandler
- // Indent a little bit so that neither the thumbnail nor the drop
- // shadow can cover up the highlight
anchors.margins: thumbnailLoader.anchors.margins
active: !toolTipDelegate.isLauncher && !albumArtImage.visible && KWindowSystem.isPlatformWayland && root.index !== -1
@@ -242,10 +294,12 @@ ColumnLayout {
}
Loader {
- active: (pipeWireLoader.item?.hasThumbnail ?? false) || (thumbnailLoader.status === Loader.Ready && !root.isMinimized)
+ active: (pipeWireLoader.item?.hasThumbnail ?? false) ||
+ (thumbnailLoader.status === Loader.Ready && !root.isMinimized)
asynchronous: true
visible: active
- anchors.fill: pipeWireLoader.active ? pipeWireLoader : thumbnailLoader
+ anchors.fill: pipeWireLoader.active ?
+ pipeWireLoader : thumbnailLoader
sourceComponent: GE.DropShadow {
horizontalOffset: 0
@@ -253,7 +307,8 @@ ColumnLayout {
radius: 8
samples: Math.round(radius * 1.5)
color: "Black"
- source: pipeWireLoader.active ? pipeWireLoader.item : thumbnailLoader.item // source could be undefined when albumArt is available, so put it in a Loader.
+ source: pipeWireLoader.active ?
+ pipeWireLoader.item : thumbnailLoader.item // source could be undefined when albumArt is available, so put it in a Loader.
}
}
@@ -269,6 +324,7 @@ ColumnLayout {
// Manual implementation of Image.PreserveAspectCrop
readonly property real scaleFactor: Math.max(hoverHandler.width / source.paintedWidth, hoverHandler.height / source.paintedHeight)
+
width: Math.round(source.paintedWidth * scaleFactor)
height: Math.round(source.paintedHeight * scaleFactor)
layer.enabled: true
@@ -287,8 +343,8 @@ ColumnLayout {
// if this is a group tooltip, we check if window title and track match, to allow distinguishing the different windows
// if this app is a browser, we also check the title, so album art is not shown when the user is on some other tab
// in all other cases we can safely show the album art without checking the title
- readonly property bool available: (status === Image.Ready || status === Image.Loading)
- && (!(toolTipDelegate.isGroup || backend.applicationCategories(launcherUrl).includes("WebBrowser")) || root.titleIncludesTrack)
+ readonly property bool available: (status === Image.Ready ||
+ status === Image.Loading) && (!(toolTipDelegate.isGroup || backend.applicationCategories(launcherUrl).includes("WebBrowser")) || root.titleIncludesTrack)
anchors.fill: hoverHandler
// Indent by one pixel to make sure we never cover up the entire highlight
@@ -301,7 +357,7 @@ ColumnLayout {
visible: available
}
- // hoverHandler has to be unloaded after the instance is pooled in order to avoid getting the old containsMouse status when the same instance is reused, so put it in a Loader.
+ // RESTORED: Loader for MouseArea to handle recycling correctly.
Loader {
id: hoverHandler
active: root.index !== -1
@@ -310,6 +366,9 @@ ColumnLayout {
rootTask: toolTipDelegate.parentTask
modelIndex: root.submodelIndex
winId: thumbnailSourceItem.winId
+
+ // FIX: Pass global hover state to the mouse area logic
+ globalHovered: rootHover.hovered
}
}
}
@@ -330,11 +389,7 @@ ColumnLayout {
// Volume controls
Loader {
- active: toolTipDelegate.parentTask !== null
- && pulseAudio.item !== null
- && toolTipDelegate.parentTask.audioIndicatorsEnabled
- && toolTipDelegate.parentTask.hasAudioStream
- && root.index !== -1 // Avoid loading when the instance is going to be destroyed
+ active: toolTipDelegate.parentTask !== null && pulseAudio.item !== null && toolTipDelegate.parentTask.audioIndicatorsEnabled && toolTipDelegate.parentTask.hasAudioStream && root.index !== -1 // Avoid loading when the instance is going to be destroyed
asynchronous: true
visible: active
Layout.fillWidth: true
@@ -342,25 +397,25 @@ ColumnLayout {
Layout.leftMargin: header.Layout.margins
Layout.rightMargin: header.Layout.margins
sourceComponent: RowLayout {
- PlasmaComponents3.ToolButton { // Mute button
+ PlasmaComponents3.ToolButton {
+ // Mute button
icon.width: Kirigami.Units.iconSizes.small
icon.height: Kirigami.Units.iconSizes.small
icon.name: if (checked) {
- "audio-volume-muted"
+ "audio-volume-muted";
} else if (slider.displayValue <= 25) {
- "audio-volume-low"
+ "audio-volume-low";
} else if (slider.displayValue <= 75) {
- "audio-volume-medium"
+ "audio-volume-medium";
} else {
- "audio-volume-high"
+ "audio-volume-high";
}
onClicked: toolTipDelegate.parentTask.toggleMuted()
checked: toolTipDelegate.parentTask.muted
PlasmaComponents3.ToolTip {
- text: parent.checked
- ? i18nc("button to unmute app", "Unmute %1", toolTipDelegate.parentTask.appName)
- : i18nc("button to mute app", "Mute %1", toolTipDelegate.parentTask.appName)
+ text: parent.checked ?
+ i18nc("button to unmute app", "Unmute %1", toolTipDelegate.parentTask.appName) : i18nc("button to mute app", "Mute %1", toolTipDelegate.parentTask.appName)
}
}
@@ -368,29 +423,31 @@ ColumnLayout {
id: slider
readonly property int displayValue: Math.round(value / to * 100)
- readonly property int loudestVolume: toolTipDelegate.parentTask.audioStreams
- .reduce((loudestVolume, stream) => Math.max(loudestVolume, stream.volume), 0)
+ readonly property int loudestVolume: toolTipDelegate.parentTask.audioStreams.reduce((loudestVolume, stream) => Math.max(loudestVolume, stream.volume), 0)
Layout.fillWidth: true
from: pulseAudio.item.minimalVolume
to: pulseAudio.item.normalVolume
value: loudestVolume
stepSize: to / 100
- opacity: toolTipDelegate.parentTask.muted ? 0.5 : 1
+ opacity: toolTipDelegate.parentTask.muted ?
+ 0.5 : 1
Accessible.name: i18nc("Accessibility data on volume slider", "Adjust volume for %1", toolTipDelegate.parentTask.appName)
- onMoved: toolTipDelegate.parentTask.audioStreams.forEach((stream) => {
- let v = Math.max(from, value)
- if (v > 0 && loudestVolume > 0) { // prevent divide by 0
+ onMoved: toolTipDelegate.parentTask.audioStreams.forEach(stream => {
+ let v = Math.max(from, value);
+ if (v > 0 && loudestVolume > 0) {
+ // prevent divide by 0
// adjust volume relative to the loudest stream
- v = Math.min(Math.round(stream.volume / loudestVolume * v), to)
+ v = Math.min(Math.round(stream.volume / loudestVolume * v), to);
}
- stream.model.Volume = v
- stream.model.Muted = v === 0
+ stream.model.Volume = v;
+ stream.model.Muted = v === 0;
})
}
- PlasmaComponents3.Label { // percent label
+ PlasmaComponents3.Label {
+ // percent label
Layout.alignment: Qt.AlignHCenter
Layout.minimumWidth: percentMetrics.advanceWidth
horizontalAlignment: Qt.AlignRight
@@ -406,7 +463,6 @@ ColumnLayout {
function generateSubText(): string {
const subTextEntries = [];
-
if (!Plasmoid.configuration.showOnlyCurrentDesktop && virtualDesktopInfo.numberOfDesktops > 1) {
if (!isOnAllVirtualDesktops && virtualDesktops.length > 0) {
const virtualDesktopNameList = virtualDesktops.map(virtualDesktop => {
@@ -414,30 +470,22 @@ ColumnLayout {
return virtualDesktopInfo.desktopNames[index];
});
- subTextEntries.push(i18nc("Comma-separated list of desktops", "On %1",
- virtualDesktopNameList.join(", ")));
+ subTextEntries.push(i18nc("Comma-separated list of desktops", "On %1", virtualDesktopNameList.join(", ")));
} else if (isOnAllVirtualDesktops) {
subTextEntries.push(i18nc("Comma-separated list of desktops", "Pinned to all desktops"));
}
}
if (activities.length === 0 && activityInfo.numberOfRunningActivities > 1) {
- subTextEntries.push(i18nc("Which virtual desktop a window is currently on",
- "Available on all activities"));
+ subTextEntries.push(i18nc("Which virtual desktop a window is currently on", "Available on all activities"));
} else if (activities.length > 0) {
- const activityNames = activities
- .filter(activity => activity !== activityInfo.currentActivity)
- .map(activity => activityInfo.activityName(activity))
- .filter(activityName => activityName !== "");
-
+ const activityNames = activities.filter(activity => activity !== activityInfo.currentActivity).map(activity => activityInfo.activityName(activity)).filter(activityName => activityName !== "");
if (Plasmoid.configuration.showOnlyCurrentActivity) {
if (activityNames.length > 0) {
- subTextEntries.push(i18nc("Activities a window is currently on (apart from the current one)",
- "Also available on %1", activityNames.join(", ")));
+ subTextEntries.push(i18nc("Activities a window is currently on (apart from the current one)", "Also available on %1", activityNames.join(", ")));
}
} else if (activityNames.length > 0) {
- subTextEntries.push(i18nc("Which activities a window is currently on",
- "Available on %1", activityNames.join(", ")));
+ subTextEntries.push(i18nc("Which activities a window is currently on", "Available on %1", activityNames.join(", ")));
}
}
diff --git a/package/contents/ui/ToolTipWindowMouseArea.qml b/package/contents/ui/ToolTipWindowMouseArea.qml
index d226a7bd..8f461965 100644
--- a/package/contents/ui/ToolTipWindowMouseArea.qml
+++ b/package/contents/ui/ToolTipWindowMouseArea.qml
@@ -11,9 +11,11 @@ pragma ComponentBehavior: Bound
import QtQuick
MouseArea {
- required property /*QModelIndex*/var modelIndex
- required property /*undefined|WId where WId = int|string*/ var winId
- required property Task rootTask
+ required property var modelIndex
+ required property var winId
+ required property var rootTask
+
+ property bool globalHovered: false
acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton
hoverEnabled: true
@@ -23,20 +25,33 @@ MouseArea {
switch (mouse.button) {
case Qt.LeftButton:
tasksModel.requestActivate(modelIndex);
- rootTask.hideImmediately();
- backend.cancelHighlightWindows();
+ rootTask.closeTooltip();
+ if (rootTask.tasksRoot) {
+ rootTask.tasksRoot.cancelHighlightWindows();
+ }
break;
case Qt.MiddleButton:
- backend.cancelHighlightWindows();
+ if (rootTask.tasksRoot) {
+ rootTask.tasksRoot.cancelHighlightWindows();
+ }
tasksModel.requestClose(modelIndex);
break;
case Qt.RightButton:
- tasks.createContextMenu(rootTask, modelIndex).show();
+ if (rootTask.tasksRoot) {
+ rootTask.tasksRoot.createContextMenu(rootTask, modelIndex).show();
+ }
break;
}
}
- onContainsMouseChanged: {
- tasks.windowsHovered([winId], containsMouse);
+ function updateHoverState() {
+ if (rootTask.tasksRoot) {
+ // Окно подсвечивается/скрывает другие, если мышь внутри этого MouseArea ИЛИ где-то еще в тултипе
+ const isHovered = containsMouse || globalHovered;
+ rootTask.tasksRoot.windowsHovered([winId], isHovered);
+ }
}
+
+ onContainsMouseChanged: updateHoverState()
+ onGlobalHoveredChanged: updateHoverState()
}
diff --git a/package/contents/ui/code/layout.js b/package/contents/ui/code/layout.js
new file mode 100644
index 00000000..b51778b0
--- /dev/null
+++ b/package/contents/ui/code/layout.js
@@ -0,0 +1,236 @@
+/*
+ SPDX-FileCopyrightText: 2012-2013 Eike Hein
+
+ SPDX-License-Identifier: GPL-2.0-or-later
+*/
+
+.import org.kde.plasma.core as PlasmaCore
+.import org.kde.kirigami as Kirigami
+
+const iconMargin = Math.round(Kirigami.Units.smallSpacing / 4);
+const labelMargin = Kirigami.Units.smallSpacing;
+
+function horizontalMargins() {
+ const spacingAdjustment = (plasmoid.configuration.iconOnly) ? (Kirigami.Settings.tabletMode ? 3 : plasmoid.configuration.iconSpacing) : 1
+ return (taskFrame.margins.left + taskFrame.margins.right) * spacingAdjustment;
+}
+
+function verticalMargins() {
+ const spacingAdjustment = (plasmoid.configuration.iconOnly) ? (Kirigami.Settings.tabletMode ? 3 : plasmoid.configuration.iconSpacing) : 1
+ return (taskFrame.margins.top + taskFrame.margins.bottom) * spacingAdjustment;
+}
+
+function adjustMargin(height, margin) {
+ var available = height - verticalMargins();
+
+ if (available < Kirigami.Units.iconSizes.small) {
+ return Math.floor((margin * (Kirigami.Units.iconSizes.small / available)) / 3);
+ }
+
+ return margin;
+}
+
+function launcherLayoutTasks() {
+ return Math.round(tasksModel.logicalLauncherCount / Math.floor(preferredMinWidth() / launcherWidth()));
+}
+
+function launcherLayoutWidthDiff() {
+ return (launcherLayoutTasks() * taskWidth()) - (tasksModel.logicalLauncherCount * launcherWidth());
+}
+
+function logicalTaskCount() {
+ var count = (tasksModel.count - tasksModel.logicalLauncherCount) + launcherLayoutTasks();
+
+ return Math.max(tasksModel.count ? 1 : 0, count);
+}
+
+function maxStripes() {
+ var length = tasks.vertical ? taskList.width : taskList.height;
+ var minimum = tasks.vertical ? preferredMinWidth() : preferredMinHeight();
+
+ return Math.min(plasmoid.configuration.maxStripes, Math.max(1, Math.floor(length / minimum)));
+}
+
+function tasksPerStripe() {
+ if (plasmoid.configuration.forceStripes) {
+ return Math.ceil(logicalTaskCount() / maxStripes());
+ } else {
+ var length = tasks.vertical ? taskList.height : taskList.width;
+ var minimum = tasks.vertical ? preferredMinHeight() : preferredMinWidth();
+
+ return Math.floor(length / minimum);
+ }
+}
+
+function calculateStripes() {
+ var stripes = plasmoid.configuration.forceStripes ? plasmoid.configuration.maxStripes : Math.min(plasmoid.configuration.maxStripes, Math.ceil(logicalTaskCount() / tasksPerStripe()));
+
+ return Math.min(stripes, maxStripes());
+}
+
+function full() {
+ return (maxStripes() == calculateStripes());
+}
+
+function optimumCapacity(width, height) {
+ var length = tasks.vertical ? height : width;
+ var maximum = tasks.vertical ? preferredMaxHeight() : preferredMaxWidth();
+
+ if (!tasks.vertical) {
+ // Fit more tasks in this case, that is possible to cut text, before combining tasks.
+ return Math.ceil(length / maximum) * maxStripes() + 1;
+ }
+
+ return Math.floor(length / maximum) * maxStripes();
+}
+
+function layoutWidth() {
+ if (plasmoid.configuration.forceStripes && !tasks.vertical) {
+ return Math.min(tasks.width - (tasksPerStripe() * plasmoid.configuration.taskSpacingSize), Math.max(preferredMaxWidth(), tasksPerStripe() * preferredMaxWidth()));
+ } else {
+ return tasks.width;
+ }
+}
+
+function layoutHeight() {
+ if (plasmoid.configuration.forceStripes && tasks.vertical) {
+ return Math.min(tasks.height - (tasksPerStripe() * plasmoid.configuration.taskSpacingSize), Math.max(preferredMaxHeight(), tasksPerStripe() * preferredMaxHeight()));
+ } else {
+ return tasks.height;
+ }
+}
+
+function preferredMinWidth() {
+ var width = launcherWidth();
+
+ if (!tasks.vertical && !tasks.iconsOnly) {
+ width +=
+ (Kirigami.Units.smallSpacing * 2) +
+ (plasmoid.configuration.maxButtonLength /*PlasmaCore.Theme.mSize(PlasmaCore.Theme.defaultFont).width * 12*/);
+ }
+
+ return width;
+}
+
+function preferredMaxWidth() {
+ if (tasks.iconsOnly) {
+ if (tasks.vertical) {
+ return tasks.width + verticalMargins();
+ } else {
+ return tasks.height + horizontalMargins();
+ }
+ }
+
+ if (plasmoid.configuration.groupingStrategy != 0 && !plasmoid.configuration.groupPopups) {
+ return preferredMinWidth();
+ }
+
+ return Math.floor(preferredMinWidth());
+}
+
+function preferredMinHeight() {
+ // TODO FIXME UPSTREAM: Port to proper font metrics for descenders once we have access to them.
+ return PlasmaCore.Theme.mSize(PlasmaCore.Theme.defaultFont).height + 4;
+}
+
+function preferredMaxHeight() {
+ if (tasks.vertical) {
+ return verticalMargins() +
+ Math.min(
+ // Do not allow the preferred icon size to exceed the width of
+ // the vertical task manager.
+ tasks.width,
+ tasks.iconsOnly ? tasks.width :
+ Math.max(
+ PlasmaCore.Theme.mSize(PlasmaCore.Theme.defaultFont).height,
+ Kirigami.Units.iconSizes.medium
+ )
+ );
+ } else {
+ return verticalMargins() +
+ Math.min(
+ Kirigami.Units.iconSizes.small * 3,
+ PlasmaCore.Theme.mSize(PlasmaCore.Theme.defaultFont).height *
+ 3);
+ }
+}
+
+// Returns the number of 'm' characters whose joint width must be available in the task button label
+// so that the button text is rendered at all.
+function minimumMColumns() {
+ return tasks.vertical ? 4 : 5;
+}
+
+function taskWidth() {
+ if (tasks.vertical) {
+ return Math.floor(taskList.width / calculateStripes());
+ } else {
+ if (full() && Math.max(1, logicalTaskCount()) > tasksPerStripe()) {
+ return Math.floor(taskList.width / Math.ceil(logicalTaskCount() / maxStripes()));
+ } else {
+ return Math.min(preferredMaxWidth(), Math.floor(taskList.width / Math.min(logicalTaskCount(), tasksPerStripe())));
+ }
+ }
+}
+
+function taskHeight() {
+ if (tasks.vertical) {
+ if (full() && Math.max(1, logicalTaskCount()) > tasksPerStripe()) {
+ return Math.floor(taskList.height / Math.ceil(logicalTaskCount() / maxStripes()));
+ } else {
+ return Math.min(preferredMaxHeight(), Math.floor(taskList.height / Math.min(logicalTaskCount(), tasksPerStripe())));
+ }
+ } else {
+ return Math.floor(taskList.height / calculateStripes());
+ }
+}
+
+function launcherWidth() {
+ var baseWidth = tasks.vertical ? preferredMinHeight() : Math.min(tasks.height, Kirigami.Units.iconSizes.small * 3);
+
+ return (baseWidth + horizontalMargins())
+ - (adjustMargin(baseWidth, taskFrame.margins.top) + adjustMargin(baseWidth, taskFrame.margins.bottom));
+}
+
+function maximumContextMenuTextWidth() {
+ return (PlasmaCore.Theme.mSize(PlasmaCore.Theme.defaultFont).width * 28);
+}
+
+function layout(container) {
+ var item;
+ var stripes = calculateStripes();
+ var taskCount = tasksModel.count - tasksModel.logicalLauncherCount;
+ var width = taskWidth();
+ var adjustedWidth = width;
+ var height = taskHeight();
+
+ if (!tasks.vertical && stripes == 1 && taskCount) {
+ var shrink = ((tasksModel.count - tasksModel.logicalLauncherCount) * preferredMaxWidth())
+ + (tasksModel.logicalLauncherCount * launcherWidth()) > taskList.width;
+ width = Math.min(shrink ? width + Math.floor(launcherLayoutWidthDiff() / taskCount) : width,
+ preferredMaxWidth());
+ }
+
+ for (var i = 0; i < container.count; ++i) {
+ item = container.itemAt(i);
+
+ if (!item) {
+ continue;
+ }
+
+ adjustedWidth = width;
+
+ if (!tasks.vertical && !tasks.iconsOnly && (plasmoid.configuration.separateLaunchers || stripes == 1)) {
+ if (item.m.IsLauncher === true
+ || (!plasmoid.configuration.separateLaunchers && item.m.IsStartup === true && item.m.HasLauncher === true)) {
+ adjustedWidth = launcherWidth();
+ } else if (stripes > 1 && i == tasksModel.logicalLauncherCount) {
+ adjustedWidth += launcherLayoutWidthDiff();
+ }
+ }
+
+ item.width = adjustedWidth;
+ item.height = height;
+ item.visible = true;
+ }
+}
diff --git a/package/contents/ui/code/tools.js b/package/contents/ui/code/tools.js
index f8cb46e5..c57ef709 100644
--- a/package/contents/ui/code/tools.js
+++ b/package/contents/ui/code/tools.js
@@ -6,7 +6,6 @@
*/
.pragma library
-
.import org.kde.taskmanager as TaskManager
.import org.kde.plasma.core as PlasmaCore // Needed by TaskManager
@@ -55,8 +54,7 @@ function activateNextPrevTask(anchor, next, wheelSkipMinimized, tasks) {
let target = taskIndexList[0];
for (let i = 0; i < taskIndexList.length; ++i) {
- if (taskIndexList[i] === activeTaskIndex)
- {
+ if (taskIndexList[i] === activeTaskIndex) {
if (next && i < (taskIndexList.length - 1)) {
target = taskIndexList[i + 1];
} else if (!next) {
@@ -119,15 +117,15 @@ function activateTask(index, model, modifiers, task, plasmoid, tasks, windowView
// every click.
for (let j = 0; j < childTaskList.length; ++j) {
const childTask = childTaskList[j];
- if (tasks.tasksModel.data(childTask, TaskManager.AbstractTasksModel.IsActive)) {
- // Found the current task. Activate the next one
- let nextTask = j + 1;
- if (nextTask >= childTaskList.length) {
- nextTask = 0;
- }
- tasks.tasksModel.requestActivate(childTaskList[nextTask]);
- break;
+ if (tasks.tasksModel.data(childTask, TaskManager.AbstractTasksModel.IsActive)) {
+ // Found the current task. Activate the next one
+ let nextTask = j + 1;
+ if (nextTask >= childTaskList.length) {
+ nextTask = 0;
}
+ tasks.tasksModel.requestActivate(childTaskList[nextTask]);
+ break;
+ }
}
}
}
@@ -181,17 +179,17 @@ function taskPrefix(prefix, location) {
let effectivePrefix;
switch (location) {
- case PlasmaCore.Types.LeftEdge:
- effectivePrefix = "west-" + prefix;
- break;
- case PlasmaCore.Types.TopEdge:
- effectivePrefix = "north-" + prefix;
- break;
- case PlasmaCore.Types.RightEdge:
- effectivePrefix = "east-" + prefix;
- break;
- default:
- effectivePrefix = "south-" + prefix;
+ case PlasmaCore.Types.LeftEdge:
+ effectivePrefix = "west-" + prefix;
+ break;
+ case PlasmaCore.Types.TopEdge:
+ effectivePrefix = "north-" + prefix;
+ break;
+ case PlasmaCore.Types.RightEdge:
+ effectivePrefix = "east-" + prefix;
+ break;
+ default:
+ effectivePrefix = "south-" + prefix;
}
return [effectivePrefix, prefix];
}
diff --git a/package/contents/ui/main.qml b/package/contents/ui/main.qml
index bd1e1a79..ed6370e1 100644
--- a/package/contents/ui/main.qml
+++ b/package/contents/ui/main.qml
@@ -6,6 +6,7 @@
import QtQuick
import QtQuick.Layouts
+import QtQuick.Controls
import org.kde.plasma.plasmoid
import org.kde.plasma.components as PlasmaComponents3
@@ -33,7 +34,7 @@ PlasmoidItem {
readonly property bool shouldShrinkToZero: tasksModel.count === 0
readonly property bool vertical: Plasmoid.formFactor === PlasmaCore.Types.Vertical
- readonly property bool iconsOnly: Plasmoid.pluginName === "org.kde.plasma.icontasks"
+ readonly property bool iconsOnly: plasmoid.configuration.iconOnly
property Task toolTipOpenedByClick
property Task toolTipAreaItem
@@ -42,7 +43,7 @@ PlasmoidItem {
readonly property Component pulseAudioComponent: Qt.createComponent("PulseAudio.qml")
property bool needLayoutRefresh: false
- property /*list where WId = int|string*/ var taskClosedWithMouseMiddleButton: []
+ property /*list where WId = int|string*/ var taskClosedWithMouseMiddleButton: []
property alias taskList: taskList
preferredRepresentation: fullRepresentation
@@ -56,21 +57,26 @@ PlasmoidItem {
}
Layout.fillWidth: vertical ? true : Plasmoid.configuration.fill
- Layout.fillHeight: !vertical ? true : Plasmoid.configuration.fill
+ Layout.fillHeight: !vertical ?
+ true : Plasmoid.configuration.fill
Layout.minimumWidth: {
if (shouldShrinkToZero) {
- return Kirigami.Units.gridUnit; // For edit mode
+ return Kirigami.Units.gridUnit;
+ // For edit mode
}
- return vertical ? 0 : LayoutMetrics.preferredMinWidth();
+ return vertical ?
+ 0 : LayoutMetrics.preferredMinWidth();
}
Layout.minimumHeight: {
if (shouldShrinkToZero) {
- return Kirigami.Units.gridUnit; // For edit mode
+ return Kirigami.Units.gridUnit;
+ // For edit mode
}
- return !vertical ? 0 : LayoutMetrics.preferredMinHeight();
+ return !vertical ?
+ 0 : LayoutMetrics.preferredMinHeight();
}
-//BEGIN TODO: this is not precise enough: launchers are smaller than full tasks
+ //BEGIN TODO: this is not precise enough: launchers are smaller than full tasks
Layout.preferredWidth: {
if (shouldShrinkToZero) {
return 0.01;
@@ -78,18 +84,18 @@ PlasmoidItem {
if (vertical) {
return Kirigami.Units.gridUnit * 10;
}
- return taskList.Layout.maximumWidth
+ return taskList.Layout.maximumWidth;
}
Layout.preferredHeight: {
if (shouldShrinkToZero) {
return 0.01;
}
if (vertical) {
- return taskList.Layout.maximumHeight
+ return taskList.Layout.maximumHeight;
}
return Kirigami.Units.gridUnit * 2;
}
-//END TODO
+ //END TODO
property Item dragSource
@@ -97,6 +103,24 @@ PlasmoidItem {
signal windowsHovered(var winIds, bool hovered)
signal activateWindowView(var winIds)
+ onWindowsHovered: (winIds, hovered) => {
+ if (!Plasmoid.configuration.highlightWindows) {
+ return;
+ }
+ DBus.SessionBus.asyncCall({
+ service: "org.kde.KWin.HighlightWindow",
+ path: "/org/kde/KWin/HighlightWindow",
+ iface: "org.kde.KWin.HighlightWindow",
+ member: "highlightWindows",
+ arguments: [hovered ? winIds : []],
+ signature: "(as)"
+ });
+ }
+
+ function cancelHighlightWindows(): DBus.DBusPendingReply {
+ return DBus.SessionBus.asyncCall({service: "org.kde.KWin.HighlightWindow", path: "/org/kde/KWin/HighlightWindow", iface: "org.kde.KWin.HighlightWindow", member: "highlightWindows", arguments: [[]], signature: "(as)"});
+ }
+
onDragSourceChanged: {
if (dragSource === null) {
tasksModel.syncLaunchers();
@@ -109,10 +133,8 @@ PlasmoidItem {
}
for (let i = 0; i < taskItems.length - 1; ++i) {
const task = taskItems[i];
-
if (!task.model.IsLauncher && !task.model.IsStartup) {
- tasksModel.requestPublishDelegateGeometry(tasksModel.makeModelIndex(task.index),
- backend.globalRect(task), task);
+ tasksModel.requestPublishDelegateGeometry(tasksModel.makeModelIndex(task.index), backend.globalRect(task), task);
}
}
}
@@ -126,10 +148,8 @@ PlasmoidItem {
}
let startupsWithLaunchers = 0;
-
for (let i = 0; i < taskRepeater.count; ++i) {
const item = taskRepeater.itemAt(i);
-
// During destruction required properties such as item.model can go null for a while,
// so in paths that can trigger on those moments, they need to be guarded
if (item?.model?.IsStartup && item.model.HasLauncher) {
@@ -149,12 +169,12 @@ PlasmoidItem {
filterByActivity: Plasmoid.configuration.showOnlyCurrentActivity
filterNotMinimized: Plasmoid.configuration.showOnlyMinimized
- hideActivatedLaunchers: tasks.iconsOnly || Plasmoid.configuration.hideLauncherOnStart
+ hideActivatedLaunchers: tasks.iconsOnly ||
+ Plasmoid.configuration.hideLauncherOnStart
sortMode: sortModeEnumValue(Plasmoid.configuration.sortingStrategy)
launchInPlace: tasks.iconsOnly && Plasmoid.configuration.sortingStrategy === 1
separateLaunchers: {
- if (!tasks.iconsOnly && !Plasmoid.configuration.separateLaunchers
- && Plasmoid.configuration.sortingStrategy === 1) {
+ if (!tasks.iconsOnly && !Plasmoid.configuration.separateLaunchers && Plasmoid.configuration.sortingStrategy === 1) {
return false;
}
@@ -163,8 +183,7 @@ PlasmoidItem {
groupMode: groupModeEnumValue(Plasmoid.configuration.groupingStrategy)
groupInline: !Plasmoid.configuration.groupPopups && !tasks.iconsOnly
- groupingWindowTasksThreshold: (Plasmoid.configuration.onlyGroupWhenFull && !tasks.iconsOnly
- ? LayoutMetrics.optimumCapacity(width, height) + 1 : -1)
+ groupingWindowTasksThreshold: (Plasmoid.configuration.onlyGroupWhenFull && !tasks.iconsOnly ? LayoutMetrics.optimumCapacity(width, height) + 1 : -1)
onLauncherListChanged: {
Plasmoid.configuration.launchers = launcherList;
@@ -216,7 +235,6 @@ PlasmoidItem {
readonly property TaskManagerApplet.Backend backend: TaskManagerApplet.Backend {
id: backend
- highlightWindows: Plasmoid.configuration.highlightWindows
onAddLauncher: {
tasks.addLauncher(url);
@@ -279,6 +297,7 @@ PlasmoidItem {
TaskManager.ActivityInfo {
id: activityInfo
+
readonly property string nullUuid: "00000000-0000-0000-0000-000000000000"
}
@@ -302,8 +321,7 @@ PlasmoidItem {
Binding {
target: Plasmoid
property: "status"
- value: (tasksModel.anyTaskDemandsAttention && Plasmoid.configuration.unhideOnAttention
- ? PlasmaCore.Types.NeedsAttentionStatus : PlasmaCore.Types.PassiveStatus)
+ value: (tasksModel.anyTaskDemandsAttention && Plasmoid.configuration.unhideOnAttention ? PlasmaCore.Types.NeedsAttentionStatus : PlasmaCore.Types.PassiveStatus)
restoreMode: Binding.RestoreBinding
}
@@ -311,7 +329,7 @@ PlasmoidItem {
target: Plasmoid.configuration
function onLaunchersChanged(): void {
- tasksModel.launcherList = Plasmoid.configuration.launchers
+ tasksModel.launcherList = Plasmoid.configuration.launchers;
}
function onGroupingAppIdBlacklistChanged(): void {
tasksModel.groupingAppIdBlacklist = Plasmoid.configuration.groupingAppIdBlacklist;
@@ -331,7 +349,8 @@ PlasmoidItem {
id: dragHelper
Drag.dragType: Drag.Automatic
- Drag.supportedActions: Qt.CopyAction | Qt.MoveAction | Qt.LinkAction
+ Drag.supportedActions: Qt.CopyAction | Qt.MoveAction |
+ Qt.LinkAction
Drag.onDragFinished: dropAction => {
tasks.dragSource = null;
}
@@ -426,20 +445,20 @@ PlasmoidItem {
Layout.maximumWidth: {
const totalMaxWidth = children.reduce((accumulator, child) => {
- if (!isFinite(child.Layout.maximumWidth)) {
- return accumulator;
- }
- return accumulator + child.Layout.maximumWidth
- }, 0);
+ if (!isFinite(child.Layout.maximumWidth)) {
+ return accumulator;
+ }
+ return accumulator + child.Layout.maximumWidth;
+ }, 0);
return Math.round(totalMaxWidth / widthOccupation);
}
Layout.maximumHeight: {
const totalMaxHeight = children.reduce((accumulator, child) => {
- if (!isFinite(child.Layout.maximumHeight)) {
- return accumulator;
- }
- return accumulator + child.Layout.maximumHeight
- }, 0);
+ if (!isFinite(child.Layout.maximumHeight)) {
+ return accumulator;
+ }
+ return accumulator + child.Layout.maximumHeight;
+ }, 0);
return Math.round(totalMaxHeight / heightOccupation);
}
width: {
@@ -465,9 +484,11 @@ PlasmoidItem {
flow: {
if (tasks.vertical) {
- return Plasmoid.configuration.forceStripes ? Grid.LeftToRight : Grid.TopToBottom
+ return Plasmoid.configuration.forceStripes ?
+ Grid.LeftToRight : Grid.TopToBottom;
}
- return Plasmoid.configuration.forceStripes ? Grid.TopToBottom : Grid.LeftToRight
+ return Plasmoid.configuration.forceStripes ?
+ Grid.TopToBottom : Grid.LeftToRight;
}
onAnimatingChanged: {
@@ -483,9 +504,7 @@ PlasmoidItem {
tasksRoot: tasks
}
onItemRemoved: (index, item) => {
- if (tasks.containsMouse && index !== taskRepeater.count &&
- item.model.WinIdList.length > 0 &&
- taskClosedWithMouseMiddleButton.includes(item.winIdList[0])) {
+ if (tasks.containsMouse && index !== taskRepeater.count && item.model.WinIdList.length > 0 && taskClosedWithMouseMiddleButton.includes(item.winIdList[0])) {
needLayoutRefresh = true;
}
taskClosedWithMouseMiddleButton = [];
@@ -534,7 +553,7 @@ PlasmoidItem {
visualParent: rootTask,
modelIndex,
mpris2Source,
- backend,
+ backend
});
return contextMenuComponent.createObject(rootTask, initialArgs);
}
@@ -554,8 +573,6 @@ PlasmoidItem {
Component.onCompleted: {
TaskTools.taskManagerInstanceCount += 1;
requestLayout.connect(iconGeometryTimer.restart);
- windowsHovered.connect(backend.windowsHovered);
- activateWindowView.connect(backend.activateWindowView);
}
Component.onDestruction: {
diff --git a/package/metadata.json b/package/metadata.json
index 45d171a3..26071bf1 100644
--- a/package/metadata.json
+++ b/package/metadata.json
@@ -3,147 +3,35 @@
"KPlugin": {
"Authors": [
{
- "Email": "hein@kde.org",
- "Name": "Eike Hein",
- "Name[ar]": "إيكي هين",
- "Name[az]": "Eike Hein",
- "Name[be]": "Eike Hein",
- "Name[bg]": "Eike Hein",
- "Name[ca@valencia]": "Eike Hein",
- "Name[ca]": "Eike Hein",
- "Name[cs]": "Eike Hein",
- "Name[da]": "Eike Hein",
- "Name[de]": "Eike Hein",
- "Name[el]": "Eike Hein",
- "Name[en_GB]": "Eike Hein",
- "Name[eo]": "Eike Hein",
- "Name[es]": "Eike Hein",
- "Name[et]": "Eike Hein",
- "Name[eu]": "Eike Hein",
- "Name[fi]": "Eike Hein",
- "Name[fr]": "Eike Hein",
- "Name[gl]": "Eike Hein",
- "Name[he]": "אייק היין",
- "Name[hu]": "Eike Hein",
- "Name[ia]": "Eike Hein",
- "Name[id]": "Eike Hein",
- "Name[ie]": "Eike Hein",
- "Name[is]": "Eike Hein",
- "Name[it]": "Eike Hein",
- "Name[ja]": "Eike Hein",
- "Name[ka]": "აიკე ჰაინი",
- "Name[ko]": "Eike Hein",
- "Name[lt]": "Eike Hein",
- "Name[lv]": "Eike Hein",
- "Name[nl]": "Eike Hein",
- "Name[nn]": "Eike Hein",
- "Name[pa]": "ਇਕੀ ਹੀਇਨ",
- "Name[pl]": "Eike Hein",
- "Name[pt]": "Eike Hein",
- "Name[pt_BR]": "Eike Hein",
- "Name[ro]": "Eike Hein",
- "Name[ru]": "Eike Hein",
- "Name[sk]": "Eike Hein",
- "Name[sl]": "Eike Hein",
- "Name[sv]": "Eike Hein",
- "Name[ta]": "எய்கே ஹைன்",
- "Name[tr]": "Eike Hein",
- "Name[uk]": "Eike Hein",
- "Name[vi]": "Eike Hein",
- "Name[x-test]": "xxEike Heinxx",
- "Name[zh_CN]": "Eike Hein",
- "Name[zh_TW]": "Eike Hein"
+ "Email": "Hein@kde.org",
+ "Name": "Eike Hein, Alexandra Stone"
+ },
+ {
+ "Email": "ideas@alexankitty.com, ",
+ "Name": "Alexandra Stone"
+ },
+ {
+ "Email": "luisbocanegra17b@gmail.com",
+ "Name": "Luis Bocanegra"
}
],
- "BugReportUrl": "https://bugs.kde.org/enter_bug.cgi?product=plasmashell&component=Task%20Manager%20and%20Icons-Only%20Task%20Manager",
"Category": "Windows and Tasks",
- "Description": "Window bar displaying icons and text",
- "Description[ar]": "شريط نافذة يعرض أيقونات ونص",
- "Description[az]": "Nişanları və mətni göstərən pəncərə zolağı",
- "Description[be]": "На панэлі акна паказваюцца значкі і тэкст",
- "Description[bg]": "Лента на задачите, показваща икони и текст",
- "Description[ca@valencia]": "Barra de finestres que mostra les icones i text",
- "Description[ca]": "Barra de finestres que mostra les icones i text",
- "Description[da]": "Vinduelinje, som viser ikoner og tekst",
- "Description[el]": "Γραμμή παραθύρου που εμφανίζει εικονίδια και κείμενο",
- "Description[en_GB]": "Window bar displaying icons and text",
- "Description[eo]": "Fenestrobreto montranta piktogramojn kaj tekston",
- "Description[es]": "Barra de ventanas que muestra iconos y texto",
- "Description[eu]": "Leiho-barra, ikonoak eta testua azaltzen dituela",
- "Description[fr]": "Barre de fenêtres, avec des icônes et du texte",
- "Description[gl]": "Barra de xanelas que amosa iconas e texto.",
- "Description[he]": "סרגל חלונות המציג סמלים וטקסט",
- "Description[hu]": "Ikonokat és szöveget megjelenítő ablaksáv",
- "Description[ia]": "Barra de finestra monstrante icones e texto",
- "Description[id]": "Bilah Jendela menampilkan ikon dan teks",
- "Description[is]": "Gluggastika sem birtir tákn og texta",
- "Description[it]": "La barra della finestra che visualizza icone e testo",
- "Description[ka]": "ფანჯრების პანელი, ხატულებსა და ტექსტის ჩვენებით",
- "Description[lt]": "Langų juosta atvaizduojanti piktogramas ir tekstą",
- "Description[lv]": "Loga josla ar ikonām un tekstu",
- "Description[nl]": "Vensterbalk met pictogrammen en tekst",
- "Description[nn]": "Vindaugslinje med ikon og tekst",
- "Description[pl]": "Pasek okna wyświetlający ikony i tekst",
- "Description[pt_BR]": "Seletor de janelas estilo barra de tarefas mostrando ícones e texto",
- "Description[ro]": "Bară cu ferestre afișând pictograme și text",
- "Description[ru]": "Панель переключения окон, использующая значки и текст",
- "Description[sk]": "Prepínač okien zobrazujúci ikony a text",
- "Description[sl]": "Vrstica oken, ki prikazuje ikone in besedilo",
- "Description[sv]": "Fönsterrad som visar ikoner och text",
- "Description[ta]": "சின்னங்களையும் பெயர்களையும் காட்டும் பணிப்பட்டை",
- "Description[tr]": "Simge ve metin görüntüleyen pencere çubuğu",
- "Description[uk]": "Панель вікон із показаними піктограмами і текстом",
- "Description[x-test]": "xxWindow bar displaying icons and textxx",
- "Description[zh_CN]": "窗口栏显示图标和文本",
- "Description[zh_TW]": "顯示圖示與文字的視窗列",
+ "Description": "More modern taskbar-style window switcher displaying icons and text, with some improvments and customization options",
+ "Description[ru]": "Современный переключатель окон в стиле панели задач с иконками, текстом и расширенными настройками",
"EnabledByDefault": true,
- "Icon": "preferences-system-windows",
- "Id": "org.kde.plasma.taskmanager",
+ "Icon": "FancyTasks.png",
+ "Id": "alexankitty.fancytasks",
"License": "GPL-2.0+",
- "Name": "Icons-and-Text Task Manager",
- "Name[ar]": "مدير مهامّ بأيقونات ونص",
- "Name[az]": "Tapşırıq menecerinin nişanı və mətni",
- "Name[be]": "Выгляд кіраўніка задач",
- "Name[bg]": "Мениджър на задачи (икони с текст)",
- "Name[ca@valencia]": "Gestor de tasques amb icones i text",
- "Name[ca]": "Gestor de tasques amb icones i text",
- "Name[da]": "Opgavelinje kun med ikoner og tekst",
- "Name[el]": "Διαχείριση εργασιών με εικονίδια και κείμενο",
- "Name[en_GB]": "Icons-and-Text Task Manager",
- "Name[eo]": "Piktogram-kaj-Teksta Taskadministrilo",
- "Name[es]": "Gestor de tareas con iconos y texto",
- "Name[eu]": "Ikonoak-eta-Testua dituen ataza-kudeatzailea",
- "Name[fr]": "Gestionnaire de tâches avec icônes et du texte",
- "Name[gl]": "Xestor de tarefas de só iconas e texto",
- "Name[he]": "מנהל משימות של סמלים וטקסט בלבד",
- "Name[hu]": "Ikonos és szöveges feladatkezelő",
- "Name[ia]": "Gerente de carga de icone-e-texto",
- "Name[id]": "Pengelola Tugas Ikon dan Teks",
- "Name[is]": "Verkefnastjóri með táknum og texta",
- "Name[it]": "Gestore dei processi solo icone e testo",
- "Name[ka]": "ხატულებიანი-და-ტექსტიანი ამოცანების მმართველი",
- "Name[lt]": "Piktogramų ir teksto užduočių tvarkytuvė",
- "Name[lv]": "Ikonu un teksta uzdevumu pārvaldnieks",
- "Name[nl]": "Takenbeheer met pictogrammen-en-tekst",
- "Name[nn]": "Oppgåvehandsamar med ikon og tekst",
- "Name[pa]": "ਆਈਕਾਨ-ਤੇ-ਲਿਖਤ ਟਾਸਕ ਮੈਨੇਜਰ",
- "Name[pl]": "Ikonowo-tekstowy przełącznik zadań",
- "Name[pt_BR]": "Gerenciador de tarefas com ícones e texto",
- "Name[ro]": "Gestionar de sarcini cu pictograme și text",
- "Name[ru]": "Панель задач (значки и текст)",
- "Name[sk]": "Správca úloh s ikonami a textom",
- "Name[sl]": "Upravljalnik opravil z ikonami in besedili",
- "Name[sv]": "Aktivitetshanterare med ikoner och text",
- "Name[ta]": "சின்னங்கள்-மற்றும்-உரை பணி மேலாளி",
- "Name[tr]": "Simgeli ve Etiketli Görev Yöneticisi",
- "Name[uk]": "Керування задачами з піктограмами і текстом",
- "Name[x-test]": "xxIcons-and-Text Task Managerxx",
- "Name[zh_CN]": "图标和文本任务管理器",
- "Name[zh_TW]": "僅圖示與文字的工作管理員",
- "Website": "https://userbase.kde.org/Plasma/Tasks"
+ "Name": "Fancy Tasks",
+ "Name[ru]": "Fancy Tasks",
+ "ServiceTypes": [
+ "Plasma/Applet"
+ ],
+ "Version": "2.0.1",
+ "Website": "https://github.com/alexankitty/FancyTasks"
},
- "X-Plasma-API-Minimum-Version": "6.0",
"X-Plasma-Provides": [
"org.kde.plasma.multitasking"
- ]
+ ],
+ "X-Plasma-API-Minimum-Version": "6.0"
}
diff --git a/package/translate/ReadMe.md b/package/translate/ReadMe.md
new file mode 100644
index 00000000..d8a22b8e
--- /dev/null
+++ b/package/translate/ReadMe.md
@@ -0,0 +1,42 @@
+> Version 7 of Zren's i18n scripts.
+
+With KDE Frameworks v5.37 and above, translations are bundled with the `*.plasmoid` file downloaded from the store.
+
+## Install Translations
+
+Go to `~/.local/share/plasma/plasmoids/Fancy Tasks/translate/` and run `sh ./build --restartplasma`.
+
+## New Translations
+
+1. Fill out [`template.pot`](template.pot) with your translations then open a [new issue](https://github.com/alexankitty/FancyTasks/issues/new), name the file `spanish.txt`, attach the txt file to the issue (drag and drop).
+
+Or if you know how to make a pull request
+
+1. Copy the `template.pot` file and name it your locale's code (Eg: `en`/`de`/`fr`) with the extension `.po`. Then fill out all the `msgstr ""`.
+
+## Scripts
+
+* `sh ./merge` will parse the `i18n()` calls in the `*.qml` files and write it to the `template.pot` file. Then it will merge any changes into the `*.po` language files.
+* `sh ./build` will convert the `*.po` files to it's binary `*.mo` version and move it to `contents/locale/...` which will bundle the translations in the `*.plasmoid` without needing the user to manually install them.
+* `sh ./plasmoidlocaletest` will run `./build` then `plasmoidviewer` (part of `plasma-sdk`).
+
+## Links
+
+* https://zren.github.io/kde/docs/widget/#translations-i18n
+* https://techbase.kde.org/Development/Tutorials/Localization/i18n_Build_Systems
+* https://api.kde.org/frameworks/ki18n/html/prg_guide.html
+
+## Examples
+
+* https://l10n.kde.org/stats/gui/trunk-kf5/team/fr/plasma-desktop/
+* https://github.com/psifidotos/nowdock-plasmoid/tree/master/po
+* https://github.com/kotelnik/plasma-applet-redshift-control/tree/master/translations
+
+## Status
+| Locale | Lines | % Done|
+|----------|---------|-------|
+| Template | 207 | |
+| nl | 171/207 | 83% |
+| pt_BR | 185/207 | 89% |
+| zh_CN | 166/207 | 80% |
+| ru_RU | 207/207 | 100% |
diff --git a/package/translate/build b/package/translate/build
new file mode 100755
index 00000000..d83b6868
--- /dev/null
+++ b/package/translate/build
@@ -0,0 +1,35 @@
+#!/bin/bash
+# Version: 8 (Modular)
+
+DIR=$(dirname $(readlink -f "$0"))
+
+plasmoidName=$("$DIR/get_metadata.sh" "Id")
+projectName="plasma_applet_${plasmoidName}"
+
+if [ -z "$plasmoidName" ]; then
+ echo "[build] Error: Couldn't read 'Id' from metadata.json."
+ exit 1
+fi
+
+if [ -z "$(which msgfmt)" ]; then
+ echo "[build] Error: msgfmt command not found. Need to install gettext"
+ echo "[build] Running 'sudo apt install gettext'"
+ sudo apt install gettext
+fi
+
+echo "[build] Compiling messages for $projectName"
+
+catalogs=$(find . -name '*.po' | sort)
+for cat in $catalogs; do
+ echo "$cat"
+ catLocale=$(basename "${cat%.*}")
+ msgfmt -o "${catLocale}.mo" "$cat"
+
+ installPath="$DIR/../contents/locale/${catLocale}/LC_MESSAGES/${projectName}.mo"
+
+ echo "[build] Install to ${installPath}"
+ mkdir -p "$(dirname "$installPath")"
+ mv "${catLocale}.mo" "${installPath}"
+done
+
+echo "[build] Done building messages"
diff --git a/package/translate/get_metadata.sh b/package/translate/get_metadata.sh
new file mode 100755
index 00000000..d08e8331
--- /dev/null
+++ b/package/translate/get_metadata.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+# Скрипт для извлечения значений из metadata.json
+# Использование: ./get_metadata.sh "Ключ"
+
+DIR=$(dirname $(readlink -f "$0"))
+METADATA_FILE="$DIR/../metadata.json"
+KEY="$1"
+
+if [ -z "$KEY" ] || [ ! -f "$METADATA_FILE" ]; then
+ exit 1
+fi
+
+# 1. Пробуем grep -P (быстро и точно)
+val=$(grep -oP "\"$KEY\"\s*:\s*\"\K[^\"]+" "$METADATA_FILE" 2> /dev/null)
+
+# 2. Если grep -P нет, используем sed (резерв)
+if [ -z "$val" ]; then
+ val=$(grep "\"$KEY\":" "$METADATA_FILE" | head -n 1 | sed -E 's/.*"'"$KEY"'"\s*:\s*"([^"]+)".*/\1/')
+fi
+
+echo "$val"
diff --git a/package/translate/jsonParse.py b/package/translate/jsonParse.py
new file mode 100644
index 00000000..50e4873b
--- /dev/null
+++ b/package/translate/jsonParse.py
@@ -0,0 +1,17 @@
+import json
+import sys
+
+f = open(sys.argv[1],)
+values = json.load(f)
+keys = sys.argv[2].split(".")
+first = True
+try:
+ for key in keys:
+ if first:
+ first = False
+ curValue = values[key]
+ else:
+ curValue = curValue[key]
+ print(curValue)
+except:
+ raise SystemExit('Error: Invalid Key or File')
diff --git a/package/translate/merge b/package/translate/merge
new file mode 100755
index 00000000..a6134652
--- /dev/null
+++ b/package/translate/merge
@@ -0,0 +1,98 @@
+#!/bin/bash
+# Version: 24 (Modular)
+
+DIR=$(dirname $(readlink -f "$0"))
+
+# Используем единый скрипт для получения данных
+plasmoidName=$("$DIR/get_metadata.sh" "Id")
+widgetName="${plasmoidName##*.}"
+bugAddress=$("$DIR/get_metadata.sh" "Website")
+
+packageRoot=".."
+projectName="plasma_applet_${plasmoidName}"
+
+if [ -z "$plasmoidName" ]; then
+ echo "[merge] Error: Couldn't read 'Id' from metadata.json."
+ exit 1
+fi
+
+if [ -z "$(which xgettext)" ]; then
+ echo "[merge] Error: xgettext command not found."
+ echo "[merge] Running 'sudo apt install gettext'"
+ sudo apt install gettext
+fi
+
+echo "[merge] Extracting messages"
+potArgs="--from-code=UTF-8 --width=200 --add-location=file"
+
+find "${packageRoot}" -name '*.cpp' -o -name '*.h' -o -name '*.c' -o -name '*.qml' -o -name '*.js' | \
+sort > "${DIR}/infiles.list"
+
+xgettext \
+ ${potArgs} \
+ --files-from="${DIR}/infiles.list" \
+ -C -kde \
+ -ci18n \
+ -ki18n:1 -ki18nc:1c,2 -ki18np:1,2 -ki18ncp:1c,2,3 \
+ -kki18n:1 -kki18nc:1c,2 -kki18np:1,2 -kki18ncp:1c,2,3 \
+ -kxi18n:1 -kxi18nc:1c,2 -kxi18np:1,2 -kxi18ncp:1c,2,3 \
+ -kkxi18n:1 -kkxi18nc:1c,2 -kkxi18np:1,2 -kkxi18ncp:1c,2,3 \
+ -kI18N_NOOP:1 -kI18NC_NOOP:1c,2 \
+ -kI18N_NOOP2:1c,2 -kI18N_NOOP2_NOSTRIP:1c,2 \
+ -ktr2i18n:1 -ktr2xi18n:1 \
+ -kN_:1 \
+ -kaliasLocale \
+ --package-name="${widgetName}" \
+ --msgid-bugs-address="${bugAddress}" \
+ -D "${packageRoot}" \
+ -D "${DIR}" \
+ -o "template.pot.new"
+
+if [ $? -ne 0 ]; then
+ echo "[merge] error while calling xgettext. aborting."
+ exit 1
+fi
+
+sed -i 's/# SOME DESCRIPTIVE TITLE./'"# Translation of ${widgetName} in LANGUAGE"'/' "template.pot.new"
+sed -i 's/# Copyright (C) YEAR THE PACKAGE'"'"'S COPYRIGHT HOLDER/'"# Copyright (C) $(date +%Y)"'/' "template.pot.new"
+
+if [ -f "template.pot" ]; then
+ newPotDate=$(grep "POT-Creation-Date:" template.pot.new | sed 's/.\{3\}$//')
+ oldPotDate=$(grep "POT-Creation-Date:" template.pot | sed 's/.\{3\}$//')
+ sed -i 's/'"${newPotDate}"'/'"${oldPotDate}"'/' "template.pot.new"
+ changes=$(diff "template.pot" "template.pot.new")
+ if [ ! -z "$changes" ]; then
+ sed -i 's/'"${oldPotDate}"'/'"${newPotDate}"'/' "template.pot.new"
+ mv "template.pot.new" "template.pot"
+ echo "Template updated."
+ else
+ rm "template.pot.new"
+ echo "No changes in template."
+ fi
+else
+ mv "template.pot.new" "template.pot"
+fi
+
+rm "${DIR}/infiles.list"
+echo "[merge] Done extracting messages"
+
+echo "[merge] Merging messages"
+catalogs=$(find . -name '*.po' | sort)
+for cat in $catalogs; do
+ echo "[merge] $cat"
+ catLocale=$(basename "${cat%.*}")
+
+ cp "$cat" "$cat.new"
+ sed -i 's/"Content-Type: text\/plain; charset=CHARSET\\n"/"Content-Type: text\/plain; charset=UTF-8\\n"/' "$cat.new"
+
+ msgmerge \
+ --width=400 \
+ --add-location=file \
+ --no-fuzzy-matching \
+ -o "$cat.new" \
+ "$cat.new" "${DIR}/template.pot"
+
+ sed -i 's/# SOME DESCRIPTIVE TITLE./'"# Translation of ${widgetName} in ${catLocale}"'/' "$cat.new"
+ mv "$cat.new" "$cat"
+done
+echo "[merge] Done merging messages"
diff --git a/package/translate/nl.po b/package/translate/nl.po
new file mode 100644
index 00000000..ee7bee4e
--- /dev/null
+++ b/package/translate/nl.po
@@ -0,0 +1,1100 @@
+# Translation of fancytasks in nl
+# Copyright (C) 2023
+# This file is distributed under the same license as the fancytasks package.
+# FIRST AUTHOR , YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: fancytasks\n"
+"Report-Msgid-Bugs-To: https://github.com/alexankitty/FancyTasks\n"
+"POT-Creation-Date: 2025-12-22 13:42+0200\n"
+"PO-Revision-Date: 2023-07-13 22:27+0200\n"
+"Last-Translator: Heimen Stoffels \n"
+"Language-Team: \n"
+"Language: nl\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 3.3.2\n"
+
+#: ../contents/config/config.qml
+msgid "Appearance"
+msgstr "Vormgeving"
+
+#: ../contents/config/config.qml
+msgid "Behavior"
+msgstr "Gedrag"
+
+#: ../contents/config/config.qml
+msgid "Indicators"
+msgstr "Indicators"
+
+#: ../contents/config/config.qml
+msgid "Pinned Applications"
+msgstr ""
+
+#: ../contents/ui/AudioStream.qml
+msgctxt "@action:button"
+msgid "Unmute"
+msgstr ""
+
+#: ../contents/ui/AudioStream.qml
+msgctxt "@action:button"
+msgid "Mute"
+msgstr ""
+
+#: ../contents/ui/AudioStream.qml
+msgctxt "@info:tooltip %1 is the window title"
+msgid "Unmute %1"
+msgstr ""
+
+#: ../contents/ui/AudioStream.qml
+msgctxt "@info:tooltip %1 is the window title"
+msgid "Mute %1"
+msgstr ""
+
+#: ../contents/ui/Badge.qml
+msgctxt "Invalid number of new messages, overlay, keep short"
+msgid "—"
+msgstr "—"
+
+#: ../contents/ui/Badge.qml
+msgctxt "Over 9999 new messages, overlay, keep short"
+msgid "9,999+"
+msgstr "9,999+"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Use plasma borders"
+msgstr "Plasmakaders gebruiken"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Icon Scale"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Icon Hover Effects"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Icon zoom factor (px):"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "How much the icon should grow when hovered (in pixels)"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Zoom animation duration (ms):"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Duration of the zoom animation in milliseconds"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Icon Size (px):"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Set icon size instead of scaling"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Button Colors:"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Using Plasma Style/Accent"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Using Color Overlay"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml ../contents/ui/ConfigIndicators.qml
+msgid "Use dominant icon color"
+msgstr "Dominante pictogramkleur gebruiken"
+
+#: ../contents/ui/ConfigAppearance.qml ../contents/ui/ConfigIndicators.qml
+msgid "Custom Color:"
+msgstr "Zelfgekozen kleur:"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Colorize inactive buttons"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Plasma Button Decorations:"
+msgstr "Plasma-knopdecoraties:"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Disable All"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Disable Inactive Buttons"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Plasma Button Direction:"
+msgstr "Plasma-knoprichting:"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Override"
+msgstr "Afdwingen"
+
+#: ../contents/ui/ConfigAppearance.qml ../contents/ui/ConfigIndicators.qml
+msgid "Be sure to use this when using as a floating widget"
+msgstr "Let op: gebruik dit alleen bij gebruik als zwevende widget"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "North"
+msgstr "Bovenaan"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "South"
+msgstr "Onderaan"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "West"
+msgstr "Links"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "East"
+msgstr "Rechts"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Maximum button length (px):"
+msgstr "Maximale knopbreedte (in px):"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Space between taskbar items (px):"
+msgstr "Ruimte tussen taakbalkitems (in px):"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@label for several checkboxes"
+msgid "General:"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@option:check section General"
+msgid "Show small window previews when hovering over tasks"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@option:check section General"
+msgid "Hide other windows when hovering over previews"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@option:check section General"
+msgid "Mark applications that play audio"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@option:check section General"
+msgid "Fill free space on panel"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@label:listbox"
+msgid "Maximum task width:"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@item:inlistbox how wide a task item should be"
+msgid "Narrow"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@item:inlistbox how wide a task item should be"
+msgid "Medium"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@item:inlistbox how wide a task item should be"
+msgid "Wide"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@label for radio button group, completes sentence: … when panel is low on space etc."
+msgid "Use multi-column view:"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@label for radio button group, completes sentence: … when panel is low on space etc."
+msgid "Use multi-row view:"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@option:radio Never use multi-column view for Task Manager"
+msgid "Never"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@option:radio completes sentence: Use multi-column/row view"
+msgid "When panel is low on space and thick enough"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@option:radio completes sentence: Use multi-column/row view"
+msgid "Always when panel is thick enough"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@label:spinbox maximum number of columns for tasks"
+msgid "Maximum columns:"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@label:spinbox maximum number of rows for tasks"
+msgid "Maximum rows:"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@label:listbox"
+msgid "Spacing between icons:"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@item:inlistbox Icon spacing"
+msgid "Small"
+msgstr "Klein"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@item:inlistbox Icon spacing"
+msgid "Normal"
+msgstr "Normaal"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@item:inlistbox Icon spacing"
+msgid "Large"
+msgstr "Groot"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@info:usagetip under a set of radio buttons when Touch Mode is on"
+msgid "Automatically set to Large when in Touch mode"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Display:"
+msgstr "Tonen:"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Show task names"
+msgstr "Pictogrammen en namen"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Show icons only"
+msgstr "Alleen pictogrammen"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label:listbox how to group tasks"
+msgid "Group:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox how to group tasks"
+msgid "Do not group"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox how to group tasks"
+msgid "By program name"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label:listbox completes sentence like: … cycles through tasks"
+msgid "Clicking grouped task:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Completes the sentence 'Clicking grouped task cycles through tasks' "
+msgid "Cycles through tasks"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Completes the sentence 'Clicking grouped task shows small window previews' "
+msgid "Shows small window previews"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Completes the sentence 'Clicking grouped task shows large window previews' "
+msgid "Shows large window previews"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Completes the sentence 'Clicking grouped task shows textual list' "
+msgid "Shows textual list"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@info displayed as InlineMessage"
+msgid "The compositor does not support displaying windows side by side, so a textual list will be displayed instead."
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check grouped task"
+msgid "Combine into single button"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check grouped task"
+msgid "Group only when the Task Manager is full"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label:listbox sort tasks in grouped task"
+msgid "Sort:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox sort tasks in grouped task"
+msgid "Do not sort"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox sort tasks in grouped task"
+msgid "Manually"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox sort tasks in grouped task"
+msgid "Alphabetically"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox sort tasks in grouped task"
+msgid "By desktop"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox sort tasks in grouped task"
+msgid "By activity"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check configure task sorting"
+msgid "Keep launchers separate"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check for icons-and-text task manager"
+msgid "Hide launchers after application startup"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label for checkbox Part of a sentence: 'Clicking active task minimizes the task'"
+msgid "Clicking active task:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check Part of a sentence: 'Clicking active task minimizes the task'"
+msgid "Minimizes the task"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label:listbox completes sentence like: … does nothing"
+msgid "Middle-clicking any task:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Part of a sentence: 'Middle-clicking any task does nothing'"
+msgid "Does nothing"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Part of a sentence: 'Middle-clicking any task closes window or group'"
+msgid "Closes window or group"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Part of a sentence: 'Middle-clicking any task opens a new window'"
+msgid "Opens a new window"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Part of a sentence: 'Middle-clicking any task minimizes/restores window or group'"
+msgid "Minimizes/Restores window or group"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Part of a sentence: 'Middle-clicking any task toggles grouping'"
+msgid "Toggles grouping"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Part of a sentence: 'Middle-clicking any task brings it to the current virtual desktop'"
+msgid "Brings it to the current virtual desktop"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label for checkbox Part of a sentence: 'Mouse wheel cycles through tasks'"
+msgid "Mouse wheel:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check Part of a sentence: 'Mouse wheel cycles through tasks'"
+msgid "Cycles through tasks"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check mouse wheel task cycling"
+msgid "Skip minimized tasks"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label for checkbox group, completes sentence like: … from current screen"
+msgid "Show only tasks:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: show only tasks"
+msgid "From current screen"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: show only tasks"
+msgid "From current desktop"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: show only tasks"
+msgid "From current activity"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: show only tasks"
+msgid "That are minimized"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label for checkbox, completes sentence: … unhide if window wants attention"
+msgid "When panel is hidden:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: When panel is hidden"
+msgid "Unhide when a window wants attention"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label for radiobutton group completes sentence like: … on the bottom"
+msgid "New tasks appear:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: New tasks appear"
+msgid "On the bottom"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: New tasks appear"
+msgid "To the right"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: New tasks appear"
+msgid "To the left"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: New tasks appear"
+msgid "On the top"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicators:"
+msgstr "Indicators:"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Disabled"
+msgstr "Uitgeschakeld"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Enabled"
+msgstr "Ingeschakeld"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Display Progress on Indicator"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Progress Color:"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Disable for Inactive Windows"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Group Overlay:"
+msgstr "Groepslaag:"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Takes effect on next time plasma groups tasks."
+msgstr "Let op: dit wordt toegepast na de volgende groepering."
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Animate Indicators:"
+msgstr "Indicators voorzien van animaties:"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator Location:"
+msgstr "Indicatorlocatie:"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Reverse shown side"
+msgstr "Aan andere zijde tonen"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Override location"
+msgstr "Locatie afdwingen"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Bottom"
+msgstr "Onderaan"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Left"
+msgstr "Links"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Right"
+msgstr "Rechts"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Top"
+msgstr "Bovenaan"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator Edge Offset (px):"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator Style:"
+msgstr "Indicatorstijl:"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Metro"
+msgstr "Metro"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Ciliora"
+msgstr "Ciliora"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Dashes"
+msgstr "Streepjes"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator Min Limit:"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator Max Limit:"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Minimize Options:"
+msgstr "Minimaliseeropties:"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Desaturate"
+msgstr "Verzadiging verminderen"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Shrink when minimized"
+msgstr "Verkleinen"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Growth/Shrink factor:"
+msgstr "Vergroot-/Verkleinfactor:"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator size (px):"
+msgstr "Indicatorgrootte (in px):"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator length (px):"
+msgstr "Indicatorbreedte (in px):"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator Radius (%):"
+msgstr "Indicatorstraal (in %):"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator margin (px):"
+msgstr "Indicatormarge (in px):"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator Color:"
+msgstr "Indicatorkleur:"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Use plasma accent color"
+msgstr "Plasma-accentkleur"
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Add Application"
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Search applications..."
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Add"
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Default Web Browser"
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Default File Manager"
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Default Mail Client"
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Default Application"
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Add applications to pin to the taskbar. Drag items to reorder them."
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Remove"
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Add Application..."
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Add Special Launcher..."
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Places"
+msgstr "Locaties"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Recent Files"
+msgstr "Recente bestanden"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Actions"
+msgstr "Acties"
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "Play previous track"
+msgid "Previous Track"
+msgstr "Vorig nummer"
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "Pause playback"
+msgid "Pause"
+msgstr "Pauzeren"
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "Start playback"
+msgid "Play"
+msgstr "Afspelen"
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "Play next track"
+msgid "Next Track"
+msgstr "Volgend nummer"
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "Stop playback"
+msgid "Stop"
+msgstr "Stoppen"
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "Quit media player app"
+msgid "Quit"
+msgstr "Afsluiten"
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "Open or bring to the front window of media player app"
+msgid "Restore"
+msgstr "Herstellen"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Mute"
+msgstr "Dempen"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Open New Window"
+msgstr "Nieuw venster openen"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Move to &Desktop"
+msgstr "Verplaatsen naar bureaubla&d"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Move &To Current Desktop"
+msgstr "Verplaa&tsen naar huidig bureaublad"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "&All Desktops"
+msgstr "&Alle bureaubladen"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "&New Desktop"
+msgstr "&Nieuw bureaublad"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Show in &Activities"
+msgstr "Tonen in &activiteiten"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Add To Current Activity"
+msgstr "Toevoegen aan huidige activiteit"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "All Activities"
+msgstr "Alle activiteiten"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Move to %1"
+msgstr "Verplaatsen naar %1"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "&Pin to Task Manager"
+msgstr "Vastma&ken aan takenbeheer"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "On All Activities"
+msgstr "Op alle activiteiten"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "On The Current Activity"
+msgstr "Op de huidige activiteit"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Unpin from Task Manager"
+msgstr "Losmaken van takenbeheer"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "More"
+msgstr "Meer"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "&Move"
+msgstr "Ver&plaatsen"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Re&size"
+msgstr "Grootte aanpa&ssen"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Ma&ximize"
+msgstr "Ma&ximaliseren"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Mi&nimize"
+msgstr "Mi&nimaliseren"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Keep &Above Others"
+msgstr "&Altijd bovenaan"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Keep &Below Others"
+msgstr "Altijd on&deraan"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "&Fullscreen"
+msgstr "&Volledig scherm"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "&Shade"
+msgstr "Op&rollen"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Allow this program to be grouped"
+msgstr "Groeperen toestaan"
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "@item:inmenu"
+msgid "&Close All"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgid "&Close"
+msgstr "&Sluiten"
+
+#: ../contents/ui/Task.qml
+msgctxt "@info:usagetip %1 application name"
+msgid "Launch %1"
+msgstr ""
+
+#: ../contents/ui/Task.qml
+msgctxt "@info:tooltip"
+msgid "There is %1 new message."
+msgid_plural "There are %1 new messages."
+msgstr[0] ""
+msgstr[1] ""
+
+#: ../contents/ui/Task.qml
+msgctxt "@info:usagetip %1 task name"
+msgid "Show Task tooltip for %1"
+msgstr ""
+
+#: ../contents/ui/Task.qml
+msgctxt "@info:usagetip %1 task name"
+msgid "Show windows side by side for %1"
+msgstr ""
+
+#: ../contents/ui/Task.qml
+msgctxt "@info:usagetip %1 task name"
+msgid "Open textual list of windows for %1"
+msgstr ""
+
+#: ../contents/ui/Task.qml
+msgid "Activate %1"
+msgstr "%1 activeren"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "button to unmute app"
+msgid "Unmute %1"
+msgstr "%1 ontdempen"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "button to mute app"
+msgid "Mute %1"
+msgstr "%1 dempen"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "Accessibility data on volume slider"
+msgid "Adjust volume for %1"
+msgstr "Volumeniveau van %1 aanpassen"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "volume percentage"
+msgid "%1%"
+msgstr "%1%"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "only used for sizing, should be widest possible string"
+msgid "100%"
+msgstr "100%"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "Comma-separated list of desktops"
+msgid "On %1"
+msgstr "Op %1"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "Comma-separated list of desktops"
+msgid "Pinned to all desktops"
+msgstr "Vastgemaakt op alle bureaubladen"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "Which virtual desktop a window is currently on"
+msgid "Available on all activities"
+msgstr "Actief op alle activiteiten"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "Activities a window is currently on (apart from the current one)"
+msgid "Also available on %1"
+msgstr "Ook actief op %1"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "Which activities a window is currently on"
+msgid "Available on %1"
+msgstr "Actief op %1"
+
+#~ msgid "General:"
+#~ msgstr "Algemeen:"
+
+#~ msgid "Show tooltips"
+#~ msgstr "Vensterminiaturen tonen"
+
+#~ msgid "Highlight windows when hovering over task tooltips"
+#~ msgstr "Vensters voorvertonen na aanwijzen van vensterminiaturen"
+
+#~ msgid "Mark applications that play audio"
+#~ msgstr "Programma's die audio afspelen markeren"
+
+#~ msgid "Maximum columns:"
+#~ msgstr "Maximaal aantal kolommen:"
+
+#~ msgid "Maximum rows:"
+#~ msgstr "Maximaal aantal rijen:"
+
+#~ msgid "Always arrange tasks in rows of as many columns"
+#~ msgstr "Taken altijd verdelen over zoveel mogelijk kolommen"
+
+#~ msgid "Always arrange tasks in columns of as many rows"
+#~ msgstr "Taken altijd verdelen over zoveel mogelijk rijen"
+
+#~ msgid "Spacing between icons:"
+#~ msgstr "Ruimte tussen pictogrammen:"
+
+#~ msgctxt "@info:usagetip under a set of radio buttons when Touch Mode is on"
+#~ msgid "Automatically set to Large when in Touch Mode"
+#~ msgstr "Automatisch ingesteld op ‘Groot’ als de aanraakmodus is ingeschakeld"
+
+#~ msgid "Group:"
+#~ msgstr "Groeperen:"
+
+#~ msgid "Do not group"
+#~ msgstr "Niet groeperen"
+
+#~ msgid "By program name"
+#~ msgstr "Op programmanaam"
+
+#~ msgid "Clicking grouped task:"
+#~ msgstr "Actie na aanklikken van gegroepeerde taak:"
+
+#~ msgctxt "Completes the sentence 'Clicking grouped task cycles through tasks' "
+#~ msgid "Cycles through tasks"
+#~ msgstr "Van taak wisselen"
+
+#~ msgctxt "Completes the sentence 'Clicking grouped task shows tooltip window thumbnails' "
+#~ msgid "Shows tooltip window thumbnails"
+#~ msgstr "Vensterminiaturen tonen"
+
+#~ msgctxt "Completes the sentence 'Clicking grouped task shows windows side by side' "
+#~ msgid "Shows windows side by side"
+#~ msgstr "Vensters naast elkaar tonen"
+
+#~ msgctxt "Completes the sentence 'Clicking grouped task shows textual list' "
+#~ msgid "Shows textual list"
+#~ msgstr "Vensterlijst tonen"
+
+#~ msgid "Tooltips are disabled, so the windows will be displayed side by side instead."
+#~ msgstr "Vensterminiaturen zijn uitgeschakeld; vensters worden naast elkaar getoond."
+
+#~ msgid "Tooltips are disabled, and the compositor does not support displaying windows side by side, so a textual list will be displayed instead"
+#~ msgstr "Vensterminiaturen zijn uitgeschakeld en de compositor heeft geen ondersteuning voor het tonen van vensters naast elkaar. Er wordt in plaats daarvan een lijst getoond."
+
+#~ msgid "The compositor does not support displaying windows side by side, so a textual list will be displayed instead."
+#~ msgstr "De compositor heeft geen ondersteuning voor het tonen van vensters naast elkaar; er wordt een lijst getoond."
+
+#~ msgid "Combine into single button"
+#~ msgstr "Combineren tot één knop"
+
+#~ msgid "Group only when the Task Manager is full"
+#~ msgstr "Alleen groeperen als takenbeheer vol is"
+
+#~ msgid "Sort:"
+#~ msgstr "Rangschikken:"
+
+#~ msgid "Do not sort"
+#~ msgstr "Niet rangschikken"
+
+#~ msgid "Manually"
+#~ msgstr "Handmatig"
+
+#~ msgid "Alphabetically"
+#~ msgstr "Op alfabetische volgorde"
+
+#~ msgid "By desktop"
+#~ msgstr "Op bureaublad"
+
+#~ msgid "By activity"
+#~ msgstr "Op activiteit"
+
+#~ msgid "Keep launchers separate"
+#~ msgstr "Starters gescheiden houden"
+
+#~ msgctxt "Part of a sentence: 'Clicking active task minimizes the task'"
+#~ msgid "Clicking active task:"
+#~ msgstr "Actie na aanklikken van actieve taak:"
+
+#~ msgctxt "Part of a sentence: 'Clicking active task minimizes the task'"
+#~ msgid "Minimizes the task"
+#~ msgstr "Taak minimaliseren"
+
+#~ msgid "Middle-clicking any task:"
+#~ msgstr "Actie na middelklikken op een taak:"
+
+#~ msgctxt "Part of a sentence: 'Middle-clicking any task does nothing'"
+#~ msgid "Does nothing"
+#~ msgstr "Niets doen"
+
+#~ msgctxt "Part of a sentence: 'Middle-clicking any task closes window or group'"
+#~ msgid "Closes window or group"
+#~ msgstr "Venster of groep sluiten"
+
+#~ msgctxt "Part of a sentence: 'Middle-clicking any task opens a new window'"
+#~ msgid "Opens a new window"
+#~ msgstr "Nieuw venster openen"
+
+#~ msgctxt "Part of a sentence: 'Middle-clicking any task minimizes/restores window or group'"
+#~ msgid "Minimizes/Restores window or group"
+#~ msgstr "Venster of groep minimaliseren/herstellen"
+
+#~ msgctxt "Part of a sentence: 'Middle-clicking any task toggles grouping'"
+#~ msgid "Toggles grouping"
+#~ msgstr "Groeperen in-/uitschakelen"
+
+#~ msgctxt "Part of a sentence: 'Middle-clicking any task brings it to the current virtual desktop'"
+#~ msgid "Brings it to the current virtual desktop"
+#~ msgstr "Verplaatsen naar huidig virtueel bureaublad"
+
+#~ msgctxt "Part of a sentence: 'Mouse wheel cycles through tasks'"
+#~ msgid "Mouse wheel:"
+#~ msgstr "Muiswiel:"
+
+#~ msgctxt "Part of a sentence: 'Mouse wheel cycles through tasks'"
+#~ msgid "Cycles through tasks"
+#~ msgstr "Van taak wisselen"
+
+#~ msgid "Skip minimized tasks"
+#~ msgstr "Geminimaliseerde taken overslaan"
+
+#~ msgid "Show only tasks:"
+#~ msgstr "Alleen taken tonen van:"
+
+#~ msgid "From current screen"
+#~ msgstr "Huidig scherm"
+
+#~ msgid "From current desktop"
+#~ msgstr "Huidig bureaublad"
+
+#~ msgid "From current activity"
+#~ msgstr "Huidige activiteit"
+
+#~ msgid "That are minimized"
+#~ msgstr "Geminimaliseerde vensters"
+
+#~ msgid "When panel is hidden:"
+#~ msgstr "Als het paneel verborgen is:"
+
+#~ msgid "Unhide when a window wants attention"
+#~ msgstr "Tonen als een venster om aandacht vraagt"
+
+#~ msgid "New tasks appear:"
+#~ msgstr "Nieuwe taken tonen aan:"
+
+#~ msgid "To the right"
+#~ msgstr "Rechterkant"
+
+#~ msgid "To the left"
+#~ msgstr "Linkerkant"
+
+#~ msgctxt "1 = number of desktop, 2 = desktop name"
+#~ msgid "&%1 %2"
+#~ msgstr "&%1 %2"
+
+#~ msgid "Press OK to fix layout issues."
+#~ msgstr "Klik op oké om indelingsproblemen op te lossen."
+
+#~ msgid "Disable"
+#~ msgstr "Uitschakelen"
+
+#~ msgid "Colorize buttons"
+#~ msgstr "Knoppen verkleuren"
+
+#~ msgid "Indicator Limit:"
+#~ msgstr "Indicatorlimiet:"
diff --git a/package/translate/plasmoidlocaletest b/package/translate/plasmoidlocaletest
new file mode 100755
index 00000000..2f4ac2c4
--- /dev/null
+++ b/package/translate/plasmoidlocaletest
@@ -0,0 +1,169 @@
+#!/bin/bash
+# Version 10
+
+function checkIfLangInstalled {
+ if [ -x "$(command -v dpkg)" ]; then
+ dpkg -l ${1} >/dev/null 2>&1 || \
+ ( echo -e "${1} not installed.\nInstalling now...\n"; sudo apt install ${1} ) || \
+ ( echo -e "\nError trying to install ${1}\n"; exit 1 )
+ elif [ -x "$(command -v pacman)" ]; then
+ # TODO: run `locale -a` and check if the locale is enabled.
+ if false; then
+ # https://wiki.archlinux.org/index.php/Locale
+ # Uncomment the locale in /etc/locale.gen
+ # Then run `locale-gen`
+ echo -e "\nPlease install this locale in System Settings first.\n"
+ exit 1
+ else
+ echo ""
+ fi
+ else
+ echo -e "\nPackage manager not recognized. If the widget is not translated, please install the package '${1}'\n"
+ fi
+}
+
+langInput="${1}"
+lang=""
+languagePack=""
+
+if [[ "$langInput" =~ ":" ]]; then # String contains a colon so assume it's a locale code.
+ lang="${langInput}"
+ IFS=: read -r l1 l2 <<< "${lang}"
+ languagePack="language-pack-${l2}"
+fi
+
+# https://stackoverflow.com/questions/3191664/list-of-all-locales-and-their-short-codes/28357857#28357857
+declare -a langArr=(
+ "af_ZA:af:Afrikaans (South Africa)"
+ "ak_GH:ak:Akan (Ghana)"
+ "am_ET:am:Amharic (Ethiopia)"
+ "ar_EG:ar:Arabic (Egypt)"
+ "as_IN:as:Assamese (India)"
+ "az_AZ:az:Azerbaijani (Azerbaijan)"
+ "be_BY:be:Belarusian (Belarus)"
+ "bem_ZM:bem:Bemba (Zambia)"
+ "bg_BG:bg:Bulgarian (Bulgaria)"
+ "bo_IN:bo:Tibetan (India)"
+ "bs_BA:bs:Bosnian (Bosnia and Herzegovina)"
+ "ca_ES:ca:Catalan (Spain)"
+ "chr_US:ch:Cherokee (United States)"
+ "cs_CZ:cs:Czech (Czech Republic)"
+ "cy_GB:cy:Welsh (United Kingdom)"
+ "da_DK:da:Danish (Denmark)"
+ "de_DE:de:German (Germany)"
+ "el_GR:el:Greek (Greece)"
+ "es_MX:es:Spanish (Mexico)"
+ "et_EE:et:Estonian (Estonia)"
+ "eu_ES:eu:Basque (Spain)"
+ "fa_IR:fa:Persian (Iran)"
+ "ff_SN:ff:Fulah (Senegal)"
+ "fi_FI:fi:Finnish (Finland)"
+ "fo_FO:fo:Faroese (Faroe Islands)"
+ "fr_CA:fr:French (Canada)"
+ "ga_IE:ga:Irish (Ireland)"
+ "gl_ES:gl:Galician (Spain)"
+ "gu_IN:gu:Gujarati (India)"
+ "gv_GB:gv:Manx (United Kingdom)"
+ "ha_NG:ha:Hausa (Nigeria)"
+ "he_IL:he:Hebrew (Israel)"
+ "hi_IN:hi:Hindi (India)"
+ "hr_HR:hr:Croatian (Croatia)"
+ "hu_HU:hu:Hungarian (Hungary)"
+ "hy_AM:hy:Armenian (Armenia)"
+ "id_ID:id:Indonesian (Indonesia)"
+ "ig_NG:ig:Igbo (Nigeria)"
+ "is_IS:is:Icelandic (Iceland)"
+ "it_IT:it:Italian (Italy)"
+ "ja_JP:ja:Japanese (Japan)"
+ "ka_GE:ka:Georgian (Georgia)"
+ "kk_KZ:kk:Kazakh (Kazakhstan)"
+ "kl_GL:kl:Kalaallisut (Greenland)"
+ "km_KH:km:Khmer (Cambodia)"
+ "kn_IN:kn:Kannada (India)"
+ "ko_KR:ko:Korean (South Korea)"
+ "ko_KR:ko:Korean (South Korea)"
+ "lg_UG:lg:Ganda (Uganda)"
+ "lt_LT:lt:Lithuanian (Lithuania)"
+ "lv_LV:lv:Latvian (Latvia)"
+ "mg_MG:mg:Malagasy (Madagascar)"
+ "mk_MK:mk:Macedonian (Macedonia)"
+ "ml_IN:ml:Malayalam (India)"
+ "mr_IN:mr:Marathi (India)"
+ "ms_MY:ms:Malay (Malaysia)"
+ "mt_MT:mt:Maltese (Malta)"
+ "my_MM:my:Burmese (Myanmar [Burma])"
+ "nb_NO:nb:Norwegian Bokmål (Norway)"
+ "ne_NP:ne:Nepali (Nepal)"
+ "nl_NL:nl:Dutch (Netherlands)"
+ "nn_NO:nn:Norwegian Nynorsk (Norway)"
+ "om_ET:om:Oromo (Ethiopia)"
+ "or_IN:or:Oriya (India)"
+ "pa_PK:pa:Punjabi (Pakistan)"
+ "pl_PL:pl:Polish (Poland)"
+ "ps_AF:ps:Pashto (Afghanistan)"
+ "pt_BR:pt:Portuguese (Brazil)"
+ "ro_RO:ro:Romanian (Romania)"
+ "ru_RU:ru:Russian (Russia)"
+ "rw_RW:rw:Kinyarwanda (Rwanda)"
+ "si_LK:si:Sinhala (Sri Lanka)"
+ "sk_SK:sk:Slovak (Slovakia)"
+ "sl_SI:sl:Slovenian (Slovenia)"
+ "so_SO:so:Somali (Somalia)"
+ "sq_AL:sq:Albanian (Albania)"
+ "sr_RS:sr:Serbian (Serbia)"
+ "sv_SE:sv:Swedish (Sweden)"
+ "sw_KE:sw:Swahili (Kenya)"
+ "ta_IN:ta:Tamil (India)"
+ "te_IN:te:Telugu (India)"
+ "th_TH:th:Thai (Thailand)"
+ "ti_ER:ti:Tigrinya (Eritrea)"
+ "to_TO:to:Tonga (Tonga)"
+ "tr_TR:tr:Turkish (Turkey)"
+ "uk_UA:uk:Ukrainian (Ukraine)"
+ "ur_IN:ur:Urdu (India)"
+ "uz_UZ:uz:Uzbek (Uzbekistan)"
+ "vi_VN:vi:Vietnamese (Vietnam)"
+ "yo_NG:yo:Yoruba (Nigeria)"
+ "yo_NG:yo:Yoruba (Nigeria)"
+ "yue_HK:yu:Cantonese (Hong Kong)"
+ "zh_CN:zh:Chinese (China)"
+ "zu_ZA:zu:Zulu (South Africa)"
+)
+
+for i in "${langArr[@]}"; do
+ IFS=: read -r l1 l2 l3 <<< "$i"
+ if [ "$langInput" == "$l2" ] || [ "$langInput" == "$l1" ]; then
+ lang="${l1}:${l2}"
+ languagePack="language-pack-${l2}"
+ fi
+done
+
+if [ -z "$lang" ]; then
+ echo "plasmoidlocaletest doesn't recognize the language '$lang'"
+ echo "Eg:"
+ scriptcmd='sh ./plasmoidlocaletest'
+ echo " ${scriptcmd} ru_RU"
+ exit 1
+fi
+
+IFS=: read -r l1 l2 <<< "${lang}"
+l1="${l1}.UTF-8"
+
+# Check if language is installed
+if [ ! -z "$languagePack" ]; then
+ if [ "$lang" == "zh_CN:zh" ]; then languagePack="language-pack-zh-hans"; fi
+
+ checkIfLangInstalled "$languagePack" || exit 1
+fi
+
+echo "LANGUAGE=\"${lang}\""
+echo "LANG=\"${l1}\""
+
+SCRIPT_DIR=$(dirname $(readlink -f "$0"))
+# Absolute path to package
+PACKAGE_DIR=$(readlink -f "$SCRIPT_DIR/..")
+
+# Build local translations
+bash "${SCRIPT_DIR}/build"
+
+LANGUAGE="${lang}" LANG="${l1}" LC_ALL="${l1}" QML_DISABLE_DISK_CACHE=true plasmawindowed "$PACKAGE_DIR"
diff --git a/package/translate/pt_BR.po b/package/translate/pt_BR.po
new file mode 100644
index 00000000..56d512bf
--- /dev/null
+++ b/package/translate/pt_BR.po
@@ -0,0 +1,1088 @@
+# Translation of Fancy Tasks in pt_BR
+# Copyright (C) 2023
+# This file is distributed under the same license as the Fancy Tasks package.
+# FIRST AUTHOR , YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: Fancy Tasks\n"
+"Report-Msgid-Bugs-To: https://github.com/alexankitty/FancyTasks\n"
+"POT-Creation-Date: 2025-12-22 13:42+0200\n"
+"PO-Revision-Date: 2023-09-21 16:18-0300\n"
+"Last-Translator: Douglas Guimarães <>\n"
+"Language-Team: \n"
+"Language: pt_BR \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../contents/config/config.qml
+msgid "Appearance"
+msgstr "Aparência"
+
+#: ../contents/config/config.qml
+msgid "Behavior"
+msgstr "Comportamento"
+
+#: ../contents/config/config.qml
+msgid "Indicators"
+msgstr "Indicadores"
+
+#: ../contents/config/config.qml
+msgid "Pinned Applications"
+msgstr ""
+
+#: ../contents/ui/AudioStream.qml
+msgctxt "@action:button"
+msgid "Unmute"
+msgstr ""
+
+#: ../contents/ui/AudioStream.qml
+msgctxt "@action:button"
+msgid "Mute"
+msgstr ""
+
+#: ../contents/ui/AudioStream.qml
+msgctxt "@info:tooltip %1 is the window title"
+msgid "Unmute %1"
+msgstr ""
+
+#: ../contents/ui/AudioStream.qml
+msgctxt "@info:tooltip %1 is the window title"
+msgid "Mute %1"
+msgstr ""
+
+#: ../contents/ui/Badge.qml
+msgctxt "Invalid number of new messages, overlay, keep short"
+msgid "—"
+msgstr "_"
+
+#: ../contents/ui/Badge.qml
+msgctxt "Over 9999 new messages, overlay, keep short"
+msgid "9,999+"
+msgstr "9,999+"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Use plasma borders"
+msgstr "Usar bordas do plasma"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Icon Scale"
+msgstr "Escala do ícone"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Icon Hover Effects"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Icon zoom factor (px):"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "How much the icon should grow when hovered (in pixels)"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Zoom animation duration (ms):"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Duration of the zoom animation in milliseconds"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Icon Size (px):"
+msgstr "Tamanho do ícone (px):"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Set icon size instead of scaling"
+msgstr "Definir pelo tamanho do ícone ao invés da escala"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Button Colors:"
+msgstr "Cores do botão:"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Using Plasma Style/Accent"
+msgstr "Usar estilo/Acentuação do Plasma"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Using Color Overlay"
+msgstr "Usar cor sobreposição"
+
+#: ../contents/ui/ConfigAppearance.qml ../contents/ui/ConfigIndicators.qml
+msgid "Use dominant icon color"
+msgstr "Usar cor predominante do ícone"
+
+#: ../contents/ui/ConfigAppearance.qml ../contents/ui/ConfigIndicators.qml
+msgid "Custom Color:"
+msgstr "Cor customizada."
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Colorize inactive buttons"
+msgstr "Colorir botões inativos"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Plasma Button Decorations:"
+msgstr "Decoração dos botões do Plasma"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Disable All"
+msgstr "Desabilitar todos"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Disable Inactive Buttons"
+msgstr "Desabilitar botões inativos"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Plasma Button Direction:"
+msgstr "Direção dos botões do Plasma"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Override"
+msgstr "Sobrescrever"
+
+#: ../contents/ui/ConfigAppearance.qml ../contents/ui/ConfigIndicators.qml
+msgid "Be sure to use this when using as a floating widget"
+msgstr "Tenha certeza que está usando como um widget flutuante"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "North"
+msgstr "Norte"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "South"
+msgstr "Sul"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "West"
+msgstr "Oeste"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "East"
+msgstr "Leste"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Maximum button length (px):"
+msgstr "Comprimento máximo do botão (px):"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Space between taskbar items (px):"
+msgstr "Espaço entre os itens da barra de tarefas (px):"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@label for several checkboxes"
+msgid "General:"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@option:check section General"
+msgid "Show small window previews when hovering over tasks"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@option:check section General"
+msgid "Hide other windows when hovering over previews"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@option:check section General"
+msgid "Mark applications that play audio"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@option:check section General"
+msgid "Fill free space on panel"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@label:listbox"
+msgid "Maximum task width:"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@item:inlistbox how wide a task item should be"
+msgid "Narrow"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@item:inlistbox how wide a task item should be"
+msgid "Medium"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@item:inlistbox how wide a task item should be"
+msgid "Wide"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@label for radio button group, completes sentence: … when panel is low on space etc."
+msgid "Use multi-column view:"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@label for radio button group, completes sentence: … when panel is low on space etc."
+msgid "Use multi-row view:"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@option:radio Never use multi-column view for Task Manager"
+msgid "Never"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@option:radio completes sentence: Use multi-column/row view"
+msgid "When panel is low on space and thick enough"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@option:radio completes sentence: Use multi-column/row view"
+msgid "Always when panel is thick enough"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@label:spinbox maximum number of columns for tasks"
+msgid "Maximum columns:"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@label:spinbox maximum number of rows for tasks"
+msgid "Maximum rows:"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@label:listbox"
+msgid "Spacing between icons:"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@item:inlistbox Icon spacing"
+msgid "Small"
+msgstr "Pequeno"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@item:inlistbox Icon spacing"
+msgid "Normal"
+msgstr "Normal"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@item:inlistbox Icon spacing"
+msgid "Large"
+msgstr "Grande"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@info:usagetip under a set of radio buttons when Touch Mode is on"
+msgid "Automatically set to Large when in Touch mode"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Display:"
+msgstr "Exibir:"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Show task names"
+msgstr "Exibir o nome das tarefas"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Show icons only"
+msgstr "Exibir apenas os ícones"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label:listbox how to group tasks"
+msgid "Group:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox how to group tasks"
+msgid "Do not group"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox how to group tasks"
+msgid "By program name"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label:listbox completes sentence like: … cycles through tasks"
+msgid "Clicking grouped task:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Completes the sentence 'Clicking grouped task cycles through tasks' "
+msgid "Cycles through tasks"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Completes the sentence 'Clicking grouped task shows small window previews' "
+msgid "Shows small window previews"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Completes the sentence 'Clicking grouped task shows large window previews' "
+msgid "Shows large window previews"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Completes the sentence 'Clicking grouped task shows textual list' "
+msgid "Shows textual list"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@info displayed as InlineMessage"
+msgid "The compositor does not support displaying windows side by side, so a textual list will be displayed instead."
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check grouped task"
+msgid "Combine into single button"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check grouped task"
+msgid "Group only when the Task Manager is full"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label:listbox sort tasks in grouped task"
+msgid "Sort:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox sort tasks in grouped task"
+msgid "Do not sort"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox sort tasks in grouped task"
+msgid "Manually"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox sort tasks in grouped task"
+msgid "Alphabetically"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox sort tasks in grouped task"
+msgid "By desktop"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox sort tasks in grouped task"
+msgid "By activity"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check configure task sorting"
+msgid "Keep launchers separate"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check for icons-and-text task manager"
+msgid "Hide launchers after application startup"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label for checkbox Part of a sentence: 'Clicking active task minimizes the task'"
+msgid "Clicking active task:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check Part of a sentence: 'Clicking active task minimizes the task'"
+msgid "Minimizes the task"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label:listbox completes sentence like: … does nothing"
+msgid "Middle-clicking any task:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Part of a sentence: 'Middle-clicking any task does nothing'"
+msgid "Does nothing"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Part of a sentence: 'Middle-clicking any task closes window or group'"
+msgid "Closes window or group"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Part of a sentence: 'Middle-clicking any task opens a new window'"
+msgid "Opens a new window"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Part of a sentence: 'Middle-clicking any task minimizes/restores window or group'"
+msgid "Minimizes/Restores window or group"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Part of a sentence: 'Middle-clicking any task toggles grouping'"
+msgid "Toggles grouping"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Part of a sentence: 'Middle-clicking any task brings it to the current virtual desktop'"
+msgid "Brings it to the current virtual desktop"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label for checkbox Part of a sentence: 'Mouse wheel cycles through tasks'"
+msgid "Mouse wheel:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check Part of a sentence: 'Mouse wheel cycles through tasks'"
+msgid "Cycles through tasks"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check mouse wheel task cycling"
+msgid "Skip minimized tasks"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label for checkbox group, completes sentence like: … from current screen"
+msgid "Show only tasks:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: show only tasks"
+msgid "From current screen"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: show only tasks"
+msgid "From current desktop"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: show only tasks"
+msgid "From current activity"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: show only tasks"
+msgid "That are minimized"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label for checkbox, completes sentence: … unhide if window wants attention"
+msgid "When panel is hidden:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: When panel is hidden"
+msgid "Unhide when a window wants attention"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label for radiobutton group completes sentence like: … on the bottom"
+msgid "New tasks appear:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: New tasks appear"
+msgid "On the bottom"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: New tasks appear"
+msgid "To the right"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: New tasks appear"
+msgid "To the left"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: New tasks appear"
+msgid "On the top"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicators:"
+msgstr "Indicadores:"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Disabled"
+msgstr "Desabilitado"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Enabled"
+msgstr "Habilitado"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Display Progress on Indicator"
+msgstr "Mostrar progresso no indicator"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Progress Color:"
+msgstr "Cor de progresso:"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Disable for Inactive Windows"
+msgstr "Desabilitar para janelas inativas"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Group Overlay:"
+msgstr "Agrupar sobreposição:"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Takes effect on next time plasma groups tasks."
+msgstr "Tem efeito na próxima vez que o plasma agrupar as tarefas."
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Animate Indicators:"
+msgstr "Animar indicadores:"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator Location:"
+msgstr "Posição do indicador:"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Reverse shown side"
+msgstr "Exibir na posiçao oposta"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Override location"
+msgstr "Sobrepor posição"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Bottom"
+msgstr "Base"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Left"
+msgstr "Esquerda"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Right"
+msgstr "Direita"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Top"
+msgstr "Topo"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator Edge Offset (px):"
+msgstr "Distância do indicador em relação da borda (px):"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator Style:"
+msgstr "Estilo do indicador:"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Metro"
+msgstr "Metro"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Ciliora"
+msgstr "Ciliora"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Dashes"
+msgstr "Dashes"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator Min Limit:"
+msgstr "Limite minímo de indicadores:"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator Max Limit:"
+msgstr "Limite máximo de indicatores:"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Minimize Options:"
+msgstr "Ao minimizar uma tarefa:"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Desaturate"
+msgstr "Desaturar"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Shrink when minimized"
+msgstr "Encolher"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Growth/Shrink factor:"
+msgstr "Fator de crecimento/encolhimento:"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator size (px):"
+msgstr "Tamanho do indicador (px):"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator length (px):"
+msgstr "Comprimento do indicador (px):"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator Radius (%):"
+msgstr "Raio do Indicador (%)"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator margin (px):"
+msgstr "Margin do indicador"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator Color:"
+msgstr "Cor do indicador:"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Use plasma accent color"
+msgstr "Usar cor de acentuação do Plasma"
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Add Application"
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Search applications..."
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Add"
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Default Web Browser"
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Default File Manager"
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Default Mail Client"
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Default Application"
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Add applications to pin to the taskbar. Drag items to reorder them."
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Remove"
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Add Application..."
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Add Special Launcher..."
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Places"
+msgstr "Locais"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Recent Files"
+msgstr "Arquivos recentes"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Actions"
+msgstr "Ações"
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "Play previous track"
+msgid "Previous Track"
+msgstr "Faixa anterior"
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "Pause playback"
+msgid "Pause"
+msgstr "Pausar"
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "Start playback"
+msgid "Play"
+msgstr "Reproduzir"
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "Play next track"
+msgid "Next Track"
+msgstr "Próxima faixa"
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "Stop playback"
+msgid "Stop"
+msgstr "Parar"
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "Quit media player app"
+msgid "Quit"
+msgstr "Fechar"
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "Open or bring to the front window of media player app"
+msgid "Restore"
+msgstr "Restaurar"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Mute"
+msgstr "Silenciar"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Open New Window"
+msgstr "Abrir nova janela"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Move to &Desktop"
+msgstr "Mover para &área de trabalho"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Move &To Current Desktop"
+msgstr "Mover ¶ a área de trabalho atual"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "&All Desktops"
+msgstr "&Todas as áreas de trabalho"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "&New Desktop"
+msgstr "&Nova área de trabalho"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Show in &Activities"
+msgstr "Mostrar em &Activities"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Add To Current Activity"
+msgstr "Adicionar a atividade atual"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "All Activities"
+msgstr "Todas as atividades"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Move to %1"
+msgstr "Mover para %1"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "&Pin to Task Manager"
+msgstr "&Fixar no gerenciador de tarefas"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "On All Activities"
+msgstr "Em todas atividades"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "On The Current Activity"
+msgstr "Na atividade atual"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Unpin from Task Manager"
+msgstr "Desafixar do gerenciador de tarefas"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "More"
+msgstr "Mais"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "&Move"
+msgstr "&Mover"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Re&size"
+msgstr "Re&dimensionar"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Ma&ximize"
+msgstr "Ma&ximizar"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Mi&nimize"
+msgstr "Mi&nimizar"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Keep &Above Others"
+msgstr "Manter &acima das outras janelas"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Keep &Below Others"
+msgstr "Manter &abaixo das outras janelas"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "&Fullscreen"
+msgstr "&Tela cheia"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "&Shade"
+msgstr "&Sombreado"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Allow this program to be grouped"
+msgstr "Permitir que este programa seja agrupado"
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "@item:inmenu"
+msgid "&Close All"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgid "&Close"
+msgstr "&Fechar"
+
+#: ../contents/ui/Task.qml
+msgctxt "@info:usagetip %1 application name"
+msgid "Launch %1"
+msgstr ""
+
+#: ../contents/ui/Task.qml
+msgctxt "@info:tooltip"
+msgid "There is %1 new message."
+msgid_plural "There are %1 new messages."
+msgstr[0] ""
+msgstr[1] ""
+
+#: ../contents/ui/Task.qml
+msgctxt "@info:usagetip %1 task name"
+msgid "Show Task tooltip for %1"
+msgstr ""
+
+#: ../contents/ui/Task.qml
+msgctxt "@info:usagetip %1 task name"
+msgid "Show windows side by side for %1"
+msgstr ""
+
+#: ../contents/ui/Task.qml
+msgctxt "@info:usagetip %1 task name"
+msgid "Open textual list of windows for %1"
+msgstr ""
+
+#: ../contents/ui/Task.qml
+msgid "Activate %1"
+msgstr "Ativo %1"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "button to unmute app"
+msgid "Unmute %1"
+msgstr "Silenciar %1"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "button to mute app"
+msgid "Mute %1"
+msgstr "Mutar %1"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "Accessibility data on volume slider"
+msgid "Adjust volume for %1"
+msgstr "Ajustar volume para %1"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "volume percentage"
+msgid "%1%"
+msgstr "%1%"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "only used for sizing, should be widest possible string"
+msgid "100%"
+msgstr "100%"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "Comma-separated list of desktops"
+msgid "On %1"
+msgstr "Em %1"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "Comma-separated list of desktops"
+msgid "Pinned to all desktops"
+msgstr "Fixar em todas as áreas de trabalho"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "Which virtual desktop a window is currently on"
+msgid "Available on all activities"
+msgstr "Disponível em todas as atividades"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "Activities a window is currently on (apart from the current one)"
+msgid "Also available on %1"
+msgstr "Disponível também em %1"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "Which activities a window is currently on"
+msgid "Available on %1"
+msgstr "Disponível em %1"
+
+#~ msgid "General:"
+#~ msgstr "Geral"
+
+#~ msgid "Show tooltips"
+#~ msgstr "Mostrar visualizações"
+
+#~ msgid "Highlight windows when hovering over task tooltips"
+#~ msgstr "Destacar janelas quando passar o cursor sobre as visualizações"
+
+#~ msgid "Mark applications that play audio"
+#~ msgstr "Marque os programas que estiverem tocando áudio"
+
+#~ msgid "Maximum columns:"
+#~ msgstr "Máximo de colunas:"
+
+#~ msgid "Maximum rows:"
+#~ msgstr "Máximo de linhas"
+
+#~ msgid "Always arrange tasks in rows of as many columns"
+#~ msgstr "Sempre organize as tarefas em linhas com o máximo de colunas"
+
+#~ msgid "Always arrange tasks in columns of as many rows"
+#~ msgstr "Sempre organize as tarefas em colunas com o máximo de linhas"
+
+#~ msgid "Spacing between icons:"
+#~ msgstr "Espaço entre os ícones:"
+
+#~ msgctxt "@info:usagetip under a set of radio buttons when Touch Mode is on"
+#~ msgid "Automatically set to Large when in Touch Mode"
+#~ msgstr "Definir automaticamente para grande no \"Touch Mode\""
+
+#~ msgid "Group:"
+#~ msgstr "Agrupar pelo:"
+
+#~ msgid "Do not group"
+#~ msgstr "Não agrupe"
+
+#~ msgid "By program name"
+#~ msgstr "Nome do programa"
+
+#~ msgid "Clicking grouped task:"
+#~ msgstr "Ao clicar em tarefas agrupadas:"
+
+#~ msgctxt "Completes the sentence 'Clicking grouped task cycles through tasks' "
+#~ msgid "Cycles through tasks"
+#~ msgstr "Percorra entre as tarefas"
+
+#~ msgctxt "Completes the sentence 'Clicking grouped task shows tooltip window thumbnails' "
+#~ msgid "Shows tooltip window thumbnails"
+#~ msgstr "Mostre as visualizações da janela"
+
+#~ msgctxt "Completes the sentence 'Clicking grouped task shows windows side by side' "
+#~ msgid "Shows windows side by side"
+#~ msgstr "Mostre as janelas lado a lado"
+
+#~ msgctxt "Completes the sentence 'Clicking grouped task shows textual list' "
+#~ msgid "Shows textual list"
+#~ msgstr "Mostre uma lista com seus títulos"
+
+#~ msgid "Tooltips are disabled, so the windows will be displayed side by side instead."
+#~ msgstr "As visualizações estão desativada, portanto as janelas serão exibidas lado a lado"
+
+#~ msgid "Tooltips are disabled, and the compositor does not support displaying windows side by side, so a textual list will be displayed instead"
+#~ msgstr "As visualizações estão desativada e o compositor não suporta exibir as janelas lado a lado, portanto as janelas serão exibidas atráves da lista de títulos"
+
+#~ msgid "The compositor does not support displaying windows side by side, so a textual list will be displayed instead."
+#~ msgstr "O compositor não suporta exibir as janelas lado a lado, portanto as janelas serão exibidas atráves da lista de títulos"
+
+#~ msgid "Combine into single button"
+#~ msgstr "Combine em um único botão"
+
+#~ msgid "Group only when the Task Manager is full"
+#~ msgstr "Agrupe apenas quando o gerenciador de tarefas estiver cheio"
+
+#~ msgid "Sort:"
+#~ msgstr "Organizar:"
+
+#~ msgid "Do not sort"
+#~ msgstr "Não organize"
+
+#~ msgid "Manually"
+#~ msgstr "Manualmente"
+
+#~ msgid "Alphabetically"
+#~ msgstr "Alfabeticamente"
+
+#~ msgid "By desktop"
+#~ msgstr "Por área de trabalho"
+
+#~ msgid "By activity"
+#~ msgstr "Por Atividade"
+
+#~ msgid "Keep launchers separate"
+#~ msgstr "Manter launchers separados "
+
+#~ msgctxt "Part of a sentence: 'Clicking active task minimizes the task'"
+#~ msgid "Clicking active task:"
+#~ msgstr "Clicando em tarefas abertas:"
+
+#~ msgctxt "Part of a sentence: 'Clicking active task minimizes the task'"
+#~ msgid "Minimizes the task"
+#~ msgstr "Minimize a tarefa"
+
+#~ msgid "Middle-clicking any task:"
+#~ msgstr "Clicando no botão do scroll em qualquer tarefa:"
+
+#~ msgctxt "Part of a sentence: 'Middle-clicking any task does nothing'"
+#~ msgid "Does nothing"
+#~ msgstr "Não faz nada"
+
+#~ msgctxt "Part of a sentence: 'Middle-clicking any task closes window or group'"
+#~ msgid "Closes window or group"
+#~ msgstr "Fecha a janela ou o grupo de janelas"
+
+#~ msgctxt "Part of a sentence: 'Middle-clicking any task opens a new window'"
+#~ msgid "Opens a new window"
+#~ msgstr "Abre uma nova janela"
+
+#~ msgctxt "Part of a sentence: 'Middle-clicking any task minimizes/restores window or group'"
+#~ msgid "Minimizes/Restores window or group"
+#~ msgstr "Minimiza/restaura a janela ou grupo de janelas"
+
+#~ msgctxt "Part of a sentence: 'Middle-clicking any task toggles grouping'"
+#~ msgid "Toggles grouping"
+#~ msgstr "Agrupa/desagrupa janelas"
+
+#~ msgctxt "Part of a sentence: 'Middle-clicking any task brings it to the current virtual desktop'"
+#~ msgid "Brings it to the current virtual desktop"
+#~ msgstr "Traz para a área de trabalho virtual atual"
+
+#~ msgctxt "Part of a sentence: 'Mouse wheel cycles through tasks'"
+#~ msgid "Mouse wheel:"
+#~ msgstr "Scroll do mouse"
+
+#~ msgctxt "Part of a sentence: 'Mouse wheel cycles through tasks'"
+#~ msgid "Cycles through tasks"
+#~ msgstr "Percorre entre as tarefas"
+
+#~ msgid "Skip minimized tasks"
+#~ msgstr "Ignorar tarefas minimizadas"
+
+#~ msgid "Show only tasks:"
+#~ msgstr "Exibir apenas tarefas:"
+
+#~ msgid "From current screen"
+#~ msgstr "No monitor atual"
+
+#~ msgid "From current desktop"
+#~ msgstr "Na área de trabalho atual"
+
+#~ msgid "From current activity"
+#~ msgstr "Na atividade atual"
+
+#~ msgid "That are minimized"
+#~ msgstr "Que estão minimizadas"
+
+#~ msgid "When panel is hidden:"
+#~ msgstr "Quando o painel está ocultado:"
+
+#~ msgid "Unhide when a window wants attention"
+#~ msgstr "Desocultar quando uma janela necessita de atenção"
+
+#~ msgid "New tasks appear:"
+#~ msgstr "Novas tarefas aparecem:"
+
+#~ msgid "To the right"
+#~ msgstr "Na direita"
+
+#~ msgid "To the left"
+#~ msgstr "Na esquerda"
+
+#~ msgctxt "1 = number of desktop, 2 = desktop name"
+#~ msgid "&%1 %2"
+#~ msgstr "&%1 %2"
diff --git a/package/translate/ru_RU.po b/package/translate/ru_RU.po
new file mode 100644
index 00000000..2218f379
--- /dev/null
+++ b/package/translate/ru_RU.po
@@ -0,0 +1,846 @@
+# Translation of Fancy Tasks in Russian
+# Copyright (C) 2025
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Fancy Tasks\n"
+"Report-Msgid-Bugs-To: https://github.com/alexankitty/FancyTasks\n"
+"POT-Creation-Date: 2025-12-22 13:42+0200\n"
+"PO-Revision-Date: 2025-12-22 13:00+0300\n"
+"Last-Translator: Vitaliy Elin \n"
+"Language-Team: Russian\n"
+"Language: ru_RU\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+
+#: ../contents/config/config.qml
+msgid "Appearance"
+msgstr "Внешний вид"
+
+#: ../contents/config/config.qml
+msgid "Behavior"
+msgstr "Поведение"
+
+#: ../contents/config/config.qml
+msgid "Indicators"
+msgstr "Индикаторы"
+
+#: ../contents/config/config.qml
+msgid "Pinned Applications"
+msgstr "Закрепленные приложения"
+
+#: ../contents/ui/AudioStream.qml
+msgctxt "@action:button"
+msgid "Unmute"
+msgstr "Включить звук"
+
+#: ../contents/ui/AudioStream.qml
+msgctxt "@action:button"
+msgid "Mute"
+msgstr "Выключить звук"
+
+#: ../contents/ui/AudioStream.qml
+msgctxt "@info:tooltip %1 is the window title"
+msgid "Unmute %1"
+msgstr "Включить звук %1"
+
+#: ../contents/ui/AudioStream.qml
+msgctxt "@info:tooltip %1 is the window title"
+msgid "Mute %1"
+msgstr "Выключить звук %1"
+
+#: ../contents/ui/Badge.qml
+msgctxt "Invalid number of new messages, overlay, keep short"
+msgid "—"
+msgstr "—"
+
+#: ../contents/ui/Badge.qml
+msgctxt "Over 9999 new messages, overlay, keep short"
+msgid "9,999+"
+msgstr "9,999+"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Use plasma borders"
+msgstr "Использовать рамки Plasma"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Icon Scale"
+msgstr "Масштаб значков"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Icon Hover Effects"
+msgstr "Эффекты наведения"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Icon zoom factor (px):"
+msgstr "Увеличение значка (px):"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "How much the icon should grow when hovered (in pixels)"
+msgstr "На сколько пикселей увеличится значок при наведении"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Zoom animation duration (ms):"
+msgstr "Длительность анимации (мс):"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Duration of the zoom animation in milliseconds"
+msgstr "Длительность анимации увеличения в миллисекундах"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Icon Size (px):"
+msgstr "Размер значков (px):"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Set icon size instead of scaling"
+msgstr "Задать размер значков вместо масштабирования"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Button Colors:"
+msgstr "Цвета кнопок:"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Using Plasma Style/Accent"
+msgstr "Использовать стиль Plasma/Акцент"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Using Color Overlay"
+msgstr "Использовать наложение цвета"
+
+#: ../contents/ui/ConfigAppearance.qml ../contents/ui/ConfigIndicators.qml
+msgid "Use dominant icon color"
+msgstr "Использовать основной цвет значка"
+
+#: ../contents/ui/ConfigAppearance.qml ../contents/ui/ConfigIndicators.qml
+msgid "Custom Color:"
+msgstr "Свой цвет:"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Colorize inactive buttons"
+msgstr "Окрашивать неактивные кнопки"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Plasma Button Decorations:"
+msgstr "Декорации кнопок Plasma:"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Disable All"
+msgstr "Отключить все"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Disable Inactive Buttons"
+msgstr "Отключить неактивные кнопки"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Plasma Button Direction:"
+msgstr "Направление кнопок Plasma:"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Override"
+msgstr "Переопределить"
+
+#: ../contents/ui/ConfigAppearance.qml ../contents/ui/ConfigIndicators.qml
+msgid "Be sure to use this when using as a floating widget"
+msgstr "Используйте это, если виджет плавающий"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "North"
+msgstr "Север"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "South"
+msgstr "Юг"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "West"
+msgstr "Запад"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "East"
+msgstr "Восток"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Maximum button length (px):"
+msgstr "Макс. длина кнопки (px):"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Space between taskbar items (px):"
+msgstr "Расстояние между элементами (px):"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "General:"
+msgstr "Общие:"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Show small window previews when hovering over tasks"
+msgstr "Показывать миниатюры окон при наведении"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Hide other windows when hovering over previews"
+msgstr "Скрывать остальные окна при наведении на миниатюру"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Mark applications that play audio"
+msgstr "Помечать приложения, воспроизводящие звук"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Fill free space on panel"
+msgstr "Заполнять свободное место панели"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Maximum task width:"
+msgstr "Макс. ширина задачи:"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Narrow"
+msgstr "Узкая"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Medium"
+msgstr "Средняя"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Wide"
+msgstr "Широкая"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Use multi-column view:"
+msgstr "Многоколоночный вид:"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Use multi-row view:"
+msgstr "Многострочный вид:"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Never"
+msgstr "Никогда"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "When panel is low on space and thick enough"
+msgstr "Когда мало места и панель широкая"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Always when panel is thick enough"
+msgstr "Всегда, если панель достаточно широкая"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Maximum columns:"
+msgstr "Макс. столбцов:"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Maximum rows:"
+msgstr "Макс. строк:"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Spacing between icons:"
+msgstr "Интервал между значками:"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Small"
+msgstr "Маленький"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Normal"
+msgstr "Обычный"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Large"
+msgstr "Большой"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Automatically set to Large when in Touch mode"
+msgstr "Автоматически «Большой» в сенсорном режиме"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Display:"
+msgstr "Отображение:"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Show task names"
+msgstr "Показывать названия задач"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Show icons only"
+msgstr "Только значки"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Group:"
+msgstr "Группировка:"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Do not group"
+msgstr "Не группировать"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "By program name"
+msgstr "По имени программы"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Clicking grouped task:"
+msgstr "Нажатие на сгруппированную задачу:"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Cycles through tasks"
+msgstr "Переключает задачи"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Shows small window previews"
+msgstr "Показывает миниатюры окон"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Shows large window previews"
+msgstr "Показывает большие превью"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Shows textual list"
+msgstr "Показывает текстовый список"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "The compositor does not support displaying windows side by side, so a textual list will be displayed instead."
+msgstr "Композитор не поддерживает показ окон рядом, будет показан текстовый список."
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Combine into single button"
+msgstr "Объединять в одну кнопку"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Group only when the Task Manager is full"
+msgstr "Группировать только при заполнении панели"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Sort:"
+msgstr "Сортировка:"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Do not sort"
+msgstr "Не сортировать"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Manually"
+msgstr "Вручную"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Alphabetically"
+msgstr "По алфавиту"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "By desktop"
+msgstr "По рабочим столам"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "By activity"
+msgstr "По комнатам"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Keep launchers separate"
+msgstr "Держать значки запуска отдельно"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Hide launchers after application startup"
+msgstr "Скрывать значки запуска после старта приложения"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Clicking active task:"
+msgstr "Нажатие на активную задачу:"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Minimizes the task"
+msgstr "Сворачивает задачу"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Middle-clicking any task:"
+msgstr "Средняя кнопка мыши:"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Does nothing"
+msgstr "Ничего не делает"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Closes window or group"
+msgstr "Закрывает окно или группу"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Opens a new window"
+msgstr "Открывает новое окно"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Minimizes/Restores window or group"
+msgstr "Сворачивает/Восстанавливает окно или группу"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Toggles grouping"
+msgstr "Переключает группировку"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Brings it to the current virtual desktop"
+msgstr "Переносит на текущий рабочий стол"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Mouse wheel:"
+msgstr "Колесо мыши:"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Skip minimized tasks"
+msgstr "Пропускать свернутые"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Show only tasks:"
+msgstr "Показывать только задачи:"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "From current screen"
+msgstr "С текущего экрана"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "From current desktop"
+msgstr "С текущего рабочего стола"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "From current activity"
+msgstr "Из текущей комнаты"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "That are minimized"
+msgstr "Которые свернуты"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "When panel is hidden:"
+msgstr "Когда панель скрыта:"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Unhide when a window wants attention"
+msgstr "Показывать, если окно требует внимания"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "New tasks appear:"
+msgstr "Новые задачи появляются:"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "On the bottom"
+msgstr "Снизу"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "To the right"
+msgstr "Справа"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "To the left"
+msgstr "Слева"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "On the top"
+msgstr "Сверху"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicators:"
+msgstr "Индикаторы:"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Disabled"
+msgstr "Отключены"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Enabled"
+msgstr "Включены"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Display Progress on Indicator"
+msgstr "Показывать прогресс на индикаторе"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Progress Color:"
+msgstr "Цвет прогресса:"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Disable for Inactive Windows"
+msgstr "Отключить для неактивных окон"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Group Overlay:"
+msgstr "Наложение группы:"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Takes effect on next time plasma groups tasks."
+msgstr "Применится при следующей группировке задач."
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Animate Indicators:"
+msgstr "Анимация индикаторов:"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator Location:"
+msgstr "Расположение индикатора:"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Reverse shown side"
+msgstr "Инвертировать сторону"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Override location"
+msgstr "Переопределить положение"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Bottom"
+msgstr "Снизу"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Left"
+msgstr "Слева"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Right"
+msgstr "Справа"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Top"
+msgstr "Сверху"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator Edge Offset (px):"
+msgstr "Отступ от края (px):"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator Style:"
+msgstr "Стиль индикатора:"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Metro"
+msgstr "Metro"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Ciliora"
+msgstr "Ciliora"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Dashes"
+msgstr "Тире"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator Min Limit:"
+msgstr "Мин. предел индикатора:"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator Max Limit:"
+msgstr "Макс. предел индикатора:"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Minimize Options:"
+msgstr "Параметры сворачивания:"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Desaturate"
+msgstr "Обесцветить"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Shrink when minimized"
+msgstr "Уменьшать при сворачивании"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Growth/Shrink factor:"
+msgstr "Коэффициент роста/уменьшения:"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator size (px):"
+msgstr "Размер индикатора (px):"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator length (px):"
+msgstr "Длина индикатора (px):"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator Radius (%):"
+msgstr "Радиус индикатора (%):"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator margin (px):"
+msgstr "Отступ индикатора (px):"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator Color:"
+msgstr "Цвет индикатора:"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Use plasma accent color"
+msgstr "Использовать цвет акцента Plasma"
+
+# --- ConfigPinnedApps.qml ---
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Add Application"
+msgstr "Добавить приложение"
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Search applications..."
+msgstr "Поиск приложений..."
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Add"
+msgstr "Добавить"
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Default Web Browser"
+msgstr "Браузер по умолчанию"
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Default File Manager"
+msgstr "Файловый менеджер"
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Default Mail Client"
+msgstr "Почтовый клиент"
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Default Application"
+msgstr "Приложение по умолчанию"
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Add applications to pin to the taskbar. Drag items to reorder them."
+msgstr "Добавьте приложения для закрепления. Перетаскивайте элементы для изменения порядка."
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Remove"
+msgstr "Удалить"
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Add Application..."
+msgstr "Добавить приложение..."
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Add Special Launcher..."
+msgstr "Добавить спец. запуск..."
+
+# --- ContextMenu.qml ---
+#: ../contents/ui/ContextMenu.qml
+msgid "Places"
+msgstr "Точки входа"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Recent Files"
+msgstr "Недавние файлы"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Actions"
+msgstr "Действия"
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "Play previous track"
+msgid "Previous Track"
+msgstr "Предыдущий трек"
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "Pause playback"
+msgid "Pause"
+msgstr "Пауза"
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "Start playback"
+msgid "Play"
+msgstr "Воспроизвести"
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "Play next track"
+msgid "Next Track"
+msgstr "Следующий трек"
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "Stop playback"
+msgid "Stop"
+msgstr "Стоп"
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "Quit media player app"
+msgid "Quit"
+msgstr "Выйти"
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "Open or bring to the front window of media player app"
+msgid "Restore"
+msgstr "Восстановить"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Mute"
+msgstr "Выключить звук"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Open New Window"
+msgstr "Открыть новое окно"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Move to &Desktop"
+msgstr "Переместить на &рабочий стол"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Move &To Current Desktop"
+msgstr "Переместить на &текущий рабочий стол"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "&All Desktops"
+msgstr "&Все рабочие столы"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "&New Desktop"
+msgstr "&Новый рабочий стол"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Show in &Activities"
+msgstr "Показать в &комнатах"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Add To Current Activity"
+msgstr "Добавить в текущую комнату"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "All Activities"
+msgstr "Все комнаты"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Move to %1"
+msgstr "Переместить в %1"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "&Pin to Task Manager"
+msgstr "&Закрепить в панели задач"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "On All Activities"
+msgstr "Во всех комнатах"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "On The Current Activity"
+msgstr "В текущей комнате"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Unpin from Task Manager"
+msgstr "Открепить от панели задач"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "More"
+msgstr "Еще"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "&Move"
+msgstr "&Переместить"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Re&size"
+msgstr "И&зменить размер"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Ma&ximize"
+msgstr "&Распахнуть"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Mi&nimize"
+msgstr "&Свернуть"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Keep &Above Others"
+msgstr "Поверх &других окон"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Keep &Below Others"
+msgstr "На &заднем плане"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "&Fullscreen"
+msgstr "&Полноэкранный режим"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "&Shade"
+msgstr "&Свернуть в заголовок"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Allow this program to be grouped"
+msgstr "Разрешить группировку этой программы"
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "@item:inmenu"
+msgid "&Close All"
+msgstr "&Закрыть все"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "&Close"
+msgstr "&Закрыть"
+
+#: ../contents/ui/Task.qml
+msgctxt "@info:usagetip %1 application name"
+msgid "Launch %1"
+msgstr "Запустить %1"
+
+#: ../contents/ui/Task.qml
+msgctxt "@info:tooltip"
+msgid "There is %1 new message."
+msgid_plural "There are %1 new messages."
+msgstr[0] "Новых сообщений: %1."
+msgstr[1] "Новых сообщений: %1."
+msgstr[2] "Новых сообщений: %1."
+
+#: ../contents/ui/Task.qml
+msgctxt "@info:usagetip %1 task name"
+msgid "Show Task tooltip for %1"
+msgstr "Показать подсказку для %1"
+
+#: ../contents/ui/Task.qml
+msgctxt "@info:usagetip %1 task name"
+msgid "Show windows side by side for %1"
+msgstr "Показать окна рядом для %1"
+
+#: ../contents/ui/Task.qml
+msgctxt "@info:usagetip %1 task name"
+msgid "Open textual list of windows for %1"
+msgstr "Открыть список окон для %1"
+
+#: ../contents/ui/Task.qml
+msgid "Activate %1"
+msgstr "Активировать %1"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "button to unmute app"
+msgid "Unmute %1"
+msgstr "Включить звук %1"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "button to mute app"
+msgid "Mute %1"
+msgstr "Выключить звук %1"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "Accessibility data on volume slider"
+msgid "Adjust volume for %1"
+msgstr "Регулировка громкости для %1"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "volume percentage"
+msgid "%1%"
+msgstr "%1%"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "only used for sizing, should be widest possible string"
+msgid "100%"
+msgstr "100%"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "Comma-separated list of desktops"
+msgid "On %1"
+msgstr "На %1"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "Comma-separated list of desktops"
+msgid "Pinned to all desktops"
+msgstr "Закреплено на всех рабочих столах"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "Which virtual desktop a window is currently on"
+msgid "Available on all activities"
+msgstr "Доступно во всех комнатах"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "Activities a window is currently on (apart from the current one)"
+msgid "Also available on %1"
+msgstr "Также доступно в %1"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "Which activities a window is currently on"
+msgid "Available on %1"
+msgstr "Доступно в %1"
diff --git a/package/translate/template.pot b/package/translate/template.pot
new file mode 100644
index 00000000..60b81319
--- /dev/null
+++ b/package/translate/template.pot
@@ -0,0 +1,914 @@
+# Translation of fancytasks in LANGUAGE
+# Copyright (C) 2025
+# This file is distributed under the same license as the fancytasks package.
+# FIRST AUTHOR , YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: fancytasks\n"
+"Report-Msgid-Bugs-To: https://github.com/alexankitty/FancyTasks\n"
+"POT-Creation-Date: 2025-12-22 13:42+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: LANGUAGE \n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
+
+#: ../contents/config/config.qml
+msgid "Appearance"
+msgstr ""
+
+#: ../contents/config/config.qml
+msgid "Behavior"
+msgstr ""
+
+#: ../contents/config/config.qml
+msgid "Indicators"
+msgstr ""
+
+#: ../contents/config/config.qml
+msgid "Pinned Applications"
+msgstr ""
+
+#: ../contents/ui/AudioStream.qml
+msgctxt "@action:button"
+msgid "Unmute"
+msgstr ""
+
+#: ../contents/ui/AudioStream.qml
+msgctxt "@action:button"
+msgid "Mute"
+msgstr ""
+
+#: ../contents/ui/AudioStream.qml
+msgctxt "@info:tooltip %1 is the window title"
+msgid "Unmute %1"
+msgstr ""
+
+#: ../contents/ui/AudioStream.qml
+msgctxt "@info:tooltip %1 is the window title"
+msgid "Mute %1"
+msgstr ""
+
+#: ../contents/ui/Badge.qml
+msgctxt "Invalid number of new messages, overlay, keep short"
+msgid "—"
+msgstr ""
+
+#: ../contents/ui/Badge.qml
+msgctxt "Over 9999 new messages, overlay, keep short"
+msgid "9,999+"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Use plasma borders"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Icon Scale"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Icon Hover Effects"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Icon zoom factor (px):"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "How much the icon should grow when hovered (in pixels)"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Zoom animation duration (ms):"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Duration of the zoom animation in milliseconds"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Icon Size (px):"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Set icon size instead of scaling"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Button Colors:"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Using Plasma Style/Accent"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Using Color Overlay"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml ../contents/ui/ConfigIndicators.qml
+msgid "Use dominant icon color"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml ../contents/ui/ConfigIndicators.qml
+msgid "Custom Color:"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Colorize inactive buttons"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Plasma Button Decorations:"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Disable All"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Disable Inactive Buttons"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Plasma Button Direction:"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Override"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml ../contents/ui/ConfigIndicators.qml
+msgid "Be sure to use this when using as a floating widget"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "North"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "South"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "West"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "East"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Maximum button length (px):"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Space between taskbar items (px):"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@label for several checkboxes"
+msgid "General:"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@option:check section General"
+msgid "Show small window previews when hovering over tasks"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@option:check section General"
+msgid "Hide other windows when hovering over previews"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@option:check section General"
+msgid "Mark applications that play audio"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@option:check section General"
+msgid "Fill free space on panel"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@label:listbox"
+msgid "Maximum task width:"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@item:inlistbox how wide a task item should be"
+msgid "Narrow"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@item:inlistbox how wide a task item should be"
+msgid "Medium"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@item:inlistbox how wide a task item should be"
+msgid "Wide"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@label for radio button group, completes sentence: … when panel is low on space etc."
+msgid "Use multi-column view:"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@label for radio button group, completes sentence: … when panel is low on space etc."
+msgid "Use multi-row view:"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@option:radio Never use multi-column view for Task Manager"
+msgid "Never"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@option:radio completes sentence: Use multi-column/row view"
+msgid "When panel is low on space and thick enough"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@option:radio completes sentence: Use multi-column/row view"
+msgid "Always when panel is thick enough"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@label:spinbox maximum number of columns for tasks"
+msgid "Maximum columns:"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@label:spinbox maximum number of rows for tasks"
+msgid "Maximum rows:"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@label:listbox"
+msgid "Spacing between icons:"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@item:inlistbox Icon spacing"
+msgid "Small"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@item:inlistbox Icon spacing"
+msgid "Normal"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@item:inlistbox Icon spacing"
+msgid "Large"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@info:usagetip under a set of radio buttons when Touch Mode is on"
+msgid "Automatically set to Large when in Touch mode"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Display:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Show task names"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Show icons only"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label:listbox how to group tasks"
+msgid "Group:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox how to group tasks"
+msgid "Do not group"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox how to group tasks"
+msgid "By program name"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label:listbox completes sentence like: … cycles through tasks"
+msgid "Clicking grouped task:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Completes the sentence 'Clicking grouped task cycles through tasks' "
+msgid "Cycles through tasks"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Completes the sentence 'Clicking grouped task shows small window previews' "
+msgid "Shows small window previews"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Completes the sentence 'Clicking grouped task shows large window previews' "
+msgid "Shows large window previews"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Completes the sentence 'Clicking grouped task shows textual list' "
+msgid "Shows textual list"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@info displayed as InlineMessage"
+msgid "The compositor does not support displaying windows side by side, so a textual list will be displayed instead."
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check grouped task"
+msgid "Combine into single button"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check grouped task"
+msgid "Group only when the Task Manager is full"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label:listbox sort tasks in grouped task"
+msgid "Sort:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox sort tasks in grouped task"
+msgid "Do not sort"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox sort tasks in grouped task"
+msgid "Manually"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox sort tasks in grouped task"
+msgid "Alphabetically"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox sort tasks in grouped task"
+msgid "By desktop"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox sort tasks in grouped task"
+msgid "By activity"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check configure task sorting"
+msgid "Keep launchers separate"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check for icons-and-text task manager"
+msgid "Hide launchers after application startup"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label for checkbox Part of a sentence: 'Clicking active task minimizes the task'"
+msgid "Clicking active task:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check Part of a sentence: 'Clicking active task minimizes the task'"
+msgid "Minimizes the task"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label:listbox completes sentence like: … does nothing"
+msgid "Middle-clicking any task:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Part of a sentence: 'Middle-clicking any task does nothing'"
+msgid "Does nothing"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Part of a sentence: 'Middle-clicking any task closes window or group'"
+msgid "Closes window or group"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Part of a sentence: 'Middle-clicking any task opens a new window'"
+msgid "Opens a new window"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Part of a sentence: 'Middle-clicking any task minimizes/restores window or group'"
+msgid "Minimizes/Restores window or group"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Part of a sentence: 'Middle-clicking any task toggles grouping'"
+msgid "Toggles grouping"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Part of a sentence: 'Middle-clicking any task brings it to the current virtual desktop'"
+msgid "Brings it to the current virtual desktop"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label for checkbox Part of a sentence: 'Mouse wheel cycles through tasks'"
+msgid "Mouse wheel:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check Part of a sentence: 'Mouse wheel cycles through tasks'"
+msgid "Cycles through tasks"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check mouse wheel task cycling"
+msgid "Skip minimized tasks"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label for checkbox group, completes sentence like: … from current screen"
+msgid "Show only tasks:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: show only tasks"
+msgid "From current screen"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: show only tasks"
+msgid "From current desktop"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: show only tasks"
+msgid "From current activity"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: show only tasks"
+msgid "That are minimized"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label for checkbox, completes sentence: … unhide if window wants attention"
+msgid "When panel is hidden:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: When panel is hidden"
+msgid "Unhide when a window wants attention"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label for radiobutton group completes sentence like: … on the bottom"
+msgid "New tasks appear:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: New tasks appear"
+msgid "On the bottom"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: New tasks appear"
+msgid "To the right"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: New tasks appear"
+msgid "To the left"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: New tasks appear"
+msgid "On the top"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicators:"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Disabled"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Enabled"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Display Progress on Indicator"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Progress Color:"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Disable for Inactive Windows"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Group Overlay:"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Takes effect on next time plasma groups tasks."
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Animate Indicators:"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator Location:"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Reverse shown side"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Override location"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Bottom"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Left"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Right"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Top"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator Edge Offset (px):"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator Style:"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Metro"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Ciliora"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Dashes"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator Min Limit:"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator Max Limit:"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Minimize Options:"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Desaturate"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Shrink when minimized"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Growth/Shrink factor:"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator size (px):"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator length (px):"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator Radius (%):"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator margin (px):"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator Color:"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Use plasma accent color"
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Add Application"
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Search applications..."
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Add"
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Default Web Browser"
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Default File Manager"
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Default Mail Client"
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Default Application"
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Add applications to pin to the taskbar. Drag items to reorder them."
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Remove"
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Add Application..."
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Add Special Launcher..."
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Places"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Recent Files"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Actions"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "Play previous track"
+msgid "Previous Track"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "Pause playback"
+msgid "Pause"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "Start playback"
+msgid "Play"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "Play next track"
+msgid "Next Track"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "Stop playback"
+msgid "Stop"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "Quit media player app"
+msgid "Quit"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "Open or bring to the front window of media player app"
+msgid "Restore"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Mute"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Open New Window"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Move to &Desktop"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Move &To Current Desktop"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgid "&All Desktops"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgid "&New Desktop"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Show in &Activities"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Add To Current Activity"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgid "All Activities"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Move to %1"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgid "&Pin to Task Manager"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgid "On All Activities"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgid "On The Current Activity"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Unpin from Task Manager"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgid "More"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgid "&Move"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Re&size"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Ma&ximize"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Mi&nimize"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Keep &Above Others"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Keep &Below Others"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgid "&Fullscreen"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgid "&Shade"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Allow this program to be grouped"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "@item:inmenu"
+msgid "&Close All"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgid "&Close"
+msgstr ""
+
+#: ../contents/ui/Task.qml
+msgctxt "@info:usagetip %1 application name"
+msgid "Launch %1"
+msgstr ""
+
+#: ../contents/ui/Task.qml
+msgctxt "@info:tooltip"
+msgid "There is %1 new message."
+msgid_plural "There are %1 new messages."
+msgstr[0] ""
+msgstr[1] ""
+
+#: ../contents/ui/Task.qml
+msgctxt "@info:usagetip %1 task name"
+msgid "Show Task tooltip for %1"
+msgstr ""
+
+#: ../contents/ui/Task.qml
+msgctxt "@info:usagetip %1 task name"
+msgid "Show windows side by side for %1"
+msgstr ""
+
+#: ../contents/ui/Task.qml
+msgctxt "@info:usagetip %1 task name"
+msgid "Open textual list of windows for %1"
+msgstr ""
+
+#: ../contents/ui/Task.qml
+msgid "Activate %1"
+msgstr ""
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "button to unmute app"
+msgid "Unmute %1"
+msgstr ""
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "button to mute app"
+msgid "Mute %1"
+msgstr ""
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "Accessibility data on volume slider"
+msgid "Adjust volume for %1"
+msgstr ""
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "volume percentage"
+msgid "%1%"
+msgstr ""
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "only used for sizing, should be widest possible string"
+msgid "100%"
+msgstr ""
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "Comma-separated list of desktops"
+msgid "On %1"
+msgstr ""
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "Comma-separated list of desktops"
+msgid "Pinned to all desktops"
+msgstr ""
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "Which virtual desktop a window is currently on"
+msgid "Available on all activities"
+msgstr ""
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "Activities a window is currently on (apart from the current one)"
+msgid "Also available on %1"
+msgstr ""
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "Which activities a window is currently on"
+msgid "Available on %1"
+msgstr ""
diff --git a/package/translate/zh_CN.po b/package/translate/zh_CN.po
new file mode 100644
index 00000000..43a74aa8
--- /dev/null
+++ b/package/translate/zh_CN.po
@@ -0,0 +1,1100 @@
+# Translation of fancytasks in zh_CN
+# Copyright (C) 2023
+# This file is distributed under the same license as the fancytasks package.
+# FIRST AUTHOR , YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: fancytasks\n"
+"Report-Msgid-Bugs-To: https://github.com/alexankitty/FancyTasks\n"
+"POT-Creation-Date: 2025-12-22 13:42+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: LANGUAGE \n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../contents/config/config.qml
+msgid "Appearance"
+msgstr "外观"
+
+#: ../contents/config/config.qml
+msgid "Behavior"
+msgstr "行为"
+
+#: ../contents/config/config.qml
+msgid "Indicators"
+msgstr "指示器"
+
+#: ../contents/config/config.qml
+msgid "Pinned Applications"
+msgstr ""
+
+#: ../contents/ui/AudioStream.qml
+msgctxt "@action:button"
+msgid "Unmute"
+msgstr ""
+
+#: ../contents/ui/AudioStream.qml
+msgctxt "@action:button"
+msgid "Mute"
+msgstr ""
+
+#: ../contents/ui/AudioStream.qml
+msgctxt "@info:tooltip %1 is the window title"
+msgid "Unmute %1"
+msgstr ""
+
+#: ../contents/ui/AudioStream.qml
+msgctxt "@info:tooltip %1 is the window title"
+msgid "Mute %1"
+msgstr ""
+
+#: ../contents/ui/Badge.qml
+msgctxt "Invalid number of new messages, overlay, keep short"
+msgid "—"
+msgstr "-"
+
+#: ../contents/ui/Badge.qml
+msgctxt "Over 9999 new messages, overlay, keep short"
+msgid "9,999+"
+msgstr "9,999+"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Use plasma borders"
+msgstr "使用plasma边框"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Icon Scale"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Icon Hover Effects"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Icon zoom factor (px):"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "How much the icon should grow when hovered (in pixels)"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Zoom animation duration (ms):"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Duration of the zoom animation in milliseconds"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Icon Size (px):"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Set icon size instead of scaling"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Button Colors:"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Using Plasma Style/Accent"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Using Color Overlay"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml ../contents/ui/ConfigIndicators.qml
+msgid "Use dominant icon color"
+msgstr "使用图标主体色"
+
+#: ../contents/ui/ConfigAppearance.qml ../contents/ui/ConfigIndicators.qml
+msgid "Custom Color:"
+msgstr "自定义颜色:"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Colorize inactive buttons"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Plasma Button Decorations:"
+msgstr "Plasma按钮装饰:"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Disable All"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Disable Inactive Buttons"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Plasma Button Direction:"
+msgstr "Plasma按钮方向"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Override"
+msgstr "覆盖"
+
+#: ../contents/ui/ConfigAppearance.qml ../contents/ui/ConfigIndicators.qml
+msgid "Be sure to use this when using as a floating widget"
+msgstr "作为浮动组件使用时需要设置"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "North"
+msgstr "北"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "South"
+msgstr "南"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "West"
+msgstr "西"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "East"
+msgstr "东"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Maximum button length (px):"
+msgstr "最大按钮长度(px)"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgid "Space between taskbar items (px):"
+msgstr "任务栏组件间距(px)"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@label for several checkboxes"
+msgid "General:"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@option:check section General"
+msgid "Show small window previews when hovering over tasks"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@option:check section General"
+msgid "Hide other windows when hovering over previews"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@option:check section General"
+msgid "Mark applications that play audio"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@option:check section General"
+msgid "Fill free space on panel"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@label:listbox"
+msgid "Maximum task width:"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@item:inlistbox how wide a task item should be"
+msgid "Narrow"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@item:inlistbox how wide a task item should be"
+msgid "Medium"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@item:inlistbox how wide a task item should be"
+msgid "Wide"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@label for radio button group, completes sentence: … when panel is low on space etc."
+msgid "Use multi-column view:"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@label for radio button group, completes sentence: … when panel is low on space etc."
+msgid "Use multi-row view:"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@option:radio Never use multi-column view for Task Manager"
+msgid "Never"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@option:radio completes sentence: Use multi-column/row view"
+msgid "When panel is low on space and thick enough"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@option:radio completes sentence: Use multi-column/row view"
+msgid "Always when panel is thick enough"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@label:spinbox maximum number of columns for tasks"
+msgid "Maximum columns:"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@label:spinbox maximum number of rows for tasks"
+msgid "Maximum rows:"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@label:listbox"
+msgid "Spacing between icons:"
+msgstr ""
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@item:inlistbox Icon spacing"
+msgid "Small"
+msgstr "小"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@item:inlistbox Icon spacing"
+msgid "Normal"
+msgstr "中"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@item:inlistbox Icon spacing"
+msgid "Large"
+msgstr "大"
+
+#: ../contents/ui/ConfigAppearance.qml
+msgctxt "@info:usagetip under a set of radio buttons when Touch Mode is on"
+msgid "Automatically set to Large when in Touch mode"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Display:"
+msgstr "显示:"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Show task names"
+msgstr "显示任务名称"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgid "Show icons only"
+msgstr "只显示图标"
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label:listbox how to group tasks"
+msgid "Group:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox how to group tasks"
+msgid "Do not group"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox how to group tasks"
+msgid "By program name"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label:listbox completes sentence like: … cycles through tasks"
+msgid "Clicking grouped task:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Completes the sentence 'Clicking grouped task cycles through tasks' "
+msgid "Cycles through tasks"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Completes the sentence 'Clicking grouped task shows small window previews' "
+msgid "Shows small window previews"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Completes the sentence 'Clicking grouped task shows large window previews' "
+msgid "Shows large window previews"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Completes the sentence 'Clicking grouped task shows textual list' "
+msgid "Shows textual list"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@info displayed as InlineMessage"
+msgid "The compositor does not support displaying windows side by side, so a textual list will be displayed instead."
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check grouped task"
+msgid "Combine into single button"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check grouped task"
+msgid "Group only when the Task Manager is full"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label:listbox sort tasks in grouped task"
+msgid "Sort:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox sort tasks in grouped task"
+msgid "Do not sort"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox sort tasks in grouped task"
+msgid "Manually"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox sort tasks in grouped task"
+msgid "Alphabetically"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox sort tasks in grouped task"
+msgid "By desktop"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox sort tasks in grouped task"
+msgid "By activity"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check configure task sorting"
+msgid "Keep launchers separate"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check for icons-and-text task manager"
+msgid "Hide launchers after application startup"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label for checkbox Part of a sentence: 'Clicking active task minimizes the task'"
+msgid "Clicking active task:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check Part of a sentence: 'Clicking active task minimizes the task'"
+msgid "Minimizes the task"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label:listbox completes sentence like: … does nothing"
+msgid "Middle-clicking any task:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Part of a sentence: 'Middle-clicking any task does nothing'"
+msgid "Does nothing"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Part of a sentence: 'Middle-clicking any task closes window or group'"
+msgid "Closes window or group"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Part of a sentence: 'Middle-clicking any task opens a new window'"
+msgid "Opens a new window"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Part of a sentence: 'Middle-clicking any task minimizes/restores window or group'"
+msgid "Minimizes/Restores window or group"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Part of a sentence: 'Middle-clicking any task toggles grouping'"
+msgid "Toggles grouping"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@item:inlistbox Part of a sentence: 'Middle-clicking any task brings it to the current virtual desktop'"
+msgid "Brings it to the current virtual desktop"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label for checkbox Part of a sentence: 'Mouse wheel cycles through tasks'"
+msgid "Mouse wheel:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check Part of a sentence: 'Mouse wheel cycles through tasks'"
+msgid "Cycles through tasks"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check mouse wheel task cycling"
+msgid "Skip minimized tasks"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label for checkbox group, completes sentence like: … from current screen"
+msgid "Show only tasks:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: show only tasks"
+msgid "From current screen"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: show only tasks"
+msgid "From current desktop"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: show only tasks"
+msgid "From current activity"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: show only tasks"
+msgid "That are minimized"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label for checkbox, completes sentence: … unhide if window wants attention"
+msgid "When panel is hidden:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: When panel is hidden"
+msgid "Unhide when a window wants attention"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@label for radiobutton group completes sentence like: … on the bottom"
+msgid "New tasks appear:"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: New tasks appear"
+msgid "On the bottom"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: New tasks appear"
+msgid "To the right"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: New tasks appear"
+msgid "To the left"
+msgstr ""
+
+#: ../contents/ui/ConfigBehavior.qml
+msgctxt "@option:check completes sentence: New tasks appear"
+msgid "On the top"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicators:"
+msgstr "指示器:"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Disabled"
+msgstr "关闭"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Enabled"
+msgstr "打开"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Display Progress on Indicator"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Progress Color:"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Disable for Inactive Windows"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Group Overlay:"
+msgstr "分组覆盖"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Takes effect on next time plasma groups tasks."
+msgstr "在下一次plasma组合任务时生效"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Animate Indicators:"
+msgstr "动态指示器"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator Location:"
+msgstr "指示器位置:"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Reverse shown side"
+msgstr "切换显示位置"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Override location"
+msgstr "覆盖位置"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Bottom"
+msgstr "底部"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Left"
+msgstr "左边"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Right"
+msgstr "右边"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Top"
+msgstr "顶部"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator Edge Offset (px):"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator Style:"
+msgstr "指示器风格"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Metro"
+msgstr "Metro"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Ciliora"
+msgstr "Ciliora"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Dashes"
+msgstr "Dashes"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator Min Limit:"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator Max Limit:"
+msgstr ""
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Minimize Options:"
+msgstr "最小化选项:"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Desaturate"
+msgstr "降低饱和度"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Shrink when minimized"
+msgstr "最小化时收缩"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Growth/Shrink factor:"
+msgstr "增长/收缩因数"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator size (px):"
+msgstr "指示器大小(px):"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator length (px):"
+msgstr "指示器长度(px):"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator Radius (%):"
+msgstr "指示器圆角(%):"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator margin (px):"
+msgstr "指示器间距(px)"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Indicator Color:"
+msgstr "指示器颜色:"
+
+#: ../contents/ui/ConfigIndicators.qml
+msgid "Use plasma accent color"
+msgstr "使用plasma预设"
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Add Application"
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Search applications..."
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Add"
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Default Web Browser"
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Default File Manager"
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Default Mail Client"
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Default Application"
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Add applications to pin to the taskbar. Drag items to reorder them."
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Remove"
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Add Application..."
+msgstr ""
+
+#: ../contents/ui/ConfigPinnedApps.qml
+msgid "Add Special Launcher..."
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Places"
+msgstr "位置"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Recent Files"
+msgstr "最近的文件"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Actions"
+msgstr "动作"
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "Play previous track"
+msgid "Previous Track"
+msgstr "上一首"
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "Pause playback"
+msgid "Pause"
+msgstr "暂停"
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "Start playback"
+msgid "Play"
+msgstr "播放"
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "Play next track"
+msgid "Next Track"
+msgstr "下一首"
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "Stop playback"
+msgid "Stop"
+msgstr "停止"
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "Quit media player app"
+msgid "Quit"
+msgstr "退出"
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "Open or bring to the front window of media player app"
+msgid "Restore"
+msgstr "恢复"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Mute"
+msgstr "静音"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Open New Window"
+msgstr "打开新窗口"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Move to &Desktop"
+msgstr "移动到桌面"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Move &To Current Desktop"
+msgstr "移动到当前桌面"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "&All Desktops"
+msgstr "所有桌面"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "&New Desktop"
+msgstr "新建桌面"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Show in &Activities"
+msgstr "在活动中展示"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Add To Current Activity"
+msgstr "添加到当前活动"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "All Activities"
+msgstr "所有活动"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Move to %1"
+msgstr "移动到%1"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "&Pin to Task Manager"
+msgstr "固定到任务栏"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "On All Activities"
+msgstr "在所有活动中"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "On The Current Activity"
+msgstr "在当前活动中"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Unpin from Task Manager"
+msgstr "从任务栏上解除固定"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "More"
+msgstr "更多"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "&Move"
+msgstr "移动"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Re&size"
+msgstr "调整大小"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Ma&ximize"
+msgstr "最大化"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Mi&nimize"
+msgstr "最小化"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Keep &Above Others"
+msgstr "置顶"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Keep &Below Others"
+msgstr "置底"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "&Fullscreen"
+msgstr "全屏"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "&Shade"
+msgstr "折叠"
+
+#: ../contents/ui/ContextMenu.qml
+msgid "Allow this program to be grouped"
+msgstr "允许这个程序被分组"
+
+#: ../contents/ui/ContextMenu.qml
+msgctxt "@item:inmenu"
+msgid "&Close All"
+msgstr ""
+
+#: ../contents/ui/ContextMenu.qml
+msgid "&Close"
+msgstr "关闭"
+
+#: ../contents/ui/Task.qml
+msgctxt "@info:usagetip %1 application name"
+msgid "Launch %1"
+msgstr ""
+
+#: ../contents/ui/Task.qml
+msgctxt "@info:tooltip"
+msgid "There is %1 new message."
+msgid_plural "There are %1 new messages."
+msgstr[0] ""
+msgstr[1] ""
+
+#: ../contents/ui/Task.qml
+msgctxt "@info:usagetip %1 task name"
+msgid "Show Task tooltip for %1"
+msgstr ""
+
+#: ../contents/ui/Task.qml
+msgctxt "@info:usagetip %1 task name"
+msgid "Show windows side by side for %1"
+msgstr ""
+
+#: ../contents/ui/Task.qml
+msgctxt "@info:usagetip %1 task name"
+msgid "Open textual list of windows for %1"
+msgstr ""
+
+#: ../contents/ui/Task.qml
+msgid "Activate %1"
+msgstr "活动 %1"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "button to unmute app"
+msgid "Unmute %1"
+msgstr "取消 %1 的静音"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "button to mute app"
+msgid "Mute %1"
+msgstr "静音 %1"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "Accessibility data on volume slider"
+msgid "Adjust volume for %1"
+msgstr "调整 %1 的音量"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "volume percentage"
+msgid "%1%"
+msgstr "%1%"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "only used for sizing, should be widest possible string"
+msgid "100%"
+msgstr "100%"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "Comma-separated list of desktops"
+msgid "On %1"
+msgstr "在 %1 上"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "Comma-separated list of desktops"
+msgid "Pinned to all desktops"
+msgstr "固定到所有桌面"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "Which virtual desktop a window is currently on"
+msgid "Available on all activities"
+msgstr "在所有活动上可用"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "Activities a window is currently on (apart from the current one)"
+msgid "Also available on %1"
+msgstr "也在 %1 上可用"
+
+#: ../contents/ui/ToolTipInstance.qml
+msgctxt "Which activities a window is currently on"
+msgid "Available on %1"
+msgstr "在 %1 上可用"
+
+#~ msgid "General:"
+#~ msgstr "通用:"
+
+#~ msgid "Show tooltips"
+#~ msgstr "显示窗口预览"
+
+#~ msgid "Highlight windows when hovering over task tooltips"
+#~ msgstr "悬浮在预览上时高亮对应窗口"
+
+#~ msgid "Mark applications that play audio"
+#~ msgstr "标记正在播放音频的应用"
+
+#~ msgid "Maximum columns:"
+#~ msgstr "最大列数"
+
+#~ msgid "Maximum rows:"
+#~ msgstr "最大行数"
+
+#~ msgid "Always arrange tasks in rows of as many columns"
+#~ msgstr "始终将任务排列成多列"
+
+#~ msgid "Always arrange tasks in columns of as many rows"
+#~ msgstr "始终将任务排列成多行"
+
+#~ msgid "Spacing between icons:"
+#~ msgstr "图标间距:"
+
+#~ msgctxt "@info:usagetip under a set of radio buttons when Touch Mode is on"
+#~ msgid "Automatically set to Large when in Touch Mode"
+#~ msgstr "触摸模式下自动设置为大"
+
+#~ msgid "Group:"
+#~ msgstr "分组:"
+
+#~ msgid "Do not group"
+#~ msgstr "不分组:"
+
+#~ msgid "By program name"
+#~ msgstr "以程序名分组"
+
+#~ msgid "Clicking grouped task:"
+#~ msgstr "点击已分组任务时:"
+
+#~ msgctxt "Completes the sentence 'Clicking grouped task cycles through tasks' "
+#~ msgid "Cycles through tasks"
+#~ msgstr "在任务中循环"
+
+#~ msgctxt "Completes the sentence 'Clicking grouped task shows tooltip window thumbnails' "
+#~ msgid "Shows tooltip window thumbnails"
+#~ msgstr "展示窗口预览"
+
+#~ msgctxt "Completes the sentence 'Clicking grouped task shows windows side by side' "
+#~ msgid "Shows windows side by side"
+#~ msgstr "并列显示窗口"
+
+#~ msgctxt "Completes the sentence 'Clicking grouped task shows textual list' "
+#~ msgid "Shows textual list"
+#~ msgstr "展示文字列表"
+
+#~ msgid "Tooltips are disabled, so the windows will be displayed side by side instead."
+#~ msgstr "窗口预览已关闭,所有窗口会被并列显示"
+
+#~ msgid "Tooltips are disabled, and the compositor does not support displaying windows side by side, so a textual list will be displayed instead"
+#~ msgstr "窗口预览已关闭,混成器不支持并列显示窗口,展示文字列表"
+
+#~ msgid "The compositor does not support displaying windows side by side, so a textual list will be displayed instead."
+#~ msgstr "混成器不支持并列显示窗口,展示文字列表"
+
+#~ msgid "Combine into single button"
+#~ msgstr "组合到一个按钮中"
+
+#~ msgid "Group only when the Task Manager is full"
+#~ msgstr "仅在任务栏已满时组合"
+
+#~ msgid "Sort:"
+#~ msgstr "排序:"
+
+#~ msgid "Do not sort"
+#~ msgstr "不排序"
+
+#~ msgid "Manually"
+#~ msgstr "手动"
+
+#~ msgid "Alphabetically"
+#~ msgstr "按字母"
+
+#~ msgid "By desktop"
+#~ msgstr "按桌面"
+
+#~ msgid "By activity"
+#~ msgstr "按活动"
+
+#~ msgid "Keep launchers separate"
+#~ msgstr "与启动器分开"
+
+#~ msgctxt "Part of a sentence: 'Clicking active task minimizes the task'"
+#~ msgid "Clicking active task:"
+#~ msgstr "点击活动任务时:"
+
+#~ msgctxt "Part of a sentence: 'Clicking active task minimizes the task'"
+#~ msgid "Minimizes the task"
+#~ msgstr "最小化"
+
+#~ msgid "Middle-clicking any task:"
+#~ msgstr "鼠标中键点击活动任务时:"
+
+#~ msgctxt "Part of a sentence: 'Middle-clicking any task does nothing'"
+#~ msgid "Does nothing"
+#~ msgstr "什么都不做"
+
+#~ msgctxt "Part of a sentence: 'Middle-clicking any task closes window or group'"
+#~ msgid "Closes window or group"
+#~ msgstr "关闭窗口或组"
+
+#~ msgctxt "Part of a sentence: 'Middle-clicking any task opens a new window'"
+#~ msgid "Opens a new window"
+#~ msgstr "打开新窗口"
+
+#~ msgctxt "Part of a sentence: 'Middle-clicking any task minimizes/restores window or group'"
+#~ msgid "Minimizes/Restores window or group"
+#~ msgstr "最小化/最大化窗口或组"
+
+#~ msgctxt "Part of a sentence: 'Middle-clicking any task toggles grouping'"
+#~ msgid "Toggles grouping"
+#~ msgstr "切换组"
+
+#~ msgctxt "Part of a sentence: 'Middle-clicking any task brings it to the current virtual desktop'"
+#~ msgid "Brings it to the current virtual desktop"
+#~ msgstr "带到当前虚拟桌面"
+
+#~ msgctxt "Part of a sentence: 'Mouse wheel cycles through tasks'"
+#~ msgid "Mouse wheel:"
+#~ msgstr "鼠标滚轮:"
+
+#~ msgctxt "Part of a sentence: 'Mouse wheel cycles through tasks'"
+#~ msgid "Cycles through tasks"
+#~ msgstr "在任务间切换"
+
+#~ msgid "Skip minimized tasks"
+#~ msgstr "跳过最小化的任务"
+
+#~ msgid "Show only tasks:"
+#~ msgstr "只展示任务"
+
+#~ msgid "From current screen"
+#~ msgstr "从当前屏幕"
+
+#~ msgid "From current desktop"
+#~ msgstr "从当前桌面"
+
+#~ msgid "From current activity"
+#~ msgstr "从当前活动"
+
+#~ msgid "That are minimized"
+#~ msgstr "最小化的任务"
+
+#~ msgid "When panel is hidden:"
+#~ msgstr "面板隐藏时:"
+
+#~ msgid "Unhide when a window wants attention"
+#~ msgstr "当窗口提示时取消隐藏"
+
+#~ msgid "New tasks appear:"
+#~ msgstr "新任务出现在:"
+
+#~ msgid "To the right"
+#~ msgstr "右边"
+
+#~ msgid "To the left"
+#~ msgstr "左边"
+
+#~ msgctxt "1 = number of desktop, 2 = desktop name"
+#~ msgid "&%1 %2"
+#~ msgstr "&%1 %2"
+
+#~ msgid "Press OK to fix layout issues."
+#~ msgstr "按OK键来修复布局问题。"
+
+#~ msgid "Disable"
+#~ msgstr "关闭"
+
+#~ msgid "Colorize buttons"
+#~ msgstr "彩色按钮"
+
+#~ msgid "Indicator Limit:"
+#~ msgstr "指示器限制:"
diff --git a/testing.sh b/testing.sh
new file mode 100755
index 00000000..340b2ff2
--- /dev/null
+++ b/testing.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
+sh $SCRIPT_DIR/build.sh
+kpackagetool6 -u $SCRIPT_DIR/release/FancyTasks.tar.gz
+QML_DISABLE_DISK_CACHE=true plasmawindowed alexankitty.fancytasks
\ No newline at end of file
diff --git a/update.sh b/update.sh
new file mode 100755
index 00000000..b4c5bbbd
--- /dev/null
+++ b/update.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+SCRIPT_DIR=$(dirname $(readlink -f "$0"))
+
+echo "Compiling translations..."
+bash "$SCRIPT_DIR/package/translate/build"
+
+echo "Updating plasmoid ..."
+kpackagetool6 -t Plasma/Applet --upgrade "$SCRIPT_DIR/package"
+
+echo "Restarting Plasma..."
+if systemctl --user is-active --quiet plasma-plasmashell.service; then
+ systemctl --user restart plasma-plasmashell.service
+else
+ plasmashell --replace > /dev/null 2>&1 &
+ disown
+fi
+
+bash "$SCRIPT_DIR/iconinstall.sh"
+echo "Update complete."