commit f9ec1fa5b37356c75758e8b65726df03df21c285 Author: Michail Vourlakos <mvourlakos@gmail.com> Date: Sun Dec 25 09:25:27 2016 +0200 initial commit for Latte !!! -initial commit based on the latest Now Dock from the corona branch and beautiful techniques and designs from the Candil Dock diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..3d3e4d19b --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +*~ +.* +!.gitignore +build +corona/build +*.kdev4 + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..120e5ebba --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,86 @@ +project(nowdock) +cmake_minimum_required(VERSION 3.0 FATAL_ERROR) + +set (CMAKE_CXX_STANDARD 11) +set(VERSION 0.5.88) +set(AUTHOR "Michail Vourlakos") +set(EMAIL "mvourlakos@gmail.com") +set(WEBSITE "https://store.kde.org/p/1154578/") + +set(QT_MIN_VERSION "5.6.0") +set(KF5_MIN_VERSION "5.26.0") + +find_package(ECM 1.8.0 REQUIRED NO_MODULE) +set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) + +find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS + Quick QuickWidgets) + +find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS + Plasma PlasmaQuick WindowSystem Declarative + I18n CoreAddons XmlGui DBusAddons Notifications) + +FIND_PROGRAM(GETTEXT_MSGFMT_EXECUTABLE msgfmt) + +IF(NOT GETTEXT_MSGFMT_EXECUTABLE) + MESSAGE( +"------ + NOTE: msgfmt not found. Translations will *not* be installed +------") +ELSE(NOT GETTEXT_MSGFMT_EXECUTABLE) + + SET(catalogname plasma_applet_org.kde.nowdock.containment) + + ADD_CUSTOM_TARGET(translations-containment ALL) + + FILE(GLOB PO_FILES po/containment/*.po) + + FOREACH(_poFile ${PO_FILES}) + GET_FILENAME_COMPONENT(_poFileName ${_poFile} NAME) + STRING(REGEX REPLACE "^${catalogname}_?" "" _langCode ${_poFileName} ) + STRING(REGEX REPLACE "\\.po$" "" _langCode ${_langCode} ) + + IF( _langCode ) + GET_FILENAME_COMPONENT(_lang ${_poFile} NAME_WE) + SET(_gmoFile ${CMAKE_CURRENT_BINARY_DIR}/containment/${_lang}.gmo) + + ADD_CUSTOM_COMMAND(TARGET translations-containment + COMMAND ${GETTEXT_MSGFMT_EXECUTABLE} --check -o ${_gmoFile} ${_poFile} + DEPENDS ${_poFile}) + INSTALL(FILES ${_gmoFile} DESTINATION ${LOCALE_INSTALL_DIR}/${_langCode}/LC_MESSAGES/ RENAME ${catalogname}.mo) + ENDIF( _langCode ) + ENDFOREACH(_poFile ${PO_FILES}) + + SET(catalogname2 plasma_applet_org.kde.store.nowdock.plasmoid) + ADD_CUSTOM_TARGET(translations-plasmoid ALL) + + FILE(GLOB PO_FILES2 po/plasmoid/*.po) + + FOREACH(_poFile ${PO_FILES2}) + GET_FILENAME_COMPONENT(_poFileName ${_poFile} NAME) + STRING(REGEX REPLACE "^${catalogname2}_?" "" _langCode ${_poFileName} ) + STRING(REGEX REPLACE "\\.po$" "" _langCode ${_langCode} ) + + IF( _langCode ) + GET_FILENAME_COMPONENT(_lang ${_poFile} NAME_WE) + SET(_gmoFile ${CMAKE_CURRENT_BINARY_DIR}/plasmoid/${_lang}.gmo) + + ADD_CUSTOM_COMMAND(TARGET translations-plasmoid + COMMAND ${GETTEXT_MSGFMT_EXECUTABLE} --check -o ${_gmoFile} ${_poFile} + DEPENDS ${_poFile}) + INSTALL(FILES ${_gmoFile} DESTINATION ${LOCALE_INSTALL_DIR}/${_langCode}/LC_MESSAGES/ RENAME ${catalogname2}.mo) + ENDIF( _langCode ) + ENDFOREACH(_poFile ${PO_FILES}) + +ENDIF(NOT GETTEXT_MSGFMT_EXECUTABLE) + +add_subdirectory(libnowdock) +add_subdirectory(containment) +plasma_install_package(build/containment/release org.kde.nowdock.containment) +add_subdirectory(plasmoid) +plasma_install_package(build/plasmoid/release org.kde.store.nowdock.plasmoid) + +plasma_install_package(shell org.kde.nowdock.shell shells shell) + +add_subdirectory(corona) + diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..9cecc1d46 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + 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. + + {one line to give the program's name and a brief idea of what it does.} + Copyright (C) {year} {name of author} + + 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 <http://www.gnu.org/licenses/>. + +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: + + {project} Copyright (C) {year} {fullname} + 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 +<http://www.gnu.org/licenses/>. + + 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 +<http://www.gnu.org/philosophy/why-not-lgpl.html>. diff --git a/README.md b/README.md new file mode 100644 index 000000000..d076f5d7d --- /dev/null +++ b/README.md @@ -0,0 +1,43 @@ +About +===== +This is a panel and plasmoid for Plasma 5 which is trying to implement a +mac style effect for its applets. It uses the Now Dock plasmoid +for its configuration and complements it as one single area +for which the user can add its applets. The applets as the user +windows are going to create a nice zoom effect on hovering. + +Installation +============ + +- **Notice:** First uninstall any locally installed now dock components by using the **uninstall-local.sh** script + +####installation script#### +- _sh install-global.sh_ + +Now Dock Panel is now ready to be used from right-click menu in the Desktop + +Translators +============ +For translations you can use the **po/nowdockpanel/plasma_applet_org.kde.store.nowdock.panel.pot**, **po/nowdockplasmoid/plasma_applet_org.kde.store.nowdock.plasmoid.pot** files and either make a **Pull Request** for your language or upload the your language file at https://github.com/psifidotos/nowdock-panel/issues/17 + + +Requirements +========== +* Plasma >= 5.8.0 + +**development packages for:** + +* Qt5Core >= 5.6.0 +* Qt5Qml >= 5.6.0 +* Qt5Quick >= 5.6.0 + +* KF5Plasma >= 5.25.0 +* KF5PlasmaQuick >= 5.25.0 +* KF5WindowSystem >= 5.25.0 +* KF5KDELibs4Support >= 5.25.0 +* KF5CoreAddons >= 5.25.0 + + + + + diff --git a/astylerc b/astylerc new file mode 100644 index 000000000..16a713ecd --- /dev/null +++ b/astylerc @@ -0,0 +1,40 @@ +#BracketStyleOptions + --style=stroustrup + --keep-one-line-blocks + --keep-one-line-statements + +#TabOptions +# --indent=force-tab=4 + --indent=spaces=4 + +#BracketModifyOptions + --attach-namespaces + --attach-inlines + +#FormattingOptions + --convert-tabs + --max-code-length=80 + --keep-one-line-blocks + --keep-one-line-statements + --close-templates + --max-code-length=200 + +#IndentationOptions + --indent-switches + --indent-preproc-block + --indent-preproc-define + --min-conditional-indent=1 + --max-instatement-indent=40 + +#PaddingOptions + --break-blocks + --pad-oper + --unpad-paren + --pad-header + --fill-empty-lines + --align-pointer=name + --align-reference=name + +#Others + --preserve-date + --verbose diff --git a/containment.metadata.desktop.template b/containment.metadata.desktop.template new file mode 100644 index 000000000..800ae2bdb --- /dev/null +++ b/containment.metadata.desktop.template @@ -0,0 +1,22 @@ +[Desktop Entry] +Encoding=UTF-8 +_Name=Now Dock +_Comment=A dock for Plasma that tries to animate its plasmoids on hovering + +Type=Service +Keywords= +NoDisplay=true + +X-KDE-PluginInfo-Author=@AUTHOR@ +X-KDE-PluginInfo-Email=@EMAIL@ +X-KDE-PluginInfo-Name=org.kde.nowdock.containment +X-KDE-PluginInfo-Version=@VERSION@ +X-KDE-PluginInfo-Website=@WEBSITE@ +X-KDE-PluginInfo-Category= +X-KDE-PluginInfo-Depends= +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-EnabledByDefault=true +X-KDE-ServiceTypes=Plasma/Applet,Plasma/Containment +X-Plasma-API=declarativeappletscript +X-Plasma-MainScript=ui/main.qml +X-Plasma-ContainmentType=Panel diff --git a/containment/CMakeLists.txt b/containment/CMakeLists.txt new file mode 100644 index 000000000..f650adcaa --- /dev/null +++ b/containment/CMakeLists.txt @@ -0,0 +1,3 @@ +file(COPY "contents" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/release) +#update the version number in the configuration window +configure_file(metadata.desktop.cmake release/metadata.desktop) diff --git a/containment/contents/code/LayoutManager.js b/containment/contents/code/LayoutManager.js new file mode 100644 index 000000000..98c9677c6 --- /dev/null +++ b/containment/contents/code/LayoutManager.js @@ -0,0 +1,273 @@ +/* + * Copyright 2013 Michail Vourlakos <mvourlakos@gmail.com> + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + + +var layout; +var root; +var plasmoid; +var lastSpacer; + + +function restore() { + var configString = String(plasmoid.configuration.appletOrder) + + //array, a cell for encoded item order + var itemsArray = configString.split(";"); + + //map applet id->order in panel + var idsOrder = new Object(); + //map order in panel -> applet pointer + var appletsOrder = new Object(); + + for (var i = 0; i < itemsArray.length; i++) { + //property name: applet id + //property value: order + idsOrder[itemsArray[i]] = i; + } + + for (var i = 0; i < plasmoid.applets.length; ++i) { + if (idsOrder[plasmoid.applets[i].id] !== undefined) { + appletsOrder[idsOrder[plasmoid.applets[i].id]] = plasmoid.applets[i]; + //ones that weren't saved in AppletOrder go to the end + } else { + appletsOrder["unordered"+i] = plasmoid.applets[i]; + } + } + + //finally, restore the applets in the correct order + for (var i in appletsOrder) { + root.addApplet(appletsOrder[i], -1, -1) + } + + //add the splitter in the correct position if it exists + if(plasmoid.configuration.splitterPosition !== -1){ + root.addInternalViewSplitter(plasmoid.configuration.splitterPosition); + } + + //rewrite, so if in the orders there were now invalid ids or if some were missing creates a correct list instead + save(); + restoreLocks(); + + //update layouts in case there is a splitter in them + root.updateLayouts(); +} + +function restoreLocks() { + var configString = String(plasmoid.configuration.lockedZoomApplets) + //array, a cell for encoded item order + var itemsArray = configString.split(";"); + + for (var i = 0; i < itemsArray.length; i++) { + for (var j = 0; j < layout.children.length; ++j) { + var child = layout.children[j]; + + if (child.applet && (child.applet.id == itemsArray[i])) { + child.lockZoom = true; + } + } + } +} + +function save() { + var ids = new Array(); + var splitterExists = false; + for (var i = 0; i < layout.children.length; ++i) { + var child = layout.children[i]; + + if (child.applet) { + ids.push(child.applet.id); + } + else if(child.isInternalViewSplitter && plasmoid.configuration.panelPosition === 10){ + splitterExists = true; + plasmoid.configuration.splitterPosition = i; + } + } + + if(!splitterExists) + plasmoid.configuration.splitterPosition = -1; + + plasmoid.configuration.appletOrder = ids.join(';'); +} + +function saveLocks() { + var ids = new Array(); + for (var i = 0; i < layout.children.length; ++i) { + var child = layout.children[i]; + + if (child.applet && child.lockZoom) { + ids.push(child.applet.id); + } + } + plasmoid.configuration.lockedZoomApplets = ids.join(';'); +} + + +function removeApplet (applet) { + for (var i = layout.children.length - 1; i >= 0; --i) { + var child = layout.children[i]; + if (child.applet === applet) { + child.destroy(); + } + } +} + +//insert item2 before item1 +function insertBefore(item1, item2) { + if (item1 === item2) { + return; + } + var removed = new Array(); + + var child; + + var i; + for (i = layout.children.length - 1; i >= 0; --i) { + child = layout.children[i]; + removed.push(child); + child.parent = root; + + if (child === item1) { + break; + } + } + + item2.parent = layout; + + for (var j = removed.length - 1; j >= 0; --j) { + removed[j].parent = layout; + } + return i; +} + +//insert item2 after item1 +function insertAfter(item1, item2) { + if (item1 === item2) { + return; + } + var removed = new Array(); + + var child; + + var i; + for (i = layout.children.length - 1; i >= 0; --i) { + child = layout.children[i]; + //never ever insert after lastSpacer + if (child === lastSpacer && item1 === lastSpacer) { + removed.push(child); + child.parent = root; + break; + } else if (child === item1) { + //Already in position, do nothing + if (layout.children[i+1] === item2) { + return; + } + break; + } + + removed.push(child); + child.parent = root; + } + + item2.parent = layout; + + for (var j = removed.length - 1; j >= 0; --j) { + removed[j].parent = layout; + } + return i; +} + +function insertAtIndex(item, position) { + if (position < 0 || (position >= layout.children.length && position !== 0)) { + return; + } + + //never ever insert after lastSpacer + var firstItem = (layout.children.length === 1) && (layout.children[0] === lastSpacer); + + //Important !!! , this is used to add the first item + if(firstItem){ + lastSpacer.parent = root; + position = 0; + } + + if(layout.children.length > 0){ + if (layout.children[position] === lastSpacer) { + --position; + } + } + + var removedItems = new Array(); + + var totalChildren = layout.children.length; + for (var i = position; i < totalChildren; ++i) { + var child = layout.children[position]; + child.parent = root; + removedItems.push(child); + } + + item.parent = layout; + for (var i in removedItems) { + removedItems[i].parent = layout; + } +} + +function insertAtCoordinates(item, x, y) { + if (root.isHorizontal) { + y = layout.height / 2; + } else { + x = layout.width / 2; + } + var child = layout.childAt(x, y); + + //if we got a place inside the space between 2 applets, we have to find it manually + if (!child) { + if (root.isHorizontal) { + for (var i = 0; i < layout.children.length; ++i) { + var candidate = layout.children[i]; + if (x >= candidate.x && x < candidate.x + candidate.width + layout.rowSpacing) { + child = candidate; + break; + } + } + } else { + for (var i = 0; i < layout.children.length; ++i) { + var candidate = layout.children[i]; + if (y >= candidate.x && y < candidate.y + candidate.height + layout.columnSpacing) { + child = candidate; + break; + } + } + } + } + //already in position + if (child === item) { + return; + } + if (!child) { + child = layout.children[0]; + } + item.parent = root; + + //PlasmaCore.Types.Vertical = 3 + if ((plasmoid.formFactor === 3 && y < child.y + child.height/2) || + (plasmoid.formFactor !== 3 && x < child.x + child.width/2)) { + return insertBefore(child, item); + } else { + return insertAfter(child, item); + } +} diff --git a/containment/contents/config/main.xml b/containment/contents/config/main.xml new file mode 100644 index 000000000..777946ebc --- /dev/null +++ b/containment/contents/config/main.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="UTF-8"?> +<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0 + http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" > + <kcfgfile name=""/> + + <group name="General"> + <entry name="appletOrder" type="String"> + <label>encoded order of items</label> + </entry> + <entry name="lockedZoomApplets" type="String"> + <label>applets that lock the zoom effect</label> + </entry> + <entry name="panelPosition" type="Enum"> + <choices> + <choice name="Center"/> + <choice name="Left"/> + <choice name="Right"/> + <choice name="Top"/> + <choice name="Bottom"/> + </choices> + <default>0</default> + </entry> + <entry name="panelVisibility" type="Enum"> + <choices> + <choice name="BelowActive"/> + <choice name="BelowMaximized"/> + <choice name="LetWindowsCover"/> + <choice name="WindowsGoBelow"/> + <choice name="AutoHide"/> + <choice name="AlwaysVisible"/> + </choices> + <default>0</default> + </entry> + <entry name="zoomLevel" type="Int"> + <default>10</default> + </entry> + <entry name="iconSize" type="Int"> + <default>64</default> + </entry> + <entry name="useThemePanel" type="Bool"> + <default>false</default> + </entry> + <entry name="panelSize" type="Int"> + <default>58</default> + </entry> + <entry name="automaticIconSize" type="Bool"> + <default>false</default> + </entry> + <entry name="smallAutomaticIconJumps" type="Bool"> + <default>true</default> + </entry> + <entry name="splitterPosition" type="Int"> + <default>-1</default> + </entry> + <entry name="shadows" type="Enum"> + <label>Shadows for the applets</label> + <choices> + <choice name="None"/> + <choice name="Locked"/> + <choice name="All"/> + </choices> + <default>1</default> + </entry> + </group> +</kcfg> diff --git a/containment/contents/icons/splitter.png b/containment/contents/icons/splitter.png new file mode 100644 index 000000000..0cc10e291 Binary files /dev/null and b/containment/contents/icons/splitter.png differ diff --git a/containment/contents/images/panel-west.png b/containment/contents/images/panel-west.png new file mode 100644 index 000000000..d215e858a Binary files /dev/null and b/containment/contents/images/panel-west.png differ diff --git a/containment/contents/ui/AddWidgetVisual.qml b/containment/contents/ui/AddWidgetVisual.qml new file mode 100644 index 000000000..a52243d58 --- /dev/null +++ b/containment/contents/ui/AddWidgetVisual.qml @@ -0,0 +1,40 @@ +/* + * Copyright 2013 Michail Vourlakos <mvourlakos@gmail.com> + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.1 + +Item{ + id: newDroppedLauncherVisual + anchors.fill: parent + + Rectangle{ + anchors.fill: parent + + radius: root.iconSize/10 + + property color tempColor: "#aa222222" + color: tempColor + border.width: 1 + border.color: "#ff656565" + + property int crossSize: Math.min(parent.width/2, parent.height/2) + + Rectangle{width: parent.crossSize; height: 4; radius:2; anchors.centerIn: parent; color: theme.highlightColor} + Rectangle{width: 4; height: parent.crossSize; radius:2; anchors.centerIn: parent; color: theme.highlightColor} + } +} diff --git a/containment/contents/ui/AppletItem.qml b/containment/contents/ui/AppletItem.qml new file mode 100644 index 000000000..c1e722bec --- /dev/null +++ b/containment/contents/ui/AppletItem.qml @@ -0,0 +1,870 @@ +/* + * Copyright 2013 Michail Vourlakos <mvourlakos@gmail.com> + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ +import QtQuick 2.1 +import QtQuick.Layouts 1.1 +import QtGraphicalEffects 1.0 + +import org.kde.plasma.plasmoid 2.0 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.kquickcontrolsaddons 2.0 + +Item { + id: container + + visible: false + width: root.isHorizontal ? computeWidth : computeWidth + shownAppletMargin + height: root.isVertical ? computeHeight : computeHeight + shownAppletMargin + + property bool animationsEnabled: true + property bool animationWasSent: false //protection flag for animation broadcasting + property bool canBeHovered: true + property bool showZoomed: false + property bool lockZoom: false + property bool isInternalViewSplitter: false + property bool isZoomed: false + + property int animationTime: root.durationTime* (1.2 *units.shortDuration) // 70 + property int hoveredIndex: layoutsContainer.hoveredIndex + property int index: -1 + property int appletMargin: (applet && (applet.pluginName === "org.kde.store.nowdock.plasmoid")) + || isInternalViewSplitter + || root.reverseLinesPosition ? 0 : root.statesLineSize + property int maxWidth: root.isHorizontal ? root.height : root.width + property int maxHeight: root.isHorizontal ? root.height : root.width + property int shownAppletMargin: applet && (applet.pluginName === "org.kde.plasma.systemtray") ? 0 : appletMargin + property int status: applet ? applet.status : -1 + + //property real animationStep: root.iconSize / 8 + property real animationStep: 6 + property real computeWidth: root.isVertical ? wrapper.width : + hiddenSpacerLeft.width+wrapper.width+hiddenSpacerRight.width + + property real computeHeight: root.isVertical ? hiddenSpacerLeft.height + wrapper.height + hiddenSpacerRight.height : + wrapper.height + + property string title: isInternalViewSplitter ? "Now Dock Splitter" : "" + + property Item applet + property Item nowDock: applet && (applet.pluginName === "org.kde.store.nowdock.plasmoid") ? + (applet.children[0] ? applet.children[0] : null) : null + property Item appletWrapper: applet && + ((applet.pluginName === "org.kde.store.nowdock.plasmoid") || + (applet.pluginName === "org.kde.plasma.systemtray")) ? wrapper : wrapperContainer + + property alias containsMouse: appletMouseArea.containsMouse + property alias pressed: appletMouseArea.pressed + + + /*onComputeHeightChanged: { + if(index==0) + console.log(computeHeight); + }*/ + + /// BEGIN functions + function checkIndex(){ + index = -1; + + for(var i=0; i<mainLayout.count; ++i){ + if(mainLayout.children[i] == container){ + index = i; + break; + } + } + + for(var i=0; i<secondLayout.count; ++i){ + if(secondLayout.children[i] == container){ + //create a very high index in order to not need to exchange hovering messages + //between mainLayout and secondLayout + index = secondLayout.beginIndex + i; + break; + } + } + + + if(container.nowDock){ + if(index===0 || index===secondLayout.beginIndex) + nowDock.disableLeftSpacer = false; + else + nowDock.disableLeftSpacer = true; + + if(index===mainLayout.count-1 || index === secondLayout.beginIndex + secondLayout.count - 1) + nowDock.disableRightSpacer = false; + else + nowDock.disableRightSpacer = true; + } + } + + //this functions gets the signal from the plasmoid, it can be used for signal items + //outside the NowDock Plasmoid + //property int debCounter: 0; + function interceptNowDockUpdateScale(dIndex, newScale, step){ + if(plasmoid.immutable){ + if(dIndex === -1){ + layoutsContainer.updateScale(index-1,newScale, step); + } + else if(dIndex === root.tasksCount){ + // debCounter++; + // console.log(debCounter+ " "+dIndex+" "+newScale+" received..."); + layoutsContainer.updateScale(index+1,newScale, step); + } + } + } + + function clearZoom(){ + if(wrapper) + wrapper.zoomScale = 1; + } + + function checkCanBeHovered(){ + if ( ((applet && (applet.Layout.minimumWidth > root.iconSize) && root.isHorizontal) || + (applet && (applet.Layout.minimumHeight > root.iconSize) && root.isVertical)) + && (applet && applet.pluginName !== "org.kde.plasma.panelspacer") ){ + canBeHovered = false; + } + else{ + canBeHovered = true; + } + } + + ///END functions + + //BEGIN connections + onAppletChanged: { + if (!applet) { + destroy(); + } + } + + onHoveredIndexChanged:{ + if ( (Math.abs(hoveredIndex-index) > 1)||(hoveredIndex == -1) ) + wrapper.zoomScale = 1; + } + + onNowDockChanged: { + if(container.nowDock){ + root.nowDock = container.nowDock; + root.nowDockContainer = container; + nowDock.nowDockPanel = root; + nowDock.forceHidePanel = true; + nowDock.updateScale.connect(interceptNowDockUpdateScale); + } + } + + onShowZoomedChanged: { + if(showZoomed){ + var newZ = container.maxHeight / root.iconSize; + wrapper.zoomScale = newZ; + } + else{ + wrapper.zoomScale = 1; + } + } + + Component.onCompleted: { + checkIndex(); + root.updateIndexes.connect(checkIndex); + root.clearZoomSignal.connect(clearZoom); + } + + Component.onDestruction: { + root.updateIndexes.disconnect(checkIndex); + root.clearZoomSignal.disconnect(clearZoom); + } + + ///END connections + + + PlasmaComponents.BusyIndicator { + z: 1000 + visible: applet && applet.busy + running: visible + anchors.centerIn: parent + width: Math.min(parent.width, parent.height) + height: width + } + + /* Rectangle{ + anchors.fill: parent + color: "transparent" + border.color: "green" + border.width: 1 + } */ + + Flow{ + id: appletFlow + width: container.computeWidth + height: container.computeHeight + + anchors.rightMargin: (nowDock || (showZoomed && !plasmoid.immutable)) || + (plasmoid.location !== PlasmaCore.Types.RightEdge) ? 0 : shownAppletMargin + anchors.leftMargin: (nowDock || (showZoomed && !plasmoid.immutable)) || + (plasmoid.location !== PlasmaCore.Types.LeftEdge) ? 0 : shownAppletMargin + anchors.topMargin: (nowDock || (showZoomed && !plasmoid.immutable)) || + (plasmoid.location !== PlasmaCore.Types.TopEdge)? 0 : shownAppletMargin + anchors.bottomMargin: (nowDock || (showZoomed && !plasmoid.immutable)) || + (plasmoid.location !== PlasmaCore.Types.BottomEdge) ? 0 : shownAppletMargin + + + // a hidden spacer for the first element to add stability + // IMPORTANT: hidden spacers must be tested on vertical !!! + Item{ + id: hiddenSpacerLeft + //we add one missing pixel from calculations + width: root.isHorizontal ? nHiddenSize : wrapper.width + height: root.isHorizontal ? wrapper.height : nHiddenSize + + ///check also if this is the first plasmoid in secondLayout + visible: (container.index === 0) || (container.index === secondLayout.beginIndex) + + property real nHiddenSize: (nScale > 0) ? (root.realSize * nScale) : 0 + property real nScale: 0 + + Behavior on nScale { + NumberAnimation { duration: container.animationTime } + } + + /* Rectangle{ + width: 1 + height: parent.height + x: parent.width/2 + border.width: 1 + border.color: "red" + color: "transparent" + } */ + } + + + Item{ + id: wrapper + + width: Math.round( nowDock ? ((container.showZoomed && root.isVertical) ? + scaledWidth : nowDock.tasksWidth) : scaledWidth ) + height: Math.round( nowDock ? ((container.showZoomed && root.isHorizontal) ? + scaledHeight : nowDock.tasksHeight ): scaledHeight ) + + property bool disableScaleWidth: false + property bool disableScaleHeight: false + property bool immutable: plasmoid.immutable + + property int appletMinimumWidth: applet && applet.Layout ? applet.Layout.minimumWidth : -1 + property int appletMinimumHeight: applet && applet.Layout ? applet.Layout.minimumHeight : -1 + + property int appletPreferredWidth: applet && applet.Layout ? applet.Layout.preferredWidth : -1 + property int appletPreferredHeight: applet && applet.Layout ? applet.Layout.preferredHeight : -1 + + property int appletMaximumWidth: applet && applet.Layout ? applet.Layout.maximumWidth : -1 + property int appletMaximumHeight: applet && applet.Layout ? applet.Layout.maximumHeight : -1 + + property int iconSize: root.iconSize + + property real scaledWidth: zoomScaleWidth * (layoutWidth + root.iconMargin) + property real scaledHeight: zoomScaleHeight * (layoutHeight + root.iconMargin) + property real zoomScaleWidth: disableScaleWidth ? 1 : zoomScale + property real zoomScaleHeight: disableScaleHeight ? 1 : zoomScale + + property int layoutWidthResult: 0 + + property int layoutWidth + property int layoutHeight + + // property int localMoreSpace: root.reverseLinesPosition ? root.statesLineSize + 2 : appletMargin + property int localMoreSpace: appletMargin + + property int moreHeight: ((applet && (applet.pluginName === "org.kde.plasma.systemtray")) || root.reverseLinesPosition) + && root.isHorizontal ? localMoreSpace : 0 + property int moreWidth: ((applet && (applet.pluginName === "org.kde.plasma.systemtray")) || root.reverseLinesPosition) + && root.isVertical ? localMoreSpace : 0 + + property real center: width / 2 + property real zoomScale: 1 + + property alias index: container.index + // property int pHeight: applet ? applet.Layout.preferredHeight : -10 + + + /*function debugLayouts(){ + if(applet){ + console.log("---------- "+ applet.pluginName +" ----------"); + console.log("MinW "+applet.Layout.minimumWidth); + console.log("PW "+applet.Layout.preferredWidth); + console.log("MaxW "+applet.Layout.maximumWidth); + console.log("FillW "+applet.Layout.fillWidth); + console.log("-----"); + console.log("MinH "+applet.Layout.minimumHeight); + console.log("PH "+applet.Layout.preferredHeight); + console.log("MaxH "+applet.Layout.maximumHeight); + console.log("FillH "+applet.Layout.fillHeight); + console.log("-----"); + console.log("LayoutW: " + layoutWidth); + console.log("LayoutH: " + layoutHeight); + } + } + + onLayoutWidthChanged: { + debugLayouts(); + } + + onLayoutHeightChanged: { + debugLayouts(); + }*/ + + onAppletMinimumWidthChanged: { + if(zoomScale == 1) + checkCanBeHovered(); + + updateLayoutWidth(); + } + + onAppletMinimumHeightChanged: { + if(zoomScale == 1) + checkCanBeHovered(); + + updateLayoutHeight(); + } + + onAppletPreferredWidthChanged: updateLayoutWidth(); + onAppletPreferredHeightChanged: updateLayoutHeight(); + + onAppletMaximumWidthChanged: updateLayoutWidth(); + onAppletMaximumHeightChanged: updateLayoutHeight(); + + onIconSizeChanged: { + updateLayoutWidth(); + updateLayoutHeight(); + } + + onImmutableChanged: { + updateLayoutWidth(); + updateLayoutHeight(); + } + + onZoomScaleChanged: { + if ((zoomScale > 1) && !container.isZoomed) { + container.isZoomed = true; + if (plasmoid.immutable && !animationWasSent) { + root.appletsAnimations++; + animationWasSent = true; + } + } else if ((zoomScale == 1) && container.isZoomed) { + container.isZoomed = false; + if (plasmoid.immutable && animationWasSent) { + root.appletsAnimations--; + animationWasSent = false; + } + } + } + + function updateLayoutHeight(){ + if(container.isInternalViewSplitter){ + if(plasmoid.immutable) + layoutHeight = 0; + else + layoutHeight = root.iconSize;// + moreHeight + root.statesLineSize; + } + else if(applet && applet.pluginName === "org.kde.plasma.panelspacer"){ + layoutHeight = root.iconSize + moreHeight; + } + else if(applet && applet.pluginName === "org.kde.plasma.systemtray" && root.isHorizontal){ + layoutHeight = root.iconSize+root.iconMargin+root.statesLineSize/2; + } + else{ + if(applet && (applet.Layout.minimumHeight > root.iconSize) && root.isVertical && (!canBeHovered)){ + // return applet.Layout.minimumHeight; + layoutHeight = applet.Layout.minimumHeight; + } //it is used for plasmoids that need to scale only one axis... e.g. the Weather Plasmoid + else if(applet + && ( (applet.Layout.maximumHeight < root.iconSize) || (applet.Layout.preferredHeight > root.iconSize)) + && root.isVertical + && !disableScaleWidth + && plasmoid.immutable ){ + disableScaleHeight = true; + //this way improves performance, probably because during animation the preferred sizes update a lot + if((applet.Layout.maximumHeight < root.iconSize)){ + layoutHeight = applet.Layout.maximumHeight; + } + else if (applet.Layout.minimumHeight > root.iconSize){ + layoutHeight = applet.Layout.minimumHeight; + } + else if ((applet.Layout.preferredHeight > root.iconSize)){ + layoutHeight = applet.Layout.preferredHeight; + } + else{ + layoutHeight = root.iconSize + moreHeight; + } + } + else + layoutHeight = root.iconSize + moreHeight; + } + //return root.iconSize + moreHeight; + } + + function updateLayoutWidth(){ + if(container.isInternalViewSplitter){ + if(plasmoid.immutable) + layoutWidth = 0; + else + layoutWidth = root.iconSize; //+ moreWidth+ root.statesLineSize; + } + else if(applet && applet.pluginName === "org.kde.plasma.panelspacer"){ + layoutWidth = root.iconSize + moreWidth; + } + else if(applet && applet.pluginName === "org.kde.plasma.systemtray" && root.isVertical){ + layoutWidth = root.iconSize+root.iconMargin+root.statesLineSize/2; + } + else{ + if(applet && (applet.Layout.minimumWidth > root.iconSize) && root.isHorizontal && (!canBeHovered)){ + layoutWidth = applet.Layout.minimumWidth; + } //it is used for plasmoids that need to scale only one axis... e.g. the Weather Plasmoid + else if(applet + && ( (applet.Layout.maximumWidth < root.iconSize) || (applet.Layout.preferredWidth > root.iconSize)) + && root.isHorizontal + && !disableScaleHeight + && plasmoid.immutable){ + disableScaleWidth = true; + //this way improves performance, probably because during animation the preferred sizes update a lot + if((applet.Layout.maximumWidth < root.iconSize)){ + // return applet.Layout.maximumWidth; + layoutWidth = applet.Layout.maximumWidth; + } + else if (applet.Layout.minimumWidth > root.iconSize){ + layoutWidth = applet.Layout.minimumWidth; + } + else if (applet.Layout.preferredWidth > root.iconSize){ + layoutWidth = applet.Layout.preferredWidth; + } + else{ + layoutWidth = root.iconSize + moreWidth; + } + } + else{ + //return root.iconSize + moreWidth; + layoutWidth = root.iconSize + moreWidth; + } + } + } + + Item{ + id:wrapperContainer + width: Math.round( container.isInternalViewSplitter ? wrapper.layoutWidth : parent.zoomScaleWidth * wrapper.layoutWidth ) + height: Math.round( container.isInternalViewSplitter ? wrapper.layoutHeight : parent.zoomScaleHeight * wrapper.layoutHeight ) + + anchors.centerIn: parent + } + + //spacer background + Loader{ + anchors.fill: wrapperContainer + active: applet && (applet.pluginName === "org.kde.plasma.panelspacer") && !plasmoid.immutable + + sourceComponent: Rectangle{ + anchors.fill: parent + border.width: 1 + border.color: theme.textColor + color: "transparent" + opacity: 0.7 + + radius: root.iconMargin + Rectangle{ + anchors.centerIn: parent + color: parent.border.color + + width: parent.width - 1 + height: parent.height - 1 + + opacity: 0.2 + } + } + } + + Loader{ + anchors.fill: wrapperContainer + active: container.isInternalViewSplitter + && !plasmoid.immutable + + rotation: root.isVertical ? 90 : 0 + + sourceComponent: Image{ + id:splitterImage + anchors.fill: parent + + source:"../icons/splitter.png" + + layer.enabled: true + layer.effect: DropShadow { + radius: shadowSize + samples: 2 * radius + color: "#ff080808" + + verticalOffset: 2 + + property int shadowSize : Math.ceil(root.iconSize / 10) + } + + Component.onCompleted: wrapper.zoomScale = 1.1 + } + } + + ///Shadow in applets + Loader{ + anchors.fill: container.appletWrapper + + active: container.applet + &&((plasmoid.configuration.shadows === 1 /*Locked Applets*/ + && (!container.canBeHovered || (container.lockZoom && (applet.pluginName !== "org.kde.store.nowdock.plasmoid"))) ) + || (plasmoid.configuration.shadows === 2 /*All Applets*/ + && (applet.pluginName !== "org.kde.store.nowdock.plasmoid"))) + + sourceComponent: DropShadow{ + anchors.fill: parent + color: "#ff080808" + samples: 2 * radius + source: container.applet + radius: shadowSize + verticalOffset: 2 + + property int shadowSize : Math.ceil(root.iconSize / 12) + } + } + + BrightnessContrast{ + id:hoveredImage + anchors.fill: wrapperContainer + enabled: opacity != 0 ? true : false + opacity: appletMouseArea.containsMouse ? 1 : 0 + + brightness: 0.25 + source: wrapperContainer + + Behavior on opacity { + NumberAnimation { duration: 300 } + } + } + + BrightnessContrast { + id: clickedEffect + anchors.fill: wrapperContainer + source: wrapperContainer + } + + /* onHeightChanged: { + if ((index == 1)|| (index==3)){ + console.log("H: "+index+" ("+zoomScale+"). "+currentLayout.children[1].height+" - "+currentLayout.children[3].height+" - "+(currentLayout.children[1].height+currentLayout.children[3].height)); + } + } + + onZoomScaleChanged:{ + if ((index == 1)|| (index==3)){ + console.log(index+" ("+zoomScale+"). "+currentLayout.children[1].height+" - "+currentLayout.children[3].height+" - "+(currentLayout.children[1].height+currentLayout.children[3].height)); + } + }*/ + + /* Rectangle{ + anchors.fill: parent + color: "transparent" + border.color: "red" + border.width: 1 + } */ + + Behavior on zoomScale { + NumberAnimation { duration: container.animationTime } + } + + function calculateScales( currentMousePosition ){ + var distanceFromHovered = Math.abs(index - layoutsContainer.hoveredIndex); + + // A new algorithm tryig to make the zoom calculation only once + // and at the same time fixing glitches + if ((distanceFromHovered == 0)&& + (currentMousePosition > 0) ){ + + var rDistance = Math.abs(currentMousePosition - center); + + //check if the mouse goes right or down according to the center + var positiveDirection = ((currentMousePosition - center) >= 0 ); + + + //finding the zoom center e.g. for zoom:1.7, calculates 0.35 + var zoomCenter = (root.zoomFactor - 1) / 2 + + //computes the in the scale e.g. 0...0.35 according to the mouse distance + //0.35 on the edge and 0 in the center + var firstComputation = (rDistance / center) * zoomCenter; + + //calculates the scaling for the neighbour tasks + var bigNeighbourZoom = Math.min(1 + zoomCenter + firstComputation, root.zoomFactor); + var smallNeighbourZoom = Math.max(1 + zoomCenter - firstComputation, 1); + + bigNeighbourZoom = Number(bigNeighbourZoom.toFixed(2)); + smallNeighbourZoom = Number(smallNeighbourZoom.toFixed(2)); + + var leftScale; + var rightScale; + + if(positiveDirection === true){ + rightScale = bigNeighbourZoom; + leftScale = smallNeighbourZoom; + } + else { + rightScale = smallNeighbourZoom; + leftScale = bigNeighbourZoom; + } + + + // console.log("--------------") + // console.debug(leftScale + " " + rightScale + " " + index); + //activate messages to update the the neighbour scales + layoutsContainer.updateScale(index-1, leftScale, 0); + layoutsContainer.updateScale(index+1, rightScale, 0); + //these messages interfere when an applet is hidden, that is why I disabled them + // currentLayout.updateScale(index-2, 1, 0); + // currentLayout.updateScale(index+2, 1, 0); + + //Left hiddenSpacer + if((index === 0 )&&(layoutsContainer.count > 1)){ + hiddenSpacerLeft.nScale = leftScale - 1; + } + + //Right hiddenSpacer ///there is one more item in the currentLayout ???? + if((index === layoutsContainer.count - 1 )&&(layoutsContainer.count>1)){ + hiddenSpacerRight.nScale = rightScale - 1; + } + + zoomScale = root.zoomFactor; + } + + } //scale + + + function signalUpdateScale(nIndex, nScale, step){ + if(container && (container.index === nIndex)){ + if ( ((canBeHovered && !lockZoom ) || container.nowDock) + && (applet && applet.status !== PlasmaCore.Types.HiddenStatus) + //&& (index != currentLayout.hoveredIndex) + ){ + if(!container.nowDock){ + if(nScale >= 0) + zoomScale = nScale + step; + else + zoomScale = zoomScale + step; + } + else{ + if(layoutsContainer.hoveredIndex<container.index) + nowDock.updateScale(0, nScale, step); + else if(layoutsContainer.hoveredIndex>=container.index) + nowDock.updateScale(root.tasksCount-1, nScale, step); + } + } ///if the applet is hidden must forward its scale events to its neighbours + else if ((applet && (applet.status === PlasmaCore.Types.HiddenStatus)) + || container.isInternalViewSplitter){ + if(layoutsContainer.hoveredIndex>index) + layoutsContainer.updateScale(index-1, nScale, step); + else if((layoutsContainer.hoveredIndex<index)) + layoutsContainer.updateScale(index+1, nScale, step); + } + } + } + + Component.onCompleted: { + layoutsContainer.updateScale.connect(signalUpdateScale); + } + }// Main task area // id:wrapper + + // a hidden spacer on the right for the last item to add stability + Item{ + id: hiddenSpacerRight + //we add one missing pixel from calculations + width: root.isHorizontal ? nHiddenSize : wrapper.width + height: root.isHorizontal ? wrapper.height : nHiddenSize + + visible: (container.index === mainLayout.count - 1) || (container.index === secondLayout.beginIndex+secondLayout.count-1) + + property real nHiddenSize: (nScale > 0) ? (root.realSize * nScale) : 0 + property real nScale: 0 + + Behavior on nScale { + NumberAnimation { duration: container.animationTime } + } + + /*Rectangle{ + width: 1 + height: parent.height + x:parent.width / 2 + border.width: 1 + border.color: "red" + color: "transparent" + }*/ + } + + }// Flow with hidden spacers inside + + MouseArea{ + id: appletMouseArea + + anchors.fill: parent + enabled: (!nowDock)&&(canBeHovered)&&(!lockZoom)&&(plasmoid.immutable) + hoverEnabled: plasmoid.immutable && (!nowDock) && canBeHovered ? true : false + propagateComposedEvents: true + + property bool pressed: false + + onClicked: { + pressed = false; + mouse.accepted = false; + } + + onContainsMouseChanged: { + if(!containsMouse){ + hiddenSpacerLeft.nScale = 0; + hiddenSpacerRight.nScale = 0; + } + } + + onEntered: { + layoutsContainer.hoveredIndex = index; + // mouseEntered = true; + /* icList.mouseWasEntered(index-2, false); + icList.mouseWasEntered(index+2, false); + icList.mouseWasEntered(index-1, true); + icList.mouseWasEntered(index+1, true); */ + if (root.isHorizontal){ + layoutsContainer.currentSpot = mouseX; + wrapper.calculateScales(mouseX); + } + else{ + layoutsContainer.currentSpot = mouseY; + wrapper.calculateScales(mouseY); + } + } + + onExited:{ + checkListHovered.start(); + } + + onPositionChanged: { + if(!pressed){ + if (root.isHorizontal){ + var step = Math.abs(layoutsContainer.currentSpot-mouse.x); + if (step >= container.animationStep){ + layoutsContainer.hoveredIndex = index; + layoutsContainer.currentSpot = mouse.x; + + wrapper.calculateScales(mouse.x); + } + } + else{ + var step = Math.abs(layoutsContainer.currentSpot-mouse.y); + if (step >= container.animationStep){ + layoutsContainer.hoveredIndex = index; + layoutsContainer.currentSpot = mouse.y; + + wrapper.calculateScales(mouse.y); + } + } + } + mouse.accepted = false; + } + + onPressed: pressed = true; + } + + //BEGIN states + states: [ + State { + name: "left" + when: (plasmoid.location === PlasmaCore.Types.LeftEdge) + + AnchorChanges { + target: appletFlow + anchors{ top:undefined; bottom:undefined; left:parent.left; right:undefined;} + } + }, + State { + name: "right" + when: (plasmoid.location === PlasmaCore.Types.RightEdge) + + AnchorChanges { + target: appletFlow + anchors{ top:undefined; bottom:undefined; left:undefined; right:parent.right;} + } + }, + State { + name: "bottom" + when: (plasmoid.location === PlasmaCore.Types.BottomEdge) + + AnchorChanges { + target: appletFlow + anchors{ top:undefined; bottom:parent.bottom; left:undefined; right:undefined;} + } + }, + State { + name: "top" + when: (plasmoid.location === PlasmaCore.Types.TopEdge) + + AnchorChanges { + target: appletFlow + anchors{ top:parent.top; bottom:undefined; left:undefined; right:undefined;} + } + } + ] + //END states + + + //BEGIN animations + SequentialAnimation{ + id: clickedAnimation + alwaysRunToEnd: true + running: appletMouseArea.pressed + + ParallelAnimation{ + PropertyAnimation { + target: clickedEffect + property: "brightness" + to: -0.35 + duration: units.longDuration + easing.type: Easing.OutQuad + } + /* PropertyAnimation { + target: wrapper + property: "zoomScale" + to: wrapper.zoomScale - (root.zoomFactor - 1) / 10 + duration: units.longDuration + easing.type: Easing.OutQuad + }*/ + } + ParallelAnimation{ + PropertyAnimation { + target: clickedEffect + property: "brightness" + to: 0 + duration: units.longDuration + easing.type: Easing.OutQuad + } + /* PropertyAnimation { + target: wrapper + property: "zoomScale" + to: root.zoomFactor + duration: units.longDuration + easing.type: Easing.OutQuad + }*/ + } + } + //END animations +} + + diff --git a/containment/contents/ui/ConfigOverlay.qml b/containment/contents/ui/ConfigOverlay.qml new file mode 100644 index 000000000..fe9ac8e82 --- /dev/null +++ b/containment/contents/ui/ConfigOverlay.qml @@ -0,0 +1,527 @@ +/* + * Copyright 2013 Michail Vourlakos <mvourlakos@gmail.com> + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.1 +import QtQuick.Layouts 1.0 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.kquickcontrolsaddons 2.0 + +MouseArea { + id: configurationArea + + z: 1000 + + anchors { + fill: parent + rightMargin: (plasmoid.formFactor !== PlasmaCore.Types.Vertical) ? toolBox.width : 0 + bottomMargin: (plasmoid.formFactor === PlasmaCore.Types.Vertical) ? toolBox.height : 0 + } + + hoverEnabled: true + + property bool isResizingLeft: false + property bool isResizingRight: false + property Item currentApplet + property Item previousCurrentApplet + + property int lastX + property int lastY + property int appletX + property int appletY + + readonly property int spacerHandleSize: units.smallSpacing + + onHeightChanged: tooltip.visible = false; + onWidthChanged: tooltip.visible = false; + + onPositionChanged: { + /*if (currentApplet && currentApplet.applet && + currentApplet.applet.pluginName == "org.kde.plasma.panelspacer") { + if (plasmoid.formFactor === PlasmaCore.Types.Vertical) { + if ((mouse.y - handle.y) < spacerHandleSize || + (mouse.y - handle.y) > (handle.height - spacerHandleSize)) { + configurationArea.cursorShape = Qt.SizeVerCursor; + } else { + configurationArea.cursorShape = Qt.ArrowCursor; + } + } else { + if ((mouse.x - handle.x) < spacerHandleSize || + (mouse.x - handle.x) > (handle.width - spacerHandleSize)) { + configurationArea.cursorShape = Qt.SizeHorCursor; + } else { + configurationArea.cursorShape = Qt.ArrowCursor; + } + } + } else { + configurationArea.cursorShape = Qt.ArrowCursor; + }*/ + + if (pressed) { + /* if (currentApplet && currentApplet.applet && + currentApplet.applet.pluginName == "org.kde.plasma.panelspacer") { + + if (isResizingLeft) { + if (plasmoid.formFactor === PlasmaCore.Types.Vertical) { + handle.y += (mouse.y - lastY); + // handle.height = currentApplet.height + (currentApplet.y - handle.y); + } else { + handle.x += (mouse.x - lastX); + // handle.width = currentApplet.width + (currentApplet.x - handle.x); + } + + lastX = mouse.x; + lastY = mouse.y; + return; + + } else if (isResizingRight) { + /* if (plasmoid.formFactor === PlasmaCore.Types.Vertical) { + handle.height = mouse.y - handle.y; + } else { + handle.width = mouse.x - handle.x; + } */ + + /* lastX = mouse.x; + lastY = mouse.y; + return; + } + }*/ + + var padding = units.gridUnit * 3; + if (currentApplet && (mouse.x < -padding || mouse.y < -padding || + mouse.x > width + padding || mouse.y > height + padding)) { + var newCont = plasmoid.containmentAt(mouse.x, mouse.y); + + if (newCont && newCont != plasmoid) { + var newPos = newCont.mapFromApplet(plasmoid, mouse.x, mouse.y); + newCont.addApplet(currentApplet.applet, newPos.x, newPos.y); + root.dragOverlay.currentApplet = null; + return; + } + } + + if(currentApplet){ + if (plasmoid.formFactor === PlasmaCore.Types.Vertical) { + currentApplet.y += (mouse.y - lastY); + // handle.y = currentApplet.y; + } else { + currentApplet.x += (mouse.x - lastX); + // handle.x = currentApplet.x; + } + } + + lastX = mouse.x; + lastY = mouse.y; + + var relevantLayout = mapFromItem(mainLayout, 0, 0); + var item = mainLayout.childAt(mouse.x-relevantLayout.x, mouse.y-relevantLayout.y); + + if (item && item !== placeHolder) { + // placeHolder.width = item.width; + // placeHolder.height = item.height; + placeHolder.parent = configurationArea; + var posInItem = mapToItem(item, mouse.x, mouse.y); + + if ((plasmoid.formFactor === PlasmaCore.Types.Vertical && posInItem.y < item.height/2) || + (plasmoid.formFactor !== PlasmaCore.Types.Vertical && posInItem.x < item.width/2)) { + root.layoutManager.insertBefore(item, placeHolder); + } else { + root.layoutManager.insertAfter(item, placeHolder); + } + } + + } else { + var relevantLayout = mapFromItem(mainLayout,0,0); + + var item = mainLayout.childAt(mouse.x-relevantLayout.x, mouse.y-relevantLayout.y); + if (root.dragOverlay && item && item !== lastSpacer) { + root.dragOverlay.currentApplet = item; + } else { + root.dragOverlay.currentApplet = null; + } + } + + if (root.dragOverlay.currentApplet && !currentApplet.isInternalViewSplitter) { + hideTimer.stop(); + + tooltip.visible = true; + tooltip.raise(); + } + } + + onExited: hideTimer.restart(); + + onCurrentAppletChanged: { + if(previousCurrentApplet && previousCurrentApplet.showZoomed) + previousCurrentApplet.showZoomed = false; + + previousCurrentApplet = currentApplet; + + + if (!currentApplet + || !root.dragOverlay.currentApplet + || (currentApplet &¤tApplet.isInternalViewSplitter)) { + hideTimer.restart(); + return; + } + + if(currentApplet.showZoomed !== undefined) + currentApplet.showZoomed = true; + + var relevantLayout = mapFromItem(mainLayout, 0, 0); + + handle.x = relevantLayout.x + currentApplet.x; + handle.y = relevantLayout.y + currentApplet.y; + handle.width = currentApplet.width; + handle.height = currentApplet.height; + + lockButton.checked = currentApplet.lockZoom; + + repositionHandler.restart(); + } + + onPressed: { + if (!root.dragOverlay.currentApplet) { + return; + } + + /* + if (currentApplet.applet.pluginName == "org.kde.plasma.panelspacer") { + if (plasmoid.formFactor === PlasmaCore.Types.Vertical) { + if ((mouse.y - handle.y) < spacerHandleSize) { + configurationArea.isResizingLeft = true; + configurationArea.isResizingRight = false; + } else if ((mouse.y - handle.y) > (handle.height - spacerHandleSize)) { + configurationArea.isResizingLeft = false; + configurationArea.isResizingRight = true; + } else { + configurationArea.isResizingLeft = false; + configurationArea.isResizingRight = false; + } + + } else { + if ((mouse.x - handle.x) < spacerHandleSize) { + configurationArea.isResizingLeft = true; + configurationArea.isResizingRight = false; + } else if ((mouse.x - handle.x) > (handle.width - spacerHandleSize)) { + configurationArea.isResizingLeft = false; + configurationArea.isResizingRight = true; + } else { + configurationArea.isResizingLeft = false; + configurationArea.isResizingRight = false; + } + } + }*/ + + var relevantApplet = mapFromItem(currentApplet, 0, 0); + appletX = mouse.x - relevantApplet.x; + appletY = mouse.y - relevantApplet.y; + + lastX = mouse.x; + lastY = mouse.y; + placeHolder.width = currentApplet.width; + placeHolder.height = currentApplet.height; + handle.width = currentApplet.width; + handle.height = currentApplet.height; + root.layoutManager.insertBefore(currentApplet, placeHolder); + currentApplet.parent = root; + currentApplet.x = lastX-appletX; + currentApplet.y = lastY-appletY; + currentApplet.z = 900; + } + + onReleased: { + if (!root.dragOverlay.currentApplet) { + return; + } + + if(currentApplet && currentApplet.applet){ + if (plasmoid.formFactor === PlasmaCore.Types.Vertical) { + currentApplet.applet.configuration.length = handle.height; + } else { + currentApplet.applet.configuration.length = handle.width; + } + } + + configurationArea.isResizingLeft = false; + configurationArea.isResizingRight = false; + + root.layoutManager.insertBefore(placeHolder, currentApplet); + placeHolder.parent = configurationArea; + currentApplet.z = 1; + + var relevantLayout = mapFromItem(mainLayout, 0, 0); + + handle.x = relevantLayout.x + currentApplet.x; + handle.y = relevantLayout.y + currentApplet.y; + // handle.width = currentApplet.width; + // handle.height = currentApplet.height; + root.layoutManager.save(); + } + + Item { + id: placeHolder + visible: configurationArea.containsMouse + Layout.fillWidth: currentApplet ? currentApplet.Layout.fillWidth : false + Layout.fillHeight: currentApplet ? currentApplet.Layout.fillHeight : false + } + + //Because of the animations for the applets the handler can not catch up to + //reposition itself when the currentApplet position or size changes. + //This timer fixes that situation + Timer { + id: repositionHandler + interval: 100 + onTriggered: handle.updatePlacement(); + } + + Timer { + id: hideTimer + interval: units.longDuration * 2 + onTriggered: tooltip.visible = false; + } + + Connections { + target: currentApplet + onXChanged: handle.updatePlacement(); + onYChanged: handle.updatePlacement(); + onWidthChanged: handle.updatePlacement(); + onHeightChanged: handle.updatePlacement() + } + + Rectangle { + id: handle + visible: configurationArea.containsMouse + color: theme.backgroundColor + radius: 3 + opacity: currentApplet ? 0.5 : 0 + + //BEGIN functions + function updatePlacement(){ + if(currentApplet){ + var transformChoords = root.mapFromItem(currentApplet, 0, 0) + + handle.x = transformChoords.x; + handle.y = transformChoords.y; + handle.width = currentApplet.width; + handle.height = currentApplet.height; + + repositionHandler.restart(); + } + } + + //END functions + PlasmaCore.IconItem { + source: "transform-move" + width: Math.min(parent.width, parent.height) + height: width + anchors.centerIn: parent + } + /* Rectangle { + anchors { + left: parent.left + top: parent.top + bottom: (plasmoid.formFactor !== PlasmaCore.Types.Vertical) ? parent.bottom : undefined + right: (plasmoid.formFactor !== PlasmaCore.Types.Vertical) ? undefined : parent.right + } + visible: currentApplet && currentApplet.applet.pluginName == "org.kde.plasma.panelspacer" + opacity: visible && !xAnim.running && !yAnim.running ? 1.0 : 0 + width: configurationArea.spacerHandleSize + height: configurationArea.spacerHandleSize + color: theme.textColor + Behavior on opacity { + NumberAnimation { + duration: units.longDuration + easing.type: Easing.InOutQuad + } + } + } + Rectangle { + anchors { + right: parent.right + top: (plasmoid.formFactor !== PlasmaCore.Types.Vertical) ? parent.top : undefined + bottom: parent.bottom + left: (plasmoid.formFactor !== PlasmaCore.Types.Vertical) ? undefined : parent.left + } + visible: currentApplet && currentApplet.applet.pluginName == "org.kde.plasma.panelspacer" + opacity: visible && !xAnim.running && !yAnim.running ? 1.0 : 0 + width: configurationArea.spacerHandleSize + height: configurationArea.spacerHandleSize + color: theme.textColor + Behavior on opacity { + NumberAnimation { + duration: units.longDuration + easing.type: Easing.InOutQuad + } + } + }*/ + + PlasmaComponents.Button{ + id: lockButton + checkable: true + iconSource: checked ? "lock" : "unlock" + width: units.iconSizes.large + visible: currentApplet && + ((currentApplet.applet && + ((currentApplet.applet.pluginName === "org.kde.plasma.systemtray") + || (currentApplet.applet.pluginName === "org.kde.store.nowdock.plasmoid")) ) + || (currentApplet.isInternalViewSplitter)) + ? false : true + + anchors.margins: 2*units.smallSpacing + tooltip: "Lock/Unlock zoom effect" + + onCheckedChanged: { + currentApplet.lockZoom = checked; + root.layoutManager.saveLocks(); + } + + states: [ + State { + name: "left" + when: plasmoid.location === PlasmaCore.Types.LeftEdge + + AnchorChanges { + target: lockButton + anchors{ top:parent.top; bottom:undefined; left:undefined; right:parent.right; } + } + }, + State { + name: "right" + when: plasmoid.location === PlasmaCore.Types.RightEdge + + AnchorChanges { + target: lockButton + anchors{ top:parent.top; bottom:undefined; left:parent.left; right:undefined; } + } + }, + State { + name: "bottom" + when: plasmoid.location === PlasmaCore.Types.BottomEdge + + AnchorChanges { + target: lockButton + anchors{ top:parent.top; bottom:undefined; left:parent.left; right:undefined; } + } + }, + State { + name: "top" + when: plasmoid.location === PlasmaCore.Types.TopEdge + + AnchorChanges { + target: lockButton + anchors{ top:undefined; bottom:parent.bottom; left:undefined; right:parent.right; } + } + } + ] + } + + Behavior on x { + enabled: !configurationArea.pressed + NumberAnimation { + id: xAnim + duration: units.longDuration + easing.type: Easing.InOutQuad + } + } + Behavior on y { + id: yAnim + enabled: !configurationArea.pressed + NumberAnimation { + duration: units.longDuration + easing.type: Easing.InOutQuad + } + } + Behavior on width { + enabled: !configurationArea.pressed + NumberAnimation { + duration: units.longDuration + easing.type: Easing.InOutQuad + } + } + Behavior on height { + enabled: !configurationArea.pressed + NumberAnimation { + duration: units.longDuration + easing.type: Easing.InOutQuad + } + } + Behavior on opacity { + NumberAnimation { + duration: units.longDuration + easing.type: Easing.InOutQuad + } + } + } + PlasmaCore.Dialog { + id: tooltip + visualParent: currentApplet + + type: PlasmaCore.Dialog.Dock + flags: Qt.WindowStaysOnTopHint|Qt.WindowDoesNotAcceptFocus|Qt.BypassWindowManagerHint + location: plasmoid.location + + onVisualParentChanged: { + if (visualParent && currentApplet && currentApplet.applet) { + configureButton.visible = currentApplet.applet.action("configure") && currentApplet.applet.action("configure").enabled; + closeButton.visible = currentApplet.applet.action("remove") && currentApplet.applet.action("remove").enabled; + label.text = currentApplet.applet.title; + } + } + + mainItem: MouseArea { + enabled: currentApplet + width: handleRow.childrenRect.width + (2 * handleRow.spacing) + height: Math.max(configureButton.height, label.contentHeight, closeButton.height) + hoverEnabled: true + onEntered: hideTimer.stop(); + onExited: hideTimer.restart(); + Row { + id: handleRow + anchors.horizontalCenter: parent.horizontalCenter + spacing: units.smallSpacing + PlasmaComponents.ToolButton { + id: configureButton + anchors.verticalCenter: parent.verticalCenter + iconSource: "configure" + onClicked: { + tooltip.visible = false; + currentApplet.applet.action("configure").trigger() + } + } + PlasmaComponents.Label { + id: label + anchors.verticalCenter: parent.verticalCenter + textFormat: Text.PlainText + maximumLineCount: 1 + } + PlasmaComponents.ToolButton { + id: closeButton + anchors.verticalCenter: parent.verticalCenter + iconSource: "window-close" + onClicked: { + tooltip.visible = false; + if(currentApplet && currentApplet.applet) + currentApplet.applet.action("remove").trigger(); + } + } + } + } + } +} diff --git a/containment/contents/ui/MagicWindow.qml b/containment/contents/ui/MagicWindow.qml new file mode 100644 index 000000000..856730d4c --- /dev/null +++ b/containment/contents/ui/MagicWindow.qml @@ -0,0 +1,507 @@ +import QtQuick 2.1 + +import org.kde.plasma.core 2.0 as PlasmaCore + +import org.kde.taskmanager 0.1 as TaskManager + +import org.kde.nowdock 0.1 as NowDock + +NowDock.PanelWindow{ + id: window + + property bool inStartup: root.inStartup + property bool normalState : false // this is being set from updateMaskArea + + property int animationSpeed: root.durationTime * 1.2 * units.longDuration + property int length: root.isVertical ? screenGeometry.height : screenGeometry.width + + //it is used in order to not break the calculations for the thickness placement + //especially in automatic icon sizes calculations + property int iconMarginOriginal: 0.12*plasmoid.configuration.iconSize + property int statesLineSizeOriginal: root.nowDock ? Math.ceil( plasmoid.configuration.iconSize/13 ) : 0 + + property int thicknessAutoHidden: 8 + property int thicknessMid: root.statesLineSize + (1 + (0.65 * (root.zoomFactor-1)))*(root.iconSize+root.iconMargin) //needed in some animations + property int thicknessNormal: root.statesLineSize + root.iconSize + root.iconMargin + 1 + property int thicknessZoom: root.statesLineSize + ((root.iconSize+root.iconMargin) * root.zoomFactor) + 2 + //it is used to keep thickness solid e.g. when iconSize changes from auto functions + property int thicknessMidOriginal: statesLineSizeOriginal + (1 + (0.65 * (root.zoomFactor-1)))*(plasmoid.configuration.iconSize+iconMarginOriginal) //needed in some animations + property int thicknessNormalOriginal: root.useThemePanel ? Math.max(thicknessNormalOriginalValue, root.realPanelSize) : thicknessNormalOriginalValue + property int thicknessNormalOriginalValue: statesLineSizeOriginal + plasmoid.configuration.iconSize + iconMarginOriginal + 1 + property int thicknessZoomOriginal: statesLineSizeOriginal + ((plasmoid.configuration.iconSize+iconMarginOriginal) * root.zoomFactor) + 2 + + + childrenLength: root.isHorizontal ? mainLayout.width : mainLayout.height + immutable: plasmoid.immutable + location: plasmoid.location + panelVisibility: plasmoid.configuration.panelVisibility + + //this is used especially in the wayland case or when the user might want + //not to make the dock to go below windows but to hide, e.g. intelligent auto-hide + //isDockWindowType: true + + + width: root.isHorizontal ? length : thicknessZoomOriginal + height: root.isHorizontal ? thicknessZoomOriginal : length + + // it is used in order to restore applets after right click menu + onDisableHidingChanged: { + if (!disableHiding) { + checkListHovered.restart(); + } + } + + onImmutableChanged: updateMaskArea(); + + onInStartupChanged: { + if (!inStartup) { + delayAnimationTimer.start(); + } + } + + onIsHoveredChanged: { + if(isHovered) { + //stop parent window timer for auto hiding + if ((panelVisibility === NowDock.PanelWindow.AutoHide)|| isDockWindowType) { + if(hideMagicWindowInAutoHide.forcedDisableHiding) { + hideMagicWindowInAutoHide.forcedDisableHiding = false; + window.disableHiding = false; + } + + hideMagicWindowInAutoHide.stop(); + } + + if (delayerTimer.running) { + delayerTimer.stop(); + } + + updateMaskArea(); + } else { + // initialize the zoom + delayerTimer.start(); + } + } + + onMustBeRaised: { + if ((panelVisibility === NowDock.PanelWindow.AutoHide) || isDockWindowType) { + slidingAnimationAutoHiddenIn.init(); + } else { + slidingAnimation.init(true,false); + } + } + + onMustBeRaisedImmediately: { + slidingAnimation.init(true,true); + } + + onMustBeLowered: { + if ((panelVisibility === NowDock.PanelWindow.AutoHide) || isDockWindowType ) { + slidingAnimationAutoHiddenOut.init(); + } else { + slidingAnimation.init(false,false); + } + } + + onNormalStateChanged: { + if(normalState && nowDock) { + nowDock.publishTasksGeometries(); + } + + if (normalState) { + root.updateAutomaticIconSize(); + } + } + + onPanelVisibilityChanged: { + if (panelVisibility !== NowDock.PanelWindow.AutoHide) { + isAutoHidden = false; + } + } + + onWidthChanged: { + if (plasmoid.immutable) { + if (windowSystem.compositingActive) { + magicWin.initialize(); + } else { + magicWin.updateTransientThickness(); + } + updateMaskArea(); + } + } + + onHeightChanged: { + if (plasmoid.immutable) { + if (windowSystem.compositingActive) { + magicWin.initialize(); + } else { + magicWin.updateTransientThickness(); + } + updateMaskArea(); + } + } + + onVisibleChanged:{ + if (visible) { //shrink the parent panel window + initialize(); + } + } + + function initializeSlidingInAnimation() { + // Hide in Startup in order to show the contents with beautiful sliding animation + var hiddenSpace; + + if ((location===PlasmaCore.Types.LeftEdge)||(location===PlasmaCore.Types.TopEdge)) { + hiddenSpace = -thicknessNormal; + } else { + hiddenSpace = thicknessNormal; + } + + if (root.isVertical) { + layoutsContainer.x = hiddenSpace; + } else { + layoutsContainer.y = hiddenSpace; + } + + layoutsContainer.opacity = 1; + + if (!inStartup) {initialize(); + delayAnimationTimer.start(); + } + } + + function updateMaskArea() { + if (!windowSystem.compositingActive) { + return; + } + + var localX = 0; + var localY = 0; + + normalState = (root.nowDockHoveredIndex === -1) && (layoutsContainer.hoveredIndex === -1) + && (root.appletsAnimations === 0) + && (root.animationsNeedBothAxis === 0) && (root.animationsNeedLength === 0) + && (!mainLayout.animatedLength) + + // debug maskArea criteria + //console.log(root.nowDockHoveredIndex + ", " + layoutsContainer.hoveredIndex + ", " + // + root.appletsAnimations+ ", " + // + root.animationsNeedBothAxis + ", " + root.animationsNeedLength + ", " + root.animationsNeedThickness +", " + // + mainLayout.animatedLength); + + var tempLength = root.isHorizontal ? width : height; + var tempThickness = root.isHorizontal ? height : width; + + var space = root.panelEdgeSpacing + 10; + + if (normalState) { + //count panel length + if(root.isHorizontal) { + tempLength = plasmoid.configuration.panelPosition === NowDock.PanelWindow.Double ? layoutsContainer.width + 0.5*space : mainLayout.width + space; + } else { + tempLength = plasmoid.configuration.panelPosition === NowDock.PanelWindow.Double ? layoutsContainer.height + 0.5*space : mainLayout.height + space; + } + + tempThickness = thicknessNormalOriginal; + + if (root.animationsNeedThickness > 0) { + tempThickness = thicknessMidOriginal; + } + + if (window.isAutoHidden && ((panelVisibility === NowDock.PanelWindow.AutoHide) || window.isDockWindowType)) { + tempThickness = thicknessAutoHidden; + } + + if (!immutable) { + tempThickness = 2; + } + + //configure x,y based on plasmoid position and root.panelAlignment(Alignment) + if ((plasmoid.location === PlasmaCore.Types.BottomEdge) || (plasmoid.location === PlasmaCore.Types.TopEdge)) { + if (plasmoid.location === PlasmaCore.Types.BottomEdge) { + localY = window.height - tempThickness; + } else if (plasmoid.location === PlasmaCore.Types.TopEdge) { + localY = 0; + } + + if (plasmoid.configuration.panelPosition === NowDock.PanelWindow.Double) { + localX = (window.width/2) - (layoutsContainer.width/2) - 0.25*space; + } else if (root.panelAlignment === NowDock.PanelWindow.Left) { + localX = 0; + } else if (root.panelAlignment === NowDock.PanelWindow.Center) { + localX = (window.width/2) - (mainLayout.width/2) - (space/2); + } else if (root.panelAlignment === NowDock.PanelWindow.Right) { + localX = window.width - mainLayout.width - (space/2); + } + } else if ((plasmoid.location === PlasmaCore.Types.LeftEdge) || (plasmoid.location === PlasmaCore.Types.RightEdge)){ + if (plasmoid.location === PlasmaCore.Types.LeftEdge) { + localX = 0; + } else if (plasmoid.location === PlasmaCore.Types.RightEdge) { + localX = window.width - tempThickness; + } + + if (plasmoid.configuration.panelPosition === NowDock.PanelWindow.Double) { + localY = (window.height/2) - (layoutsContainer.height/2) - 0.25*space; + } else if (root.panelAlignment === NowDock.PanelWindow.Top) { + localY = 0; + } else if (root.panelAlignment === NowDock.PanelWindow.Center) { + localY = (window.height/2) - (mainLayout.height/2) - (space/2); + } else if (root.panelAlignment === NowDock.PanelWindow.Bottom) { + localY = window.height - mainLayout.height - (space/2); + } + } + } else { + if(root.isHorizontal) + tempLength = screenGeometry.width; + else + tempLength = screenGeometry.height; + + //grow only on length and not thickness + if(mainLayout.animatedLength) { + tempThickness = thicknessNormalOriginal; + + if (root.animationsNeedThickness > 0) { + tempThickness = thicknessMidOriginal; + } + + //configure the x,y position based on thickness + if(plasmoid.location === PlasmaCore.Types.RightEdge) + localX = window.width - tempThickness; + else if(plasmoid.location === PlasmaCore.Types.BottomEdge) + localY = window.height - tempThickness; + } else{ + //use all thickness space + tempThickness = thicknessZoomOriginal; + } + } + + var maskLength = maskArea.width; //in Horizontal + if (root.isVertical) { + maskLength = maskArea.height; + } + + var maskThickness = maskArea.height; //in Horizontal + if (root.isVertical) { + maskThickness = maskArea.width; + } + + // console.log("Not updating mask..."); + if( maskArea.x !== localX || maskArea.y !== localY + || maskLength !== tempLength || maskThickness !== tempThickness) { + + // console.log("Updating mask..."); + var newMaskArea = Qt.rect(-1,-1,0,0); + newMaskArea.x = localX; + newMaskArea.y = localY; + + if (isHorizontal) { + newMaskArea.width = tempLength; + newMaskArea.height = tempThickness; + } else { + newMaskArea.width = tempThickness; + newMaskArea.height = tempLength; + } + + maskArea = newMaskArea; + } + + } + + function updateTransientThickness() { + var thickness; + + if (root.isVertical) { + thickness = root.width; + } else { + thickness = root.height; + } + + var newThickness = statesLineSizeOriginal + plasmoid.configuration.iconSize + iconMarginOriginal; + + if (!windowSystem.compositingActive) { + newThickness += iconMarginOriginal; + } + + if (thickness<newThickness) { + setTransientThickness(newThickness); + } + } + + Loader{ + anchors.fill: parent + active: root.debugMode + + sourceComponent: Item{ + anchors.fill:parent + + Rectangle{ + id: windowBackground + anchors.fill: parent + border.color: "red" + border.width: 1 + color: "transparent" + } + + Rectangle{ + x: maskArea.x + y: maskArea.y + height: maskArea.height + width: maskArea.width + + border.color: "green" + border.width: 1 + color: "transparent" + } + } + } + + /***Hiding/Showing Animations*****/ + + SequentialAnimation{ + id: slidingAnimation + + property bool inHalf: false + property bool raiseFlag: false + property bool immediateShow: false + + SequentialAnimation{ + PropertyAnimation { + target: layoutsContainer + property: root.isVertical ? "x" : "y" + to: ((location===PlasmaCore.Types.LeftEdge)||(location===PlasmaCore.Types.TopEdge)) ? -thicknessNormal : thicknessNormal + duration: slidingAnimation.immediateShow ? 100 : window.animationSpeed + easing.type: Easing.OutQuad + } + + PropertyAnimation { + target: slidingAnimation + property: "inHalf" + to: true + duration: 100 + } + + PropertyAnimation { + target: layoutsContainer + property: root.isVertical ? "x" : "y" + to: 0 + duration: window.animationSpeed + easing.type: Easing.OutQuad + } + } + + onStopped: { + inHalf = false; + raiseFlag = false; + immediateShow = false; + + if (!plasmoid.immutable) { + updateTransientThickness(); + } + } + + onInHalfChanged: { + if (inHalf) { + if (raiseFlag) { + window.showOnTop(); + } else { + window.showOnBottom(); + } + } + } + + function init(raise, immediate) { + if(window.visible) { + raiseFlag = raise; + immediateShow = immediate; + // if (!running) { + start(); + // } + } + } + } + //////////////// Auto Hide Animations - Slide In - Out + SequentialAnimation{ + id: slidingAnimationAutoHiddenOut + + PropertyAnimation { + target: layoutsContainer + property: root.isVertical ? "x" : "y" + to: ((location===PlasmaCore.Types.LeftEdge)||(location===PlasmaCore.Types.TopEdge)) ? -thicknessNormal : thicknessNormal + duration: window.animationSpeed + easing.type: Easing.OutQuad + } + + onStopped: { + window.isAutoHidden = true; + updateMaskArea(); + } + + function init() { + start(); + } + } + + SequentialAnimation{ + id: slidingAnimationAutoHiddenIn + + PropertyAnimation { + target: layoutsContainer + property: root.isVertical ? "x" : "y" + to: 0 + duration: window.animationSpeed + easing.type: Easing.OutQuad + } + + onStopped: { + if (!plasmoid.immutable) { + updateTransientThickness(); + } + } + + function init() { + window.isAutoHidden = false; + updateMaskArea(); + start(); + } + } + + ///////////// External Connections ////// + TaskManager.ActivityInfo { + onCurrentActivityChanged: { + window.disableHiding = true; + + if (window.isAutoHidden) { + window.mustBeRaised(); + } + + hideMagicWindowInAutoHide.forcedDisableHiding = true; + hideMagicWindowInAutoHide.start(); + } + } + + ////////////// Timers ////// + //Timer to delay onLeave event + Timer { + id: delayerTimer + interval: 400 + onTriggered: { + root.clearZoom(); + if (root.nowDock) { + nowDock.clearZoom(); + } + } + } + + //Timer to delay onLeave event + Timer { + id: delayAnimationTimer + interval: window.inStartup ? 1000 : 500 + onTriggered: { + layoutsContainer.opacity = 1; + if ((window.panelVisibility !== NowDock.PanelWindow.AutoHide) && !window.isDockWindowType) { + slidingAnimation.init(true,false); + } else { + slidingAnimationAutoHiddenIn.init(); + } + } + } +} diff --git a/containment/contents/ui/PanelBox.qml b/containment/contents/ui/PanelBox.qml new file mode 100644 index 000000000..9dbcb0a25 --- /dev/null +++ b/containment/contents/ui/PanelBox.qml @@ -0,0 +1,414 @@ +/* + * Copyright 2013 Michail Vourlakos <mvourlakos@gmail.com> + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.1 +import QtQuick.Layouts 1.1 +import QtQuick.Window 2.2 + +import org.kde.plasma.plasmoid 2.0 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.kquickcontrolsaddons 2.0 + +import org.kde.nowdock 0.1 as NowDock + +Item{ + id:barLine + + opacity: root.useThemePanel ? 1 : 0 + // parent: root + z:0 + + property int panelWidth: secondLayout.count > 0 && root.isHorizontal && plasmoid.immutable ? + layoutsContainer.width + 2*spacing : mainLayout.width + spacing + property int panelHeight: secondLayout.count > 0 && root.isVertical && plasmoid.immutable ? + layoutsContainer.height + 2*spacing : mainLayout.height + spacing + + width: root.isHorizontal ? panelWidth : smallSize + height: root.isVertical ? panelHeight : smallSize + + property int spacing: (root.panelAlignment === NowDock.PanelWindow.Center + || plasmoid.configuration.panelPosition === NowDock.PanelWindow.Double) ? + root.panelEdgeSpacing : root.panelEdgeSpacing/2 + property int smallSize: Math.max(3.7*root.statesLineSize, 16) + + Behavior on opacity{ + NumberAnimation { duration: 150 } + } + + /// plasmoid's default panel + /* BorderImage{ + anchors.fill:parent + source: "../images/panel-west.png" + border { left:8; right:8; top:8; bottom:8 } + + opacity: (!root.useThemePanel) ? 1 : 0 + + visible: (opacity == 0) ? false : true + + horizontalTileMode: BorderImage.Stretch + verticalTileMode: BorderImage.Stretch + + Behavior on opacity{ + NumberAnimation { duration: 200 } + } + }*/ + + + /// item which is used as anchors for the plasma's theme + Item{ + id:belower + + width: (plasmoid.location === PlasmaCore.Types.LeftEdge) ? shadowsSvgItem.margins.left : shadowsSvgItem.margins.right + height: (plasmoid.location === PlasmaCore.Types.BottomEdge)? shadowsSvgItem.margins.bottom : shadowsSvgItem.margins.top + } + + + /// the current theme's panel + PlasmaCore.FrameSvgItem{ + id: shadowsSvgItem + + width: root.isVertical ? panelSize + margins.left + margins.right: parent.width + margins.left + margins.right + height: root.isVertical ? parent.height + margins.left + margins.right : panelSize + margins.top + margins.bottom + + imagePath: "translucent/widgets/panel-background" + prefix:"shadow" + + opacity: root.useThemePanel ? 1 : 0 + visible: (opacity == 0) ? false : true + + property int panelSize: ((plasmoid.location === PlasmaCore.Types.BottomEdge) || + (plasmoid.location === PlasmaCore.Types.TopEdge)) ? + root.themePanelSize + belower.height: + root.themePanelSize + belower.width + + Behavior on opacity{ + NumberAnimation { duration: 200 } + } + + Binding { + target: root + property: "realPanelSize" + when: shadowsSvgItem + value: { + var space = ((plasmoid.location === PlasmaCore.Types.BottomEdge) || + (plasmoid.location === PlasmaCore.Types.TopEdge)) ? + belower.height : belower.width + + return shadowsSvgItem.panelSize + space; + } + + } + + + PlasmaCore.FrameSvgItem{ + anchors.margins: belower.width-1 + anchors.fill:parent + // imagePath: root.transparentPanel ? "translucent/widgets/panel-background" : + // "widgets/panel-background" + imagePath: "widgets/panel-background" + } + } + + //BEGIN states + //user set Panel Positions + //0-Center, 1-Left, 2-Right, 3-Top, 4-Bottom + states: [ + ///Left + State { + name: "leftCenter" + when: (plasmoid.location === PlasmaCore.Types.LeftEdge)&&(root.panelAlignment === NowDock.PanelWindow.Center) + + AnchorChanges { + target: barLine + anchors{ top:undefined; bottom:undefined; left:parent.left; right:undefined; horizontalCenter:undefined; verticalCenter:parent.verticalCenter} + } + AnchorChanges { + target: belower + anchors{ top:undefined; bottom:undefined; left:undefined; right:parent.left; horizontalCenter:undefined; verticalCenter:parent.verticalCenter} + } + AnchorChanges { + target: shadowsSvgItem + anchors{ top:undefined; bottom:undefined; left:belower.left; right:undefined; horizontalCenter:undefined; verticalCenter:parent.verticalCenter} + } + }, + ///Left + State { + name: "leftTop" + when: (plasmoid.location === PlasmaCore.Types.LeftEdge)&&(root.panelAlignment === NowDock.PanelWindow.Top) + + AnchorChanges { + target: barLine + anchors{ top:parent.top; bottom:undefined; left:parent.left; right:undefined; horizontalCenter:undefined; verticalCenter:undefined} + } + AnchorChanges { + target: belower + anchors{ top:undefined; bottom:parent.top; left:undefined; right:parent.left; horizontalCenter:undefined; verticalCenter:undefined} + } + AnchorChanges { + target: shadowsSvgItem + anchors{ top:belower.top; bottom:undefined; left:belower.left; right:undefined; horizontalCenter:undefined; verticalCenter:undefined} + } + }, + ///Left + State { + name: "leftBottom" + when: (plasmoid.location === PlasmaCore.Types.LeftEdge)&&(root.panelAlignment === NowDock.PanelWindow.Bottom) + + AnchorChanges { + target: barLine + anchors{ top:undefined; bottom:parent.bottom; left:parent.left; right:undefined; horizontalCenter:undefined; verticalCenter:undefined} + } + AnchorChanges { + target: belower + anchors{ top:parent.bottom; bottom:undefined; left:undefined; right:parent.left; horizontalCenter:undefined; verticalCenter:undefined} + } + AnchorChanges { + target: shadowsSvgItem + anchors{ top:undefined; bottom:belower.bottom; left:belower.left; right:undefined; horizontalCenter:undefined; verticalCenter:undefined} + } + }, + State { + name: "leftDouble" + when: (plasmoid.location === PlasmaCore.Types.LeftEdge)&&(root.panelAlignment === NowDock.PanelWindow.Double) + + AnchorChanges { + target: barLine + anchors{ top:undefined; bottom:undefined; left:parent.left; right:undefined; horizontalCenter:undefined; verticalCenter:parent.verticalCenter} + } + AnchorChanges { + target: belower + anchors{ top:undefined; bottom:undefined; left:undefined; right:parent.left; horizontalCenter:undefined; verticalCenter:parent.verticalCenter} + } + AnchorChanges { + target: shadowsSvgItem + anchors{ top:undefined; bottom:undefined; left:belower.left; right:undefined; horizontalCenter:undefined; verticalCenter:parent.verticalCenter} + } + }, + ///Right + State { + name: "rightCenter" + when: (plasmoid.location === PlasmaCore.Types.RightEdge)&&(root.panelAlignment === NowDock.PanelWindow.Center) + + AnchorChanges { + target: barLine + anchors{ top:undefined; bottom:undefined; left:undefined; right:parent.right; horizontalCenter:undefined; verticalCenter:parent.verticalCenter} + } + AnchorChanges { + target: belower + anchors{ top:undefined; bottom:undefined; left:parent.right; right:undefined; horizontalCenter:undefined; verticalCenter:parent.verticalCenter} + } + AnchorChanges { + target: shadowsSvgItem + anchors{ top:undefined; bottom:undefined; left:undefined; right:belower.right; horizontalCenter:undefined; verticalCenter:parent.verticalCenter} + } + }, + State { + name: "rightTop" + when: (plasmoid.location === PlasmaCore.Types.RightEdge)&&(root.panelAlignment === NowDock.PanelWindow.Top) + + AnchorChanges { + target: barLine + anchors{ top:parent.top; bottom:undefined; left:undefined; right:parent.right; horizontalCenter:undefined; verticalCenter:undefined} + } + AnchorChanges { + target: belower + anchors{ top:undefined; bottom:parent.top; left:parent.right; right:undefined; horizontalCenter:undefined; verticalCenter:undefined} + } + AnchorChanges { + target: shadowsSvgItem + anchors{ top:belower.top; bottom:undefined; left:undefined; right:belower.right; horizontalCenter:undefined; verticalCenter:undefined} + } + }, + State { + name: "rightBottom" + when: (plasmoid.location === PlasmaCore.Types.RightEdge)&&(root.panelAlignment === NowDock.PanelWindow.Bottom) + + AnchorChanges { + target: barLine + anchors{ top:undefined; bottom:parent.bottom; left:undefined; right:parent.right; horizontalCenter:undefined; verticalCenter:undefined } + } + AnchorChanges { + target: belower + anchors{ top:parent.bottom; bottom:undefined; left:parent.right; right:undefined; horizontalCenter:undefined; verticalCenter:undefined} + } + AnchorChanges { + target: shadowsSvgItem + anchors{ top:undefined; bottom:belower.bottom; left:undefined; right:belower.right; horizontalCenter:undefined; verticalCenter:undefined} + } + }, + State { + name: "rightDouble" + when: (plasmoid.location === PlasmaCore.Types.RightEdge)&&(root.panelAlignment === NowDock.PanelWindow.Double) + + AnchorChanges { + target: barLine + anchors{ top:undefined; bottom:undefined; left:undefined; right:parent.right; horizontalCenter:undefined; verticalCenter:parent.verticalCenter} + } + AnchorChanges { + target: belower + anchors{ top:undefined; bottom:undefined; left:parent.right; right:undefined; horizontalCenter:undefined; verticalCenter:parent.verticalCenter} + } + AnchorChanges { + target: shadowsSvgItem + anchors{ top:undefined; bottom:undefined; left:undefined; right:belower.right; horizontalCenter:undefined; verticalCenter:parent.verticalCenter} + } + }, + ///Bottom + State { + name: "bottomCenter" + when: (plasmoid.location === PlasmaCore.Types.BottomEdge)&&(root.panelAlignment === NowDock.PanelWindow.Center) + + AnchorChanges { + target: barLine + anchors{ top:undefined; bottom:parent.bottom; left:undefined; right:undefined; horizontalCenter:parent.horizontalCenter; verticalCenter:undefined} + } + AnchorChanges { + target: belower + anchors{ top:parent.bottom; bottom:undefined; left:undefined; right:undefined; horizontalCenter:parent.horizontalCenter; verticalCenter:undefined} + } + AnchorChanges { + target: shadowsSvgItem + anchors{ top:undefined; bottom:belower.bottom; left:undefined; right:undefined; horizontalCenter:parent.horizontalCenter; verticalCenter:undefined} + } + }, + State { + name: "bottomLeft" + when: (plasmoid.location === PlasmaCore.Types.BottomEdge)&&(root.panelAlignment === NowDock.PanelWindow.Left) + + AnchorChanges { + target: barLine + anchors{ top:undefined; bottom:parent.bottom; left:parent.left; right:undefined; horizontalCenter:undefined; verticalCenter:undefined} + } + AnchorChanges { + target: belower + anchors{ top:parent.bottom; bottom:undefined; left:undefined; right:parent.left; horizontalCenter:undefined; verticalCenter:undefined} + } + AnchorChanges { + target: shadowsSvgItem + anchors{ top:undefined; bottom:belower.bottom; left:belower.left; right:undefined; horizontalCenter:undefined; verticalCenter:undefined} + } + }, + State { + name: "bottomRight" + when: (plasmoid.location === PlasmaCore.Types.BottomEdge)&&(root.panelAlignment === NowDock.PanelWindow.Right) + + AnchorChanges { + target: barLine + anchors{ top:undefined; bottom:parent.bottom; left:undefined; right:parent.right; horizontalCenter:undefined; verticalCenter:undefined} + } + AnchorChanges { + target: belower + anchors{ top:parent.bottom; bottom:undefined; left:parent.right; right:undefined; horizontalCenter:undefined; verticalCenter:undefined} + } + AnchorChanges { + target: shadowsSvgItem + anchors{ top:undefined; bottom:belower.bottom; left:undefined; right:belower.right; horizontalCenter:undefined; verticalCenter:undefined} + } + }, + State { + name: "bottomDouble" + when: (plasmoid.location === PlasmaCore.Types.BottomEdge)&&(root.panelAlignment === NowDock.PanelWindow.Double) + + AnchorChanges { + target: barLine + anchors{ top:undefined; bottom:parent.bottom; left:undefined; right:undefined; horizontalCenter:parent.horizontalCenter; verticalCenter:undefined} + } + AnchorChanges { + target: belower + anchors{ top:parent.bottom; bottom:undefined; left:undefined; right:undefined; horizontalCenter:parent.horizontalCenter; verticalCenter:undefined} + } + AnchorChanges { + target: shadowsSvgItem + anchors{ top:undefined; bottom:belower.bottom; left:undefined; right:undefined; horizontalCenter:parent.horizontalCenter; verticalCenter:undefined} + } + }, + ///Top + State { + name: "topCenter" + when: (plasmoid.location === PlasmaCore.Types.TopEdge)&&(root.panelAlignment === NowDock.PanelWindow.Center) + + AnchorChanges { + target: barLine + anchors{ top:parent.top; bottom:undefined; left:undefined; right:undefined; horizontalCenter:parent.horizontalCenter; verticalCenter:undefined} + } + AnchorChanges { + target: belower + anchors{ top:undefined; bottom:parent.top; left:undefined; right:undefined; horizontalCenter:parent.horizontalCenter; verticalCenter:undefined} + } + AnchorChanges { + target: shadowsSvgItem + anchors{ top:belower.top; bottom:undefined; left:undefined; right:undefined; horizontalCenter:parent.horizontalCenter; verticalCenter:undefined} + } + }, + State { + name: "topLeft" + when: (plasmoid.location === PlasmaCore.Types.TopEdge)&&(root.panelAlignment === NowDock.PanelWindow.Left) + + AnchorChanges { + target: barLine + anchors{ top:parent.top; bottom:undefined; left:parent.left; right:undefined; horizontalCenter:undefined; verticalCenter:undefined} + } + AnchorChanges { + target: belower + anchors{ top:undefined; bottom:parent.top; left:undefined; right:parent.left; horizontalCenter:undefined; verticalCenter:undefined} + } + AnchorChanges { + target: shadowsSvgItem + anchors{ top:belower.top; bottom:undefined; left:belower.left; right:undefined; horizontalCenter:undefined; verticalCenter:undefined} + } + }, + State { + name: "topRight" + when: (plasmoid.location === PlasmaCore.Types.TopEdge)&&(root.panelAlignment === NowDock.PanelWindow.Right) + + AnchorChanges { + target: barLine + anchors{ top:parent.top; bottom:undefined; left:undefined; right:parent.right; horizontalCenter:undefined; verticalCenter:undefined} + } + AnchorChanges { + target: belower + anchors{ top:undefined; bottom:parent.top; left:parent.right; right:undefined; horizontalCenter:undefined; verticalCenter:undefined} + } + AnchorChanges { + target: shadowsSvgItem + anchors{ top:belower.top; bottom:undefined; left:undefined; right:belower.right; horizontalCenter:undefined; verticalCenter:undefined} + } + }, + State { + name: "topDouble" + when: (plasmoid.location === PlasmaCore.Types.TopEdge)&&(root.panelAlignment === NowDock.PanelWindow.Double) + + AnchorChanges { + target: barLine + anchors{ top:parent.top; bottom:undefined; left:undefined; right:undefined; horizontalCenter:parent.horizontalCenter; verticalCenter:undefined} + } + AnchorChanges { + target: belower + anchors{ top:undefined; bottom:parent.top; left:undefined; right:undefined; horizontalCenter:parent.horizontalCenter; verticalCenter:undefined} + } + AnchorChanges { + target: shadowsSvgItem + anchors{ top:belower.top; bottom:undefined; left:undefined; right:undefined; horizontalCenter:parent.horizontalCenter; verticalCenter:undefined} + } + } + ] + //END states +} + diff --git a/containment/contents/ui/VisibilityManager.qml b/containment/contents/ui/VisibilityManager.qml new file mode 100644 index 000000000..07230befa --- /dev/null +++ b/containment/contents/ui/VisibilityManager.qml @@ -0,0 +1,446 @@ +import QtQuick 2.1 +import QtQuick.Window 2.2 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.plasmoid 2.0 + +import org.kde.taskmanager 0.1 as TaskManager + +import org.kde.nowdock 0.1 as NowDock + +Item{ + id: manager + + anchors.fill: parent + + property QtObject window + + property bool inStartup: root.inStartup + property bool normalState : false // this is being set from updateMaskArea + + property int animationSpeed: root.durationTime * 1.2 * units.longDuration + property int length: root.isVertical ? Screen.height : Screen.width //screenGeometry.height : screenGeometry.width + + //it is used in order to not break the calculations for the thickness placement + //especially in automatic icon sizes calculations + property int iconMarginOriginal: 0.12*plasmoid.configuration.iconSize + property int statesLineSizeOriginal: root.nowDock ? Math.ceil( plasmoid.configuration.iconSize/13 ) : 0 + + property int thicknessAutoHidden: 2 + property int thicknessMid: root.statesLineSize + (1 + (0.65 * (root.zoomFactor-1)))*(root.iconSize+root.iconMargin) //needed in some animations + property int thicknessNormal: root.statesLineSize + root.iconSize + root.iconMargin + 1 + property int thicknessZoom: root.statesLineSize + ((root.iconSize+root.iconMargin) * root.zoomFactor) + 2 + //it is used to keep thickness solid e.g. when iconSize changes from auto functions + property int thicknessMidOriginal: statesLineSizeOriginal + (1 + (0.65 * (root.zoomFactor-1)))*(plasmoid.configuration.iconSize+iconMarginOriginal) //needed in some animations + property int thicknessNormalOriginal: root.useThemePanel ? Math.max(thicknessNormalOriginalValue, root.realPanelSize) : thicknessNormalOriginalValue + property int thicknessNormalOriginalValue: statesLineSizeOriginal + plasmoid.configuration.iconSize + iconMarginOriginal + 1 + property int thicknessZoomOriginal: statesLineSizeOriginal + ((plasmoid.configuration.iconSize+iconMarginOriginal) * root.zoomFactor) + 2 + + Binding{ + //this is way to avoid warnings for null during initialization phase + target: window ? window.visibility : manager + property:"panelVisibility" + when: window && window.visibility + value: plasmoid.configuration.panelVisibility + } + + Binding{ + target: window + property:"maxThickness" + when: window + value: thicknessZoomOriginal + } + + onWindowChanged: { + if(window) { + window.visibility.onDisableHidingChanged.connect(slotDisableHidingChanged); + window.visibility.onIsHoveredChanged.connect(slotIsHoveredChanged); + window.visibility.onMustBeLowered.connect(slotMustBeLowered); + window.visibility.onMustBeRaised.connect(slotMustBeRaised); + window.visibility.onMustBeRaisedImmediately.connect(slotMustBeRaisedImmediately); + window.visibility.onPanelVisibilityChanged.connect(slotPanelVisibilityChanged); + } + } + + onInStartupChanged: { + if (!inStartup) { + delayAnimationTimer.start(); + } + } + + onNormalStateChanged: { + if(normalState && nowDock) { + nowDock.publishTasksGeometries(); + } + + if (normalState) { + root.updateAutomaticIconSize(); + } + } + + onThicknessZoomOriginalChanged: updateMaskArea(); + + + function slotDisableHidingChanged() { + if (!window.disableHiding) { + checkListHovered.restart(); + } + } + + function slotIsHoveredChanged() { + if(window.isHovered) { + //stop parent window timer for auto hiding + if ((window.visibility.panelVisibility === NowDock.PanelWindow.AutoHide)|| window.visibility.isDockWindowType) { + if(hideMagicWindowInAutoHide.forcedDisableHiding) { + hideMagicWindowInAutoHide.forcedDisableHiding = false; + window.visibility.disableHiding = false; + } + + hideMagicWindowInAutoHide.stop(); + } + + if (delayerTimer.running) { + delayerTimer.stop(); + } + + updateMaskArea(); + } else { + // initialize the zoom + delayerTimer.start(); + } + } + + function slotMustBeRaised() { + if ((window.visibility.panelVisibility === NowDock.PanelWindow.AutoHide) || window.visibility.isDockWindowType) { + slidingAnimationAutoHiddenIn.init(); + } else { + slidingAnimation.init(true,false); + } + } + + function slotMustBeRaisedImmediately() { + slidingAnimation.init(true,true); + } + + function slotMustBeLowered() { + if ((window.visibility.panelVisibility === NowDock.PanelWindow.AutoHide) || window.visibility.isDockWindowType ) { + slidingAnimationAutoHiddenOut.init(); + } else { + slidingAnimation.init(false,false); + } + } + + function slotPanelVisibilityChanged() { + if (window.visibility.panelVisibility !== NowDock.PanelWindow.AutoHide) { + window.visibility.isAutoHidden = false; + } + } + + ///test maskArea + function updateMaskArea() { + if (!windowSystem.compositingActive || !window) { + return; + } + + var localX = 0; + var localY = 0; + + normalState = (root.nowDockHoveredIndex === -1) && (layoutsContainer.hoveredIndex === -1) + && (root.appletsAnimations === 0) + && (root.animationsNeedBothAxis === 0) && (root.animationsNeedLength === 0) + && (!mainLayout.animatedLength) + + // debug maskArea criteria + // console.log(root.nowDockHoveredIndex + ", " + layoutsContainer.hoveredIndex + ", " + // + root.appletsAnimations+ ", " + // + root.animationsNeedBothAxis + ", " + root.animationsNeedLength + ", " + root.animationsNeedThickness +", " + // + mainLayout.animatedLength); + + var tempLength = root.isHorizontal ? width : height; + var tempThickness = root.isHorizontal ? height : width; + + var space = root.panelEdgeSpacing + 10; + + if (normalState && plasmoid.immutable) { + //count panel length + if(root.isHorizontal) { + tempLength = plasmoid.configuration.panelPosition === NowDock.Types.Double ? layoutsContainer.width + 0.5*space : mainLayout.width + space; + } else { + tempLength = plasmoid.configuration.panelPosition === NowDock.Types.Double ? layoutsContainer.height + 0.5*space : mainLayout.height + space; + } + + tempThickness = thicknessNormalOriginal; + + if (root.animationsNeedThickness > 0) { + tempThickness = thicknessMidOriginal; + } + + if (window.visibility.isAutoHidden && ((window.visibility.panelVisibility === NowDock.Types.AutoHide) || window.visibility.isDockWindowType)) { + tempThickness = thicknessAutoHidden; + } + + //configure x,y based on plasmoid position and root.panelAlignment(Alignment) + if ((plasmoid.location === PlasmaCore.Types.BottomEdge) || (plasmoid.location === PlasmaCore.Types.TopEdge)) { + if (plasmoid.location === PlasmaCore.Types.BottomEdge) { + localY = window.height - tempThickness; + } else if (plasmoid.location === PlasmaCore.Types.TopEdge) { + localY = 0; + } + + if (plasmoid.configuration.panelPosition === NowDock.Types.Double) { + localX = (window.width/2) - (layoutsContainer.width/2) - 0.25*space; + } else if (root.panelAlignment === NowDock.Types.Left) { + localX = 0; + } else if (root.panelAlignment === NowDock.Types.Center) { + localX = (window.width/2) - (mainLayout.width/2) - (space/2); + } else if (root.panelAlignment === NowDock.Types.Right) { + localX = window.width - mainLayout.width - (space/2); + } + } else if ((plasmoid.location === PlasmaCore.Types.LeftEdge) || (plasmoid.location === PlasmaCore.Types.RightEdge)){ + if (plasmoid.location === PlasmaCore.Types.LeftEdge) { + localX = 0; + } else if (plasmoid.location === PlasmaCore.Types.RightEdge) { + localX = window.width - tempThickness; + } + + if (plasmoid.configuration.panelPosition === NowDock.Types.Double) { + localY = (window.height/2) - (layoutsContainer.height/2) - 0.25*space; + } else if (root.panelAlignment === NowDock.Types.Top) { + localY = 0; + } else if (root.panelAlignment === NowDock.Types.Center) { + localY = (window.height/2) - (mainLayout.height/2) - (space/2); + } else if (root.panelAlignment === NowDock.Types.Bottom) { + localY = window.height - mainLayout.height - (space/2); + } + } + } else { + if(root.isHorizontal) + tempLength = Screen.width; //screenGeometry.width; + else + tempLength = Screen.height; //screenGeometry.height; + + //grow only on length and not thickness + if(mainLayout.animatedLength || !plasmoid.immutable) { + tempThickness = thicknessNormalOriginal; + + if (root.animationsNeedThickness > 0) { + tempThickness = thicknessMidOriginal; + } + + //configure the x,y position based on thickness + if(plasmoid.location === PlasmaCore.Types.RightEdge) + localX = window.width - tempThickness; + else if(plasmoid.location === PlasmaCore.Types.BottomEdge) + localY = window.height - tempThickness; + } else{ + //use all thickness space + tempThickness = thicknessZoomOriginal; + } + } + var maskArea = window.maskArea; + + var maskLength = maskArea.width; //in Horizontal + if (root.isVertical) { + maskLength = maskArea.height; + } + + var maskThickness = maskArea.height; //in Horizontal + if (root.isVertical) { + maskThickness = maskArea.width; + } + + // console.log("Not updating mask..."); + if( maskArea.x !== localX || maskArea.y !== localY + || maskLength !== tempLength || maskThickness !== tempThickness) { + + // console.log("Updating mask..."); + var newMaskArea = Qt.rect(-1,-1,0,0); + newMaskArea.x = localX; + newMaskArea.y = localY; + + if (isHorizontal) { + newMaskArea.width = tempLength; + newMaskArea.height = tempThickness; + } else { + newMaskArea.width = tempThickness; + newMaskArea.height = tempLength; + } + + window.maskArea = newMaskArea; + } + } + + Loader{ + anchors.fill: parent + active: root.debugMode + + sourceComponent: Item{ + anchors.fill:parent + + Rectangle{ + id: windowBackground + anchors.fill: parent + border.color: "red" + border.width: 1 + color: "transparent" + } + + Rectangle{ + x: window ? window.maskArea.x : 0 + y: window ? window.maskArea.y : 0 + height: window ? window.maskArea.height : -1 + width: window ? window.maskArea.width : -1 + + border.color: "green" + border.width: 1 + color: "transparent" + } + } + } + + /***Hiding/Showing Animations*****/ + + SequentialAnimation{ + id: slidingAnimation + + property bool inHalf: false + property bool raiseFlag: false + property bool immediateShow: false + + SequentialAnimation{ + PropertyAnimation { + target: layoutsContainer + property: root.isVertical ? "x" : "y" + to: ((plasmoid.location===PlasmaCore.Types.LeftEdge)||(plasmoid.location===PlasmaCore.Types.TopEdge)) ? -thicknessNormal : thicknessNormal + duration: slidingAnimation.immediateShow ? 100 : manager.animationSpeed + easing.type: Easing.OutQuad + } + + PropertyAnimation { + target: slidingAnimation + property: "inHalf" + to: true + duration: 100 + } + + PropertyAnimation { + target: layoutsContainer + property: root.isVertical ? "x" : "y" + to: 0 + duration: manager.animationSpeed + easing.type: Easing.OutQuad + } + } + + onStopped: { + inHalf = false; + raiseFlag = false; + immediateShow = false; + } + + onInHalfChanged: { + if (inHalf) { + if (raiseFlag) { + window.visibility.showOnTop(); + } else { + window.visibility.showOnBottom(); + } + } + } + + function init(raise, immediate) { + // if(window.visible) { + raiseFlag = raise; + immediateShow = immediate; + start(); + // } + } + } + //////////////// Auto Hide Animations - Slide In - Out + SequentialAnimation{ + id: slidingAnimationAutoHiddenOut + + PropertyAnimation { + target: layoutsContainer + property: root.isVertical ? "x" : "y" + to: ((plasmoid.location===PlasmaCore.Types.LeftEdge)||(plasmoid.location===PlasmaCore.Types.TopEdge)) ? -thicknessNormal : thicknessNormal + duration: manager.animationSpeed + easing.type: Easing.OutQuad + } + + onStopped: { + window.visibility.isAutoHidden = true; + updateMaskArea(); + } + + function init() { + start(); + } + } + + SequentialAnimation{ + id: slidingAnimationAutoHiddenIn + + PropertyAnimation { + target: layoutsContainer + property: root.isVertical ? "x" : "y" + to: 0 + duration: manager.animationSpeed + easing.type: Easing.OutQuad + } + + onStopped: { + if (!plasmoid.immutable) { + // updateTransientThickness(); + } + } + + function init() { + window.visibility.isAutoHidden = false; + updateMaskArea(); + start(); + } + } + + ///////////// External Connections ////// + TaskManager.ActivityInfo { + onCurrentActivityChanged: { + window.visibility.disableHiding = true; + + if (window.visibility.isAutoHidden) { + window.visibility.mustBeRaised(); + } + + hideMagicWindowInAutoHide.forcedDisableHiding = true; + hideMagicWindowInAutoHide.start(); + } + } + + ////////////// Timers ////// + //Timer to delay onLeave event + Timer { + id: delayerTimer + interval: 400 + onTriggered: { + root.clearZoom(); + if (root.nowDock) { + nowDock.clearZoom(); + } + } + } + + //Timer to delay onLeave event + Timer { + id: delayAnimationTimer + interval: manager.inStartup ? 1000 : 500 + onTriggered: { + layoutsContainer.opacity = 1; + if ((window.visibility.panelVisibility !== NowDock.PanelWindow.AutoHide) && !window.visibility.isDockWindowType) { + slidingAnimation.init(true,false); + } else { + slidingAnimationAutoHiddenIn.init(); + } + } + } + +} diff --git a/containment/contents/ui/main.qml b/containment/contents/ui/main.qml new file mode 100644 index 000000000..7718a7954 --- /dev/null +++ b/containment/contents/ui/main.qml @@ -0,0 +1,1295 @@ +/* + * Copyright 2013 Michail Vourlakos <mvourlakos@gmail.com> + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.1 +import QtQuick.Layouts 1.1 +import QtQuick.Window 2.2 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.kquickcontrolsaddons 2.0 +import org.kde.draganddrop 2.0 as DragDrop +import org.kde.plasma.plasmoid 2.0 + +import org.kde.nowdock 0.1 as NowDock + +import "LayoutManager.js" as LayoutManager + +DragDrop.DropArea { + id: root + objectName: "dockLayoutView" + + //// BEGIN SIGNALS + signal clearZoomSignal(); + signal updateIndexes(); + //// + + ////BEGIN properties + property bool debugMode: true + + property bool automaticSize: plasmoid.configuration.automaticIconSize + property bool immutable: plasmoid.immutable + property bool inStartup: true + property bool isHorizontal: plasmoid.formFactor == PlasmaCore.Types.Horizontal + property bool isVertical: !isHorizontal + property bool isHovered: nowDock ? ((nowDockHoveredIndex !== -1) && (layoutsContainer.hoveredIndex !== -1)) //|| wholeArea.containsMouse + : (layoutsContainer.hoveredIndex !== -1) || wholeArea.containsMouse + property bool normalState : false + property bool onlyAddingStarup: true //is used for the initialization phase in startup where there arent removals, this variable provides a way to grow icon size + //FIXME: possibly this is going to be the default behavior, this user choice + //has been dropped from the Dock Configuration Window + //property bool smallAutomaticIconJumps: plasmoid.configuration.smallAutomaticIconJumps + property bool smallAutomaticIconJumps: true + property bool useThemePanel: noApplets === 0 ? true : plasmoid.configuration.useThemePanel + + + property int animationsNeedBothAxis:0 //animations need space in both axes, e.g zooming a task + property int animationsNeedLength: 0 // animations need length, e.g. adding a task + property int animationsNeedThickness: 0 // animations need thickness, e.g. bouncing animation + property int appletsAnimations: 0 //zoomed applets it is used basically on masking for magic window + property int automaticIconSizeBasedSize: -1 //it is not set, this is the defautl + property int iconSize: (automaticIconSizeBasedSize > 0 && plasmoid.immutable) ? Math.min(automaticIconSizeBasedSize, plasmoid.configuration.iconSize) : + plasmoid.configuration.iconSize + property int iconStep: 8 + property int panelEdgeSpacing: iconSize / 3 + //FIXME: this is not needed any more probably + property int previousAllTasks: -1 //is used to forbit updateAutomaticIconSize when hovering + property int realSize: iconSize + iconMargin + property int realPanelSize + property int themePanelSize: plasmoid.configuration.panelSize + + ///FIXME: <delete> I can't remember why this is needed, maybe for the anchorings!!! In order for the Double Layout to not mess the anchorings... + property int mainLayoutPosition: !plasmoid.immutable ? NowDock.Types.Center : (root.isVertical ? NowDock.Types.Top : NowDock.Types.Left) + ///FIXME: <delete> + //property int panelAlignment: plasmoid.configuration.panelPosition !== NowDock.Types.Double ? plasmoid.configuration.panelPosition : mainLayoutPosition + + property int panelAlignment: plasmoid.immutable ? plasmoid.configuration.panelPosition : NowDock.Types.Center + // property int panelAlignment: plasmoid.configuration.panelPosition + + + property real zoomFactor: windowSystem.compositingActive ? ( 1 + (plasmoid.configuration.zoomLevel / 20) ) : 1 + + + property var iconsArray: [16, 22, 32, 48, 64, 96, 128, 256] + property var layoutManager: LayoutManager + + property Item dragOverlay + property Item toolBox + property Item nowDockContainer + property Item nowDock + property QtObject dockView + + // TO BE DELETED, if not needed: property int counter:0; + + ///BEGIN properties from nowDock + property bool reverseLinesPosition: nowDock ? nowDock.reverseLinesPosition : false + + property int durationTime: nowDock ? nowDock.durationTime : 2 + property int nowDockHoveredIndex: nowDock ? nowDock.hoveredIndex : -1 + property int iconMargin: nowDock ? nowDock.iconMargin : 0.12 * iconSize + property int statesLineSize: nowDock ? nowDock.statesLineSize : 0 + property int tasksCount: nowDock ? nowDock.tasksCount : 0 + ///END properties from nowDock + + /* Layout.preferredWidth: plasmoid.immutable ? + (plasmoid.configuration.panelPosition === NowDock.Types.Double ? + layoutsContainer.width + 0.5*iconMargin : mainLayout.width + iconMargin) : + Screen.width //on unlocked state use the maximum + + Layout.preferredHeight: plasmoid.immutable ? + (plasmoid.configuration.panelPosition === NowDock.Types.Double ? + layoutsContainer.height + 0.5*iconMargin : mainLayout.height + iconMargin) : + Screen.height //on unlocked state use the maximum*/ + + Plasmoid.backgroundHints: PlasmaCore.Types.NoBackground + + //// BEGIN properties in functions + property int noApplets: { + var count1 = 0; + var count2 = 0; + + count1 = mainLayout.children.length; + var tempLength = mainLayout.children.length; + + for (var i=tempLength-1; i>=0; --i) { + var applet = mainLayout.children[i]; + if (applet && (applet === dndSpacer || applet === lastSpacer || applet.isInternalViewSplitter)) + count1--; + } + + count2 = secondLayout.children.length; + tempLength = secondLayout.children.length; + + for (var i=tempLength-1; i>=0; --i) { + var applet = secondLayout.children[i]; + if (applet && (applet === dndSpacer || applet === lastSpacer || applet.isInternalViewSplitter)) + count2--; + } + + return (count1 + count2); + } + + ///The index of user's current icon size + property int currentIconIndex:{ + for(var i=iconsArray.length-1; i>=0; --i){ + if(iconsArray[i] === iconSize){ + return i; + } + } + return 3; + } + + //// END properties in functions + + ////////////////END properties + + //////////////////////////BEGIN states + //user set Panel Positions + // 0-Center, 1-Left, 2-Right, 3-Top, 4-Bottom + states: [ + ///Left Edge + State { + name: "leftCenter" + when: (plasmoid.location === PlasmaCore.Types.LeftEdge)&&(root.panelAlignment === NowDock.Types.Center) + + AnchorChanges { + target: mainLayout + anchors{ top:undefined; bottom:undefined; left:parent.left; right:undefined; horizontalCenter:undefined; verticalCenter:parent.verticalCenter} + } + PropertyChanges{ + target: mainLayout; horizontalItemAlignment: Grid.AlignLeft; verticalItemAlignment: Grid.AlignVCenter; + anchors.leftMargin: 0; anchors.rightMargin:0; anchors.topMargin:0; anchors.bottomMargin:0; + } + }, + State { + name: "leftTop" + when: (plasmoid.location === PlasmaCore.Types.LeftEdge)&&(root.panelAlignment === NowDock.Types.Top) + + AnchorChanges { + target: mainLayout + anchors{ top:parent.top; bottom:undefined; left:parent.left; right:undefined; horizontalCenter:undefined; verticalCenter:undefined} + } + PropertyChanges{ + target: mainLayout; horizontalItemAlignment: Grid.AlignLeft; verticalItemAlignment: Grid.AlignVCenter; + anchors.leftMargin: 0; anchors.rightMargin:0; anchors.topMargin:0; anchors.bottomMargin:0; + } + }, + State { + name: "leftBottom" + when: (plasmoid.location === PlasmaCore.Types.LeftEdge)&&(root.panelAlignment === NowDock.Types.Bottom) + + AnchorChanges { + target: mainLayout + anchors{ top:undefined; bottom:parent.bottom; left:parent.left; right:undefined; horizontalCenter:undefined; verticalCenter:undefined} + } + PropertyChanges{ + target: mainLayout; horizontalItemAlignment: Grid.AlignLeft; verticalItemAlignment: Grid.AlignVCenter; + anchors.leftMargin: 0; anchors.rightMargin:0; anchors.topMargin:0; anchors.bottomMargin:0; + } + }, + State { + name: "leftDouble" + when: (plasmoid.location === PlasmaCore.Types.LeftEdge)&&(root.panelAlignment === NowDock.Types.Double) + + AnchorChanges { + target: mainLayout + anchors{ top:parent.top; bottom:undefined; left:parent.left; right:undefined; horizontalCenter:undefined; verticalCenter:undefined} + } + PropertyChanges{ + target: mainLayout; horizontalItemAlignment: Grid.AlignLeft; verticalItemAlignment: Grid.AlignVCenter; + anchors.leftMargin: 0; anchors.rightMargin:0; anchors.topMargin:panelEdgeSpacing/2; anchors.bottomMargin:0; + } + }, + ///Right Edge + State { + name: "rightCenter" + when: (plasmoid.location === PlasmaCore.Types.RightEdge)&&(root.panelAlignment === NowDock.Types.Center) + + AnchorChanges { + target: mainLayout + anchors{ top:undefined; bottom:undefined; left:undefined; right:parent.right; horizontalCenter:undefined; verticalCenter:parent.verticalCenter} + } + PropertyChanges{ + target: mainLayout; horizontalItemAlignment: Grid.AlignRight; verticalItemAlignment: Grid.AlignVCenter; + anchors.leftMargin: 0; anchors.rightMargin:0; anchors.topMargin:0; anchors.bottomMargin:0; + } + }, + State { + name: "rightTop" + when: (plasmoid.location === PlasmaCore.Types.RightEdge)&&(root.panelAlignment === NowDock.Types.Top) + + AnchorChanges { + target: mainLayout + anchors{ top:parent.top; bottom:undefined; left:undefined; right:parent.right; horizontalCenter:undefined; verticalCenter:undefined} + } + PropertyChanges{ + target: mainLayout; horizontalItemAlignment: Grid.AlignRight; verticalItemAlignment: Grid.AlignVCenter; + anchors.leftMargin: 0; anchors.rightMargin:0; anchors.topMargin:0; anchors.bottomMargin:0; + } + }, + State { + name: "rightBottom" + when: (plasmoid.location === PlasmaCore.Types.RightEdge)&&(root.panelAlignment === NowDock.Types.Bottom) + + AnchorChanges { + target: mainLayout + anchors{ top:undefined; bottom:parent.bottom; left:undefined; right:parent.right; horizontalCenter:undefined; verticalCenter:undefined} + } + PropertyChanges{ + target: mainLayout; horizontalItemAlignment: Grid.AlignRight; verticalItemAlignment: Grid.AlignVCenter; + anchors.leftMargin: 0; anchors.rightMargin:0; anchors.topMargin:0; anchors.bottomMargin:0; + } + }, + State { + name: "rightDouble" + when: (plasmoid.location === PlasmaCore.Types.RightEdge)&&(root.panelAlignment === NowDock.Types.Double) + + AnchorChanges { + target: mainLayout + anchors{ top:parent.top; bottom:undefined; left:undefined; right:parent.right; horizontalCenter:undefined; verticalCenter:undefined} + } + PropertyChanges{ + target: mainLayout; horizontalItemAlignment: Grid.AlignRight; verticalItemAlignment: Grid.AlignVCenter; + anchors.leftMargin: 0; anchors.rightMargin:0; anchors.topMargin:panelEdgeSpacing/2; anchors.bottomMargin:0; + } + }, + ///Bottom Edge + State { + name: "bottomCenter" + when: (plasmoid.location === PlasmaCore.Types.BottomEdge)&&(root.panelAlignment === NowDock.Types.Center) + + AnchorChanges { + target: mainLayout + anchors{ top:undefined; bottom:parent.bottom; left:undefined; right:undefined; horizontalCenter:parent.horizontalCenter; verticalCenter:undefined} + } + PropertyChanges{ + target: mainLayout; horizontalItemAlignment: Grid.AlignHCenter; verticalItemAlignment: Grid.AlignBottom + anchors.leftMargin: 0; anchors.rightMargin:0; anchors.topMargin:0; anchors.bottomMargin:0; + } + }, + State { + name: "bottomLeft" + when: (plasmoid.location === PlasmaCore.Types.BottomEdge)&&(root.panelAlignment === NowDock.Types.Left) + + AnchorChanges { + target: mainLayout + anchors{ top:undefined; bottom:parent.bottom; left:parent.left; right:undefined; horizontalCenter:undefined; verticalCenter:undefined} + } + PropertyChanges{ + target: mainLayout; horizontalItemAlignment: Grid.AlignHCenter; verticalItemAlignment: Grid.AlignBottom + anchors.leftMargin: 0; anchors.rightMargin:0; anchors.topMargin:0; anchors.bottomMargin:0; + } + }, + State { + name: "bottomRight" + when: (plasmoid.location === PlasmaCore.Types.BottomEdge)&&(root.panelAlignment === NowDock.Types.Right) + + AnchorChanges { + target: mainLayout + anchors{ top:undefined; bottom:parent.bottom; left:undefined; right:parent.right; horizontalCenter:undefined; verticalCenter:undefined} + } + PropertyChanges{ + target: mainLayout; horizontalItemAlignment: Grid.AlignHCenter; verticalItemAlignment: Grid.AlignBottom + anchors.leftMargin: 0; anchors.rightMargin:0; anchors.topMargin:0; anchors.bottomMargin:0; + } + }, + State { + name: "bottomDouble" + when: (plasmoid.location === PlasmaCore.Types.BottomEdge)&&(root.panelAlignment === NowDock.Types.Double) + + AnchorChanges { + target: mainLayout + anchors{ top:undefined; bottom:parent.bottom; left:parent.left; right:undefined; horizontalCenter:undefined; verticalCenter:undefined} + } + PropertyChanges{ + target: mainLayout; horizontalItemAlignment: Grid.AlignHCenter; verticalItemAlignment: Grid.AlignBottom + anchors.leftMargin: panelEdgeSpacing/2; anchors.rightMargin:0; anchors.topMargin:0; anchors.bottomMargin:0; + } + }, + ///Top Edge + State { + name: "topCenter" + when: (plasmoid.location === PlasmaCore.Types.TopEdge)&&(root.panelAlignment === NowDock.Types.Center) + + AnchorChanges { + target: mainLayout + anchors{ top:parent.top; bottom:undefined; left:undefined; right:undefined; horizontalCenter:parent.horizontalCenter; verticalCenter:undefined} + } + PropertyChanges{ + target: mainLayout; horizontalItemAlignment: Grid.AlignHCenter; verticalItemAlignment: Grid.AlignTop + anchors.leftMargin: 0; anchors.rightMargin:0; anchors.topMargin:0; anchors.bottomMargin:0; + } + }, + State { + name: "topLeft" + when: (plasmoid.location === PlasmaCore.Types.TopEdge)&&(root.panelAlignment === NowDock.Types.Left) + + AnchorChanges { + target: mainLayout + anchors{ top:parent.top; bottom:undefined; left:parent.left; right:undefined; horizontalCenter:undefined; verticalCenter:undefined} + } + PropertyChanges{ + target: mainLayout; horizontalItemAlignment: Grid.AlignHCenter; verticalItemAlignment: Grid.AlignTop + anchors.leftMargin: 0; anchors.rightMargin:0; anchors.topMargin:0; anchors.bottomMargin:0; + } + }, + State { + name: "topRight" + when: (plasmoid.location === PlasmaCore.Types.TopEdge)&&(root.panelAlignment === NowDock.Types.Right) + + AnchorChanges { + target: mainLayout + anchors{ top:parent.top; bottom:undefined; left:undefined; right:parent.right; horizontalCenter:undefined; verticalCenter:undefined} + } + PropertyChanges{ + target: mainLayout; horizontalItemAlignment: Grid.AlignHCenter; verticalItemAlignment: Grid.AlignTop + anchors.leftMargin: 0; anchors.rightMargin:0; anchors.topMargin:0; anchors.bottomMargin:0; + } + }, + State { + name: "topDouble" + when: (plasmoid.location === PlasmaCore.Types.TopEdge)&&(root.panelAlignment === NowDock.Types.Double) + + AnchorChanges { + target: mainLayout + anchors{ top:parent.top; bottom:undefined; left:parent.left; right:undefined; horizontalCenter:undefined; verticalCenter:undefined} + } + PropertyChanges{ + target: mainLayout; horizontalItemAlignment: Grid.AlignHCenter; verticalItemAlignment: Grid.AlignTop + anchors.leftMargin: panelEdgeSpacing/2; anchors.rightMargin:0; anchors.topMargin:0; anchors.bottomMargin:0; + } + } + ] + ////////////////END states + + + //// BEGIN OF Behaviors + Behavior on iconSize { + NumberAnimation { duration: 200 } + } + //// END OF Behaviors + + //////////////START OF CONNECTIONS + onAppletsAnimationsChanged: visibilityManager.updateMaskArea(); + + onDragEnter: { + if (plasmoid.immutable) { + event.ignore(); + return; + } + + var relevantLayout = mainLayout.mapFromItem(root, event.x, event.y); + LayoutManager.insertAtCoordinates(dndSpacer, relevantLayout.x, relevantLayout.y) + dndSpacer.opacity = 1; + } + + onDragMove: { + var relevantLayout = mainLayout.mapFromItem(root, event.x, event.y); + LayoutManager.insertAtCoordinates(dndSpacer, relevantLayout.x, relevantLayout.y) + dndSpacer.opacity = 1; + } + + onDragLeave: { + dndSpacer.opacity = 0; + dndSpacer.parent = root; + } + + onDrop: { + var relevantLayout = mainLayout.mapFromItem(root, event.x, event.y); + plasmoid.processMimeData(event.mimeData, relevantLayout.x, relevantLayout.y); + event.accept(event.proposedAction); + + dndSpacer.opacity = 0; + containmentSizeSyncTimer.restart(); + } + + onIsHoveredChanged: { + if (isHovered){ + dockView.visibility.showOnTopCheck(); + } + } + + onHeightChanged: { + containmentSizeSyncTimer.restart() + if (startupTimer.running) { + startupTimer.restart(); + } + } + + onNowDockChanged: { + if (nowDock) { + nowDock.signalAnimationsNeedBothAxis.connect(slotAnimationsNeedBothAxis); + nowDock.signalAnimationsNeedLength.connect(slotAnimationsNeedLength); + nowDock.signalAnimationsNeedThickness.connect(slotAnimationsNeedThickness); + nowDock.signalDraggingState.connect(slotDisableHiding); + } + } + + onToolBoxChanged: { + containmentSizeSyncTimer.restart(); + if (startupTimer.running) { + startupTimer.restart(); + } + } + + onWidthChanged: { + containmentSizeSyncTimer.restart() + if (startupTimer.running) { + startupTimer.restart(); + } + } + + // onIconSizeChanged: console.log("Icon Size Changed:"+iconSize); + + Component.onCompleted: { + // currentLayout.isLayoutHorizontal = isHorizontal + LayoutManager.plasmoid = plasmoid; + LayoutManager.root = root; + LayoutManager.layout = mainLayout; + LayoutManager.lastSpacer = lastSpacer; + LayoutManager.restore(); + containmentSizeSyncTimer.restart(); + plasmoid.action("configure").visible = !plasmoid.immutable; + plasmoid.action("configure").enabled = !plasmoid.immutable; + + if (!plasmoid.immutable) { + inStartup = false; + } + } + + Component.onDestruction: { + console.log("Destroying Now Dock Panel..."); + } + + Containment.onAppletAdded: { + addApplet(applet, x, y); + LayoutManager.save(); + } + + Containment.onAppletRemoved: { + LayoutManager.removeApplet(applet); + var flexibleFound = false; + for (var i = 0; i < mainLayout.children.length; ++i) { + var applet = mainLayout.children[i].applet; + if (applet && ((root.isHorizontal && applet.Layout.fillWidth) || + (!root.isHorizontal && applet.Layout.fillHeight)) && + applet.visible) { + flexibleFound = true; + break + } + } + if (!flexibleFound) { + lastSpacer.parent = mainLayout; + } + + LayoutManager.save(); + // magicWin.removeAppletItem(applet); + } + + Plasmoid.onUserConfiguringChanged: { + console.log("user configuring:"+plasmoid.userConfiguring + " immutable:"+plasmoid.immutable); + if (plasmoid.immutable) { + if (dragOverlay) { + dragOverlay.destroy(); + } + return; + } + + if (plasmoid.userConfiguring) { + console.log("applets------"); + for (var i = 0; i < plasmoid.applets.length; ++i) { + console.log("applet:"+i); + plasmoid.applets[i].expanded = false; + } + if (!dragOverlay) { + var component = Qt.createComponent("ConfigOverlay.qml"); + if (component.status == Component.Ready) { + dragOverlay = component.createObject(root); + } else { + console.log("Could not create ConfigOverlay"); + console.log(component.errorString()); + } + component.destroy(); + } else { + dragOverlay.visible = true; + } + } else { + if (dragOverlay) { + dragOverlay.visible = false; + dragOverlay.destroy(); + } + } + } + + Plasmoid.onFormFactorChanged: containmentSizeSyncTimer.restart(); + + Plasmoid.onImmutableChanged: { + console.log("plasmoid immutable state: "+plasmoid.immutable); + containmentSizeSyncTimer.restart(); + plasmoid.action("configure").visible = !plasmoid.immutable; + plasmoid.action("configure").enabled = !plasmoid.immutable; + + updateLayouts(); + + ///Set Preferred Sizes/// + ///Notice: they are set here because if they are set with a binding + ///they break the !immutable experience, the dock becomes too small + ///to add applets + if (plasmoid.immutable) { + if(root.isHorizontal) { + root.Layout.preferredWidth = (plasmoid.configuration.panelPosition === NowDock.Types.Double ? + layoutsContainer.width + 0.5*iconMargin : mainLayout.width + iconMargin); + } else { + root.Layout.preferredHeight = (plasmoid.configuration.panelPosition === NowDock.Types.Double ? + layoutsContainer.height + 0.5*iconMargin : mainLayout.height + iconMargin); + } + } else { + if (root.isHorizontal) { + root.Layout.preferredWidth = Screen.width; + } else { + root.Layout.preferredHeight = Screen.height; + } + } + + if (plasmoid.immutable) { + if (windowSystem.compositingActive) { + // magicWin.initialize(); + } + + dockView.visibility.disableHiding = false; + } else { + dockView.visibility.disableHiding = true; + dockView.visibility.mustBeRaised(); + } + + + visibilityManager.updateMaskArea(); + + // console.log(magicWin.visible + " - "+magicWin.x+" - " + magicWin.y+" - "+magicWin.width+" - "+magicWin.height); + /* if (magicWin) { + if (plasmoid.immutable) { + if (windowSystem.compositingActive) { + magicWin.initialize(); + } else { + magicWin.updateTransientThickness(); + } + magicWin.disableHiding = false; + } else { + magicWin.disableHiding = true; + magicWin.mustBeRaised(); + } + }*/ + } + //////////////END OF CONNECTIONS + + //////////////START OF FUNCTIONS + function addApplet(applet, x, y) { + var container = appletContainerComponent.createObject(root) + + container.applet = applet; + applet.parent = container.appletWrapper; + + applet.anchors.fill = container.appletWrapper; + + applet.visible = true; + + + // don't show applet if it choses to be hidden but still make it + // accessible in the panelcontroller + container.visible = Qt.binding(function() { + return applet.status !== PlasmaCore.Types.HiddenStatus || (!plasmoid.immutable && plasmoid.userConfiguring) + }) + + addContainerInLayout(container, applet, x, y); + + // adding the AppletQuickItem to the Now Dock in order to be + // used for right clicking events + // magicWin.addAppletItem(applet); + } + + function addContainerInLayout(container, applet, x, y){ + // Is there a DND placeholder? Replace it! + if (dndSpacer.parent === mainLayout) { + LayoutManager.insertBefore(dndSpacer, container); + dndSpacer.parent = root; + return; + // If the provided position is valid, use it. + } else if (x >= 0 && y >= 0) { + var index = LayoutManager.insertAtCoordinates(container, x , y); + + // Fall through to determining an appropriate insert position. + } else { + var before = null; + container.animationsEnabled = false; + + if (lastSpacer.parent === mainLayout) { + before = lastSpacer; + } + + // Insert icons to the left of whatever is at the center (usually a Task Manager), + // if it exists. + // FIXME TODO: This is a real-world fix to produce a sensible initial position for + // launcher icons added by launcher menu applets. The basic approach has been used + // since Plasma 1. However, "add launcher to X" is a generic-enough concept and + // frequent-enough occurence that we'd like to abstract it further in the future + // and get rid of the uglyness of parties external to the containment adding applets + // of a specific type, and the containment caring about the applet type. In a better + // system the containment would be informed of requested launchers, and determine by + // itself what it wants to do with that information. + if (!startupTimer.running && applet.pluginName == "org.kde.plasma.icon") { + var middle = mainLayout.childAt(root.width / 2, root.height / 2); + + if (middle) { + before = middle; + } + + // Otherwise if lastSpacer is here, enqueue before it. + } + + if (before) { + LayoutManager.insertBefore(before, container); + + // Fall through to adding at the end. + } else { + container.parent = mainLayout; + } + + //event compress the enable of animations + startupTimer.restart(); + } + + //Important, removes the first children of the mainLayout after the first + //applet has been added + lastSpacer.parent = root; + + updateIndexes(); + } + + function addInternalViewSplitter(pos){ + if(!internalViewSplitterExists()){ + var container = appletContainerComponent.createObject(root); + + container.isInternalViewSplitter = true; + container.visible = true; + + if(pos >=0 ) + layoutManager.insertAtIndex(container, pos); + else + layoutManager.insertAtIndex(container, Math.floor(mainLayout.count / 2)); + + layoutManager.save(); + // addContainerInLayout(container, x, y); + } + } + + function checkLastSpacer() { + lastSpacer.parent = root + + var expands = false; + + if (isHorizontal) { + for (var container in mainLayout.children) { + var item = mainLayout.children[container]; + if (item.Layout && item.Layout.fillWidth) { + expands = true; + } + } + } else { + for (var container in mainLayout.children) { + var item = mainLayout.children[container]; + if (item.Layout && item.Layout.fillHeight) { + expands = true; + } + } + } + if (!expands) { + lastSpacer.parent = mainLayout + } + } + + function checkLayoutsAnimatedLength() { + //After the last animations we must check again after a small delay in order + //to disable the automaticSizeUpdate + if (animatedLengthTimer.running) { + animatedLengthTimer.restart(); + } else { + animatedLengthTimer.start(); + } + + if (!dockView.visibility.isHovered && (root.animationsNeedBothAxis === 0) + && (root.animationsNeedLength===0) && (root.appletsAnimations === 0)) { + mainLayout.animatedLength = true; + } else { + mainLayout.animatedLength = false; + } + + visibilityManager.updateMaskArea(); + } + + function clearZoom(){ + //console.log("Panel clear...."); + if (dockView.visibility.disableHiding) { + return; + } + + layoutsContainer.currentSpot = -1000; + layoutsContainer.hoveredIndex = -1; + root.clearZoomSignal(); + } + + function containsMouse(){ + var result = root.outsideContainsMouse(); + + if(result) + return true; + + if(!result && nowDock && nowDock.outsideContainsMouse()){ + layoutsContainer.hoveredIndex = nowDockContainer.index; + return true; + } + + if (nowDock){ + nowDock.clearZoom(); + } + + return false; + } + + function internalViewSplitterExists(){ + for (var container in mainLayout.children) { + var item = mainLayout.children[container]; + if(item && item.isInternalViewSplitter) + return true; + } + return false; + } + + function outsideContainsMouse(){ + var applets = mainLayout.children; + + for(var i=0; i<applets.length; ++i){ + var applet = applets[i]; + + if(applet && applet.containsMouse){ + return true; + } + } + + ///check second layout also + var applets = secondLayout.children; + + for(var i=0; i<applets.length; ++i){ + var applet = applets[i]; + + if(applet && applet.containsMouse){ + return true; + } + } + + return false; + } + + function removeInternalViewSplitter(){ + for (var container in mainLayout.children) { + var item = mainLayout.children[container]; + if(item && item.isInternalViewSplitter) + item.destroy(); + } + + layoutManager.save(); + } + + function sizeIsFromAutomaticMode(size){ + + for(var i=iconsArray.length-1; i>=0; --i){ + if(iconsArray[i] === size){ + return true; + } + } + + return false; + } + + function slotAnimationsNeedBothAxis(value) { + if (animationsNeedBothAxis === value) { + return; + } + + animationsNeedBothAxis = value; + visibilityManager.updateMaskArea(); + } + + function slotAnimationsNeedLength(value) { + if (animationsNeedLength === value) { + return; + } + + animationsNeedLength = value; + visibilityManager.updateMaskArea(); + } + + function slotAnimationsNeedThickness(value) { + if (animationsNeedThickness === value) { + return; + } + + animationsNeedThickness = value; + visibilityManager.updateMaskArea(); + } + + function slotDisableHiding(value) { + dockView.visibility.disableHiding = value; + } + + function updateAutomaticIconSize() { + if (visibilityManager.normalState && !animatedLengthTimer.running && plasmoid.immutable + && (iconSize===plasmoid.configuration.iconSize || iconSize === automaticIconSizeBasedSize) ) { + var layoutLength; + var maxLength = dockView.maxLength; + // console.log("------Entered check-----"); + + if (root.isVertical) { + layoutLength = (plasmoid.configuration.panelPosition === NowDock.Types.Double) ? + mainLayout.height+secondLayout.height : mainLayout.height + } else { + layoutLength = (plasmoid.configuration.panelPosition === NowDock.Types.Double) ? + mainLayout.width+secondLayout.width : mainLayout.width + } + + var toShrinkLimit = maxLength-(zoomFactor*(iconSize+2*iconMargin)); + var toGrowLimit = maxLength-1.5*(zoomFactor*(iconSize+2*iconMargin)); + + if (layoutLength > toShrinkLimit) { //must shrink + // console.log("step3"); + var nextIconSize = plasmoid.configuration.iconSize; + + do { + nextIconSize = nextIconSize - iconStep; + var factor = nextIconSize / iconSize; + var nextLength = factor * layoutLength; + + } while ( (nextLength>toShrinkLimit) && (nextIconSize !== 16)); + + automaticIconSizeBasedSize = nextIconSize; + // console.log("Step 3 - found:"+automaticIconSizeBasedSize); + } else if ((layoutLength<toGrowLimit + && (iconSize === automaticIconSizeBasedSize)) ) { //must grow probably + // console.log("step4"); + var nextIconSize2 = automaticIconSizeBasedSize; + var foundGoodSize = -1; + + do { + nextIconSize2 = nextIconSize2 + iconStep; + var factor2 = nextIconSize2 / automaticIconSizeBasedSize; + var nextLength2 = factor2 * layoutLength; + + if (nextLength2 < toGrowLimit) { + foundGoodSize = nextIconSize2; + } + } while ( (nextLength2<toGrowLimit) && (nextIconSize2 !== plasmoid.configuration.iconSize )); + + if (foundGoodSize > 0) { + if (foundGoodSize === plasmoid.configuration.iconSize) { + automaticIconSizeBasedSize = -1; + } else { + automaticIconSizeBasedSize = foundGoodSize; + } + // console.log("Step 4 - found:"+automaticIconSizeBasedSize); + } else { + // console.log("Step 4 - did not found..."); + } + } + } + } + + function updateLayouts(){ + if(plasmoid.immutable){ + var splitter = -1; + + var totalChildren = mainLayout.children.length; + for (var i=0; i<totalChildren; ++i) { + var item; + if(splitter === -1) + item = mainLayout.children[i]; + else{ + item = mainLayout.children[splitter+1]; + item.parent = secondLayout; + } + + if(item.isInternalViewSplitter) { + splitter = i; + } + } + } + else{ + var totalChildren2 = secondLayout.children.length; + + for (var i=totalChildren2-1; i>=0; --i) { + var item2 = secondLayout.children[0]; + item2.parent = mainLayout; + } + } + + updateIndexes(); + } + //END functions + + + ////BEGIN interfaces + NowDock.WindowSystem { + id:windowSystem + } + + ////END interfaces + + ///////////////BEGIN components + Component { + id: appletContainerComponent + AppletItem{} + } + ///////////////END components + + ///////////////BEGIN UI elements + Item { + id: lastSpacer + parent: mainLayout + + Layout.fillWidth: true + Layout.fillHeight: true + + Rectangle{ + anchors.fill: parent + color: "transparent" + border.color: "yellow" + border.width: 1 + } + } + + Item { + id: dndSpacer + + property int normalSize: root.statesLineSizeOriginal + plasmoid.configuration.iconSize + root.iconMarginOriginal - 1 + + width: normalSize + height: normalSize + + Layout.preferredWidth: width + Layout.preferredHeight: height + opacity: 0 + + AddWidgetVisual{} + } + + Loader{ + anchors.fill: parent + active: root.debugMode + + sourceComponent: Item{ + Rectangle{ + anchors.fill: parent + color: "yellow" + opacity: 0.30 + } + } + } + + /* MouseArea{ + id: wholeArea + anchors.fill: parent + hoverEnabled: true + onEntered: { + showWindow(); + } + + onExited: { + if (plasmoid.immutable && magicWin && !magicWin.isHovered + && ((magicWin.panelVisibility === NowDock.Types.AutoHide) || magicWin.isDockWindowType) ) { + hideMagicWindowInAutoHide.start(); + } + } + + onPositionChanged: { + showWindow(); + } + + function showWindow() { + if (plasmoid.immutable && magicWin + && ((magicWin.panelVisibility === NowDock.Types.AutoHide) || magicWin.isDockWindowType) ) { + magicWin.updateMaskArea(); + magicWin.mustBeRaised(); + } else { + magicWin.showOnTopCheck(); + } + } + }*/ + + VisibilityManager{ + id: visibilityManager + + window: dockView + } + + Item{ + id: layoutsContainer + + signal updateScale(int delegateIndex, real newScale, real step) + // property bool parentMagicWinFlag: plasmoid.immutable && magicWin && !root.inStartup && windowSystem.compositingActive + //&& !(root.inStartup && magicWin.panelVisibility === NowDock.Types.AutoHide) + + property int allCount: root.nowDock ? mainLayout.count-1+nowDock.tasksCount : mainLayout.count + property int currentSpot: -1000 + property int hoveredIndex: -1 + + x: (plasmoid.configuration.panelPosition === NowDock.Types.Double) && root.isHorizontal + && plasmoid.immutable && windowSystem.compositingActive ? + (dockView.width/2) - (dockView.visibility.maxLength/2): 0 + y: (plasmoid.configuration.panelPosition === NowDock.Types.Double) && root.isVertical + && plasmoid.immutable && windowSystem.compositingActive ? + (dockView.height/2) - (dockView.visibility.maxLength/2): 0 + width: (plasmoid.configuration.panelPosition === NowDock.Types.Double) && root.isHorizontal && plasmoid.immutable ? + dockView.visibility.maxLength : parent.width + height: (plasmoid.configuration.panelPosition === NowDock.Types.Double) && root.isVertical && plasmoid.immutable ? + dockView.visibility.maxLength : parent.height + + Component.onCompleted: { + if(plasmoid.immutable) { + opacity = 0; + } else { + opacity = 1; + } + } + + /* onParentChanged: { + if (magicWin && magicWin.contentItem && (parent === magicWin.contentItem)) { + magicWin.updateMaskArea(); + } + } + + onParentMagicWinFlagChanged: { + if (parentMagicWinFlag) { + opacity = 0; + // magicWin.visible = true; + parent = magicWin.contentItem; + magicWin.initializeSlidingInAnimation(); + } else { + parent = root; + if (!windowSystem.compositingActive) { + // magicWin.visible = false; + magicWin.updateTransientThickness(); + } + } + }*/ + + Loader{ + anchors.fill: parent + + // FIX IT && TEST IT: it is crashing Plasma with two Now Docks one of which has only + // task manager (small) + //active: root.useThemePanel + active: windowSystem.compositingActive + + sourceComponent: PanelBox{} + } + + // This is the main Layout, in contrary with the others + Grid{ + id: mainLayout + + columns: root.isVertical ? 1 : 0 + columnSpacing: 0 + flow: isHorizontal ? Grid.LeftToRight : Grid.TopToBottom + rows: root.isHorizontal ? 1 : 0 + rowSpacing: 0 + + + Layout.preferredWidth: width + Layout.preferredHeight: height + + property bool animatedLength: false + property int count: children.length + + onHeightChanged: { + if (root.isVertical && plasmoid.immutable) { + checkLayoutsAnimatedLength(); + } + } + + onWidthChanged: { + if (root.isHorizontal && plasmoid.immutable) { + checkLayoutsAnimatedLength(); + } + } + + } + + Grid{ + id:secondLayout + + columns: root.isVertical ? 1 : 0 + columnSpacing: 0 + flow: isHorizontal ? Grid.LeftToRight : Grid.TopToBottom + rows: root.isHorizontal ? 1 : 0 + rowSpacing: 0 + + + Layout.preferredWidth: width + Layout.preferredHeight: height + + // anchors.right: parent.right + // anchors.bottom: parent.bottom + + property int beginIndex: 100 + property int count: children.length + + states:[ + State { + name: "bottom" + when: (plasmoid.location === PlasmaCore.Types.BottomEdge)&&(root.panelAlignment === NowDock.Types.Double) + + AnchorChanges { + target: secondLayout + anchors{ top:undefined; bottom:parent.bottom; left:undefined; right:parent.right; horizontalCenter:undefined; verticalCenter:undefined} + } + PropertyChanges{ + target: secondLayout; horizontalItemAlignment: Grid.AlignHCenter; verticalItemAlignment: Grid.AlignBottom + anchors.leftMargin: 0; anchors.rightMargin:panelEdgeSpacing/2; anchors.topMargin:0; anchors.bottomMargin:0; + } + }, + State { + name: "left" + when: (plasmoid.location === PlasmaCore.Types.LeftEdge)&&(root.panelAlignment === NowDock.Types.Double) + + AnchorChanges { + target: secondLayout + anchors{ top:undefined; bottom:parent.bottom; left:parent.left; right:undefined; horizontalCenter:undefined; verticalCenter:undefined} + } + PropertyChanges{ + target: secondLayout; horizontalItemAlignment: Grid.AlignLeft; verticalItemAlignment: Grid.AlignVCenter; + anchors.leftMargin: 0; anchors.rightMargin:0; anchors.topMargin:0; anchors.bottomMargin:panelEdgeSpacing/2; + } + }, + State { + name: "right" + when: (plasmoid.location === PlasmaCore.Types.RightEdge)&&(root.panelAlignment === NowDock.Types.Double) + + AnchorChanges { + target: secondLayout + anchors{ top:undefined; bottom:parent.bottom; left:undefined; right:parent.right; horizontalCenter:undefined; verticalCenter:undefined} + } + PropertyChanges{ + target: secondLayout; horizontalItemAlignment: Grid.AlignRight; verticalItemAlignment: Grid.AlignVCenter; + anchors.leftMargin: 0; anchors.rightMargin:0; anchors.topMargin:0; anchors.bottomMargin:panelEdgeSpacing/2; + } + }, + State { + name: "top" + when: (plasmoid.location === PlasmaCore.Types.TopEdge)&&(root.panelAlignment === NowDock.Types.Double) + + AnchorChanges { + target: secondLayout + anchors{ top:parent.top; bottom:undefined; left:undefined; right:parent.right; horizontalCenter:undefined; verticalCenter:undefined} + } + PropertyChanges{ + target: secondLayout; horizontalItemAlignment: Grid.AlignHCenter; verticalItemAlignment: Grid.AlignTop + anchors.leftMargin: 0; anchors.rightMargin:panelEdgeSpacing/2; anchors.topMargin:0; anchors.bottomMargin:0; + } + } + ] + } + } + + ///////////////END UI elements + + ///////////////BEGIN TIMER elements + Timer { + id:hideMagicWindowInAutoHide + interval:2500 + + //it is used in activity change situation + property bool forcedDisableHiding: false + + onTriggered: { + if (forcedDisableHiding) { + forcedDisableHiding = false; + dockView.visibility.disableHiding = false; + } + + var visibility = dockView.visibility; + + if (plasmoid.immutable && !visibility.isHovered //&& !wholeArea.containsMouse + && ((visibility.panelVisibility === NowDock.Types.AutoHide) || visibility.isDockWindowType) ) { + visibility.mustBeLowered(); + } + } + } + + + Timer { + id: animatedLengthTimer + interval: 150 + onTriggered: { + // if (!magicWin.isHovered && (appletsAnimations === 0) + // && (root.animationsNeedLength === 0) && (root.animationsNeedBothAxis === 0)) { + if ((appletsAnimations === 0) && (root.animationsNeedLength === 0) && (root.animationsNeedBothAxis === 0)) { + mainLayout.animatedLength = false; + visibilityManager.updateMaskArea(); + } + } + } + + //Timer to check if the mouse is still inside the ListView + Timer{ + id:checkListHovered + repeat:false; + interval:120; + + onTriggered: { + if(!root.containsMouse()) + root.clearZoom(); + } + } + + Timer { + id: containmentSizeSyncTimer + interval: 150 + onTriggered: { + dndSpacer.parent = root; + // currentLayout.x = (Qt.application.layoutDirection === Qt.RightToLeft && !plasmoid.immutable) ? toolBox.width : 0; + // currentLayout.y = 0 + /* currentLayout.width = root.width - (isHorizontal && toolBox && !plasmoid.immutable ? toolBox.width : 0) + currentLayout.height = root.height - (!isHorizontal && toolBox && !plasmoid.immutable ? toolBox.height : 0) */ + // currentLayout.isLayoutHorizontal = isHorizontal + } + } + + //FIXME: I don't see other ways at the moment a way to see when the UI is REALLY ready + Timer { + id: startupTimer + interval: 4000 + onTriggered: { + for (var i = 0; i < mainLayout.children.length; ++i) { + if ( mainLayout.children[i].hasOwnProperty('animationsEnabled') ) { + mainLayout.children[i].animationsEnabled = true; + } + } + inStartup = false; + } + } + + ///////////////END TIMER elements +} diff --git a/containment/metadata.desktop.cmake b/containment/metadata.desktop.cmake new file mode 100644 index 000000000..10677aef4 --- /dev/null +++ b/containment/metadata.desktop.cmake @@ -0,0 +1,22 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=Now Dock +Comment=A dock for Plasma that tries to animate its plasmoids on hovering + +Type=Service +Keywords= +NoDisplay=true + +X-KDE-PluginInfo-Author=@AUTHOR@ +X-KDE-PluginInfo-Email=@EMAIL@ +X-KDE-PluginInfo-Name=org.kde.nowdock.containment +X-KDE-PluginInfo-Version=@VERSION@ +X-KDE-PluginInfo-Website=@WEBSITE@ +X-KDE-PluginInfo-Category= +X-KDE-PluginInfo-Depends= +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-EnabledByDefault=true +X-KDE-ServiceTypes=Plasma/Applet,Plasma/Containment +X-Plasma-API=declarativeappletscript +X-Plasma-MainScript=ui/main.qml +X-Plasma-ContainmentType=Panel diff --git a/corona/CMakeLists.txt b/corona/CMakeLists.txt new file mode 100644 index 000000000..6799b780e --- /dev/null +++ b/corona/CMakeLists.txt @@ -0,0 +1,47 @@ + +include(KDEInstallDirs) +include(KDECMakeSettings) +#include(KDECompilerSettings NO_POLICY_SCOPE) +#include(ECMPackageConfigHelpers) +include(CMakePackageConfigHelpers) +include(WriteBasicConfigVersionFile) +include(CheckIncludeFiles) +include(ECMOptionalAddSubdirectory) +include(ECMQtDeclareLoggingCategory) +include(KDEPackageAppTemplates) + + +set(nowdock-app_SRCS + ../libnowdock/types.cpp + abstractinterface.cpp + xwindowinterface.cpp + visibilitymanager.cpp + nowdockconfigview.cpp + nowdockview.cpp + packageplugins/shell/nowdockpackage.cpp + nowdockcorona.cpp + main.cpp +) + +#add_subdirectory(packageplugins) + +add_executable(nowdock ${nowdock-app_SRCS}) + +target_link_libraries( + nowdock + Qt5::Widgets + Qt5::Quick + Qt5::Qml + KF5::I18n + KF5::CoreAddons + KF5::XmlGui + KF5::PlasmaQuick + KF5::Plasma + KF5::QuickAddons + KF5::DBusAddons + KF5::Notifications +) + +install(TARGETS nowdock ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) +#even if hidden, the desktop file is needed anyways for kdbusservice::unique +install(FILES nowdock.desktop DESTINATION ${KDE_INSTALL_APPDIR}) diff --git a/corona/abstractinterface.cpp b/corona/abstractinterface.cpp new file mode 100644 index 000000000..3153160b7 --- /dev/null +++ b/corona/abstractinterface.cpp @@ -0,0 +1,42 @@ +#include "abstractinterface.h" + +#include <QObject> +#include <QQuickWindow> + +namespace NowDock { + +AbstractInterface::AbstractInterface(QQuickWindow *dock) : + QObject(dock), + m_isDockWindowType(false), + m_dockNumber(0) +{ + m_dockWindow = dock; +} + +void AbstractInterface::setDockNumber(unsigned int no) +{ + if (m_dockNumber == no) { + return; + } + + m_dockNumber = no; + + emit dockNumberChanged(m_dockNumber); +} + +unsigned int AbstractInterface::dockNumber() const +{ + return m_dockNumber; +} + + +void AbstractInterface::setMaskArea(QRect area) +{ + if (m_maskArea == area) { + return; + } + + m_maskArea = area; +} + +} diff --git a/corona/abstractinterface.h b/corona/abstractinterface.h new file mode 100644 index 000000000..df2b30d7b --- /dev/null +++ b/corona/abstractinterface.h @@ -0,0 +1,56 @@ +#ifndef ABSTRACTINTERFACE_H +#define ABSTRACTINTERFACE_H + +#include <QObject> +#include <QQuickWindow> + +namespace NowDock { + +class AbstractInterface : public QObject { + Q_OBJECT + +public: + explicit AbstractInterface(QQuickWindow *dock); + + virtual bool activeIsDialog() const = 0; + virtual bool activeIsMaximized() const = 0; + virtual bool desktopIsActive() const = 0; + virtual bool dockIntersectsActiveWindow() const = 0; + virtual bool dockIsCovered(bool totally = false) const = 0; + virtual bool dockIsCovering() const = 0; + virtual bool dockIsOnTop() const = 0; + virtual bool dockInNormalState() const = 0; + virtual bool dockIsBelow() const = 0; + + //FIXME: This may not be needed, it would be better to investigate in KWindowSystem + //its behavior when setting the window type to NET::Dock + virtual void setDockDefaultFlags(bool dock = false) = 0; + virtual void setDockToAllDesktops() = 0; + virtual void showDockAsNormal() = 0; + virtual void showDockOnBottom() = 0; + virtual void showDockOnTop() = 0; + + void setDockNumber(unsigned int no); + unsigned int dockNumber() const; + + void setMaskArea(QRect area); + +Q_SIGNALS: + void activeWindowChanged(); + void dockNumberChanged(unsigned int no); + void windowInAttention(bool); + //FIXME: there is a chance that this signal is not needed at all + void windowChanged(); + +protected: + bool m_isDockWindowType; + int m_dockNumber; + + QRect m_maskArea; + + QQuickWindow *m_dockWindow; +}; + +} + +#endif diff --git a/corona/main.cpp b/corona/main.cpp new file mode 100644 index 000000000..979d15324 --- /dev/null +++ b/corona/main.cpp @@ -0,0 +1,77 @@ +/* + * Copyright 2014 Bhushan Shah <bhush94@gmail.com> + * Copyright 2014 Marco Martin <notmart@gmail.com> + * + * 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 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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 <http://www.gnu.org/licenses/> + */ + +#include "nowdockcorona.h" + +#include <memory> + +#include <QApplication> +#include <QQuickWindow> +#include <qcommandlineparser.h> +#include <qcommandlineoption.h> +#include <QDebug> + +#include <KLocalizedString> + +//! COLORS +#define CNORMAL "\e[0m" +#define CIGREEN "\e[1;32m" +#define CGREEN "\e[0;32m" +#define CICYAN "\e[1;36m" +#define CCYAN "\e[0;36m" +#define CIRED "\e[1;31m" +#define CRED "\e[0;31m" + +#ifdef QT_NO_DEBUG + #define DEPTH "1" +#else + #define DEPTH "8" +#endif + +static const char version[] = "0.1"; + +int main(int argc, char **argv) +{ + QQuickWindow::setDefaultAlphaBuffer(true); + + QApplication app(argc, argv); + app.setApplicationVersion(version); + app.setOrganizationDomain(QStringLiteral("nowdock")); + app.setApplicationName(QStringLiteral("NowDock")); + + //! set pattern for debug messages + //! [%{type}] [%{function}:%{line}] - %{message} [%{backtrace}] + qSetMessagePattern(QStringLiteral( + CIGREEN "[%{type} " CGREEN "%{time h:mm:ss.zzzz}" CIGREEN "]" CNORMAL +#ifndef QT_NO_DEBUG + CIRED " [" CCYAN "%{function}" CIRED ":" CCYAN "%{line}" CIRED "]" +#endif + CICYAN " - " CNORMAL "%{message}" + CIRED "%{if-fatal}\n%{backtrace depth=" DEPTH " separator=\"\n\"}%{endif}" + "%{if-warning}\n%{backtrace depth=" DEPTH " separator=\"\n\"}%{endif}" + "%{if-critical}\n%{backtrace depth=" DEPTH " separator=\"\n\"}%{endif}" CNORMAL)); + + // qputenv("QT_QUICK_CONTROLS_1_STYLE", "Desktop"); + NowDockCorona corona; + + return app.exec(); +} + diff --git a/corona/nowdock.desktop b/corona/nowdock.desktop new file mode 100644 index 000000000..041ce5c42 --- /dev/null +++ b/corona/nowdock.desktop @@ -0,0 +1,16 @@ +[Desktop Entry] +Name=Now Dock +Comment=Now Dock Application +Exec=nowdock %u +Hidden=true +Icon=plasma +InitialPreference=1 +Path= +StartupNotify=true +Terminal=false +TerminalOptions= +Type=Application +X-DBUS-ServiceName= +X-DBUS-StartupType=unique +X-KDE-SubstituteUID=false +X-KDE-Username= diff --git a/corona/nowdockconfigview.cpp b/corona/nowdockconfigview.cpp new file mode 100644 index 000000000..3f646c332 --- /dev/null +++ b/corona/nowdockconfigview.cpp @@ -0,0 +1,204 @@ +/* +* Copyright 2016 Smith AR <audoban@openmailbox.org> +* +* This file is part of Candil-Dock +* +* Candil-Dock 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. +* +* Candil-Dock 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 <http://www.gnu.org/licenses/>. +*/ + +#include "nowdockconfigview.h" +#include "nowdockview.h" + +#include <QQuickItem> +#include <QQmlContext> +#include <QScreen> + +#include <KWindowSystem> +#include <KWindowEffects> + +#include <plasma/package.h> + +NowDockConfigView::NowDockConfigView(Plasma::Containment *containment, NowDockView *dockView, QWindow *parent) + : PlasmaQuick::ConfigView(containment, parent), m_containment(containment), m_dockView(dockView) +{ + m_deleterTimer.setSingleShot(true); + m_deleterTimer.setInterval(10 * 1000); + connect(&m_deleterTimer, &QTimer::timeout, this, &QObject::deleteLater); + connect(dockView, &QObject::destroyed, this, &QObject::deleteLater); + + m_screenSyncTimer.setSingleShot(true); + m_screenSyncTimer.setInterval(100); + + connect(dockView, SIGNAL(screenChanged(QScreen *)), &m_screenSyncTimer, SLOT(start())); + + connect(&m_screenSyncTimer, &QTimer::timeout, this, [this]() { + setScreen(m_dockView->screen()); + setFlags(wFlags()); + syncGeometry(); + syncSlideEffect(); + }); + + //containment->setLocation(Plasma::Types::LeftEdge); + /*connect(containment, &Plasma::Containment::immutabilityChanged + , [&](Plasma::Types::ImmutabilityType type) { + if (type != Plasma::Types::Mutable && isVisible()) + hide(); + });*/ +} + +NowDockConfigView::~NowDockConfigView() +{ +} + +void NowDockConfigView::init() +{ + setDefaultAlphaBuffer(true); + setColor(Qt::transparent); + rootContext()->setContextProperty(QStringLiteral("dock"), m_dockView); + auto source = QUrl::fromLocalFile(m_containment->corona()->kPackage().filePath("nowdockconfigurationui")); + setSource(source); + syncSlideEffect(); +} + +inline Qt::WindowFlags NowDockConfigView::wFlags() const +{ + return (flags() | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint) & ~Qt::WindowDoesNotAcceptFocus; +} + +void NowDockConfigView::syncGeometry() +{ + if (!m_containment || !rootObject()) + return; + + const auto location = m_containment->location(); + const auto sGeometry = screen()->geometry(); + + switch (m_containment->formFactor()) { + case Plasma::Types::Horizontal: { + const QSize size(rootObject()->width(), rootObject()->height()); + setMaximumSize(size); + setMinimumSize(size); + resize(size); + + if (location == Plasma::Types::TopEdge) { + setPosition(sGeometry.center().x() - size.width() / 2 + , m_dockView->currentThickness()); + } else if (location == Plasma::Types::BottomEdge) { + setPosition(sGeometry.center().x() - size.width() / 2 + , sGeometry.height() - m_dockView->currentThickness() - size.height()); + } + } + break; + + case Plasma::Types::Vertical: { + const QSize size(rootObject()->width(), rootObject()->height()); + setMaximumSize(size); + setMinimumSize(size); + resize(size); + + if (location == Plasma::Types::LeftEdge) { + setPosition(m_dockView->currentThickness() + , sGeometry.center().y() - size.height() / 2); + } else if (location == Plasma::Types::RightEdge) { + setPosition(sGeometry.width() - m_dockView->currentThickness() - size.width() + , sGeometry.center().y() - size.height() / 2); + } + } + break; + + default: + qWarning() << "no sync geometry, wrong formFactor"; + //<< qEnumToStr(m_containment->formFactor()); + break; + } +} + +void NowDockConfigView::syncSlideEffect() +{ + if (!m_containment) + return; + + KWindowEffects::SlideFromLocation slideLocation{KWindowEffects::NoEdge}; + + switch (m_containment->location()) { + case Plasma::Types::TopEdge: + slideLocation = KWindowEffects::TopEdge; + break; + + case Plasma::Types::RightEdge: + slideLocation = KWindowEffects::RightEdge; + break; + + case Plasma::Types::BottomEdge: + slideLocation = KWindowEffects::BottomEdge; + break; + + case Plasma::Types::LeftEdge: + slideLocation = KWindowEffects::LeftEdge; + break; + + default: + qDebug() << staticMetaObject.className() << "wrong location";// << qEnumToStr(m_containment->location()); + break; + } + + KWindowEffects::slideWindow(winId(), slideLocation, -1); +} + +void NowDockConfigView::showEvent(QShowEvent *ev) +{ + KWindowSystem::setType(winId(), NET::Dock); + setFlags(wFlags()); + KWindowSystem::setState(winId(), NET::KeepAbove | NET::SkipPager | NET::SkipTaskbar); + KWindowSystem::forceActiveWindow(winId()); + KWindowEffects::enableBlurBehind(winId(), true); + + syncGeometry(); + syncSlideEffect(); + + if (m_containment) + m_containment->setUserConfiguring(true); + + // m_dockView->visibility()->forceShow(true); + // m_dockView->visibility()->showImmediately(); + m_screenSyncTimer.start(); + m_deleterTimer.stop(); + ConfigView::showEvent(ev); +} + +void NowDockConfigView::hideEvent(QHideEvent *ev) +{ + // m_dockView->visibility()->forceShow(false); + // m_dockView->visibility()->restore(); + m_deleterTimer.start(); + + if (m_containment) { + m_dockView->saveConfig(); + m_containment->setUserConfiguring(false); + } + + ConfigView::hideEvent(ev); +} + +void NowDockConfigView::focusOutEvent(QFocusEvent *ev) +{ + Q_UNUSED(ev); + const auto *focusWindow = qGuiApp->focusWindow(); + + if (focusWindow && focusWindow->flags().testFlag(Qt::Popup)) + return; + + hide(); +} +// kate: indent-mode cstyle; indent-width 4; replace-tabs on; diff --git a/corona/nowdockconfigview.h b/corona/nowdockconfigview.h new file mode 100644 index 000000000..772d6d563 --- /dev/null +++ b/corona/nowdockconfigview.h @@ -0,0 +1,63 @@ +/* +* Copyright 2016 Smith AR <audoban@openmailbox.org> +* +* This file is part of Candil-Dock +* +* Candil-Dock 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. +* +* Candil-Dock 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef NOWDOCKCONFIGVIEW_H +#define NOWDOCKCONFIGVIEW_H + +#include "plasmaquick/configview.h" + +#include <QObject> +#include <QWindow> +#include <QPointer> +#include <QTimer> + +namespace Plasma { +class Applet; +class Containment; +} + +class NowDockView; + +class NowDockConfigView : public PlasmaQuick::ConfigView { + Q_OBJECT + +public: + NowDockConfigView(Plasma::Containment *containment, NowDockView *dockView, QWindow *parent = nullptr); + ~NowDockConfigView() override; + + void init() override; + Qt::WindowFlags wFlags() const; + +protected: + void showEvent(QShowEvent *ev) override; + void hideEvent(QHideEvent *ev) override; + void focusOutEvent(QFocusEvent *ev) override; + + void syncGeometry(); + void syncSlideEffect(); + +private: + Plasma::Containment *m_containment{nullptr}; + QPointer<NowDockView> m_dockView; + QTimer m_deleterTimer; + QTimer m_screenSyncTimer; + +}; +#endif //DOCKCONFIGVIEW_H +// kate: indent-mode cstyle; indent-width 4; replace-tabs on; diff --git a/corona/nowdockcorona.cpp b/corona/nowdockcorona.cpp new file mode 100644 index 000000000..f789f0ce6 --- /dev/null +++ b/corona/nowdockcorona.cpp @@ -0,0 +1,231 @@ +/* + * Copyright 2014 Bhushan Shah <bhush94@gmail.com> + * Copyright 2014 Marco Martin <notmart@gmail.com> + * + * 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 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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 <http://www.gnu.org/licenses/> + */ + +#include "nowdockcorona.h" +#include "nowdockview.h" +//#include "visibilitymanager.h" +#include "packageplugins/shell/nowdockpackage.h" + +#include <QAction> +#include <QScreen> +#include <QDebug> + +#include <KActionCollection> +#include <KPluginMetaData> + +#include <Plasma> +#include <Plasma/Corona> +#include <Plasma/Containment> +#include <KLocalizedString> +#include <KPackage/Package> +#include <KPackage/PackageLoader> + +NowDockCorona::NowDockCorona(QObject *parent) + : Plasma::Corona(parent) +{ + KPackage::Package package(new NowDockPackage(this)); + + if (!package.isValid()) { + qWarning() << staticMetaObject.className() + << "the package" << package.metadata().rawData() << "is invalid!"; + return; + } else { + qDebug() << staticMetaObject.className() + << "the package" << package.metadata().rawData() << "is valid!"; + } + + setKPackage(package); + qmlRegisterTypes(); + connect(this, &Corona::containmentAdded, this, &NowDockCorona::addDock); + + loadLayout(); + + /*QAction *addDock = actions()->add<QAction>(QStringLiteral("add dock")); + connect(addDock, &QAction::triggered, this, &NowDockCorona::loadDefaultLayout); + addDock->setText(i18n("Add New Dock")); + addDock->setAutoRepeat(true); + addDock->setStatusTip(tr("Adds a new dock in the environment")); + addDock->setVisible(true); + addDock->setEnabled(true); + + addDock->setIcon(QIcon::fromTheme(QStringLiteral("object-locked"))); + addDock->setData(Plasma::Types::ControlAction); + addDock->setShortcut(QKeySequence(QStringLiteral("alt+d, l"))); + addDock->setShortcutContext(Qt::ApplicationShortcut);*/ +} + +NowDockCorona::~NowDockCorona() +{ + for (auto c : m_containments) + c->deleteLater(); + + qDebug() << "deleted" << this; +} + +int NowDockCorona::numScreens() const +{ + return qGuiApp->screens().count(); +} + +QRect NowDockCorona::screenGeometry(int id) const +{ + const auto screens = qGuiApp->screens(); + + if (id >= 0 && id < screens.count()) { + return screens[id]->geometry(); + } + + return qGuiApp->primaryScreen()->geometry(); +} + +QRegion NowDockCorona::availableScreenRegion(int id) const +{ + const auto screens = qGuiApp->screens(); + + if (id >= 0 && id < screens.count()) { + return screens[id]->geometry(); + } + + return qGuiApp->primaryScreen()->availableGeometry(); +} + +QRect NowDockCorona::availableScreenRect(int id) const +{ + const auto screens = qGuiApp->screens(); + + if (id >= 0 && id < screens.count()) { + return screens[id]->availableGeometry(); + } + + return qGuiApp->primaryScreen()->availableGeometry(); +} + +QList<Plasma::Types::Location> NowDockCorona::freeEdges(int screen) const +{ + using Plasma::Types; + QList<Types::Location> edges{Types::TopEdge, Types::BottomEdge + , Types::LeftEdge, Types::RightEdge}; + + for (const NowDockView *cont : m_containments) { + if (cont && cont->containment()->screen() == screen) + edges.removeOne(cont->location()); + } + + return edges; +} + +int NowDockCorona::screenForContainment(const Plasma::Containment *containment) const +{ + return 0; + + while (const auto *parentCont = qobject_cast<const Plasma::Applet *>(containment->parent())) { + if (parentCont->isContainment()) + containment = qobject_cast<const Plasma::Containment *>(parentCont); + } + + for (auto *view : m_containments) { + if (view && view->containment() == containment) + return containment->screen(); + } + + return -1; +} + +void NowDockCorona::addDock(Plasma::Containment *containment) +{ + if (!containment || !containment->kPackage().isValid()) { + qWarning() << "the requested containment plugin can not be located or loaded"; + return; + } + + qWarning() << "Adding dock for container..."; + + auto dockView = new NowDockView(this); + dockView->init(); + dockView->setContainment(containment); + dockView->show(); + //dockView->showNormal(); + + m_containments.push_back(dockView); +} + +void NowDockCorona::loadDefaultLayout() +{ + + qDebug() << "loading default layout"; + //! Settting mutable for create a containment + setImmutability(Plasma::Types::Mutable); + + QVariantList args; + auto defaultContainment = createContainmentDelayed("org.kde.nowdock.containment", args); + //auto defaultContainment = createContainmentDelayed("org.kde.panel", args); + defaultContainment->setContainmentType(Plasma::Types::PanelContainment); + defaultContainment->init(); + + if (!defaultContainment || !defaultContainment->kPackage().isValid()) { + qWarning() << "the requested containment plugin can not be located or loaded"; + return; + } + + auto config = defaultContainment->config(); + + config.writeEntry("dock", "initial"); + // config.writeEntry("alignment", (int)Dock::Center); + // config.deleteEntry("wallpaperplugin"); + + switch (containments().size()) { + case 0: + defaultContainment->setLocation(Plasma::Types::LeftEdge); + break; + + case 1: + defaultContainment->setLocation(Plasma::Types::RightEdge); + break; + + case 2: + defaultContainment->setLocation(Plasma::Types::TopEdge); + break; + + default: + defaultContainment->setLocation(Plasma::Types::BottomEdge); + break; + } + + auto cfg = defaultContainment->config(); + defaultContainment->save(cfg); + + addDock(defaultContainment); + + defaultContainment->createApplet(QStringLiteral("org.kde.store.nowdock.plasmoid")); + defaultContainment->createApplet(QStringLiteral("org.kde.plasma.analogclock")); +} + +inline void NowDockCorona::qmlRegisterTypes() const +{ + constexpr auto uri = "org.kde.nowdock.shell"; + constexpr auto vMajor = 0; + constexpr auto vMinor = 2; + +// qmlRegisterUncreatableType<Candil::Dock>(uri, vMajor, vMinor, "Dock", "class Dock uncreatable"); +// qmlRegisterUncreatableType<Candil::VisibilityManager>(uri, vMajor, vMinor, "VisibilityManager", "class VisibilityManager uncreatable"); +// qmlRegisterUncreatableType<NowDockView>(uri, vMajor, vMinor, "DockView", "class DockView uncreatable"); + qmlRegisterType<QScreen>(); +} diff --git a/corona/nowdockcorona.h b/corona/nowdockcorona.h new file mode 100644 index 000000000..cb3c4d8ea --- /dev/null +++ b/corona/nowdockcorona.h @@ -0,0 +1,64 @@ +/* + * Copyright 2014 Bhushan Shah <bhush94@gmail.com> + * Copyright 2014 Marco Martin <notmart@gmail.com> + * + * 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 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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 <http://www.gnu.org/licenses/> + */ + +#ifndef NOWDOCKCORONA_H +#define NOWDOCKCORONA_H + +#include <QObject> + +#include "nowdockview.h" + + +namespace Plasma { +class Corona; +class Containment; +class Types; +} + +class NowDockCorona : public Plasma::Corona { + Q_OBJECT + +public: + NowDockCorona(QObject *parent = nullptr); + ~NowDockCorona() override; + + int numScreens() const override; + QRect screenGeometry(int id) const override; + QRegion availableScreenRegion(int id) const override; + QRect availableScreenRect(int id) const override; + + QList<Plasma::Types::Location> freeEdges(int screen) const; + + int screenForContainment(const Plasma::Containment *containment) const override; + + void addDock(Plasma::Containment *containment); + +public slots: + void loadDefaultLayout() override; + +private: + void qmlRegisterTypes() const; + + std::vector<NowDockView *> m_containments; +}; + + +#endif diff --git a/corona/nowdockview.cpp b/corona/nowdockview.cpp new file mode 100644 index 000000000..16c869a81 --- /dev/null +++ b/corona/nowdockview.cpp @@ -0,0 +1,553 @@ +/* + * Copyright 2014 Bhushan Shah <bhush94@gmail.com> + * Copyright 2014 Marco Martin <notmart@gmail.com> + * + * 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 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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 <http://www.gnu.org/licenses/> + */ + +#include "nowdockview.h" +#include "nowdockconfigview.h" +#include "visibilitymanager.h" + +#include <QQmlContext> +#include <QQmlProperty> +#include <QQuickItem> +#include <QMetaEnum> +//#include <QtX11Extras/QX11Info> + +#include <NETWM> +#include <KWindowSystem> +#include <Plasma/Containment> +#include <KActionCollection> + +#include "nowdockcorona.h" +//using Candil::Dock; + +NowDockView::NowDockView(Plasma::Corona *corona, QScreen *targetScreen) + : PlasmaQuick::ContainmentView(corona), + m_corona(corona) +{ + KWindowSystem::setType(winId(), NET::Dock); + KWindowSystem::setState(winId(), NET::SkipTaskbar | NET::SkipPager); + + setVisible(false); + setTitle(corona->kPackage().metadata().name()); + setIcon(QIcon::fromTheme(corona->kPackage().metadata().iconName())); + + setResizeMode(QuickViewSharedEngine::SizeRootObjectToView); + setClearBeforeRendering(true); + /* setFlags(Qt::FramelessWindowHint + | Qt::WindowStaysOnTopHint + | Qt::NoDropShadowWindowHint + | Qt::WindowDoesNotAcceptFocus);*/ + + // NETWinInfo winfo(QX11Info::connection(), winId(), winId(), 0, 0); + // winfo.setAllowedActions(NET::ActionChangeDesktop); + + if (targetScreen) + adaptToScreen(targetScreen); + else + adaptToScreen(qGuiApp->primaryScreen()); + + m_timerGeometry.setSingleShot(true); + m_timerGeometry.setInterval(400); + + m_lockGeometry.setSingleShot(true); + m_lockGeometry.setInterval(700); + + connect(this, &NowDockView::containmentChanged + , this, [&]() { + if (!containment()) + return; + + if (!m_visibility) { + m_visibility = new VisibilityManager(this); + } + + m_visibility->setContainment(containment()); + }, Qt::DirectConnection); +} + +NowDockView::~NowDockView() +{ +} + +void NowDockView::init() +{ + connect(this, &NowDockView::screenChanged + , this, &NowDockView::adaptToScreen + , Qt::QueuedConnection); + + + connect(&m_timerGeometry, &QTimer::timeout, [&]() { + initWindow(); + }); + + connect(this, &NowDockView::locationChanged, [&]() { + //! avoid glitches + m_timerGeometry.start(); + }); + + connect(KWindowSystem::self(), &KWindowSystem::compositingChanged + , this, [&]() { + emit compositingChanged(); + } , Qt::QueuedConnection); + + connect(this, &NowDockView::screenGeometryChanged + , this, &NowDockView::updateDockPosition + , Qt::QueuedConnection); + + connect(this, SIGNAL(widthChanged(int)), this, SIGNAL(widthChanged())); + connect(this, SIGNAL(heightChanged(int)), this, SIGNAL(heightChanged())); + + rootContext()->setContextProperty(QStringLiteral("panel"), this); + setSource(corona()->kPackage().filePath("nowdockui")); + + + connect(this, SIGNAL(xChanged(int)), this, SLOT(updateDockPositionSlot())); + connect(this, SIGNAL(yChanged(int)), this, SLOT(updateDockPositionSlot())); + + connect(&m_lockGeometry, &QTimer::timeout, [&]() { + updateDockPosition(); + }); + + qDebug() << "SOURCE:" << source(); + + initialize(); +} + + +void NowDockView::initialize() +{ + m_secondInitPass = true; + m_timerGeometry.start(); +} + +void NowDockView::initWindow() +{ + // m_visibility->updateVisibilityFlags(); + + updateDockPosition(); + resizeWindow(); + + // The initialization phase makes two passes because + // changing the window style and type wants a small delay + // and afterwards the second pass positions them correctly + if (m_secondInitPass) { + m_timerGeometry.start(); + m_secondInitPass = false; + setVisible(true); + } +} + +void NowDockView::updateDockPositionSlot() +{ + if (!m_lockGeometry.isActive()) { + m_lockGeometry.start(); + } +} + +//!BEGIN SLOTS +void NowDockView::adaptToScreen(QScreen *screen) +{ + setScreen(screen); + + if (formFactor() == Plasma::Types::Vertical) + m_maxLength = screen->size().height(); + else + m_maxLength = screen->size().width(); + +// KWindowSystem::setOnAllDesktops(winId(), true); +// KWindowSystem::setType(winId(), NET::Dock); + + if (containment()) + containment()->reactToScreenChange(); + + m_timerGeometry.start(); +} + +void NowDockView::addNewDock() +{ + NowDockCorona *corona = dynamic_cast<NowDockCorona *>(m_corona); + + if (corona) { + corona->loadDefaultLayout(); + } +} + +QQmlListProperty<QScreen> NowDockView::screens() +{ + return QQmlListProperty<QScreen>(this, nullptr, &countScreens, &atScreens); +} + +int NowDockView::countScreens(QQmlListProperty<QScreen> *property) +{ + Q_UNUSED(property) + return qGuiApp->screens().count(); +} + +QScreen *NowDockView::atScreens(QQmlListProperty<QScreen> *property, int index) +{ + Q_UNUSED(property) + return qGuiApp->screens().at(index); +} + +void NowDockView::showConfigurationInterface(Plasma::Applet *applet) +{ + if (!applet || !applet->containment()) + return; + + Plasma::Containment *c = qobject_cast<Plasma::Containment *>(applet); + + if (m_configView && c && c->isContainment() && c == containment()) { + if (m_configView->isVisible()) { + m_configView->hide(); + } else { + m_configView->show(); + m_configView->requestActivate(); + } + + return; + } else if (m_configView) { + if (m_configView->applet() == applet) { + m_configView->show(); + m_configView->requestActivate(); + return; + } else { + m_configView->hide(); + m_configView->deleteLater(); + } + } + + if (c && containment() && c->isContainment() && c->id() == containment()->id()) { + m_configView = new NowDockConfigView(c, this); + } else { + m_configView = new PlasmaQuick::ConfigView(applet); + } + + m_configView->init(); + m_configView->show(); + m_configView->requestActivate(); +} + +void NowDockView::resizeWindow() +{ + setVisible(true); + + QSize screenSize = screen()->size(); + + if (formFactor() == Plasma::Types::Vertical) { + const QSize size{maxThickness(), screenSize.height()}; + setMinimumSize(size); + setMaximumSize(size); + resize(size); + + qDebug() << "dock size:" << size; + } else { + const QSize size{screenSize.width(), maxThickness()}; + setMinimumSize(size); + setMaximumSize(size); + resize(size); + + qDebug() << "dock size:" << size; + } +} + +inline void NowDockView::updateDockPosition() +{ + if (!containment()) + return; + + const QRect screenGeometry = screen()->geometry(); + QPoint position; + + qDebug() << "current dock geometry: " << geometry(); + + // containment()->setFormFactor(Plasma::Types::Horizontal); + position = {0, 0}; + m_maxLength = screenGeometry.width(); + + switch (location()) { + case Plasma::Types::TopEdge: + containment()->setFormFactor(Plasma::Types::Horizontal); + position = {screenGeometry.x(), screenGeometry.y()}; + m_maxLength = screenGeometry.width(); + break; + + case Plasma::Types::BottomEdge: + containment()->setFormFactor(Plasma::Types::Horizontal); + position = {screenGeometry.x(), screenGeometry.y() + screenGeometry.height() - height()}; + m_maxLength = screenGeometry.width(); + break; + + case Plasma::Types::RightEdge: + containment()->setFormFactor(Plasma::Types::Vertical); + position = {screenGeometry.x() + screenGeometry.width() - width(), screenGeometry.y()}; + m_maxLength = screenGeometry.height(); + break; + + case Plasma::Types::LeftEdge: + containment()->setFormFactor(Plasma::Types::Vertical); + position = {screenGeometry.x(), screenGeometry.y()}; + m_maxLength = screenGeometry.height(); + break; + + default: + qWarning() << "wrong location, couldn't update the panel position" + << location(); + } + + emit maxLengthChanged(); + setX(position.x()); + setY(position.y()); + //setPosition(position); + qDebug() << "dock position:" << position; +} + +int NowDockView::currentThickness() const +{ + if (containment()->formFactor() == Plasma::Types::Vertical) { + return m_maskArea.isNull() ? width() : m_maskArea.width(); + } else { + return m_maskArea.isNull() ? height() : m_maskArea.height(); + } +} + +bool NowDockView::compositing() const +{ + return KWindowSystem::compositingActive(); +} + +/*Candil::VisibilityManager *NowDockView::visibility() +{ + return m_visibility.data(); +}*/ + +int NowDockView::maxThickness() const +{ + return m_maxThickness; +} + +void NowDockView::setMaxThickness(int thickness) +{ + if (m_maxThickness == thickness) + return; + + m_maxThickness = thickness; + m_timerGeometry.start(); + emit maxThicknessChanged(); +} + +int NowDockView::length() const +{ + return m_length; +} + +void NowDockView::setLength(int length) +{ + if (m_length == length) + return; + + if (length > m_maxLength) + m_length = m_maxLength; + else + m_length = length; + + m_timerGeometry.start(); + emit lengthChanged(); +} + +int NowDockView::maxLength() const +{ + return m_maxLength; +} + +void NowDockView::setMaxLength(int maxLength) +{ + if (m_maxLength == maxLength) + return; + + m_maxLength = maxLength; + emit maxLengthChanged(); +} + + +QRect NowDockView::maskArea() const +{ + return m_maskArea; +} + +void NowDockView::setMaskArea(QRect area) +{ + if (m_maskArea == area) { + return; + } + + m_maskArea = area; + m_visibility->setMaskArea(area); + + setMask(m_maskArea); + + //qDebug() << "dock mask set:" << m_maskArea; + emit maskAreaChanged(); +} + +/*Dock::Alignment NowDockView::alignment() const +{ + return m_alignment; +} + +void NowDockView::setAlignment(Dock::Alignment align) +{ + if (m_alignment == align) + return; + + m_alignment = align; + emit alignmentChanged(); +} +*/ +int NowDockView::offset() const +{ + return m_offset; +} + +void NowDockView::setOffset(int offset) +{ + if (m_offset == offset) + return; + + m_offset = offset; + m_timerGeometry.start(); + emit offsetChanged(); +} + +void NowDockView::updateOffset() +{ + if (!containment()) + return; + + const float offsetPercent = containment()->config().readEntry("offset").toFloat(); + const int offset = offsetPercent * (m_maxLength - m_length) / 2; + + if (offset == m_offset) + return; + + m_offset = offset; + emit offsetChanged(); +} + +VisibilityManager *NowDockView::visibility() +{ + return m_visibility; +} + +bool NowDockView::event(QEvent *e) +{ + + /* if (ev->type() == QEvent::Enter) { + m_visibility->show(); + emit entered(); + } else if (ev->type() == QEvent::Leave) { + m_visibility->restore(); + emit exited(); + } */ + + //return QQuickWindow::event(e); + if (m_visibility) { + m_visibility->event(e); + } + + return ContainmentView::event(e); +} + +/*void NowDockView::showEvent(QShowEvent *ev) +{ + KWindowSystem::setType(winId(), NET::Dock); + KWindowSystem::setOnAllDesktops(winId(), true); + + //QQuickWindow::showEvent(ev); + ContainmentView::showEvent(ev); +}*/ + +bool NowDockView::containmentContainsPosition(const QPointF &point) const +{ + QQuickItem *containmentItem = containment()->property("_plasma_graphicObject").value<QQuickItem *>(); + + if (!containmentItem) { + return false; + } + + return QRectF(containmentItem->mapToScene(QPoint(0, 0)), QSizeF(containmentItem->width(), containmentItem->height())).contains(point); +} + +QPointF NowDockView::positionAdjustedForContainment(const QPointF &point) const +{ + QQuickItem *containmentItem = containment()->property("_plasma_graphicObject").value<QQuickItem *>(); + + if (!containmentItem) { + return point; + } + + QRectF containmentRect(containmentItem->mapToScene(QPoint(0, 0)), QSizeF(containmentItem->width(), containmentItem->height())); + + return QPointF(qBound(containmentRect.left() + 2, point.x(), containmentRect.right() - 2), + qBound(containmentRect.top() + 2, point.y(), containmentRect.bottom() - 2)); +} + + + +void NowDockView::saveConfig() +{ + if (!containment()) + return; + + const auto writeEntry = [&](const char *entry, const QVariant & value) { + containment()->config().writeEntry(entry, value); + }; + + //! convert offset to percent, range [-1,1] 0 is Centered + //! offsetPercent = offset * 2 / (maxLength - length) + // const float offsetPercent = m_offset * 2.0f / (m_maxLength - m_length); + // writeEntry("offset", offsetPercent); + // writeEntry("iconSize", m_iconSize); + // writeEntry("zoomFactor", m_zoomFactor); + // writeEntry("alignment", static_cast<int>(m_alignment)); +} + +void NowDockView::restoreConfig() +{ + if (!containment()) + return; + + const auto readEntry = [&](const char *entry, QVariant defaultValue) -> QVariant { + return containment()->config().readEntry(entry, defaultValue); + }; + //! convert offset-percent to pixels + //! offset = offsetPercent * (maxLength - length) / 2 +// const float offsetPercent {readEntry("offset", 0).toFloat()}; + // const int offset {static_cast<int>(offsetPercent * (m_maxLength - m_length) / 2)}; + // setOffset(offset); + + // setIconSize(readEntry("iconSize", 32).toInt()); + // setZoomFactor(readEntry("zoomFactor", 1.0).toFloat()); + // setAlignment(static_cast<Dock::Alignment>(readEntry("alignment", Dock::Center).toInt())); +} + +//!END SLOTS + + +//!END namespace diff --git a/corona/nowdockview.h b/corona/nowdockview.h new file mode 100644 index 000000000..b0903e720 --- /dev/null +++ b/corona/nowdockview.h @@ -0,0 +1,163 @@ +/* + * Copyright 2014 Bhushan Shah <bhush94@gmail.com> + * Copyright 2014 Marco Martin <notmart@gmail.com> + * + * 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 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * 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 <http://www.gnu.org/licenses/> + */ + +#ifndef NOWDOCKVIEW_H +#define NOWDOCKVIEW_H + +#include <climits> + +#include "plasmaquick/configview.h" +#include "plasmaquick/containmentview.h" +#include "visibilitymanager.h" + +#include <QQuickView> +#include <QQmlListProperty> +#include <QScreen> +#include <QPointer> +#include <QTimer> + +namespace Plasma { +class Types; +class Corona; +class Containment; +} + +/*namespace Candil { +class Dock; +class DockView; +class DockConfigView; +class VisibilityManager; +}*/ + +class NowDockView : public PlasmaQuick::ContainmentView { + Q_OBJECT + + + Q_PROPERTY(bool compositing READ compositing NOTIFY compositingChanged) + + Q_PROPERTY(int height READ height NOTIFY heightChanged) + Q_PROPERTY(int length READ length WRITE setLength NOTIFY lengthChanged) + Q_PROPERTY(int maxLength READ maxLength WRITE setMaxLength NOTIFY maxLengthChanged) + Q_PROPERTY(int maxThickness READ maxThickness WRITE setMaxThickness NOTIFY maxThicknessChanged) + Q_PROPERTY(int offset READ offset WRITE setOffset NOTIFY offsetChanged) + Q_PROPERTY(int width READ width NOTIFY widthChanged) + + Q_PROPERTY(QRect maskArea READ maskArea WRITE setMaskArea NOTIFY maskAreaChanged) + + Q_PROPERTY(VisibilityManager *visibility READ visibility NOTIFY visibilityChanged) + + Q_PROPERTY(QQmlListProperty<QScreen> screens READ screens) + +public: + NowDockView(Plasma::Corona *corona, QScreen *targetScreen = nullptr); + virtual ~NowDockView(); + + void init(); + + // Candil::VisibilityManager *visibility(); + + int maxThickness() const; + void setMaxThickness(int thickness); + + int length() const; + void setLength(int length); + + QRect maskArea() const; + void setMaskArea(QRect area); + + int maxLength() const; + void setMaxLength(int maxLength); + + // Dock::Alignment alignment() const; + // void setAlignment(Dock::Alignment align); + + int offset() const; + void setOffset(int offset); + + void updateOffset(); + + VisibilityManager *visibility(); + + bool compositing() const; + int currentThickness() const; + + void adaptToScreen(QScreen *screen); + + QQmlListProperty<QScreen> screens(); + static int countScreens(QQmlListProperty<QScreen> *property); + static QScreen *atScreens(QQmlListProperty<QScreen> *property, int index); + +public slots: + Q_INVOKABLE void addNewDock(); + Q_INVOKABLE void initialize(); + void resizeWindow(); + void restoreConfig(); + void saveConfig(); + void updateDockPosition(); + +protected slots: + void showConfigurationInterface(Plasma::Applet *applet) override; + +protected: + bool event(QEvent *ev) override; +// void showEvent(QShowEvent *ev) override; + +signals: +// void visibilityChanged(); + void alignmentChanged(); + void compositingChanged(); + void heightChanged(); + void lengthChanged(); + void maskAreaChanged(); + void maxLengthChanged(); + void maxThicknessChanged(); + void offsetChanged(); + void visibilityChanged(); + void widthChanged(); + +public Q_SLOTS: + void updateDockPositionSlot(); + +private: + bool m_secondInitPass; + + int m_offset{0}; + int m_maxThickness{24}; + int m_length{0}; + int m_maxLength{INT_MAX}; + + QRect m_dockGeometry; + QRect m_maskArea; + QPointer<PlasmaQuick::ConfigView> m_configView; + + QTimer m_timerGeometry; + QTimer m_lockGeometry; + Plasma::Theme *theme{nullptr}; + Plasma::Corona *m_corona; + + QPointer<VisibilityManager> m_visibility; + + bool containmentContainsPosition(const QPointF &point) const; + QPointF positionAdjustedForContainment(const QPointF &point) const; + void initWindow(); +}; + +#endif diff --git a/corona/packageplugins/CMakeLists.txt b/corona/packageplugins/CMakeLists.txt new file mode 100644 index 000000000..c37265486 --- /dev/null +++ b/corona/packageplugins/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(shell) diff --git a/corona/packageplugins/shell/CMakeLists.txt b/corona/packageplugins/shell/CMakeLists.txt new file mode 100644 index 000000000..a775e8c7f --- /dev/null +++ b/corona/packageplugins/shell/CMakeLists.txt @@ -0,0 +1 @@ +set(nowdock-app_SRCS nowdockpackage.cpp) diff --git a/corona/packageplugins/shell/nowdockpackage.cpp b/corona/packageplugins/shell/nowdockpackage.cpp new file mode 100644 index 000000000..ba3da39a8 --- /dev/null +++ b/corona/packageplugins/shell/nowdockpackage.cpp @@ -0,0 +1,43 @@ +#include "nowdockpackage.h" + +#include <KPackage/PackageLoader> +#include <KI18n/KLocalizedString> +#include <QDebug> + +NowDockPackage::NowDockPackage(QObject *parent, const QVariantList &args) + : KPackage::PackageStructure(parent, args) +{ +} + +NowDockPackage::~NowDockPackage() +{ +} + +void NowDockPackage::initPackage(KPackage::Package *package) +{ + auto fallback = KPackage::PackageLoader::self()->loadPackage("Plasma/Shell", "org.kde.plasma.desktop"); + + package->setDefaultPackageRoot(QStringLiteral("plasma/shells/")); + package->setPath("org.kde.nowdock.shell"); + package->addFileDefinition("nowdockui", QStringLiteral("views/Panel.qml"), i18n("Now Dock panel")); + //Configuration + package->addFileDefinition("nowdockconfigurationui", QStringLiteral("configuration/NowDockConfiguration.qml"), i18n("Dock configuration UI")); + package->addFileDefinition("configmodel", QStringLiteral("configuration/config.qml"), i18n("Config model")); + package->setFallbackPackage(fallback); + qDebug() << "package is valid" << package->isValid(); +} + +void NowDockPackage::pathChanged(KPackage::Package *package) +{ + if (!package->metadata().isValid()) + return; + + const QString pluginName = package->metadata().pluginId(); + + if (!pluginName.isEmpty() && pluginName != "org.kde.nowdock.shell") { + auto fallback = KPackage::PackageLoader::self()->loadPackage("NowDock/Shell", "org.kde.nowdock.shell"); + package->setFallbackPackage(fallback); + } else if (pluginName.isEmpty() || pluginName == "org.kde.nowdock.shell") { + package->setFallbackPackage(KPackage::Package()); + } +} diff --git a/corona/packageplugins/shell/nowdockpackage.h b/corona/packageplugins/shell/nowdockpackage.h new file mode 100644 index 000000000..d7937c982 --- /dev/null +++ b/corona/packageplugins/shell/nowdockpackage.h @@ -0,0 +1,18 @@ +#ifndef NOWDOCKPACKAGE_H +#define NOWDOCKPACKAGE_H + +#include <QObject> + +#include <KPackage/PackageStructure> + +class NowDockPackage : public KPackage::PackageStructure { + Q_OBJECT + +public: + explicit NowDockPackage(QObject *parent = 0, const QVariantList &args = QVariantList()); + + ~NowDockPackage() override; + void initPackage(KPackage::Package *package) override; + void pathChanged(KPackage::Package *package) override; +}; +#endif // NOWDOCKPACKAGE_H diff --git a/corona/plasmaquick/configview.h b/corona/plasmaquick/configview.h new file mode 100644 index 000000000..e962fc4e7 --- /dev/null +++ b/corona/plasmaquick/configview.h @@ -0,0 +1,89 @@ +/* + * Copyright 2013 Marco Martin <mart@kde.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef CONFIGVIEW_H +#define CONFIGVIEW_H + +#include <QQuickView> + +// +// W A R N I N G +// ------------- +// +// This file is not part of the public Plasma API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +namespace Plasma { +class Applet; +} + +namespace PlasmaQuick { + +class ConfigViewPrivate; + +class ConfigModel; + +class ConfigView : public QQuickView { + Q_OBJECT + Q_PROPERTY(PlasmaQuick::ConfigModel *configModel READ configModel CONSTANT) + Q_PROPERTY(QString appletGlobalShortcut READ appletGlobalShortcut WRITE setAppletGlobalShortcut NOTIFY appletGlobalShortcutChanged) + +public: + /** + * @param applet the applet of this ConfigView + * @param parent the QWindow in which this ConfigView is parented to + **/ + ConfigView(Plasma::Applet *applet, QWindow *parent = 0); + ~ConfigView() override; + + virtual void init(); + + Plasma::Applet *applet(); + + QString appletGlobalShortcut() const; + void setAppletGlobalShortcut(const QString &shortcut); + + /** + * @return the ConfigModel of the ConfigView + **/ + PlasmaQuick::ConfigModel *configModel() const; + +Q_SIGNALS: + void appletGlobalShortcutChanged(); + +protected: + void hideEvent(QHideEvent *ev) override; + void resizeEvent(QResizeEvent *re) override; + +private: + ConfigViewPrivate *const d; + + Q_PRIVATE_SLOT(d, void updateMinimumWidth()) + Q_PRIVATE_SLOT(d, void updateMinimumHeight()) + Q_PRIVATE_SLOT(d, void updateMaximumWidth()) + Q_PRIVATE_SLOT(d, void updateMaximumHeight()) +}; + +} + +#endif // multiple inclusion guard diff --git a/corona/plasmaquick/containmentview.h b/corona/plasmaquick/containmentview.h new file mode 100644 index 000000000..1c6759baa --- /dev/null +++ b/corona/plasmaquick/containmentview.h @@ -0,0 +1,133 @@ +/* + * Copyright 2012 Marco Martin <mart@kde.org> + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef PLASMAQUICKCONTAINMENTVIEW_H +#define PLASMAQUICKCONTAINMENTVIEW_H + +#include <kquickaddons/quickviewsharedengine.h> + +#include "plasma/corona.h" +#include "plasma/containment.h" + +// +// W A R N I N G +// ------------- +// +// This file is not part of the public Plasma API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +namespace PlasmaQuick { + +class ContainmentViewPrivate; + +class ContainmentView : public KQuickAddons::QuickViewSharedEngine { + Q_OBJECT + Q_PROPERTY(Plasma::Types::Location location READ location WRITE setLocation NOTIFY locationChanged) + Q_PROPERTY(Plasma::Types::FormFactor formFactor READ formFactor NOTIFY formFactorChanged) + Q_PROPERTY(QRectF screenGeometry READ screenGeometry NOTIFY screenGeometryChanged) + +public: + /** + * @param corona the corona of this view + * @param parent the QWindow this ContainmentView is parented to + **/ + explicit ContainmentView(Plasma::Corona *corona, QWindow *parent = 0); + ~ContainmentView() override; + + /** + * @return the corona of this view + **/ + Plasma::Corona *corona() const; + + /** + * @return the KConfigGroup of this view + **/ + virtual KConfigGroup config() const; + + /** + * sets the containment for this view + * @param cont the containment of this view + **/ + void setContainment(Plasma::Containment *cont); + + /** + * @return the containment of this ContainmentView + **/ + Plasma::Containment *containment() const; + + /** + * @return the location of this ContainmentView + **/ + Plasma::Types::Location location() const; + + /** + * Sets the location of the ContainmentView + * @param location the location of the ContainmentView + **/ + void setLocation(Plasma::Types::Location location); + + /** + * @return the formfactor of the ContainmentView + **/ + Plasma::Types::FormFactor formFactor() const; + + /** + * @return the screenGeometry of the ContainmentView + **/ + QRectF screenGeometry(); + +protected Q_SLOTS: + /** + * It will be called when the configuration is requested + */ + virtual void showConfigurationInterface(Plasma::Applet *applet); + +Q_SIGNALS: + /** + * emitted when the location is changed + **/ + void locationChanged(Plasma::Types::Location location); + + /** + * emitted when the formfactor is changed + **/ + void formFactorChanged(Plasma::Types::FormFactor formFactor); + + /** + * emitted when the containment is changed + **/ + void containmentChanged(); + + /** + * emitted when the screenGeometry is changed + **/ + void screenGeometryChanged(); + +private: + ContainmentViewPrivate *const d; + Q_PRIVATE_SLOT(d, void updateDestroyed(bool)) + friend class ContainmentViewPrivate; +}; + +} + +#endif // CONTAINMENTVIEW_H diff --git a/corona/plasmaquick/view.h b/corona/plasmaquick/view.h new file mode 100644 index 000000000..d178159b7 --- /dev/null +++ b/corona/plasmaquick/view.h @@ -0,0 +1,132 @@ +/* + * Copyright 2012 Marco Martin <mart@kde.org> + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef PLASMAQUICKVIEW_H +#define PLASMAQUICKVIEW_H + +#include <QtQuick/QQuickView> + +#include "plasma/corona.h" +#include "plasma/containment.h" + +// +// W A R N I N G +// ------------- +// +// This file is not part of the public Plasma API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +namespace PlasmaQuick { + +class ViewPrivate; + +class View : public QQuickView { + Q_OBJECT + Q_PROPERTY(Plasma::Types::Location location READ location WRITE setLocation NOTIFY locationChanged) + Q_PROPERTY(Plasma::Types::FormFactor formFactor READ formFactor NOTIFY formFactorChanged) + Q_PROPERTY(QRectF screenGeometry READ screenGeometry NOTIFY screenGeometryChanged) + +public: + /** + * @param corona the corona of this view + * @param parent the QWindow this View is parented to + **/ + explicit View(Plasma::Corona *corona, QWindow *parent = 0); + ~View() override; + + /** + * @return the corona of this view + **/ + Plasma::Corona *corona() const; + + /** + * @return the KConfigGroup of this view + **/ + virtual KConfigGroup config() const; + + /** + * sets the containment for this view + * @param cont the containment of this view + **/ + void setContainment(Plasma::Containment *cont); + + /** + * @return the containment of this View + **/ + Plasma::Containment *containment() const; + + /** + * @return the location of this View + **/ + Plasma::Types::Location location() const; + + /** + * Sets the location of the View + * @param location the location of the View + **/ + void setLocation(Plasma::Types::Location location); + + /** + * @return the formfactor of the View + **/ + Plasma::Types::FormFactor formFactor() const; + + /** + * @return the screenGeometry of the View + **/ + QRectF screenGeometry(); + +protected Q_SLOTS: + /** + * It will be called when the configuration is requested + */ + virtual void showConfigurationInterface(Plasma::Applet *applet); + +Q_SIGNALS: + /** + * emitted when the location is changed + **/ + void locationChanged(Plasma::Types::Location location); + + /** + * emitted when the formfactor is changed + **/ + void formFactorChanged(Plasma::Types::FormFactor formFactor); + + /** + * emitted when the containment is changed + **/ + void containmentChanged(); + + /** + * emitted when the screenGeometry is changed + **/ + void screenGeometryChanged(); + +private: + ViewPrivate *const d; + friend class ViewPrivate; +}; + +} + +#endif // View_H diff --git a/corona/visibilitymanager.cpp b/corona/visibilitymanager.cpp new file mode 100644 index 000000000..361d1e257 --- /dev/null +++ b/corona/visibilitymanager.cpp @@ -0,0 +1,450 @@ +#include "visibilitymanager.h" + +#include "abstractinterface.h" +#include "xwindowinterface.h" +#include "plasmaquick/containmentview.h" + +#include "../libnowdock/types.h" + +#include <QDebug> + +#include <Plasma/Containment> + +VisibilityManager::VisibilityManager(PlasmaQuick::ContainmentView *view) : + QObject(view), + m_disableHiding(false), + m_isAutoHidden(false), + m_isDockWindowType(true), + m_isHovered(false), + m_secondInitPass(false), + m_windowIsInAttention(false), + m_childrenLength(-1), + m_view(view) +{ +// m_windowSystem = new WindowSystem(this); +// connect(m_windowSystem, SIGNAL(compositingChanged()), this, SLOT(compositingChanged())); + + m_interface = new NowDock::XWindowInterface(m_view); + connect(m_interface, SIGNAL(windowInAttention(bool)), this, SLOT(setWindowInAttention(bool))); + connect(m_interface, SIGNAL(activeWindowChanged()), this, SLOT(activeWindowChanged())); + m_interface->setDockToAllDesktops(); + //fixes a bug in plasma-framework with wrong popups placement + m_interface->setDockNumber(2); + +// connect(this, SIGNAL(screenChanged(QScreen *)), this, SLOT(screenChanged(QScreen *))); +// setPanelScreen(screen()); + + m_updateStateTimer.setSingleShot(true); + m_updateStateTimer.setInterval(900); + connect(&m_updateStateTimer, &QTimer::timeout, this, &VisibilityManager::updateState); + + m_initTimer.setSingleShot(true); + m_initTimer.setInterval(400); + connect(&m_initTimer, &QTimer::timeout, this, &VisibilityManager::initWindow); + + connect(this, SIGNAL(panelVisibilityChanged()), this, SLOT(updateVisibilityFlags())); + setPanelVisibility(NowDock::Types::BelowActive); +// updateVisibilityFlags(); + +// connect(this, SIGNAL(locationChanged()), this, SLOT(updateWindowPosition())); + connect(this, SIGNAL(windowInAttentionChanged()), this, SLOT(updateState())); + + initialize(); +} + +VisibilityManager::~VisibilityManager() +{ +} + +void VisibilityManager::setContainment(Plasma::Containment *containment) +{ + if (containment == m_containment) { + return; + } + + m_containment = containment; +// setVisibility(mode); +} + + +NowDock::Types::Visibility VisibilityManager::panelVisibility() const +{ + return m_panelVisibility; +} + +void VisibilityManager::setPanelVisibility(NowDock::Types::Visibility state) +{ + if (m_panelVisibility == state) { + return; + } + + m_panelVisibility = state; + emit panelVisibilityChanged(); +} + +bool VisibilityManager::isAutoHidden() const +{ + return m_isAutoHidden; +} + +void VisibilityManager::setIsAutoHidden(bool state) +{ + if (m_isAutoHidden == state) { + return; + } + + m_isAutoHidden = state; + emit isAutoHiddenChanged(); +} + +bool VisibilityManager::windowInAttention() const +{ + return m_windowIsInAttention; +} + +void VisibilityManager::setWindowInAttention(bool state) +{ + if (m_windowIsInAttention == state) { + return; + } + + m_windowIsInAttention = state; + emit windowInAttentionChanged(); +} + +bool VisibilityManager::disableHiding() const +{ + return m_disableHiding; +} + +void VisibilityManager::setDisableHiding(bool value) +{ + if (m_disableHiding == value) { + return; + } + + m_disableHiding = value; + + emit disableHidingChanged(); + + if (!m_disableHiding) { + m_updateStateTimer.start(); + } +} + +bool VisibilityManager::isDockWindowType() const +{ + return m_isDockWindowType; +} + +void VisibilityManager::setIsDockWindowType(bool state) +{ + if (m_isDockWindowType == state) { + return; + } + + m_isDockWindowType = state; + + updateVisibilityFlags(); + + emit isDockWindowTypeChanged(); + + m_updateStateTimer.start(); +} + +bool VisibilityManager::isHovered() const +{ + return m_isHovered; +} + +void VisibilityManager::setIsHovered(bool state) +{ + if (m_isHovered == state) { + return; + } + + m_isHovered = state; + emit isHoveredChanged(); +} + +void VisibilityManager::setMaskArea(QRect area) +{ + m_interface->setMaskArea(area); +} + +/*******************************/ + +void VisibilityManager::initialize() +{ + m_secondInitPass = true; + m_initTimer.start(); +} + +void VisibilityManager::initWindow() +{ + updateVisibilityFlags(); + +// updateWindowPosition(); + + // The initialization phase makes two passes because + // changing the window style and type wants a small delay + // and afterwards the second pass positions them correctly + if (m_secondInitPass) { + m_initTimer.start(); + m_secondInitPass = false; + } +} + + +/*void VisibilityManager::updateWindowPosition() +{ + //setPanelScreen(screen()); + // qDebug() << "updateWindowPosition: start..."; + if (!transientParent() || !transientParent()->screen()) { + // qDebug() << "updateWindowPosition: break transient..."; + return; + } + + // qDebug() << "updateWindowPosition: transientParent setting screen position..."; + setPanelScreen(transientParent()->screen()); + + if (!m_screen || m_screenGeometry.isNull()) { + // qDebug() << "updateWindowPosition: break m_screen..."; + return; + } + // qDebug() << "updateWindowPosition: check passed..."; + // qDebug() << m_screen->geometry().x() << " - " << m_screen->geometry().y() << " - " << m_screen->geometry().width() << " - " << m_screen->geometry().height(); + + if (m_location == Plasma::Types::BottomEdge) { + setX(m_screenGeometry.x()); + setY(m_screenGeometry.y()+m_screenGeometry.height() - height()); + } else if (m_location == Plasma::Types::TopEdge) { + setX(m_screenGeometry.x()); + setY(m_screenGeometry.y()); + } else if (m_location == Plasma::Types::LeftEdge) { + setX(m_screenGeometry.x()); + setY(m_screenGeometry.y()); + } else if (m_location == Plasma::Types::RightEdge) { + setX(m_screenGeometry.x()+m_screenGeometry.width() - width()); + setY(m_screenGeometry.y()); + } + + ///FIXME: in come cases the below can not catch up and this may be the reason + //that on start up in some cases dock's contents are not shown, + //needs a timer maybe? + /*if (m_screen != screen()) { + setScreen(m_screen); + }*/ +//} + +void VisibilityManager::updateVisibilityFlags() +{ + m_interface->setDockToAllDesktops(); + + /* if ((m_panelVisibility == AutoHide)||(m_isDockWindowType)) { + m_updateStateTimer.setInterval(2500); + } else { + m_updateStateTimer.setInterval(1500); + }*/ + + m_interface->setDockDefaultFlags(m_isDockWindowType); + +// updateWindowPosition(); + if (!m_isDockWindowType) { + showOnTop(); + } + + m_updateStateTimer.start(); +} + +/* + * It is used from the m_updateStateTimer in order to check the dock's + * visibility and trigger events and actions which are needed to + * respond accordingly + */ +void VisibilityManager::updateState() +{ + // qDebug() << "in update state disableHiding:" <<m_disableHiding; + + //update the dock behavior + switch (m_panelVisibility) { + case NowDock::Types::BelowActive: + if (!m_interface->desktopIsActive() && m_interface->dockIntersectsActiveWindow()) { + if (m_interface->dockIsOnTop() || (m_isDockWindowType && !m_isAutoHidden)) { + // qDebug() << m_isHovered << " - " << m_windowIsInAttention << " - "<< m_disableHiding; + if (!m_isHovered && !m_windowIsInAttention && !m_disableHiding) { + // qDebug() << "must be lowered...."; + emit mustBeLowered(); //showNormal(); + } + } else { + if (m_windowIsInAttention) { + if (!m_isDockWindowType || (m_isDockWindowType && m_isAutoHidden)) { + emit mustBeRaised(); //showOnTop(); + } + } + } + } else { + if (!m_interface->activeIsDialog()) { + if ((!m_interface->desktopIsActive() && m_interface->dockIsCovered()) + || (m_isDockWindowType && m_isAutoHidden)) { + // qDebug() << "must be raised...."; + emit mustBeRaised(); + } else { + showOnTop(); + } + } + } + + break; + + case NowDock::Types::BelowMaximized: + if (!m_interface->desktopIsActive() && m_interface->activeIsMaximized() && m_interface->dockIntersectsActiveWindow()) { + if (m_interface->dockIsOnTop() || (m_isDockWindowType && !m_isAutoHidden)) { + if (!m_isHovered && !m_windowIsInAttention && !m_disableHiding) { + emit mustBeLowered(); //showNormal(); + } + } else { + if (m_windowIsInAttention) { + if (!m_isDockWindowType || (m_isDockWindowType && m_isAutoHidden)) { + emit mustBeRaised(); //showOnTop(); + } + } + } + } else { + if ((!m_interface->desktopIsActive() && m_interface->dockIsCovered()) + || (m_isDockWindowType && m_isAutoHidden)) { + emit mustBeRaised(); + } else { + showOnTop(); + } + } + + break; + + case NowDock::Types::LetWindowsCover: + + //this is not supported in clean Dock Window Types such as in wayland case + if (m_isDockWindowType) { + return; + } + + if (!m_isHovered && m_interface->dockIsOnTop()) { + if (m_interface->dockIsCovering()) { + if (!m_disableHiding) { + emit mustBeLowered(); + } + } else { + showOnBottom(); + } + } else if (m_windowIsInAttention) { + if (!m_interface->dockIsOnTop()) { + if (m_interface->dockIsCovered()) { + emit mustBeRaised(); + } else { + showOnTop(); + } + } + } + + break; + + case NowDock::Types::WindowsGoBelow: + //Do nothing, the dock is OnTop state in every case + break; + + case NowDock::Types::AutoHide: + if (m_windowIsInAttention && m_isAutoHidden) { + emit mustBeRaised(); + } else if (!m_isHovered && !m_disableHiding) { + emit mustBeLowered(); + } + + break; + + case NowDock::Types::AlwaysVisible: + //Do nothing, the dock in OnTop state in every case + break; + } + +} + +void VisibilityManager::showOnTop() +{ + // qDebug() << "reached make top..."; + m_interface->showDockOnTop(); +} + +void VisibilityManager::showNormal() +{ + // qDebug() << "reached make normal..."; + m_interface->showDockAsNormal(); +} + +void VisibilityManager::showOnBottom() +{ + // qDebug() << "reached make bottom..."; + m_interface->showDockOnBottom(); +} + +/***************/ +void VisibilityManager::activeWindowChanged() +{ + if ((m_panelVisibility == NowDock::Types::WindowsGoBelow) + || (m_panelVisibility == NowDock::Types::AlwaysVisible) + || (m_panelVisibility == NowDock::Types::AutoHide)) { + return; + } + + //this check is important because otherwise the signals are so often + //that the timer is never triggered + if (!m_updateStateTimer.isActive()) { + m_updateStateTimer.start(); + } +} + + +//It is used in order to trigger a beautiful slide in effect when +//the dock is totally hidden underneath +void VisibilityManager::showOnTopCheck() +{ + if ((m_panelVisibility == NowDock::Types::BelowActive) || (m_panelVisibility == NowDock::Types::BelowMaximized) + || (m_panelVisibility == NowDock::Types::LetWindowsCover)) { + if (m_interface->dockIsCovered(true)) { + m_updateStateTimer.stop(); + setIsHovered(true); + + emit mustBeRaisedImmediately(); + } else { + showOnTop(); + } + } +} + +bool VisibilityManager::event(QEvent *event) +{ + if (!event) { + return false; + } + + if ((event->type() == QEvent::Enter) && !m_isHovered) { + m_updateStateTimer.stop(); + setIsHovered(true); + + if ((m_panelVisibility == NowDock::Types::AutoHide) || (m_isDockWindowType)) { + if (m_isAutoHidden) { + emit mustBeRaised(); + } + } else { + showOnTop(); + } + } else if (event->type() == QEvent::Leave) { + setIsHovered(false); + + if ((m_panelVisibility != NowDock::Types::WindowsGoBelow) + && (m_panelVisibility != NowDock::Types::AlwaysVisible)) { + m_updateStateTimer.start(); + } + } + + return true; +} diff --git a/corona/visibilitymanager.h b/corona/visibilitymanager.h new file mode 100644 index 000000000..f343c8289 --- /dev/null +++ b/corona/visibilitymanager.h @@ -0,0 +1,106 @@ +#ifndef VISIBILITYMANAGER_H +#define VISIBILITYMANAGER_H + +#include <QObject> +#include "../libnowdock/types.h" +#include "abstractinterface.h" +#include "plasmaquick/containmentview.h" + +#include <QTimer> + +#include <Plasma/Containment> + +class VisibilityManager : public QObject { + Q_OBJECT + + Q_PROPERTY(bool disableHiding READ disableHiding WRITE setDisableHiding NOTIFY disableHidingChanged) + //Q_PROPERTY(bool immutable READ immutable WRITE setImmutable NOTIFY immutableChanged) + Q_PROPERTY(bool isAutoHidden READ isAutoHidden WRITE setIsAutoHidden NOTIFY isAutoHiddenChanged) + Q_PROPERTY(bool isDockWindowType READ isDockWindowType WRITE setIsDockWindowType NOTIFY isDockWindowTypeChanged) + Q_PROPERTY(bool isHovered READ isHovered NOTIFY isHoveredChanged) + Q_PROPERTY(bool windowInAttention READ windowInAttention WRITE setWindowInAttention NOTIFY windowInAttentionChanged) + + Q_PROPERTY(NowDock::Types::Visibility panelVisibility READ panelVisibility WRITE setPanelVisibility NOTIFY panelVisibilityChanged) + +public: + explicit VisibilityManager(PlasmaQuick::ContainmentView *view); + ~VisibilityManager(); + + bool disableHiding() const; + void setDisableHiding(bool state); + + bool isAutoHidden() const; + void setIsAutoHidden(bool state); + + bool isDockWindowType() const; + void setIsDockWindowType(bool state); + + bool isHovered() const; + + bool windowInAttention() const; + + NowDock::Types::Visibility panelVisibility() const; + void setContainment(Plasma::Containment *contaiment); + void setMaskArea(QRect area); + void setPanelVisibility(NowDock::Types::Visibility state); + +public slots: + Q_INVOKABLE void initialize(); + Q_INVOKABLE void showNormal(); + Q_INVOKABLE void showOnTop(); + Q_INVOKABLE void showOnTopCheck(); + Q_INVOKABLE void showOnBottom(); + bool event(QEvent *event); + void setWindowInAttention(bool state); + void updateVisibilityFlags(); + +Q_SIGNALS: + void disableHidingChanged(); + void isAutoHiddenChanged(); + void isDockWindowTypeChanged(); + void isHoveredChanged(); + void mustBeLowered(); //are used to triger the sliding animations from the qml part + void mustBeRaised(); + void mustBeRaisedImmediately(); + void panelVisibilityChanged(); + void windowInAttentionChanged(); + +private Q_SLOTS: + void activeWindowChanged(); + //void compositingChanged(); + void updateState(); + void initWindow(); + void setIsHovered(bool state); + //void screenChanged(QScreen *screen); + //void setScreenGeometry(QRect geometry); + //void updateWindowPosition(); + +private: + bool m_disableHiding; + bool m_isAutoHidden; + bool m_isDockWindowType; + bool m_isHovered; + //second pass of the initialization + bool m_secondInitPass; + bool m_windowIsInAttention; + + int m_childrenLength; + QRect m_maskArea; + + QTimer m_initTimer; + QTimer m_updateStateTimer; + + Plasma::Containment *m_containment; + PlasmaQuick::ContainmentView *m_view; + + NowDock::AbstractInterface *m_interface; + NowDock::Types::Visibility m_panelVisibility; + + +}; + + +#endif + + + diff --git a/corona/xwindowinterface.cpp b/corona/xwindowinterface.cpp new file mode 100644 index 000000000..530695c21 --- /dev/null +++ b/corona/xwindowinterface.cpp @@ -0,0 +1,361 @@ +#include "xwindowinterface.h" + +#include <QDebug> + +#include <KWindowInfo> +#include <KWindowSystem> + +namespace NowDock { + +XWindowInterface::XWindowInterface(QQuickWindow *parent) : + AbstractInterface(parent), + m_demandsAttention(0) +{ + m_activeWindow = KWindowSystem::activeWindow(); + + connect(KWindowSystem::self(), SIGNAL(activeWindowChanged(WId)), this, SLOT(activeWindowChanged(WId))); + connect(KWindowSystem::self(), SIGNAL(windowChanged(WId, NET::Properties, NET::Properties2)), this, SLOT(windowChanged(WId, NET::Properties, NET::Properties2))); + connect(KWindowSystem::self(), SIGNAL(windowRemoved(WId)), this, SLOT(windowRemoved(WId))); + + connect(this, SIGNAL(dockNumberChanged(uint)), this, SLOT(dockNumberChanged(uint))); +} + +XWindowInterface::~XWindowInterface() +{ +} + +void XWindowInterface::dockNumberChanged(unsigned int no) +{ + if (no == 1) { + m_dockWindow->setFlags(Qt::Tool | Qt::WindowDoesNotAcceptFocus | Qt::FramelessWindowHint); + } +} + +void XWindowInterface::setDockToAllDesktops() +{ + KWindowSystem::setOnAllDesktops(m_dockWindow->winId(), true); +} + +void XWindowInterface::setDockDefaultFlags(bool dock) +{ + //Notice: the Qt::Tool flag even though it works perfectly for a single Now Dock + //it creates a strange situation when there are two and more Now Dock's + //in that case it is used only for the first created Now Dock + m_isDockWindowType = dock; + + if ((m_dockNumber == 1) && (!m_isDockWindowType)) { + m_dockWindow->setFlags(Qt::Tool | Qt::WindowDoesNotAcceptFocus | Qt::FramelessWindowHint); + } else { + KWindowSystem::setType(m_dockWindow->winId(), NET::Dock); + KWindowSystem::setState(m_dockWindow->winId(), NET::SkipTaskbar | NET::SkipPager); + } +} + +void XWindowInterface::showDockOnTop() +{ + //this is the only way in order to not break the case of two and more NowDocks + //there is a small issue that the pop ups from locked plasmoids are opened + //on the maximum thickness + + //qDebug() << "Docknumber:" << m_dockNumber; + if (m_isDockWindowType) { + return; + } + + if (m_dockNumber != 1) { + KWindowSystem::setType(m_dockWindow->winId(), NET::Dock); + } + + KWindowSystem::clearState(m_dockWindow->winId(), NET::KeepBelow); + KWindowSystem::setState(m_dockWindow->winId(), NET::KeepAbove); +} + +void XWindowInterface::showDockAsNormal() +{ + // qDebug() << "reached make normal..."; + if (m_isDockWindowType) { + return; + } + + if (m_dockNumber != 1) { + m_dockWindow->setFlags(Qt::Tool | Qt::WindowDoesNotAcceptFocus | Qt::FramelessWindowHint); + } + + KWindowSystem::clearState(m_dockWindow->winId(), NET::KeepAbove); + KWindowSystem::clearState(m_dockWindow->winId(), NET::KeepBelow); +} + +void XWindowInterface::showDockOnBottom() +{ + // qDebug() << "reached make bottom..."; + if (m_isDockWindowType) { + return; + } + + if (m_dockNumber != 1) { + m_dockWindow->setFlags(Qt::Tool | Qt::WindowDoesNotAcceptFocus | Qt::FramelessWindowHint); + } + + KWindowSystem::clearState(m_dockWindow->winId(), NET::KeepAbove); + KWindowSystem::setState(m_dockWindow->winId(), NET::KeepBelow); +} + + +bool XWindowInterface::isDesktop(WId id) const +{ + KWindowInfo info(id, NET::WMWindowType); + + if (!info.valid()) { + return false; + } + + NET::WindowType type = info.windowType(NET::DesktopMask | NET::DockMask | NET::DialogMask); + + return type == NET::Desktop; +} + +bool XWindowInterface::isDialog(WId id) const +{ + KWindowInfo info(id, NET::WMWindowType); + + if (!info.valid()) { + return false; + } + + NET::WindowType type = info.windowType(NET::DesktopMask | NET::DockMask | NET::DialogMask); + + return type == NET::Dialog; +} + +bool XWindowInterface::isMaximized(WId id) const +{ + KWindowInfo info(id, NET::WMState); + + if (!info.valid()) { + return false; + } + + return (info.hasState(NET::Max)); +} + +bool XWindowInterface::isNormal(WId id) const +{ + return (!isOnBottom(id) && !isOnTop(id)); +} + +bool XWindowInterface::isOnBottom(WId id) const +{ + KWindowInfo info(id, NET::WMState); + + if (!info.valid()) { + return false; + } + + return (info.hasState(NET::KeepBelow)); +} + +bool XWindowInterface::isOnTop(WId id) const +{ + KWindowInfo info(id, NET::WMState); + + if (!info.valid()) { + return false; + } + + return (info.hasState(NET::KeepAbove)); +} + +bool XWindowInterface::activeIsDialog() const +{ + return isDialog(m_activeWindow); +} + +bool XWindowInterface::activeIsMaximized() const +{ + return isMaximized(m_activeWindow); +} + + +bool XWindowInterface::desktopIsActive() const +{ + return isDesktop(m_activeWindow); +} + +bool XWindowInterface::dockIsOnTop() const +{ + return isOnTop(m_dockWindow->winId()); + +} + +bool XWindowInterface::dockInNormalState() const +{ + return isNormal(m_dockWindow->winId()); +} + +bool XWindowInterface::dockIsBelow() const +{ + return isOnBottom(m_dockWindow->winId()); +} + +bool XWindowInterface::dockIntersectsActiveWindow() const +{ + KWindowInfo activeInfo(m_activeWindow, NET::WMGeometry); + + if (activeInfo.valid()) { + QRect maskSize; + + if (!m_maskArea.isNull()) { + maskSize = QRect(m_dockWindow->x() + m_maskArea.x(), m_dockWindow->y() + m_maskArea.y(), m_maskArea.width(), m_maskArea.height()); + } else { + maskSize = QRect(m_dockWindow->x(), m_dockWindow->y(), m_dockWindow->width(), m_dockWindow->height()); + } + + return maskSize.intersects(activeInfo.geometry()); + } else { + return false; + } +} + + +bool XWindowInterface::dockIsCovered(bool totally) const +{ + int currentDockPos = -1; + + QList<WId> windows = KWindowSystem::stackingOrder(); + int size = windows.count(); + + for (int i = size - 1; i >= 0; --i) { + WId window = windows.at(i); + + if (window == m_dockWindow->winId()) { + currentDockPos = i; + break; + } + } + + if (currentDockPos >= 0) { + QRect maskSize; + + if (!m_maskArea.isNull()) { + maskSize = QRect(m_dockWindow->x() + m_maskArea.x(), m_dockWindow->y() + m_maskArea.y(), m_maskArea.width(), m_maskArea.height()); + } else { + maskSize = QRect(m_dockWindow->x(), m_dockWindow->y(), m_dockWindow->width(), m_dockWindow->height()); + } + + WId transient = 0; + + if (m_dockWindow->transientParent()) { + transient = m_dockWindow->transientParent()->winId(); + } + + for (int j = size - 1; j > currentDockPos; --j) { + WId window = windows.at(j); + + KWindowInfo info(window, NET::WMState | NET::XAWMState | NET::WMGeometry); + + if (info.valid() && !isDesktop(window) && transient != window && !info.isMinimized()) { + if (totally) { + QRect winGeometry = info.geometry(); + + if ((maskSize.left() >= winGeometry.left()) && (maskSize.top() >= winGeometry.top()) + && (maskSize.right() <= winGeometry.right()) && (maskSize.bottom() <= winGeometry.bottom())) { + return true; + } + } else { + if (maskSize.intersects(info.geometry())) { + return true; + } + } + } + } + } + + return false; +} + +bool XWindowInterface::dockIsCovering() const +{ + int currentDockPos = -1; + + QList<WId> windows = KWindowSystem::stackingOrder(); + int size = windows.count(); + + for (int i = size - 1; i >= 0; --i) { + WId window = windows.at(i); + + if (window == m_dockWindow->winId()) { + currentDockPos = i; + break; + } + } + + if (currentDockPos >= 0) { + QRect maskSize; + + if (!m_maskArea.isNull()) { + maskSize = QRect(m_dockWindow->x() + m_maskArea.x(), m_dockWindow->y() + m_maskArea.y(), m_maskArea.width(), m_maskArea.height()); + } else { + maskSize = QRect(m_dockWindow->x(), m_dockWindow->y(), m_dockWindow->width(), m_dockWindow->height()); + } + + WId transient = 0; + + if (m_dockWindow->transientParent()) { + transient = m_dockWindow->transientParent()->winId(); + } + + for (int j = currentDockPos - 1; j >= 0; --j) { + WId window = windows.at(j); + + KWindowInfo info(window, NET::WMState | NET::XAWMState | NET::WMGeometry); + + if (info.valid() && !isDesktop(window) && transient != window && !info.isMinimized() && maskSize.intersects(info.geometry())) { + return true; + } + } + } + + return false; +} + +/* + * SLOTS + */ + +void XWindowInterface::activeWindowChanged(WId win) +{ + m_activeWindow = win; + + emit AbstractInterface::activeWindowChanged(); +} + +void XWindowInterface::windowChanged(WId id, NET::Properties properties, NET::Properties2 properties2) +{ + KWindowInfo info(id, NET::WMState | NET::CloseWindow); + + if (info.valid()) { + if ((m_demandsAttention == 0) && info.hasState(NET::DemandsAttention)) { + m_demandsAttention = id; + emit windowInAttention(true); + } else if ((m_demandsAttention == id) && !info.hasState(NET::DemandsAttention)) { + m_demandsAttention = 0; + emit windowInAttention(false); + } + } + + // emit AbstractInterface::windowChanged(); + + if (id == m_activeWindow) { + emit AbstractInterface::activeWindowChanged(); + } +} + +void XWindowInterface::windowRemoved(WId id) +{ + if (id == m_demandsAttention) { + m_demandsAttention = 0; + emit AbstractInterface::windowInAttention(false); + } +} + +} diff --git a/corona/xwindowinterface.h b/corona/xwindowinterface.h new file mode 100644 index 000000000..178687e77 --- /dev/null +++ b/corona/xwindowinterface.h @@ -0,0 +1,60 @@ +#ifndef XWINDOWINTERFACE_H +#define XWINDOWINTERFACE_H + +#include <QObject> + +#include <KWindowInfo> + +#include "abstractinterface.h" + +namespace NowDock { + +class XWindowInterface : public AbstractInterface { + Q_OBJECT + +public: + explicit XWindowInterface(QQuickWindow *parent); + ~XWindowInterface(); + + bool activeIsDialog() const; + bool activeIsMaximized() const; + bool dockIntersectsActiveWindow() const; + bool desktopIsActive() const; + bool dockIsCovered(bool totally = false) const; + bool dockIsCovering() const; + bool dockIsOnTop() const; + bool dockInNormalState() const; + bool dockIsBelow() const; + + void setDockDefaultFlags(bool dock = false); + void setDockToAllDesktops(); + void setDockToAlwaysVisible(); + void showDockAsNormal(); + void showDockOnBottom(); + void showDockOnTop(); + +private Q_SLOTS: + void activeWindowChanged(WId win); + void dockNumberChanged(unsigned int no); + void windowChanged(WId id, NET::Properties properties, NET::Properties2 properties2); + void windowRemoved(WId id); + +private: + WId m_activeWindow; + WId m_demandsAttention; + + bool isDesktop(WId id) const; + bool isDialog(WId id) const; + bool isMaximized(WId id) const; + bool isNormal(WId id) const; + bool isOnBottom(WId id) const; + bool isOnTop(WId id) const; +}; + +} + +#endif + + + + diff --git a/formatter.sh b/formatter.sh new file mode 100644 index 000000000..c3223bede --- /dev/null +++ b/formatter.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +astyle --options='astylerc' `find -maxdepth 3 -name '*.cpp' -o -name '*.h'` diff --git a/install.sh b/install.sh new file mode 100644 index 000000000..c59457bab --- /dev/null +++ b/install.sh @@ -0,0 +1,14 @@ +#!/bin/bash +#Author: Michail Vourlakos +#Summary: Installation script for Now Dock Panel +#This script was written and tested on openSuSe Leap 42.1 +set -e + +if ! [ -a build ] ; then + mkdir build +fi + +cd build +cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=RELEASE .. +make +sudo make install diff --git a/libnowdock/CMakeLists.txt b/libnowdock/CMakeLists.txt new file mode 100644 index 000000000..5725f41cc --- /dev/null +++ b/libnowdock/CMakeLists.txt @@ -0,0 +1,39 @@ +set (REQUIRED_QT_VERSION "5.6.0") + +find_package(ECM 1.8.0 REQUIRED NO_MODULE) +find_package(Qt5 ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE COMPONENTS Quick Qml) + +find_package(KF5 REQUIRED COMPONENTS + Plasma + PlasmaQuick + WindowSystem + KDELibs4Support + CoreAddons +) + +set(CMAKE_AUTOMOC ON) + +set(nowdock_SRCS + nowdockplugin.cpp + panelwindow.cpp + windowsystem.cpp + xwindowinterface.cpp + abstractinterface.cpp + types.cpp +) + +add_library(nowdockplugin SHARED ${nowdock_SRCS}) + +target_link_libraries(nowdockplugin + Qt5::Quick + Qt5::Qml + KF5::Plasma + KF5::PlasmaQuick + KF5::WindowSystem + KF5::KDELibs4Support + KF5::CoreAddons +) + +install(TARGETS nowdockplugin DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/nowdock) + +install(FILES qmldir DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/nowdock) diff --git a/libnowdock/abstractinterface.cpp b/libnowdock/abstractinterface.cpp new file mode 100644 index 000000000..3153160b7 --- /dev/null +++ b/libnowdock/abstractinterface.cpp @@ -0,0 +1,42 @@ +#include "abstractinterface.h" + +#include <QObject> +#include <QQuickWindow> + +namespace NowDock { + +AbstractInterface::AbstractInterface(QQuickWindow *dock) : + QObject(dock), + m_isDockWindowType(false), + m_dockNumber(0) +{ + m_dockWindow = dock; +} + +void AbstractInterface::setDockNumber(unsigned int no) +{ + if (m_dockNumber == no) { + return; + } + + m_dockNumber = no; + + emit dockNumberChanged(m_dockNumber); +} + +unsigned int AbstractInterface::dockNumber() const +{ + return m_dockNumber; +} + + +void AbstractInterface::setMaskArea(QRect area) +{ + if (m_maskArea == area) { + return; + } + + m_maskArea = area; +} + +} diff --git a/libnowdock/abstractinterface.h b/libnowdock/abstractinterface.h new file mode 100644 index 000000000..df2b30d7b --- /dev/null +++ b/libnowdock/abstractinterface.h @@ -0,0 +1,56 @@ +#ifndef ABSTRACTINTERFACE_H +#define ABSTRACTINTERFACE_H + +#include <QObject> +#include <QQuickWindow> + +namespace NowDock { + +class AbstractInterface : public QObject { + Q_OBJECT + +public: + explicit AbstractInterface(QQuickWindow *dock); + + virtual bool activeIsDialog() const = 0; + virtual bool activeIsMaximized() const = 0; + virtual bool desktopIsActive() const = 0; + virtual bool dockIntersectsActiveWindow() const = 0; + virtual bool dockIsCovered(bool totally = false) const = 0; + virtual bool dockIsCovering() const = 0; + virtual bool dockIsOnTop() const = 0; + virtual bool dockInNormalState() const = 0; + virtual bool dockIsBelow() const = 0; + + //FIXME: This may not be needed, it would be better to investigate in KWindowSystem + //its behavior when setting the window type to NET::Dock + virtual void setDockDefaultFlags(bool dock = false) = 0; + virtual void setDockToAllDesktops() = 0; + virtual void showDockAsNormal() = 0; + virtual void showDockOnBottom() = 0; + virtual void showDockOnTop() = 0; + + void setDockNumber(unsigned int no); + unsigned int dockNumber() const; + + void setMaskArea(QRect area); + +Q_SIGNALS: + void activeWindowChanged(); + void dockNumberChanged(unsigned int no); + void windowInAttention(bool); + //FIXME: there is a chance that this signal is not needed at all + void windowChanged(); + +protected: + bool m_isDockWindowType; + int m_dockNumber; + + QRect m_maskArea; + + QQuickWindow *m_dockWindow; +}; + +} + +#endif diff --git a/libnowdock/nowdockplugin.cpp b/libnowdock/nowdockplugin.cpp new file mode 100644 index 000000000..313633ca1 --- /dev/null +++ b/libnowdock/nowdockplugin.cpp @@ -0,0 +1,17 @@ +#include "nowdockplugin.h" +#include "panelwindow.h" +#include "windowsystem.h" +#include "types.h" + +#include <qqml.h> + +void NowDockPlugin::registerTypes(const char *uri) +{ + Q_ASSERT(uri == QLatin1String("org.kde.nowdock")); + + qmlRegisterUncreatableType<NowDock::Types>(uri, 0, 1, "Types", "NowDock Types uncreatable"); + + qmlRegisterType<NowDock::PanelWindow>(uri, 0, 1, "PanelWindow"); + qmlRegisterType<NowDock::WindowSystem>(uri, 0, 1, "WindowSystem"); +} + diff --git a/libnowdock/nowdockplugin.h b/libnowdock/nowdockplugin.h new file mode 100644 index 000000000..1a4dd835c --- /dev/null +++ b/libnowdock/nowdockplugin.h @@ -0,0 +1,14 @@ +#ifndef NOWDOCKPLUGIN_H +#define NOWDOCKPLUGIN_H + +#include <QQmlExtensionPlugin> + +class NowDockPlugin : public QQmlExtensionPlugin { + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface") + +public: + void registerTypes(const char *uri); +}; + +#endif diff --git a/libnowdock/panelwindow.cpp b/libnowdock/panelwindow.cpp new file mode 100644 index 000000000..ea1455a92 --- /dev/null +++ b/libnowdock/panelwindow.cpp @@ -0,0 +1,1120 @@ +#include "panelwindow.h" + +#include "xwindowinterface.h" +#include "windowsystem.h" + +#include <QMenu> +#include <QQuickWindow> +#include <QRegion> +#include <QScreen> +#include <QTimer> +#include <QWindow> + +#include <QDebug> + +#include <KActionCollection> +#include <KAuthorized> +#include <KLocalizedString> +//#include <KPluginMetaData> +#include <KPluginInfo> + +#include <Plasma/Applet> +#include <Plasma/Corona> +#include <Plasma/Containment> +#include <Plasma/ContainmentActions> +#include <Plasma/Corona> +#include <PlasmaQuick/AppletQuickItem> + +namespace NowDock { + +PanelWindow::PanelWindow(QQuickWindow *parent) : + QQuickWindow(parent), + m_disableHiding(false), + m_immutable(true), + m_isAutoHidden(false), + m_isDockWindowType(false), + m_isHovered(false), + m_secondInitPass(false), + m_windowIsInAttention(false), + m_childrenLength(-1), + m_tempThickness(-1), + m_screen(0), + m_transient(0) +{ + setClearBeforeRendering(true); + setColor(QColor(Qt::transparent)); + setFlags(Qt::FramelessWindowHint | Qt::WindowDoesNotAcceptFocus); + + m_windowSystem = new WindowSystem(this); + connect(m_windowSystem, SIGNAL(compositingChanged()), this, SLOT(compositingChanged())); + + m_interface = new XWindowInterface(this); + connect(m_interface, SIGNAL(windowInAttention(bool)), this, SLOT(setWindowInAttention(bool))); + connect(m_interface, SIGNAL(activeWindowChanged()), this, SLOT(activeWindowChanged())); + m_interface->setDockToAllDesktops(); + + connect(this, SIGNAL(screenChanged(QScreen *)), this, SLOT(screenChanged(QScreen *))); + setPanelScreen(screen()); + + m_updateStateTimer.setSingleShot(true); + m_updateStateTimer.setInterval(900); + connect(&m_updateStateTimer, &QTimer::timeout, this, &PanelWindow::updateState); + + m_initTimer.setSingleShot(true); + m_initTimer.setInterval(400); + connect(&m_initTimer, &QTimer::timeout, this, &PanelWindow::initWindow); + + m_triggerShrinkTransient.setSingleShot(true); + m_triggerShrinkTransient.setInterval(500); + connect(&m_triggerShrinkTransient, &QTimer::timeout, this, &PanelWindow::shrinkTransient); + + connect(this, SIGNAL(panelVisibilityChanged()), this, SLOT(updateVisibilityFlags())); + setPanelVisibility(BelowActive); + updateVisibilityFlags(); + + connect(this, SIGNAL(locationChanged()), this, SLOT(updateWindowPosition())); + connect(this, SIGNAL(windowInAttentionChanged()), this, SLOT(updateState())); + + initialize(); +} + +PanelWindow::~PanelWindow() +{ + qDebug() << "Destroying Now Dock - Magic Window"; +} + +QRect PanelWindow::maskArea() const +{ + return m_maskArea; +} + +void PanelWindow::setMaskArea(QRect area) +{ + if (m_maskArea == area) { + return; + } + + m_maskArea = area; + m_interface->setMaskArea(area); + + setMask(m_maskArea); + + emit maskAreaChanged(); +} + +Plasma::Types::Location PanelWindow::location() const +{ + return m_location; +} + +void PanelWindow::setLocation(Plasma::Types::Location location) +{ + if (m_location == location) { + return; + } + + m_location = location; + + setPanelOrientation(m_location); + + emit locationChanged(); +} + +unsigned int PanelWindow::maximumLength() const +{ + return m_maximumLength; +} + +void PanelWindow::updateMaximumLength() +{ + if (!transientParent()) { + return; + } + + unsigned int length = 0; + + if (m_panelOrientation == Qt::Horizontal) { + length = transientParent()->maximumWidth(); + } else { + length = transientParent()->maximumHeight(); + } + + if (m_maximumLength == length) { + return; + } + + m_maximumLength = length; + + emit maximumLengthChanged(); +} + +PanelWindow::PanelVisibility PanelWindow::panelVisibility() const +{ + return m_panelVisibility; +} + +void PanelWindow::setPanelVisibility(PanelWindow::PanelVisibility state) +{ + if (m_panelVisibility == state) { + return; + } + + m_panelVisibility = state; + emit panelVisibilityChanged(); +} + +QRect PanelWindow::screenGeometry() const +{ + return m_screenGeometry; +} + +void PanelWindow::setScreenGeometry(QRect geometry) +{ + if (m_screenGeometry == geometry) { + return; + } + + m_screenGeometry = geometry; + updateWindowPosition(); + + emit screenGeometryChanged(); +} + +bool PanelWindow::isAutoHidden() const +{ + return m_isAutoHidden; +} + +void PanelWindow::setIsAutoHidden(bool state) +{ + if (m_isAutoHidden == state) { + return; + } + + m_isAutoHidden = state; + emit isAutoHiddenChanged(); +} + + +bool PanelWindow::windowInAttention() const +{ + return m_windowIsInAttention; +} + +void PanelWindow::setWindowInAttention(bool state) +{ + if (m_windowIsInAttention == state) { + return; + } + + m_windowIsInAttention = state; + emit windowInAttentionChanged(); +} + +int PanelWindow::childrenLength() const +{ + return m_childrenLength; +} + +void PanelWindow::setChildrenLength(int value) +{ + if (m_childrenLength == value) { + return; + } + + m_childrenLength = value; + emit childrenLengthChanged(); +} + +bool PanelWindow::disableHiding() const +{ + return m_disableHiding; +} + +void PanelWindow::setDisableHiding(bool value) +{ + if (m_disableHiding == value) { + return; + } + + m_disableHiding = value; + + emit disableHidingChanged(); + + if (!m_disableHiding) { + m_updateStateTimer.start(); + } +} + +bool PanelWindow::isDockWindowType() const +{ + return m_isDockWindowType; +} + +void PanelWindow::setIsDockWindowType(bool state) +{ + if (m_isDockWindowType == state) { + return; + } + + m_isDockWindowType = state; + + updateVisibilityFlags(); + + emit isDockWindowTypeChanged(); + + m_updateStateTimer.start(); +} + + +bool PanelWindow::immutable() const +{ + return m_immutable; +} + +void PanelWindow::setImmutable(bool state) +{ + if (m_immutable == state) { + return; + } + + m_immutable = state; + + emit immutableChanged(); +} + + +bool PanelWindow::isHovered() const +{ + return m_isHovered; +} + +void PanelWindow::setIsHovered(bool state) +{ + if (m_isHovered == state) { + return; + } + + m_isHovered = state; + emit isHoveredChanged(); +} + +void PanelWindow::setPanelOrientation(Plasma::Types::Location location) +{ + if ((location == Plasma::Types::LeftEdge) || (location == Plasma::Types::RightEdge)) { + m_panelOrientation = Qt::Vertical; + } else { + m_panelOrientation = Qt::Horizontal; + } +} + +void PanelWindow::setPanelScreen(QScreen *screen) +{ + if ((m_screen == screen) || (!screen)) { + return; + } + + if (m_screen) { + // qDebug() << m_screen->geometry(); + disconnect(m_screen, SIGNAL(geometryChanged(QRect)), this, SLOT(setScreenGeometry(QRect))); + } + + m_screen = screen; + setScreenGeometry(screen->geometry()); + updateWindowPosition(); + + connect(m_screen, SIGNAL(geometryChanged(QRect)), this, SLOT(setScreenGeometry(QRect))); +} + +void PanelWindow::compositingChanged() +{ + if (m_windowSystem->compositingActive()) { + if (!m_triggerShrinkTransient.isActive()) { + m_triggerShrinkTransient.start(); + } + } +} + +//This calls a timer onX or onY change of the +//transient. Otherwise a loop is created all the time +//and hangs the thread +void PanelWindow::transientPositionChanged() +{ + if (!m_triggerShrinkTransient.isActive()) { + m_triggerShrinkTransient.start(); + } +} + +void PanelWindow::updateTransient() +{ + if (m_transient == transientParent()) { + return; + } + + if (m_transient) { + disconnect(m_transient, SIGNAL(xChanged(int)), this, SLOT(setTransient())); + disconnect(m_transient, SIGNAL(yChanged(int)), this, SLOT(setTransient())); + } + + m_transient = transientParent(); + + connect(m_transient, SIGNAL(xChanged(int)), this, SLOT(transientPositionChanged())); + connect(m_transient, SIGNAL(yChanged(int)), this, SLOT(transientPositionChanged())); + + shrinkTransient(); +} + +void PanelWindow::setTransientThickness(unsigned int thickness) +{ + QWindow *transient = transientParent(); + + if ((thickness > 0) && transient) { + unsigned int newSize = thickness; + + // qDebug() << "inside setTransientThickness"; + if (transient && transient->screen()) { + // qDebug() << "setTransientThickness: transientParent setting screen position..."; + setPanelScreen(transient->screen()); + } + + if (transient) { + if (m_location == Plasma::Types::BottomEdge) { + transient->setMinimumHeight(newSize); + transient->setMaximumHeight(newSize); + transient->setHeight(newSize); + transient->setWidth(m_maximumLength); + + transient->setY(m_screenGeometry.y() + m_screenGeometry.height() - newSize); + } else if (m_location == Plasma::Types::TopEdge) { + transient->setMinimumHeight(newSize); + transient->setMaximumHeight(newSize); + transient->setHeight(newSize); + transient->setWidth(m_maximumLength); + + transient->setY(m_screenGeometry.y()); + } else if (m_location == Plasma::Types::LeftEdge) { + transient->setMinimumWidth(newSize); + transient->setMaximumWidth(newSize); + transient->setWidth(newSize); + transient->setHeight(m_maximumLength); + + transient->setX(m_screenGeometry.x()); + } else if (m_location == Plasma::Types::RightEdge) { + transient->setMinimumWidth(newSize); + transient->setMaximumWidth(newSize); + transient->setWidth(newSize); + transient->setHeight(m_maximumLength); + + transient->setX(m_screenGeometry.x() + m_screenGeometry.width() - newSize); + } + + if (m_tempThickness < 0) { + m_tempThickness = newSize; + m_secondInitPass = false; + m_initTimer.start(); + } else { + m_tempThickness = -1; + } + } + } +} + +/******************************/ + +void PanelWindow::addAppletItem(QObject *item) +{ + PlasmaQuick::AppletQuickItem *dynItem = qobject_cast<PlasmaQuick::AppletQuickItem *>(item); + + //This is used in order to set the local containment variable + if (dynItem && !m_containment) { + Plasma::Applet *applet = dynItem->applet(); + + if (applet) { + m_containment = applet->containment(); + + //Count the NowDock's + //Notice: the Qt::Tool flag even though it works perfectly for a single Now Dock + //it creates a strange situation when there are two and more Now Dock's + //in that case it is used only for the first created Now Dock + //the following code sets the dock's number + Plasma::Corona *corona = m_containment->corona(); + int docks = 0; + + foreach (Plasma::Containment *con, corona->containments()) { + //DEPRECATED code: expecting Frameworks 5.28 to be general available to replace this code + KPluginInfo info = con->pluginInfo(); + + if (info.pluginName() == "org.kde.store.nowdock.panel") { + docks++; + + if (m_containment == con) { + m_interface->setDockNumber(docks); + m_interface->showDockOnTop(); + m_updateStateTimer.start(); + // qDebug() << "Now Dock Panels counter :" << docks; + break; + } + } + } + } + } + + if (!dynItem || m_appletItems.contains(dynItem)) { + return; + } + + m_appletItems.append(dynItem); +} + +void PanelWindow::removeAppletItem(QObject *item) +{ + PlasmaQuick::AppletQuickItem *dynItem = qobject_cast<PlasmaQuick::AppletQuickItem *>(item); + + if (!dynItem) { + return; + } + + m_appletItems.removeAll(dynItem); +} + +/*******************************/ + +void PanelWindow::initialize() +{ + m_secondInitPass = true; + m_initTimer.start(); +} + +void PanelWindow::initWindow() +{ + updateVisibilityFlags(); + updateTransient(); + + if (m_tempThickness > 0) { + setTransientThickness(m_tempThickness); + } + + updateWindowPosition(); + + // The initialization phase makes two passes because + // changing the window style and type wants a small delay + // and afterwards the second pass positions them correctly + if (m_secondInitPass) { + m_initTimer.start(); + m_secondInitPass = false; + } +} + +void PanelWindow::shrinkTransient() +{ + if (m_immutable && m_windowSystem->compositingActive() && transientParent()) { + // qDebug() <<"shrinkTransient: start..."; + + if (transientParent() && transientParent()->screen()) { + // qDebug() << "transientParent: setting screen position..."; + setPanelScreen(transientParent()->screen()); + } + + if (!m_screen) { + return; + } + + updateMaximumLength(); + + int newSize = 15; + int transWidth = transientParent()->width(); + int transHeight = transientParent()->height(); + int centerX = x() + width() / 2; + int centerY = y() + height() / 2; + + QWindow *transient = transientParent(); + int screenLength = ((m_location == Plasma::Types::BottomEdge) || (m_location == Plasma::Types::TopEdge)) ? + m_screenGeometry.width() : m_screenGeometry.height(); + + int tempLength = qMax(screenLength / 2, m_childrenLength); + + if (transient) { + if ((m_location == Plasma::Types::BottomEdge) || (m_location == Plasma::Types::TopEdge)) { + if (transient->height() != newSize) { + transient->setMinimumHeight(0); + transient->setHeight(newSize); + } + + if (transient->width() != tempLength) { + transient->setWidth(tempLength); + } + + int newX = centerX - transWidth / 2; + + if (transient->x() != newX) { + transient->setX(centerX - transWidth / 2); + } + + int newY = 0; + + if (m_location == Plasma::Types::BottomEdge) { + newY = m_screenGeometry.y() + m_screenGeometry.height() - newSize; + } else if (m_location == Plasma::Types::TopEdge) { + newY = m_screenGeometry.y(); + } + + if (transient->y() != newY) { + transient->setY(newY); + } + } else if ((m_location == Plasma::Types::LeftEdge) || (m_location == Plasma::Types::RightEdge)) { + if (transient->width() != newSize) { + transient->setMinimumWidth(0); + transient->setWidth(newSize); + } + + if (transient->height() != tempLength) { + transient->setHeight(tempLength); + } + + int newY = centerY - transHeight / 2; + + if (transient->y() != newY) { + transient->setY(newY); + } + + int newX = 0; + + if (m_location == Plasma::Types::LeftEdge) { + newX = m_screenGeometry.x(); + } else if (m_location == Plasma::Types::RightEdge) { + newX = m_screenGeometry.x() + m_screenGeometry.width() - newSize; + } + + if (transient->x() != newX) { + transient->setX(newX); + } + } + } + } +} + +void PanelWindow::updateWindowPosition() +{ + //setPanelScreen(screen()); + // qDebug() << "updateWindowPosition: start..."; + if (!transientParent() || !transientParent()->screen()) { + // qDebug() << "updateWindowPosition: break transient..."; + return; + } + + // qDebug() << "updateWindowPosition: transientParent setting screen position..."; + setPanelScreen(transientParent()->screen()); + + if (!m_screen || m_screenGeometry.isNull()) { + // qDebug() << "updateWindowPosition: break m_screen..."; + return; + } + + // qDebug() << "updateWindowPosition: check passed..."; + // qDebug() << m_screen->geometry().x() << " - " << m_screen->geometry().y() << " - " << m_screen->geometry().width() << " - " << m_screen->geometry().height(); + + if (m_location == Plasma::Types::BottomEdge) { + setX(m_screenGeometry.x()); + setY(m_screenGeometry.y() + m_screenGeometry.height() - height()); + } else if (m_location == Plasma::Types::TopEdge) { + setX(m_screenGeometry.x()); + setY(m_screenGeometry.y()); + } else if (m_location == Plasma::Types::LeftEdge) { + setX(m_screenGeometry.x()); + setY(m_screenGeometry.y()); + } else if (m_location == Plasma::Types::RightEdge) { + setX(m_screenGeometry.x() + m_screenGeometry.width() - width()); + setY(m_screenGeometry.y()); + } + + ///FIXME: in come cases the below can not catch up and this may be the reason + //that on start up in some cases dock's contents are not shown, + //needs a timer maybe? + /*if (m_screen != screen()) { + setScreen(m_screen); + }*/ +} + +void PanelWindow::updateVisibilityFlags() +{ + m_interface->setDockToAllDesktops(); + + /* if ((m_panelVisibility == AutoHide)||(m_isDockWindowType)) { + m_updateStateTimer.setInterval(2500); + } else { + m_updateStateTimer.setInterval(1500); + }*/ + + m_interface->setDockDefaultFlags(m_isDockWindowType); + updateWindowPosition(); + + if (!m_isDockWindowType) { + showOnTop(); + } + + m_updateStateTimer.start(); +} + +void PanelWindow::menuAboutToHide() +{ + setDisableHiding(false); + m_updateStateTimer.start(); +} + +/* + * It is used from the m_updateStateTimer in order to check the dock's + * visibility and trigger events and actions which are needed to + * respond accordingly + */ +void PanelWindow::updateState() +{ + // qDebug() << "in update state disableHiding:" <<m_disableHiding; + + //update the dock behavior + switch (m_panelVisibility) { + case BelowActive: + if (!m_interface->desktopIsActive() && m_interface->dockIntersectsActiveWindow()) { + if (m_interface->dockIsOnTop() || (m_isDockWindowType && !m_isAutoHidden)) { + // qDebug() << m_isHovered << " - " << m_windowIsInAttention << " - "<< m_disableHiding; + if (!m_isHovered && !m_windowIsInAttention && !m_disableHiding) { + // qDebug() << "must be lowered...."; + emit mustBeLowered(); //showNormal(); + } + } else { + if (m_windowIsInAttention) { + if (!m_isDockWindowType || (m_isDockWindowType && m_isAutoHidden)) { + emit mustBeRaised(); //showOnTop(); + } + } + } + } else { + if (!m_interface->activeIsDialog()) { + if ((!m_interface->desktopIsActive() && m_interface->dockIsCovered()) + || (m_isDockWindowType && m_isAutoHidden)) { + // qDebug() << "must be raised...."; + emit mustBeRaised(); + } else { + showOnTop(); + } + } + } + + break; + + case BelowMaximized: + if (!m_interface->desktopIsActive() && m_interface->activeIsMaximized() && m_interface->dockIntersectsActiveWindow()) { + if (m_interface->dockIsOnTop() || (m_isDockWindowType && !m_isAutoHidden)) { + if (!m_isHovered && !m_windowIsInAttention && !m_disableHiding) { + emit mustBeLowered(); //showNormal(); + } + } else { + if (m_windowIsInAttention) { + if (!m_isDockWindowType || (m_isDockWindowType && m_isAutoHidden)) { + emit mustBeRaised(); //showOnTop(); + } + } + } + } else { + if ((!m_interface->desktopIsActive() && m_interface->dockIsCovered()) + || (m_isDockWindowType && m_isAutoHidden)) { + emit mustBeRaised(); + } else { + showOnTop(); + } + } + + break; + + case LetWindowsCover: + + //this is not supported in clean Dock Window Types such as in wayland case + if (m_isDockWindowType) { + return; + } + + if (!m_isHovered && m_interface->dockIsOnTop()) { + if (m_interface->dockIsCovering()) { + if (!m_disableHiding) { + emit mustBeLowered(); + } + } else { + showOnBottom(); + } + } else if (m_windowIsInAttention) { + if (!m_interface->dockIsOnTop()) { + if (m_interface->dockIsCovered()) { + emit mustBeRaised(); + } else { + showOnTop(); + } + } + } + + break; + + case WindowsGoBelow: + //Do nothing, the dock is OnTop state in every case + break; + + case AutoHide: + if (m_windowIsInAttention && m_isAutoHidden) { + emit mustBeRaised(); + } else if (!m_isHovered && !m_disableHiding) { + emit mustBeLowered(); + } + + break; + + case AlwaysVisible: + //Do nothing, the dock in OnTop state in every case + break; + } + +} + +void PanelWindow::showOnTop() +{ + // qDebug() << "reached make top..."; + m_interface->showDockOnTop(); +} + +void PanelWindow::showNormal() +{ + // qDebug() << "reached make normal..."; + m_interface->showDockAsNormal(); +} + +void PanelWindow::showOnBottom() +{ + // qDebug() << "reached make bottom..."; + m_interface->showDockOnBottom(); +} + + +/***************/ +void PanelWindow::activeWindowChanged() +{ + if ((m_panelVisibility == WindowsGoBelow) + || (m_panelVisibility == AlwaysVisible) + || (m_panelVisibility == AutoHide)) { + return; + } + + //this check is important because otherwise the signals are so often + //that the timer is never triggered + if (!m_updateStateTimer.isActive()) { + m_updateStateTimer.start(); + } +} + + +//It is used in order to trigger a beautiful slide in effect when +//the dock is totally hidden underneath +void PanelWindow::showOnTopCheck() +{ + if ((m_panelVisibility == BelowActive) || (m_panelVisibility == BelowMaximized) + || (m_panelVisibility == LetWindowsCover)) { + if (m_interface->dockIsCovered(true)) { + m_updateStateTimer.stop(); + setIsHovered(true); + updateTransient(); + + emit mustBeRaisedImmediately(); + } else { + showOnTop(); + } + } +} + +bool PanelWindow::event(QEvent *event) +{ + if (!event) { + return false; + } + + if ((event->type() == QEvent::Enter) && !m_isHovered) { + m_updateStateTimer.stop(); + setIsHovered(true); + + updateTransient(); + + if ((m_panelVisibility == AutoHide) || (m_isDockWindowType)) { + if (m_isAutoHidden) { + emit mustBeRaised(); + } + } else { + showOnTop(); + } + } else if (event->type() == QEvent::Leave) { + setIsHovered(false); + + if ((m_panelVisibility != WindowsGoBelow) + && (m_panelVisibility != AlwaysVisible)) { + m_updateStateTimer.start(); + } + } + + return QQuickWindow::event(event); +} + +void PanelWindow::mouseReleaseEvent(QMouseEvent *event) +{ + if (!event || !m_containment) { + return; + } + + QQuickWindow::mouseReleaseEvent(event); + + event->setAccepted(m_containment->containmentActions().contains(Plasma::ContainmentActions::eventToString(event))); +} + +void PanelWindow::mousePressEvent(QMouseEvent *event) +{ + if (!event || !m_containment) { + return; + } + + QQuickWindow::mousePressEvent(event); + + //even if the menu is executed synchronously, other events may be processed + //by the qml incubator when plasma is loading, so we need to guard there + if (m_contextMenu) { + m_contextMenu.data()->close(); + return; + } + + const QString trigger = Plasma::ContainmentActions::eventToString(event); + Plasma::ContainmentActions *plugin = m_containment->containmentActions().value(trigger); + + if (!plugin || plugin->contextualActions().isEmpty()) { + event->setAccepted(false); + return; + } + + //the plugin can be a single action or a context menu + //Don't have an action list? execute as single action + //and set the event position as action data + if (plugin->contextualActions().length() == 1) { + QAction *action = plugin->contextualActions().at(0); + action->setData(event->pos()); + action->trigger(); + event->accept(); + return; + } + + //FIXME: very inefficient appletAt() implementation + Plasma::Applet *applet = 0; + + foreach (PlasmaQuick::AppletQuickItem *ai, m_appletItems) { + if (ai && ai->isVisible() && ai->contains(ai->mapFromItem(contentItem(), event->pos()))) { + applet = ai->applet(); + break; + } else { + ai = 0; + } + } + + QMenu *desktopMenu = new QMenu; + desktopMenu->setAttribute(Qt::WA_DeleteOnClose); + + m_contextMenu = desktopMenu; + + if (this->mouseGrabberItem()) { + //workaround, this fixes for me most of the right click menu behavior + if (applet) { + //DEPRECATED code: expecting Frameworks 5.28 to be general available to replace this code + KPluginInfo info = applet->pluginInfo(); + + //gives the systemtray direct right click behavior for its applets + if (info.pluginName() != "org.kde.plasma.systemtray") { + this->mouseGrabberItem()->ungrabMouse(); + } + } + + return; + } + + if (applet) { + emit applet->contextualActionsAboutToShow(); + addAppletActions(desktopMenu, applet, event); + } else { + emit m_containment->contextualActionsAboutToShow(); + addContainmentActions(desktopMenu, event); + } + + //this is a workaround where Qt now creates the menu widget + //in .exec before oxygen can polish it and set the following attribute + desktopMenu->setAttribute(Qt::WA_TranslucentBackground); + //end workaround + + QPoint pos = event->globalPos(); + + if (applet) { + desktopMenu->adjustSize(); + + QRect scr = m_screenGeometry; + + int smallStep = 3; + + int x = event->globalPos().x() + smallStep; + int y = event->globalPos().y() + smallStep; + + //qDebug()<<x << " - "<<y; + + if (event->globalPos().x() > scr.center().x()) { + x = event->globalPos().x() - desktopMenu->width() - smallStep; + } + + if (event->globalPos().y() > scr.center().y()) { + y = event->globalPos().y() - desktopMenu->height() - smallStep; + } + + pos = QPoint(x, y); + } + + if (desktopMenu->isEmpty()) { + delete desktopMenu; + event->accept(); + return; + } + + connect(desktopMenu, SIGNAL(aboutToHide()), this, SLOT(menuAboutToHide())); + setDisableHiding(true); + desktopMenu->popup(pos); + + event->setAccepted(true); +} + +void PanelWindow::addAppletActions(QMenu *desktopMenu, Plasma::Applet *applet, QEvent *event) +{ + if (!m_containment) { + return; + } + + foreach (QAction *action, applet->contextualActions()) { + if (action) { + desktopMenu->addAction(action); + } + } + + if (!applet->failedToLaunch()) { + QAction *runAssociatedApplication = applet->actions()->action(QStringLiteral("run associated application")); + + if (runAssociatedApplication && runAssociatedApplication->isEnabled()) { + desktopMenu->addAction(runAssociatedApplication); + } + + QAction *configureApplet = applet->actions()->action(QStringLiteral("configure")); + + if (configureApplet && configureApplet->isEnabled()) { + desktopMenu->addAction(configureApplet); + } + + QAction *appletAlternatives = applet->actions()->action(QStringLiteral("alternatives")); + + if (appletAlternatives && appletAlternatives->isEnabled()) { + desktopMenu->addAction(appletAlternatives); + } + } + + QMenu *containmentMenu = new QMenu(i18nc("%1 is the name of the containment", "%1 Options", m_containment->title()), desktopMenu); + addContainmentActions(containmentMenu, event); + + if (!containmentMenu->isEmpty()) { + int enabled = 0; + //count number of real actions + QListIterator<QAction *> actionsIt(containmentMenu->actions()); + + while (enabled < 3 && actionsIt.hasNext()) { + QAction *action = actionsIt.next(); + + if (action->isVisible() && !action->isSeparator()) { + ++enabled; + } + } + + if (enabled) { + //if there is only one, don't create a submenu + if (enabled < 2) { + foreach (QAction *action, containmentMenu->actions()) { + if (action->isVisible() && !action->isSeparator()) { + desktopMenu->addAction(action); + } + } + } else { + desktopMenu->addMenu(containmentMenu); + } + } + } + + if (m_containment->immutability() == Plasma::Types::Mutable && + (m_containment->containmentType() != Plasma::Types::PanelContainment || m_containment->isUserConfiguring())) { + QAction *closeApplet = applet->actions()->action(QStringLiteral("remove")); + + //qDebug() << "checking for removal" << closeApplet; + if (closeApplet) { + if (!desktopMenu->isEmpty()) { + desktopMenu->addSeparator(); + } + + //qDebug() << "adding close action" << closeApplet->isEnabled() << closeApplet->isVisible(); + desktopMenu->addAction(closeApplet); + } + } +} + + +void PanelWindow::addContainmentActions(QMenu *desktopMenu, QEvent *event) +{ + if (!m_containment) { + return; + } + + if (m_containment->corona()->immutability() != Plasma::Types::Mutable && + !KAuthorized::authorizeAction(QStringLiteral("plasma/containment_actions"))) { + //qDebug() << "immutability"; + return; + } + + //this is what ContainmentPrivate::prepareContainmentActions was + const QString trigger = Plasma::ContainmentActions::eventToString(event); + Plasma::ContainmentActions *plugin = m_containment->containmentActions().value(trigger); + + if (!plugin) { + return; + } + + if (plugin->containment() != m_containment) { + plugin->setContainment(m_containment); + + // now configure it + KConfigGroup cfg(m_containment->corona()->config(), "ActionPlugins"); + cfg = KConfigGroup(&cfg, QString::number(m_containment->containmentType())); + KConfigGroup pluginConfig = KConfigGroup(&cfg, trigger); + plugin->restore(pluginConfig); + } + + QList<QAction *> actions = plugin->contextualActions(); + + if (actions.isEmpty()) { + //it probably didn't bother implementing the function. give the user a chance to set + //a better plugin. note that if the user sets no-plugin this won't happen... + if ((m_containment->containmentType() != Plasma::Types::PanelContainment && + m_containment->containmentType() != Plasma::Types::CustomPanelContainment) && + m_containment->actions()->action(QStringLiteral("configure"))) { + desktopMenu->addAction(m_containment->actions()->action(QStringLiteral("configure"))); + } + } else { + desktopMenu->addActions(actions); + } + + return; +} + +void PanelWindow::screenChanged(QScreen *screen) +{ + // qDebug() << "screen changed..."; + setPanelScreen(screen); +} + +} //NowDock namespace diff --git a/libnowdock/panelwindow.h b/libnowdock/panelwindow.h new file mode 100644 index 000000000..9fa71bcfd --- /dev/null +++ b/libnowdock/panelwindow.h @@ -0,0 +1,203 @@ +#ifndef PANELWINDOW_H +#define PANELWINDOW_H + +#include <QMenu> +#include <QQuickWindow> +#include <QTimer> + +#include <plasma/plasma.h> + +#include <Plasma/Applet> +#include <Plasma/Containment> +#include <PlasmaQuick/AppletQuickItem> + +#include "abstractinterface.h" +#include "windowsystem.h" + +namespace NowDock { + +class PanelWindow : public QQuickWindow { + Q_OBJECT + Q_ENUMS(PanelVisibility) + Q_ENUMS(Alignment) + + Q_PROPERTY(bool disableHiding READ disableHiding WRITE setDisableHiding NOTIFY disableHidingChanged) + + Q_PROPERTY(bool immutable READ immutable WRITE setImmutable NOTIFY immutableChanged) + + Q_PROPERTY(bool isAutoHidden READ isAutoHidden WRITE setIsAutoHidden NOTIFY isAutoHiddenChanged) + + Q_PROPERTY(bool isDockWindowType READ isDockWindowType WRITE setIsDockWindowType NOTIFY isDockWindowTypeChanged) + + Q_PROPERTY(bool isHovered READ isHovered NOTIFY isHoveredChanged) + + Q_PROPERTY(bool windowInAttention READ windowInAttention WRITE setWindowInAttention NOTIFY windowInAttentionChanged) + + Q_PROPERTY(int childrenLength READ childrenLength WRITE setChildrenLength NOTIFY childrenLengthChanged) + + Q_PROPERTY(unsigned int maximumLength READ maximumLength NOTIFY maximumLengthChanged) + + /** + * the window mask, can be used in real transparent panels that set only the visual area + * of the window + * @since 5.8 + */ + Q_PROPERTY(QRect maskArea READ maskArea WRITE setMaskArea NOTIFY maskAreaChanged) + + /** + * the dock's screen geometry, e.g. it is used to set correctly x, y values + */ + Q_PROPERTY(QRect screenGeometry READ screenGeometry NOTIFY screenGeometryChanged) + + Q_PROPERTY(Plasma::Types::Location location READ location WRITE setLocation NOTIFY locationChanged) + + Q_PROPERTY(PanelVisibility panelVisibility READ panelVisibility WRITE setPanelVisibility NOTIFY panelVisibilityChanged) + +public: + enum PanelVisibility { + BelowActive = 0, /** always visible except if ovelaps with the active window, no area reserved */ + BelowMaximized, /** always visible except if ovelaps with an active maximize window, no area reserved */ + LetWindowsCover, /** always visible, windows will go over the panel, no area reserved */ + WindowsGoBelow, /** default, always visible, windows will go under the panel, no area reserved */ + AutoHide, /** the panel will be shownn only if the mouse cursor is on screen edges */ + AlwaysVisible, /** always visible panel, "Normal" plasma panel, accompanies plasma's "Always Visible" */ + }; + + enum Alignment { + Center = 0, + Left, + Right, + Top, + Bottom, + Double = 10 + }; + + explicit PanelWindow(QQuickWindow *parent = Q_NULLPTR); + ~PanelWindow(); + + bool disableHiding() const; + void setDisableHiding(bool state); + + bool immutable() const; + void setImmutable(bool state); + + bool isAutoHidden() const; + void setIsAutoHidden(bool state); + + bool isDockWindowType() const; + void setIsDockWindowType(bool state); + + bool isHovered() const; + + bool windowInAttention() const; + // void setWindowInAttention(bool state); + + int childrenLength() const; + void setChildrenLength(int value); + + unsigned int maximumLength() const; + + QRect maskArea() const; + void setMaskArea(QRect area); + + QRect screenGeometry() const; + + Plasma::Types::Location location() const; + void setLocation(Plasma::Types::Location location); + + PanelVisibility panelVisibility() const; + void setPanelVisibility(PanelVisibility state); + +Q_SIGNALS: + void childrenLengthChanged(); + void disableHidingChanged(); + void immutableChanged(); + void isAutoHiddenChanged(); + void isDockWindowTypeChanged(); + void isHoveredChanged(); + void locationChanged(); + void maskAreaChanged(); + void maximumLengthChanged(); + void mustBeLowered(); + void mustBeRaised(); //are used to triger the sliding animations from the qml part + void mustBeRaisedImmediately(); + void panelVisibilityChanged(); + void screenGeometryChanged(); + void windowInAttentionChanged(); + +public slots: + Q_INVOKABLE void addAppletItem(QObject *item); + Q_INVOKABLE void initialize(); + Q_INVOKABLE void removeAppletItem(QObject *item); + Q_INVOKABLE void setTransientThickness(unsigned int thickness); + Q_INVOKABLE void showNormal(); + Q_INVOKABLE void showOnTop(); + Q_INVOKABLE void showOnTopCheck(); + Q_INVOKABLE void showOnBottom(); + void setWindowInAttention(bool state); + + +protected: + bool event(QEvent *event) override; + void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; + void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE; + +private Q_SLOTS: + void activeWindowChanged(); + void compositingChanged(); + void updateState(); + void initWindow(); + void menuAboutToHide(); + void setIsHovered(bool state); + void screenChanged(QScreen *screen); + void setScreenGeometry(QRect geometry); + void shrinkTransient(); + void transientPositionChanged(); + void updateVisibilityFlags(); + void updateWindowPosition(); + +private: + bool m_disableHiding; + bool m_immutable; + bool m_isAutoHidden; + bool m_isDockWindowType; + bool m_isHovered; + //second pass of the initialization + bool m_secondInitPass; + bool m_windowIsInAttention; + + int m_childrenLength; + int m_tempThickness; + unsigned int m_maximumLength; + + QPointer<Plasma::Containment> m_containment; + QRect m_maskArea; + QRect m_screenGeometry; + QScreen *m_screen; + QList<PlasmaQuick::AppletQuickItem *> m_appletItems; + QTimer m_initTimer; + QTimer m_triggerShrinkTransient; + QTimer m_updateStateTimer; + QWeakPointer<QMenu> m_contextMenu; + QWindow *m_transient; + + Qt::Orientations m_panelOrientation; + + Plasma::Types::Location m_location; + + PanelVisibility m_panelVisibility; + + AbstractInterface *m_interface; + WindowSystem *m_windowSystem; + + void addAppletActions(QMenu *desktopMenu, Plasma::Applet *applet, QEvent *event); + void addContainmentActions(QMenu *desktopMenu, QEvent *event); + void setPanelOrientation(Plasma::Types::Location location); + void setPanelScreen(QScreen *screen); + void updateMaximumLength(); + void updateTransient(); +}; + +} //NowDock namespace + +#endif diff --git a/libnowdock/qmldir b/libnowdock/qmldir new file mode 100644 index 000000000..113d11478 --- /dev/null +++ b/libnowdock/qmldir @@ -0,0 +1,2 @@ +module org.kde.nowdock +plugin nowdockplugin diff --git a/libnowdock/types.cpp b/libnowdock/types.cpp new file mode 100644 index 000000000..8e71fd109 --- /dev/null +++ b/libnowdock/types.cpp @@ -0,0 +1 @@ +#include "types.h" diff --git a/libnowdock/types.h b/libnowdock/types.h new file mode 100644 index 000000000..3b281cbb2 --- /dev/null +++ b/libnowdock/types.h @@ -0,0 +1,53 @@ +#ifndef TYPES_H +#define TYPES_H + +#include <QObject> +#include <QMetaEnum> +#include <QMetaType> + +namespace NowDock { + +class Types { + Q_GADGET + +public: + Types() = delete; + ~Types() {} + + enum Visibility { + BelowActive = 0, /** always visible except if ovelaps with the active window, no area reserved */ + BelowMaximized, /** always visible except if ovelaps with an active maximize window, no area reserved */ + LetWindowsCover, /** always visible, windows will go over the panel, no area reserved */ + WindowsGoBelow, /** default, always visible, windows will go under the panel, no area reserved */ + AutoHide, /** the panel will be shownn only if the mouse cursor is on screen edges */ + AlwaysVisible, /** always visible panel, "Normal" plasma panel, accompanies plasma's "Always Visible" */ + }; + Q_ENUM(Visibility) + + enum Alignment { + Center = 0, + Left, + Right, + Top, + Bottom, + Double = 10 + }; + Q_ENUM(Alignment) + + + enum VisibilityState { + /*! + * @brief the dock is visible + */ + Visible = 1, + + /*! + * @brief the dock is hidden + */ + Hidden = 0 + }; + Q_ENUM(VisibilityState) +}; + +}//end of namespace +#endif diff --git a/libnowdock/windowsystem.cpp b/libnowdock/windowsystem.cpp new file mode 100644 index 000000000..16ccb3c9c --- /dev/null +++ b/libnowdock/windowsystem.cpp @@ -0,0 +1,27 @@ +#include "windowsystem.h" + +#include <KWindowSystem> + +namespace NowDock { + +WindowSystem::WindowSystem(QObject *parent) : + QObject(parent) +{ + connect(KWindowSystem::self(), SIGNAL(compositingChanged(bool)), this, SLOT(compositingChanged(bool))); +} + +WindowSystem::~WindowSystem() +{ +} + +bool WindowSystem::compositingActive() const +{ + return KWindowSystem::compositingActive(); +} + +void WindowSystem::compositingChanged(bool state) +{ + emit compositingChanged(); +} + +} diff --git a/libnowdock/windowsystem.h b/libnowdock/windowsystem.h new file mode 100644 index 000000000..04fdbad7c --- /dev/null +++ b/libnowdock/windowsystem.h @@ -0,0 +1,28 @@ +#ifndef WINDOWSYSTEM_H +#define WINDOWSYSTEM_H + +#include "abstractinterface.h" + +namespace NowDock { + +class WindowSystem : public QObject { + Q_OBJECT + + Q_PROPERTY(bool compositingActive READ compositingActive NOTIFY compositingChanged) + +public: + explicit WindowSystem(QObject *parent = Q_NULLPTR); + ~WindowSystem(); + + bool compositingActive() const; + +Q_SIGNALS: + void compositingChanged(); + +private Q_SLOTS: + void compositingChanged(bool state); +}; + +}//NowDock namespace + +#endif diff --git a/libnowdock/xwindowinterface.cpp b/libnowdock/xwindowinterface.cpp new file mode 100644 index 000000000..530695c21 --- /dev/null +++ b/libnowdock/xwindowinterface.cpp @@ -0,0 +1,361 @@ +#include "xwindowinterface.h" + +#include <QDebug> + +#include <KWindowInfo> +#include <KWindowSystem> + +namespace NowDock { + +XWindowInterface::XWindowInterface(QQuickWindow *parent) : + AbstractInterface(parent), + m_demandsAttention(0) +{ + m_activeWindow = KWindowSystem::activeWindow(); + + connect(KWindowSystem::self(), SIGNAL(activeWindowChanged(WId)), this, SLOT(activeWindowChanged(WId))); + connect(KWindowSystem::self(), SIGNAL(windowChanged(WId, NET::Properties, NET::Properties2)), this, SLOT(windowChanged(WId, NET::Properties, NET::Properties2))); + connect(KWindowSystem::self(), SIGNAL(windowRemoved(WId)), this, SLOT(windowRemoved(WId))); + + connect(this, SIGNAL(dockNumberChanged(uint)), this, SLOT(dockNumberChanged(uint))); +} + +XWindowInterface::~XWindowInterface() +{ +} + +void XWindowInterface::dockNumberChanged(unsigned int no) +{ + if (no == 1) { + m_dockWindow->setFlags(Qt::Tool | Qt::WindowDoesNotAcceptFocus | Qt::FramelessWindowHint); + } +} + +void XWindowInterface::setDockToAllDesktops() +{ + KWindowSystem::setOnAllDesktops(m_dockWindow->winId(), true); +} + +void XWindowInterface::setDockDefaultFlags(bool dock) +{ + //Notice: the Qt::Tool flag even though it works perfectly for a single Now Dock + //it creates a strange situation when there are two and more Now Dock's + //in that case it is used only for the first created Now Dock + m_isDockWindowType = dock; + + if ((m_dockNumber == 1) && (!m_isDockWindowType)) { + m_dockWindow->setFlags(Qt::Tool | Qt::WindowDoesNotAcceptFocus | Qt::FramelessWindowHint); + } else { + KWindowSystem::setType(m_dockWindow->winId(), NET::Dock); + KWindowSystem::setState(m_dockWindow->winId(), NET::SkipTaskbar | NET::SkipPager); + } +} + +void XWindowInterface::showDockOnTop() +{ + //this is the only way in order to not break the case of two and more NowDocks + //there is a small issue that the pop ups from locked plasmoids are opened + //on the maximum thickness + + //qDebug() << "Docknumber:" << m_dockNumber; + if (m_isDockWindowType) { + return; + } + + if (m_dockNumber != 1) { + KWindowSystem::setType(m_dockWindow->winId(), NET::Dock); + } + + KWindowSystem::clearState(m_dockWindow->winId(), NET::KeepBelow); + KWindowSystem::setState(m_dockWindow->winId(), NET::KeepAbove); +} + +void XWindowInterface::showDockAsNormal() +{ + // qDebug() << "reached make normal..."; + if (m_isDockWindowType) { + return; + } + + if (m_dockNumber != 1) { + m_dockWindow->setFlags(Qt::Tool | Qt::WindowDoesNotAcceptFocus | Qt::FramelessWindowHint); + } + + KWindowSystem::clearState(m_dockWindow->winId(), NET::KeepAbove); + KWindowSystem::clearState(m_dockWindow->winId(), NET::KeepBelow); +} + +void XWindowInterface::showDockOnBottom() +{ + // qDebug() << "reached make bottom..."; + if (m_isDockWindowType) { + return; + } + + if (m_dockNumber != 1) { + m_dockWindow->setFlags(Qt::Tool | Qt::WindowDoesNotAcceptFocus | Qt::FramelessWindowHint); + } + + KWindowSystem::clearState(m_dockWindow->winId(), NET::KeepAbove); + KWindowSystem::setState(m_dockWindow->winId(), NET::KeepBelow); +} + + +bool XWindowInterface::isDesktop(WId id) const +{ + KWindowInfo info(id, NET::WMWindowType); + + if (!info.valid()) { + return false; + } + + NET::WindowType type = info.windowType(NET::DesktopMask | NET::DockMask | NET::DialogMask); + + return type == NET::Desktop; +} + +bool XWindowInterface::isDialog(WId id) const +{ + KWindowInfo info(id, NET::WMWindowType); + + if (!info.valid()) { + return false; + } + + NET::WindowType type = info.windowType(NET::DesktopMask | NET::DockMask | NET::DialogMask); + + return type == NET::Dialog; +} + +bool XWindowInterface::isMaximized(WId id) const +{ + KWindowInfo info(id, NET::WMState); + + if (!info.valid()) { + return false; + } + + return (info.hasState(NET::Max)); +} + +bool XWindowInterface::isNormal(WId id) const +{ + return (!isOnBottom(id) && !isOnTop(id)); +} + +bool XWindowInterface::isOnBottom(WId id) const +{ + KWindowInfo info(id, NET::WMState); + + if (!info.valid()) { + return false; + } + + return (info.hasState(NET::KeepBelow)); +} + +bool XWindowInterface::isOnTop(WId id) const +{ + KWindowInfo info(id, NET::WMState); + + if (!info.valid()) { + return false; + } + + return (info.hasState(NET::KeepAbove)); +} + +bool XWindowInterface::activeIsDialog() const +{ + return isDialog(m_activeWindow); +} + +bool XWindowInterface::activeIsMaximized() const +{ + return isMaximized(m_activeWindow); +} + + +bool XWindowInterface::desktopIsActive() const +{ + return isDesktop(m_activeWindow); +} + +bool XWindowInterface::dockIsOnTop() const +{ + return isOnTop(m_dockWindow->winId()); + +} + +bool XWindowInterface::dockInNormalState() const +{ + return isNormal(m_dockWindow->winId()); +} + +bool XWindowInterface::dockIsBelow() const +{ + return isOnBottom(m_dockWindow->winId()); +} + +bool XWindowInterface::dockIntersectsActiveWindow() const +{ + KWindowInfo activeInfo(m_activeWindow, NET::WMGeometry); + + if (activeInfo.valid()) { + QRect maskSize; + + if (!m_maskArea.isNull()) { + maskSize = QRect(m_dockWindow->x() + m_maskArea.x(), m_dockWindow->y() + m_maskArea.y(), m_maskArea.width(), m_maskArea.height()); + } else { + maskSize = QRect(m_dockWindow->x(), m_dockWindow->y(), m_dockWindow->width(), m_dockWindow->height()); + } + + return maskSize.intersects(activeInfo.geometry()); + } else { + return false; + } +} + + +bool XWindowInterface::dockIsCovered(bool totally) const +{ + int currentDockPos = -1; + + QList<WId> windows = KWindowSystem::stackingOrder(); + int size = windows.count(); + + for (int i = size - 1; i >= 0; --i) { + WId window = windows.at(i); + + if (window == m_dockWindow->winId()) { + currentDockPos = i; + break; + } + } + + if (currentDockPos >= 0) { + QRect maskSize; + + if (!m_maskArea.isNull()) { + maskSize = QRect(m_dockWindow->x() + m_maskArea.x(), m_dockWindow->y() + m_maskArea.y(), m_maskArea.width(), m_maskArea.height()); + } else { + maskSize = QRect(m_dockWindow->x(), m_dockWindow->y(), m_dockWindow->width(), m_dockWindow->height()); + } + + WId transient = 0; + + if (m_dockWindow->transientParent()) { + transient = m_dockWindow->transientParent()->winId(); + } + + for (int j = size - 1; j > currentDockPos; --j) { + WId window = windows.at(j); + + KWindowInfo info(window, NET::WMState | NET::XAWMState | NET::WMGeometry); + + if (info.valid() && !isDesktop(window) && transient != window && !info.isMinimized()) { + if (totally) { + QRect winGeometry = info.geometry(); + + if ((maskSize.left() >= winGeometry.left()) && (maskSize.top() >= winGeometry.top()) + && (maskSize.right() <= winGeometry.right()) && (maskSize.bottom() <= winGeometry.bottom())) { + return true; + } + } else { + if (maskSize.intersects(info.geometry())) { + return true; + } + } + } + } + } + + return false; +} + +bool XWindowInterface::dockIsCovering() const +{ + int currentDockPos = -1; + + QList<WId> windows = KWindowSystem::stackingOrder(); + int size = windows.count(); + + for (int i = size - 1; i >= 0; --i) { + WId window = windows.at(i); + + if (window == m_dockWindow->winId()) { + currentDockPos = i; + break; + } + } + + if (currentDockPos >= 0) { + QRect maskSize; + + if (!m_maskArea.isNull()) { + maskSize = QRect(m_dockWindow->x() + m_maskArea.x(), m_dockWindow->y() + m_maskArea.y(), m_maskArea.width(), m_maskArea.height()); + } else { + maskSize = QRect(m_dockWindow->x(), m_dockWindow->y(), m_dockWindow->width(), m_dockWindow->height()); + } + + WId transient = 0; + + if (m_dockWindow->transientParent()) { + transient = m_dockWindow->transientParent()->winId(); + } + + for (int j = currentDockPos - 1; j >= 0; --j) { + WId window = windows.at(j); + + KWindowInfo info(window, NET::WMState | NET::XAWMState | NET::WMGeometry); + + if (info.valid() && !isDesktop(window) && transient != window && !info.isMinimized() && maskSize.intersects(info.geometry())) { + return true; + } + } + } + + return false; +} + +/* + * SLOTS + */ + +void XWindowInterface::activeWindowChanged(WId win) +{ + m_activeWindow = win; + + emit AbstractInterface::activeWindowChanged(); +} + +void XWindowInterface::windowChanged(WId id, NET::Properties properties, NET::Properties2 properties2) +{ + KWindowInfo info(id, NET::WMState | NET::CloseWindow); + + if (info.valid()) { + if ((m_demandsAttention == 0) && info.hasState(NET::DemandsAttention)) { + m_demandsAttention = id; + emit windowInAttention(true); + } else if ((m_demandsAttention == id) && !info.hasState(NET::DemandsAttention)) { + m_demandsAttention = 0; + emit windowInAttention(false); + } + } + + // emit AbstractInterface::windowChanged(); + + if (id == m_activeWindow) { + emit AbstractInterface::activeWindowChanged(); + } +} + +void XWindowInterface::windowRemoved(WId id) +{ + if (id == m_demandsAttention) { + m_demandsAttention = 0; + emit AbstractInterface::windowInAttention(false); + } +} + +} diff --git a/libnowdock/xwindowinterface.h b/libnowdock/xwindowinterface.h new file mode 100644 index 000000000..178687e77 --- /dev/null +++ b/libnowdock/xwindowinterface.h @@ -0,0 +1,60 @@ +#ifndef XWINDOWINTERFACE_H +#define XWINDOWINTERFACE_H + +#include <QObject> + +#include <KWindowInfo> + +#include "abstractinterface.h" + +namespace NowDock { + +class XWindowInterface : public AbstractInterface { + Q_OBJECT + +public: + explicit XWindowInterface(QQuickWindow *parent); + ~XWindowInterface(); + + bool activeIsDialog() const; + bool activeIsMaximized() const; + bool dockIntersectsActiveWindow() const; + bool desktopIsActive() const; + bool dockIsCovered(bool totally = false) const; + bool dockIsCovering() const; + bool dockIsOnTop() const; + bool dockInNormalState() const; + bool dockIsBelow() const; + + void setDockDefaultFlags(bool dock = false); + void setDockToAllDesktops(); + void setDockToAlwaysVisible(); + void showDockAsNormal(); + void showDockOnBottom(); + void showDockOnTop(); + +private Q_SLOTS: + void activeWindowChanged(WId win); + void dockNumberChanged(unsigned int no); + void windowChanged(WId id, NET::Properties properties, NET::Properties2 properties2); + void windowRemoved(WId id); + +private: + WId m_activeWindow; + WId m_demandsAttention; + + bool isDesktop(WId id) const; + bool isDialog(WId id) const; + bool isMaximized(WId id) const; + bool isNormal(WId id) const; + bool isOnBottom(WId id) const; + bool isOnTop(WId id) const; +}; + +} + +#endif + + + + diff --git a/plasmoid.metadata.desktop.template b/plasmoid.metadata.desktop.template new file mode 100644 index 000000000..3357cb7bb --- /dev/null +++ b/plasmoid.metadata.desktop.template @@ -0,0 +1,23 @@ +[Desktop Entry] +_Name=Now Dock +_Comment=Switch between running applications + + +Type=Service +Icon=preferences-system-windows +X-KDE-ServiceTypes=Plasma/Applet +X-Plasma-API=declarativeappletscript +X-Plasma-MainScript=ui/main.qml +X-Plasma-Provides=org.kde.plasma.multitasking +X-KDE-PluginInfo-Author=@AUTHOR@ +X-KDE-PluginInfo-Email=@EMAIL@ +X-KDE-PluginInfo-Name=org.kde.store.nowdock.plasmoid +X-KDE-PluginInfo-Version=@VERSION@ +X-KDE-PluginInfo-Website=@WEBSITE@ +X-KDE-PluginInfo-Category=Windows and Tasks +X-KDE-PluginInfo-Depends= +X-KDE-PluginInfo-License=GPL v2+ +X-KDE-PluginInfo-EnabledByDefault=true + + + diff --git a/plasmoid/CMakeLists.txt b/plasmoid/CMakeLists.txt new file mode 100644 index 000000000..f68a970ff --- /dev/null +++ b/plasmoid/CMakeLists.txt @@ -0,0 +1,6 @@ +file(COPY "contents" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/release) +#update the version number in the configuration window +file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/release/contents/ui/ConfigAppearance.qml.cmake) +configure_file(metadata.desktop.cmake release/metadata.desktop) +configure_file(contents/ui/ConfigAppearance.qml.cmake release/contents/ui/ConfigAppearance.qml) + diff --git a/plasmoid/contents/code/activitiesTools.js b/plasmoid/contents/code/activitiesTools.js new file mode 100644 index 000000000..68bbdeb35 --- /dev/null +++ b/plasmoid/contents/code/activitiesTools.js @@ -0,0 +1,335 @@ +/* + * Copyright 2013 Michail Vourlakos <mvourlakos@gmail.com> + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +var currentActivity; +var launchersOnActivities; +//it is used as the first item in the stringList in order to check the list is ok +var indicator = 'multi'; +var plasmoid; + +function restoreLaunchers(){ + ///this is a stringlist of types activityId, number of launchers, launchers + if(plasmoid && plasmoid.configuration && currentActivity != "00000000-0000-0000-0000-000000000000"){ + var values = plasmoid.configuration.launchers; + values = values.split(";"); + var returnedStringList = []; + + // console.log("----------------------- Restoring ---------------------"); + // console.log("Full Restoration:"+values); + var type = values.splice(0,1); + + // console.log("Pass 1 - " +type); + if (type == indicator){ + // console.log("Pass 2"); + while (values.length > 2){ + // console.log("Pass 3 - "+values); + var actId = values[0]; + var subLaunchers = values.splice(2,values[1]); + // console.log("To Be Restored launchers, "+actId+ ", "+subLaunchers.length+", "+subLaunchers); + + var activityRecord = get(actId); + if(activityRecord){ + // console.log("Activity item found..."); + if(activityRecord.launchers) + activityRecord.launchers.splice(0,activityRecord.launchers.length); + + activityRecord.launchers = subLaunchers; + } + else{ + // console.log("Activity item is added..."); + var res = {id:values[0], launchers:subLaunchers}; + launchersOnActivities.push(res); + } + + values.splice(0,2); + + } + } + + if (get('*')) + returnedStringList = returnedStringList.concat(get('*').launchers); + else{ + var result = {id:'*', launchers:[]}; + launchersOnActivities.push(result); + } + + if (get(currentActivity)) + returnedStringList = returnedStringList.concat(get(currentActivity).launchers); + + // console.log("Restored Strings:"+returnedStringList); + + return returnedStringList; + } + else + return []; +} + +function saveLaunchers(){ + // console.log("----------------------- Saving ---------------------"); + + var returnedStringList = []; + returnedStringList.push(indicator); + + // console.log("Array Size:"+launchersOnActivities.length); + var size = launchersOnActivities.length; + for(var i=size-1; i>=0; --i){ + var activitySaving = get(launchersOnActivities[i].id); + // console.log("Saving, "+activitySaving.id + " - "+activitySaving.launchers.length+" - "+activitySaving.launchers); + if(activitySaving.launchers.length>0){ + /* console.log("------- "+activitySaving.id+" ----------"); + for(var j=0; j<activitySaving.launchers.length; ++j){ + console.log(activitySaving.launchers[j]); + }*/ + + returnedStringList = returnedStringList.concat(String(activitySaving.id)); + returnedStringList = returnedStringList.concat(String(activitySaving.launchers.length)); + // this creates segmentation faults in some cases!!!! + // returnedStringList = returnedStringList.concat(activitySaving.launchers); + + //this fixes the segmentation fault of previous command.... + for(var j=0; j<activitySaving.launchers.length; ++j){ + returnedStringList.push(String(activitySaving.launchers[j])); + } + + ///check returnedStringList integrity + /* for(var j=0; j<returnedStringList.length; ++j){ + console.log("--- "+j+". "+returnedStringList[j]); + }*/ + } + } + + /* console.log("I am out..... "+returnedStringList.length); + for(var i=0; i<returnedStringList.length; ++i){ + console.log(i+". "+returnedStringList[i]); + }*/ + + // console.log("IMPORTANT SAVED LIST: "+returnedStringList); + + plasmoid.configuration.launchers = returnedStringList.join(";"); +} + +function updateLaunchers(launcherList){ + // console.log("----------------------- Updating ---------------------"); + // console.log("---- Full Launchers ------"); + /* for(var j=0; j<launcherList.length; ++j){ + console.log(launcherList[j]); + } */ + + + var tempList; + if(launcherList.length > 0){ + tempList = launcherList.slice(0); + } + else{ + tempList =[]; + } + + // console.log("IMPORTANT SENT LIST: "+tempList.length+" - "+launcherList.length+" - "+tempList); + // + // console.log("In memory Defaults:" + get('*').launchers); + // if(get(currentActivity)) + // console.log("In memory Current: "+get(currentActivity).launchers.length+ ' - ' + get(currentActivity).launchers); + + var currentList = get(currentActivity); + + var resultedCurrent = []; + // console.log("-------------"); + + for(var i=tempList.length-1; i>=0; --i){ + var index=-1; + if(currentList){ + index = getIndex(String(tempList[i]), currentList.launchers); + } + + if(index >=0 || isInSpecificActivity(tempList[i])){ + var result = tempList.splice(i,1); + resultedCurrent.unshift(String(result)); + } + } + + // console.log("Resulted Current: "+resultedCurrent); + + /* console.log("---- To Be Set List ------"); + for(var j=0; j<resultedCurrent.length; ++j){ + console.log(resultedCurrent[j]); + }*/ + + setActivityLaunchers(resultedCurrent, currentActivity); + setDefaultLaunchers(tempList); + + saveLaunchers(); +} + +///////////// +function cleanupRecords(activities){ + for(var i=0; i<launchersOnActivities.length; ++i){ + if(launchersOnActivities[i].id != '*'){ + var index = getIndex(launchersOnActivities[i].id, activities); + if(index < 0){ + console.log("Orphaned Activity Settings removed:"+launchersOnActivities[i].id); + launchersOnActivities.splice(index,1); + } + } + } + saveLaunchers(); +} + + +function getIndex(id, list){ + if(list){ + for(var i=0; i<list.length; ++i){ + if(id == list[i]) + return i; + } + } + + return -1; +} + +function get(actId){ + for(var i=0; i<launchersOnActivities.length; ++i){ + if(launchersOnActivities[i].id == actId) + return launchersOnActivities[i]; + } + + return; +} + +function isOnAllActivities(id){ + var defaults = get('*').launchers; + var index = getIndex(String(id),defaults); + if (index>=0) + return true; + + // console.log("Check exists:"+id+ " - "+ "pos:"+index+" ,,, "+ get('*').activities); + return false; +} + +function isInSpecificActivity(id){ + for(var i=0; i<launchersOnActivities.length; ++i){ + var activity = launchersOnActivities[i]; + if(activity.id != "*"){ + var launchers = activity.launchers; + var index = getIndex(String(id),launchers) + if (index>=0) + return true; + } + } + + return false; +} + + +function setDefaultLaunchers(launchersList){ + // console.log("Set Default Launchers"); + if(!get('*')){ + var result = {id:'*', launchers:launchersList}; + launchersOnActivities.push(result); + } + else{ + if (get('*').launchers) + get('*').launchers.splice(0,get('*').launchers.length); + get('*').launchers=launchersList; + } + + // console.log("Default:::: "+get('*').launchers); +} + +function setActivityLaunchers(launchersList, actId){ + // console.log("Set Activity Launchers"); + var currentList = get(actId); + // console.log("-------------"); + // console.log("ResultedForMemory: "+launchersList); + if(currentList){ + if(currentList.launchers) + currentList.launchers.splice(0,currentList.launchers.length); + + // console.log("list exists"); + currentList.launchers=launchersList; + } + else{ + // console.log("new list"); + var res = {id:actId, launchers:launchersList}; + launchersOnActivities.push(res); + } + + /* console.log("New Memory List: "+get(actId).launchers); + for(var j=0; j<get(actId).launchers.length; ++j){ + console.log(get(actId).launchers[j]); + } */ +} + +///from launcher to All Activities to the current only and vice versa +function toggleLauncherState(id){ + if (isOnAllActivities(id)){ + removeFromList(id,'*') + addToList(id, currentActivity); + } + else{ + removeFromAllSimpleLists(id); + addToList(id, '*'); + } + + saveLaunchers(); +} + +function removeLauncher(id){ + if(get(currentActivity)) + removeFromList(id, currentActivity); + + removeFromList(id, '*'); +} + +function removeFromAllSimpleLists(id){ + for(var i=0; i<launchersOnActivities.length; ++i){ + var activity = launchersOnActivities[i]; + if(activity.id != "*"){ + removeFromList(id, activity.id); + } + } +} + + +function removeFromList(id, activityId){ + var list = get(activityId); + if(list){ + var activityList = list.launchers; + var index = getIndex(String(id),activityList); + + if(index >= 0){ + activityList.splice(index, 1); + } + } +} + +function addToList(id, activityId){ + var activity = get(activityId); + if(!activity){ + var res = {id:activityId, launchers:[]}; + res.launchers.push(id); + launchersOnActivities.push(res); + } + else{ + var launcherList = activity.launchers; + var index = getIndex(id,launcherList); + if(index<0){ + launcherList.push(id); + } + + } +} diff --git a/plasmoid/contents/code/layout.js b/plasmoid/contents/code/layout.js new file mode 100644 index 000000000..3fb6c8bad --- /dev/null +++ b/plasmoid/contents/code/layout.js @@ -0,0 +1,205 @@ +/* + * Copyright 2013 Michail Vourlakos <mvourlakos@gmail.com> + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +function horizontalMargins() { + return taskFrame.margins.left + taskFrame.margins.right; +} + +function verticalMargins() { + return taskFrame.margins.top + taskFrame.margins.bottom; +} + +function adjustMargin(height, margin) { + var available = height - verticalMargins(); + + if (available < units.iconSizes.small) { + return Math.floor((margin * (units.iconSizes.small / available)) / 3); + } + + return margin; +} + +function launcherLayoutTasks() { + return Math.round(tasksModel.launcherCount / Math.floor(preferredMinWidth() / launcherWidth())); +} + +function launcherLayoutWidthDiff() { + return (launcherLayoutTasks() * taskWidth()) - (tasksModel.launcherCount * launcherWidth()); +} + +function logicalTaskCount() { + var count = (tasksModel.count - tasksModel.launcherCount) + 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(); + + return (Math.ceil(length / maximum) * maxStripes()); +} + +function layoutWidth() { + if (plasmoid.configuration.forceStripes && !tasks.vertical) { + return Math.min(tasks.width, Math.max(preferredMaxWidth(), tasksPerStripe() * preferredMaxWidth())); + } else { + return tasks.width; + } +} + +function layoutHeight() { + if (plasmoid.configuration.forceStripes && tasks.vertical) { + return Math.min(tasks.height, Math.max(preferredMaxHeight(), tasksPerStripe() * preferredMaxHeight())); + } else { + return tasks.height; + } +} + +function preferredMinWidth() { + var width = launcherWidth(); + + if (!tasks.vertical && !tasks.iconsOnly) { + width += (units.smallSpacing * 2) + (theme.mSize(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() * 1.6); +} + +function preferredMinHeight() { + // TODO FIXME UPSTREAM: Port to proper font metrics for descenders once we have access to them. + return theme.mSize(theme.defaultFont).height + 4; +} + +function preferredMaxHeight() { + return verticalMargins() + Math.min(units.iconSizes.small * 3, theme.mSize(theme.defaultFont).height * 3); +} + +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, units.iconSizes.small * 3); + + return (baseWidth + horizontalMargins()) + - (adjustMargin(baseWidth, taskFrame.margins.top) + adjustMargin(baseWidth, taskFrame.margins.bottom)); +} + +function layout(container) { + var item; + var stripes = calculateStripes(); + var taskCount = tasksModel.count - tasksModel.launcherCount; + var width = taskWidth(); + var adjustedWidth = width; + var height = taskHeight(); + + if (!tasks.vertical && stripes == 1 && taskCount) + { + var shrink = ((tasksModel.count - tasksModel.launcherCount) * preferredMaxWidth()) + + (tasksModel.launcherCount * 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) { + adjustedWidth = launcherWidth(); + } else if (stripes > 1 && i == tasksModel.launcherCount) { + adjustedWidth += launcherLayoutWidthDiff(); + } + } + + item.width = adjustedWidth; + item.height = height; + item.visible = true; + } +} diff --git a/plasmoid/contents/code/tools.js b/plasmoid/contents/code/tools.js new file mode 100644 index 000000000..28f215792 --- /dev/null +++ b/plasmoid/contents/code/tools.js @@ -0,0 +1,144 @@ +/* + * Copyright 2013 Michail Vourlakos <mvourlakos@gmail.com> + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + + +function wheelActivateNextPrevTask(wheelDelta, eventDelta) { + // magic number 120 for common "one click" + // See: http://qt-project.org/doc/qt-5/qml-qtquick-wheelevent.html#angleDelta-prop + wheelDelta += eventDelta; + var increment = 0; + while (wheelDelta >= 120) { + wheelDelta -= 120; + increment++; + } + while (wheelDelta <= -120) { + wheelDelta += 120; + increment--; + } + while (increment != 0) { + activateNextPrevTask(increment < 0) + increment += (increment < 0) ? 1 : -1; + } + + return wheelDelta; +} + +function activateNextPrevTask(next) { + // FIXME TODO: Unnecessarily convoluted and costly; optimize. + + var taskIndexList = []; + var activeTaskIndex = tasksModel.activeTask; + + for (var i = 0; i < taskList.children.length - 1; ++i) { + var task = taskList.children[i]; + var modelIndex = task.modelIndex(i); + + if (task !== undefined){ + if (task.IsLauncher !== true && task.IsStartup !== true) { + if (task.m.IsGroupParent === true) { + for (var j = 0; j < tasksModel.rowCount(modelIndex); ++j) { + taskIndexList.push(tasksModel.makeModelIndex(i, j)); + } + } else { + taskIndexList.push(modelIndex); + } + } + } + } + + if (!taskIndexList.length) { + return; + } + + var target = taskIndexList[0]; + + for (var i = 0; i < taskIndexList.length; ++i) { + if (taskIndexList[i] === activeTaskIndex) + { + if (next && i < (taskIndexList.length - 1)) { + target = taskIndexList[i + 1]; + } else if (!next) { + if (i) { + target = taskIndexList[i - 1]; + } else { + target = taskIndexList[taskIndexList.length - 1]; + } + } + + break; + } + } + + tasksModel.requestActivate(target); +} + +function insertIndexAt(above, x, y) { + if (above && above.itemIndex) { + return above.itemIndex; + } else { + var distance = panel.vertical ? y : x; + //var step = panel.vertical ? LayoutManager.taskWidth() : LayoutManager.taskHeight(); + var step = panel.realSize; + var stripe = Math.ceil(distance / step); + + /* if (stripe === LayoutManager.calculateStripes()) { + return tasksModel.count - 1; + } else { + return stripe * LayoutManager.tasksPerStripe(); + }*/ + + return stripe-1; + } +} + + +function publishIconGeometries(taskItems) { + for (var i = 0; i < taskItems.length - 1; ++i) { + var task = taskItems[i]; + + if (task !== undefined){ + if (task.IsLauncher !== true && task.IsStartup !== true) { + tasksModel.requestPublishDelegateGeometry(tasksModel.makeModelIndex(task.itemIndex), + backend.globalRect(task), task); + } + } + } +} + +function taskPrefix(prefix) { + var effectivePrefix; + + switch (plasmoid.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; + } + if (!frame.hasElementPrefix(effectivePrefix)) { + return prefix; + } + return effectivePrefix; + +} diff --git a/plasmoid/contents/config/config.qml b/plasmoid/contents/config/config.qml new file mode 100644 index 000000000..2dc9241b5 --- /dev/null +++ b/plasmoid/contents/config/config.qml @@ -0,0 +1,40 @@ +/* + * Copyright 2013 Michail Vourlakos <mvourlakos@gmail.com> + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + + +import QtQuick 2.0 + +import org.kde.plasma.configuration 2.0 + +ConfigModel { + ConfigCategory { + name: i18n("Appearance") + icon: "preferences-desktop-display-color" + source: "ConfigAppearance.qml" + } + ConfigCategory { + name: i18n("Panel") + icon: "window-duplicate" + source: "ConfigPanel.qml" + } + ConfigCategory { + name: i18n("Interaction") + icon: "preferences-system-windows-move" + source: "ConfigInteraction.qml" + } +} diff --git a/plasmoid/contents/config/main.xml b/plasmoid/contents/config/main.xml new file mode 100644 index 000000000..cf8591b0a --- /dev/null +++ b/plasmoid/contents/config/main.xml @@ -0,0 +1,108 @@ +<?xml version="1.0" encoding="UTF-8"?> +<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0 + http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" > + <kcfgfile name=""/> + + <group name="General"> + <entry name="showOnlyCurrentScreen" type="Bool"> + <default>false</default> + </entry> + <entry name="showOnlyCurrentDesktop" type="Bool"> + <default>false</default> + </entry> + <entry name="showOnlyCurrentActivity" type="Bool"> + <default>true</default> + </entry> + <entry name="groupingAppIdBlacklist" type="StringList"> + <default></default> + </entry> + <entry name="groupingLauncherUrlBlacklist" type="StringList"> + <default></default> + </entry> + <entry name="separateLaunchers" type="Bool"> + <default>true</default> + </entry> + <entry name="wheelEnabled" type="Bool"> + <default>true</default> + </entry> + <entry name="launchers" type="String"> + <default></default> + </entry> + <entry name="middleClickAction" type="Enum"> + <choices> + <choice name="None"/> + <choice name="Close"/> + <choice name="NewInstance"/> + <choice name="ToggleMinimized"/> + </choices> + <default>2</default> + </entry> + <entry name="plasmoidPosition" type="Enum"> + <choices> + <choice name="Center"/> + <choice name="Left"/> + <choice name="Right"/> + <choice name="Top"/> + <choice name="Bottom"/> + </choices> + <default>0</default> + </entry> + <entry name="smartLaunchersEnabled" type="Bool"> + <default>true</default> + </entry> + <entry name="showBarLine" type="Bool"> + <default>true</default> + </entry> + <entry name="showShadows" type="Bool"> + <default>true</default> + </entry> + <entry name="showGlow" type="Bool"> + <default>true</default> + </entry> + <entry name="zoomLevel" type="Int"> + <default>14</default> + </entry> + <entry name="iconSize" type="Int"> + <default>48</default> + </entry> + <entry name="zoomHelper" type="Bool"> + <default>false</default> + </entry> + <entry name="threeColorsWindows" type="Bool"> + <default>false</default> + </entry> + <entry name="dotsOnActive" type="Bool"> + <default>false</default> + </entry> + <entry name="useThemePanel" type="Bool"> + <default>true</default> + </entry> + <entry name="panelSize" type="Int"> + <default>10</default> + </entry> + <entry name="transparentPanel" type="Bool"> + <default>false</default> + </entry> + <entry name="highlightWindows" type="Bool"> + <default>false</default> + </entry> + <entry name="isInNowDockPanel" type="Bool"> + <default>false</default> + </entry> + <entry name="showToolTips" type="Bool"> + <default>false</default> + </entry> + <entry name="durationTime" type="Int"> + <default>2</default> + </entry> + <entry name="reverseLinesPosition" type="Bool"> + <default>false</default> + </entry> + <entry name="showWindowActions" type="Bool"> + <default>false</default> + </entry> + </group> +</kcfg> + diff --git a/plasmoid/contents/images/panel-west.png b/plasmoid/contents/images/panel-west.png new file mode 100644 index 000000000..d215e858a Binary files /dev/null and b/plasmoid/contents/images/panel-west.png differ diff --git a/plasmoid/contents/ui/CircleText.qml b/plasmoid/contents/ui/CircleText.qml new file mode 100644 index 000000000..b246caaca --- /dev/null +++ b/plasmoid/contents/ui/CircleText.qml @@ -0,0 +1,124 @@ +/* + * Copyright 2013 Michail Vourlakos <mvourlakos@gmail.com> + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.2 +import QtGraphicalEffects 1.0 + +Item { + property double proportion + + property string valueLabel + property string numberValue + + property bool fullCircle + property bool showNumber + property bool showLabel + + property double circleOpacity: 1 + + property double fontPixelSize: partSize // * 0.55 + + property double stdThickness: partSize < 0 ? 0 : partSize // "/2.1" + property double circleThicknessAttr: fullCircle ? 0 : stdThickness * 0.9 + property double partSize: height / 2 + property double pi2: Math.PI * 2 + + onProportionChanged: { + repaint() + } + + function repaint() { + canvas.requestPaint() + } + + Canvas { + id: canvas + + property int lineWidth: 1 + property bool fill: true + property bool stroke: true + property real alpha: 1.0 + + // edge bleeding fix + readonly property double filler: 0.01 + + width: parent.width + height: parent.height + antialiasing: true + opacity: 1.0 + + onPaint: { + var ctx = getContext('2d') + ctx.clearRect(0, 0, canvas.width, canvas.height) + ctx.fillStyle = theme.highlightColor + + var startRadian = - Math.PI / 2 + + var radians = pi2 * proportion + + ctx.beginPath(); + ctx.arc(width/2, height/2, stdThickness, startRadian, startRadian + radians + filler, false) + ctx.arc(width/2, height/2, circleThicknessAttr, startRadian + radians + filler, startRadian, true) + + ctx.closePath() + ctx.fill() + } + } + + DropShadow { + anchors.fill: canvas + radius: 4 + samples: 8 + spread: 0.5 + fast: true + color: theme.backgroundColor + source: canvas + } + + Text { + id: valueText + anchors.centerIn: parent + text: numberValue + font.pixelSize: fontPixelSize + color: theme.textColor + visible: showNumber + } + + DropShadow { + anchors.fill: valueText + radius: 3 + samples: 8 + spread: 0.6 + fast: true + color: theme.backgroundColor + source: valueText + visible: showNumber + } + + Text { + id: valueTextLabel + anchors.bottom: parent.bottom + anchors.bottomMargin: -2 + anchors.right: parent.right + text: valueLabel + font.pixelSize: fontPixelSize * 0.65 + color: theme.textColor + visible: true + } +} + diff --git a/plasmoid/contents/ui/ConfigAppearance.qml.cmake b/plasmoid/contents/ui/ConfigAppearance.qml.cmake new file mode 100644 index 000000000..acfdaf0af --- /dev/null +++ b/plasmoid/contents/ui/ConfigAppearance.qml.cmake @@ -0,0 +1,333 @@ +/* + * Copyright 2013 Michail Vourlakos <mvourlakos@gmail.com> + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.0 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 +import QtGraphicalEffects 1.0 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents + +Item { + id: mainItem + + width: childrenRect.width + height: childrenRect.height + + property bool vertical: (plasmoid.formFactor == PlasmaCore.Types.Vertical) + + property alias cfg_zoomHelper: zoomHelper.checked + property alias cfg_zoomLevel: zoomLevel.value + property alias cfg_showShadows: showShadows.checked + property alias cfg_showGlow: showGlow.checked + property alias cfg_iconSize: iconSizeCmb.realValue + property alias cfg_threeColorsWindows: threeColorsWindows.checked + property alias cfg_dotsOnActive: dotsOnActive.checked + property alias cfg_durationTime : durationTime.value + property alias cfg_reverseLinesPosition : reverseLinesPosition.checked + + property alias cfg_isInNowDockPanel: mainItem.isInNowDockPanel + + property bool isInNowDockPanel + + ColumnLayout { + id:mainColumn + spacing: 15 + width: parent.width-40 + + //Layout.fillWidth: true + + GroupBox { + title: "" + flat: true + Layout.fillWidth: true + + ColumnLayout { + Layout.fillWidth: true + width: mainItem.width-40 + + RowLayout{ + + Label { + text: i18n("Icon size: ") + } + + ComboBox { + // 16, 22, 32, 48, 64,128, 256 + id: iconSizeCmb + enabled: !mainItem.isInNowDockPanel + + property int realValue + property bool startup: true + model: ["16px.", "22px.", "32px.", "48px.", "64px.", "96px", "128px.", "256px."] + + onCurrentIndexChanged: { + switch(currentIndex){ + case 0: + realValue = 16; + break; + case 1: + realValue = 22; + break; + case 2: + realValue = 32; + break; + case 3: + realValue = 48; + break; + case 4: + realValue = 64; + break; + case 5: + realValue = 96; + break; + case 6: + realValue = 128; + break; + case 7: + realValue = 256; + break; + default: + realValue = 64; + break + } + } + + onRealValueChanged: { + if(startup){ + switch (realValue){ + case 16: + currentIndex = 0; + break; + case 22: + currentIndex = 1; + break; + case 32: + currentIndex = 2; + break; + case 48: + currentIndex = 3; + break; + case 64: + currentIndex = 4; + break; + case 96: + currentIndex = 5; + break; + case 128: + currentIndex = 6; + break; + case 256: + currentIndex = 7; + break; + default: + currentIndex = 4; + break + } + startup = false; + } + } + } + + Label { + id: versionLabel + + font.italic: true + horizontalAlignment: Text.AlignRight + Layout.alignment: Qt.AlignRight + Layout.fillWidth: true + + text: i18n("ver: ") + "@VERSION@" + } + } + + + CheckBox { + id: showShadows + text: i18n("Enable shadows for icons") + enabled: true + } + + CheckBox { + id: showGlow + text: i18n("Show glow around windows points") + enabled: true + } + + CheckBox { + id: threeColorsWindows + text: i18n("Different color for minimized windows") + enabled: true + } + + CheckBox { + id: dotsOnActive + text: i18n("Dots on active window") + enabled: true + } + + CheckBox { + id: reverseLinesPosition + text: i18n("Reverse position for lines and dots") + enabled: true + } + } + } + + GridLayout{ + id: animationsGridLayout + Layout.fillWidth: true + columns: 3 + + + Label { + id: durationTimeLabel + + Layout.alignment: Qt.AlignHCenter + horizontalAlignment: Text.AlignHCenter + + text: i18n("Animations: ") + } + + Slider { + id: durationTime + enabled: true + Layout.fillWidth: true + minimumValue: 0 + maximumValue: 3 + stepSize: 1 + tickmarksEnabled: true + } + Label { + enabled: durationTime.value > 0 + Layout.alignment: Qt.AlignHCenter + horizontalAlignment: Text.AlignHCenter + font.italic: durationTime.value > 0 ? false : true + + property string textUsed: durationTime.value > 0 ? i18n("duration") : i18n("disabled") + + text: (durationTime.value > 0 ? ("x" + durationTime.value) + " " + textUsed : textUsed ) + } + + Label{Layout.columnSpan: 3} + + Item{ + enabled: !mainItem.isInNowDockPanel + Layout.columnSpan: 3 + Layout.fillWidth: true + Label { + text: i18n("Zoom") + anchors.centerIn: parent + font.bold: true + font.italic: true + } + } + + ////// + + Label { + enabled: !mainItem.isInNowDockPanel + Layout.alignment: Qt.AlignHCenter + horizontalAlignment: Text.AlignHCenter + + text: i18n("Level: ") + } + + Slider { + id: zoomLevel + enabled: !mainItem.isInNowDockPanel + Layout.fillWidth: true + minimumValue: 0 + maximumValue: 20 + stepSize: 1 + tickmarksEnabled: true + } + + Label { + id:zoomLevelText + enabled: !mainItem.isInNowDockPanel + Layout.minimumWidth: metricsLabel2.width + Layout.maximumWidth: metricsLabel2.width + Layout.alignment: Qt.AlignHCenter + horizontalAlignment: Text.AlignHCenter + //Layout.alignment: Qt.AlignRight + + + property real fixedZoom: (1 + (zoomLevel.value / 20)) + text: "x"+ fixedZoom.toFixed(2) + + Label{ + id:metricsLabel2 + visible: false + text: "x1.50" + } + } + ///// + //spacer to set a minimumWidth for sliders + //Layout.minimumWidth didnt work + Label{} + // Label{Layout.maximumWidth: 275} + Label{} + + //////// + + CheckBox { + id: zoomHelper + enabled: !mainItem.isInNowDockPanel + text: i18n("Show a red line on the limit needed for animations") + + Layout.columnSpan: 3 + } + } + } + + DropShadow { + id:shadowText + anchors.fill: inNowDockLabel + enabled: isInNowDockPanel + radius: 3 + samples: 5 + color: "#cc080808" + source: inNowDockLabel + + verticalOffset: 2 + horizontalOffset: -1 + visible: isInNowDockPanel + } + + + Label { + id:inNowDockLabel + anchors.horizontalCenter: mainItem.horizontalCenter + anchors.bottom: mainColumn.bottom + anchors.bottomMargin: mainColumn.height / 12 + // anchors.verticalCenterOffset: (mainColumn.height / 4) + + width: 0.85 * mainItem.width + text: i18n("For the disabled settings you should use the Now Dock Panel Configuration Window") + visible: mainItem.isInNowDockPanel + + horizontalAlignment: Text.AlignHCenter + // font.bold: true + font.italic: true + font.pointSize: 1.2 * theme.defaultFont.pointSize + + wrapMode: Text.WordWrap + } +} diff --git a/plasmoid/contents/ui/ConfigInteraction.qml b/plasmoid/contents/ui/ConfigInteraction.qml new file mode 100644 index 000000000..284bf5ecd --- /dev/null +++ b/plasmoid/contents/ui/ConfigInteraction.qml @@ -0,0 +1,133 @@ +/* + * Copyright 2013 Michail Vourlakos <mvourlakos@gmail.com> + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + + +import QtQuick 2.0 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents + +Item { + width: childrenRect.width + height: childrenRect.height + + property bool vertical: (plasmoid.formFactor == PlasmaCore.Types.Vertical) + + property alias cfg_wheelEnabled: wheelEnabled.checked + property alias cfg_middleClickAction: middleClickAction.currentIndex + + property alias cfg_showOnlyCurrentScreen: showOnlyCurrentScreen.checked + property alias cfg_showOnlyCurrentDesktop: showOnlyCurrentDesktop.checked + property alias cfg_showOnlyCurrentActivity: showOnlyCurrentActivity.checked + + property alias cfg_highlightWindows: highlightWindowsChk.checked + property alias cfg_smartLaunchersEnabled: smartLaunchersChk.checked + property alias cfg_showToolTips: showPreviewsChk.checked + property alias cfg_showWindowActions: windowActionsChk.checked + + ColumnLayout{ + spacing: 15 + + GroupBox { + title: "" + flat: true + Layout.fillWidth: true + + ColumnLayout { + Layout.fillWidth: true + + CheckBox { + id: wheelEnabled + text: i18n("Cycle through tasks with mouse wheel") + enabled: false + } + + CheckBox { + id: showPreviewsChk + Layout.fillWidth: true + text: i18n("Preview windows on hovering") + } + + CheckBox { + id: highlightWindowsChk + Layout.fillWidth: true + text: i18n("Highlight windows on hovering") + } + + CheckBox { + id: windowActionsChk + Layout.fillWidth: true + text: i18n("Show window actions in the context menu") + } + + CheckBox { + id: smartLaunchersChk + Layout.fillWidth: true + text: i18n("Show progress information in task buttons") + } + + RowLayout { + Label { + text: i18n("On middle-click:") + } + + ComboBox { + id: middleClickAction + Layout.fillWidth: true + model: [i18nc("The click action", "None"), i18n("Close Window or Group"), i18n("New Instance"), i18n("Minimize/Restore Window or Group")] + } + } + } + } + + + ColumnLayout { + Layout.fillWidth: true + + + Label { + text: i18n("Filters") + // Layout.fillWidth: true + anchors.horizontalCenter: parent.horizontalCenter + // anchors.centerIn: parent + font.bold: true + font.italic: true + } + + + CheckBox { + id: showOnlyCurrentScreen + text: i18n("Show only tasks from the current screen") + } + + CheckBox { + id: showOnlyCurrentDesktop + text: i18n("Show only tasks from the current desktop") + } + + CheckBox { + id: showOnlyCurrentActivity + text: i18n("Show only tasks from the current activity") + } + } + + } + +} diff --git a/plasmoid/contents/ui/ConfigPanel.qml b/plasmoid/contents/ui/ConfigPanel.qml new file mode 100644 index 000000000..f6a0a0b18 --- /dev/null +++ b/plasmoid/contents/ui/ConfigPanel.qml @@ -0,0 +1,180 @@ +/* + * Copyright 2013 Michail Vourlakos <mvourlakos@gmail.com> + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.0 +import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 +import QtGraphicalEffects 1.0 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents + +Item { + id: mainItem + + width: childrenRect.width + height: childrenRect.height + + property bool vertical: (plasmoid.formFactor == PlasmaCore.Types.Vertical) + + property alias cfg_showBarLine: showBarLine.checked + property alias cfg_useThemePanel: useThemePanel.checked + property alias cfg_panelSize: panelSize.value + property alias cfg_transparentPanel: transparentPanel.checked + property alias cfg_plasmoidPosition: panelPositionCmb.currentIndex + property alias cfg_isInNowDockPanel: mainItem.isInNowDockPanel + + property bool isInNowDockPanel + + ColumnLayout { + + id:mainColumn + spacing: 15 + Layout.fillWidth: true + + GridLayout{ + enabled: !mainItem.isInNowDockPanel + Layout.fillWidth: true + columns: 3 + property bool panelConfigEnabled: showBarLine.checked && useThemePanel.checked + + + Label { + text: i18n("Position: ") + } + + ComboBox { + // 16, 22, 32, 48, 64,128, 256 + id: panelPositionCmb + + Layout.fillWidth: true + model: [i18n("Center"), i18n("Left"), i18n("Right"), i18n("Top"), i18n("Bottom")] + } + Label{} + + + CheckBox { + id: showBarLine + Layout.columnSpan: 3 + text: i18n("Show bar line for tasks") + enabled: true + } + + CheckBox { + id: useThemePanel + Layout.columnSpan: 3 + text: i18n("Use plasma theme panel") + enabled: showBarLine.checked + } + + CheckBox { + id: transparentPanel + Layout.columnSpan: 3 + text: i18n("Use transparency in the panel") + enabled: parent.panelConfigEnabled + } + + + Label { + id: panelLabel + text: i18n("Size: ") + enabled: parent.panelConfigEnabled + } + + Slider { + id: panelSize + enabled: parent.panelConfigEnabled + Layout.fillWidth: true + minimumValue: 0 + maximumValue: 256 + stepSize: 2 + tickmarksEnabled: false + } + + Label { + enabled: parent.panelConfigEnabled + Layout.minimumWidth: metricsLabel.width + Layout.maximumWidth: metricsLabel.width + Layout.alignment: Qt.AlignRight + horizontalAlignment: Text.AlignRight + + text: ( panelSize.value + " px." ) + + Label{ + id:metricsLabel + visible: false + text: panelSize.maximumValue+" px." + } + } + + /* Label{ + Layout.columnSpan: 3 + Layout.fillWidth: false + Layout.alignment: Qt.AlignRight + Layout.maximumWidth: zoomLevel.width + zoomLevelText.width + panelLabel.width + horizontalAlignment: Text.AlignRight + text: i18n("in panel placement, themes that have set a <b>specific</b> panel transparent work better") + wrapMode: Text.WordWrap + font.italic: true + enabled: parent.panelConfigEnabled + }*/ + + ///// + //spacer to set a minimumWidth for sliders + //Layout.minimumWidth didnt work + Label{} + Label{Layout.minimumWidth: 280} + Label{} + + } + } + + DropShadow { + id:shadowText + anchors.fill: inNowDockLabel + enabled: isInNowDockPanel + radius: 3 + samples: 5 + color: "#cc080808" + source: inNowDockLabel + + verticalOffset: 2 + horizontalOffset: -1 + visible: isInNowDockPanel + } + + + Label { + id:inNowDockLabel + anchors.horizontalCenter: mainItem.horizontalCenter + anchors.verticalCenter: mainColumn.verticalCenter + // anchors.verticalCenterOffset: (mainColumn.height / 4) + + width: 0.85 * mainItem.width + text: i18n("For the disabled settings you should use the Now Dock Panel Configuration Window") + visible: mainItem.isInNowDockPanel + + horizontalAlignment: Text.AlignHCenter + // font.bold: true + font.italic: true + font.pointSize: 1.2 * theme.defaultFont.pointSize + + wrapMode: Text.WordWrap + } + +} diff --git a/plasmoid/contents/ui/ContextMenu.qml b/plasmoid/contents/ui/ContextMenu.qml new file mode 100644 index 000000000..dc77710c4 --- /dev/null +++ b/plasmoid/contents/ui/ContextMenu.qml @@ -0,0 +1,624 @@ +/* + * Copyright 2013 Michail Vourlakos <mvourlakos@gmail.com> + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ +import QtQuick 2.0 + +import org.kde.plasma.plasmoid 2.0 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.activities 0.1 as Activities + +import "../code/activitiesTools.js" as ActivitiesTools + +PlasmaComponents.ContextMenu { + id: menu + + property QtObject mpris2Source + + placement: { + if (plasmoid.location == PlasmaCore.Types.LeftEdge) { + return PlasmaCore.Types.RightPosedTopAlignedPopup; + } else if (plasmoid.location == PlasmaCore.Types.TopEdge) { + return PlasmaCore.Types.BottomPosedLeftAlignedPopup; + } else { + return PlasmaCore.Types.TopPosedLeftAlignedPopup; + } + } + + minimumWidth: visualParent ? visualParent.width : 1 + + property bool isOnAllActivitiesLauncher: true + + property int activitiesCount: 0 + + onStatusChanged: { + if (visualParent && visualParent.m.LauncherUrlWithoutIcon != null && status == PlasmaComponents.DialogStatus.Open) { + launcherToggleAction.checked = (tasksModel.launcherPosition(visualParent.m.LauncherUrlWithoutIcon) != -1); + updateOnAllActivitiesLauncher(); + } else if (status == PlasmaComponents.DialogStatus.Closed) { + checkListHovered.start(); + menu.destroy(); + } + } + + function show() { + //trying to use the dragging mechanism in order to not hide the dock + panel.disableRestoreZoom = true; + panel.signalDraggingState(true); + loadDynamicLaunchActions(visualParent.m.LauncherUrlWithoutIcon); + // backend.ungrabMouse(visualParent); + openRelative(); + } + + function newMenuItem(parent) { + return Qt.createQmlObject( + "import org.kde.plasma.components 2.0 as PlasmaComponents;" + + "PlasmaComponents.MenuItem {}", + parent); + } + + function newSeparator(parent) { + return Qt.createQmlObject( + "import org.kde.plasma.components 2.0 as PlasmaComponents;" + + "PlasmaComponents.MenuItem { separator: true }", + parent); + } + + function loadDynamicLaunchActions(launcherUrl) { + var actionList = backend.jumpListActions(launcherUrl, menu); + + for (var i = 0; i < actionList.length; ++i) { + var item = newMenuItem(menu); + item.action = actionList[i]; + menu.addMenuItem(item, virtualDesktopsMenuItem); + } + + if (actionList.length > 0) { + menu.addMenuItem(newSeparator(menu), virtualDesktopsMenuItem); + } + + var actionList = backend.recentDocumentActions(launcherUrl, menu); + + for (var i = 0; i < actionList.length; ++i) { + var item = newMenuItem(menu); + item.action = actionList[i]; + menu.addMenuItem(item, virtualDesktopsMenuItem); + } + + if (actionList.length > 0) { + menu.addMenuItem(newSeparator(menu), virtualDesktopsMenuItem); + } + + // Add Media Player control actions + var sourceName = mpris2Source.sourceNameForLauncherUrl(launcherUrl); + + if (sourceName) { + var playerData = mpris2Source.data[sourceName] + + if (playerData.CanControl) { + var menuItem = menu.newMenuItem(menu); + menuItem.text = i18nc("Play previous track", "Previous Track"); + menuItem.icon = "media-skip-backward"; + menuItem.enabled = Qt.binding(function() { + return playerData.CanGoPrevious; + }); + menuItem.clicked.connect(function() { + mpris2Source.goPrevious(sourceName); + }); + menu.addMenuItem(menuItem, virtualDesktopsMenuItem); + + menuItem = menu.newMenuItem(menu); + // PlasmaCore Menu doesn't actually handle icons or labels changing at runtime... + menuItem.text = Qt.binding(function() { + return playerData.PlaybackStatus === "Playing" ? i18nc("Pause playback", "Pause") : i18nc("Start playback", "Play"); + }); + menuItem.icon = Qt.binding(function() { + return playerData.PlaybackStatus === "Playing" ? "media-playback-pause" : "media-playback-start"; + }); + menuItem.clicked.connect(function() { + mpris2Source.playPause(sourceName); + }); + menu.addMenuItem(menuItem, virtualDesktopsMenuItem); + + menuItem = menu.newMenuItem(menu); + menuItem.text = i18nc("Stop playback", "Stop"); + menuItem.icon = "media-playback-stop"; + menuItem.clicked.connect(function() { + mpris2Source.stop(sourceName); + }); + menu.addMenuItem(menuItem, virtualDesktopsMenuItem); + + menuItem = menu.newMenuItem(menu); + menuItem.text = i18nc("Play next track", "Next Track"); + menuItem.icon = "media-skip-forward"; + menuItem.enabled = Qt.binding(function() { + return playerData.CanGoNext; + }); + menuItem.clicked.connect(function() { + mpris2Source.goNext(sourceName); + }); + menu.addMenuItem(menuItem, virtualDesktopsMenuItem); + + menu.addMenuItem(newSeparator(menu), virtualDesktopsMenuItem); + + // If we don't have a window associated with the player but we can quit + // it through MPRIS we'll offer a "Quit" option instead of "Close" + if (!closeWindowItem.visible && playerData.CanQuit) { + menuItem = menu.newMenuItem(menu); + menuItem.text = i18nc("Quit media player app", "Quit"); + menuItem.icon = "application-exit"; + menuItem.visible = Qt.binding(function() { + return !closeWindowItem.visible; + }); + menuItem.clicked.connect(function() { + mpris2Source.quit(sourceName); + }); + menu.addMenuItem(menuItem); + } + + // If we don't have a window associated with the player but we can raise + // it through MPRIS we'll offer a "Restore" option + if (!startNewInstanceItem.visible && playerData.CanRaise) { + menuItem = menu.newMenuItem(menu); + menuItem.text = i18nc("Open or bring to the front window of media player app", "Restore"); + menuItem.icon = playerData["Desktop Icon Name"]; + menuItem.visible = Qt.binding(function() { + return !startNewInstanceItem.visible; + }); + menuItem.clicked.connect(function() { + mpris2Source.raise(sourceName); + }); + menu.addMenuItem(menuItem, startNewInstanceItem); + } + } + } + } + + function updateOnAllActivitiesLauncher(){ + isOnAllActivitiesLauncher = ActivitiesTools.isOnAllActivities(visualParent.m.LauncherUrlWithoutIcon); + } + + Component.onCompleted: { + ActivitiesTools.launchersOnActivities = panel.launchersOnActivities + ActivitiesTools.currentActivity = activityInfo.currentActivity; + ActivitiesTools.plasmoid = plasmoid; + // updateOnAllActivitiesLauncher(); + } + + + Component.onDestruction: { + backend.ungrabMouse(visualParent); + panel.signalDraggingState(false); + panel.disableRestoreZoom = false; + checkListHovered.start(); + } + + /// Sub Items + + PlasmaComponents.MenuItem { + id: virtualDesktopsMenuItem + + visible: virtualDesktopInfo.numberOfDesktops > 1 + && (visualParent && visualParent.m.IsLauncher !== true + && visualParent.m.IsStartup !== true + && visualParent.m.IsVirtualDesktopChangeable === true) + + enabled: visible + + text: i18n("Move To Desktop") + + Connections { + target: virtualDesktopInfo + + onNumberOfDesktopsChanged: virtualDesktopsMenu.refresh() + onDesktopNamesChanged: virtualDesktopsMenu.refresh() + } + + PlasmaComponents.ContextMenu { + id: virtualDesktopsMenu + + visualParent: virtualDesktopsMenuItem.action + + function refresh() { + clearMenuItems(); + + if (virtualDesktopInfo.numberOfDesktops <= 1) { + return; + } + + var menuItem = menu.newMenuItem(virtualDesktopsMenu); + menuItem.text = i18n("Move To Current Desktop"); + menuItem.enabled = Qt.binding(function() { + return menu.visualParent && menu.visualParent.m.VirtualDesktop != virtualDesktopInfo.currentDesktop; + }); + menuItem.clicked.connect(function() { + tasksModel.requestVirtualDesktop(menu.visualParent.modelIndex(), 0); + }); + + menuItem = menu.newMenuItem(virtualDesktopsMenu); + menuItem.text = i18n("All Desktops"); + menuItem.checkable = true; + menuItem.checked = Qt.binding(function() { + return menu.visualParent && menu.visualParent.m.IsOnAllVirtualDesktops === true; + }); + menuItem.clicked.connect(function() { + tasksModel.requestVirtualDesktop(menu.visualParent.modelIndex(), 0); + }); + backend.setActionGroup(menuItem.action); + + menu.newSeparator(virtualDesktopsMenu); + + for (var i = 0; i < virtualDesktopInfo.desktopNames.length; ++i) { + menuItem = menu.newMenuItem(virtualDesktopsMenu); + //menuItem.text = i18nc("1 = number of desktop, 2 = desktop name", "%1 Desktop %2", i + 1, virtualDesktopInfo.desktopNames[i]); + menuItem.text = (i + 1) + ". " + virtualDesktopInfo.desktopNames[i]; + menuItem.checkable = true; + menuItem.checked = Qt.binding((function(i) { + return function() { return menu.visualParent && menu.visualParent.m.VirtualDesktop == (i + 1) }; + })(i)); + menuItem.clicked.connect((function(i) { + return function() { return tasksModel.requestVirtualDesktop(menu.visualParent.modelIndex(), i + 1); }; + })(i)); + backend.setActionGroup(menuItem.action); + } + + menu.newSeparator(virtualDesktopsMenu); + + menuItem = menu.newMenuItem(virtualDesktopsMenu); + menuItem.text = i18n("New Desktop"); + menuItem.clicked.connect(function() { + tasksModel.requestVirtualDesktop(menu.visualParent.modelIndex(), virtualDesktopInfo.numberOfDesktops + 1) + }); + } + + Component.onCompleted: refresh() + } + } + + + PlasmaComponents.MenuItem { + id: activitiesDesktopsMenuItem + + visible: activityInfo.numberOfRunningActivities > 1 + && (visualParent && !visualParent.m.IsLauncher + && !visualParent.m.IsStartup) + + enabled: visible + + text: i18n("Move To &Activity") + + Connections { + target: activityInfo + + onNumberOfRunningActivitiesChanged: activitiesDesktopsMenu.refresh() + } + + PlasmaComponents.ContextMenu { + id: activitiesDesktopsMenu + + visualParent: activitiesDesktopsMenuItem.action + + function refresh() { + clearMenuItems(); + + if (activityInfo.numberOfRunningActivities <= 1) { + return; + } + + var menuItem = menu.newMenuItem(activitiesDesktopsMenu); + menuItem.text = i18n("Add To Current Activity"); + menuItem.enabled = Qt.binding(function() { + return menu.visualParent && menu.visualParent.m.Activities.length > 0 && + menu.visualParent.m.Activities.indexOf(activityInfo.currentActivity) < 0; + }); + menuItem.clicked.connect(function() { + tasksModel.requestActivities(menu.visualParent.modelIndex(), menu.visualParent.m.Activities.concat(activityInfo.currentActivity)); + }); + + menuItem = menu.newMenuItem(activitiesDesktopsMenu); + menuItem.text = i18n("All Activities"); + menuItem.checkable = true; + menuItem.checked = Qt.binding(function() { + return menu.visualParent && menu.visualParent.m.Activities.length === 0; + }); + menuItem.clicked.connect(function() { + var checked = menuItem.checked; + var newActivities = menu.visualParent.m.Activities; + var size = newActivities.length; + + newActivities = undefined; // will cast to an empty QStringList i.e all activities + if (size === 0) { + newActivities = new Array(activityInfo.currentActivity); + } + + tasksModel.requestActivities(menu.visualParent.modelIndex(), newActivities); + }); + + menu.newSeparator(activitiesDesktopsMenu); + + var runningActivities = activityInfo.runningActivities(); + for (var i = 0; i < runningActivities.length; ++i) { + var activityId = runningActivities[i]; + + menuItem = menu.newMenuItem(activitiesDesktopsMenu); + menuItem.text = activityInfo.activityName(runningActivities[i]); + menuItem.checkable = true; + menuItem.checked = Qt.binding( (function(activityId) { + return function() { + return menu.visualParent && menu.visualParent.m.Activities.indexOf(activityId) >= 0; + }; + })(activityId)); + menuItem.clicked.connect((function(activityId) { + return function () { + var checked = menuItem.checked; + var newActivities = menu.visualParent.m.Activities; + var index = newActivities.indexOf(activityId) + + if (index < 0) { + newActivities = newActivities.concat(activityId); + } else { + //newActivities = newActivities.splice(index, 1); //this does not work!!! + newActivities.splice(index, 1); + } + return tasksModel.requestActivities(menu.visualParent.modelIndex(), newActivities); + }; + })(activityId)); + } + + menu.newSeparator(activitiesDesktopsMenu); + } + + Component.onCompleted: refresh() + } + } + + + + PlasmaComponents.MenuItem { + visible: (visualParent + && visualParent.m.IsLauncher !== true + && visualParent.m.IsStartup !== true + && plasmoid.configuration.showWindowActions) + + enabled: visualParent && visualParent.m.IsMinimizable === true + + checkable: true + checked: visualParent && visualParent.m.IsMinimized === true + + text: i18n("Minimize") + + onClicked: tasksModel.requestToggleMinimized(visualParent.modelIndex()) + } + + PlasmaComponents.MenuItem { + visible: (visualParent + && visualParent.m.IsLauncher !== true + && visualParent.m.IsStartup !== true + && plasmoid.configuration.showWindowActions) + + enabled: visualParent && visualParent.m.IsMaximizable === true + + checkable: true + checked: visualParent && visualParent.m.IsMaximized === true + + text: i18n("Maximize") + + onClicked: tasksModel.requestToggleMaximized(visualParent.modelIndex()) + } + + PlasmaComponents.MenuItem { + id: moreActionsMenuItem + + visible: (visualParent + && visualParent.m.IsLauncher !== true + && visualParent.m.IsStartup !== true + && plasmoid.configuration.showWindowActions) + + enabled: visible + + text: i18n("More Actions") + + PlasmaComponents.ContextMenu { + visualParent: moreActionsMenuItem.action + + PlasmaComponents.MenuItem { + enabled: menu.visualParent && menu.visualParent.m.IsMovable === true + + text: i18n("Move") + icon: "transform-move" + + onClicked: tasksModel.requestMove(menu.visualParent.modelIndex()) + } + + PlasmaComponents.MenuItem { + enabled: menu.visualParent && menu.visualParent.m.IsResizable === true + + text: i18n("Resize") + + onClicked: tasksModel.requestResize(menu.visualParent.modelIndex()) + } + + PlasmaComponents.MenuItem { + checkable: true + checked: menu.visualParent && menu.visualParent.m.IsKeepAbove === true + + text: i18n("Keep Above Others") + icon: "go-up" + + onClicked: tasksModel.requestToggleKeepAbove(menu.visualParent.modelIndex()) + } + + PlasmaComponents.MenuItem { + checkable: true + checked: menu.visualParent && menu.visualParent.m.IsKeepBelow === true + + text: i18n("Keep Below Others") + icon: "go-down" + + onClicked: tasksModel.requestToggleKeepBelow(menu.visualParent.modelIndex()) + } + + PlasmaComponents.MenuItem { + enabled: menu.visualParent && menu.visualParent.m.IsFullScreenable === true + + checkable: true + checked: menu.visualParent && menu.visualParent.m.IsFullScreen === true + + text: i18n("Fullscreen") + icon: "view-fullscreen" + + onClicked: tasksModel.requestToggleFullScreen(menu.visualParent.modelIndex()) + } + + PlasmaComponents.MenuItem { + enabled: menu.visualParent && menu.visualParent.m.IsShadeable === true + + checkable: true + checked: menu.visualParent && menu.visualParent.m.IsShaded === true + + text: i18n("Shade") + + onClicked: tasksModel.requestToggleShaded(menu.visualParent.modelIndex()) + } + + PlasmaComponents.MenuItem { + separator: true + } + + PlasmaComponents.MenuItem { + visible: (plasmoid.configuration.groupingStrategy != 0) && menu.visualParent.m.IsWindow === true + + checkable: true + checked: menu.visualParent && menu.visualParent.m.IsGroupable === true + + text: i18n("Allow this program to be grouped") + + onClicked: tasksModel.requestToggleGrouping(menu.visualParent.modelIndex()) + } + } + } + + PlasmaComponents.MenuItem { + id: startNewInstanceItem + visible: (visualParent && visualParent.m.IsLauncher !== true && visualParent.m.IsStartup !== true) + + enabled: visualParent && visualParent.m.LauncherUrlWithoutIcon != null + + text: i18n("Start New Instance") + icon: "system-run" + + onClicked: tasksModel.requestNewInstance(visualParent.modelIndex()) + } + + PlasmaComponents.MenuItem { + separator: true + + visible: (visualParent + && visualParent.m.IsLauncher !== true + && visualParent.m.IsStartup !== true + && plasmoid.configuration.showWindowActions) + } + + PlasmaComponents.MenuItem { + id: launcherToggleOnAllActivitiesAction + visible: launcherToggleAction.visible && launcherToggleAction.checked && activitiesCount > 1 + enabled: visualParent && visualParent.m.LauncherUrlWithoutIcon != null + + checkable: true + checked: isOnAllActivitiesLauncher + text: i18n("Show Launcher On All Activities") + + onClicked:{ + ActivitiesTools.toggleLauncherState(visualParent.m.LauncherUrlWithoutIcon); + updateOnAllActivitiesLauncher(); + } + } + + PlasmaComponents.MenuItem { + id: launcherToggleAction + + visible: (visualParent && visualParent.m.IsLauncher !== true && visualParent.m.IsStartup !== true) + + enabled: visualParent && visualParent.m.LauncherUrlWithoutIcon != null + + checkable: true + + text: i18n("Show Launcher When Not Running") + + onClicked: { + if (tasksModel.launcherPosition(visualParent.m.LauncherUrlWithoutIcon) != -1) { + tasksModel.requestRemoveLauncher(visualParent.m.LauncherUrlWithoutIcon); + } else { + tasksModel.requestAddLauncher(visualParent.m.LauncherUrl); + } + } + } + + PlasmaComponents.MenuItem { + visible: (visualParent && visualParent.m.IsLauncher === true) && activitiesCount > 1 + + checkable: true + checked: isOnAllActivitiesLauncher + text: i18n("Show Launcher On All Activities") + + onClicked:{ + ActivitiesTools.toggleLauncherState(visualParent.m.LauncherUrlWithoutIcon); + updateOnAllActivitiesLauncher(); + } + } + + PlasmaComponents.MenuItem { + visible: (visualParent && visualParent.m.IsLauncher === true) + + text: i18n("Remove Launcher") + + onClicked: tasksModel.requestRemoveLauncher(visualParent.m.LauncherUrlWithoutIcon); + } + + + + + PlasmaComponents.MenuItem { + property QtObject configureAction: null + + enabled: configureAction && configureAction.enabled + + text: configureAction ? configureAction.text : "" + icon: configureAction ? configureAction.icon : "" + + onClicked: configureAction.trigger() + + Component.onCompleted: configureAction = plasmoid.action("configure") + } + + PlasmaComponents.MenuItem { + separator: true + } + + PlasmaComponents.MenuItem { + id: closeWindowItem + visible: (visualParent && visualParent.m.IsLauncher !== true && visualParent.m.IsStartup !== true) + + enabled: visualParent && visualParent.m.IsClosable === true + + text: i18n("Close") + icon: "window-close" + + onClicked: tasksModel.requestClose(visualParent.modelIndex()) + } +} diff --git a/plasmoid/contents/ui/GlowPoint.qml b/plasmoid/contents/ui/GlowPoint.qml new file mode 100644 index 000000000..c10644b8f --- /dev/null +++ b/plasmoid/contents/ui/GlowPoint.qml @@ -0,0 +1,102 @@ +/* + * Copyright 2013 Michail Vourlakos <mvourlakos@gmail.com> + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.0 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as Components + +import QtGraphicalEffects 1.0 + +Item{ + // property string color + id: glowItem + + property bool roundCorners: true + property bool showAttention: false + + property int animation: plasmoid.configuration.durationTime*units.longDuration + + property color attentionColor: colorScopePalette.negativeTextColor // "#ffff1717" + property color basicColor: "blue" + + onShowAttentionChanged: { + if(showAttention == false){ + smallCircle.color = basicColor; + } + } + + Item{ + id:mainGlow + anchors.fill: parent + + Rectangle { + id: smallCircle + anchors.centerIn: parent + anchors.fill: parent + + color: glowItem.basicColor + radius: glowItem.roundCorners ? Math.min(width,height) / 2 : 0 + + + SequentialAnimation{ + running: (glowItem.showAttention == true) + loops: Animation.Infinite + + PropertyAnimation { + target: smallCircle + property: "color" + to: glowItem.attentionColor + duration: glowItem.animation + easing.type: Easing.InOutQuad + } + + PropertyAnimation { + target:smallCircle + property: "color" + to: glowItem.basicColor + duration: glowItem.animation + easing.type: Easing.InOutQuad + } + } + } + + RectangularGlow { + id:recGlow + anchors.fill: smallCircle + glowRadius: 2 * Math.min(smallCircle.width, smallCircle.height) + spread: 0.2 + color: smallCircle.color + //color: "#cc222222" + // cornerRadius: smallCircle.radius + glowRadius + opacity: panel.showBarLine ? 0.25 : 0.45 + visible: panel.glow + } + + /* BrightnessContrast { + anchors.fill: recGlow + source: recGlow + anchors.margins: 1 + brightness: 0.4 + contrast: 0.3 + visible: panel.glow + }*/ + } + + + +} diff --git a/plasmoid/contents/ui/MouseHandler.qml b/plasmoid/contents/ui/MouseHandler.qml new file mode 100644 index 000000000..7031f5588 --- /dev/null +++ b/plasmoid/contents/ui/MouseHandler.qml @@ -0,0 +1,193 @@ +/* + * Copyright 2013 Michail Vourlakos <mvourlakos@gmail.com> + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + + +import QtQuick 2.0 + +import org.kde.draganddrop 2.0 + +import org.kde.taskmanager 0.1 as TaskManager + +//import "../code/layout.js" as LayoutManager +import "../code/tools.js" as TaskTools + +Item { + // signal urlDropped(url url) + signal urlsDropped(var urls) + + property Item target + property Item ignoredItem + property bool moved: false + + property alias hoveredItem: dropHandler.hoveredItem + property alias onlyLaunchers: dropHandler.onlyLaunchers + + + Timer { + id: ignoreItemTimer + + repeat: false + interval: 120 + + onTriggered: { + ignoredItem = null; + } + } + + Connections { + target: panel + + onDragSourceChanged: { + if (!dragSource) { + ignoredItem = null; + ignoreItemTimer.stop(); + } + } + } + + DropArea { + id: dropHandler + + anchors.fill: parent + + preventStealing: true; + + property int droppedPosition: -1; + property bool onlyLaunchers: false; + + property Item hoveredItem + + onDragEnter:{ + if(panel.dragSource == null){ + panel.dropNewLauncher = true; + + var createLaunchers = event.mimeData.urls.every(function (item) { + return backend.isApplication(item) + }); + + + if (createLaunchers) + onlyLaunchers = true; + } + } + + onDragMove: { + if(panel.dragSource == null){ + panel.dropNewLauncher = true; + } + + if (target.animating) { + return; + } + + var above = target.childAtPos(event.x, event.y); + + // If we're mixing launcher tasks with other tasks and are moving + // a (small) launcher task across a non-launcher task, don't allow + // the latter to be the move target twice in a row for a while, as + // it will naturally be moved underneath the cursor as result of the + // initial move, due to being far larger than the launcher delegate. + // TODO: This restriction (minus the timer, which improves things) + // has been proven out in the EITM fork, but could be improved later + // by tracking the cursor movement vector and allowing the drag if + // the movement direction has reversed, etablishing user intent to + // move back. + /* if (panel.dragSource != null + && panel.dragSource.m.IsLauncher === true && above != null + && above.m != null + && above.m.IsLauncher !== true && above == ignoredItem) { + return; + } else { + ignoredItem = null; + }*/ + if (panel.dragSource == null + && ignoredItem == above) + return; + + //at some point it was needed the following && above != ignoredItem + //but know not... strange... && above != ignoredItem + //I use the ignoredItem in order to reduce the move calls as much + //as possible + if (tasksModel.sortMode == TaskManager.TasksModel.SortManual && panel.dragSource && ignoredItem == null) { + var insertAt = TaskTools.insertIndexAt(above, event.x, event.y); + + if (panel.dragSource != above && panel.dragSource.itemIndex != insertAt) { + // console.log(panel.dragSource.itemIndex + " - "+insertAt); + tasksModel.move(panel.dragSource.itemIndex, insertAt); + ignoredItem = above; + ignoreItemTimer.restart(); + } + } else if (!panel.dragSource && above && hoveredItem != above) { + hoveredItem = above; + panel.dropNewLauncher = true; + // activationTimer.restart(); + } else if (!above) { + panel.dropNewLauncher = true; + hoveredItem = null; + // activationTimer.stop(); + } + } + + onDragLeave: { + hoveredItem = null; + panel.dropNewLauncher = false; + onlyLaunchers = false; + activationTimer.stop(); + } + + onDrop: { + // Reject internal drops. + panel.dropNewLauncher = false; + onlyLaunchers = false; + + if (event.mimeData.formats.indexOf("application/x-orgkdeplasmataskmanager_taskbuttonitem") >= 0) { + return; + } + + if (event.mimeData.hasUrls) { + parent.urlsDropped(event.mimeData.urls); + } + } + + Timer { + id: activationTimer + + interval: 250 + repeat: false + + onTriggered: { + /* if (parent.hoveredItem.m.IsGroupParent === true) { + groupDialog.visualParent = parent.hoveredItem; + groupDialog.visible = true; + } else if (parent.hoveredItem.m.IsLauncher !== true) { + tasksModel.requestActivate(parent.hoveredItem.modelIndex()); + }*/ + } + } + } +/* + MouseArea { + id: wheelHandler + + anchors.fill: parent + property int wheelDelta: 0; + enabled: plasmoid.configuration.wheelEnabled + + onWheel: wheelDelta = TaskTools.wheelActivateNextPrevTask(wheelDelta, wheel.angleDelta.y); + } */ +} diff --git a/plasmoid/contents/ui/TaskActiveItem.qml b/plasmoid/contents/ui/TaskActiveItem.qml new file mode 100644 index 000000000..38cdb1de5 --- /dev/null +++ b/plasmoid/contents/ui/TaskActiveItem.qml @@ -0,0 +1,45 @@ +/* + * Copyright 2013 Michail Vourlakos <mvourlakos@gmail.com> + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.0 +import QtQuick.Layouts 1.1 +import QtGraphicalEffects 1.0 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents + + +Item{ + width: ( icList.orientation === Qt.Horizontal ) ? wrapper.regulatorWidth : 6 + height: ( icList.orientation === Qt.Vertical ) ? wrapper.regulatorHeight : 6 + + Rectangle{ + opacity: m.IsActive ? 1 : 0 + visible: false + + color: theme.buttonFocusColor + + width: ( icList.orientation === Qt.Horizontal ) ? parent.width : 3 + height: ( icList.orientation === Qt.Vertical ) ? parent.height : 3 + + anchors.bottom: (panel.position === PlasmaCore.Types.BottomPositioned) ? parent.bottom : undefined + anchors.top: (panel.position === PlasmaCore.Types.TopPositioned) ? parent.top : undefined + anchors.left: (panel.position === PlasmaCore.Types.LeftPositioned) ? parent.left : undefined + anchors.right: (panel.position === PlasmaCore.Types.RightPositioned) ? parent.right : undefined + } +}// active indicator diff --git a/plasmoid/contents/ui/TaskDelegate.qml b/plasmoid/contents/ui/TaskDelegate.qml new file mode 100644 index 000000000..38d921375 --- /dev/null +++ b/plasmoid/contents/ui/TaskDelegate.qml @@ -0,0 +1,1076 @@ +/* + * Copyright 2013 Michail Vourlakos <mvourlakos@gmail.com> + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.0 +import QtQuick.Layouts 1.1 +import QtGraphicalEffects 1.0 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.plasmoid 2.0 + +import org.kde.plasma.private.taskmanager 0.1 as TaskManagerApplet + + +MouseArea{ + id: mainItemContainer + + visible: (isStartup) ? false : true + + anchors.bottom: (panel.position === PlasmaCore.Types.BottomPositioned) ? parent.bottom : undefined + anchors.top: (panel.position === PlasmaCore.Types.TopPositioned) ? parent.top : undefined + anchors.left: (panel.position === PlasmaCore.Types.LeftPositioned) ? parent.left : undefined + anchors.right: (panel.position === PlasmaCore.Types.RightPositioned) ? parent.right : undefined + + width: panel.vertical ? wrapper.width : + hiddenSpacerLeft.width+wrapper.width+hiddenSpacerRight.width + + height: panel.vertical ? hiddenSpacerLeft.height + wrapper.height + hiddenSpacerRight.height : + wrapper.height + + acceptedButtons: Qt.LeftButton | Qt.MidButton | Qt.RightButton + hoverEnabled: (inAnimation !== true)&& (!IsStartup)&&(!panel.taskInAnimation)&&(plasmoid.immutable || panel.debugLocation) + // hoverEnabled: true + + property bool buffersAreReady: false + property bool delayingRemove: ListView.delayRemove + //states that exist in windows in a Group of windows + property bool hasActive: isActive + property bool hasMinimized: (IsGroupParent === true) ? tasksWindows.hasMinimized : isMinimized + property bool hasShown: (IsGroupParent === true) ? tasksWindows.hasShown : !isMinimized + property bool inAnimation: true + property bool inBlockingAnimation: false + property bool inPopup: false + + property bool isActive: (IsActive === true) ? true : false + property bool isDemandingAttention: (IsDemandingAttention === true) ? true : false + property bool isDragged: false + property bool isGroupParent: (IsGroupParent === true) ? true : false + property bool isLauncher: (IsLauncher === true) ? true : false + property bool isMinimized: (IsMinimized === true) ? true : false + property bool isStartup: (IsStartup === true) ? true : false + property bool isWindow: (IsWindow === true) ? true : false + property bool isZoomed: false + + property bool mouseEntered: false + property bool pressed: false + + property int animationTime: plasmoid.configuration.durationTime * 1.2 * units.shortDuration + property int hoveredIndex: icList.hoveredIndex + property int itemIndex: index + property int lastButtonClicked: -1; + property int pressX: -1 + property int pressY: -1 + property int resistanceDelay: 750 + + property real animationStep: panel.iconSize / 8 + + property string activity: tasksModel.activity + + readonly property var m: model + + property QtObject contextMenu: null + property QtObject draggingResistaner: null + property QtObject hoveredTimerObj: null + + signal groupWindowAdded(); + signal groupWindowRemoved(); + signal checkWindowsStates(); + + /* onHasMinimizedChanged:{ + console.log(AppId); + if(AppId == "org.kde.dolphin"){ + console.log("1. Minimized:"+hasMinimized); + console.log("2. Active:"+hasActive); + console.log("3. Shown:"+hasShown); + } + } + onHasShownChanged:{ + console.log(AppId); + if(AppId == "org.kde.dolphin"){ + console.log("1. Minimized:"+hasMinimized); + console.log("2. Active:"+hasActive); + console.log("3. Shown:"+hasShown); + } + } + onHasActiveChanged:{ + console.log(AppId); + if(AppId == "org.kde.dolphin"){ + console.log("1. Minimized:"+hasMinimized); + console.log("2. Active:"+hasActive); + console.log("3. Shown:"+hasShown); + } + }*/ + + /* Rectangle{ + anchors.fill: parent + border.width: 1 + border.color: "blue" + color: "transparent" + // visible: IsStartup ? true : false + } */ + + Behavior on opacity { + // NumberAnimation { duration: (IsStartup || (IsLauncher) ) ? 0 : 400 } + NumberAnimation { duration: plasmoid.configuration.durationTime*units.longDuration } + } + + + /* PlasmaCore.ToolTipArea { + id: toolTip + + anchors.fill: parent + + // active: !inPopup && !groupDialog.visible && plasmoid.configuration.showToolTips + active: plasmoid.configuration.showToolTips && mainItemContainer.isWindow && mainItemContainer.containsMouse + enabled: mainItemContainer.isWindow + + interactive: true + location: plasmoid.location + + mainItem: toolTipDelegate + + onContainsMouseChanged: { + + } + + }*/ + + + TaskWindows{ + id: tasksWindows + + property int previousCount: 0 + + onWindowsCountChanged: { + if ((windowsCount >= 2) && (windowsCount > previousCount)){ + if(panel.dragSource == null) + mainItemContainer.groupWindowAdded(); + } + else if ((windowsCount >=1) &&(windowsCount < previousCount)){ + //sometimes this is triggered in dragging with no reason + if(panel.dragSource == null) + mainItemContainer.groupWindowRemoved(); + } + + previousCount = windowsCount; + } + } + + + Flow{ + width: parent.width + height: parent.height + + // a hidden spacer for the first element to add stability + // IMPORTANT: hidden spacers must be tested on vertical !!! + Item{ + id: hiddenSpacerLeft + //we add one missing pixel from calculations + width: panel.vertical ? wrapper.width : nHiddenSize + height: panel.vertical ? nHiddenSize : wrapper.height + + visible: (index === 0) + + property real nHiddenSize: (nScale > 0) ? (panel.realSize * nScale) : 0 + property real nScale: 0 + + Behavior on nScale { + NumberAnimation { duration: mainItemContainer.animationTime } + } + + /* Rectangle{ + width:parent.width + height:1 + y:parent.height / 2 + border.width: 1 + border.color: "red" + color: "transparent" + }*/ + } + + Item{ + id: wrapper + + opacity: 0 + width: (mainItemContainer.isStartup) ? 0 : showDelegateWidth + height: (mainItemContainer.isStartup) ? 0 : showDelegateheight + + //size needed fom the states below icons + //property int statesLineSize: panel.statesLineSize + property int addedSpace: panel.statesLineSize //7 + property real showDelegateWidth: panel.vertical ? basicScalingWidth+addedSpace : + basicScalingWidth + property real showDelegateheight: panel.vertical ? basicScalingHeight : + basicScalingHeight + addedSpace + + //scales which are used mainly for activating InLauncher + ////Scalers/////// + property bool inTempScaling: (((tempScaleWidth !== 1) || (tempScaleHeight !== 1) ) && (!mainItemContainer.mouseEntered) ) + + property real scale: 1; + property real tempScaleWidth: 0 + property real tempScaleHeight: 0 + + property real scaleWidth: (inTempScaling == true) ? tempScaleWidth : scale + property real scaleHeight: (inTempScaling == true) ? tempScaleHeight : scale + + ///Dont use Math.floor it adds one pixel in animations and creates glitches + property real cleanScaling: panel.realSize * scale + + property real basicScalingWidth : (inTempScaling == true) ? (panel.realSize * scaleWidth) : cleanScaling + property real basicScalingHeight : (inTempScaling == true) ? (panel.realSize * scaleHeight) : cleanScaling + + property real regulatorWidth: basicScalingWidth-2; + property real regulatorHeight: basicScalingHeight-2; + /// end of Scalers/////// + + //property int curIndex: icList.hoveredIndex + // property int index: mainItemContainer.Positioner.index + property real center: width / 2 + + signal runLauncherAnimation(); + + /* Rectangle{ + anchors.fill: parent + border.width: 1 + border.color: "green" + color: "transparent" + }*/ + + Behavior on scale { + NumberAnimation { duration: mainItemContainer.animationTime } + } + + + Flow{ + anchors.bottom: (panel.position === PlasmaCore.Types.BottomPositioned) ? parent.bottom : undefined + anchors.top: (panel.position === PlasmaCore.Types.TopPositioned) ? parent.top : undefined + anchors.left: (panel.position === PlasmaCore.Types.LeftPositioned) ? parent.left : undefined + anchors.right: (panel.position === PlasmaCore.Types.RightPositioned) ? parent.right : undefined + + anchors.horizontalCenter: !parent.vertical ? parent.horizontalCenter : undefined + anchors.verticalCenter: parent.vertical ? parent.verticalCenter : undefined + + width: wrapper.width + height: wrapper.height + + flow: panel.vertical ? Flow.TopToBottom : Flow.LeftToRight + + Loader{ + id: firstIndicator + active:( (((panel.position === PlasmaCore.Types.TopPositioned) || (panel.position === PlasmaCore.Types.LeftPositioned)) + && !plasmoid.configuration.reverseLinesPosition) + || (((panel.position === PlasmaCore.Types.BottomPositioned) || (panel.position === PlasmaCore.Types.RightPositioned)) + && plasmoid.configuration.reverseLinesPosition) ) + sourceComponent: Component{ + TaskGroupItem{} + } + } + + TaskIconItem{} + + Loader{ + id: secondIndicator + active: !firstIndicator.active + sourceComponent: Component{ + TaskGroupItem{} + } + } + + }//Flow + + function calculateScales( currentMousePosition ){ + if (!plasmoid.immutable) { + return; + } + + var distanceFromHovered = Math.abs(index - icList.hoveredIndex); + + // A new algorithm tryig to make the zoom calculation only once + // and at the same time fixing glitches + if ((distanceFromHovered == 0)&& + (currentMousePosition > 0)&& + (panel.dragSource == null) ){ + + var rDistance = Math.abs(currentMousePosition - center); + + //check if the mouse goes right or down according to the center + var positiveDirection = ((currentMousePosition - center) >= 0 ); + + + //finding the zoom center e.g. for zoom:1.7, calculates 0.35 + var zoomCenter = (panel.zoomFactor - 1) / 2 + + //computes the in the scale e.g. 0...0.35 according to the mouse distance + //0.35 on the edge and 0 in the center + var firstComputation = (rDistance / center) * zoomCenter; + + //calculates the scaling for the neighbour tasks + var bigNeighbourZoom = Math.min(1 + zoomCenter + firstComputation, panel.zoomFactor); + var smallNeighbourZoom = Math.max(1 + zoomCenter - firstComputation, 1); + + bigNeighbourZoom = Number(bigNeighbourZoom.toFixed(2)); + smallNeighbourZoom = Number(smallNeighbourZoom.toFixed(2)); + + var leftScale; + var rightScale; + + if(positiveDirection === true){ + rightScale = bigNeighbourZoom; + leftScale = smallNeighbourZoom; + } + else { + rightScale = smallNeighbourZoom; + leftScale = bigNeighbourZoom; + } + + // console.debug(leftScale + " " + rightScale + " " + index); + + + //activate messages to update the the neighbour scales + panel.updateScale(index+1, rightScale, 0); + panel.updateScale(index-1, leftScale, 0); + panel.updateScale(index+2, 1, 0); + panel.updateScale(index-2, 1, 0); + + //Left hiddenSpacer + if(((index === 0 )&&(icList.count > 1)) && !panel.disableLeftSpacer){ + hiddenSpacerLeft.nScale = leftScale - 1; + } + + //Right hiddenSpacer + if(((index === icList.count - 1 )&&(icList.count>1)) && (!panel.disableRightSpacer)){ + hiddenSpacerRight.nScale = rightScale - 1; + } + + scale = panel.zoomFactor; + } + + } //scale + + + function signalUpdateScale(nIndex, nScale, step){ + if ((index === nIndex)&&(!mainItemContainer.inAnimation)){ + if(nScale >= 0) + scale = nScale + step; + else + scale = scale + step; + // console.log(index+ ", "+scale); + } + } + + function sendEndOfNeedBothAxisAnimation(){ + if (mainItemContainer.isZoomed) { + mainItemContainer.isZoomed = false; + if (panel.animationsNeedBothAxis > 0) { + panel.setAnimationsNeedBothAxis(panel.animationsNeedBothAxis - 1); + } + } + } + + onScaleChanged: { + if ((scale > 1) && !mainItemContainer.isZoomed) { + mainItemContainer.isZoomed = true; + panel.setAnimationsNeedBothAxis(panel.animationsNeedBothAxis + 1); + } else if ((scale == 1) && mainItemContainer.isZoomed) { + sendEndOfNeedBothAxisAnimation(); + } + } + + Component.onCompleted: { + panel.updateScale.connect(signalUpdateScale); + } + }// Main task area // id:wrapper + + // a hidden spacer on the right for the last item to add stability + Item{ + id: hiddenSpacerRight + //we add one missing pixel from calculations + width: panel.vertical ? wrapper.width : nHiddenSize + height: panel.vertical ? nHiddenSize : wrapper.height + + visible: (index === icList.count - 1) + + property real nHiddenSize: (nScale > 0) ? (panel.realSize * nScale) : 0 + property real nScale: 0 + + Behavior on nScale { + NumberAnimation { duration: mainItemContainer.animationTime } + } + + /* Rectangle{ + width:parent.width + height:1 + y:parent.height / 2 + border.width: 1 + border.color: "red" + color: "transparent" + }*/ + } + + }// Flow with hidden spacers inside + + Component { + id: taskInitComponent + Timer { + id: timer + + interval: 800 + repeat: false + + onTriggered: { + // mainItemContainer.hoverEnabled = true; + tasksModel.requestPublishDelegateGeometry(mainItemContainer.modelIndex(), + backend.globalRect(mainItemContainer), mainItemContainer); + timer.destroy(); + } + + Component.onCompleted: timer.start() + } + } + + ////// Values Changes ///// + //restore scales when there is no zoom factor for that item or + //the mouse is out of the ListView + // onItemIndexChanged: { + // } + + onHoveredIndexChanged: { + var distanceFromHovered = Math.abs(index - icList.hoveredIndex); + + if( (distanceFromHovered > 1) || (hoveredIndex < 0)){ + if(!isDragged) + wrapper.scale = 1; + hiddenSpacerLeft.nScale = 0; + hiddenSpacerRight.nScale = 0; + } + } + + + onIsDraggedChanged: { + if(isDragged && (plasmoid.immutable)){ + panel.dragSource = mainItemContainer; + dragHelper.startDrag(mainItemContainer, model.MimeType, model.MimeData, + model.LauncherUrlWithoutIcon, model.decoration); + pressX = -1; + pressY = -1; + } + } + + onDelayingRemoveChanged: { + if(delayingRemove && isWindow) + groupWindowRemoved(); + } + + onIsWindowChanged: { + if (isWindow) { + taskInitComponent.createObject(mainItemContainer); + } + } + + onIsMinimizedChanged: { + checkWindowsStates(); + } + + onIsActiveChanged: { + checkWindowsStates(); + } + + ////// End of Values Changes ///// + + + ///////////////// Mouse Area Events /////////////////// + onEntered: { + checkListHovered.stop(); + + if((!inAnimation)&&(panel.dragSource == null)&&(!panel.taskInAnimation)){ + icList.hoveredIndex = index; + mouseEntered = true; + panel.mouseWasEntered(index-2, false); + panel.mouseWasEntered(index+2, false); + panel.mouseWasEntered(index-1, true); + panel.mouseWasEntered(index+1, true); + + if (icList.orientation == Qt.Horizontal){ + icList.currentSpot = mouseX; + wrapper.calculateScales(mouseX); + } + else{ + icList.currentSpot = mouseY; + wrapper.calculateScales(mouseY); + } + } + } + + // IMPORTANT: This must be improved ! even for small miliseconds it reduces performance + onExited: { + mouseEntered = false; + if(mainItemContainer.contextMenu && mainItemContainer.contextMenu.status == PlasmaComponents.DialogStatus.Open){ + ///dont check to restore zooms + } + else{ + if(!inAnimation){ + checkListHovered.start(); + } + } + + /* if(draggingResistaner != null){ + draggingResistaner.destroy(); + draggingResistaner = null; + isDragged = false; + }*/ + } + + onPositionChanged: { + checkListHovered.stop(); + + if((inAnimation == false)&&(!panel.taskInAnimation)&&(!panel.disableRestoreZoom)){ + if(panel.dragSource == null){ + if (icList.orientation == Qt.Horizontal){ + var step = Math.abs(icList.currentSpot-mouse.x); + if (step >= animationStep){ + icList.hoveredIndex = index; + icList.currentSpot = mouse.x; + + wrapper.calculateScales(mouse.x); + } + } + else{ + var step = Math.abs(icList.currentSpot-mouse.y); + if (step >= animationStep){ + icList.hoveredIndex = index; + icList.currentSpot = mouse.y; + + wrapper.calculateScales(mouse.y); + } + } + } + + // mouse.button is always 0 here, hence checking with mouse.buttons + if (pressX != -1 && mouse.buttons == Qt.LeftButton + && isDragged + && plasmoid.immutable + && dragHelper.isDrag(pressX, pressY, mouse.x, mouse.y) ) { + panel.dragSource = mainItemContainer; + dragHelper.startDrag(mainItemContainer, model.MimeType, model.MimeData, + model.LauncherUrlWithoutIcon, model.decoration); + pressX = -1; + pressY = -1; + } + else{ + /* if(draggingResistaner != null){ + draggingResistaner.destroy(); + draggingResistaner = null; + } + isDragged = false;*/ + } + } + } + + onContainsMouseChanged:{ + if(!containsMouse){ + hiddenSpacerLeft.nScale = 0; + hiddenSpacerRight.nScale = 0; + + if(!inAnimation) + pressed=false; + } + + if (isWindow) { + panel.windowsHovered(model.LegacyWinIdList, containsMouse); + } + + + ////window previews///////// + if (isWindow) { + if(containsMouse && panel.showPreviews && windowSystem.compositingActive){ + hoveredTimerObj = hoveredTimerComponent.createObject(mainItemContainer); + preparePreviewWindow(); + } + else{ + if (hoveredTimerObj){ + hoveredTimerObj.stop(); + hoveredTimerObj.destroy(); + } + } + } + } + + onPressed: { + if (windowSystem.compositingActive) { + windowsPreviewDlg.hide(); + } + + if ((mouse.button == Qt.LeftButton)||(mouse.button == Qt.MidButton)) { + lastButtonClicked = mouse.button; + pressed = true; + pressX = mouse.x; + pressY = mouse.y; + + if(draggingResistaner == null) + draggingResistaner = resistanerTimerComponent.createObject(mainItemContainer); + } + else if (mouse.button == Qt.RightButton){ + panel.createContextMenu(mainItemContainer).show(); + } + + if (hoveredTimerObj){ + hoveredTimerObj.stop(); + hoveredTimerObj.destroy(); + } + } + + onReleased: { + if (draggingResistaner != null){ + draggingResistaner.destroy(); + draggingResistaner = null; + } + + if(pressed && !inBlockingAnimation){ + if (mouse.button == Qt.MidButton){ + if( !mainItemContainer.isLauncher){ + if (plasmoid.configuration.middleClickAction == TaskManagerApplet.Backend.NewInstance) { + tasksModel.requestNewInstance(modelIndex()); + } else if (plasmoid.configuration.middleClickAction == TaskManagerApplet.Backend.Close) { + tasksModel.requestClose(modelIndex()); + } else if (plasmoid.configuration.middleClickAction == TaskManagerApplet.Backend.ToggleMinimized) { + tasksModel.requestToggleMinimized(modelIndex()); + } + } + else { + mouseEntered = false; + wrapper.runLauncherAnimation(); + } + } + else if (mouse.button == Qt.LeftButton){ + if( mainItemContainer.isLauncher){ + mouseEntered = false; + wrapper.runLauncherAnimation(); + } + else{ + if (model.IsGroupParent) { + if (windowSystem.compositingActive) { + panel.presentWindows(model.LegacyWinIdList); + } else { + if ((windowsPreviewDlg.visualParent === mainItemContainer)&&(windowsPreviewDlg.visible)) { + windowsPreviewDlg.hide(); + } else { + preparePreviewWindow(); + windowsPreviewDlg.show(); + } + } + } else { + if (IsMinimized === true) { + var i = modelIndex(); + tasksModel.requestToggleMinimized(i); + tasksModel.requestActivate(i); + } else if (IsActive === true) { + tasksModel.requestToggleMinimized(modelIndex()); + } else { + tasksModel.requestActivate(modelIndex()); + } + } + + } + } + } + + pressed = false; + + if(!inAnimation) + checkListHovered.startDuration(3*units.longDuration); + } + + ///////////////// End Of Mouse Area Events /////////////////// + + ///// Handlers for Signals ///// + function signalMouseWasEntered(nIndex, value){ + if( index === nIndex) + mouseEntered = value; + } + + function animationStarted(){ + // console.log("Animation started: " + index); + inAnimation = true; + } + + function animationEnded(){ + // console.log("Animation ended: " + index); + inAnimation = false; + // checkListHovered.startDuration(3*units.longDuration); + } + + function clearZoom(){ + // console.log("Clear zoom: " + index); + if (wrapper) + wrapper.scale=1; + } + + function handlerDraggingFinished(){ + isDragged = false; + } + ///// End of Handlers ////// + + + + ///// Helper functions ///// + function preparePreviewWindow(){ + windowsPreviewDlg.visualParent = mainItemContainer; + + toolTipDelegate.parentIndex = index; + + toolTipDelegate.windows = Qt.binding(function() { + return model.LegacyWinIdList; + }); + toolTipDelegate.mainText = Qt.binding(function() { + return model.display; + }); + toolTipDelegate.icon = Qt.binding(function() { + return model.decoration; + }); + toolTipDelegate.subText = Qt.binding(function() { + return model.IsLauncher === true ? model.GenericName : generateSubText(model); + }); + toolTipDelegate.launcherUrl = Qt.binding(function() { + return model.LauncherUrlWithoutIcon; + }); + + toolTipDelegate.titles = tasksWindows.windowsTitles(); + } + + + function launcherAction(){ + // if ((lastButtonClicked == Qt.LeftButton)||(lastButtonClicked == Qt.MidButton)){ + tasksModel.requestActivate(modelIndex()); + // } + } + + ///window previews/// + function generateSubText(task) { + var subTextEntries = new Array(); + + if (!plasmoid.configuration.showOnlyCurrentDesktop + && virtualDesktopInfo.numberOfDesktops > 1 + && model.IsOnAllVirtualDesktops !== true + && model.VirtualDesktop != -1 + && model.VirtualDesktop != undefined) { + subTextEntries.push(i18n("On %1", virtualDesktopInfo.desktopNames[model.VirtualDesktop - 1])); + } + + if (model.Activities == undefined) { + return subTextEntries.join("\n"); + } + + if (model.Activities.length == 0 && activityInfo.numberOfRunningActivities > 1) { + subTextEntries.push(i18nc("Which virtual desktop a window is currently on", + "Available on all activities")); + } else if (model.Activities.length > 0) { + var activityNames = new Array(); + + for (var i = 0; i < model.Activities.length; i++) { + var activity = model.Activities[i]; + + if (plasmoid.configuration.showOnlyCurrentActivity) { + if (activity != activityInfo.currentActivity) { + activityNames.push(activityInfo.activityName(model.Activities[i])); + } + } else if (activity != activityInfo.currentActivity) { + activityNames.push(activityInfo.activityName(model.Activities[i])); + } + } + + 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(", "))); + } + } else if (activityNames.length > 0) { + subTextEntries.push(i18nc("Which activities a window is currently on", + "Available on %1", activityNames.join(", "))); + } + } + + return subTextEntries.join("\n"); + } + ///window previews//// + + + + function modelIndex(){ + return tasksModel.makeModelIndex(index); + } + + function setBlockingAnimation(value){ + inBlockingAnimation = value; + } + + function slotPublishGeometries() { + if (isWindow || isStartup || isGroupParent) { + tasksModel.requestPublishDelegateGeometry(mainItemContainer.modelIndex(), + backend.globalRect(mainItemContainer), mainItemContainer); + } + } + + //fix wrong positioning of launchers.... + onActivityChanged:{ + for(var i=0; i<tasksModel.launcherList.length; ++i){ + if ((tasksModel.launcherList[i] == LauncherUrlWithoutIcon) && (i != index)){ + updatePosition.restart(); + } + } + } + + ///// End of Helper functions //// + + Component.onCompleted: { + panel.mouseWasEntered.connect(signalMouseWasEntered); + panel.draggingFinished.connect(handlerDraggingFinished); + panel.clearZoomSignal.connect(clearZoom); + panel.publishTasksGeometries.connect(slotPublishGeometries); + + //fix wrong positioning of launchers.... + for(var i=0; i<tasksModel.launcherList.length; ++i){ + if ((tasksModel.launcherList[i] == LauncherUrlWithoutIcon) && (i != index)){ + updatePosition.restart(); + } + } + + showWindowAnimation.showWindow(); + } + + Component.onDestruction: { + wrapper.sendEndOfNeedBothAxisAnimation(); + } + + Timer{ + id:updatePosition + interval: 800 + + onTriggered: { + for(var i=0; i<tasksModel.launcherList.length; ++i){ + if ((tasksModel.launcherList[i] == LauncherUrlWithoutIcon) && (i != index)){ + // console.log("Launch List:"+tasksModel.launcherList); + // console.log("Move from timer "+AppId+" - from:"+ index + " to:" + i + " - total:"+tasksModel.count); + tasksModel.move(index, i); + } + } + } + } + + + /////Animations + + ///item's added Animation + SequentialAnimation{ + id:showWindowAnimation + property int speed: windowSystem.compositingActive ? plasmoid.configuration.durationTime* (1.2*units.longDuration) : 0 + + PropertyAnimation { + target: wrapper + property: (icList.orientation == Qt.Vertical) ? "tempScaleHeight" : "tempScaleWidth" + to: 1 + duration: showWindowAnimation.speed + easing.type: Easing.OutQuad + } + + ParallelAnimation{ + + PropertyAnimation { + target: wrapper + property: (icList.orientation == Qt.Vertical) ? "tempScaleWidth" : "tempScaleHeight" + to: 1 + duration: showWindowAnimation.speed + easing.type: Easing.OutQuad + } + + + PropertyAnimation { + target: wrapper + property: "opacity" + from: 0 + to: 1 + duration: showWindowAnimation.speed + easing.type: Easing.OutQuad + } + } + + onStopped: { + if(mainItemContainer.isWindow || mainItemContainer.isStartup){ + taskInitComponent.createObject(wrapper); + if (mainItemContainer.isDemandingAttention){ + mainItemContainer.groupWindowAdded(); + } + } + mainItemContainer.inAnimation = false; + } + + function init(){ + wrapper.tempScaleWidth = 0; + wrapper.tempScaleHeight = 0; + } + + function showWindow(){ + if(mainItemContainer.isLauncher || mainItemContainer.isStartup + || icList.delayingRemoval || (!mainItemContainer.buffersAreReady && !panel.initializatedBuffers)){ + delayShowWindow.createObject(mainItemContainer); + } + else{ + execute(); + } + } + + function execute(){ + init(); + start(); + } + } + + //A Timer to check how much time the task is hovered in order to check if we must + //show window previews + Component { + id: hoveredTimerComponent + Timer { + id: hoveredTimer + + interval: 2*plasmoid.configuration.durationTime*units.longDuration + + repeat: false + + onTriggered: { + if(mainItemContainer.containsMouse){ + // console.log("Hovered Timer...."); + windowsPreviewDlg.show(); + //windowsPreviewDlg.visible = true; + } + + hoveredTimer.destroy(); + } + + Component.onCompleted: hoveredTimer.start() + } + } + + + //A Timer to help in resist a bit to dragging, the user must try + //to press a little first before dragging Started + Component { + id: resistanerTimerComponent + Timer { + id: resistanerTimer + interval: mainItemContainer.resistanceDelay + repeat: false + + onTriggered: { + if (!mainItemContainer.inBlockingAnimation){ + mainItemContainer.isDragged = true; + } + resistanerTimer.destroy(); + } + + Component.onCompleted: resistanerTimer.start() + } + } + + + ///trying to compete with the crazy situation in the tasksModel + ///with launchers and startups... There are windows that stay + ///startup mode e.g. chrome, libreoffice... not showing startups + ///the user can lose windows... + ///Based on the animations, windows are shown directly, startups + ///are shown after 5secs of existence, and launchers after 200ms + ///for launchers this is set in order to give to a window the time + ///to desappear and then show the launcher... + + + // property int mainDelay: IsLauncher ? 800 : 400 + // property int mainDelay: icList.delayingRemoval ? 2*showWindowAnimation.speed : 450 + + //BE CAREFUL: this interval (e.g. 700ms) must be lower from the removal animation + //duration e.g.(800ms) because there are situattions that because of this some + //launchers delay A LOT to reappear, e.g google-chrome + //I will blacklist google-chrome as I have not found any other case for this bug + //to appear, but even this way there are cases that still appears... + property int mainDelay: (AppId == "google-chrome") ? 0 : 2*plasmoid.configuration.durationTime*showWindowAnimation.speed + property int windowDelay: mainItemContainer.isStartup ? 3*plasmoid.configuration.durationTime*units.longDuration : mainDelay + + Component { + id: delayShowWindow + Timer { + id: timerWindow + + interval: windowDelay + + repeat: false + + onTriggered: { + //console.log("I am in here: "+mainItemContainer.windowDelay); + // showWindowAnimation.execute(); + if(!mainItemContainer.buffersAreReady && !panel.initializatedBuffers) + showWindowAnimation.showWindow(); + else + showWindowAnimation.execute(); + + timerWindow.destroy(); + } + + Component.onCompleted: timerWindow.start() + } + } + + ///Item's Removal Animation + + ListView.onRemove: SequentialAnimation { + PropertyAction { target: mainItemContainer; property: "ListView.delayRemove"; value: true } + PropertyAction { target: mainItemContainer; property: "inAnimation"; value: true } + PropertyAction { target: icList; property: "delayingRemoval"; value: true } + PropertyAction { target: wrapper; property: "opacity"; value: isWindow ? 0 : 1 } + //animation mainly for launchers removal and startups + ParallelAnimation{ + id: removalAnimation + + // property int speed: (IsStartup && !mainItemContainer.visible)? 0 : 400 + //property int speed: 400 + + NumberAnimation { target: wrapper; property: "opacity"; to: 0; duration: showWindowAnimation.speed; easing.type: Easing.InQuad } + + PropertyAnimation { + target: wrapper + property: (icList.orientation == Qt.Vertical) ? "tempScaleWidth" : "tempScaleHeight" + to: 0 + duration: showWindowAnimation.speed + easing.type: Easing.InQuad + } + } + + //smooth move into place the surrounding tasks + PropertyAnimation { + target: wrapper + property: (icList.orientation == Qt.Vertical) ? "tempScaleHeight" : "tempScaleWidth" + to: 0 + duration: showWindowAnimation.speed + easing.type: Easing.InQuad + } + + PropertyAction { target: mainItemContainer; property: "inAnimation"; value: false } + PropertyAction { target: mainItemContainer; property: "ListView.delayRemove"; value: false } + PropertyAction { target: icList; property: "delayingRemoval"; value: false } + } + +}// main Item + diff --git a/plasmoid/contents/ui/TaskGroupItem.qml b/plasmoid/contents/ui/TaskGroupItem.qml new file mode 100644 index 000000000..40e2604f6 --- /dev/null +++ b/plasmoid/contents/ui/TaskGroupItem.qml @@ -0,0 +1,171 @@ +/* + * Copyright 2013 Michail Vourlakos <mvourlakos@gmail.com> + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.0 +import QtQuick.Layouts 1.1 +import QtGraphicalEffects 1.0 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents + +Item{ + id:glowFrame + width: ( icList.orientation === Qt.Horizontal ) ? wrapper.regulatorWidth : size + height: ( icList.orientation === Qt.Vertical ) ? wrapper.regulatorHeight : size + + //property int size: Math.ceil( panel.iconSize/13 ) //5 + property int size: panel.statesLineSize + + //SystemPalette { id: myPalette; colorGroup: SystemPalette.Active } + property color isActiveColor: theme.buttonFocusColor + //property color isShownColor: plasmoid.configuration.threeColorsWindows ? panel.shownDotColor : isActiveColor + property color isShownColor: isActiveColor + property color minimizedColor: plasmoid.configuration.threeColorsWindows ? panel.minimizedDotColor : isActiveColor + property color notActiveColor: mainItemContainer.hasMinimized ? minimizedColor : isShownColor + + /* Rectangle{ + anchors.fill: parent + border.width: 1 + border.color: "yellow" + color: "transparent" + opacity:0.6 + }*/ + + Item{ + anchors.centerIn: parent + + width: flowItem.width + height: flowItem.height + + Flow{ + id: flowItem + flow: ( icList.orientation === Qt.Vertical ) ? Flow.TopToBottom : Flow.LeftToRight + + GlowPoint{ + id:firstPoint + visible: ( !IsLauncher ) ? true: false + + basicColor: (mainItemContainer.hasActive) ? + glowFrame.isActiveColor : glowFrame.notActiveColor + + roundCorners: true + showAttention: model.IsDemandingAttention ? true : false + + property int stateWidth: mainItemContainer.isGroupParent ? (wrapper.regulatorWidth - secondPoint.width) : wrapper.regulatorWidth - spacer.width + property int stateHeight: mainItemContainer.isGroupParent ? wrapper.regulatorHeight - secondPoint.height : wrapper.regulatorHeight - spacer.height + + property int animationTime: plasmoid.configuration.durationTime* (0.7*units.longDuration) + + property bool isActive: mainItemContainer.hasActive + || (panel.showPreviews && windowsPreviewDlg.activeItem && (windowsPreviewDlg.activeItem === mainItemContainer)) + + property bool vertical: panel.vertical + + property real scaleFactor: wrapper.scale + + function updateInitialSizes(){ + if(glowFrame){ + if(vertical) + width = glowFrame.size; + else + height = glowFrame.size; + + if(vertical && isActive) + height = stateHeight; + else + height = glowFrame.size; + + if(!vertical && isActive) + width = stateWidth; + else + width = glowFrame.size; + } + } + + + onIsActiveChanged: { + // if(mainItemContainer.hasActive || windowsPreviewDlg.visible) + activeAndReverseAnimation.start(); + } + + onScaleFactorChanged: { + if(!activeAndReverseAnimation.running && !panel.vertical && isActive){ + width = stateWidth; + } + else if (!activeAndReverseAnimation.running && panel.vertical && isActive){ + height = stateHeight; + } + } + + onStateWidthChanged:{ + if(!activeAndReverseAnimation.running && !vertical && isActive) + width = stateWidth; + } + + onStateHeightChanged:{ + if(!activeAndReverseAnimation.running && vertical && isActive) + height = stateHeight; + } + + onVerticalChanged: updateInitialSizes(); + + Component.onCompleted: { + updateInitialSizes(); + + panel.onIconSizeChanged.connect(updateInitialSizes); + } + + NumberAnimation{ + id: activeAndReverseAnimation + target: firstPoint + property: panel.vertical ? "height" : "width" + to: mainItemContainer.hasActive + || (panel.showPreviews && windowsPreviewDlg.activeItem && (windowsPreviewDlg.activeItem === mainItemContainer)) + ? (panel.vertical ? firstPoint.stateHeight : firstPoint.stateWidth) : glowFrame.size + duration: firstPoint.animationTime + easing.type: Easing.InQuad + + onStopped: firstPoint.updateInitialSizes() + } + } + + Item{ + id:spacer + width: mainItemContainer.isGroupParent ? 0.5*glowFrame.size : 0 + height: mainItemContainer.isGroupParent ? 0.5*glowFrame.size : 0 + } + + GlowPoint{ + id:secondPoint + width: visible ? glowFrame.size : 0 + height: width + + basicColor: ((mainItemContainer.hasActive)&&(!(mainItemContainer.hasMinimized))) ? state2Color : state1Color + roundCorners: true + visible: ( mainItemContainer.isGroupParent && plasmoid.configuration.dotsOnActive ) + || (mainItemContainer.isGroupParent && !mainItemContainer.hasActive)? true: false + + //when there is no active window + property color state1Color: mainItemContainer.hasShown ? glowFrame.isShownColor : glowFrame.minimizedColor + //when there is active window + property color state2Color: mainItemContainer.hasMinimized ? glowFrame.minimizedColor : glowFrame.isShownColor + } + } + } +}// number of windows indicator + diff --git a/plasmoid/contents/ui/TaskIconBuffers.qml b/plasmoid/contents/ui/TaskIconBuffers.qml new file mode 100644 index 000000000..bf5054963 --- /dev/null +++ b/plasmoid/contents/ui/TaskIconBuffers.qml @@ -0,0 +1,146 @@ +import QtQuick 2.0 +import QtGraphicalEffects 1.0 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents + +import org.kde.kquickcontrolsaddons 2.0 as KQuickControlAddons + +Component { + id: imageBufferingComponent + Item { + id: yourImageWithLoadedIconContainer + anchors.fill: parent + + visible: false + + property QtObject imageTimer + + function updateImage(){ + if(!imageTimer) + imageTimer = tttTimer.createObject(iconImage); + else{ + imageTimer.loop = 1; + imageTimer.restart(); + } + } + + Item{ + id:fixedIcon + + width: panel.iconSize + 2*shadowImageNoActive.radius + height: width + + visible:false + + KQuickControlAddons.QIconItem{ + id: iconImage + + width: panel.iconSize + height: width + anchors.centerIn: parent + + icon: decoration + state: KQuickControlAddons.QIconItem.DefaultState + + enabled: true + visible: true + + smooth: true + // onIconChanged: updateBuffers(); + + property int counter: 0 + + function updateBuffers(){ + if((index !== -1) &&(!centralItem.toBeDestroyed) &&(!mainItemContainer.delayingRemove)){ + if(panel.initializationStep){ + panel.initializationStep = false; + } + + centralItem.firstDrawed = true; + // counter++; + // console.log(counter); + + if(shadowedImage && shadowedImage.source) + shadowedImage.source.destroy(); + + + if(panel.enableShadows == true){ + shadowImageNoActive.grabToImage(function(result) { + shadowedImage.source = result.url + result.destroy(); + }, Qt.size(fixedIcon.width,fixedIcon.height) ); + } + else{ + fixedIcon.grabToImage(function(result) { + shadowedImage.source = result.url; + result.destroy(); + }, Qt.size(fixedIcon.width,fixedIcon.height) ); + } + + mainItemContainer.buffersAreReady = true; + if(!panel.initializatedBuffers) + panel.noInitCreatedBuffers++; + + iconImageBuffer.opacity = 1; + } + } + + Component{ + id:tttTimer + + Timer{ + id:ttt + repeat: true + interval: loop <= 1 ? centralItem.shadowInterval : 2500 + + property int loop: 0; + + onTriggered: { + iconImage.updateBuffers(); + + loop++; + + // console.log(loop+' - '+interval); + if(loop > 2) + ttt.destroy(300); + } + + Component.onCompleted: ttt.start(); + }// end of timer + + }//end of component of timer + + Component.onCompleted: { + yourImageWithLoadedIconContainer.updateImage(); + } + + Component.onDestruction: { + if(yourImageWithLoadedIconContainer.imageTimer) + yourImageWithLoadedIconContainer.imageTimer.destroy(); + } + } + } + + DropShadow { + id:shadowImageNoActive + visible: false + width: fixedIcon.width + height: fixedIcon.height + anchors.centerIn: fixedIcon + + radius: centralItem.shadowSize + samples: 2 * radius + color: "#ff080808" + source: fixedIcon + + verticalOffset: 2 + } + + Component.onDestruction: { + if(yourImageWithLoadedIconContainer.imageTimer) + yourImageWithLoadedIconContainer.imageTimer.destroy(); + } + } +} + diff --git a/plasmoid/contents/ui/TaskIconItem.qml b/plasmoid/contents/ui/TaskIconItem.qml new file mode 100644 index 000000000..cd7dba934 --- /dev/null +++ b/plasmoid/contents/ui/TaskIconItem.qml @@ -0,0 +1,864 @@ +/* + * Copyright 2013 Michail Vourlakos <mvourlakos@gmail.com> + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.0 +import QtQuick.Layouts 1.1 +import QtGraphicalEffects 1.0 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.private.taskmanager 0.1 as TaskManagerApplet + +import org.kde.kquickcontrolsaddons 2.0 as KQuickControlAddons + +//I am using KQuickControlAddons.QIconItem even though onExit it triggers the following error +//QObject::~QObject: Timers cannot be stopped from another thread +//but it increases performance almost to double during animation + +Item{ + id: centralItem + + width: wrapper.regulatorWidth + height: wrapper.regulatorHeight + + //big interval to show shadows only after all the crappy adds and removes of tasks + //have happened + property bool firstDrawed: true + property bool toBeDestroyed: false + + // three intervals in order to create the necessarty buffers from the + // PlasmaCore.IconItem, one big interval for the first creation of the + // plasmoid, a second one for the first creation of a task and a small one + // for simple updates. + // This is done before especially on initialization stage some visuals + // are not ready and empty buffers are created + + //property int firstDrawedInterval: panel.initializationStep ? 2000 : 1000 + // property int shadowInterval: firstDrawed ? firstDrawedInterval : 250 + property int shadowInterval: firstDrawed ? 1000 : 250 + + property int shadowSize : Math.ceil(panel.iconSize / 10) + + readonly property bool smartLauncherEnabled: ((mainItemContainer.isStartup === false) && (plasmoid.configuration.smartLaunchersEnabled)) + readonly property variant iconDecoration: decoration + property QtObject buffers: null + property QtObject smartLauncherItem: null + + /* Rectangle{ + anchors.fill: parent + border.width: 1 + border.color: "green" + color: "transparent" + } */ + + Connections{ + target: panel + onZoomFactorChanged: updateImages() + onIconSizeChanged: updateImages() + onEnableShadowsChanged: updateImages() + } + + onIconDecorationChanged: { + updateImages(); + } + + Rectangle{ + id: draggedRectangle + width: iconImageBuffer.width+1 + height: iconImageBuffer.height+1 + anchors.centerIn: iconImageBuffer + opacity: 0 + radius: 3 + anchors.margins: 5 + + property color tempColor: theme.highlightColor + color: tempColor + border.width: 1 + border.color: theme.highlightColor + + onTempColorChanged: tempColor.a = 0.35; + } + + //temporary buffers containing the normal Image icon and the zoomed Image icon + // Image{id:zoomedImage; visible:false} + // Image{id:normalImage; visible:false} + Image{ + id:shadowedImage + anchors.centerIn:iconImageBuffer + // anchors.horizontalCenter: iconImageBuffer.horizontalCenter + //anchors.verticalCenter: iconImageBuffer.verticalCenter + + width:iconImageBuffer.width+2*shadowSize + height:iconImageBuffer.height+2*shadowSize + + //visible: plasmoid.configuration.showShadows + visible: false + + states: State { + name: "reparented" + ParentChange { target: shadowedImage; parent: panel; } + } + + //Corize to use when a window is removed.... + /* Colorize{ + id: removeImageColorizer + source: parent + anchors.fill: parent + + enabled: false + visible: false + + hue: 0 + saturation: 0 + lightness: 0 + }*/ + } + + /* Rectangle{ + anchors.fill: iconImageBuffer + border.width: 1 + border.color: "red" + color: "transparent" + }*/ + + KQuickControlAddons.QIconItem{ + id: iconImageBuffer + + // anchors.centerIn: parent + + width: Math.round(newTempSize) //+ 2*centralItem.shadowSize + height: Math.round(width) + icon: decoration + + property int zoomedSize: panel.zoomFactor * panel.iconSize + + property real basicScalingWidth : wrapper.inTempScaling ? (panel.iconSize * wrapper.scaleWidth) : + panel.iconSize * wrapper.scale + property real basicScalingHeight : wrapper.inTempScaling ? (panel.iconSize * wrapper.scaleHeight) : + panel.iconSize * wrapper.scale + + property real newTempSize: (wrapper.opacity == 1) ? Math.min(basicScalingWidth, basicScalingHeight) : + Math.max(basicScalingWidth, basicScalingHeight) + + ///states for launcher animation + states: [ + State{ + name: "*" + when: !launcherAnimation.running && !newWindowAnimation.running + + AnchorChanges{ + target:iconImageBuffer; + anchors.horizontalCenter: parent.horizontalCenter; + anchors.verticalCenter: parent.verticalCenter; + anchors.right: undefined; + anchors.left: undefined; + anchors.top: undefined; + anchors.bottom: undefined; + } + }, + + State{ + name: "animating" + when: launcherAnimation.running || newWindowAnimation.running + + AnchorChanges{ + target:iconImageBuffer; + anchors.horizontalCenter: undefined; + anchors.verticalCenter: undefined; + anchors.right: panel.position === PlasmaCore.Types.LeftPositioned ? parent.right : undefined; + anchors.left: panel.position === PlasmaCore.Types.RightPositioned ? parent.left : undefined; + anchors.top: panel.position === PlasmaCore.Types.BottomPositioned ? parent.top : undefined; + anchors.bottom: panel.position === PlasmaCore.Types.TopPositioned ? parent.bottom : undefined; + } + } + ] + + ///transitions, basic for the anchor changes + transitions: [ + Transition{ + from: "animating" + to: "*" + + AnchorAnimation { duration: 1.5*plasmoid.configuration.durationTime*units.longDuration } + } + ] + } + + ///Shadow in tasks + Loader{ + anchors.fill: iconImageBuffer + active: plasmoid.configuration.showShadows + + sourceComponent: DropShadow{ + anchors.fill: parent + color: "#ff080808" + samples: 2 * radius + source: iconImageBuffer + radius: centralItem.shadowSize + verticalOffset: 2 + } + } + + + VisualAddItem{ + id: dropFilesVisual + anchors.fill: iconImageBuffer + + visible: opacity == 0 ? false : true + opacity: panel.dropNewLauncher && !mouseHandler.onlyLaunchers + && (panel.dragSource == null) && (mouseHandler.hoveredItem === mainItemContainer) ? 1 : 0 + } + + BrightnessContrast{ + id:hoveredImage + opacity: mainItemContainer.containsMouse ? 1 : 0 + anchors.fill: iconImageBuffer + + brightness: 0.25 + source: iconImageBuffer + + Behavior on opacity { + NumberAnimation { duration: plasmoid.configuration.durationTime*units.longDuration } + } + } + + BrightnessContrast { + id: brightnessTaskEffect + anchors.fill: iconImageBuffer + source: iconImageBuffer + + visible: clickedAnimation.running + } + + Colorize{ + id: stateColorizer + source: iconImageBuffer + anchors.fill: iconImageBuffer + //visible: false + opacity:0 + + hue:0 + saturation:0 + lightness:0 + } + + //Something to show until the buffers are updated + + //KQuickControlAddons.QIconItem{ + /* + PlasmaCore.IconItem{ + id: iconImageBackground + + //property real relatedSize: panel.iconSize * ( (doubleSize - 7) / doubleSize ); + // width: (visible) ? relatedSize * wrapper.scale : panel.iconSize + width: (visible) ? panel.iconSize * wrapper.scale : panel.iconSize + height: width + anchors.centerIn: parent + + // state: wrapper.containsMouse ? KQuickControlAddons.QIconItem.ActiveState : KQuickControlAddons.QIconItem.DefaultState + // icon: decoration + active: wrapper.containsMouse + enabled: true + source: decoration + usesPlasmaTheme: false + + visible: ((iconImageBuffer.opacity == 1) && (panel.enableShadows)) ? false : true + + Component{ + id:hideBackTimer + + Timer{ + id:hideBackgroundTimer + repeat:false + interval: centralItem.shadowInterval + + onTriggered: { + // iconImageBackground.visible = false; + iconImageBuffer.opacity = 1; + hideBackgroundTimer.destroy(); + // iconImageBuffer.visible = false; + } + + Component.onCompleted: hideBackgroundTimer.start(); + } + } + }*/ + + Loader{ + id:defaultWithShadow + //sourceComponent: imageBufferingComponent + sourceComponent: TaskIconBuffers{} + active: mainItemContainer.isStartup ? false : true + } + + Loader { + anchors.fill: iconImageBuffer + asynchronous: true + source: "TaskProgressOverlay.qml" + active: (centralItem.smartLauncherEnabled && centralItem.smartLauncherItem + && centralItem.smartLauncherItem.progressVisible) + } + + ///////Activate animation///// + SequentialAnimation{ + id: clickedAnimation + + property bool pressed: mainItemContainer.pressed + property int speed: plasmoid.configuration.durationTime*units.longDuration + + ParallelAnimation{ + PropertyAnimation { + target: brightnessTaskEffect + property: "brightness" + to: -0.5 + duration: clickedAnimation.speed + easing.type: Easing.OutQuad + } + PropertyAnimation { + target: wrapper + property: "scale" + to: panel.taskInAnimation ? 0.9 : wrapper.scale - (panel.zoomFactor - 1) / 2 + duration: clickedAnimation.speed + easing.type: Easing.OutQuad + } + } + + ParallelAnimation{ + PropertyAnimation { + target: brightnessTaskEffect + property: "brightness" + to: 0 + duration: clickedAnimation.speed + easing.type: Easing.OutQuad + } + PropertyAnimation { + target: wrapper + property: "scale" + to: panel.taskInAnimation ? 1 : panel.zoomFactor + duration: clickedAnimation.speed + easing.type: Easing.OutQuad + } + } + + + onPressedChanged: { + if( (pressed)&& + ((mainItemContainer.lastButtonClicked == Qt.LeftButton)||(mainItemContainer.lastButtonClicked == Qt.MidButton)) ){ + mainItemContainer.animationStarted(); + start(); + } + } + + onStopped: { + if( !mainItemContainer.isDragged){ + mainItemContainer.animationEnded(); + checkListHovered.startDuration(6*units.longDuration); + } + } + } + + + Component.onCompleted: { + if (smartLauncherEnabled && !smartLauncherItem) { + var smartLauncher = Qt.createQmlObject( + " import org.kde.plasma.private.taskmanager 0.1 as TaskManagerApplet; TaskManagerApplet.SmartLauncherItem { }", + centralItem); + + smartLauncher.launcherUrl = Qt.binding(function() { return model.LauncherUrlWithoutIcon; }); + + smartLauncherItem = smartLauncher; + } + } + + Component.onDestruction: { + centralItem.toBeDestroyed = true; + + if(shadowedImage && shadowedImage.source) + shadowedImage.source.destroy(); + + if(removingAnimation.removingItem) + removingAnimation.removingItem.destroy(); + + gc(); + } + ////end of activate animation//// + + ////bouncing task, e.g. on launcher activating and when a new window is + ////added in a group task + SequentialAnimation{ + id:launcherAnimation + + property bool launchedAlready: false + property int speed: plasmoid.configuration.durationTime * 0.8 * units.longDuration + + SequentialAnimation{ + ParallelAnimation{ + PropertyAnimation { + target: wrapper + property: (icList.orientation == Qt.Vertical) ? "tempScaleWidth" : "tempScaleHeight" + to: panel.zoomFactor + duration: launcherAnimation.speed + easing.type: Easing.OutQuad + } + + PropertyAnimation { + target: wrapper + property: (icList.orientation == Qt.Horizontal) ? "tempScaleWidth" : "tempScaleHeight" + to: 1 + duration: launcherAnimation.speed + easing.type: Easing.OutQuad + } + } + + PropertyAnimation { + target: wrapper + property: (icList.orientation == Qt.Vertical) ? "tempScaleWidth" : "tempScaleHeight" + to: 1 + duration: 3*plasmoid.configuration.durationTime*launcherAnimation.speed + easing.type: Easing.OutBounce + } + + ParallelAnimation{ + PropertyAnimation { + target: wrapper + property: (icList.orientation == Qt.Vertical) ? "tempScaleHeight" : "tempScaleWidth" + to: 1 + duration: plasmoid.configuration.durationTime*launcherAnimation.speed + easing.type: Easing.OutBounce + } + + PropertyAnimation { + target: wrapper + property: "scale" + to: 1 + duration: plasmoid.configuration.durationTime*launcherAnimation.speed + easing.type: Easing.OutQuad + } + } + } + + + onStopped: { + //wrapper.scale = 1; + /* if ( panel.noTasksInAnimation>0 ) { + panel.noTasksInAnimation--; + } + if ( panel.animations>0 ) { + panel.animations--; + }*/ + //console.log ("Nooo 2: "+panel.noTasksInAnimation + " - "+panel.animations); + clearAnimationsSignals(); + + mainItemContainer.setBlockingAnimation(false); + mainItemContainer.animationEnded(); + mainItemContainer.launcherAction(); + } + + function clearAnimationsSignals() { + if ( launchedAlready && panel.noTasksInAnimation>0 ) { + panel.noTasksInAnimation--; + } + + if ( launchedAlready && panel.animationsNeedThickness>0 ) { + panel.setAnimationsNeedThickness( panel.animationsNeedThickness-1 ); + } + + launchedAlready = false; + } + + function init(){ + //console.log ("Nooo 1 : "+panel.noTasksInAnimation); + if(!launchedAlready) { + launchedAlready = true; + panel.setAnimationsNeedThickness( panel.animationsNeedThickness+1 ); + panel.noTasksInAnimation++; + mainItemContainer.setBlockingAnimation(true); + } + + wrapper.tempScaleWidth = wrapper.scale; + wrapper.tempScaleHeight = wrapper.scale; + + icList.hoveredIndex = -1; + } + + function bounceLauncher(){ + if(panel.zoomFactor > 1){ + mainItemContainer.animationStarted(); + init(); + start(); + } + else + stopped(); + } + + + Component.onCompleted: { + wrapper.runLauncherAnimation.connect(bounceLauncher); + } + + Component.onDestruction: { + clearAnimationsSignals(); + } + } + /////////////////// end of launcher animation + + + ////////////////// new window and needs attention animation + SequentialAnimation{ + id:newWindowAnimation + + property int speed: plasmoid.configuration.durationTime*units.longDuration + property bool isDemandingAttention: (IsDemandingAttention === true) ? true : false + property bool entered: mainItemContainer.mouseEntered + property bool needsThicknessSent: false //flag to check if the signal for thickness was sent + + SequentialAnimation{ + ParallelAnimation{ + PropertyAnimation { + target: wrapper + property: (icList.orientation == Qt.Vertical) ? "tempScaleWidth" : "tempScaleHeight" + to: 1 + (0.6 * (panel.zoomFactor-1)) + duration: newWindowAnimation.speed + easing.type: Easing.OutQuad + } + + PropertyAnimation { + target: wrapper + property: (icList.orientation == Qt.Horizontal) ? "tempScaleWidth" : "tempScaleHeight" + to: 1 + duration: newWindowAnimation.speed + easing.type: Easing.OutQuad + } + } + + PropertyAnimation { + target: wrapper + property: (icList.orientation == Qt.Vertical) ? "tempScaleWidth" : "tempScaleHeight" + to: 1 + duration: 3*plasmoid.configuration.durationTime*newWindowAnimation.speed + easing.type: Easing.OutBounce + } + } + + function clear(){ + loops = 1; + newWindowAnimation.stop(); + // iconImageBuffer.anchors.centerIn = iconImageBuffer.parent; + + wrapper.tempScaleWidth = 1; + wrapper.tempScaleHeight = 1; + } + + onStopped: { + sendEndOfNeedThicknessAnimation(); + clear(); + } + + onIsDemandingAttentionChanged: { + if( (!isDemandingAttention)&&(running)){ + clear(); + // wrapper.animationEnded(); + } + else if(isDemandingAttention){ + bounceNewWindow(); + } + } + + function sendEndOfNeedThicknessAnimation(){ + if (needsThicknessSent) { + needsThicknessSent = false; + if (panel.animationsNeedThickness > 0) { + panel.setAnimationsNeedThickness( panel.animationsNeedThickness-1 ); + } + } + } + + function init(){ + wrapper.tempScaleWidth = wrapper.scale; + wrapper.tempScaleHeight = wrapper.scale; + + if(!isDemandingAttention) + loops = 2; + else + loops = 20; + + if (!needsThicknessSent) { + needsThicknessSent = true; + panel.setAnimationsNeedThickness( panel.animationsNeedThickness+1 ); + } + + // icList.hoveredIndex = -1; + } + + function bounceNewWindow(){ + newWindowAnimation.init(); + start(); + } + + Component.onCompleted: { + mainItemContainer.groupWindowAdded.connect(bounceNewWindow); + } + + Component.onDestruction: { + sendEndOfNeedThicknessAnimation(); + } + } + + /////Removing a Window from a group//// + + Item{ + id:removingAnimation + + function init(){ + var relavantPoint + if(plasmoid.configuration.showShadows) + relavantPoint = panel.mapFromItem(shadowedImage,0,0); + else + relavantPoint = panel.mapFromItem(iconImageBuffer,0,0); + + + var removingItem = removeTaskComponent.createObject(panel); + removingItem.x = relavantPoint.x; + removingItem.y = relavantPoint.y; + + removingItem.start(); + } + + function removeTask(){ + if(centralItem.firstDrawed && !centralItem.toBeDestroyed + && mainItemContainer.buffersAreReady && plasmoid.configuration.showShadows + && windowSystem.compositingActive){ + removingAnimation.init(); + } + } + + + Component.onCompleted: { + mainItemContainer.groupWindowRemoved.connect(removeTask); + } + + ///////////// Component for animating removing window from group + + Component { + id: removeTaskComponent + Item{ + id: removeTask + width: plasmoid.configuration.showShadows ? shadowedImage.width : iconImageBuffer.width + height: plasmoid.configuration.showShadows ? shadowedImage.height : iconImageBuffer.height + //parent: panel + + visible: false + + Image { + id: tempRemoveIcon + source: plasmoid.configuration.showShadows ? shadowedImage.source : iconImageBuffer.source + anchors.fill: parent + } + + Colorize{ + source: tempRemoveIcon + anchors.fill: tempRemoveIcon + + hue: 0 + saturation: 0 + lightness: 0 + } + + ParallelAnimation{ + id: componentRemoveAnimation + + property int speed: 2*plasmoid.configuration.durationTime*units.longDuration + property Item removingItem: parent + property int toPoint: 0 + + PropertyAnimation { + target: removeTask + property: "opacity" + to: 0 + duration: componentRemoveAnimation.speed + easing.type: Easing.InQuad + } + + PropertyAnimation { + target: removeTask + property: (icList.orientation == Qt.Horizontal) ? "y" : "x" + to: componentRemoveAnimation.toPoint + duration: componentRemoveAnimation.speed + easing.type: Easing.InQuad + } + + onStopped: { + removeTask.destroy(); + gc(); + } + } + + function start(){ + var tempPoint = 0; + + if(icList.orientation == Qt.Horizontal) + tempPoint = y; + else + tempPoint = x; + + if( (panel.position === PlasmaCore.Types.BottomPositioned) || + (panel.position === PlasmaCore.Types.RightPositioned) ){ + componentRemoveAnimation.toPoint = tempPoint + panel.iconSize; + } + else{ + componentRemoveAnimation.toPoint = tempPoint - panel.iconSize; + } + + visible = true; + componentRemoveAnimation.start(); + } + + } + } + } + + //////////// States //////////////////// + states: [ + State{ + name: "*" + when: !mainItemContainer.isDragged + }, + + State{ + name: "isDragged" + when: ( (mainItemContainer.isDragged) && (plasmoid.immutable) ) + + // PropertyChanges { target: clickedAnimation; running:false } + PropertyChanges { target: wrapper; scale:1 + ((panel.zoomFactor - 1) / 2)} + } + ] + + //////////// Transitions ////////////// + + transitions: [ + Transition{ + id: isDraggedTransition + to: "isDragged" + property int speed: plasmoid.configuration.durationTime*units.longDuration + + SequentialAnimation{ + ParallelAnimation{ + PropertyAnimation { + target: draggedRectangle + property: "opacity" + to: 1 + duration: isDraggedTransition.speed + easing.type: Easing.OutQuad + } + + PropertyAnimation { + target: iconImageBuffer + property: "opacity" + to: 0 + duration: isDraggedTransition.speed + easing.type: Easing.OutQuad + } + + PropertyAnimation { + target: stateColorizer + property: "opacity" + to: 1 + duration: isDraggedTransition.speed + easing.type: Easing.OutQuad + } + } + } + + onRunningChanged: { + if(running){ + mainItemContainer.animationStarted(); + //panel.animations++; + panel.updateScale(index-1, 1, 0); + panel.updateScale(index+1, 1, 0); + } + } + }, + Transition{ + id: defaultTransition + from: "isDragged" + to: "*" + property int speed: plasmoid.configuration.durationTime*units.longDuration + + SequentialAnimation{ + ParallelAnimation{ + PropertyAnimation { + target: draggedRectangle + property: "opacity" + to: 0 + duration: defaultTransition.speed + easing.type: Easing.OutQuad + } + + PropertyAnimation { + target: iconImageBuffer + property: "opacity" + to: 1 + duration: defaultTransition.speed + easing.type: Easing.OutQuad + } + + PropertyAnimation { + target: stateColorizer + property: "opacity" + to: 0 + duration: isDraggedTransition.speed + easing.type: Easing.OutQuad + } + } + + PropertyAnimation { + target: wrapper + property: "scale" + to: 1; + duration: isDraggedTransition.speed + easing.type: Easing.OutQuad + } + + } + + onRunningChanged: { + if(!running){ + var halfZoom = 1 + ((panel.zoomFactor - 1) / 2); + panel.updateScale(index-1, halfZoom, 0); + panel.updateScale(index+1, halfZoom, 0); + + mainItemContainer.animationEnded(); + // panel.animations--; + } + } + } + ] + + + ////////////////////////// + + function updateImages(){ + if(panel){ + if(defaultWithShadow.item){ + defaultWithShadow.item.updateImage(); + } + } + } + + +}// Icon Item diff --git a/plasmoid/contents/ui/TaskProgressOverlay.qml b/plasmoid/contents/ui/TaskProgressOverlay.qml new file mode 100644 index 000000000..a0c298ebf --- /dev/null +++ b/plasmoid/contents/ui/TaskProgressOverlay.qml @@ -0,0 +1,75 @@ +/* + * Copyright 2013 Michail Vourlakos <mvourlakos@gmail.com> + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.0 + +import org.kde.plasma.core 2.0 as PlasmaCore + +Item { + id: background + anchors.fill: parent + + CircleText { + id: progressCircle + width: 0.42 * parent.height + height: width + numberValue: centralItem.smartLauncherItem.count + fullCircle: true + showNumber: true + proportion: centralItem.smartLauncherItem.progress / 100 + + states: [ + State { + name: "left" + when: (panel.position === PlasmaCore.Types.LeftPositioned) + + AnchorChanges { + target: progressCircle + anchors{ top:background.top; bottom:undefined; left:undefined; right:background.right;} + } + }, + State { + name: "right" + when: (panel.position === PlasmaCore.Types.RightPositioned) + + AnchorChanges { + target: progressCircle + anchors{ top:background.top; bottom:undefined; left:background.left; right:undefined;} + } + }, + State { + name: "bottom" + when: (panel.position === PlasmaCore.Types.BottomPositioned) + + AnchorChanges { + target: progressCircle + anchors{ top:background.top; bottom:undefined; left:undefined; right:background.right;} + } + }, + State { + name: "top" + when: (panel.position === PlasmaCore.Types.TopPositioned) + + AnchorChanges { + target: progressCircle + anchors{ top:background.top; bottom:undefined; left:undefined; right:background.right;} + } + } + ] + } +} diff --git a/plasmoid/contents/ui/TaskWindows.qml b/plasmoid/contents/ui/TaskWindows.qml new file mode 100644 index 000000000..0286564f5 --- /dev/null +++ b/plasmoid/contents/ui/TaskWindows.qml @@ -0,0 +1,159 @@ +/* + * Copyright 2013 Michail Vourlakos <mvourlakos@gmail.com> + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.0 +import QtQml.Models 2.2 + +//trying to do a very simple thing to count how many windows does +//a task instace has... +//Workaround the mess with launchers, startups, windows etc. + +Item{ + id: windowsContainer + property int windowsCount: 0 + + property bool isLauncher: IsLauncher ? true : false + property bool isStartup: IsStartup ? true : false + property bool isWindow: IsWindow ? true : false + + onIsLauncherChanged: updateCounter(); + // onIsStartupChanged: updateCounter(); + // onIsWindowChanged: updateCounter(); + + //states that exist in windows in a Group of windows + property bool hasMinimized: false; + property bool hasShown: false; + property bool hasActive: false; + + //FIXME: For some reason the index is not updated correctly in some cases (e.g. window dragging, repositioning launchers) + // and this way much beautiful information are lost, an activity change back and return, + // it fixes this sometimes... + DelegateModel { + id: windowsLocalModel + model: tasksModel //icList.model + rootIndex: tasksModel.makeModelIndex(currentIndex >=0 ? currentIndex : index) + + property int currentIndex: -1 + + delegate: Item{ + property string title: model.display + } + + onCountChanged:{ + windowsContainer.updateCounter(); + } + } + + function initializeStates(){ + hasMinimized = false; + hasShown = false; + hasActive = false; + + if(IsGroupParent){ + checkInternalStates(); + } + else{ + if(mainItemContainer.isMinimized){ + hasMinimized = true; + } + else if(mainItemContainer.isActive) + hasActive = true; + else + hasShown = true; + } + } + + function checkInternalStates(){ + windowsLocalModel.currentIndex = index; + var childs = windowsLocalModel.items; + + for(var i=0; i<childs.count; ++i){ + var kid = childs.get(i); + + if(kid.model.IsMinimized) + hasMinimized = true; + else if (kid.model.IsActive) + hasActive = true; + else + hasShown = true; + } + } + + function windowsTitles() { + var result = new Array; + + windowsLocalModel.currentIndex = index; + var childs = windowsLocalModel.items; + + for(var i=0; i<childs.count; ++i){ + var kid = childs.get(i); + var title = kid.model.display + + //console.log(title); + // FIXME: we may need a way to remove the app name from the end + /* var lst = title.lastIndexOf(" - "); + if (lst > 0) { + title = title.substring(0, lst); + }*/ + + result.push(title); + } + + return result; + } + + + Component.onCompleted: { + mainItemContainer.checkWindowsStates.connect(initializeStates); + updateCounter(); + } + + function updateCounter(){ + // console.log("--------- "+ index+" -------"); + if(index>=0){ + if(IsGroupParent){ + windowsLocalModel.currentIndex = index; + var tempC = windowsLocalModel.count; + + if (tempC == 0){ + if(isLauncher){ + windowsCount = 0; + } + else if(isWindow || isStartup){ + windowsCount = 1; + } + } + else{ + windowsCount = tempC; + } + } + else{ + if(isLauncher){ + windowsCount = 0; + } + else if(isWindow || isStartup){ + windowsCount = 1; + } + } + + initializeStates(); + } + + } + +} diff --git a/plasmoid/contents/ui/ToolTipDelegate.qml b/plasmoid/contents/ui/ToolTipDelegate.qml new file mode 100644 index 000000000..de1fd875a --- /dev/null +++ b/plasmoid/contents/ui/ToolTipDelegate.qml @@ -0,0 +1,438 @@ +/* +* Copyright 2013 by Sebastian Kügler <sebas@kde.org> +* Copyright 2014 by Martin Gräßlin <mgraesslin@kde.org> +* Copyright 2016 by Kai Uwe Broulik <kde@privat.broulik.de> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU Library General Public License as +* published by the Free Software Foundation; either version 2, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Library General Public License for more details +* +* You should have received a copy of the GNU Library General Public +* License along with this program; if not, write to the +* Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. +*/ + +import QtQuick 2.0 +import QtQuick.Layouts 1.1 +import QtGraphicalEffects 1.0 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.extras 2.0 as PlasmaExtras +import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons + +//Column { +MouseArea{ + id: tooltipContentItem + hoverEnabled: true + + property Item toolTip + property var parentIndex + property var titles + property var windows + property string mainText + property string subText + property variant icon + property url launcherUrl + property bool group: windows ? (windows.length > 1) : false + + readonly property int thumbnailWidth: units.gridUnit * 15 + readonly property int thumbnailHeight: units.gridUnit * 10 + + property int preferredTextWidth: theme.mSize(theme.defaultFont).width * 30 + property int _s: units.largeSpacing / 2 + + Layout.minimumWidth: Math.max(thumbnailWidth, windowRow.width, appLabelRow.width) + _s + Layout.minimumHeight: childrenRect.height + Layout.maximumWidth: Layout.minimumWidth + Layout.maximumHeight: Layout.minimumHeight + + onContainsMouseChanged: { + checkMouseInside(); + } + + function checkMouseInside(){ + var isInside = containsMouse || windowRow.containsMouse(); + if (isInside){ + toolTipDelegate.currentItem = parentIndex; + } + else{ + toolTipDelegate.currentItem = -1; + checkListHovered.restart(); + } + } + + states: State { + when: mpris2Source.hasPlayer + + PropertyChanges { + target: thumbnailSourceItem + opacity: 0 // cannot set visible to false or else WindowThumbnail won't provide thumbnail + } + PropertyChanges { + target: playerControlsOpacityMask + visible: true + source: thumbnailSourceItem + maskSource: playerControlsShadowMask + } + PropertyChanges { + target: playerControlsRow + visible: mpris2Source.hasPlayer + } + } + + PlasmaCore.DataSource { + id: mpris2Source + readonly property string current: { + var desktopFileName = launcherUrl.toString().split('/').pop().replace(".desktop", "") + + for (var i = 0, length = sources.length; i < length; ++i) { + var source = sources[i]; + var sourceData = data[source]; + + if (sourceData && sourceData.DesktopEntry === desktopFileName) { + return source + } + } + + return "" + } + + readonly property bool hasPlayer: !!current + + readonly property bool playing: hasPlayer && data[current].PlaybackStatus === "Playing" + readonly property bool canControl: hasPlayer && data[current].CanControl + readonly property bool canGoBack: hasPlayer && data[current].CanGoPrevious + readonly property bool canGoNext: hasPlayer && data[current].CanGoNext + readonly property bool canRaise: hasPlayer && data[current].CanRaise + + readonly property var currentMetadata: hasPlayer ? data[current].Metadata : ({}) + + readonly property string track: { + var xesamTitle = currentMetadata["xesam:title"] + if (xesamTitle) { + return xesamTitle + } + // if no track title is given, print out the file name + var xesamUrl = currentMetadata["xesam:url"] ? currentMetadata["xesam:url"].toString() : "" + if (!xesamUrl) { + return "" + } + var lastSlashPos = xesamUrl.lastIndexOf('/') + if (lastSlashPos < 0) { + return "" + } + var lastUrlPart = xesamUrl.substring(lastSlashPos + 1) + return decodeURIComponent(lastUrlPart) + } + readonly property string artist: currentMetadata["xesam:artist"] || "" + readonly property string albumArt: currentMetadata["mpris:artUrl"] || "" + + function goPrevious() { + startOperation("Previous") + } + + function goNext() { + startOperation("Next") + } + + function playPause() { + startOperation("PlayPause") + } + + function raise() { + startOperation("Raise") + } + + function startOperation(op) { + var service = mpris2Source.serviceForSource(current) + var operation = service.operationDescription(op) + return service.startOperationCall(operation) + } + + engine: "mpris2" + connectedSources: sources + } + + Column{ + spacing: _s + + Item { + id: thumbnailContainer + width: Math.max(tooltipContentItem.width, windowRow.width) + height: albumArtImage.available ? albumArtImage.height : + raisePlayerArea.visible ? raisePlayerArea.height : + windowRow.height + + Item { + id: thumbnailSourceItem + anchors.fill: parent + + PlasmaExtras.ScrollArea { + id: scrollArea + anchors.horizontalCenter: parent.horizontalCenter + width: Math.max(windowRow.width, thumbnailWidth) + height: parent.height + + visible: !albumArtImage.available + + verticalScrollBarPolicy: Qt.ScrollBarAlwaysOff + horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff + + Component.onCompleted: { + flickableItem.interactive = Qt.binding(function() { + return contentItem.width > viewport.width; + }); + } + + Row { + id: windowRow + width: childrenRect.width + height: childrenRect.height + spacing: units.largeSpacing + + Repeater { + model: (plasmoid.configuration.showToolTips && !albumArtImage.available) + || !windowSystem.compositingActive ? windows : null + + Column{ + PlasmaCore.WindowThumbnail { + id: windowThumbnail + + width: thumbnailWidth + height: thumbnailHeight + + winId: modelData + + ToolTipWindowMouseArea { + anchors.fill: parent + modelIndex: tasksModel.makeModelIndex(parentIndex, group ? index : -1) + winId: modelData + thumbnailItem: parent + } + } + + PlasmaComponents.Label{ + text: titles && titles[index] ? titles[index] : "" + wrapMode: Text.Wrap + font.italic: true + elide: Text.ElideRight + opacity: 0.7 + textFormat: Text.PlainText + verticalAlignment: Text.AlignVCenter + + width: thumbnailWidth + } + } + + } + + function containsMouse(){ + for(var i=0; i<children.length-1; ++i){ + if(children[i].children[0].containsMouse) + return true; + } + + return false; + } + } + } + + Image { + id: albumArtImage + // also Image.Loading to prevent loading thumbnails just because the album art takes a split second to load + readonly property bool available: status === Image.Ready || status === Image.Loading + + anchors.centerIn: parent + width: parent.width + height: thumbnailHeight + sourceSize: Qt.size(thumbnailWidth, thumbnailHeight) + asynchronous: true + source: mpris2Source.albumArt + fillMode: Image.PreserveAspectCrop + visible: available + + ToolTipWindowMouseArea { + id: albumMouseArea + + anchors.fill: parent + modelIndex: tasksModel.makeModelIndex(parentIndex)//, group ? (index ? index : -1) : -1) + winId: windows != undefined ? (windows[0] || 0) : 0 + } + } + + MouseArea { + id: raisePlayerArea + anchors.centerIn: parent + width: thumbnailWidth + height: thumbnailHeight + + // if there's no window associated with this task, we might still be able to raise the player + visible: windows == undefined || !windows[0] && mpris2Source.canRaise + onClicked: mpris2Source.raise() + + PlasmaCore.IconItem { + anchors.fill: parent + source: icon + animated: false + usesPlasmaTheme: false + visible: !albumArtImage.available + } + } + } + + Item { + id: playerControlsShadowMask + anchors.fill: thumbnailSourceItem + visible: false // OpacityMask would render it + + Rectangle { + width: parent.width + height: parent.height - playerControlsRow.height + } + + Rectangle { + anchors.bottom: parent.bottom + width: parent.width + height: playerControlsRow.height + opacity: 0.2 + } + } + + OpacityMask { + id: playerControlsOpacityMask + anchors.fill: thumbnailSourceItem + visible: false + } + + // prevent accidental click-through when a control is disabled + MouseArea { + anchors.fill: playerControlsRow + enabled: playerControlsRow.visible + } + + RowLayout { + id: playerControlsRow + anchors { + horizontalCenter: parent.horizontalCenter + bottom: thumbnailSourceItem.bottom + } + width: thumbnailWidth + spacing: 0 + enabled: mpris2Source.canControl + visible: false + + ColumnLayout { + Layout.fillWidth: true + spacing: 0 + + PlasmaExtras.Heading { + Layout.fillWidth: true + level: 4 + wrapMode: Text.NoWrap + elide: Text.ElideRight + text: mpris2Source.track || "" + } + + PlasmaExtras.Heading { + Layout.fillWidth: true + level: 5 + wrapMode: Text.NoWrap + elide: Text.ElideRight + text: mpris2Source.artist || "" + } + } + + PlasmaComponents.ToolButton { + enabled: mpris2Source.canGoBack + iconName: "media-skip-backward" + tooltip: i18nc("Go to previous song", "Previous") + Accessible.name: tooltip + onClicked: mpris2Source.goPrevious() + } + + PlasmaComponents.ToolButton { + Layout.fillHeight: true + Layout.preferredWidth: height // make this button bigger + iconName: mpris2Source.playing ? "media-playback-pause" : "media-playback-start" + tooltip: mpris2Source.playing ? i18nc("Pause player", "Pause") : i18nc("Start player", "Play") + Accessible.name: tooltip + onClicked: mpris2Source.playPause() + } + + PlasmaComponents.ToolButton { + enabled: mpris2Source.canGoNext + iconName: "media-skip-forward" + tooltip: i18nc("Go to next song", "Next") + Accessible.name: tooltip + onClicked: mpris2Source.goNext() + } + } + } + + Row { + id: appLabelRow + width: childrenRect.width + _s + height: childrenRect.height + units.largeSpacing + spacing: units.largeSpacing + + Item { + id: imageContainer + width: tooltipIcon.width + height: tooltipIcon.height + y: _s + + PlasmaCore.IconItem { + id: tooltipIcon + x: _s + width: units.iconSizes.desktop + height: width + animated: false + usesPlasmaTheme: false + source: icon + } + } + + Column { + id: mainColumn + y: _s + + //This instance is purely for metrics + PlasmaExtras.Heading { + id: tooltipMaintextPlaceholder + visible: false + level: 3 + text: mainText + textFormat: Text.PlainText + } + PlasmaExtras.Heading { + id: tooltipMaintext + level: 3 + width: Math.min(tooltipMaintextPlaceholder.width, preferredTextWidth) + //width: 400 + elide: Text.ElideRight + text: mainText + textFormat: Text.PlainText + } + PlasmaComponents.Label { + id: tooltipSubtext + width: tooltipContentItem.preferredTextWidth + height: Math.min(theme.mSize(theme.defaultFont), contentHeight) + wrapMode: Text.WordWrap + text: subText + textFormat: Text.PlainText + opacity: 0.5 + visible: text !== "" + } + } + } + + } +} diff --git a/plasmoid/contents/ui/ToolTipWindowMouseArea.qml b/plasmoid/contents/ui/ToolTipWindowMouseArea.qml new file mode 100644 index 000000000..8d4f76fe1 --- /dev/null +++ b/plasmoid/contents/ui/ToolTipWindowMouseArea.qml @@ -0,0 +1,61 @@ +/* +* Copyright 2013 by Sebastian Kügler <sebas@kde.org> +* Copyright 2014 by Martin Gräßlin <mgraesslin@kde.org> +* Copyright 2016 by Kai Uwe Broulik <kde@privat.broulik.de> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU Library General Public License as +* published by the Free Software Foundation; either version 2, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Library General Public License for more details +* +* You should have received a copy of the GNU Library General Public +* License along with this program; if not, write to the +* Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. +*/ + +import QtQuick 2.0 + +import org.kde.plasma.components 2.0 as PlasmaComponents + +MouseArea { + property var modelIndex + property int winId // FIXME Legacy + property Item thumbnailItem + + acceptedButtons: Qt.LeftButton + hoverEnabled: true + enabled: winId != 0 + + onClicked: { + tasksModel.requestActivate(modelIndex); + windowsPreviewDlg.hide(); + //toolTip.hideToolTip(); + } + + onContainsMouseChanged: { + tooltipContentItem.checkMouseInside(); + + panel.windowsHovered([winId], containsMouse); + } + + PlasmaComponents.ToolButton { + anchors { + top: parent.top + topMargin: thumbnailItem ? (thumbnailItem.height - thumbnailItem.paintedHeight) / 2 : 0 + right: parent.right + rightMargin: thumbnailItem ? (thumbnailItem.width - thumbnailItem.paintedWidth) / 2 : 0 + } + + iconSource: "window-close" + visible: parent.containsMouse && winId != 0 + tooltip: i18nc("close this window", "Close") + + onClicked: tasksModel.requestClose(modelIndex); + } +} diff --git a/plasmoid/contents/ui/VisualAddItem.qml b/plasmoid/contents/ui/VisualAddItem.qml new file mode 100644 index 000000000..9b702d51d --- /dev/null +++ b/plasmoid/contents/ui/VisualAddItem.qml @@ -0,0 +1,44 @@ +/* + * Copyright 2013 Michail Vourlakos <mvourlakos@gmail.com> + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.0 + +import org.kde.plasma.core 2.0 as PlasmaCore + +Item{ + Rectangle{ + anchors.fill: parent + + anchors.bottom: (panel.position === PlasmaCore.Types.TopPositioned) ? parent.bottom : undefined + anchors.top: (panel.position === PlasmaCore.Types.BottomPositioned) ? parent.top : undefined + anchors.left: (panel.position === PlasmaCore.Types.RightPositioned) ? parent.left : undefined + anchors.right: (panel.position === PlasmaCore.Types.LeftPositioned) ? parent.right : undefined + + radius: panel.iconSize/10 + + property color tempColor: "#aa222222" + color: tempColor + border.width: 1 + border.color: "#ff656565" + + property int crossSize: Math.min(parent.width/2, parent.height/2) + + Rectangle{width: parent.crossSize; height: 4; anchors.centerIn: parent; color: theme.highlightColor} + Rectangle{width: 4; height: parent.crossSize; anchors.centerIn: parent; color: theme.highlightColor} + } +} diff --git a/plasmoid/contents/ui/main.qml b/plasmoid/contents/ui/main.qml new file mode 100644 index 000000000..fcdebe97a --- /dev/null +++ b/plasmoid/contents/ui/main.qml @@ -0,0 +1,1158 @@ +/* + * Copyright 2013 Michail Vourlakos <mvourlakos@gmail.com> + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.0 +import QtQuick.Layouts 1.1 +import QtGraphicalEffects 1.0 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.plasmoid 2.0 + +import org.kde.taskmanager 0.1 as TaskManager +import org.kde.plasma.private.taskmanager 0.1 as TaskManagerApplet + +import org.kde.activities 0.1 as Activities + +import org.kde.nowdock 0.1 as NowDock + +import "../code/tools.js" as TaskTools +import "../code/activitiesTools.js" as ActivitiesTools + +Item { + id:panel + + Layout.fillHeight: userPanelPosition === 0 ? true : false + Layout.fillWidth: userPanelPosition === 0 ? true : false + + ///IMPORTANT: These values must be tested when the Now Dock Panel support + ///also the four new anchors. A small issue is shown between the animation + /// of the now dock plasmoid and the neighbour widgets... + Layout.minimumWidth: (userPanelPosition !== 0)&&(!nowDockPanel) ? clearWidth : -1 + Layout.minimumHeight: (userPanelPosition !== 0)&&(!nowDockPanel) ? clearHeight : -1 + Layout.preferredWidth: (userPanelPosition !== 0)&&(!nowDockPanel) ? tasksWidth : -1 + Layout.preferredHeight: (userPanelPosition !== 0)&&(!nowDockPanel) ? tasksHeight : -1 + + property bool debugLocation: false + + property bool disableRestoreZoom: false //blocks restore animation in rightClick + property bool dropNewLauncher: false + property bool enableShadows: plasmoid.configuration.showShadows + property bool glow: plasmoid.configuration.showGlow + property bool initializationStep: true + property bool initializatedBuffers: noInitCreatedBuffers >= tasksStarting ? true : false + property bool isHovered: false + property bool showBarLine: plasmoid.configuration.showBarLine + property bool showPreviews: plasmoid.configuration.showToolTips + property bool useThemePanel: plasmoid.configuration.useThemePanel + property bool taskInAnimation: noTasksInAnimation > 0 ? true : false + property bool transparentPanel: plasmoid.configuration.transparentPanel + property bool vertical: ((panel.position === PlasmaCore.Types.LeftPositioned) || + (panel.position === PlasmaCore.Types.RightPositioned)) ? true : false + + property int animationsNeedBothAxis:0 //animations need space in both axes, e.g zooming a task + property int animationsNeedLength: 0 // animations need length, e.g. adding a task + property int animationsNeedThickness: 0 // animations need thickness, e.g. bouncing animation + property int clearWidth + property int clearHeight + + //property int iconMargin: 5 + property int iconMargin: 0.12*iconSize + + property int newLocationDebugUse: PlasmaCore.Types.BottomPositioned + property int newDroppedPosition: -1 + property int noInitCreatedBuffers: 0 + property int noTasksInAnimation: 0 + property int themePanelSize: plasmoid.configuration.panelSize + + property int position : PlasmaCore.Types.BottomPositioned + property int tasksStarting: 0 + property int realSize: iconSize + iconMargin + property int statesLineSize: Math.ceil( panel.iconSize/13 ) + + property real textColorLuma: 0.2126*theme.textColor.r + 0.7152*theme.textColor.g + 0.0722*theme.textColor.b + + property variant launchersOnActivities: [] + + property QtObject contextMenuComponent: Qt.createComponent("ContextMenu.qml"); + property Item dragSource: null + + property color minimizedDotColor: textColorLuma > 0.5 ? Qt.darker(theme.textColor, 1+ (1-textColorLuma)) : Qt.lighter(theme.textColor, 1+(1-textColorLuma)) + + //BEGIN Now Dock Panel properties + property bool forceHidePanel: false + property bool disableLeftSpacer: false + property bool disableRightSpacer: false + property bool reverseLinesPosition: plasmoid.configuration.reverseLinesPosition + + property int durationTime: plasmoid.configuration.durationTime + property int iconSize: nowDockPanel ? nowDockPanel.iconSize : Math.max(plasmoid.configuration.iconSize, 16) + property int tasksHeight: mouseHandler.height + property int tasksWidth: mouseHandler.width + property int userPanelPosition: nowDockPanel ? nowDockPanel.panelAlignment : plasmoid.configuration.plasmoidPosition + + property real zoomFactor: nowDockPanel ? nowDockPanel.zoomFactor : ( 1 + (plasmoid.configuration.zoomLevel / 20) ) + + property alias tasksCount: tasksModel.count + property alias hoveredIndex: icList.hoveredIndex + + property Item nowDockPanel: null + //END Now Dock Panel properties + + + Plasmoid.preferredRepresentation: Plasmoid.fullRepresentation + Plasmoid.backgroundHints: PlasmaCore.Types.NoBackground + + signal clearZoomSignal(); + signal draggingFinished(); + signal mouseWasEntered(int delegateIndex, bool value); + signal presentWindows(variant winIds); + signal requestLayout; + signal signalAnimationsNeedBothAxis(int value); + signal signalAnimationsNeedLength(int value); + signal signalAnimationsNeedThickness(int value); + signal signalDraggingState(bool value); + //trigger updating scaling of neighbour delegates of zoomed delegate + signal updateScale(int delegateIndex, real newScale, real step) + signal publishTasksGeometries(); + signal windowsHovered(variant winIds, bool hovered) + + //onAnimationsChanged: console.log(animations); + /* Rectangle{ + anchors.fill: parent + border.width: 1 + border.color: "red" + color: "white" + } */ + onNowDockPanelChanged: { + if (nowDockPanel) + plasmoid.configuration.isInNowDockPanel = true; + else + plasmoid.configuration.isInNowDockPanel = false; + } + + + Connections { + target: plasmoid + onLocationChanged: { + panel.updatePosition(); + iconGeometryTimer.start(); + } + } + + Connections { + target: plasmoid.configuration + + // onLaunchersChanged: tasksModel.launcherList = plasmoid.configuration.launchers + onGroupingAppIdBlacklistChanged: tasksModel.groupingAppIdBlacklist = plasmoid.configuration.groupingAppIdBlacklist; + onGroupingLauncherUrlBlacklistChanged: tasksModel.groupingLauncherUrlBlacklist = plasmoid.configuration.groupingLauncherUrlBlacklist; + } + + function setAnimationsNeedBothAxis(value) { + if (value === animationsNeedBothAxis) { + return; + } + + animationsNeedBothAxis = value; + signalAnimationsNeedBothAxis(animationsNeedBothAxis); + } + + function setAnimationsNeedLength(value) { + if (value === animationsNeedLength) { + return; + } + animationsNeedLength = value; + signalAnimationsNeedLength(animationsNeedLength); + } + + function setAnimationsNeedThickness(value) { + if (value === animationsNeedThickness) { + return; + } + animationsNeedThickness = value; + signalAnimationsNeedThickness(animationsNeedThickness); + } + + ///// + PlasmaCore.ColorScope{ + id: colorScopePalette + } + + ///// + + onDragSourceChanged: { + if (dragSource == null) { + panel.draggingFinished(); + panel.signalDraggingState(false); + + tasksModel.syncLaunchers(); + } else { + panel.signalDraggingState(true); + } + } + + /////Winwow previews/////////// + + ToolTipDelegate { + id: toolTipDelegate + visible: false + + property int currentItem: -1 + } + + // FIXME: at some point this must be dropped with NowDock plugin + ////BEGIN interfaces + NowDock.WindowSystem{ + id:windowSystem + } + + PlasmaCore.Dialog{ + id: windowsPreviewDlg + // hideOnWindowDeactivate: false + + type: PlasmaCore.Dialog.Tooltip + // flags: Qt.WindowStaysOnTopHint + location: plasmoid.location + + visible: false + + mainItem: toolTipDelegate + + property Item activeItem: null + + function hide(){ + visible = false; + //activeItem = null; + initializePreviewComponent.createObject(windowsPreviewDlg); + } + + function show(){ + var tasks = icList.contentItem.children; + + for(var i=0; i<tasks.length; ++i){ + var task = tasks[i]; + + if(task && task.isActive){ + activeItem = task; + break; + } + } + + visible = true; + } + } + + //A Timer to delay the initialization of the active item in order + //to not break then active item animation + Component { + id: initializePreviewComponent + Timer { + id: initializePreviewTimer + interval: 300 + repeat: false + + onTriggered: { + windowsPreviewDlg.activeItem = null; + initializePreviewTimer.destroy(); + } + + Component.onCompleted: initializePreviewTimer.start() + } + } + + + + /////Window Previews///////// + + TaskManager.TasksModel { + id: tasksModel + + virtualDesktop: virtualDesktopInfo.currentDesktop + screenGeometry: plasmoid.screenGeometry + // comment in order to support LTS Plasma 5.8 + // screen: plasmoid.screen + activity: activityInfo.currentActivity + + filterByVirtualDesktop: plasmoid.configuration.showOnlyCurrentDesktop + filterByScreen: plasmoid.configuration.showOnlyCurrentScreen + filterByActivity: plasmoid.configuration.showOnlyCurrentActivity + + launchInPlace: true + separateLaunchers: false + groupInline: false + + groupMode: TaskManager.TasksModel.GroupApplications + sortMode: TaskManager.TasksModel.SortManual + + onActivityChanged: { + ActivitiesTools.currentActivity = activity; + // console.log("Updated :"+activity); + + launcherList = ActivitiesTools.restoreLaunchers(); + //panel.updateImplicits(); + //panelGeometryTimer.start(); + } + + // onCountChanged: { + // updateImplicits(); + // } + + onLauncherListChanged: { + // plasmoid.configuration.launchers = launcherList; + ActivitiesTools.updateLaunchers(launcherList); + } + + onGroupingAppIdBlacklistChanged: { + plasmoid.configuration.groupingAppIdBlacklist = groupingAppIdBlacklist; + } + + onGroupingLauncherUrlBlacklistChanged: { + plasmoid.configuration.groupingLauncherUrlBlacklist = groupingLauncherUrlBlacklist; + } + + onAnyTaskDemandsAttentionChanged: { + if (anyTaskDemandsAttention){ + plasmoid.status = PlasmaCore.Types.RequiresAttentionStatus; + attentionTimerComponent.createObject(panel); + } + } + + + Component.onCompleted: { + ActivitiesTools.launchersOnActivities = panel.launchersOnActivities + ActivitiesTools.currentActivity = activityInfo.currentActivity; + ActivitiesTools.plasmoid = plasmoid; + + launcherList = ActivitiesTools.restoreLaunchers(); + groupingAppIdBlacklist = plasmoid.configuration.groupingAppIdBlacklist; + groupingLauncherUrlBlacklist = plasmoid.configuration.groupingLauncherUrlBlacklist; + + icList.model = tasksModel; + tasksStarting = count; + } + } + + TaskManagerApplet.Backend { + id: backend + + taskManagerItem: panel + toolTipItem: toolTipDelegate + highlightWindows: plasmoid.configuration.highlightWindows + + onAddLauncher: { + tasksModel.requestAddLauncher(url); + // tasksModel.move(pos, newDroppedPosition); + } + } + + TaskManagerApplet.DragHelper { + id: dragHelper + + dragIconSize: units.iconSizes.medium + } + + TaskManager.VirtualDesktopInfo { + id: virtualDesktopInfo + } + + TaskManager.ActivityInfo { + id: activityInfo + } + + PlasmaCore.DataSource { + id: mpris2Source + engine: "mpris2" + connectedSources: sources + + function sourceNameForLauncherUrl(launcherUrl) { + if (!launcherUrl) { + return ""; + } + + // MPRIS spec explicitly mentions that "DesktopEntry" is with .desktop extension trimmed + // Moreover, remove URL parameters, like wmClass (part after the question mark) + var desktopFileName = launcherUrl.toString().split('/').pop().split('?')[0].replace(".desktop", "") + + for (var i = 0, length = sources.length; i < length; ++i) { + var source = sources[i]; + var sourceData = data[source]; + + if (sourceData && sourceData.DesktopEntry === desktopFileName) { + return source + } + } + + return "" + } + + function startOperation(source, op) { + var service = serviceForSource(source) + var operation = service.operationDescription(op) + return service.startOperationCall(operation) + } + + function goPrevious(source) { + startOperation(source, "Previous"); + } + function goNext(source) { + startOperation(source, "Next"); + } + function playPause(source) { + startOperation(source, "PlayPause"); + } + function stop(source) { + startOperation(source, "Stop"); + } + function raise(source) { + startOperation(source, "Raise"); + } + function quit(source) { + startOperation(source, "Quit"); + } + } + + + /* IconsModel{ + id: iconsmdl + }*/ + + Component{ + id: attentionTimerComponent + Timer{ + id: attentionTimer + interval:6500 + onTriggered: { + plasmoid.status = PlasmaCore.Types.PassiveStatus; + destroy(); + } + Component.onCompleted: { + start(); + } + } + } + + //Timer to check if the mouse is still inside the ListView + Timer{ + id:checkListHovered + repeat:false; + interval: 120; + + onTriggered: { + if (!panel.containsMouse()) + panel.clearZoom(); + + interval = 120; + } + + function startDuration( duration){ + interval = duration; + + start(); + } + } + + + ///Red Liner!!! show the upper needed limit for annimations + Rectangle{ + anchors.horizontalCenter: !panel.vertical ? parent.horizontalCenter : undefined + anchors.verticalCenter: panel.vertical ? parent.verticalCenter : undefined + + width: panel.vertical ? 1 : 2 * panel.iconSize + height: panel.vertical ? 2 * panel.iconSize : 1 + color: "red" + x: (panel.position === PlasmaCore.Types.LeftPositioned) ? neededSpace : parent.width - neededSpace + y: (panel.position === PlasmaCore.Types.TopPositioned) ? neededSpace : parent.height - neededSpace + + visible: plasmoid.configuration.zoomHelper + + property int neededSpace: zoomFactor*(iconSize+iconMargin) + statesLineSize + } + + Item{ + id:barLine + + opacity: (tasksModel.count > 0) && panel.initializatedBuffers ? 1 : 0 + + /* anchors.bottom: (panel.position === PlasmaCore.Types.BottomPositioned) ? parent.bottom : undefined + anchors.top: (panel.position === PlasmaCore.Types.TopPositioned) ? parent.top : undefined + anchors.left: (panel.position === PlasmaCore.Types.LeftPositioned) ? parent.left : undefined + anchors.right: (panel.position === PlasmaCore.Types.RightPositioned) ? parent.right : undefined + + anchors.horizontalCenter: !parent.vertical ? parent.horizontalCenter : undefined + anchors.verticalCenter: parent.vertical ? parent.verticalCenter : undefined */ + + width: ( icList.orientation === Qt.Horizontal ) ? icList.width + spacing : smallSize + height: ( icList.orientation === Qt.Vertical ) ? icList.height + spacing : smallSize + + property int spacing: panel.iconSize / 2 + property int smallSize: Math.max(3.7*panel.statesLineSize, 16) + + Behavior on opacity{ + NumberAnimation { duration: plasmoid.configuration.durationTime*units.longDuration } + } + + /// plasmoid's default panel + BorderImage{ + anchors.fill:parent + source: "../images/panel-west.png" + border { left:8; right:8; top:8; bottom:8 } + + opacity: (plasmoid.configuration.showBarLine && !plasmoid.configuration.useThemePanel && !panel.forceHidePanel) ? 1 : 0 + + visible: (opacity == 0) ? false : true + + horizontalTileMode: BorderImage.Stretch + verticalTileMode: BorderImage.Stretch + + Behavior on opacity{ + NumberAnimation { duration: plasmoid.configuration.durationTime*units.longDuration } + } + } + + + /// item which is used as anchors for the plasma's theme + Item{ + id:belower + + width: (panel.position === PlasmaCore.Types.LeftPositioned) ? shadowsSvgItem.margins.left : shadowsSvgItem.margins.right + height: (panel.position === PlasmaCore.Types.BottomPositioned)? shadowsSvgItem.margins.bottom : shadowsSvgItem.margins.top + + anchors.top: (panel.position === PlasmaCore.Types.BottomPositioned) ? parent.bottom : undefined + anchors.bottom: (panel.position === PlasmaCore.Types.TopPositioned) ? parent.top : undefined + anchors.right: (panel.position === PlasmaCore.Types.LeftPositioned) ? parent.left : undefined + anchors.left: (panel.position === PlasmaCore.Types.RightPositioned) ? parent.right : undefined + } + + + /// the current theme's panel + PlasmaCore.FrameSvgItem{ + id: shadowsSvgItem + + anchors.bottom: (panel.position === PlasmaCore.Types.BottomPositioned) ? belower.bottom : undefined + anchors.top: (panel.position === PlasmaCore.Types.TopPositioned) ? belower.top : undefined + anchors.left: (panel.position === PlasmaCore.Types.LeftPositioned) ? belower.left : undefined + anchors.right: (panel.position === PlasmaCore.Types.RightPositioned) ? belower.right : undefined + + anchors.horizontalCenter: !panel.vertical ? parent.horizontalCenter : undefined + anchors.verticalCenter: panel.vertical ? parent.verticalCenter : undefined + + width: panel.vertical ? panelSize + margins.left + margins.right: parent.width + height: panel.vertical ? parent.height : panelSize + margins.top + margins.bottom + + imagePath: "translucent/widgets/panel-background" + prefix:"shadow" + + opacity: (plasmoid.configuration.showBarLine && plasmoid.configuration.useThemePanel && !panel.forceHidePanel) ? 1 : 0 + visible: (opacity == 0) ? false : true + + property int panelSize: ((panel.position === PlasmaCore.Types.BottomPositioned) || + (panel.position === PlasmaCore.Types.TopPositioned)) ? + plasmoid.configuration.panelSize + belower.height: + plasmoid.configuration.panelSize + belower.width + + Behavior on opacity{ + NumberAnimation { duration: plasmoid.configuration.durationTime*units.longDuration } + } + + + PlasmaCore.FrameSvgItem{ + anchors.margins: belower.width-1 + anchors.fill:parent + imagePath: plasmoid.configuration.transparentPanel ? "translucent/widgets/panel-background" : + "widgets/panel-background" + } + } + + + MouseHandler { + id: mouseHandler + anchors.bottom: (panel.position === PlasmaCore.Types.BottomPositioned) ? icList.bottom : undefined + anchors.top: (panel.position === PlasmaCore.Types.TopPositioned) ? icList.top : undefined + anchors.left: (panel.position === PlasmaCore.Types.LeftPositioned) ? icList.left : undefined + anchors.right: (panel.position === PlasmaCore.Types.RightPositioned) ? icList.right : undefined + + anchors.horizontalCenter: !panel.vertical ? icList.horizontalCenter : undefined + anchors.verticalCenter: panel.vertical ? icList.verticalCenter : undefined + + width: panel.vertical ? maxSize : icList.width + height: panel.vertical ? icList.height : maxSize + + target: icList + + property int maxSize: panel.statesLineSize + panel.iconSize + panel.iconMargin - 1 + + onUrlsDropped: { + // If all dropped URLs point to application desktop files, we'll add a launcher for each of them. + var createLaunchers = urls.every(function (item) { + return backend.isApplication(item) + }); + + if (createLaunchers) { + urls.forEach(function (item) { + addLauncher(item); + }); + return; + } + + if (!hoveredItem) { + return; + } + + // DeclarativeMimeData urls is a QJsonArray but requestOpenUrls expects a proper QList<QUrl>. + var urlsList = backend.jsonArrayToUrlList(urls); + + // Otherwise we'll just start a new instance of the application with the URLs as argument, + // as you probably don't expect some of your files to open in the app and others to spawn launchers. + tasksModel.requestOpenUrls(hoveredItem.modelIndex(), urlsList); + } + } + + + ListView { + id:icList + + property int currentSpot : -1000 + property int hoveredIndex : -1 + property int previousCount : 0 + + property int tasksCount: contentItem.children.length + + property bool delayingRemoval: false + + onTasksCountChanged: updateImplicits(); + + // property int count: children ? children.length : 0 + /* anchors.bottom: (panel.position === PlasmaCore.Types.BottomPositioned) ? parent.bottom : undefined + anchors.top: (panel.position === PlasmaCore.Types.TopPositioned) ? parent.top : undefined + anchors.left: (panel.position === PlasmaCore.Types.LeftPositioned) ? parent.left : undefined + anchors.right: (panel.position === PlasmaCore.Types.RightPositioned) ? parent.right : undefined + + anchors.horizontalCenter: !panel.vertical ? parent.horizontalCenter : undefined + anchors.verticalCenter: panel.vertical ? parent.verticalCenter : undefined */ + + width: contentWidth + height: contentHeight + + orientation: Qt.Horizontal + + delegate: TaskDelegate{} + + /*Rectangle{ + anchors.fill: parent + border.width: 1 + border.color: "red" + color: "transparent" + }*/ + + //the duration of this animation should be as small as possible + //it fixes a small issue with the dragging an item to change it's + //position, if the duration is too big there is a point in the + //list that an item is going back and forth too fast + + //more of a trouble + moveDisplaced: Transition { + NumberAnimation { properties: "x,y"; duration: plasmoid.configuration.durationTime*units.shortDuration; easing.type: Easing.Linear } + } + + ///this transition can not be used with dragging !!!! I breaks + ///the lists indexes !!!!! + /* move: Transition { + NumberAnimation { properties: "x,y"; duration: units.longDuration; easing.type: Easing.Linear } + } */ + + function childAtPos(x, y){ + var tasks = icList.contentItem.children; + + for(var i=0; i<tasks.length; ++i){ + var task = tasks[i]; + + var choords = mapFromItem(task,0, 0); + + if(choords.y < 0) + choords.y = 0; + if(choords.x < 0) + choords.x = 0; + + if( (x>=choords.x) && (x<=choords.x+task.width) + && (y>=choords.y) && (y<=choords.y+task.height)){ + return task + } + } + + return null; + } + } + + VisualAddItem{ + id: newDroppedLauncherVisual + anchors.fill: mouseHandler + + visible: opacity == 0 ? false : true + opacity: panel.dropNewLauncher && mouseHandler.onlyLaunchers && (panel.dragSource == null)? 1 : 0 + } + } + + //// helpers + + Timer { + id: iconGeometryTimer + // INVESTIGATE: such big interval but unfortunately it doesnot work otherwise + interval: 500 + repeat: false + + onTriggered: { + // console.debug("Found children: "+icList.contentItem.children.length); + TaskTools.publishIconGeometries(icList.contentItem.children); + } + } + + ////Activities List + ////it can be used to cleanup the launchers from garbage-deleted activities.... + Item{ + id: activityModelInstance + property int count: activityModelRepeater.count + + Repeater { + id:activityModelRepeater + model: Activities.ActivityModel { + id: activityModel + // shownStates: "Running" + } + delegate: Item { + visible: false + property string activityId: model.id + property string activityName: model.name + } + } + + function activities(){ + var activitiesResult = []; + + for(var i=0; i<activityModelInstance.count; ++i){ + console.log(children[i].activityId); + activitiesResult.push(children[i].activityId); + } + + return activitiesResult; + } + + onCountChanged: { + if(activityInfo.currentActivity != "00000000-0000-0000-0000-000000000000"){ + console.log("----------- Now Dock Signal: Activities number was changed ---------"); + var allActivities = activities(); + ActivitiesTools.cleanupRecords(allActivities); + console.log("----------- Now Dock Signal End ---------"); + } + } + } + + ///////// + + //// functions + function movePanel(obj, newPosition){ + var bLine = obj; + if (newPosition === PlasmaCore.Types.BottomPositioned){ + bLine.anchors.horizontalCenter = bLine.parent.horizontalCenter; + bLine.anchors.verticalCenter = undefined; + bLine.anchors.bottom = bLine.parent.bottom; + bLine.anchors.top = undefined; + bLine.anchors.left = undefined; + bLine.anchors.right = undefined; + } + else if (newPosition === PlasmaCore.Types.TopPositioned){ + bLine.anchors.horizontalCenter = bLine.parent.horizontalCenter; + bLine.anchors.verticalCenter = undefined; + bLine.anchors.bottom = undefined; + bLine.anchors.top = bLine.parent.top; + bLine.anchors.left = undefined; + bLine.anchors.right = undefined; + } + else if (newPosition === PlasmaCore.Types.LeftPositioned){ + bLine.anchors.horizontalCenter = undefined; + bLine.anchors.verticalCenter = bLine.parent.verticalCenter; + bLine.anchors.bottom = undefined; + bLine.anchors.top = undefined; + bLine.anchors.left = bLine.parent.left; + bLine.anchors.right = undefined; + } + else if (newPosition === PlasmaCore.Types.RightPositioned){ + bLine.anchors.horizontalCenter = undefined; + bLine.anchors.verticalCenter = bLine.parent.verticalCenter; + bLine.anchors.bottom = undefined; + bLine.anchors.top = undefined; + bLine.anchors.left =undefined; + bLine.anchors.right = bLine.parent.right; + } + } + + property int ncounter:0 + + function updateImplicits(){ + if(icList.previousCount !== icList.count){ + icList.previousCount = icList.count; + + var zoomedLength = Math.floor( 1.2 * (iconSize+iconMargin) * (panel.zoomFactor)); + var bigAxis = (tasksModel.count-1) * (iconSize+iconMargin) + zoomedLength; + var smallAxis = zoomedLength + statesLineSize; + + var clearBigAxis = tasksModel.count * (iconSize+iconMargin) + (barLine.spacing/2); + var clearSmallAxis = (iconSize+iconMargin)+statesLineSize; + + // debugging code + // ncounter++; + // console.log("Implicits______ "+ncounter+". - "+tasksModel.count); + + if (panel.vertical){ + panel.implicitWidth = smallAxis; + panel.implicitHeight = bigAxis; + panel.clearWidth = clearSmallAxis; + panel.clearHeight = clearBigAxis; + } + else{ + panel.implicitWidth = bigAxis; + panel.implicitHeight = smallAxis; + panel.clearWidth = clearBigAxis; + panel.clearHeight = clearSmallAxis; + } + + iconGeometryTimer.restart(); + } + } + + PlasmaComponents.Button{ + id: orientationBtn + text:"Orientation" + + anchors.centerIn: parent + visible: panel.debugLocation + + onClicked:{ + switch(panel.position){ + case PlasmaCore.Types.BottomPositioned: + panel.newLocationDebugUse = PlasmaCore.Types.LeftEdge; + break; + case PlasmaCore.Types.LeftPositioned: + panel.newLocationDebugUse = PlasmaCore.Types.TopEdge; + break; + case PlasmaCore.Types.TopPositioned: + panel.newLocationDebugUse = PlasmaCore.Types.RightEdge; + break; + case PlasmaCore.Types.RightPositioned: + panel.newLocationDebugUse = PlasmaCore.Types.BottomEdge; + break; + } + updatePosition(); + } + } + + + function updatePosition(){ + var newPosition; + var tempVertical=false; + + var positionUsed; + + + if (panel.debugLocation) + positionUsed = panel.newLocationDebugUse; + else + positionUsed = plasmoid.location; + + switch (positionUsed) { + case PlasmaCore.Types.LeftEdge: + newPosition = PlasmaCore.Types.LeftPositioned; + tempVertical = true; + break; + case PlasmaCore.Types.RightEdge: + newPosition = PlasmaCore.Types.RightPositioned; + tempVertical = true; + break; + case PlasmaCore.Types.TopEdge: + newPosition = PlasmaCore.Types.TopPositioned; + break; + default: + newPosition = PlasmaCore.Types.BottomPositioned; + break + } + + movePanel(barLine,newPosition); + movePanel(icList,newPosition); + + if(tempVertical) + icList.orientation = Qt.Vertical; + else + icList.orientation = Qt.Horizontal; + + panel.position = newPosition; + } + + function outsideContainsMouse(){ + if (disableRestoreZoom) { + return true; + } + + var tasks = icList.contentItem.children; + + if(toolTipDelegate.currentItem != -1) + return true; + + for(var i=0; i<tasks.length; ++i){ + var task = tasks[i]; + + //l console.log("Checking "+i+" - "+task.index+" - "+task.containsMouse); + if(task && task.containsMouse){ + return true; + } + } + + return false; + } + + function containsMouse(){ + if (disableRestoreZoom) { + return; + } + + var result = panel.outsideContainsMouse(); + + if ((!result || toolTipDelegate.parentIndex != icList.hoveredIndex) && windowSystem.compositingActive) { + windowsPreviewDlg.hide(); + } + + if (result) + return true; + + if (!result && nowDockPanel && nowDockPanel.outsideContainsMouse()) + return true; + + if (nowDockPanel) + nowDockPanel.clearZoom(); + + return false; + } + + function clearZoom(){ + //console.log("Plasmoid clear..."); + if (disableRestoreZoom) { + return; + } + + icList.currentSpot = -1000; + icList.hoveredIndex = -1; + panel.clearZoomSignal(); + } + + function hasLauncher(url) { + return tasksModel.launcherPosition(url) != -1; + } + + function addLauncher(url) { + tasksModel.requestAddLauncher(url); + } + + function resetDragSource() { + dragSource = null; + } + + function createContextMenu(task) { + var menu = panel.contextMenuComponent.createObject(task); + menu.visualParent = task; + menu.mpris2Source = mpris2Source; + menu.activitiesCount = activityModelInstance.count; + return menu; + } + + Component.onCompleted: { + updatePosition(); + + panel.presentWindows.connect(backend.presentWindows); + panel.windowsHovered.connect(backend.windowsHovered); + // mouseHandler.urlDropped.connect(backend.urlDropped); + dragHelper.dropped.connect(resetDragSource); + } + + //BEGIN states + //user set Panel Positions + // 0-Center, 1-Left, 2-Right, 3-Top, 4-Bottom + states: [ + + ///Bottom Edge + State { + name: "bottomCenter" + when: (panel.position === PlasmaCore.Types.BottomPosition && userPanelPosition===NowDock.Types.Center) + + AnchorChanges { + target: barLine + anchors{ top:undefined; bottom:parent.bottom; left:undefined; right:undefined; horizontalCenter:parent.horizontalCenter; verticalCenter:undefined} + } + AnchorChanges { + target: icList + anchors{ top:undefined; bottom:parent.bottom; left:undefined; right:undefined; horizontalCenter:parent.horizontalCenter; verticalCenter:undefined} + } + }, + State { + name: "bottomLeft" + when: (panel.position === PlasmaCore.Types.BottomPosition && userPanelPosition===NowDock.Types.Left) + + AnchorChanges { + target: barLine + anchors{ top:undefined; bottom:parent.bottom; left:parent.left; right:undefined; horizontalCenter:undefined; verticalCenter:undefined} + } + AnchorChanges { + target: icList + anchors{ top:undefined; bottom:parent.bottom; left:parent.left; right:undefined; horizontalCenter:undefined; verticalCenter:undefined} + } + }, + State { + name: "bottomRight" + when: (panel.position === PlasmaCore.Types.BottomPosition && userPanelPosition===NowDock.Types.Right) + + AnchorChanges { + target: barLine + anchors{ top:undefined; bottom:parent.bottom; left:undefined; right:parent.right; horizontalCenter:undefined; verticalCenter:undefined} + } + AnchorChanges { + target: icList + anchors{ top:undefined; bottom:parent.bottom; left:undefined; right:parent.right; horizontalCenter:undefined; verticalCenter:undefined} + } + }, + ///Top Edge + State { + name: "topCenter" + when: (panel.position === PlasmaCore.Types.TopPosition && userPanelPosition===NowDock.Types.Center) + + AnchorChanges { + target: barLine + anchors{ top:parent.top; bottom:undefined; left:undefined; right:undefined; horizontalCenter:parent.horizontalCenter; verticalCenter:undefined} + } + AnchorChanges { + target: icList + anchors{ top:parent.top; bottom:undefined; left:undefined; right:undefined; horizontalCenter:parent.horizontalCenter; verticalCenter:undefined} + } + }, + State { + name: "topLeft" + when: (panel.position === PlasmaCore.Types.TopPosition && userPanelPosition===NowDock.Types.Left) + + AnchorChanges { + target: barLine + anchors{ top:parent.top; bottom:undefined; left:parent.left; right:undefined; horizontalCenter:undefined; verticalCenter:undefined} + } + AnchorChanges { + target: icList + anchors{ top:parent.top; bottom:undefined; left:parent.left; right:undefined; horizontalCenter:undefined; verticalCenter:undefined} + } + }, + State { + name: "topRight" + when: (panel.position === PlasmaCore.Types.TopPosition && userPanelPosition===NowDock.Types.Right) + + AnchorChanges { + target: barLine + anchors{ top:parent.top; bottom:undefined; left:undefined; right:parent.right; horizontalCenter:undefined; verticalCenter:undefined} + } + AnchorChanges { + target: icList + anchors{ top:parent.top; bottom:undefined; left:undefined; right:parent.right; horizontalCenter:undefined; verticalCenter:undefined} + } + }, + ////Left Edge + State { + name: "leftCenter" + when: (panel.position === PlasmaCore.Types.LeftPosition && userPanelPosition===NowDock.Types.Center) + + AnchorChanges { + target: barLine + anchors{ top:undefined; bottom:undefined; left:parent.left; right:undefined; horizontalCenter:undefined; verticalCenter:parent.verticalCenter} + } + AnchorChanges { + target: icList + anchors{ top:undefined; bottom:undefined; left:parent.left; right:undefined; horizontalCenter:undefined; verticalCenter:parent.verticalCenter} + } + }, + State { + name: "leftTop" + when: (panel.position === PlasmaCore.Types.LeftPosition && userPanelPosition===NowDock.Types.Top) + + AnchorChanges { + target: barLine + anchors{ top:parent.top; bottom:undefined; left:parent.left; right:undefined; horizontalCenter:undefined; verticalCenter:undefined} + } + AnchorChanges { + target: icList + anchors{ top:parent.top; bottom:undefined; left:parent.left; right:undefined; horizontalCenter:undefined; verticalCenter:undefined} + } + }, + State { + name: "leftBottom" + when: (panel.position === PlasmaCore.Types.LeftPosition && userPanelPosition===NowDock.Types.Bottom) + + AnchorChanges { + target: barLine + anchors{ top:undefined; bottom:parent.bottom; left:parent.left; right:undefined; horizontalCenter:undefined; verticalCenter:undefined} + } + AnchorChanges { + target: icList + anchors{ top:undefined; bottom:parent.bottom; left:parent.left; right:undefined; horizontalCenter:undefined; verticalCenter:undefined} + } + }, + ///Right Edge + State { + name: "rightCenter" + when: (panel.position === PlasmaCore.Types.RightPosition && userPanelPosition===NowDock.Types.Center) + + AnchorChanges { + target: barLine + anchors{ top:undefined; bottom:undefined; left:undefined; right:parent.right; horizontalCenter:undefined; verticalCenter:parent.verticalCenter} + } + AnchorChanges { + target: icList + anchors{ top:undefined; bottom:undefined; left:undefined; right:parent.right; horizontalCenter:undefined; verticalCenter:parent.verticalCenter} + } + }, + State { + name: "rightTop" + when: (panel.position === PlasmaCore.Types.RightPosition && userPanelPosition===NowDock.Types.Top) + + AnchorChanges { + target: barLine + anchors{ top:parent.top; bottom:undefined; left:undefined; right:parent.right; horizontalCenter:undefined; verticalCenter:undefined} + } + AnchorChanges { + target: icList + anchors{ top:parent.top; bottom:undefined; left:undefined; right:parent.right; horizontalCenter:undefined; verticalCenter:undefined} + } + }, + State { + name: "rightBottom" + when: (panel.position === PlasmaCore.Types.RightPosition && userPanelPosition===NowDock.Types.Bottom) + + AnchorChanges { + target: barLine + anchors{ top:undefined; bottom:parent.bottom; left:undefined; right:parent.right; horizontalCenter:undefined; verticalCenter:undefined} + } + AnchorChanges { + target: icList + anchors{ top:undefined; bottom:parent.bottom; left:undefined; right:parent.right; horizontalCenter:undefined; verticalCenter:undefined} + } + } + + ] + //END states + +} diff --git a/plasmoid/metadata.desktop.cmake b/plasmoid/metadata.desktop.cmake new file mode 100644 index 000000000..b963b87c1 --- /dev/null +++ b/plasmoid/metadata.desktop.cmake @@ -0,0 +1,29 @@ +[Desktop Entry] +Name=Now Dock +Name[el]=Now Dock +Name[pl]=Now Dock +Name[zh_TW]=Now Dock +Comment=Switch between running applications +Comment[el]=Εναλλαγή μεταξύ ενεργών εφαρμογών +Comment[pl]=Przełączanie między uruchomionymi aplikacjami +Comment[zh_TW]=在執行中的應用程式間切換 + + +Type=Service +Icon=preferences-system-windows +X-KDE-ServiceTypes=Plasma/Applet +X-Plasma-API=declarativeappletscript +X-Plasma-MainScript=ui/main.qml +X-Plasma-Provides=org.kde.plasma.multitasking +X-KDE-PluginInfo-Author=@AUTHOR@ +X-KDE-PluginInfo-Email=@EMAIL@ +X-KDE-PluginInfo-Name=org.kde.store.nowdock.plasmoid +X-KDE-PluginInfo-Version=@VERSION@ +X-KDE-PluginInfo-Website=@WEBSITE@ +X-KDE-PluginInfo-Category=Windows and Tasks +X-KDE-PluginInfo-Depends= +X-KDE-PluginInfo-License=GPL v2+ +X-KDE-PluginInfo-EnabledByDefault=true + + + diff --git a/po/Messages.sh b/po/Messages.sh new file mode 100644 index 000000000..7bc558d6d --- /dev/null +++ b/po/Messages.sh @@ -0,0 +1,113 @@ +#!/bin/sh + +BASEDIR="../.." # root of translatable sources +PROJECT="plasma_applet_org.kde.nowdock.containment" # project name +PROJECTPATH="../../containment" # project path +BUGADDR="https://github.com/psifidotos/nowdock-panel/" # MSGID-Bugs +WDIR="`pwd`/containment" # working dir + +PROJECTPLASMOID="plasma_applet_org.kde.store.nowdock.plasmoid" # project name +PROJECTPATHPLASMOID="../../plasmoid" # project path +WDIRPLASMOID="`pwd`/plasmoid" # working di + +echo "Preparing rc files for panel" + +cd containment + +# we use simple sorting to make sure the lines do not jump around too much from system to system +find "${PROJECTPATH}" -name '*.rc' -o -name '*.ui' -o -name '*.kcfg' | sort > "${WDIR}/rcfiles.list" +xargs --arg-file="${WDIR}/rcfiles.list" extractrc > "${WDIR}/rc.cpp" + +# additional string for KAboutData +# echo 'i18nc("NAME OF TRANSLATORS","Your names");' >> "${WDIR}/rc.cpp" +# echo 'i18nc("EMAIL OF TRANSLATORS","Your emails");' >> "${WDIR}/rc.cpp" + +intltool-extract --quiet --type=gettext/ini ../../containment.metadata.desktop.template + +cat ../../containment.metadata.desktop.template.h >> ${WDIR}/rc.cpp + +rm ../../containment.metadata.desktop.template.h + +echo "Done preparing rc files for panel" +echo "Extracting messages for panel" + +# see above on sorting +find "${PROJECTPATH}" -name '*.cpp' -o -name '*.h' -o -name '*.c' -o -name '*.qml' -o -name '*.qml.cmake' | sort > "${WDIR}/infiles.list" +echo "rc.cpp" >> "${WDIR}/infiles.list" + +xgettext --from-code=UTF-8 -C -kde -ci18n -ki18n:1 -ki18nc:1c,2 -ki18np:1,2 -ki18ncp:1c,2,3 \ + -ktr2i18n:1 -kI18N_NOOP:1 -kI18N_NOOP2:1c,2 -kN_:1 -kaliasLocale -kki18n:1 -kki18nc:1c,2 \ + -kki18np:1,2 -kki18ncp:1c,2,3 --msgid-bugs-address="${BUGADDR}" --files-from=infiles.list \ + -D "${BASEDIR}" -D "${WDIR}" -o "${PROJECT}.pot" || \ + { echo "error while calling xgettext. aborting."; exit 1; } +echo "Done extracting messages for panel" + +echo "Merging translations for panel" +catalogs=`find . -name '*.po'` +for cat in $catalogs; do + echo "$cat" + msgmerge -o "$cat.new" "$cat" "${WDIR}/${PROJECT}.pot" + mv "$cat.new" "$cat" +done + +intltool-merge --quiet --desktop-style . ../../containment.metadata.desktop.template "${PROJECTPATH}"/metadata.desktop.cmake + +echo "Done merging translations for panel" +echo "Cleaning up" +rm "${WDIR}/rcfiles.list" +rm "${WDIR}/infiles.list" +rm "${WDIR}/rc.cpp" +echo "Done translations for panel" + +#---------------------- Plasmoid section ----------------# + +#!/bin/sh + +#BASEDIR=".." # root of translatable sources + +echo "Preparing rc files for plasmoid" +cd ../plasmoid + +# we use simple sorting to make sure the lines do not jump around too much from system to system +find "${PROJECTPATHPLASMOID}" -name '*.rc' -o -name '*.ui' -o -name '*.kcfg' | sort > "${WDIRPLASMOID}/rcfiles.list" +xargs --arg-file="${WDIRPLASMOID}/rcfiles.list" extractrc > "${WDIRPLASMOID}/rc.cpp" + +intltool-extract --quiet --type=gettext/ini ../../plasmoid.metadata.desktop.template +cat ../../plasmoid.metadata.desktop.template.h >> ${WDIRPLASMOID}/rc.cpp +rm ../../plasmoid.metadata.desktop.template.h + +echo "Done preparing rc files for plasmoid" +echo "Extracting messages for plasmoid" + +# see above on sorting + +find "${PROJECTPATHPLASMOID}" -name '*.cpp' -o -name '*.h' -o -name '*.c' -o -name '*.qml' -o -name '*.qml.cmake' | sort > "${WDIRPLASMOID}/infiles.list" +echo "rc.cpp" >> "${WDIRPLASMOID}/infiles.list" + +xgettext --from-code=UTF-8 -C -kde -ci18n -ki18n:1 -ki18nc:1c,2 -ki18np:1,2 -ki18ncp:1c,2,3 \ + -ktr2i18n:1 -kI18N_NOOP:1 -kI18N_NOOP2:1c,2 -kN_:1 -kaliasLocale -kki18n:1 -kki18nc:1c,2 \ + -kki18np:1,2 -kki18ncp:1c,2,3 --msgid-bugs-address="${BUGADDR}" --files-from=infiles.list \ + -D "${BASEDIR}" -D "${WDIRPLASMOID}" -o "${PROJECTPLASMOID}.pot" || \ + { echo "error while calling xgettext. aborting."; exit 1; } +echo "Done extracting messages for plasmoid" + +echo "Merging translations for plasmoid" +catalogs=`find . -name '*.po'` +for cat in $catalogs; do + echo "$cat" + msgmerge -o "$cat.new" "$cat" "${WDIRPLASMOID}/${PROJECTPLASMOID}.pot" + mv "$cat.new" "$cat" +done + +intltool-merge --quiet --desktop-style . ../../plasmoid.metadata.desktop.template "${PROJECTPATHPLASMOID}"/metadata.desktop.cmake + +echo "Done merging translations for plasmoid" +echo "Cleaning up for plasmoid" +rm "${WDIRPLASMOID}/rcfiles.list" +rm "${WDIRPLASMOID}/infiles.list" +rm "${WDIRPLASMOID}/rc.cpp" +echo "Done" + + + + diff --git a/po/containment/el.po b/po/containment/el.po new file mode 100644 index 000000000..f76a778c3 --- /dev/null +++ b/po/containment/el.po @@ -0,0 +1,113 @@ +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Michail Vοurlakos <mvourlakos@gmail.com>, 2016. +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: https://github.com/psifidotos/nowdock-panel/\n" +"POT-Creation-Date: 2016-12-23 15:19+0200\n" +"PO-Revision-Date: 2016-11-25 00:08+0200\n" +"Last-Translator: Michail Vοurlakos <mvourlakos@gmail.com>\n" +"Language-Team: Greek <kde-i18n-doc@kde.org>\n" +"Language: el_GR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Lokalize 2.0\n" + +#: rc.cpp:1 +#, fuzzy +msgid "Now Dock" +msgstr "Now Dock Πίνακας" + +#: rc.cpp:2 +#, fuzzy +msgid "A dock for Plasma that tries to animate its plasmoids on hovering" +msgstr "" +"Ένας πίνακας του Plasma που χρησιμοποιεί ποικίλα εφέ για τις εφαρμογές του" + +#~ msgid "Applets Alignment" +#~ msgstr "Στοίχιση Εφαρμογών" + +#~ msgid "ver: " +#~ msgstr "εκδ: " + +#~ msgid "Top" +#~ msgstr "Πάνω" + +#~ msgid "Left" +#~ msgstr "Αριστερά" + +#~ msgid "Center" +#~ msgstr "Κέντρο" + +#~ msgid "Bottom" +#~ msgstr "Κάτω" + +#~ msgid "Right" +#~ msgstr "Δεξιά" + +#~ msgid "Visibility" +#~ msgstr "Ορατότητα" + +#~ msgid "Below Active" +#~ msgstr "Κάτω από Ενεργό" + +#~ msgid "Below Maximized" +#~ msgstr "Κάτω από Μεγιστοποίηση" + +#~ msgid "Let Windows Cover" +#~ msgstr "Παράθυρα από Πάνω" + +#~ msgid "Windows Go Below" +#~ msgstr "Παράθυρα από Κάτω" + +#~ msgid "Auto Hide" +#~ msgstr "Αυτόματη Απόκρυψη" + +#~ msgid "Always Visible" +#~ msgstr "Πάντα Ορατό" + +#~ msgid "Applets Size" +#~ msgstr "Μέγεθος Εφαρμογών" + +#~ msgid "Zoom On Hover" +#~ msgstr "Εστίαση" + +#~ msgid "Zoom Factor" +#~ msgstr "Συντελεστής Εστίασης" + +#~ msgid "Background" +#~ msgstr "Παρασκήνιο" + +#~ msgid "Show Panel Background" +#~ msgstr "Εμφάνιση Παρασκηνίου" + +#~ msgid "Size" +#~ msgstr "Μέγεθος" + +#~ msgid "Shadows" +#~ msgstr "Σκιές" + +#~ msgid "None" +#~ msgstr "Καμία" + +#~ msgid "Only for locked applets" +#~ msgstr "Μόνο κλειδωμένες εφαρμογές" + +#~ msgid "All applets" +#~ msgstr "Παντού" + +#~ msgid "Now Dock Default Panel" +#~ msgstr "Προκαθορισμένος Now Dock" + +#~ msgid "Empty Now Dock Panel" +#~ msgstr "Κενός Now Dock" + +#~ msgid "Automatic" +#~ msgstr "Αυτόματα" + +#~ msgid "Small steps for icon sizes in automatic modes" +#~ msgstr "Μικρά βήματα στην αυτόματη εναλλαγή μεγέθους" diff --git a/po/containment/pl.po b/po/containment/pl.po new file mode 100644 index 000000000..a9a002dfb --- /dev/null +++ b/po/containment/pl.po @@ -0,0 +1,84 @@ +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Damian Kopeć <damikope@gmail.com>, 2016. +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: https://github.com/psifidotos/nowdock-panel/\n" +"POT-Creation-Date: 2016-12-23 15:19+0200\n" +"PO-Revision-Date: 2016-10-14 14:20+0100\n" +"Last-Translator: Damian Kopeć <damikope@gmail.com>\n" +"Language-Team: Polish <kde-i18n-doc@kde.org>\n" +"Language: pl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Lokalize 2.0\n" + +#: rc.cpp:1 +#, fuzzy +msgid "Now Dock" +msgstr "Panel Now Dock" + +#: rc.cpp:2 +#, fuzzy +msgid "A dock for Plasma that tries to animate its plasmoids on hovering" +msgstr "Panel plazmy próbujący animowac plazmoidy podczas najechania na nie" + +#~ msgid "Applets Alignment" +#~ msgstr "Wyrównanie Apletów " + +#~ msgid "Top" +#~ msgstr "Góra" + +#~ msgid "Left" +#~ msgstr "Lewa" + +#~ msgid "Center" +#~ msgstr "Środek" + +#~ msgid "Bottom" +#~ msgstr "Dół" + +#~ msgid "Right" +#~ msgstr "Prawa" + +#~ msgid "Applets Size" +#~ msgstr "Wielkość Apletów" + +#~ msgid "Zoom On Hover" +#~ msgstr "Zbliżenie Przy Najechaniu" + +#~ msgid "Zoom Factor" +#~ msgstr "Stopień Zbliżenia" + +#~ msgid "Background" +#~ msgstr "Tło" + +#~ msgid "Show Panel Background" +#~ msgstr "Pokaż Tło Panelu" + +#~ msgid "Size" +#~ msgstr "Rozmiar" + +#, fuzzy +#~ msgid "Only for locked applets" +#~ msgstr "Cień dla zablokowanych apletów" + +#, fuzzy +#~ msgid "All applets" +#~ msgstr "Wielkość Apletów" + +#~ msgid "Now Dock Default Panel" +#~ msgstr "Domyślny Panel Now Dock" + +#~ msgid "Empty Now Dock Panel" +#~ msgstr "Pusty Panel Now Dock" + +#~ msgid "Automatic" +#~ msgstr "Automatyczna" + +#~ msgid "Small steps for icon sizes in automatic modes" +#~ msgstr "Mały skok wielkości ikon w tryb automatycznym" diff --git a/po/containment/plasma_applet_org.kde.nowdock.containment.pot b/po/containment/plasma_applet_org.kde.nowdock.containment.pot new file mode 100644 index 000000000..6420d789b --- /dev/null +++ b/po/containment/plasma_applet_org.kde.nowdock.containment.pot @@ -0,0 +1,26 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: https://github.com/psifidotos/nowdock-panel/\n" +"POT-Creation-Date: 2016-12-23 15:19+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: rc.cpp:1 +msgid "Now Dock" +msgstr "" + +#: rc.cpp:2 +msgid "A dock for Plasma that tries to animate its plasmoids on hovering" +msgstr "" diff --git a/po/containment/plasma_applet_org.kde.store.nowdock.panel.pot b/po/containment/plasma_applet_org.kde.store.nowdock.panel.pot new file mode 100644 index 000000000..69a338c1c --- /dev/null +++ b/po/containment/plasma_applet_org.kde.store.nowdock.panel.pot @@ -0,0 +1,34 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: https://github.com/psifidotos/nowdock-panel/\n" +"POT-Creation-Date: 2016-12-22 18:00+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: rc.cpp:1 +msgid "Now Dock" +msgstr "" + +#: rc.cpp:2 +msgid "A dock for Plasma that tries to animate its plasmoids on hovering" +msgstr "" + +#: rc.cpp:3 +msgid "Now Dock Default Panel" +msgstr "" + +#: rc.cpp:4 +msgid "Empty Now Dock Panel" +msgstr "" diff --git a/po/containment/ro.po b/po/containment/ro.po new file mode 100644 index 000000000..9c1b243ea --- /dev/null +++ b/po/containment/ro.po @@ -0,0 +1,85 @@ +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Viorel-Cătălin Răpițeanu <rapiteanu.catalin@gmail.com>, 2016. +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: https://github.com/psifidotos/nowdock-panel/\n" +"POT-Creation-Date: 2016-12-23 15:19+0200\n" +"PO-Revision-Date: 2016-09-25 04:15+0300\n" +"Last-Translator: Viorel-Cătălin Răpițeanu <rapiteanu DOT catalin AT gmail " +"DOT com>\n" +"Language-Team: Romanian <kde-i18n-doc@kde.org>\n" +"Language: ro_RO\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==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?" +"2:1));\n" +"X-Generator: Poedit 1.8.9\n" + +#: rc.cpp:1 +#, fuzzy +msgid "Now Dock" +msgstr "Panou Now Dock" + +#: rc.cpp:2 +#, fuzzy +msgid "A dock for Plasma that tries to animate its plasmoids on hovering" +msgstr "" +"Un panou plasma care încearcă să animeze miniaplicațiile la trecerea peste" + +#~ msgid "Applets Alignment" +#~ msgstr "Alinierea miniaplicațiilor" + +#~ msgid "Top" +#~ msgstr "Sus" + +#~ msgid "Left" +#~ msgstr "Stânga" + +#~ msgid "Center" +#~ msgstr "Centru" + +#~ msgid "Bottom" +#~ msgstr "Jos" + +#~ msgid "Right" +#~ msgstr "Dreapta" + +#~ msgid "Applets Size" +#~ msgstr "Dimensiunea miniaplicațiilor" + +#~ msgid "Zoom On Hover" +#~ msgstr "Panoramează la trecerea peste" + +#~ msgid "Zoom Factor" +#~ msgstr "Factor de panoramare" + +#~ msgid "Background" +#~ msgstr "Fundal" + +#~ msgid "Show Panel Background" +#~ msgstr "Arată fundalul panoului" + +#~ msgid "Size" +#~ msgstr "Dimensiune" + +#, fuzzy +#~ msgid "All applets" +#~ msgstr "Dimensiunea miniaplicațiilor" + +#, fuzzy +#~ msgid "Now Dock Default Panel" +#~ msgstr "Panou Now Dock" + +#, fuzzy +#~ msgid "Empty Now Dock Panel" +#~ msgstr "Panou Now Dock" + +#~ msgid "Automatic" +#~ msgstr "Automat" + +#~ msgid "Small steps for icon sizes in automatic modes" +#~ msgstr "Pași mici pentru dimensiunea pictogramelor în modurile automate" diff --git a/po/containment/zh_TW.po b/po/containment/zh_TW.po new file mode 100644 index 000000000..982d61b80 --- /dev/null +++ b/po/containment/zh_TW.po @@ -0,0 +1,112 @@ +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Jeff Huang <s8321414@gmail.com>, 2016. +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: https://github.com/psifidotos/nowdock-panel/\n" +"POT-Creation-Date: 2016-12-23 15:19+0200\n" +"PO-Revision-Date: 2016-11-26 17:31+0800\n" +"Last-Translator: Jeff Huang <s8321414@gmail.com>\n" +"Language-Team: Chinese <kde-i18n-doc@kde.org>\n" +"Language: zh_TW\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Lokalize 2.0\n" + +#: rc.cpp:1 +#, fuzzy +msgid "Now Dock" +msgstr "Now Dock 面板" + +#: rc.cpp:2 +#, fuzzy +msgid "A dock for Plasma that tries to animate its plasmoids on hovering" +msgstr "一個嘗試讓滑鼠游標停在上面時會讓它的 plasmoid 產生動畫的 plasma 面板" + +#~ msgid "Applets Alignment" +#~ msgstr "小程式對齊" + +#~ msgid "ver: " +#~ msgstr "版本:" + +#~ msgid "Top" +#~ msgstr "頂部" + +#~ msgid "Left" +#~ msgstr "左邊" + +#~ msgid "Center" +#~ msgstr "置中" + +#~ msgid "Bottom" +#~ msgstr "底部" + +#~ msgid "Right" +#~ msgstr "右邊" + +#~ msgid "Visibility" +#~ msgstr "可見度" + +#~ msgid "Below Active" +#~ msgstr "在作用中的視窗後" + +#~ msgid "Below Maximized" +#~ msgstr "在最大化的視窗後" + +#~ msgid "Let Windows Cover" +#~ msgstr " 讓視窗蓋過" + +#~ msgid "Windows Go Below" +#~ msgstr "視窗在下面" + +#~ msgid "Auto Hide" +#~ msgstr "自動隱藏" + +#~ msgid "Always Visible" +#~ msgstr "永遠可見" + +#~ msgid "Applets Size" +#~ msgstr "小程式大小" + +#~ msgid "Zoom On Hover" +#~ msgstr "滑鼠游標置於其上時縮放" + +#~ msgid "Zoom Factor" +#~ msgstr "縮放係數" + +#~ msgid "Background" +#~ msgstr "背景" + +#~ msgid "Show Panel Background" +#~ msgstr "顯示面板背景" + +#~ msgid "Size" +#~ msgstr "大小" + +#~ msgid "Shadows" +#~ msgstr "陰影" + +#~ msgid "None" +#~ msgstr "無" + +#~ msgid "Only for locked applets" +#~ msgstr "僅鎖定小程式" + +#~ msgid "All applets" +#~ msgstr "所有小程式" + +#~ msgid "Now Dock Default Panel" +#~ msgstr "Now Dock 預設面板" + +#~ msgid "Empty Now Dock Panel" +#~ msgstr "空的 Now Dock 面板" + +#~ msgid "Automatic" +#~ msgstr "自動" + +#~ msgid "Small steps for icon sizes in automatic modes" +#~ msgstr "自動模式中,圖示大小少量步進" diff --git a/po/plasmoid/el.po b/po/plasmoid/el.po new file mode 100644 index 000000000..38afb0afa --- /dev/null +++ b/po/plasmoid/el.po @@ -0,0 +1,364 @@ +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Michail Vοurlakos <mvourlakos@gmail.com>, 2016. +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: https://github.com/psifidotos/nowdock-panel/\n" +"POT-Creation-Date: 2016-12-23 15:19+0200\n" +"PO-Revision-Date: 2016-11-25 00:01+0200\n" +"Last-Translator: Michail Vοurlakos <mvourlakos@gmail.com>\n" +"Language-Team: Greek <kde-i18n-doc@kde.org>\n" +"Language: el_GR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Lokalize 2.0\n" + +#: ../../plasmoid/contents/config/config.qml:26 +msgid "Appearance" +msgstr "Εμφάνιση" + +#: ../../plasmoid/contents/config/config.qml:31 +msgid "Panel" +msgstr "Πίνακας" + +#: ../../plasmoid/contents/config/config.qml:36 +msgid "Interaction" +msgstr "Αλληλεπίδραση" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:68 +msgid "Icon size: " +msgstr "Μέγεθος: " + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:156 +msgid "ver: " +msgstr "εκδ: " + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:163 +msgid "Enable shadows for icons" +msgstr "Σκιές στα εικονίδια" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:169 +msgid "Show glow around windows points" +msgstr "Λάμψη στα σημεία κατάστασης των παραθύρων" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:175 +msgid "Different color for minimized windows" +msgstr "Διαφορετικό χρώμα για ελαχιστοποιημένα παράθυρα" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:181 +msgid "Dots on active window" +msgstr "Τελείες στο ενεργό παράθυρο" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:187 +msgid "Reverse position for lines and dots" +msgstr "Αντίστροφη θέση για γραμμές και τελείες" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:205 +msgid "Animations: " +msgstr "Εφέ: " + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:223 +msgid "duration" +msgstr "διάρκεια" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:223 +msgid "disabled" +msgstr "ανενεργό" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:235 +msgid "Zoom" +msgstr "Εστίαση" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:249 +msgid "Level: " +msgstr "Επίπεδο:" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:293 +msgid "Show a red line on the limit needed for animations" +msgstr "Εμφάνιση κόκκινης γραμμής στο όριο των εφέ" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:323 +#: ../../plasmoid/contents/ui/ConfigPanel.qml:169 +msgid "" +"For the disabled settings you should use the Now Dock Panel Configuration " +"Window" +msgstr "" +"Για τις ανενεργές ρυθμίσεις πρέπει να χρησιμοποιήσετε το Παράθυρο Ρυθμίσεων " +"του Πίνακα Now Dock" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:58 +msgid "Cycle through tasks with mouse wheel" +msgstr "Περιήγηση στις εργασίες με τη ροδέλα του ποντικιού" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:65 +msgid "Preview windows on hovering" +msgstr "Προεπισκοπήσεις παραθύρων" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:71 +msgid "Highlight windows on hovering" +msgstr "Τονισμός παραθύρων" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:77 +msgid "Show window actions in the context menu" +msgstr "Ενέργειες παραθύρου στο μενού επιλογών" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:83 +msgid "Show progress information in task buttons" +msgstr "Εμφάνιση προόδου εργασιών" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:88 +msgid "On middle-click:" +msgstr "Στο μεσαίο κλικ:" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:94 +msgctxt "The click action" +msgid "None" +msgstr "Καμία" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:94 +msgid "Close Window or Group" +msgstr "Κλείσιμο παραθύρου ή ομάδας" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:94 +msgid "New Instance" +msgstr "Νέο στιγμιότυπο" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:94 +msgid "Minimize/Restore Window or Group" +msgstr "Ελαχιστοποίηση/Επαναφορά παραθύρου ή ομάδας" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:106 +msgid "Filters" +msgstr "Φίλτρα" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:117 +msgid "Show only tasks from the current screen" +msgstr "Εμφάνιση εργασιών μόνο της τρέχουσας οθόνης" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:122 +msgid "Show only tasks from the current desktop" +msgstr "Εμφάνιση εργασιών μόνο της τρέχουσας επιφάνειας εργασίας" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:127 +msgid "Show only tasks from the current activity" +msgstr "Εμφάνιση εργασιών μόνο της τρέχουσας δραστηριότητας" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:58 +msgid "Position: " +msgstr "Θέση: " + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:66 +msgid "Center" +msgstr "Κέντρο" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:66 +msgid "Left" +msgstr "Αριστερά" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:66 +msgid "Right" +msgstr "Δεξιά" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:66 +msgid "Top" +msgstr "Πάνω" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:66 +msgid "Bottom" +msgstr "Κάτω" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:74 +msgid "Show bar line for tasks" +msgstr "Εμφάνιση υποβάθρου για τις εργασίες" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:81 +msgid "Use plasma theme panel" +msgstr "Πίνακας από το θέμα του Plasma" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:88 +msgid "Use transparency in the panel" +msgstr "Πίνακας με διαφάνεια" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:95 +msgid "Size: " +msgstr "Μέγεθος: " + +#: ../../plasmoid/contents/ui/ContextMenu.qml:115 +msgctxt "Play previous track" +msgid "Previous Track" +msgstr "Προηγούμενο" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:128 +msgctxt "Pause playback" +msgid "Pause" +msgstr "Παύση" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:128 +msgctxt "Start playback" +msgid "Play" +msgstr "Έναρξη" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:139 +msgctxt "Stop playback" +msgid "Stop" +msgstr "Διακοπή" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:147 +msgctxt "Play next track" +msgid "Next Track" +msgstr "Επόμενο" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:163 +msgctxt "Quit media player app" +msgid "Quit" +msgstr "Έξοδος" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:178 +msgctxt "Open or bring to the front window of media player app" +msgid "Restore" +msgstr "Επαναφορά" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:223 +msgid "Move To Desktop" +msgstr "Στην Επιφάνεια Εργασίας" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:245 +msgid "Move To Current Desktop" +msgstr "Στην Τρέχουσα Επιφάνεια Εργασίας" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:254 +msgid "All Desktops" +msgstr "Όλες οι Επιφάνειες Εργασίας" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:283 +msgid "New Desktop" +msgstr "Νέα Επιφάνεια Εργασίας" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:303 +msgid "Move To &Activity" +msgstr "Δραστηριότητες" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:324 +msgid "Add To Current Activity" +msgstr "Στην Τρέχουσα Δραστηριότητα" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:334 +msgid "All Activities" +msgstr "Όλες τις Δραστηριότητες" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:403 +msgid "Minimize" +msgstr "Ελαχιστοποίηση" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:419 +msgid "Maximize" +msgstr "Μεγιστοποίηση" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:434 +msgid "More Actions" +msgstr "Περισσότερες ενέργειες" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:442 +msgid "Move" +msgstr "Μετακίνηση" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:451 +msgid "Resize" +msgstr "Αλλαγή μεγέθους" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:460 +msgid "Keep Above Others" +msgstr "Διατήρηση πάνω από τα άλλα" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:470 +msgid "Keep Below Others" +msgstr "Διατήρηση κάτω από τα άλλα" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:482 +msgid "Fullscreen" +msgstr "Πλήρης οθόνη" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:494 +msgid "Shade" +msgstr "Τύλιγμα" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:509 +msgid "Allow this program to be grouped" +msgstr "Να επιτρέπεται η ομαδοποίηση του προγράμματος" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:522 +msgid "Start New Instance" +msgstr "Νέο Στιγμιότυπο" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:544 +#: ../../plasmoid/contents/ui/ContextMenu.qml:577 +msgid "Show Launcher On All Activities" +msgstr "Εκκινητής σε Όλες τις Δραστηριότητες" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:561 +msgid "Show Launcher When Not Running" +msgstr "Εκκινητής όταν δεν είναι Ενεργό" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:588 +msgid "Remove Launcher" +msgstr "Αφαίρεση Εκκινητή" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:619 +msgid "Close" +msgstr "Κλείσιμο" + +#: ../../plasmoid/contents/ui/TaskDelegate.qml:773 +msgid "On %1" +msgstr "Σε %1" + +#: ../../plasmoid/contents/ui/TaskDelegate.qml:782 +msgctxt "Which virtual desktop a window is currently on" +msgid "Available on all activities" +msgstr "Σε όλες τις δραστηριότητες" + +#: ../../plasmoid/contents/ui/TaskDelegate.qml:801 +msgctxt "Activities a window is currently on (apart from the current one)" +msgid "Also available on %1" +msgstr "Διαθέσιμο επίσης σε %1" + +#: ../../plasmoid/contents/ui/TaskDelegate.qml:805 +msgctxt "Which activities a window is currently on" +msgid "Available on %1" +msgstr "Διαθέσιμο σε %1" + +#: ../../plasmoid/contents/ui/ToolTipDelegate.qml:356 +msgctxt "Go to previous song" +msgid "Previous" +msgstr "Προηγούμενο" + +#: ../../plasmoid/contents/ui/ToolTipDelegate.qml:365 +msgctxt "Pause player" +msgid "Pause" +msgstr "Παύση" + +#: ../../plasmoid/contents/ui/ToolTipDelegate.qml:365 +msgctxt "Start player" +msgid "Play" +msgstr "Έναρξη" + +#: ../../plasmoid/contents/ui/ToolTipDelegate.qml:373 +msgctxt "Go to next song" +msgid "Next" +msgstr "Επόμενο" + +#: ../../plasmoid/contents/ui/ToolTipWindowMouseArea.qml:57 +msgctxt "close this window" +msgid "Close" +msgstr "Κλείσιμο" + +#: rc.cpp:1 +msgid "Now Dock" +msgstr "Now Dock" + +#: rc.cpp:2 +msgid "Switch between running applications" +msgstr "Εναλλαγή μεταξύ ενεργών εφαρμογών" diff --git a/po/plasmoid/pl.po b/po/plasmoid/pl.po new file mode 100644 index 000000000..88a0a9fc0 --- /dev/null +++ b/po/plasmoid/pl.po @@ -0,0 +1,364 @@ +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Damian Kopeć <damikope@gmail.com>, 2016. +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: https://github.com/psifidotos/nowdock-panel/\n" +"POT-Creation-Date: 2016-12-23 15:19+0200\n" +"PO-Revision-Date: 2016-10-16 13:14+0100\n" +"Last-Translator: Damian Kopeć <damikope@gmail.com>\n" +"Language-Team: Polish <kde-i18n-doc@kde.org>\n" +"Language: pl\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==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " +"|| n%100>=20) ? 1 : 2);\n" +"X-Generator: Lokalize 2.0\n" + +#: ../../plasmoid/contents/config/config.qml:26 +msgid "Appearance" +msgstr "Wygląd " + +#: ../../plasmoid/contents/config/config.qml:31 +msgid "Panel" +msgstr "Panel" + +# może być lepsze/kontekst +#: ../../plasmoid/contents/config/config.qml:36 +msgid "Interaction" +msgstr "Interakcja" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:68 +msgid "Icon size: " +msgstr "Rozmiar ikon:" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:156 +msgid "ver: " +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:163 +msgid "Enable shadows for icons" +msgstr "Włącz wyświetlanie cieni ikon" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:169 +msgid "Show glow around windows points" +msgstr "Pokaż poświatę przy punktach okien" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:175 +msgid "Different color for minimized windows" +msgstr "Inny kolor zminimalizowanych okien" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:181 +msgid "Dots on active window" +msgstr "Kropki na aktywnych oknach" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:187 +msgid "Reverse position for lines and dots" +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:205 +msgid "Animations: " +msgstr "Animacje:" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:223 +msgid "duration" +msgstr "czas trwania" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:223 +msgid "disabled" +msgstr "wyłączone" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:235 +msgid "Zoom" +msgstr "Zbliżenie" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:249 +msgid "Level: " +msgstr "Poziom:" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:293 +msgid "Show a red line on the limit needed for animations" +msgstr "Pokaż czerwoną linię określającą miejsce potrzebne dla animacji" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:323 +#: ../../plasmoid/contents/ui/ConfigPanel.qml:169 +msgid "" +"For the disabled settings you should use the Now Dock Panel Configuration " +"Window" +msgstr "Dla niedostępnych ustawień użyj Okna Konfiguracji Panelu Now Dock" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:58 +msgid "Cycle through tasks with mouse wheel" +msgstr "Zmiana aktywnego zadania kółkiem myszy" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:65 +msgid "Preview windows on hovering" +msgstr "Podgląd okna przy najechaniu kursorem" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:71 +msgid "Highlight windows on hovering" +msgstr "Zaznaczenie okna przy najechaniu kursorem" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:77 +msgid "Show window actions in the context menu" +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:83 +msgid "Show progress information in task buttons" +msgstr "Pokaż informacje o postępie na przyciskach" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:88 +msgid "On middle-click:" +msgstr "Środkowy Przycisk Myszy:" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:94 +msgctxt "The click action" +msgid "None" +msgstr "Nic" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:94 +msgid "Close Window or Group" +msgstr "Zamknij Okno lub Grupę" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:94 +msgid "New Instance" +msgstr "Nowa instancja" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:94 +msgid "Minimize/Restore Window or Group" +msgstr "Zminimalizuj/Przywróć Okno lub Grupę" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:106 +msgid "Filters" +msgstr "Filtry" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:117 +msgid "Show only tasks from the current screen" +msgstr "Pokaż tylko zadania z obecnego ekranu" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:122 +msgid "Show only tasks from the current desktop" +msgstr "Pokaż tylko zadania z obecnego pulpitu" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:127 +msgid "Show only tasks from the current activity" +msgstr "Pokaż tylko zadania z obecnej aktywności" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:58 +msgid "Position: " +msgstr "Pozycja:" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:66 +msgid "Center" +msgstr "Środek" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:66 +msgid "Left" +msgstr "Lewa" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:66 +msgid "Right" +msgstr "Prawa" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:66 +msgid "Top" +msgstr "Góra" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:66 +msgid "Bottom" +msgstr "Dół" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:74 +msgid "Show bar line for tasks" +msgstr "Pokaż linie rozdzielające dla zadań" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:81 +msgid "Use plasma theme panel" +msgstr "Użyj motywu panelu plazmy" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:88 +msgid "Use transparency in the panel" +msgstr "Użyj przezroczystości w panelu" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:95 +msgid "Size: " +msgstr "Rozmiar:" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:115 +msgctxt "Play previous track" +msgid "Previous Track" +msgstr "Poprzedni utwór" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:128 +msgctxt "Pause playback" +msgid "Pause" +msgstr "Pauza" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:128 +msgctxt "Start playback" +msgid "Play" +msgstr "Odtwórz" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:139 +msgctxt "Stop playback" +msgid "Stop" +msgstr "Zatrzymaj" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:147 +msgctxt "Play next track" +msgid "Next Track" +msgstr "Następny utwór" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:163 +msgctxt "Quit media player app" +msgid "Quit" +msgstr "Wyjdź" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:178 +msgctxt "Open or bring to the front window of media player app" +msgid "Restore" +msgstr "Przywróć" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:223 +msgid "Move To Desktop" +msgstr "Przenieś Na Pulpit" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:245 +msgid "Move To Current Desktop" +msgstr "Przenieś Na Obecny Pulpit" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:254 +msgid "All Desktops" +msgstr "Wszystkie Pulpity" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:283 +msgid "New Desktop" +msgstr "Nowy Pulpit" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:303 +msgid "Move To &Activity" +msgstr "Przenieś do &Activity" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:324 +msgid "Add To Current Activity" +msgstr "Dodaj Do Obecnej Aktywności" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:334 +msgid "All Activities" +msgstr "Wszystkie Aktywności" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:403 +msgid "Minimize" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:419 +msgid "Maximize" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:434 +msgid "More Actions" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:442 +msgid "Move" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:451 +msgid "Resize" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:460 +msgid "Keep Above Others" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:470 +msgid "Keep Below Others" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:482 +msgid "Fullscreen" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:494 +msgid "Shade" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:509 +msgid "Allow this program to be grouped" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:522 +msgid "Start New Instance" +msgstr "Utwórz nową instancję" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:544 +#: ../../plasmoid/contents/ui/ContextMenu.qml:577 +msgid "Show Launcher On All Activities" +msgstr "Pokaż Skrót Na Wszystkich Aktywnościach" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:561 +msgid "Show Launcher When Not Running" +msgstr "Pokaż Skrót Kiedy Aplikacja Nie Jest Uruchomiona" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:588 +msgid "Remove Launcher" +msgstr "Usuń Skrót" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:619 +msgid "Close" +msgstr "Zamknij" + +#: ../../plasmoid/contents/ui/TaskDelegate.qml:773 +msgid "On %1" +msgstr "Na %1" + +#: ../../plasmoid/contents/ui/TaskDelegate.qml:782 +msgctxt "Which virtual desktop a window is currently on" +msgid "Available on all activities" +msgstr "Dostępne We Wszystkich Aktywnościach" + +#: ../../plasmoid/contents/ui/TaskDelegate.qml:801 +msgctxt "Activities a window is currently on (apart from the current one)" +msgid "Also available on %1" +msgstr "Dostępne Także na %1" + +#: ../../plasmoid/contents/ui/TaskDelegate.qml:805 +msgctxt "Which activities a window is currently on" +msgid "Available on %1" +msgstr "Dostępne na %1" + +#: ../../plasmoid/contents/ui/ToolTipDelegate.qml:356 +msgctxt "Go to previous song" +msgid "Previous" +msgstr "Poprzedni" + +#: ../../plasmoid/contents/ui/ToolTipDelegate.qml:365 +msgctxt "Pause player" +msgid "Pause" +msgstr "Pauza" + +#: ../../plasmoid/contents/ui/ToolTipDelegate.qml:365 +msgctxt "Start player" +msgid "Play" +msgstr "Odtwórz" + +#: ../../plasmoid/contents/ui/ToolTipDelegate.qml:373 +msgctxt "Go to next song" +msgid "Next" +msgstr "Następny" + +#: ../../plasmoid/contents/ui/ToolTipWindowMouseArea.qml:57 +msgctxt "close this window" +msgid "Close" +msgstr "Zamknij" + +#: rc.cpp:1 +msgid "Now Dock" +msgstr "Now Dock" + +#: rc.cpp:2 +msgid "Switch between running applications" +msgstr "Przełączanie między uruchomionymi aplikacjami" diff --git a/po/plasmoid/plasma_applet_org.kde.store.nowdock.plasmoid.pot b/po/plasmoid/plasma_applet_org.kde.store.nowdock.plasmoid.pot new file mode 100644 index 000000000..3a727186d --- /dev/null +++ b/po/plasmoid/plasma_applet_org.kde.store.nowdock.plasmoid.pot @@ -0,0 +1,362 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: https://github.com/psifidotos/nowdock-panel/\n" +"POT-Creation-Date: 2016-12-23 15:19+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: ../../plasmoid/contents/config/config.qml:26 +msgid "Appearance" +msgstr "" + +#: ../../plasmoid/contents/config/config.qml:31 +msgid "Panel" +msgstr "" + +#: ../../plasmoid/contents/config/config.qml:36 +msgid "Interaction" +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:68 +msgid "Icon size: " +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:156 +msgid "ver: " +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:163 +msgid "Enable shadows for icons" +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:169 +msgid "Show glow around windows points" +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:175 +msgid "Different color for minimized windows" +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:181 +msgid "Dots on active window" +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:187 +msgid "Reverse position for lines and dots" +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:205 +msgid "Animations: " +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:223 +msgid "duration" +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:223 +msgid "disabled" +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:235 +msgid "Zoom" +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:249 +msgid "Level: " +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:293 +msgid "Show a red line on the limit needed for animations" +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:323 +#: ../../plasmoid/contents/ui/ConfigPanel.qml:169 +msgid "" +"For the disabled settings you should use the Now Dock Panel Configuration " +"Window" +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:58 +msgid "Cycle through tasks with mouse wheel" +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:65 +msgid "Preview windows on hovering" +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:71 +msgid "Highlight windows on hovering" +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:77 +msgid "Show window actions in the context menu" +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:83 +msgid "Show progress information in task buttons" +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:88 +msgid "On middle-click:" +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:94 +msgctxt "The click action" +msgid "None" +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:94 +msgid "Close Window or Group" +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:94 +msgid "New Instance" +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:94 +msgid "Minimize/Restore Window or Group" +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:106 +msgid "Filters" +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:117 +msgid "Show only tasks from the current screen" +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:122 +msgid "Show only tasks from the current desktop" +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:127 +msgid "Show only tasks from the current activity" +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:58 +msgid "Position: " +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:66 +msgid "Center" +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:66 +msgid "Left" +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:66 +msgid "Right" +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:66 +msgid "Top" +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:66 +msgid "Bottom" +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:74 +msgid "Show bar line for tasks" +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:81 +msgid "Use plasma theme panel" +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:88 +msgid "Use transparency in the panel" +msgstr "" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:95 +msgid "Size: " +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:115 +msgctxt "Play previous track" +msgid "Previous Track" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:128 +msgctxt "Pause playback" +msgid "Pause" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:128 +msgctxt "Start playback" +msgid "Play" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:139 +msgctxt "Stop playback" +msgid "Stop" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:147 +msgctxt "Play next track" +msgid "Next Track" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:163 +msgctxt "Quit media player app" +msgid "Quit" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:178 +msgctxt "Open or bring to the front window of media player app" +msgid "Restore" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:223 +msgid "Move To Desktop" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:245 +msgid "Move To Current Desktop" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:254 +msgid "All Desktops" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:283 +msgid "New Desktop" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:303 +msgid "Move To &Activity" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:324 +msgid "Add To Current Activity" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:334 +msgid "All Activities" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:403 +msgid "Minimize" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:419 +msgid "Maximize" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:434 +msgid "More Actions" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:442 +msgid "Move" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:451 +msgid "Resize" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:460 +msgid "Keep Above Others" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:470 +msgid "Keep Below Others" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:482 +msgid "Fullscreen" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:494 +msgid "Shade" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:509 +msgid "Allow this program to be grouped" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:522 +msgid "Start New Instance" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:544 +#: ../../plasmoid/contents/ui/ContextMenu.qml:577 +msgid "Show Launcher On All Activities" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:561 +msgid "Show Launcher When Not Running" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:588 +msgid "Remove Launcher" +msgstr "" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:619 +msgid "Close" +msgstr "" + +#: ../../plasmoid/contents/ui/TaskDelegate.qml:773 +msgid "On %1" +msgstr "" + +#: ../../plasmoid/contents/ui/TaskDelegate.qml:782 +msgctxt "Which virtual desktop a window is currently on" +msgid "Available on all activities" +msgstr "" + +#: ../../plasmoid/contents/ui/TaskDelegate.qml:801 +msgctxt "Activities a window is currently on (apart from the current one)" +msgid "Also available on %1" +msgstr "" + +#: ../../plasmoid/contents/ui/TaskDelegate.qml:805 +msgctxt "Which activities a window is currently on" +msgid "Available on %1" +msgstr "" + +#: ../../plasmoid/contents/ui/ToolTipDelegate.qml:356 +msgctxt "Go to previous song" +msgid "Previous" +msgstr "" + +#: ../../plasmoid/contents/ui/ToolTipDelegate.qml:365 +msgctxt "Pause player" +msgid "Pause" +msgstr "" + +#: ../../plasmoid/contents/ui/ToolTipDelegate.qml:365 +msgctxt "Start player" +msgid "Play" +msgstr "" + +#: ../../plasmoid/contents/ui/ToolTipDelegate.qml:373 +msgctxt "Go to next song" +msgid "Next" +msgstr "" + +#: ../../plasmoid/contents/ui/ToolTipWindowMouseArea.qml:57 +msgctxt "close this window" +msgid "Close" +msgstr "" + +#: rc.cpp:1 +msgid "Now Dock" +msgstr "" + +#: rc.cpp:2 +msgid "Switch between running applications" +msgstr "" diff --git a/po/plasmoid/zh_TW.po b/po/plasmoid/zh_TW.po new file mode 100644 index 000000000..4689182be --- /dev/null +++ b/po/plasmoid/zh_TW.po @@ -0,0 +1,362 @@ +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Jeff Huang <s8321414@gmail.com>, 2016. +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: https://github.com/psifidotos/nowdock-panel/\n" +"POT-Creation-Date: 2016-12-23 15:19+0200\n" +"PO-Revision-Date: 2016-11-26 17:34+0800\n" +"Last-Translator: Jeff Huang <s8321414@gmail.com>\n" +"Language-Team: Chinese <kde-i18n-doc@kde.org>\n" +"Language: zh_TW\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Lokalize 2.0\n" + +#: ../../plasmoid/contents/config/config.qml:26 +msgid "Appearance" +msgstr "外觀" + +#: ../../plasmoid/contents/config/config.qml:31 +msgid "Panel" +msgstr "面板" + +#: ../../plasmoid/contents/config/config.qml:36 +msgid "Interaction" +msgstr "反應" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:68 +msgid "Icon size: " +msgstr "圖示大小:" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:156 +msgid "ver: " +msgstr "版本:" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:163 +msgid "Enable shadows for icons" +msgstr "啟用圖示陰影" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:169 +msgid "Show glow around windows points" +msgstr "在視窗點周圍顯示光暈" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:175 +msgid "Different color for minimized windows" +msgstr "最小化視窗使用不同的顏色" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:181 +msgid "Dots on active window" +msgstr "作用中的視窗顯示點" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:187 +msgid "Reverse position for lines and dots" +msgstr "線與點位置反轉" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:205 +msgid "Animations: " +msgstr "動畫:" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:223 +msgid "duration" +msgstr "期間" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:223 +msgid "disabled" +msgstr "已停用" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:235 +msgid "Zoom" +msgstr "縮放" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:249 +msgid "Level: " +msgstr "等級:" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:293 +msgid "Show a red line on the limit needed for animations" +msgstr "在需要限制的動畫上顯示紅線" + +#: ../../plasmoid/contents/ui/ConfigAppearance.qml.cmake:323 +#: ../../plasmoid/contents/ui/ConfigPanel.qml:169 +msgid "" +"For the disabled settings you should use the Now Dock Panel Configuration " +"Window" +msgstr "為了已停用的設定,您必須使用 Now Dock 面板設定視窗" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:58 +msgid "Cycle through tasks with mouse wheel" +msgstr "使用滑鼠滾輪循環切換工作" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:65 +msgid "Preview windows on hovering" +msgstr "在滑鼠游標停留其上時預覽視窗" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:71 +msgid "Highlight windows on hovering" +msgstr "在滑鼠游標停留其上時突顯視窗" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:77 +msgid "Show window actions in the context menu" +msgstr "在右鍵選單中顯示視窗動作" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:83 +msgid "Show progress information in task buttons" +msgstr "在工具按鍵裡顯示進度資訊" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:88 +msgid "On middle-click:" +msgstr "點擊滑鼠中鍵:" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:94 +msgctxt "The click action" +msgid "None" +msgstr "無" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:94 +msgid "Close Window or Group" +msgstr "關閉視窗或群組" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:94 +msgid "New Instance" +msgstr "新的實體" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:94 +msgid "Minimize/Restore Window or Group" +msgstr "最小化/復原視窗" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:106 +msgid "Filters" +msgstr "過濾器" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:117 +msgid "Show only tasks from the current screen" +msgstr "只顯示目前螢幕上的工作" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:122 +msgid "Show only tasks from the current desktop" +msgstr "只顯示目前桌面上的工作" + +#: ../../plasmoid/contents/ui/ConfigInteraction.qml:127 +msgid "Show only tasks from the current activity" +msgstr "只顯示目前活動裡的工作" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:58 +msgid "Position: " +msgstr "位置:" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:66 +msgid "Center" +msgstr "置中" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:66 +msgid "Left" +msgstr "左邊" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:66 +msgid "Right" +msgstr "右邊" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:66 +msgid "Top" +msgstr "頂部" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:66 +msgid "Bottom" +msgstr "底部" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:74 +msgid "Show bar line for tasks" +msgstr "為工作顯示條狀列" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:81 +msgid "Use plasma theme panel" +msgstr "使用 Plasma 主題面板" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:88 +msgid "Use transparency in the panel" +msgstr "在面板中使用漸層" + +#: ../../plasmoid/contents/ui/ConfigPanel.qml:95 +msgid "Size: " +msgstr "大小:" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:115 +msgctxt "Play previous track" +msgid "Previous Track" +msgstr "前一軌" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:128 +msgctxt "Pause playback" +msgid "Pause" +msgstr "暫停" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:128 +msgctxt "Start playback" +msgid "Play" +msgstr "播放" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:139 +msgctxt "Stop playback" +msgid "Stop" +msgstr "停止" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:147 +msgctxt "Play next track" +msgid "Next Track" +msgstr "下一軌" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:163 +msgctxt "Quit media player app" +msgid "Quit" +msgstr "離開" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:178 +msgctxt "Open or bring to the front window of media player app" +msgid "Restore" +msgstr "恢復" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:223 +msgid "Move To Desktop" +msgstr "移動到桌面" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:245 +msgid "Move To Current Desktop" +msgstr "移到目前桌面" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:254 +msgid "All Desktops" +msgstr "所有桌面" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:283 +msgid "New Desktop" +msgstr "新增桌面" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:303 +msgid "Move To &Activity" +msgstr "移動到活動(&A)" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:324 +msgid "Add To Current Activity" +msgstr "加入到目前的活動" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:334 +msgid "All Activities" +msgstr "所有的活動" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:403 +msgid "Minimize" +msgstr "最小化" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:419 +msgid "Maximize" +msgstr "最大化" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:434 +msgid "More Actions" +msgstr "更多動作" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:442 +msgid "Move" +msgstr "移動" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:451 +msgid "Resize" +msgstr "調整大小" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:460 +msgid "Keep Above Others" +msgstr "顯示在最上層" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:470 +msgid "Keep Below Others" +msgstr "顯示在最下層" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:482 +msgid "Fullscreen" +msgstr "全螢幕" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:494 +msgid "Shade" +msgstr "陰影" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:509 +msgid "Allow this program to be grouped" +msgstr "允許此程式被歸類" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:522 +msgid "Start New Instance" +msgstr "開啟新的實體" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:544 +#: ../../plasmoid/contents/ui/ContextMenu.qml:577 +msgid "Show Launcher On All Activities" +msgstr "在所有活動中顯示啟動器" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:561 +msgid "Show Launcher When Not Running" +msgstr "當未執行時顯示啟動器" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:588 +msgid "Remove Launcher" +msgstr "移除啟動器" + +#: ../../plasmoid/contents/ui/ContextMenu.qml:619 +msgid "Close" +msgstr "關閉" + +#: ../../plasmoid/contents/ui/TaskDelegate.qml:773 +msgid "On %1" +msgstr "於 %1" + +#: ../../plasmoid/contents/ui/TaskDelegate.qml:782 +msgctxt "Which virtual desktop a window is currently on" +msgid "Available on all activities" +msgstr "所有活動都可使用" + +#: ../../plasmoid/contents/ui/TaskDelegate.qml:801 +msgctxt "Activities a window is currently on (apart from the current one)" +msgid "Also available on %1" +msgstr "也可在 %1 上使用" + +#: ../../plasmoid/contents/ui/TaskDelegate.qml:805 +msgctxt "Which activities a window is currently on" +msgid "Available on %1" +msgstr "於 %1 上使用" + +#: ../../plasmoid/contents/ui/ToolTipDelegate.qml:356 +msgctxt "Go to previous song" +msgid "Previous" +msgstr "前一個" + +#: ../../plasmoid/contents/ui/ToolTipDelegate.qml:365 +msgctxt "Pause player" +msgid "Pause" +msgstr "暫停" + +#: ../../plasmoid/contents/ui/ToolTipDelegate.qml:365 +msgctxt "Start player" +msgid "Play" +msgstr "啟動" + +#: ../../plasmoid/contents/ui/ToolTipDelegate.qml:373 +msgctxt "Go to next song" +msgid "Next" +msgstr "下一個" + +#: ../../plasmoid/contents/ui/ToolTipWindowMouseArea.qml:57 +msgctxt "close this window" +msgid "Close" +msgstr "關閉" + +#: rc.cpp:1 +msgid "Now Dock" +msgstr "Now Dock" + +#: rc.cpp:2 +msgid "Switch between running applications" +msgstr "在執行中的應用程式間切換" diff --git a/po/update-metadata.sh b/po/update-metadata.sh new file mode 100644 index 000000000..8be059193 --- /dev/null +++ b/po/update-metadata.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +BASEDIR=".." # root of translatable sources +PROJECT="plasma_applet_org.kde.store.nowdock.panel" # project name +PROJECTPATH="../../containment" # project path +PROJECTPATHPLASMOID="../../plasmoid" # project path +BUGADDR="https://github.com/psifidotos/nowdock-panel/" # MSGID-Bugs +WDIR="`pwd`/containment" # working dir + +cd containment +intltool-merge --quiet --desktop-style . ../../containment.metadata.desktop.template "${PROJECTPATH}"/metadata.desktop.cmake +echo "metadata.desktop files for panel were updated..." + +cd ../plasmoid +intltool-merge --quiet --desktop-style . ../../plasmoid.metadata.desktop.template "${PROJECTPATHPLASMOID}"/metadata.desktop.cmake +echo "metadata.desktop files for plasmoid were updated..." + diff --git a/shell/contents/configuration/NowDockConfiguration-candil.qml b/shell/contents/configuration/NowDockConfiguration-candil.qml new file mode 100644 index 000000000..389f76b38 --- /dev/null +++ b/shell/contents/configuration/NowDockConfiguration-candil.qml @@ -0,0 +1,366 @@ +/* +* Copyright 2016 Smith AR <audoban@openmailbox.org> +* +* This file is part of Candil-Dock +* +* Candil-Dock 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. +* +* Candil-Dock 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 <http://www.gnu.org/licenses/>. +*/ + +import QtQuick 2.5 +import QtQuick.Window 2.2 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles.Plasma 2.0 as Styles +import QtQuick.Layouts 1.1 + +import org.kde.plasma.plasmoid 2.0 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.extras 2.0 as PlasmaExtras + +//import org.candildock.shell 1.0 + +//import "../controls" as Controls + +PlasmaCore.FrameSvgItem { + id: root + + imagePath: "dialogs/background" + + Component.onCompleted: { + console.log("showing candil dock configuration") + } + + property Item dock; + + width: content.width + units.largeSpacing * 2 + height: content.height + units.smallSpacing * 2 + + + //! BEGIN: UI Components + GridLayout { + id: content + + anchors.centerIn: parent + width: implicitWidth + height: implicitHeight + Layout.minimumWidth: width + Layout.minimumHeight: height + + rowSpacing: units.smallSpacing + columnSpacing: units.largeSpacing + + columns: 2 + + PlasmaExtras.Heading { + id: appearanceHeading + Layout.columnSpan: 2 + Layout.fillWidth: true + text: i18nc("@title:group config ui", "Appearance") + level: 2 + } + + //! BEGIN: Location + PlasmaComponents.Label { + Layout.row: 1 + Layout.alignment: Qt.AlignRight + text: i18nc("@label:listbox config ui", "Location:") + } + + PlasmaComponents.ButtonRow { + Layout.fillWidth: true + spacing: 1 + exclusive: true + PlasmaComponents.ToolButton { + text: i18nc("@item:inlistbox config ui, location", "Top") + flat: false + checked: dock.location === edge + checkable: true + property int edge: PlasmaCore.Types.TopEdge + } + PlasmaComponents.ToolButton { + text: i18nc("@item:inlistbox config ui, location", "Bottom") + flat: false + checked: dock.location === edge + checkable: true + property int edge: PlasmaCore.Types.BottomEdge + } + PlasmaComponents.ToolButton { + text: i18nc("@item:inlistbox config ui, location", "Left") + flat: false + checked: dock.location === edge + checkable: true + property int edge: PlasmaCore.Types.LeftEdge + } + PlasmaComponents.ToolButton { + text: i18nc("@item:inlistbox config ui, location", "Right") + flat: false + checked: dock.location === edge + checkable: true + property int edge: PlasmaCore.Types.RightEdge + } + onCheckedButtonChanged: { + dock.location = checkedButton.edge + } + } + //! END: Location + + //! BEGIN: Alignment + PlasmaComponents.Label { + Layout.row: 2 + Layout.alignment: Qt.AlignRight + text: i18nc("@label:listbox config ui", "Alignment:") + } + + PlasmaComponents.ButtonRow { + Layout.fillWidth: true + spacing: 1 + exclusive: true + PlasmaComponents.ToolButton { + text: i18nc("@item:inlistbox config ui, alignment", "Begin") + flat: false + checked: dock.alignment === align + checkable: true + property int align: Dock.Begin + } + PlasmaComponents.ToolButton { + text: i18nc("@item:inlistbox config ui, alignment", "Center") + flat: false + checked: dock.alignment === align + checkable: true + property int align: Dock.Center + } + PlasmaComponents.ToolButton { + text: i18nc("@item:inlistbox config ui, alignment", "End") + flat: false + checked: dock.alignment === align + checkable: true + property int align: Dock.End + } + PlasmaComponents.ToolButton { + text: i18nc("@item:inlistbox config ui, alignment", "Fill") + flat: false + checked: dock.alignment === align + checkable: true + property int align: Dock.Fill + } + onCheckedButtonChanged: { + dock.alignment = checkedButton.align + } + } + + PlasmaComponents.Label { + Layout.row: 3 + Layout.alignment: Qt.AlignRight + text: i18nc("@label:slider config ui, align icons", + "Alignment of icons:") + } + + RowLayout { + Layout.fillWidth: true + spacing: units.largeSpacing + + readonly property int maxOffset: (dock.maxLength - dock.length) / 2 + + PlasmaComponents.Slider { + id: alignmentSlider + Layout.fillWidth: true + enabled: dock.alignment === Dock.Center + maximumValue: 200 + minimumValue: 0 + stepSize: 2 + + Component.onCompleted: { + value = 100 * (dock.offset / parent.maxOffset + 1) + dock.offset = Qt.binding(function () { + return parent.maxOffset * (value / 100 - 1) + }) + } + + updateValueWhileDragging: true + } + PlasmaComponents.Label { + Layout.alignment: Qt.AlignLeft + Layout.minimumWidth: theme.mSize(theme.defaultFont).width * 4 + Layout.maximumWidth: Layout.minimumWidth + + horizontalAlignment: Text.AlignLeft + text: i18nc("@label:slider percent", "%1%", (100 * dock.offset / parent.maxOffset) | 0) + } + } + //! END: Alignment + + //! BEGIN: Icons + PlasmaComponents.Label { + Layout.row: 4 + Layout.alignment: Qt.AlignRight + text: i18nc("@label:spinbox config ui, size icons", "Size icons:") + } + + RowLayout { + Layout.fillWidth: true + spacing: units.largeSpacing + + /* Controls.SpinBox { + suffix: i18nc( + "@item:spinbox config ui, size icons, suffix in pixels", + " pixels") + maximumValue: 128 + minimumValue: 48 + stepSize: 2 + value: dock.iconSize + + onValueChanged: { + dock.iconSize = value + } + }*/ + + PlasmaComponents.Label { + text: i18nc("@label:slider config ui, icons zoom", "Zoom:") + Layout.alignment: Qt.AlignRight + } + + PlasmaComponents.Slider { + Layout.fillWidth: true + value: 100 + maximumValue: 200 + minimumValue: 100 + stepSize: 1 + updateValueWhileDragging: true + + Component.onCompleted: { + value = (dock.zoomFactor * 100) | 0 + dock.zoomFactor = Qt.binding(function(){ + return value / 100 + }) + } + } + } + //! END: Icons + PlasmaExtras.Heading { + Layout.row: 5 + Layout.columnSpan: 2 + Layout.fillWidth: true + text: i18nc("@title:group config ui", "Behavior") + level: 2 + } + //! BEGIN: Visibility + PlasmaComponents.Label { + Layout.row: 6 + Layout.alignment: Qt.AlignRight + text: i18nc("@label:listbox config ui", "Visibility:") + } + + PlasmaComponents.ComboBox { + Layout.fillWidth: true + model: [i18nc("@item:inlistbox config ui, visibility", + "Normal"), i18nc( + "@item:inlistbox config ui, visibility", + "Auto hide"), i18nc( + "@item:inlistbox config ui, visibility", + "Dodge active window"), i18nc( + "@item:inlistbox config ui, visibility", "Dodge windows")] + currentIndex: dock.visibility.mode + onActivated: { + dock.visibility.mode = index + } + } + + RowLayout { + Layout.row: 7 + Layout.column: 1 + Layout.fillWidth: true + spacing: units.largeSpacing + + PlasmaComponents.Label { + Layout.fillWidth: false + horizontalAlignment: Text.AlignRight + text: i18nc("@label:spinbox config ui, timers", + "Delay in show:") + } + /* Controls.SpinBox { + enabled: dock.visibility.mode !== Dock.Normal + Layout.fillWidth: false + Layout.maximumWidth: implicitWidth + maximumValue: 3000 + minimumValue: 0 + value: dock.visibility.timerShow + stepSize: 100 + + onValueChanged: { + dock.visibility.timerShow = value + } + + suffix: i18nc( + "@item:spinbox config ui, suffix in milliseconds", + " ms") + }*/ + PlasmaComponents.Label { + Layout.fillWidth: true + horizontalAlignment: Text.AlignRight + text: i18nc("@label:spinbox config ui, timers", + "Delay in hidding:") + } + /* Controls.SpinBox { + enabled: dock.visibility.mode !== Dock.Normal + Layout.fillWidth: false + Layout.maximumWidth: implicitWidth + maximumValue: 3000 + minimumValue: 0 + value: dock.visibility.timerHide + stepSize: 100 + + onValueChanged: { + dock.visibility.timerHide = value + } + + suffix: i18nc( + "@item:spinbox config ui, suffix in milliseconds", + " ms") + }*/ + } + + RowLayout { + Layout.row: 8 + Layout.column: 1 + Layout.fillWidth: true + spacing: units.largeSpacing + + PlasmaComponents.Label { + text: i18nc("@option:check config ui", "Show in all screens:") + Layout.alignment: Qt.AlignRight + } + Switch { + id: switchScreen + // TODO: Show on all screens + style: Styles.SwitchStyle { + property bool checked: switchScreen.checked + } + } + PlasmaComponents.ComboBox { + Layout.fillWidth: true + Component.onCompleted: { + var screens = [] + + for (var i = 0; i < dock.screens.length; i++) { + screens.push(dock.screens[i].name) + } + + model = screens + } + } + } + //! END: Visibility + } + //! END: UI Components +} diff --git a/shell/contents/configuration/NowDockConfiguration.qml b/shell/contents/configuration/NowDockConfiguration.qml new file mode 100644 index 000000000..e9623375e --- /dev/null +++ b/shell/contents/configuration/NowDockConfiguration.qml @@ -0,0 +1,606 @@ +import QtQuick 2.0 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.0 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents + +import org.kde.plasma.plasmoid 2.0 + +import org.kde.nowdock 0.1 as NowDock + +PlasmaCore.FrameSvgItem { + imagePath: "dialogs/background" + + width: Math.max(420,noneShadow.width + lockedAppletsShadow.width + allAppletsShadow.width) + height: mainColumn.height+10 + + property bool panelIsVertical: plasmoid.formFactor === PlasmaCore.Types.Vertical + + signal updateThickness(); + signal removeInternalViewSplitter(); + signal addInternalViewSplitter(); + + Column{ + id:mainColumn + anchors.horizontalCenter: parent.horizontalCenter + spacing: 1.5*theme.defaultFont.pointSize + width: parent.width - 10 + + Column{ + width:parent.width + spacing: 0.8*theme.defaultFont.pointSize + + RowLayout{ + width: parent.width + PlasmaComponents.Label{ + text: i18n("Applets Alignment") + font.pointSize: 1.5 * theme.defaultFont.pointSize + Layout.alignment: Qt.AlignLeft + } + + PlasmaComponents.Label{ + font.pointSize: theme.defaultFont.pointSize + font.italic: true + opacity: 0.6 + + Layout.alignment: Qt.AlignRight + horizontalAlignment: Text.AlignRight + // width: parent.width + + text: i18n("ver: ") +"@VERSION@" + + } + } + + //user set Panel Positions + // 0-Center, 1-Left, 2-Right, 3-Top, 4-Bottom + Flow{ + width: parent.width + spacing: 2 + + property bool inStartup: true + property int panelPosition: plasmoid.configuration.panelPosition + + + function updatePanelPositionVisual(){ + if((panelPosition == NowDock.Types.Left)||(panelPosition == NowDock.Types.Top)){ + firstPosition.checked = true; + centerPosition.checked = false; + lastPosition.checked = false; + splitTwoPosition.checked = false; + removeInternalViewSplitter(); + } + else if(panelPosition == NowDock.Types.Center){ + firstPosition.checked = false; + centerPosition.checked = true; + lastPosition.checked = false; + splitTwoPosition.checked = false; + removeInternalViewSplitter(); + } + else if((panelPosition == NowDock.Types.Right)||(panelPosition == NowDock.Types.Bottom)){ + firstPosition.checked = false; + centerPosition.checked = false; + lastPosition.checked = true; + splitTwoPosition.checked = false; + removeInternalViewSplitter(); + } + else if (panelPosition == NowDock.Types.Double){ + firstPosition.checked = false; + centerPosition.checked = false; + lastPosition.checked = false; + splitTwoPosition.checked = true; + //add the splitter visual + addInternalViewSplitter(-1); + } + } + + onPanelPositionChanged: updatePanelPositionVisual(); + + Component.onCompleted: { + updatePanelPositionVisual(); + inStartup = false; + } + + PlasmaComponents.Button{ + id: firstPosition + checkable: true + text: panelIsVertical ? i18n("Top") : i18n("Left") + width: (parent.width / 3) - 1 + + onCheckedChanged: { + if(checked && !parent.inStartup){ + if(panelIsVertical) + plasmoid.configuration.panelPosition = NowDock.Types.Top + else + plasmoid.configuration.panelPosition = NowDock.Types.Left + } + } + onClicked: checked=true; + } + PlasmaComponents.Button{ + id: centerPosition + checkable: true + text: i18n("Center") + width: (parent.width / 3) - 1 + + onCheckedChanged: { + if(checked && !parent.inStartup){ + plasmoid.configuration.panelPosition = NowDock.Types.Center + } + } + onClicked: checked=true; + } + PlasmaComponents.Button{ + id: lastPosition + checkable: true + text: panelIsVertical ? i18n("Bottom") : i18n("Right") + width: (parent.width / 3) - 2 + + onCheckedChanged: { + if(checked && !parent.inStartup){ + if(panelIsVertical) + plasmoid.configuration.panelPosition = NowDock.Types.Bottom + else + plasmoid.configuration.panelPosition = NowDock.Types.Right + } + } + onClicked: checked=true; + } + + PlasmaComponents.Button{ + id: splitTwoPosition + checkable: true + text: panelIsVertical ? i18n("Top")+ " | "+ i18n("Bottom") : i18n("Left") +" | "+ i18n("Right") + width: parent.width + + onCheckedChanged: { + if(checked && !parent.inStartup){ + plasmoid.configuration.panelPosition = NowDock.Types.Double; + } + } + onClicked: checked=true; + } + } + } + + + // BelowActive = 0, /** always visible except if ovelaps with the active window, no area reserved */ + // BelowMaximized, /** always visible except if ovelaps with an active maximize window, no area reserved */ + // LetWindowsCover, /** always visible, windows will go over the panel, no area reserved */ + // WindowsGoBelow, /** default, always visible, windows will go under the panel, no area reserved */ + // AutoHide, /** the panel will be shownn only if the mouse cursor is on screen edges */ + // AlwaysVisible, /** always visible panel, "Normal" plasma panel, accompanies plasma's "Always Visible" */ + /********** Panel Visibility ****************/ + + Column{ + width:parent.width + spacing: 0.8*theme.defaultFont.pointSize + PlasmaComponents.Label{ + text: i18n("Visibility") + font.pointSize: 1.5 * theme.defaultFont.pointSize + } + + //user set Panel Visibility + // 0-BelowActive, 1-BelowMaximized, 2-LetWindowsCover, 3-WindowsGoBelow, 4-AutoHide, 5-AlwaysVisible + Flow{ + width: parent.width + spacing: 2 + + property bool inStartup: true + property int panelVisibility: plasmoid.configuration.panelVisibility + + + function updatePanelVisibilityVisual(){ + if (panelVisibility === 0) + firstState.checked = true; + else + firstState.checked = false; + + if (panelVisibility === 1) + secondState.checked = true; + else + secondState.checked = false; + + if (panelVisibility === 2) + thirdState.checked = true; + else + thirdState.checked = false; + + if (panelVisibility === 3) + fourthState.checked = true; + else + fourthState.checked = false; + + if (panelVisibility === 4) + fifthState.checked = true; + else + fifthState.checked = false; + + if (panelVisibility === 5) + sixthState.checked = true; + else + sixthState.checked = false; + } + + onPanelVisibilityChanged: updatePanelVisibilityVisual(); + + Component.onCompleted: { + updatePanelVisibilityVisual(); + inStartup = false; + } + + PlasmaComponents.Button{ + id: firstState + checkable: true + text: i18n("Below Active") + width: (parent.width / 2) - 1 + + onCheckedChanged: { + if(checked && !parent.inStartup){ + plasmoid.configuration.panelVisibility = 0 + } + } + onClicked: checked=true; + } + PlasmaComponents.Button{ + id: secondState + checkable: true + text: i18n("Below Maximized") + width: (parent.width / 2) - 1 + + onCheckedChanged: { + if(checked && !parent.inStartup){ + plasmoid.configuration.panelVisibility = 1 + } + } + onClicked: checked=true; + } + PlasmaComponents.Button{ + id: thirdState + checkable: true + text: i18n("Let Windows Cover") + width: (parent.width / 2) - 1 + + onCheckedChanged: { + if(checked && !parent.inStartup){ + plasmoid.configuration.panelVisibility = 2 + } + } + onClicked: checked=true; + } + + PlasmaComponents.Button{ + id: fourthState + checkable: true + text: i18n("Windows Go Below") + width: (parent.width/2) - 1 + + onCheckedChanged: { + if(checked && !parent.inStartup){ + plasmoid.configuration.panelVisibility = 3 + } + } + onClicked: checked=true; + } + + PlasmaComponents.Button{ + id: fifthState + checkable: true + text: i18n("Auto Hide") + width: (parent.width/2) - 1 + + onCheckedChanged: { + if(checked && !parent.inStartup){ + plasmoid.configuration.panelVisibility = 4 + } + } + onClicked: checked=true; + } + PlasmaComponents.Button{ + id: sixthState + checkable: true + text: i18n("Always Visible") + width: (parent.width/2) - 1 + + onCheckedChanged: { + if(checked && !parent.inStartup){ + plasmoid.configuration.panelVisibility = 5 + } + } + onClicked: checked=true; + } + } + } + + //////////////// Applets Size + + Column{ + width:parent.width + spacing: 0.8*theme.defaultFont.pointSize + + PlasmaComponents.Label{ + text: i18n("Applets Size") + font.pointSize: 1.5 * theme.defaultFont.pointSize + } + + RowLayout{ + width: parent.width + + property int step: 8 + + PlasmaComponents.Button{ + text:"-" + + Layout.preferredWidth: parent.height + Layout.preferredHeight: parent.height + + onClicked: appletsSizeSlider.value -= parent.step + } + + PlasmaComponents.Slider{ + id:appletsSizeSlider + + valueIndicatorText: i18n("Applets Size") + valueIndicatorVisible: true + + minimumValue: 16 + maximumValue: 128 + + stepSize: parent.step + + Layout.fillWidth:true + + property bool inStartup:true + + Component.onCompleted: { + value = plasmoid.configuration.iconSize; + inStartup = false; + } + + onValueChanged:{ + if(!inStartup){ + plasmoid.configuration.iconSize = value; + updateThickness(); + } + } + } + + PlasmaComponents.Button{ + text:"+" + + Layout.preferredWidth: parent.height + Layout.preferredHeight: parent.height + + onClicked: appletsSizeSlider.value += parent.step; + } + + PlasmaComponents.Label{ + text: plasmoid.configuration.iconSize + " px." + } + } + } + + + + /********** Zoom On Hover ****************/ + Column{ + width: parent.width + spacing: 0.8*theme.defaultFont.pointSize + PlasmaComponents.Label{ + text: i18n("Zoom On Hover") + font.pointSize: 1.5 * theme.defaultFont.pointSize + } + + RowLayout{ + width: parent.width + + PlasmaComponents.Button{ + text:"-" + + Layout.preferredWidth: parent.height + Layout.preferredHeight: parent.height + + onClicked: zoomSlider.value -= 0.05 + } + + PlasmaComponents.Slider{ + id:zoomSlider + + valueIndicatorText: i18n("Zoom Factor") + valueIndicatorVisible: true + + minimumValue: 1 + maximumValue: 2 + + stepSize: 0.05 + + Layout.fillWidth:true + + property bool inStartup:true + + Component.onCompleted: { + value = Number(1 + plasmoid.configuration.zoomLevel/20).toFixed(2) + inStartup = false; + // console.log("Slider:"+value); + } + + onValueChanged:{ + if(!inStartup){ + var result = Math.round((value - 1)*20) + plasmoid.configuration.zoomLevel = result + // console.log("Store:"+result); + } + } + } + + PlasmaComponents.Button{ + text:"+" + + Layout.preferredWidth: parent.height + Layout.preferredHeight: parent.height + + onClicked: zoomSlider.value += 0.05 + } + + PlasmaComponents.Label{ + enabled: showBackground.checked + text: " "+Number(zoomSlider.value).toFixed(2) + } + + } + } + + + Column{ + width: parent.width + spacing: 0.8*theme.defaultFont.pointSize + PlasmaComponents.Label{ + text: i18n("Background") + font.pointSize: 1.5 * theme.defaultFont.pointSize + } + + PlasmaComponents.CheckBox{ + id: showBackground + text: i18n("Show Panel Background") + + property bool inStartup: true + onCheckedChanged:{ + if(!inStartup) + plasmoid.configuration.useThemePanel = checked; + } + + Component.onCompleted: { + checked = plasmoid.configuration.useThemePanel; + inStartup = false; + } + } + + RowLayout{ + width: parent.width + + PlasmaComponents.Button{ + enabled: showBackground.checked + text:"-" + + Layout.preferredWidth: parent.height + Layout.preferredHeight: parent.height + + onClicked: panelSizeSlider.value -= 2 + } + + PlasmaComponents.Slider{ + id:panelSizeSlider + enabled: showBackground.checked + valueIndicatorText: i18n("Size") + valueIndicatorVisible: true + + minimumValue: 0 + maximumValue: 256 + + stepSize: 2 + + Layout.fillWidth:true + + property bool inStartup: true + + Component.onCompleted: { + value = plasmoid.configuration.panelSize + inStartup = false; + } + + onValueChanged: { + if(!inStartup) + plasmoid.configuration.panelSize = value; + } + } + + PlasmaComponents.Button{ + enabled: showBackground.checked + text:"+" + + Layout.preferredWidth: parent.height + Layout.preferredHeight: parent.height + + onClicked: panelSizeSlider.value += 2 + } + + + PlasmaComponents.Label{ + enabled: showBackground.checked + text: panelSizeSlider.value + " px." + } + + } + } + + Column{ + width: parent.width + spacing: 0.8*theme.defaultFont.pointSize + PlasmaComponents.Label{ + text: i18n("Shadows") + font.pointSize: 1.5 * theme.defaultFont.pointSize + } + + RowLayout { + width: parent.width + + ExclusiveGroup { + id: shadowsGroup + property bool inStartup: true + + onCurrentChanged: { + if (!inStartup) { + if (current === noneShadow){ + plasmoid.configuration.shadows = 0; /*No Shadows*/ + } else if (current === lockedAppletsShadow){ + plasmoid.configuration.shadows = 1; /*Locked Applets Shadows*/ + } else if (current === allAppletsShadow){ + plasmoid.configuration.shadows = 2; /*All Applets Shadows*/ + } + } + } + + Component.onCompleted: { + if (plasmoid.configuration.shadows === 0 /*No Shadows*/){ + noneShadow.checked = true; + } else if (plasmoid.configuration.shadows === 1 /*Locked Applets*/) { + lockedAppletsShadow.checked = true; + } else if (plasmoid.configuration.shadows === 2 /*All Applets*/) { + allAppletsShadow.checked = true; + } + + inStartup = false; + } + } + + PlasmaComponents.RadioButton { + id: noneShadow + text: i18n("None") + exclusiveGroup: shadowsGroup + } + PlasmaComponents.RadioButton { + id: lockedAppletsShadow + text: i18n("Only for locked applets") + exclusiveGroup: shadowsGroup + } + PlasmaComponents.RadioButton { + id: allAppletsShadow + text: i18n("All applets") + exclusiveGroup: shadowsGroup + } + + } + } + + PlasmaComponents.Button{ + enabled: true + text: i18n("Add New Dock") + + onClicked: dock.addNewDock(); + } + } +} + + diff --git a/shell/contents/configuration/config.qml b/shell/contents/configuration/config.qml new file mode 100644 index 000000000..4db1d3a9d --- /dev/null +++ b/shell/contents/configuration/config.qml @@ -0,0 +1,30 @@ +/* +* Copyright 2016 Smith AR <audoban@openmailbox.org> +* +* This file is part of Candil-Dock +* +* Candil-Dock 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. +* +* Candil-Dock 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 <http://www.gnu.org/licenses/>. +*/ + +import QtQuick 2.0 + +import org.kde.plasma.configuration 2.0 + +ConfigModel { + ConfigCategory { + name: i18n("General") + icon: "preferences-system-windows" + source: "NowDockConfiguration.qml" + } +} diff --git a/shell/contents/views/Panel.qml b/shell/contents/views/Panel.qml new file mode 100644 index 000000000..5f5c4b531 --- /dev/null +++ b/shell/contents/views/Panel.qml @@ -0,0 +1,114 @@ +/* + * Copyright 2012 Marco Martin <mart@kde.org> + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + +import QtQuick 2.0 +import QtQuick.Layouts 1.1 + +import org.kde.plasma.core 2.0 as PlasmaCore + +PlasmaCore.FrameSvgItem { + id: root + + //imagePath: containment && containment.backgroundHints === PlasmaCore.Types.NoBackground ? "" : "widgets/panel-background" + //imagePath: "widgets/panel-background" + imagePath: "" + prefix:"" + // onRepaintNeeded: adjustPrefix(); + + //enabledBorders: panel.enabledBorders + + property Item containment + property Item dockLayout + + readonly property bool verticalPanel: containment && containment.formFactor === PlasmaCore.Types.Vertical + + /* Rectangle{ + anchors.fill: parent + color: "transparent" + border.color: "blue" + border.width: 1 + }*/ + + function adjustPrefix() { + if (!containment) { + return ""; + } + var pre; + switch (containment.location) { + case PlasmaCore.Types.LeftEdge: + pre = "west"; + break; + case PlasmaCore.Types.TopEdge: + pre = "north"; + break; + case PlasmaCore.Types.RightEdge: + pre = "east"; + break; + case PlasmaCore.Types.BottomEdge: + pre = "south"; + break; + default: + prefix = ""; + } + if (hasElementPrefix(pre)) { + prefix = pre; + } else { + prefix = ""; + } + } + + onContainmentChanged: { + if (!containment) { + return; + } + containment.parent = containmentParent; + containment.visible = true; + containment.anchors.fill = containmentParent; + containment.locationChanged.connect(adjustPrefix); + adjustPrefix(); + + for(var i=0; i<containment.children.length; ++i){ + if (containment.children[i].objectName === "dockLayoutView") { + dockLayout = containment.children[i]; + dockLayout.dockView = panel; + } + } + } + + Binding { + target: panel + property: "length" + when: containment + value: { + if (!containment) { + return; + } + if (verticalPanel) { + return containment.Layout.preferredHeight + } else { + return containment.Layout.preferredWidth + } + } + } + + + Item { + id: containmentParent + anchors.fill: parent + } +} diff --git a/shell/metadata.desktop b/shell/metadata.desktop new file mode 100644 index 000000000..6f457f040 --- /dev/null +++ b/shell/metadata.desktop @@ -0,0 +1,14 @@ +[Desktop Entry] +Name=Now Dock Shell +Encoding=UTF-8 +Keywords=shell +Type=Service + +X-KDE-ServiceTypes=Plasma/Shell +X-KDE-ParentApp=nowdock +X-KDE-PluginInfo-Author=Michail Vourlakos +X-KDE-PluginInfo-Email=mvourlakos@gmail.com +X-KDE-PluginInfo-License=GPLv3+ +X-KDE-PluginInfo-Name=org.kde.nowdock.shell +X-KDE-PluginInfo-Version=1.0 +X-KDE-PluginInfo-Website=https://github.com/psifidotos/nowdock-panel diff --git a/uninstall.sh b/uninstall.sh new file mode 100644 index 000000000..c91a3c791 --- /dev/null +++ b/uninstall.sh @@ -0,0 +1,11 @@ +#!/bin/bash +#Author: Michail Vourlakos +#Summary: Uninstallation script for Now Dock Panel +#This script was written and tested on openSuSe Leap 42.1 + +if [ -f build/install_manifest.txt ]; then + echo "Uninstallation file exists..." + sudo xargs rm < build/install_manifest.txt +else + echo "Uninstallation file does not exist." +fi