summaryrefslogtreecommitdiff
path: root/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com
diff options
context:
space:
mode:
authornippy <you@example.com>2026-04-18 13:49:56 +0200
committernippy <you@example.com>2026-04-18 13:49:56 +0200
commit5d94c0a7d44a2255b81815a52a7056a94a39842d (patch)
tree759bdea9645c6a62f9e1e4c001f7d81cccd120d2 /raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com
parente79cdf210b267f21a186255ce1a4d50029439d54 (diff)
downloadRaveOS-PKGBUILD-5d94c0a7d44a2255b81815a52a7056a94a39842d.tar.gz
RaveOS-PKGBUILD-5d94c0a7d44a2255b81815a52a7056a94a39842d.zip
update Raveos themes
Diffstat (limited to 'raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com')
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/COPYING339
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/README.md53
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/Settings.ui3100
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appIconIndicators.js1305
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appIcons.js1635
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appIconsDecorator.js168
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appSpread.js235
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dash.js1177
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dbusmenuUtils.js280
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dependencies/gi.js14
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dependencies/shell/extensions/extension.js1
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dependencies/shell/misc.js5
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dependencies/shell/ui.js18
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/desktopIconsIntegration.js170
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/docking.js2698
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/extension.js18
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/fileManager1API.js201
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/imports.js17
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/intellihide.js337
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/launcherAPI.js287
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ar/LC_MESSAGES/dashtodock.mobin0 -> 16188 bytes
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/be/LC_MESSAGES/dashtodock.mobin0 -> 17759 bytes
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/bg/LC_MESSAGES/dashtodock.mobin0 -> 3761 bytes
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/cs/LC_MESSAGES/dashtodock.mobin0 -> 10315 bytes
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/de/LC_MESSAGES/dashtodock.mobin0 -> 14194 bytes
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/el/LC_MESSAGES/dashtodock.mobin0 -> 12042 bytes
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/es/LC_MESSAGES/dashtodock.mobin0 -> 12854 bytes
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/eu/LC_MESSAGES/dashtodock.mobin0 -> 10171 bytes
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/fr/LC_MESSAGES/dashtodock.mobin0 -> 14414 bytes
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/gl/LC_MESSAGES/dashtodock.mobin0 -> 8811 bytes
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/hu/LC_MESSAGES/dashtodock.mobin0 -> 14071 bytes
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/id/LC_MESSAGES/dashtodock.mobin0 -> 8549 bytes
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/it/LC_MESSAGES/dashtodock.mobin0 -> 11038 bytes
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ja/LC_MESSAGES/dashtodock.mobin0 -> 12116 bytes
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ka/LC_MESSAGES/dashtodock.mobin0 -> 21983 bytes
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/kk/LC_MESSAGES/dashtodock.mobin0 -> 18116 bytes
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ko/LC_MESSAGES/dashtodock.mobin0 -> 14009 bytes
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/lv/LC_MESSAGES/dashtodock.mobin0 -> 13719 bytes
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/nb/LC_MESSAGES/dashtodock.mobin0 -> 9191 bytes
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/nl/LC_MESSAGES/dashtodock.mobin0 -> 10065 bytes
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/pl/LC_MESSAGES/dashtodock.mobin0 -> 10837 bytes
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/pt/LC_MESSAGES/dashtodock.mobin0 -> 9758 bytes
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/pt_BR/LC_MESSAGES/dashtodock.mobin0 -> 11998 bytes
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ro/LC_MESSAGES/dashtodock.mobin0 -> 14377 bytes
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ru/LC_MESSAGES/dashtodock.mobin0 -> 18114 bytes
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sk/LC_MESSAGES/dashtodock.mobin0 -> 8918 bytes
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sl/LC_MESSAGES/dashtodock.mobin0 -> 13120 bytes
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sr/LC_MESSAGES/dashtodock.mobin0 -> 11174 bytes
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sr@latin/LC_MESSAGES/dashtodock.mobin0 -> 8812 bytes
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sv/LC_MESSAGES/dashtodock.mobin0 -> 13656 bytes
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/tr/LC_MESSAGES/dashtodock.mobin0 -> 14912 bytes
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/uk_UA/LC_MESSAGES/dashtodock.mobin0 -> 18074 bytes
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/zh_CN/LC_MESSAGES/dashtodock.mobin0 -> 12245 bytes
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/zh_TW/LC_MESSAGES/dashtodock.mobin0 -> 12073 bytes
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locations.js1535
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locationsWorker.js91
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/media/glossy.svg139
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/media/highlight_stacked_bg.svg82
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/media/highlight_stacked_bg_h.svg82
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/media/logo.svg528
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/metadata.json18
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/notificationsMonitor.js121
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/prefs.js1168
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/schemas/gschemas.compiledbin0 -> 8725 bytes
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/schemas/org.gnome.shell.extensions.dash-to-dock.gschema.xml644
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/stylesheet.css1478
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/theming.js572
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/utils.js735
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/windowPreview.js678
69 files changed, 19929 insertions, 0 deletions
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/COPYING b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/COPYING
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) 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
+this service 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 make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. 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.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the 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 a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE 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.
+
+ 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
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <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 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.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision 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, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This 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.
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/README.md b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/README.md
new file mode 100644
index 0000000..b0dc634
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/README.md
@@ -0,0 +1,53 @@
+# Dash to Dock
+![screenshot](https://github.com/micheleg/dash-to-dock/raw/master/media/screenshot.jpg)
+
+## A dock for the GNOME Shell
+This extension enhances the dash moving it out of the overview and transforming it in a dock for an easier launching of applications and a faster switching between windows and desktops without having to leave the desktop view.
+
+[<img src="https://micheleg.github.io/dash-to-dock/media/get-it-on-ego.png" height="100">](https://extensions.gnome.org/extension/307/dash-to-dock)
+
+For additional installation instructions and more information visit [https://micheleg.github.io/dash-to-dock/](https://micheleg.github.io/dash-to-dock/).
+
+## Installation from source
+
+The extension can be installed directly from source, either for the convenience of using git or to test the latest development version. Clone the desired branch with git
+
+### Build Dependencies
+
+To compile the stylesheet you'll need an implementation of SASS. Dash to Dock supports `dart-sass` (`sass`), `sassc`, and `ruby-sass`. Every distro should have at least one of these implementations, we recommend using `dart-sass` (`sass`) or `sassc` over `ruby-sass` as `ruby-sass` is deprecated.
+
+By default, Dash to Dock will attempt to build with `sassc`. To change this behavior set the `SASS` environment variable to either `dart` or `ruby`.
+
+```bash
+export SASS=dart
+# or...
+export SASS=ruby
+```
+
+### Building
+
+Clone the repository or download the branch from github. A simple Makefile is included.
+
+Next use `make` to install the extension into your home directory. A Shell reload is required <kbd>Alt</kbd> + <kbd>F2</kbd> <kbd>r</kbd> <kbd>Enter</kbd> under Xorg or under Wayland you may have to logout and login. The extension has to be enabled with *gnome-extensions-app* (GNOME Extensions) or with *dconf*.
+
+```bash
+git clone https://github.com/micheleg/dash-to-dock.git
+make -C dash-to-dock install
+```
+
+If `msgfmt` is not available on your system, you will see an error message like the following:
+
+```bash
+make: msgfmt: No such file or directory
+```
+
+In this case install the `gettext` package from your distribution's repository.
+
+
+## Bug Reporting
+
+Bugs should be reported to the Github bug tracker [https://github.com/micheleg/dash-to-dock/issues](https://github.com/micheleg/dash-to-dock/issues).
+
+## License
+Dash to Dock Gnome Shell extension is distributed under the terms of the GNU General Public License,
+version 2 or later. See the COPYING file for details.
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/Settings.ui b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/Settings.ui
new file mode 100644
index 0000000..b16ca59
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/Settings.ui
@@ -0,0 +1,3100 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk" version="4.0"/>
+ <object class="GtkAdjustment" id="animation_time_adjustment">
+ <property name="upper">1</property>
+ <property name="step-increment">0.050000000000000003</property>
+ <property name="page-increment">0.25</property>
+ </object>
+ <object class="GtkBox" id="box_middle_click_options">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkFrame" id="frame_middle_click_options">
+ <property name="can_focus">0</property>
+ <property name="child">
+ <object class="GtkListBox" id="listbox_middle_click_options">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="vexpand">1</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow11">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="child">
+ <object class="GtkGrid" id="buitin_theme7">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="builtin_theme_description4">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">When set to minimize, double clicking minimizes all the windows of the application.</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</property>
+ <property name="max_width_chars">40</property>
+
+ <style>
+ <class name="dim-label"/>
+ </style>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="builtin_theme_label4">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">Shift+Click action</property>
+ <property name="xalign">0</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="shift_click_action_combo">
+ <property name="can_focus">0</property>
+ <property name="valign">center</property>
+ <items>
+ <item translatable="yes">Raise window</item>
+ <item translatable="yes">Minimize window</item>
+ <item translatable="yes">Launch new instance</item>
+ <item translatable="yes">Cycle through windows</item>
+ <item translatable="yes">Minimize or overview</item>
+ <item translatable="yes">Show window previews</item>
+ <item translatable="yes">Minimize or show previews</item>
+ <item translatable="yes">Focus or show previews</item>
+ <item translatable="yes">Focus or app spread</item>
+ <item translatable="yes">Focus, minimize or show previews</item>
+ <item translatable="yes">Focus, minimize or app spread</item>
+ <item translatable="yes">Quit</item>
+ </items>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ <property name="row-span">2</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow_middle_click">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="child">
+ <object class="GtkGrid" id="grid_middle_click">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="description_middle_click">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">Behavior for Middle-Click.</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</property>
+ <property name="max_width_chars">40</property>
+
+ <style>
+ <class name="dim-label"/>
+ </style>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label_middle_click">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">Middle-Click action</property>
+ <property name="xalign">0</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="middle_click_action_combo">
+ <property name="can_focus">0</property>
+ <property name="valign">center</property>
+ <items>
+ <item translatable="yes">Raise window</item>
+ <item translatable="yes">Minimize window</item>
+ <item translatable="yes">Launch new instance</item>
+ <item translatable="yes">Cycle through windows</item>
+ <item translatable="yes">Minimize or overview</item>
+ <item translatable="yes">Show window previews</item>
+ <item translatable="yes">Minimize or show previews</item>
+ <item translatable="yes">Focus or show previews</item>
+ <item translatable="yes">Focus or app spread</item>
+ <item translatable="yes">Focus, minimize or show previews</item>
+ <item translatable="yes">Focus, minimize or app spread</item>
+ <item translatable="yes">Quit</item>
+ </items>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ <property name="row-span">2</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow_shift_middle_click">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="child">
+ <object class="GtkGrid" id="grid_shift_middle_click">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="description_shift_middle_click">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">Behavior for Shift+Middle-Click.</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</property>
+ <property name="max_width_chars">40</property>
+
+ <style>
+ <class name="dim-label"/>
+ </style>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label_shift_middle_click">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">Shift+Middle-Click action</property>
+ <property name="xalign">0</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="shift_middle_click_action_combo">
+ <property name="can_focus">0</property>
+ <property name="valign">center</property>
+ <items>
+ <item translatable="yes">Raise window</item>
+ <item translatable="yes">Minimize window</item>
+ <item translatable="yes">Launch new instance</item>
+ <item translatable="yes">Cycle through windows</item>
+ <item translatable="yes">Minimize or overview</item>
+ <item translatable="yes">Show window previews</item>
+ <item translatable="yes">Minimize or show previews</item>
+ <item translatable="yes">Focus or show previews</item>
+ <item translatable="yes">Focus or app spread</item>
+ <item translatable="yes">Focus, minimize or show previews</item>
+ <item translatable="yes">Focus, minimize or app spread</item>
+ <item translatable="yes">Quit</item>
+ </items>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ <property name="row-span">2</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </property>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ <object class="GtkAdjustment" id="custom_opacity_adjustement">
+ <property name="upper">1</property>
+ <property name="step-increment">0.01</property>
+ <property name="page-increment">0.10</property>
+ </object>
+ <object class="GtkAdjustment" id="dock_size_adjustment">
+ <property name="lower">0.33</property>
+ <property name="upper">1</property>
+ <property name="step-increment">0.01</property>
+ <property name="page-increment">0.10</property>
+ </object>
+ <object class="GtkAdjustment" id="dot_border_width_adjustment">
+ <property name="upper">10</property>
+ <property name="step-increment">1</property>
+ <property name="page-increment">5</property>
+ </object>
+ <object class="GtkBox" id="running_dots_advance_settings_box">
+ <property name="hexpand">1</property>
+ <property name="vexpand">1</property>
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkFrame" id="frame1">
+ <property name="can_focus">0</property>
+
+ <property name="child">
+ <object class="GtkListBox" id="listbox7">
+ <property name="can_focus">0</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow10">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="child">
+ <object class="GtkBox" id="dot_style_box">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="unity_backlit_items_label">
+ <property name="hexpand">1</property>
+ <property name="xalign">0</property>
+ <property name="can_focus">0</property>
+ <property name="valign">center</property>
+ <property name="label" translatable="yes">Enable Unity7 like glossy backlit items</property>
+
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="unity_backlit_items_switch">
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="apply_gloss_effect_checkbutton">
+ <property name="label" translatable="yes">Apply glossy effect.</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <child>
+ <object class="GtkLabel" id="dominant_color_label">
+ <property name="hexpand">1</property>
+ <property name="xalign">0</property>
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Use dominant color</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="dominant_color_switch"/>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkGrid" id="grid1">
+ <property name="can_focus">0</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkSwitch" id="dot_style_switch">
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label4">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Customize indicator style</property>
+ <property name="justify">fill</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="dot_style_settings_box">
+ <property name="can_focus">0</property>
+ <property name="margin_bottom">1</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkBox" id="dot_color_box">
+ <property name="can_focus">0</property>
+ <property name="spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="dot_color_label">
+ <property name="hexpand">1</property>
+ <property name="xalign">0</property>
+ <property name="can_focus">0</property>
+ <property name="label" translatable="yes">Color</property>
+
+ </object>
+ </child>
+ <child>
+ <object class="GtkColorButton" id="dot_color_colorbutton">
+ <property name="receives_default">1</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="border_color_box">
+ <property name="can_focus">0</property>
+ <property name="spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="dot_border_color_label">
+ <property name="hexpand">1</property>
+ <property name="xalign">0</property>
+ <property name="can_focus">0</property>
+ <property name="label" translatable="yes">Border color</property>
+
+ </object>
+ </child>
+ <child>
+ <object class="GtkColorButton" id="dot_border_color_colorbutton">
+ <property name="receives_default">1</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="box_boder_width_box">
+ <property name="can_focus">0</property>
+ <property name="spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="dot_border_width_label">
+ <property name="hexpand">1</property>
+ <property name="xalign">0</property>
+ <property name="can_focus">0</property>
+ <property name="label" translatable="yes">Border width</property>
+
+ </object>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="dot_border_width_spin_button">
+ <property name="adjustment">dot_border_width_adjustment</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </property>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ <object class="GtkAdjustment" id="hide_timeout_adjustment">
+ <property name="upper">1</property>
+ <property name="step-increment">0.050000000000000003</property>
+ <property name="page-increment">0.25</property>
+ </object>
+ <object class="GtkAdjustment" id="icon_size_adjustment">
+ <property name="lower">16</property>
+ <property name="upper">128</property>
+ <property name="step-increment">1</property>
+ <property name="page-increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="preview_size_adjustment">
+ <property name="lower">0.0</property>
+ <property name="upper">1</property>
+ <property name="step_increment">0.01</property>
+ <property name="page_increment">0.1</property>
+ </object>
+ <object class="GtkNotebook" id="settings_notebook">
+ <property name="margin_start">6</property>
+ <property name="margin_end">6</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <child>
+ <object class="GtkNotebookPage">
+ <property name="child">
+ <object class="GtkBox" id="position_and_size">
+ <property name="can_focus">0</property>
+ <property name="margin_start">24</property>
+ <property name="margin_end">24</property>
+ <property name="margin_top">24</property>
+ <property name="margin_bottom">24</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">24</property>
+ <child>
+ <object class="GtkFrame" id="dock_display">
+ <property name="can_focus">0</property>
+
+ <property name="child">
+ <object class="GtkListBox" id="dock_display_listbox">
+ <property name="can_focus">0</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="dock_monitor_listboxrow">
+ <property name="child">
+ <object class="GtkGrid" id="dock_monitor_grid">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="dock_monitor_label">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Show the dock on</property>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="dock_monitor_combo">
+ <property name="can_focus">0</property>
+ <property name="width_request">400</property>
+ <property name="valign">center</property>
+ <signal name="changed" handler="dock_display_combo_changed_cb" />
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="multi_monitor_button">
+ <property name="label" translatable="yes">Show on all monitors</property>
+ <property name="margin_top">12</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">2</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="dock_position_listboxrow">
+ <property name="width_request">100</property>
+ <property name="child">
+ <object class="GtkBox" id="dock_position_box">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="dock_position_label">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Position on screen</property>
+
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="dock_position_butttons_box">
+ <property name="can_focus">0</property>
+ <property name="spacing">32</property>
+ <child>
+ <object class="GtkCheckButton" id="position_left_button">
+ <property name="label" translatable="yes">Left</property>
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+
+ <signal name="toggled" handler="position_left_button_toggled_cb" />
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="position_bottom_button">
+ <property name="label" translatable="yes">Bottom</property>
+ <property name="halign">center</property>
+
+ <property name="group">position_left_button</property>
+ <signal name="toggled" handler="position_bottom_button_toggled_cb" />
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="position_top_button">
+ <property name="label" translatable="yes">Top</property>
+ <property name="halign">center</property>
+
+
+ <property name="group">position_left_button</property>
+ <signal name="toggled" handler="position_top_button_toggled_cb" />
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="position_right_button">
+ <property name="label" translatable="yes">Right</property>
+ <property name="valign">center</property>
+
+ <property name="group">position_left_button</property>
+ <signal name="toggled" handler="position_right_button_toggled_cb" />
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </property>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkFrame" id="intelligent_autohide_frame">
+ <property name="can_focus">0</property>
+
+ <property name="child">
+ <object class="GtkListBox" id="listbox3">
+ <property name="can_focus">0</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow14">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="child">
+ <object class="GtkGrid" id="intelligent_autohide_grid">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="builtin_theme_description7">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Hide the dock when it obstructs a window of the current application. More refined settings are available.</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="builtin_theme_label8">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Intelligent autohide</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="box3">
+ <property name="can_focus">0</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkButton" id="intelligent_autohide_button">
+ <property name="receives_default">1</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+
+ <child>
+ <object class="GtkImage" id="image">
+ <property name="can_focus">0</property>
+ <property name="icon_name">emblem-system-symbolic</property>
+ </object>
+ </child>
+ <style>
+ <class name="circular"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="intelligent_autohide_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ </object>
+ </child>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ <property name="row-span">2</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </property>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkFrame" id="size_frame">
+ <property name="can_focus">0</property>
+
+ <property name="child">
+ <object class="GtkListBox" id="size_listbox">
+ <property name="can_focus">0</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="dock_size_listboxrow">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="child">
+ <object class="GtkGrid" id="dock_size_grid">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="dock_size_label">
+ <property name="can_focus">0</property>
+ <property name="label" translatable="yes">Dock size limit</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkScale" id="dock_size_scale">
+ <property name="draw-value">1</property>
+ <property name="valign">baseline</property>
+ <property name="hexpand">1</property>
+ <property name="adjustment">dock_size_adjustment</property>
+ <property name="round_digits">0</property>
+ <property name="digits">2</property>
+ <property name="value_pos">right</property>
+ <signal name="value-changed" handler="dock_size_scale_value_changed_cb" />
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="dock_size_extend_checkbutton">
+ <property name="label" translatable="yes">Panel mode: extend to the screen edge</property>
+ <property name="margin_top">12</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="dock_center_icons_check">
+ <property name="label" translatable="yes">Place icons to the center</property>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">2</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="icon_size_listboxrow">
+ <property name="child">
+ <object class="GtkGrid" id="icon_size_grid">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="builtin_theme_label6">
+ <property name="can_focus">0</property>
+ <property name="label" translatable="yes">Icon size limit</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkScale" id="icon_size_scale">
+ <property name="draw-value">1</property>
+ <property name="valign">baseline</property>
+ <property name="hexpand">1</property>
+ <property name="adjustment">icon_size_adjustment</property>
+ <property name="round_digits">1</property>
+ <property name="digits">0</property>
+ <property name="value_pos">right</property>
+ <signal name="value-changed" handler="icon_size_scale_value_changed_cb" />
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="icon_size_fixed_checkbutton">
+ <property name="label" translatable="yes">Fixed icon size: scroll to reveal other icons</property>
+ <property name="margin_top">12</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="preview_size_listboxrow">
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkGrid" id="preview_size_grid">
+ <property name="can_focus">False</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="builtin_theme_label7">
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Preview size scale</property>
+ <property name="xalign">0</property>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkScale" id="preview_size_scale">
+ <property name="hexpand">1</property>
+ <property name="draw-value">1</property>
+ <property name="adjustment">preview_size_adjustment</property>
+ <property name="restrict_to_fill_level">0</property>
+ <property name="fill_level">0</property>
+ <property name="round_digits">0</property>
+ <property name="digits">2</property>
+ <property name="value_pos">right</property>
+ <!-- <signal name="format-value" handler="preview_size_scale_format_value_cb" /> -->
+ <signal name="value-changed" handler="preview_size_scale_value_changed_cb" />
+ </object>
+ </child>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ <property name="tab">
+ <object class="GtkLabel" id="general_label">
+ <property name="can_focus">0</property>
+ <property name="label" translatable="yes">Position and size</property>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkNotebookPage">
+ <property name="position">1</property>
+ <property name="child">
+ <object class="GtkBox" id="apps">
+ <property name="can_focus">0</property>
+ <property name="margin_start">24</property>
+ <property name="margin_end">24</property>
+ <property name="margin_top">24</property>
+ <property name="margin_bottom">24</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">24</property>
+ <child>
+ <object class="GtkFrame" id="customize_theme1">
+ <property name="can_focus">0</property>
+
+ <property name="child">
+ <object class="GtkListBox" id="listbox9">
+ <property name="can_focus">0</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow6">
+ <property name="child">
+ <object class="GtkGrid" id="shrink_dash1">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkSwitch" id="show_favorite_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="shrink_dash_label1">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Show pinned applications</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow16">
+ <property name="child">
+ <object class="GtkGrid" id="shrink_dash2">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkSwitch" id="show_running_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="shrink_dash_label2">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Show running applications</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="application_button_isolation_button">
+ <property name="label" translatable="yes">Isolate workspaces</property>
+ <property name="halign">start</property>
+ <property name="margin_top">12</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">2</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="application_button_urgent_button">
+ <property name="label" translatable="yes">Show urgent windows despite current workspace</property>
+ <property name="halign">start</property>
+ <property name="margin_top">12</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">3</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="application_button_monitor_isolation_button">
+ <property name="label" translatable="yes">Isolate monitors</property>
+ <property name="halign">start</property>
+ <property name="margin_top">12</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">4</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="windows_preview_button">
+ <property name="margin_top">3</property>
+ <property name="label" translatable="yes">Show open windows' previews</property>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="child">
+ <object class="GtkGrid">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkSwitch" id="scroll_to_icon_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Keep the focused application always visible in the dash</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow17">
+ <property name="child">
+ <object class="GtkGrid" id="shrink_dash3">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkSwitch" id="show_applications_button_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ <property name="row-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="shrink_dash_description1">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">If disabled, these settings are accessible from gnome-tweak-tool or the extension website.</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="shrink_dash_label3">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Show &lt;i&gt;Applications&lt;/i&gt; icon</property>
+ <property name="use_markup">1</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="application_button_first_button">
+ <property name="label" translatable="yes">Move at beginning of the dock</property>
+ <property name="halign">start</property>
+ <property name="margin_top">12</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">2</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="application_button_animation_button">
+ <style>
+ <class name="text-button"/>
+ </style>
+ <property name="margin_top">3</property>
+ <child>
+ <object class="GtkLabel" id="label3">
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Animate &lt;i&gt;Show Applications&lt;/i&gt;</property>
+ <property name="use_markup">1</property>
+ </object>
+ </child>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">3</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="show_apps_always_in_the_edge">
+ <style>
+ <class name="text-button"/>
+ </style>
+ <property name="margin_top">3</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Put &lt;i&gt;Show Applications&lt;/i&gt; in a dock edge when using Panel mode</property>
+ <property name="use_markup">1</property>
+ </object>
+ </child>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">4</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow18">
+ <property name="child">
+ <object class="GtkGrid" id="shrink_dash4">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkSwitch" id="show_trash_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="shrink_dash_label4">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Show trash can</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow19">
+ <property name="child">
+ <object class="GtkGrid" id="shrink_dash5">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkSwitch" id="show_mounts_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="shrink_dash_label5">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Show volumes and devices</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="show_only_mounted_devices_check">
+ <property name="label" translatable="yes">Only if mounted</property>
+ <property name="halign">start</property>
+ <property name="margin_top">12</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ <property name="column-span">1</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="show_network_volumes_check">
+ <property name="label" translatable="yes">Include network volumes</property>
+ <property name="halign">start</property>
+ <property name="margin_top">12</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">2</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="isolate_locations_row">
+ <property name="child">
+ <object class="GtkGrid">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkSwitch" id="isolate_locations_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Isolate volumes, devices and trash windows from file manager</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="wiggle_urgent_applications_row">
+ <property name="child">
+ <object class="GtkGrid">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkSwitch" id="wiggle_urgent_applications_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Wiggle urgent applications</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="hide_tooltip_row">
+ <property name="child">
+ <object class="GtkGrid">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkSwitch" id="hide_tooltip_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Hide application tooltip</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="child">
+ <object class="GtkGrid">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkSwitch" id="show_icons_emblems_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Show icons emblems</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkLabel">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">When enabled application icons will show notification counters and progress-bars (if Unity API is used).</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ </layout>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkCheckButton" id="notifications_counter_check">
+ <property name="label" translatable="yes">Show the number of unread notifications</property>
+ <property name="halign">start</property>
+ <property name="margin_top">12</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">2</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="applications_override_counter">
+ <style>
+ <class name="text-button"/>
+ </style>
+ <property name="margin_top">3</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Application-provided counter overrides the notifications counter</property>
+ </object>
+ </child>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">3</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </property>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ <property name="tab">
+ <object class="GtkLabel" id="launchers_label">
+ <property name="can_focus">0</property>
+ <property name="label" translatable="yes">Launchers</property>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkNotebookPage">
+ <property name="position">2</property>
+ <property name="child">
+ <object class="GtkBox" id="behaviour">
+ <property name="can_focus">0</property>
+ <property name="margin_start">24</property>
+ <property name="margin_end">24</property>
+ <property name="margin_top">24</property>
+ <property name="margin_bottom">24</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">24</property>
+ <child>
+ <object class="GtkFrame" id="hot_keys_frame">
+ <property name="can_focus">0</property>
+
+ <property name="child">
+ <object class="GtkListBox" id="hot_keys_listbox">
+ <property name="can_focus">0</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="hot_keys_listboxrow">
+ <property name="child">
+ <object class="GtkGrid" id="hot_keys_grid">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="hot_keys_description">
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">Enable Super+(0-9) as shortcuts to activate apps. It can also be used together with Shift and Ctrl.</property>
+ <property name="use_markup">1</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</property>
+
+ <style>
+ <class name="dim-label"/>
+ </style>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="hot_keys_label">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Use keyboard shortcuts to activate apps</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="overlay_box">
+ <property name="can_focus">0</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkButton" id="overlay_button">
+ <property name="receives_default">1</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+
+ <child>
+ <object class="GtkImage" id="image_overlay">
+ <property name="can_focus">0</property>
+ <property name="icon_name">emblem-system-symbolic</property>
+ </object>
+ </child>
+ <style>
+ <class name="circular"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="hot_keys_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ </object>
+ </child>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ <property name="row-span">2</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </property>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkFrame" id="built_in_theme_frame3">
+ <property name="can_focus">0</property>
+
+ <property name="child">
+ <object class="GtkListBox" id="listbox6">
+ <property name="can_focus">0</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow9">
+ <property name="child">
+ <object class="GtkGrid" id="buitin_theme5">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="builtin_theme_description5">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Behaviour when clicking on the icon of a running application.</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</property>
+
+ <style>
+ <class name="dim-label"/>
+ </style>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="builtin_theme_label5">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Click action</property>
+ <property name="xalign">0</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="click_box">
+ <property name="can_focus">0</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkButton" id="middle_click_options_button">
+ <property name="receives_default">1</property>
+ <property name="valign">center</property>
+ <child>
+ <object class="GtkImage" id="middle_click_image">
+ <property name="can_focus">0</property>
+ <property name="icon_name">emblem-system-symbolic</property>
+ </object>
+ </child>
+ <style>
+ <class name="circular"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="click_action_combo">
+ <property name="can_focus">0</property>
+ <property name="valign">center</property>
+ <items>
+ <item translatable="yes">Raise window</item>
+ <item translatable="yes">Minimize</item>
+ <item translatable="yes">Launch new instance</item>
+ <item translatable="yes">Cycle through windows</item>
+ <item translatable="yes">Minimize or overview</item>
+ <item translatable="yes">Show window previews</item>
+ <item translatable="yes">Minimize or show previews</item>
+ <item translatable="yes">Focus or show previews</item>
+ <item translatable="yes">Focus or app spread</item>
+ <item translatable="yes">Focus, minimize or show previews</item>
+ <item translatable="yes">Focus, minimize or app spread</item>
+ </items>
+ </object>
+ </child>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ <property name="row-span">2</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </property>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkFrame" id="built_in_theme_frame_scroll">
+ <property name="can_focus">0</property>
+
+ <property name="child">
+ <object class="GtkListBox" id="listbox5">
+ <property name="can_focus">0</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow_scroll">
+ <property name="child">
+ <object class="GtkGrid" id="buitin_theme_scroll">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="builtin_theme_description_scroll">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Behaviour when scrolling on the icon of an application.</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</property>
+
+ <style>
+ <class name="dim-label"/>
+ </style>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="builtin_theme_label_scroll">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Scroll action</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="click_box_scroll">
+ <property name="can_focus">0</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkComboBoxText" id="scroll_action_combo">
+ <property name="can_focus">0</property>
+ <property name="valign">center</property>
+ <items>
+ <item translatable="yes">Do nothing</item>
+ <item translatable="yes">Cycle through windows</item>
+ <item translatable="yes">Switch workspace</item>
+ </items>
+ </object>
+ </child>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ <property name="row-span">2</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </property>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ <property name="tab">
+ <object class="GtkLabel" id="behaviour_label">
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Behavior</property>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkNotebookPage">
+ <property name="position">3</property>
+ <property name="child">
+ <object class="GtkBox" id="appearance">
+ <property name="can_focus">0</property>
+ <property name="margin_start">24</property>
+ <property name="margin_end">24</property>
+ <property name="margin_top">24</property>
+ <property name="margin_bottom">24</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">24</property>
+ <child>
+ <object class="GtkFrame" id="built_in_theme_frame">
+ <property name="can_focus">0</property>
+
+ <property name="child">
+ <object class="GtkListBox" id="listbox1">
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow2">
+ <property name="child">
+ <object class="GtkGrid" id="shrink_dash">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkSwitch" id="shrink_dash_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ <property name="row-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="shrink_dash_description">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Save space reducing padding and border radius.</property>
+
+ <style>
+ <class name="dim-label"/>
+ </style>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="shrink_dash_label">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Shrink the dash</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow_rnd_border">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="spacing">32</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <child>
+ <object class="GtkLabel" id="force_straight_corner_label">
+ <property name="hexpand">1</property>
+ <property name="can_focus">0</property>
+ <property name="valign">center</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Force straight corner</property>
+
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="force_straight_corner_switch">
+ <property name="valign">center</property>
+ <property name="margin_start">3</property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ <object class="GtkListBoxRow">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="spacing">32</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="hexpand">1</property>
+ <property name="can_focus">0</property>
+ <property name="valign">center</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Show overview on startup</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="show_overview_on_startup_switch">
+ <property name="valign">center</property>
+ <property name="margin_start">3</property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </property>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkFrame">
+ <property name="can_focus">0</property>
+
+ <property name="child">
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="child">
+ <object class="GtkGrid" id="buitin_theme">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="builtin_theme_description">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Few customizations meant to integrate the dock with the default GNOME theme. Alternatively, specific options can be enabled below.</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</property>
+
+ <style>
+ <class name="dim-label"/>
+ </style>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="builtin_theme_label">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Use built-in theme</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="builtin_theme_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ <property name="row-span">2</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBox" id="customize_theme">
+ <property name="can_focus">0</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow3">
+ <property name="child">
+ <object class="GtkGrid" id="running_dots">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="running_dots_label">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Customize windows counter indicators</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="box4">
+ <property name="can_focus">0</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkButton" id="running_indicators_advance_settings_button">
+ <property name="receives_default">1</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+
+ <child>
+ <object class="GtkImage" id="image1">
+ <property name="can_focus">0</property>
+ <property name="icon_name">emblem-system-symbolic</property>
+ </object>
+ </child>
+ <style>
+ <class name="circular"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="running_indicators_combo">
+ <property name="can_focus">0</property>
+ <items>
+ <item translatable="yes">Default</item>
+ <item translatable="yes">Dots</item>
+ <item translatable="yes">Squares</item>
+ <item translatable="yes">Dashes</item>
+ <item translatable="yes">Segmented</item>
+ <item translatable="yes">Solid</item>
+ <item translatable="yes">Ciliora</item>
+ <item translatable="yes">Metro</item>
+ <item translatable="yes">Binary</item>
+ <item translatable="yes">Dot</item>
+ </items>
+ </object>
+ </child>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ <property name="row-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="custom_background_color_listboxrow">
+ <property name="child">
+ <object class="GtkGrid" id="custom_background_color_grid">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="custom_background_color_description">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Set the background color for the dash.</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</property>
+
+ <style>
+ <class name="dim-label"/>
+ </style>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="custom_background_color_label">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Customize the dash color</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="custom_background_color_box">
+ <property name="can_focus">0</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkColorButton" id="custom_background_color">
+ <property name="receives_default">1</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="custom_background_color_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ </object>
+ </child>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ <property name="row-span">2</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow4">
+ <property name="child">
+ <object class="GtkBox" id="box2">
+ <property name="can_focus">0</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkGrid" id="customize_opacity">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="customize_opacity_description">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Tune the dash background opacity.</property>
+
+ <style>
+ <class name="dim-label"/>
+ </style>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="customize_opacity_label">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Customize opacity</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="customize_opacity_box">
+ <property name="can_focus">0</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkButton" id="dynamic_opacity_button">
+ <property name="receives_default">1</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <child>
+ <object class="GtkImage" id="dynamic_opacity_image">
+ <property name="can_focus">0</property>
+ <property name="icon_name">emblem-system-symbolic</property>
+ </object>
+ </child>
+ <style>
+ <class name="circular"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="customize_opacity_combo">
+ <property name="can_focus">0</property>
+ <property name="valign">center</property>
+ <items>
+ <item id="0" translatable="yes">Default</item>
+ <item id="1" translatable="yes">Fixed</item>
+ <item id="3" translatable="yes">Dynamic</item>
+ </items>
+ </object>
+ </child>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ <property name="row-span">2</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="custom_opacity">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="custom_opacity_label">
+ <property name="can_focus">0</property>
+ <property name="label" translatable="yes">Opacity</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkScale" id="custom_opacity_scale">
+ <property name="hexpand">1</property>
+ <property name="draw-value">1</property>
+ <property name="adjustment">custom_opacity_adjustement</property>
+
+ <property name="restrict_to_fill_level">0</property>
+ <property name="fill_level">0</property>
+ <property name="round_digits">0</property>
+ <property name="digits">2</property>
+ <property name="value_pos">right</property>
+ <signal name="value-changed" handler="custom_opacity_scale_value_changed_cb" />
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ <property name="tab">
+ <object class="GtkLabel" id="appearance_label">
+ <property name="can_focus">0</property>
+ <property name="label" translatable="yes">Appearance</property>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkNotebookPage">
+ <property name="position">4</property>
+ <property name="child">
+ <object class="GtkBox" id="about">
+ <property name="visible">0</property>
+ <property name="can_focus">0</property>
+ <property name="margin_top">24</property>
+ <property name="margin_bottom">24</property>
+ <property name="hexpand">1</property>
+ <property name="vexpand">1</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">5</property>
+ <child>
+ <object class="GtkImage" id="logo">
+ <property name="can_focus">0</property>
+ <property name="file">./media/logo.svg</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="extension_name">
+ <property name="can_focus">0</property>
+ <property name="label">&lt;b&gt;Dash to Dock&lt;/b&gt;</property>
+ <property name="use_markup">1</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="can_focus">0</property>
+ <property name="halign">center</property>
+ <child>
+ <object class="GtkLabel" id="extension_version_label">
+ <property name="can_focus">0</property>
+ <property name="halign">end</property>
+ <property name="label" translatable="yes">version: </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="extension_version">
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="label">...</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="extension_description">
+ <property name="can_focus">0</property>
+ <property name="label" translatable="yes">Moves the dash out of the overview transforming it in a dock</property>
+ <property name="justify">center</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="box10">
+ <property name="can_focus">0</property>
+ <property name="halign">center</property>
+ <property name="spacing">5</property>
+ <child>
+ <object class="GtkLabel" id="label15">
+ <property name="can_focus">0</property>
+ <property name="label" translatable="yes">Created by</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label16">
+ <property name="label">Michele (&lt;a href=&quot;mailto:micxgx@gmail.com&quot;&gt;micxgx@gmail.com&lt;/a&gt;)</property>
+ <property name="use_markup">1</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLinkButton" id="homepage_link">
+ <property name="label" translatable="yes">Webpage</property>
+ <property name="receives_default">1</property>
+ <property name="halign">center</property>
+
+ <property name="uri">https://micheleg.github.io/dash-to-dock/</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="vexpand">1</property>
+ <property name="valign">end</property>
+ <property name="label" translatable="yes">&lt;span size=&quot;small&quot;&gt;This program comes with ABSOLUTELY NO WARRANTY.
+See the &lt;a href=&quot;https://www.gnu.org/licenses/old-licenses/gpl-2.0.html&quot;&gt;GNU General Public License, version 2 or later&lt;/a&gt; for details.&lt;/span&gt;</property>
+ <property name="use_markup">1</property>
+ <property name="justify">center</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</property>
+ </object>
+ </child>
+ </object>
+ </property>
+ <property name="tab">
+ <object class="GtkLabel" id="about_label">
+ <property name="can_focus">0</property>
+ <property name="label" translatable="yes">About</property>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ <object class="GtkAdjustment" id="max_opacity_adjustement">
+ <property name="upper">1</property>
+ <property name="step-increment">0.01</property>
+ <property name="page-increment">0.10000000000000001</property>
+ </object>
+ <object class="GtkAdjustment" id="min_opacity_adjustement">
+ <property name="upper">1</property>
+ <property name="step-increment">0.01</property>
+ <property name="page-increment">0.10000000000000001</property>
+ </object>
+ <object class="GtkBox" id="advanced_transparency_dialog">
+ <property name="hexpand">1</property>
+ <property name="vexpand">1</property>
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkFrame" id="advanced_transparency_frame">
+ <property name="can_focus">0</property>
+
+ <property name="child">
+ <object class="GtkListBox" id="advanced_transparency_listbox">
+ <property name="can_focus">0</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="advanced_transparency_listboxrow">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="child">
+ <object class="GtkBox" id="advanced_transparency_box">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkBox" id="min_alpha_box_1">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="customize_alphas_label">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">Customize minimum and maximum opacity values</property>
+ <property name="justify">fill</property>
+ <property name="xalign">0</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="customize_alphas_switch">
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="min_alpha_box">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="min_alpha_label">
+ <property name="can_focus">0</property>
+ <property name="label" translatable="yes">Minimum opacity</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkScale" id="min_alpha_scale">
+ <property name="hexpand">1</property>
+ <property name="draw-value">1</property>
+ <property name="adjustment">min_opacity_adjustement</property>
+
+ <property name="restrict_to_fill_level">0</property>
+ <property name="fill_level">0</property>
+ <property name="round_digits">0</property>
+ <property name="digits">2</property>
+ <property name="value_pos">right</property>
+ <signal name="value-changed" handler="min_opacity_scale_value_changed_cb" />
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="max_alpha_box">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="max_alpha_label">
+ <property name="can_focus">0</property>
+ <property name="label" translatable="yes">Maximum opacity</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkScale" id="max_alpha_scale">
+ <property name="hexpand">1</property>
+ <property name="draw-value">1</property>
+ <property name="adjustment">max_opacity_adjustement</property>
+
+ <property name="restrict_to_fill_level">0</property>
+ <property name="fill_level">0</property>
+ <property name="round_digits">0</property>
+ <property name="digits">2</property>
+ <property name="value_pos">right</property>
+ <signal name="value-changed" handler="max_opacity_scale_value_changed_cb" />
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </property>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ <object class="GtkAdjustment" id="pressure_threshold_adjustment">
+ <property name="upper">100</property>
+ <property name="step-increment">5</property>
+ <property name="page-increment">25</property>
+ </object>
+ <object class="GtkAdjustment" id="shortcut_time_adjustment">
+ <property name="upper">10</property>
+ <property name="step-increment">0.25</property>
+ <property name="page-increment">1</property>
+ </object>
+ <object class="GtkBox" id="box_overlay_shortcut">
+ <property name="hexpand">1</property>
+ <property name="vexpand">1</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkFrame" id="frame_overlay_show_dock">
+ <property name="child">
+ <object class="GtkListBox" id="listbox_overlay_shortcut">
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow_overlay_shortcut">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="child">
+ <object class="GtkGrid" id="grid_overlay">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkSwitch" id="overlay_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ <property name="row-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="overlay_label">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">Number overlay</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="overlay_description">
+ <property name="can_focus">0</property>
+ <property name="label" translatable="yes">Temporarily show the application numbers over the icons, corresponding to the shortcut.</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</property>
+ <property name="max_width_chars">40</property>
+
+ <style>
+ <class name="dim-label"/>
+ </style>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow_show_dock">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="child">
+ <object class="GtkGrid" id="grid_show_dock">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkSwitch" id="show_dock_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ <property name="row-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="show_dock_label">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Show the dock if it is hidden</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="show_dock_description">
+ <property name="can_focus">0</property>
+ <property name="label" translatable="yes">If using autohide, the dock will appear for a short time when triggering the shortcut.</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</property>
+ <property name="max_width_chars">40</property>
+
+ <style>
+ <class name="dim-label"/>
+ </style>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow_extra_shortcut">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="child">
+ <object class="GtkGrid" id="grid_shortcut">
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkEntry" id="shortcut_entry">
+ <property name="valign">center</property>
+ <property name="width_chars">12</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="shortcut_label">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">Shortcut for the options above</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="shortcut_description">
+ <property name="can_focus">0</property>
+ <property name="label" translatable="yes">Syntax: &lt;Shift&gt;, &lt;Ctrl&gt;, &lt;Alt&gt;, &lt;Super&gt;</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</property>
+ <property name="max_width_chars">40</property>
+
+ <style>
+ <class name="dim-label"/>
+ </style>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow_timeout">
+ <property name="child">
+ <object class="GtkGrid" id="grid_timeout">
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="hexpand">1</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkSpinButton" id="timeout_spinbutton">
+ <property name="halign">end</property>
+ <property name="adjustment">shortcut_time_adjustment</property>
+ <property name="digits">3</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="shortcut_timeout_label">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">Hide timeout (s)</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </property>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ <object class="GtkAdjustment" id="show_timeout_adjustment">
+ <property name="upper">1</property>
+ <property name="step-increment">0.050000000000000003</property>
+ <property name="page-increment">0.25</property>
+ </object>
+ <object class="GtkBox" id="intelligent_autohide_advanced_settings_box">
+ <property name="hexpand">1</property>
+ <property name="vexpand">1</property>
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkFrame" id="intelligent_autohide_advanced_settings_frame">
+ <property name="can_focus">0</property>
+
+ <property name="child">
+ <object class="GtkListBox" id="listbox4">
+ <property name="can_focus">0</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow8">
+ <property name="child">
+ <object class="GtkGrid" id="buitin_theme2">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="builtin_theme_description2">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Show the dock by mouse hover on the screen edge.</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</property>
+
+ <style>
+ <class name="dim-label"/>
+ </style>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="builtin_theme_label2">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">Autohide</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="autohide_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ <property name="row-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="require_pressure_checkbutton">
+ <property name="label" translatable="yes">Push to show: require pressure to show the dock</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">3</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="autohide_enable_in_fullscreen_checkbutton">
+ <property name="label" translatable="yes">Enable in fullscreen mode</property>
+ <property name="margin_top">12</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">2</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="show_dock_urgent_notify_checkbutton">
+ <property name="label" translatable="yes">Show dock for urgent notifications</property>
+ <property name="margin_top">12</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">4</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow15">
+ <property name="child">
+ <object class="GtkGrid" id="intellihide_grid">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="builtin_theme_description3">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Show the dock when it doesn&apos;t obstruct application windows.</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</property>
+
+ <style>
+ <class name="dim-label"/>
+ </style>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="builtin_theme_label3">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">Dodge windows</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="intellihide_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ <property name="row-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="intellihide_mode_box">
+ <property name="can_focus">0</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkCheckButton" id="all_windows_radio_button">
+ <property name="label" translatable="yes">All windows</property>
+ <property name="margin_top">12</property>
+
+ <signal name="toggled" handler="all_windows_radio_button_toggled_cb" />
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="focus_application_windows_radio_button">
+ <property name="label" translatable="yes">Only focused application&apos;s windows</property>
+ <property name="group">all_windows_radio_button</property>
+ <signal name="toggled" handler="focus_application_windows_radio_button_toggled_cb" />
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="maximized_windows_radio_button">
+ <property name="label" translatable="yes">Only maximized windows</property>
+ <property name="group">all_windows_radio_button</property>
+ <signal name="toggled" handler="maximized_windows_radio_button_toggled_cb" />
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="always_on_top_radio_button">
+ <property name="label" translatable="yes">Always on top</property>
+ <property name="group">all_windows_radio_button</property>
+ <signal name="toggled" handler="always_on_top_radio_button_toggled_cb" />
+ </object>
+ </child>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">2</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow5">
+ <property name="child">
+ <object class="GtkGrid" id="grid2">
+ <property name="can_focus">0</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="hexpand">1</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkSpinButton" id="animation_duration_spinbutton">
+ <property name="halign">end</property>
+ <property name="adjustment">animation_time_adjustment</property>
+ <property name="digits">3</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label2">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">Animation duration (s)</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="hide_timeout_spinbutton">
+ <property name="halign">end</property>
+ <property name="adjustment">hide_timeout_adjustment</property>
+ <property name="digits">3</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">1</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="show_timeout_spinbutton">
+ <property name="halign">end</property>
+ <property name="adjustment">show_timeout_adjustment</property>
+ <property name="digits">3</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="pressure_threshold_spinbutton">
+ <property name="text">0.000</property>
+ <property name="adjustment">pressure_threshold_adjustment</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">3</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label9">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">Hide timeout (s)</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="show_timeout_label">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">Show timeout (s)</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="pressure_threshold_label">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">Pressure threshold</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">3</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </property>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appIconIndicators.js b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appIconIndicators.js
new file mode 100644
index 0000000..d981c0f
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appIconIndicators.js
@@ -0,0 +1,1305 @@
+import {
+ Clutter,
+ Cogl,
+ GdkPixbuf,
+ Gio,
+ GObject,
+ Pango,
+ St,
+} from './dependencies/gi.js';
+
+import {Main} from './dependencies/shell/ui.js';
+
+import {
+ Docking,
+ Utils,
+} from './imports.js';
+
+const {cairo: Cairo} = imports;
+
+const RunningIndicatorStyle = Object.freeze({
+ DEFAULT: 0,
+ DOTS: 1,
+ SQUARES: 2,
+ DASHES: 3,
+ SEGMENTED: 4,
+ SOLID: 5,
+ CILIORA: 6,
+ METRO: 7,
+ BINARY: 8,
+ DOT: 9,
+});
+
+const MAX_WINDOWS_CLASSES = 4;
+
+
+/*
+ * This is the main indicator class to be used. The desired behavior is
+ * obtained by composing the desired classes below based on the settings.
+ *
+ */
+export class AppIconIndicator {
+ constructor(source) {
+ this._indicators = [];
+
+ // Choose the style for the running indicators
+ let runningIndicator = null;
+ let runningIndicatorStyle;
+
+ const {settings} = Docking.DockManager;
+ if (settings.applyCustomTheme)
+ runningIndicatorStyle = RunningIndicatorStyle.DOTS;
+ else
+ ({runningIndicatorStyle} = settings);
+
+ if (settings.showIconsEmblems &&
+ !Docking.DockManager.getDefault().notificationsMonitor.dndMode) {
+ const unityIndicator = new UnityIndicator(source);
+ this._indicators.push(unityIndicator);
+ }
+
+ switch (runningIndicatorStyle) {
+ case RunningIndicatorStyle.DEFAULT:
+ runningIndicator = new RunningIndicatorDefault(source);
+ break;
+
+ case RunningIndicatorStyle.DOTS:
+ runningIndicator = new RunningIndicatorDots(source);
+ break;
+
+ case RunningIndicatorStyle.SQUARES:
+ runningIndicator = new RunningIndicatorSquares(source);
+ break;
+
+ case RunningIndicatorStyle.DASHES:
+ runningIndicator = new RunningIndicatorDashes(source);
+ break;
+
+ case RunningIndicatorStyle.SEGMENTED:
+ runningIndicator = new RunningIndicatorSegmented(source);
+ break;
+
+ case RunningIndicatorStyle.SOLID:
+ runningIndicator = new RunningIndicatorSolid(source);
+ break;
+
+ case RunningIndicatorStyle.CILIORA:
+ runningIndicator = new RunningIndicatorCiliora(source);
+ break;
+
+ case RunningIndicatorStyle.METRO:
+ runningIndicator = new RunningIndicatorMetro(source);
+ break;
+
+ case RunningIndicatorStyle.BINARY:
+ runningIndicator = new RunningIndicatorBinary(source);
+ break;
+
+ case RunningIndicatorStyle.DOT:
+ runningIndicator = new RunningIndicatorDot(source);
+ break;
+
+ default:
+ runningIndicator = new RunningIndicatorBase(source);
+ }
+
+ this._indicators.push(runningIndicator);
+ }
+
+ update() {
+ for (let i = 0; i < this._indicators.length; i++) {
+ const indicator = this._indicators[i];
+ indicator.update();
+ }
+ }
+
+ destroy() {
+ for (let i = 0; i < this._indicators.length; i++) {
+ const indicator = this._indicators[i];
+ indicator.destroy();
+ }
+ }
+}
+
+/*
+ * Base class to be inherited by all indicators of any kind
+*/
+class IndicatorBase {
+ constructor(source) {
+ this._source = source;
+ this._signalsHandler = new Utils.GlobalSignalsHandler(this._source);
+ }
+
+ update() {
+ }
+
+ destroy() {
+ this._source = null;
+ this._signalsHandler.destroy();
+ this._signalsHandler = null;
+ }
+}
+
+/*
+ * A base indicator class for running style, from which all other RunningIndicators should derive,
+ * providing some basic methods, variables definitions and their update, css style classes handling.
+ *
+ */
+class RunningIndicatorBase extends IndicatorBase {
+ constructor(source) {
+ super(source);
+
+ this._side = Utils.getPosition();
+ this._dominantColorExtractor = new DominantColorExtractor(this._source.app);
+ this._signalsHandler.add(this._source, 'notify::running', () => this.update());
+ this._signalsHandler.add(this._source, 'notify::focused', () => this.update());
+ this._signalsHandler.add(this._source, 'notify::windows-count', () => this._updateCounterClass());
+ this.update();
+ }
+
+ get _number() {
+ return Math.min(this._source.windowsCount, MAX_WINDOWS_CLASSES);
+ }
+
+ update() {
+ this._updateCounterClass();
+ this._updateDefaultDot();
+ }
+
+ _updateCounterClass() {
+ for (let i = 1; i <= MAX_WINDOWS_CLASSES; i++) {
+ const className = `running${i}`;
+ if (i !== this._number)
+ this._source.remove_style_class_name(className);
+ else
+ this._source.add_style_class_name(className);
+ }
+ }
+
+ _updateDefaultDot() {
+ if (this._source.running)
+ this._source._dot.show();
+ else
+ this._source._dot.hide();
+ }
+
+ _hideDefaultDot() {
+ // I use opacity to hide the default dot because the show/hide function
+ // are used by the parent class.
+ this._source._dot.opacity = 0;
+ }
+
+ _restoreDefaultDot() {
+ this._source._dot.opacity = 255;
+ }
+
+ _enableBacklight() {
+ const colorPalette = this._dominantColorExtractor._getColorPalette();
+
+ // Fallback
+ if (!colorPalette) {
+ this._source._iconContainer.set_style(
+ 'border-radius: 5px;' +
+ 'background-gradient-direction: vertical;' +
+ 'background-gradient-start: #e0e0e0;' +
+ 'background-gradient-end: darkgray;'
+ );
+
+ return;
+ }
+
+ this._source._iconContainer.set_style(
+ `${'border-radius: 5px;' +
+ 'background-gradient-direction: vertical;' +
+ 'background-gradient-start: '}${colorPalette.original};` +
+ `background-gradient-end: ${colorPalette.darker};`
+ );
+ }
+
+ _disableBacklight() {
+ this._source._iconContainer.set_style(null);
+ }
+
+ destroy() {
+ this._disableBacklight();
+ // Remove glossy background if the children still exists
+ if (this._source._iconContainer.get_children().length > 1)
+ this._source._iconContainer.get_children()[1].set_style(null);
+ this._restoreDefaultDot();
+
+ super.destroy();
+ }
+}
+
+// We add a css class so third parties themes can limit their indicator customization
+// to the case we do nothing
+class RunningIndicatorDefault extends RunningIndicatorBase {
+ constructor(source) {
+ super(source);
+ this._source.add_style_class_name('default');
+ }
+
+ destroy() {
+ this._source.remove_style_class_name('default');
+ super.destroy();
+ }
+}
+
+const IndicatorDrawingArea = GObject.registerClass(
+class IndicatorDrawingArea extends St.DrawingArea {
+ vfunc_allocate(box) {
+ if (box.x1 !== 0 || box.y1 !== 0)
+ return super.vfunc_allocate(box);
+
+ // We assume that the are is a rectangle in the operations below:
+ const size = Math.min(box.get_width(), box.get_height());
+ box.x2 = size;
+ box.y2 = size;
+ this.set_allocation(box);
+
+ return super.vfunc_allocate(box);
+ }
+});
+
+class RunningIndicatorDots extends RunningIndicatorBase {
+ constructor(source) {
+ super(source);
+
+ this._hideDefaultDot();
+
+ this._area = new IndicatorDrawingArea({
+ x_expand: true,
+ y_expand: true,
+ });
+
+ // We draw for the bottom case and rotate the canvas for other placements
+ // set center of rotations to the center
+ this._area.set_pivot_point(0.5, 0.5);
+
+ switch (this._side) {
+ case St.Side.TOP:
+ this._area.rotation_angle_z = 180;
+ break;
+
+ case St.Side.BOTTOM:
+ // nothing
+ break;
+
+ case St.Side.LEFT:
+ this._area.rotation_angle_z = 90;
+ break;
+
+ case St.Side.RIGHT:
+ this._area.rotation_angle_z = -90;
+ break;
+ }
+
+ this._area.connectObject('repaint', this._updateIndicator.bind(this), this);
+ this._source._iconContainer.add_child(this._area);
+
+ const keys = ['custom-theme-running-dots-color',
+ 'custom-theme-running-dots-border-color',
+ 'custom-theme-running-dots-border-width',
+ 'custom-theme-customize-running-dots',
+ 'unity-backlit-items',
+ 'apply-glossy-effect',
+ 'running-indicator-dominant-color'];
+
+ keys.forEach(function (key) {
+ this._signalsHandler.add(
+ Docking.DockManager.settings,
+ `changed::${key}`,
+ this.update.bind(this)
+ );
+ }, this);
+
+ // Apply glossy background
+ // TODO: move to enable/disableBacklit to apply it only to the running apps?
+ // TODO: move to css class for theming support
+ const {extension} = Docking.DockManager;
+ this._glossyBackgroundStyle = `background-image: url('${extension.path}/media/glossy.svg');` +
+ 'background-size: contain;';
+ }
+
+ update() {
+ super.update();
+
+ // Enable / Disable the backlight of running apps
+ if (!Docking.DockManager.settings.applyCustomTheme &&
+ Docking.DockManager.settings.unityBacklitItems) {
+ const [icon] = this._source._iconContainer.get_children();
+ icon.set_style(
+ Docking.DockManager.settings.applyGlossyEffect
+ ? this._glossyBackgroundStyle : null);
+ if (this._source.running)
+ this._enableBacklight();
+ else
+ this._disableBacklight();
+ } else {
+ this._disableBacklight();
+ this._source._iconContainer.get_children()[1].set_style(null);
+ }
+
+ if (this._area)
+ this._area.queue_repaint();
+ }
+
+ _computeStyle() {
+ const [width, height] = this._area.get_surface_size();
+ this._width = height;
+ this._height = width;
+
+ // By default re-use the style - background color, and border width and color -
+ // of the default dot
+ const themeNode = this._source._dot.get_theme_node();
+ this._borderColor = themeNode.get_border_color(this._side);
+ this._borderWidth = themeNode.get_border_width(this._side);
+ this._bodyColor = themeNode.get_background_color();
+
+ const {settings} = Docking.DockManager;
+ if (!settings.applyCustomTheme) {
+ // Adjust for the backlit case
+ const Color = Clutter.Color ?? Cogl.Color;
+
+ if (settings.unityBacklitItems) {
+ // Use dominant color for dots too if the backlit is enables
+ const colorPalette = this._dominantColorExtractor._getColorPalette();
+
+ // Slightly adjust the styling
+ this._borderWidth = 2;
+
+ if (colorPalette) {
+ [, this._borderColor] = Color.from_string(colorPalette.lighter);
+ [, this._bodyColor] = Color.from_string(colorPalette.darker);
+ } else {
+ // Fallback
+ [, this._borderColor] = Color.from_string('white');
+ [, this._bodyColor] = Color.from_string('gray');
+ }
+ }
+
+ // Apply dominant color if requested
+ if (settings.runningIndicatorDominantColor) {
+ const colorPalette = this._dominantColorExtractor._getColorPalette();
+ if (colorPalette)
+ [, this._bodyColor] = Color.from_string(colorPalette.original);
+ else
+ // Fallback
+ [, this._bodyColor] = Color.from_string(settings.customThemeRunningDotsColor);
+ }
+
+ // Finally, use customize style if requested
+ if (settings.customThemeCustomizeRunningDots) {
+ [, this._borderColor] = Color.from_string(settings.customThemeRunningDotsBorderColor);
+ this._borderWidth = settings.customThemeRunningDotsBorderWidth;
+ [, this._bodyColor] = Color.from_string(settings.customThemeRunningDotsColor);
+ }
+ }
+
+ // Define the radius as an arbitrary size, but keep large enough to account
+ // for the drawing of the border.
+ this._radius = Math.max(this._width / 22, this._borderWidth / 2);
+ this._padding = 0; // distance from the margin
+ this._spacing = this._radius + this._borderWidth; // separation between the dots
+ }
+
+ _updateIndicator() {
+ const cr = this._area.get_context();
+
+ this._computeStyle();
+ this._drawIndicator(cr);
+ cr.$dispose();
+ }
+
+ _drawIndicator(cr) {
+ // Draw the required numbers of dots
+ const n = this._number;
+
+ cr.setLineWidth(this._borderWidth);
+ Utils.cairoSetSourceColor(cr, this._borderColor);
+
+ // draw for the bottom case:
+ cr.translate(
+ (this._width - (2 * n) * this._radius - (n - 1) * this._spacing) / 2,
+ this._height - this._padding);
+
+ for (let i = 0; i < n; i++) {
+ cr.newSubPath();
+ cr.arc((2 * i + 1) * this._radius + i * this._spacing,
+ -this._radius - this._borderWidth / 2,
+ this._radius, 0, 2 * Math.PI);
+ }
+
+ cr.strokePreserve();
+ Utils.cairoSetSourceColor(cr, this._bodyColor);
+ cr.fill();
+ }
+
+ destroy() {
+ this._area.destroy();
+ super.destroy();
+ }
+}
+
+// Adapted from dash-to-panel by Jason DeRose
+// https://github.com/jderose9/dash-to-panel
+class RunningIndicatorCiliora extends RunningIndicatorDots {
+ _drawIndicator(cr) {
+ if (this._source.running) {
+ const size = Math.max(this._width / 20, this._borderWidth);
+ const spacing = size; // separation between the dots
+ const lineLength = this._width - (size * (this._number - 1)) - (spacing * (this._number - 1));
+ let padding = this._borderWidth;
+ // For the backlit case here we don't want the outer border visible
+ if (Docking.DockManager.settings.unityBacklitItems &&
+ !Docking.DockManager.settings.customThemeCustomizeRunningDots)
+ padding = 0;
+ const yOffset = this._height - padding - size;
+
+ cr.setLineWidth(this._borderWidth);
+ Utils.cairoSetSourceColor(cr, this._borderColor);
+
+ cr.translate(0, yOffset);
+ cr.newSubPath();
+ cr.rectangle(0, 0, lineLength, size);
+ for (let i = 1; i < this._number; i++) {
+ cr.newSubPath();
+ cr.rectangle(lineLength + (i * spacing) + ((i - 1) * size), 0, size, size);
+ }
+
+ cr.strokePreserve();
+ Utils.cairoSetSourceColor(cr, this._bodyColor);
+ cr.fill();
+ }
+ }
+}
+
+// Adapted from dash-to-panel by Jason DeRose
+// https://github.com/jderose9/dash-to-panel
+class RunningIndicatorSegmented extends RunningIndicatorDots {
+ _drawIndicator(cr) {
+ if (this._source.running) {
+ const size = Math.max(this._width / 20, this._borderWidth);
+ const spacing = Math.ceil(this._width / 18); // separation between the dots
+ const dashLength = Math.ceil((this._width - ((this._number - 1) * spacing)) / this._number);
+ let padding = this._borderWidth;
+ // For the backlit case here we don't want the outer border visible
+ if (Docking.DockManager.settings.unityBacklitItems &&
+ !Docking.DockManager.settings.customThemeCustomizeRunningDots)
+ padding = 0;
+ const yOffset = this._height - padding - size;
+
+ cr.setLineWidth(this._borderWidth);
+ Utils.cairoSetSourceColor(cr, this._borderColor);
+
+ cr.translate(0, yOffset);
+ for (let i = 0; i < this._number; i++) {
+ cr.newSubPath();
+ cr.rectangle(i * dashLength + i * spacing, 0, dashLength, size);
+ }
+
+ cr.strokePreserve();
+ Utils.cairoSetSourceColor(cr, this._bodyColor);
+ cr.fill();
+ }
+ }
+}
+
+// Adapted from dash-to-panel by Jason DeRose
+// https://github.com/jderose9/dash-to-panel
+class RunningIndicatorSolid extends RunningIndicatorDots {
+ _drawIndicator(cr) {
+ if (this._source.running) {
+ const size = Math.max(this._width / 20, this._borderWidth);
+ let padding = this._borderWidth;
+ // For the backlit case here we don't want the outer border visible
+ if (Docking.DockManager.settings.unityBacklitItems &&
+ !Docking.DockManager.settings.customThemeCustomizeRunningDots)
+ padding = 0;
+ const yOffset = this._height - padding - size;
+
+ cr.setLineWidth(this._borderWidth);
+ Utils.cairoSetSourceColor(cr, this._borderColor);
+
+ cr.translate(0, yOffset);
+ cr.newSubPath();
+ cr.rectangle(0, 0, this._width, size);
+
+ cr.strokePreserve();
+ Utils.cairoSetSourceColor(cr, this._bodyColor);
+ cr.fill();
+ }
+ }
+}
+
+// Adapted from dash-to-panel by Jason DeRose
+// https://github.com/jderose9/dash-to-panel
+class RunningIndicatorSquares extends RunningIndicatorDots {
+ _drawIndicator(cr) {
+ if (this._source.running) {
+ const size = Math.max(this._width / 11, this._borderWidth);
+ const padding = this._borderWidth;
+ const spacing = Math.ceil(this._width / 18); // separation between the dots
+ const yOffset = this._height - padding - size;
+
+ cr.setLineWidth(this._borderWidth);
+ Utils.cairoSetSourceColor(cr, this._borderColor);
+
+ cr.translate(
+ Math.floor((this._width - this._number * size - (this._number - 1) * spacing) / 2),
+ yOffset);
+
+ for (let i = 0; i < this._number; i++) {
+ cr.newSubPath();
+ cr.rectangle(i * size + i * spacing, 0, size, size);
+ }
+ cr.strokePreserve();
+ Utils.cairoSetSourceColor(cr, this._bodyColor);
+ cr.fill();
+ }
+ }
+}
+
+// Adapted from dash-to-panel by Jason DeRose
+// https://github.com/jderose9/dash-to-panel
+class RunningIndicatorDashes extends RunningIndicatorDots {
+ _drawIndicator(cr) {
+ if (this._source.running) {
+ const size = Math.max(this._width / 20, this._borderWidth);
+ const padding = this._borderWidth;
+ const spacing = Math.ceil(this._width / 18); // separation between the dots
+ const dashLength = Math.floor(this._width / 4) - spacing;
+ const yOffset = this._height - padding - size;
+
+ cr.setLineWidth(this._borderWidth);
+ Utils.cairoSetSourceColor(cr, this._borderColor);
+
+ cr.translate(
+ Math.floor((this._width - this._number * dashLength - (this._number - 1) * spacing) / 2),
+ yOffset);
+
+ for (let i = 0; i < this._number; i++) {
+ cr.newSubPath();
+ cr.rectangle(i * dashLength + i * spacing, 0, dashLength, size);
+ }
+
+ cr.strokePreserve();
+ Utils.cairoSetSourceColor(cr, this._bodyColor);
+ cr.fill();
+ }
+ }
+}
+
+// Adapted from dash-to-panel by Jason DeRose
+// https://github.com/jderose9/dash-to-panel
+class RunningIndicatorMetro extends RunningIndicatorDots {
+ constructor(source) {
+ super(source);
+ this._source.add_style_class_name('metro');
+ }
+
+ destroy() {
+ this._source.remove_style_class_name('metro');
+ super.destroy();
+ }
+
+ _drawIndicator(cr) {
+ if (this._source.running) {
+ const size = Math.max(this._width / 20, this._borderWidth);
+ let padding = 0;
+ // For the backlit case here we don't want the outer border visible
+ if (Docking.DockManager.settings.unityBacklitItems &&
+ !Docking.DockManager.settings.customThemeCustomizeRunningDots)
+ padding = 0;
+ const yOffset = this._height - padding - size;
+
+ const n = this._number;
+ if (n <= 1) {
+ cr.translate(0, yOffset);
+ Utils.cairoSetSourceColor(cr, this._bodyColor);
+ cr.newSubPath();
+ cr.rectangle(0, 0, this._width, size);
+ cr.fill();
+ } else {
+ // need to scale with the SVG for the stacked highlight
+ const blackenedLength = (1 / 48) * this._width;
+ const darkenedLength = this._source.focused
+ ? (2 / 48) * this._width : (10 / 48) * this._width;
+ const blackenedColor = this._bodyColor.shade(.3);
+ const darkenedColor = this._bodyColor.shade(.7);
+
+ cr.translate(0, yOffset);
+
+ Utils.cairoSetSourceColor(cr, this._bodyColor);
+ cr.newSubPath();
+ cr.rectangle(0, 0, this._width - darkenedLength - blackenedLength, size);
+ cr.fill();
+ Utils.cairoSetSourceColor(cr, blackenedColor);
+ cr.newSubPath();
+ cr.rectangle(this._width - darkenedLength - blackenedLength, 0, 1, size);
+ cr.fill();
+ Utils.cairoSetSourceColor(cr, darkenedColor);
+ cr.newSubPath();
+ cr.rectangle(this._width - darkenedLength, 0, darkenedLength, size);
+ cr.fill();
+ }
+ }
+ }
+}
+
+class RunningIndicatorBinary extends RunningIndicatorDots {
+ _drawIndicator(cr) {
+ // Draw the required numbers of dots
+ const n = Math.min(15, this._source.windowsCount);
+
+ if (this._source.running) {
+ const size = Math.max(this._width / 11, this._borderWidth);
+ const spacing = Math.ceil(this._width / 18);
+ const yOffset = this._height - size;
+ const binaryValue = String(`0000${(n >>> 0).toString(2)}`).slice(-4);
+
+ cr.setLineWidth(this._borderWidth);
+ Utils.cairoSetSourceColor(cr, this._borderColor);
+
+ cr.translate(Math.floor((this._width - 4 * size - (4 - 1) * spacing) / 2), yOffset);
+ for (let i = 0; i < binaryValue.length; i++) {
+ if (binaryValue[i] === '1') {
+ cr.newSubPath();
+ cr.arc((2 * i + 1) * this._radius + i * spacing,
+ -this._radius - this._borderWidth / 2,
+ this._radius, 0, 2 * Math.PI);
+ } else {
+ cr.newSubPath();
+ cr.rectangle(i * size + i * spacing,
+ -this._radius - this._borderWidth / 2 - size / 5,
+ size, size / 3);
+ }
+ }
+ cr.strokePreserve();
+ Utils.cairoSetSourceColor(cr, this._bodyColor);
+ cr.fill();
+ }
+ }
+}
+
+class RunningIndicatorDot extends RunningIndicatorDots {
+ _computeStyle() {
+ super._computeStyle();
+
+ this._radius = Math.max(this._width / 26, this._borderWidth / 2);
+ }
+
+ _drawIndicator(cr) {
+ if (!this._source.running)
+ return;
+
+ cr.setLineWidth(this._borderWidth);
+ Utils.cairoSetSourceColor(cr, this._borderColor);
+
+ // draw from the bottom case:
+ cr.translate(
+ (this._width - 2 * this._radius) / 2,
+ this._height - this._padding);
+ cr.newSubPath();
+ cr.arc(this._radius,
+ -this._radius - this._borderWidth / 2,
+ this._radius, 0, 2 * Math.PI);
+
+ cr.strokePreserve();
+ Utils.cairoSetSourceColor(cr, this._bodyColor);
+ cr.fill();
+ }
+}
+
+/*
+ * Unity like notification and progress indicators
+ */
+export class UnityIndicator extends IndicatorBase {
+ static defaultProgressBar = {
+ // default values for the progress bar itself
+ background: {
+ colorStart: {red: 204, green: 204, blue: 204, alpha: 255},
+ colorEnd: null,
+ },
+ border: {
+ colorStart: {red: 230, green: 230, blue: 230, alpha: 255},
+ colorEnd: null,
+ },
+ };
+
+ static defaultProgressBarTrack = {
+ // default values for the progress bar track
+ background: {
+ colorStart: {red: 64, green: 64, blue: 64, alpha: 255},
+ colorEnd: {red: 89, green: 89, blue: 89, alpha: 255},
+ offsetStart: 0.4,
+ offsetEnd: 0.9,
+ },
+ border: {
+ colorStart: {red: 128, green: 128, blue: 128, alpha: 26},
+ colorEnd: {red: 204, green: 204, blue: 204, alpha: 102},
+ offsetStart: 0.5,
+ offsetEnd: 0.9,
+ },
+ };
+
+ static notificationBadgeSignals = Symbol('notification-badge-signals');
+
+ constructor(source) {
+ super(source);
+
+ const {remoteModel, notificationsMonitor} = Docking.DockManager.getDefault();
+ const remoteEntry = remoteModel.lookupById(this._source.app.id);
+ this._remoteEntry = remoteEntry;
+
+ this._signalsHandler.add([
+ remoteEntry,
+ ['count-changed', 'count-visible-changed'],
+ () => this._updateNotificationsCount(),
+ ], [
+ remoteEntry,
+ ['progress-changed', 'progress-visible-changed'],
+ (sender, {progress, progress_visible: progressVisible}) =>
+ this.setProgress(progressVisible ? progress : -1),
+ ], [
+ remoteEntry,
+ 'urgent-changed',
+ (sender, {urgent}) => this.setUrgent(urgent),
+ ], [
+ remoteEntry,
+ 'updating-changed',
+ (sender, {updating}) => this.setUpdating(updating),
+ ], [
+ notificationsMonitor,
+ 'changed',
+ () => this._updateNotificationsCount(),
+ ], [
+ this._source,
+ 'style-changed',
+ () => this._updateIconStyle(),
+ ]);
+
+ this._updateNotificationsCount();
+ this.setProgress(this._remoteEntry.progress_visible
+ ? this._remoteEntry.progress : -1);
+ this.setUrgent(this._remoteEntry.urgent);
+ this.setUpdating(this._remoteEntry.updating);
+ }
+
+ destroy() {
+ this._notificationBadgeBin?.destroy();
+ this._notificationBadgeBin = null;
+ this._hideProgressOverlay();
+ this.setUrgent(false);
+ this.setUpdating(false);
+ this._remoteEntry = null;
+
+ super.destroy();
+ }
+
+ _updateNotificationBadgeStyle() {
+ const themeContext = St.ThemeContext.get_for_stage(global.stage);
+ const fontDesc = themeContext.get_font();
+ const defaultFontSize = fontDesc.get_size() / 1024;
+ let fontSize = defaultFontSize * 0.9;
+ const {iconSize} = Main.overview.dash;
+ const defaultIconSize = Docking.DockManager.settings.get_default_value(
+ 'dash-max-icon-size').unpack();
+
+ if (!fontDesc.get_size_is_absolute()) {
+ // fontSize was expressed in points, so convert to pixel
+ fontSize /= 0.75;
+ }
+
+ let sizeMultiplier;
+ if (iconSize < defaultIconSize) {
+ sizeMultiplier = Math.max(24, Math.min(iconSize +
+ iconSize * 0.3, defaultIconSize)) / defaultIconSize;
+ } else {
+ sizeMultiplier = iconSize / defaultIconSize;
+ }
+
+ fontSize = Math.round(sizeMultiplier * fontSize);
+ const leftMargin = Math.round(sizeMultiplier * 3);
+
+ this._notificationBadgeBin.child.set_style(
+ `font-size: ${fontSize}px;` +
+ `margin-left: ${leftMargin}px`
+ );
+ }
+
+ _notificationBadgeCountToText(count) {
+ if (count <= 9999) {
+ return count.toString();
+ } else if (count < 1e5) {
+ const thousands = count / 1e3;
+ return `${thousands.toFixed(1).toString()}k`;
+ } else if (count < 1e6) {
+ const thousands = count / 1e3;
+ return `${thousands.toFixed(0).toString()}k`;
+ } else if (count < 1e8) {
+ const millions = count / 1e6;
+ return `${millions.toFixed(1).toString()}M`;
+ } else if (count < 1e9) {
+ const millions = count / 1e6;
+ return `${millions.toFixed(0).toString()}M`;
+ } else {
+ const billions = count / 1e9;
+ return `${billions.toFixed(1).toString()}B`;
+ }
+ }
+
+ _updateNotificationsCount() {
+ const remoteCount = this._remoteEntry['count-visible']
+ ? this._remoteEntry.count ?? 0 : 0;
+
+ if (remoteCount > 0 &&
+ Docking.DockManager.settings.applicationCounterOverridesNotifications) {
+ this.setNotificationCount(remoteCount);
+ return;
+ }
+
+ const {notificationsMonitor} = Docking.DockManager.getDefault();
+ const notificationsCount = notificationsMonitor.getAppNotificationsCount(
+ this._source.app.id);
+
+ this.setNotificationCount(remoteCount + notificationsCount);
+ }
+
+ _updateNotificationsBadge(text) {
+ if (this._notificationBadgeBin) {
+ this._notificationBadgeBin.child.text = text;
+ return;
+ }
+
+ this._notificationBadgeBin = new St.Bin({
+ child: new St.Label({
+ styleClass: 'notification-badge',
+ text,
+ }),
+ xAlign: Clutter.ActorAlign.END,
+ yAlign: Clutter.ActorAlign.START,
+ xExpand: true,
+ yExpand: true,
+ });
+ this._notificationBadgeBin.child.clutterText.ellipsize =
+ Pango.EllipsizeMode.MIDDLE;
+
+ this._source._iconContainer.add_child(this._notificationBadgeBin);
+ this._updateNotificationBadgeStyle();
+
+ const themeContext = St.ThemeContext.get_for_stage(global.stage);
+ this._signalsHandler.addWithLabel(UnityIndicator.notificationBadgeSignals, [
+ themeContext,
+ 'changed',
+ () => this._updateNotificationBadgeStyle(),
+ ], [
+ themeContext,
+ 'notify::scale-factor',
+ () => this._updateNotificationBadgeStyle(),
+ ], [
+ this._source._iconContainer,
+ 'notify::size',
+ () => this._updateNotificationBadgeStyle(),
+ ]);
+ }
+
+ setNotificationCount(count) {
+ if (count > 0) {
+ const text = this._notificationBadgeCountToText(count);
+ this._updateNotificationsBadge(text);
+ } else if (this._notificationBadgeBin) {
+ this._signalsHandler.removeWithLabel(UnityIndicator.notificationBadgeSignals);
+ this._notificationBadgeBin.destroy();
+ this._notificationBadgeBin = null;
+ }
+ }
+
+ _showProgressOverlay() {
+ if (this._progressOverlayArea) {
+ this._updateProgressOverlay();
+ return;
+ }
+
+ this._progressOverlayArea = new St.DrawingArea({x_expand: true, y_expand: true});
+ this._progressOverlayArea.add_style_class_name('progress-bar');
+ this._progressOverlayArea.connect('repaint', () => {
+ this._drawProgressOverlay(this._progressOverlayArea);
+ });
+
+ this._source._iconContainer.add_child(this._progressOverlayArea);
+ this._updateProgressOverlay();
+ }
+
+ _hideProgressOverlay() {
+ this._progressOverlayArea?.destroy();
+ this._progressOverlayArea = null;
+ }
+
+ _updateProgressOverlay() {
+ this._progressOverlayArea?.queue_repaint();
+ }
+
+ _readGradientData(node, elementName, defaultValues) {
+ const output = {
+ colorStart: defaultValues.colorStart,
+ colorEnd: defaultValues.colorEnd,
+ offsetStart: defaultValues.offsetStart ?? 0.0,
+ offsetEnd: defaultValues.offsetEnd ?? 1.0,
+ };
+
+ const [hasElementName, elementNameValue] = node.lookup_color(elementName, false);
+ if (hasElementName) {
+ output.colorStart = elementNameValue;
+ output.colorEnd = null;
+ } else {
+ const [hasColorStart, colorStartValue] = node.lookup_color(`${elementName}-color-start`, false);
+ const [hasColorEnd, colorEndValue] = node.lookup_color(`${elementName}-color-end`, false);
+ if (hasColorStart && hasColorEnd) {
+ output.colorStart = colorStartValue;
+ output.colorEnd = colorEndValue;
+ }
+ }
+
+ const [hasOffsetStart, offsetStartValue] = node.lookup_color(`${elementName}-offset-start`, false);
+ if (hasOffsetStart)
+ output.offsetStart = offsetStartValue;
+
+ const [hasOffsetEnd, offsetEndValue] = node.lookup_color(`${elementName}-offset-end`, false);
+ if (hasOffsetEnd)
+ output.offsetEnd = offsetEndValue;
+
+ return output;
+ }
+
+ _readThemeDoubleValue(node, elementName, defaultValue) {
+ const [hasValue, value] = node.lookup_double(elementName, false);
+ return hasValue ? value : defaultValue;
+ }
+
+ _readElementData(node, elementName, defaultValues) {
+ return {
+ background: this._readGradientData(node, `${elementName}-background`, defaultValues.background),
+ border: this._readGradientData(node, `${elementName}-border`, defaultValues.border),
+ lineWidth: this._readThemeDoubleValue(node, `${elementName}-line-width`,
+ defaultValues.lineWidth ?? 1.0),
+ };
+ }
+
+ _createGradient(values, x0, y0, x1, y1) {
+ if (values.colorEnd) {
+ const gradient = new Cairo.LinearGradient(x0, y0, x1, y1);
+ gradient.addColorStopRGBA(values.offsetStart,
+ values.colorStart.red / 255,
+ values.colorStart.green / 255,
+ values.colorStart.blue / 255,
+ values.colorStart.alpha / 255);
+ gradient.addColorStopRGBA(values.offsetEnd,
+ values.colorEnd.red / 255,
+ values.colorEnd.green / 255,
+ values.colorEnd.blue / 255,
+ values.colorEnd.alpha / 255);
+ return gradient;
+ } else {
+ const gradient = Cairo.SolidPattern.createRGBA(values.colorStart.red / 255,
+ values.colorStart.green / 255,
+ values.colorStart.blue / 255,
+ values.colorStart.alpha / 255);
+ return gradient;
+ }
+ }
+
+ _drawProgressOverlay(area) {
+ const {scaleFactor} = St.ThemeContext.get_for_stage(global.stage);
+ const [surfaceWidth, surfaceHeight] = area.get_surface_size();
+ const cr = area.get_context();
+ const node = this._progressOverlayArea.get_theme_node();
+ const iconSize = this._source.icon.iconSize * scaleFactor;
+
+ let x = Math.floor((surfaceWidth - iconSize) / 2);
+ let y = Math.floor((surfaceHeight - iconSize) / 2);
+
+ const readThemeValue = element =>
+ this._readThemeDoubleValue(node, `-progress-bar-${element}`);
+
+ y = readThemeValue('top-offset') ?? y;
+
+ const baseLineWidth = Math.floor(Number(scaleFactor));
+ const horizontalPadding = iconSize *
+ Utils.clampDouble(readThemeValue('horizontal-padding') ?? 0.05);
+ const verticalPadding = iconSize *
+ Utils.clampDouble(readThemeValue('vertical-padding') ?? 0.05);
+ const heightFactor =
+ Utils.clampDouble(readThemeValue('height-factor') ?? 0.20);
+
+ let width = iconSize - 2.0 * horizontalPadding;
+ let height = Math.floor(Math.min(18.0 * scaleFactor, heightFactor * iconSize));
+ x += horizontalPadding;
+
+ const valign = Utils.clampDouble(readThemeValue('valign') ?? 1);
+ y += (iconSize - height - verticalPadding) * valign;
+
+ const progressBarTrack = this._readElementData(node,
+ '-progress-bar-track',
+ UnityIndicator.defaultProgressBarTrack);
+
+ const progressBar = this._readElementData(node,
+ '-progress-bar',
+ UnityIndicator.defaultProgressBar);
+
+ // Draw the track
+ let lineWidth = baseLineWidth * progressBarTrack.lineWidth;
+ cr.setLineWidth(lineWidth);
+
+ x += lineWidth;
+ y += lineWidth;
+ width -= 2.0 * lineWidth;
+ height -= 2.0 * lineWidth;
+
+ let fill = this._createGradient(progressBarTrack.background, 0, y, 0, y + height);
+ let stroke = this._createGradient(progressBarTrack.border, 0, y, 0, y + height);
+ Utils.drawRoundedLine(cr, x + lineWidth / 2.0,
+ y + lineWidth / 2.0, width, height, true, true, stroke, fill);
+
+ // Draw the finished bar
+ lineWidth = baseLineWidth * progressBar.lineWidth;
+ cr.setLineWidth(lineWidth);
+
+ x += lineWidth;
+ y += lineWidth;
+ width -= 2.0 * lineWidth;
+ height -= 2.0 * lineWidth;
+
+ const finishedWidth = Math.ceil(this._progress * width);
+ fill = this._createGradient(progressBar.background, 0, y, 0, y + height);
+ stroke = this._createGradient(progressBar.border, 0, y, 0, y + height);
+
+ if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL) {
+ Utils.drawRoundedLine(cr,
+ x + lineWidth / 2.0 + width - finishedWidth, y + lineWidth / 2.0,
+ finishedWidth, height, true, true, stroke, fill);
+ } else {
+ Utils.drawRoundedLine(cr, x + lineWidth / 2.0, y + lineWidth / 2.0,
+ finishedWidth, height, true, true, stroke, fill);
+ }
+
+ cr.$dispose();
+ }
+
+ setProgress(progress) {
+ if (progress < 0) {
+ this._hideProgressOverlay();
+ } else {
+ this._progress = Math.min(progress, 1.0);
+ this._showProgressOverlay();
+ }
+ }
+
+ setUrgent(urgent) {
+ if (urgent || this._isUrgent !== undefined)
+ this._source.urgent = urgent;
+
+ if (urgent)
+ this._isUrgent = urgent;
+ else
+ delete this._isUrgent;
+ }
+
+ setUpdating(updating) {
+ this._source.updating = updating;
+ }
+
+ _updateIconStyle() {
+ const opacity = this._readThemeDoubleValue(this._source.get_theme_node(),
+ 'opacity') ?? (this._source.updating ? 0.5 : 1);
+ this._source.icon.set_opacity(255 * opacity);
+ }
+}
+
+
+// Global icon cache. Used for Unity7 styling.
+const iconCacheMap = new Map();
+// Max number of items to store
+// We don't expect to ever reach this number, but let's put an hard limit to avoid
+// even the remote possibility of the cached items to grow indefinitely.
+const MAX_CACHED_ITEMS = 1000;
+// When the size exceed it, the oldest 'n' ones are deleted
+const BATCH_SIZE_TO_DELETE = 50;
+// The icon size used to extract the dominant color
+const DOMINANT_COLOR_ICON_SIZE = 64;
+
+// Compute dominant color from the app icon.
+// The color is cached for efficiency.
+class DominantColorExtractor {
+ constructor(app) {
+ this._app = app;
+ }
+
+ /**
+ * Try to get the pixel buffer for the current icon, if not fail gracefully
+ */
+ _getIconPixBuf() {
+ let iconTexture = this._app.create_icon_texture(16);
+ const themeLoader = Docking.DockManager.iconTheme;
+
+ // Unable to load the icon texture, use fallback
+ if (iconTexture instanceof St.Icon === false)
+ return null;
+
+
+ iconTexture = iconTexture.get_gicon();
+
+ // Unable to load the icon texture, use fallback
+ if (!iconTexture)
+ return null;
+
+ if (iconTexture instanceof Gio.FileIcon) {
+ // Use GdkPixBuf to load the pixel buffer from the provided file path
+ return GdkPixbuf.Pixbuf.new_from_file(iconTexture.get_file().get_path());
+ } else if (iconTexture instanceof Gio.ThemedIcon) {
+ // Get the first pixel buffer available in the icon theme
+ const iconNames = iconTexture.get_names();
+ const iconInfo = themeLoader.choose_icon(iconNames, DOMINANT_COLOR_ICON_SIZE, 0);
+
+ if (iconInfo)
+ return iconInfo.load_icon();
+ else
+ return null;
+ }
+
+ // Use GdkPixBuf to load the pixel buffer from memory
+ // iconTexture.load is available unless iconTexture is not an instance of Gio.LoadableIcon
+ // this means that iconTexture is an instance of Gio.EmblemedIcon,
+ // which may be converted to a normal icon via iconTexture.get_icon?
+ const [iconBuffer] = iconTexture.load(DOMINANT_COLOR_ICON_SIZE, null);
+ return GdkPixbuf.Pixbuf.new_from_stream(iconBuffer, null);
+ }
+
+ /**
+ * The backlight color choosing algorithm was mostly ported to javascript from the
+ * Unity7 C++ source of Canonicals:
+ * https://bazaar.launchpad.net/~unity-team/unity/trunk/view/head:/launcher/LauncherIcon.cpp
+ * so it more or less works the same way.
+ */
+ _getColorPalette() {
+ if (iconCacheMap.get(this._app.get_id())) {
+ // We already know the answer
+ return iconCacheMap.get(this._app.get_id());
+ }
+
+ const pixBuf = this._getIconPixBuf();
+ if (!pixBuf)
+ return null;
+
+ let pixels = pixBuf.get_pixels();
+
+ let total = 0,
+ rTotal = 0,
+ gTotal = 0,
+ bTotal = 0;
+
+ let resampleX = 1;
+ let resampleY = 1;
+
+ // Resampling of large icons
+ // We resample icons larger than twice the desired size, as the resampling
+ // to a size s
+ // DOMINANT_COLOR_ICON_SIZE < s < 2*DOMINANT_COLOR_ICON_SIZE,
+ // most of the case exactly DOMINANT_COLOR_ICON_SIZE as the icon size is
+ // typically a multiple of it.
+ const width = pixBuf.get_width();
+ const height = pixBuf.get_height();
+
+ // Resample
+ if (height >= 2 * DOMINANT_COLOR_ICON_SIZE)
+ resampleY = Math.floor(height / DOMINANT_COLOR_ICON_SIZE);
+
+ if (width >= 2 * DOMINANT_COLOR_ICON_SIZE)
+ resampleX = Math.floor(width / DOMINANT_COLOR_ICON_SIZE);
+
+ if (resampleX !== 1 || resampleY !== 1)
+ pixels = this._resamplePixels(pixels, resampleX, resampleY);
+
+ // computing the limit outside the for (where it would be repeated at each iteration)
+ // for performance reasons
+ const limit = pixels.length;
+ for (let offset = 0; offset < limit; offset += 4) {
+ const r = pixels[offset],
+ g = pixels[offset + 1],
+ b = pixels[offset + 2],
+ a = pixels[offset + 3];
+
+ const saturation = Math.max(r, g, b) - Math.min(r, g, b);
+ const relevance = 0.1 * 255 * 255 + 0.9 * a * saturation;
+
+ rTotal += r * relevance;
+ gTotal += g * relevance;
+ bTotal += b * relevance;
+
+ total += relevance;
+ }
+
+ total *= 255;
+
+ const r = rTotal / total,
+ g = gTotal / total,
+ b = bTotal / total;
+
+ const hsv = Utils.ColorUtils.RGBtoHSV(r * 255, g * 255, b * 255);
+
+ if (hsv.s > 0.15)
+ hsv.s = 0.65;
+ hsv.v = 0.90;
+
+ const rgb = Utils.ColorUtils.HSVtoRGB(hsv.h, hsv.s, hsv.v);
+
+ // Cache the result.
+ const backgroundColor = {
+ lighter: Utils.ColorUtils.ColorLuminance(rgb.r, rgb.g, rgb.b, 0.2),
+ original: Utils.ColorUtils.ColorLuminance(rgb.r, rgb.g, rgb.b, 0),
+ darker: Utils.ColorUtils.ColorLuminance(rgb.r, rgb.g, rgb.b, -0.5),
+ };
+
+ if (iconCacheMap.size >= MAX_CACHED_ITEMS) {
+ // delete oldest cached values (which are in order of insertions)
+ let ctr = 0;
+ for (const key of iconCacheMap.keys()) {
+ if (++ctr > BATCH_SIZE_TO_DELETE)
+ break;
+ iconCacheMap.delete(key);
+ }
+ }
+
+ iconCacheMap.set(this._app.get_id(), backgroundColor);
+
+ return backgroundColor;
+ }
+
+ /**
+ * Downscale large icons before scanning for the backlight color to
+ * improve performance.
+ *
+ * @param pixBuf
+ * @param pixels
+ * @param resampleX
+ * @param resampleY
+ *
+ * @returns [];
+ */
+ _resamplePixels(pixels, resampleX, resampleY) {
+ const resampledPixels = [];
+ // computing the limit outside the for (where it would be repeated at each iteration)
+ // for performance reasons
+ const limit = pixels.length / (resampleX * resampleY) / 4;
+ for (let i = 0; i < limit; i++) {
+ const pixel = i * resampleX * resampleY;
+
+ resampledPixels.push(pixels[pixel * 4]);
+ resampledPixels.push(pixels[pixel * 4 + 1]);
+ resampledPixels.push(pixels[pixel * 4 + 2]);
+ resampledPixels.push(pixels[pixel * 4 + 3]);
+ }
+
+ return resampledPixels;
+ }
+}
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appIcons.js b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appIcons.js
new file mode 100644
index 0000000..6b0ac24
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appIcons.js
@@ -0,0 +1,1635 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+import {
+ Clutter,
+ Gio,
+ GLib,
+ GObject,
+ Meta,
+ Mtk,
+ Shell,
+ St,
+} from './dependencies/gi.js';
+
+import {
+ AppDisplay,
+ AppFavorites,
+ BoxPointer,
+ Dash,
+ Main,
+ PopupMenu,
+} from './dependencies/shell/ui.js';
+
+import {
+ ParentalControlsManager,
+ Util,
+} from './dependencies/shell/misc.js';
+
+import {
+ AppIconIndicators,
+ DBusMenuUtils,
+ Docking,
+ Locations,
+ Theming,
+ Utils,
+ WindowPreview,
+} from './imports.js';
+
+import {Extension} from './dependencies/shell/extensions/extension.js';
+
+// Use __ () and N__() for the extension gettext domain, and reuse
+// the shell domain with the default _() and N_()
+const {gettext: __, ngettext} = Extension;
+
+const DBusMenu = await DBusMenuUtils.haveDBusMenu();
+
+const tracker = Shell.WindowTracker.get_default();
+
+const Labels = Object.freeze({
+ ISOLATE_MONITORS: Symbol('isolate-monitors'),
+ ISOLATE_WORKSPACES: Symbol('isolate-workspaces'),
+ URGENT_WINDOWS: Symbol('urgent-windows'),
+});
+
+const clickAction = Object.freeze({
+ SKIP: 0,
+ MINIMIZE: 1,
+ LAUNCH: 2,
+ CYCLE_WINDOWS: 3,
+ MINIMIZE_OR_OVERVIEW: 4,
+ PREVIEWS: 5,
+ MINIMIZE_OR_PREVIEWS: 6,
+ FOCUS_OR_PREVIEWS: 7,
+ FOCUS_OR_APP_SPREAD: 8,
+ FOCUS_MINIMIZE_OR_PREVIEWS: 9,
+ FOCUS_MINIMIZE_OR_APP_SPREAD: 10,
+ QUIT: 11,
+});
+
+const scrollAction = Object.freeze({
+ DO_NOTHING: 0,
+ CYCLE_WINDOWS: 1,
+ SWITCH_WORKSPACE: 2,
+});
+
+// module "Dash" did not export DASH_ITEM_LABEL_SHOW_TIME, so let's define it.
+const DASH_ITEM_LABEL_SHOW_TIME = Dash.DASH_ITEM_LABEL_SHOW_TIME ?? 150;
+
+let recentlyClickedAppLoopId = 0;
+let recentlyClickedApp = null;
+let recentlyClickedAppWindows = null;
+let recentlyClickedAppIndex = 0;
+let recentlyClickedAppMonitor = -1;
+
+/**
+ * Extend AppIcon
+ *
+ * - Apply a css class based on the number of windows of each application (#N);
+ * - Customized indicators for running applications in place of the default "dot" style which is hidden (#N);
+ * a class of the form "running#N" is applied to the AppWellIcon actor.
+ * like the original .running one.
+ * - Add a .focused style to the focused app
+ * - Customize click actions.
+ * - Update minimization animation target
+ * - Update menu if open on windows change
+ */
+const DockAbstractAppIcon = GObject.registerClass({
+ GTypeFlags: GObject.TypeFlags.ABSTRACT,
+ Properties: {
+ 'focused': GObject.ParamSpec.boolean(
+ 'focused', 'focused', 'focused',
+ GObject.ParamFlags.READWRITE,
+ false),
+ 'running': GObject.ParamSpec.boolean(
+ 'running', 'running', 'running',
+ GObject.ParamFlags.READWRITE,
+ false),
+ 'urgent': GObject.ParamSpec.boolean(
+ 'urgent', 'urgent', 'urgent',
+ GObject.ParamFlags.READWRITE,
+ false),
+ 'updating': GObject.ParamSpec.boolean(
+ 'updating', 'updating', 'updating',
+ GObject.ParamFlags.READWRITE,
+ false),
+ 'windows-count': GObject.ParamSpec.uint(
+ 'windows-count', 'windows-count', 'windows-count',
+ GObject.ParamFlags.READWRITE,
+ 0, GLib.MAXUINT32, 0),
+ },
+}, class DockAbstractAppIcon extends Dash.DashIcon {
+ // settings are required inside.
+ _init(app, monitorIndex, iconAnimator) {
+ super._init(app);
+
+ // a prefix is required to avoid conflicting with the parent class variable
+ this.monitorIndex = monitorIndex;
+ this._signalsHandler = new Utils.GlobalSignalsHandler(this);
+ this.iconAnimator = iconAnimator;
+ this._indicator = new AppIconIndicators.AppIconIndicator(this);
+ this._urgentWindows = new Set();
+
+ // Monitor windows-changes instead of app state.
+ // Keep using the same Id and function callback (that is extended)
+ if (this._stateChangedId > 0) {
+ this.app.disconnect(this._stateChangedId);
+ this._stateChangedId = 0;
+ }
+
+ this._signalsHandler.add(this.app, 'windows-changed', () => this._updateWindows());
+ this._signalsHandler.add(this.app, 'notify::state', () => this._updateRunningState());
+ this._signalsHandler.add(global.display, 'window-demands-attention', (_dpy, window) =>
+ this._onWindowDemandsAttention(window));
+ this._signalsHandler.add(global.display, 'window-marked-urgent', (_dpy, window) =>
+ this._onWindowDemandsAttention(window));
+
+ // In Wayland sessions, this signal is needed to track the state of windows dragged
+ // from one monitor to another. As this is triggered quite often (whenever a new
+ // window of any application opened or moved to a different desktop),
+ // we restrict this signal to the case when Labels.ISOLATE_MONITORS is true,
+ // and if there are at least 2 monitors.
+ if (Docking.DockManager.settings.isolateMonitors &&
+ Main.layoutManager.monitors.length > 1) {
+ this._signalsHandler.addWithLabel(Labels.ISOLATE_MONITORS,
+ global.display,
+ 'window-entered-monitor',
+ this._onWindowEntered.bind(this));
+ }
+
+ this.connect('notify::running', () => {
+ if (this.running)
+ this.add_style_class_name('running');
+ else
+ this.remove_style_class_name('running');
+ });
+ this.notify('running');
+
+ this.connect('notify::focused', () => {
+ if (this.focused)
+ this.add_style_class_name('focused');
+ else
+ this.remove_style_class_name('focused');
+ });
+ this.notify('focused');
+
+ this.connect('notify::updating', () => {
+ if (this.updating)
+ this.add_style_class_name('updating');
+ else
+ this.remove_style_class_name('updating');
+ });
+ this.notify('updating');
+
+ const {notificationsMonitor} = Docking.DockManager.getDefault();
+
+ this.connect('notify::urgent', () => {
+ const icon = this.icon._iconBin;
+ this._signalsHandler.removeWithLabel(Labels.URGENT_WINDOWS);
+ if (this.urgent) {
+ if (Docking.DockManager.settings.danceUrgentApplications &&
+ notificationsMonitor.enabled) {
+ icon.set_pivot_point(0.5, 0.5);
+ this.iconAnimator.addAnimation(icon, 'wiggle');
+ }
+ if (this.running && !this._urgentWindows.size) {
+ const urgentWindows = this.getInterestingWindows();
+ urgentWindows.forEach(w => (w._manualUrgency = true));
+ this._updateUrgentWindows(urgentWindows);
+ }
+ } else {
+ this.iconAnimator.removeAnimation(icon, 'wiggle');
+ icon.rotation_angle_z = 0;
+ this._urgentWindows.forEach(w => delete w._manualUrgency);
+ this._updateUrgentWindows();
+ }
+ });
+ this.notify('urgent');
+
+ this._progressOverlayArea = null;
+ this._progress = 0;
+
+ [
+ 'apply-custom-theme',
+ 'running-indicator-style',
+ 'show-icons-emblems',
+ 'show-icons-notifications-counter',
+ 'application-counter-overrides-notifications',
+ ].forEach(key => {
+ this._signalsHandler.add(
+ Docking.DockManager.settings,
+ `changed::${key}`, () => {
+ this._indicator.destroy();
+ this._indicator = new AppIconIndicators.AppIconIndicator(this);
+ }
+ );
+ });
+
+ this._signalsHandler.add(notificationsMonitor, 'state-changed', () => {
+ this._indicator.destroy();
+ this._indicator = new AppIconIndicators.AppIconIndicator(this);
+ });
+
+ this._updateState();
+ this._numberOverlay();
+
+ this._previewMenuManager = null;
+ this._previewMenu = null;
+ }
+
+ _onDestroy() {
+ super._onDestroy();
+
+ // This is necessary due to an upstream bug
+ // https://bugzilla.gnome.org/show_bug.cgi?id=757556
+ // It can be safely removed once it get solved upstream.
+ this._menu?.close(false);
+ delete this._menu;
+ }
+
+ ownsWindow(window) {
+ return this.app === tracker.get_window_app(window);
+ }
+
+ _onWindowEntered(metaScreen, monitorIndex, metaWin) {
+ if (this.ownsWindow(metaWin))
+ this._updateWindows();
+ }
+
+ vfunc_scroll_event(scrollEvent) {
+ const {settings} = Docking.DockManager;
+ const isEnabled = settings.scrollAction === scrollAction.CYCLE_WINDOWS;
+ if (!isEnabled)
+ return Clutter.EVENT_PROPAGATE;
+
+ // We only activate windows of running applications, i.e. we never open new windows
+ // We check if the app is running, and that the # of windows is > 0 in
+ // case we use workspace isolation,
+ if (!this.running)
+ return Clutter.EVENT_PROPAGATE;
+
+ if (this._optionalScrollCycleWindowsDeadTimeId) {
+ return Clutter.EVENT_PROPAGATE;
+ } else {
+ this._optionalScrollCycleWindowsDeadTimeId = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT, 250, () => {
+ this._optionalScrollCycleWindowsDeadTimeId = 0;
+ });
+ }
+
+ let direction = null;
+
+ switch (scrollEvent.direction) {
+ case Clutter.ScrollDirection.UP:
+ direction = Meta.MotionDirection.UP;
+ break;
+ case Clutter.ScrollDirection.DOWN:
+ direction = Meta.MotionDirection.DOWN;
+ break;
+ case Clutter.ScrollDirection.SMOOTH: {
+ const [, dy] = Clutter.get_current_event().get_scroll_delta();
+ if (dy < 0)
+ direction = Meta.MotionDirection.UP;
+ else if (dy > 0)
+ direction = Meta.MotionDirection.DOWN;
+ }
+ break;
+ }
+
+ if (!Main.overview.visible) {
+ const reversed = direction === Meta.MotionDirection.UP;
+ if (this.focused && !this._urgentWindows.size) {
+ this._cycleThroughWindows(reversed);
+ } else {
+ // Activate the first window
+ const windows = this.getInterestingWindows();
+ if (windows.length > 0) {
+ const [w] = windows;
+ Main.activateWindow(w);
+ }
+ }
+ } else {
+ this.app.activate();
+ }
+ return Clutter.EVENT_STOP;
+ }
+
+ _updateWindows() {
+ if (this._menu && this._menu.isOpen)
+ this._menu.update();
+
+ this._updateState();
+ this.updateIconGeometry();
+ }
+
+ _updateState() {
+ this._urgentWindows.clear();
+ const interestingWindows = this.getInterestingWindows();
+ this.windowsCount = interestingWindows.length;
+ this._updateRunningState();
+ this._updateFocusState();
+ this._updateUrgentWindows(interestingWindows);
+
+ if (Docking.DockManager.settings.isolateWorkspaces) {
+ this._signalsHandler.removeWithLabel(Labels.ISOLATE_WORKSPACES);
+ interestingWindows.forEach(window =>
+ this._signalsHandler.addWithLabel(Labels.ISOLATE_WORKSPACES,
+ window, 'workspace-changed', () => this._updateWindows()));
+ }
+ }
+
+ _updateRunningState() {
+ this.running = (this.app.state === Shell.AppState.RUNNING) && this.windowsCount;
+ }
+
+ _updateFocusState() {
+ this.focused = tracker.focus_app === this.app && this.running;
+ }
+
+ _updateUrgentWindows(interestingWindows) {
+ this._signalsHandler.removeWithLabel(Labels.URGENT_WINDOWS);
+ this._urgentWindows.clear();
+ if (interestingWindows === undefined)
+ interestingWindows = this.getInterestingWindows();
+ interestingWindows.filter(isWindowUrgent).forEach(win => this._addUrgentWindow(win));
+ this.urgent = !!this._urgentWindows.size;
+ }
+
+ _onWindowDemandsAttention(window) {
+ if (this.ownsWindow(window) && isWindowUrgent(window))
+ this._addUrgentWindow(window);
+ }
+
+ _updateDotStyle() {
+ super._updateDotStyle();
+ const themeNode = this._dot.get_theme_node();
+ this._dot.translationX = themeNode.get_length('offset-x');
+ }
+
+ _addUrgentWindow(window) {
+ if (this._urgentWindows.has(window))
+ return;
+
+ if (window._manualUrgency && window.has_focus()) {
+ delete window._manualUrgency;
+ return;
+ }
+
+ this._urgentWindows.add(window);
+ this.urgent = true;
+
+ const onDemandsAttentionChanged = () => {
+ if (!isWindowUrgent(window))
+ this._updateUrgentWindows();
+ };
+
+ if (window.demandsAttention) {
+ this._signalsHandler.addWithLabel(Labels.URGENT_WINDOWS, window,
+ 'notify::demands-attention', () => onDemandsAttentionChanged());
+ }
+ if (window.urgent) {
+ this._signalsHandler.addWithLabel(Labels.URGENT_WINDOWS, window,
+ 'notify::urgent', () => onDemandsAttentionChanged());
+ }
+ if (window._manualUrgency) {
+ this._signalsHandler.addWithLabel(Labels.URGENT_WINDOWS, window,
+ 'focus', () => {
+ delete window._manualUrgency;
+ onDemandsAttentionChanged();
+ });
+ }
+ }
+
+ /**
+ * Update target for minimization animation
+ */
+ updateIconGeometry() {
+ // If (for unknown reason) the actor is not on the stage the reported size
+ // and position are random values, which might exceeds the integer range
+ // resulting in an error when assigned to the a rect. This is a more like
+ // a workaround to prevent flooding the system with errors.
+ if (!this.get_stage())
+ return;
+
+ const rect = new Mtk.Rectangle();
+
+ [rect.x, rect.y] = this.get_transformed_position();
+ [rect.width, rect.height] = this.get_transformed_size();
+
+ let windows = this.getWindows();
+ if (Docking.DockManager.settings.multiMonitor) {
+ const {monitorIndex} = this;
+ windows = windows.filter(w => w.get_monitor() === monitorIndex);
+ }
+ windows.forEach(w => w.set_icon_geometry(rect));
+ }
+
+ _updateRunningStyle() {
+ // The logic originally in this function has been moved to
+ // AppIconIndicatorBase._updateDefaultDot(). However it cannot be removed as
+ // it called by the parent constructor.
+ }
+
+ popupMenu() {
+ this._removeMenuTimeout?.();
+ this.fake_release();
+ this._draggable.fakeRelease?.();
+
+ if (!this._menu) {
+ this._menu = new DockAppIconMenu(this);
+ this._menu.connect('activate-window', (menu, window) => {
+ if (window) {
+ Main.activateWindow(window);
+ } else {
+ Main.overview.hide();
+ Main.panel.closeCalendar();
+ }
+ });
+ this._menu.connect('open-state-changed', (menu, isPoppedUp) => {
+ if (!isPoppedUp) {
+ this._onMenuPoppedDown();
+ } else {
+ // Setting the max-height is s useful if part of the menu is
+ // scrollable so the minimum height is smaller than the natural height.
+ const monitorIndex = Main.layoutManager.findIndexForActor(this);
+ const workArea = Main.layoutManager.getWorkAreaForMonitor(monitorIndex);
+ const position = Utils.getPosition();
+ const {scaleFactor} = St.ThemeContext.get_for_stage(global.stage);
+ const isHorizontal = position === St.Side.TOP || position === St.Side.BOTTOM;
+ // If horizontal also remove the height of the dash
+ const {dockFixed: fixedDock} = Docking.DockManager.settings;
+ const additionalMargin = isHorizontal && !fixedDock ? Main.overview.dash.height : 0;
+ const verticalMargins = this._menu.actor.margin_top + this._menu.actor.margin_bottom;
+ const maxMenuHeight = workArea.height - additionalMargin - verticalMargins;
+ // Also set a max width to the menu, so long labels (long windows title) get truncated
+ this._menu.actor.style = 'max-width: 400px; ' +
+ `max-height: ${Math.round(maxMenuHeight / scaleFactor)}px;`;
+ }
+ });
+ const id = Main.overview.connect('hiding', () => {
+ this._menu.close();
+ });
+ this._menu.actor.connect('destroy', () => {
+ Main.overview.disconnect(id);
+ });
+
+ this._menuManager.addMenu(this._menu);
+ }
+
+ this.emit('menu-state-changed', true);
+
+ this.set_hover(true);
+ this._menu.popup();
+ // Removed in GNOME 50.
+ this._menuManager.ignoreRelease?.();
+ this.emit('sync-tooltip');
+
+ return false;
+ }
+
+ activate(button) {
+ const event = Clutter.get_current_event();
+ let modifiers = event ? event.get_state() : 0;
+
+ // Only consider SHIFT and CONTROL as modifiers (exclude SUPER, CAPS-LOCK, etc.)
+ modifiers &= Clutter.ModifierType.SHIFT_MASK | Clutter.ModifierType.CONTROL_MASK;
+
+ // We don't change the CTRL-click behavior: in such case we just chain
+ // up the parent method and return.
+ if (modifiers & Clutter.ModifierType.CONTROL_MASK) {
+ // Keep default behavior: launch new window
+ // By calling the parent method I make it compatible
+ // with other extensions tweaking ctrl + click
+ super.activate(button);
+ return;
+ }
+
+ // We check what type of click we have and if the modifier SHIFT is
+ // being used. We then define what buttonAction should be for this
+ // event.
+ let buttonAction = 0;
+ const {settings} = Docking.DockManager;
+ if (button && button === 2) {
+ if (modifiers & Clutter.ModifierType.SHIFT_MASK)
+ buttonAction = settings.shiftMiddleClickAction;
+ else
+ buttonAction = settings.middleClickAction;
+ } else if (button && button === 1) {
+ if (modifiers & Clutter.ModifierType.SHIFT_MASK)
+ buttonAction = settings.shiftClickAction;
+ else
+ buttonAction = settings.clickAction;
+ }
+
+ switch (buttonAction) {
+ case clickAction.FOCUS_OR_APP_SPREAD:
+ if (!Docking.DockManager.getDefault().appSpread.supported)
+ buttonAction = clickAction.FOCUS_OR_PREVIEWS;
+ break;
+
+ case clickAction.FOCUS_MINIMIZE_OR_APP_SPREAD:
+ if (!Docking.DockManager.getDefault().appSpread.supported)
+ buttonAction = clickAction.FOCUS_MINIMIZE_OR_PREVIEWS;
+ break;
+ }
+
+ // We check if the app is running, and that the # of windows is > 0 in
+ // case we use workspace isolation.
+ const windows = this.getInterestingWindows();
+
+ // Some action modes (e.g. MINIMIZE_OR_OVERVIEW) require overview to remain open
+ // This variable keeps track of this
+ let shouldHideOverview = true;
+
+ // We customize the action only when the application is already running
+ if (this.running) {
+ const hasUrgentWindows = !!this._urgentWindows.size;
+ const singleOrUrgentWindows = windows.length === 1 || hasUrgentWindows;
+ switch (buttonAction) {
+ case clickAction.MINIMIZE:
+ // In overview just activate the app, unless the action is explicitly
+ // requested with a keyboard modifier
+ if (!Main.overview.visible || modifiers) {
+ // If we have button=2 or a modifier, allow minimization even if
+ // the app is not focused
+ if (this.focused && !hasUrgentWindows || button === 2 ||
+ modifiers & Clutter.ModifierType.SHIFT_MASK) {
+ // minimize all windows on double click and always in
+ // the case of primary click without additional modifiers
+ let clickCount = 0;
+ if (Clutter.EventType.CLUTTER_BUTTON_PRESS)
+ clickCount = event.get_click_count();
+ const allWindows = (button === 1 && !modifiers) || clickCount > 1;
+ this._minimizeWindow(allWindows);
+ } else {
+ this._activateAllWindows();
+ }
+ } else {
+ const [w] = windows;
+ Main.activateWindow(w);
+ }
+ break;
+
+ case clickAction.MINIMIZE_OR_OVERVIEW:
+ // When a single window is present, toggle minimization
+ // If only one windows is present toggle minimization, but
+ // only when triggered with the simple click action
+ // (no modifiers, no middle click).
+ if (singleOrUrgentWindows && !modifiers && button === 1) {
+ const [w] = windows;
+ if (this.focused) {
+ if (buttonAction !== clickAction.FOCUS_OR_APP_SPREAD) {
+ // Window is raised, minimize it
+ this._minimizeWindow(w);
+ }
+ } else {
+ // Window is minimized, raise it
+ Main.activateWindow(w);
+ }
+ // Launch overview when multiple windows are present
+ // TODO: only show current app windows when gnome shell API will allow it
+ } else {
+ shouldHideOverview = false;
+ Main.overview.toggle();
+ }
+ break;
+
+ case clickAction.CYCLE_WINDOWS:
+ if (!Main.overview.visible) {
+ if (this.focused && !hasUrgentWindows) {
+ this._cycleThroughWindows();
+ } else {
+ // Activate the first window
+ const [w] = windows;
+ Main.activateWindow(w);
+ }
+ } else {
+ this.app.activate();
+ }
+ break;
+
+ case clickAction.FOCUS_OR_PREVIEWS:
+ if (this.focused && !hasUrgentWindows &&
+ (windows.length > 1 || modifiers || button !== 1)) {
+ this._windowPreviews();
+ } else {
+ // Activate the first window
+ const [w] = windows;
+ Main.activateWindow(w);
+ }
+ break;
+
+ case clickAction.FOCUS_MINIMIZE_OR_PREVIEWS:
+ if (this.focused && !hasUrgentWindows) {
+ if (windows.length > 1 || modifiers || button !== 1)
+ this._windowPreviews();
+ else if (!Main.overview.visible)
+ this._minimizeWindow();
+ } else {
+ // Activate the first window
+ const [w] = windows;
+ Main.activateWindow(w);
+ }
+ break;
+
+ case clickAction.LAUNCH:
+ this.launchNewWindow();
+ break;
+
+ case clickAction.PREVIEWS:
+ if (!Main.overview.visible) {
+ // If only one windows is present just switch to it,
+ // but only when triggered with the simple click action
+ // (no modifiers, no middle click).
+ if (singleOrUrgentWindows && !modifiers && button === 1) {
+ const [w] = windows;
+ Main.activateWindow(w);
+ } else {
+ this._windowPreviews();
+ }
+ } else {
+ this.app.activate();
+ }
+ break;
+
+ case clickAction.MINIMIZE_OR_PREVIEWS:
+ // When a single window is present, toggle minimization
+ // If only one windows is present toggle minimization, but only
+ // when triggered with the standard click action (no modifiers,
+ // no middle click).
+ if (!Main.overview.visible) {
+ if (singleOrUrgentWindows && !modifiers && button === 1) {
+ const [w] = windows;
+ if (this.focused) {
+ // Window is raised, minimize it
+ this._minimizeWindow(w);
+ } else {
+ // Window is minimized, raise it
+ Main.activateWindow(w);
+ }
+ } else {
+ // Launch previews when multiple windows are present
+ this._windowPreviews();
+ }
+ } else {
+ this.app.activate();
+ }
+ break;
+
+ case clickAction.FOCUS_OR_APP_SPREAD:
+ if (this.focused && !singleOrUrgentWindows && !modifiers && button === 1) {
+ shouldHideOverview = false;
+ Docking.DockManager.getDefault().appSpread.toggle(this.app);
+ } else {
+ // Activate the first window
+ Main.activateWindow(windows[0]);
+ }
+ break;
+
+ case clickAction.FOCUS_MINIMIZE_OR_APP_SPREAD:
+ if (this.focused && !singleOrUrgentWindows && !modifiers && button === 1) {
+ shouldHideOverview = false;
+ Docking.DockManager.getDefault().appSpread.toggle(this.app);
+ } else if (!this.focused) {
+ // Activate the first window
+ Main.activateWindow(windows[0]);
+ } else {
+ this._minimizeWindow();
+ }
+ break;
+
+ case clickAction.QUIT:
+ this.closeAllWindows();
+ break;
+
+ case clickAction.SKIP:
+ Main.activateWindow(windows[0]);
+ break;
+ }
+ } else {
+ this.launchNewWindow();
+ }
+
+ // Hide overview except when action mode requires it
+ if (shouldHideOverview)
+ Main.overview.hide();
+ }
+
+ shouldShowTooltip() {
+ return super.shouldShowTooltip() && !this._previewMenu?.isOpen &&
+ !Docking.DockManager.settings.hideTooltip;
+ }
+
+ _windowPreviews() {
+ if (!this._previewMenu) {
+ this._previewMenuManager = new PopupMenu.PopupMenuManager(this);
+
+ this._previewMenu = new WindowPreview.WindowPreviewMenu(this);
+
+ this._previewMenuManager.addMenu(this._previewMenu);
+
+ this._previewMenu.connect('open-state-changed', (menu, isPoppedUp) => {
+ if (!isPoppedUp)
+ this._onMenuPoppedDown();
+ });
+ const id = Main.overview.connect('hiding', () => {
+ this._previewMenu.close();
+ });
+ this._previewMenu.actor.connect('destroy', () => {
+ Main.overview.disconnect(id);
+ });
+ }
+
+ this.emit('menu-state-changed', !this._previewMenu.isOpen);
+
+ if (this._previewMenu.isOpen)
+ this._previewMenu.close();
+ else
+ this._previewMenu.popup();
+
+ return false;
+ }
+
+ // Try to do the right thing when attempting to launch a new window of an app. In
+ // particular, if the application doesn't allow to launch a new window, activate
+ // the existing window instead.
+ launchNewWindow() {
+ if (this.updating) {
+ const icon = Gio.Icon.new_for_string('action-unavailable-symbolic');
+ Main.osdWindowManager.show(-1, icon,
+ _('%s is updating, try again later').format(this.name),
+ null);
+ return;
+ }
+
+ if (this.app.state === Shell.AppState.RUNNING &&
+ this.app.can_open_new_window()) {
+ this.animateLaunch();
+ this.app.open_new_window(-1);
+ } else {
+ // Try to manually activate the first window. Otherwise, when the
+ // app is activated by switching to a different workspace, a launch
+ // spinning icon is shown and disappears only after a timeout.
+ const windows = this.getWindows();
+ if (windows.length > 0) {
+ Main.activateWindow(windows[0]);
+ } else {
+ this.app.activate();
+ this.animateLaunch();
+ }
+ }
+ }
+
+ _numberOverlay() {
+ // Add label for a Hot-Key visual aid
+ this._numberOverlayLabel = new St.Label();
+ this._numberOverlayBin = new St.Bin({
+ child: this._numberOverlayLabel,
+ x_align: Clutter.ActorAlign.START,
+ y_align: Clutter.ActorAlign.START,
+ x_expand: true, y_expand: true,
+ });
+ this._numberOverlayLabel.add_style_class_name('number-overlay');
+ this._numberOverlayOrder = -1;
+ this._numberOverlayBin.hide();
+
+ this._iconContainer.add_child(this._numberOverlayBin);
+ }
+
+ updateNumberOverlay() {
+ // We apply an overall scale factor that might come from a HiDPI monitor.
+ // Clutter dimensions are in physical pixels, but CSS measures are in logical
+ // pixels, so make sure to consider the scale.
+ const scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
+ // Set the font size to something smaller than the whole icon so it is
+ // still visible. The border radius is large to make the shape circular
+ const [minWidth_, natWidth] = this._iconContainer.get_preferred_width(-1);
+ const fontSize = Math.round(Math.max(12, 0.3 * natWidth) / scaleFactor);
+ const size = Math.round(fontSize * 1.2);
+ this._numberOverlayLabel.set_style(
+ `font-size: ${fontSize}px;` +
+ `border-radius: ${this.icon.iconSize}px;` +
+ `width: ${size}px; height: ${size}px;`
+ );
+ }
+
+ setNumberOverlay(number) {
+ this._numberOverlayOrder = number;
+ this._numberOverlayLabel.set_text(number.toString());
+ }
+
+ toggleNumberOverlay(activate) {
+ if (activate && this._numberOverlayOrder > -1) {
+ this.updateNumberOverlay();
+ this._numberOverlayBin.show();
+ } else {
+ this._numberOverlayBin.hide();
+ }
+ }
+
+ _minimizeWindow(param) {
+ // Param true make all app windows minimize
+ const windows = this.getInterestingWindows();
+ const currentWorkspace = global.workspace_manager.get_active_workspace();
+ for (let i = 0; i < windows.length; i++) {
+ const w = windows[i];
+ if (w.get_workspace() === currentWorkspace && w.showing_on_its_workspace()) {
+ w.minimize();
+ // Just minimize one window. By specification it should be the
+ // focused window on the current workspace.
+ if (!param)
+ break;
+ }
+ }
+ }
+
+ // By default only non minimized windows are activated.
+ // This activates all windows in the current workspace.
+ _activateAllWindows() {
+ // First activate first window so workspace is switched if needed.
+ // We don't do this if isolation is on!
+ if (!Docking.DockManager.settings.isolateWorkspaces &&
+ !Docking.DockManager.settings.isolateMonitors) {
+ if (!this.running)
+ this.animateLaunch();
+ this.app.activate();
+ }
+
+ // then activate all other app windows in the current workspace
+ const windows = this.getInterestingWindows();
+ const activeWorkspace = global.workspace_manager.get_active_workspace_index();
+
+ if (windows.length <= 0)
+ return;
+
+ for (let i = windows.length - 1; i >= 0; i--) {
+ if (windows[i].get_workspace()?.index() === activeWorkspace)
+ Main.activateWindow(windows[i]);
+ }
+ }
+
+ // This closes all windows of the app.
+ closeAllWindows() {
+ const windows = this.getInterestingWindows();
+ const time = global.get_current_time();
+ windows.forEach(w => w.delete(time));
+ }
+
+ _cycleThroughWindows(reversed) {
+ // Store for a little amount of time last clicked app and its windows
+ // since the order changes upon window interaction
+ const MEMORY_TIME = 3000;
+
+ const appWindows = this.getInterestingWindows();
+
+ if (appWindows.length < 1)
+ return;
+
+ if (recentlyClickedAppLoopId > 0)
+ GLib.source_remove(recentlyClickedAppLoopId);
+ recentlyClickedAppLoopId = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT, MEMORY_TIME, this._resetRecentlyClickedApp);
+
+ // If there isn't already a list of windows for the current app,
+ // or the stored list is outdated, use the current windows list.
+ const monitorIsolation = Docking.DockManager.settings.isolateMonitors;
+ if (!recentlyClickedApp ||
+ recentlyClickedApp.get_id() !== this.app.get_id() ||
+ recentlyClickedAppWindows.length !== appWindows.length ||
+ (recentlyClickedAppMonitor !== this.monitorIndex && monitorIsolation)) {
+ recentlyClickedApp = this.app;
+ recentlyClickedAppWindows = appWindows;
+ recentlyClickedAppMonitor = this.monitorIndex;
+ recentlyClickedAppIndex = 0;
+ }
+
+ if (reversed) {
+ recentlyClickedAppIndex--;
+ if (recentlyClickedAppIndex < 0)
+ recentlyClickedAppIndex = recentlyClickedAppWindows.length - 1;
+ } else {
+ recentlyClickedAppIndex++;
+ }
+ const index = recentlyClickedAppIndex % recentlyClickedAppWindows.length;
+ const window = recentlyClickedAppWindows[index];
+
+ Main.activateWindow(window);
+ }
+
+ _resetRecentlyClickedApp() {
+ if (recentlyClickedAppLoopId > 0)
+ GLib.source_remove(recentlyClickedAppLoopId);
+ recentlyClickedAppLoopId = 0;
+ recentlyClickedApp = null;
+ recentlyClickedAppWindows = null;
+ recentlyClickedAppIndex = 0;
+ recentlyClickedAppMonitor = -1;
+
+ return false;
+ }
+
+ getWindows() {
+ return this.app.get_windows();
+ }
+
+ // Filter out unnecessary windows, for instance
+ // nautilus desktop window.
+ getInterestingWindows() {
+ const interestingWindows = getInterestingWindows(this.getWindows(),
+ this.monitorIndex);
+
+ if (!this._urgentWindows.size)
+ return interestingWindows;
+
+ return [...new Set([...this._urgentWindows, ...interestingWindows])];
+ }
+
+ getSnapName() {
+ return this.app.appInfo?.get_string('X-SnapInstanceName');
+ }
+});
+
+const DockAppIcon = GObject.registerClass({
+}, class DockAppIcon extends DockAbstractAppIcon {
+ _init(app, monitorIndex, iconAnimator) {
+ super._init(app, monitorIndex, iconAnimator);
+
+ this._signalsHandler.add(tracker, 'notify::focus-app', () => this._updateFocusState());
+ }
+});
+
+const DockLocationAppIcon = GObject.registerClass({
+}, class DockLocationAppIcon extends DockAbstractAppIcon {
+ _init(app, monitorIndex, iconAnimator) {
+ if (!(app.appInfo instanceof Locations.LocationAppInfo))
+ throw new Error('Provided application %s is not a Location'.format(app));
+
+ super._init(app, monitorIndex, iconAnimator);
+
+ if (Docking.DockManager.settings.isolateLocations) {
+ this._signalsHandler.add(tracker, 'notify::focus-app', () => this._updateFocusState());
+ } else {
+ this._signalsHandler.add(global.display, 'notify::focus-window',
+ () => this._updateFocusState());
+ }
+
+ this._signalsHandler.add(this.app, 'notify::icon', () => this.icon.update());
+ }
+
+ get location() {
+ return this.app.location;
+ }
+
+ _updateFocusState() {
+ if (Docking.DockManager.settings.isolateLocations) {
+ super._updateFocusState();
+ return;
+ }
+
+ this.focused = this.app.isFocused && this.running;
+ }
+});
+
+/**
+ * @param app
+ * @param monitorIndex
+ * @param iconAnimator
+ */
+export function makeAppIcon(app, monitorIndex, iconAnimator) {
+ if (app.appInfo instanceof Locations.LocationAppInfo)
+ return new DockLocationAppIcon(app, monitorIndex, iconAnimator);
+
+ return new DockAppIcon(app, monitorIndex, iconAnimator);
+}
+
+/**
+ * DockAppIconMenu
+ *
+ * - set popup arrow side based on dash orientation
+ * - Add close windows option based on quitfromdash extension
+ * (https://github.com/deuill/shell-extension-quitfromdash)
+ * - Add open windows thumbnails instead of list
+ * - update menu when application windows change
+ */
+const DockAppIconMenu = class DockAppIconMenu extends PopupMenu.PopupMenu {
+ constructor(source) {
+ super(source, 0.5, Utils.getPosition());
+
+ this._signalsHandler = new Utils.GlobalSignalsHandler(this);
+
+ // We want to keep the item hovered while the menu is up
+ this.blockSourceEvents = true;
+
+ this.actor.add_style_class_name('app-menu');
+ this.actor.add_style_class_name('dock-app-menu');
+
+ // Chain our visibility and lifecycle to that of the source
+ this._signalsHandler.add(source, 'notify::mapped', () => {
+ if (!source.mapped)
+ this.close();
+ });
+ source.connect('destroy', () => this.destroy());
+
+ Main.uiGroup.add_child(this.actor);
+
+ const {remoteModel} = Docking.DockManager.getDefault();
+ const remoteModelApp = remoteModel?.lookupById(this.sourceActor?.app?.id);
+ if (remoteModelApp && DBusMenu) {
+ const [onQuickList, onDynamicSection] = Utils.splitHandler((sender,
+ {quicklist}, dynamicSection) => {
+ dynamicSection.removeAll();
+ if (quicklist) {
+ quicklist.get_children().forEach(remoteItem =>
+ dynamicSection.addMenuItem(
+ DBusMenuUtils.makePopupMenuItem(remoteItem, false)));
+ }
+ });
+
+ this._signalsHandler.add([
+ remoteModelApp,
+ 'quicklist-changed',
+ onQuickList,
+ ], [
+ this,
+ 'dynamic-section-changed',
+ onDynamicSection,
+ ]);
+ }
+ }
+
+ destroy() {
+ super.destroy();
+ delete this.sourceActor;
+ delete this._signalsHandler;
+ }
+
+ _appendSeparator() {
+ this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+ }
+
+ _appendMenuItem(labelText, params) {
+ const item = new PopupMenu.PopupMenuItem(labelText, params);
+ this.addMenuItem(item);
+ return item;
+ }
+
+ popup(_activatingButton) {
+ this._rebuildMenu();
+ this.open(BoxPointer.PopupAnimation.FULL);
+ }
+
+ removeAll() {
+ super.removeAll();
+
+ delete this._allWindowsMenuItem;
+ delete this._quitMenuItem;
+ }
+
+ _rebuildMenu() {
+ this.removeAll();
+
+ const appItemLabel = this.sourceActor.updating
+ ? _('%s is being updated…').format(this.sourceActor.name)
+ : this.sourceActor.name;
+ this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem(appItemLabel));
+
+ const {app} = this.sourceActor;
+
+ if (Docking.DockManager.settings.showWindowsPreview) {
+ // Display the app windows menu items and the separator between windows
+ // of the current desktop and other windows.
+ const windows = this.sourceActor.getInterestingWindows();
+
+ this._allWindowsMenuItem = new PopupMenu.PopupSubMenuMenuItem(__('All Windows'), false);
+ if (this._allWindowsMenuItem.menu?.actor)
+ this._allWindowsMenuItem.menu.actor.overlayScrollbars = true;
+ this._allWindowsMenuItem.hide();
+ if (windows.length > 0)
+ this.addMenuItem(this._allWindowsMenuItem);
+ } else {
+ const windows = this.sourceActor.getInterestingWindows();
+
+ if (windows.length > 0) {
+ this.addMenuItem(
+ /* Translators: This is the heading of a list of open windows */
+ new PopupMenu.PopupSeparatorMenuItem(_('Open Windows')));
+ }
+
+ windows.forEach(window => {
+ const title = window.title ? window.title : app.get_name();
+ const item = this._appendMenuItem(title);
+ item.connect('activate', () => {
+ this.emit('activate-window', window);
+ });
+ });
+ }
+
+ if (!app.is_window_backed()) {
+ this._appendSeparator();
+
+ const appInfo = app.get_app_info();
+ const actions = this.sourceActor.updating ? [] : appInfo.list_actions();
+ if (!this.sourceActor.updating &&
+ app.can_open_new_window() &&
+ actions.indexOf('new-window') === -1) {
+ const newMenuItem = this._appendMenuItem(_('New Window'));
+ newMenuItem.connect('activate', () => {
+ if (app.state === Shell.AppState.STOPPED)
+ this.sourceActor.animateLaunch();
+
+ app.open_new_window(-1);
+ this.emit('activate-window', null);
+ });
+ this._appendSeparator();
+ }
+
+ if (!this.sourceActor.updating &&
+ Docking.DockManager.getDefault().discreteGpuAvailable &&
+ app.state === Shell.AppState.STOPPED) {
+ const appPrefersNonDefaultGPU = appInfo.get_boolean('PrefersNonDefaultGPU');
+ const gpuPref = appPrefersNonDefaultGPU
+ ? Shell.AppLaunchGpu.DEFAULT
+ : Shell.AppLaunchGpu.DISCRETE;
+ const gpuMenuItem = this._appendMenuItem(appPrefersNonDefaultGPU
+ ? _('Launch using Integrated Graphics Card')
+ : _('Launch using Discrete Graphics Card'));
+ gpuMenuItem.connect('activate', () => {
+ this.sourceActor.animateLaunch();
+ app.launch(0, -1, gpuPref);
+ this.emit('activate-window', null);
+ });
+ }
+
+ for (let i = 0; i < actions.length; i++) {
+ const action = actions[i];
+ const item = this._appendMenuItem(appInfo.get_action_name(action));
+ item.sensitive = !appInfo.busy;
+ item.connect('activate', (emitter, event) => {
+ app.launch_action(action, event.get_time(), -1);
+ this.emit('activate-window', null);
+ });
+ }
+
+ const canFavorite = global.settings.is_writable('favorite-apps') &&
+ (this.sourceActor instanceof DockAppIcon) &&
+ ParentalControlsManager.getDefault().shouldShowApp(app.appInfo);
+
+ if (canFavorite) {
+ this._appendSeparator();
+
+ const isFavorite = AppFavorites.getAppFavorites().isFavorite(app.get_id());
+ if (isFavorite) {
+ const item = this._appendMenuItem(_('Unpin'));
+ item.connect('activate', () => {
+ const favs = AppFavorites.getAppFavorites();
+ favs.removeFavorite(app.get_id());
+ });
+ } else {
+ const item = this._appendMenuItem(__('Pin to Dock'));
+ item.connect('activate', () => {
+ const favs = AppFavorites.getAppFavorites();
+ favs.addFavorite(app.get_id());
+ });
+ }
+ }
+
+ if (Shell.AppSystem.get_default().lookup_app('org.gnome.Software.desktop') &&
+ this.sourceActor instanceof DockAppIcon &&
+ !this.sourceActor.getSnapName()) {
+ this._appendSeparator();
+ const item = this._appendMenuItem(_('App Details'));
+ item.connect('activate', () => {
+ const id = app.get_id();
+ const args = GLib.Variant.new('(ss)', [id, '']);
+ Gio.DBus.get(Gio.BusType.SESSION, null,
+ (o, res) => {
+ const bus = Gio.DBus.get_finish(res);
+ bus.call('org.gnome.Software',
+ '/org/gnome/Software',
+ 'org.gtk.Actions', 'Activate',
+ GLib.Variant.new('(sava{sv})',
+ ['details', [args], null]),
+ null, 0, -1, null, null);
+ Main.overview.hide();
+ });
+ });
+ }
+
+ if (this.sourceActor instanceof DockAppIcon) {
+ const snapName = this.sourceActor.getSnapName();
+ const snapStore = snapName
+ ? Shell.AppSystem.get_default().lookup_app(
+ 'snap-store_snap-store.desktop') : null;
+
+ if (snapStore) {
+ this._appendSeparator();
+ const item = this._appendMenuItem(_('App Details'));
+ item.connect('activate', (_, event) => {
+ snapStore.activate_full(-1, event.get_time());
+ Util.spawnApp(
+ [...snapStore.appInfo.get_commandline().split(' '), snapName]);
+ Main.overview.hide();
+ });
+ }
+ }
+ }
+
+ // dynamic menu
+ const items = this._getMenuItems();
+ let i = items.length;
+ if (Shell.AppSystem.get_default().lookup_app('org.gnome.Software.desktop'))
+ i -= 2;
+
+ if (global.settings.is_writable('favorite-apps'))
+ i -= 2;
+
+ if (i < 0)
+ i = 0;
+
+ const dynamicSection = new PopupMenu.PopupMenuSection();
+ this.addMenuItem(dynamicSection, i);
+ this.emit('dynamic-section-changed', dynamicSection);
+
+ // quit menu
+ this._appendSeparator();
+ this._quitMenuItem = this._appendMenuItem(_('Quit'));
+ this._quitMenuItem.connect('activate', () => this.sourceActor.closeAllWindows());
+
+ this.update();
+ }
+
+ // update menu content when application windows change. This is desirable as actions
+ // acting on windows (closing) are performed while the menu is shown.
+ update() {
+ // update, show or hide the quit menu
+ if (this.sourceActor.windowsCount > 0) {
+ if (this.sourceActor.windowsCount === 1) {
+ this._quitMenuItem.label.set_text(_('Quit'));
+ } else {
+ this._quitMenuItem.label.set_text(ngettext(
+ 'Quit %d Window', 'Quit %d Windows', this.sourceActor.windowsCount).format(
+ this.sourceActor.windowsCount));
+ }
+
+ this._quitMenuItem.actor.show();
+ } else {
+ this._quitMenuItem.actor.hide();
+ }
+
+ if (Docking.DockManager.settings.showWindowsPreview) {
+ const windows = this.sourceActor.getInterestingWindows();
+
+ // update, show, or hide the allWindows menu
+ // Check if there are new windows not already displayed. In such case,
+ // repopulate the allWindows menu. Windows removal is already handled
+ // by each preview being connected to the destroy signal
+ const oldWindows = this._allWindowsMenuItem.menu._getMenuItems().map(item => {
+ return item._window;
+ });
+
+ const newWindows = windows.filter(w =>
+ oldWindows.indexOf(w) < 0);
+ if (newWindows.length > 0) {
+ this._populateAllWindowMenu(windows);
+
+ // Try to set the width to that of the submenu.
+ // TODO: can't get the actual size, getting a bit less.
+ // Temporary workaround: add 15px to compensate
+ this._allWindowsMenuItem.width = this._allWindowsMenuItem.menu.actor.width + 15;
+ }
+
+ // The menu is created hidden and never hidden after being shown.
+ // Instead, a signal connected to its items destroy will set is
+ // insensitive if no more windows preview are shown.
+ if (windows.length > 0) {
+ this._allWindowsMenuItem.show();
+ this._allWindowsMenuItem.setSensitive(true);
+
+ if (Docking.DockManager.settings.defaultWindowsPreviewToOpen)
+ this._allWindowsMenuItem.menu.open();
+ }
+ }
+
+ // Update separators
+ this._getMenuItems().forEach(item => {
+ if ('label' in item)
+ this._updateSeparatorVisibility(item);
+ });
+ }
+
+ _populateAllWindowMenu(windows) {
+ this._allWindowsMenuItem.menu.removeAll();
+
+ if (windows.length > 0) {
+ const activeWorkspace = global.workspace_manager.get_active_workspace();
+ let separatorShown = windows[0].get_workspace() !== activeWorkspace;
+
+ for (let i = 0; i < windows.length; i++) {
+ const window = windows[i];
+ if (!separatorShown && window.get_workspace() !== activeWorkspace) {
+ this._allWindowsMenuItem.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+ separatorShown = true;
+ }
+
+ const item = new WindowPreview.WindowPreviewMenuItem(window,
+ St.Side.LEFT);
+ this._allWindowsMenuItem.menu.addMenuItem(item);
+ item.connect('activate', () => {
+ this.emit('activate-window', window);
+ });
+
+ // This is to achieve a more graceful transition when the last
+ // window is closed.
+ item.connect('destroy', () => {
+ // It's still counting the item just going to be destroyed
+ if (this._allWindowsMenuItem.menu._getMenuItems().length === 1)
+ this._allWindowsMenuItem.setSensitive(false);
+ });
+ }
+ }
+ }
+};
+
+/**
+ * @param w
+ */
+function isWindowUrgent(w) {
+ return w.urgent || w.demandsAttention || w._manualUrgency;
+}
+
+/**
+ * Filter out unnecessary windows, for instance
+ * nautilus desktop window.
+ *
+ * @param windows
+ * @param monitorIndex
+ */
+export function getInterestingWindows(windows, monitorIndex) {
+ const {settings} = Docking.DockManager;
+
+ // When using workspace isolation, we filter out windows
+ // that are neither in the current workspace nor marked urgent
+ if (settings.isolateWorkspaces) {
+ const showUrgent = settings.workspaceAgnosticUrgentWindows;
+ const activeWorkspace = global.workspace_manager.get_active_workspace();
+ windows = windows.filter(w => {
+ const inWorkspace = w.get_workspace() === activeWorkspace;
+ return inWorkspace || (showUrgent && isWindowUrgent(w));
+ });
+ }
+
+ if (settings.isolateMonitors && monitorIndex >= 0) {
+ windows = windows.filter(w => {
+ return w.get_monitor() === monitorIndex;
+ });
+ }
+
+ return windows.filter(w => !w.skipTaskbar);
+}
+
+/**
+ * A ShowAppsIcon improved class.
+ *
+ * - set label position based on dash orientation
+ * Note: we are am reusing most machinery of the appIcon class.
+ * - implement a popupMenu based on the AppIcon code
+ * Note: we are reusing most machinery of the appIcon class)
+ *
+ */
+
+export const DockShowAppsIcon = GObject.registerClass({
+ Signals: {
+ 'menu-state-changed': {param_types: [GObject.TYPE_BOOLEAN]},
+ 'sync-tooltip': {},
+ },
+}
+, class DockShowAppsIcon extends Dash.ShowAppsIcon {
+ _init(position) {
+ super._init();
+
+ // Re-use appIcon methods
+ const {prototype: appIconPrototype} = AppDisplay.AppIcon;
+ this.toggleButton.y_expand = false;
+ this.toggleButton.connect('popup-menu', () =>
+ appIconPrototype._onKeyboardPopupMenu.call(this));
+ this.toggleButton.connect('clicked', () =>
+ this._removeMenuTimeout());
+
+ this.reactive = true;
+ this.toggleButton.popupMenu = (...args) =>
+ this.popupMenu(...args);
+ this.toggleButton._setPopupTimeout = (...args) =>
+ this._setPopupTimeout(...args);
+ this.toggleButton._removeMenuTimeout = (...args) =>
+ this._removeMenuTimeout(...args);
+
+ this.label?.add_style_class_name(Theming.PositionStyleClass[position]);
+ if (Docking.DockManager.settings.customThemeShrink)
+ this.label?.add_style_class_name('shrink');
+
+ this._menu = null;
+ this._menuManager = new PopupMenu.PopupMenuManager(this);
+ this._menuTimeoutId = 0;
+
+ this._maybeEnablePopupGestures();
+ }
+
+ _createIcon(size) {
+ this._iconActor = super._createIcon(size);
+ this._iconActor.fallbackIconName = this._iconActor.iconName;
+ this._iconActor.fallbackGicon = this._iconActor.gicon;
+ this._iconActor.iconName = `view-app-grid-${Main.sessionMode.currentMode}-symbolic`;
+ return this._iconActor;
+ }
+
+ vfunc_leave_event(...args) {
+ return AppDisplay.AppIcon.prototype.vfunc_leave_event.call(
+ this.toggleButton, ...args);
+ }
+
+ vfunc_button_press_event(...args) {
+ try {
+ // TODO: Drop this when not supporting GNOME 48 (and older) anymore.
+ return AppDisplay.AppIcon.prototype.vfunc_button_press_event.call(
+ this.toggleButton, ...args);
+ } catch {
+ return Clutter.EVENT_PROPAGATE;
+ }
+ }
+
+ vfunc_touch_event(...args) {
+ try {
+ // TODO: Drop this when not supporting GNOME 48 (and older) anymore.
+ return AppDisplay.AppIcon.prototype.vfunc_touch_event.call(
+ this.toggleButton, ...args);
+ } catch {
+ return Clutter.EVENT_PROPAGATE;
+ }
+ }
+
+ showLabel(...args) {
+ itemShowLabel.call(this, ...args);
+ }
+
+ setForcedHighlight(...args) {
+ AppDisplay.AppIcon.prototype.setForcedHighlight.call(this, ...args);
+ }
+
+ _onMenuPoppedDown(...args) {
+ AppDisplay.AppIcon.prototype._onMenuPoppedDown.call(this, ...args);
+ }
+
+ _setPopupTimeout(...args) {
+ AppDisplay.AppIcon.prototype._setPopupTimeout.call(this, ...args);
+ }
+
+ _removeMenuTimeout(...args) {
+ AppDisplay.AppIcon.prototype._removeMenuTimeout?.call(this, ...args);
+ }
+
+ _hasPopupMenu() {
+ return Docking.DockManager.extension.uuid !== 'ubuntu-dock@ubuntu.com';
+ }
+
+ _maybeEnablePopupGestures() {
+ if (!this._hasPopupMenu())
+ return;
+
+ if (!Clutter.LongPressGesture || !Clutter.ClickGesture)
+ return;
+
+ const longPressGesture = new Clutter.LongPressGesture();
+ longPressGesture.connect('recognize', () => this.popupMenu());
+ this.add_action(longPressGesture);
+
+ const rightClickGesture = new Clutter.ClickGesture({
+ required_button: Clutter.BUTTON_SECONDARY,
+ recognize_on_press: true,
+ });
+ rightClickGesture.connect('recognize', () => this.popupMenu());
+ this.add_action(rightClickGesture);
+ }
+
+ popupMenu() {
+ if (!this._hasPopupMenu())
+ return false;
+
+ this._removeMenuTimeout();
+ this.toggleButton.fake_release();
+
+ if (!this._menu) {
+ this._menu = new DockShowAppsIconMenu(this);
+ this._menu.connect('open-state-changed', (menu, isPoppedUp) => {
+ if (!isPoppedUp)
+ this._onMenuPoppedDown();
+ });
+ const id = Main.overview.connect('hiding', () => {
+ this._menu.close();
+ });
+ this._menu.actor.connect('destroy', () => {
+ Main.overview.disconnect(id);
+ });
+ this._menuManager.addMenu(this._menu);
+ }
+
+ this.emit('menu-state-changed', true);
+
+ this.toggleButton.set_hover(true);
+ this._menu.popup();
+ // Removed in GNOME 50.
+ this._menuManager.ignoreRelease?.();
+ this.emit('sync-tooltip');
+
+ return false;
+ }
+});
+
+
+/**
+ * A menu for the showAppsIcon
+ */
+class DockShowAppsIconMenu extends DockAppIconMenu {
+ _rebuildMenu() {
+ this.removeAll();
+
+ this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem(__('Dash to Dock')));
+
+ const item = this._appendMenuItem(_('Settings'));
+ item.connect('activate', () =>
+ Docking.DockManager.extension.openPreferences());
+ }
+}
+
+/**
+ * This function is used for both DockShowAppsIcon and DockDashItemContainer
+ */
+export function itemShowLabel() {
+ /* eslint-disable no-invalid-this */
+ // Check if the label is still present at all. When switching workspace, the
+ // item might have been destroyed in between.
+ if (!this._labelText || !this.label.get_stage())
+ return;
+
+ this.label.set_text(this._labelText);
+ this.label.opacity = 0;
+ this.label.show();
+
+ const [stageX, stageY] = this.get_transformed_position();
+ const node = this.label.get_theme_node();
+
+ const itemWidth = this.allocation.x2 - this.allocation.x1;
+ const itemHeight = this.allocation.y2 - this.allocation.y1;
+
+ const labelWidth = this.label.get_width();
+ const labelHeight = this.label.get_height();
+
+ let x, y, xOffset, yOffset;
+
+ const position = Utils.getPosition();
+ const labelOffset = node.get_length('-x-offset');
+
+ switch (position) {
+ case St.Side.LEFT:
+ yOffset = Math.floor((itemHeight - labelHeight) / 2);
+ y = stageY + yOffset;
+ xOffset = labelOffset;
+ x = stageX + this.get_width() + xOffset;
+ break;
+ case St.Side.RIGHT:
+ yOffset = Math.floor((itemHeight - labelHeight) / 2);
+ y = stageY + yOffset;
+ xOffset = labelOffset;
+ x = Math.round(stageX) - labelWidth - xOffset;
+ break;
+ case St.Side.TOP:
+ y = stageY + labelOffset + itemHeight;
+ xOffset = Math.floor((itemWidth - labelWidth) / 2);
+ x = stageX + xOffset;
+ break;
+ case St.Side.BOTTOM:
+ yOffset = labelOffset;
+ y = stageY - labelHeight - yOffset;
+ xOffset = Math.floor((itemWidth - labelWidth) / 2);
+ x = stageX + xOffset;
+ break;
+ }
+
+ // keep the label inside the screen border
+ // Only needed fot the x coordinate.
+
+ // Leave a few pixel gap
+ const gap = 5;
+ const monitor = Main.layoutManager.findMonitorForActor(this);
+ if (x - monitor.x < gap)
+ x += monitor.x - x + labelOffset;
+ else if (x + labelWidth > monitor.x + monitor.width - gap)
+ x -= x + labelWidth - (monitor.x + monitor.width) + gap;
+
+ this.label.remove_all_transitions();
+ this.label.set_position(x, y);
+ this.label.ease({
+ opacity: 255,
+ duration: DASH_ITEM_LABEL_SHOW_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ /* eslint-enable no-invalid-this */
+}
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appIconsDecorator.js b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appIconsDecorator.js
new file mode 100644
index 0000000..89afa3e
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appIconsDecorator.js
@@ -0,0 +1,168 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+
+import {
+ Docking,
+ AppIconIndicators,
+ Utils,
+} from './imports.js';
+
+import {
+ Gio,
+} from './dependencies/gi.js';
+
+import {
+ AppMenu,
+ AppDisplay,
+ Main,
+ PopupMenu,
+} from './dependencies/shell/ui.js';
+
+const Labels = Object.freeze({
+ GENERIC: Symbol('generic'),
+ ICONS: Symbol('icons'),
+});
+
+export class AppIconsDecorator {
+ constructor() {
+ this._signals = new Utils.GlobalSignalsHandler();
+ this._methodInjections = new Utils.InjectionsHandler();
+ this._propertyInjections = new Utils.PropertyInjectionsHandler(
+ null, {allowNewProperty: true});
+ this._indicators = new Set();
+
+ this._patchAppIcons();
+ this._decorateIcons();
+ }
+
+ destroy() {
+ this._signals?.destroy();
+ delete this._signals;
+ this._methodInjections?.destroy();
+ delete this._methodInjections;
+ this._propertyInjections?.destroy();
+ delete this._propertyInjections;
+ this._indicators?.forEach(i => i.destroy());
+ this._indicators?.clear();
+ delete this._indicators;
+ }
+
+ _decorateIcon(parentIcon, signalLabel = Labels.GENERIC) {
+ const indicator = new AppIconIndicators.UnityIndicator(parentIcon);
+ this._indicators.add(indicator);
+ this._signals.addWithLabel(signalLabel, parentIcon, 'destroy', () => {
+ this._indicators.delete(indicator);
+ indicator.destroy();
+ });
+ return indicator;
+ }
+
+ _decorateIcons() {
+ const {appDisplay} = Docking.DockManager.getDefault().overviewControls;
+
+ const decorateAppIcons = () => {
+ this._indicators.forEach(i => i.destroy());
+ this._indicators.clear();
+ this._signals.removeWithLabel(Labels.ICONS);
+
+ const decorateViewIcons = view => {
+ const items = view.getAllItems();
+ items.forEach(i => {
+ if (i instanceof AppDisplay.AppIcon) {
+ this._decorateIcon(i, Labels.ICONS);
+ } else if (i instanceof AppDisplay.FolderIcon) {
+ decorateViewIcons(i.view);
+ this._signals.addWithLabel(Labels.ICONS, i.view,
+ 'view-loaded', () => decorateAppIcons());
+ }
+ });
+ };
+ decorateViewIcons(appDisplay);
+ };
+
+ this._signals.add(appDisplay, 'view-loaded', () => decorateAppIcons());
+ decorateAppIcons();
+ }
+
+ _patchAppIcons() {
+ const self = this;
+
+ this._methodInjections.add(AppDisplay.AppSearchProvider.prototype,
+ 'createResultObject', function (originalFunction, ...args) {
+ /* eslint-disable no-invalid-this */
+ const result = originalFunction.call(this, ...args);
+ if (result instanceof AppDisplay.AppIcon)
+ self._decorateIcon(result);
+ return result;
+ /* eslint-enable no-invalid-this */
+ });
+
+ this._methodInjections.add(AppDisplay.AppIcon.prototype,
+ 'activate', function (originalFunction, ...args) {
+ /* eslint-disable no-invalid-this */
+ if (this.updating) {
+ const icon = Gio.Icon.new_for_string('action-unavailable-symbolic');
+ Main.osdWindowManager.show(-1, icon,
+ _('%s is updating, try again later').format(this.name),
+ null);
+ return;
+ }
+
+ originalFunction.call(this, ...args);
+ /* eslint-enable no-invalid-this */
+ });
+
+ const appIconsTypes = [
+ AppDisplay.AppSearchProvider,
+ AppDisplay.AppIcon,
+ ];
+ appIconsTypes.forEach(type =>
+ this._propertyInjections.add(type.prototype, 'updating', {
+ get() {
+ return !!this.__d2dUpdating;
+ },
+ set(updating) {
+ if (this.updating === updating)
+ return;
+ this.__d2dUpdating = updating;
+ if (updating)
+ this.add_style_class_name('updating');
+ else
+ this.remove_style_class_name('updating');
+ },
+ }));
+
+ this._methodInjections.add(AppMenu.AppMenu.prototype,
+ 'open', function (originalFunction, ...args) {
+ /* eslint-disable no-invalid-this */
+ if (!this.sourceActor.updating) {
+ originalFunction.call(this, ...args);
+ return;
+ }
+
+ if (this.isOpen)
+ return;
+
+ if (this.isEmpty())
+ return;
+
+ // Temporarily hide all the menu items a part the Pinning and
+ // the details one while we're updating.
+ const validItems = [
+ this._toggleFavoriteItem,
+ this._detailsItem,
+ ];
+ const items = this._getMenuItems().filter(
+ i => !validItems.includes(i)).map(i =>
+ i instanceof PopupMenu.PopupMenuBase ? i.actor : i);
+ const itemsVisibility = items.map(i => i.visible);
+ items.forEach(i => (i.visible = false));
+ const menuClosedId = this.connect('menu-closed', () => {
+ this.disconnect(menuClosedId);
+ items.forEach((i, idx) => (i.visible = itemsVisibility[idx]));
+ });
+ originalFunction.call(this, ...args);
+ /* eslint-enable no-invalid-this */
+ });
+ }
+}
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appSpread.js b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appSpread.js
new file mode 100644
index 0000000..790ef64
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appSpread.js
@@ -0,0 +1,235 @@
+import {Atk, Clutter} from './dependencies/gi.js';
+
+import {
+ Main,
+ SearchController,
+ Workspace,
+ WorkspaceThumbnail,
+} from './dependencies/shell/ui.js';
+
+import {Utils} from './imports.js';
+
+const APP_SPREAD_RESTORE_ACTION = 'dock-app-spread-restore';
+
+export class AppSpread {
+ constructor() {
+ this.app = null;
+ this.supported = true;
+ this.windows = [];
+
+ // fail early and do nothing, if mandatory gnome shell functions are missing
+ if (Main.overview.isDummy ||
+ !Workspace?.Workspace?.prototype._isOverviewWindow ||
+ !WorkspaceThumbnail?.WorkspaceThumbnail?.prototype._isOverviewWindow) {
+ log('Dash to dock: Unable to temporarily replace shell functions ' +
+ 'for app spread - using previews instead');
+ this.supported = false;
+ return;
+ }
+
+ this._signalHandlers = new Utils.GlobalSignalsHandler();
+ this._methodInjections = new Utils.InjectionsHandler();
+ this._vfuncInjections = new Utils.VFuncInjectionsHandler();
+ }
+
+ get isInAppSpread() {
+ return !!this.app;
+ }
+
+ destroy() {
+ if (!this.supported)
+ return;
+ this._hideAppSpread();
+ this._signalHandlers.destroy();
+ this._methodInjections.destroy();
+ this._vfuncInjections.destroy();
+ }
+
+ toggle(app) {
+ const newApp = this.app !== app;
+ if (this.app)
+ Main.overview.hide(); // also triggers hook 'hidden'
+
+ if (app && newApp)
+ this._showAppSpread(app);
+ }
+
+ _updateWindows() {
+ this.windows = this.app.get_windows();
+ }
+
+ _restoreDefaultWindows() {
+ const {workspaceManager} = global;
+
+ for (let i = 0; i < workspaceManager.nWorkspaces; i++) {
+ const metaWorkspace = workspaceManager.get_workspace_by_index(i);
+ metaWorkspace.list_windows().forEach(w => metaWorkspace.emit('window-added', w));
+ }
+ }
+
+ _filterWindows() {
+ const {workspaceManager} = global;
+
+ for (let i = 0; i < workspaceManager.nWorkspaces; i++) {
+ const metaWorkspace = workspaceManager.get_workspace_by_index(i);
+ metaWorkspace.list_windows().filter(w => !this.windows.includes(w)).forEach(
+ w => metaWorkspace.emit('window-removed', w));
+ }
+ }
+
+ _restoreDefaultOverview() {
+ this._hideAppSpread();
+ this._restoreDefaultWindows();
+ }
+
+ _showAppSpread(app) {
+ if (this.isInAppSpread)
+ return;
+
+ // Checked in overview "hide" event handler _hideAppSpread
+ this.app = app;
+ this._updateWindows();
+
+ // we need to hook into overview 'hidden' like this, in case app spread
+ // overview is hidden by choosing another app it should then do its
+ // cleanup too
+ this._signalHandlers.add(Main.overview, 'hidden', () => this._hideAppSpread());
+
+ const appSpread = this;
+ this._methodInjections.add([
+ // Filter workspaces to only show current app windows
+ Workspace.Workspace.prototype, '_isOverviewWindow',
+ function (originalMethod, window) {
+ /* eslint-disable no-invalid-this */
+ const isOverviewWindow = originalMethod.call(this, window);
+ return isOverviewWindow && appSpread.windows.includes(window);
+ /* eslint-enable no-invalid-this */
+ },
+ ],
+ [
+ // Filter thumbnails to only show current app windows
+ WorkspaceThumbnail.WorkspaceThumbnail.prototype, '_isOverviewWindow',
+ function (originalMethod, windowActor) {
+ /* eslint-disable no-invalid-this */
+ const isOverviewWindow = originalMethod.call(this, windowActor);
+ return isOverviewWindow && appSpread.windows.includes(windowActor.metaWindow);
+ /* eslint-enable no-invalid-this */
+ },
+ ]);
+
+ const activitiesButton = Main.panel.statusArea?.activities;
+
+ if (activitiesButton) {
+ this._signalHandlers.add(Main.overview, 'showing', () => {
+ activitiesButton.remove_style_pseudo_class('overview');
+ activitiesButton.remove_accessible_state(Atk.StateType.CHECKED);
+ });
+
+ let hasEventVFunc = false;
+ try {
+ hasEventVFunc = !!activitiesButton.constructor.prototype.vfunc_event;
+ } catch {}
+
+ if (hasEventVFunc) {
+ this._vfuncInjections.add([
+ activitiesButton.constructor.prototype,
+ 'event',
+ function (event) {
+ if (event.type() === Clutter.EventType.TOUCH_END ||
+ event.type() === Clutter.EventType.BUTTON_RELEASE) {
+ if (Main.overview.shouldToggleByCornerOrButton())
+ appSpread._restoreDefaultOverview();
+ }
+ return Clutter.EVENT_PROPAGATE;
+ },
+ ]);
+ } else {
+ /* Shell >= 50 uses gestures on the activities button */
+ const click = new Clutter.ClickGesture();
+ click.set_recognize_on_press(true);
+ click.set_enabled(true);
+ click.connect('recognize', () => {
+ if (Main.overview.shouldToggleByCornerOrButton())
+ appSpread._restoreDefaultOverview();
+ });
+ activitiesButton.add_action_with_name(APP_SPREAD_RESTORE_ACTION, click);
+ }
+
+ this._vfuncInjections.add([
+ activitiesButton.constructor.prototype,
+ 'key_release_event',
+ function (keyEvent) {
+ const keyval = keyEvent.get_key_symbol?.() ?? keyEvent.keyval;
+ if (keyval === Clutter.KEY_Return || keyval === Clutter.KEY_space) {
+ if (Main.overview.shouldToggleByCornerOrButton())
+ appSpread._restoreDefaultOverview();
+ }
+ return Clutter.EVENT_PROPAGATE;
+ },
+ ]);
+ }
+
+ this._signalHandlers.add(Main.overview.dash.showAppsButton, 'notify::checked', () => {
+ if (Main.overview.dash.showAppsButton.checked)
+ this._restoreDefaultOverview();
+ });
+
+ // If closing windows in AppSpread, and only one window left:
+ // exit app spread and focus remaining window (handled in _hideAppSpread)
+ this._signalHandlers.add(this.app, 'windows-changed', () => {
+ this._updateWindows();
+
+ if (this.windows.length <= 1)
+ Main.overview.hide();
+ });
+
+ this._disableSearch();
+
+ Main.overview.show();
+ }
+
+ _hideAppSpread() {
+ if (!this.isInAppSpread)
+ return;
+
+ if (Main.overview.visible) {
+ Main.panel.statusArea?.activities.add_style_pseudo_class('overview');
+ Main.panel.statusArea?.activities.add_accessible_state(Atk.StateType.CHECKED);
+ }
+
+ // Restore original behaviour
+ this.app = null;
+ this._enableSearch();
+ this._methodInjections.clear();
+ this._signalHandlers.clear();
+ this._vfuncInjections.clear();
+ Main.panel.statusArea?.activities.remove_action_by_name(APP_SPREAD_RESTORE_ACTION);
+
+ // Check reason for leaving AppSpread was closing app windows and only one window left...
+ if (this.windows.length === 1)
+ Main.activateWindow(this.windows[0]);
+
+ this.windows = [];
+ }
+
+ _disableSearch() {
+ if (Main.overview.searchEntry) {
+ Main.overview.searchEntry.opacity = 0;
+ Main.overview.searchEntry.reactive = false;
+ }
+
+ if (!SearchController.SearchController.prototype._shouldTriggerSearch)
+ return;
+
+ this._methodInjections.add(
+ SearchController.SearchController.prototype,
+ '_shouldTriggerSearch', () => false);
+ }
+
+ _enableSearch() {
+ if (Main.overview.searchEntry) {
+ Main.overview.searchEntry.opacity = 255;
+ Main.overview.searchEntry.reactive = true;
+ }
+ }
+}
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dash.js b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dash.js
new file mode 100644
index 0000000..956bbeb
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dash.js
@@ -0,0 +1,1177 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+import {
+ Clutter,
+ Gio,
+ GLib,
+ GObject,
+ Shell,
+ St,
+} from './dependencies/gi.js';
+
+import {
+ AppFavorites,
+ Dash,
+ DND,
+ Main,
+} from './dependencies/shell/ui.js';
+
+import {
+ Util,
+} from './dependencies/shell/misc.js';
+
+import {
+ AppIcons,
+ Docking,
+ Theming,
+ Utils,
+} from './imports.js';
+
+// module "Dash" did not export DASH_ANIMATION_TIME in old versions
+// so we just define it like it is defined in Dash;
+// taken from https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/dash.js
+const DASH_ANIMATION_TIME = Dash.DASH_ANIMATION_TIME ?? 200;
+const DASH_VISIBILITY_TIMEOUT = 3;
+
+const Labels = Object.freeze({
+ SHOW_MOUNTS: Symbol('show-mounts'),
+ FIRST_LAST_CHILD_WORKAROUND: Symbol('first-last-child-workaround'),
+});
+
+/**
+ * Extend DashItemContainer
+ *
+ * - set label position based on dash orientation
+ *
+ */
+const DockDashItemContainer = GObject.registerClass(
+class DockDashItemContainer extends Dash.DashItemContainer {
+ _init(position) {
+ super._init();
+
+ this.label?.add_style_class_name(Theming.PositionStyleClass[position]);
+ if (Docking.DockManager.settings.customThemeShrink)
+ this.label?.add_style_class_name('shrink');
+ }
+
+ showLabel() {
+ return AppIcons.itemShowLabel.call(this);
+ }
+
+ // we override the method show taken from:
+ // https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/dash.js
+ // in order to apply a little modification at the end of the animation
+ // which makes sure that the icon background is not blurry
+ show(animate) {
+ if (this.child == null)
+ return;
+
+ this.ease({
+ scale_x: 1,
+ scale_y: 1,
+ opacity: 255,
+ duration: animate ? DASH_ANIMATION_TIME : 0,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ // when the animation is ended, we simulate
+ // a hover to gain back focus and unblur the
+ // background
+ this.set_hover(true);
+ },
+ });
+ }
+});
+
+const DockDashIconsVerticalLayout = GObject.registerClass(
+ class DockDashIconsVerticalLayout extends Clutter.BoxLayout {
+ _init() {
+ super._init({
+ orientation: Clutter.Orientation.VERTICAL,
+ });
+ }
+
+ vfunc_get_preferred_height(container, forWidth) {
+ const [natHeight] = super.vfunc_get_preferred_height(container, forWidth);
+ return [natHeight, 0];
+ }
+ });
+
+
+const baseIconSizes = [16, 22, 24, 32, 48, 64, 96, 128];
+
+/**
+ * This class is a fork of the upstream dash class (ui.dash.js)
+ *
+ * Summary of changes:
+ * - disconnect global signals adding a destroy method;
+ * - play animations even when not in overview mode
+ * - set a maximum icon size
+ * - show running and/or favorite applications
+ * - hide showApps label when the custom menu is shown.
+ * - add scrollview
+ * ensure actor is visible on keyfocus inseid the scrollview
+ * - add 128px icon size, might be useful for hidpi display
+ * - sync minimization application target position.
+ * - keep running apps ordered.
+ */
+export const DockDash = GObject.registerClass({
+ Properties: {
+ 'requires-visibility': GObject.ParamSpec.boolean(
+ 'requires-visibility', 'requires-visibility', 'requires-visibility',
+ GObject.ParamFlags.READWRITE,
+ false),
+ },
+ Signals: {
+ 'menu-opened': {},
+ 'menu-closed': {},
+ 'icon-size-changed': {},
+ },
+}, class DockDash extends St.Widget {
+ _init(monitorIndex) {
+ // Initialize icon variables and size
+ super._init({
+ name: 'dash',
+ offscreen_redirect: Clutter.OffscreenRedirect.ALWAYS,
+ layout_manager: new Clutter.BinLayout(),
+ });
+
+ this._maxWidth = -1;
+ this._maxHeight = -1;
+ this.iconSize = Docking.DockManager.settings.dashMaxIconSize;
+ this._availableIconSizes = baseIconSizes;
+ this._shownInitially = false;
+ this._initializeIconSize(this.iconSize);
+ this._signalsHandler = new Utils.GlobalSignalsHandler(this);
+
+ this._separator = null;
+
+ this._monitorIndex = monitorIndex;
+ this._position = Utils.getPosition();
+ this._isHorizontal = (this._position === St.Side.TOP) ||
+ (this._position === St.Side.BOTTOM);
+
+ this._dragPlaceholder = null;
+ this._dragPlaceholderPos = -1;
+ this._animatingPlaceholdersCount = 0;
+ this._showLabelTimeoutId = 0;
+ this._resetHoverTimeoutId = 0;
+ this._labelShowing = false;
+
+ this._dashContainer = new St.BoxLayout({
+ name: 'dashtodockDashContainer',
+ x_align: Clutter.ActorAlign.CENTER,
+ y_align: Clutter.ActorAlign.CENTER,
+ vertical: !this._isHorizontal,
+ y_expand: this._isHorizontal,
+ x_expand: !this._isHorizontal,
+ });
+
+ this._scrollView = new St.ScrollView({
+ name: 'dashtodockDashScrollview',
+ hscrollbar_policy: this._isHorizontal ? St.PolicyType.EXTERNAL : St.PolicyType.NEVER,
+ vscrollbar_policy: this._isHorizontal ? St.PolicyType.NEVER : St.PolicyType.EXTERNAL,
+ x_expand: this._isHorizontal,
+ y_expand: !this._isHorizontal,
+ enable_mouse_scrolling: false,
+ });
+
+ this._scrollView.connect('scroll-event', this._onScrollEvent.bind(this));
+
+ this._boxContainer = new St.BoxLayout({
+ name: 'dashtodockBoxContainer',
+ x_align: Clutter.ActorAlign.FILL,
+ y_align: Clutter.ActorAlign.FILL,
+ vertical: !this._isHorizontal,
+ });
+ this._boxContainer.add_style_class_name(Theming.PositionStyleClass[this._position]);
+
+ const rtl = Clutter.get_default_text_direction() === Clutter.TextDirection.RTL;
+ this._box = new St.BoxLayout({
+ vertical: !this._isHorizontal,
+ clip_to_allocation: false,
+ ...!this._isHorizontal ? {layout_manager: new DockDashIconsVerticalLayout()} : {},
+ x_align: rtl ? Clutter.ActorAlign.END : Clutter.ActorAlign.START,
+ y_align: this._isHorizontal ? Clutter.ActorAlign.CENTER : Clutter.ActorAlign.START,
+ y_expand: !this._isHorizontal,
+ x_expand: this._isHorizontal,
+ });
+ this._box._delegate = this;
+ this._boxContainer.add_child(this._box);
+ Utils.addActor(this._scrollView, this._boxContainer);
+ this._dashContainer.add_child(this._scrollView);
+
+ this._showAppsIcon = new AppIcons.DockShowAppsIcon(this._position);
+ this._showAppsIcon.show(false);
+ this._showAppsIcon.icon.setIconSize(this.iconSize);
+ this._showAppsIcon.x_expand = false;
+ this._showAppsIcon.y_expand = false;
+ this.showAppsButton.connect('notify::hover', a => {
+ if (this._showAppsIcon.get_parent() === this._boxContainer)
+ this._ensureItemVisibility(a);
+ });
+ if (!this._isHorizontal)
+ this._showAppsIcon.y_align = Clutter.ActorAlign.START;
+ this._hookUpLabel(this._showAppsIcon);
+ this._showAppsIcon.connect('menu-state-changed', (_icon, opened) => {
+ this._itemMenuStateChanged(this._showAppsIcon, opened);
+ });
+ this.updateShowAppsButton();
+
+ this._background = new St.Widget({
+ style_class: 'dash-background',
+ y_expand: this._isHorizontal,
+ x_expand: !this._isHorizontal,
+ });
+
+ const sizerBox = new Clutter.Actor();
+ sizerBox.add_constraint(new Clutter.BindConstraint({
+ source: this._isHorizontal ? this._showAppsIcon.icon : this._dashContainer,
+ coordinate: Clutter.BindCoordinate.HEIGHT,
+ }));
+ sizerBox.add_constraint(new Clutter.BindConstraint({
+ source: this._isHorizontal ? this._dashContainer : this._showAppsIcon.icon,
+ coordinate: Clutter.BindCoordinate.WIDTH,
+ }));
+ this._background.add_child(sizerBox);
+
+ this.add_child(this._background);
+ this.add_child(this._dashContainer);
+
+ this._workId = Main.initializeDeferredWork(this._box, this._redisplay.bind(this));
+
+ this._shellSettings = new Gio.Settings({
+ schema_id: 'org.gnome.shell',
+ });
+
+ this._appSystem = Shell.AppSystem.get_default();
+
+ this.iconAnimator = new Docking.IconAnimator(this);
+
+ this._signalsHandler.add([
+ this._appSystem,
+ 'installed-changed',
+ () => {
+ AppFavorites.getAppFavorites().reload();
+ this._queueRedisplay();
+ },
+ ], [
+ AppFavorites.getAppFavorites(),
+ 'changed',
+ this._queueRedisplay.bind(this),
+ ], [
+ this._appSystem,
+ 'app-state-changed',
+ this._queueRedisplay.bind(this),
+ ], [
+ Main.overview,
+ 'item-drag-begin',
+ this._onItemDragBegin.bind(this),
+ ], [
+ Main.overview,
+ 'item-drag-end',
+ this._onItemDragEnd.bind(this),
+ ], [
+ Main.overview,
+ 'item-drag-cancelled',
+ this._onItemDragCancelled.bind(this),
+ ], [
+ Main.overview,
+ 'window-drag-begin',
+ this._onWindowDragBegin.bind(this),
+ ], [
+ Main.overview,
+ 'window-drag-cancelled',
+ this._onWindowDragEnd.bind(this),
+ ], [
+ Main.overview,
+ 'window-drag-end',
+ this._onWindowDragEnd.bind(this),
+ ]);
+
+ this.connect('destroy', this._onDestroy.bind(this));
+ }
+
+ vfunc_get_preferred_height(forWidth) {
+ const [minHeight, natHeight] = super.vfunc_get_preferred_height.call(this, forWidth);
+ if (!this._isHorizontal && this._maxHeight !== -1 && natHeight > this._maxHeight)
+ return [minHeight, this._maxHeight];
+ else
+ return [minHeight, natHeight];
+ }
+
+ vfunc_get_preferred_width(forHeight) {
+ const [minWidth, natWidth] = super.vfunc_get_preferred_width.call(this, forHeight);
+ if (this._isHorizontal && this._maxWidth !== -1 && natWidth > this._maxWidth)
+ return [minWidth, this._maxWidth];
+ else
+ return [minWidth, natWidth];
+ }
+
+ get _container() {
+ return this._dashContainer;
+ }
+
+ _onDestroy() {
+ this.iconAnimator.destroy();
+
+ if (this._requiresVisibilityTimeout) {
+ GLib.source_remove(this._requiresVisibilityTimeout);
+ delete this._requiresVisibilityTimeout;
+ }
+
+ if (this._ensureActorVisibilityTimeoutId) {
+ GLib.source_remove(this._ensureActorVisibilityTimeoutId);
+ delete this._ensureActorVisibilityTimeoutId;
+ }
+ }
+
+
+ _onItemDragBegin(...args) {
+ return Dash.Dash.prototype._onItemDragBegin.call(this, ...args);
+ }
+
+ _onItemDragCancelled(...args) {
+ return Dash.Dash.prototype._onItemDragCancelled.call(this, ...args);
+ }
+
+ _onItemDragEnd(...args) {
+ return Dash.Dash.prototype._onItemDragEnd.call(this, ...args);
+ }
+
+ _endItemDrag(...args) {
+ return Dash.Dash.prototype._endItemDrag.call(this, ...args);
+ }
+
+ _onItemDragMotion(...args) {
+ return Dash.Dash.prototype._onItemDragMotion.call(this, ...args);
+ }
+
+ _appIdListToHash(...args) {
+ return Dash.Dash.prototype._appIdListToHash.call(this, ...args);
+ }
+
+ _queueRedisplay(...args) {
+ return Dash.Dash.prototype._queueRedisplay.call(this, ...args);
+ }
+
+ _hookUpLabel(...args) {
+ return Dash.Dash.prototype._hookUpLabel.call(this, ...args);
+ }
+
+ _syncLabel(...args) {
+ return Dash.Dash.prototype._syncLabel.call(this, ...args);
+ }
+
+ _clearDragPlaceholder(...args) {
+ return Dash.Dash.prototype._clearDragPlaceholder.call(this, ...args);
+ }
+
+ _clearEmptyDropTarget(...args) {
+ return Dash.Dash.prototype._clearEmptyDropTarget.call(this, ...args);
+ }
+
+ handleDragOver(source, actor, x, y, time) {
+ let ret;
+ if (this._isHorizontal) {
+ ret = Dash.Dash.prototype.handleDragOver.call(this, source, actor, x, y, time);
+
+ if (ret === DND.DragMotionResult.CONTINUE)
+ return ret;
+ } else {
+ const propertyInjections = new Utils.PropertyInjectionsHandler();
+ propertyInjections.add(this._box, 'width', {
+ get: () => this._box.get_children().reduce((a, c) => a + c.height, 0),
+ });
+
+ if (this._dragPlaceholder) {
+ propertyInjections.add(this._dragPlaceholder, 'width', {
+ get: () => this._dragPlaceholder.height,
+ });
+ }
+
+ ret = Dash.Dash.prototype.handleDragOver.call(this, source, actor, y, x, time);
+ propertyInjections.destroy();
+
+ if (ret === DND.DragMotionResult.CONTINUE)
+ return ret;
+
+ if (this._dragPlaceholder) {
+ this._dragPlaceholder.child.set_width(this.iconSize / 2);
+ this._dragPlaceholder.child.set_height(this.iconSize);
+
+ let pos = this._dragPlaceholderPos;
+ if (this._isHorizontal &&
+ Clutter.get_default_text_direction() === Clutter.TextDirection.RTL)
+ pos = this._box.get_children() - 1 - pos;
+
+ if (pos !== this._dragPlaceholderPos) {
+ this._dragPlaceholderPos = pos;
+ this._box.set_child_at_index(this._dragPlaceholder,
+ this._dragPlaceholderPos);
+ }
+ }
+ }
+
+ if (this._dragPlaceholder) {
+ // Ensure the next and previous icon are visible when moving the
+ // placeholder (we're assuming there's room for both of them)
+ const children = this._box.get_children();
+ if (this._dragPlaceholderPos > 0) {
+ ensureActorVisibleInScrollView(this._scrollView,
+ children[this._dragPlaceholderPos - 1]);
+ }
+
+ if (this._dragPlaceholderPos >= -1 &&
+ this._dragPlaceholderPos < children.length - 1) {
+ ensureActorVisibleInScrollView(this._scrollView,
+ children[this._dragPlaceholderPos + 1]);
+ }
+ }
+
+ return ret;
+ }
+
+ acceptDrop(...args) {
+ return Dash.Dash.prototype.acceptDrop.call(this, ...args);
+ }
+
+ _onWindowDragBegin(...args) {
+ return Dash.Dash.prototype._onWindowDragBegin.call(this, ...args);
+ }
+
+ _onWindowDragEnd(...args) {
+ return Dash.Dash.prototype._onWindowDragEnd.call(this, ...args);
+ }
+
+ _onScrollEvent(actor, event) {
+ // If scroll is not used because the icon is resized, let the scroll event propagate.
+ if (!Docking.DockManager.settings.iconSizeFixed)
+ return Clutter.EVENT_PROPAGATE;
+
+ // reset timeout to avid conflicts with the mousehover event
+ this._ensureItemVisibility(null);
+
+ // Skip to avoid double events mouse
+ if (event.get_scroll_direction() !== Clutter.ScrollDirection.SMOOTH)
+ return Clutter.EVENT_STOP;
+
+
+ let adjustment, delta = 0;
+
+ if (this._isHorizontal) {
+ adjustment = this._scrollView.get_hadjustment
+ ? this._scrollView.get_hadjustment()
+ : this._scrollView.get_hscroll_bar().get_adjustment();
+ } else {
+ adjustment = this._scrollView.get_vadjustment
+ ? this._scrollView.get_vadjustment()
+ : this._scrollView.get_vscroll_bar().get_adjustment();
+ }
+
+ const increment = adjustment.step_increment;
+ const [dx, dy] = event.get_scroll_delta();
+
+ if (this._isHorizontal)
+ delta = (Math.abs(dx) > Math.abs(dy) ? dx : dy) * increment;
+ else
+ delta = dy * increment;
+
+ const value = adjustment.get_value();
+
+ // TODO: Remove this if possible.
+ if (Number.isNaN(value))
+ adjustment.set_value(delta);
+ else
+ adjustment.set_value(value + delta);
+
+ return Clutter.EVENT_STOP;
+ }
+
+ _ensureItemVisibility(actor) {
+ if (actor?.hover) {
+ const destroyId =
+ actor.connect('destroy', () => this._ensureItemVisibility(null));
+ this._ensureActorVisibilityTimeoutId = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT, 100, () => {
+ actor.disconnect(destroyId);
+ ensureActorVisibleInScrollView(this._scrollView, actor);
+ this._ensureActorVisibilityTimeoutId = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ } else if (this._ensureActorVisibilityTimeoutId) {
+ GLib.source_remove(this._ensureActorVisibilityTimeoutId);
+ this._ensureActorVisibilityTimeoutId = 0;
+ }
+ }
+
+ _createAppItem(app) {
+ const appIcon = new AppIcons.makeAppIcon(app, this._monitorIndex, this.iconAnimator);
+
+ if (appIcon._draggable) {
+ appIcon._draggable.connect('drag-begin', () => {
+ appIcon.opacity = 50;
+ });
+ appIcon._draggable.connect('drag-end', () => {
+ appIcon.opacity = 255;
+ });
+ }
+
+ appIcon.connectObject('menu-state-changed', (_, opened) => {
+ this._itemMenuStateChanged(item, opened);
+ }, this);
+
+ const item = new DockDashItemContainer(this._position);
+ item.setChild(appIcon);
+
+ appIcon.connectObject('notify::hover', a => this._ensureItemVisibility(a), this);
+ appIcon.connectObject('clicked', actor => {
+ ensureActorVisibleInScrollView(this._scrollView, actor);
+ }, this);
+
+ appIcon.connectObject('key-focus-in', actor => {
+ const [xShift, yShift] = ensureActorVisibleInScrollView(this._scrollView, actor);
+
+ // This signal is triggered also by mouse click. The popup menu is opened at the original
+ // coordinates. Thus correct for the shift which is going to be applied to the scrollview.
+ if (appIcon._menu) {
+ appIcon._menu._boxPointer.xOffset = -xShift;
+ appIcon._menu._boxPointer.yOffset = -yShift;
+ }
+ }, this);
+
+ appIcon.connectObject('notify::focused', () => {
+ const {settings} = Docking.DockManager;
+ if (appIcon.focused && settings.scrollToFocusedApplication)
+ ensureActorVisibleInScrollView(this._scrollView, item);
+ }, this);
+
+ appIcon.connectObject('notify::urgent', () => {
+ if (appIcon.urgent) {
+ ensureActorVisibleInScrollView(this._scrollView, item);
+ if (Docking.DockManager.settings.showDockUrgentNotify)
+ this._requireVisibility();
+ }
+ }, this);
+
+ // Override default AppIcon label_actor, now the
+ // accessible_name is set at DashItemContainer.setLabelText
+ appIcon.label_actor = null;
+ item.setLabelText(app.get_name());
+
+ appIcon.icon.setIconSize(this.iconSize);
+ this._hookUpLabel(item, appIcon);
+
+ item.connectObject('notify::position', () => appIcon.updateIconGeometry(), appIcon);
+ item.connectObject('notify::size', () => appIcon.updateIconGeometry(), appIcon);
+
+ return item;
+ }
+
+ _requireVisibility() {
+ this.requiresVisibility = true;
+
+ if (this._requiresVisibilityTimeout)
+ GLib.source_remove(this._requiresVisibilityTimeout);
+
+ this._requiresVisibilityTimeout = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT,
+ DASH_VISIBILITY_TIMEOUT, () => {
+ this._requiresVisibilityTimeout = 0;
+ this.requiresVisibility = false;
+ });
+ }
+
+ /**
+ * Return an array with the "proper" appIcons currently in the dash
+ */
+ getAppIcons() {
+ // Only consider children which are "proper"
+ // icons (i.e. ignoring drag placeholders) and which are not
+ // animating out (which means they will be destroyed at the end of
+ // the animation)
+ const iconChildren = this._box.get_children().filter(actor => {
+ return actor.child &&
+ !!actor.child.icon &&
+ !actor.animatingOut;
+ });
+
+ const appIcons = iconChildren.map(actor => {
+ return actor.child;
+ });
+
+ return appIcons;
+ }
+
+ _itemMenuStateChanged(item, opened) {
+ Dash.Dash.prototype._itemMenuStateChanged.call(this, item, opened);
+
+ if (opened) {
+ this.emit('menu-opened');
+ } else {
+ // I want to listen from outside when a menu is closed. I used to
+ // add a custom signal to the appIcon, since gnome 3.8 the signal
+ // calling this callback was added upstream.
+ this.emit('menu-closed');
+ }
+ }
+
+ _adjustIconSize() {
+ // For the icon size, we only consider children which are "proper"
+ // icons (i.e. ignoring drag placeholders) and which are not
+ // animating out (which means they will be destroyed at the end of
+ // the animation)
+ const iconChildren = this._box.get_children().filter(actor => {
+ return actor.child &&
+ actor.child._delegate &&
+ actor.child._delegate.icon &&
+ !actor.animatingOut;
+ });
+
+ iconChildren.push(this._showAppsIcon);
+
+ if (this._maxWidth === -1 && this._maxHeight === -1)
+ return;
+
+ // Check if the container is present in the stage. This avoids critical
+ // errors when unlocking the screen
+ if (!this._container.get_stage())
+ return;
+
+ const themeNode = this._dashContainer.get_theme_node();
+ const maxAllocation = new Clutter.ActorBox({
+ x1: 0,
+ y1: 0,
+ x2: this._isHorizontal ? this._maxWidth : 42 /* whatever */,
+ y2: this._isHorizontal ? 42 : this._maxHeight,
+ });
+ const maxContent = themeNode.get_content_box(maxAllocation);
+ let availSpace;
+ if (this._isHorizontal)
+ availSpace = maxContent.get_width();
+ else
+ availSpace = maxContent.get_height();
+
+ const spacing = themeNode.get_length('spacing');
+
+ const [{child: firstButton}] = iconChildren;
+ const {child: firstIcon} = firstButton?.icon ?? {child: null};
+
+ // if no icons there's nothing to adjust
+ if (!firstIcon)
+ return;
+
+ // Enforce valid spacings during the size request
+ firstIcon.ensure_style();
+ const [, , iconWidth, iconHeight] = firstIcon.get_preferred_size();
+ const [, , buttonWidth, buttonHeight] = firstButton.get_preferred_size();
+
+ if (this._isHorizontal) {
+ // Subtract icon padding and box spacing from the available width
+ availSpace -= iconChildren.length * (buttonWidth - iconWidth) +
+ (iconChildren.length - 1) * spacing;
+
+ if (this._separator) {
+ const [, , separatorWidth] = this._separator.get_preferred_size();
+ availSpace -= separatorWidth + spacing;
+ }
+ } else {
+ // Subtract icon padding and box spacing from the available height
+ availSpace -= iconChildren.length * (buttonHeight - iconHeight) +
+ (iconChildren.length - 1) * spacing;
+
+ if (this._separator) {
+ const [, , , separatorHeight] = this._separator.get_preferred_size();
+ availSpace -= separatorHeight + spacing;
+ }
+ }
+
+ const maxIconSize = availSpace / iconChildren.length;
+ const {scaleFactor} = St.ThemeContext.get_for_stage(global.stage);
+ const iconSizes = this._availableIconSizes.map(s => s * scaleFactor);
+
+ let [newIconSize] = this._availableIconSizes;
+ for (let i = 0; i < iconSizes.length; i++) {
+ if (iconSizes[i] <= maxIconSize)
+ newIconSize = this._availableIconSizes[i];
+ }
+
+ if (newIconSize === this.iconSize)
+ return;
+
+ const oldIconSize = this.iconSize;
+ this.iconSize = newIconSize;
+ this.emit('icon-size-changed');
+
+ const scale = oldIconSize / newIconSize;
+ for (let i = 0; i < iconChildren.length; i++) {
+ const {icon} = iconChildren[i].child._delegate;
+
+ // Set the new size immediately, to keep the icons' sizes
+ // in sync with this.iconSize
+ icon.setIconSize(this.iconSize);
+
+ // Don't animate the icon size change when the overview
+ // is transitioning, not visible or when initially filling
+ // the dash
+ if (!Main.overview.visible || Main.overview.animationInProgress ||
+ !this._shownInitially)
+ continue;
+
+ const [targetWidth, targetHeight] = icon.icon.get_size();
+
+ // Scale the icon's texture to the previous size and
+ // tween to the new size
+ icon.icon.set_size(icon.icon.width * scale,
+ icon.icon.height * scale);
+
+ icon.icon.ease({
+ width: targetWidth,
+ height: targetHeight,
+ duration: DASH_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ }
+
+ if (this._separator) {
+ const animateProperties = this._isHorizontal
+ ? {height: this.iconSize} : {width: this.iconSize};
+
+ this._separator.ease({
+ ...animateProperties,
+ duration: DASH_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ }
+ }
+
+ _redisplay() {
+ const favorites = AppFavorites.getAppFavorites().getFavoriteMap();
+
+ let running = this._appSystem.get_running();
+ const dockManager = Docking.DockManager.getDefault();
+ const {settings} = dockManager;
+
+ this._scrollView.set({
+ xAlign: Clutter.ActorAlign.FILL,
+ yAlign: Clutter.ActorAlign.FILL,
+ });
+ if (dockManager.settings.dockExtended) {
+ if (!this._isHorizontal) {
+ this._scrollView.yAlign = dockManager.settings.alwaysCenterIcons
+ ? Clutter.ActorAlign.CENTER : Clutter.ActorAlign.START;
+ } else {
+ this._scrollView.xAlign = dockManager.settings.alwaysCenterIcons
+ ? Clutter.ActorAlign.CENTER : Clutter.ActorAlign.START;
+ }
+ }
+
+ if (settings.isolateWorkspaces ||
+ settings.isolateMonitors) {
+ // When using isolation, we filter out apps that have no windows in
+ // the current workspace
+ const monitorIndex = this._monitorIndex;
+ running = running.filter(app =>
+ AppIcons.getInterestingWindows(app.get_windows(), monitorIndex).length);
+ }
+
+ const children = this._box.get_children().filter(actor => {
+ return actor.child &&
+ actor.child._delegate &&
+ actor.child._delegate.app;
+ });
+ // Apps currently in the dash
+ let oldApps = children.map(actor => actor.child._delegate.app);
+ // Apps supposed to be in the dash
+ const newApps = [];
+
+ const {showFavorites} = settings;
+ if (showFavorites)
+ newApps.push(...Object.values(favorites));
+
+ if (settings.showRunning) {
+ // We reorder the running apps so that they don't change position on the
+ // dash with every redisplay() call
+
+ // First: add the apps from the oldApps list that are still running
+ oldApps.forEach(oldApp => {
+ const index = running.indexOf(oldApp);
+ if (index > -1) {
+ const [app] = running.splice(index, 1);
+ if (!showFavorites || !(app.get_id() in favorites))
+ newApps.push(app);
+ }
+ });
+
+ // Second: add the new apps
+ running.forEach(app => {
+ if (!showFavorites || !(app.get_id() in favorites))
+ newApps.push(app);
+ });
+ }
+
+ this._signalsHandler.removeWithLabel(Labels.SHOW_MOUNTS);
+ if (dockManager.removables) {
+ this._signalsHandler.addWithLabel(Labels.SHOW_MOUNTS,
+ dockManager.removables, 'changed', this._queueRedisplay.bind(this));
+ dockManager.removables.getApps().forEach(removable => {
+ if (!newApps.includes(removable))
+ newApps.push(removable);
+ });
+ } else {
+ oldApps = oldApps.filter(app => !app.location || app.isTrash);
+ }
+
+ if (dockManager.trash) {
+ const trashApp = dockManager.trash.getApp();
+ if (!newApps.includes(trashApp))
+ newApps.push(trashApp);
+ } else {
+ oldApps = oldApps.filter(app => !app.isTrash);
+ }
+
+ // Temporary remove the separator so that we don't compute to position icons
+ const oldSeparatorPos = this._box.get_children().indexOf(this._separator);
+ if (this._separator)
+ this._box.remove_child(this._separator);
+
+ // Figure out the actual changes to the list of items; we iterate
+ // over both the list of items currently in the dash and the list
+ // of items expected there, and collect additions and removals.
+ // Moves are both an addition and a removal, where the order of
+ // the operations depends on whether we encounter the position
+ // where the item has been added first or the one from where it
+ // was removed.
+ // There is an assumption that only one item is moved at a given
+ // time; when moving several items at once, everything will still
+ // end up at the right position, but there might be additional
+ // additions/removals (e.g. it might remove all the launchers
+ // and add them back in the new order even if a smaller set of
+ // additions and removals is possible).
+ // If above assumptions turns out to be a problem, we might need
+ // to use a more sophisticated algorithm, e.g. Longest Common
+ // Subsequence as used by diff.
+
+ const addedItems = [];
+ const removedActors = [];
+
+ let newIndex = 0;
+ let oldIndex = 0;
+ while (newIndex < newApps.length || oldIndex < oldApps.length) {
+ const oldApp = oldApps.length > oldIndex ? oldApps[oldIndex] : null;
+ const newApp = newApps.length > newIndex ? newApps[newIndex] : null;
+
+ // No change at oldIndex/newIndex
+ if (oldApp === newApp) {
+ oldIndex++;
+ newIndex++;
+ continue;
+ }
+
+ // App removed at oldIndex
+ if (oldApp && !newApps.includes(oldApp)) {
+ removedActors.push(children[oldIndex]);
+ oldIndex++;
+ continue;
+ }
+
+ // App added at newIndex
+ if (newApp && !oldApps.includes(newApp)) {
+ addedItems.push({
+ app: newApp,
+ item: this._createAppItem(newApp),
+ pos: newIndex,
+ });
+ newIndex++;
+ continue;
+ }
+
+ // App moved
+ const nextApp = newApps.length > newIndex + 1
+ ? newApps[newIndex + 1] : null;
+ const insertHere = nextApp && nextApp === oldApp;
+ const alreadyRemoved = removedActors.reduce((result, actor) => {
+ const removedApp = actor.child._delegate.app;
+ return result || removedApp === newApp;
+ }, false);
+
+ if (insertHere || alreadyRemoved) {
+ const newItem = this._createAppItem(newApp);
+ addedItems.push({
+ app: newApp,
+ item: newItem,
+ pos: newIndex + removedActors.length,
+ });
+ newIndex++;
+ } else {
+ removedActors.push(children[oldIndex]);
+ oldIndex++;
+ }
+ }
+
+ for (let i = 0; i < addedItems.length; i++) {
+ this._box.insert_child_at_index(addedItems[i].item,
+ addedItems[i].pos);
+ }
+
+ for (let i = 0; i < removedActors.length; i++) {
+ const item = removedActors[i];
+
+ // Don't animate item removal when the overview is transitioning
+ // or hidden
+ if (!Main.overview.animationInProgress)
+ item.animateOutAndDestroy();
+ else
+ item.destroy();
+ }
+
+ // Update separator
+ const nFavorites = Object.keys(favorites).length;
+ const nIcons = children.length + addedItems.length - removedActors.length;
+ if (nFavorites > 0 && nFavorites < nIcons) {
+ if (!this._separator) {
+ this._separator = new St.Widget({
+ style_class: 'dash-separator',
+ x_align: this._isHorizontal
+ ? Clutter.ActorAlign.FILL : Clutter.ActorAlign.CENTER,
+ y_align: this._isHorizontal
+ ? Clutter.ActorAlign.CENTER : Clutter.ActorAlign.FILL,
+ width: this._isHorizontal ? -1 : this.iconSize,
+ height: this._isHorizontal ? this.iconSize : -1,
+ reactive: true,
+ track_hover: true,
+ });
+ this._separator.connect('notify::hover', a => this._ensureItemVisibility(a));
+ }
+ let pos = nFavorites + this._animatingPlaceholdersCount;
+ if (this._dragPlaceholder)
+ pos++;
+ const removedFavorites = removedActors.filter(a =>
+ children.indexOf(a) < oldSeparatorPos);
+ pos += removedFavorites.length;
+ this._box.insert_child_at_index(this._separator, pos);
+ } else if (this._separator) {
+ this._separator.destroy();
+ this._separator = null;
+ }
+
+ this._adjustIconSize();
+
+ // Skip animations on first run when adding the initial set
+ // of items, to avoid all items zooming in at once
+ const animate = this._shownInitially &&
+ !Main.layoutManager._startingUp;
+
+ if (!this._shownInitially)
+ this._shownInitially = true;
+
+ addedItems.forEach(({item}) => item.show(animate));
+
+ // Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=692744
+ // Without it, StBoxLayout may use a stale size cache
+ this._box.queue_relayout();
+
+ // This will update the size, and the corresponding number for each icon
+ this._updateNumberOverlay();
+
+ this.updateShowAppsButton();
+ }
+
+ _updateNumberOverlay() {
+ const appIcons = this.getAppIcons();
+ let counter = 1;
+ appIcons.forEach(icon => {
+ if (counter < 10) {
+ icon.setNumberOverlay(counter);
+ counter++;
+ } else if (counter === 10) {
+ icon.setNumberOverlay(0);
+ counter++;
+ } else {
+ // No overlay after 10
+ icon.setNumberOverlay(-1);
+ }
+ icon.updateNumberOverlay();
+ });
+ }
+
+ toggleNumberOverlay(activate) {
+ const appIcons = this.getAppIcons();
+ appIcons.forEach(icon => {
+ icon.toggleNumberOverlay(activate);
+ });
+ }
+
+ _initializeIconSize(maxSize) {
+ const maxAllowed = baseIconSizes[baseIconSizes.length - 1];
+ maxSize = Math.min(maxSize, maxAllowed);
+
+ if (Docking.DockManager.settings.iconSizeFixed) {
+ this._availableIconSizes = [maxSize];
+ } else {
+ this._availableIconSizes = baseIconSizes.filter(val => {
+ return val < maxSize;
+ });
+ this._availableIconSizes.push(maxSize);
+ }
+ }
+
+ setIconSize(maxSize, doNotAnimate) {
+ this._initializeIconSize(maxSize);
+
+ if (doNotAnimate)
+ this._shownInitially = false;
+
+ this._queueRedisplay();
+ }
+
+ /**
+ * Reset the displayed apps icon to maintain the correct order when changing
+ * show favorites/show running settings
+ */
+ resetAppIcons() {
+ const children = this._box.get_children().filter(actor => {
+ return actor.child &&
+ !!actor.child.icon;
+ });
+ for (let i = 0; i < children.length; i++) {
+ const item = children[i];
+ item.destroy();
+ }
+
+ // to avoid ugly animations, just suppress them like when dash is first loaded.
+ this._shownInitially = false;
+ this._redisplay();
+ }
+
+ get showAppsButton() {
+ return this._showAppsIcon.toggleButton;
+ }
+
+ showShowAppsButton() {
+ this._showAppsIcon.visible = true;
+ this._showAppsIcon.show(true);
+ this.updateShowAppsButton();
+ }
+
+ hideShowAppsButton() {
+ this._showAppsIcon.visible = false;
+ }
+
+ setMaxSize(maxWidth, maxHeight) {
+ if (this._maxWidth === maxWidth &&
+ this._maxHeight === maxHeight)
+ return;
+
+ this._maxWidth = maxWidth;
+ this._maxHeight = maxHeight;
+ this._queueRedisplay();
+ }
+
+ updateShowAppsButton() {
+ if (this._showAppsIcon.get_parent() && !this._showAppsIcon.visible)
+ return;
+
+ const {settings} = Docking.DockManager;
+ const notifiedProperties = [];
+ const showAppsContainer = settings.showAppsAlwaysInTheEdge || !settings.dockExtended
+ ? this._dashContainer : this._boxContainer;
+
+ this._signalsHandler.addWithLabel(Labels.FIRST_LAST_CHILD_WORKAROUND,
+ showAppsContainer, 'notify',
+ (_obj, pspec) => notifiedProperties.push(pspec.name));
+
+ if (this._showAppsIcon.get_parent() !== showAppsContainer) {
+ this._showAppsIcon.get_parent()?.remove_child(this._showAppsIcon);
+
+ if (Docking.DockManager.settings.showAppsAtTop)
+ showAppsContainer.insert_child_below(this._showAppsIcon, null);
+ else
+ showAppsContainer.insert_child_above(this._showAppsIcon, null);
+ } else if (settings.showAppsAtTop) {
+ showAppsContainer.set_child_below_sibling(this._showAppsIcon, null);
+ } else {
+ showAppsContainer.set_child_above_sibling(this._showAppsIcon, null);
+ }
+
+ this._signalsHandler.removeWithLabel(Labels.FIRST_LAST_CHILD_WORKAROUND);
+
+ // This is indeed ugly, but we need to ensure that the last and first
+ // visible widgets are re-computed by St, that is buggy because of a
+ // mutter issue that is being fixed:
+ // https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2047
+ if (!notifiedProperties.includes('first-child'))
+ showAppsContainer.notify('first-child');
+ if (!notifiedProperties.includes('last-child'))
+ showAppsContainer.notify('last-child');
+ }
+});
+
+
+/**
+ * This is a copy of the same function in utils.js, but also adjust horizontal scrolling
+ * and perform few further checks on the current value to avoid changing the values when
+ * it would be clamp to the current one in any case.
+ * Return the amount of shift applied
+ *
+ * @param scrollView
+ * @param actor
+ */
+function ensureActorVisibleInScrollView(scrollView, actor) {
+ // access to scrollView.[hv]scroll was deprecated in gnome 46
+ // instead, adjustment can be accessed directly
+ // keep old way for backwards compatibility (gnome <= 45)
+ const vAdjustment = scrollView.vadjustment ?? scrollView.vscroll.adjustment;
+ const hAdjustment = scrollView.hadjustment ?? scrollView.hscroll.adjustment;
+ const {value: vValue0, pageSize: vPageSize, upper: vUpper} = vAdjustment;
+ const {value: hValue0, pageSize: hPageSize, upper: hUpper} = hAdjustment;
+ let [hValue, vValue] = [hValue0, vValue0];
+ let vOffset = 0;
+ let hOffset = 0;
+
+ const fade = scrollView.get_effect('fade');
+ if (fade) {
+ vOffset = fade.fade_margins.top;
+ hOffset = fade.fade_margins.left;
+ }
+
+ const box = actor.get_allocation_box();
+ let {y1} = box, {y2} = box, {x1} = box, {x2} = box;
+
+ let parent = actor.get_parent();
+ while (parent !== scrollView) {
+ if (!parent)
+ throw new Error('Actor not in scroll view');
+
+ const parentBox = parent.get_allocation_box();
+ y1 += parentBox.y1;
+ y2 += parentBox.y1;
+ x1 += parentBox.x1;
+ x2 += parentBox.x1;
+ parent = parent.get_parent();
+ }
+
+ if (y1 < vValue + vOffset)
+ vValue = Math.max(0, y1 - vOffset);
+ else if (vValue < vUpper - vPageSize && y2 > vValue + vPageSize - vOffset)
+ vValue = Math.min(vUpper - vPageSize, y2 + vOffset - vPageSize);
+
+ if (x1 < hValue + hOffset)
+ hValue = Math.max(0, x1 - hOffset);
+ else if (hValue < hUpper - hPageSize && x2 > hValue + hPageSize - hOffset)
+ hValue = Math.min(hUpper - hPageSize, x2 + hOffset - hPageSize);
+
+ if (vValue !== vValue0) {
+ vAdjustment.ease(vValue, {
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ duration: Util.SCROLL_TIME,
+ });
+ }
+
+ if (hValue !== hValue0) {
+ hAdjustment.ease(hValue, {
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ duration: Util.SCROLL_TIME,
+ });
+ }
+
+ return [hValue - hValue0, vValue - vValue0];
+}
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dbusmenuUtils.js b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dbusmenuUtils.js
new file mode 100644
index 0000000..d992b1a
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dbusmenuUtils.js
@@ -0,0 +1,280 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+import {
+ Atk,
+ Clutter,
+ Gio,
+ GLib,
+ St,
+} from './dependencies/gi.js';
+
+import {PopupMenu} from './dependencies/shell/ui.js';
+
+import {Utils} from './imports.js';
+
+// Dbusmenu features not (yet) supported:
+//
+// * The CHILD_DISPLAY property
+//
+// This seems to have only one possible value in the Dbusmenu API, so
+// there's little point in depending on it--the code in libdbusmenu sets it
+// if and only if an item has children, so for our purposes it's simpler
+// and more intuitive to just check children.length. (This does ignore the
+// possibility of a program not using libdbusmenu and setting CHILD_DISPLAY
+// independently, perhaps to indicate that an childless menu item should
+// nevertheless be displayed like a submenu.)
+//
+// * Children more than two levels deep
+//
+// PopupMenu doesn't seem to support submenus in submenus.
+//
+// * Shortcut keys
+//
+// If these keys are supposed to be installed as global shortcuts, we'd
+// have to query these aggressively and not wait for the DBus menu to be
+// mapped to a popup menu. A shortcut key that only works once the popup
+// menu is open and has key focus is possibly of marginal value.
+
+/**
+ *
+ */
+export async function haveDBusMenu() {
+ try {
+ const {default: DBusMenu} = await import('gi://Dbusmenu');
+ return DBusMenu;
+ } catch (e) {
+ log(`Failed to import DBusMenu, quicklists are not available: ${e}`);
+ return null;
+ }
+}
+
+const DBusMenu = await haveDBusMenu();
+
+/**
+ * @param dbusmenuItem
+ * @param deep
+ */
+export function makePopupMenuItem(dbusmenuItem, deep) {
+ // These are the only properties guaranteed to be available when the root
+ // item is first announced. Other properties might be loaded already, but
+ // be sure to connect to Dbusmenu.MENUITEM_SIGNAL_PROPERTY_CHANGED to get
+ // the most up-to-date values in case they aren't.
+ const itemType = dbusmenuItem.property_get(DBusMenu.MENUITEM_PROP_TYPE);
+ const label = dbusmenuItem.property_get(DBusMenu.MENUITEM_PROP_LABEL);
+ const visible = dbusmenuItem.property_get_bool(DBusMenu.MENUITEM_PROP_VISIBLE);
+ const enabled = dbusmenuItem.property_get_bool(DBusMenu.MENUITEM_PROP_ENABLED);
+ const accessibleDesc = dbusmenuItem.property_get(DBusMenu.MENUITEM_PROP_ACCESSIBLE_DESC);
+ // const childDisplay = dbusmenuItem.property_get(Dbusmenu.MENUITEM_PROP_CHILD_DISPLAY);
+
+ let item;
+ const signalsHandler = new Utils.GlobalSignalsHandler();
+ const wantIcon = itemType === DBusMenu.CLIENT_TYPES_IMAGE;
+
+ // If the basic type of the menu item needs to change, call this.
+ const recreateItem = () => {
+ const newItem = makePopupMenuItem(dbusmenuItem, deep);
+ const parentMenu = item._parent;
+ parentMenu.addMenuItem(newItem);
+ // Reminder: Clutter thinks of later entries in the child list as
+ // "above" earlier ones, so "above" here means "below" in terms of the
+ // menu's vertical order.
+ parentMenu.actor.set_child_above_sibling(newItem.actor, item.actor);
+ if (newItem.menu)
+ parentMenu.actor.set_child_above_sibling(newItem.menu.actor, newItem.actor);
+
+ parentMenu.actor.remove_child(item.actor);
+ item.destroy();
+ item = null;
+ };
+
+ const updateDisposition = () => {
+ const disposition = dbusmenuItem.property_get(DBusMenu.MENUITEM_PROP_DISPOSITION);
+ let iconName = null;
+ switch (disposition) {
+ case DBusMenu.MENUITEM_DISPOSITION_ALERT:
+ case DBusMenu.MENUITEM_DISPOSITION_WARNING:
+ iconName = 'dialog-warning-symbolic';
+ break;
+ case DBusMenu.MENUITEM_DISPOSITION_INFORMATIVE:
+ iconName = 'dialog-information-symbolic';
+ break;
+ }
+ if (iconName) {
+ item._dispositionIcon = new St.Icon({
+ icon_name: iconName,
+ style_class: 'popup-menu-icon',
+ y_align: Clutter.ActorAlign.CENTER,
+ y_expand: true,
+ });
+ let expander;
+ for (let child = item.label.get_next_sibling(); ; child = child.get_next_sibling()) {
+ if (!child) {
+ expander = new St.Bin({
+ style_class: 'popup-menu-item-expander',
+ x_expand: true,
+ });
+ item.actor.add_child(expander);
+ break;
+ } else if (child instanceof St.Widget &&
+ child.has_style_class_name('popup-menu-item-expander')) {
+ expander = child;
+ break;
+ }
+ }
+ item.actor.insert_child_above(item._dispositionIcon, expander);
+ } else if (item._dispositionIcon) {
+ item.actor.remove_child(item._dispositionIcon);
+ item._dispositionIcon = null;
+ }
+ };
+
+ const updateIcon = () => {
+ if (!wantIcon)
+ return;
+
+ const iconData = dbusmenuItem.property_get_byte_array(DBusMenu.MENUITEM_PROP_ICON_DATA);
+ const iconName = dbusmenuItem.property_get(DBusMenu.MENUITEM_PROP_ICON_NAME);
+ if (iconName)
+ item.icon.icon_name = iconName;
+ else if (iconData.length)
+ item.icon.gicon = Gio.BytesIcon.new(iconData);
+ };
+
+ const updateOrnament = () => {
+ const toggleType = dbusmenuItem.property_get(DBusMenu.MENUITEM_PROP_TOGGLE_TYPE);
+ switch (toggleType) {
+ case DBusMenu.MENUITEM_TOGGLE_CHECK:
+ item.actor.accessible_role = Atk.Role.CHECK_MENU_ITEM;
+ break;
+ case DBusMenu.MENUITEM_TOGGLE_RADIO:
+ item.actor.accessible_role = Atk.Role.RADIO_MENU_ITEM;
+ break;
+ default:
+ item.actor.accessible_role = Atk.Role.MENU_ITEM;
+ }
+ let ornament = PopupMenu.Ornament.NONE;
+ const state = dbusmenuItem.property_get_int(DBusMenu.MENUITEM_PROP_TOGGLE_STATE);
+ if (state === DBusMenu.MENUITEM_TOGGLE_STATE_UNKNOWN) {
+ // PopupMenu doesn't natively support an "unknown" ornament, but we
+ // can hack one in:
+ item.setOrnament(ornament);
+ item.actor.add_accessible_state(Atk.StateType.INDETERMINATE);
+ item._ornamentLabel.text = '\u2501';
+ item.actor.remove_style_pseudo_class('checked');
+ } else {
+ item.actor.remove_accessible_state(Atk.StateType.INDETERMINATE);
+ if (state === DBusMenu.MENUITEM_TOGGLE_STATE_CHECKED) {
+ if (toggleType === DBusMenu.MENUITEM_TOGGLE_CHECK)
+ ornament = PopupMenu.Ornament.CHECK;
+ else if (toggleType === DBusMenu.MENUITEM_TOGGLE_RADIO)
+ ornament = PopupMenu.Ornament.DOT;
+
+ item.actor.add_style_pseudo_class('checked');
+ } else {
+ item.actor.remove_style_pseudo_class('checked');
+ }
+ item.setOrnament(ornament);
+ }
+ };
+
+ const onPropertyChanged = (_, name, value) => {
+ // `value` is null when a property is cleared, so handle those cases
+ // with sensible defaults.
+ switch (name) {
+ case DBusMenu.MENUITEM_PROP_TYPE:
+ recreateItem();
+ break;
+ case DBusMenu.MENUITEM_PROP_ENABLED:
+ item.setSensitive(value ? value.unpack() : false);
+ break;
+ case DBusMenu.MENUITEM_PROP_LABEL:
+ item.label.text = value ? value.unpack() : '';
+ break;
+ case DBusMenu.MENUITEM_PROP_VISIBLE:
+ item.actor.visible = value ? value.unpack() : false;
+ break;
+ case DBusMenu.MENUITEM_PROP_DISPOSITION:
+ updateDisposition();
+ break;
+ case DBusMenu.MENUITEM_PROP_ACCESSIBLE_DESC:
+ item.actor.get_accessible().accessible_description = value && value.unpack() || '';
+ break;
+ case DBusMenu.MENUITEM_PROP_ICON_DATA:
+ case DBusMenu.MENUITEM_PROP_ICON_NAME:
+ updateIcon();
+ break;
+ case DBusMenu.MENUITEM_PROP_TOGGLE_TYPE:
+ case DBusMenu.MENUITEM_PROP_TOGGLE_STATE:
+ updateOrnament();
+ break;
+ }
+ };
+
+
+ // Start actually building the menu item.
+ const children = dbusmenuItem.get_children();
+ if (children.length && !deep) {
+ // Make a submenu.
+ item = new PopupMenu.PopupSubMenuMenuItem(label, wantIcon);
+ const updateChildren = () => {
+ const itemChildren = dbusmenuItem.get_children();
+ if (!itemChildren.length) {
+ recreateItem();
+ return;
+ }
+
+ item.menu.removeAll();
+ itemChildren.forEach(remoteChild =>
+ item.menu.addMenuItem(makePopupMenuItem(remoteChild, true)));
+ };
+ updateChildren();
+ signalsHandler.add(
+ [dbusmenuItem, DBusMenu.MENUITEM_SIGNAL_CHILD_ADDED, updateChildren],
+ [dbusmenuItem, DBusMenu.MENUITEM_SIGNAL_CHILD_MOVED, updateChildren],
+ [dbusmenuItem, DBusMenu.MENUITEM_SIGNAL_CHILD_REMOVED, updateChildren]);
+ } else {
+ // Don't make a submenu.
+ if (!deep) {
+ // We only have the potential to get a submenu if we aren't deep.
+ signalsHandler.add(
+ [dbusmenuItem, DBusMenu.MENUITEM_SIGNAL_CHILD_ADDED, recreateItem],
+ [dbusmenuItem, DBusMenu.MENUITEM_SIGNAL_CHILD_MOVED, recreateItem],
+ [dbusmenuItem, DBusMenu.MENUITEM_SIGNAL_CHILD_REMOVED, recreateItem]);
+ }
+
+ if (itemType === DBusMenu.CLIENT_TYPES_SEPARATOR) {
+ item = new PopupMenu.PopupSeparatorMenuItem();
+ } else if (wantIcon) {
+ item = new PopupMenu.PopupImageMenuItem(label, null);
+ item.icon = item._icon;
+ } else {
+ item = new PopupMenu.PopupMenuItem(label);
+ }
+ }
+
+ // Set common initial properties.
+ item.actor.visible = visible;
+ item.setSensitive(enabled);
+ if (accessibleDesc)
+ item.actor.get_accessible().accessible_description = accessibleDesc;
+
+ updateDisposition();
+ updateIcon();
+ updateOrnament();
+
+ // Prevent an initial resize flicker.
+ if (wantIcon)
+ item.icon.icon_size = 16;
+
+
+ signalsHandler.add(dbusmenuItem, DBusMenu.MENUITEM_SIGNAL_PROPERTY_CHANGED, onPropertyChanged);
+
+ // Connections on item will be lost when item is disposed; there's no need
+ // to add them to signalsHandler.
+ item.connect('activate', () =>
+ dbusmenuItem.handle_event(DBusMenu.MENUITEM_EVENT_ACTIVATED,
+ new GLib.Variant('i', 0), Math.floor(Date.now() / 1000)));
+ item.connect('destroy', () => signalsHandler.destroy());
+
+ return item;
+}
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dependencies/gi.js b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dependencies/gi.js
new file mode 100644
index 0000000..c4b5385
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dependencies/gi.js
@@ -0,0 +1,14 @@
+export {default as Atk} from 'gi://Atk';
+export {default as Clutter} from 'gi://Clutter';
+export {default as Cogl} from 'gi://Cogl';
+export {default as GLib} from 'gi://GLib';
+export {default as GObject} from 'gi://GObject';
+export {default as GdkPixbuf} from 'gi://GdkPixbuf';
+export {default as Gio} from 'gi://Gio';
+export {default as GioUnix} from 'gi://GioUnix';
+export {default as Meta} from 'gi://Meta';
+export {default as Mtk} from 'gi://Mtk';
+export {default as Pango} from 'gi://Pango';
+export {default as Shell} from 'gi://Shell';
+export {default as St} from 'gi://St';
+
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dependencies/shell/extensions/extension.js b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dependencies/shell/extensions/extension.js
new file mode 100644
index 0000000..bd9db98
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dependencies/shell/extensions/extension.js
@@ -0,0 +1 @@
+export * as Extension from 'resource:///org/gnome/shell/extensions/extension.js';
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dependencies/shell/misc.js b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dependencies/shell/misc.js
new file mode 100644
index 0000000..cfd4b95
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dependencies/shell/misc.js
@@ -0,0 +1,5 @@
+export * as AnimationUtils from 'resource:///org/gnome/shell/misc/animationUtils.js';
+export * as Config from 'resource:///org/gnome/shell/misc/config.js';
+export * as ExtensionUtils from 'resource:///org/gnome/shell/misc/extensionUtils.js';
+export * as ParentalControlsManager from 'resource:///org/gnome/shell/misc/parentalControlsManager.js';
+export * as Util from 'resource:///org/gnome/shell/misc/util.js';
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dependencies/shell/ui.js b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dependencies/shell/ui.js
new file mode 100644
index 0000000..33f1082
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dependencies/shell/ui.js
@@ -0,0 +1,18 @@
+export * as AppDisplay from 'resource:///org/gnome/shell/ui/appDisplay.js';
+export * as AppMenu from 'resource:///org/gnome/shell/ui/appMenu.js';
+export * as AppFavorites from 'resource:///org/gnome/shell/ui/appFavorites.js';
+export * as BoxPointer from 'resource:///org/gnome/shell/ui/boxpointer.js';
+export * as DND from 'resource:///org/gnome/shell/ui/dnd.js';
+export * as Dash from 'resource:///org/gnome/shell/ui/dash.js';
+export * as Layout from 'resource:///org/gnome/shell/ui/layout.js';
+export * as Main from 'resource:///org/gnome/shell/ui/main.js';
+export * as Overview from 'resource:///org/gnome/shell/ui/overview.js';
+export * as OverviewControls from 'resource:///org/gnome/shell/ui/overviewControls.js';
+export * as PointerWatcher from 'resource:///org/gnome/shell/ui/pointerWatcher.js';
+export * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
+export * as SearchController from 'resource:///org/gnome/shell/ui/searchController.js';
+export * as ShellMountOperation from 'resource:///org/gnome/shell/ui/shellMountOperation.js';
+export * as Workspace from 'resource:///org/gnome/shell/ui/workspace.js';
+export * as WorkspaceSwitcherPopup from 'resource:///org/gnome/shell/ui/workspaceSwitcherPopup.js';
+export * as WorkspaceThumbnail from 'resource:///org/gnome/shell/ui/workspaceThumbnail.js';
+export * as WorkspacesView from 'resource:///org/gnome/shell/ui/workspacesView.js';
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/desktopIconsIntegration.js b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/desktopIconsIntegration.js
new file mode 100644
index 0000000..ca07747
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/desktopIconsIntegration.js
@@ -0,0 +1,170 @@
+/*
+ * The code in this file is distributed under a "1-clause BSD license",
+ * which makes it compatible with GPLv2 and GPLv3 too, and others.
+ *
+ * License text:
+ *
+ * Copyright (C) 2021 Sergio Costas (rastersoft@gmail.com)
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Integration class
+ *
+ * This class must be added to other extensions in order to integrate
+ * them with Desktop Icons NG. It allows an extension to notify how much margin
+ * it uses in each side of each monitor.
+ *
+ * DON'T SEND PATCHES TO THIS FILE TO THE EXTENSION MAINTAINER. SEND THEM TO
+ * DESKTOP ICONS NG MAINTAINER: https://gitlab.com/rastersoft/desktop-icons-ng
+ *
+ * In the *enable()* function, create a *DesktopIconsUsableAreaClass()*
+ * object with
+ *
+ * new DesktopIconsIntegration.DesktopIconsUsableAreaClass(object);
+ *
+ * Now, in the *disable()* function just call to the *destroy()* method before
+ * nullifying the pointer. You must create a new object in enable() the next
+ * time the extension is enabled.
+ *
+ * In your code, every time you change the margins, you should call first to
+ * *resetMargins()* method to clear the current margins, and then call to
+ * *setMargins(...)* method as many times as you need to set the margins in each
+ * monitor. You don't need to call it for all the monitors, only for those where
+ * you are painting something. If you don't set values for a monitor, they will
+ * be considered zero.
+ *
+ * The margins values are relative to the monitor border.
+ *
+ *******************************************************************************/
+
+import GLib from 'gi://GLib';
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+import * as ExtensionUtils from 'resource:///org/gnome/shell/misc/extensionUtils.js';
+import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js';
+
+const IDENTIFIER_UUID = '130cbc66-235c-4bd6-8571-98d2d8bba5e2';
+
+export class DesktopIconsUsableAreaClass {
+ _checkIfExtensionIsEnabled(extension) {
+ return (extension?.state === ExtensionUtils.ExtensionState.ENABLED) ||
+ (extension?.state === ExtensionUtils.ExtensionState.ACTIVE);
+ }
+
+ constructor() {
+ const Me = Extension.lookupByURL(import.meta.url);
+ this._UUID = Me.uuid;
+ this._extensionManager = Main.extensionManager;
+ this._timedMarginsID = 0;
+ this._margins = {};
+ this._emID = this._extensionManager.connect('extension-state-changed', (_obj, extension) => {
+ if (!extension)
+ return;
+
+ // If an extension is being enabled and lacks the
+ // DesktopIconsUsableArea object, we can avoid launching a refresh
+ if (this._checkIfExtensionIsEnabled(extension)) {
+ this._sendMarginsToExtension(extension);
+ return;
+ }
+ // if the extension is being disabled, we must do a full refresh,
+ // because if there were other extensions originally
+ // loaded after that extension, those extensions will be disabled
+ // and enabled again without notification
+ this._changedMargins();
+ });
+ }
+
+ /**
+ * Sets or updates the top, bottom, left and right margins for a
+ * monitor. Values are measured from the monitor border (and NOT from
+ * the workspace border).
+ *
+ * @param {int} monitor Monitor number to which set the margins.
+ * A negative value means "the primary monitor".
+ * @param {int} top Top margin in pixels
+ * @param {int} bottom Bottom margin in pixels
+ * @param {int} left Left margin in pixels
+ * @param {int} right Right margin in pixels
+ */
+ setMargins(monitor, top, bottom, left, right) {
+ this._margins[monitor] = {
+ top,
+ bottom,
+ left,
+ right,
+ };
+ this._changedMargins();
+ }
+
+ /**
+ * Clears the current margins. Must be called before configuring the monitors
+ * margins with setMargins().
+ */
+ resetMargins() {
+ this._margins = {};
+ this._changedMargins();
+ }
+
+ /**
+ * Disconnects all the signals and removes the margins.
+ */
+ destroy() {
+ if (this._emID) {
+ this._extensionManager.disconnect(this._emID);
+ this._emID = 0;
+ }
+ if (this._timedMarginsID) {
+ GLib.source_remove(this._timedMarginsID);
+ this._timedMarginsID = 0;
+ }
+ this._margins = null;
+ this._changedMargins();
+ }
+
+ _changedMargins() {
+ if (this._timedMarginsID)
+ GLib.source_remove(this._timedMarginsID);
+
+ this._timedMarginsID = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 100, () => {
+ this._sendMarginsToAll();
+ this._timedMarginsID = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+
+ _sendMarginsToAll() {
+ this._extensionManager.getUuids().forEach(uuid =>
+ this._sendMarginsToExtension(this._extensionManager.lookup(uuid)));
+ }
+
+ _sendMarginsToExtension(extension) {
+ // check that the extension is an extension that has the logic to accept
+ // working margins
+ if (!this._checkIfExtensionIsEnabled(extension))
+ return;
+
+ const usableArea = extension?.stateObj?.DesktopIconsUsableArea;
+ if (usableArea?.uuid === IDENTIFIER_UUID) {
+ usableArea.setMarginsForExtension(
+ this._UUID, this._margins);
+ }
+ }
+}
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/docking.js b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/docking.js
new file mode 100644
index 0000000..393b1de
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/docking.js
@@ -0,0 +1,2698 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+import {
+ Clutter,
+ GLib,
+ Gio,
+ GObject,
+ Meta,
+ Shell,
+ St,
+} from './dependencies/gi.js';
+
+import {
+ AppMenu,
+ AppDisplay,
+ Layout,
+ Main,
+ OverviewControls,
+ PointerWatcher,
+ Workspace,
+ WorkspacesView,
+ WorkspaceSwitcherPopup,
+} from './dependencies/shell/ui.js';
+
+import {
+ AnimationUtils,
+} from './dependencies/shell/misc.js';
+
+import {
+ AppIconsDecorator,
+ AppSpread,
+ DockDash,
+ DesktopIconsIntegration,
+ FileManager1API,
+ Intellihide,
+ LauncherAPI,
+ Locations,
+ NotificationsMonitor,
+ Theming,
+ Utils,
+} from './imports.js';
+
+import {Extension} from './dependencies/shell/extensions/extension.js';
+
+// Use __ () and N__() for the extension gettext domain, and reuse
+// the shell domain with the default _() and N_()
+const {gettext: __} = Extension;
+
+const {signals: Signals} = imports;
+
+const DOCK_DWELL_CHECK_INTERVAL = 100;
+const ICON_ANIMATOR_DURATION = 3000;
+const STARTUP_ANIMATION_TIME = 500;
+
+export const State = Object.freeze({
+ HIDDEN: 0,
+ SHOWING: 1,
+ SHOWN: 2,
+ HIDING: 3,
+});
+
+const scrollAction = Object.freeze({
+ DO_NOTHING: 0,
+ CYCLE_WINDOWS: 1,
+ SWITCH_WORKSPACE: 2,
+});
+
+const Labels = Object.freeze({
+ INITIALIZE: Symbol('initialize'),
+ ISOLATION: Symbol('isolation'),
+ LOCATIONS: Symbol('locations'),
+ MAIN_DASH: Symbol('main-dash'),
+ OLD_DASH_CHANGES: Symbol('old-dash-changes'),
+ SETTINGS: Symbol('settings'),
+ STARTUP_ANIMATION: Symbol('startup-animation'),
+ WORKSPACE_SWITCH_SCROLL: Symbol('workspace-switch-scroll'),
+});
+
+/**
+ * A simple St.Widget with one child whose allocation takes into account the
+ * slide out of its child via the slide-x property ([0:1]).
+ *
+ * Required since I want to track the input region of this container which is
+ * based on its allocation even if the child overflows the parent actor. By doing
+ * this the region of the dash that is slide-out is not stealing anymore the input
+ * regions making the extension usable when the primary monitor is the right one.
+ *
+ * The slide-x parameter can be used to directly animate the sliding. The parent
+ * must have a WEST (SOUTH) anchor_point to achieve the sliding to the RIGHT (BOTTOM)
+ * side.
+ */
+const DashSlideContainer = GObject.registerClass({
+ Properties: {
+ 'monitor-index': GObject.ParamSpec.uint(
+ 'monitor-index', 'monitor-index', 'monitor-index',
+ GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
+ 0, GLib.MAXUINT32, 0),
+ 'side': GObject.ParamSpec.enum(
+ 'side', 'side', 'side',
+ GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
+ St.Side, St.Side.LEFT),
+ 'slide-x': GObject.ParamSpec.double(
+ 'slide-x', 'slide-x', 'slide-x',
+ GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT,
+ 0, 1, 1),
+ },
+}, class DashSlideContainer extends St.Bin {
+ _init(params = {}) {
+ super._init(params);
+
+ this._slideoutSize = 0; // minimum size when slided out
+ this.connect('notify::slide-x', () => this.queue_relayout());
+
+ if (this.side === St.Side.TOP && DockManager.settings.dockFixed) {
+ this._signalsHandler = new Utils.GlobalSignalsHandler(this);
+ this._signalsHandler.add(Main.panel, 'notify::height',
+ () => this.queue_relayout());
+ }
+ }
+
+ vfunc_allocate(box) {
+ const contentBox = this.get_theme_node().get_content_box(box);
+
+ this.set_allocation(box);
+
+ if (!this.child)
+ return;
+
+ const availWidth = contentBox.x2 - contentBox.x1;
+ let availHeight = contentBox.y2 - contentBox.y1;
+ const [, , natChildWidth, natChildHeight] =
+ this.child.get_preferred_size();
+
+ const childWidth = natChildWidth;
+ const childHeight = natChildHeight;
+
+ const childBox = new Clutter.ActorBox();
+
+ const slideoutSize = this._slideoutSize;
+
+ if (this.side === St.Side.LEFT) {
+ childBox.x1 = (this.slideX - 1) * (childWidth - slideoutSize);
+ childBox.x2 = slideoutSize + this.slideX * (childWidth - slideoutSize);
+ childBox.y1 = 0;
+ childBox.y2 = childBox.y1 + childHeight;
+ } else if ((this.side === St.Side.RIGHT) || (this.side === St.Side.BOTTOM)) {
+ childBox.x1 = 0;
+ childBox.x2 = childWidth;
+ childBox.y1 = 0;
+ childBox.y2 = childBox.y1 + childHeight;
+ } else if (this.side === St.Side.TOP) {
+ const monitor = Main.layoutManager.monitors[this.monitorIndex];
+ let yOffset = 0;
+ if (Main.panel.x === monitor.x && Main.panel.y === monitor.y &&
+ DockManager.settings.dockFixed)
+ yOffset = Main.panel.height;
+ childBox.x1 = 0;
+ childBox.x2 = childWidth;
+ childBox.y1 = (this.slideX - 1) * (childHeight - slideoutSize) + yOffset;
+ childBox.y2 = slideoutSize + this.slideX * (childHeight - slideoutSize) + yOffset;
+ availHeight += yOffset;
+ }
+
+ this.child.allocate(childBox);
+
+ this.child.set_clip(-childBox.x1, -childBox.y1,
+ -childBox.x1 + availWidth, -childBox.y1 + availHeight);
+ }
+
+ /**
+ * Just the child width but taking into account the slided out part
+ *
+ * @param forHeight
+ */
+ vfunc_get_preferred_width(forHeight) {
+ let [minWidth, natWidth] = super.vfunc_get_preferred_width(forHeight || 0);
+ if ((this.side === St.Side.LEFT) || (this.side === St.Side.RIGHT)) {
+ minWidth = (minWidth - this._slideoutSize) * this.slideX + this._slideoutSize;
+ natWidth = (natWidth - this._slideoutSize) * this.slideX + this._slideoutSize;
+ }
+ return [minWidth, natWidth];
+ }
+
+ /**
+ * Just the child height but taking into account the slided out part
+ *
+ * @param forWidth
+ */
+ vfunc_get_preferred_height(forWidth) {
+ let [minHeight, natHeight] = super.vfunc_get_preferred_height(forWidth || 0);
+ if ((this.side === St.Side.TOP) || (this.side === St.Side.BOTTOM)) {
+ minHeight = (minHeight - this._slideoutSize) * this.slideX + this._slideoutSize;
+ natHeight = (natHeight - this._slideoutSize) * this.slideX + this._slideoutSize;
+
+ if (this.side === St.Side.TOP && DockManager.settings.dockFixed) {
+ const monitor = Main.layoutManager.monitors[this.monitorIndex];
+ if (Main.panel.x === monitor.x && Main.panel.y === monitor.y) {
+ minHeight += Main.panel.height;
+ natHeight += Main.panel.height;
+ }
+ }
+ }
+ return [minHeight, natHeight];
+ }
+});
+
+const DockedDash = GObject.registerClass({
+ Properties: {
+ 'is-main': GObject.ParamSpec.boolean(
+ 'is-main', 'is-main', 'is-main',
+ GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
+ false),
+ 'monitor-index': GObject.ParamSpec.uint(
+ 'monitor-index', 'monitor-index', 'monitor-index',
+ GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
+ 0, GLib.MAXUINT32, 0),
+ },
+ Signals: {
+ 'showing': {},
+ 'hiding': {},
+ },
+}, class DashToDock extends St.Bin {
+ _init(params) {
+ this._position = Utils.getPosition();
+
+ // This is the centering actor
+ super._init({
+ ...params,
+ name: 'dashtodockContainer',
+ reactive: false,
+ style_class: Theming.PositionStyleClass[this._position],
+ });
+
+ if (this.monitorIndex === undefined) {
+ // Hello turkish locale, gjs has instead defined this.monitorIndex
+ // See: https://gitlab.gnome.org/GNOME/gjs/-/merge_requests/742
+ this.monitorIndex = this.monitor_index;
+ }
+
+ this._rtl = Clutter.get_default_text_direction() === Clutter.TextDirection.RTL;
+
+ // Load settings
+ const {settings} = DockManager;
+ this._isHorizontal = (this._position === St.Side.TOP) || (this._position === St.Side.BOTTOM);
+
+ // Temporary ignore hover events linked to autohide for whatever reason
+ this._ignoreHover = false;
+ this._oldIgnoreHover = null;
+ // This variables are linked to the settings regardles of autohide or intellihide
+ // being temporary disable. Get set by _updateVisibilityMode;
+ this._autohideIsEnabled = null;
+ this._intellihideIsEnabled = null;
+
+ // This variable marks if _disableUnredirect() is called
+ // to help restore the original state when intelihide is disabled.
+ this._unredirectDisabled = false;
+
+ // Create intellihide object to monitor windows overlapping
+ this._intellihide = new Intellihide.Intellihide(this.monitorIndex);
+
+ // initialize dock state
+ this._dockState = State.HIDDEN;
+
+ // Put dock on the required monitor
+ this._monitor = Main.layoutManager.monitors[this.monitorIndex];
+
+ // this store size and the position where the dash is shown;
+ // used by intellihide module to check window overlap.
+ this.staticBox = new Clutter.ActorBox();
+
+ // Initialize pressure barrier variables
+ this._canUsePressure = false;
+ this._pressureBarrier = null;
+ this._barrier = null;
+ this._removeBarrierTimeoutId = 0;
+
+ // Initialize dwelling system variables
+ this._dockDwelling = false;
+ this._dockWatch = null;
+ this._dockDwellUserTime = 0;
+ this._dockDwellTimeoutId = 0;
+
+ // Create a new dash object
+ this.dash = new DockDash.DockDash(this.monitorIndex);
+
+ if (Main.overview.isDummy || !settings.showShowAppsButton)
+ this.dash.hideShowAppsButton();
+
+ // Create the containers for sliding in and out and
+ // centering, turn on track hover
+ // This is the sliding actor whose allocation is to be tracked for input regions
+ this._slider = new DashSlideContainer({
+ monitor_index: this._monitor.index,
+ side: this._position,
+ slide_x: Main.layoutManager._startingUp ? 0 : 1,
+ ...this._isHorizontal ? {
+ x_align: Clutter.ActorAlign.CENTER,
+ } : {
+ y_align: Clutter.ActorAlign.CENTER,
+ },
+ });
+
+ // This is the actor whose hover status us tracked for autohide
+ this._box = new St.BoxLayout({
+ name: 'dashtodockBox',
+ reactive: true,
+ track_hover: true,
+ });
+ this._box.connect('notify::hover', this._hoverChanged.bind(this));
+
+ // Connect global signals
+ this._signalsHandler = new Utils.GlobalSignalsHandler(this);
+ this._bindSettingsChanges();
+ this._signalsHandler.add([
+ // update when workarea changes, for instance if other extensions modify the struts
+ // (like moving th panel at the bottom)
+ global.display,
+ 'workareas-changed',
+ this._resetPosition.bind(this),
+ ], [
+ global.display,
+ 'in-fullscreen-changed',
+ this._updateBarrier.bind(this),
+ ], [
+ // Monitor windows overlapping
+ this._intellihide,
+ 'status-changed',
+ this._updateDashVisibility.bind(this),
+ ], [
+ this.dash,
+ 'menu-opened',
+ () => {
+ this._onMenuOpened();
+ },
+ ], [
+ // sync hover after a popupmenu is closed
+ this.dash,
+ 'menu-closed',
+ () => {
+ this._onMenuClosed();
+ },
+ ], [
+ this.dash,
+ 'notify::requires-visibility',
+ () => this._updateDashVisibility(),
+ ]);
+
+ if (!Main.overview.isDummy) {
+ this._signalsHandler.add([
+ Main.overview,
+ 'item-drag-begin',
+ this._onDragStart.bind(this),
+ ], [
+ Main.overview,
+ 'item-drag-end',
+ this._onDragEnd.bind(this),
+ ], [
+ Main.overview,
+ 'item-drag-cancelled',
+ this._onDragEnd.bind(this),
+ ], [
+ Main.overview,
+ 'showing',
+ this._onOverviewShowing.bind(this),
+ ], [
+ Main.overview,
+ 'hiding',
+ this._onOverviewHiding.bind(this),
+ ],
+ [
+ Main.overview,
+ 'hidden',
+ this._onOverviewHidden.bind(this),
+ ]);
+ }
+
+ this._themeManager = new Theming.ThemeManager(this);
+ this._signalsHandler.add(this._themeManager, 'updated',
+ () => this.dash.resetAppIcons());
+
+ this._signalsHandler.add(DockManager.iconTheme, 'changed',
+ () => this.dash.resetAppIcons());
+
+ // Since the actor is not a topLevel child and its parent is now not added to the Chrome,
+ // the allocation change of the parent container (slide in and slideout) doesn't trigger
+ // anymore an update of the input regions. Force the update manually.
+ this.connect('notify::allocation',
+ Main.layoutManager._queueUpdateRegions.bind(Main.layoutManager));
+
+
+ // Since Clutter has no longer ClutterAllocationFlags,
+ // "allocation-changed" signal has been removed. MR !1245
+ this.dash._container.connect('notify::allocation', this._updateStaticBox.bind(this));
+ this._slider.connect(this._isHorizontal ? 'notify::x' : 'notify::y',
+ this._updateStaticBox.bind(this));
+
+ // Load optional features that need to be activated for one dock only
+ if (this.isMain)
+ this._enableExtraFeatures();
+ // Load optional features that need to be activated once per dock
+ this._optionalScrollWorkspaceSwitch();
+
+ // Delay operations that require the shell to be fully loaded and with
+ // user theme applied.
+
+ this._signalsHandler.addWithLabel(Labels.INITIALIZE, global.stage,
+ 'after-paint', () => this._initialize());
+
+ // Add dash container actor and the container to the Chrome.
+ this.set_child(this._slider);
+ this._slider.set_child(this._box);
+ this._box.add_child(this.dash);
+
+ // Add aligning container without tracking it for input region
+ this._trackDock();
+
+ // Create and apply height/width constraint to the dash.
+ if (this._isHorizontal) {
+ this.connect('notify::width', () => {
+ this.dash.setMaxSize(this.width, this.height);
+ });
+ } else {
+ this.connect('notify::height', () => {
+ this.dash.setMaxSize(this.width, this.height);
+ });
+ }
+
+ if (this._position === St.Side.RIGHT) {
+ this.connect('notify::width', () =>
+ (this.translation_x = -this.width));
+ } else if (this._position === St.Side.BOTTOM) {
+ this.connect('notify::height', () =>
+ (this.translation_y = -this.height));
+ }
+
+ // Set initial position
+ this._resetPosition();
+
+ this.connect('destroy', this._onDestroy.bind(this));
+ }
+
+ get position() {
+ return this._position;
+ }
+
+ get isHorizontal() {
+ return this._isHorizontal;
+ }
+
+ _untrackDock() {
+ Main.layoutManager.untrackChrome(this);
+ }
+
+ _trackDock() {
+ if (DockManager.settings.dockFixed) {
+ if (this.get_parent())
+ Main.layoutManager.removeChrome(this);
+ Main.layoutManager.addChrome(this, {
+ trackFullscreen: true,
+ affectsStruts: true,
+ });
+ } else {
+ if (this.get_parent())
+ Main.layoutManager.removeChrome(this);
+ Main.layoutManager.addChrome(this);
+ }
+ }
+
+ _initialize() {
+ this._signalsHandler.removeWithLabel(Labels.INITIALIZE);
+
+ // Apply custom css class according to the settings
+ this._themeManager.updateCustomTheme();
+
+ this._updateVisibilityMode();
+
+ // In case we are already inside the overview when the extension is loaded,
+ // for instance on unlocking the screen if it was locked with the overview open.
+ if (Main.overview.visibleTarget)
+ this._onOverviewShowing();
+
+ this._updateAutoHideBarriers();
+ }
+
+ _onDestroy() {
+ // The dash, intellihide and themeManager have global signals as well internally
+ this.dash.destroy();
+ this._intellihide.destroy();
+ this._themeManager.destroy();
+
+ if (this._marginLater) {
+ Utils.laterRemove(this._marginLater);
+ delete this._marginLater;
+ }
+
+ if (this._triggerTimeoutId)
+ GLib.source_remove(this._triggerTimeoutId);
+
+ this._restoreUnredirect();
+
+ // Remove barrier timeout
+ if (this._removeBarrierTimeoutId > 0)
+ GLib.source_remove(this._removeBarrierTimeoutId);
+
+ // Remove existing barrier
+ this._removeBarrier();
+
+ // Remove pointer watcher
+ if (this._dockWatch) {
+ PointerWatcher.getPointerWatcher()._removeWatch(this._dockWatch);
+ this._dockWatch = null;
+ }
+ }
+
+ _updateAutoHideBarriers() {
+ // Remove pointer watcher
+ if (this._dockWatch) {
+ PointerWatcher.getPointerWatcher()._removeWatch(this._dockWatch);
+ this._dockWatch = null;
+ }
+
+ // Setup pressure barrier (GS38+ only)
+ this._updatePressureBarrier();
+ this._updateBarrier();
+
+ // setup dwelling system if pressure barriers are not available
+ this._setupDockDwellIfNeeded();
+ }
+
+ _bindSettingsChanges() {
+ const {settings} = DockManager;
+ this._signalsHandler.add([
+ settings,
+ 'changed::scroll-action',
+ () => {
+ this._optionalScrollWorkspaceSwitch();
+ },
+ ], [
+ settings,
+ 'changed::dash-max-icon-size',
+ () => {
+ this.dash.setIconSize(settings.dashMaxIconSize);
+ },
+ ], [
+ settings,
+ 'changed::icon-size-fixed',
+ () => {
+ this.dash.setIconSize(settings.dashMaxIconSize);
+ },
+ ], [
+ settings,
+ 'changed::show-favorites',
+ () => {
+ this.dash.resetAppIcons();
+ },
+ ], [
+ settings,
+ 'changed::show-trash',
+ () => {
+ this.dash.resetAppIcons();
+ },
+ Utils.SignalsHandlerFlags.CONNECT_AFTER,
+ ], [
+ settings,
+ 'changed::show-mounts',
+ () => {
+ this.dash.resetAppIcons();
+ },
+ Utils.SignalsHandlerFlags.CONNECT_AFTER,
+ ], [
+ settings,
+ 'changed::isolate-locations',
+ () => this.dash.resetAppIcons(),
+ Utils.SignalsHandlerFlags.CONNECT_AFTER,
+ ], [
+ settings,
+ 'changed::dance-urgent-applications',
+ () => this.dash.resetAppIcons(),
+ Utils.SignalsHandlerFlags.CONNECT_AFTER,
+ ], [
+ settings,
+ 'changed::show-running',
+ () => {
+ this.dash.resetAppIcons();
+ },
+ ], [
+ settings,
+ 'changed::show-apps-always-in-the-edge',
+ () => {
+ this.dash.updateShowAppsButton();
+ },
+ ], [
+ settings,
+ 'changed::show-apps-at-top',
+ () => {
+ this.dash.updateShowAppsButton();
+ },
+ ], [
+ settings,
+ 'changed::show-show-apps-button',
+ () => {
+ if (!Main.overview.isDummy &&
+ settings.showShowAppsButton)
+ this.dash.showShowAppsButton();
+ else
+ this.dash.hideShowAppsButton();
+ },
+ ], [
+ settings,
+ 'changed::dock-fixed',
+ () => {
+ this._untrackDock();
+ this._trackDock();
+
+ this._resetPosition();
+ this._updateAutoHideBarriers();
+ this._updateVisibilityMode();
+ },
+ ], [
+ settings,
+ 'changed::manualhide',
+ () => {
+ this._updateVisibilityMode();
+ },
+ ], [
+ settings,
+ 'changed::intellihide',
+ () => {
+ this._updateVisibilityMode();
+ this._updateVisibleDesktop();
+ },
+ ], [
+ settings,
+ 'changed::intellihide-mode',
+ () => {
+ this._intellihide.forceUpdate();
+ },
+ ], [
+ settings,
+ 'changed::autohide',
+ () => {
+ this._updateVisibilityMode();
+ this._updateAutoHideBarriers();
+ },
+ ], [
+ settings,
+ 'changed::autohide-in-fullscreen',
+ this._updateBarrier.bind(this),
+ ], [
+ settings,
+ 'changed::show-dock-urgent-notify',
+ () => {
+ this.dash.resetAppIcons();
+ },
+ ],
+ [
+ settings,
+ 'changed::extend-height',
+ this._resetPosition.bind(this),
+ ], [
+ settings,
+ 'changed::height-fraction',
+ this._resetPosition.bind(this),
+ ], [
+ settings,
+ 'changed::always-center-icons',
+ () => this.dash.resetAppIcons(),
+ ], [
+ settings,
+ 'changed::require-pressure-to-show',
+ () => this._updateAutoHideBarriers(),
+ ], [
+ settings,
+ 'changed::pressure-threshold',
+ () => {
+ this._updatePressureBarrier();
+ this._updateBarrier();
+ },
+ ]);
+ }
+
+ _disableUnredirect() {
+ if (!this._unredirectDisabled) {
+ if (Meta.disable_unredirect_for_display !== undefined)
+ Meta.disable_unredirect_for_display(global.display);
+ else if (global.compositor.disable_unredirect !== undefined)
+ global.compositor.disable_unredirect();
+ this._unredirectDisabled = true;
+ }
+ }
+
+ _restoreUnredirect() {
+ if (this._unredirectDisabled) {
+ if (Meta.enable_unredirect_for_display !== undefined)
+ Meta.enable_unredirect_for_display(global.display);
+ else if (global.compositor.enable_unredirect !== undefined)
+ global.compositor.enable_unredirect();
+ this._unredirectDisabled = false;
+ }
+ }
+
+ /**
+ * This is call when visibility settings change
+ */
+ _updateVisibilityMode() {
+ const {settings} = DockManager;
+ if (DockManager.settings.dockFixed || DockManager.settings.manualhide) {
+ this._autohideIsEnabled = false;
+ this._intellihideIsEnabled = false;
+ } else {
+ this._autohideIsEnabled = settings.autohide;
+ this._intellihideIsEnabled = settings.intellihide;
+ }
+
+ if (this._autohideIsEnabled)
+ this.add_style_class_name('autohide');
+ else
+ this.remove_style_class_name('autohide');
+
+ if (this._intellihideIsEnabled) {
+ this._intellihide.enable();
+ } else {
+ this._intellihide.disable();
+ this._restoreUnredirect();
+ }
+
+ this._updateDashVisibility();
+ }
+
+ /**
+ * Show/hide dash based on, in order of priority:
+ * overview visibility
+ * fixed mode
+ * intellihide
+ * autohide
+ * overview visibility
+ */
+ _updateDashVisibility() {
+ if (DockManager.settings.manualhide) {
+ this._ignoreHover = true;
+ this._removeAnimations();
+ this._animateOut(0, 0);
+ return;
+ }
+
+ if (Main.overview.visibleTarget)
+ return;
+
+ const {settings} = DockManager;
+
+ if (DockManager.settings.dockFixed) {
+ this._removeAnimations();
+ this._animateIn(settings.animationTime, 0);
+ } else if (this._intellihideIsEnabled) {
+ if (!this.dash.requiresVisibility && this._intellihide.getOverlapStatus()) {
+ this._ignoreHover = false;
+ // Do not hide if autohide is enabled and mouse is hover
+ if (!this._box.hover || !this._autohideIsEnabled)
+ this._animateOut(settings.animationTime, 0);
+ } else {
+ this._ignoreHover = true;
+ this._removeAnimations();
+ this._animateIn(settings.animationTime, 0);
+ }
+ } else if (this._autohideIsEnabled) {
+ this._ignoreHover = false;
+
+ if (this._box.hover || this.dash.requiresVisibility)
+ this._animateIn(settings.animationTime, 0);
+ else
+ this._animateOut(settings.animationTime, 0);
+ } else {
+ this._animateOut(settings.animationTime, 0);
+ }
+ }
+
+ _onOverviewShowing() {
+ this.add_style_class_name('overview');
+
+ this._ignoreHover = true;
+ this._intellihide.disable();
+ this._removeAnimations();
+ this._animateIn(DockManager.settings.animationTime, 0);
+ }
+
+ _onOverviewHiding() {
+ this._intellihide.enable();
+ this._updateDashVisibility();
+ }
+
+ _onOverviewHidden() {
+ this.remove_style_class_name('overview');
+ this._updateDashVisibility();
+ }
+
+ _onMenuOpened() {
+ this._ignoreHover = true;
+ }
+
+ _onMenuClosed() {
+ this._ignoreHover = false;
+ this._box.sync_hover();
+ this._updateDashVisibility();
+ }
+
+ _hoverChanged() {
+ if (!this._ignoreHover) {
+ // Skip if dock is not in autohide mode for instance because it is shown
+ // by intellihide.
+ if (this._autohideIsEnabled) {
+ if (this._box.hover || Main.overview.visible)
+ this._show();
+ else
+ this._hide();
+ }
+ }
+ }
+
+ getDockState() {
+ return this._dockState;
+ }
+
+ _show() {
+ this._delayedHide = false;
+ if ((this._dockState === State.HIDDEN) || (this._dockState === State.HIDING)) {
+ if (this._dockState === State.HIDING)
+ // suppress all potential queued transitions - i.e. added but not started,
+ // always give priority to show
+ this._removeAnimations();
+
+ this.emit('showing');
+ this._animateIn(DockManager.settings.animationTime, 0);
+ }
+ }
+
+ _hide() {
+ // If no hiding animation is running or queued
+ if ((this._dockState === State.SHOWN) || (this._dockState === State.SHOWING)) {
+ const {settings} = DockManager;
+ const delay = settings.hideDelay;
+
+ if (this._dockState === State.SHOWING) {
+ // if a show already started, let it finish; queue hide without removing the show.
+ // to obtain this, we wait for the animateIn animation to be completed
+ this._delayedHide = true;
+ return;
+ }
+
+ this.emit('hiding');
+ this._animateOut(settings.animationTime, delay);
+ }
+ }
+
+ _animateIn(time, delay) {
+ if (this._intellihideIsEnabled)
+ this._disableUnredirect();
+ this._dockState = State.SHOWING;
+ this.dash.iconAnimator.start();
+ this._delayedHide = false;
+
+ this._slider.ease_property('slide-x', 1, {
+ duration: time * 1000,
+ delay: delay * 1000,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ this._dockState = State.SHOWN;
+ // Remove barrier so that mouse pointer is released and can
+ // monitors on other side of dock.
+ // NOTE: Delay needed to keep mouse from moving past dock and
+ // re-hiding dock immediately. This gives users an opportunity
+ // to hover over the dock
+ if (this._removeBarrierTimeoutId > 0)
+ GLib.source_remove(this._removeBarrierTimeoutId);
+
+ if (!this._delayedHide) {
+ this._removeBarrierTimeoutId = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT, 100, this._removeBarrier.bind(this));
+ } else {
+ this._hide();
+ }
+ },
+ });
+ }
+
+ _animateOut(time, delay) {
+ this._dockState = State.HIDING;
+
+ this._slider.ease_property('slide-x', 0, {
+ duration: time * 1000,
+ delay: delay * 1000,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ this._dockState = State.HIDDEN;
+ if (this._intellihideIsEnabled)
+ this._restoreUnredirect();
+ // Remove queued barrier removal timeout if any
+ if (this._removeBarrierTimeoutId > 0)
+ GLib.source_remove(this._removeBarrierTimeoutId);
+ this._updateBarrier();
+ this.dash.iconAnimator.pause();
+ },
+ });
+ }
+
+ /**
+ * Dwelling system based on the GNOME Shell 3.14 messageTray code.
+ */
+ _setupDockDwellIfNeeded() {
+ // If we don't have extended barrier features, then we need
+ // to support the old tray dwelling mechanism.
+ if (this._autohideIsEnabled &&
+ (!Utils.supportsExtendedBarriers() ||
+ !DockManager.settings.requirePressureToShow)) {
+ const pointerWatcher = PointerWatcher.getPointerWatcher();
+ this._dockWatch = pointerWatcher.addWatch(
+ DOCK_DWELL_CHECK_INTERVAL, this._checkDockDwell.bind(this));
+ this._dockDwelling = false;
+ this._dockDwellUserTime = 0;
+ }
+ }
+
+ _checkDockDwell(x, y) {
+ const workArea = Main.layoutManager.getWorkAreaForMonitor(this._monitor.index);
+ let shouldDwell;
+ // Check for the correct screen edge, extending the sensitive area to the whole workarea,
+ // minus 1 px to avoid conflicting with other active corners.
+ if (this._position === St.Side.LEFT) {
+ shouldDwell = (x === this._monitor.x) && (y > workArea.y) &&
+ (y < workArea.y + workArea.height);
+ } else if (this._position === St.Side.RIGHT) {
+ shouldDwell = (x === this._monitor.x + this._monitor.width - 1) &&
+ (y > workArea.y) && (y < workArea.y + workArea.height);
+ } else if (this._position === St.Side.TOP) {
+ shouldDwell = (y === this._monitor.y) && (x > workArea.x) &&
+ (x < workArea.x + workArea.width);
+ } else if (this._position === St.Side.BOTTOM) {
+ shouldDwell = (y === this._monitor.y + this._monitor.height - 1) &&
+ (x > workArea.x) && (x < workArea.x + workArea.width);
+ }
+
+ if (shouldDwell) {
+ // We only set up dwell timeout when the user is not hovering over the dock
+ // already (!this._box.hover).
+ // The _dockDwelling variable is used so that we only try to
+ // fire off one dock dwell - if it fails (because, say, the user has the mouse down),
+ // we don't try again until the user moves the mouse up and down again.
+ if (!this._dockDwelling && !this._box.hover && (this._dockDwellTimeoutId === 0)) {
+ // Save the interaction timestamp so we can detect user input
+ const focusWindow = global.display.focus_window;
+ this._dockDwellUserTime = focusWindow ? focusWindow.user_time : 0;
+
+ this._dockDwellTimeoutId = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT,
+ DockManager.settings.showDelay * 1000,
+ this._dockDwellTimeout.bind(this));
+ GLib.Source.set_name_by_id(this._dockDwellTimeoutId,
+ '[dash-to-dock] this._dockDwellTimeout');
+ }
+ this._dockDwelling = true;
+ } else {
+ this._cancelDockDwell();
+ this._dockDwelling = false;
+ }
+ }
+
+ _cancelDockDwell() {
+ if (this._dockDwellTimeoutId !== 0) {
+ GLib.source_remove(this._dockDwellTimeoutId);
+ this._dockDwellTimeoutId = 0;
+ }
+ }
+
+ _dockDwellTimeout() {
+ this._dockDwellTimeoutId = 0;
+
+ if (!DockManager.settings.autohideInFullscreen &&
+ this._monitor.inFullscreen)
+ return GLib.SOURCE_REMOVE;
+
+ // We don't want to open the tray when a modal dialog
+ // is up, so we check the modal count for that. When we are in the
+ // overview we have to take the overview's modal push into account
+ if (Main.modalCount > (Main.overview.visible ? 1 : 0))
+ return GLib.SOURCE_REMOVE;
+
+ // If the user interacted with the focus window since we started the tray
+ // dwell (by clicking or typing), don't activate the message tray
+ const focusWindow = global.display.focus_window;
+ const currentUserTime = focusWindow ? focusWindow.user_time : 0;
+ if (currentUserTime !== this._dockDwellUserTime)
+ return GLib.SOURCE_REMOVE;
+
+ // Reuse the pressure version function, the logic is the same
+ this._onPressureSensed();
+ return GLib.SOURCE_REMOVE;
+ }
+
+ _updatePressureBarrier() {
+ const {settings} = DockManager;
+ this._canUsePressure = Utils.supportsExtendedBarriers();
+ const {pressureThreshold} = settings;
+
+ // Remove existing pressure barrier
+ if (this._pressureBarrier) {
+ this._pressureBarrier.destroy();
+ this._pressureBarrier = null;
+ }
+
+ if (this._barrier) {
+ this._barrier.destroy();
+ this._barrier = null;
+ }
+
+ // Create new pressure barrier based on pressure threshold setting
+ if (this._canUsePressure && this._autohideIsEnabled &&
+ DockManager.settings.requirePressureToShow) {
+ this._pressureBarrier = new Layout.PressureBarrier(
+ pressureThreshold, settings.showDelay * 1000,
+ Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW);
+ this._pressureBarrier.connect('trigger', _barrier => {
+ if (!settings.autohideInFullscreen && this._monitor.inFullscreen)
+ return;
+ this._onPressureSensed();
+ });
+ }
+ }
+
+ /**
+ * handler for mouse pressure sensed
+ */
+ _onPressureSensed() {
+ if (Main.overview.visibleTarget)
+ return;
+
+ if (this._triggerTimeoutId)
+ GLib.source_remove(this._triggerTimeoutId);
+
+ // In case the mouse move away from the dock area before hovering it,
+ // in such case the leave event would never be triggered and the dock
+ // would stay visible forever.
+ this._triggerTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 250, () => {
+ const [x, y, mods_] = global.get_pointer();
+ let shouldHide = true;
+ switch (this._position) {
+ case St.Side.LEFT:
+ if (x <= this.staticBox.x2 &&
+ x >= this._monitor.x &&
+ y >= this._monitor.y &&
+ y <= this._monitor.y + this._monitor.height)
+ shouldHide = false;
+
+ break;
+ case St.Side.RIGHT:
+ if (x >= this.staticBox.x1 &&
+ x <= this._monitor.x + this._monitor.width &&
+ y >= this._monitor.y &&
+ y <= this._monitor.y + this._monitor.height)
+ shouldHide = false;
+
+ break;
+ case St.Side.TOP:
+ if (x >= this._monitor.x &&
+ x <= this._monitor.x + this._monitor.width &&
+ y <= this.staticBox.y2 &&
+ y >= this._monitor.y)
+ shouldHide = false;
+
+ break;
+ case St.Side.BOTTOM:
+ if (x >= this._monitor.x &&
+ x <= this._monitor.x + this._monitor.width &&
+ y >= this.staticBox.y1 &&
+ y <= this._monitor.y + this._monitor.height)
+ shouldHide = false;
+ }
+ if (shouldHide) {
+ this._triggerTimeoutId = 0;
+ this._hoverChanged();
+ return GLib.SOURCE_REMOVE;
+ } else {
+ return GLib.SOURCE_CONTINUE;
+ }
+ });
+
+ this._show();
+ }
+
+ /**
+ * Remove pressure barrier
+ */
+ _removeBarrier() {
+ if (this._barrier) {
+ if (this._pressureBarrier)
+ this._pressureBarrier.removeBarrier(this._barrier);
+ this._barrier.destroy();
+ this._barrier = null;
+ }
+ this._removeBarrierTimeoutId = 0;
+ return false;
+ }
+
+ /**
+ * Update pressure barrier size
+ */
+ _updateBarrier() {
+ // Remove existing barrier
+ this._removeBarrier();
+
+ // The barrier needs to be removed in fullscreen with autohide disabled
+ // otherwise the mouse can get trapped on monitor.
+ if (this._monitor.inFullscreen &&
+ !DockManager.settings.autohideInFullscreen)
+ return;
+
+ // Manually reset pressure barrier
+ // This is necessary because we remove the pressure barrier when it is
+ // triggered to show the dock
+ if (this._pressureBarrier) {
+ this._pressureBarrier._reset();
+ this._pressureBarrier._isTriggered = false;
+ }
+
+ // Create new barrier
+ // The barrier extends to the whole workarea, minus 1 px to avoid
+ // conflicting with other active corners
+ // Note: dash in fixed position doesn't use pressure barrier.
+ if (this._canUsePressure && this._autohideIsEnabled &&
+ DockManager.settings.requirePressureToShow) {
+ let x1, x2, y1, y2, direction;
+ const workArea = Main.layoutManager.getWorkAreaForMonitor(
+ this._monitor.index);
+
+ if (this._position === St.Side.LEFT) {
+ x1 = this._monitor.x + 1;
+ x2 = x1;
+ y1 = workArea.y + 1;
+ y2 = workArea.y + workArea.height - 1;
+ direction = Meta.BarrierDirection.POSITIVE_X;
+ } else if (this._position === St.Side.RIGHT) {
+ x1 = this._monitor.x + this._monitor.width - 1;
+ x2 = x1;
+ y1 = workArea.y + 1;
+ y2 = workArea.y + workArea.height - 1;
+ direction = Meta.BarrierDirection.NEGATIVE_X;
+ } else if (this._position === St.Side.TOP) {
+ x1 = workArea.x + 1;
+ x2 = workArea.x + workArea.width - 1;
+ y1 = this._monitor.y;
+ y2 = y1;
+ direction = Meta.BarrierDirection.POSITIVE_Y;
+ } else if (this._position === St.Side.BOTTOM) {
+ x1 = workArea.x + 1;
+ x2 = workArea.x + workArea.width - 1;
+ y1 = this._monitor.y + this._monitor.height;
+ y2 = y1;
+ direction = Meta.BarrierDirection.NEGATIVE_Y;
+ }
+
+ if (this._pressureBarrier && this._dockState === State.HIDDEN) {
+ this._barrier = new Meta.Barrier({
+ backend: global.backend,
+ x1,
+ x2,
+ y1,
+ y2,
+ directions: direction,
+ });
+ this._pressureBarrier.addBarrier(this._barrier);
+ }
+ }
+ }
+
+ _isPrimaryMonitor() {
+ return this.monitorIndex === Main.layoutManager.primaryIndex;
+ }
+
+ _resetPosition() {
+ // Ensure variables linked to settings are updated.
+ this._updateVisibilityMode();
+
+ const {dockFixed: fixedIsEnabled, dockExtended: extendHeight} = DockManager.settings;
+
+ if (fixedIsEnabled)
+ this.add_style_class_name('fixed');
+ else
+ this.remove_style_class_name('fixed');
+
+ // Note: do not use the workarea coordinates in the direction on which the dock is placed,
+ // to avoid a loop [position change -> workArea change -> position change] with
+ // fixed dock.
+ const workArea = Main.layoutManager.getWorkAreaForMonitor(this.monitorIndex);
+
+ let fraction = DockManager.settings.heightFraction;
+ if (extendHeight)
+ fraction = 1;
+ else if ((fraction < 0) || (fraction > 1))
+ fraction = 0.95;
+
+ if (this._isHorizontal) {
+ this.width = Math.round(fraction * workArea.width);
+
+ let posY = this._monitor.y;
+ if (this._position === St.Side.BOTTOM)
+ posY += this._monitor.height;
+
+ this.x = workArea.x + Math.round((1 - fraction) / 2 * workArea.width);
+ this.y = posY;
+
+ if (extendHeight) {
+ this.dash._container.set_width(this.width);
+ this.add_style_class_name('extended');
+ } else {
+ this.dash._container.set_width(-1);
+ this.remove_style_class_name('extended');
+ }
+ } else {
+ this.height = Math.round(fraction * workArea.height);
+
+ let posX = this._monitor.x;
+ if (this._position === St.Side.RIGHT)
+ posX += this._monitor.width;
+
+ this.x = posX;
+ this.y = workArea.y + Math.round((1 - fraction) / 2 * workArea.height);
+
+ if (extendHeight) {
+ this.dash._container.set_height(this.height);
+ this.add_style_class_name('extended');
+ } else {
+ this.dash._container.set_height(-1);
+ this.remove_style_class_name('extended');
+ }
+ }
+ }
+
+ _updateVisibleDesktop() {
+ if (!this._intellihideIsEnabled)
+ return;
+
+ const {desktopIconsUsableArea} = DockManager.getDefault();
+ if (this._position === St.Side.BOTTOM)
+ desktopIconsUsableArea.setMargins(this.monitorIndex, 0, this._box.height, 0, 0);
+ else if (this._position === St.Side.TOP)
+ desktopIconsUsableArea.setMargins(this.monitorIndex, this._box.height, 0, 0, 0);
+ else if (this._position === St.Side.RIGHT)
+ desktopIconsUsableArea.setMargins(this.monitorIndex, 0, 0, 0, this._box.width);
+ else if (this._position === St.Side.LEFT)
+ desktopIconsUsableArea.setMargins(this.monitorIndex, 0, 0, this._box.width, 0);
+ }
+
+ _updateStaticBox() {
+ this.staticBox.init_rect(
+ this.x + this._slider.x - (this._position === St.Side.RIGHT ? this._box.width : 0),
+ this.y + this._slider.y - (this._position === St.Side.BOTTOM ? this._box.height : 0),
+ this._box.width,
+ this._box.height
+ );
+
+ this._intellihide.updateTargetBox(this.staticBox);
+ this._updateVisibleDesktop();
+ }
+
+ _removeAnimations() {
+ this._slider.remove_all_transitions();
+ }
+
+ _onDragStart() {
+ this._oldIgnoreHover = this._ignoreHover;
+ this._ignoreHover = true;
+ this._animateIn(DockManager.settings.animationTime, 0);
+ }
+
+ _onDragEnd() {
+ if (this._oldIgnoreHover)
+ this._ignoreHover = this._oldIgnoreHover;
+ this._oldIgnoreHover = null;
+ this._box.sync_hover();
+ this._updateDashVisibility();
+ }
+
+ /**
+ * Show dock and give key focus to it
+ */
+ _onAccessibilityFocus() {
+ this._box.navigate_focus(null, St.DirectionType.TAB_FORWARD, false);
+ this._animateIn(DockManager.settings.animationTime, 0);
+ }
+
+ // Optional features to be enabled only for the main Dock
+ _enableExtraFeatures() {
+ // Restore dash accessibility
+ Main.ctrlAltTabManager.addGroup(
+ this.dash, _('Dash'), 'user-bookmarks-symbolic',
+ {focusCallback: this._onAccessibilityFocus.bind(this)});
+ }
+
+ /**
+ * Switch workspace by scrolling over the dock
+ */
+ _optionalScrollWorkspaceSwitch() {
+ const isEnabled = () =>
+ DockManager.settings.scrollAction === scrollAction.SWITCH_WORKSPACE;
+
+ const enable = () => {
+ this._signalsHandler.removeWithLabel(Labels.WORKSPACE_SWITCH_SCROLL);
+
+ this._signalsHandler.addWithLabel(Labels.WORKSPACE_SWITCH_SCROLL,
+ this._box, 'scroll-event', (_, e) => onScrollEvent(e));
+ };
+
+ const disable = () => {
+ this._signalsHandler.removeWithLabel(Labels.WORKSPACE_SWITCH_SCROLL);
+
+ if (this._optionalScrollWorkspaceSwitchDeadTimeId) {
+ GLib.source_remove(this._optionalScrollWorkspaceSwitchDeadTimeId);
+ this._optionalScrollWorkspaceSwitchDeadTimeId = 0;
+ }
+ };
+
+ DockManager.settings.connect('changed::scroll-action', () => {
+ if (isEnabled())
+ enable();
+ else
+ disable();
+ });
+
+ if (isEnabled())
+ enable();
+
+ // This was inspired to desktop-scroller@obsidien.github.com
+ const onScrollEvent = event => {
+ // When in overview change workspace only in windows view
+ if (Main.overview.visible)
+ return false;
+
+ const activeWs = global.workspace_manager.get_active_workspace();
+ let direction = null;
+
+ let prevDirection, nextDirection;
+ if (global.workspace_manager.layout_columns > global.workspace_manager.layout_rows) {
+ prevDirection = Meta.MotionDirection.UP;
+ nextDirection = Meta.MotionDirection.DOWN;
+ } else {
+ prevDirection = Meta.MotionDirection.LEFT;
+ nextDirection = Meta.MotionDirection.RIGHT;
+ }
+
+ switch (event.get_scroll_direction()) {
+ case Clutter.ScrollDirection.UP:
+ direction = prevDirection;
+ break;
+ case Clutter.ScrollDirection.DOWN:
+ direction = nextDirection;
+ break;
+ case Clutter.ScrollDirection.SMOOTH: {
+ const [dx_, dy] = event.get_scroll_delta();
+ if (dy < 0)
+ direction = prevDirection;
+ else if (dy > 0)
+ direction = nextDirection;
+ }
+ break;
+ }
+
+ if (direction) {
+ // Prevent scroll events from triggering too many workspace switches
+ // by adding a 250ms dead time between each scroll event.
+ // Useful on laptops when using a touch pad.
+
+ // During the deadtime do nothing
+ if (this._optionalScrollWorkspaceSwitchDeadTimeId) {
+ return false;
+ } else {
+ this._optionalScrollWorkspaceSwitchDeadTimeId = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT, 250, () => {
+ this._optionalScrollWorkspaceSwitchDeadTimeId = 0;
+ });
+ }
+
+ let ws;
+
+ ws = activeWs.get_neighbor(direction);
+
+ if (!Main.wm._workspaceSwitcherPopup) {
+ // Support Workspace Grid extension showing their custom
+ // Grid Workspace Switcher
+ if (global.workspace_manager.workspace_grid !== undefined) {
+ Main.wm._workspaceSwitcherPopup =
+ global.workspace_manager.workspace_grid.getWorkspaceSwitcherPopup();
+ } else {
+ Main.wm._workspaceSwitcherPopup = new WorkspaceSwitcherPopup.WorkspaceSwitcherPopup();
+ }
+ }
+ // Set the actor non reactive, so that it doesn't prevent the
+ // clicks events from reaching the dash actor. I can't see a reason
+ // why it should be reactive.
+ Main.wm._workspaceSwitcherPopup.reactive = false;
+ Main.wm._workspaceSwitcherPopup.connect('destroy', () => {
+ Main.wm._workspaceSwitcherPopup = null;
+ });
+
+ // If Workspace Grid is installed, let them handle the scroll behavior.
+ if (global.workspace_manager.workspace_grid !== undefined)
+ ws = global.workspace_manager.workspace_grid.actionMoveWorkspace(direction);
+ else
+ Main.wm.actionMoveWorkspace(ws);
+
+ // Do not show workspaceSwitcher in overview
+ if (!Main.overview.visible)
+ Main.wm._workspaceSwitcherPopup.display(ws.index());
+
+ return true;
+ } else {
+ return false;
+ }
+ };
+ }
+
+ _activateApp(appIndex) {
+ const children = this.dash._box.get_children().filter(actor => {
+ return actor.child &&
+ actor.child.app;
+ });
+
+ // Apps currently in the dash
+ const apps = children.map(actor => {
+ return actor.child;
+ });
+
+ // Activate with button = 1, i.e. same as left click
+ const button = 1;
+ if (appIndex < apps.length)
+ apps[appIndex].activate(button);
+ }
+});
+
+/*
+ * Handle keyboard shortcuts
+ */
+const NUM_HOTKEYS = 10;
+
+const KeyboardShortcuts = class DashToDockKeyboardShortcuts {
+ constructor() {
+ this._signalsHandler = new Utils.GlobalSignalsHandler();
+
+ this._hotKeysEnabled = false;
+ if (DockManager.settings.hotKeys)
+ this._enableHotKeys();
+
+ this._signalsHandler.add([
+ DockManager.settings,
+ 'changed::hot-keys',
+ () => {
+ if (DockManager.settings.hotKeys)
+ this._enableHotKeys.bind(this)();
+ else
+ this._disableHotKeys.bind(this)();
+ },
+ ]);
+
+ this._optionalNumberOverlay();
+ }
+
+ destroy() {
+ DockManager.allDocks.forEach(dock => {
+ if (dock._numberOverlayTimeoutId) {
+ GLib.source_remove(dock._numberOverlayTimeoutId);
+ dock._numberOverlayTimeoutId = 0;
+ }
+ });
+
+ // Remove keybindings
+ this._disableHotKeys();
+ this._disableExtraShortcut();
+ this._signalsHandler.destroy();
+ }
+
+ _enableHotKeys() {
+ if (this._hotKeysEnabled)
+ return;
+
+ // Setup keyboard bindings for dash elements
+ const keys = ['app-hotkey-', 'app-shift-hotkey-', 'app-ctrl-hotkey-'];
+ const {mainDock} = DockManager.getDefault();
+ keys.forEach(function (key) {
+ for (let i = 0; i < NUM_HOTKEYS; i++) {
+ const appNum = i;
+ Main.wm.addKeybinding(key + (i + 1), DockManager.settings,
+ Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
+ Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW,
+ () => {
+ mainDock._activateApp(appNum);
+ this._showOverlay();
+ });
+ }
+ }, this);
+
+ this._hotKeysEnabled = true;
+ }
+
+ _disableHotKeys() {
+ if (!this._hotKeysEnabled)
+ return;
+
+ const keys = ['app-hotkey-', 'app-shift-hotkey-', 'app-ctrl-hotkey-'];
+ keys.forEach(key => {
+ for (let i = 0; i < NUM_HOTKEYS; i++)
+ Main.wm.removeKeybinding(key + (i + 1));
+ }, this);
+
+ this._hotKeysEnabled = false;
+ }
+
+ _optionalNumberOverlay() {
+ const {settings} = DockManager;
+ this._shortcutIsSet = false;
+ // Enable extra shortcut if either 'overlay' or 'show-dock' are true
+ if (settings.hotKeys &&
+ (settings.hotkeysOverlay || settings.hotkeysShowDock))
+ this._enableExtraShortcut();
+
+ this._signalsHandler.add([
+ settings,
+ 'changed::hot-keys',
+ this._checkHotkeysOptions.bind(this),
+ ], [
+ settings,
+ 'changed::hotkeys-overlay',
+ this._checkHotkeysOptions.bind(this),
+ ], [
+ settings,
+ 'changed::hotkeys-show-dock',
+ this._checkHotkeysOptions.bind(this),
+ ]);
+ }
+
+ _checkHotkeysOptions() {
+ const {settings} = DockManager;
+
+ if (settings.hotKeys &&
+ (settings.hotkeysOverlay || settings.hotkeysShowDock))
+ this._enableExtraShortcut();
+ else
+ this._disableExtraShortcut();
+ }
+
+ _enableExtraShortcut() {
+ if (!this._shortcutIsSet) {
+ Main.wm.addKeybinding('shortcut', DockManager.settings,
+ Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
+ Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW,
+ this._showOverlay.bind(this));
+ this._shortcutIsSet = true;
+ }
+ }
+
+ _disableExtraShortcut() {
+ if (this._shortcutIsSet) {
+ Main.wm.removeKeybinding('shortcut');
+ this._shortcutIsSet = false;
+ }
+ }
+
+ _showOverlay() {
+ for (const dock of DockManager.allDocks) {
+ if (DockManager.settings.hotkeysOverlay)
+ dock.dash.toggleNumberOverlay(true);
+
+ // Restart the counting if the shortcut is pressed again
+ if (dock._numberOverlayTimeoutId) {
+ GLib.source_remove(dock._numberOverlayTimeoutId);
+ dock._numberOverlayTimeoutId = 0;
+ }
+
+ // Hide the overlay/dock after the timeout
+ const timeout = DockManager.settings.shortcutTimeout * 1000;
+ dock._numberOverlayTimeoutId = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT, timeout, () => {
+ dock._numberOverlayTimeoutId = 0;
+ dock.dash.toggleNumberOverlay(false);
+ // Hide the dock again if necessary
+ dock._updateDashVisibility();
+ });
+
+ // Show the dock if it is hidden
+ if (DockManager.settings.hotkeysShowDock) {
+ const showDock = dock._intellihideIsEnabled || dock._autohideIsEnabled;
+ if (showDock)
+ dock._show();
+ }
+ }
+ }
+};
+
+/**
+ * Isolate overview to open new windows for inactive apps
+ * Note: the future implementation is not fully contained here.
+ * Some bits are around in other methods of other classes.
+ * This class just take care of enabling/disabling the option.
+ */
+const WorkspaceIsolation = class DashToDockWorkspaceIsolation {
+ constructor() {
+ const {settings} = DockManager;
+
+ this._signalsHandler = new Utils.GlobalSignalsHandler();
+ this._injectionsHandler = new Utils.InjectionsHandler();
+
+ const updateAllDocks = () => {
+ DockManager.allDocks.forEach(dock =>
+ dock.dash.resetAppIcons());
+ if (settings.isolateWorkspaces ||
+ settings.isolateMonitors)
+ this._enable.bind(this)();
+ else
+ this._disable.bind(this)();
+ };
+ this._signalsHandler.add(
+ [settings, 'changed::isolate-workspaces', updateAllDocks],
+ [settings, 'changed::workspace-agnostic-urgent-windows', updateAllDocks],
+ [settings, 'changed::isolate-monitors', updateAllDocks]
+ );
+
+ if (settings.isolateWorkspaces ||
+ settings.isolateMonitors)
+ this._enable();
+ }
+
+ _enable() {
+ // ensure I never double-register/inject
+ // although it should never happen
+ this._disable();
+
+ DockManager.allDocks.forEach(dock => {
+ this._signalsHandler.addWithLabel(
+ Labels.ISOLATION,
+ [global.display, 'restacked', () => dock.dash._queueRedisplay()],
+ [global.display, 'window-marked-urgent', () => dock.dash._queueRedisplay()],
+ [global.display, 'window-demands-attention', () => dock.dash._queueRedisplay()],
+ [global.window_manager, 'switch-workspace', () => dock.dash._queueRedisplay()]
+ );
+
+ // This last signal is only needed for monitor isolation, as windows
+ // might migrate from one monitor to another without triggering 'restacked'
+ if (DockManager.settings.isolateMonitors) {
+ this._signalsHandler.addWithLabel(Labels.ISOLATION,
+ global.display,
+ 'window-entered-monitor',
+ dock.dash._queueRedisplay.bind(dock.dash));
+ }
+ }, this);
+
+ /**
+ * here this is the Shell.App
+ */
+ function IsolatedOverview() {
+ // These lines take care of Nautilus for icons on Desktop
+ const activeWorkspaceIndex =
+ global.workspaceManager.get_active_workspace_index();
+ const windows = this.get_windows().filter(w =>
+ !w.skipTaskbar && w.get_workspace().index() === activeWorkspaceIndex);
+
+ if (windows.length)
+ return Main.activateWindow(windows[0]);
+ return this.open_new_window(-1);
+ }
+
+ this._injectionsHandler.addWithLabel(Labels.ISOLATION,
+ Shell.App.prototype,
+ 'activate',
+ IsolatedOverview);
+ }
+
+ _disable() {
+ this._signalsHandler.removeWithLabel(Labels.ISOLATION);
+ this._injectionsHandler.removeWithLabel(Labels.ISOLATION);
+ }
+
+ destroy() {
+ this._signalsHandler.destroy();
+ this._injectionsHandler.destroy();
+ }
+};
+
+
+export class DockManager {
+ constructor(extension) {
+ if (DockManager._singleton)
+ throw new Error('DashToDock has been already initialized');
+ DockManager._singleton = this;
+ this._extension = extension;
+ this._signalsHandler = new Utils.GlobalSignalsHandler(this);
+ this._methodInjections = new Utils.InjectionsHandler(this);
+ this._vfuncInjections = new Utils.VFuncInjectionsHandler(this);
+ this._propertyInjections = new Utils.PropertyInjectionsHandler(this);
+ this._settings = this._extension.getSettings(
+ 'org.gnome.shell.extensions.dash-to-dock');
+ this._appSwitcherSettings = new Gio.Settings({schema_id: 'org.gnome.shell.app-switcher'});
+ this._mapSettingsValues();
+
+ this._iconTheme = new St.IconTheme();
+
+ this._desktopIconsUsableArea = new DesktopIconsIntegration.DesktopIconsUsableAreaClass();
+ this._oldDash = Main.overview.isDummy ? null : Main.overview.dash;
+ this._discreteGpuAvailable = AppDisplay.discreteGpuAvailable;
+ this._appSpread = new AppSpread.AppSpread();
+ this._notificationsMonitor = new NotificationsMonitor.NotificationsMonitor();
+
+ const needsRemoteModel = () =>
+ !this._notificationsMonitor.dndMode && this._settings.showIconsEmblems;
+
+ const ensureRemoteModel = () => {
+ if (needsRemoteModel && !this._remoteModel) {
+ this._remoteModel = new LauncherAPI.LauncherEntryRemoteModel();
+ this._appIconsDecorator = new AppIconsDecorator.AppIconsDecorator();
+ } else if (!needsRemoteModel) {
+ this._remoteModel?.destroy();
+ delete this._remoteModel;
+ this._appIconsDecorator?.destroy();
+ delete this._appIconsDecorator;
+ }
+ };
+ ensureRemoteModel();
+
+ this._notificationsMonitor.connect('changed', ensureRemoteModel);
+ this._settings.connect('changed::show-icons-emblems', ensureRemoteModel);
+
+ if (this._discreteGpuAvailable === undefined) {
+ const updateDiscreteGpuAvailable = () => {
+ const switcherooProxy = global.get_switcheroo_control();
+ if (switcherooProxy) {
+ const prop = switcherooProxy.get_cached_property('HasDualGpu');
+ this._discreteGpuAvailable = prop?.unpack() ?? false;
+ } else {
+ this._discreteGpuAvailable = false;
+ }
+ };
+ this._signalsHandler.add(global, 'notify::switcheroo-control',
+ () => updateDiscreteGpuAvailable());
+ updateDiscreteGpuAvailable();
+ }
+
+ // Connect relevant signals to the toggling function
+ this._bindSettingsChanges();
+
+ this._ensureLocations();
+
+ /* Array of all the docks created */
+ this._allDocks = [];
+ this._createDocks();
+
+ this._overrideAppMenus();
+
+ // status variable: true when the overview is shown through the dash
+ // applications button.
+ this._forcedOverview = false;
+ }
+
+ static getDefault() {
+ return DockManager._singleton;
+ }
+
+ static get allDocks() {
+ return DockManager.getDefault()._allDocks;
+ }
+
+ static get extension() {
+ return DockManager.getDefault().extension;
+ }
+
+ static get settings() {
+ return DockManager.getDefault().settings;
+ }
+
+ get extension() {
+ return this._extension;
+ }
+
+ get settings() {
+ return this._settings;
+ }
+
+ static get iconTheme() {
+ return DockManager.getDefault().iconTheme;
+ }
+
+ get settings() { // eslint-disable-line no-dupe-class-members
+ return this._settings;
+ }
+
+ get iconTheme() {
+ return this._iconTheme;
+ }
+
+ get fm1Client() {
+ return this._fm1Client;
+ }
+
+ get remoteModel() {
+ return this._remoteModel;
+ }
+
+ get mainDock() {
+ return this._allDocks.length ? this._allDocks[0] : null;
+ }
+
+ get removables() {
+ return this._removables;
+ }
+
+ get trash() {
+ return this._trash;
+ }
+
+ get desktopIconsUsableArea() {
+ return this._desktopIconsUsableArea;
+ }
+
+ get discreteGpuAvailable() {
+ return AppDisplay.discreteGpuAvailable || this._discreteGpuAvailable;
+ }
+
+ get appSpread() {
+ return this._appSpread;
+ }
+
+ get notificationsMonitor() {
+ return this._notificationsMonitor;
+ }
+
+ getDockByMonitor(monitorIndex) {
+ return this._allDocks.find(d => d.monitorIndex === monitorIndex);
+ }
+
+ _ensureLocations() {
+ const {showMounts, showTrash} = this.settings;
+
+ if (showTrash || showMounts) {
+ if (!this._fm1Client)
+ this._fm1Client = new FileManager1API.FileManager1Client();
+ } else if (this._fm1Client) {
+ this._fm1Client.destroy();
+ this._fm1Client = null;
+ }
+
+ if (showMounts && !this._removables) {
+ this._removables = new Locations.Removables();
+ } else if (!showMounts && this._removables) {
+ this._removables.destroy();
+ this._removables = null;
+ }
+
+ if (showTrash && !this._trash) {
+ this._trash = new Locations.Trash();
+ } else if (!showTrash && this._trash) {
+ this._trash.destroy();
+ this._trash = null;
+ }
+
+ Locations.unWrapFileManagerApp();
+ [this._methodInjections, this._propertyInjections].forEach(
+ injections => injections.removeWithLabel(Labels.LOCATIONS));
+
+ if (showMounts || showTrash) {
+ if (this.settings.isolateLocations) {
+ const fileManagerApp = Locations.wrapFileManagerApp();
+
+ this._methodInjections.addWithLabel(Labels.LOCATIONS, [
+ Shell.AppSystem.prototype, 'get_running',
+ function (originalMethod, ...args) {
+ /* eslint-disable no-invalid-this */
+ const runningApps = originalMethod.call(this, ...args);
+ const locationApps = Locations.getRunningApps();
+ if (!locationApps.length)
+ return runningApps;
+
+ const fileManagerIdx = runningApps.indexOf(fileManagerApp);
+ if (fileManagerIdx > -1 && fileManagerApp?.state !== Shell.AppState.RUNNING)
+ runningApps.splice(fileManagerIdx, 1);
+
+ return [...runningApps, ...locationApps].sort(Utils.shellAppCompare);
+ /* eslint-enable no-invalid-this */
+ },
+ ],
+ [
+ Shell.WindowTracker.prototype, 'get_window_app',
+ function (originalMethod, window) {
+ /* eslint-disable no-invalid-this */
+ const locationApp = Locations.getRunningApps().find(a =>
+ a.get_windows().includes(window));
+ return locationApp ?? originalMethod.call(this, window);
+ /* eslint-enable no-invalid-this */
+ },
+ ],
+ [
+ Shell.WindowTracker.prototype, 'get_app_from_pid',
+ function (originalMethod, pid) {
+ /* eslint-disable no-invalid-this */
+ const locationApp = Locations.getRunningApps().find(a =>
+ a.get_pids().includes(pid));
+ return locationApp ?? originalMethod.call(this, pid);
+ /* eslint-enable no-invalid-this */
+ },
+ ]);
+
+ const {get: defaultFocusAppGetter} = Object.getOwnPropertyDescriptor(
+ Shell.WindowTracker.prototype, 'focus_app');
+ this._propertyInjections.addWithLabel(Labels.LOCATIONS,
+ Shell.WindowTracker.prototype, 'focus_app', {
+ get() {
+ const locationApp = Locations.getRunningApps().find(a => a.isFocused);
+ return locationApp ?? defaultFocusAppGetter.call(this);
+ },
+ });
+ }
+ }
+ }
+
+ _toggle() {
+ if (this._toggleLater)
+ return;
+
+ this._toggleLater = Utils.laterAdd(Meta.LaterType.BEFORE_REDRAW, () => {
+ delete this._toggleLater;
+ this._restoreDash();
+ this._deleteDocks();
+ this._createDocks();
+ this.emit('toggled');
+ });
+ }
+
+ _mapExternalSetting(settings, key, mappedKey, mapValueFunction) {
+ const camelMappedKey = mappedKey.replace(/-([a-z\d])/g, k => k[1].toUpperCase());
+
+ const dockPropertyDesc = Object.getOwnPropertyDescriptor(this.settings, camelMappedKey);
+
+ if (!dockPropertyDesc)
+ throw new Error('Setting %s not found in dock'.format(mappedKey));
+
+ const mappedValue = () => mapValueFunction(settings.get_value(key).recursiveUnpack());
+ Object.defineProperty(this.settings, camelMappedKey, {
+ get: () => mappedValue() ?? dockPropertyDesc.value,
+ set: value => {
+ if (mappedValue())
+ dockPropertyDesc.value = value;
+ },
+ });
+
+ this._signalsHandler.addWithLabel(Labels.SETTINGS, settings,
+ 'changed::%s'.format(key), () => {
+ this._signalsHandler.blockWithLabel(Labels.SETTINGS);
+ this.settings.emit('changed::%s'.format(mappedKey), mappedKey);
+ this._signalsHandler.unblockWithLabel(Labels.SETTINGS);
+ });
+ }
+
+ _mapSettingsValues() {
+ this.settings.settingsSchema.list_keys().forEach(key => {
+ const camelKey = key.replace(/-([a-z\d])/g, k => k[1].toUpperCase());
+ const updateSetting = () => {
+ const schemaKey = this.settings.settingsSchema.get_key(key);
+ if (schemaKey.get_range().deepUnpack()[0] === 'enum')
+ this.settings[camelKey] = this.settings.get_enum(key);
+ else
+ this.settings[camelKey] = this.settings.get_value(key).recursiveUnpack();
+ };
+ updateSetting();
+ this._signalsHandler.addWithLabel(Labels.SETTINGS, this.settings,
+ `changed::${key}`, updateSetting);
+ if (key !== camelKey) {
+ Object.defineProperty(this.settings, key,
+ {get: () => this.settings[camelKey]});
+ }
+ });
+ Object.defineProperties(this.settings, {
+ dockExtended: {get: () => this.settings.extendHeight},
+ });
+ }
+
+ _bindSettingsChanges() {
+ // Connect relevant signals to the toggling function
+ this._signalsHandler.addWithLabel(Labels.SETTINGS, [
+ Utils.getMonitorManager(),
+ 'monitors-changed',
+ this._toggle.bind(this),
+ ], [
+ Main.sessionMode,
+ 'updated',
+ this._toggle.bind(this),
+ ], [
+ this._settings,
+ 'changed::multi-monitor',
+ this._toggle.bind(this),
+ ], [
+ this._settings,
+ 'changed::preferred-monitor',
+ this._toggle.bind(this),
+ ], [
+ this._settings,
+ 'changed::preferred-monitor-by-connector',
+ this._toggle.bind(this),
+ ], [
+ this._settings,
+ 'changed::dock-position',
+ this._toggle.bind(this),
+ ], [
+ this._settings,
+ 'changed::extend-height',
+ () => this._adjustPanelCorners(),
+ ], [
+ this._settings,
+ 'changed::dock-fixed',
+ () => this._adjustPanelCorners(),
+ ], [
+ this._settings,
+ 'changed::show-trash',
+ () => this._ensureLocations(),
+ ], [
+ this._settings,
+ 'changed::show-mounts',
+ () => this._ensureLocations(),
+ ], [
+ this._settings,
+ 'changed::isolate-locations',
+ () => this._ensureLocations(),
+ ], [
+ this._settings,
+ 'changed::intellihide',
+ () => {
+ if (!this._settings.intellihide)
+ this._desktopIconsUsableArea.resetMargins();
+ },
+ ]);
+
+ this._mapExternalSetting(this._appSwitcherSettings, 'current-workspace-only',
+ 'isolate-workspaces', value => value || undefined);
+ }
+
+ _createDocks() {
+ // If there are no monitors (headless configurations, but it can also
+ // happen temporary while disconnecting and reconnecting monitors), just
+ // do nothing. When a monitor will be connected we we'll be notified and
+ // and thus create the docks. This prevents pointing trying to access
+ // monitors throughout the code, were we are assuming that at least the
+ // primary monitor is present.
+ if (Main.layoutManager.monitors.length <= 0)
+ return;
+
+
+ this._preferredMonitorIndex = this.settings.preferredMonitor;
+ if (this._preferredMonitorIndex === -2) {
+ const monitorManager = Utils.getMonitorManager();
+ this._preferredMonitorIndex = monitorManager.get_monitor_for_connector(
+ this.settings.preferredMonitorByConnector);
+ } else if (this._preferredMonitorIndex >= 0) {
+ // Primary monitor used to be always 0 in Gdk, but the shell has a different
+ // concept (where the order depends on mutter order).
+ // So even if now the extension settings may use the same logic of the shell
+ // we prefer not to break the previously configured systems, and so we still
+ // assume that the gsettings monitor numbering follows the old strategy.
+ // This ensure the indexing in the settings and in the shell are matched,
+ // i.e. that we start counting from the primaryMonitorIndex
+ this._preferredMonitorIndex =
+ (Main.layoutManager.primaryIndex + this._preferredMonitorIndex) %
+ Main.layoutManager.monitors.length;
+ }
+
+ // In case of multi-monitor, we consider the dock on the primary monitor
+ // to be the preferred (main) one regardless of the settings the dock
+ // goes on the primary monitor also if the settings are inconsistent
+ // (e.g. desired monitor not connected).
+ if (this.settings.multiMonitor ||
+ this._preferredMonitorIndex < 0 ||
+ this._preferredMonitorIndex > Main.layoutManager.monitors.length - 1)
+ this._preferredMonitorIndex = Main.layoutManager.primaryIndex;
+
+
+ // First we create the main Dock, to get the extra features to bind to this one
+ let dock = new DockedDash({
+ monitorIndex: this._preferredMonitorIndex,
+ isMain: true,
+ });
+ this._allDocks.push(dock);
+
+ // connect app icon into the view selector
+ dock.dash.showAppsButton.connect('notify::checked',
+ this._onShowAppsButtonToggled.bind(this));
+
+ // Make the necessary changes to Main.overview.dash
+ this._prepareMainDash();
+
+ // Adjust corners if necessary
+ this._adjustPanelCorners();
+
+ if (this.settings.multiMonitor) {
+ const nMon = Main.layoutManager.monitors.length;
+ for (let iMon = 0; iMon < nMon; iMon++) {
+ if (iMon === this._preferredMonitorIndex)
+ continue;
+ dock = new DockedDash({monitorIndex: iMon});
+ this._allDocks.push(dock);
+ // connect app icon into the view selector
+ dock.dash.showAppsButton.connect('notify::checked',
+ this._onShowAppsButtonToggled.bind(this));
+ }
+ }
+
+ // Load optional features. We load *after* the docks are created, since
+ // we need to connect the signals to all dock instances.
+ this._workspaceIsolation = new WorkspaceIsolation();
+ this._keyboardShortcuts = new KeyboardShortcuts();
+
+ this.emit('docks-ready');
+ }
+
+ _prepareStartupAnimation() {
+ DockManager.allDocks.forEach(dock => {
+ const {dash} = dock;
+
+ dock.opacity = 255;
+ dash.set({
+ opacity: 0,
+ translation_x: 0,
+ translation_y: 0,
+ });
+ });
+ }
+
+ _runStartupAnimation() {
+ DockManager.allDocks.forEach(dock => {
+ const {dash} = dock;
+
+ switch (dock.position) {
+ case St.Side.LEFT:
+ dash.translation_x = -dash.width;
+ break;
+ case St.Side.RIGHT:
+ dash.translation_x = dash.width;
+ break;
+ case St.Side.BOTTOM:
+ dash.translation_y = dash.height;
+ break;
+ case St.Side.TOP:
+ dash.translation_y = -dash.height;
+ break;
+ }
+
+ dash.ease({
+ opacity: 255,
+ translation_x: 0,
+ translation_y: 0,
+ duration: STARTUP_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ });
+ }
+
+ _prepareMainDash() {
+ // Ensure Main.overview.dash is set to our dash in dummy mode
+ // while just use the default getter otherwise.
+ // The getter must be dynamic and not set only when we've a dummy
+ // overview because the mode can change dynamically.
+ this._propertyInjections.removeWithLabel(Labels.MAIN_DASH);
+ const defaultDashGetter = Object.getOwnPropertyDescriptor(
+ Main.overview.constructor.prototype, 'dash').get;
+ this._propertyInjections.addWithLabel(Labels.MAIN_DASH, Main.overview, 'dash', {
+ get: () => Main.overview.isDummy
+ ? this.mainDock.dash : defaultDashGetter.call(Main.overview),
+ });
+
+ if (Main.overview.isDummy)
+ return;
+
+ // Hide usual Dash
+ this._oldDash.hide();
+
+ // Also set dash width to 1, so it's almost not taken into account by code
+ // calculating the reserved space in the overview. The reason to keep it at 1 is
+ // to allow its visibility change to trigger an allocation of the appGrid which
+ // in turn is triggering the appsIcon spring animation, required when no other
+ // actors has this effect, i.e in horizontal mode and without the workspaceThumbnails
+ // 1 static workspace only)
+ this._oldDash.set_height(1);
+
+ this._signalsHandler.addWithLabel(Labels.OLD_DASH_CHANGES, [
+ this._oldDash,
+ 'notify::visible',
+ () => this._oldDash.hide(),
+ ], [
+ this._oldDash,
+ 'notify::height',
+ () => this._oldDash.set_height(1),
+ ]);
+
+ // Pretend I'm the dash: meant to make app grid swarm animation come from
+ // the right position of the appShowButton.
+ this.overviewControls.dash = this.mainDock.dash;
+ this.searchController._showAppsButton = this.mainDock.dash.showAppsButton;
+
+ // We also need to ignore max-size changes
+ this._methodInjections.addWithLabel(Labels.MAIN_DASH, this._oldDash,
+ 'setMaxSize', () => {});
+ this._methodInjections.addWithLabel(Labels.MAIN_DASH, this._oldDash,
+ 'allocate', () => {});
+ // And to return the preferred height depending on the state
+ this._methodInjections.addWithLabel(Labels.MAIN_DASH, this._oldDash,
+ 'get_preferred_height', (_originalMethod, ...args) => {
+ if (this.mainDock.isHorizontal && !this.settings.dockFixed)
+ return this.mainDock.get_preferred_height(...args);
+ return [0, 0];
+ });
+
+ // FIXME: https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2890
+ // const { ControlsManagerLayout } = OverviewControls;
+ const ControlsManagerLayout = this.overviewControls.layout_manager.constructor;
+
+ const maybeAdjustBoxSize = (state, box, spacing) => {
+ // ensure that an undefined value will be converted into a valid one
+ spacing = spacing ?? 0;
+
+ if (state === OverviewControls.ControlsState.WINDOW_PICKER) {
+ const searchBox = this.overviewControls._searchEntry.get_allocation_box();
+ const {shouldShow: wsThumbnails} = this.overviewControls._thumbnailsBox;
+
+ if (!wsThumbnails) {
+ box.y1 += spacing;
+ box.y2 -= spacing;
+ }
+
+ box.y2 -= searchBox.get_height() + 2 * spacing;
+ }
+
+ return box;
+ };
+
+ const maybeAdjustBoxToDock = (state, box, spacing) => {
+ maybeAdjustBoxSize(state, box, spacing);
+
+ if (this.mainDock.isHorizontal || this.settings.dockFixed)
+ return box;
+
+ const [, preferredWidth] = this.mainDock.get_preferred_width(
+ box.get_height());
+
+ if (this.mainDock.position === St.Side.LEFT)
+ box.x1 += preferredWidth;
+ else if (this.mainDock.position === St.Side.RIGHT)
+ box.x2 -= preferredWidth;
+
+ return box;
+ };
+
+ this._vfuncInjections.addWithLabel(Labels.MAIN_DASH, ControlsManagerLayout.prototype,
+ 'allocate', function (container) {
+ /* eslint-disable no-invalid-this */
+ const oldPostAllocation = this._runPostAllocation;
+ this._runPostAllocation = () => {};
+
+ const monitor = Main.layoutManager.findMonitorForActor(container);
+ const workArea = Main.layoutManager.getWorkAreaForMonitor(monitor.index);
+ const startX = workArea.x - monitor.x;
+ const startY = workArea.y - monitor.y;
+ const workAreaBox = new Clutter.ActorBox();
+ workAreaBox.set_origin(startX, startY);
+ workAreaBox.set_size(workArea.width, workArea.height);
+
+ // GNOME 46 changes "spacing" to "_spacing".
+ const spacing = this.spacing ?? this._spacing;
+
+ maybeAdjustBoxToDock(undefined, workAreaBox, spacing);
+ const oldStartY = workAreaBox.y1;
+
+ const propertyInjections = new Utils.PropertyInjectionsHandler();
+ propertyInjections.add(Main.layoutManager.panelBox, 'height', {value: startY});
+
+ if (Main.layoutManager.panelBox.y === Main.layoutManager.primaryMonitor.y)
+ workAreaBox.y1 -= oldStartY;
+
+ this.vfunc_allocate(container, workAreaBox);
+
+ propertyInjections.destroy();
+ workAreaBox.y1 = oldStartY;
+
+ const adjustActorHorizontalAllocation = actor => {
+ if (!actor.visible || !workAreaBox.x1)
+ return;
+
+ const contentBox = actor.get_allocation_box();
+ contentBox.set_size(workAreaBox.get_width(), contentBox.get_height());
+ contentBox.set_origin(workAreaBox.x1, contentBox.y1);
+ actor.allocate(contentBox);
+ };
+
+ [this._searchEntry, this._workspacesThumbnails, this._searchController].forEach(
+ actor => adjustActorHorizontalAllocation(actor));
+
+ this._runPostAllocation = oldPostAllocation;
+ this._runPostAllocation();
+ /* eslint-enable no-invalid-this */
+ });
+
+ /**
+ * This can be removed or bypassed when GNOME/gnome-shell!1892 will be merged
+ *
+ * @param originalFunction
+ * @param state
+ * @param workAreaBox
+ * @param {...any} args
+ */
+ function workspaceBoxOriginFixer(originalFunction, state, workAreaBox, ...args) {
+ /* eslint-disable no-invalid-this */
+ const workspaceBox = originalFunction.call(this, state, workAreaBox, ...args);
+ workspaceBox.set_origin(workAreaBox.x1, workspaceBox.y1);
+ return workspaceBox;
+ /* eslint-enable no-invalid-this */
+ }
+
+ this._methodInjections.addWithLabel(Labels.MAIN_DASH, [
+ ControlsManagerLayout.prototype,
+ '_computeWorkspacesBoxForState',
+ function (originalFunction, state, ...args) {
+ /* eslint-disable no-invalid-this */
+ if (state === OverviewControls.ControlsState.HIDDEN)
+ return originalFunction.call(this, state, ...args);
+
+ const box = workspaceBoxOriginFixer.call(this, originalFunction, state, ...args);
+ // GNOME 46 changes "spacing" to "_spacing".
+ const spacing = this.spacing ?? this._spacing;
+ const dock = DockManager.getDefault().getDockByMonitor(Main.layoutManager.primaryIndex);
+ if (!dock)
+ return box;
+ else
+ return maybeAdjustBoxSize(state, box, spacing);
+ /* eslint-enable no-invalid-this */
+ },
+ ], [
+ WorkspacesView.SecondaryMonitorDisplay.prototype,
+ '_getWorkspacesBoxForState',
+ function (originalFunction, state, ...args) {
+ /* eslint-disable no-invalid-this */
+ if (state === OverviewControls.ControlsState.HIDDEN)
+ return originalFunction.call(this, state, ...args);
+
+ const box = workspaceBoxOriginFixer.call(this, originalFunction, state, ...args);
+ const dock = DockManager.getDefault().getDockByMonitor(this._monitorIndex);
+ if (!dock)
+ return box;
+ if (state === OverviewControls.ControlsState.WINDOW_PICKER &&
+ dock.position === St.Side.BOTTOM) {
+ const [, preferredHeight] = dock.get_preferred_height(box.get_width());
+ box.y2 -= preferredHeight;
+ }
+ return box;
+ /* eslint-enable no-invalid-this */
+ },
+ ], [
+ ControlsManagerLayout.prototype,
+ '_getAppDisplayBoxForState',
+ function (originalFunction, ...args) {
+ /* eslint-disable no-invalid-this */
+ return workspaceBoxOriginFixer.call(this, originalFunction, ...args);
+ /* eslint-enable no-invalid-this */
+ },
+ ]);
+
+ this._vfuncInjections.addWithLabel(Labels.MAIN_DASH, Workspace.WorkspaceBackground.prototype,
+ 'allocate', function (box) {
+ /* eslint-disable no-invalid-this */
+ this.vfunc_allocate(box);
+
+ // This code has been submitted upstream via GNOME/gnome-shell!1892
+ // so can be removed when that gets merged (or bypassed on newer shell
+ // versions).
+ const monitor = Main.layoutManager.monitors[this._monitorIndex];
+ const [contentWidth, contentHeight] = this._bin.get_content_box().get_size();
+ const [mX1, mX2] = [monitor.x, monitor.x + monitor.width];
+ const [mY1, mY2] = [monitor.y, monitor.y + monitor.height];
+ const [wX1, wX2] = [this._workarea.x, this._workarea.x + this._workarea.width];
+ const [wY1, wY2] = [this._workarea.y, this._workarea.y + this._workarea.height];
+ const xScale = contentWidth / this._workarea.width;
+ const yScale = contentHeight / this._workarea.height;
+ const leftOffset = wX1 - mX1;
+ const topOffset = wY1 - mY1;
+ const rightOffset = mX2 - wX2;
+ const bottomOffset = mY2 - wY2;
+
+ const contentBox = new Clutter.ActorBox();
+ contentBox.set_origin(-leftOffset * xScale, -topOffset * yScale);
+ contentBox.set_size(
+ contentWidth + (leftOffset + rightOffset) * xScale,
+ contentHeight + (topOffset + bottomOffset) * yScale);
+
+ this._backgroundGroup.allocate(contentBox);
+ /* eslint-enable no-invalid-this */
+ });
+
+ // Reduce the space that the workspaces can use in secondary monitors
+ this._methodInjections.addWithLabel(Labels.MAIN_DASH, WorkspacesView.WorkspacesView.prototype,
+ '_getFirstFitAllWorkspaceBox', function (originalFunction, ...args) {
+ /* eslint-disable no-invalid-this */
+ const box = originalFunction.call(this, ...args);
+ if (DockManager.settings.dockFixed ||
+ this._monitorIndex === Main.layoutManager.primaryIndex)
+ return box;
+
+ const dock = DockManager.getDefault().getDockByMonitor(this._monitorIndex);
+ if (!dock)
+ return box;
+
+ if (dock.isHorizontal) {
+ const [, preferredHeight] = dock.get_preferred_height(box.get_width());
+ box.y2 -= preferredHeight;
+ if (dock.position === St.Side.TOP)
+ box.set_origin(box.x1, box.y1 + preferredHeight);
+ } else {
+ const [, preferredWidth] = dock.get_preferred_width(box.get_height());
+ box.x2 -= preferredWidth / 2;
+ if (dock.position === St.Side.LEFT)
+ box.set_origin(box.x1 + preferredWidth, box.y1);
+ }
+ return box;
+ /* eslint-enable no-invalid-this */
+ });
+
+ if (AppDisplay.BaseAppView?.prototype?._pageForCoords) {
+ // Ensure we handle Dnd events happening on the dock when we're
+ // dragging from AppDisplay.
+ // Remove when merged
+ // https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2002
+ this._methodInjections.addWithLabel(Labels.MAIN_DASH,
+ AppDisplay.BaseAppView.prototype,
+ '_pageForCoords', function (originalFunction, ...args) {
+ /* eslint-disable no-invalid-this */
+ if (!this._scrollView.has_pointer)
+ return AppDisplay.SidePages.NONE;
+ return originalFunction.call(this, ...args);
+ /* eslint-enable no-invalid-this */
+ });
+ }
+
+ if (Main.layoutManager._startingUp) {
+ this._prepareStartupAnimation();
+
+ const hadOverview = Main.sessionMode.hasOverview;
+
+ // Convince LayoutManager to use the legacy startup animation:
+ // Reset overview controls state to HIDDEN, as skipping the startup
+ // overview leaves it stuck at WINDOW_PICKER
+ if (this._settings.disableOverviewOnStartup) {
+ Main.sessionMode.hasOverview = false;
+ Main.overview._overview.controls._stateAdjustment.value =
+ OverviewControls.ControlsState.HIDDEN;
+ }
+
+ this._signalsHandler.addWithLabel(Labels.STARTUP_ANIMATION,
+ Main.layoutManager, 'startup-complete', () => {
+ this._signalsHandler.removeWithLabel(Labels.STARTUP_ANIMATION);
+ Main.sessionMode.hasOverview = hadOverview;
+ this._runStartupAnimation();
+ });
+ }
+ }
+
+ _deleteDocks() {
+ if (!this._allDocks.length)
+ return;
+
+ // Remove extra features
+ this._workspaceIsolation.destroy();
+ this._keyboardShortcuts.destroy();
+ this._desktopIconsUsableArea.resetMargins();
+
+ // Delete all docks
+ this._allDocks.forEach(d => d.destroy());
+ this._allDocks = [];
+
+ this.emit('docks-destroyed');
+ }
+
+ _restoreDash() {
+ if (!this._oldDash)
+ return;
+
+ this._signalsHandler.removeWithLabel(Labels.OLD_DASH_CHANGES);
+ [this._methodInjections, this._vfuncInjections, this._propertyInjections].forEach(
+ injections => injections.removeWithLabel(Labels.MAIN_DASH));
+
+ this.overviewControls.layout_manager._dash = this._oldDash;
+ this.overviewControls.dash = this._oldDash;
+ this.searchController._showAppsButton = this._oldDash.showAppsButton;
+ Main.overview.dash.show();
+ Main.overview.dash.set_height(-1); // reset default dash size
+ // This force the recalculation of the icon size
+ Main.overview.dash._maxHeight = -1;
+ }
+
+ get overviewControls() {
+ return Main.overview._overview.controls;
+ }
+
+ get searchController() {
+ return this.overviewControls._searchController;
+ }
+
+ _onShowAppsButtonToggled(button) {
+ const {checked} = button;
+ const {overviewControls} = this;
+
+ if (!Main.overview.visible) {
+ this.mainDock.dash.showAppsButton._fromDesktop = true;
+ Main.overview.show(OverviewControls.ControlsState.APP_GRID);
+ } else if (!checked && this.mainDock.dash.showAppsButton._fromDesktop) {
+ Main.overview.hide();
+ this.mainDock.dash.showAppsButton._fromDesktop = false;
+ } else {
+ // TODO: I'm not sure how reliable this is, we might need to move the
+ // _onShowAppsButtonToggled logic into the extension.
+ if (!checked)
+ this.mainDock.dash.showAppsButton._fromDesktop = false;
+
+
+ // Instead of "syncing" the stock button, let's call its callback directly.
+ overviewControls._onShowAppsButtonToggled();
+ }
+
+ // Because we "disconnected" from the search controller, we have to manage its state.
+ this.searchController._setSearchActive(false);
+ }
+
+ _overrideAppMenus() {
+ this._methodInjections.add(AppMenu.AppMenu.prototype,
+ '_updateFavoriteItem', function (originalFunction, ...args) {
+ /* eslint-disable no-invalid-this */
+ originalFunction.call(this, ...args);
+ if (!this._toggleFavoriteItem.visible)
+ return;
+
+ const {id} = this._app;
+ this._toggleFavoriteItem.label.text = this._appFavorites.isFavorite(id)
+ ? _('Unpin') : __('Pin to Dock');
+ /* eslint-enable no-invalid-this */
+ });
+ }
+
+ destroy() {
+ this.emit('destroy');
+ if (this._toggleLater) {
+ Utils.laterRemove(this._toggleLater);
+ delete this._toggleLater;
+ }
+ this._restoreDash();
+ this._deleteDocks();
+ this._revertPanelCorners();
+ if (this._oldSelectorMargin)
+ this.searchController.margin_bottom = this._oldSelectorMargin;
+ if (this._fm1Client) {
+ this._fm1Client.destroy();
+ this._fm1Client = null;
+ }
+ this._notificationsMonitor.destroy();
+ this._appSpread.destroy();
+ this._trash?.destroy();
+ this._trash = null;
+ Locations.unWrapFileManagerApp();
+ this._removables?.destroy();
+ this._removables = null;
+ this._iconTheme = null;
+ this._remoteModel?.destroy();
+ this._appIconsDecorator?.destroy();
+ this._settings = null;
+ this._appSwitcherSettings = null;
+ this._oldDash = null;
+
+ this._desktopIconsUsableArea?.destroy();
+ this._desktopIconsUsableArea = null;
+ this._extension = null;
+ DockManager._singleton = null;
+ }
+
+ /**
+ * Adjust Panel corners, remove this when 41 won't be supported anymore
+ */
+ _adjustPanelCorners() {
+ if (!this._hasPanelCorners())
+ return;
+
+ const position = Utils.getPosition();
+ const isHorizontal = (position === St.Side.TOP) || (position === St.Side.BOTTOM);
+ const dockOnPrimary = this._settings.multiMonitor ||
+ this._preferredMonitorIndex === Main.layoutManager.primaryIndex;
+
+ if (!isHorizontal && dockOnPrimary && this.settings.dockExtended && this.settings.dockFixed) {
+ Main.panel._rightCorner.hide();
+ Main.panel._leftCorner.hide();
+ } else {
+ this._revertPanelCorners();
+ }
+ }
+
+ _revertPanelCorners() {
+ if (!this._hasPanelCorners())
+ return;
+
+ Main.panel._leftCorner.show();
+ Main.panel._rightCorner.show();
+ }
+
+ _hasPanelCorners() {
+ return !!Main.panel?._rightCorner && !!Main.panel?._leftCorner;
+ }
+}
+Signals.addSignalMethods(DockManager.prototype);
+
+// This class drives long-running icon animations, to keep them running in sync
+// with each other, and to save CPU by pausing them when the dock is hidden.
+export class IconAnimator {
+ constructor(actor) {
+ this._count = 0;
+ this._started = false;
+ this._animations = {
+ wiggle: [],
+ };
+ this._timeline = new Clutter.Timeline({
+ duration: AnimationUtils.adjustAnimationTime(ICON_ANIMATOR_DURATION) || 1,
+ repeat_count: -1,
+ actor,
+ });
+
+ this._updateSettings();
+ this._settingsChangedId = St.Settings.get().connect('notify',
+ () => this._updateSettings());
+
+ this._timeline.connect('new-frame', () => {
+ const progress = this._timeline.get_progress();
+ const wiggleRotation = progress < 1 / 6 ? 15 * Math.sin(progress * 24 * Math.PI) : 0;
+ const wigglers = this._animations.wiggle;
+ for (let i = 0, iMax = wigglers.length; i < iMax; i++)
+ wigglers[i].target.rotation_angle_z = wiggleRotation;
+ });
+ }
+
+ _updateSettings() {
+ this._timeline.set_duration(
+ AnimationUtils.adjustAnimationTime(ICON_ANIMATOR_DURATION) || 1);
+ }
+
+ destroy() {
+ St.Settings.get().disconnect(this._settingsChangedId);
+ this._timeline.stop();
+ this._timeline = null;
+ for (const pairs of Object.values(this._animations)) {
+ for (let i = 0, iMax = pairs.length; i < iMax; i++) {
+ const pair = pairs[i];
+ pair.target.disconnect(pair.targetDestroyId);
+ }
+ }
+ this._animations = null;
+ }
+
+ pause() {
+ if (this._started && this._count > 0)
+ this._timeline.stop();
+
+ this._started = false;
+ }
+
+ start() {
+ if (!this._started && this._count > 0)
+ this._timeline.start();
+
+ this._started = true;
+ }
+
+ addAnimation(target, name) {
+ const targetDestroyId = target.connect('destroy',
+ () => this.removeAnimation(target, name));
+ this._animations[name].push({target, targetDestroyId});
+ if (this._started && this._count === 0)
+ this._timeline.start();
+
+ this._count++;
+ }
+
+ removeAnimation(target, name) {
+ const pairs = this._animations[name];
+ for (let i = 0, iMax = pairs.length; i < iMax; i++) {
+ const pair = pairs[i];
+ if (pair.target === target) {
+ target.disconnect(pair.targetDestroyId);
+ pairs.splice(i, 1);
+ this._count--;
+ if (this._started && this._count === 0)
+ this._timeline.stop();
+
+ return;
+ }
+ }
+ }
+}
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/extension.js b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/extension.js
new file mode 100644
index 0000000..68de2ac
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/extension.js
@@ -0,0 +1,18 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+import {DockManager} from './docking.js';
+import {Extension} from './dependencies/shell/extensions/extension.js';
+
+// We export this so it can be accessed by other extensions
+export let dockManager;
+
+export default class DashToDockExtension extends Extension.Extension {
+ enable() {
+ dockManager = new DockManager(this);
+ }
+
+ disable() {
+ dockManager?.destroy();
+ dockManager = null;
+ }
+}
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/fileManager1API.js b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/fileManager1API.js
new file mode 100644
index 0000000..192214a
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/fileManager1API.js
@@ -0,0 +1,201 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+import {GLib, Gio} from './dependencies/gi.js';
+const {signals: Signals} = imports;
+
+import {Utils} from './imports.js';
+
+const FileManager1Iface = '<node><interface name="org.freedesktop.FileManager1">\
+ <property name="OpenWindowsWithLocations" type="a{sas}" access="read"/>\
+ </interface></node>';
+
+const FileManager1Proxy = Gio.DBusProxy.makeProxyWrapper(FileManager1Iface);
+
+const Labels = Object.freeze({
+ WINDOWS: Symbol('windows'),
+});
+
+/**
+ * This class implements a client for the org.freedesktop.FileManager1 dbus
+ * interface, and specifically for the OpenWindowsWithLocations property
+ * which is published by Nautilus, but is not an official part of the interface.
+ *
+ * The property is a map from window identifiers to a list of locations open in
+ * the window.
+ */
+export class FileManager1Client {
+ constructor() {
+ this._signalsHandler = new Utils.GlobalSignalsHandler();
+ this._cancellable = new Gio.Cancellable();
+
+ this._windowsByPath = new Map();
+ this._windowsByLocation = new Map();
+ this._proxy = new FileManager1Proxy(Gio.DBus.session,
+ 'org.freedesktop.FileManager1',
+ '/org/freedesktop/FileManager1',
+ (initable, error) => {
+ // Use async construction to avoid blocking on errors.
+ if (error) {
+ if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ global.log(error);
+ } else {
+ this._updateWindows();
+ this._updateLocationMap();
+ }
+ }, this._cancellable);
+
+ this._signalsHandler.add([
+ this._proxy,
+ 'g-properties-changed',
+ this._onPropertyChanged.bind(this),
+ ], [
+ // We must additionally listen for Screen events to know when to
+ // rebuild our location map when the set of available windows changes.
+ global.workspaceManager,
+ 'workspace-added',
+ () => this._onWindowsChanged(),
+ ], [
+ global.workspaceManager,
+ 'workspace-removed',
+ () => this._onWindowsChanged(),
+ ], [
+ global.display,
+ 'window-entered-monitor',
+ () => this._onWindowsChanged(),
+ ], [
+ global.display,
+ 'window-left-monitor',
+ () => this._onWindowsChanged(),
+ ]);
+ }
+
+ destroy() {
+ if (this._windowsUpdateIdle) {
+ GLib.source_remove(this._windowsUpdateIdle);
+ delete this._windowsUpdateIdle;
+ }
+ this._cancellable.cancel();
+ this._signalsHandler.destroy();
+ this._windowsByLocation.clear();
+ this._windowsByPath.clear();
+ this._proxy = null;
+ }
+
+ /**
+ * Return an array of windows that are showing a location or
+ * sub-directories of that location.
+ *
+ * @param location
+ */
+ getWindows(location) {
+ if (!location)
+ return [];
+
+ location += location.endsWith('/') ? '' : '/';
+ const windows = [];
+ this._windowsByLocation.forEach((wins, l) => {
+ if (l.startsWith(location))
+ windows.push(...wins);
+ });
+ return [...new Set(windows)];
+ }
+
+ _onPropertyChanged(proxy, changed, _invalidated) {
+ const property = changed.unpack();
+ if (property &&
+ ('OpenWindowsWithLocations' in property))
+ this._updateLocationMap();
+ }
+
+ _updateWindows() {
+ const oldSize = this._windowsByPath.size;
+ const oldPaths = this._windowsByPath.keys();
+ this._windowsByPath = Utils.getWindowsByObjectPath();
+
+ if (oldSize !== this._windowsByPath.size)
+ return true;
+
+ return [...oldPaths].some(path => !this._windowsByPath.has(path));
+ }
+
+ _onWindowsChanged() {
+ if (this._windowsUpdateIdle)
+ return;
+
+ this._windowsUpdateIdle = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
+ if (this._updateWindows())
+ this._updateLocationMap();
+
+ delete this._windowsUpdateIdle;
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+
+ _updateLocationMap() {
+ const properties = this._proxy.get_cached_property_names();
+ if (!properties) {
+ // Nothing to check yet.
+ return;
+ }
+
+ if (properties.includes('OpenWindowsWithLocations'))
+ this._updateFromPaths();
+ }
+
+ _locationMapsEquals(mapA, mapB) {
+ if (mapA.size !== mapB.size)
+ return false;
+
+ const setsEquals = (a, b) => a.size === b.size &&
+ [...a].every(value => b.has(value));
+
+ for (const [key, val] of mapA) {
+ const windowsSet = mapB.get(key);
+ if (!windowsSet || !setsEquals(windowsSet, val))
+ return false;
+ }
+ return true;
+ }
+
+ _updateFromPaths() {
+ const locationsByWindowsPath = this._proxy.OpenWindowsWithLocations;
+
+ const windowsByLocation = new Map();
+ this._signalsHandler.removeWithLabel(Labels.WINDOWS);
+
+ Object.entries(locationsByWindowsPath).forEach(([windowPath, locations]) => {
+ locations.forEach(location => {
+ const win = this._windowsByPath.get(windowPath);
+ const windowGroup = win ? [win] : [];
+
+ win?.foreach_transient(w => windowGroup.push(w) || true);
+
+ windowGroup.forEach(window => {
+ location += location.endsWith('/') ? '' : '/';
+ // Use a set to deduplicate when a window has a
+ // location open in multiple tabs.
+ const windows = windowsByLocation.get(location) || new Set();
+ windows.add(window);
+
+ if (windows.size === 1)
+ windowsByLocation.set(location, windows);
+
+ this._signalsHandler.addWithLabel(Labels.WINDOWS, window,
+ 'unmanaged', () => {
+ const wins = this._windowsByLocation.get(location);
+ wins.delete(window);
+ if (!wins.size)
+ this._windowsByLocation.delete(location);
+ this.emit('windows-changed');
+ });
+ });
+ });
+ });
+
+ if (!this._locationMapsEquals(this._windowsByLocation, windowsByLocation)) {
+ this._windowsByLocation = windowsByLocation;
+ this.emit('windows-changed');
+ }
+ }
+}
+Signals.addSignalMethods(FileManager1Client.prototype);
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/imports.js b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/imports.js
new file mode 100644
index 0000000..94bf337
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/imports.js
@@ -0,0 +1,17 @@
+export * as AppIconIndicators from './appIconIndicators.js';
+export * as AppIcons from './appIcons.js';
+export * as AppIconsDecorator from './appIconsDecorator.js';
+export * as AppSpread from './appSpread.js';
+export * as DockDash from './dash.js';
+export * as DBusMenuUtils from './dbusmenuUtils.js';
+export * as DesktopIconsIntegration from './desktopIconsIntegration.js';
+export * as Docking from './docking.js';
+export * as Extension from './extension.js';
+export * as FileManager1API from './fileManager1API.js';
+export * as Intellihide from './intellihide.js';
+export * as LauncherAPI from './launcherAPI.js';
+export * as Locations from './locations.js';
+export * as NotificationsMonitor from './notificationsMonitor.js';
+export * as Theming from './theming.js';
+export * as Utils from './utils.js';
+export * as WindowPreview from './windowPreview.js';
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/intellihide.js b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/intellihide.js
new file mode 100644
index 0000000..9668af2
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/intellihide.js
@@ -0,0 +1,337 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+import {
+ GLib,
+ Meta,
+ Shell,
+} from './dependencies/gi.js';
+
+import {
+ Docking,
+ Utils,
+} from './imports.js';
+
+const {signals: Signals} = imports;
+
+// A good compromise between reactivity and efficiency; to be tuned.
+const INTELLIHIDE_CHECK_INTERVAL = 100;
+
+const OverlapStatus = Object.freeze({
+ UNDEFINED: -1,
+ FALSE: 0,
+ TRUE: 1,
+});
+
+const IntellihideMode = Object.freeze({
+ ALL_WINDOWS: 0,
+ FOCUS_APPLICATION_WINDOWS: 1,
+ MAXIMIZED_WINDOWS: 2,
+ ALWAYS_ON_TOP: 3,
+});
+
+// List of windows type taken into account. Order is important (keep the original
+// enum order).
+const handledWindowTypes = [
+ Meta.WindowType.NORMAL,
+ Meta.WindowType.DOCK,
+ Meta.WindowType.DIALOG,
+ Meta.WindowType.MODAL_DIALOG,
+ Meta.WindowType.TOOLBAR,
+ Meta.WindowType.MENU,
+ Meta.WindowType.UTILITY,
+ Meta.WindowType.SPLASHSCREEN,
+ Meta.WindowType.DROPDOWN_MENU,
+];
+
+// List of applications, ignore windows of these applications in considering intellihide
+const ignoreApps = ['com.rastersoft.ding', 'com.desktop.ding'];
+
+/**
+ * A rough and ugly implementation of the intellihide behaviour.
+ * Intallihide object: emit 'status-changed' signal when the overlap of windows
+ * with the provided targetBoxClutter.ActorBox changes;
+ */
+export class Intellihide {
+ constructor(monitorIndex) {
+ // Load settings
+ this._monitorIndex = monitorIndex;
+
+ this._signalsHandler = new Utils.GlobalSignalsHandler();
+ this._tracker = Shell.WindowTracker.get_default();
+ this._focusApp = null; // The application whose window is focused.
+ this._topApp = null; // The application whose window is on top on the monitor with the dock.
+
+ this._isEnabled = false;
+ this._status = OverlapStatus.UNDEFINED;
+ this._targetBox = null;
+
+ this._checkOverlapTimeoutContinue = false;
+ this._checkOverlapTimeoutId = 0;
+
+ this._trackedWindows = new Map();
+
+ // Connect global signals
+ this._signalsHandler.add([
+ // Add signals on windows created from now on
+ global.display,
+ 'window-created',
+ this._windowCreated.bind(this),
+ ], [
+ // triggered for instance when the window list order changes,
+ // included when the workspace is switched
+ global.display,
+ 'restacked',
+ this._checkOverlap.bind(this),
+ ], [
+ // when windows are alwasy on top, the focus window can change
+ // without the windows being restacked. Thus monitor window focus change.
+ this._tracker,
+ 'notify::focus-app',
+ this._checkOverlap.bind(this),
+ ], [
+ // update wne monitor changes, for instance in multimonitor when monitor are attached
+ Utils.getMonitorManager(),
+ 'monitors-changed',
+ this._checkOverlap.bind(this),
+ ]);
+ }
+
+ destroy() {
+ // Disconnect global signals
+ this._signalsHandler.destroy();
+
+ // Remove residual windows signals
+ this.disable();
+ }
+
+ enable() {
+ this._isEnabled = true;
+ this._status = OverlapStatus.UNDEFINED;
+ global.get_window_actors().forEach(function (wa) {
+ this._addWindowSignals(wa);
+ }, this);
+ this._doCheckOverlap();
+ }
+
+ disable() {
+ this._isEnabled = false;
+
+ for (const wa of this._trackedWindows.keys())
+ this._removeWindowSignals(wa);
+
+ this._trackedWindows.clear();
+
+ if (this._checkOverlapTimeoutId > 0) {
+ GLib.source_remove(this._checkOverlapTimeoutId);
+ this._checkOverlapTimeoutId = 0;
+ }
+ }
+
+ _windowCreated(display, metaWindow) {
+ this._addWindowSignals(metaWindow.get_compositor_private());
+ this._doCheckOverlap();
+ }
+
+ _addWindowSignals(wa) {
+ if (!this._handledWindow(wa))
+ return;
+ const signalId = wa.connect('notify::allocation', this._checkOverlap.bind(this));
+ this._trackedWindows.set(wa, signalId);
+ wa.connect('destroy', this._removeWindowSignals.bind(this));
+ }
+
+ _removeWindowSignals(wa) {
+ if (this._trackedWindows.get(wa)) {
+ wa.disconnect(this._trackedWindows.get(wa));
+ this._trackedWindows.delete(wa);
+ }
+ }
+
+ updateTargetBox(box) {
+ this._targetBox = box;
+ this._checkOverlap();
+ }
+
+ forceUpdate() {
+ this._status = OverlapStatus.UNDEFINED;
+ this._doCheckOverlap();
+ }
+
+ getOverlapStatus() {
+ return this._status === OverlapStatus.TRUE;
+ }
+
+ _checkOverlap() {
+ if (!this._isEnabled || !this._targetBox)
+ return;
+
+ /* Limit the number of calls to the doCheckOverlap function */
+ if (this._checkOverlapTimeoutId) {
+ this._checkOverlapTimeoutContinue = true;
+ return;
+ }
+
+ this._doCheckOverlap();
+
+ this._checkOverlapTimeoutId = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT, INTELLIHIDE_CHECK_INTERVAL, () => {
+ this._doCheckOverlap();
+ if (this._checkOverlapTimeoutContinue) {
+ this._checkOverlapTimeoutContinue = false;
+ return GLib.SOURCE_CONTINUE;
+ } else {
+ this._checkOverlapTimeoutId = 0;
+ return GLib.SOURCE_REMOVE;
+ }
+ });
+ }
+
+ _doCheckOverlap() {
+ if (!this._isEnabled || !this._targetBox)
+ return;
+
+ let overlaps = OverlapStatus.FALSE;
+ let windows = global.get_window_actors().filter(wa => this._handledWindow(wa));
+
+ if (windows.length > 0) {
+ /*
+ * Get the top window on the monitor where the dock is placed.
+ * The idea is that we dont want to overlap with the windows of the topmost application,
+ * event is it's not the focused app -- for instance because in multimonitor the user
+ * select a window in the secondary monitor.
+ */
+
+ let topWindow = null;
+ for (let i = windows.length - 1; i >= 0; i--) {
+ const metaWin = windows[i].get_meta_window();
+ if (metaWin.get_monitor() === this._monitorIndex) {
+ topWindow = metaWin;
+ break;
+ }
+ }
+
+ if (topWindow) {
+ this._topApp = this._tracker.get_window_app(topWindow);
+ // If there isn't a focused app, use that of the window on top
+ this._focusApp = this._tracker.focus_app || this._topApp;
+
+ windows = windows.filter(this._intellihideFilterInteresting, this);
+
+ for (let i = 0; i < windows.length; i++) {
+ const win = windows[i].get_meta_window();
+
+ if (win) {
+ const rect = win.get_frame_rect();
+
+ const test = (rect.x < this._targetBox.x2) &&
+ (rect.x + rect.width >= this._targetBox.x1) &&
+ (rect.y < this._targetBox.y2) &&
+ (rect.y + rect.height >= this._targetBox.y1);
+
+ if (test) {
+ overlaps = OverlapStatus.TRUE;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (this._status !== overlaps) {
+ this._status = overlaps;
+ this.emit('status-changed', this._status);
+ }
+ }
+
+ // Filter interesting windows to be considered for intellihide.
+ // Consider all windows visible on the current workspace.
+ // Optionally skip windows of other applications
+ _intellihideFilterInteresting(wa) {
+ const metaWin = wa.get_meta_window();
+ const currentWorkspace = global.workspace_manager.get_active_workspace_index();
+ const workspace = metaWin.get_workspace();
+ const workspaceIndex = workspace.index();
+
+ // Depending on the intellihide mode, exclude non-relevent windows
+ switch (Docking.DockManager.settings.intellihideMode) {
+ case IntellihideMode.ALL_WINDOWS:
+ // Do nothing
+ break;
+
+ case IntellihideMode.FOCUS_APPLICATION_WINDOWS:
+ // Skip windows of other apps
+ if (this._focusApp) {
+ // The DropDownTerminal extension is not an application per se
+ // so we match its window by wm class instead
+ if (metaWin.get_wm_class() === 'DropDownTerminalWindow')
+ return true;
+
+ const currentApp = this._tracker.get_window_app(metaWin);
+ const focusWindow = global.display.get_focus_window();
+
+ // Consider half maximized windows side by side
+ // and windows which are alwayson top
+ if (currentApp !== this._focusApp && currentApp !== this._topApp &&
+ !((focusWindow && focusWindow.maximized_vertically &&
+ !focusWindow.maximized_horizontally) &&
+ (metaWin.maximized_vertically && !metaWin.maximized_horizontally) &&
+ metaWin.get_monitor() === focusWindow.get_monitor()) &&
+ !metaWin.is_above())
+ return false;
+ }
+ break;
+
+ case IntellihideMode.MAXIMIZED_WINDOWS:
+ // Skip unmaximized windows
+ if (!metaWin.maximized_vertically && !metaWin.maximized_horizontally && !metaWin.fullscreen)
+ return false;
+ break;
+
+ case IntellihideMode.ALWAYS_ON_TOP:
+ // Always on top, except for fullscreen windows
+ if (this._focusApp) {
+ const {focusWindow} = global.display;
+ if (!focusWindow?.fullscreen)
+ return false;
+ }
+ break;
+ }
+
+ if (workspaceIndex === currentWorkspace && metaWin.showing_on_its_workspace())
+ return true;
+ else
+ return false;
+ }
+
+ // Filter windows by type
+ // inspired by Opacify@gnome-shell.localdomain.pl
+ _handledWindow(wa) {
+ const metaWindow = wa.get_meta_window();
+
+ if (!metaWindow)
+ return false;
+
+ // The DING extension desktop window needs to be excluded
+ // so we match its window by application id and window property.
+ const wmApp = metaWindow.get_gtk_application_id();
+ if (ignoreApps.includes(wmApp) && metaWindow.is_skip_taskbar())
+ return false;
+
+ // The DropDownTerminal extension uses the POPUP_MENU window type hint
+ // so we match its window by wm class instead
+ if (metaWindow.get_wm_class() === 'DropDownTerminalWindow')
+ return true;
+
+ const wtype = metaWindow.get_window_type();
+ for (let i = 0; i < handledWindowTypes.length; i++) {
+ const hwtype = handledWindowTypes[i];
+ if (hwtype === wtype)
+ return true;
+ else if (hwtype > wtype)
+ return false;
+ }
+ return false;
+ }
+}
+
+Signals.addSignalMethods(Intellihide.prototype);
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/launcherAPI.js b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/launcherAPI.js
new file mode 100644
index 0000000..8778d32
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/launcherAPI.js
@@ -0,0 +1,287 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+import {Gio} from './dependencies/gi.js';
+import {DBusMenuUtils} from './imports.js';
+
+const DBusMenu = await DBusMenuUtils.haveDBusMenu();
+
+export class LauncherEntryRemoteModel {
+ constructor() {
+ this._entrySourceStacks = new Map();
+ this._remoteMaps = new Map();
+
+ this._launcher_entry_dbus_signal_id =
+ Gio.DBus.session.signal_subscribe(null, // sender
+ 'com.canonical.Unity.LauncherEntry', // iface
+ 'Update', // member
+ null, // path
+ null, // arg0
+ Gio.DBusSignalFlags.NONE,
+ (_connection, senderName, _objectPath, _interfaceName, _signalName, parameters) =>
+ this._onUpdate(senderName, ...parameters.deep_unpack()));
+
+ this._dbus_name_owner_changed_signal_id =
+ Gio.DBus.session.signal_subscribe('org.freedesktop.DBus', // sender
+ 'org.freedesktop.DBus', // interface
+ 'NameOwnerChanged', // member
+ '/org/freedesktop/DBus', // path
+ null, // arg0
+ Gio.DBusSignalFlags.NONE,
+ (connection, _senderName, _objectPath, _interfaceName, _signalName, parameters) =>
+ this._onDBusNameChange(...parameters.deep_unpack().slice(1)));
+
+ this._acquireUnityDBus();
+ }
+
+ destroy() {
+ if (this._launcher_entry_dbus_signal_id)
+ Gio.DBus.session.signal_unsubscribe(this._launcher_entry_dbus_signal_id);
+
+
+ if (this._dbus_name_owner_changed_signal_id)
+ Gio.DBus.session.signal_unsubscribe(this._dbus_name_owner_changed_signal_id);
+
+
+ this._releaseUnityDBus();
+ }
+
+ _lookupStackById(appId) {
+ let sourceStack = this._entrySourceStacks.get(appId);
+ if (!sourceStack) {
+ sourceStack = new PropertySourceStack(new LauncherEntry(),
+ launcherEntryDefaults);
+ this._entrySourceStacks.set(appId, sourceStack);
+ }
+
+ return sourceStack;
+ }
+
+ lookupById(appId) {
+ return this._lookupStackById(appId).target;
+ }
+
+ _acquireUnityDBus() {
+ if (!this._unity_bus_id) {
+ this._unity_bus_id = Gio.DBus.session.own_name('com.canonical.Unity',
+ Gio.BusNameOwnerFlags.ALLOW_REPLACEMENT | Gio.BusNameOwnerFlags.REPLACE,
+ null, () => (this._unity_bus_id = 0));
+ }
+ }
+
+ _releaseUnityDBus() {
+ if (this._unity_bus_id) {
+ Gio.DBus.session.unown_name(this._unity_bus_id);
+ this._unity_bus_id = 0;
+ }
+ }
+
+ _onDBusNameChange(before, after) {
+ if (!before || !this._remoteMaps.size)
+ return;
+
+ const remoteMap = this._remoteMaps.get(before);
+ if (!remoteMap)
+ return;
+
+ this._remoteMaps.delete(before);
+ if (after && !this._remoteMaps.has(after)) {
+ this._remoteMaps.set(after, remoteMap);
+ } else {
+ for (const [appId, remote] of remoteMap) {
+ const sourceStack = this._entrySourceStacks.get(appId);
+ const changed = sourceStack.remove(remote);
+ if (changed)
+ sourceStack.target._emitChangedEvents(changed);
+ }
+ }
+ }
+
+ _onUpdate(senderName, appUri, properties) {
+ if (!senderName)
+ return;
+
+
+ const appId = appUri.replace(/(^\w+:|^)\/\//, '');
+ if (!appId)
+ return;
+
+
+ let remoteMap = this._remoteMaps.get(senderName);
+ if (!remoteMap)
+ this._remoteMaps.set(senderName, remoteMap = new Map());
+
+ let remote = remoteMap.get(appId);
+ if (!remote)
+ remoteMap.set(appId, remote = Object.assign({}, launcherEntryDefaults));
+
+ for (const name in properties) {
+ if (name === 'quicklist' && DBusMenu) {
+ const quicklistPath = properties[name].unpack();
+ if (quicklistPath &&
+ (!remote._quicklistMenuClient ||
+ remote._quicklistMenuClient.dbus_object !== quicklistPath)) {
+ remote.quicklist = null;
+ let menuClient = remote._quicklistMenuClient;
+ if (menuClient) {
+ menuClient.dbus_object = quicklistPath;
+ } else {
+ // This property should not be enumerable
+ Object.defineProperty(remote, '_quicklistMenuClient', {
+ writable: true,
+ value: menuClient = new DBusMenu.Client({
+ dbus_name: senderName,
+ dbus_object: quicklistPath,
+ }),
+ });
+ }
+ const handler = () => {
+ const root = menuClient.get_root();
+ if (remote.quicklist !== root) {
+ remote.quicklist = root;
+ if (sourceStack.isTop(remote)) {
+ sourceStack.target.quicklist = root;
+ sourceStack.target._emitChangedEvents(['quicklist']);
+ }
+ }
+ };
+ menuClient.connect(DBusMenu.CLIENT_SIGNAL_ROOT_CHANGED, handler);
+ }
+ } else {
+ remote[name] = properties[name].unpack();
+ }
+ }
+
+ const sourceStack = this._lookupStackById(appId);
+ sourceStack.target._emitChangedEvents(sourceStack.update(remote));
+ }
+}
+
+const launcherEntryDefaults = Object.freeze({
+ count: 0,
+ progress: 0,
+ urgent: false,
+ updating: false,
+ quicklist: null,
+ 'count-visible': false,
+ 'progress-visible': false,
+});
+
+const LauncherEntry = class DashToDockLauncherEntry {
+ constructor() {
+ this._connections = new Map();
+ this._handlers = new Map();
+ this._nextId = 0;
+ }
+
+ connect(eventNames, callback) {
+ if (typeof eventNames === 'string')
+ eventNames = [eventNames];
+
+ callback(this, this);
+ const id = this._nextId++;
+ const handler = {id, callback};
+ eventNames.forEach(name => {
+ let handlerList = this._handlers.get(name);
+ if (!handlerList)
+ this._handlers.set(name, handlerList = []);
+
+ handlerList.push(handler);
+ });
+ this._connections.set(id, eventNames);
+ return id;
+ }
+
+ disconnect(id) {
+ const eventNames = this._connections.get(id);
+ if (!eventNames)
+ return;
+
+ this._connections.delete(id);
+ eventNames.forEach(name => {
+ const handlerList = this._handlers.get(name);
+ if (handlerList) {
+ for (let i = 0, iMax = handlerList.length; i < iMax; i++) {
+ if (handlerList[i].id === id) {
+ handlerList.splice(i, 1);
+ break;
+ }
+ }
+ }
+ });
+ }
+
+ _emitChangedEvents(propertyNames) {
+ const handlers = new Set();
+ propertyNames.forEach(name => {
+ const handlerList = this._handlers.get(`${name}-changed`);
+ if (handlerList) {
+ for (let i = 0, iMax = handlerList.length; i < iMax; i++)
+ handlers.add(handlerList[i]);
+ }
+ });
+ Array.from(handlers).sort((x, y) => x.id - y.id).forEach(handler => handler.callback(this, this));
+ }
+};
+
+for (const [name, defaultValue] of Object.entries(launcherEntryDefaults)) {
+ const jsName = name.replaceAll('-', '_');
+ LauncherEntry.prototype[jsName] = defaultValue;
+ if (jsName !== name) {
+ Object.defineProperty(LauncherEntry.prototype, name, {
+ get() {
+ return this[jsName];
+ },
+ set(value) {
+ this[jsName] = value;
+ },
+ });
+ }
+}
+
+const PropertySourceStack = class DashToDockPropertySourceStack {
+ constructor(target, bottom) {
+ this.target = target;
+ this._bottom = bottom;
+ this._stack = [];
+ }
+
+ isTop(source) {
+ return this._stack.length > 0 && this._stack[this._stack.length - 1] === source;
+ }
+
+ update(source) {
+ if (!this.isTop(source)) {
+ this.remove(source);
+ this._stack.push(source);
+ }
+ return this._assignFrom(source);
+ }
+
+ remove(source) {
+ const stack = this._stack;
+ const top = stack[stack.length - 1];
+ if (top === source) {
+ stack.length--;
+ return this._assignFrom(stack.length > 0 ? stack[stack.length - 1] : this._bottom);
+ }
+ for (let i = 0, iMax = stack.length; i < iMax; i++) {
+ if (stack[i] === source) {
+ stack.splice(i, 1);
+ break;
+ }
+ }
+
+ return null;
+ }
+
+ _assignFrom(source) {
+ const changedProperties = [];
+ for (const name in source) {
+ if (this.target[name] !== source[name]) {
+ this.target[name] = source[name];
+ changedProperties.push(name);
+ }
+ }
+ return changedProperties;
+ }
+};
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ar/LC_MESSAGES/dashtodock.mo b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ar/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..ffb5786
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ar/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/be/LC_MESSAGES/dashtodock.mo b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/be/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..ba9a252
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/be/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/bg/LC_MESSAGES/dashtodock.mo b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/bg/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..87e8892
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/bg/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/cs/LC_MESSAGES/dashtodock.mo b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/cs/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..7b092bf
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/cs/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/de/LC_MESSAGES/dashtodock.mo b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/de/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..0f7dc82
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/de/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/el/LC_MESSAGES/dashtodock.mo b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/el/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..c64aa83
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/el/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/es/LC_MESSAGES/dashtodock.mo b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/es/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..d143515
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/es/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/eu/LC_MESSAGES/dashtodock.mo b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/eu/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..044406f
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/eu/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/fr/LC_MESSAGES/dashtodock.mo b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/fr/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..9ca1002
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/fr/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/gl/LC_MESSAGES/dashtodock.mo b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/gl/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..110db82
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/gl/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/hu/LC_MESSAGES/dashtodock.mo b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/hu/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..b8a17f9
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/hu/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/id/LC_MESSAGES/dashtodock.mo b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/id/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..3ea32d5
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/id/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/it/LC_MESSAGES/dashtodock.mo b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/it/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..56361a5
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/it/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ja/LC_MESSAGES/dashtodock.mo b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ja/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..a2ba4ef
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ja/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ka/LC_MESSAGES/dashtodock.mo b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ka/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..47042cf
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ka/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/kk/LC_MESSAGES/dashtodock.mo b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/kk/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..f5f92c0
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/kk/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ko/LC_MESSAGES/dashtodock.mo b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ko/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..d9c5308
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ko/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/lv/LC_MESSAGES/dashtodock.mo b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/lv/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..e6d146a
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/lv/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/nb/LC_MESSAGES/dashtodock.mo b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/nb/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..db5ad46
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/nb/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/nl/LC_MESSAGES/dashtodock.mo b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/nl/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..e926ee0
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/nl/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/pl/LC_MESSAGES/dashtodock.mo b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/pl/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..a48230d
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/pl/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/pt/LC_MESSAGES/dashtodock.mo b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/pt/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..9733e40
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/pt/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/pt_BR/LC_MESSAGES/dashtodock.mo b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/pt_BR/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..de2a4e6
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/pt_BR/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ro/LC_MESSAGES/dashtodock.mo b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ro/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..a3c2b23
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ro/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ru/LC_MESSAGES/dashtodock.mo b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ru/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..69ed645
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ru/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sk/LC_MESSAGES/dashtodock.mo b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sk/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..9348b7e
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sk/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sl/LC_MESSAGES/dashtodock.mo b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sl/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..2f30b3a
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sl/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sr/LC_MESSAGES/dashtodock.mo b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sr/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..b5e6fb6
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sr/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sr@latin/LC_MESSAGES/dashtodock.mo b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sr@latin/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..8dfc69e
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sr@latin/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sv/LC_MESSAGES/dashtodock.mo b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sv/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..d6cdf84
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sv/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/tr/LC_MESSAGES/dashtodock.mo b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/tr/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..6a12cfb
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/tr/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/uk_UA/LC_MESSAGES/dashtodock.mo b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/uk_UA/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..d71acaf
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/uk_UA/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/zh_CN/LC_MESSAGES/dashtodock.mo b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/zh_CN/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..b701af8
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/zh_CN/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/zh_TW/LC_MESSAGES/dashtodock.mo b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/zh_TW/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..1bd8e0a
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/zh_TW/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locations.js b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locations.js
new file mode 100644
index 0000000..534466c
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locations.js
@@ -0,0 +1,1535 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+import {
+ Gio,
+ GioUnix,
+ GLib,
+ GObject,
+ Shell,
+ St,
+} from './dependencies/gi.js';
+
+import {ShellMountOperation} from './dependencies/shell/ui.js';
+
+import {
+ Docking,
+ Utils,
+} from './imports.js';
+
+import {Extension} from './dependencies/shell/extensions/extension.js';
+
+// Use __ () and N__() for the extension gettext domain, and reuse
+// the shell domain with the default _() and N_()
+const {gettext: __} = Extension;
+
+const {signals: Signals} = imports;
+
+const FALLBACK_REMOVABLE_MEDIA_ICON = 'drive-removable-media';
+const FALLBACK_TRASH_ICON = 'user-trash';
+const FILE_MANAGER_DESKTOP_APP_ID = 'org.gnome.Nautilus.desktop';
+const ATTRIBUTE_METADATA_CUSTOM_ICON = 'metadata::custom-icon';
+const TRASH_URI = 'trash://';
+const UPDATE_TRASH_DELAY = 1000;
+const LAUNCH_HANDLER_MAX_WAIT = 200;
+
+const NautilusFileOperations2Interface = '<node>\
+ <interface name="org.gnome.Nautilus.FileOperations2">\
+ <method name="EmptyTrash">\
+ <arg type="b" name="ask_confirmation" direction="in"/>\
+ <arg type="a{sv}" name="platform_data" direction="in"/>\
+ </method>\
+ </interface>\
+</node>';
+
+const NautilusFileOperations2ProxyInterface =
+ Gio.DBusProxy.makeProxyWrapper(NautilusFileOperations2Interface);
+
+const Labels = Object.freeze({
+ LOCATION_WINDOWS: Symbol('location-windows'),
+ WINDOWS_CHANGED: Symbol('windows-changed'),
+});
+
+const GJS_SUPPORTS_FILE_IFACE_PROMISES = imports.system.version >= 17101;
+
+if (GJS_SUPPORTS_FILE_IFACE_PROMISES) {
+ Gio._promisify(Gio.File.prototype, 'query_info_async');
+ Gio._promisify(Gio.File.prototype, 'query_default_handler_async');
+}
+
+
+/**
+ *
+ */
+function makeNautilusFileOperationsProxy() {
+ const proxy = new NautilusFileOperations2ProxyInterface(
+ Gio.DBus.session,
+ 'org.gnome.Nautilus',
+ '/org/gnome/Nautilus/FileOperations2', (_p, error) => {
+ if (error)
+ logError(error, 'Error connecting to Nautilus');
+ }
+ );
+
+ proxy.platformData = params => {
+ const defaultParams = {
+ parentHandle: '',
+ timestamp: global.get_current_time(),
+ windowPosition: 'center',
+ };
+ const {parentHandle, timestamp, windowPosition} = {
+ ...defaultParams,
+ ...params,
+ };
+
+ return {
+ 'parent-handle': new GLib.Variant('s', parentHandle),
+ 'timestamp': new GLib.Variant('u', timestamp),
+ 'window-position': new GLib.Variant('s', windowPosition),
+ };
+ };
+
+ return proxy;
+}
+
+export const LocationAppInfo = GObject.registerClass({
+ Implements: [Gio.AppInfo],
+ Properties: {
+ 'location': GObject.ParamSpec.object(
+ 'location', 'location', 'location',
+ GObject.ParamFlags.READWRITE,
+ Gio.File.$gtype),
+ 'name': GObject.ParamSpec.string(
+ 'name', 'name', 'name',
+ GObject.ParamFlags.READWRITE,
+ null),
+ 'icon': GObject.ParamSpec.object(
+ 'icon', 'icon', 'icon',
+ GObject.ParamFlags.READWRITE,
+ Gio.Icon.$gtype),
+ 'cancellable': GObject.ParamSpec.object(
+ 'cancellable', 'cancellable', 'cancellable',
+ GObject.ParamFlags.READWRITE,
+ Gio.Cancellable.$gtype),
+ },
+}, class LocationAppInfo extends GioUnix.DesktopAppInfo {
+ static get GJS_BINARY_PATH() {
+ if (!this._gjsBinaryPath)
+ this._gjsBinaryPath = GLib.find_program_in_path('gjs');
+
+ return this._gjsBinaryPath;
+ }
+
+ list_actions() {
+ return [];
+ }
+
+ get_action_name() {
+ return null;
+ }
+
+ get_boolean() {
+ return false;
+ }
+
+ vfunc_dup() {
+ return new LocationAppInfo({
+ location: this.location,
+ name: this.name,
+ icon: this.icon,
+ cancellable: this.cancellable,
+ });
+ }
+
+ vfunc_equal(other) {
+ if (this.location)
+ return this.location.equal(other?.location);
+
+ return this.name === other.name &&
+ (this.icon ? this.icon.equal(other?.icon) : !other?.icon);
+ }
+
+ vfunc_get_id() {
+ return 'location:%s'.format(this.location?.get_uri());
+ }
+
+ vfunc_get_name() {
+ return this.name;
+ }
+
+ vfunc_get_description() {
+ return null;
+ }
+
+ vfunc_get_executable() {
+ return null;
+ }
+
+ vfunc_get_icon() {
+ return this.icon;
+ }
+
+ vfunc_launch(files, context) {
+ if (files?.length) {
+ throw new GLib.Error(Gio.IOErrorEnum,
+ Gio.IOErrorEnum.NOT_SUPPORTED, 'Launching with files not supported');
+ }
+
+ return this.getHandlerApp().launch_uris([this.location.get_uri()], context);
+ }
+
+ vfunc_supports_uris() {
+ return false;
+ }
+
+ vfunc_supports_files() {
+ return false;
+ }
+
+ vfunc_launch_uris(uris, context) {
+ return this.launch(uris, context);
+ }
+
+ vfunc_should_show() {
+ return true;
+ }
+
+ vfunc_set_as_default_for_type() {
+ throw new GLib.Error(Gio.IOErrorEnum,
+ Gio.IOErrorEnum.NOT_SUPPORTED, 'Not supported');
+ }
+
+ vfunc_set_as_default_for_extension() {
+ throw new GLib.Error(Gio.IOErrorEnum,
+ Gio.IOErrorEnum.NOT_SUPPORTED, 'Not supported');
+ }
+
+ vfunc_add_supports_type() {
+ throw new GLib.Error(Gio.IOErrorEnum,
+ Gio.IOErrorEnum.NOT_SUPPORTED, 'Not supported');
+ }
+
+ vfunc_can_remove_supports_type() {
+ return false;
+ }
+
+ vfunc_remove_supports_type() {
+ return false;
+ }
+
+ vfunc_can_delete() {
+ return false;
+ }
+
+ vfunc_do_delete() {
+ return false;
+ }
+
+ vfunc_get_commandline() {
+ try {
+ return this.getHandlerApp().get_commandline();
+ } catch {
+ return this._getFallbackCommandLine();
+ }
+ }
+
+ vfunc_get_display_name() {
+ return this.name;
+ }
+
+ vfunc_set_as_last_used_for_type() {
+ throw new GLib.Error(Gio.IOErrorEnum,
+ Gio.IOErrorEnum.NOT_SUPPORTED, 'Not supported');
+ }
+
+ vfunc_get_supported_types() {
+ return [];
+ }
+
+ _getFallbackCommandLine() {
+ return `gio open ${this.location?.get_uri()}`;
+ }
+
+ async _queryLocationIcons(params) {
+ const icons = {standard: null, custom: null};
+ if (!this.location)
+ return icons;
+
+ const cancellable = params.cancellable ?? this.cancellable;
+ const iconsQuery = [];
+ if (params?.standard)
+ iconsQuery.push(Gio.FILE_ATTRIBUTE_STANDARD_ICON);
+
+ if (params?.custom)
+ iconsQuery.push(ATTRIBUTE_METADATA_CUSTOM_ICON);
+
+ if (!iconsQuery.length)
+ throw new Error('Invalid Query Location Icons parameters');
+
+ let info;
+ try {
+ if (!GJS_SUPPORTS_FILE_IFACE_PROMISES) {
+ Gio._promisify(this.location.constructor.prototype,
+ 'query_info_async', 'query_info_finish');
+ }
+ info = await this.location.query_info_async(
+ iconsQuery.join(','),
+ Gio.FileQueryInfoFlags.NONE,
+ GLib.PRIORITY_LOW, cancellable);
+ if (info.has_attribute(Gio.FILE_ATTRIBUTE_STANDARD_ICON))
+ icons.standard = info.get_icon();
+ } catch (e) {
+ if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND) ||
+ e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_MOUNTED))
+ return icons;
+ throw e;
+ }
+
+ const customIcon = info.get_attribute_string(ATTRIBUTE_METADATA_CUSTOM_ICON);
+ if (customIcon) {
+ const customIconFile = GLib.uri_parse_scheme(customIcon)
+ ? Gio.File.new_for_uri(customIcon) : Gio.File.new_for_path(customIcon);
+ const iconFileInfo = await customIconFile.query_info_async(
+ Gio.FILE_ATTRIBUTE_STANDARD_TYPE,
+ Gio.FileQueryInfoFlags.NONE,
+ GLib.PRIORITY_LOW, cancellable);
+
+ if (iconFileInfo.get_file_type() === Gio.FileType.REGULAR)
+ icons.custom = Gio.FileIcon.new(customIconFile);
+ }
+
+ return icons;
+ }
+
+ async _updateLocationIcon(params = {standard: true, custom: true}) {
+ const cancellable = new Utils.CancellableChild(this.cancellable);
+
+ try {
+ this._updateIconCancellable?.cancel();
+ this._updateIconCancellable = cancellable;
+
+ const icons = await this._queryLocationIcons({cancellable, ...params});
+ const icon = icons.custom ?? icons.standard;
+
+ if (icon && !icon.equal(this.icon))
+ this.icon = icon;
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ logError(e, 'Impossible to update icon for %s'.format(this.get_id()));
+ } finally {
+ cancellable.cancel();
+ if (this._updateIconCancellable === cancellable)
+ delete this._updateIconCancellable;
+ }
+ }
+
+ async _getHandlerAppAsync(cancellable) {
+ if (!this.location)
+ return null;
+
+ try {
+ if (!GJS_SUPPORTS_FILE_IFACE_PROMISES) {
+ Gio._promisify(this.location.constructor.prototype,
+ 'query_default_handler_async',
+ 'query_default_handler_finish');
+ }
+
+ return await this.location.query_default_handler_async(
+ GLib.PRIORITY_DEFAULT, cancellable);
+ } catch (e) {
+ if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_MOUNTED))
+ return getFileManagerApp()?.appInfo;
+
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
+ logError(e, 'Impossible to find an URI handler for %s'.format(
+ this.get_id()));
+ }
+
+ throw e;
+ }
+ }
+
+ _getHandlerAppFromWorker(cancellable) {
+ const locationsWorker = GLib.build_filenamev([
+ Docking.DockManager.extension.path,
+ 'locationsWorker.js',
+ ]);
+ const locationsWorkerArgs = [LocationAppInfo.GJS_BINARY_PATH, '-m',
+ locationsWorker, 'handler', this.location.get_uri(),
+ '--timeout', `${LAUNCH_HANDLER_MAX_WAIT}`];
+ const subProcess = Gio.Subprocess.new(locationsWorkerArgs,
+ Gio.SubprocessFlags.STDOUT_PIPE | Gio.SubprocessFlags.STDERR_PIPE);
+
+ try {
+ const [, stdOut, stdErr] = subProcess.communicate(null, cancellable);
+ subProcess.wait(cancellable);
+ const errorCode = subProcess.get_exit_status();
+ const textDecoder = new TextDecoder();
+
+ if (errorCode) {
+ const errorLines = textDecoder.decode(stdErr.toArray()).split('\n');
+ const error = new GLib.Error(Gio.IOErrorEnum,
+ errorCode === GLib.MAXUINT8 ? 0 : errorCode, errorLines[0]);
+ error.stack = `${errorLines.slice(3).join('\n')}${error.stack}`;
+ throw error;
+ }
+
+ const desktopId = textDecoder.decode(stdOut.toArray()).trim();
+ const handlerApp = Shell.AppSystem.get_default().lookup_app(desktopId)?.appInfo;
+ return handlerApp;
+ } finally {
+ subProcess.force_exit();
+ }
+ }
+
+ getHandlerApp() {
+ if (this._handlerApp)
+ return this._handlerApp;
+
+ if (!this.location)
+ return null;
+
+ const cancellable = new Utils.CancellableChild(this.cancellable);
+
+ try {
+ if (LocationAppInfo.GJS_BINARY_PATH)
+ this._handlerApp = this._getHandlerAppFromWorker(cancellable);
+ else
+ this._handlerApp = this.location.query_default_handler(cancellable);
+
+ if (!this._handlerApp) {
+ throw new GLib.Error(Gio.IOErrorEnum,
+ Gio.IOErrorEnum.NOT_FOUND, `Handler for ${this.location} not found`);
+ }
+ } catch (e) {
+ if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_MOUNTED))
+ return getFileManagerApp()?.appInfo;
+
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
+ logError(e, 'Impossible to find an URI handler for %s'.format(
+ this.get_id()));
+ }
+
+ throw e;
+ }
+
+ return this._handlerApp;
+ }
+
+ destroy() {
+ this.location = null;
+ this.icon = null;
+ this.name = null;
+ this._handlerApp = null;
+ this.cancellable?.cancel();
+ }
+});
+
+const RemovableAction = Object.freeze({
+ MOUNT: 'mount',
+ UNMOUNT: 'unmount',
+ EJECT: 'eject',
+});
+
+const MountableVolumeAppInfo = GObject.registerClass({
+ Implements: [Gio.AppInfo],
+ Properties: {
+ 'volume': GObject.ParamSpec.object(
+ 'volume', 'volume', 'volume',
+ GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
+ Gio.Volume.$gtype),
+ 'mount': GObject.ParamSpec.object(
+ 'mount', 'mount', 'mount',
+ GObject.ParamFlags.READWRITE,
+ Gio.Mount.$gtype),
+ 'busy': GObject.ParamSpec.boolean(
+ 'busy', 'busy', 'busy',
+ GObject.ParamFlags.READWRITE,
+ false),
+ },
+},
+class MountableVolumeAppInfo extends LocationAppInfo {
+ _init(volume, cancellable = null) {
+ super._init({
+ volume,
+ cancellable,
+ });
+
+ this._signalsHandler = new Utils.GlobalSignalsHandler();
+
+ const updateAndMonitor = () => {
+ this._update();
+ this._monitorChanges();
+ };
+ updateAndMonitor();
+ this._mountChanged = this.connect('notify::mount', updateAndMonitor);
+
+ if (!this.mount && this.volume.get_identifier('class') === 'network') {
+ // For some devices the mount point isn't advertised promptly
+ // even if it's already existing, and there's no signaling about
+ this._lazyUpdater = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 2, () => {
+ this._update();
+ delete this._lazyUpdater;
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+ }
+
+ get busy() {
+ return !!this._currentAction;
+ }
+
+ get currentAction() {
+ return this._currentAction;
+ }
+
+ destroy() {
+ if (this._lazyUpdater) {
+ GLib.source_remove(this._lazyUpdater);
+ delete this._lazyUpdater;
+ }
+ this.disconnect(this._mountChanged);
+ this.mount = null;
+ this._signalsHandler.destroy();
+
+ super.destroy();
+ }
+
+ vfunc_dup() {
+ return new MountableVolumeAppInfo({
+ volume: this.volume,
+ cancellable: this.cancellable,
+ });
+ }
+
+ vfunc_get_id() {
+ const uuid = this.mount?.get_uuid() ?? this.volume.get_uuid();
+ return uuid ? 'mountable-volume:%s'.format(uuid) : super.vfunc_get_id();
+ }
+
+ vfunc_equal(other) {
+ if (this.volume === other?.volume && this.mount === other?.mount)
+ return true;
+
+ return this.get_id() === other?.get_id();
+ }
+
+ list_actions() {
+ const actions = [];
+ const {mount} = this;
+
+ if (mount) {
+ if (this.mount.can_unmount())
+ actions.push(RemovableAction.UNMOUNT);
+ if (this.mount.can_eject())
+ actions.push(RemovableAction.EJECT);
+
+ return actions;
+ }
+
+ if (this.volume.can_mount())
+ actions.push(RemovableAction.MOUNT);
+ if (this.volume.can_eject())
+ actions.push(RemovableAction.EJECT);
+
+ return actions;
+ }
+
+ get_action_name(action) {
+ switch (action) {
+ case RemovableAction.MOUNT:
+ return __('Mount');
+ case RemovableAction.UNMOUNT:
+ return __('Unmount');
+ case RemovableAction.EJECT:
+ return __('Eject');
+ default:
+ return null;
+ }
+ }
+
+ vfunc_launch(files, context) {
+ if (this.mount || files?.length)
+ return super.vfunc_launch(files, context);
+
+ this.mountAndLaunch(files, context);
+ return true;
+ }
+
+ _update() {
+ this.mount = this.volume.get_mount();
+
+ const removable = this.mount ?? this.volume;
+ this.name = removable.get_name();
+ this.icon = removable.get_icon();
+
+ this.location = this.mount?.get_default_location() ??
+ this.volume.get_activation_root();
+
+ this._updateLocationIcon({custom: true});
+ }
+
+ _monitorChanges() {
+ this._signalsHandler.destroy();
+
+ const removable = this.mount ?? this.volume;
+ this._signalsHandler.add(removable, 'changed', () => this._update());
+
+ if (this.mount) {
+ this._signalsHandler.add(this.mount, 'pre-unmount', () => this._update());
+ this._signalsHandler.add(this.mount, 'unmounted', () => this._update());
+ }
+ }
+
+ async mountAndLaunch(files, context) {
+ if (this.mount)
+ return super.vfunc_launch(files, context);
+
+ try {
+ await this.launchAction(RemovableAction.MOUNT);
+ if (!this.mount) {
+ throw new Error('No mounted location to open for %s'.format(
+ this.get_id()));
+ }
+
+ return super.vfunc_launch(files, context);
+ } catch (e) {
+ logError(e, 'Mount and launch %s'.format(this.get_id()));
+ return false;
+ }
+ }
+
+ _notifyActionError(action, message) {
+ switch (action) {
+ case RemovableAction.MOUNT:
+ global.notify_error(__('Failed to mount “%s”'.format(
+ this.get_name())), message);
+ break;
+
+ case RemovableAction.UNMOUNT:
+ global.notify_error(__('Failed to unmount “%s”'.format(
+ this.get_name())), message);
+ break;
+
+ case RemovableAction.EJECT:
+ global.notify_error(__('Failed to eject “%s”'.format(
+ this.get_name())), message);
+ break;
+ }
+ }
+
+ async launchAction(action) {
+ if (!this.list_actions().includes(action))
+ throw new Error('Action %s is not supported by %s', action, this);
+
+ switch (this._currentAction) {
+ case RemovableAction.MOUNT:
+ this._notifyActionError(action,
+ __('Mount operation already in progress'));
+ break;
+
+ case RemovableAction.UNMOUNT:
+ this._notifyActionError(action,
+ __('Unmount operation already in progress'));
+ break;
+
+ case RemovableAction.EJECT:
+ this._notifyActionError(action,
+ __('Eject operation already in progress'));
+ break;
+
+ default:
+ if (this._currentAction) {
+ throw new Error('Another action %s is being performed in %s'.format(
+ this._currentAction, this));
+ }
+ }
+
+ this._currentAction = action;
+ this.notify('busy');
+ const removable = this.mount ?? this.volume;
+ const operation = new ShellMountOperation.ShellMountOperation(removable);
+ try {
+ switch (action) {
+ case RemovableAction.MOUNT:
+ await this.volume.mount(Gio.MountMountFlags.NONE, operation.mountOp,
+ this.cancellable);
+ return true;
+
+ case RemovableAction.UNMOUNT:
+ await this.mount.unmount_with_operation(Gio.MountUnmountFlags.FORCE,
+ operation.mountOp, this.cancellable);
+ return true;
+
+ case RemovableAction.EJECT:
+ await removable.eject_with_operation(Gio.MountUnmountFlags.FORCE,
+ operation.mountOp, this.cancellable);
+ return true;
+
+ default:
+ logError(new Error(), 'No action %s on removable %s'.format(action,
+ removable.get_name()));
+ return false;
+ }
+ } catch (e) {
+ if (action === RemovableAction.MOUNT &&
+ e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.ALREADY_MOUNTED))
+ return true;
+ else if (action === RemovableAction.UNMOUNT &&
+ e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_MOUNTED))
+ return true;
+
+ if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED))
+ this._notifyActionError(action, e.message);
+
+ if (action === RemovableAction.MOUNT && this._isEncryptedMountError(e)) {
+ delete this._currentAction;
+ operation.close();
+ return this.launchAction(action);
+ }
+
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
+ logError(e, 'Impossible to %s removable %s'.format(action,
+ removable.get_name()));
+ }
+
+ return false;
+ } finally {
+ delete this._currentAction;
+ this.notify('busy');
+ this._update();
+ operation.close();
+ }
+ }
+
+ _isEncryptedMountError(error) {
+ // FIXME: we will always get G_IO_ERROR_FAILED from the gvfs udisks
+ // backend, see https://bugs.freedesktop.org/show_bug.cgi?id=51271
+
+ if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED))
+ return false;
+
+ // cryptsetup
+ if (error.message.includes('No key available with this passphrase'))
+ return true;
+
+ // udisks (no password)
+ if (error.message.includes('No key available to unlock device'))
+ return true;
+
+ // libblockdev wrong password opening LUKS device
+ if (error.message.includes('Failed to activate device: Incorrect passphrase'))
+ return true;
+
+ // cryptsetup returns EINVAL in many cases, including wrong TCRYPT password/parameters
+ if (error.message.includes('Failed to load device\'s parameters: Invalid argument') ||
+ error.message.includes(`Failed to load device's parameters: ${GLib.strerror(22 /* EINVAL */)}`))
+ return true;
+
+ // cryptsetup returns EPERM when the TCRYPT header can't be decrypted
+ // with the provided password/parameters.
+ if (error.message.includes('Failed to load device\'s parameters: Operation not permitted') ||
+ error.message.includes(`Failed to load device's parameters: ${GLib.strerror(1 /* EPERM */)}`))
+ return true;
+
+ return false;
+ }
+});
+
+const TrashAppInfo = GObject.registerClass({
+ Implements: [Gio.AppInfo],
+ Properties: {
+ 'empty': GObject.ParamSpec.boolean(
+ 'empty', 'empty', 'empty',
+ GObject.ParamFlags.READWRITE,
+ true),
+ },
+},
+class TrashAppInfo extends LocationAppInfo {
+ static initPromises(file) {
+ if (TrashAppInfo._promisified)
+ return;
+
+ const trashProto = file.constructor.prototype;
+ Gio._promisify(Gio.FileEnumerator.prototype, 'close_async', 'close_finish');
+ Gio._promisify(Gio.FileEnumerator.prototype, 'next_files_async', 'next_files_finish');
+ Gio._promisify(trashProto, 'enumerate_children_async', 'enumerate_children_finish');
+ Gio._promisify(trashProto, 'query_info_async', 'query_info_finish');
+ TrashAppInfo._promisified = true;
+ }
+
+ _init(cancellable = null) {
+ super._init({
+ location: Gio.file_new_for_uri(TRASH_URI),
+ name: __('Trash'),
+ icon: Gio.ThemedIcon.new(FALLBACK_TRASH_ICON),
+ cancellable,
+ });
+ TrashAppInfo.initPromises(this.location);
+
+ try {
+ this._monitor = this.location.monitor_directory(0, this.cancellable);
+ this._schedUpdateId = 0;
+ this._monitorChangedId = this._monitor.connect('changed', () =>
+ this._onTrashChange());
+ } catch (e) {
+ if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ return;
+ logError(e, 'Impossible to monitor trash');
+ }
+ this._updateTrash();
+
+ this.connect('notify::empty', () => this._updateLocationIcon());
+ this.notify('empty');
+ }
+
+ destroy() {
+ if (this._schedUpdateId) {
+ GLib.source_remove(this._schedUpdateId);
+ this._schedUpdateId = 0;
+ }
+ this._updateTrashCancellable?.cancel();
+ this._monitor?.disconnect(this._monitorChangedId);
+ this._monitor = null;
+
+ super.destroy();
+ }
+
+ list_actions() {
+ return this.empty ? [] : ['empty-trash'];
+ }
+
+ get_action_name(action) {
+ switch (action) {
+ case 'empty-trash':
+ return __('Empty Trash');
+ default:
+ return null;
+ }
+ }
+
+ _onTrashChange() {
+ if (this._schedUpdateId) {
+ GLib.source_remove(this._schedUpdateId);
+ this._schedUpdateId = 0;
+ }
+
+ if (this._monitor.is_cancelled())
+ return;
+
+ this._schedUpdateId = GLib.timeout_add(GLib.PRIORITY_LOW,
+ UPDATE_TRASH_DELAY, () => {
+ this._schedUpdateId = 0;
+ this._updateTrash();
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+
+ async _updateTrash() {
+ const priority = GLib.PRIORITY_LOW;
+ this._updateTrashCancellable?.cancel();
+ const cancellable = new Utils.CancellableChild(this.cancellable);
+ this._updateTrashCancellable = cancellable;
+
+ try {
+ const trashInfo = await this.location.query_info_async(
+ Gio.FILE_ATTRIBUTE_TRASH_ITEM_COUNT,
+ Gio.FileQueryInfoFlags.NONE,
+ priority, cancellable);
+ this.empty = !trashInfo.get_attribute_uint32(
+ Gio.FILE_ATTRIBUTE_TRASH_ITEM_COUNT);
+ return;
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ logError(e, 'Impossible to get trash children from infos');
+ } finally {
+ cancellable.cancel();
+ if (this._updateIconCancellable === cancellable)
+ delete this._updateTrashCancellable;
+ }
+
+ try {
+ const childrenEnumerator = await this.location.enumerate_children_async(
+ Gio.FILE_ATTRIBUTE_STANDARD_TYPE, Gio.FileQueryInfoFlags.NONE,
+ priority, cancellable);
+ const children = await childrenEnumerator.next_files_async(1,
+ priority, cancellable);
+ this.empty = !children.length;
+
+ await childrenEnumerator.close_async(priority, null);
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ logError(e, 'Impossible to enumerate trash children');
+ } finally {
+ cancellable.cancel();
+ if (this._updateIconCancellable === cancellable)
+ delete this._updateTrashCancellable;
+ }
+ }
+
+ launchAction(action, timestamp) {
+ if (!this.list_actions().includes(action))
+ throw new Error('Action %s is not supported by %s', action, this);
+
+ const nautilus = makeNautilusFileOperationsProxy();
+ const askConfirmation = true;
+ nautilus.EmptyTrashRemote(askConfirmation,
+ nautilus.platformData({timestamp}), (_p, error) => {
+ if (error)
+ logError(error, 'Empty trash failed');
+ }, this.cancellable);
+ }
+});
+
+/**
+ * @param shellApp
+ */
+function wrapWindowsBackedApp(shellApp) {
+ if (shellApp._dtdData)
+ throw new Error('%s has been already wrapped'.format(shellApp));
+
+ shellApp._dtdData = {
+ windows: [],
+ state: undefined,
+ startingWorkspace: 0,
+ isFocused: false,
+ proxyProperties: [],
+ sources: new Set(),
+ signalConnections: new Utils.GlobalSignalsHandler(),
+ methodInjections: new Utils.InjectionsHandler(),
+ propertyInjections: new Utils.PropertyInjectionsHandler(),
+ addProxyProperties(parent, proxyProperties) {
+ Object.entries(proxyProperties).forEach(([p, o]) => {
+ const publicProp = o.public ? p : `_${p}`;
+ const get = o.getter && o.value instanceof Function
+ ? () => this[p]() : () => this[p];
+ Object.defineProperty(parent, publicProp, Object.assign({
+ get,
+ set: v => (this[p] = v),
+ configurable: true,
+ enumerable: !!o.enumerable,
+ }, o.readOnly ? {set: undefined} : {}));
+ if (o.value)
+ this[p] = o.value;
+ this.proxyProperties.push(publicProp);
+ });
+ },
+ destroy() {
+ this.windows = [];
+ this.proxyProperties = [];
+ this.sources.forEach(s => GLib.source_remove(s));
+ this.sources.clear();
+ this.signalConnections.destroy();
+ this.methodInjections.destroy();
+ this.propertyInjections.destroy();
+ },
+ };
+
+ shellApp._dtdData.addProxyProperties(shellApp, {
+ windows: {},
+ state: {},
+ startingWorkspace: {},
+ isFocused: {public: true},
+ signalConnections: {readOnly: true},
+ sources: {readOnly: true},
+ checkFocused: {},
+ setDtdData: {},
+ });
+
+ shellApp._setDtdData = function (data, params = {}) {
+ for (const [name, value] of Object.entries(data)) {
+ if (params.readOnly && name in this._dtdData)
+ throw new Error('Property %s is already defined'.format(name));
+ const defaultParams = {public: true, readOnly: true};
+ this._dtdData.addProxyProperties(this, {
+ [name]: {...defaultParams, ...params, value},
+ });
+ }
+ };
+
+ const m = (...args) => shellApp._dtdData.methodInjections.add(shellApp, ...args);
+ const p = (...args) => shellApp._dtdData.propertyInjections.add(shellApp, ...args);
+
+ // mi is Method injector, pi is Property injector
+ shellApp._setDtdData({mi: m, pi: p}, {public: false});
+
+ m('get_state', () => shellApp._state ?? shellApp._getStateByWindows());
+ p('state', {get: () => shellApp.get_state()});
+
+ m('get_windows', () => shellApp._windows);
+ m('get_n_windows', () => shellApp._windows.length);
+ m('get_pids', () => shellApp._windows.reduce((pids, w) => {
+ if (w.get_pid() > 0 && !pids.includes(w.get_pid()))
+ pids.push(w.get_pid());
+ return pids;
+ }, []));
+ m('is_on_workspace', (_om, workspace) => shellApp._windows.some(w =>
+ w.get_workspace() === workspace) ||
+ (shellApp.state === Shell.AppState.STARTING &&
+ [-1, workspace.index()].includes(shellApp._startingWorkspace)));
+ m('request_quit', () => shellApp._windows.filter(w =>
+ w.can_close()).forEach(w => w.delete(global.get_current_time())));
+
+ shellApp._setDtdData({
+ _getStateByWindows() {
+ return this.get_n_windows() ? Shell.AppState.RUNNING : Shell.AppState.STOPPED;
+ },
+
+ _updateWindows() {
+ throw new GObject.NotImplementedError(`_updateWindows in ${this.constructor.name}`);
+ },
+
+ _notifyStateChanged() {
+ Shell.AppSystem.get_default().emit('app-state-changed', this);
+ this.notify('state');
+ },
+
+ _setState(state) {
+ const oldState = this.state;
+ this._state = state;
+
+ if (this.state !== oldState)
+ this._notifyStateChanged();
+ },
+
+ _setWindows(windows) {
+ const oldState = this.state;
+ const oldWindows = this._windows.slice();
+ const result = {windowsChanged: false, stateChanged: false};
+ this._state = undefined;
+
+ if (windows.length !== oldWindows.length ||
+ windows.some((win, index) => win !== oldWindows[index])) {
+ this._windows = windows.filter(w => !w.is_override_redirect());
+ this.emit('windows-changed');
+ result.windowsChanged = true;
+ }
+
+ if (this.state !== oldState) {
+ this._notifyStateChanged();
+ this._checkFocused();
+ result.stateChanged = true;
+ }
+
+ return result;
+ },
+ }, {readOnly: false});
+
+ shellApp._sources.add(GLib.idle_add(GLib.DEFAULT_PRIORITY, () => {
+ shellApp._updateWindows();
+ shellApp._sources.delete(GLib.main_current_source().source_id);
+ return GLib.SOURCE_REMOVE;
+ }));
+
+ const windowTracker = Shell.WindowTracker.get_default();
+ shellApp._checkFocused = function () {
+ if (this._windows.some(w => w.has_focus())) {
+ this.isFocused = true;
+ windowTracker.notify('focus-app');
+ } else if (this.isFocused) {
+ this.isFocused = false;
+ windowTracker.notify('focus-app');
+ }
+ };
+
+ shellApp._checkFocused();
+ shellApp._signalConnections.add(global.display, 'notify::focus-window', () =>
+ shellApp._checkFocused());
+
+ // Re-implements shell_app_activate_window for generic activation and alt-tab support
+ m('activate_window', function (_om, window, timestamp) {
+ /* eslint-disable no-invalid-this */
+ if (!window)
+ [window] = this.get_windows();
+ else if (!this._windows.includes(window))
+ return;
+
+ const currentWorkspace = global.workspace_manager.get_active_workspace();
+ const workspace = window.get_workspace();
+ const sameWorkspaceWindows = this.get_windows().filter(w =>
+ w.get_workspace() === workspace);
+ sameWorkspaceWindows.forEach(w => w.raise());
+
+ if (workspace !== currentWorkspace)
+ workspace.activate_with_focus(window, timestamp);
+ else
+ window.activate(timestamp);
+ /* eslint-enable no-invalid-this */
+ });
+
+ // Re-implements shell_app_activate_full for generic activation and dash support
+ m('activate_full', function (_om, workspace, timestamp) {
+ /* eslint-disable no-invalid-this */
+ if (!timestamp)
+ timestamp = global.get_current_time();
+
+ switch (this.state) {
+ case Shell.AppState.STOPPED:
+ try {
+ this._startingWorkspace = workspace;
+ this._setState(Shell.AppState.STARTING);
+ this.launch(timestamp, workspace, Shell.AppLaunchGpu.APP_PREF);
+ } catch (e) {
+ logError(e);
+ this._setState(Shell.AppState.STOPPED);
+ global.notify_error(_('Failed to launch “%s”'.format(
+ this.get_name())), e.message);
+ }
+ break;
+ case Shell.AppState.RUNNING:
+ this.activate_window(null, timestamp);
+ break;
+ }
+ /* eslint-enable no-invalid-this */
+ });
+
+ m('activate', () => shellApp.activate_full(-1, 0));
+
+ m('compare', (_om, other) => Utils.shellAppCompare(shellApp, other));
+
+ const {destroy: defaultDestroy} = shellApp;
+ shellApp.destroy = function () {
+ /* eslint-disable no-invalid-this */
+ this._dtdData.proxyProperties.forEach(prop => delete this[prop]);
+ this._dtdData.destroy();
+ this._dtdData = undefined;
+ this.appInfo.destroy?.();
+ this.destroy = defaultDestroy;
+ defaultDestroy?.call(this);
+ /* eslint-enable no-invalid-this */
+ };
+
+ return shellApp;
+}
+
+/**
+ * We can't inherit from Shell.App as it's a final type, so let's patch it
+ *
+ * @param params
+ */
+function makeLocationApp(params) {
+ if (!(params?.appInfo instanceof LocationAppInfo))
+ throw new TypeError('Invalid location');
+
+ const {fallbackIconName} = params;
+ delete params.fallbackIconName;
+
+ const shellApp = new Shell.App(params);
+ wrapWindowsBackedApp(shellApp);
+
+ shellApp._setDtdData({
+ location: () => shellApp.appInfo.location,
+ isTrash: shellApp.appInfo instanceof TrashAppInfo,
+ }, {getter: true, enumerable: true});
+
+ shellApp._mi('toString', defaultToString =>
+ '[LocationApp "%s" - %s]'.format(shellApp.get_id(),
+ defaultToString.call(shellApp)));
+
+ shellApp._mi('launch', (_om, timestamp, workspace, _gpuPref) =>
+ shellApp.appInfo.launch([],
+ global.create_app_launch_context(timestamp, workspace)));
+
+ shellApp._mi('launch_action', (_om, actionName, ...args) =>
+ shellApp.appInfo.launchAction(actionName, ...args));
+
+ shellApp._mi('create_icon_texture', (_om, iconSize) => new St.Icon({
+ iconSize,
+ gicon: shellApp.icon,
+ fallbackIconName,
+ }));
+
+ // FIXME: We need to add a new API to Nautilus to open new windows
+ shellApp._mi('can_open_new_window', () => {
+ try {
+ if (!shellApp.get_n_windows())
+ return true;
+
+ const handlerApp = shellApp.appInfo.getHandlerApp();
+
+ if (handlerApp.has_key('SingleMainWindow'))
+ return !handlerApp.get_boolean('SingleMainWindow');
+
+ if (handlerApp.has_key('X-GNOME-SingleWindow'))
+ return !handlerApp.get_boolean('X-GNOME-SingleWindow');
+
+ if (handlerApp.get_commandline()?.split(' ').includes('--new-window'))
+ return true;
+
+ const [window] = shellApp.get_windows();
+ if (window && window.get_gtk_window_object_path())
+ return window.get_gtk_application_id() === null;
+
+ return true;
+ } catch {
+ return false;
+ }
+ });
+
+ shellApp._mi('open_new_window', function (_om, workspace) {
+ /* eslint-disable no-invalid-this */
+ const context = global.create_app_launch_context(0, workspace);
+ if (!this.get_n_windows()) {
+ this.appInfo.launch([], context);
+ return;
+ }
+ const appId = this.appInfo.get_id();
+ Gio.AppInfo.create_from_commandline(this.appInfo.get_commandline(),
+ this.appInfo.get_id(), appId
+ ? Gio.AppInfoCreateFlags.SUPPORTS_STARTUP_NOTIFICATION
+ : Gio.AppInfoCreateFlags.NONE).launch_uris(
+ [this.appInfo.location.get_uri()], context);
+ /* eslint-enable no-invalid-this */
+ });
+
+ if (shellApp.appInfo instanceof MountableVolumeAppInfo) {
+ shellApp._mi('get_busy', function (parentGetBusy) {
+ /* eslint-disable no-invalid-this */
+ if (this.appInfo.busy)
+ return true;
+ return parentGetBusy.call(this);
+ /* eslint-enable no-invalid-this */
+ });
+ shellApp._pi('busy', {get: () => shellApp.get_busy()});
+ shellApp._signalConnections.add(shellApp.appInfo, 'notify::busy', _ =>
+ shellApp.notify('busy'));
+ }
+
+ shellApp._mi('get_windows', function () {
+ /* eslint-disable no-invalid-this */
+ if (this._needsResort)
+ this._sortWindows();
+ return this._windows;
+ /* eslint-enable no-invalid-this */
+ });
+
+ const {fm1Client} = Docking.DockManager.getDefault();
+ shellApp._setDtdData({
+ _needsResort: true,
+
+ _windowsOrderChanged() {
+ this._needsResort = true;
+ this.emit('windows-changed');
+ },
+
+ _sortWindows() {
+ this._windows.sort(Utils.shellWindowsCompare);
+ this._needsResort = false;
+ },
+
+ _updateWindows() {
+ const windows = fm1Client.getWindows(this.location?.get_uri()).sort(
+ Utils.shellWindowsCompare);
+ const {windowsChanged} = this._setWindows(windows);
+
+ if (!windowsChanged)
+ return;
+
+ this._signalConnections.removeWithLabel(Labels.LOCATION_WINDOWS);
+ windows.forEach(w =>
+ this._signalConnections.addWithLabel(Labels.LOCATION_WINDOWS, w,
+ 'notify::user-time', () => {
+ if (w !== this._windows[0])
+ this._windowsOrderChanged();
+ }));
+ },
+ }, {readOnly: false});
+
+ shellApp._signalConnections.add(fm1Client, 'windows-changed', () =>
+ shellApp._updateWindows());
+ shellApp._signalConnections.add(shellApp.appInfo, 'notify::icon', () =>
+ shellApp.notify('icon'));
+ shellApp._signalConnections.add(global.workspaceManager,
+ 'workspace-switched', () => shellApp._windowsOrderChanged());
+
+ return shellApp;
+}
+
+/**
+ *
+ */
+function getFileManagerApp() {
+ return Shell.AppSystem.get_default().lookup_app(FILE_MANAGER_DESKTOP_APP_ID);
+}
+
+/**
+ *
+ */
+export function wrapFileManagerApp() {
+ const fileManagerApp = getFileManagerApp();
+ if (!fileManagerApp)
+ return null;
+
+ if (fileManagerApp._dtdData)
+ return fileManagerApp;
+
+ const originalGetWindows = fileManagerApp.get_windows;
+ wrapWindowsBackedApp(fileManagerApp);
+
+ const {removables, trash} = Docking.DockManager.getDefault();
+ fileManagerApp._signalConnections.addWithLabel(Labels.WINDOWS_CHANGED,
+ fileManagerApp, 'windows-changed', () => {
+ fileManagerApp.stop_emission_by_name('windows-changed');
+ // Let's wait for the location app to take control before of us
+ const id = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 50, () => {
+ fileManagerApp._sources.delete(id);
+ fileManagerApp._updateWindows();
+ return GLib.SOURCE_REMOVE;
+ });
+ fileManagerApp._sources.add(id);
+ });
+
+ fileManagerApp._signalConnections.add(global.workspaceManager,
+ 'workspace-switched', () => {
+ fileManagerApp._signalConnections.blockWithLabel(Labels.WINDOWS_CHANGED);
+ fileManagerApp.emit('windows-changed');
+ fileManagerApp._signalConnections.unblockWithLabel(Labels.WINDOWS_CHANGED);
+ });
+
+ if (removables) {
+ fileManagerApp._signalConnections.add(removables, 'changed', () =>
+ fileManagerApp._updateWindows());
+ fileManagerApp._signalConnections.add(removables, 'windows-changed', () =>
+ fileManagerApp._updateWindows());
+ }
+
+ if (trash?.getApp()) {
+ fileManagerApp._signalConnections.add(trash.getApp(), 'windows-changed', () =>
+ fileManagerApp._updateWindows());
+ }
+
+ fileManagerApp._updateWindows = function () {
+ const locationWindows = [];
+ getRunningApps().forEach(a => locationWindows.push(...a.get_windows()));
+ const windows = originalGetWindows.call(this).filter(w =>
+ !locationWindows.includes(w));
+
+ this._signalConnections.blockWithLabel(Labels.WINDOWS_CHANGED);
+ this._setWindows(windows);
+ this._signalConnections.unblockWithLabel(Labels.WINDOWS_CHANGED);
+ };
+
+ fileManagerApp._mi('toString', defaultToString =>
+ '[FileManagerApp - %s]'.format(defaultToString.call(fileManagerApp)));
+
+ return fileManagerApp;
+}
+
+/**
+ *
+ */
+export function unWrapFileManagerApp() {
+ const fileManagerApp = getFileManagerApp();
+ if (!fileManagerApp || !fileManagerApp._dtdData)
+ return;
+
+ fileManagerApp.destroy();
+}
+
+/**
+ * This class maintains a Shell.App representing the Trash and keeps it
+ * up-to-date as the trash fills and is emptied over time.
+ */
+export class Trash {
+ destroy() {
+ this._trashApp?.destroy();
+ }
+
+ _ensureApp() {
+ if (this._trashApp)
+ return;
+
+ this._trashApp = makeLocationApp({
+ appInfo: new TrashAppInfo(new Gio.Cancellable()),
+ fallbackIconName: FALLBACK_TRASH_ICON,
+ });
+ }
+
+ getApp() {
+ this._ensureApp();
+ return this._trashApp;
+ }
+}
+
+/**
+ * This class maintains Shell.App representations for removable devices
+ * plugged into the system, and keeps the list of Apps up-to-date as
+ * devices come and go and are mounted and unmounted.
+ */
+export class Removables {
+ static initVolumePromises(object) {
+ // TODO: This can be simplified using actual interface type when we
+ // can depend on gjs 1.72
+ if (!(object instanceof Gio.Volume) || object.constructor.prototype._d2dPromisified)
+ return;
+
+ Gio._promisify(object.constructor.prototype, 'mount', 'mount_finish');
+ Gio._promisify(object.constructor.prototype, 'eject_with_operation',
+ 'eject_with_operation_finish');
+ object.constructor.prototype._d2dPromisified = true;
+ }
+
+ static initMountPromises(object) {
+ // TODO: This can be simplified using actual interface type when we
+ // can depend on gjs 1.72
+ if (!(object instanceof Gio.Mount) || object.constructor.prototype._d2dPromisified)
+ return;
+
+ Gio._promisify(object.constructor.prototype, 'eject_with_operation',
+ 'eject_with_operation_finish');
+ Gio._promisify(object.constructor.prototype, 'unmount_with_operation',
+ 'unmount_with_operation_finish');
+ object.constructor.prototype._d2dPromisified = true;
+ }
+
+ constructor() {
+ this._signalsHandler = new Utils.GlobalSignalsHandler();
+
+ this._monitor = Gio.VolumeMonitor.get();
+ this._cancellable = new Gio.Cancellable();
+
+ this._monitor.get_mounts().forEach(m => Removables.initMountPromises(m));
+ this._updateVolumes();
+
+ this._signalsHandler.add([
+ this._monitor,
+ 'volume-added',
+ (_, volume) => this._onVolumeAdded(volume),
+ ], [
+ this._monitor,
+ 'volume-removed',
+ (_, volume) => this._onVolumeRemoved(volume),
+ ], [
+ this._monitor,
+ 'mount-added',
+ (_, mount) => this._onMountAdded(mount),
+ ], [
+ Docking.DockManager.settings,
+ 'changed::show-mounts-only-mounted',
+ () => this._updateVolumes(),
+ ], [
+ Docking.DockManager.settings,
+ 'changed::show-mounts-network',
+ () => this._updateVolumes(),
+ ]);
+ }
+
+ destroy() {
+ this._volumeApps.forEach(a => a.destroy());
+ this._volumeApps = [];
+ this._cancellable.cancel();
+ this._cancellable = null;
+ this._signalsHandler.destroy();
+ this._monitor = null;
+ }
+
+ _updateVolumes() {
+ this._volumeApps?.forEach(a => a.destroy());
+ this._volumeApps = [];
+ this.emit('changed');
+
+ this._monitor.get_volumes().forEach(v => this._onVolumeAdded(v));
+ }
+
+ _onVolumeAdded(volume) {
+ Removables.initVolumePromises(volume);
+
+ if (!Docking.DockManager.settings.showMountsNetwork &&
+ volume.get_identifier('class') === 'network')
+ return;
+
+
+ const mount = volume.get_mount();
+ if (mount) {
+ if (mount.is_shadowed())
+ return;
+ if (!mount.can_eject() && !mount.can_unmount())
+ return;
+ } else {
+ if (Docking.DockManager.settings.showMountsOnlyMounted)
+ return;
+ if (!volume.can_mount() && !volume.can_eject())
+ return;
+ }
+
+ const appInfo = new MountableVolumeAppInfo(volume,
+ new Utils.CancellableChild(this._cancellable));
+ const volumeApp = makeLocationApp({
+ appInfo,
+ fallbackIconName: FALLBACK_REMOVABLE_MEDIA_ICON,
+ });
+
+ volumeApp._signalConnections.add(volumeApp, 'windows-changed',
+ () => this.emit('windows-changed', volumeApp));
+
+ if (Docking.DockManager.settings.showMountsOnlyMounted) {
+ volumeApp._signalConnections.add(appInfo, 'notify::mount',
+ () => !appInfo.mount && this._onVolumeRemoved(appInfo.volume));
+ }
+
+ this._volumeApps.push(volumeApp);
+ this.emit('changed');
+ }
+
+ _onVolumeRemoved(volume) {
+ const volumeIndex = this._volumeApps.findIndex(({appInfo}) =>
+ appInfo.volume === volume);
+ if (volumeIndex !== -1) {
+ const [volumeApp] = this._volumeApps.splice(volumeIndex, 1);
+ // We don't care about cancelling the ongoing operations from now on.
+ volumeApp.appInfo.cancellable = null;
+ volumeApp.destroy();
+ this.emit('changed');
+ }
+ }
+
+ _onMountAdded(mount) {
+ Removables.initMountPromises(mount);
+
+ if (!Docking.DockManager.settings.showMountsOnlyMounted)
+ return;
+
+ if (!this._volumeApps.find(({appInfo}) => appInfo.mount === mount)) {
+ // In some Gio.Mount implementations the volume may be set after
+ // mount is emitted, so we could just ignore it as we'll get it
+ // later via volume-added
+ const volume = mount.get_volume();
+ if (volume)
+ this._onVolumeAdded(volume);
+ }
+ }
+
+ getApps() {
+ return this._volumeApps;
+ }
+}
+Signals.addSignalMethods(Removables.prototype);
+
+/**
+ *
+ */
+function getApps() {
+ const dockManager = Docking.DockManager.getDefault();
+ const locationApps = [];
+
+ if (dockManager.removables)
+ locationApps.push(...dockManager.removables.getApps());
+
+ if (dockManager.trash)
+ locationApps.push(dockManager.trash.getApp());
+
+ return locationApps;
+}
+
+/**
+ *
+ */
+export function getRunningApps() {
+ return getApps().filter(a => a.state === Shell.AppState.RUNNING);
+}
+
+/**
+ *
+ */
+export function getStartingApps() {
+ return getApps().filter(a => a.state === Shell.AppState.STARTING);
+}
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locationsWorker.js b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locationsWorker.js
new file mode 100644
index 0000000..054c92c
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locationsWorker.js
@@ -0,0 +1,91 @@
+#!/usr/bin/env gjs
+
+import GLib from 'gi://GLib';
+import Gio from 'gi://Gio';
+
+const GJS_SUPPORTS_FILE_IFACE_PROMISES = imports.system.version >= 17101;
+
+if (GJS_SUPPORTS_FILE_IFACE_PROMISES)
+ Gio._promisify(Gio.File.prototype, 'query_default_handler_async');
+
+function getHandlerAppAsync(location, cancellable) {
+ if (!location)
+ return null;
+
+ if (!GJS_SUPPORTS_FILE_IFACE_PROMISES) {
+ Gio._promisify(location.constructor.prototype,
+ 'query_default_handler_async',
+ 'query_default_handler_finish');
+ }
+
+ return location.query_default_handler_async(
+ GLib.PRIORITY_DEFAULT, cancellable);
+}
+
+async function mainAsync(argv) {
+ if (argv.length < 1) {
+ const currentBinary = GLib.path_get_basename(new Error().fileName);
+ printerr(`Usage: ${currentBinary} <action> uri [ --timeout <value> ]`);
+ return 1;
+ }
+
+ const [action, uri] = argv;
+ let timeout = 200;
+
+ if (action !== 'handler')
+ throw new TypeError(`Unexpected action ${action}`);
+
+ for (let i = 1; i < argv.length; ++i) {
+ if (argv[i] === '--timeout' && i < argv.length - 1)
+ timeout = argv[++i];
+ }
+
+ const location = Gio.File.new_for_uri(uri);
+ const cancellable = new Gio.Cancellable();
+
+ // GVfs providers could hang when querying the file information, so we
+ // workaround this by using the async API in a sync way, but we need to
+ // use a timeout to avoid this to hang forever, better than hang the
+ // shell.
+ let launchMaxWaitId;
+
+ try {
+ const handler = await Promise.race([
+ getHandlerAppAsync(location, cancellable),
+ new Promise((_resolve, reject) => {
+ launchMaxWaitId = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT, timeout, () => {
+ launchMaxWaitId = 0;
+ cancellable.cancel();
+ reject(new GLib.Error(Gio.IOErrorEnum,
+ Gio.IOErrorEnum.TIMED_OUT,
+ `Searching for ${location.get_uri()} ` +
+ 'handler took too long'));
+ return GLib.SOURCE_REMOVE;
+ });
+ }),
+ ]);
+
+ print(handler.get_id());
+ } catch (e) {
+ printerr(e.message);
+ logError(e);
+ return e.code ? e.code : GLib.MAXUINT8;
+ } finally {
+ if (launchMaxWaitId)
+ GLib.source_remove(launchMaxWaitId);
+ }
+
+ return 0;
+}
+
+function main(args) {
+ let ret;
+ const loop = new GLib.MainLoop(null, false);
+ mainAsync(args).then(r => (ret = r)).catch(logError).finally(() => loop.quit());
+ loop.run();
+
+ return ret;
+}
+
+imports.system.exit(main(ARGV));
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/media/glossy.svg b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/media/glossy.svg
new file mode 100644
index 0000000..55b71ba
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/media/glossy.svg
@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="18.343554mm"
+ height="18.343554mm"
+ viewBox="0 0 14.674843 14.674842"
+ version="1.1"
+ id="svg4941"
+ sodipodi:docname="glossy.svg"
+ inkscape:version="0.92.1 r15371">
+ <defs
+ id="defs4935">
+ <linearGradient
+ id="linearGradient6812"
+ inkscape:collect="always">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="0"
+ id="stop6810" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="1"
+ id="stop6808" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient18962"
+ id="linearGradient35463"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.29132751,0,0,0.15428114,-54.210829,160.22776)"
+ x1="214.71877"
+ y1="404.36081"
+ x2="214.71877"
+ y2="443.54596" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient18962">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop18964" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop18966" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient18806">
+ <stop
+ style="stop-color:#ff0101;stop-opacity:1;"
+ offset="0"
+ id="stop18808" />
+ <stop
+ style="stop-color:#800000;stop-opacity:1;"
+ offset="1"
+ id="stop18810" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient6812"
+ id="radialGradient6798"
+ cx="7.3538475"
+ cy="230.28426"
+ fx="7.3538475"
+ fy="230.28426"
+ r="7.2099228"
+ gradientTransform="matrix(5.9484829,-0.0346444,0.01679088,3.0681664,-40.338609,-476.01412)"
+ gradientUnits="userSpaceOnUse" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="10.68"
+ inkscape:cx="65.485107"
+ inkscape:cy="29.432163"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="2560"
+ inkscape:window-height="1406"
+ inkscape:window-x="1920"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ scale-x="0.8"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0" />
+ <metadata
+ id="metadata4938">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-222.92515)">
+ <rect
+ inkscape:export-ydpi="180"
+ inkscape:export-xdpi="180"
+ inkscape:export-filename="C:\Arbeit\Blog\Tutorials\glossybutton\Glossy_Button_Tutorial.png"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.35100002;fill:url(#linearGradient35463);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.39747861;marker:none;enable-background:accumulate"
+ id="rect19155"
+ width="14.634871"
+ height="3.7392156"
+ x="0.039808333"
+ y="222.98268"
+ rx="1.5496143"
+ ry="0.82064426" />
+ <rect
+ style="opacity:0.427;fill:url(#radialGradient6798);fill-opacity:1;stroke:#ffffff;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect6706"
+ width="14.363673"
+ height="14.404656"
+ x="0.090466507"
+ y="223.07919" />
+ </g>
+</svg>
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/media/highlight_stacked_bg.svg b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/media/highlight_stacked_bg.svg
new file mode 100644
index 0000000..19be5a9
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/media/highlight_stacked_bg.svg
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ viewBox="-0.7 0 48 48"
+ version="1.1"
+ id="svg10"
+ sodipodi:docname="highlight_stacked_bg.svg"
+ width="48"
+ height="48"
+ inkscape:version="0.92.1 r15371">
+ <metadata
+ id="metadata16">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs14" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1920"
+ inkscape:window-height="951"
+ id="namedview12"
+ showgrid="false"
+ viewbox-x="-0.7"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:zoom="3.8125"
+ inkscape:cx="-63.872219"
+ inkscape:cy="15.195756"
+ inkscape:window-x="0"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg10" />
+ <g
+ id="g8"
+ transform="matrix(1,0,0,48,-0.7,0)"
+ style="opacity:0.25;fill:#eeeeee;stroke-width:0.14433756">
+ <rect
+ width="45"
+ height="1"
+ id="rect2"
+ x="0"
+ y="0"
+ style="stroke-width:0.14433756" />
+ <rect
+ x="45"
+ width="1"
+ height="1"
+ id="rect4"
+ y="0"
+ style="opacity:0.2;stroke-width:0.02083333" />
+ <rect
+ x="46"
+ width="2"
+ height="1"
+ id="rect6"
+ y="0"
+ style="opacity:0.6;stroke-width:0.02083333" />
+ </g>
+</svg>
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/media/highlight_stacked_bg_h.svg b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/media/highlight_stacked_bg_h.svg
new file mode 100644
index 0000000..eeaa869
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/media/highlight_stacked_bg_h.svg
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ viewBox="-0.7 0 48 48"
+ version="1.1"
+ id="svg10"
+ sodipodi:docname="highlight_stacked_bg_h.svg"
+ width="48"
+ height="48"
+ inkscape:version="0.92.1 r15371">
+ <metadata
+ id="metadata16">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs14" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1853"
+ inkscape:window-height="1016"
+ id="namedview12"
+ showgrid="false"
+ viewbox-x="-0.7"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:zoom="3.8125"
+ inkscape:cx="-63.872219"
+ inkscape:cy="15.195756"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg10" />
+ <g
+ id="g8"
+ transform="matrix(0,-1,-48,0,47.3,48)"
+ style="opacity:0.25;fill:#eeeeee;stroke-width:0.14433756">
+ <rect
+ width="45"
+ height="1"
+ id="rect2"
+ x="0"
+ y="0"
+ style="stroke-width:0.14433756" />
+ <rect
+ x="45"
+ width="1"
+ height="1"
+ id="rect4"
+ y="0"
+ style="opacity:0.2;stroke-width:0.02083333" />
+ <rect
+ x="46"
+ width="2"
+ height="1"
+ id="rect6"
+ y="0"
+ style="opacity:0.6;stroke-width:0.02083333" />
+ </g>
+</svg>
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/media/logo.svg b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/media/logo.svg
new file mode 100644
index 0000000..eebd0b1
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/media/logo.svg
@@ -0,0 +1,528 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="33.866665mm"
+ height="33.866684mm"
+ viewBox="0 0 33.866665 33.866683"
+ id="svg5179"
+ version="1.1"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="logo.svg">
+ <defs
+ id="defs5181">
+ <clipPath
+ clipPathUnits="userSpaceOnUse"
+ id="clipPath4379-92-4-9-6-8-0">
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.83189655;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate"
+ id="rect4381-17-7-5-2-0-6"
+ width="19.934219"
+ height="33.52573"
+ x="356.02826"
+ y="457.71631" />
+ </clipPath>
+ <filter
+ style="color-interpolation-filters:sRGB"
+ inkscape:collect="always"
+ id="filter4435-8-5-3-2-13-8"
+ x="-0.22881356"
+ width="1.4576271"
+ y="-0.22881356"
+ height="1.4576271">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.0352993"
+ id="feGaussianBlur4437-6-7-9-8-8-1" />
+ </filter>
+ <filter
+ style="color-interpolation-filters:sRGB"
+ inkscape:collect="always"
+ id="filter4365-71-5-7-0-6-2"
+ x="-0.21864407"
+ width="1.437288"
+ y="-0.21864407"
+ height="1.437288">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.98928601"
+ id="feGaussianBlur4367-74-5-92-0-6-5" />
+ </filter>
+ <clipPath
+ clipPathUnits="userSpaceOnUse"
+ id="clipPath4379-6-7-5-8-6-01-2">
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.83189655;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate"
+ id="rect4381-1-8-5-2-0-2-7"
+ width="19.934219"
+ height="33.52573"
+ x="356.02826"
+ y="457.71631" />
+ </clipPath>
+ <filter
+ style="color-interpolation-filters:sRGB"
+ inkscape:collect="always"
+ id="filter4435-6-1-2-8-2-2-7"
+ x="-0.22881356"
+ width="1.4576271"
+ y="-0.22881356"
+ height="1.4576271">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.0352993"
+ id="feGaussianBlur4437-1-1-3-60-1-4-4" />
+ </filter>
+ <filter
+ style="color-interpolation-filters:sRGB"
+ inkscape:collect="always"
+ id="filter4365-4-5-2-24-7-3-3"
+ x="-0.21864407"
+ width="1.437288"
+ y="-0.21864407"
+ height="1.437288">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.98928601"
+ id="feGaussianBlur4367-7-0-7-7-9-0-3" />
+ </filter>
+ <clipPath
+ clipPathUnits="userSpaceOnUse"
+ id="clipPath4379-5-6-0-9-8-7-9">
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.83189655;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate"
+ id="rect4381-6-8-5-9-9-2-4"
+ width="19.934219"
+ height="33.52573"
+ x="356.02826"
+ y="457.71631" />
+ </clipPath>
+ <filter
+ style="color-interpolation-filters:sRGB"
+ inkscape:collect="always"
+ id="filter4435-63-9-2-4-1-2-6"
+ x="-0.22881356"
+ width="1.4576271"
+ y="-0.22881356"
+ height="1.4576271">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.0352993"
+ id="feGaussianBlur4437-0-5-6-8-8-9-9" />
+ </filter>
+ <filter
+ style="color-interpolation-filters:sRGB"
+ inkscape:collect="always"
+ id="filter4365-2-4-3-6-3-1-7"
+ x="-0.21864407"
+ width="1.437288"
+ y="-0.21864407"
+ height="1.437288">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.98928601"
+ id="feGaussianBlur4367-1-2-5-3-5-8-3" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ style="color-interpolation-filters:sRGB"
+ id="filter4255"
+ x="-0.20374454"
+ width="1.4074891"
+ y="-0.13779147"
+ height="1.2755829">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.25863247"
+ id="feGaussianBlur4257" />
+ </filter>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="8"
+ inkscape:cx="60.090739"
+ inkscape:cy="60.108985"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="1861"
+ inkscape:window-height="1023"
+ inkscape:window-x="0"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1" />
+ <metadata
+ id="metadata5184">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(136.97858,-11.552354)">
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#0055d4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate"
+ id="rect4006-4-6-9-2-0-6"
+ width="33.83363"
+ height="33.859909"
+ x="-136.9473"
+ y="11.552354"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <path
+ inkscape:connector-curvature="0"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.15440008;marker:none;filter:url(#filter4365-3);enable-background:accumulate"
+ d="m -130.12265,11.559157 c -4.30029,5.691881 -6.67207,12.608761 -6.82289,19.674442 -0.0115,0.54232 -0.0147,1.0766 0,1.62024 0.11433,4.23572 1.04846,8.50668 2.82497,12.565201 l 31.00865,0 0,-33.859883 -27.01073,0 z"
+ id="path6097-2-6-0-89-4"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <path
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:accumulate"
+ d="m -136.9473,18.430158 0,0.7896 0,20.641361 0,0.7896 1.23782,0 2.26288,0 1.60528,0 c 0.68577,0 1.23783,-0.3548 1.23783,-0.7896 l 0,-20.641361 c 0,-0.4398 -0.55206,-0.7896 -1.23783,-0.7896 l -1.60528,0 -2.26288,0 z"
+ id="rect4008-7-9-2-0-3-4"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccssssccc"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <path
+ inkscape:connector-curvature="0"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.15;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.15440008;marker:none;filter:url(#filter4365-3);enable-background:accumulate"
+ d="m -119.36792,11.559157 c -10.47023,5.721881 -17.57762,16.847401 -17.57762,29.627402 0,1.43804 0.0897,2.841801 0.26432,4.232481 l 33.5693,0 0,-33.859883 -16.256,0 z"
+ id="path6097-4-5-23-9"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate"
+ id="rect4247-4-4-5-3-8-1"
+ width="33.83363"
+ height="2.1162443"
+ x="-136.9473"
+ y="11.552354"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <path
+ inkscape:connector-curvature="0"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate"
+ d="m -103.11365,13.668597 0,1.05812 c 0,-0.58196 -0.47338,-1.05812 -1.05731,-1.05812 l 1.05731,0 z"
+ id="rect4272-0-7-8-1-1-3-3-1"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate"
+ id="rect4031-9-9-2-4-2-5"
+ width="4.2292037"
+ height="4.2324886"
+ x="-135.89"
+ y="19.488146"
+ rx="1.0583334"
+ ry="1.0583334"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <path
+ inkscape:connector-curvature="0"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate"
+ d="m -136.94728,13.668597 0,1.05812 c 0,-0.58196 0.47337,-1.05812 1.0573,-1.05812 l -1.0573,0 z"
+ id="rect4272-0-2-1-74-41-1-6"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <g
+ id="g4353-9-2-1-5-5-4"
+ transform="matrix(0.10331261,0,0,0.10339285,-173.76079,-27.453246)"
+ clip-path="url(#clipPath4379-92-4-9-6-8-0)"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099">
+ <circle
+ r="5.4295697"
+ cy="477.71164"
+ cx="274.13016"
+ transform="matrix(0.94749688,0,0,0.94749688,96.290796,21.848877)"
+ id="path3153-1-7-3-5-60-3-6"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.42241378;fill:#d7eef4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;filter:url(#filter4435-8-5-3-2-13-8);enable-background:accumulate" />
+ <circle
+ r="5.4295697"
+ cy="477.71164"
+ cx="274.13016"
+ transform="matrix(0.24231546,0,0,0.24231546,289.60229,358.72226)"
+ id="path3153-2-4-1-6-6-9-4-1"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#d7eef4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;filter:url(#filter4365-71-5-7-0-6-2);enable-background:accumulate" />
+ </g>
+ <g
+ id="g4589-4-1-1-3-6-2"
+ transform="matrix(0.49926208,0,0,0.49964988,-318.21072,-206.05794)"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099">
+ <g
+ clip-path="url(#clipPath4379-6-7-5-8-6-01-2)"
+ transform="matrix(0.20693061,0,0,0.20693061,289.32686,368.5622)"
+ id="g4353-66-1-4-2-6-94-5">
+ <circle
+ r="5.4295697"
+ cy="477.71164"
+ cx="274.13016"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.42241378;fill:#d7eef4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;filter:url(#filter4435-6-1-2-8-2-2-7);enable-background:accumulate"
+ id="path3153-1-6-4-5-63-7-1-0"
+ transform="matrix(0.94749688,0,0,0.94749688,96.290796,21.848877)" />
+ <circle
+ r="5.4295697"
+ cy="477.71164"
+ cx="274.13016"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#d7eef4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;filter:url(#filter4365-4-5-2-24-7-3-3);enable-background:accumulate"
+ id="path3153-2-4-7-6-5-8-5-9-5"
+ transform="matrix(0.24231546,0,0,0.24231546,289.60229,358.72226)" />
+ </g>
+ <g
+ clip-path="url(#clipPath4379-5-6-0-9-8-7-9)"
+ transform="matrix(0.20693061,0,0,0.20693061,289.32686,367.53449)"
+ id="g4353-7-2-2-6-4-5-1">
+ <circle
+ r="5.4295697"
+ cy="477.71164"
+ cx="274.13016"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.42241378;fill:#d7eef4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;filter:url(#filter4435-63-9-2-4-1-2-6);enable-background:accumulate"
+ id="path3153-1-19-3-1-5-5-7-8"
+ transform="matrix(0.94749688,0,0,0.94749688,96.290796,21.848877)" />
+ <circle
+ r="5.4295697"
+ cy="477.71164"
+ cx="274.13016"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#d7eef4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;filter:url(#filter4365-2-4-3-6-3-1-7);enable-background:accumulate"
+ id="path3153-2-4-5-7-9-9-9-7-6"
+ transform="matrix(0.24231546,0,0,0.24231546,289.60229,358.72226)" />
+ </g>
+ </g>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:1.28805089px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none"
+ x="-124.44726"
+ y="13.10139"
+ id="text4824-5-2-0-4-8"
+ sodipodi:linespacing="125%"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099"
+ transform="scale(0.99961185,1.0003883)"><tspan
+ sodipodi:role="line"
+ id="tspan4826-16-3-8-8-1"
+ x="-124.44726"
+ y="13.10139">Dash to Dock</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:1.28805089px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none"
+ x="-136.50272"
+ y="13.10139"
+ id="text4824-8-8-6-8-7-4"
+ sodipodi:linespacing="125%"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099"
+ transform="scale(0.99961185,1.0003883)"><tspan
+ sodipodi:role="line"
+ id="tspan4826-1-7-7-5-07-5"
+ x="-136.50272"
+ y="13.10139">Michele</tspan></text>
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate"
+ id="rect4031-9-0-8-5-4-0-7-6"
+ width="4.2292037"
+ height="4.2324886"
+ x="-135.89"
+ y="24.778917"
+ rx="1.0583334"
+ ry="1.0583334"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate"
+ id="rect4031-9-0-7-3-3-6-0-1"
+ width="4.2292037"
+ height="4.2324886"
+ x="-135.89"
+ y="30.069445"
+ rx="1.0583334"
+ ry="1.0583334"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate"
+ id="rect4031-9-0-6-5-1-3-9-0"
+ width="4.2292037"
+ height="4.2324886"
+ x="-135.89"
+ y="35.359974"
+ rx="1.0583334"
+ ry="1.0583334"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <path
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.5;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:accumulate"
+ d="m -136.9473,17.901078 0,0.52908 2.42849,0 2.21372,0 c 0.94338,0 1.7016,0.3372 1.7016,0.77704 l 0,20.649921 c 0,0.43476 -0.75822,0.7936 -1.7016,0.7936 l -2.21372,0 -2.42849,0 0,0.52904 0.90862,0 2.64325,0 1.88332,0 c 0.80005,0 1.43727,-0.3712 1.43727,-0.82664 l 0,-21.625361 c 0,-0.46072 -0.63722,-0.82668 -1.43727,-0.82668 l -1.88332,0 -2.64325,0 z"
+ id="rect4008-7-0-0-3-3-3-7-9"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccsssscccccssssccc"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <path
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:accumulate"
+ d="m -136.9473,17.901078 0,0.52908 2.42849,0 2.21372,0 c 0.94338,0 1.7016,0.3372 1.7016,0.77704 l 0,20.649921 c 0,0.43476 -0.75822,0.7936 -1.7016,0.7936 l -2.21372,0 -2.42849,0 0,0.52904 0.90862,0 2.64325,0 1.88332,0 c 0.80005,0 1.43727,-0.3712 1.43727,-0.82664 l 0,-21.625361 c 0,-0.46072 -0.63722,-0.82668 -1.43727,-0.82668 l -1.88332,0 -2.64325,0 z"
+ id="rect4008-7-0-0-3-1-5-0-5-5"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccsssscccccssssccc"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#f2f2f2;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;filter:url(#filter4365-3);enable-background:accumulate"
+ id="rect6777-7-9-6-9-8"
+ width="20.108335"
+ height="18.256252"
+ x="-125.24149"
+ y="19.139757"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="45"
+ inkscape:export-ydpi="45" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.13229166;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ id="rect4923-8-7-8-2"
+ width="3.7041669"
+ height="3.7041669"
+ x="-116.71888"
+ y="30.163927"
+ rx="1.0583334"
+ ry="1.0583334" />
+ <path
+ inkscape:connector-curvature="0"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.15;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;filter:url(#filter4365-3);enable-background:accumulate"
+ d="m -111.94623,19.146638 c -5.49508,1.3884 -10.21465,5.00036 -13.29531,9.92188 l 0,8.334361 20.10833,0 0,-18.256241 -6.81302,0 z"
+ id="path6862-84-2-2-6-7"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="45"
+ inkscape:export-ydpi="45" />
+ <path
+ inkscape:connector-curvature="0"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#cccccc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;filter:url(#filter4365-3);enable-background:accumulate"
+ d="m -125.02657,18.882038 c -0.11728,0 -0.21496,0.0812 -0.21496,0.1984 l 0,0.44648 0,1.2568 0,0.2148 0.21496,0 19.67838,0 0.215,0 0,-0.2148 0,-1.2568 0,-0.44648 c 0,-0.1172 -0.0977,-0.1984 -0.215,-0.1984 l -19.67838,0 z"
+ id="rect6779-5-8-6-4-6"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="45"
+ inkscape:export-ydpi="45" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#999999;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;filter:url(#filter4365-3);enable-background:accumulate"
+ id="rect6779-2-3-9-9-0-8"
+ width="20.108335"
+ height="0.5291667"
+ x="-125.24149"
+ y="20.991808"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="45"
+ inkscape:export-ydpi="45" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;filter:url(#filter4365-3);enable-background:accumulate"
+ id="rect6779-2-4-8-0-7-1-1"
+ width="15.875001"
+ height="0.5291667"
+ x="21.521105"
+ y="105.13315"
+ transform="rotate(90)"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="45"
+ inkscape:export-ydpi="45" />
+ <g
+ id="g6839-1-5-1-33-0"
+ transform="matrix(0.02002288,0.02002284,-0.02002288,0.02002284,-106.62848,-6.0229242)"
+ style="fill:#1a1a1a"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="45"
+ inkscape:export-ydpi="45">
+ <rect
+ y="616.07727"
+ x="653.01312"
+ height="41.542522"
+ width="11.313708"
+ id="rect6819-8-9-2-56-9"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;filter:url(#filter4365-3);enable-background:accumulate" />
+ <rect
+ transform="rotate(90)"
+ y="-679.44122"
+ x="631.19165"
+ height="41.542522"
+ width="11.313708"
+ id="rect6819-3-9-4-3-1-5"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;filter:url(#filter4365-3);enable-background:accumulate" />
+ </g>
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.13229166;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ id="rect4923-6-8-9-1"
+ width="3.7041669"
+ height="3.7041669"
+ x="-123.59805"
+ y="30.163927"
+ rx="1.0583334"
+ ry="1.0583334" />
+ <path
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.26458335;stroke-miterlimit:4;stroke-dasharray:none;marker:none;enable-background:accumulate;opacity:0.866;filter:url(#filter4255)"
+ d="m -121.46776,32.043964 -5e-4,1.742839 -4.9e-4,1.742839 0.71518,-0.708051 0.99716,1.727136 1.33421,-0.770304 -0.99542,-1.724104 0.96903,-0.268366 -1.50959,-0.870995 z"
+ id="path6155-6-0-01-4-5-6-0-0"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccccc" />
+ <path
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.13229167;stroke-miterlimit:4;stroke-dasharray:none;marker:none;filter:url(#filter4365-3);enable-background:accumulate"
+ d="m -121.86464,32.043964 -5e-4,1.742839 -4.9e-4,1.742839 0.71518,-0.708051 1.05563,1.8284 1.3342,-0.770304 -1.05388,-1.825368 0.96903,-0.268366 -1.50959,-0.870995 z"
+ id="path6155-6-0-8-0-7-97-5"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccccc" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.13229166;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ id="rect4923-4-8-4"
+ width="3.7041669"
+ height="3.7041669"
+ x="-123.59805"
+ y="23.020128"
+ rx="1.0583334"
+ ry="1.0583334" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.13229166;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ id="rect4923-2-6-7-8"
+ width="3.7041669"
+ height="3.7041669"
+ x="-116.71888"
+ y="23.020128"
+ rx="1.0583334"
+ ry="1.0583334" />
+ </g>
+</svg>
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/metadata.json b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/metadata.json
new file mode 100644
index 0000000..cbb546f
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/metadata.json
@@ -0,0 +1,18 @@
+{
+ "_generated": "Generated by SweetTooth, do not edit",
+ "description": "A dock for the Gnome Shell. This extension moves the dash out of the overview transforming it in a dock for an easier launching of applications and a faster switching between windows and desktops. Side and bottom placement options are available.",
+ "gettext-domain": "dashtodock",
+ "name": "Dash to Dock",
+ "original-author": "micxgx@gmail.com",
+ "shell-version": [
+ "45",
+ "46",
+ "47",
+ "48",
+ "49",
+ "50"
+ ],
+ "url": "https://micheleg.github.io/dash-to-dock/",
+ "uuid": "dash-to-dock@micxgx.gmail.com",
+ "version": 104
+} \ No newline at end of file
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/notificationsMonitor.js b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/notificationsMonitor.js
new file mode 100644
index 0000000..7dca35d
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/notificationsMonitor.js
@@ -0,0 +1,121 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+import {Gio} from './dependencies/gi.js';
+import {Main} from './dependencies/shell/ui.js';
+
+import {
+ Docking,
+ Utils,
+} from './imports.js';
+
+const {signals: Signals} = imports;
+
+const Labels = Object.freeze({
+ SOURCES: Symbol('sources'),
+ NOTIFICATIONS: Symbol('notifications'),
+});
+export class NotificationsMonitor {
+ constructor() {
+ this._settings = new Gio.Settings({
+ schema_id: 'org.gnome.desktop.notifications',
+ });
+
+ this._appNotifications = Object.create(null);
+ this._signalsHandler = new Utils.GlobalSignalsHandler(this);
+
+ const getIsEnabled = () => !this.dndMode &&
+ Docking.DockManager.settings.showIconsNotificationsCounter;
+
+ this._isEnabled = getIsEnabled();
+ const checkIsEnabled = () => {
+ const isEnabled = getIsEnabled();
+ if (isEnabled !== this._isEnabled) {
+ this._isEnabled = isEnabled;
+ this.emit('state-changed');
+
+ this._updateState();
+ }
+ };
+
+ this._dndMode = !this._settings.get_boolean('show-banners');
+ this._signalsHandler.add(this._settings, 'changed::show-banners', () => {
+ this._dndMode = !this._settings.get_boolean('show-banners');
+ checkIsEnabled();
+ });
+ this._signalsHandler.add(Docking.DockManager.settings,
+ 'changed::show-icons-notifications-counter', checkIsEnabled);
+
+ this._updateState();
+ }
+
+ destroy() {
+ this.emit('destroy');
+ this._signalsHandler?.destroy();
+ this._signalsHandler = null;
+ this._appNotifications = null;
+ this._settings = null;
+ }
+
+ get enabled() {
+ return this._isEnabled;
+ }
+
+ get dndMode() {
+ return this._dndMode;
+ }
+
+ getAppNotificationsCount(appId) {
+ return this._appNotifications[appId] ?? 0;
+ }
+
+ _updateState() {
+ if (this.enabled) {
+ this._signalsHandler.addWithLabel(Labels.SOURCES, Main.messageTray,
+ 'source-added', () => this._checkNotifications());
+ this._signalsHandler.addWithLabel(Labels.SOURCES, Main.messageTray,
+ 'source-removed', () => this._checkNotifications());
+ } else {
+ this._signalsHandler.removeWithLabel(Labels.SOURCES);
+ }
+
+ this._checkNotifications();
+ }
+
+ _checkNotifications() {
+ this._appNotifications = Object.create(null);
+ this._signalsHandler.removeWithLabel(Labels.NOTIFICATIONS);
+
+ if (this.enabled) {
+ Main.messageTray.getSources().forEach(source => {
+ this._signalsHandler.addWithLabel(Labels.NOTIFICATIONS, source,
+ 'notification-added', () => this._checkNotifications());
+
+ source.notifications.forEach(notification => {
+ const app = notification.source?.app ?? notification.source?._app;
+ const appId = app?.id ?? app?._appId;
+
+ if (appId) {
+ if (notification.resident) {
+ if (notification.acknowledged)
+ return;
+
+ this._signalsHandler.addWithLabel(Labels.NOTIFICATIONS,
+ notification, 'notify::acknowledged',
+ () => this._checkNotifications());
+ }
+
+ this._signalsHandler.addWithLabel(Labels.NOTIFICATIONS,
+ notification, 'destroy', () => this._checkNotifications());
+
+ this._appNotifications[appId] =
+ (this._appNotifications[appId] ?? 0) + 1;
+ }
+ });
+ });
+ }
+
+ this.emit('changed');
+ }
+}
+
+Signals.addSignalMethods(NotificationsMonitor.prototype);
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/prefs.js b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/prefs.js
new file mode 100644
index 0000000..fae0b99
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/prefs.js
@@ -0,0 +1,1168 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+import GLib from 'gi://GLib';
+import GObject from 'gi://GObject';
+import Gdk from 'gi://Gdk';
+import Gio from 'gi://Gio';
+import Gtk from 'gi://Gtk';
+
+import {
+ ExtensionPreferences,
+
+ // Use __ () and N__() for the extension gettext domain, and reuse
+ // the shell domain with the default _() and N_()
+ gettext as __,
+} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
+
+const SCALE_UPDATE_TIMEOUT = 500;
+const DEFAULT_ICONS_SIZES = [128, 96, 64, 48, 32, 24, 16];
+
+const TransparencyMode = Object.freeze({
+ DEFAULT: 0,
+ FIXED: 1,
+ DYNAMIC: 3,
+});
+
+const RunningIndicatorStyle = Object.freeze({
+ DEFAULT: 0,
+ DOTS: 1,
+ SQUARES: 2,
+ DASHES: 3,
+ SEGMENTED: 4,
+ SOLID: 5,
+ CILIORA: 6,
+ METRO: 7,
+});
+
+const MonitorsConfig = GObject.registerClass({
+ Signals: {
+ 'updated': {},
+ },
+}, class MonitorsConfig extends GObject.Object {
+ static get XML_INTERFACE() {
+ return '<node>\
+ <interface name="org.gnome.Mutter.DisplayConfig">\
+ <method name="GetCurrentState">\
+ <arg name="serial" direction="out" type="u" />\
+ <arg name="monitors" direction="out" type="a((ssss)a(siiddada{sv})a{sv})" />\
+ <arg name="logical_monitors" direction="out" type="a(iiduba(ssss)a{sv})" />\
+ <arg name="properties" direction="out" type="a{sv}" />\
+ </method>\
+ <signal name="MonitorsChanged" />\
+ </interface>\
+ </node>';
+ }
+
+ static get ProxyWrapper() {
+ return Gio.DBusProxy.makeProxyWrapper(MonitorsConfig.XML_INTERFACE);
+ }
+
+ constructor() {
+ super();
+
+ this._monitorsConfigProxy = new MonitorsConfig.ProxyWrapper(
+ Gio.DBus.session,
+ 'org.gnome.Mutter.DisplayConfig',
+ '/org/gnome/Mutter/DisplayConfig'
+ );
+
+ // Connecting to a D-Bus signal
+ this._monitorsConfigProxy.connectSignal('MonitorsChanged',
+ () => this._updateResources());
+
+ this._primaryMonitor = null;
+ this._monitors = [];
+ this._logicalMonitors = [];
+
+ this._updateResources();
+ }
+
+ _updateResources() {
+ this._monitorsConfigProxy.GetCurrentStateRemote((resources, err) => {
+ if (err) {
+ logError(err);
+ return;
+ }
+
+ const [serial_, monitors, logicalMonitors] = resources;
+ let index = 0;
+ for (const monitor of monitors) {
+ const [monitorSpecs, modes_, props] = monitor;
+ const [connector, vendor, product, serial] = monitorSpecs;
+ this._monitors.push({
+ index: index++,
+ active: false,
+ connector, vendor, product, serial,
+ displayName: props['display-name'].unpack(),
+ });
+ }
+
+ for (const logicalMonitor of logicalMonitors) {
+ const [x_, y_, scale_, transform_, isPrimary, monitorsSpecs] =
+ logicalMonitor;
+
+ // We only care about the first one really
+ for (const monitorSpecs of monitorsSpecs) {
+ const [connector, vendor, product, serial] = monitorSpecs;
+ const monitor = this._monitors.find(m =>
+ m.connector === connector && m.vendor === vendor &&
+ m.product === product && m.serial === serial);
+
+ if (monitor) {
+ monitor.active = true;
+ monitor.isPrimary = isPrimary;
+ if (monitor.isPrimary)
+ this._primaryMonitor = monitor;
+ break;
+ }
+ }
+ }
+
+ const activeMonitors = this._monitors.filter(m => m.active);
+ if (activeMonitors.length > 1 && logicalMonitors.length === 1) {
+ // We're in cloning mode, so let's just activate the primary monitor
+ this._monitors.forEach(m => (m.active = false));
+ this._primaryMonitor.active = true;
+ }
+
+ this._updateMonitorsIndexes();
+ this.emit('updated');
+ });
+ }
+
+ _updateMonitorsIndexes() {
+ // This function ensures that we follow the old Gdk indexing strategy
+ // for monitors, it can be removed when we don't care about breaking
+ // old user configurations or external apps configuring this extension
+ // such as ubuntu's gnome-control-center.
+ const {index: primaryMonitorIndex} = this._primaryMonitor;
+ for (const monitor of this._monitors) {
+ let {index} = monitor;
+ // The The dock uses the Gdk index for monitors, where the primary monitor
+ // always has index 0, so let's follow what dash-to-dock does in docking.js
+ // (as part of _createDocks), but using inverted math
+ index -= primaryMonitorIndex;
+
+ if (index < 0)
+ index += this._monitors.length;
+
+ monitor.index = index;
+ }
+ }
+
+ get primaryMonitor() {
+ return this._primaryMonitor;
+ }
+
+ get monitors() {
+ return this._monitors;
+ }
+});
+
+/**
+ * @param settings
+ */
+function setShortcut(settings) {
+ const shortcutText = settings.get_string('shortcut-text');
+ const [success, key, mods] = Gtk.accelerator_parse(shortcutText);
+
+ if (success && Gtk.accelerator_valid(key, mods)) {
+ const shortcut = Gtk.accelerator_name(key, mods);
+ settings.set_strv('shortcut', [shortcut]);
+ } else {
+ settings.set_strv('shortcut', []);
+ }
+}
+
+const DockSettings = GObject.registerClass({
+ Implements: [Gtk.BuilderScope],
+}, class DashToDockSettings extends GObject.Object {
+ _init(extensionPreferences) {
+ super._init();
+
+ this._extensionPreferences = extensionPreferences;
+ this._settings = extensionPreferences.getSettings(
+ 'org.gnome.shell.extensions.dash-to-dock');
+ this._appSwitcherSettings = new Gio.Settings({schema_id: 'org.gnome.shell.app-switcher'});
+ this._rtl = Gtk.Widget.get_default_direction() === Gtk.TextDirection.RTL;
+
+ this._builder = new Gtk.Builder();
+ this._builder.set_scope(this);
+ this._builder.set_translation_domain(
+ extensionPreferences.metadata['gettext-domain']);
+ this._builder.add_from_file(`${extensionPreferences.path}/Settings.ui`);
+
+ this.widget = this._builder.get_object('settings_notebook');
+
+ // Set a reasonable initial window height
+ this.widget.connect('realize', () => {
+ const rootWindow = this.widget.get_root();
+ rootWindow.set_default_size(-1, 850);
+ rootWindow.connect('close-request', () => this._onWindowsClosed());
+ });
+
+ // Timeout to delay the update of the settings
+ this._dock_size_timeout = 0;
+ this._icon_size_timeout = 0;
+ this._opacity_timeout = 0;
+
+ this._monitorsConfig = new MonitorsConfig();
+ this._bindSettings();
+ }
+
+ _onWindowsClosed() {
+ if (this._dock_size_timeout) {
+ GLib.source_remove(this._dock_size_timeout);
+ delete this._dock_size_timeout;
+ }
+
+ if (this._icon_size_timeout) {
+ GLib.source_remove(this._icon_size_timeout);
+ delete this._icon_size_timeout;
+ }
+
+ if (this._opacity_timeout) {
+ GLib.source_remove(this._opacity_timeout);
+ delete this._opacity_timeout;
+ }
+ }
+
+ vfunc_create_closure(builder, handlerName, flags, connectObject) {
+ if (flags & Gtk.BuilderClosureFlags.SWAPPED)
+ throw new Error('Unsupported template signal flag "swapped"');
+
+ if (typeof this[handlerName] === 'undefined')
+ throw new Error(`${handlerName} is undefined`);
+
+ return this[handlerName].bind(connectObject || this);
+ }
+
+ dock_display_combo_changed_cb(combo) {
+ if (!this._monitors?.length || this._updatingSettings)
+ return;
+
+ const preferredMonitor = this._monitors[combo.get_active()].connector;
+
+ this._updatingSettings = true;
+ this._settings.set_string('preferred-monitor-by-connector', preferredMonitor);
+ this._settings.set_int('preferred-monitor', -2);
+ this._updatingSettings = false;
+ }
+
+ position_top_button_toggled_cb(button) {
+ if (button.get_active())
+ this._settings.set_enum('dock-position', 0);
+ }
+
+ position_right_button_toggled_cb(button) {
+ if (button.get_active())
+ this._settings.set_enum('dock-position', 1);
+ }
+
+ position_bottom_button_toggled_cb(button) {
+ if (button.get_active())
+ this._settings.set_enum('dock-position', 2);
+ }
+
+ position_left_button_toggled_cb(button) {
+ if (button.get_active())
+ this._settings.set_enum('dock-position', 3);
+ }
+
+ icon_size_combo_changed_cb(combo) {
+ this._settings.set_int('dash-max-icon-size', this._allIconSizes[combo.get_active()]);
+ }
+
+ dock_size_scale_value_changed_cb(scale) {
+ // Avoid settings the size continuously
+ if (this._dock_size_timeout > 0)
+ GLib.source_remove(this._dock_size_timeout);
+ this._dock_size_timeout = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT, SCALE_UPDATE_TIMEOUT, () => {
+ this._settings.set_double('height-fraction', scale.get_value());
+ this._dock_size_timeout = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+
+ icon_size_scale_value_changed_cb(scale) {
+ // Avoid settings the size consinuosly
+ if (this._icon_size_timeout > 0)
+ GLib.source_remove(this._icon_size_timeout);
+ this._icon_size_timeout = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT, SCALE_UPDATE_TIMEOUT, () => {
+ this._settings.set_int('dash-max-icon-size', scale.get_value());
+ this._icon_size_timeout = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+
+ preview_size_scale_format_value_cb(scale, value) {
+ return value === 0 ? 'auto' : value;
+ }
+
+ preview_size_scale_value_changed_cb(scale) {
+ this._settings.set_double('preview-size-scale', scale.get_value());
+ }
+
+ custom_opacity_scale_value_changed_cb(scale) {
+ // Avoid settings the opacity consinuosly as it's change is animated
+ if (this._opacity_timeout > 0)
+ GLib.source_remove(this._opacity_timeout);
+ this._opacity_timeout = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT, SCALE_UPDATE_TIMEOUT, () => {
+ this._settings.set_double('background-opacity', scale.get_value());
+ this._opacity_timeout = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+
+ min_opacity_scale_value_changed_cb(scale) {
+ // Avoid settings the opacity consinuosly as it's change is animated
+ if (this._opacity_timeout > 0)
+ GLib.source_remove(this._opacity_timeout);
+ this._opacity_timeout = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT, SCALE_UPDATE_TIMEOUT, () => {
+ this._settings.set_double('min-alpha', scale.get_value());
+ this._opacity_timeout = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+
+ max_opacity_scale_value_changed_cb(scale) {
+ // Avoid settings the opacity consinuosly as it's change is animated
+ if (this._opacity_timeout > 0)
+ GLib.source_remove(this._opacity_timeout);
+ this._opacity_timeout = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT, SCALE_UPDATE_TIMEOUT, () => {
+ this._settings.set_double('max-alpha', scale.get_value());
+ this._opacity_timeout = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+
+ all_windows_radio_button_toggled_cb(button) {
+ if (button.get_active())
+ this._settings.set_enum('intellihide-mode', 0);
+ }
+
+ focus_application_windows_radio_button_toggled_cb(button) {
+ if (button.get_active())
+ this._settings.set_enum('intellihide-mode', 1);
+ }
+
+ maximized_windows_radio_button_toggled_cb(button) {
+ if (button.get_active())
+ this._settings.set_enum('intellihide-mode', 2);
+ }
+
+ always_on_top_radio_button_toggled_cb(button) {
+ if (button.get_active())
+ this._settings.set_enum('intellihide-mode', 3);
+ }
+
+ _updateMonitorsSettings() {
+ // Monitor options
+ const preferredMonitor = this._settings.get_int('preferred-monitor');
+ const preferredMonitorByConnector = this._settings.get_string('preferred-monitor-by-connector');
+ const dockMonitorCombo = this._builder.get_object('dock_monitor_combo');
+
+ this._monitors = [];
+ dockMonitorCombo.remove_all();
+ let primaryIndex = -1;
+
+ // Add connected monitors
+ for (const monitor of this._monitorsConfig.monitors) {
+ if (!monitor.active && monitor.index !== preferredMonitor)
+ continue;
+
+ if (monitor.isPrimary) {
+ dockMonitorCombo.append_text(
+ /* Translators: This will be followed by Display Name - Connector. */
+ `${__('Primary monitor: ') + monitor.displayName} - ${
+ monitor.connector}`);
+ primaryIndex = this._monitors.length;
+ } else {
+ dockMonitorCombo.append_text(
+ /* Translators: Followed by monitor index, Display Name - Connector. */
+ `${__('Secondary monitor ') + (monitor.index + 1)} - ${
+ monitor.displayName} - ${monitor.connector}`);
+ }
+
+ this._monitors.push(monitor);
+
+ if (monitor.index === preferredMonitor ||
+ (preferredMonitor === -2 && preferredMonitorByConnector === monitor.connector))
+ dockMonitorCombo.set_active(this._monitors.length - 1);
+ }
+
+ if (dockMonitorCombo.get_active() < 0 && primaryIndex >= 0)
+ dockMonitorCombo.set_active(primaryIndex);
+ }
+
+ _bindSettings() {
+ // Position and size panel
+
+ this._updateMonitorsSettings();
+ this._monitorsConfig.connect('updated',
+ () => this._updateMonitorsSettings());
+ this._settings.connect('changed::preferred-monitor',
+ () => this._updateMonitorsSettings());
+ this._settings.connect('changed::preferred-monitor-by-connector',
+ () => this._updateMonitorsSettings());
+
+ // Position option
+ const position = this._settings.get_enum('dock-position');
+
+ switch (position) {
+ case 0:
+ this._builder.get_object('position_top_button').set_active(true);
+ break;
+ case 1:
+ this._builder.get_object('position_right_button').set_active(true);
+ break;
+ case 2:
+ this._builder.get_object('position_bottom_button').set_active(true);
+ break;
+ case 3:
+ this._builder.get_object('position_left_button').set_active(true);
+ break;
+ }
+
+ if (this._rtl) {
+ /* Left is Right in rtl as a setting */
+ this._builder.get_object('position_left_button').set_label(__('Right'));
+ this._builder.get_object('position_right_button').set_label(__('Left'));
+ }
+
+ // Intelligent autohide options
+ this._settings.bind('dock-fixed',
+ this._builder.get_object('intelligent_autohide_switch'),
+ 'active',
+ Gio.SettingsBindFlags.INVERT_BOOLEAN);
+ this._settings.bind('dock-fixed',
+ this._builder.get_object('intelligent_autohide_button'),
+ 'sensitive',
+ Gio.SettingsBindFlags.INVERT_BOOLEAN);
+ this._settings.bind('autohide',
+ this._builder.get_object('autohide_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('autohide-in-fullscreen',
+ this._builder.get_object('autohide_enable_in_fullscreen_checkbutton'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('show-dock-urgent-notify',
+ this._builder.get_object('show_dock_urgent_notify_checkbutton'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('require-pressure-to-show',
+ this._builder.get_object('require_pressure_checkbutton'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('intellihide',
+ this._builder.get_object('intellihide_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('animation-time',
+ this._builder.get_object('animation_duration_spinbutton'),
+ 'value',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('hide-delay',
+ this._builder.get_object('hide_timeout_spinbutton'),
+ 'value',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('show-delay',
+ this._builder.get_object('show_timeout_spinbutton'),
+ 'value',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('pressure-threshold',
+ this._builder.get_object('pressure_threshold_spinbutton'),
+ 'value',
+ Gio.SettingsBindFlags.DEFAULT);
+
+ // this._builder.get_object('animation_duration_spinbutton').set_value(
+ // this._settings.get_double('animation-time'));
+
+ // Create dialog for intelligent autohide advanced settings
+ this._builder.get_object('intelligent_autohide_button').connect('clicked', () => {
+ const dialog = new Gtk.Dialog({
+ title: __('Intelligent autohide customization'),
+ transient_for: this.widget.get_root(),
+ use_header_bar: true,
+ modal: true,
+ });
+
+ // GTK+ leaves positive values for application-defined response ids.
+ // Use +1 for the reset action
+ dialog.add_button(__('Reset to defaults'), 1);
+
+ const box = this._builder.get_object('intelligent_autohide_advanced_settings_box');
+ dialog.get_content_area().append(box);
+
+ this._settings.bind('intellihide',
+ this._builder.get_object('intellihide_mode_box'),
+ 'sensitive',
+ Gio.SettingsBindFlags.GET);
+
+ // intellihide mode
+
+ const intellihideModeRadioButtons = [
+ this._builder.get_object('all_windows_radio_button'),
+ this._builder.get_object('focus_application_windows_radio_button'),
+ this._builder.get_object('maximized_windows_radio_button'),
+ this._builder.get_object('always_on_top_radio_button'),
+ ];
+
+ intellihideModeRadioButtons[this._settings.get_enum('intellihide-mode')].set_active(true);
+
+ this._settings.bind('autohide',
+ this._builder.get_object('require_pressure_checkbutton'),
+ 'sensitive',
+ Gio.SettingsBindFlags.GET);
+
+ this._settings.bind('autohide',
+ this._builder.get_object('autohide_enable_in_fullscreen_checkbutton'),
+ 'sensitive',
+ Gio.SettingsBindFlags.GET);
+
+ this._settings.bind('autohide',
+ this._builder.get_object('show_dock_urgent_notify_checkbutton'),
+ 'sensitive',
+ Gio.SettingsBindFlags.GET);
+
+ this._settings.bind('require-pressure-to-show',
+ this._builder.get_object('show_timeout_spinbutton'),
+ 'sensitive',
+ Gio.SettingsBindFlags.INVERT_BOOLEAN);
+ this._settings.bind('require-pressure-to-show',
+ this._builder.get_object('show_timeout_label'),
+ 'sensitive',
+ Gio.SettingsBindFlags.INVERT_BOOLEAN);
+ this._settings.bind('require-pressure-to-show',
+ this._builder.get_object('pressure_threshold_spinbutton'),
+ 'sensitive',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('require-pressure-to-show',
+ this._builder.get_object('pressure_threshold_label'),
+ 'sensitive',
+ Gio.SettingsBindFlags.DEFAULT);
+
+ dialog.connect('response', (_, id) => {
+ if (id === 1) {
+ // restore default settings for the relevant keys
+ const keys = ['intellihide', 'autohide', 'intellihide-mode',
+ 'autohide-in-fullscreen', 'show-dock-urgent-notify',
+ 'require-pressure-to-show', 'animation-time',
+ 'show-delay', 'hide-delay', 'pressure-threshold'];
+ keys.forEach(function (val) {
+ this._settings.set_value(val, this._settings.get_default_value(val));
+ }, this);
+ intellihideModeRadioButtons[this._settings.get_enum('intellihide-mode')].set_active(true);
+ } else {
+ // remove the settings box so it doesn't get destroyed;
+ dialog.get_content_area().remove(box);
+ dialog.destroy();
+ }
+ });
+
+ dialog.present();
+ });
+
+ // size options
+ const dockSizeScale = this._builder.get_object('dock_size_scale');
+ dockSizeScale.set_value(this._settings.get_double('height-fraction'));
+ dockSizeScale.add_mark(0.9, Gtk.PositionType.TOP, null);
+ dockSizeScale.set_format_value_func((_, value) => {
+ return `${Math.round(value * 100)} %`;
+ });
+ const iconSizeScale = this._builder.get_object('icon_size_scale');
+ iconSizeScale.set_range(8, DEFAULT_ICONS_SIZES[0]);
+ iconSizeScale.set_value(this._settings.get_int('dash-max-icon-size'));
+ DEFAULT_ICONS_SIZES.forEach(val => {
+ iconSizeScale.add_mark(val, Gtk.PositionType.TOP, val.toString());
+ });
+ iconSizeScale.set_format_value_func((_, value) => {
+ return `${value} px`;
+ });
+ this._builder.get_object('preview_size_scale').set_value(
+ this._settings.get_double('preview-size-scale'));
+
+ // Corrent for rtl languages
+ if (this._rtl) {
+ // Flip value position: this is not done automatically
+ dockSizeScale.set_value_pos(Gtk.PositionType.LEFT);
+ iconSizeScale.set_value_pos(Gtk.PositionType.LEFT);
+ // I suppose due to a bug, having a more than one mark and one above
+ // a value of 100 makes the rendering of the marks wrong in rtl.
+ // This doesn't happen setting the scale as not flippable
+ // and then manually inverting it
+ iconSizeScale.set_flippable(false);
+ iconSizeScale.set_inverted(true);
+ }
+
+ this._settings.bind('icon-size-fixed',
+ this._builder.get_object('icon_size_fixed_checkbutton'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('extend-height',
+ this._builder.get_object('dock_size_extend_checkbutton'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('extend-height',
+ this._builder.get_object('dock_size_scale'),
+ 'sensitive',
+ Gio.SettingsBindFlags.INVERT_BOOLEAN);
+ this._settings.bind('always-center-icons',
+ this._builder.get_object('dock_center_icons_check'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('extend-height',
+ this._builder.get_object('dock_center_icons_check'),
+ 'sensitive',
+ Gio.SettingsBindFlags.DEFAULT);
+
+ this._settings.bind('multi-monitor',
+ this._builder.get_object('dock_monitor_combo'),
+ 'sensitive',
+ Gio.SettingsBindFlags.INVERT_BOOLEAN);
+
+
+ // Apps panel
+
+ this._settings.bind('show-running',
+ this._builder.get_object('show_running_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ const applicationButtonIsolationButton =
+ this._builder.get_object('application_button_isolation_button');
+ this._settings.bind('isolate-workspaces',
+ applicationButtonIsolationButton,
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ applicationButtonIsolationButton.connect(
+ 'notify::sensitive', check => {
+ if (check.sensitive) {
+ [check.label] = check.label.split('\n');
+ } else {
+ check.label += `\n${
+ __('Managed by GNOME Multitasking\'s Application Switching setting.')}`;
+ }
+ });
+ this._appSwitcherSettings.bind('current-workspace-only',
+ applicationButtonIsolationButton,
+ 'sensitive',
+ Gio.SettingsBindFlags.INVERT_BOOLEAN |
+ Gio.SettingsBindFlags.SYNC_CREATE);
+ this._settings.bind('workspace-agnostic-urgent-windows',
+ this._builder.get_object('application_button_urgent_button'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('isolate-monitors',
+ this._builder.get_object('application_button_monitor_isolation_button'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('show-windows-preview',
+ this._builder.get_object('windows_preview_button'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('multi-monitor',
+ this._builder.get_object('multi_monitor_button'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('show-favorites',
+ this._builder.get_object('show_favorite_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('show-trash',
+ this._builder.get_object('show_trash_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('show-mounts',
+ this._builder.get_object('show_mounts_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('show-mounts-only-mounted',
+ this._builder.get_object('show_only_mounted_devices_check'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('show-mounts-network',
+ this._builder.get_object('show_network_volumes_check'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('isolate-locations',
+ this._builder.get_object('isolate_locations_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ const isolateLocationsBindings = ['show_trash_switch', 'show_mounts_switch'];
+ const updateIsolateLocations = () => {
+ this._builder.get_object('isolate_locations_row').sensitive =
+ isolateLocationsBindings.some(s => this._builder.get_object(s).active);
+ };
+ updateIsolateLocations();
+ isolateLocationsBindings.forEach(s => this._builder.get_object(s).connect(
+ 'notify::active', () => updateIsolateLocations()));
+ this._settings.bind('dance-urgent-applications',
+ this._builder.get_object('wiggle_urgent_applications_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('hide-tooltip',
+ this._builder.get_object('hide_tooltip_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('show-icons-emblems',
+ this._builder.get_object('show_icons_emblems_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ const notificationsCounterCheck = this._builder.get_object(
+ 'notifications_counter_check');
+ this._settings.bind('show-icons-notifications-counter',
+ notificationsCounterCheck,
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('show-icons-emblems',
+ notificationsCounterCheck,
+ 'sensitive',
+ Gio.SettingsBindFlags.GET);
+
+ const applicationsOverrideCounter =
+ this._builder.get_object('applications_override_counter');
+ this._settings.bind('application-counter-overrides-notifications',
+ applicationsOverrideCounter,
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ notificationsCounterCheck.bind_property('active',
+ applicationsOverrideCounter, 'sensitive',
+ GObject.BindingFlags.SYNC_CREATE);
+ this._settings.connect('changed::show-icons-emblems', () => {
+ if (this._settings.get_boolean('show-icons-emblems'))
+ applicationsOverrideCounter.sensitive = notificationsCounterCheck.active;
+ else
+ applicationsOverrideCounter.sensitive = false;
+ });
+ this._settings.bind('show-show-apps-button',
+ this._builder.get_object('show_applications_button_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('show-apps-at-top',
+ this._builder.get_object('application_button_first_button'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('show-show-apps-button',
+ this._builder.get_object('application_button_first_button'),
+ 'sensitive',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('show-show-apps-button',
+ this._builder.get_object('application_button_animation_button'),
+ 'sensitive',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('show-apps-always-in-the-edge',
+ this._builder.get_object('show_apps_always_in_the_edge'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('show-show-apps-button',
+ this._builder.get_object('show_apps_always_in_the_edge'),
+ 'sensitive',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('scroll-to-focused-application',
+ this._builder.get_object('scroll_to_icon_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+
+
+ // Behavior panel
+
+ this._settings.bind('hot-keys',
+ this._builder.get_object('hot_keys_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('hot-keys',
+ this._builder.get_object('overlay_button'),
+ 'sensitive',
+ Gio.SettingsBindFlags.DEFAULT);
+
+ this._builder.get_object('click_action_combo').set_active(this._settings.get_enum('click-action'));
+ this._builder.get_object('click_action_combo').connect('changed', widget => {
+ this._settings.set_enum('click-action', widget.get_active());
+ });
+
+ this._builder.get_object('scroll_action_combo').set_active(this._settings.get_enum('scroll-action'));
+ this._builder.get_object('scroll_action_combo').connect('changed', widget => {
+ this._settings.set_enum('scroll-action', widget.get_active());
+ });
+
+ this._builder.get_object('shift_click_action_combo').connect('changed', widget => {
+ this._settings.set_enum('shift-click-action', widget.get_active());
+ });
+
+ this._builder.get_object('middle_click_action_combo').connect('changed', widget => {
+ this._settings.set_enum('middle-click-action', widget.get_active());
+ });
+ this._builder.get_object('shift_middle_click_action_combo').connect('changed', widget => {
+ this._settings.set_enum('shift-middle-click-action', widget.get_active());
+ });
+
+ // Create dialog for number overlay options
+ this._builder.get_object('overlay_button').connect('clicked', () => {
+ const dialog = new Gtk.Dialog({
+ title: __('Show dock and application numbers'),
+ transient_for: this.widget.get_root(),
+ use_header_bar: true,
+ modal: true,
+ });
+
+ // GTK+ leaves positive values for application-defined response ids.
+ // Use +1 for the reset action
+ dialog.add_button(__('Reset to defaults'), 1);
+
+ const box = this._builder.get_object('box_overlay_shortcut');
+ dialog.get_content_area().append(box);
+
+ this._builder.get_object('overlay_switch').set_active(
+ this._settings.get_boolean('hotkeys-overlay'));
+ this._builder.get_object('show_dock_switch').set_active(
+ this._settings.get_boolean('hotkeys-show-dock'));
+
+ // We need to update the shortcut 'strv' when the text is modified
+ this._settings.connect('changed::shortcut-text', () => setShortcut(this._settings));
+ this._settings.bind('shortcut-text',
+ this._builder.get_object('shortcut_entry'),
+ 'text',
+ Gio.SettingsBindFlags.DEFAULT);
+
+ this._settings.bind('hotkeys-overlay',
+ this._builder.get_object('overlay_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('hotkeys-show-dock',
+ this._builder.get_object('show_dock_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('shortcut-timeout',
+ this._builder.get_object('timeout_spinbutton'),
+ 'value',
+ Gio.SettingsBindFlags.DEFAULT);
+
+ dialog.connect('response', (_, id) => {
+ if (id === 1) {
+ // restore default settings for the relevant keys
+ const keys = ['shortcut-text', 'hotkeys-overlay',
+ 'hotkeys-show-dock', 'shortcut-timeout'];
+ keys.forEach(function (val) {
+ this._settings.set_value(val, this._settings.get_default_value(val));
+ }, this);
+ } else {
+ // remove the settings box so it doesn't get destroyed;
+ dialog.get_content_area().remove(box);
+ dialog.destroy();
+ }
+ });
+
+ dialog.present();
+ });
+
+ // Create dialog for middle-click options
+ this._builder.get_object('middle_click_options_button').connect('clicked', () => {
+ const dialog = new Gtk.Dialog({
+ title: __('Customize middle-click behavior'),
+ transient_for: this.widget.get_root(),
+ use_header_bar: true,
+ modal: true,
+ });
+
+ // GTK+ leaves positive values for application-defined response ids.
+ // Use +1 for the reset action
+ dialog.add_button(__('Reset to defaults'), 1);
+
+ const box = this._builder.get_object('box_middle_click_options');
+ dialog.get_content_area().append(box);
+
+ this._builder.get_object('shift_click_action_combo').set_active(
+ this._settings.get_enum('shift-click-action'));
+
+ this._builder.get_object('middle_click_action_combo').set_active(
+ this._settings.get_enum('middle-click-action'));
+
+ this._builder.get_object('shift_middle_click_action_combo').set_active(
+ this._settings.get_enum('shift-middle-click-action'));
+
+ this._settings.bind('shift-click-action',
+ this._builder.get_object('shift_click_action_combo'),
+ 'active-id',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('middle-click-action',
+ this._builder.get_object('middle_click_action_combo'),
+ 'active-id',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('shift-middle-click-action',
+ this._builder.get_object('shift_middle_click_action_combo'),
+ 'active-id',
+ Gio.SettingsBindFlags.DEFAULT);
+
+ dialog.connect('response', (_, id) => {
+ if (id === 1) {
+ // restore default settings for the relevant keys
+ const keys = ['shift-click-action', 'middle-click-action', 'shift-middle-click-action'];
+ keys.forEach(function (val) {
+ this._settings.set_value(val, this._settings.get_default_value(val));
+ }, this);
+ this._builder.get_object('shift_click_action_combo').set_active(
+ this._settings.get_enum('shift-click-action'));
+ this._builder.get_object('middle_click_action_combo').set_active(
+ this._settings.get_enum('middle-click-action'));
+ this._builder.get_object('shift_middle_click_action_combo').set_active(
+ this._settings.get_enum('shift-middle-click-action'));
+ } else {
+ // remove the settings box so it doesn't get destroyed;
+ dialog.get_content_area().remove(box);
+ dialog.destroy();
+ }
+ });
+
+ dialog.present();
+ });
+
+ // Appearance Panel
+
+ this._settings.bind('apply-custom-theme',
+ this._builder.get_object('customize_theme'),
+ 'sensitive',
+ Gio.SettingsBindFlags.INVERT_BOOLEAN | Gio.SettingsBindFlags.GET);
+ this._settings.bind('apply-custom-theme',
+ this._builder.get_object('builtin_theme_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('custom-theme-shrink',
+ this._builder.get_object('shrink_dash_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+
+ // Running indicators
+ this._builder.get_object('running_indicators_combo').set_active(
+ this._settings.get_enum('running-indicator-style')
+ );
+ this._builder.get_object('running_indicators_combo').connect(
+ 'changed',
+ widget => {
+ this._settings.set_enum('running-indicator-style', widget.get_active());
+ }
+ );
+
+ if (this._settings.get_enum('running-indicator-style') === RunningIndicatorStyle.DEFAULT)
+ this._builder.get_object('running_indicators_advance_settings_button').set_sensitive(false);
+
+ this._settings.connect('changed::running-indicator-style', () => {
+ if (this._settings.get_enum('running-indicator-style') === RunningIndicatorStyle.DEFAULT)
+ this._builder.get_object('running_indicators_advance_settings_button').set_sensitive(false);
+ else
+ this._builder.get_object('running_indicators_advance_settings_button').set_sensitive(true);
+ });
+
+ // Create dialog for running indicators advanced settings
+ this._builder.get_object('running_indicators_advance_settings_button').connect('clicked', () => {
+ const dialog = new Gtk.Dialog({
+ title: __('Customize running indicators'),
+ transient_for: this.widget.get_root(),
+ use_header_bar: true,
+ modal: true,
+ });
+
+ const box = this._builder.get_object('running_dots_advance_settings_box');
+ dialog.get_content_area().append(box);
+
+ this._settings.bind('running-indicator-dominant-color',
+ this._builder.get_object('dominant_color_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+
+ this._settings.bind('custom-theme-customize-running-dots',
+ this._builder.get_object('dot_style_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('custom-theme-customize-running-dots',
+ this._builder.get_object('dot_style_settings_box'),
+ 'sensitive', Gio.SettingsBindFlags.DEFAULT);
+
+ const rgba = new Gdk.RGBA();
+ rgba.parse(this._settings.get_string('custom-theme-running-dots-color'));
+ this._builder.get_object('dot_color_colorbutton').set_rgba(rgba);
+
+ this._builder.get_object('dot_color_colorbutton').connect('notify::rgba', button => {
+ const css = button.rgba.to_string();
+
+ this._settings.set_string('custom-theme-running-dots-color', css);
+ });
+
+ rgba.parse(this._settings.get_string('custom-theme-running-dots-border-color'));
+ this._builder.get_object('dot_border_color_colorbutton').set_rgba(rgba);
+
+ this._builder.get_object('dot_border_color_colorbutton').connect('notify::rgba', button => {
+ const css = button.rgba.to_string();
+
+ this._settings.set_string('custom-theme-running-dots-border-color', css);
+ });
+
+ this._settings.bind('custom-theme-running-dots-border-width',
+ this._builder.get_object('dot_border_width_spin_button'),
+ 'value',
+ Gio.SettingsBindFlags.DEFAULT);
+
+
+ dialog.connect('response', () => {
+ // remove the settings box so it doesn't get destroyed;
+ dialog.get_content_area().remove(box);
+ dialog.destroy();
+ });
+
+ dialog.present();
+ });
+
+ this._settings.bind('custom-background-color',
+ this._builder.get_object('custom_background_color_switch'),
+ 'active', Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('custom-background-color',
+ this._builder.get_object('custom_background_color'),
+ 'sensitive', Gio.SettingsBindFlags.DEFAULT);
+
+ const rgba = new Gdk.RGBA();
+ rgba.parse(this._settings.get_string('background-color'));
+ this._builder.get_object('custom_background_color').set_rgba(rgba);
+
+ this._builder.get_object('custom_background_color').connect('notify::rgba', button => {
+ const css = button.rgba.to_string();
+
+ this._settings.set_string('background-color', css);
+ });
+
+ // Opacity
+ this._builder.get_object('customize_opacity_combo').set_active_id(
+ this._settings.get_enum('transparency-mode').toString()
+ );
+ this._builder.get_object('customize_opacity_combo').connect(
+ 'changed',
+ widget => {
+ this._settings.set_enum('transparency-mode', parseInt(widget.get_active_id()));
+ }
+ );
+
+ const customOpacityScale = this._builder.get_object('custom_opacity_scale');
+ customOpacityScale.set_value(this._settings.get_double('background-opacity'));
+ customOpacityScale.set_format_value_func((_, value) => {
+ return `${Math.round(value * 100)}%`;
+ });
+
+ if (this._settings.get_enum('transparency-mode') !== TransparencyMode.FIXED)
+ this._builder.get_object('custom_opacity_scale').set_sensitive(false);
+
+ this._settings.connect('changed::transparency-mode', () => {
+ if (this._settings.get_enum('transparency-mode') !== TransparencyMode.FIXED)
+ this._builder.get_object('custom_opacity_scale').set_sensitive(false);
+ else
+ this._builder.get_object('custom_opacity_scale').set_sensitive(true);
+ });
+
+ if (this._settings.get_enum('transparency-mode') !== TransparencyMode.DYNAMIC)
+ this._builder.get_object('dynamic_opacity_button').set_sensitive(false);
+
+
+ this._settings.connect('changed::transparency-mode', () => {
+ if (this._settings.get_enum('transparency-mode') !== TransparencyMode.DYNAMIC)
+ this._builder.get_object('dynamic_opacity_button').set_sensitive(false);
+
+ else
+ this._builder.get_object('dynamic_opacity_button').set_sensitive(true);
+ });
+
+ // Create dialog for transparency advanced settings
+ this._builder.get_object('dynamic_opacity_button').connect('clicked', () => {
+ const dialog = new Gtk.Dialog({
+ title: __('Customize opacity'),
+ transient_for: this.widget.get_root(),
+ use_header_bar: true,
+ modal: true,
+ });
+
+ const box = this._builder.get_object('advanced_transparency_dialog');
+ dialog.get_content_area().append(box);
+
+ this._settings.bind(
+ 'customize-alphas',
+ this._builder.get_object('customize_alphas_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ this._settings.bind(
+ 'customize-alphas',
+ this._builder.get_object('min_alpha_scale'),
+ 'sensitive',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ this._settings.bind(
+ 'customize-alphas',
+ this._builder.get_object('max_alpha_scale'),
+ 'sensitive',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+
+ const minAlphaScale = this._builder.get_object('min_alpha_scale');
+ const maxAlphaScale = this._builder.get_object('max_alpha_scale');
+ minAlphaScale.set_value(
+ this._settings.get_double('min-alpha')
+ );
+ minAlphaScale.set_format_value_func((_, value) => {
+ return `${Math.round(value * 100)} %`;
+ });
+ maxAlphaScale.set_format_value_func((_, value) => {
+ return `${Math.round(value * 100)} %`;
+ });
+
+ maxAlphaScale.set_value(
+ this._settings.get_double('max-alpha')
+ );
+
+ dialog.connect('response', () => {
+ // remove the settings box so it doesn't get destroyed;
+ dialog.get_content_area().remove(box);
+ dialog.destroy();
+ });
+
+ dialog.present();
+ });
+
+
+ this._settings.bind('unity-backlit-items',
+ this._builder.get_object('unity_backlit_items_switch'),
+ 'active', Gio.SettingsBindFlags.DEFAULT
+ );
+ this._settings.bind('apply-glossy-effect',
+ this._builder.get_object('apply_gloss_effect_checkbutton'),
+ 'active', Gio.SettingsBindFlags.DEFAULT
+ );
+ this._settings.bind('unity-backlit-items',
+ this._builder.get_object('apply_gloss_effect_checkbutton'),
+ 'sensitive',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+
+ this._settings.bind('force-straight-corner',
+ this._builder.get_object('force_straight_corner_switch'),
+ 'active', Gio.SettingsBindFlags.DEFAULT);
+
+ this._settings.bind('disable-overview-on-startup',
+ this._builder.get_object('show_overview_on_startup_switch'),
+ 'active', Gio.SettingsBindFlags.INVERT_BOOLEAN);
+
+ // About Panel
+
+ this._builder.get_object('extension_version').set_label(
+ `${this._extensionPreferences.metadata.version}`);
+ }
+});
+
+export default class DockPreferences extends ExtensionPreferences {
+ getPreferencesWidget() {
+ const settings = new DockSettings(this);
+ const {widget} = settings;
+ return widget;
+ }
+}
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/schemas/gschemas.compiled b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/schemas/gschemas.compiled
new file mode 100644
index 0000000..a67f68f
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/schemas/gschemas.compiled
Binary files differ
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/schemas/org.gnome.shell.extensions.dash-to-dock.gschema.xml b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/schemas/org.gnome.shell.extensions.dash-to-dock.gschema.xml
new file mode 100644
index 0000000..3a762c3
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/schemas/org.gnome.shell.extensions.dash-to-dock.gschema.xml
@@ -0,0 +1,644 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<schemalist gettext-domain="gnome-shell-extensions">
+ <enum id='org.gnome.shell.extensions.dash-to-dock.clickAction'>
+ <value value='0' nick='skip'/>
+ <value value='1' nick='minimize'/>
+ <value value='2' nick='launch'/>
+ <value value='3' nick='cycle-windows'/>
+ <value value='4' nick='minimize-or-overview'/>
+ <value value='5' nick='previews'/>
+ <value value='6' nick='minimize-or-previews'/>
+ <value value='7' nick='focus-or-previews'/>
+ <value value='8' nick='focus-or-appspread'/>
+ <value value='9' nick='focus-minimize-or-previews'/>
+ <value value='10' nick='focus-minimize-or-appspread'/>
+ <value value='11' nick='quit'/>
+ </enum>
+ <enum id='org.gnome.shell.extensions.dash-to-dock.scrollAction'>
+ <value value='0' nick='do-nothing'/>
+ <value value='1' nick='cycle-windows'/>
+ <value value='2' nick='switch-workspace'/>
+ </enum>
+ <!-- this is mean to Match StSide. LEFT and RIGHT actual position in reversed in
+ rtl languages -->
+ <enum id='org.gnome.shell.extensions.dash-to-dock.position'>
+ <value value='0' nick='TOP'/>
+ <value value='1' nick='RIGHT'/>
+ <value value='2' nick='BOTTOM'/>
+ <value value='3' nick='LEFT'/>
+ </enum>
+ <enum id='org.gnome.shell.extensions.dash-to-dock.intellihide-mode'>
+ <value value='0' nick='ALL_WINDOWS'/>
+ <value value='1' nick='FOCUS_APPLICATION_WINDOWS'/>
+ <value value='2' nick='MAXIMIZED_WINDOWS'/>
+ <value value='3' nick='ALWAYS_ON_TOP'/>
+ </enum>
+ <enum id='org.gnome.shell.extensions.dash-to-dock.transparency-mode'>
+ <value value='0' nick='DEFAULT'/>
+ <value value='1' nick='FIXED'/>
+ <value value='3' nick='DYNAMIC'/>
+ </enum>
+ <enum id='org.gnome.shell.extensions.dash-to-dock.running-indicator-style'>
+ <value value='0' nick='DEFAULT'/>
+ <value value='1' nick='DOTS'/>
+ <value value='2' nick='SQUARES'/>
+ <value value='3' nick='DASHES'/>
+ <value value='4' nick='SEGMENTED'/>
+ <value value='5' nick='SOLID'/>
+ <value value='6' nick='CILIORA'/>
+ <value value='7' nick='METRO'/>
+ <value value='8' nick='BINARY'/>
+ <value value='9' nick='DOT'/>
+ </enum>
+ <schema path="/org/gnome/shell/extensions/dash-to-dock/" id="org.gnome.shell.extensions.dash-to-dock">
+ <key name="dock-position" enum="org.gnome.shell.extensions.dash-to-dock.position">
+ <default>'BOTTOM'</default>
+ <summary>Dock position</summary>
+ <description>Dock is shown on the Left, Right, Top or Bottom side of the screen.</description>
+ </key>
+ <key type="d" name="animation-time">
+ <default>0.2</default>
+ <summary>Animation time</summary>
+ <description>Sets the time duration of the autohide effect.</description>
+ </key>
+ <key type="d" name="show-delay">
+ <default>0.25</default>
+ <summary>Show delay</summary>
+ <description>Sets the delay after the mouse reaches the screen border before showing the dock.</description>
+ </key>
+ <key type="d" name="hide-delay">
+ <default>0.20</default>
+ <summary>Show delay</summary>
+ <description>Sets the delay after the mouse left the dock before hiding it.</description>
+ </key>
+ <key type="b" name="disable-overview-on-startup">
+ <default>false</default>
+ <summary>Do not show overview on startup</summary>
+ <description>Whether to show or not the overview during startup animation.</description>
+ </key>
+ <key type="b" name="custom-background-color">
+ <default>false</default>
+ <summary>Set a custom dash background background color</summary>
+ <description>Sets the color for the dash background.</description>
+ </key>
+ <key type="s" name="background-color">
+ <default>"#ffffff"</default>
+ <summary>Dash background color.</summary>
+ <description>Customize the background color of the dash.</description>
+ </key>
+ <key name="transparency-mode" enum="org.gnome.shell.extensions.dash-to-dock.transparency-mode">
+ <default>'DEFAULT'</default>
+ <summary>Transparency mode for the dock</summary>
+ <description>FIXED: constant transparency. DYNAMIC: dock takes the opaque style only when windows are close to it.</description>
+ </key>
+ <key name="running-indicator-style" enum="org.gnome.shell.extensions.dash-to-dock.running-indicator-style">
+ <default>'DEFAULT'</default>
+ <summary>...</summary>
+ <description>DEFAULT: .... DOTS: ....</description>
+ </key>
+ <key type="b" name="running-indicator-dominant-color">
+ <default>false</default>
+ <summary>Use application icon dominant color for the indicator color</summary>
+ <description></description>
+ </key>
+ <key type="b" name="customize-alphas">
+ <default>false</default>
+ <summary>Manually set the min and max opacity</summary>
+ <description>For the dynamic mode, the min/max opacity values will be given by 'min-alpha' and 'max-alpha'.</description>
+ </key>
+ <key type="d" name="min-alpha">
+ <default>0.2</default>
+ <summary>Opacity of the dash background when free-floating</summary>
+ <description>Sets the opacity of the dash background when no windows are close.</description>
+ </key>
+ <key type="d" name="max-alpha">
+ <default>0.8</default>
+ <summary>Opacity of the dash background when windows are close.</summary>
+ <description>Sets the opacity of the dash background when windows are close.</description>
+ </key>
+ <key type="d" name="background-opacity">
+ <default>0.8</default>
+ <summary>Opacity of the dash background</summary>
+ <description>Sets the opacity of the dash background when in autohide mode.</description>
+ </key>
+ <key type="b" name="manualhide">
+ <default>false</default>
+ <summary>Dock not shown on desktop</summary>
+ <description>Enable or disable manualhide mode</description>
+ </key>
+ <key type="b" name="intellihide">
+ <default>true</default>
+ <summary>Dock dodges windows</summary>
+ <description>Enable or disable intellihide mode</description>
+ </key>
+ <key name="intellihide-mode" enum="org.gnome.shell.extensions.dash-to-dock.intellihide-mode">
+ <default>'FOCUS_APPLICATION_WINDOWS'</default>
+ <summary>Define which windows are considered for intellihide.</summary>
+ <description></description>
+ </key>
+ <key type="b" name="autohide">
+ <default>true</default>
+ <summary>Dock shown on mouse over</summary>
+ <description>Enable or disable autohide mode</description>
+ </key>
+ <key type="b" name="require-pressure-to-show">
+ <default>true</default>
+ <summary>Require pressure to show dash</summary>
+ <description>Enable or disable requiring pressure to show the dash</description>
+ </key>
+ <key type="d" name="pressure-threshold">
+ <default>100</default>
+ <summary>Pressure threshold</summary>
+ <description>Sets how much pressure is needed to show the dash.</description>
+ </key>
+ <key type="b" name="autohide-in-fullscreen">
+ <default>false</default>
+ <summary>Enable autohide in fullscreen mode.</summary>
+ <description>Enable autohide in fullscreen mode.</description>
+ </key>
+ <key type="b" name="show-dock-urgent-notify">
+ <default>true</default>
+ <summary>Show dock for urgent notifications.</summary>
+ <description>Show dock when urgent notifications are received.</description>
+ </key>
+ <key type="b" name="dock-fixed">
+ <default>false</default>
+ <summary>Dock always visible</summary>
+ <description>Dock is always visible</description>
+ </key>
+ <key type="b" name="scroll-switch-workspace">
+ <default>true</default>
+ <summary>Switch workspace by scrolling over the dock</summary>
+ <description>Add the possibility to switch workspace by mouse scrolling over the dock.</description>
+ </key>
+ <key type="i" name="dash-max-icon-size">
+ <default>48</default>
+ <summary>Maximum dash icon size</summary>
+ <description>Set the allowed maximum dash icon size. Allowed range: 16..64.</description>
+ </key>
+ <key type="d" name="preview-size-scale">
+ <default>0</default>
+ <summary>Preview size scale</summary>
+ <description>Set the allowed maximum dash preview size scale. Allowed range: 0,00..1,00.</description>
+ </key>
+ <key type="b" name="icon-size-fixed">
+ <default>false</default>
+ <summary>Fixed icon size</summary>
+ <description>Keep the icon size fixed by scrolling the dock.</description>
+ </key>
+ <key type="b" name="apply-custom-theme">
+ <default>false</default>
+ <summary>Apply custom theme</summary>
+ <description>Apply customization to the dash appearance</description>
+ </key>
+ <key type="b" name="custom-theme-shrink">
+ <default>false</default>
+ <summary>TODO</summary>
+ <description>TODO</description>
+ </key>
+ <key type="b" name="custom-theme-customize-running-dots">
+ <default>false</default>
+ <summary>Customize the style of the running application indicators.</summary>
+ <description>Customize the style of the running application indicators.</description>
+ </key>
+ <key type="s" name="custom-theme-running-dots-color">
+ <default>"#ffffff"</default>
+ <summary>Running application indicators color</summary>
+ <description>Customize the color of the running application indicators.</description>
+ </key>
+ <key type="s" name="custom-theme-running-dots-border-color">
+ <default>"#ffffff"</default>
+ <summary>Running application indicators border color.</summary>
+ <description>Customize the border color of the running application indicators.</description>
+ </key>
+ <key type="i" name="custom-theme-running-dots-border-width">
+ <default>0</default>
+ <summary>Running application indicators border width.</summary>
+ <description>Customize the border width of the running application indicators.</description>
+ </key>
+ <key type="b" name="show-running">
+ <default>true</default>
+ <summary>Show running apps</summary>
+ <description>Show or hide running applications icons in the dash</description>
+ </key>
+ <key type="b" name="isolate-workspaces">
+ <default>false</default>
+ <summary>Provide workspace isolation</summary>
+ <description>Dash shows only windows from the current workspace, this is applied only if gnome-shell 'current-workspace-only' app-switcher settings is disabled</description>
+ </key>
+ <key type="b" name="workspace-agnostic-urgent-windows">
+ <default>true</default>
+ <summary>Show urgent windows on each workspace</summary>
+ <description>Show or hide urgent windows on each workspace in the dash</description>
+ </key>
+ <key type="b" name="isolate-monitors">
+ <default>false</default>
+ <summary>Provide monitor isolation</summary>
+ <description>Dash shows only windows from the monitor</description>
+ </key>
+ <key type="b" name="scroll-to-focused-application">
+ <default>true</default>
+ <summary>Scroll to focused application</summary>
+ <description>Ensure that the focused application icon is always visible in the dash</description>
+ </key>
+ <key type="b" name="show-windows-preview">
+ <default>true</default>
+ <summary>Show preview of the open windows</summary>
+ <description>Replace open windows list with windows previews</description>
+ </key>
+ <key type="b" name="default-windows-preview-to-open">
+ <default>false</default>
+ <summary>Open windows preview by default</summary>
+ <description>Window previews will show windows by default, rather than hide them behind a drop dowm menu.</description>
+ </key>
+ <key type="b" name="show-favorites">
+ <default>true</default>
+ <summary>Show favorites apps</summary>
+ <description>Show or hide favorite applications icons in the dash</description>
+ </key>
+ <key type="b" name="show-trash">
+ <default>true</default>
+ <summary>Show trash can</summary>
+ <description>Show or hide the trash can icon in the dash</description>
+ </key>
+ <key type="b" name="show-mounts">
+ <default>true</default>
+ <summary>Show mounted volumes and devices</summary>
+ <description>Show or hide mounted volume and device icons in the dash</description>
+ </key>
+ <key type="b" name="show-mounts-only-mounted">
+ <default>true</default>
+ <summary>Only show mounted volumes and devices</summary>
+ <description>Show or hide unmounted volume and device icons in the dash</description>
+ </key>
+ <key type="b" name="show-mounts-network">
+ <default>false</default>
+ <summary>Show network mounted volumes</summary>
+ <description>Show or hide network volumes in the dash</description>
+ </key>
+ <key type="b" name="isolate-locations">
+ <default>true</default>
+ <summary>Isolate volumes, devices and trash windows</summary>
+ <description>Consider volume, devices and trash as different application windows and not part of the file manager</description>
+ </key>
+ <key type="b" name="dance-urgent-applications">
+ <default>true</default>
+ <summary>Wiggle urgent applications</summary>
+ <description>Wiggle urgent application icons in the dash</description>
+ </key>
+ <key type="b" name="show-show-apps-button">
+ <default>true</default>
+ <summary>Show applications button</summary>
+ <description>Show applications button in the dash</description>
+ </key>
+ <key type="b" name="show-apps-at-top">
+ <default>false</default>
+ <summary>Show application button on the left</summary>
+ <description>Show application button on the left of the dash</description>
+ </key>
+ <key type="b" name="show-apps-always-in-the-edge">
+ <default>true</default>
+ <summary>Show application button on the edge when using centered panel mode</summary>
+ </key>
+ <key type="b" name="bolt-support">
+ <default>true</default>
+ <summary>Basic compatibility with bolt extensions</summary>
+ <description>Make the extension work properly when bolt extensions is enabled</description>
+ </key>
+ <key type="d" name="height-fraction">
+ <default>0.90</default>
+ <summary>Dock max height/width (fraction of available space)</summary>
+ </key>
+ <key type="b" name="extend-height">
+ <default>false</default>
+ <summary>Extend the dock container to all the available height/width</summary>
+ </key>
+ <key type="b" name="always-center-icons">
+ <default>false</default>
+ <summary>Center icons when dock container is extended to all the available size</summary>
+ </key>
+ <key type="i" name="preferred-monitor">
+ <default>-2</default>
+ <summary>DEPRECATED: Monitor on which putting the dock (by index)</summary>
+ <description>Set on which monitor to put the dock, use -1 for the primary one. Use -2 refer to the 'preferred-monitor-by-connector' setting instead (this is the preferred value).</description>
+ </key>
+ <key type="s" name="preferred-monitor-by-connector">
+ <default>"primary"</default>
+ <summary>Monitor on which putting the dock (by connector)</summary>
+ <description>Set on which monitor to put the dock, for example 'DisplayPort-0'. Use 'primary' for primary one. This setting is only used if 'preferred-monitor' is -2.</description>
+ </key>
+ <key type="b" name="multi-monitor">
+ <default>false</default>
+ <summary>Enable multi-monitor docks</summary>
+ <description>Show a dock on every monitor</description>
+ </key>
+ <key type="b" name="minimize-shift">
+ <default>true</default>
+ <summary>Minimize on shift+click</summary>
+ </key>
+ <key type="b" name="activate-single-window">
+ <default>true</default>
+ <summary>Activate only one window</summary>
+ </key>
+ <key name="click-action" enum="org.gnome.shell.extensions.dash-to-dock.clickAction">
+ <default>'cycle-windows'</default>
+ <summary>Action when clicking on a running app</summary>
+ <description>Set the action that is executed when clicking on the icon of a running application</description>
+ </key>
+ <key name="scroll-action" enum="org.gnome.shell.extensions.dash-to-dock.scrollAction">
+ <default>'do-nothing'</default>
+ <summary>Action when scrolling app</summary>
+ <description>Set the action that is executed when scrolling on the application icon</description>
+ </key>
+ <key name="shift-click-action" enum="org.gnome.shell.extensions.dash-to-dock.clickAction">
+ <default>'minimize'</default>
+ <summary>Action when shift+clicking on a running app</summary>
+ <description>Set the action that is executed when shift+clicking on the icon of a running application</description>
+ </key>
+ <key name="middle-click-action" enum="org.gnome.shell.extensions.dash-to-dock.clickAction">
+ <default>'launch'</default>
+ <summary>Action when clicking on a running app</summary>
+ <description>Set the action that is executed when middle-clicking on the icon of a running application</description>
+ </key>
+ <key name="shift-middle-click-action" enum="org.gnome.shell.extensions.dash-to-dock.clickAction">
+ <default>'launch'</default>
+ <summary>Action when clicking on a running app</summary>
+ <description>Set the action that is executed when shift+middle-clicking on the icon of a running application</description>
+ </key>
+ <key type="b" name="hot-keys">
+ <default>true</default>
+ <summary>Super Hot-Keys</summary>
+ <description>Launch and switch between dash items using Super+(0-9)</description>
+ </key>
+ <key type="b" name="hotkeys-show-dock">
+ <default>true</default>
+ <summary>Show the dock when using the hotkeys</summary>
+ <description>The dock will be quickly shown so that the number-overlay is visible and app activation is easier</description>
+ </key>
+ <key type="s" name="shortcut-text">
+ <default>"&lt;Super&gt;q"</default>
+ <summary>Keybinding to show the dock and the number overlay.</summary>
+ <description>Behavior depends on hotkeys-show-dock and hotkeys-overlay.</description>
+ </key>
+ <key type="as" name="shortcut">
+ <default><![CDATA[['<Super>q']]]></default>
+ <summary>Keybinding to show the dock and the number overlay.</summary>
+ <description>Behavior depends on hotkeys-show-dock and hotkeys-overlay.</description>
+ </key>
+ <key type="d" name="shortcut-timeout">
+ <default>2</default>
+ <summary>Timeout to hide the dock</summary>
+ <description>Sets the time duration before the dock is hidden again.</description>
+ </key>
+ <key type="b" name="hotkeys-overlay">
+ <default>true</default>
+ <summary>Show the dock when using the hotkeys</summary>
+ <description>The dock will be quickly shown so that the number-overlay is visible and app activation is easier</description>
+ </key>
+ <key name="app-ctrl-hotkey-1" type="as">
+ <default><![CDATA[['<Ctrl><Super>1']]]></default>
+ <summary>Keybinding to launch 1st dash app</summary>
+ <description>
+ Keybinding to launch 1st app.
+ </description>
+ </key>
+ <key name="app-ctrl-hotkey-2" type="as">
+ <default><![CDATA[['<Ctrl><Super>2']]]></default>
+ <summary>Keybinding to launch 2nd dash app</summary>
+ <description>
+ Keybinding to launch 2nd app.
+ </description>
+ </key>
+ <key name="app-ctrl-hotkey-3" type="as">
+ <default><![CDATA[['<Ctrl><Super>3']]]></default>
+ <summary>Keybinding to launch 3rd dash app</summary>
+ <description>
+ Keybinding to launch 3rd app.
+ </description>
+ </key>
+ <key name="app-ctrl-hotkey-4" type="as">
+ <default><![CDATA[['<Ctrl><Super>4']]]></default>
+ <summary>Keybinding to launch 4th dash app</summary>
+ <description>
+ Keybinding to launch 4th app.
+ </description>
+ </key>
+ <key name="app-ctrl-hotkey-5" type="as">
+ <default><![CDATA[['<Ctrl><Super>5']]]></default>
+ <summary>Keybinding to launch 5th dash app</summary>
+ <description>
+ Keybinding to launch 5th app.
+ </description>
+ </key>
+ <key name="app-ctrl-hotkey-6" type="as">
+ <default><![CDATA[['<Ctrl><Super>6']]]></default>
+ <summary>Keybinding to launch 6th dash app</summary>
+ <description>
+ Keybinding to launch 6th app.
+ </description>
+ </key>
+ <key name="app-ctrl-hotkey-7" type="as">
+ <default><![CDATA[['<Ctrl><Super>7']]]></default>
+ <summary>Keybinding to launch 7th dash app</summary>
+ <description>
+ Keybinding to launch 7th app.
+ </description>
+ </key>
+ <key name="app-ctrl-hotkey-8" type="as">
+ <default><![CDATA[['<Ctrl><Super>8']]]></default>
+ <summary>Keybinding to launch 8th dash app</summary>
+ <description>
+ Keybinding to launch 8th app.
+ </description>
+ </key>
+ <key name="app-ctrl-hotkey-9" type="as">
+ <default><![CDATA[['<Ctrl><Super>9']]]></default>
+ <summary>Keybinding to launch 9th dash app</summary>
+ <description>
+ Keybinding to launch 9th app.
+ </description>
+ </key>
+ <key name="app-ctrl-hotkey-10" type="as">
+ <default><![CDATA[['<Ctrl><Super>0']]]></default>
+ <summary>Keybinding to launch 10th dash app</summary>
+ <description>
+ Keybinding to launch 10th app.
+ </description>
+ </key>
+ <key name="app-shift-hotkey-1" type="as">
+ <default><![CDATA[['<Shift><Super>1']]]></default>
+ <summary>Keybinding to trigger 1st dash app with shift behavior</summary>
+ <description>
+ Keybinding to trigger 1st app with shift behavior.
+ </description>
+ </key>
+ <key name="app-shift-hotkey-2" type="as">
+ <default><![CDATA[['<Shift><Super>2']]]></default>
+ <summary>Keybinding to trigger 2nd dash app with shift behavior</summary>
+ <description>
+ Keybinding to trigger 2nd app with shift behavior.
+ </description>
+ </key>
+ <key name="app-shift-hotkey-3" type="as">
+ <default><![CDATA[['<Shift><Super>3']]]></default>
+ <summary>Keybinding to trigger 3rd dash app with shift behavior</summary>
+ <description>
+ Keybinding to trigger 3rd app with shift behavior.
+ </description>
+ </key>
+ <key name="app-shift-hotkey-4" type="as">
+ <default><![CDATA[['<Shift><Super>4']]]></default>
+ <summary>Keybinding to trigger 4th dash app with shift behavior</summary>
+ <description>
+ Keybinding to trigger 4th app with shift behavior.
+ </description>
+ </key>
+ <key name="app-shift-hotkey-5" type="as">
+ <default><![CDATA[['<Shift><Super>5']]]></default>
+ <summary>Keybinding to trigger 5th dash app with shift behavior</summary>
+ <description>
+ Keybinding to trigger 5th app with shift behavior.
+ </description>
+ </key>
+ <key name="app-shift-hotkey-6" type="as">
+ <default><![CDATA[['<Shift><Super>6']]]></default>
+ <summary>Keybinding to trigger 6th dash app with shift behavior</summary>
+ <description>
+ Keybinding to trigger 6th app with shift behavior.
+ </description>
+ </key>
+ <key name="app-shift-hotkey-7" type="as">
+ <default><![CDATA[['<Shift><Super>7']]]></default>
+ <summary>Keybinding to trigger 7th dash app with shift behavior</summary>
+ <description>
+ Keybinding to trigger 7th app with shift behavior.
+ </description>
+ </key>
+ <key name="app-shift-hotkey-8" type="as">
+ <default><![CDATA[['<Shift><Super>8']]]></default>
+ <summary>Keybinding to trigger 8th dash app with shift behavior</summary>
+ <description>
+ Keybinding to trigger 8th app with shift behavior.
+ </description>
+ </key>
+ <key name="app-shift-hotkey-9" type="as">
+ <default><![CDATA[['<Shift><Super>9']]]></default>
+ <summary>Keybinding to trigger 9th dash app with shift behavior</summary>
+ <description>
+ Keybinding to trigger 9th app with shift behavior.
+ </description>
+ </key>
+ <key name="app-shift-hotkey-10" type="as">
+ <default><![CDATA[['<Shift><Super>0']]]></default>
+ <summary>Keybinding to trigger 10th dash app with shift behavior</summary>
+ <description>
+ Keybinding to trigger 10th app with shift behavior.
+ </description>
+ </key>
+ <key name="app-hotkey-1" type="as">
+ <default><![CDATA[['<Super>1']]]></default>
+ <summary>Keybinding to trigger 1st dash app</summary>
+ <description>
+ Keybinding to either show or launch the 1st application in the dash.
+ </description>
+ </key>
+ <key name="app-hotkey-2" type="as">
+ <default><![CDATA[['<Super>2']]]></default>
+ <summary>Keybinding to trigger 2nd dash app</summary>
+ <description>
+ Keybinding to either show or launch the 2nd application in the dash.
+ </description>
+ </key>
+ <key name="app-hotkey-3" type="as">
+ <default><![CDATA[['<Super>3']]]></default>
+ <summary>Keybinding to trigger 3rd dash app</summary>
+ <description>
+ Keybinding to either show or launch the 3rd application in the dash.
+ </description>
+ </key>
+ <key name="app-hotkey-4" type="as">
+ <default><![CDATA[['<Super>4']]]></default>
+ <summary>Keybinding to trigger 4th dash app</summary>
+ <description>
+ Keybinding to either show or launch the 4th application in the dash.
+ </description>
+ </key>
+ <key name="app-hotkey-5" type="as">
+ <default><![CDATA[['<Super>5']]]></default>
+ <summary>Keybinding to trigger 5th dash app</summary>
+ <description>
+ Keybinding to either show or launch the 5th application in the dash.
+ </description>
+ </key>
+ <key name="app-hotkey-6" type="as">
+ <default><![CDATA[['<Super>6']]]></default>
+ <summary>Keybinding to trigger 6th dash app</summary>
+ <description>
+ Keybinding to either show or launch the 6th application in the dash.
+ </description>
+ </key>
+ <key name="app-hotkey-7" type="as">
+ <default><![CDATA[['<Super>7']]]></default>
+ <summary>Keybinding to trigger 7th dash app</summary>
+ <description>
+ Keybinding to either show or launch the 7th application in the dash.
+ </description>
+ </key>
+ <key name="app-hotkey-8" type="as">
+ <default><![CDATA[['<Super>8']]]></default>
+ <summary>Keybinding to trigger 8th dash app</summary>
+ <description>
+ Keybinding to either show or launch the 8th application in the dash.
+ </description>
+ </key>
+ <key name="app-hotkey-9" type="as">
+ <default><![CDATA[['<Super>9']]]></default>
+ <summary>Keybinding to trigger 9th dash app</summary>
+ <description>
+ Keybinding to either show or launch the 9th application in the dash.
+ </description>
+ </key>
+ <key name="app-hotkey-10" type="as">
+ <default><![CDATA[['<Super>0']]]></default>
+ <summary>Keybinding to trigger 10th dash app</summary>
+ <description>
+ Keybinding to either show or launch the 10th application in the dash.
+ </description>
+ </key>
+ <key name="force-straight-corner" type="b">
+ <default>false</default>
+ <summary>Force straight corners in dash</summary>
+ <description>Make the borders in the dash non rounded</description>
+ </key>
+ <key name="unity-backlit-items" type="b">
+ <default>false</default>
+ <summary>Enable unity7 like glossy backlit items</summary>
+ <description>Emulate the unity7 backlit glossy items behaviour</description>
+ </key>
+ <key name="apply-glossy-effect" type="b">
+ <default>true</default>
+ <summary>Enable glossy effect</summary>
+ <description>Toggle the glossy effect of the unity7 backlit feature.</description>
+ </key>
+ <key name="hide-tooltip" type="b">
+ <default>false</default>
+ <summary>Hide application tooltip</summary>
+ <description>Hide tooltip when hovering application in the dash.</description>
+ </key>
+ <key name="show-icons-emblems" type="b">
+ <default>true</default>
+ <summary>Show application icons emblems</summary>
+ <description>Show application icons emblems such as notification counters an progress bars.</description>
+ </key>
+ <key name="show-icons-notifications-counter" type="b">
+ <default>true</default>
+ <summary>Show the number of unread notifications in icons</summary>
+ <description>Show application icons emblems such as notification</description>
+ </key>
+ <key name="application-counter-overrides-notifications" type="b">
+ <default>true</default>
+ <summary>Wether the application-provioded counter via Unity API should override the notifications or be summed to it</summary>
+ <description>If an application provides a counter through the Unity API, then such value will be used. Otherwise the counter will be summed with the unread notification numbers (if enabled)</description>
+ </key>
+ </schema>
+</schemalist>
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/stylesheet.css b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/stylesheet.css
new file mode 100644
index 0000000..b8d1935
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/stylesheet.css
@@ -0,0 +1,1478 @@
+.dash-item-container .overview-tile .overview-icon,
+.dash-item-container .show-apps .overview-icon {
+ background-color: rgba(255, 255, 255, 0); }
+
+.dash-item-container .overview-tile:hover .overview-icon,
+.dash-item-container .overview-tile.focused .overview-icon,
+.dash-item-container .show-apps:hover .overview-icon {
+ background-color: rgba(238, 238, 238, 0.2); }
+
+.dash-item-container .overview-tile .app-grid-running-dot {
+ margin-bottom: 8px;
+ offset-y: 2px; }
+
+#dashtodockContainer.bottom #dash {
+ margin: 0px;
+ padding: 0px; }
+ #dashtodockContainer.bottom #dash .dash-background {
+ margin: 0;
+ margin-bottom: 4px;
+ padding: 0;
+ border-radius: 24px;
+ spacing: 6px; }
+ #dashtodockContainer.bottom #dash .dash-separator {
+ background-color: rgba(255, 255, 255, 0.1);
+ margin: 0 4px;
+ width: 1px;
+ height: 0;
+ margin-bottom: 4px; }
+ #dashtodockContainer.bottom #dash #dashtodockDashContainer {
+ padding: 10px;
+ padding-bottom: 0;
+ padding-top: 0; }
+ #dashtodockContainer.bottom #dash .dash-item-container {
+ /* Disable all margins defined upstream, we handle them here */
+ margin: 0 2px; }
+ #dashtodockContainer.bottom #dash .dash-item-container > * {
+ margin: 0; }
+ #dashtodockContainer.bottom #dash .dash-item-container:ltr:first-child, #dashtodockContainer.bottom #dash .dash-item-container:rtl:last-child {
+ margin-left: 0; }
+ #dashtodockContainer.bottom #dash .dash-item-container:ltr:last-child, #dashtodockContainer.bottom #dash .dash-item-container:rtl:first-child {
+ margin-right: 0; }
+ #dashtodockContainer.bottom #dash .dash-item-container .app-well-app,
+ #dashtodockContainer.bottom #dash .dash-item-container .show-apps,
+ #dashtodockContainer.bottom #dash .dash-item-container .overview-tile {
+ padding: 2px 2px;
+ padding-bottom: 14px;
+ padding-top: 10px; }
+ #dashtodockContainer.bottom #dash .dash-item-container .app-well-app:ltr:first-child, #dashtodockContainer.bottom #dash .dash-item-container .app-well-app:rtl:last-child,
+ #dashtodockContainer.bottom #dash .dash-item-container .show-apps:ltr:first-child,
+ #dashtodockContainer.bottom #dash .dash-item-container .show-apps:rtl:last-child,
+ #dashtodockContainer.bottom #dash .dash-item-container .overview-tile:ltr:first-child,
+ #dashtodockContainer.bottom #dash .dash-item-container .overview-tile:rtl:last-child {
+ padding-left: 0; }
+ #dashtodockContainer.bottom #dash .dash-item-container .app-well-app:ltr:last-child, #dashtodockContainer.bottom #dash .dash-item-container .app-well-app:rtl:first-child,
+ #dashtodockContainer.bottom #dash .dash-item-container .show-apps:ltr:last-child,
+ #dashtodockContainer.bottom #dash .dash-item-container .show-apps:rtl:first-child,
+ #dashtodockContainer.bottom #dash .dash-item-container .overview-tile:ltr:last-child,
+ #dashtodockContainer.bottom #dash .dash-item-container .overview-tile:rtl:first-child {
+ padding-right: 0; }
+ #dashtodockContainer.bottom #dash .dash-item-container .app-well-app.updating,
+ #dashtodockContainer.bottom #dash .dash-item-container .show-apps.updating,
+ #dashtodockContainer.bottom #dash .dash-item-container .overview-tile.updating {
+ opacity: 0.5; }
+ #dashtodockContainer.bottom #dash .dash-item-container .app-well-app.running .overview-icon,
+ #dashtodockContainer.bottom #dash .dash-item-container .overview-tile.running .overview-icon {
+ background-image: none; }
+ #dashtodockContainer.bottom #dash .dash-item-container .app-well-app.focused .overview-icon,
+ #dashtodockContainer.bottom #dash .dash-item-container .overview-tile.focused .overview-icon {
+ background-color: rgba(238, 238, 238, 0.2); }
+ #dashtodockContainer.bottom #dash .dash-item-container .app-well-app .app-well-app-running-dot,
+ #dashtodockContainer.bottom #dash .dash-item-container .app-well-app .app-grid-running-dot,
+ #dashtodockContainer.bottom #dash .dash-item-container .overview-tile .app-well-app-running-dot,
+ #dashtodockContainer.bottom #dash .dash-item-container .overview-tile .app-grid-running-dot {
+ margin-bottom: 2px; }
+ #dashtodockContainer.bottom #dash .dash-item-container > StButton {
+ transition-duration: 250;
+ background-size: contain; }
+
+#dashtodockContainer.bottom.fixed #dash .dash-background {
+ margin-top: 4px; }
+
+#dashtodockContainer.bottom.fixed #dash .dash-item-container .app-well-app,
+#dashtodockContainer.bottom.fixed #dash .dash-item-container .show-apps {
+ padding-top: 14px; }
+
+.dash-label.bottom {
+ margin-bottom: 10px;
+ -x-offset: 0;
+ -y-offset: 0; }
+
+#dashtodockContainer.bottom.shrink #dash {
+ margin: 0px;
+ padding: 0px; }
+ #dashtodockContainer.bottom.shrink #dash .dash-background {
+ margin: 0;
+ margin-bottom: 4px;
+ padding: 0;
+ border-radius: 18px;
+ spacing: 2px; }
+ #dashtodockContainer.bottom.shrink #dash .dash-separator {
+ background-color: rgba(255, 255, 255, 0.1);
+ margin: 0 1px;
+ width: 1px;
+ height: 0;
+ margin-bottom: 4px; }
+ #dashtodockContainer.bottom.shrink #dash #dashtodockDashContainer {
+ padding: 3px;
+ padding-bottom: 0;
+ padding-top: 0; }
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container {
+ /* Disable all margins defined upstream, we handle them here */
+ margin: 0 2px; }
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container > * {
+ margin: 0; }
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container:ltr:first-child, #dashtodockContainer.bottom.shrink #dash .dash-item-container:rtl:last-child {
+ margin-left: 0; }
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container:ltr:last-child, #dashtodockContainer.bottom.shrink #dash .dash-item-container:rtl:first-child {
+ margin-right: 0; }
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .app-well-app,
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .show-apps,
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .overview-tile {
+ padding: 1px 1px;
+ padding-bottom: 7px;
+ padding-top: 3px; }
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .app-well-app:ltr:first-child, #dashtodockContainer.bottom.shrink #dash .dash-item-container .app-well-app:rtl:last-child,
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .show-apps:ltr:first-child,
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .show-apps:rtl:last-child,
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .overview-tile:ltr:first-child,
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .overview-tile:rtl:last-child {
+ padding-left: 0; }
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .app-well-app:ltr:last-child, #dashtodockContainer.bottom.shrink #dash .dash-item-container .app-well-app:rtl:first-child,
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .show-apps:ltr:last-child,
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .show-apps:rtl:first-child,
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .overview-tile:ltr:last-child,
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .overview-tile:rtl:first-child {
+ padding-right: 0; }
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .app-well-app.updating,
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .show-apps.updating,
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .overview-tile.updating {
+ opacity: 0.5; }
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .app-well-app.running .overview-icon,
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .overview-tile.running .overview-icon {
+ background-image: none; }
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .app-well-app.focused .overview-icon,
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .overview-tile.focused .overview-icon {
+ background-color: rgba(238, 238, 238, 0.2); }
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .app-well-app .app-well-app-running-dot,
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .app-well-app .app-grid-running-dot,
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .overview-tile .app-well-app-running-dot,
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .overview-tile .app-grid-running-dot {
+ margin-bottom: 2px; }
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container > StButton {
+ transition-duration: 250;
+ background-size: contain; }
+
+#dashtodockContainer.bottom.shrink.fixed #dash .dash-background {
+ margin-top: 1px; }
+
+#dashtodockContainer.bottom.shrink.fixed #dash .dash-item-container .app-well-app,
+#dashtodockContainer.bottom.shrink.fixed #dash .dash-item-container .show-apps {
+ padding-top: 4px; }
+
+.dash-label.bottom.shrink {
+ margin-bottom: 3px;
+ -x-offset: 0;
+ -y-offset: 0; }
+
+#dashtodockContainer.bottom.extended #dash {
+ margin: 0px;
+ padding: 0px; }
+ #dashtodockContainer.bottom.extended #dash .dash-background {
+ margin: 0;
+ margin-bottom: 0;
+ padding: 0;
+ border-radius: 0;
+ spacing: 6px; }
+ #dashtodockContainer.bottom.extended #dash .dash-separator {
+ background-color: rgba(255, 255, 255, 0.1);
+ margin: 0 4px;
+ width: 1px;
+ height: 0;
+ margin-bottom: 0; }
+ #dashtodockContainer.bottom.extended #dash #dashtodockDashContainer {
+ padding: 0;
+ padding-bottom: 0;
+ padding-top: 0; }
+ #dashtodockContainer.bottom.extended #dash #dashtodockDashContainer > :first-child .show-apps {
+ padding-left: 8px; }
+ #dashtodockContainer.bottom.extended #dash #dashtodockDashContainer #dashtodockDashScrollview:first-child {
+ padding-left: 8px; }
+ #dashtodockContainer.bottom.extended #dash #dashtodockDashContainer > :last-child .show-apps {
+ padding-right: 8px; }
+ #dashtodockContainer.bottom.extended #dash #dashtodockDashContainer #dashtodockDashScrollview:last-child {
+ padding-right: 8px; }
+ #dashtodockContainer.bottom.extended #dash .dash-item-container {
+ /* Disable all margins defined upstream, we handle them here */
+ margin: 0 2px; }
+ #dashtodockContainer.bottom.extended #dash .dash-item-container > * {
+ margin: 0; }
+ #dashtodockContainer.bottom.extended #dash .dash-item-container:ltr:first-child, #dashtodockContainer.bottom.extended #dash .dash-item-container:rtl:last-child {
+ margin-left: 0; }
+ #dashtodockContainer.bottom.extended #dash .dash-item-container:ltr:last-child, #dashtodockContainer.bottom.extended #dash .dash-item-container:rtl:first-child {
+ margin-right: 0; }
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .app-well-app,
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .show-apps,
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .overview-tile {
+ padding: 2px 2px;
+ padding-bottom: 10px;
+ padding-top: 10px; }
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .app-well-app:ltr:first-child, #dashtodockContainer.bottom.extended #dash .dash-item-container .app-well-app:rtl:last-child,
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .show-apps:ltr:first-child,
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .show-apps:rtl:last-child,
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .overview-tile:ltr:first-child,
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .overview-tile:rtl:last-child {
+ padding-left: 0; }
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .app-well-app:ltr:last-child, #dashtodockContainer.bottom.extended #dash .dash-item-container .app-well-app:rtl:first-child,
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .show-apps:ltr:last-child,
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .show-apps:rtl:first-child,
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .overview-tile:ltr:last-child,
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .overview-tile:rtl:first-child {
+ padding-right: 0; }
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .app-well-app.updating,
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .show-apps.updating,
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .overview-tile.updating {
+ opacity: 0.5; }
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .app-well-app.running .overview-icon,
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .overview-tile.running .overview-icon {
+ background-image: none; }
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .app-well-app.focused .overview-icon,
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .overview-tile.focused .overview-icon {
+ background-color: rgba(238, 238, 238, 0.2); }
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .app-well-app .app-well-app-running-dot,
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .app-well-app .app-grid-running-dot,
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .overview-tile .app-well-app-running-dot,
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .overview-tile .app-grid-running-dot {
+ margin-bottom: 2px; }
+ #dashtodockContainer.bottom.extended #dash .dash-item-container > StButton {
+ transition-duration: 250;
+ background-size: contain; }
+
+#dashtodockContainer.bottom.extended.fixed #dash .dash-background {
+ margin-top: 0; }
+
+#dashtodockContainer.bottom.extended.fixed #dash .dash-item-container .app-well-app,
+#dashtodockContainer.bottom.extended.fixed #dash .dash-item-container .show-apps {
+ padding-top: 10px; }
+
+.dash-label.bottom {
+ margin-bottom: 10px;
+ -x-offset: 0;
+ -y-offset: 0; }
+
+#dashtodockContainer.bottom.extended.shrink #dash {
+ margin: 0px;
+ padding: 0px; }
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-background {
+ margin: 0;
+ margin-bottom: 0;
+ padding: 0;
+ border-radius: 0;
+ spacing: 2px; }
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-separator {
+ background-color: rgba(255, 255, 255, 0.1);
+ margin: 0 1px;
+ width: 1px;
+ height: 0;
+ margin-bottom: 0; }
+ #dashtodockContainer.bottom.extended.shrink #dash #dashtodockDashContainer {
+ padding: 0;
+ padding-bottom: 0;
+ padding-top: 0; }
+ #dashtodockContainer.bottom.extended.shrink #dash #dashtodockDashContainer > :first-child .show-apps {
+ padding-left: 6px; }
+ #dashtodockContainer.bottom.extended.shrink #dash #dashtodockDashContainer #dashtodockDashScrollview:first-child {
+ padding-left: 6px; }
+ #dashtodockContainer.bottom.extended.shrink #dash #dashtodockDashContainer > :last-child .show-apps {
+ padding-right: 6px; }
+ #dashtodockContainer.bottom.extended.shrink #dash #dashtodockDashContainer #dashtodockDashScrollview:last-child {
+ padding-right: 6px; }
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container {
+ /* Disable all margins defined upstream, we handle them here */
+ margin: 0 2px; }
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container > * {
+ margin: 0; }
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container:ltr:first-child, #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container:rtl:last-child {
+ margin-left: 0; }
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container:ltr:last-child, #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container:rtl:first-child {
+ margin-right: 0; }
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .app-well-app,
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .show-apps,
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .overview-tile {
+ padding: 1px 1px;
+ padding-bottom: 3px;
+ padding-top: 3px; }
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .app-well-app:ltr:first-child, #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .app-well-app:rtl:last-child,
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .show-apps:ltr:first-child,
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .show-apps:rtl:last-child,
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .overview-tile:ltr:first-child,
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .overview-tile:rtl:last-child {
+ padding-left: 0; }
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .app-well-app:ltr:last-child, #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .app-well-app:rtl:first-child,
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .show-apps:ltr:last-child,
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .show-apps:rtl:first-child,
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .overview-tile:ltr:last-child,
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .overview-tile:rtl:first-child {
+ padding-right: 0; }
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .app-well-app.updating,
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .show-apps.updating,
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .overview-tile.updating {
+ opacity: 0.5; }
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .app-well-app.running .overview-icon,
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .overview-tile.running .overview-icon {
+ background-image: none; }
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .app-well-app.focused .overview-icon,
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .overview-tile.focused .overview-icon {
+ background-color: rgba(238, 238, 238, 0.2); }
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .app-well-app .app-well-app-running-dot,
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .app-well-app .app-grid-running-dot,
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .overview-tile .app-well-app-running-dot,
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .overview-tile .app-grid-running-dot {
+ margin-bottom: 2px; }
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container > StButton {
+ transition-duration: 250;
+ background-size: contain; }
+
+#dashtodockContainer.bottom.extended.shrink.fixed #dash .dash-background {
+ margin-top: 0; }
+
+#dashtodockContainer.bottom.extended.shrink.fixed #dash .dash-item-container .app-well-app,
+#dashtodockContainer.bottom.extended.shrink.fixed #dash .dash-item-container .show-apps {
+ padding-top: 3px; }
+
+.dash-label.bottom.shrink {
+ margin-bottom: 3px;
+ -x-offset: 0;
+ -y-offset: 0; }
+
+.dashtodock-app-well-preview-menu-item.bottom {
+ padding: 0.5em 0.5em; }
+ .dashtodock-app-well-preview-menu-item.bottom:ltr:first-child, .dashtodock-app-well-preview-menu-item.bottom:rtl:last-child {
+ padding-left: 1em; }
+ .dashtodock-app-well-preview-menu-item.bottom:ltr:last-child, .dashtodock-app-well-preview-menu-item.bottom:rtl:first-child {
+ padding-right: 1em; }
+
+#dashtodockContainer.top #dash {
+ margin: 0px;
+ padding: 0px; }
+ #dashtodockContainer.top #dash .dash-background {
+ margin: 0;
+ margin-top: 4px;
+ padding: 0;
+ border-radius: 24px;
+ spacing: 6px; }
+ #dashtodockContainer.top #dash .dash-separator {
+ background-color: rgba(255, 255, 255, 0.1);
+ margin: 0 4px;
+ width: 1px;
+ height: 0;
+ margin-top: 4px; }
+ #dashtodockContainer.top #dash #dashtodockDashContainer {
+ padding: 10px;
+ padding-top: 0;
+ padding-bottom: 0; }
+ #dashtodockContainer.top #dash .dash-item-container {
+ /* Disable all margins defined upstream, we handle them here */
+ margin: 0 2px; }
+ #dashtodockContainer.top #dash .dash-item-container > * {
+ margin: 0; }
+ #dashtodockContainer.top #dash .dash-item-container:ltr:first-child, #dashtodockContainer.top #dash .dash-item-container:rtl:last-child {
+ margin-left: 0; }
+ #dashtodockContainer.top #dash .dash-item-container:ltr:last-child, #dashtodockContainer.top #dash .dash-item-container:rtl:first-child {
+ margin-right: 0; }
+ #dashtodockContainer.top #dash .dash-item-container .app-well-app,
+ #dashtodockContainer.top #dash .dash-item-container .show-apps,
+ #dashtodockContainer.top #dash .dash-item-container .overview-tile {
+ padding: 2px 2px;
+ padding-top: 14px;
+ padding-bottom: 10px; }
+ #dashtodockContainer.top #dash .dash-item-container .app-well-app:ltr:first-child, #dashtodockContainer.top #dash .dash-item-container .app-well-app:rtl:last-child,
+ #dashtodockContainer.top #dash .dash-item-container .show-apps:ltr:first-child,
+ #dashtodockContainer.top #dash .dash-item-container .show-apps:rtl:last-child,
+ #dashtodockContainer.top #dash .dash-item-container .overview-tile:ltr:first-child,
+ #dashtodockContainer.top #dash .dash-item-container .overview-tile:rtl:last-child {
+ padding-left: 0; }
+ #dashtodockContainer.top #dash .dash-item-container .app-well-app:ltr:last-child, #dashtodockContainer.top #dash .dash-item-container .app-well-app:rtl:first-child,
+ #dashtodockContainer.top #dash .dash-item-container .show-apps:ltr:last-child,
+ #dashtodockContainer.top #dash .dash-item-container .show-apps:rtl:first-child,
+ #dashtodockContainer.top #dash .dash-item-container .overview-tile:ltr:last-child,
+ #dashtodockContainer.top #dash .dash-item-container .overview-tile:rtl:first-child {
+ padding-right: 0; }
+ #dashtodockContainer.top #dash .dash-item-container .app-well-app.updating,
+ #dashtodockContainer.top #dash .dash-item-container .show-apps.updating,
+ #dashtodockContainer.top #dash .dash-item-container .overview-tile.updating {
+ opacity: 0.5; }
+ #dashtodockContainer.top #dash .dash-item-container .app-well-app.running .overview-icon,
+ #dashtodockContainer.top #dash .dash-item-container .overview-tile.running .overview-icon {
+ background-image: none; }
+ #dashtodockContainer.top #dash .dash-item-container .app-well-app.focused .overview-icon,
+ #dashtodockContainer.top #dash .dash-item-container .overview-tile.focused .overview-icon {
+ background-color: rgba(238, 238, 238, 0.2); }
+ #dashtodockContainer.top #dash .dash-item-container .app-well-app .app-well-app-running-dot,
+ #dashtodockContainer.top #dash .dash-item-container .app-well-app .app-grid-running-dot,
+ #dashtodockContainer.top #dash .dash-item-container .overview-tile .app-well-app-running-dot,
+ #dashtodockContainer.top #dash .dash-item-container .overview-tile .app-grid-running-dot {
+ margin-bottom: 2px; }
+ #dashtodockContainer.top #dash .dash-item-container > StButton {
+ transition-duration: 250;
+ background-size: contain; }
+
+#dashtodockContainer.top.fixed #dash .dash-background {
+ margin-bottom: 4px; }
+
+#dashtodockContainer.top.fixed #dash .dash-item-container .app-well-app,
+#dashtodockContainer.top.fixed #dash .dash-item-container .show-apps {
+ padding-bottom: 14px; }
+
+.dash-label.top {
+ margin-top: 10px;
+ -x-offset: 0;
+ -y-offset: 0; }
+
+#dashtodockContainer.top.shrink #dash {
+ margin: 0px;
+ padding: 0px; }
+ #dashtodockContainer.top.shrink #dash .dash-background {
+ margin: 0;
+ margin-top: 4px;
+ padding: 0;
+ border-radius: 18px;
+ spacing: 2px; }
+ #dashtodockContainer.top.shrink #dash .dash-separator {
+ background-color: rgba(255, 255, 255, 0.1);
+ margin: 0 1px;
+ width: 1px;
+ height: 0;
+ margin-top: 4px; }
+ #dashtodockContainer.top.shrink #dash #dashtodockDashContainer {
+ padding: 3px;
+ padding-top: 0;
+ padding-bottom: 0; }
+ #dashtodockContainer.top.shrink #dash .dash-item-container {
+ /* Disable all margins defined upstream, we handle them here */
+ margin: 0 2px; }
+ #dashtodockContainer.top.shrink #dash .dash-item-container > * {
+ margin: 0; }
+ #dashtodockContainer.top.shrink #dash .dash-item-container:ltr:first-child, #dashtodockContainer.top.shrink #dash .dash-item-container:rtl:last-child {
+ margin-left: 0; }
+ #dashtodockContainer.top.shrink #dash .dash-item-container:ltr:last-child, #dashtodockContainer.top.shrink #dash .dash-item-container:rtl:first-child {
+ margin-right: 0; }
+ #dashtodockContainer.top.shrink #dash .dash-item-container .app-well-app,
+ #dashtodockContainer.top.shrink #dash .dash-item-container .show-apps,
+ #dashtodockContainer.top.shrink #dash .dash-item-container .overview-tile {
+ padding: 1px 1px;
+ padding-top: 7px;
+ padding-bottom: 3px; }
+ #dashtodockContainer.top.shrink #dash .dash-item-container .app-well-app:ltr:first-child, #dashtodockContainer.top.shrink #dash .dash-item-container .app-well-app:rtl:last-child,
+ #dashtodockContainer.top.shrink #dash .dash-item-container .show-apps:ltr:first-child,
+ #dashtodockContainer.top.shrink #dash .dash-item-container .show-apps:rtl:last-child,
+ #dashtodockContainer.top.shrink #dash .dash-item-container .overview-tile:ltr:first-child,
+ #dashtodockContainer.top.shrink #dash .dash-item-container .overview-tile:rtl:last-child {
+ padding-left: 0; }
+ #dashtodockContainer.top.shrink #dash .dash-item-container .app-well-app:ltr:last-child, #dashtodockContainer.top.shrink #dash .dash-item-container .app-well-app:rtl:first-child,
+ #dashtodockContainer.top.shrink #dash .dash-item-container .show-apps:ltr:last-child,
+ #dashtodockContainer.top.shrink #dash .dash-item-container .show-apps:rtl:first-child,
+ #dashtodockContainer.top.shrink #dash .dash-item-container .overview-tile:ltr:last-child,
+ #dashtodockContainer.top.shrink #dash .dash-item-container .overview-tile:rtl:first-child {
+ padding-right: 0; }
+ #dashtodockContainer.top.shrink #dash .dash-item-container .app-well-app.updating,
+ #dashtodockContainer.top.shrink #dash .dash-item-container .show-apps.updating,
+ #dashtodockContainer.top.shrink #dash .dash-item-container .overview-tile.updating {
+ opacity: 0.5; }
+ #dashtodockContainer.top.shrink #dash .dash-item-container .app-well-app.running .overview-icon,
+ #dashtodockContainer.top.shrink #dash .dash-item-container .overview-tile.running .overview-icon {
+ background-image: none; }
+ #dashtodockContainer.top.shrink #dash .dash-item-container .app-well-app.focused .overview-icon,
+ #dashtodockContainer.top.shrink #dash .dash-item-container .overview-tile.focused .overview-icon {
+ background-color: rgba(238, 238, 238, 0.2); }
+ #dashtodockContainer.top.shrink #dash .dash-item-container .app-well-app .app-well-app-running-dot,
+ #dashtodockContainer.top.shrink #dash .dash-item-container .app-well-app .app-grid-running-dot,
+ #dashtodockContainer.top.shrink #dash .dash-item-container .overview-tile .app-well-app-running-dot,
+ #dashtodockContainer.top.shrink #dash .dash-item-container .overview-tile .app-grid-running-dot {
+ margin-bottom: 2px; }
+ #dashtodockContainer.top.shrink #dash .dash-item-container > StButton {
+ transition-duration: 250;
+ background-size: contain; }
+
+#dashtodockContainer.top.shrink.fixed #dash .dash-background {
+ margin-bottom: 1px; }
+
+#dashtodockContainer.top.shrink.fixed #dash .dash-item-container .app-well-app,
+#dashtodockContainer.top.shrink.fixed #dash .dash-item-container .show-apps {
+ padding-bottom: 4px; }
+
+.dash-label.top.shrink {
+ margin-top: 3px;
+ -x-offset: 0;
+ -y-offset: 0; }
+
+#dashtodockContainer.top.extended #dash {
+ margin: 0px;
+ padding: 0px; }
+ #dashtodockContainer.top.extended #dash .dash-background {
+ margin: 0;
+ margin-top: 0;
+ padding: 0;
+ border-radius: 0;
+ spacing: 6px; }
+ #dashtodockContainer.top.extended #dash .dash-separator {
+ background-color: rgba(255, 255, 255, 0.1);
+ margin: 0 4px;
+ width: 1px;
+ height: 0;
+ margin-top: 0; }
+ #dashtodockContainer.top.extended #dash #dashtodockDashContainer {
+ padding: 0;
+ padding-top: 0;
+ padding-bottom: 0; }
+ #dashtodockContainer.top.extended #dash #dashtodockDashContainer > :first-child .show-apps {
+ padding-left: 8px; }
+ #dashtodockContainer.top.extended #dash #dashtodockDashContainer #dashtodockDashScrollview:first-child {
+ padding-left: 8px; }
+ #dashtodockContainer.top.extended #dash #dashtodockDashContainer > :last-child .show-apps {
+ padding-right: 8px; }
+ #dashtodockContainer.top.extended #dash #dashtodockDashContainer #dashtodockDashScrollview:last-child {
+ padding-right: 8px; }
+ #dashtodockContainer.top.extended #dash .dash-item-container {
+ /* Disable all margins defined upstream, we handle them here */
+ margin: 0 2px; }
+ #dashtodockContainer.top.extended #dash .dash-item-container > * {
+ margin: 0; }
+ #dashtodockContainer.top.extended #dash .dash-item-container:ltr:first-child, #dashtodockContainer.top.extended #dash .dash-item-container:rtl:last-child {
+ margin-left: 0; }
+ #dashtodockContainer.top.extended #dash .dash-item-container:ltr:last-child, #dashtodockContainer.top.extended #dash .dash-item-container:rtl:first-child {
+ margin-right: 0; }
+ #dashtodockContainer.top.extended #dash .dash-item-container .app-well-app,
+ #dashtodockContainer.top.extended #dash .dash-item-container .show-apps,
+ #dashtodockContainer.top.extended #dash .dash-item-container .overview-tile {
+ padding: 2px 2px;
+ padding-top: 10px;
+ padding-bottom: 10px; }
+ #dashtodockContainer.top.extended #dash .dash-item-container .app-well-app:ltr:first-child, #dashtodockContainer.top.extended #dash .dash-item-container .app-well-app:rtl:last-child,
+ #dashtodockContainer.top.extended #dash .dash-item-container .show-apps:ltr:first-child,
+ #dashtodockContainer.top.extended #dash .dash-item-container .show-apps:rtl:last-child,
+ #dashtodockContainer.top.extended #dash .dash-item-container .overview-tile:ltr:first-child,
+ #dashtodockContainer.top.extended #dash .dash-item-container .overview-tile:rtl:last-child {
+ padding-left: 0; }
+ #dashtodockContainer.top.extended #dash .dash-item-container .app-well-app:ltr:last-child, #dashtodockContainer.top.extended #dash .dash-item-container .app-well-app:rtl:first-child,
+ #dashtodockContainer.top.extended #dash .dash-item-container .show-apps:ltr:last-child,
+ #dashtodockContainer.top.extended #dash .dash-item-container .show-apps:rtl:first-child,
+ #dashtodockContainer.top.extended #dash .dash-item-container .overview-tile:ltr:last-child,
+ #dashtodockContainer.top.extended #dash .dash-item-container .overview-tile:rtl:first-child {
+ padding-right: 0; }
+ #dashtodockContainer.top.extended #dash .dash-item-container .app-well-app.updating,
+ #dashtodockContainer.top.extended #dash .dash-item-container .show-apps.updating,
+ #dashtodockContainer.top.extended #dash .dash-item-container .overview-tile.updating {
+ opacity: 0.5; }
+ #dashtodockContainer.top.extended #dash .dash-item-container .app-well-app.running .overview-icon,
+ #dashtodockContainer.top.extended #dash .dash-item-container .overview-tile.running .overview-icon {
+ background-image: none; }
+ #dashtodockContainer.top.extended #dash .dash-item-container .app-well-app.focused .overview-icon,
+ #dashtodockContainer.top.extended #dash .dash-item-container .overview-tile.focused .overview-icon {
+ background-color: rgba(238, 238, 238, 0.2); }
+ #dashtodockContainer.top.extended #dash .dash-item-container .app-well-app .app-well-app-running-dot,
+ #dashtodockContainer.top.extended #dash .dash-item-container .app-well-app .app-grid-running-dot,
+ #dashtodockContainer.top.extended #dash .dash-item-container .overview-tile .app-well-app-running-dot,
+ #dashtodockContainer.top.extended #dash .dash-item-container .overview-tile .app-grid-running-dot {
+ margin-bottom: 2px; }
+ #dashtodockContainer.top.extended #dash .dash-item-container > StButton {
+ transition-duration: 250;
+ background-size: contain; }
+
+#dashtodockContainer.top.extended.fixed #dash .dash-background {
+ margin-bottom: 0; }
+
+#dashtodockContainer.top.extended.fixed #dash .dash-item-container .app-well-app,
+#dashtodockContainer.top.extended.fixed #dash .dash-item-container .show-apps {
+ padding-bottom: 10px; }
+
+.dash-label.top {
+ margin-top: 10px;
+ -x-offset: 0;
+ -y-offset: 0; }
+
+#dashtodockContainer.top.extended.shrink #dash {
+ margin: 0px;
+ padding: 0px; }
+ #dashtodockContainer.top.extended.shrink #dash .dash-background {
+ margin: 0;
+ margin-top: 0;
+ padding: 0;
+ border-radius: 0;
+ spacing: 2px; }
+ #dashtodockContainer.top.extended.shrink #dash .dash-separator {
+ background-color: rgba(255, 255, 255, 0.1);
+ margin: 0 1px;
+ width: 1px;
+ height: 0;
+ margin-top: 0; }
+ #dashtodockContainer.top.extended.shrink #dash #dashtodockDashContainer {
+ padding: 0;
+ padding-top: 0;
+ padding-bottom: 0; }
+ #dashtodockContainer.top.extended.shrink #dash #dashtodockDashContainer > :first-child .show-apps {
+ padding-left: 6px; }
+ #dashtodockContainer.top.extended.shrink #dash #dashtodockDashContainer #dashtodockDashScrollview:first-child {
+ padding-left: 6px; }
+ #dashtodockContainer.top.extended.shrink #dash #dashtodockDashContainer > :last-child .show-apps {
+ padding-right: 6px; }
+ #dashtodockContainer.top.extended.shrink #dash #dashtodockDashContainer #dashtodockDashScrollview:last-child {
+ padding-right: 6px; }
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container {
+ /* Disable all margins defined upstream, we handle them here */
+ margin: 0 2px; }
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container > * {
+ margin: 0; }
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container:ltr:first-child, #dashtodockContainer.top.extended.shrink #dash .dash-item-container:rtl:last-child {
+ margin-left: 0; }
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container:ltr:last-child, #dashtodockContainer.top.extended.shrink #dash .dash-item-container:rtl:first-child {
+ margin-right: 0; }
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .app-well-app,
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .show-apps,
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .overview-tile {
+ padding: 1px 1px;
+ padding-top: 3px;
+ padding-bottom: 3px; }
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .app-well-app:ltr:first-child, #dashtodockContainer.top.extended.shrink #dash .dash-item-container .app-well-app:rtl:last-child,
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .show-apps:ltr:first-child,
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .show-apps:rtl:last-child,
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .overview-tile:ltr:first-child,
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .overview-tile:rtl:last-child {
+ padding-left: 0; }
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .app-well-app:ltr:last-child, #dashtodockContainer.top.extended.shrink #dash .dash-item-container .app-well-app:rtl:first-child,
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .show-apps:ltr:last-child,
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .show-apps:rtl:first-child,
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .overview-tile:ltr:last-child,
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .overview-tile:rtl:first-child {
+ padding-right: 0; }
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .app-well-app.updating,
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .show-apps.updating,
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .overview-tile.updating {
+ opacity: 0.5; }
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .app-well-app.running .overview-icon,
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .overview-tile.running .overview-icon {
+ background-image: none; }
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .app-well-app.focused .overview-icon,
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .overview-tile.focused .overview-icon {
+ background-color: rgba(238, 238, 238, 0.2); }
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .app-well-app .app-well-app-running-dot,
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .app-well-app .app-grid-running-dot,
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .overview-tile .app-well-app-running-dot,
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .overview-tile .app-grid-running-dot {
+ margin-bottom: 2px; }
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container > StButton {
+ transition-duration: 250;
+ background-size: contain; }
+
+#dashtodockContainer.top.extended.shrink.fixed #dash .dash-background {
+ margin-bottom: 0; }
+
+#dashtodockContainer.top.extended.shrink.fixed #dash .dash-item-container .app-well-app,
+#dashtodockContainer.top.extended.shrink.fixed #dash .dash-item-container .show-apps {
+ padding-bottom: 3px; }
+
+.dash-label.top.shrink {
+ margin-top: 3px;
+ -x-offset: 0;
+ -y-offset: 0; }
+
+.dashtodock-app-well-preview-menu-item.top {
+ padding: 0.5em 0.5em; }
+ .dashtodock-app-well-preview-menu-item.top:ltr:first-child, .dashtodock-app-well-preview-menu-item.top:rtl:last-child {
+ padding-left: 1em; }
+ .dashtodock-app-well-preview-menu-item.top:ltr:last-child, .dashtodock-app-well-preview-menu-item.top:rtl:first-child {
+ padding-right: 1em; }
+
+#dashtodockContainer.left #dash {
+ margin: 0px;
+ padding: 0px; }
+ #dashtodockContainer.left #dash .dash-background {
+ margin: 0;
+ margin-left: 4px;
+ padding: 0;
+ border-radius: 24px;
+ spacing: 6px; }
+ #dashtodockContainer.left #dash .dash-separator {
+ background-color: rgba(255, 255, 255, 0.1);
+ width: 0;
+ height: 1px;
+ margin: 4px 0;
+ margin-left: 4px; }
+ #dashtodockContainer.left #dash #dashtodockDashContainer {
+ padding: 10px;
+ padding-left: 0;
+ padding-right: 0; }
+ #dashtodockContainer.left #dash .dash-item-container {
+ /* Disable all margins defined upstream, we handle them here */
+ margin: 2px 0; }
+ #dashtodockContainer.left #dash .dash-item-container > * {
+ margin: 0; }
+ #dashtodockContainer.left #dash .dash-item-container:first-child {
+ margin-top: 0; }
+ #dashtodockContainer.left #dash .dash-item-container:last-child {
+ margin-bottom: 0; }
+ #dashtodockContainer.left #dash .dash-item-container .app-well-app,
+ #dashtodockContainer.left #dash .dash-item-container .show-apps,
+ #dashtodockContainer.left #dash .dash-item-container .overview-tile {
+ padding: 2px 2px;
+ padding-left: 14px;
+ padding-right: 10px; }
+ #dashtodockContainer.left #dash .dash-item-container .app-well-app:first-child,
+ #dashtodockContainer.left #dash .dash-item-container .show-apps:first-child,
+ #dashtodockContainer.left #dash .dash-item-container .overview-tile:first-child {
+ padding-top: 0; }
+ #dashtodockContainer.left #dash .dash-item-container .app-well-app:last-child,
+ #dashtodockContainer.left #dash .dash-item-container .show-apps:last-child,
+ #dashtodockContainer.left #dash .dash-item-container .overview-tile:last-child {
+ padding-bottom: 0; }
+ #dashtodockContainer.left #dash .dash-item-container .app-well-app.updating,
+ #dashtodockContainer.left #dash .dash-item-container .show-apps.updating,
+ #dashtodockContainer.left #dash .dash-item-container .overview-tile.updating {
+ opacity: 0.5; }
+ #dashtodockContainer.left #dash .dash-item-container .app-well-app.running .overview-icon,
+ #dashtodockContainer.left #dash .dash-item-container .overview-tile.running .overview-icon {
+ background-image: none; }
+ #dashtodockContainer.left #dash .dash-item-container .app-well-app.focused .overview-icon,
+ #dashtodockContainer.left #dash .dash-item-container .overview-tile.focused .overview-icon {
+ background-color: rgba(238, 238, 238, 0.2); }
+ #dashtodockContainer.left #dash .dash-item-container .app-well-app .app-well-app-running-dot,
+ #dashtodockContainer.left #dash .dash-item-container .app-well-app .app-grid-running-dot,
+ #dashtodockContainer.left #dash .dash-item-container .overview-tile .app-well-app-running-dot,
+ #dashtodockContainer.left #dash .dash-item-container .overview-tile .app-grid-running-dot {
+ margin-bottom: 2px; }
+ #dashtodockContainer.left #dash .dash-item-container > StButton {
+ transition-duration: 250;
+ background-size: contain; }
+
+#dashtodockContainer.left.fixed #dash .dash-background {
+ margin-right: 4px; }
+
+#dashtodockContainer.left.fixed #dash .dash-item-container .app-well-app,
+#dashtodockContainer.left.fixed #dash .dash-item-container .show-apps {
+ padding-right: 14px; }
+
+.dash-label.left {
+ margin: 0;
+ -x-offset: 10px;
+ -y-offset: 0; }
+
+#dashtodockContainer.left.shrink #dash {
+ margin: 0px;
+ padding: 0px; }
+ #dashtodockContainer.left.shrink #dash .dash-background {
+ margin: 0;
+ margin-left: 4px;
+ padding: 0;
+ border-radius: 18px;
+ spacing: 2px; }
+ #dashtodockContainer.left.shrink #dash .dash-separator {
+ background-color: rgba(255, 255, 255, 0.1);
+ width: 0;
+ height: 1px;
+ margin: 1px 0;
+ margin-left: 4px; }
+ #dashtodockContainer.left.shrink #dash #dashtodockDashContainer {
+ padding: 3px;
+ padding-left: 0;
+ padding-right: 0; }
+ #dashtodockContainer.left.shrink #dash .dash-item-container {
+ /* Disable all margins defined upstream, we handle them here */
+ margin: 2px 0; }
+ #dashtodockContainer.left.shrink #dash .dash-item-container > * {
+ margin: 0; }
+ #dashtodockContainer.left.shrink #dash .dash-item-container:first-child {
+ margin-top: 0; }
+ #dashtodockContainer.left.shrink #dash .dash-item-container:last-child {
+ margin-bottom: 0; }
+ #dashtodockContainer.left.shrink #dash .dash-item-container .app-well-app,
+ #dashtodockContainer.left.shrink #dash .dash-item-container .show-apps,
+ #dashtodockContainer.left.shrink #dash .dash-item-container .overview-tile {
+ padding: 1px 1px;
+ padding-left: 7px;
+ padding-right: 3px; }
+ #dashtodockContainer.left.shrink #dash .dash-item-container .app-well-app:first-child,
+ #dashtodockContainer.left.shrink #dash .dash-item-container .show-apps:first-child,
+ #dashtodockContainer.left.shrink #dash .dash-item-container .overview-tile:first-child {
+ padding-top: 0; }
+ #dashtodockContainer.left.shrink #dash .dash-item-container .app-well-app:last-child,
+ #dashtodockContainer.left.shrink #dash .dash-item-container .show-apps:last-child,
+ #dashtodockContainer.left.shrink #dash .dash-item-container .overview-tile:last-child {
+ padding-bottom: 0; }
+ #dashtodockContainer.left.shrink #dash .dash-item-container .app-well-app.updating,
+ #dashtodockContainer.left.shrink #dash .dash-item-container .show-apps.updating,
+ #dashtodockContainer.left.shrink #dash .dash-item-container .overview-tile.updating {
+ opacity: 0.5; }
+ #dashtodockContainer.left.shrink #dash .dash-item-container .app-well-app.running .overview-icon,
+ #dashtodockContainer.left.shrink #dash .dash-item-container .overview-tile.running .overview-icon {
+ background-image: none; }
+ #dashtodockContainer.left.shrink #dash .dash-item-container .app-well-app.focused .overview-icon,
+ #dashtodockContainer.left.shrink #dash .dash-item-container .overview-tile.focused .overview-icon {
+ background-color: rgba(238, 238, 238, 0.2); }
+ #dashtodockContainer.left.shrink #dash .dash-item-container .app-well-app .app-well-app-running-dot,
+ #dashtodockContainer.left.shrink #dash .dash-item-container .app-well-app .app-grid-running-dot,
+ #dashtodockContainer.left.shrink #dash .dash-item-container .overview-tile .app-well-app-running-dot,
+ #dashtodockContainer.left.shrink #dash .dash-item-container .overview-tile .app-grid-running-dot {
+ margin-bottom: 2px; }
+ #dashtodockContainer.left.shrink #dash .dash-item-container > StButton {
+ transition-duration: 250;
+ background-size: contain; }
+
+#dashtodockContainer.left.shrink.fixed #dash .dash-background {
+ margin-right: 1px; }
+
+#dashtodockContainer.left.shrink.fixed #dash .dash-item-container .app-well-app,
+#dashtodockContainer.left.shrink.fixed #dash .dash-item-container .show-apps {
+ padding-right: 4px; }
+
+.dash-label.left.shrink {
+ margin: 0;
+ -x-offset: 3px;
+ -y-offset: 0; }
+
+#dashtodockContainer.left.extended #dash {
+ margin: 0px;
+ padding: 0px; }
+ #dashtodockContainer.left.extended #dash .dash-background {
+ margin: 0;
+ margin-left: 0;
+ padding: 0;
+ border-radius: 0;
+ spacing: 6px; }
+ #dashtodockContainer.left.extended #dash .dash-separator {
+ background-color: rgba(255, 255, 255, 0.1);
+ width: 0;
+ height: 1px;
+ margin: 4px 0;
+ margin-left: 0; }
+ #dashtodockContainer.left.extended #dash #dashtodockDashContainer {
+ padding: 0;
+ padding-left: 0;
+ padding-right: 0; }
+ #dashtodockContainer.left.extended #dash #dashtodockDashContainer > :first-child .show-apps {
+ padding-top: 8px; }
+ #dashtodockContainer.left.extended #dash #dashtodockDashContainer #dashtodockDashScrollview:first-child {
+ padding-top: 8px; }
+ #dashtodockContainer.left.extended #dash #dashtodockDashContainer > :last-child .show-apps {
+ padding-bottom: 8px; }
+ #dashtodockContainer.left.extended #dash #dashtodockDashContainer #dashtodockDashScrollview:last-child {
+ padding-bottom: 8px; }
+ #dashtodockContainer.left.extended #dash .dash-item-container {
+ /* Disable all margins defined upstream, we handle them here */
+ margin: 2px 0; }
+ #dashtodockContainer.left.extended #dash .dash-item-container > * {
+ margin: 0; }
+ #dashtodockContainer.left.extended #dash .dash-item-container:first-child {
+ margin-top: 0; }
+ #dashtodockContainer.left.extended #dash .dash-item-container:last-child {
+ margin-bottom: 0; }
+ #dashtodockContainer.left.extended #dash .dash-item-container .app-well-app,
+ #dashtodockContainer.left.extended #dash .dash-item-container .show-apps,
+ #dashtodockContainer.left.extended #dash .dash-item-container .overview-tile {
+ padding: 2px 2px;
+ padding-left: 10px;
+ padding-right: 10px; }
+ #dashtodockContainer.left.extended #dash .dash-item-container .app-well-app:first-child,
+ #dashtodockContainer.left.extended #dash .dash-item-container .show-apps:first-child,
+ #dashtodockContainer.left.extended #dash .dash-item-container .overview-tile:first-child {
+ padding-top: 0; }
+ #dashtodockContainer.left.extended #dash .dash-item-container .app-well-app:last-child,
+ #dashtodockContainer.left.extended #dash .dash-item-container .show-apps:last-child,
+ #dashtodockContainer.left.extended #dash .dash-item-container .overview-tile:last-child {
+ padding-bottom: 0; }
+ #dashtodockContainer.left.extended #dash .dash-item-container .app-well-app.updating,
+ #dashtodockContainer.left.extended #dash .dash-item-container .show-apps.updating,
+ #dashtodockContainer.left.extended #dash .dash-item-container .overview-tile.updating {
+ opacity: 0.5; }
+ #dashtodockContainer.left.extended #dash .dash-item-container .app-well-app.running .overview-icon,
+ #dashtodockContainer.left.extended #dash .dash-item-container .overview-tile.running .overview-icon {
+ background-image: none; }
+ #dashtodockContainer.left.extended #dash .dash-item-container .app-well-app.focused .overview-icon,
+ #dashtodockContainer.left.extended #dash .dash-item-container .overview-tile.focused .overview-icon {
+ background-color: rgba(238, 238, 238, 0.2); }
+ #dashtodockContainer.left.extended #dash .dash-item-container .app-well-app .app-well-app-running-dot,
+ #dashtodockContainer.left.extended #dash .dash-item-container .app-well-app .app-grid-running-dot,
+ #dashtodockContainer.left.extended #dash .dash-item-container .overview-tile .app-well-app-running-dot,
+ #dashtodockContainer.left.extended #dash .dash-item-container .overview-tile .app-grid-running-dot {
+ margin-bottom: 2px; }
+ #dashtodockContainer.left.extended #dash .dash-item-container > StButton {
+ transition-duration: 250;
+ background-size: contain; }
+
+#dashtodockContainer.left.extended.fixed #dash .dash-background {
+ margin-right: 0; }
+
+#dashtodockContainer.left.extended.fixed #dash .dash-item-container .app-well-app,
+#dashtodockContainer.left.extended.fixed #dash .dash-item-container .show-apps {
+ padding-right: 10px; }
+
+.dash-label.left {
+ margin: 0;
+ -x-offset: 10px;
+ -y-offset: 0; }
+
+#dashtodockContainer.left.extended.shrink #dash {
+ margin: 0px;
+ padding: 0px; }
+ #dashtodockContainer.left.extended.shrink #dash .dash-background {
+ margin: 0;
+ margin-left: 0;
+ padding: 0;
+ border-radius: 0;
+ spacing: 2px; }
+ #dashtodockContainer.left.extended.shrink #dash .dash-separator {
+ background-color: rgba(255, 255, 255, 0.1);
+ width: 0;
+ height: 1px;
+ margin: 1px 0;
+ margin-left: 0; }
+ #dashtodockContainer.left.extended.shrink #dash #dashtodockDashContainer {
+ padding: 0;
+ padding-left: 0;
+ padding-right: 0; }
+ #dashtodockContainer.left.extended.shrink #dash #dashtodockDashContainer > :first-child .show-apps {
+ padding-top: 6px; }
+ #dashtodockContainer.left.extended.shrink #dash #dashtodockDashContainer #dashtodockDashScrollview:first-child {
+ padding-top: 6px; }
+ #dashtodockContainer.left.extended.shrink #dash #dashtodockDashContainer > :last-child .show-apps {
+ padding-bottom: 6px; }
+ #dashtodockContainer.left.extended.shrink #dash #dashtodockDashContainer #dashtodockDashScrollview:last-child {
+ padding-bottom: 6px; }
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container {
+ /* Disable all margins defined upstream, we handle them here */
+ margin: 2px 0; }
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container > * {
+ margin: 0; }
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container:first-child {
+ margin-top: 0; }
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container:last-child {
+ margin-bottom: 0; }
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .app-well-app,
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .show-apps,
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .overview-tile {
+ padding: 1px 1px;
+ padding-left: 3px;
+ padding-right: 3px; }
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .app-well-app:first-child,
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .show-apps:first-child,
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .overview-tile:first-child {
+ padding-top: 0; }
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .app-well-app:last-child,
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .show-apps:last-child,
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .overview-tile:last-child {
+ padding-bottom: 0; }
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .app-well-app.updating,
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .show-apps.updating,
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .overview-tile.updating {
+ opacity: 0.5; }
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .app-well-app.running .overview-icon,
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .overview-tile.running .overview-icon {
+ background-image: none; }
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .app-well-app.focused .overview-icon,
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .overview-tile.focused .overview-icon {
+ background-color: rgba(238, 238, 238, 0.2); }
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .app-well-app .app-well-app-running-dot,
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .app-well-app .app-grid-running-dot,
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .overview-tile .app-well-app-running-dot,
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .overview-tile .app-grid-running-dot {
+ margin-bottom: 2px; }
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container > StButton {
+ transition-duration: 250;
+ background-size: contain; }
+
+#dashtodockContainer.left.extended.shrink.fixed #dash .dash-background {
+ margin-right: 0; }
+
+#dashtodockContainer.left.extended.shrink.fixed #dash .dash-item-container .app-well-app,
+#dashtodockContainer.left.extended.shrink.fixed #dash .dash-item-container .show-apps {
+ padding-right: 3px; }
+
+.dash-label.left.shrink {
+ margin: 0;
+ -x-offset: 3px;
+ -y-offset: 0; }
+
+.dashtodock-app-well-preview-menu-item.left {
+ padding: 0.5em 0.5em; }
+ .dashtodock-app-well-preview-menu-item.left:first-child {
+ padding-top: 1em; }
+ .dashtodock-app-well-preview-menu-item.left:last-child {
+ padding-bottom: 1em; }
+
+#dashtodockContainer.right #dash {
+ margin: 0px;
+ padding: 0px; }
+ #dashtodockContainer.right #dash .dash-background {
+ margin: 0;
+ margin-right: 4px;
+ padding: 0;
+ border-radius: 24px;
+ spacing: 6px; }
+ #dashtodockContainer.right #dash .dash-separator {
+ background-color: rgba(255, 255, 255, 0.1);
+ width: 0;
+ height: 1px;
+ margin: 4px 0;
+ margin-right: 4px; }
+ #dashtodockContainer.right #dash #dashtodockDashContainer {
+ padding: 10px;
+ padding-right: 0;
+ padding-left: 0; }
+ #dashtodockContainer.right #dash .dash-item-container {
+ /* Disable all margins defined upstream, we handle them here */
+ margin: 2px 0; }
+ #dashtodockContainer.right #dash .dash-item-container > * {
+ margin: 0; }
+ #dashtodockContainer.right #dash .dash-item-container:first-child {
+ margin-top: 0; }
+ #dashtodockContainer.right #dash .dash-item-container:last-child {
+ margin-bottom: 0; }
+ #dashtodockContainer.right #dash .dash-item-container .app-well-app,
+ #dashtodockContainer.right #dash .dash-item-container .show-apps,
+ #dashtodockContainer.right #dash .dash-item-container .overview-tile {
+ padding: 2px 2px;
+ padding-right: 14px;
+ padding-left: 10px; }
+ #dashtodockContainer.right #dash .dash-item-container .app-well-app:first-child,
+ #dashtodockContainer.right #dash .dash-item-container .show-apps:first-child,
+ #dashtodockContainer.right #dash .dash-item-container .overview-tile:first-child {
+ padding-top: 0; }
+ #dashtodockContainer.right #dash .dash-item-container .app-well-app:last-child,
+ #dashtodockContainer.right #dash .dash-item-container .show-apps:last-child,
+ #dashtodockContainer.right #dash .dash-item-container .overview-tile:last-child {
+ padding-bottom: 0; }
+ #dashtodockContainer.right #dash .dash-item-container .app-well-app.updating,
+ #dashtodockContainer.right #dash .dash-item-container .show-apps.updating,
+ #dashtodockContainer.right #dash .dash-item-container .overview-tile.updating {
+ opacity: 0.5; }
+ #dashtodockContainer.right #dash .dash-item-container .app-well-app.running .overview-icon,
+ #dashtodockContainer.right #dash .dash-item-container .overview-tile.running .overview-icon {
+ background-image: none; }
+ #dashtodockContainer.right #dash .dash-item-container .app-well-app.focused .overview-icon,
+ #dashtodockContainer.right #dash .dash-item-container .overview-tile.focused .overview-icon {
+ background-color: rgba(238, 238, 238, 0.2); }
+ #dashtodockContainer.right #dash .dash-item-container .app-well-app .app-well-app-running-dot,
+ #dashtodockContainer.right #dash .dash-item-container .app-well-app .app-grid-running-dot,
+ #dashtodockContainer.right #dash .dash-item-container .overview-tile .app-well-app-running-dot,
+ #dashtodockContainer.right #dash .dash-item-container .overview-tile .app-grid-running-dot {
+ margin-bottom: 2px; }
+ #dashtodockContainer.right #dash .dash-item-container > StButton {
+ transition-duration: 250;
+ background-size: contain; }
+
+#dashtodockContainer.right.fixed #dash .dash-background {
+ margin-left: 4px; }
+
+#dashtodockContainer.right.fixed #dash .dash-item-container .app-well-app,
+#dashtodockContainer.right.fixed #dash .dash-item-container .show-apps {
+ padding-left: 14px; }
+
+.dash-label.right {
+ margin: 0;
+ -x-offset: 10px;
+ -y-offset: 0; }
+
+#dashtodockContainer.right.shrink #dash {
+ margin: 0px;
+ padding: 0px; }
+ #dashtodockContainer.right.shrink #dash .dash-background {
+ margin: 0;
+ margin-right: 4px;
+ padding: 0;
+ border-radius: 18px;
+ spacing: 2px; }
+ #dashtodockContainer.right.shrink #dash .dash-separator {
+ background-color: rgba(255, 255, 255, 0.1);
+ width: 0;
+ height: 1px;
+ margin: 1px 0;
+ margin-right: 4px; }
+ #dashtodockContainer.right.shrink #dash #dashtodockDashContainer {
+ padding: 3px;
+ padding-right: 0;
+ padding-left: 0; }
+ #dashtodockContainer.right.shrink #dash .dash-item-container {
+ /* Disable all margins defined upstream, we handle them here */
+ margin: 2px 0; }
+ #dashtodockContainer.right.shrink #dash .dash-item-container > * {
+ margin: 0; }
+ #dashtodockContainer.right.shrink #dash .dash-item-container:first-child {
+ margin-top: 0; }
+ #dashtodockContainer.right.shrink #dash .dash-item-container:last-child {
+ margin-bottom: 0; }
+ #dashtodockContainer.right.shrink #dash .dash-item-container .app-well-app,
+ #dashtodockContainer.right.shrink #dash .dash-item-container .show-apps,
+ #dashtodockContainer.right.shrink #dash .dash-item-container .overview-tile {
+ padding: 1px 1px;
+ padding-right: 7px;
+ padding-left: 3px; }
+ #dashtodockContainer.right.shrink #dash .dash-item-container .app-well-app:first-child,
+ #dashtodockContainer.right.shrink #dash .dash-item-container .show-apps:first-child,
+ #dashtodockContainer.right.shrink #dash .dash-item-container .overview-tile:first-child {
+ padding-top: 0; }
+ #dashtodockContainer.right.shrink #dash .dash-item-container .app-well-app:last-child,
+ #dashtodockContainer.right.shrink #dash .dash-item-container .show-apps:last-child,
+ #dashtodockContainer.right.shrink #dash .dash-item-container .overview-tile:last-child {
+ padding-bottom: 0; }
+ #dashtodockContainer.right.shrink #dash .dash-item-container .app-well-app.updating,
+ #dashtodockContainer.right.shrink #dash .dash-item-container .show-apps.updating,
+ #dashtodockContainer.right.shrink #dash .dash-item-container .overview-tile.updating {
+ opacity: 0.5; }
+ #dashtodockContainer.right.shrink #dash .dash-item-container .app-well-app.running .overview-icon,
+ #dashtodockContainer.right.shrink #dash .dash-item-container .overview-tile.running .overview-icon {
+ background-image: none; }
+ #dashtodockContainer.right.shrink #dash .dash-item-container .app-well-app.focused .overview-icon,
+ #dashtodockContainer.right.shrink #dash .dash-item-container .overview-tile.focused .overview-icon {
+ background-color: rgba(238, 238, 238, 0.2); }
+ #dashtodockContainer.right.shrink #dash .dash-item-container .app-well-app .app-well-app-running-dot,
+ #dashtodockContainer.right.shrink #dash .dash-item-container .app-well-app .app-grid-running-dot,
+ #dashtodockContainer.right.shrink #dash .dash-item-container .overview-tile .app-well-app-running-dot,
+ #dashtodockContainer.right.shrink #dash .dash-item-container .overview-tile .app-grid-running-dot {
+ margin-bottom: 2px; }
+ #dashtodockContainer.right.shrink #dash .dash-item-container > StButton {
+ transition-duration: 250;
+ background-size: contain; }
+
+#dashtodockContainer.right.shrink.fixed #dash .dash-background {
+ margin-left: 1px; }
+
+#dashtodockContainer.right.shrink.fixed #dash .dash-item-container .app-well-app,
+#dashtodockContainer.right.shrink.fixed #dash .dash-item-container .show-apps {
+ padding-left: 4px; }
+
+.dash-label.right.shrink {
+ margin: 0;
+ -x-offset: 3px;
+ -y-offset: 0; }
+
+#dashtodockContainer.right.extended #dash {
+ margin: 0px;
+ padding: 0px; }
+ #dashtodockContainer.right.extended #dash .dash-background {
+ margin: 0;
+ margin-right: 0;
+ padding: 0;
+ border-radius: 0;
+ spacing: 6px; }
+ #dashtodockContainer.right.extended #dash .dash-separator {
+ background-color: rgba(255, 255, 255, 0.1);
+ width: 0;
+ height: 1px;
+ margin: 4px 0;
+ margin-right: 0; }
+ #dashtodockContainer.right.extended #dash #dashtodockDashContainer {
+ padding: 0;
+ padding-right: 0;
+ padding-left: 0; }
+ #dashtodockContainer.right.extended #dash #dashtodockDashContainer > :first-child .show-apps {
+ padding-top: 8px; }
+ #dashtodockContainer.right.extended #dash #dashtodockDashContainer #dashtodockDashScrollview:first-child {
+ padding-top: 8px; }
+ #dashtodockContainer.right.extended #dash #dashtodockDashContainer > :last-child .show-apps {
+ padding-bottom: 8px; }
+ #dashtodockContainer.right.extended #dash #dashtodockDashContainer #dashtodockDashScrollview:last-child {
+ padding-bottom: 8px; }
+ #dashtodockContainer.right.extended #dash .dash-item-container {
+ /* Disable all margins defined upstream, we handle them here */
+ margin: 2px 0; }
+ #dashtodockContainer.right.extended #dash .dash-item-container > * {
+ margin: 0; }
+ #dashtodockContainer.right.extended #dash .dash-item-container:first-child {
+ margin-top: 0; }
+ #dashtodockContainer.right.extended #dash .dash-item-container:last-child {
+ margin-bottom: 0; }
+ #dashtodockContainer.right.extended #dash .dash-item-container .app-well-app,
+ #dashtodockContainer.right.extended #dash .dash-item-container .show-apps,
+ #dashtodockContainer.right.extended #dash .dash-item-container .overview-tile {
+ padding: 2px 2px;
+ padding-right: 10px;
+ padding-left: 10px; }
+ #dashtodockContainer.right.extended #dash .dash-item-container .app-well-app:first-child,
+ #dashtodockContainer.right.extended #dash .dash-item-container .show-apps:first-child,
+ #dashtodockContainer.right.extended #dash .dash-item-container .overview-tile:first-child {
+ padding-top: 0; }
+ #dashtodockContainer.right.extended #dash .dash-item-container .app-well-app:last-child,
+ #dashtodockContainer.right.extended #dash .dash-item-container .show-apps:last-child,
+ #dashtodockContainer.right.extended #dash .dash-item-container .overview-tile:last-child {
+ padding-bottom: 0; }
+ #dashtodockContainer.right.extended #dash .dash-item-container .app-well-app.updating,
+ #dashtodockContainer.right.extended #dash .dash-item-container .show-apps.updating,
+ #dashtodockContainer.right.extended #dash .dash-item-container .overview-tile.updating {
+ opacity: 0.5; }
+ #dashtodockContainer.right.extended #dash .dash-item-container .app-well-app.running .overview-icon,
+ #dashtodockContainer.right.extended #dash .dash-item-container .overview-tile.running .overview-icon {
+ background-image: none; }
+ #dashtodockContainer.right.extended #dash .dash-item-container .app-well-app.focused .overview-icon,
+ #dashtodockContainer.right.extended #dash .dash-item-container .overview-tile.focused .overview-icon {
+ background-color: rgba(238, 238, 238, 0.2); }
+ #dashtodockContainer.right.extended #dash .dash-item-container .app-well-app .app-well-app-running-dot,
+ #dashtodockContainer.right.extended #dash .dash-item-container .app-well-app .app-grid-running-dot,
+ #dashtodockContainer.right.extended #dash .dash-item-container .overview-tile .app-well-app-running-dot,
+ #dashtodockContainer.right.extended #dash .dash-item-container .overview-tile .app-grid-running-dot {
+ margin-bottom: 2px; }
+ #dashtodockContainer.right.extended #dash .dash-item-container > StButton {
+ transition-duration: 250;
+ background-size: contain; }
+
+#dashtodockContainer.right.extended.fixed #dash .dash-background {
+ margin-left: 0; }
+
+#dashtodockContainer.right.extended.fixed #dash .dash-item-container .app-well-app,
+#dashtodockContainer.right.extended.fixed #dash .dash-item-container .show-apps {
+ padding-left: 10px; }
+
+.dash-label.right {
+ margin: 0;
+ -x-offset: 10px;
+ -y-offset: 0; }
+
+#dashtodockContainer.right.extended.shrink #dash {
+ margin: 0px;
+ padding: 0px; }
+ #dashtodockContainer.right.extended.shrink #dash .dash-background {
+ margin: 0;
+ margin-right: 0;
+ padding: 0;
+ border-radius: 0;
+ spacing: 2px; }
+ #dashtodockContainer.right.extended.shrink #dash .dash-separator {
+ background-color: rgba(255, 255, 255, 0.1);
+ width: 0;
+ height: 1px;
+ margin: 1px 0;
+ margin-right: 0; }
+ #dashtodockContainer.right.extended.shrink #dash #dashtodockDashContainer {
+ padding: 0;
+ padding-right: 0;
+ padding-left: 0; }
+ #dashtodockContainer.right.extended.shrink #dash #dashtodockDashContainer > :first-child .show-apps {
+ padding-top: 6px; }
+ #dashtodockContainer.right.extended.shrink #dash #dashtodockDashContainer #dashtodockDashScrollview:first-child {
+ padding-top: 6px; }
+ #dashtodockContainer.right.extended.shrink #dash #dashtodockDashContainer > :last-child .show-apps {
+ padding-bottom: 6px; }
+ #dashtodockContainer.right.extended.shrink #dash #dashtodockDashContainer #dashtodockDashScrollview:last-child {
+ padding-bottom: 6px; }
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container {
+ /* Disable all margins defined upstream, we handle them here */
+ margin: 2px 0; }
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container > * {
+ margin: 0; }
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container:first-child {
+ margin-top: 0; }
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container:last-child {
+ margin-bottom: 0; }
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .app-well-app,
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .show-apps,
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .overview-tile {
+ padding: 1px 1px;
+ padding-right: 3px;
+ padding-left: 3px; }
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .app-well-app:first-child,
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .show-apps:first-child,
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .overview-tile:first-child {
+ padding-top: 0; }
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .app-well-app:last-child,
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .show-apps:last-child,
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .overview-tile:last-child {
+ padding-bottom: 0; }
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .app-well-app.updating,
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .show-apps.updating,
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .overview-tile.updating {
+ opacity: 0.5; }
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .app-well-app.running .overview-icon,
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .overview-tile.running .overview-icon {
+ background-image: none; }
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .app-well-app.focused .overview-icon,
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .overview-tile.focused .overview-icon {
+ background-color: rgba(238, 238, 238, 0.2); }
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .app-well-app .app-well-app-running-dot,
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .app-well-app .app-grid-running-dot,
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .overview-tile .app-well-app-running-dot,
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .overview-tile .app-grid-running-dot {
+ margin-bottom: 2px; }
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container > StButton {
+ transition-duration: 250;
+ background-size: contain; }
+
+#dashtodockContainer.right.extended.shrink.fixed #dash .dash-background {
+ margin-left: 0; }
+
+#dashtodockContainer.right.extended.shrink.fixed #dash .dash-item-container .app-well-app,
+#dashtodockContainer.right.extended.shrink.fixed #dash .dash-item-container .show-apps {
+ padding-left: 3px; }
+
+.dash-label.right.shrink {
+ margin: 0;
+ -x-offset: 3px;
+ -y-offset: 0; }
+
+.dashtodock-app-well-preview-menu-item.right {
+ padding: 0.5em 0.5em; }
+ .dashtodock-app-well-preview-menu-item.right:first-child {
+ padding-top: 1em; }
+ .dashtodock-app-well-preview-menu-item.right:last-child {
+ padding-bottom: 1em; }
+
+#dashtodockContainer.top.shrink #dash .dash-background {
+ margin-top: 4px;
+ margin-bottom: 0; }
+
+#dashtodockContainer.straight-corner #dash .dash-background,
+#dashtodockContainer.shrink.straight-corner #dash .dash-background {
+ border-radius: 0px; }
+
+/* Scrollview style */
+.bottom #dashtodockDashScrollview,
+.top #dashtodockDashScrollview {
+ -st-hfade-offset: 24px; }
+
+.left #dashtodockDashScrollview,
+.right #dashtodockDashScrollview {
+ -st-vfade-offset: 24px; }
+
+#dashtodockContainer.dashtodock #dash .dash-background {
+ background: #303030; }
+
+#dashtodockContainer.dashtodock .progress-bar,
+.overview-tile .progress-bar,
+.icon-grid .progress-bar {
+ /* Customization of the progress bar style. The possible elements
+ * are:
+ *
+ * -XXXXXX-YYYYYY: plain color
+ * -XXXXXX-YYYYYY-color-start: start color for gradient
+ * -XXXXXX-YYYYYY-color-end: end color for gradient
+ * -XXXXXX-YYYYYY-offset-start: offset for start color in gradient (0-1)
+ * -XXXXXX-YYYYYY-offset-end: offset for end color in gradient (0-1)
+ * -XXXXXX-line-width: width value for the line used for the border
+ *
+ * XXXXXX can be either
+ *
+ * progress-bar-track: the "track" over which the progress bar is shown
+ * progress-bar: the progress bar itself
+ *
+ * YYYYYY can be either
+ *
+ * background: for the background color
+ * border: for the border color
+ *
+ * plain color entries have priority over gradients, so if both are set for
+ * an element, the plain color will be used.
+ *
+ * Examples:
+ *
+ * -progress-bar-track-background: rgba(128, 128, 0, 0.75);
+ * -progress-bar-track-border: rgba(0, 0, 255, 0.25);
+ *
+ * -progress-bar-background: rgba(0, 204, 204, 1);
+ * -progress-bar-border: rgba(230, 230, 230, 1);
+ *
+ * Default values when these entries aren't set are equivalent to:
+ * -progress-bar-track-background-color-start: 64, 64, 64, 1.0
+ * -progress-bar-track-background-color-end: 89, 89, 89, 1.0
+ * -progress-bar-track-background-start-offset: 0.4
+ * -progress-bar-track-background-end-offset: 0.9
+ * -progress-bar-track-border-color-start: 128, 128, 128, 0.1
+ * -progress-bar-track-border-color-end: 89, 89, 89, 0.4
+ * -progress-bar-track-border-start-offset: 0.5
+ * -progress-bar-track-border-end-offset: 0.9
+ * -progress-bar-track-line-width: 1
+ * -progress-bar-background: 204, 204, 204, 1.0
+ * -progress-bar-border: 230, 230, 230, 1.0
+ * -progress-bar-height-factor: (0.20) 20% of the icon size
+ * -progress-bar-line-width: 1
+ * -progress-bar-top-offset: undefined
+ * -progress-bar-valign: 1.0
+ * -progress-bar-horizontal-padding: 0.05 (5% of icon size)
+ * -progress-bar-vertical-padding: 0.05 (5% of icon size)
+ */
+ -progress-bar-track-background: rgba(0, 0, 0, 0.45);
+ -progress-bar-track-border: rgba(0, 0, 0, 0.7);
+ -progress-bar-background: white;
+ -progress-bar-border: white; }
+
+#overview .progress-bar,
+.apps-scroll-view .progress-bar {
+ -progress-bar-height-factor: 0.15;
+ -progress-bar-top-offset: 0;
+ -progress-bar-valign: 0.95; }
+
+#overview .grid-search-results .overview-tile .progress-bar,
+#overview .app-folder-dialog .progress-bar {
+ -progress-bar-height-factor: 0.13; }
+
+#dashtodockContainer.top #dash .placeholder,
+#dashtodockContainer.bottom #dash .placeholder {
+ width: 32px;
+ height: 1px; }
+
+/*
+ * This is applied to a dummy actor. Only the alpha value for the background and border color
+ * and the transition-duration are used
+ */
+#dashtodockContainer.dummy-opaque {
+ background-color: rgba(0, 0, 0, 0.8);
+ border-color: rgba(0, 0, 0, 0.4);
+ transition-duration: 300ms; }
+
+/*
+ * This is applied to a dummy actor. Only the alpha value for the background and border color
+ * and the transition-duration are used
+ */
+#dashtodockContainer.dummy-transparent {
+ background-color: rgba(0, 0, 0, 0.2);
+ border-color: rgba(0, 0, 0, 0.1);
+ transition-duration: 500ms; }
+
+#dashtodockContainer .number-overlay,
+.overview-tile .number-overlay,
+.icon-grid .number-overlay {
+ color: white;
+ background-color: rgba(0, 0, 0, 0.8);
+ text-align: center; }
+
+#dashtodockContainer .notification-badge,
+.overview-tile .notification-badge,
+.icon-grid .notification-badge {
+ color: white;
+ background-color: red;
+ padding: 0.2em 0.5em;
+ border-radius: 1em;
+ font-weight: bold;
+ text-align: center;
+ margin: 2px; }
+
+#dashtodockPreviewSeparator.popup-separator-menu-item-horizontal {
+ width: 1px;
+ height: auto;
+ border-right-width: 1px;
+ margin: 32px 0px; }
+
+#dashtodockContainer .metro .overview-icon {
+ border-radius: 0px; }
+
+#dashtodockContainer.bottom .metro.running2.focused,
+#dashtodockContainer.top .metro.running2.focused {
+ background-image: url("./media/highlight_stacked_bg.svg");
+ background-position: 0px 0px;
+ background-size: contain; }
+
+#dashtodockContainer.left .metro.running2.focused,
+#dashtodockContainer.right .metro.running2.focused {
+ background-image: url("./media/highlight_stacked_bg_h.svg");
+ background-position: 0px 0px;
+ background-size: contain; }
+
+#dashtodockContainer.bottom .metro.running3.focused,
+#dashtodockContainer.top .metro.running3.focused {
+ background-image: url("./media/highlight_stacked_bg.svg");
+ background-position: 0px 0px;
+ background-size: contain; }
+
+#dashtodockContainer.left .metro.running3.focused,
+#dashtodockContainer.right .metro.running3.focused {
+ background-image: url("./media/highlight_stacked_bg_h.svg");
+ background-position: 0px 0px;
+ background-size: contain; }
+
+#dashtodockContainer.bottom .metro.running4.focused,
+#dashtodockContainer.top .metro.running4.focused {
+ background-image: url("./media/highlight_stacked_bg.svg");
+ background-position: 0px 0px;
+ background-size: contain; }
+
+#dashtodockContainer.left .metro.running4.focused,
+#dashtodockContainer.right .metro.running4.focused {
+ background-image: url("./media/highlight_stacked_bg_h.svg");
+ background-position: 0px 0px;
+ background-size: contain; }
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/theming.js b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/theming.js
new file mode 100644
index 0000000..611eb40
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/theming.js
@@ -0,0 +1,572 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+import {
+ Clutter,
+ Cogl,
+ GObject,
+ Meta,
+ St,
+} from './dependencies/gi.js';
+
+import {Main} from './dependencies/shell/ui.js';
+
+import {
+ Docking,
+ Utils,
+} from './imports.js';
+
+const {signals: Signals} = imports;
+
+/*
+ * DEFAULT: transparency given by theme
+ * FIXED: constant transparency chosen by user
+ * DYNAMIC: apply 'transparent' style when no windows are close to the dock
+ * */
+const TransparencyMode = {
+ DEFAULT: 0,
+ FIXED: 1,
+ DYNAMIC: 3,
+};
+
+const Labels = Object.freeze({
+ TRANSPARENCY: Symbol('transparency'),
+});
+
+export const PositionStyleClass = Object.freeze([
+ 'top',
+ 'right',
+ 'bottom',
+ 'left',
+]);
+
+/**
+ * Manage theme customization and custom theme support
+ */
+export class ThemeManager {
+ constructor(dock) {
+ this._signalsHandler = new Utils.GlobalSignalsHandler(this);
+ this._bindSettingsChanges();
+ this._actor = dock;
+ this._dash = dock.dash;
+
+ // initialize colors with generic values
+ this._customizedBackground = {red: 0, green: 0, blue: 0, alpha: 0};
+ this._customizedBorder = {red: 0, green: 0, blue: 0, alpha: 0};
+ this._transparency = new Transparency(dock);
+
+ this._signalsHandler.add([
+ // When theme changes re-obtain default background color
+ St.ThemeContext.get_for_stage(global.stage),
+ 'changed',
+ this.updateCustomTheme.bind(this),
+ ], [
+ // update :overview pseudoclass
+ Main.overview,
+ 'showing',
+ this._onOverviewShowing.bind(this),
+ ], [
+ Main.overview,
+ 'hiding',
+ this._onOverviewHiding.bind(this),
+ ]);
+
+ this._updateCustomStyleClasses();
+
+ // destroy themeManager when the managed actor is destroyed (e.g. extension unload)
+ // in order to disconnect signals
+ this._signalsHandler.add(this._actor, 'destroy', () => this.destroy());
+ }
+
+ destroy() {
+ this.emit('destroy');
+ this._transparency.destroy();
+ this._destroyed = true;
+ }
+
+ _onOverviewShowing() {
+ this._actor.add_style_pseudo_class('overview');
+ }
+
+ _onOverviewHiding() {
+ this._actor.remove_style_pseudo_class('overview');
+ }
+
+ _updateDashOpacity() {
+ const newAlpha = Docking.DockManager.settings.backgroundOpacity;
+
+ const [backgroundColor, borderColor] = this._getDefaultColors();
+
+ if (!backgroundColor)
+ return;
+
+ // Get the background and border alphas. We check the background alpha
+ // for a minimum of .001 to prevent division by 0 errors
+ const backgroundAlpha = Math.max(Math.round(backgroundColor.alpha / 2.55) / 100, .001);
+ let borderAlpha = Math.round(borderColor.alpha / 2.55) / 100;
+
+ // The border and background alphas should remain in sync
+ // We also limit the borderAlpha to a maximum of 1 (full opacity)
+ borderAlpha = Math.min((borderAlpha / backgroundAlpha) * newAlpha, 1);
+
+ this._customizedBackground = `rgba(${
+ backgroundColor.red},${
+ backgroundColor.green},${
+ backgroundColor.blue},${
+ newAlpha})`;
+
+ this._customizedBorder = `rgba(${
+ borderColor.red},${
+ borderColor.green},${
+ borderColor.blue},${
+ borderAlpha})`;
+ }
+
+ _getDefaultColors() {
+ // Prevent shell crash if the actor is not on the stage.
+ // It happens enabling/disabling repeatedly the extension
+ if (!this._dash._background.get_stage())
+ return [null, null];
+
+ // Remove custom style
+ const oldStyle = this._dash._background.get_style();
+ this._dash._background.set_style(null);
+
+ const themeNode = this._dash._background.get_theme_node();
+ this._dash._background.set_style(oldStyle);
+
+ const backgroundColor = themeNode.get_background_color();
+
+ // Just in case the theme has different border colors ..
+ // We want to find the inside border-color of the dock because it is
+ // the side most visible to the user. We do this by finding the side
+ // opposite the position
+ const position = Utils.getPosition();
+ let side = position + 2;
+ if (side > 3)
+ side = Math.abs(side - 4);
+
+ const borderColor = themeNode.get_border_color(side);
+
+ return [backgroundColor, borderColor];
+ }
+
+ _updateDashColor() {
+ // Retrieve the color. If needed we will adjust it before passing it to
+ // this._transparency.
+ let [backgroundColor] = this._getDefaultColors();
+
+ if (!backgroundColor)
+ return;
+
+ const {settings} = Docking.DockManager;
+
+ if (settings.customBackgroundColor) {
+ // When applying a custom color, we need to check the alpha value,
+ // if not the opacity will always be overridden by the color below.
+ // Note that if using 'dynamic' transparency modes,
+ // the opacity will be set by the opaque/transparent styles anyway.
+ let newAlpha = Math.round(backgroundColor.alpha / 2.55) / 100;
+
+ ({backgroundColor} = settings);
+ // backgroundColor is a string like rgb(0,0,0)
+ const Color = Clutter.Color ?? Cogl.Color;
+ const [ret, color] = Color.from_string(backgroundColor);
+ if (!ret) {
+ logError(new Error(`${backgroundColor} is not a valid color string`));
+ return;
+ }
+
+ if (settings.transparencyMode === TransparencyMode.FIXED) {
+ newAlpha = settings.backgroundOpacity;
+ this._customizedBackground =
+ `rgba(${color.red}, ${color.green}, ${color.blue}, ${newAlpha})`;
+ } else {
+ this._customizedBackground = backgroundColor;
+ }
+
+ this._customizedBorder = this._customizedBackground;
+
+ color.alpha = newAlpha * 255;
+ this._transparency.setColor(color);
+ } else {
+ // backgroundColor is a {Clutter,Cogl}.Color object
+ this._transparency.setColor(backgroundColor);
+ }
+ }
+
+ _updateCustomStyleClasses() {
+ const {settings} = Docking.DockManager;
+
+ if (settings.applyCustomTheme)
+ this._actor.add_style_class_name('dashtodock');
+ else
+ this._actor.remove_style_class_name('dashtodock');
+
+ if (settings.customThemeShrink)
+ this._actor.add_style_class_name('shrink');
+ else
+ this._actor.remove_style_class_name('shrink');
+
+ if (settings.runningIndicatorStyle !== 0)
+ this._actor.add_style_class_name('running-dots');
+ else
+ this._actor.remove_style_class_name('running-dots');
+
+ // If not the built-in theme option is not selected
+ if (!settings.applyCustomTheme) {
+ if (settings.forceStraightCorner)
+ this._actor.add_style_class_name('straight-corner');
+ else
+ this._actor.remove_style_class_name('straight-corner');
+ } else {
+ this._actor.remove_style_class_name('straight-corner');
+ }
+ }
+
+ updateCustomTheme() {
+ if (this._destroyed)
+ throw new Error(`Impossible to update a destroyed ${this.constructor.name}`);
+ this._updateCustomStyleClasses();
+ this._updateDashOpacity();
+ this._updateDashColor();
+ this._adjustTheme();
+ this.emit('updated');
+ }
+
+ /**
+ * Reimported back and adapted from atomdock
+ */
+ _adjustTheme() {
+ // Prevent shell crash if the actor is not on the stage.
+ // It happens enabling/disabling repeatedly the extension
+ if (!this._dash._background.get_stage())
+ return;
+
+ const {settings} = Docking.DockManager;
+
+ // Remove prior style edits
+ this._dash._background.set_style(null);
+ this._transparency.disable();
+
+ // If built-in theme is enabled do nothing else
+ if (settings.applyCustomTheme)
+ return;
+
+ let newStyle = '';
+ const position = Utils.getPosition(settings);
+
+ // obtain theme border settings
+ const themeNode = this._dash._background.get_theme_node();
+ const borderColor = themeNode.get_border_color(St.Side.TOP);
+ const borderWidth = themeNode.get_border_width(St.Side.TOP);
+
+ // We're copying border and corner styles to left border and top-left
+ // corner, also removing bottom border and bottom-right corner styles
+ let borderMissingStyle = '';
+
+ if (this._rtl && (position !== St.Side.RIGHT)) {
+ borderMissingStyle = `border-right: ${borderWidth}px solid ${
+ borderColor.to_string()};`;
+ } else if (!this._rtl && (position !== St.Side.LEFT)) {
+ borderMissingStyle = `border-left: ${borderWidth}px solid ${
+ borderColor.to_string()};`;
+ }
+
+ newStyle = borderMissingStyle;
+
+ if (newStyle) {
+ // I do call set_style possibly twice so that only the background gets the transition.
+ // The transition-property css rules seems to be unsupported
+ this._dash._background.set_style(newStyle);
+ }
+
+ // Customize background
+ const fixedTransparency = settings.transparencyMode === TransparencyMode.FIXED;
+ const defaultTransparency = settings.transparencyMode === TransparencyMode.DEFAULT;
+ if (!defaultTransparency && !fixedTransparency) {
+ this._transparency.enable();
+ } else if (!defaultTransparency || settings.customBackgroundColor) {
+ newStyle = `${newStyle}background-color:${this._customizedBackground}; ` +
+ `border-color:${this._customizedBorder}; ` +
+ 'transition-delay: 0s; transition-duration: 0.250s;';
+ this._dash._background.set_style(newStyle);
+ }
+ }
+
+ _bindSettingsChanges() {
+ const keys = ['transparency-mode',
+ 'customize-alphas',
+ 'min-alpha',
+ 'max-alpha',
+ 'background-opacity',
+ 'custom-background-color',
+ 'background-color',
+ 'apply-custom-theme',
+ 'custom-theme-shrink',
+ 'custom-theme-running-dots',
+ 'extend-height',
+ 'force-straight-corner'];
+
+ this._signalsHandler.add(...keys.map(key => [
+ Docking.DockManager.settings,
+ `changed::${key}`,
+ () => this.updateCustomTheme(),
+ ]));
+ }
+}
+Signals.addSignalMethods(ThemeManager.prototype);
+
+/**
+ * The following class is based on the following upstream commit:
+ * https://git.gnome.org/browse/gnome-shell/commit/?id=447bf55e45b00426ed908b1b1035f472c2466956
+ * Transparency when free-floating
+ */
+class Transparency {
+ constructor(dock) {
+ this._dash = dock.dash;
+ this._actor = this._dash._container;
+ this._backgroundActor = this._dash._background;
+ this._dockActor = dock;
+ this._dock = dock;
+ this._panel = Main.panel;
+ this._position = Utils.getPosition();
+
+ // All these properties are replaced with the ones in the .dummy-opaque
+ // and .dummy-transparent css classes
+ this._backgroundColor = '0,0,0';
+ this._transparentAlpha = '0.2';
+ this._opaqueAlpha = '1';
+ this._transparentAlphaBorder = '0.1';
+ this._opaqueAlphaBorder = '0.5';
+ this._transparentTransition = '0ms';
+ this._opaqueTransition = '0ms';
+ this._base_actor_style = '';
+
+ this._signalsHandler = new Utils.GlobalSignalsHandler();
+ this._trackedWindows = new Map();
+ }
+
+ enable() {
+ // ensure I never double-register/inject
+ // although it should never happen
+ this.disable();
+
+ this._base_actor_style = this._actor.get_style();
+ if (!this._base_actor_style)
+ this._base_actor_style = '';
+
+
+ let addedSignal = 'child-added';
+ let removedSignal = 'child-removed';
+
+ // for compatibility with Gnome Shell 45
+ if (GObject.signal_lookup('actor-added', global.window_group)) {
+ addedSignal = 'actor-added';
+ removedSignal = 'actor-removed';
+ }
+
+ this._signalsHandler.addWithLabel(Labels.TRANSPARENCY, [
+ global.window_group,
+ addedSignal,
+ this._onWindowActorAdded.bind(this),
+ ], [
+ global.window_group,
+ removedSignal,
+ this._onWindowActorRemoved.bind(this),
+ ], [
+ global.window_manager,
+ 'switch-workspace',
+ this._updateSolidStyle.bind(this),
+ ], [
+ Main.overview,
+ 'hiding',
+ this._updateSolidStyle.bind(this),
+ ], [
+ Main.overview,
+ 'showing',
+ this._updateSolidStyle.bind(this),
+ ]);
+
+ // Window signals
+ global.window_group.get_children().filter(child => {
+ // An irrelevant window actor ('Gnome-shell') produces an error when the signals are
+ // disconnected, therefore do not add signals to it.
+ return child instanceof Meta.WindowActor &&
+ child.get_meta_window().get_wm_class() !== 'Gnome-shell';
+ }).forEach(function (win) {
+ this._onWindowActorAdded(null, win);
+ }, this);
+
+ if (this._actor.get_stage())
+ this._updateSolidStyle();
+
+ this._updateStyles();
+ this._updateSolidStyle();
+
+ this.emit('transparency-enabled');
+ }
+
+ disable() {
+ // ensure I never double-register/inject
+ // although it should never happen
+ this._signalsHandler.removeWithLabel(Labels.TRANSPARENCY);
+
+ for (const key of this._trackedWindows.keys()) {
+ this._trackedWindows.get(key).forEach(id => {
+ key.disconnect(id);
+ });
+ }
+ this._trackedWindows.clear();
+
+ this.emit('transparency-disabled');
+ }
+
+ destroy() {
+ this.disable();
+ this._signalsHandler.destroy();
+ }
+
+ _onWindowActorAdded(container, metaWindowActor) {
+ const signalIds = [];
+ ['notify::allocation', 'notify::visible'].forEach(s => {
+ signalIds.push(metaWindowActor.connect(s, this._updateSolidStyle.bind(this)));
+ });
+ this._trackedWindows.set(metaWindowActor, signalIds);
+ }
+
+ _onWindowActorRemoved(container, metaWindowActor) {
+ if (!this._trackedWindows.get(metaWindowActor))
+ return;
+
+ this._trackedWindows.get(metaWindowActor).forEach(id => {
+ metaWindowActor.disconnect(id);
+ });
+ this._trackedWindows.delete(metaWindowActor);
+ this._updateSolidStyle();
+ }
+
+ _updateSolidStyle() {
+ const isNear = this._dockIsNear();
+ if (isNear) {
+ this._backgroundActor.set_style(this._opaque_style);
+ this._dockActor.remove_style_class_name('transparent');
+ this._dockActor.add_style_class_name('opaque');
+ } else {
+ this._backgroundActor.set_style(this._transparent_style);
+ this._dockActor.remove_style_class_name('opaque');
+ this._dockActor.add_style_class_name('transparent');
+ }
+
+ this.emit('solid-style-updated', isNear);
+ }
+
+ _dockIsNear() {
+ if (this._dockActor.has_style_pseudo_class('overview'))
+ return false;
+ /* Get all the windows in the active workspace that are in the primary monitor and visible */
+ const activeWorkspace = global.workspace_manager.get_active_workspace();
+ const dash = this._dash;
+ const windows = activeWorkspace.list_windows().filter(metaWindow => {
+ return metaWindow.get_monitor() === dash._monitorIndex &&
+ metaWindow.showing_on_its_workspace() &&
+ metaWindow.get_window_type() !== Meta.WindowType.DESKTOP &&
+ !metaWindow.skip_taskbar;
+ });
+
+ /* Check if at least one window is near enough to the panel.
+ * If the dock is hidden, we need to account for the space it would take
+ * up when it slides out. This is avoid an ugly transition.
+ * */
+ let factor = 0;
+ if (!Docking.DockManager.settings.dockFixed &&
+ this._dock.getDockState() === Docking.State.HIDDEN)
+ factor = 1;
+ const [leftCoord, topCoord] = this._actor.get_transformed_position();
+ let threshold;
+ if (this._position === St.Side.LEFT)
+ threshold = leftCoord + this._actor.get_width() * (factor + 1);
+ else if (this._position === St.Side.RIGHT)
+ threshold = leftCoord - this._actor.get_width() * factor;
+ else if (this._position === St.Side.TOP)
+ threshold = topCoord + this._actor.get_height() * (factor + 1);
+ else
+ threshold = topCoord - this._actor.get_height() * factor;
+
+ const scale = St.ThemeContext.get_for_stage(global.stage).scale_factor;
+ const isNearEnough = windows.some(metaWindow => {
+ let coord;
+ if (this._position === St.Side.LEFT) {
+ coord = metaWindow.get_frame_rect().x;
+ return coord < threshold + 5 * scale;
+ } else if (this._position === St.Side.RIGHT) {
+ coord = metaWindow.get_frame_rect().x + metaWindow.get_frame_rect().width;
+ return coord > threshold - 5 * scale;
+ } else if (this._position === St.Side.TOP) {
+ coord = metaWindow.get_frame_rect().y;
+ return coord < threshold + 5 * scale;
+ } else {
+ coord = metaWindow.get_frame_rect().y + metaWindow.get_frame_rect().height;
+ return coord > threshold - 5 * scale;
+ }
+ });
+
+ return isNearEnough;
+ }
+
+ _updateStyles() {
+ this._getAlphas();
+
+ this._transparent_style = `${this._base_actor_style
+ }background-color: rgba(${
+ this._backgroundColor}, ${this._transparentAlpha});` +
+ `border-color: rgba(${
+ this._backgroundColor}, ${this._transparentAlphaBorder});` +
+ `transition-duration: ${this._transparentTransition}ms;`;
+
+ this._opaque_style = `${this._base_actor_style
+ }background-color: rgba(${
+ this._backgroundColor}, ${this._opaqueAlpha});` +
+ `border-color: rgba(${
+ this._backgroundColor},${this._opaqueAlphaBorder});` +
+ `transition-duration: ${this._opaqueTransition}ms;`;
+
+ this.emit('styles-updated');
+ }
+
+ setColor(color) {
+ this._backgroundColor = `${color.red},${color.green},${color.blue}`;
+ this._updateStyles();
+ }
+
+ _getAlphas() {
+ // Create dummy object and add to the uiGroup to get it to the stage
+ const dummyObject = new St.Bin({
+ name: 'dashtodockContainer',
+ });
+ Main.uiGroup.add_child(dummyObject);
+
+ dummyObject.add_style_class_name('dummy-opaque');
+ let themeNode = dummyObject.get_theme_node();
+ this._opaqueAlpha = themeNode.get_background_color().alpha / 255;
+ this._opaqueAlphaBorder = themeNode.get_border_color(0).alpha / 255;
+ this._opaqueTransition = themeNode.get_transition_duration();
+
+ dummyObject.add_style_class_name('dummy-transparent');
+ themeNode = dummyObject.get_theme_node();
+ this._transparentAlpha = themeNode.get_background_color().alpha / 255;
+ this._transparentAlphaBorder = themeNode.get_border_color(0).alpha / 255;
+ this._transparentTransition = themeNode.get_transition_duration();
+
+ Main.uiGroup.remove_child(dummyObject);
+
+ const {settings} = Docking.DockManager;
+
+ if (settings.customizeAlphas) {
+ this._opaqueAlpha = settings.maxAlpha;
+ this._opaqueAlphaBorder = this._opaqueAlpha / 2;
+ this._transparentAlpha = settings.minAlpha;
+ this._transparentAlphaBorder = this._transparentAlpha / 2;
+ }
+ }
+}
+Signals.addSignalMethods(Transparency.prototype);
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/utils.js b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/utils.js
new file mode 100644
index 0000000..67b4d58
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/utils.js
@@ -0,0 +1,735 @@
+import {
+ Clutter,
+ GLib,
+ Gio,
+ GObject,
+ Meta,
+ Shell,
+ St,
+} from './dependencies/gi.js';
+
+import {
+ Docking,
+} from './imports.js';
+
+const {_gi: Gi} = imports;
+
+export const SignalsHandlerFlags = Object.freeze({
+ NONE: 0,
+ CONNECT_AFTER: 1,
+});
+
+const GENERIC_KEY = Symbol('generic');
+
+/**
+ * Simplify global signals and function injections handling
+ * abstract class
+ */
+const BasicHandler = class DashToDockBasicHandler {
+ static get genericKey() {
+ return GENERIC_KEY;
+ }
+
+ constructor(parentObject) {
+ this._storage = Object.create(null);
+
+ if (parentObject) {
+ if (!(parentObject.connect instanceof Function))
+ throw new TypeError('Not a valid parent object');
+
+ if (!(parentObject instanceof GObject.Object) ||
+ GObject.signal_lookup('destroy', parentObject.constructor.$gtype)) {
+ this._parentObject = parentObject;
+ this._destroyId = parentObject.connect('destroy', () => this.destroy());
+ }
+ }
+ }
+
+ add(...args) {
+ // Convert arguments object to array, concatenate with generic
+ // Call addWithLabel with ags as if they were passed arguments
+ this.addWithLabel(GENERIC_KEY, ...args);
+ }
+
+ clear() {
+ Object.getOwnPropertySymbols(this._storage).forEach(label =>
+ this.removeWithLabel(label));
+ }
+
+ destroy() {
+ this._parentObject?.disconnect(this._destroyId);
+ this._parentObject = null;
+
+ this.clear();
+ }
+
+ block() {
+ Object.getOwnPropertySymbols(this._storage).forEach(label =>
+ this.blockWithLabel(label));
+ }
+
+ unblock() {
+ Object.getOwnPropertySymbols(this._storage).forEach(label =>
+ this.unblockWithLabel(label));
+ }
+
+ addWithLabel(label, ...args) {
+ if (typeof label !== 'symbol')
+ throw new Error(`Invalid label ${label}, must be a symbol`);
+
+ let argsArray = [...args];
+ if (argsArray.every(arg => !Array.isArray(arg)))
+ argsArray = [argsArray];
+
+ if (this._storage[label] === undefined)
+ this._storage[label] = [];
+
+ // Skip first element of the arguments
+ for (const argArray of argsArray) {
+ if (argArray.length < 3)
+ throw new Error('Unexpected number of arguments');
+ const item = this._storage[label];
+ try {
+ item.push(this._create(...argArray));
+ } catch (e) {
+ logError(e);
+ }
+ }
+ }
+
+ removeWithLabel(label) {
+ this._storage[label]?.reverse().forEach(item => this._remove(item));
+ delete this._storage[label];
+ }
+
+ blockWithLabel(label) {
+ (this._storage[label] || []).forEach(item => this._block(item));
+ }
+
+ unblockWithLabel(label) {
+ (this._storage[label] || []).forEach(item => this._unblock(item));
+ }
+
+ _removeByItem(item) {
+ Object.getOwnPropertySymbols(this._storage).forEach(label =>
+ (this._storage[label] = this._storage[label].filter(it => {
+ if (!this._itemsEqual(it, item))
+ return true;
+ this._remove(item);
+ return false;
+ })));
+ }
+
+ // Virtual methods to be implemented by subclass
+
+ /**
+ * Create single element to be stored in the storage structure
+ *
+ * @param _object
+ * @param _element
+ * @param _callback
+ */
+ _create(_object, _element, _callback) {
+ throw new GObject.NotImplementedError(`_create in ${this.constructor.name}`);
+ }
+
+ /**
+ * Correctly delete single element
+ *
+ * @param _item
+ */
+ _remove(_item) {
+ throw new GObject.NotImplementedError(`_remove in ${this.constructor.name}`);
+ }
+
+ /**
+ * Block single element
+ *
+ * @param _item
+ */
+ _block(_item) {
+ throw new GObject.NotImplementedError(`_block in ${this.constructor.name}`);
+ }
+
+ /**
+ * Unblock single element
+ *
+ * @param _item
+ */
+ _unblock(_item) {
+ throw new GObject.NotImplementedError(`_unblock in ${this.constructor.name}`);
+ }
+
+ _itemsEqual(itemA, itemB) {
+ if (itemA === itemB)
+ return true;
+
+ if (itemA.length !== itemB.length)
+ return false;
+
+ return itemA.every((_, idx) => itemA[idx] === itemB[idx]);
+ }
+};
+
+/**
+ * Manage global signals
+ */
+export class GlobalSignalsHandler extends BasicHandler {
+ _create(object, event, callback, flags = SignalsHandlerFlags.NONE) {
+ if (!object)
+ throw new Error('Impossible to connect to an invalid object');
+
+ const after = flags === SignalsHandlerFlags.CONNECT_AFTER;
+ const connector = after ? object.connect_after : object.connect;
+
+ if (!connector) {
+ throw new Error(`Requested to connect to signal '${event}', ` +
+ `but no implementation for 'connect${after ? '_after' : ''}' ` +
+ `found in ${object.constructor.name}`);
+ }
+
+ const item = [object];
+ const isDestroy = event === 'destroy';
+ const isParentObject = object === this._parentObject;
+
+ if (isDestroy && !isParentObject) {
+ const originalCallback = callback;
+ callback = () => {
+ this._removeByItem(item);
+ originalCallback();
+ };
+ }
+ const id = connector.call(object, event, callback);
+ item.push(id);
+
+ if (isDestroy && isParentObject) {
+ this._parentObject.disconnect(this._destroyId);
+ this._destroyId =
+ this._parentObject.connect('destroy', () => this.destroy());
+ }
+
+ return item;
+ }
+
+ _remove(item) {
+ const [object, id] = item;
+ object.disconnect(id);
+ }
+
+ _block(item) {
+ const [object, id] = item;
+
+ if (object instanceof GObject.Object)
+ GObject.Object.prototype.block_signal_handler.call(object, id);
+ }
+
+ _unblock(item) {
+ const [object, id] = item;
+
+ if (object instanceof GObject.Object)
+ GObject.Object.prototype.unblock_signal_handler.call(object, id);
+ }
+}
+
+/**
+ * Color manipulation utilities
+ */
+export class ColorUtils {
+ // Darken or brigthen color by a fraction dlum
+ // Each rgb value is modified by the same fraction.
+ // Return "#rrggbb" string
+ static ColorLuminance(r, g, b, dlum) {
+ let rgbString = '#';
+
+ rgbString += ColorUtils._decimalToHex(Math.round(Math.min(Math.max(r * (1 + dlum), 0), 255)), 2);
+ rgbString += ColorUtils._decimalToHex(Math.round(Math.min(Math.max(g * (1 + dlum), 0), 255)), 2);
+ rgbString += ColorUtils._decimalToHex(Math.round(Math.min(Math.max(b * (1 + dlum), 0), 255)), 2);
+
+ return rgbString;
+ }
+
+ // Convert decimal to an hexadecimal string adding the desired padding
+ static _decimalToHex(d, padding) {
+ let hex = d.toString(16);
+ while (hex.length < padding)
+ hex = `0${hex}`;
+ return hex;
+ }
+
+ // Convert hsv ([0-1, 0-1, 0-1]) to rgb ([0-255, 0-255, 0-255]).
+ // Following algorithm in https://en.wikipedia.org/wiki/HSL_and_HSV
+ // here with h = [0,1] instead of [0, 360]
+ // Accept either (h,s,v) independently or {h:h, s:s, v:v} object.
+ // Return {r:r, g:g, b:b} object.
+ static HSVtoRGB(h, s, v) {
+ if (arguments.length === 1)
+ ({s, v, h} = h);
+
+ let r, g, b;
+ const c = v * s;
+ const h1 = h * 6;
+ const x = c * (1 - Math.abs(h1 % 2 - 1));
+ const m = v - c;
+
+ if (h1 <= 1) {
+ r = c + m;
+ g = x + m;
+ b = m;
+ } else if (h1 <= 2) {
+ r = x + m;
+ g = c + m;
+ b = m;
+ } else if (h1 <= 3) {
+ r = m;
+ g = c + m;
+ b = x + m;
+ } else if (h1 <= 4) {
+ r = m;
+ g = x + m;
+ b = c + m;
+ } else if (h1 <= 5) {
+ r = x + m;
+ g = m;
+ b = c + m;
+ } else {
+ r = c + m;
+ g = m;
+ b = x + m;
+ }
+
+ return {
+ r: Math.round(r * 255),
+ g: Math.round(g * 255),
+ b: Math.round(b * 255),
+ };
+ }
+
+ // Convert rgb ([0-255, 0-255, 0-255]) to hsv ([0-1, 0-1, 0-1]).
+ // Following algorithm in https://en.wikipedia.org/wiki/HSL_and_HSV
+ // here with h = [0,1] instead of [0, 360]
+ // Accept either (r,g,b) independently or {r:r, g:g, b:b} object.
+ // Return {h:h, s:s, v:v} object.
+ static RGBtoHSV(r, g, b) {
+ if (arguments.length === 1)
+ ({r, g, b} = r);
+
+ let h, s;
+
+ const M = Math.max(r, g, b);
+ const m = Math.min(r, g, b);
+ const c = M - m;
+
+ if (c === 0)
+ h = 0;
+ else if (M === r)
+ h = ((g - b) / c) % 6;
+ else if (M === g)
+ h = (b - r) / c + 2;
+ else
+ h = (r - g) / c + 4;
+
+ h /= 6;
+ const v = M / 255;
+ if (M !== 0)
+ s = c / M;
+ else
+ s = 0;
+
+ return {
+ h,
+ s,
+ v,
+ };
+ }
+}
+
+/**
+ * Manage function injection: both instances and prototype can be overridden
+ * and restored
+ */
+export class InjectionsHandler extends BasicHandler {
+ _create(object, name, injectedFunction) {
+ const original = object[name];
+
+ if (!(original instanceof Function))
+ throw new Error(`Function ${name}() is not available for ${object}`);
+
+ object[name] = function (...args) {
+ return injectedFunction.call(this, original, ...args);
+ };
+ return [object, name, original];
+ }
+
+ _remove(item) {
+ const [object, name, original] = item;
+ object[name] = original;
+ }
+}
+
+/**
+ * Manage vfunction injection: both instances and prototype can be overridden
+ * and restored
+ */
+export class VFuncInjectionsHandler extends BasicHandler {
+ _create(prototype, name, injectedFunction) {
+ const original = prototype[`vfunc_${name}`];
+ if (!(original instanceof Function))
+ throw new Error(`Virtual function ${name} is not available for ${prototype}`);
+ this._replaceVFunc(prototype, name, injectedFunction);
+ return [prototype, name];
+ }
+
+ _remove(item) {
+ const [prototype, name] = item;
+ const originalVFunc = prototype[`vfunc_${name}`];
+ try {
+ // This may fail if trying to reset to a never-overridden vfunc
+ // as gjs doesn't consider it a function, even if it's true that
+ // originalVFunc instanceof Function.
+ this._replaceVFunc(prototype, name, originalVFunc);
+ } catch {
+ try {
+ this._replaceVFunc(prototype, name, function (...args) {
+ // eslint-disable-next-line no-invalid-this
+ return originalVFunc.call(this, ...args);
+ });
+ } catch (e) {
+ logError(e, `Removing vfunc_${name}`);
+ }
+ }
+ }
+
+ _replaceVFunc(prototype, name, func) {
+ if (Gi.gobject_prototype_symbol && Gi.gobject_prototype_symbol in prototype)
+ prototype = prototype[Gi.gobject_prototype_symbol];
+
+ return prototype[Gi.hook_up_vfunc_symbol](name, func);
+ }
+}
+
+/**
+ * Manage properties injection: both instances and prototype can be overridden
+ * and restored
+ */
+export class PropertyInjectionsHandler extends BasicHandler {
+ constructor(parentObject, params) {
+ super(parentObject);
+ this._params = params;
+ }
+
+ _create(instance, name, injectedPropertyDescriptor) {
+ if (!this._params?.allowNewProperty && !(name in instance))
+ throw new Error(`Object ${instance} has no '${name}' property`);
+
+ const {prototype} = instance.constructor;
+ const originalPropertyDescriptor = Object.getOwnPropertyDescriptor(prototype, name) ??
+ Object.getOwnPropertyDescriptor(instance, name);
+
+ Object.defineProperty(instance, name, {
+ ...originalPropertyDescriptor,
+ ...injectedPropertyDescriptor,
+ ...{configurable: true},
+ });
+ return [instance, name, originalPropertyDescriptor];
+ }
+
+ _remove(item) {
+ const [instance, name, originalPropertyDescriptor] = item;
+ if (originalPropertyDescriptor)
+ Object.defineProperty(instance, name, originalPropertyDescriptor);
+ else
+ delete instance[name];
+ }
+}
+
+/**
+ * Return the actual position reverseing left and right in rtl
+ */
+export function getPosition() {
+ const position = Docking.DockManager.settings.dockPosition;
+ if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL) {
+ if (position === St.Side.LEFT)
+ return St.Side.RIGHT;
+ else if (position === St.Side.RIGHT)
+ return St.Side.LEFT;
+ }
+ return position;
+}
+
+/**
+ * @param cr
+ * @param x
+ * @param y
+ * @param width
+ * @param height
+ * @param isRoundLeft
+ * @param isRoundRight
+ * @param stroke
+ * @param fill
+ */
+export function drawRoundedLine(cr, x, y, width, height, isRoundLeft, isRoundRight, stroke, fill) {
+ if (height > width) {
+ y += Math.floor((height - width) / 2.0);
+ height = width;
+ }
+
+ height = 2.0 * Math.floor(height / 2.0);
+
+ const leftRadius = isRoundLeft ? height / 2.0 : 0.0;
+ const rightRadius = isRoundRight ? height / 2.0 : 0.0;
+
+ cr.moveTo(x + width - rightRadius, y);
+ cr.lineTo(x + leftRadius, y);
+ if (isRoundLeft)
+ cr.arcNegative(x + leftRadius, y + leftRadius, leftRadius, -Math.PI / 2, Math.PI / 2);
+ else
+ cr.lineTo(x, y + height);
+ cr.lineTo(x + width - rightRadius, y + height);
+ if (isRoundRight)
+ cr.arcNegative(x + width - rightRadius, y + rightRadius, rightRadius, Math.PI / 2, -Math.PI / 2);
+ else
+ cr.lineTo(x + width, y);
+ cr.closePath();
+
+ if (fill) {
+ cr.setSource(fill);
+ cr.fillPreserve();
+ }
+ if (stroke)
+ cr.setSource(stroke);
+ cr.stroke();
+}
+
+/**
+ * Convert a signal handler with n value parameters (that is, excluding the
+ * signal source parameter) to an array of n handlers that are each responsible
+ * for receiving one of the n values and calling the original handler with the
+ * most up-to-date arguments.
+ *
+ * @param handler
+ */
+export function splitHandler(handler) {
+ if (handler.length > 30)
+ throw new Error('too many parameters');
+
+ const count = handler.length - 1;
+ let missingValueBits = (1 << count) - 1;
+ const values = Array.from({length: count});
+ return values.map((_ignored, i) => {
+ const mask = ~(1 << i);
+ return (obj, value) => {
+ values[i] = value;
+ missingValueBits &= mask;
+ if (missingValueBits === 0)
+ handler(obj, ...values);
+ };
+ });
+}
+
+/**
+ * Construct a map of gtk application window object paths to MetaWindows.
+ */
+export function getWindowsByObjectPath() {
+ const windowsByObjectPath = new Map();
+ const {workspaceManager} = global;
+ const workspaces = [...new Array(workspaceManager.nWorkspaces)].map(
+ (_c, i) => workspaceManager.get_workspace_by_index(i));
+
+ workspaces.forEach(ws => {
+ ws.list_windows().forEach(w => {
+ const path = w.get_gtk_window_object_path();
+ if (path)
+ windowsByObjectPath.set(path, w);
+ });
+ });
+
+ return windowsByObjectPath;
+}
+
+/**
+ * Re-implements shell_app_compare so that can be used to resort running apps
+ *
+ * @param appA
+ * @param appB
+ */
+export function shellAppCompare(appA, appB) {
+ if (appA.state !== appB.state) {
+ if (appA.state === Shell.AppState.RUNNING)
+ return -1;
+ return 1;
+ }
+
+ const windowsA = appA.get_windows();
+ const windowsB = appB.get_windows();
+
+ const isMinimized = windows => !windows.some(w => w.showing_on_its_workspace());
+ const minimizedB = isMinimized(windowsB);
+ if (isMinimized(windowsA) !== minimizedB) {
+ if (minimizedB)
+ return -1;
+ return 1;
+ }
+
+ if (appA.state === Shell.AppState.RUNNING) {
+ if (windowsA.length && !windowsB.length)
+ return -1;
+ else if (!windowsA.length && windowsB.length)
+ return 1;
+
+ const lastUserTime = windows =>
+ Math.max(...windows.map(w => w.get_user_time()));
+ return lastUserTime(windowsB) - lastUserTime(windowsA);
+ }
+
+ return 0;
+}
+
+/**
+ * Re-implements shell_app_compare_windows
+ *
+ * @param winA
+ * @param winB
+ */
+export function shellWindowsCompare(winA, winB) {
+ const activeWorkspace = global.workspaceManager.get_active_workspace();
+ const wsA = winA.get_workspace() === activeWorkspace;
+ const wsB = winB.get_workspace() === activeWorkspace;
+
+ if (wsA && !wsB)
+ return -1;
+ else if (!wsA && wsB)
+ return 1;
+
+ const visA = winA.showing_on_its_workspace();
+ const visB = winB.showing_on_its_workspace();
+
+ if (visA && !visB)
+ return -1;
+ else if (!visA && visB)
+ return 1;
+
+ return winB.get_user_time() - winA.get_user_time();
+}
+
+export const CancellableChild = GObject.registerClass({
+ Properties: {
+ 'parent': GObject.ParamSpec.object(
+ 'parent', 'parent', 'parent',
+ GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
+ Gio.Cancellable.$gtype),
+ },
+},
+class CancellableChild extends Gio.Cancellable {
+ _init(parent) {
+ if (parent && !(parent instanceof Gio.Cancellable))
+ throw TypeError('Not a valid cancellable');
+
+ super._init({parent});
+
+ if (parent?.is_cancelled()) {
+ this.cancel();
+ return;
+ }
+
+ this._connectToParent();
+ }
+
+ _connectToParent() {
+ this._connectId = this.parent?.connect(() => {
+ this._realCancel();
+
+ if (this._disconnectIdle)
+ return;
+
+ this._disconnectIdle = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
+ delete this._disconnectIdle;
+ this._disconnectFromParent();
+ return GLib.SOURCE_REMOVE;
+ });
+ });
+ }
+
+ _disconnectFromParent() {
+ if (this._connectId && !this._disconnectIdle) {
+ this.parent.disconnect(this._connectId);
+ delete this._connectId;
+ }
+ }
+
+ _realCancel() {
+ Gio.Cancellable.prototype.cancel.call(this);
+ }
+
+ cancel() {
+ this._disconnectFromParent();
+ this._realCancel();
+ }
+});
+
+/**
+ *
+ */
+export function getMonitorManager() {
+ return global.backend.get_monitor_manager?.() ?? Meta.MonitorManager.get();
+}
+
+/**
+ * @param laterType
+ * @param callback
+ */
+export function laterAdd(laterType, callback) {
+ return global.compositor?.get_laters?.().add(laterType, callback) ??
+ Meta.later_add(laterType, callback);
+}
+
+/**
+ * @param id
+ */
+export function laterRemove(id) {
+ if (global.compositor?.get_laters)
+ global.compositor?.get_laters().remove(id);
+ else
+ Meta.later_remove(id);
+}
+
+/**
+ * Up to Gnome Shell 45, the Cairo Context object didn't export the
+ * `setSourceColor()` method, so Clutter included a function call for
+ * that, written in C. In Gnome Shell 46, the method was finally exported,
+ * so that function was removed.
+ *
+ * This function is, thus, required for Gnome Shell 45 compatibility.
+ *
+ * @param {*} cr A cairo context
+ * @param {*} sourceColor The new color for source
+ */
+export function cairoSetSourceColor(cr, sourceColor) {
+ if (Clutter.cairo_set_source_color)
+ Clutter.cairo_set_source_color(cr, sourceColor);
+ else
+ cr.setSourceColor(sourceColor);
+}
+
+/**
+ * Specifies if the system supports extended barriers. This function
+ * is required for Gnome Shell 45 compatibility, which used
+ * `global.display.supports_extended_barriers`. Gnome Shell 46 moved
+ * that into global.backend.capabilities.
+ *
+ * @returns True if the system supports extended barriers.
+ */
+export function supportsExtendedBarriers() {
+ if (global.display.supports_extended_barriers)
+ return global.display.supports_extended_barriers();
+ return !!(global.backend.capabilities & Meta.BackendCapabilities.BARRIERS);
+}
+
+export function addActor(element, actor) {
+ if (element.add_actor)
+ element.add_actor(actor);
+ else
+ element.add_child(actor);
+}
+
+export const clamp = (v, m, M) => Math.min(Math.max(v, m), M);
+export const clampDouble = v => clamp(v, 0, 1);
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/windowPreview.js b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/windowPreview.js
new file mode 100644
index 0000000..337d5f1
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/windowPreview.js
@@ -0,0 +1,678 @@
+/*
+ * Credits:
+ * This file is based on code from the Dash to Panel extension by Jason DeRose
+ * and code from the Taskbar extension by Zorin OS
+ * Some code was also adapted from the upstream Gnome Shell source code.
+ */
+
+import {
+ Clutter,
+ GLib,
+ GObject,
+ Meta,
+ St,
+} from './dependencies/gi.js';
+
+import {
+ BoxPointer,
+ Main,
+ PopupMenu,
+ Workspace,
+} from './dependencies/shell/ui.js';
+
+import {
+ Docking,
+ Theming,
+ Utils,
+} from './imports.js';
+
+const PREVIEW_MAX_WIDTH = 250;
+const PREVIEW_MAX_HEIGHT = 150;
+
+const PREVIEW_ANIMATION_DURATION = 250;
+const MAX_PREVIEW_GENERATION_ATTEMPTS = 15;
+
+const MENU_MARGINS = 10;
+
+export class WindowPreviewMenu extends PopupMenu.PopupMenu {
+ constructor(source) {
+ super(source, 0.5, Utils.getPosition());
+
+ // We want to keep the item hovered while the menu is up
+ this.blockSourceEvents = true;
+
+ this._source = source;
+ this._app = this._source.app;
+ const workArea = Main.layoutManager.getWorkAreaForMonitor(
+ this._source.monitorIndex);
+ const {scaleFactor} = St.ThemeContext.get_for_stage(global.stage);
+
+ this.actor.add_style_class_name('app-menu');
+ this.actor.set_style(
+ `max-width: ${Math.round(workArea.width / scaleFactor) - MENU_MARGINS}px; ` +
+ `max-height: ${Math.round(workArea.height / scaleFactor) - MENU_MARGINS}px;`);
+ this.actor.hide();
+
+ // Chain our visibility and lifecycle to that of the source
+ this._mappedId = this._source.connect('notify::mapped', () => {
+ if (!this._source.mapped)
+ this.close();
+ });
+ this._destroyId = this._source.connect('destroy', this.destroy.bind(this));
+
+ Utils.addActor(Main.uiGroup, this.actor);
+
+ this.connect('destroy', this._onDestroy.bind(this));
+ }
+
+ _redisplay() {
+ if (this._previewBox)
+ this._previewBox.destroy();
+ this._previewBox = new WindowPreviewList(this._source);
+ this.addMenuItem(this._previewBox);
+ this._previewBox._redisplay();
+ }
+
+ popup() {
+ const windows = this._source.getInterestingWindows();
+ if (windows.length > 0) {
+ this._redisplay();
+ this.open(BoxPointer.PopupAnimation.FULL);
+ this.actor.navigate_focus(null, St.DirectionType.TAB_FORWARD, false);
+ this._source.emit('sync-tooltip');
+ }
+ }
+
+ _onDestroy() {
+ if (this._mappedId)
+ this._source.disconnect(this._mappedId);
+
+ if (this._destroyId)
+ this._source.disconnect(this._destroyId);
+ }
+}
+
+class WindowPreviewList extends PopupMenu.PopupMenuSection {
+ constructor(source) {
+ super();
+ this.actor = new St.ScrollView({
+ name: 'dashtodockWindowScrollview',
+ hscrollbar_policy: St.PolicyType.NEVER,
+ vscrollbar_policy: St.PolicyType.NEVER,
+ overlay_scrollbars: true,
+ enable_mouse_scrolling: true,
+ });
+
+ this.actor.connect('scroll-event', this._onScrollEvent.bind(this));
+
+ const position = Utils.getPosition();
+ this.isHorizontal = position === St.Side.BOTTOM || position === St.Side.TOP;
+ this.box.set_vertical(!this.isHorizontal);
+ this.box.set_name('dashtodockWindowList');
+ Utils.addActor(this.actor, this.box);
+ this.actor._delegate = this;
+
+ this._shownInitially = false;
+
+ this._source = source;
+ this.app = source.app;
+
+ this._redisplayId = Main.initializeDeferredWork(this.actor, this._redisplay.bind(this));
+
+ this.actor.connect('destroy', this._onDestroy.bind(this));
+ this._stateChangedId = this.app.connect('windows-changed',
+ this._queueRedisplay.bind(this));
+ }
+
+ _queueRedisplay() {
+ Main.queueDeferredWork(this._redisplayId);
+ }
+
+ _onScrollEvent(actor, event) {
+ // Event coordinates are relative to the stage but can be transformed
+ // as the actor will only receive events within his bounds.
+ const [stageX, stageY] = event.get_coords();
+ const [,, eventY] = actor.transform_stage_point(stageX, stageY);
+ const [, actorH] = actor.get_size();
+
+ // If the scroll event is within a 1px margin from
+ // the relevant edge of the actor, let the event propagate.
+ if (eventY >= actorH - 2)
+ return Clutter.EVENT_PROPAGATE;
+
+ // Skip to avoid double events mouse
+ if (event.is_pointer_emulated())
+ return Clutter.EVENT_STOP;
+
+ let adjustment, delta;
+
+ if (this.isHorizontal)
+ adjustment = this.actor.get_hscroll_bar().get_adjustment();
+ else
+ adjustment = this.actor.get_vscroll_bar().get_adjustment();
+
+ const increment = adjustment.step_increment;
+
+ switch (event.get_scroll_direction()) {
+ case Clutter.ScrollDirection.UP:
+ delta = -increment;
+ break;
+ case Clutter.ScrollDirection.DOWN:
+ delta = Number(increment);
+ break;
+ case Clutter.ScrollDirection.SMOOTH: {
+ const [dx, dy] = event.get_scroll_delta();
+ delta = dy * increment;
+ delta += dx * increment;
+ break;
+ }
+ }
+
+ adjustment.set_value(adjustment.get_value() + delta);
+
+ return Clutter.EVENT_STOP;
+ }
+
+ _onDestroy() {
+ this.app.disconnect(this._stateChangedId);
+ this._stateChangedId = 0;
+ }
+
+ _createPreviewItem(window) {
+ const preview = new WindowPreviewMenuItem(window, Utils.getPosition());
+ return preview;
+ }
+
+ _redisplay() {
+ const children = this._getMenuItems().filter(actor => {
+ return actor._window;
+ });
+
+ // Windows currently on the menu
+ const oldWin = children.map(actor => {
+ return actor._window;
+ });
+
+ // All app windows with a static order
+ const newWin = this._source.getInterestingWindows().sort((a, b) =>
+ a.get_stable_sequence() > b.get_stable_sequence());
+
+ const addedItems = [];
+ const removedActors = [];
+
+ let newIndex = 0;
+ let oldIndex = 0;
+
+ while (newIndex < newWin.length || oldIndex < oldWin.length) {
+ const currentOldWin = oldWin[oldIndex];
+ const currentNewWin = newWin[newIndex];
+
+ // No change at oldIndex/newIndex
+ if (currentOldWin === currentNewWin) {
+ oldIndex++;
+ newIndex++;
+ continue;
+ }
+
+ // Window removed at oldIndex
+ if (currentOldWin && !newWin.includes(currentOldWin)) {
+ removedActors.push(children[oldIndex]);
+ oldIndex++;
+ continue;
+ }
+
+ // Window added at newIndex
+ if (currentNewWin && !oldWin.includes(currentNewWin)) {
+ addedItems.push({
+ item: this._createPreviewItem(currentNewWin),
+ pos: newIndex,
+ });
+ newIndex++;
+ continue;
+ }
+
+ // Window moved
+ const insertHere = newWin[newIndex + 1] &&
+ newWin[newIndex + 1] === currentOldWin;
+ const alreadyRemoved = removedActors.reduce((result, actor) =>
+ result || actor._window === currentNewWin, false);
+
+ if (insertHere || alreadyRemoved) {
+ addedItems.push({
+ item: this._createPreviewItem(currentNewWin),
+ pos: newIndex + removedActors.length,
+ });
+ newIndex++;
+ } else {
+ removedActors.push(children[oldIndex]);
+ oldIndex++;
+ }
+ }
+
+ for (let i = 0; i < addedItems.length; i++) {
+ this.addMenuItem(addedItems[i].item,
+ addedItems[i].pos);
+ }
+
+ for (let i = 0; i < removedActors.length; i++) {
+ const item = removedActors[i];
+ if (this._shownInitially)
+ item._animateOutAndDestroy();
+ else
+ item.actor.destroy();
+ }
+
+ // Skip animations on first run when adding the initial set
+ // of items, to avoid all items zooming in at once
+ const animate = this._shownInitially;
+
+ if (!this._shownInitially)
+ this._shownInitially = true;
+
+ for (let i = 0; i < addedItems.length; i++)
+ addedItems[i].item.show(animate);
+
+ // Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=692744
+ // Without it, StBoxLayout may use a stale size cache
+ this.box.queue_relayout();
+
+ if (newWin.length < 1)
+ this._getTopMenu().close(~0);
+
+ // As for upstream:
+ // St.ScrollView always requests space horizontally for a possible vertical
+ // scrollbar if in AUTOMATIC mode. Doing better would require implementation
+ // of width-for-height in St.BoxLayout and St.ScrollView. This looks bad
+ // when we *don't* need it, so turn off the scrollbar when that's true.
+ // Dynamic changes in whether we need it aren't handled properly.
+ const needsScrollbar = this._needsScrollbar();
+ const scrollbarPolicy = needsScrollbar
+ ? St.PolicyType.AUTOMATIC : St.PolicyType.NEVER;
+ if (this.isHorizontal)
+ this.actor.hscrollbarPolicy = scrollbarPolicy;
+ else
+ this.actor.vscrollbarPolicy = scrollbarPolicy;
+
+ if (needsScrollbar)
+ this.actor.add_style_pseudo_class('scrolled');
+ else
+ this.actor.remove_style_pseudo_class('scrolled');
+ }
+
+ _needsScrollbar() {
+ const topMenu = this._getTopMenu();
+ const topThemeNode = topMenu.actor.get_theme_node();
+ if (this.isHorizontal) {
+ const [topMinWidth_, topNaturalWidth] =
+ topMenu.actor.get_preferred_width(-1);
+ const topMaxWidth = topThemeNode.get_max_width();
+ return topMaxWidth >= 0 && topNaturalWidth >= topMaxWidth;
+ } else {
+ const [topMinHeight_, topNaturalHeight] =
+ topMenu.actor.get_preferred_height(-1);
+ const topMaxHeight = topThemeNode.get_max_height();
+ return topMaxHeight >= 0 && topNaturalHeight >= topMaxHeight;
+ }
+ }
+
+ isAnimatingOut() {
+ return this.actor.get_children().reduce((result, actor) => {
+ return result || actor.animatingOut;
+ }, false);
+ }
+}
+
+export const WindowPreviewMenuItem = GObject.registerClass(
+class WindowPreviewMenuItem extends PopupMenu.PopupBaseMenuItem {
+ _init(window, position, params) {
+ super._init(params);
+
+ this._window = window;
+ this._destroyId = 0;
+ this._windowAddedId = 0;
+
+ // We don't want this: it adds spacing on the left of the item.
+ this.remove_child(this._ornamentIcon);
+ this.add_style_class_name('dashtodock-app-well-preview-menu-item');
+ this.add_style_class_name(Theming.PositionStyleClass[position]);
+ if (Docking.DockManager.settings.customThemeShrink)
+ this.add_style_class_name('shrink');
+
+ // Now we don't have to set PREVIEW_MAX_WIDTH and PREVIEW_MAX_HEIGHT as
+ // preview size - that made all kinds of windows either stretched or
+ // squished (aspect ratio problem)
+ this._cloneBin = new St.Bin();
+
+ this._updateWindowPreviewSize();
+
+ // TODO: improve the way the closebutton is layout. Just use some padding
+ // for the moment.
+ this._cloneBin.set_style('padding-bottom: 0.5em');
+
+ const buttonLayout = Meta.prefs_get_button_layout();
+ this.closeButton = new St.Button({
+ style_class: 'window-close',
+ opacity: 0,
+ x_expand: true,
+ y_expand: true,
+ x_align: buttonLayout.left_buttons.includes(Meta.ButtonFunction.CLOSE)
+ ? Clutter.ActorAlign.START : Clutter.ActorAlign.END,
+ y_align: Clutter.ActorAlign.START,
+ });
+ Utils.addActor(this.closeButton, new St.Icon({icon_name: 'window-close-symbolic'}));
+ this.closeButton.connect('clicked', () => this._closeWindow());
+
+ const overlayGroup = new Clutter.Actor({
+ layout_manager: new Clutter.BinLayout(),
+ y_expand: true,
+ });
+
+ overlayGroup.add_child(this._cloneBin);
+ overlayGroup.add_child(this.closeButton);
+
+ const label = new St.Label({text: window.get_title()});
+ label.set_style(`max-width: ${PREVIEW_MAX_WIDTH}px`);
+ const labelBin = new St.Bin({
+ child: label,
+ x_align: Clutter.ActorAlign.CENTER,
+ });
+
+ this._windowTitleId = this._window.connect('notify::title', () => {
+ label.set_text(this._window.get_title());
+ });
+
+ const box = new St.BoxLayout({
+ vertical: true,
+ reactive: true,
+ x_expand: true,
+ });
+
+ if (box.add) {
+ box.add(overlayGroup);
+ box.add(labelBin);
+ } else {
+ box.add_child(overlayGroup);
+ box.add_child(labelBin);
+ }
+ this._box = box;
+ this.add_child(box);
+
+ this._cloneTexture(window);
+
+ this.connect('destroy', this._onDestroy.bind(this));
+ }
+
+ vfunc_style_changed() {
+ super.vfunc_style_changed();
+
+ // For some crazy clutter / St reason we can't just have this handled
+ // automatically or here via vfunc_allocate + vfunc_get_preferred_*
+ // because if we do so, the St paddings on first / last child are lost
+ const themeNode = this.get_theme_node();
+ let [minWidth, naturalWidth] = this._box.get_preferred_width(-1);
+ let [minHeight, naturalHeight] = this._box.get_preferred_height(naturalWidth);
+ [minWidth, naturalWidth] = themeNode.adjust_preferred_width(minWidth, naturalWidth);
+ [minHeight, naturalHeight] = themeNode.adjust_preferred_height(minHeight, naturalHeight);
+ this.set({minWidth, naturalWidth, minHeight, naturalHeight});
+ }
+
+ _getWindowPreviewSize() {
+ const emptySize = [0, 0, 0];
+
+ const mutterWindow = this._window.get_compositor_private();
+ if (!mutterWindow?.get_texture())
+ return emptySize;
+
+ const [width, height] = mutterWindow.get_size();
+ if (!width || !height)
+ return emptySize;
+
+ let {previewSizeScale: scale} = Docking.DockManager.settings;
+ if (!scale) {
+ // a simple example with 1680x1050:
+ // * 250/1680 = 0,1488
+ // * 150/1050 = 0,1429
+ // => scale is 0,1429
+ scale = Math.min(1.0, PREVIEW_MAX_WIDTH / width, PREVIEW_MAX_HEIGHT / height);
+ }
+
+ scale *= St.ThemeContext.get_for_stage(global.stage).scaleFactor;
+
+ // width and height that we wanna multiply by scale
+ return [width, height, scale];
+ }
+
+ _updateWindowPreviewSize() {
+ // This gets the actual windows size for the preview
+ [this._width, this._height, this._scale] = this._getWindowPreviewSize();
+ this._cloneBin.set_size(this._width * this._scale, this._height * this._scale);
+ }
+
+ _cloneTexture(metaWin) {
+ // Newly-created windows are added to a workspace before
+ // the compositor finds out about them...
+ if (!this._width || !this._height) {
+ this._cloneTextureLater = Utils.laterAdd(Meta.LaterType.BEFORE_REDRAW, () => {
+ // Check if there's still a point in getting the texture,
+ // otherwise this could go on indefinitely
+ this._updateWindowPreviewSize();
+
+ if (this._width && this._height) {
+ this._cloneTexture(metaWin);
+ } else {
+ this._cloneAttempt = (this._cloneAttempt || 0) + 1;
+ if (this._cloneAttempt < MAX_PREVIEW_GENERATION_ATTEMPTS)
+ return GLib.SOURCE_CONTINUE;
+ }
+ delete this._cloneTextureLater;
+ return GLib.SOURCE_REMOVE;
+ });
+ return;
+ }
+
+ const mutterWindow = metaWin.get_compositor_private();
+ const clone = new Clutter.Clone({
+ source: mutterWindow,
+ reactive: true,
+ width: this._width * this._scale,
+ height: this._height * this._scale,
+ });
+
+ // when the source actor is destroyed, i.e. the window closed, first destroy the clone
+ // and then destroy the menu item (do this animating out)
+ this._destroyId = mutterWindow.connect('destroy', () => {
+ clone.destroy();
+ this._destroyId = 0; // avoid to try to disconnect this signal from mutterWindow in _onDestroy(),
+ // as the object was just destroyed
+ this._animateOutAndDestroy();
+ });
+
+ this._clone = clone;
+ this._mutterWindow = mutterWindow;
+ this._cloneBin.set_child(this._clone);
+
+ this._clone.connect('destroy', () => {
+ if (this._destroyId) {
+ mutterWindow.disconnect(this._destroyId);
+ this._destroyId = 0;
+ }
+ this._clone = null;
+ });
+ }
+
+ _windowCanClose() {
+ return this._window.can_close() &&
+ !this._hasAttachedDialogs();
+ }
+
+ _closeWindow() {
+ this._workspace = this._window.get_workspace();
+
+ // This mechanism is copied from the workspace.js upstream code
+ // It forces window activation if the windows don't get closed,
+ // for instance because asking user confirmation, by monitoring the opening of
+ // such additional confirmation window
+ this._windowAddedId = this._workspace.connect('window-added',
+ this._onWindowAdded.bind(this));
+
+ this.deleteAllWindows();
+ }
+
+ deleteAllWindows() {
+ // Delete all windows, starting from the bottom-most (most-modal) one
+ // let windows = this._window.get_compositor_private().get_children();
+ const windows = this._clone.get_children();
+ for (let i = windows.length - 1; i >= 1; i--) {
+ const realWindow = windows[i].source;
+ const metaWindow = realWindow.meta_window;
+
+ metaWindow.delete(global.get_current_time());
+ }
+
+ this._window.delete(global.get_current_time());
+ }
+
+ _onWindowAdded(workspace, win) {
+ const metaWindow = this._window;
+
+ if (win.get_transient_for() === metaWindow) {
+ workspace.disconnect(this._windowAddedId);
+ this._windowAddedId = 0;
+
+ // use an idle handler to avoid mapping problems -
+ // see comment in Workspace._windowAdded
+ const activationEvent = Clutter.get_current_event();
+ this._windowAddedLater = Utils.laterAdd(Meta.LaterType.BEFORE_REDRAW, () => {
+ delete this._windowAddedLater;
+ this.emit('activate', activationEvent);
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+ }
+
+ _hasAttachedDialogs() {
+ // count transient windows
+ let n = 0;
+ this._window.foreach_transient(() => {
+ n++;
+ });
+ return n > 0;
+ }
+
+ vfunc_key_focus_in() {
+ super.vfunc_key_focus_in();
+ this._showCloseButton();
+ }
+
+ vfunc_key_focus_out() {
+ super.vfunc_key_focus_out();
+ this._hideCloseButton();
+ }
+
+ vfunc_enter_event(crossingEvent) {
+ this._showCloseButton();
+ return super.vfunc_enter_event(crossingEvent);
+ }
+
+ vfunc_leave_event(crossingEvent) {
+ this._hideCloseButton();
+ return super.vfunc_leave_event(crossingEvent);
+ }
+
+ _idleToggleCloseButton() {
+ this._idleToggleCloseId = 0;
+
+ this._hideCloseButton();
+
+ return GLib.SOURCE_REMOVE;
+ }
+
+ _showCloseButton() {
+ if (this._windowCanClose()) {
+ this.closeButton.show();
+ this.closeButton.remove_all_transitions();
+ this.closeButton.ease({
+ opacity: 255,
+ duration: Workspace.WINDOW_OVERLAY_FADE_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ }
+ }
+
+ _hideCloseButton() {
+ if (this.closeButton.has_pointer ||
+ this.get_children().some(a => a.has_pointer))
+ return;
+
+ this.closeButton.remove_all_transitions();
+ this.closeButton.ease({
+ opacity: 0,
+ duration: Workspace.WINDOW_OVERLAY_FADE_TIME,
+ mode: Clutter.AnimationMode.EASE_IN_QUAD,
+ });
+ }
+
+ show(animate) {
+ const fullWidth = this.get_width();
+
+ this.opacity = 0;
+ this.set_width(0);
+
+ const time = animate ? PREVIEW_ANIMATION_DURATION : 0;
+ this.remove_all_transitions();
+ this.ease({
+ opacity: 255,
+ width: fullWidth,
+ duration: time,
+ mode: Clutter.AnimationMode.EASE_IN_OUT_QUAD,
+ });
+ }
+
+ _animateOutAndDestroy() {
+ this.remove_all_transitions();
+ this.ease({
+ opacity: 0,
+ duration: PREVIEW_ANIMATION_DURATION,
+ });
+
+ this.ease({
+ width: 0,
+ height: 0,
+ duration: PREVIEW_ANIMATION_DURATION,
+ delay: PREVIEW_ANIMATION_DURATION,
+ onComplete: () => this.destroy(),
+ });
+ }
+
+ activate() {
+ Main.activateWindow(this._window);
+ this._getTopMenu().close();
+ }
+
+ _onDestroy() {
+ if (this._cloneTextureLater) {
+ Utils.laterRemove(this._cloneTextureLater);
+ delete this._cloneTextureLater;
+ }
+
+ if (this._windowAddedLater) {
+ Utils.laterRemove(this._windowAddedLater);
+ delete this._windowAddedLater;
+ }
+
+ if (this._windowAddedId > 0) {
+ this._workspace.disconnect(this._windowAddedId);
+ this._windowAddedId = 0;
+ }
+
+ if (this._destroyId > 0) {
+ this._mutterWindow.disconnect(this._destroyId);
+ this._destroyId = 0;
+ }
+
+ if (this._windowTitleId > 0) {
+ this._window.disconnect(this._windowTitleId);
+ this._windowTitleId = 0;
+ }
+ }
+});